From c463113ea9af31e69daa9b9fce97947cd2ec2283 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 10 Aug 2026 02:05:58 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A5=20Sync=202026-08-10=2002:05:58?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LICENSE | 340 ++++++++++++++++++ luci-app-passwall2/Makefile | 2 +- .../model/cbi/passwall2/client/acl_config.lua | 12 - .../cbi/passwall2/client/shunt_rules.lua | 13 - .../model/cbi/passwall2/client/type/ray.lua | 19 +- .../cbi/passwall2/client/type/sing-box.lua | 19 +- .../model/cbi/passwall2/server/type/ray.lua | 9 - luci-app-passwall2/luasrc/passwall2/api.lua | 40 ++- luci-theme-footstrap/Makefile | 2 +- .../styles/base/40-tables.css | 74 ++-- .../styles/pages/90-processes.css | 27 ++ luci-theme-footstrap/styles/theme/45-misc.css | 21 ++ .../ucode/template/themes/footstrap/header.ut | 2 +- .../themes/footstrap/partials/head.ut | 22 +- openwrt-natmapt/Makefile | 4 +- 15 files changed, 504 insertions(+), 102 deletions(-) create mode 100644 LICENSE create mode 100644 luci-theme-footstrap/styles/pages/90-processes.css diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..d60c31a9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/luci-app-passwall2/Makefile b/luci-app-passwall2/Makefile index ee4030a9..a1629ad8 100644 --- a/luci-app-passwall2/Makefile +++ b/luci-app-passwall2/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-passwall2 PKG_VERSION:=26.8.7 -PKG_RELEASE:=74 +PKG_RELEASE:=75 PKG_PO_VERSION:=$(PKG_VERSION) PKG_CONFIG_DEPENDS:= \ diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/acl_config.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/acl_config.lua index 2ba194c7..1272d660 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/acl_config.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/acl_config.lua @@ -23,17 +23,6 @@ for k, e in ipairs(api.get_valid_nodes()) do nodes_table[#nodes_table + 1] = e end -local dynamicList_write = function(self, section, value) - local new_t = {} - if type(value) == "table" then - new_t = api.table_remove_duplicates(value) - else - new_t = { value } - end - local new_val = table.concat(new_t, " ") - return DynamicList.write(self, section, new_val) -end - local doh_validate = function(self, value, t) if value ~= "" then local flag = 0 @@ -168,7 +157,6 @@ sources.validate = function(self, value, t) return value end -sources.write = dynamicList_write o = s:option(ListValue, "mode", translate("Mode")) o:value("0", translate("No Proxy")) diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/shunt_rules.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/shunt_rules.lua index f751a04a..2e59c050 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/shunt_rules.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/shunt_rules.lua @@ -167,19 +167,6 @@ source.validate = function(self, value, t) return value end -local dynamicList_write = function(self, section, value) - local new_t = {} - if type(value) == "table" then - new_t = api.table_remove_duplicates(value) - else - new_t = { value } - end - local new_val = table.concat(new_t, " ") - return DynamicList.write(self, section, new_val) -end - -source.write = dynamicList_write - --[[ -- Too low usage rate, hidden sourcePort = s:option(Value, "sourcePort", translate("Source port")) diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua index 3b1198ec..69c74cb9 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua @@ -116,12 +116,12 @@ if load_balancing_options then -- [[ Load balancing Start ]] end end -- Reading the old DynamicList - function o.cfgvalue(self, section) - return m.uci:get_list(appname, section, "balancing_node") or {} + function o.custom_cfgvalue(self, section) + return table.concat(m:get(section, "balancing_node") or {}, " ") end -- Write-and-hold DynamicList function o.custom_write(self, section, value) - local old = m.uci:get_list(appname, section, "balancing_node") or {} + local old = m:get(section, "balancing_node") or {} local new, set = {}, {} for v in value:gmatch("%S+") do new[#new + 1] = v @@ -129,13 +129,13 @@ if load_balancing_options then -- [[ Load balancing Start ]] end for _, v in ipairs(old) do if not set[v] then - m.uci:set_list(appname, section, "balancing_node", new) + m:set(section, "balancing_node", new) return end set[v] = nil end for _ in pairs(set) do - m.uci:set_list(appname, section, "balancing_node", new) + m:set(section, "balancing_node", new) return end end @@ -533,15 +533,6 @@ o:value("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384") o:value("TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256") o:value("TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256") o:depends({ [_n("tls")] = true, [_n("reality")] = false }) -function o.custom_write(self, section, value) - local new_t - if type(value) == "table" then - new_t = api.table_remove_duplicates(value) - else - new_t = { value } - end - m:set(section, self.option:sub(1 + #option_prefix), new_t) -end o = s:option(TextValue, _n("reality_mldsa65Verify"), "ML-DSA-65 " .. translate("Public key")) o.default = "" diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua index bdbff970..d5828592 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua @@ -118,12 +118,12 @@ if load_urltest_options then -- [[ URLTest Start ]] end end -- Reading the old DynamicList - function o.cfgvalue(self, section) - return m.uci:get_list(appname, section, "urltest_node") or {} + function o.custom_cfgvalue(self, section) + return table.concat(m:get(section, "urltest_node") or {}, " ") end -- Write-and-hold DynamicList function o.custom_write(self, section, value) - local old = m.uci:get_list(appname, section, "urltest_node") or {} + local old = m:get(section, "urltest_node") or {} local new, set = {}, {} for v in value:gmatch("%S+") do new[#new + 1] = v @@ -131,13 +131,13 @@ if load_urltest_options then -- [[ URLTest Start ]] end for _, v in ipairs(old) do if not set[v] then - m.uci:set_list(appname, section, "urltest_node", new) + m:set(section, "urltest_node", new) return end set[v] = nil end for _ in pairs(set) do - m.uci:set_list(appname, section, "urltest_node", new) + m:set(section, "urltest_node", new) return end end @@ -589,15 +589,6 @@ o:value("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384") o:value("TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256") o:value("TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256") o:depends({ [_n("tls")] = true }) -function o.custom_write(self, section, value) - local new_t - if type(value) == "table" then - new_t = api.table_remove_duplicates(value) - else - new_t = { value } - end - m:set(section, self.option:sub(1 + #option_prefix), new_t) -end o = s:option(Flag, _n("ech"), translate("ECH")) o.default = "0" diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua index c07ec992..7fa58696 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua @@ -218,15 +218,6 @@ o:depends({ [_n("reality")] = true }) o = s:option(DynamicList, _n("reality_serverNames"), translate("serverNames")) o:depends({ [_n("reality")] = true }) -function o.custom_write(self, section, value) - local new_t = {} - if type(value) == "table" then - new_t = api.table_remove_duplicates(value) - else - new_t = { value } - end - m:set(section, self.option:sub(1 + #option_prefix), new_t) -end o = s:option(ListValue, _n("alpn"), translate("alpn")) o.default = "default" diff --git a/luci-app-passwall2/luasrc/passwall2/api.lua b/luci-app-passwall2/luasrc/passwall2/api.lua index c1d3dee7..27d6485a 100644 --- a/luci-app-passwall2/luasrc/passwall2/api.lua +++ b/luci-app-passwall2/luasrc/passwall2/api.lua @@ -5,6 +5,7 @@ nixio = require "nixio" fs = require "nixio.fs" sys = require "luci.sys" uci = require"luci.model.uci".cursor() +cbi = require "luci.cbi" util = require "luci.util" datatypes = require "luci.cbi.datatypes" jsonc = require "luci.jsonc" @@ -1386,7 +1387,6 @@ function set_apply_on_parse(map) end function set_default_cbi() - local cbi = require "luci.cbi" if true then --TextValue local TextValue = cbi.TextValue @@ -1396,10 +1396,28 @@ function set_default_cbi() self.template = appname .. "/cbi/tvalue" end end + if true then + --DynamicList + local DynamicList = cbi.DynamicList + function DynamicList.write(self, section, value) + local new_t = {} + if type(value) == "table" then + new_t = table_remove_duplicates(value) + else + new_t = { value } + end + local new_val + if self.cast == "string" then + new_val = table.concat(new_t, " ") + else + new_val = new_t + end + return cbi.AbstractValue.write(self, section, new_val) + end + end end function return_map(map) - local cbi = require "luci.cbi" local api = require "luci.passwall2.api" if true then -- header @@ -1457,11 +1475,25 @@ function luci_types(id, m, s, type_name, option_prefix) if self.custom_write then self:custom_write(section, value) else + local new_val = value + if util.instanceof(self, cbi.DynamicList) then + local new_t = {} + if type(value) == "table" then + new_t = table_remove_duplicates(value) + else + new_t = { value } + end + if self.cast == "string" then + new_val = table.concat(new_t, " ") + else + new_val = new_t + end + end if self.rewrite_option then - m:set(section, self.rewrite_option, value) + m:set(section, self.rewrite_option, new_val) else if self.option:find(option_prefix) == 1 then - m:set(section, self.option:sub(1 + #option_prefix), value) + m:set(section, self.option:sub(1 + #option_prefix), new_val) end end end diff --git a/luci-theme-footstrap/Makefile b/luci-theme-footstrap/Makefile index 1b3361e6..d39dc469 100644 --- a/luci-theme-footstrap/Makefile +++ b/luci-theme-footstrap/Makefile @@ -17,7 +17,7 @@ LUCI_NAME:=luci-theme-footstrap FOOTSTRAP_VERSION?= ifneq ($(FOOTSTRAP_VERSION),) PKG_VERSION:=$(FOOTSTRAP_VERSION) -PKG_RELEASE:=7 +PKG_RELEASE:=8 endif LUCI_TITLE:=Footstrap Theme diff --git a/luci-theme-footstrap/styles/base/40-tables.css b/luci-theme-footstrap/styles/base/40-tables.css index 3d838768..6f0b07ce 100644 --- a/luci-theme-footstrap/styles/base/40-tables.css +++ b/luci-theme-footstrap/styles/base/40-tables.css @@ -33,35 +33,55 @@ * is what an inherited number looks like. Symmetric, on the grid. */ padding: var(--fs-space-2) var(--fs-space-3); text-align: start; - /* AN UNBREAKABLE VALUE MUST NOT LEAVE THE CARD, in ANY .table. Stock has always had this - * (`overflow-wrap: break-word` on every cell); the theme replaced it with a rule that first - * asks what KIND of table it is — theme/30-tables.css breaks long values only for - * `#view .table[id] .td, table.fs-dt .td`, i.e. a table carrying an id or a - * `.tr.table-titles` header row. A third-party status page rendering a key/value - * `
` with neither matched nothing, so its cells kept - * `overflow-wrap: normal` and one unbroken token — an ICCID, a firmware string, an APN, a - * base64 key, a DUID — held the column open and pushed the table outside its card. - * Measured against luci-app-3ginfo-lite's own markup (`admin/modem`, three such tables). - * - * NOT scoped to the phone tier, and that was measured rather than assumed. Scoping it to - * `max-width: 767px` looked like the cautious choice and left a hole exactly where the - * column is narrow but the WINDOW is not: with a 130-character token the table escaped its - * card by 147px at 900px, 247px at 800px and 279px at 768px, and was contained at every - * width with this rule unscoped. That is the same argument the fit engine is built on — - * whether something fits is a property of the CONTENT and its column, not of the viewport. - * - * The cost is nil, which is the other half of the decision: across 8 router pages × 4 - * widths × 849 text cells, turning this on changes NOTHING — same cell overflows (0), same - * tables past their column (0), same multi-line cells, same mid-word breaks, same total - * line count. It bites only on the unbreakable token it exists for. - * - * `anywhere`, not `break-word`: `break-word` permits the break without reducing the cell's - * min-content contribution, so a `display: table` box still cannot shrink below its widest - * token and the column stays open — tried, and the table stayed 355px wide in a 346px - * column. The narrower `anywhere` rules in theme/ keep winning where they apply. */ - overflow-wrap: anywhere; } + /* AN UNBREAKABLE VALUE MUST NOT LEAVE THE CARD, in ANY .table. Stock has always had this + * (`overflow-wrap: break-word` on every cell); the theme replaced it with a rule that first + * asks what KIND of table it is — theme/30-tables.css breaks long values only for + * `#view .table[id] .td, table.fs-dt .td`, i.e. a table carrying an id or a + * `.tr.table-titles` header row. A third-party status page rendering a key/value + * `
` with neither matched nothing, so its cells kept + * `overflow-wrap: normal` and one unbroken token — an ICCID, a firmware string, an APN, a + * base64 key, a DUID — held the column open and pushed the table outside its card. + * Measured against luci-app-3ginfo-lite's own markup (`admin/modem`, three such tables). + * + * NOT scoped to the phone tier, and that was measured rather than assumed. Scoping it to + * `max-width: 767px` looked like the cautious choice and left a hole exactly where the + * column is narrow but the WINDOW is not: with a 130-character token the table escaped its + * card by 147px at 900px, 247px at 800px and 279px at 768px, and was contained at every + * width with this rule unscoped. That is the same argument the fit engine is built on — + * whether something fits is a property of the CONTENT and its column, not of the viewport. + * + * The cost is nil, which is the other half of the decision: across 8 router pages × 4 + * widths × 849 text cells, turning this on changes NOTHING — same cell overflows (0), same + * tables past their column (0), same multi-line cells, same mid-word breaks, same total + * line count. It bites only on the unbreakable token it exists for. + * + * `anywhere`, not `break-word`: `break-word` permits the break without reducing the cell's + * min-content contribution, so a `display: table` box still cannot shrink below its widest + * token and the column stays open — tried, and the table stayed 355px wide in a 346px + * column. The narrower `anywhere` rules in theme/ keep winning where they apply. + * + * SET PER ROLE, and that is the whole of the second rule: a VALUE may break anywhere, a LABEL + * breaks at words. `.th` and `.td` never match the same element, so neither line depends on the + * other's source order, and the pair rule above keeps only what both cells share. + * + * `anywhere` costs a min-content of ONE CHARACTER. On a value that is the point — the column + * may collapse to nothing rather than push the table out of its card. On a header it is the bug + * it looks like: an auto-layout table hands the surplus to whichever column asks loudest, and + * with every text column's floor at one character the header row is what pays. Measured on + * Status → Processes at 1228px against a Russian catalogue, where the actions column is + * `nowrap` and 477px wide: `PID` came out as "PI/D", `Владелец` as "ВЛ/АД/ЕЛЕ/Ц", `root` in the + * body as "roo/t", and the header row stood 104px tall. With the header floor back at its + * longest WORD: 54px, every label and every owner cell on one line, the value columns 47/94/152 + * against 39/49/90, and the table still inside its card to the pixel (issue #32). + * + * `break-word` for the label, not `normal`: a header that IS one long token — a third-party + * table naming a column after a key — must still break rather than overflow. It just may not + * shrink the column below that word, which is what a header row is for. */ + .table .td { overflow-wrap: anywhere; } + .table .th { overflow-wrap: break-word; } + .table .tr:first-child .th { padding-top: var(--fs-space-2); font-weight: var(--fs-weight-bold); diff --git a/luci-theme-footstrap/styles/pages/90-processes.css b/luci-theme-footstrap/styles/pages/90-processes.css new file mode 100644 index 00000000..7cd09763 --- /dev/null +++ b/luci-theme-footstrap/styles/pages/90-processes.css @@ -0,0 +1,27 @@ +@layer page { + /* Processes (luci-mod-status): the PID column, which must not split. + * + * Every data cell may break to fit — `overflow-wrap: anywhere` in base/40-tables.css, which is + * what reflows a wide table into the content column instead of scrolling it. A PID is the same + * kind of value a MAC address is: atomic, short, meaningless once a line break lands in the + * middle of it. `384` came out as "38/4" and `4810` as "481/0" — 23 of 26 rows on the router, + * measured at 1228px against a Russian catalogue, where the actions column is `nowrap` and + * 477px wide and the table has no width left to give. Stock luci-theme-bootstrap wraps 0 of the + * same 26, so this is the theme's own break to take back (issue #32, and issue #5 before it). + * + * Exactly the shape pages/50-leases.css and pages/60-assoclist.css already use, for exactly + * their reason: `white-space: nowrap` re-raises the column's floor to the value itself, and the + * floor is the point — a PID is four or five digits, so it costs the table ~40px it would + * otherwise spend splitting a number. `:not(.fs-stacked)` is the whole guard: once fs-select.js + * has measured that the table no longer fits and carded it, every cell must be free to wrap + * again. + * + * FIRST child, not counted from the end, and that is safe here where it would not be on the + * leases tables: `processes.js` renders one fixed header row — PID, Owner, Command, CPU usage, + * Memory usage, actions — with no feature-dependent leading column of the kind `40_dhcp.js` + * inserts. If upstream ever prepends one, this rule nowraps that column instead, which is why + * it is scoped to the page rather than to `.fs-dt .td:first-child` at large. */ + body[data-page="admin-status-processes"] #view table.fs-dt:not(.fs-stacked) .td:first-child { + white-space: nowrap; + } +} diff --git a/luci-theme-footstrap/styles/theme/45-misc.css b/luci-theme-footstrap/styles/theme/45-misc.css index b2930d9c..8345d6d8 100644 --- a/luci-theme-footstrap/styles/theme/45-misc.css +++ b/luci-theme-footstrap/styles/theme/45-misc.css @@ -24,6 +24,27 @@ margin-inline: calc(-1 * (var(--fs-card-pad) + 1px)); } + /* …and the same box's frame, which the same inline style paints BLACK: every realtime view + * writes `border:1px solid #000;background:#fff` on it (bandwidth.js, load.js, wireless.js, + * connections.js, channel_analysis.js, and every luci-app-*-status copy of them). Black is a + * colour no palette of this theme contains; on a dark surface the graph reads as a framed + * cut-out, and the frame lands exactly on the card's own border edge because of the bleed + * above. Recoloured to the hairline every other card contour uses (issue #32). + * + * WIDER than the bleed rule on purpose: channel_analysis.js hangs off a bare [data-tab] div and + * must not be bled — but its border is just as black, so the shape here asks only "an + * inline-styled box wrapping an ", one `#view` deep or ten. + * + * Only the COLOUR moves. The 1px stays: it is the graph's own edge, and the plot draws to it. + * `background: #fff` is left alone deliberately — the is sized to the box's content edge + * (`#view.offsetWidth - 2`, the same arithmetic the bleed restores), so the white never paints; + * a flag against it would be a rule that changes nothing. Only an author !important outranks an + * inline declaration, and `border-color` alone is enough: the width and style already agree + * with what the theme would ask for. */ + #view div[style]:has(> svg) { + border-color: var(--fs-border) !important; + } + /* WCAG 2.2 SC 1.4.1 (Use of Color, A): inside a block of text a link may not be identified * by COLOUR ALONE — it needs a non-colour cue or 3:1 against the surrounding text, and * --fs-accent on --fs-text measures 1.4:1 in dark mode. So: underline links in prose. diff --git a/luci-theme-footstrap/ucode/template/themes/footstrap/header.ut b/luci-theme-footstrap/ucode/template/themes/footstrap/header.ut index 2fc250d9..21733dba 100644 --- a/luci-theme-footstrap/ucode/template/themes/footstrap/header.ut +++ b/luci-theme-footstrap/ucode/template/themes/footstrap/header.ut @@ -89,7 +89,7 @@ rule — and the overview layout include, which gates on it — silently did not apply there. The SPA router stamps the resolved leaf path, so both ways of arriving agree. #} - + {# The wallpaper PATTERN's paint surface, and nothing else: 15-wallpaper.css masks this element so an uploaded SVG takes the theme's ink. A mask applies to an element's whole diff --git a/luci-theme-footstrap/ucode/template/themes/footstrap/partials/head.ut b/luci-theme-footstrap/ucode/template/themes/footstrap/partials/head.ut index 7b12a5a7..bc0c652c 100644 --- a/luci-theme-footstrap/ucode/template/themes/footstrap/partials/head.ut +++ b/luci-theme-footstrap/ucode/template/themes/footstrap/partials/head.ut @@ -414,10 +414,24 @@ {% endif %} - {% if (node?.css): %} - {# uhttpd sends no Cache-Control, so without a version key the browser applies HEURISTIC - freshness and can serve a stale app stylesheet for days after an upgrade. #} - + {% if (dispatched?.css): %} + {# The `css` property of the dispatched menu.d node — an app naming a stylesheet the server + should link for its page, instead of the view injecting one at module eval (which is what + fs-sheets.js spends its existence containing). + + `dispatched`, not `node`: the dispatcher exports the resolved node as `dispatched` + (`runtime.env.dispatched = resolved.node`) and never binds a `node`. Every ucode theme + carried the same typo until it was fixed upstream in openwrt/luci#8920, so the link had + never once been emitted. Requires `css: 'string'` in the dispatcher's node schema, added + by that same commit — on a luci-base without it the property is filtered out of the tree + and this stays false, which is what 24.10 and 25.12 do. + + uhttpd sends no Cache-Control, so without a version key the browser applies HEURISTIC + freshness and can serve a stale app stylesheet for days after an upgrade. + + Deliberately NOT `data-fs-shell`: this sheet belongs to one page, so the SPA router must + judge and contain it like any other app sheet rather than let it ride along. #} + {% endif %} {% if (css): %} {# no `title` attribute: on a