diff --git a/luci-theme-design/.gitignore b/luci-theme-design/.gitignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/luci-theme-design/CHANGELOG.md b/luci-theme-design/CHANGELOG.md deleted file mode 100644 index a5265683d..000000000 --- a/luci-theme-design/CHANGELOG.md +++ /dev/null @@ -1,49 +0,0 @@ -# 更新日志 - -## [7.0] - 2025-12-05 - -### 新增 -- ✨ 支持 OpenWrt 23.x 版本 -- ✨ 支持 OpenWrt 24.x 版本 -- ✨ 新增版本兼容层 (`compat.js`) - -### 改进 -- 🔧 优化 LuCI API 调用,兼容新旧版本 -- 🔧 jQuery 依赖改为可选,无 jQuery 环境下自动降级 -- 🔧 改进 ubus 调用方式,支持不同版本 -- 🔧 优化菜单渲染逻辑,增加空值检查 -- 🔧 改进资源加载,添加版本化 URL - -### 修复 -- 🐛 修复新版本 LuCI 中 `dispatchpath` 未定义问题 -- 🐛 修复新版本中 `requestpath` 访问错误 -- 🐛 修复无 jQuery 环境下动画失效问题 -- 🐛 修复某些元素可能不存在导致的错误 - -### 技术改进 -- 📦 更新 Makefile 版本号到 7.0 -- 📦 改进错误处理和边界检查 -- 📦 优化代码结构,提高可维护性 - -## [6.0] - 2023-02-24 - -### 之前的更新 -- 修复安装 package 提示信息背景泛白 -- 优化菜单缩放 -- 优化显示网口 down 状态显示图标 -- 优化 logo 显示 -- 新增各设备状态图标显示 -- 更换 logo 显示为字体 "OpenWrt",支持以主机名显示 logo -- 修复部分插件显示 bug -- 修复 vssr 状态 bar -- 修复诸多 bug -- 修复兼容部分插件样式 -- 修复 aliyundrive-webdav 样式 -- 修复 vssr 在 iOS/iPadOS WebApp 模式下显示异常 -- 修复 openclash 插件在 iOS/iPadOS WebApp 模式下 env(safe-area-inset-bottom) = 0 -- 优化菜单 hover action 状态分辨 -- 支持 luci-app-wizard 向导菜单 -- Update header box-shadow style -- Update uci-change overflow -- Fix nlbw component -- Added QSDK/QWRT wizard and iStore menu icon fonts diff --git a/luci-theme-design/COMPATIBILITY.md b/luci-theme-design/COMPATIBILITY.md deleted file mode 100644 index ee143ad52..000000000 --- a/luci-theme-design/COMPATIBILITY.md +++ /dev/null @@ -1,67 +0,0 @@ -# 版本兼容性说明 - -## 支持的 OpenWrt 版本 - -本主题已针对以下 OpenWrt 版本进行优化和测试: - -- ✅ **OpenWrt 21.x** - 完全支持 -- ✅ **OpenWrt 23.x** - 完全支持 -- ✅ **OpenWrt 24.x** - 完全支持 - -## 主要兼容性改进 - -### 1. LuCI API 兼容 -- 兼容新旧版本的 `L.env.dispatchpath` 和 `L.env.requestpath` -- 支持不同版本的 ubus 调用方式 -- 自动检测并适配 LuCI 版本 - -### 2. JavaScript 依赖 -- jQuery 可选:在没有 jQuery 的环境下使用原生 JavaScript -- 动画效果降级:无 jQuery 时使用 CSS 过渡 -- 兼容层自动检测运行环境 - -### 3. 资源加载 -- 版本化资源 URL,避免缓存问题 -- 兼容不同版本的资源路径结构 - -## 已知问题 - -### OpenWrt 23/24 特定问题 -1. 某些第三方插件可能需要单独适配 -2. 如果遇到菜单显示问题,请清除浏览器缓存 - -## 测试建议 - -编译安装后,请测试以下功能: -- [ ] 登录界面显示正常 -- [ ] 主菜单展开/收起 -- [ ] 移动端响应式布局 -- [ ] 深色模式切换 -- [ ] 各插件页面样式 - -## 反馈问题 - -如果在特定版本遇到问题,请提供: -1. OpenWrt 版本号 -2. LuCI 版本号 -3. 浏览器类型和版本 -4. 具体问题截图 - -## 技术细节 - -### 兼容层实现 -主题包含 `compat.js` 兼容层,自动检测 LuCI 版本并提供统一接口。 - -### 版本检测 -```javascript -// 检测是否为新版本 LuCI (23+) -if (L.env.luci_version) { - var majorVersion = parseInt(L.env.luci_version.split('.')[0]); - var isNewVersion = majorVersion >= 23; -} -``` - -### jQuery 降级 -当 jQuery 不可用时,自动使用原生 DOM API: -- `$(el).slideUp()` → `el.style.display = 'none'` -- `$(el).css()` → `el.style.property = value` diff --git a/luci-theme-design/LICENSE b/luci-theme-design/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/luci-theme-design/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/luci-theme-design/Makefile b/luci-theme-design/Makefile index d516301bf..bab427d2f 100644 --- a/luci-theme-design/Makefile +++ b/luci-theme-design/Makefile @@ -7,8 +7,9 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=Design Theme LUCI_DEPENDS:= -PKG_VERSION:=7.0 -PKG_RELEASE:=2 +PKG_LICENSE:=Apache-2.0 +PKG_VERSION:=7.1 +PKG_RELEASE:=3 LUCI_MINIFY_CSS:= CONFIG_LUCI_CSSTIDY:= diff --git a/luci-theme-design/README.md b/luci-theme-design/README.md deleted file mode 100644 index 269c000ad..000000000 --- a/luci-theme-design/README.md +++ /dev/null @@ -1,140 +0,0 @@ - - -# luci-theme-design - -**luci-theme-design**基于[luci-theme-neobird](https://github.com/thinktip/luci-theme-neobird)二次开发 - -## 版本支持 - -- ✅ OpenWrt 21.x -- ✅ OpenWrt 23.x -- ✅ OpenWrt 24.x -- 适用于官方 [OpenWrt](https://github.com/openwrt/openwrt) - -- 修复安装package提示信息背景泛白 -- 优化菜单缩放 -- 优化显示网口down状态显示图标 -- 优化logo显示 -- 新增各设备状态图标显示 -- 更换logo显示为字体"OpenWrt",支持以主机名显示logo -- 修复部分插件显示bug -- 修复vssr状态bar -- 修复诸多bug -- 修复兼容部分插件样式 -- 修复aliyundrive-webdav样式 -- 修复vssr在iOS/iPadOS WebApp模式下显示异常 -- 修复openclash插件在iOS/iPadOS WebApp 模式下env(safe-area-inset-bottom) = 0 -- 优化菜单hover action状态分辨 -- 支持luci-app-wizard向导菜单 -- Update header box-shadow style -- Update uci-change overflow -- Fix nlbw component -- Added QSDK/QWRT wizard and iStore menu icon fonts -- 新增 OpenWrt 23/24 版本兼容支持 -- 优化 LuCI 新旧版本兼容层 -- 修复新版本 API 调用问题 - -## 主要特点 - -- 针对移动端优化,特别适合手机端做为webapp使用 -- 修改和优化了很多插件显示,完善的icon图标,尽量视觉统一 -- 简洁的登录界面,底部导航栏,类App的沉浸式体验; -- 适配深色模式,适配系统自动切换; - -## 体验Webapp方法 - -- 在移动端(iOS/iPadOS)浏览器打开管理界面,添加到主屏幕即可。 -- 想要实现完全的沉浸式(无浏览器导航、无地址栏等)体验,需要使用SSL证书,请自行申请域名、证书、安装并启用。 -- 如果不使用SSL证书,基于安全原因,iOS/iPadOS 在打开新的页面后,将会显示浏览器顶部菜单栏。 - -## PS - -- 资源接口icon未完善,如果有能力画图的欢迎pr,但请确保跟现有icon颜色风格一致 -- 有bug欢迎提issue -- 主题个人配色可能会不符合大众胃口,欢迎提配色建议 - -## 自行编译 - -### OpenWrt 21/23/24 通用方法 - -```bash -# 进入 OpenWrt 源码目录 -cd openwrt - -# 克隆主题到 package 目录 -git clone https://github.com/gngpp/luci-theme-design.git package/luci-theme-design - -# 配置编译选项 -make menuconfig -# 选择: LuCI -> 3. Themes -> luci-theme-design - -# 编译 -make package/luci-theme-design/compile V=s -``` - -### 安装后测试 - -```bash -# 上传测试脚本到路由器 -scp test-compatibility.sh root@192.168.1.1:/tmp/ - -# SSH 登录路由器 -ssh root@192.168.1.1 - -# 运行兼容性测试 -sh /tmp/test-compatibility.sh -``` - -### 手动激活主题 - -如果主题未自动激活,可以手动设置: - -```bash -uci set luci.main.mediaurlbase='/luci-static/design' -uci commit luci -/etc/init.d/uhttpd restart -``` - -## 版本兼容性 - -详细的兼容性信息请查看 [COMPATIBILITY.md](COMPATIBILITY.md) - -- OpenWrt 21.x - 完全支持 ✅ -- OpenWrt 23.x - 完全支持 ✅ -- OpenWrt 24.x - 完全支持 ✅ - -## 故障排除 - -### 主题显示异常 -1. 清除浏览器缓存(Ctrl+F5 强制刷新) -2. 检查主题文件是否完整安装 -3. 运行兼容性测试脚本 - -### 菜单无法展开 -1. 检查浏览器控制台是否有 JavaScript 错误 -2. 确认 LuCI 版本兼容性 -3. 尝试禁用浏览器扩展 - -### 移动端显示问题 -1. 确保使用 HTTPS(WebApp 模式需要) -2. 清除浏览器缓存 -3. 重新添加到主屏幕 - -## 预览(ps: 下面PC端普通字体为苹果浏览器字体,只有移动端是正常显示的) - -
iOS - - -
- -
iPadOS - - -
- - - - - - - diff --git a/luci-theme-design/htdocs/luci-static/design/css/compat-openwrt23.css b/luci-theme-design/htdocs/luci-static/design/css/compat-openwrt23.css deleted file mode 100644 index e1e5a26df..000000000 --- a/luci-theme-design/htdocs/luci-static/design/css/compat-openwrt23.css +++ /dev/null @@ -1,431 +0,0 @@ -/** - * OpenWrt 23/24 兼容性样式补ä¸? - * - * 这个文件包含针对 OpenWrt 23.x å’?24.x 的样式修å¤? - * 新版 LuCI 使用了一些新çš?CSS 类名和结æž? - */ - -/* ============================================ - 1. 新版 LuCI 视图容器兼容 - ============================================ */ - -/* 新版 LuCI 使用 #view 作为主要内容容器 */ -#view { - padding: 0; -} - -#view > * { - margin-bottom: 1rem; -} - -/* 兼容新版 LuCI çš?.cbi-map 结构 */ -.cbi-map { - margin-top: 10px; -} - -/* ============================================ - 2. 新版表单元素样式 - ============================================ */ - -/* 新版 LuCI çš?input 样式兼容 */ -.cbi-input-text, -.cbi-input-password, -.cbi-input-select { - color: var(--inputtext_color); - padding: 5px 10px; - font-size: 0.92rem; - border: var(--input_border); - background-color: var(--inputbg_color); - border-radius: 8px; - height: 2.8rem; - line-height: 2.8rem; - max-width: 550px; -} - -/* 新版 checkbox å’?radio 样式 */ -.cbi-input-checkbox, -.cbi-input-radio { - width: 1.2rem; - height: 1.2rem; - margin-right: 0.5rem; - vertical-align: middle; - accent-color: var(--active_color); -} - -/* ============================================ - 3. 新版按钮样式兼容 - ============================================ */ - -/* 新版 LuCI 按钮类名 */ -.btn.cbi-button, -button.cbi-button { - -webkit-appearance: none; - appearance: none; - text-transform: uppercase; - color: #fff; - background-color: #337ab7; - transition: all 0.2s ease-in-out; - display: inline-block; - border: none; - cursor: pointer; - text-align: center; - white-space: nowrap; - min-width: 80px; - padding: 0 8px; - height: 35px; - line-height: 35px; - border-radius: 8px; -} - -/* 新版 primary/positive 按钮 */ -.cbi-button-positive, -.cbi-button-action { - color: var(--bttext_color) !important; - background-color: var(--active_color) !important; -} - -.cbi-button-positive:hover, -.cbi-button-action:hover { - background-color: #4a8a80 !important; -} - -/* 新版 negative 按钮 */ -.cbi-button-negative { - color: #fff !important; - background-color: #d9534f !important; -} - -.cbi-button-negative:hover { - background-color: #c9302c !important; -} - -/* 新版 neutral 按钮 */ -.cbi-button-neutral { - color: #fff !important; - background-color: #617486 !important; -} - -/* ============================================ - 4. 新版表格样式兼容 - ============================================ */ - -/* 新版 LuCI 表格结构 */ -.table.cbi-section-table { - width: 100%; - border-collapse: collapse; -} - -.table.cbi-section-table .tr { - display: table-row; -} - -.table.cbi-section-table .th, -.table.cbi-section-table .td { - display: table-cell; - padding: 12px; - vertical-align: middle; -} - -/* 新版表格标题è¡?*/ -.tr.cbi-section-table-titles { - background-color: var(--inputbg_color); - font-weight: bold; -} - -/* ============================================ - 5. 新版下拉菜单兼容 - ============================================ */ - -/* 新版 cbi-dropdown 样式 */ -.cbi-dropdown { - position: relative; - display: inline-flex; - min-height: 2.8rem; - border: var(--input_border); - background-color: var(--inputbg_color); - border-radius: 8px; -} - -.cbi-dropdown > ul { - display: flex; - overflow-x: hidden; - overflow-y: auto; - width: 100%; - margin: 0 !important; - padding: 0; - list-style: none; -} - -.cbi-dropdown > ul > li { - display: flex; - align-items: center; - padding: 0.5em; -} - -.cbi-dropdown[open] > ul.dropdown { - position: absolute; - z-index: 1100; - top: 100%; - left: 0; - right: 0; - background: var(--sectionbg_color); - border: var(--input_border); - border-radius: 0 0 8px 8px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); -} - -.cbi-dropdown[open] > ul.dropdown > li { - padding: 0.5em 1em; - cursor: pointer; -} - -.cbi-dropdown[open] > ul.dropdown > li:hover { - background-color: var(--active_color); - color: #fff; -} - -/* ============================================ - 6. 新版标签页样式兼å®? - ============================================ */ - -/* 新版 LuCI 标签é¡?*/ -.cbi-tabmenu { - display: flex; - flex-wrap: wrap; - border-bottom: var(--tabmenu_bottom); - margin-bottom: 1rem; -} - -.cbi-tabmenu > li { - display: inline-block; - margin-right: 0.5rem; -} - -.cbi-tabmenu > li > a { - display: block; - padding: 0.5rem 1rem; - text-decoration: none; - color: var(--text_color); - border-bottom: 2px solid transparent; - transition: all 0.2s; -} - -.cbi-tabmenu > li.cbi-tab > a, -.cbi-tabmenu > li > a:hover { - color: var(--active_color); - border-bottom-color: var(--active_color); -} - -/* ============================================ - 7. 新版模态框样式兼容 - ============================================ */ - -/* 新版 modal 样式 */ -.modal { - display: flex; - flex-wrap: wrap; - align-items: center; - width: 90%; - max-width: 600px; - min-height: 32px; - margin: 5em auto; - padding: 1em; - border-radius: 8px; - background: var(--model_background); - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); -} - -.modal > * { - flex-basis: 100%; - margin-bottom: 0.5em; -} - -.modal > h4 { - color: var(--text_color); - margin-bottom: 1em; -} - -.modal > .btn, -.modal > .cbi-button { - flex-basis: auto; - margin: 0.25em; -} - -/* ============================================ - 8. 新版进度条样式兼å®? - ============================================ */ - -/* 新版进度æ�?*/ -.cbi-progressbar { - position: relative; - min-width: 11rem; - height: 1.5rem; - background-color: var(--progressbar_color); - border-radius: 5px; - overflow: hidden; -} - -.cbi-progressbar > div { - height: 100%; - background: var(--progressbar); - transition: width 0.25s ease-in; -} - -.cbi-progressbar::after { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - content: attr(title); - text-align: center; - line-height: 1.5rem; - font-size: 0.75rem; - color: var(--progressbartxt_color); -} - -/* ============================================ - 9. 新版状态指示器兼容 - ============================================ */ - -/* 新版状态徽ç«?*/ -.ifacebadge { - display: inline-flex; - align-items: center; - gap: 0.2rem; - padding: 0.3rem 0.6rem; - background-color: var(--active_color); - color: #fff; - border-radius: 8px; - font-size: 0.85rem; -} - -.ifacebadge > img, -.ifacebadge > em { - width: 16px; - height: 16px; -} - -/* 区域徽章 */ -.zonebadge { - display: inline-block; - padding: 0.2rem 0.5rem; - border-radius: 4px; -} - -/* ============================================ - 10. 新版响应式布局兼容 - ============================================ */ - -@media screen and (max-width: 992px) { - /* 新版移动端菜å�?*/ - .main-left { - transform: translateX(-100%); - transition: transform 0.3s ease; - } - - .main-left.active, - .main-left[style*="width: 17rem"], - .main-left:not([style*="width: 0"]) { - transform: translateX(0); - } -} - -/* ============================================ - 11. 新版 LuCI 特定页面修复 - ============================================ */ - -/* 系统概览页面 */ -[data-page="admin-status-overview"] .cbi-section { - margin-bottom: 1rem; -} - -/* 网络接口页面 */ -[data-page="admin-network-network"] .ifacebox { - margin: 0.5rem; - border-radius: 8px; - overflow: hidden; -} - -/* 无线页面 */ -[data-page="admin-network-wireless"] .cbi-section-table { - overflow-x: auto; -} - -/* 软件包页é�?*/ -[data-page="admin-system-opkg"] .controls { - display: flex; - flex-wrap: wrap; - gap: 0.5rem; - margin-bottom: 1rem; -} - -[data-page="admin-system-opkg"] .controls input[type="text"] { - flex: 1; - min-width: 200px; -} - -/* ============================================ - 12. 深色模式增强 - ============================================ */ - -@media (prefers-color-scheme: dark) { - /* 新版深色模式下的代码å�?*/ - code, pre { - background-color: #2d2d2d; - color: #f8f8f2; - } - - /* 新版深色模式下的表格 */ - .table.cbi-section-table .tr:nth-child(even) { - background-color: rgba(255, 255, 255, 0.05); - } - - /* 新版深色模式下的输入æ¡?*/ - .cbi-input-text:focus, - .cbi-input-password:focus, - .cbi-input-select:focus { - border-color: var(--active_color); - box-shadow: 0 0 6px var(--active_color); - } -} - -/* ============================================ - 13. 打印样式 - ============================================ */ - -@media print { - .main-left, - .navbar, - header, - .showSide, - .darkMask { - display: none !important; - } - - .main-right { - width: 100% !important; - margin: 0 !important; - } -} - -/* ============================================ - 14. 无障碍访问增å¼? - ============================================ */ - -/* 焦点可见æ€?*/ -:focus-visible { - outline: 2px solid var(--active_color); - outline-offset: 2px; -} - -/* 减少动画 (用户偏好) */ -@media (prefers-reduced-motion: reduce) { - *, - *::before, - *::after { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} diff --git a/luci-theme-design/htdocs/luci-static/design/css/style.css b/luci-theme-design/htdocs/luci-static/design/css/style.css index ccc32fb09..c9edb9318 100644 --- a/luci-theme-design/htdocs/luci-static/design/css/style.css +++ b/luci-theme-design/htdocs/luci-static/design/css/style.css @@ -115,6 +115,11 @@ src: url('../fonts/Cocon-Regular-Font.otf') format('opentype'); } +@font-face { + font-family: 'genjyuugothic-medium'; + src: url('../fonts/GenJyuuGothic-Medium.otf') format('opentype'); +} + div{ font-family: 'HYk2gj'; } @@ -289,22 +294,19 @@ input:focus { -moz-box-shadow:0 0 6px #948FE1; } -select, -.cbi-dropdown { +select { width: inherit; cursor: default; } select:not([multiple="multiple"]):focus, -input:not(.cbi-button):focus, -.cbi-dropdown:focus { +input:not(.cbi-button):focus { border-color: #948FE1; box-shadow:0 0 6px #948FE1; -webkit-box-shadow:0 0 6px #948FE1; -moz-box-shadow:0 0 6px #948FE1; } -.cbi-dropdown, select[multiple="multiple"] { height: auto; } @@ -401,7 +403,10 @@ small { width: 100%; height: 100%; z-index: 1000; - display: block; + display: none; + visibility: hidden; + opacity: 0; + pointer-events: none; background-color: var(--bgwhite); top: 0; } @@ -502,7 +507,7 @@ header > .fill > .container > #logo > img { header > .fill > .container > .brand { font-size: 25px; - font-family: "cocon_regular_font"; + font-family: 'genjyuugothic-medium', -apple-system, 'Microsoft Yahei', sans-serif, Helvetica, Arial, sans-serif; /*font-weight:bold;*/ line-height:60px; /*color: white;*/ @@ -515,15 +520,15 @@ header > .fill > .container > .brand { height:60px; text-align:center; float:left; - font-weight:900; - letter-spacing:1px; + font-weight:700; + letter-spacing:.04em; margin-left: 10px; } header > .fill > .container > a[class="brand"]:after { content:""; font-size:14px; - font-family:Tahoma; + font-family: inherit; position: absolute; top:-10px; font-weight:normal !important; @@ -547,22 +552,92 @@ header > .fill > .container > .brand-hostname { header > .fill > .container > .status { position: absolute; - top: 25%; + top: 50%; right: 1em; + display: flex; + align-items: center; + gap: 0.2rem; float: right; font-size: 1.5rem; font-family: 'design'; - line-height: unset !important; + line-height: 1 !important; + transform: translateY(-50%); } header > .fill > .container > .status > * { position: relative; - top: .2rem; - float: left; - margin-left: .3rem; + top: auto; + float: none; + margin-left: 0.3rem; cursor: pointer; } +#indicators > [data-indicator] { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 1.6rem; + min-height: 1.6rem; + padding: 0; + border-radius: 999px; + background: transparent; + box-shadow: none; + text-transform: none; + text-shadow: none; +} + +#indicators > [data-indicator="poll-status"], +#indicators > [data-indicator="uci-changes"] { + font-size: 0; +} + +#indicators > [data-indicator="poll-status"]::before, +#indicators > [data-indicator="uci-changes"]::before { + font-family: "design" !important; + font-style: normal; + font-weight: normal; + line-height: 1; + text-transform: none; + -webkit-font-smoothing: antialiased; +} + +#indicators > [data-indicator="poll-status"]::before { + content: "\e6b9"; + font-size: 1.5rem; +} + +#indicators > [data-indicator="poll-status"][data-style="inactive"]::before { + color: var(--progressbar_color); +} + +#indicators > [data-indicator="poll-status"]:not([data-style="inactive"])::before { + color: var(--active_color); +} + +#indicators > [data-indicator="uci-changes"]::before { + content: "\e739"; + font-size: 1.45rem; + color: var(--active_color); +} + +#indicators > [data-indicator="uci-changes"][data-count]:not([data-count=""])::after { + content: attr(data-count); + position: absolute; + top: -0.18rem; + right: -0.08rem; + min-width: 0.95rem; + height: 0.95rem; + padding: 0 0.14rem; + border-radius: 999px; + background: var(--active_color); + color: var(--bgwhite); + font-family: Tahoma, sans-serif; + font-size: 0.62rem; + font-weight: normal; + line-height: 0.95rem; + text-align: center; +} + /* .modemenu-buttons { display: flex; flex-wrap: wrap; @@ -781,6 +856,21 @@ span[data-indicator="uci-changes"] { content: "\72"; } +.main > .main-left > .nav > .slide > a[data-title="Modem"]:before, +.main > .main-left > .nav > li > a[data-title="Modem"]:before, +.main > .main-left > .nav > .slide > a[data-title="Mobile"]:before, +.main > .main-left > .nav > li > a[data-title="Mobile"]:before, +.main > .main-left > .nav > .slide > a[data-title="Mobile_Service"]:before, +.main > .main-left > .nav > li > a[data-title="Mobile_Service"]:before, +.main > .main-left > .nav > .slide > a[data-title="Cellular"]:before, +.main > .main-left > .nav > li > a[data-title="Cellular"]:before, +.main > .main-left > .nav > .slide > a[data-title="WWAN"]:before, +.main > .main-left > .nav > li > a[data-title="WWAN"]:before, +.main > .main-left > .nav > .slide > a[data-title="ModemManager"]:before, +.main > .main-left > .nav > li > a[data-title="ModemManager"]:before { + content: "\e67e"; +} + .main > .main-left > .nav > .slide > a[data-title="Bandwidth Monitor"]:before { content: "\e764"; } @@ -797,23 +887,26 @@ span[data-indicator="uci-changes"] { content: "\e7dd"; } -.main > .main-left > .nav > li > a[data-title="Inital Setup"]:before { - content: "\e67e"; -} - -.main > .main-left > .nav > li > a[href="/cgi-bin/luci//admin/wizard"]:before { - content: "\e67e"; +.main > .main-left > .nav > li > a[data-title="Inital Setup"]:before, +.main > .main-left > .nav > li > a[data-title="Setup_Wizard"]:before, +.main > .main-left > .nav > li > a[href="/cgi-bin/luci//admin/wizard"]:before, +.main > .main-left > .nav > li > a[href="/cgi-bin/luci/admin/wizard"]:before { + content: "\e719"; + width: 1.2rem; + text-align: center; } .main > .main-left > .nav > li > a[data-title="iStore"]:before { content: "\e676"; } -.main > .main-left > .nav > li > a[data-title="Logout"] { +.main > .main-left > .nav > li > a[data-title="Logout"], +.main > .main-left > .nav > li > a[data-title="Log_out"] { padding: 0.675rem 0 0.675rem 2.5rem; } -.main > .main-left > .nav > li > a[data-title="Logout"]:before { +.main > .main-left > .nav > li > a[data-title="Logout"]:before, +.main > .main-left > .nav > li > a[data-title="Log_out"]:before { content: "\e641"; } @@ -898,7 +991,11 @@ body[class*="node-"] > .main > .main-left > .nav > .slide > .menu.active::before /* ripple effect */ .main > .main-left > .nav > .slide > .menu, -.main > .main-left > .nav > li:last-child a +.main > .main-left > .nav > li:last-child a, +.main > .main-left > .nav > li > a[data-title="Inital Setup"], +.main > .main-left > .nav > li > a[data-title="Setup_Wizard"], +.main > .main-left > .nav > li > a[href="/cgi-bin/luci//admin/wizard"], +.main > .main-left > .nav > li > a[href="/cgi-bin/luci/admin/wizard"] { display: block; padding: 0.675rem 0 0.675rem 2.5rem; @@ -1059,15 +1156,25 @@ fieldset > fieldset, box-shadow: none; } +/* Avoid clipping LuCI dropdown popups inside nested form sections. */ +fieldset, +fieldset.cbi-section { + overflow-y: visible; +} + .cbi-section > h3:first-child, .panel-title { width: 100%; display: block; padding: 10px; - font-weight: bold; + font-weight: 700; font-size: 1rem; } +[data-tab-title] > h3:first-child { + font-weight: 700; +} + .cbi-section > h4:first-child, .cbi-section > p:first-child, [data-tab-title] > h3:first-child, @@ -1144,7 +1251,6 @@ table > tr:last-child { margin-bottom: 0; } -.cbi-section-table-row > .cbi-value-field .cbi-dropdown, .cbi-section-table-row > .cbi-value-field .cbi-input-select, .cbi-section-table-row > .cbi-value-field .cbi-input-text, .cbi-section-table-row > .cbi-value-field .cbi-input-password { @@ -1281,6 +1387,7 @@ form.inline + form.inline, .cbi-button-edit { color: #fff !important; background-color: var(--active_color); + border-color: var(--active_color); } [data-page="admin-status-iptables"] .right { @@ -1358,6 +1465,7 @@ form.inline + form.inline, height: 2.5rem; line-height: 2.5rem; font-size: 0.92rem; + font-weight: 700; margin: 0 10px; } @@ -1410,6 +1518,7 @@ form.inline + form.inline, overflow: hidden; height: 0; opacity: 0; + pointer-events: none; margin: 0; padding: 0rem 1rem !important; } @@ -1418,6 +1527,7 @@ form.inline + form.inline, overflow: auto; height: auto; opacity: 1; + pointer-events: auto; transition: opacity .25s ease-in; margin: inherit !important; display: block; @@ -1491,7 +1601,6 @@ form.inline + form.inline, font-weight: normal; } -.cbi-value-field .cbi-dropdown, .cbi-value-field .cbi-input-select, .cbi-value input[type="text"], .cbi-value input[type="password"] { @@ -1651,90 +1760,106 @@ form.inline + form.inline, min-width: 13rem; } -.cbi-dynlist, -.cbi-dropdown { +.cbi-dynlist { position: relative; display: inline-flex; padding: 0; } -.cbi-dropdown[placeholder*="select"] { - max-width: 25rem; - height: auto; - margin-top: -3px; +.cbi-dropdown { + position: relative; + display: inline-flex; + align-items: stretch; + width: 100%; + max-width: 550px; + min-height: 2.8rem; + color: var(--inputtext_color); + background: var(--inputbg_color); + border: var(--input_border); + border-radius: 8px; + transition: color 150ms ease, border-color 150ms ease, box-shadow 150ms ease, background-color 150ms ease; } .cbi-dropdown > ul { display: flex; - overflow-x: hidden; - overflow-y: auto; + flex: 1 1 auto; + overflow: hidden; width: 100%; margin: 0 !important; padding: 0; list-style: none; - outline: 0; +} + +.cbi-dropdown.btn, +.cbi-dropdown.cbi-button { + width: auto; + max-width: none; + min-width: 105px; + min-height: 35px; + height: 35px; + line-height: 35px; + color: #fff; + background: transparent; + border: none; + box-shadow: none; +} + +.cbi-dropdown.btn > ul:not(.dropdown), +.cbi-dropdown.cbi-button > ul:not(.dropdown) { + width: auto; + padding-left: 0.5em; +} + +.cbi-dropdown.btn.spinning > ul:not(.dropdown), +.cbi-dropdown.cbi-button.spinning > ul:not(.dropdown) { + padding-left: 0; } .cbi-dropdown > ul.preview { display: none; } -.cbi-dropdown > .open { - flex-basis: 15px; -} - .cbi-dropdown > .open, .cbi-dropdown > .more { - font-size: 1rem; - font-weight: 900; - line-height: 2; - display: flex; - flex-direction: column; - flex-grow: 0; - flex-shrink: 0; + display: inline-flex; + align-items: center; justify-content: center; - padding: 0 .25em; - cursor: default; - text-align: center; - outline: 0; + flex: 0 0 auto; + min-width: 2.25rem; + padding: 0 0.6rem; + color: var(--inputtext_color); + border-left: 1px solid rgba(0, 0, 0, 0.08); } -.cbi-dropdown.btn { - min-height: 1.8rem; - padding-top: 0px; - padding-bottom: 0px; - padding-right: 0px; +.cbi-dropdown.btn > .open, +.cbi-dropdown.btn > .more, +.cbi-dropdown.cbi-button > .open, +.cbi-dropdown.cbi-button > .more { + color: inherit; + border-left: 1px solid rgba(255, 255, 255, 0.28); } -.cbi-dropdown.btn > .open { - font-size: 0.8rem; - padding: 0px; - margin: 0 5px; -} - -.cbi-dropdown.btn > div { - margin: 0px; +.cbi-dropdown > .open { + font-size: 0.85rem; } .cbi-dropdown > .more, .cbi-dropdown > ul > li[placeholder] { - font-weight: bold; + color: rgba(118, 131, 143, 0.85); display: none; - color: #777; - text-shadow: 1px 1px 0 #fff; } .cbi-dropdown > ul > li { display: none; - overflow: hidden; align-items: center; - align-self: center; - flex-grow: 1; - flex-shrink: 1; - min-height: 20px; - padding: .25em; + flex: 1 1 auto; + min-height: 2.8rem; + padding: 0 0.75rem; + overflow: hidden; white-space: nowrap; text-overflow: ellipsis; + color: var(--inputtext_color); + background: transparent; } .cbi-dropdown > ul > li .hide-open { @@ -1745,52 +1870,6 @@ form.inline + form.inline, display: none; } -.cbi-dropdown > ul > li[display]:not([display="0"]) { - border-left: thin solid #ccc; -} - -.cbi-dropdown > ul > li[data-value*="1"] { - padding-left: 15px !important; -} - -.cbi-dropdown[empty] > ul { - max-width: 1px; -} - -.cbi-dropdown > ul > li > form { - display: none; - margin: 0; - padding: 0; - pointer-events: none; -} - -.cbi-dropdown > ul > li img { - margin-right: .25em; - vertical-align: middle; -} - -.cbi-dropdown > ul > li > form > input[type="checkbox"] { - height: auto; - margin: 0; -} - -.cbi-dropdown > ul > li input[type="text"] { - height: 20px; -} - -.cbi-dropdown[open] > ul.dropdown { - position: absolute; - z-index: 1100; - display: block; - width: auto; - max-height: 200px !important; - /* border: thin solid #918e8c; */ - background: var(--active_color); - /* box-shadow: 0 0 4px #918e8c; */ - /* color: var(--main-menu-color); */ - text-align: center; -} - .cbi-dropdown > ul > li[display], .cbi-dropdown[open] > ul.preview, .cbi-dropdown[open] > ul.dropdown > li, @@ -1800,7 +1879,6 @@ form.inline + form.inline, .cbi-dropdown[multiple][empty] > .more { display: flex; align-items: center; - flex-grow: 1; } .cbi-dropdown[empty] > ul > li, @@ -1809,6 +1887,94 @@ form.inline + form.inline, display: block; } +.cbi-dropdown > ul > li > form { + display: none; + margin: 0; + padding: 0; + pointer-events: none; +} + +.cbi-dropdown > ul > li img { + margin-right: 0.35rem; + vertical-align: middle; +} + +.cbi-dropdown > ul > li input[type="text"] { + height: 2.2rem; + margin: 0; + padding: 0; + border: none; + background: transparent; + box-shadow: none; +} + +.cbi-dropdown[open], +.cbi-dropdown:focus-within { + border-color: #948FE1; + box-shadow: 0 0 6px rgba(148, 143, 225, 0.35); +} + +.cbi-dropdown.btn[open], +.cbi-dropdown.btn:focus-within, +.cbi-dropdown.cbi-button[open], +.cbi-dropdown.cbi-button:focus-within { + box-shadow: 0 1px 1px rgba(0, 0, 0, .05); +} + +/* Keep LuCI dropdowns functional by only fixing layering/clipping. */ +.cbi-dropdown[open] { + position: relative; +} + +.cbi-dropdown[open] > ul.dropdown { + position: absolute; + top: auto; + bottom: auto; + left: 0; + z-index: 1200; + display: block; + min-width: 100%; + padding: 0.25rem 0; + max-height: 200px; + overflow-x: hidden; + overflow-y: auto; + background: var(--sectionbg_color); + border: var(--input_border); + border-radius: 10px; + box-shadow: 0 16px 32px rgba(0, 0, 0, 0.12); +} + +.cbi-dropdown.btn[open] > ul.dropdown, +.cbi-dropdown.cbi-button[open] > ul.dropdown { + color: var(--text_color); + background: var(--sectionbg_color); + border: var(--input_border); +} + +.cbi-dropdown.btn[open] > ul.dropdown > li[selected], +.cbi-dropdown.cbi-button[open] > ul.dropdown > li[selected] { + color: #2f6f9f; + background: #d9eefb; +} + +.cbi-dropdown.btn[open] > ul.dropdown > li.focus:not([selected]), +.cbi-dropdown.btn[open] > ul.dropdown > li:hover:not([selected]), +.cbi-dropdown.cbi-button[open] > ul.dropdown > li.focus:not([selected]), +.cbi-dropdown.cbi-button[open] > ul.dropdown > li:hover:not([selected]) { + color: #2f6f9f; + background: #eef7ff; +} + +.cbi-dropdown[open] > ul.dropdown > li { + position: relative; + z-index: 1; + display: flex; + width: 100%; + background: var(--sectionbg_color); + min-height: 2.5rem; + padding: 0 0.85rem; +} + .cbi-dropdown[open] > ul.dropdown > li .hide-open { display: none; } @@ -1817,26 +1983,23 @@ form.inline + form.inline, display: initial; } -/* .cbi-dropdown[open] > ul.dropdown > li { - border-bottom: thin solid #ccc; -} */ +.cbi-dropdown[open] > ul.dropdown > li[unselectable] { + opacity: 0.7; +} .cbi-dropdown[open] > ul.dropdown > li[selected] { + color: #fff; background: var(--active_color); } -.cbi-dropdown[open] > ul.dropdown > li.focus { - background: linear-gradient(90deg, #a3c2e8 0%, #84aad9 100%); +.cbi-dropdown[open] > ul.dropdown > li.focus:not([selected]), +.cbi-dropdown[open] > ul.dropdown > li:hover:not([selected]) { + background: rgba(94, 166, 155, 0.16); } -.cbi-dropdown[open] > ul.dropdown > li:last-child { - margin-bottom: 0; - border-bottom: 0; - background: #627486; -} - -.cbi-dropdown[open] > ul.dropdown > li[unselectable] { - opacity: .7; +.cbi-dropdown.btn[open] > ul.dropdown > li, +.cbi-dropdown.cbi-button[open] > ul.dropdown > li { + color: var(--text_color); } .cbi-dropdown[open] > ul.dropdown > li > input.create-item-input:first-child:last-child { @@ -1845,15 +2008,37 @@ form.inline + form.inline, .cbi-dropdown[disabled] { pointer-events: none; - opacity: .6; + opacity: 0.6; } -.cbi-dropdown .zonebadge { - width: 100%; +.td.cbi-value-field.cbi-dropdown-open, +td.cbi-value-field.cbi-dropdown-open, +.cbi-section-table-row > .cbi-value-field.cbi-dropdown-open { + position: relative; + overflow: visible !important; + z-index: 100; } -.cbi-dropdown[open] .zonebadge { - width: auto; +/* Keep opened dropdowns inside modal flow so menus don't get clipped or spill + * outside the dialog frame. */ +body.modal-overlay-active #modal_overlay .modal.cbi-modal .cbi-value-field.cbi-dropdown-open { + position: relative; + overflow: visible !important; + margin-bottom: min(18rem, 50vh); + z-index: 120; +} + +body.modal-overlay-active #modal_overlay .modal.cbi-modal [data-tab-active="true"], +body.modal-overlay-active #modal_overlay .modal.cbi-modal .cbi-section-node, +body.modal-overlay-active #modal_overlay .modal.cbi-modal .cbi-section-node-tabbed { + overflow: visible !important; +} + +.cbi-section, +.cbi-section-create, +.cbi-section-table, +.cbi-section-table-row { + overflow: visible; } /* progressbar */ @@ -1880,7 +2065,7 @@ form.inline + form.inline, .cbi-progressbar::after { /* font-weight: bold; */ /* font-family: monospace; */ - font-size: 0.75rem !important; + font-size: 1.0rem !important; font-size-adjust: .38 !important; line-height: 1.5rem !important; position: absolute !important; @@ -1952,17 +2137,37 @@ form.inline + form.inline, color: var(--model_text_color); } -.modal ul { +.modal > ul, +.modal > ol { margin-left: 2.2em; word-break: break-word; color: var(--model_text_color); } -.modal li { +.modal > ul > li, +.modal > ol > li { list-style-type: square; color: var(--model_text_color); } +/* Keep LuCI widget lists in modals under JS control instead of generic modal list styling. */ +.modal .cbi-tabmenu, +.modal .cbi-dropdown > ul, +.modal .cbi-dropdown > ul.preview, +.modal .cbi-dropdown[open] > ul.dropdown { + margin-left: 0 !important; + padding-left: 0 !important; + list-style: none !important; + color: inherit; +} + +.modal .cbi-tabmenu > li, +.modal .cbi-dropdown > ul > li, +.modal .cbi-dropdown[open] > ul.dropdown > li { + list-style: none !important; + color: inherit; +} + .modal p { padding-left: .25rem; word-break: break-word; @@ -1988,6 +2193,16 @@ form.inline + form.inline, max-height: none; } +[data-page="admin-network-wireless"] #modal_overlay > .modal.cbi-modal { + width: calc(100vw - 2.5rem); + max-width: 860px; + max-height: calc(100vh - 6.5rem); + margin: 2rem auto; + padding: .85rem 1rem; + align-items: flex-start; + overflow-y: auto; +} + body.modal-overlay-active { overflow: hidden; height: 100vh; @@ -2032,6 +2247,82 @@ body.modal-overlay-active #modal_overlay { margin: 0; } +/* Keep LuCI split action buttons readable and vertically centered. */ +.cbi-dropdown.btn > ul > li[display], +.cbi-dropdown.cbi-button > ul > li[display] { + display: flex; + align-items: center; + min-height: 35px; + height: 35px; + padding-top: 0; + padding-bottom: 0; + line-height: 35px; + color: #fff; +} + +.cbi-dropdown.btn > .open, +.cbi-dropdown.cbi-button > .open { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 2.25rem; + min-height: 35px; + height: 35px; + line-height: 35px; + color: transparent; + opacity: 1; + font-size: 0; + position: relative; +} + +.cbi-dropdown.btn > .open::before, +.cbi-dropdown.cbi-button > .open::before { + content: "▾"; + display: block; + font-size: 1rem; + font-weight: 700; + line-height: 1; + color: #fff; + text-indent: 0; +} + +.cbi-dropdown.btn > .more, +.cbi-dropdown.cbi-button > .more { + display: none; +} + +.right .cbi-dropdown.btn, +.right .cbi-dropdown.cbi-button, +.right .cbi-dropdown.btn > ul > li[display], +.right .cbi-dropdown.cbi-button > ul > li[display], +.right .cbi-dropdown.btn > .open, +.right .cbi-dropdown.cbi-button > .open { + line-height: 35px; + vertical-align: middle; +} + +/* Override generic .btn/.cbi-button rules so split dropdown buttons keep the + arrow inside the same row instead of wrapping below the main label. */ +.cbi-dropdown.btn, +.cbi-dropdown.cbi-button, +.btn.cbi-button.cbi-dropdown, +div.cbi-dropdown.btn, +div.cbi-dropdown.cbi-button { + display: inline-flex !important; + align-items: stretch; + flex-wrap: nowrap; + padding: 0 !important; + white-space: nowrap; + vertical-align: middle; +} + +.cbi-dropdown.btn > ul, +.cbi-dropdown.cbi-button > ul { + width: auto; + flex: 0 1 auto; + overflow: hidden; +} + .right, .right::before { text-align: right !important; @@ -2059,10 +2350,53 @@ body.modal-overlay-active #modal_overlay { .cbi-page-actions { text-align: center; } + .cbi-page-actions > form[method="post"] { display: inline-block; } +[data-page="admin-network-network"] .cbi-button.cbi-button-neutral.reconnect { + color: #fff !important; + background-color: #337ab7 !important; + border-color: #337ab7 !important; +} + +[data-page="admin-network-network"] .cbi-button.cbi-button-neutral.reconnect:hover { + background-color: #286090 !important; + border-color: #286090 !important; +} + +[data-page="admin-network-network"] .cbi-button.cbi-button-edit { + color: #fff !important; + background-color: #5ea69b !important; + border-color: #5ea69b !important; +} + +[data-page="admin-network-network"] .cbi-button.cbi-button-edit:hover { + background-color: #4a8a80 !important; + border-color: #4a8a80 !important; +} + +[data-page="admin-network-network"] .cbi-button.cbi-button-add { + color: #fff !important; + background-color: #337ab7 !important; + border-color: #337ab7 !important; +} + +[data-page="admin-network-network"] .cbi-button.cbi-button-add:hover { + background-color: #286090 !important; + border-color: #286090 !important; +} + +[data-page="admin-network-network"] td > .ifacebadge, +[data-page="admin-network-network"] .td > .ifacebadge, +[data-page="admin-network-network"] .cbi-section-table .ifacebadge { + background-color: #eef2f3 !important; + color: #5f6b72 !important; + border: 1px solid #d8e0e3 !important; + box-shadow: inset 0 1px 0 rgb(255 255 255 / 70%), 0 1px 2px rgb(0 0 0 / 4%) !important; +} + .th[data-type="button"], .td[data-type="button"], .th[data-type="fvalue"], @@ -2081,9 +2415,11 @@ body.modal-overlay-active #modal_overlay { td > .ifacebadge, .td > .ifacebadge { font-size: .8rem; - background-color: var(--active_color); - color: white; - border-radius: 8px; + background-color: #eef2f3; + color: #5f6b72; + border: 1px solid #d8e0e3; + border-radius: 10px; + box-shadow: inset 0 1px 0 rgb(255 255 255 / 70%), 0 1px 2px rgb(0 0 0 / 4%); } .ifacebadge > em, @@ -2092,6 +2428,23 @@ td > .ifacebadge, align-self: flex-start; } +[data-page="admin-network-network"] .theme-network-icon, +[data-page="admin-network-network"] .ifacebadge > img, +[data-page="admin-network-network"] .cbi-tooltip-container > img.middle { + width: 1.65rem; + height: 1.65rem; + min-width: 1.65rem; + object-fit: contain; + vertical-align: middle; + flex-shrink: 0; +} + +[data-page="admin-network-network"] .cbi-tooltip.ifacebadge.large > img { + width: 1.95rem; + height: 1.95rem; + min-width: 1.95rem; +} + .network-status-table { display: flex; flex-wrap: wrap; @@ -2126,6 +2479,99 @@ td > .ifacebadge, padding: .5em; } +/* wireless overview */ +[data-page="admin-network-wireless"] #maincontent h3:first-of-type, +.node-admin-network-wireless #maincontent h3:first-of-type { + font-size: 1.5em !important; + font-weight: 800; + padding: 0 12px 12px 12px; + line-height: 1.2; +} + +[data-page="admin-network-wireless"] #cbi-wireless .cbi-section-table-row > .td[data-name="_stat"], +.node-admin-network-wireless #cbi-wireless .cbi-section-table-row > .td[data-name="_stat"] { + text-align: left !important; +} + +[data-page="admin-network-wireless"] #cbi-wireless > .table, +.node-admin-network-wireless #cbi-wireless > .table { + table-layout: fixed; + width: 100%; +} + +[data-page="admin-network-wireless"] #cbi-wireless .cbi-section-table-row > .td[data-name="_badge"], +.node-admin-network-wireless #cbi-wireless .cbi-section-table-row > .td[data-name="_badge"] { + width: 17rem !important; + min-width: 17rem; + max-width: 17rem; +} + +[data-page="admin-network-wireless"] #cbi-wireless .wireless-radio-badge, +.node-admin-network-wireless #cbi-wireless .wireless-radio-badge { + display: flex; + justify-content: center; + align-items: center; +} + +[data-page="admin-network-wireless"] #cbi-wireless .wireless-radio-badge > img, +.node-admin-network-wireless #cbi-wireless .wireless-radio-badge > img { + width: 88px !important; + height: 88px !important; + min-width: 88px !important; +} + +[data-page="admin-network-wireless"] #cbi-wireless .wireless-radio-status, +.node-admin-network-wireless #cbi-wireless .wireless-radio-status, +[data-page="admin-network-wireless"] #cbi-wireless .wireless-network-status, +.node-admin-network-wireless #cbi-wireless .wireless-network-status { + text-align: left; + max-width: 100%; +} + +[data-page="admin-network-wireless"] #cbi-wireless .wireless-radio-title, +.node-admin-network-wireless #cbi-wireless .wireless-radio-title { + font-size: 1.08rem; + font-weight: 700; + line-height: 1.4; + color: var(--text_color); +} + +[data-page="admin-network-wireless"] #cbi-wireless .wireless-radio-meta, +.node-admin-network-wireless #cbi-wireless .wireless-radio-meta, +[data-page="admin-network-wireless"] #cbi-wireless .wireless-network-status-row, +.node-admin-network-wireless #cbi-wireless .wireless-network-status-row { + margin-top: .3rem; + line-height: 1.7; + color: var(--text_color); + text-align: left; +} + +[data-page="admin-network-wireless"] #cbi-wireless .wireless-network-status-note, +.node-admin-network-wireless #cbi-wireless .wireless-network-status-note { + margin-top: .4rem; +} + +[data-page="admin-network-wireless"] #cbi-wireless .wireless-status-sep, +.node-admin-network-wireless #cbi-wireless .wireless-status-sep { + display: inline-block; + padding: 0 .35rem; + color: var(--text_color); + opacity: .7; +} + +[data-page="admin-network-wireless"] #cbi-wireless .cbi-section-table-row > .td.cbi-section-actions, +.node-admin-network-wireless #cbi-wireless .cbi-section-table-row > .td.cbi-section-actions { + width: 18.5rem !important; + min-width: 18.5rem; + max-width: 18.5rem; +} + +[data-page="admin-network-wireless"] #cbi-wireless .cbi-section-table-row > .td[data-name="_stat"], +.node-admin-network-wireless #cbi-wireless .cbi-section-table-row > .td[data-name="_stat"] { + width: auto !important; +} + + /* textarea */ #content_syslog { padding: 5px; @@ -2162,11 +2608,14 @@ td > .ifacebadge, #syslog { width: 100%; min-height: 15rem; - padding: 10px; + padding: 12px 14px; margin-bottom: 20px; border-radius: 0; background-color: var(--sectionbg_color); border: none; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace; + font-size: 13px !important; + line-height: 1.55 !important; } #syslog:focus { @@ -2293,6 +2742,122 @@ td > .ifacebadge, padding: .25em; } +[data-page="admin-status-overview"] .port-status-grid, +[data-page="admin-status-overview"] .cbi-section > div[style*="grid-template-columns:repeat(auto-fit, minmax(70px, 1fr))"] { + display: grid !important; + align-items: stretch; + justify-items: stretch; + width: 100%; + max-width: 100%; + padding: 0.2rem 0.6rem 0.35rem; + margin: 0 auto 1rem !important; +} + +[data-page="admin-status-overview"] .port-status-slot { + display: flex; + justify-content: center; + align-items: stretch; + min-width: 0; + padding: 0.15rem 0.65rem 0.75rem; +} + +[data-page="admin-status-overview"] .port-status-card, +[data-page="admin-status-overview"] .cbi-section > div[style*="grid-template-columns:repeat(auto-fit, minmax(70px, 1fr))"] > .ifacebox { + margin: 0 !important; + width: fit-content; + min-width: 76px !important; + max-width: 108px !important; + border-radius: 10px; + overflow: hidden; + background: var(--sectionbg_color); + box-shadow: 3px 3px 3px rgb(0 0 0 / 5%); + text-align: center; +} + +[data-page="admin-status-overview"] .port-status-card .ifacebox-head, +[data-page="admin-status-overview"] .cbi-section > div[style*="grid-template-columns:repeat(auto-fit, minmax(70px, 1fr))"] > .ifacebox > .ifacebox-head { + display: flex; + align-items: center; + justify-content: center; + padding: 0.24rem 0.45rem 0.22rem; + background: #eef2f3; + border-radius: 4px 4px 0 0; + box-shadow: inset 0 -1px 0 rgb(0 0 0 / 6%); + font-size: 0.76rem; + font-weight: 700; + line-height: 1.2; + text-align: center; + color: #68757b; + letter-spacing: 0.01em; + text-transform: none; +} + +[data-page="admin-status-overview"] .port-status-card .ifacebox-head strong, +[data-page="admin-status-overview"] .cbi-section > div[style*="grid-template-columns:repeat(auto-fit, minmax(70px, 1fr))"] > .ifacebox > .ifacebox-head strong { + display: block; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-weight: inherit; +} + +[data-page="admin-status-overview"] .port-status-card .ifacebox-body, +[data-page="admin-status-overview"] .cbi-section > div[style*="grid-template-columns:repeat(auto-fit, minmax(70px, 1fr))"] > .ifacebox > .ifacebox-body { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 0.4rem 0.45rem; + gap: 0.14rem; + text-align: center; +} + +[data-page="admin-status-overview"] .port-status-card .ifacebox-body img, +[data-page="admin-status-overview"] .cbi-section > div[style*="grid-template-columns:repeat(auto-fit, minmax(70px, 1fr))"] > .ifacebox > .ifacebox-body img { + background-size: 24px 24px !important; + width: 24px !important; + height: 24px !important; + padding: 0 0 0 24px !important; + margin: 0.05rem auto; +} + +[data-page="admin-status-overview"] .port-status-card .ifacebox-body img + br, +[data-page="admin-status-overview"] .cbi-section > div[style*="grid-template-columns:repeat(auto-fit, minmax(70px, 1fr))"] > .ifacebox > .ifacebox-body img + br { + display: none; +} + +[data-page="admin-status-overview"] .port-status-card .ifacebox-body > span, +[data-page="admin-status-overview"] .cbi-section > div[style*="grid-template-columns:repeat(auto-fit, minmax(70px, 1fr))"] > .ifacebox > .ifacebox-body > span { + display: block; + width: 100%; + text-align: center; +} + +[data-page="admin-status-overview"] .port-status-card .ifacebox-head.cbi-tooltip-container, +[data-page="admin-status-overview"] .cbi-section > div[style*="grid-template-columns:repeat(auto-fit, minmax(70px, 1fr))"] > .ifacebox > .ifacebox-head.cbi-tooltip-container { + display: flex; + gap: 0.18rem; + padding: 0 0.45rem 0.2rem; + background: transparent; +} + +[data-page="admin-status-overview"] .port-status-card .ifacebox-head.cbi-tooltip-container .zonebadge, +[data-page="admin-status-overview"] .cbi-section > div[style*="grid-template-columns:repeat(auto-fit, minmax(70px, 1fr))"] > .ifacebox > .ifacebox-head.cbi-tooltip-container .zonebadge { + flex: 1; + height: 4px !important; + padding: 0 !important; + border-radius: 999px; +} + +[data-page="admin-status-overview"] .port-status-card .ifacebox-body .cbi-tooltip-container, +[data-page="admin-status-overview"] .cbi-section > div[style*="grid-template-columns:repeat(auto-fit, minmax(70px, 1fr))"] > .ifacebox > .ifacebox-body .cbi-tooltip-container { + width: 100%; + text-align: center !important; + font-size: 0.76rem !important; + line-height: 1.35; +} + .cbi-image-button { margin-left: .5rem; } @@ -2355,10 +2920,6 @@ input[type="radio"]:focus { background-color: #eee; } -.cbi-section-table-row > .cbi-value-field .cbi-dropdown { - min-width: 7rem; -} - .cbi-section-create { display: inline-flex; align-items: center; @@ -2385,10 +2946,6 @@ td.cbi-value-field var, border-top: thin solid #ccc; } -.cbi-dropdown-container { - position: relative; -} - .cbi-tooltip-container, span[data-tooltip], span[data-tooltip] .label { @@ -2507,6 +3064,45 @@ label[data-index][data-depends] { box-shadow: none; } +[data-page="admin-network-diagnostics"] .td.left { + display: inline-flex; + align-items: center; + gap: 0.65rem; +} + +[data-page="admin-network-diagnostics"] .td.left > input[type="text"], +[data-page="admin-network-diagnostics"] .td.left > select { + margin: 0 !important; + align-self: center; +} + +[data-page="admin-network-diagnostics"] .td.left > .diag-action { + display: inline-flex; + align-items: center; + align-self: center; + margin: 0; +} + +[data-page="admin-network-diagnostics"] .td.left > .diag-action > .cbi-button { + margin: 0 !important; + align-self: center; +} + +.cbi-section > .table[width="100%"] { + border-collapse: separate; + border-spacing: 0 0.55rem; +} + +.cbi-section > .table[width="100%"] tr > td { + padding-top: 0.45rem; + padding-bottom: 0.45rem; + line-height: 1.5; +} + +.cbi-section > .table[width="100%"] tr > td:first-child { + padding-left: 0.9rem; +} + input[name="ping"], input[name="traceroute"], input[name="nslookup"] { @@ -2788,6 +3384,38 @@ input[name="nslookup"] { width: 23rem; } +[data-page="admin-network-wireless"] #wifi_assoclist_table .ifacebadge, +.node-admin-network-wireless #wifi_assoclist_table .ifacebadge { + padding: .22rem .5rem; + gap: .18rem; + border-radius: 8px; + align-items: center; + line-height: 1.25; +} + +[data-page="admin-network-wireless"] #wifi_assoclist_table .ifacebadge > img, +.node-admin-network-wireless #wifi_assoclist_table .ifacebadge > img { + width: 16px !important; + height: 16px !important; + min-width: 16px !important; + margin-right: .2rem !important; + align-self: center; +} + +[data-page="admin-network-wireless"] #wifi_assoclist_table .ifacebadge > span, +.node-admin-network-wireless #wifi_assoclist_table .ifacebadge > span { + display: inline-flex; + align-items: baseline; + flex-wrap: wrap; + gap: .15rem; +} + +[data-page="admin-network-wireless"] #wifi_assoclist_table > .tr > .td, +.node-admin-network-wireless #wifi_assoclist_table > .tr > .td { + padding-top: .45rem; + padding-bottom: .45rem; +} + /* firewall */ #iptables { margin: 0; @@ -3074,6 +3702,10 @@ input[name="nslookup"] { width: 100% !important; } + [data-page="admin-network-diagnostics"] .td.left { + display: block; + } + .node-network-diagnostics > .main .cbi-map div.cbi-section > div input[type="text"] { } @@ -3208,13 +3840,32 @@ input[name="nslookup"] { display: block; overflow: auto; } - .node-admin-status-routes #view table, +.node-admin-status-routes #view table, .node-admin-status-processes #view table, .node-admin-status-realtime-connections #view table[id*=connections] { display: block; white-space: nowrap; } + #view [data-table] > h3 { + margin-top: 1.75rem; + margin-bottom: 1.15rem; + padding: 12px 12px 12px 12px; + line-height: 1.35; + } + + #view [data-chain] > h4 { + margin-top: 1.15rem; + margin-bottom: 0.85rem; + padding-top: 0.3rem; + padding-bottom: 0.2rem; + line-height: 1.35; + } + + #view [data-chain] > table { + margin-bottom: 1.25rem; + } + } @@ -3580,19 +4231,6 @@ td.cbi-button, .cbi-button-apply, background: none !important; } -/* fix */ -.cbi-dropdown.btn.cbi-button.cbi-button-apply.important, -.cbi-dropdown.btn.cbi-button.cbi-button-negative.important { - line-height: unset; - min-width: 105px; - padding-left: 5px; -} -.cbi-dropdown.btn.cbi-button.cbi-button-action { - line-height: unset; - min-width: 110px; - padding-left: 5px; -} - .node-admin-system-ttyd-ttyd iframe { border-radius: 5px !important; } @@ -3624,3 +4262,548 @@ td.cbi-button, .cbi-button-apply, .node-admin-status-overview > .main #view td:nth-child(2) { white-space: normal; } + +/* Merged overrides formerly kept in compat-openwrt23.css. */ + +#view { + padding: 0; +} + +#view > * { + margin-bottom: 1rem; +} + +.cbi-map { + margin-top: 10px; +} + +.cbi-input-text, +.cbi-input-password, +.cbi-input-select { + color: var(--inputtext_color); + padding: 5px 10px; + font-size: 0.92rem; + border: var(--input_border); + background-color: var(--inputbg_color); + border-radius: 8px; + height: 2.8rem; + line-height: 2.8rem; + max-width: 550px; +} + +.cbi-input-checkbox, +.cbi-input-radio { + width: 1.2rem; + height: 1.2rem; + margin-right: 0.5rem; + vertical-align: middle; + accent-color: var(--active_color); +} + +.btn.cbi-button, +button.cbi-button { + -webkit-appearance: none; + appearance: none; + text-transform: uppercase; + color: #fff; + background-color: #337ab7; + transition: all 0.2s ease-in-out; + display: inline-block; + border: none; + cursor: pointer; + text-align: center; + white-space: nowrap; + min-width: 80px; + padding: 0 8px; + height: 35px; + line-height: 35px; + border-radius: 8px; +} + +.cbi-button-positive, +.cbi-button-action { + color: var(--bttext_color) !important; + background-color: var(--active_color) !important; +} + +.cbi-button-positive:hover, +.cbi-button-action:hover { + background-color: #4a8a80 !important; +} + +.cbi-button-negative { + color: #fff !important; + background-color: #d9534f !important; +} + +.cbi-button-negative:hover { + background-color: #c9302c !important; +} + +.cbi-button-neutral { + color: #fff !important; + background-color: #617486 !important; +} + +.table.cbi-section-table { + width: 100%; + border-collapse: collapse; +} + +.table.cbi-section-table .tr { + display: table-row; +} + +.table.cbi-section-table .th, +.table.cbi-section-table .td { + display: table-cell; + padding: 12px; + vertical-align: middle; +} + +.tr.cbi-section-table-titles { + background-color: var(--inputbg_color); + font-weight: bold; +} + +.cbi-dropdown { + position: relative; + display: inline-flex; + min-height: 2.8rem; + border: var(--input_border); + background-color: var(--inputbg_color); + border-radius: 8px; +} + +.cbi-dropdown:not(.btn):not(.cbi-button) { + cursor: pointer; + color: var(--inputtext_color); +} + +.cbi-dropdown > ul { + display: flex; + overflow-x: hidden; + overflow-y: auto; + width: 100%; + margin: 0 !important; + padding: 0; + list-style: none; +} + +.cbi-dropdown:not(.btn):not(.cbi-button) > ul > li { + display: none; + overflow: hidden; + align-items: center; + align-self: center; + flex-grow: 1; + flex-shrink: 1; + min-height: 2.8rem; + padding: 0.5em 0.75em; + white-space: nowrap; + text-overflow: ellipsis; + color: var(--inputtext_color); +} + +.cbi-dropdown:not(.btn):not(.cbi-button) > ul > li[display], +.cbi-dropdown:not(.btn):not(.cbi-button)[open] > ul.preview, +.cbi-dropdown:not(.btn):not(.cbi-button)[open] > ul.dropdown > li, +.cbi-dropdown[multiple] > ul > li > label, +.cbi-dropdown[multiple][open] > ul.dropdown > li, +.cbi-dropdown[multiple][more] > .more, +.cbi-dropdown[multiple][empty] > .more { + display: flex; + align-items: center; + flex-grow: 1; +} + +.cbi-dropdown[empty] > ul > li, +.cbi-dropdown[optional][open] > ul.dropdown > li[placeholder], +.cbi-dropdown[multiple][open] > ul.dropdown > li > form { + display: block; +} + +.cbi-dropdown:not(.btn):not(.cbi-button) > ul > li[display]:not([display="0"]) { + border-left: 1px solid #d9e3e6; +} + +.cbi-dropdown:not(.btn):not(.cbi-button) > ul > li[data-value] { + padding-left: 0.75em !important; +} + +.cbi-dropdown:not(.btn):not(.cbi-button) > .open, +.cbi-dropdown:not(.btn):not(.cbi-button) > .more { + color: var(--inputtext_color); + line-height: 1; +} + +.cbi-dropdown:not(.btn):not(.cbi-button) > .more, +.cbi-dropdown:not(.btn):not(.cbi-button) > ul > li[placeholder] { + color: #87929a; + text-shadow: none; +} + +.cbi-dropdown:not(.btn):not(.cbi-button)[open] > ul.dropdown { + position: absolute; + z-index: 1100; + top: auto; + bottom: auto; + left: 0; + right: auto; + display: block; + min-width: 100%; + width: auto; + max-width: none; + max-height: 260px !important; + overflow-x: hidden; + overflow-y: auto; + background: var(--sectionbg_color); + border: var(--input_border); + border-radius: 0 0 8px 8px; + box-shadow: 0 8px 18px rgb(0 0 0 / 10%); + text-align: left; +} + +.cbi-dropdown:not(.btn):not(.cbi-button)[open] > ul.dropdown > li { + width: 100%; + padding: 0.55em 0.85em; + cursor: pointer; + color: var(--inputtext_color); + border-bottom: 1px solid #e9eef0; + background: transparent; +} + +.cbi-dropdown:not(.btn):not(.cbi-button)[open] > ul.dropdown > li[selected] { + background: rgb(94 166 155 / 16%); + color: var(--active_color); +} + +.cbi-dropdown:not(.btn):not(.cbi-button)[open] > ul.dropdown > li.focus, +.cbi-dropdown:not(.btn):not(.cbi-button)[open] > ul.dropdown > li:hover { + background: rgb(94 166 155 / 10%); + color: var(--inputtext_color); +} + +.cbi-dropdown:not(.btn):not(.cbi-button)[open] > ul.dropdown > li:last-child { + margin-bottom: 0; + border-bottom: 0; + background: transparent; +} + +.cbi-dropdown:not(.btn):not(.cbi-button)[open] > ul.dropdown > li > input.create-item-input:first-child:last-child { + width: 100%; + height: 2.1rem; + padding: 0 0.6em; + border: var(--input_border); + border-radius: 6px; + background: var(--inputbg_color); +} + +.cbi-tabmenu { + display: flex; + flex-wrap: wrap; + border-bottom: var(--tabmenu_bottom); + margin-bottom: 1rem; +} + +.cbi-tabmenu > li { + display: inline-block; + margin-right: 0.5rem; +} + +.cbi-tabmenu > li > a { + display: block; + padding: 0.5rem 1rem; + text-decoration: none; + color: var(--text_color); + border-bottom: 2px solid transparent; + transition: all 0.2s; +} + +.cbi-tabmenu > li.cbi-tab > a, +.cbi-tabmenu > li > a:hover { + color: var(--active_color); + border-bottom-color: var(--active_color); +} + +.modal { + display: flex; + flex-wrap: wrap; + align-items: center; + width: 90%; + max-width: 600px; + min-height: 32px; + margin: 5em auto; + padding: 1em; + border-radius: 8px; + background: var(--model_background); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); +} + +.modal > * { + flex-basis: 100%; + margin-bottom: 0.5em; +} + +.modal > h4 { + color: var(--text_color); + margin-bottom: 1em; +} + +.modal > .btn, +.modal > .cbi-button { + flex-basis: auto; + margin: 0.25em; +} + +[data-page="admin-network-wireless"] #modal_overlay > .modal.cbi-modal { + width: calc(100vw - 2.5rem); + max-width: 860px; + max-height: calc(100vh - 6.5rem); + margin: 2rem auto; + padding: 0.85rem 1rem; + align-items: flex-start; + overflow-y: auto; +} + +body.modal-overlay-active #modal_overlay .modal.cbi-modal [data-tab-active="true"], +body.modal-overlay-active #modal_overlay .modal.cbi-modal .cbi-section-node, +body.modal-overlay-active #modal_overlay .modal.cbi-modal .cbi-section-node-tabbed, +body.modal-overlay-active #modal_overlay .modal.cbi-modal .cbi-value-field.cbi-dropdown-open { + overflow: visible !important; +} + +/* Undo generic modal list styling for LuCI dropdown widgets inside wireless edit dialogs. */ +[data-page="admin-network-wireless"] #modal_overlay .modal.cbi-modal .cbi-dropdown > ul, +[data-page="admin-network-wireless"] #modal_overlay .modal.cbi-modal .cbi-dropdown > ul.preview, +[data-page="admin-network-wireless"] #modal_overlay .modal.cbi-modal .cbi-dropdown[open] > ul.dropdown { + margin-left: 0 !important; + padding-left: 0 !important; + list-style: none !important; +} + +[data-page="admin-network-wireless"] #modal_overlay .modal.cbi-modal .cbi-dropdown > ul > li, +[data-page="admin-network-wireless"] #modal_overlay .modal.cbi-modal .cbi-dropdown[open] > ul.dropdown > li { + list-style: none !important; + margin-left: 0 !important; +} + +/* Keep NetworkSelect choices readable in wireless modal dropdowns. */ +[data-page="admin-network-wireless"] #modal_overlay .modal.cbi-modal .cbi-dropdown[open] > ul.dropdown > li { + display: flex !important; + align-items: center; + color: var(--inputtext_color); +} + +[data-page="admin-network-wireless"] #modal_overlay .modal.cbi-modal .cbi-dropdown[multiple] > ul > li > form, +[data-page="admin-network-wireless"] #modal_overlay .modal.cbi-modal .cbi-dropdown[multiple][open] > ul.dropdown > li > form { + display: block !important; + flex: 0 0 auto; + margin: 0 !important; + padding: 0 !important; +} + +[data-page="admin-network-wireless"] #modal_overlay .modal.cbi-modal .cbi-dropdown[multiple] > ul > li > label, +[data-page="admin-network-wireless"] #modal_overlay .modal.cbi-modal .cbi-dropdown[multiple][open] > ul.dropdown > li > label { + display: flex !important; + align-items: center; + flex: 1 1 auto; + min-width: 0; + margin: 0 !important; + overflow: visible; + color: inherit !important; +} + +[data-page="admin-network-wireless"] #modal_overlay .modal.cbi-modal .cbi-dropdown[multiple][open] > ul.dropdown > li { + gap: 0.55rem; +} + +/* Fallback text rendering for NetworkSelect choices when badge markup is hidden by theme rules. */ +[data-page="admin-network-wireless"] #modal_overlay .modal.cbi-modal .cbi-dropdown > ul > li[data-value]:not([data-value=""])::before { + content: attr(data-value); + display: inline-block; + color: inherit; + font-size: 0.92rem; + line-height: 1.4; + white-space: nowrap; + margin-right: 0.5rem; + flex: 0 0 auto; +} + +[data-page="admin-network-wireless"] #modal_overlay .modal.cbi-modal .cbi-dropdown[open] > ul.dropdown > li[selected]::before { + color: #fff; +} + +[data-page="admin-network-wireless"] #modal_overlay .modal.cbi-modal .cbi-dropdown[multiple] > ul > li[data-value]::before, +[data-page="admin-network-wireless"] #modal_overlay .modal.cbi-modal .cbi-dropdown[multiple][open] > ul.dropdown > li[data-value]::before { + content: none; + display: none !important; +} + +[data-page="admin-network-wireless"] #modal_overlay .modal.cbi-modal .cbi-dropdown[open] > ul.dropdown > li .ifacebadge, +[data-page="admin-network-wireless"] #modal_overlay .modal.cbi-modal .cbi-dropdown[open] > ul.dropdown > li .zonebadge { + display: inline-flex !important; + align-items: center; + gap: 0.2rem; + max-width: 100%; + padding: 0.3rem 0.6rem; + color: #5f6b72 !important; + background-color: #eef2f3 !important; + border: 1px solid #d8e0e3 !important; + border-radius: 10px; + box-shadow: inset 0 1px 0 rgb(255 255 255 / 70%), 0 1px 2px rgb(0 0 0 / 4%); + font-size: 0.85rem; + line-height: 1.4; + white-space: nowrap; + visibility: visible !important; + opacity: 1 !important; + margin: 0 !important; +} + +[data-page="admin-network-wireless"] #modal_overlay .modal.cbi-modal .cbi-dropdown[open] > ul.dropdown > li .ifacebadge > em, +[data-page="admin-network-wireless"] #modal_overlay .modal.cbi-modal .cbi-dropdown[open] > ul.dropdown > li .ifacebadge > img { + align-self: center; +} + +.cbi-progressbar { + position: relative; + min-width: 11rem; + height: 1.5rem; + background-color: var(--progressbar_color); + border-radius: 5px; + overflow: hidden; +} + +.cbi-progressbar > div { + height: 100%; + background: var(--progressbar); + transition: width 0.25s ease-in; +} + +.cbi-progressbar::after { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + content: attr(title); + text-align: center; + line-height: 1.5rem; + font-size: 0.75rem; + color: var(--progressbartxt_color); +} + +.ifacebadge { + display: inline-flex; + align-items: center; + gap: 0.2rem; + padding: 0.3rem 0.6rem; + background-color: #eef2f3; + color: #5f6b72; + border: 1px solid #d8e0e3; + border-radius: 10px; + box-shadow: inset 0 1px 0 rgb(255 255 255 / 70%), 0 1px 2px rgb(0 0 0 / 4%); + font-size: 0.85rem; +} + +.ifacebadge > img, +.ifacebadge > em { + width: 16px; + height: 16px; +} + +.zonebadge { + display: inline-block; + padding: 0.2rem 0.5rem; + border-radius: 4px; +} + +@media screen and (max-width: 992px) { + + .main-left { + transform: translateX(-100%); + transition: transform 0.3s ease; + } + + .main-left.active, + .main-left[style*="width: 17rem"], + .main-left:not([style*="width: 0"]) { + transform: translateX(0); + } +} + +[data-page="admin-status-overview"] .cbi-section { + margin-bottom: 1rem; +} + +[data-page="admin-network-network"] .ifacebox { + margin: 0.5rem; + border-radius: 8px; + overflow: hidden; +} + +[data-page="admin-network-wireless"] .cbi-section-table { + overflow-x: auto; +} + +[data-page="admin-system-opkg"] .controls { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-bottom: 1rem; +} + +[data-page="admin-system-opkg"] .controls input[type="text"] { + flex: 1; + min-width: 200px; +} + +@media (prefers-color-scheme: dark) { + + code, pre { + background-color: #2d2d2d; + color: #f8f8f2; + } + + + .table.cbi-section-table .tr:nth-child(even) { + background-color: rgba(255, 255, 255, 0.05); + } + + + .cbi-input-text:focus, + .cbi-input-password:focus, + .cbi-input-select:focus { + border-color: var(--active_color); + box-shadow: 0 0 6px var(--active_color); + } +} + +@media print { + .main-left, + .navbar, + header, + .showSide, + .darkMask { + display: none !important; + } + + .main-right { + width: 100% !important; + margin: 0 !important; + } +} + +:focus-visible { + outline: 2px solid var(--active_color); + outline-offset: 2px; +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} diff --git a/luci-theme-design/htdocs/luci-static/design/favicon.ico b/luci-theme-design/htdocs/luci-static/design/favicon.ico index f82da9bb2..e65f20310 100644 Binary files a/luci-theme-design/htdocs/luci-static/design/favicon.ico and b/luci-theme-design/htdocs/luci-static/design/favicon.ico differ diff --git a/luci-theme-design/htdocs/luci-static/design/fonts/GenJyuuGothic-Medium.otf b/luci-theme-design/htdocs/luci-static/design/fonts/GenJyuuGothic-Medium.otf new file mode 100644 index 000000000..916b4a11d Binary files /dev/null and b/luci-theme-design/htdocs/luci-static/design/fonts/GenJyuuGothic-Medium.otf differ diff --git a/luci-theme-design/htdocs/luci-static/design/images/switch.png b/luci-theme-design/htdocs/luci-static/design/images/switch.png new file mode 100644 index 000000000..ade12a538 Binary files /dev/null and b/luci-theme-design/htdocs/luci-static/design/images/switch.png differ diff --git a/luci-theme-design/htdocs/luci-static/design/images/switch_disabled.png b/luci-theme-design/htdocs/luci-static/design/images/switch_disabled.png new file mode 100644 index 000000000..346d75278 Binary files /dev/null and b/luci-theme-design/htdocs/luci-static/design/images/switch_disabled.png differ diff --git a/luci-theme-design/htdocs/luci-static/design/js/style.js b/luci-theme-design/htdocs/luci-static/design/js/style.js index 2b641e5be..23da7e5b7 100644 --- a/luci-theme-design/htdocs/luci-static/design/js/style.js +++ b/luci-theme-design/htdocs/luci-static/design/js/style.js @@ -1,28 +1,404 @@ (function ($) { + function applySafeAreaFix() { + var url = self.location.href; - // 修复某些插件导致在https下env(safe-area-inset-bottom)为0的情况 - var url = self.location.href; - if ((/(iPhone|iPad|iPod|iOS|Mac|Macintosh)/i.test(navigator.userAgent)) && url.indexOf("openclash") != -1 ) { - var oMeta = document.createElement('meta'); - oMeta.content = 'width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0,viewport-fit=cover'; - oMeta.name = 'viewport'; - document.getElementsByTagName('head')[0].appendChild(oMeta); + // 修复某些插件导致在 https 下 env(safe-area-inset-bottom) 为 0 的情况 + if ((/(iPhone|iPad|iPod|iOS|Mac|Macintosh)/i.test(navigator.userAgent)) && url.indexOf("openclash") !== -1) { + var oMeta = document.createElement('meta'); + oMeta.content = 'width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0,viewport-fit=cover'; + oMeta.name = 'viewport'; + document.getElementsByTagName('head')[0].appendChild(oMeta); + } } - // 设置indicators图标,(放在menu script执行之前passwall某些代码错误中断导致失败) - document.getElementById("indicators").addEventListener('DOMSubtreeModified', function () { - var child = document.getElementById("indicators"); - if (child.firstElementChild.getAttribute("data-indicator") != "uci-changes") { - child.firstElementChild.textContent = eval("'\ue6b9'") - } - }, false); + function setHeaderShadow() { + if (window.innerWidth <= 992) { + $("header").css("box-shadow", "0 2px 4px rgb(0 0 0 / 8%)"); + } else { + $("header").css("box-shadow", "17rem 2px 4px rgb(0 0 0 / 8%)"); + } + } - // 监听窗口大小,动态设置header box阴影长度 - $(window).resize( function () { - if (window.innerWidth <= 992) { - $("header").css("box-shadow", "0 2px 4px rgb(0 0 0 / 8%)") - } else { - $("header").css("box-shadow", "17rem 2px 4px rgb(0 0 0 / 8%)") - } + function syncIndicators() { + var indicatorRoot = document.getElementById("indicators"); + + if (!indicatorRoot) { + return; + } + + Array.prototype.forEach.call(indicatorRoot.querySelectorAll('[data-indicator]'), function (indicator) { + var id = indicator.getAttribute('data-indicator'); + var label = (indicator.textContent || '').trim(); + + if (!label) { + label = indicator.getAttribute('aria-label') || indicator.getAttribute('title') || ''; + } + + if (id === 'poll-status') { + indicator.setAttribute('title', label); + indicator.setAttribute('aria-label', label); + } else if (id === 'uci-changes') { + var match = label.match(/(\d+)\s*$/); + + indicator.setAttribute('title', label); + indicator.setAttribute('aria-label', label); + indicator.setAttribute('data-count', match ? match[1] : ''); + } }); -})(jQuery); \ No newline at end of file + } + + function initIndicators() { + var indicatorRoot = document.getElementById("indicators"); + + if (!indicatorRoot) { + return; + } + + syncIndicators(); + + new MutationObserver(syncIndicators).observe(indicatorRoot, { + childList: true, + subtree: true, + characterData: true, + attributes: true, + attributeFilter: ['data-style'] + }); + } + + function syncOverviewPortStatus() { + var view = document.getElementById('view'); + var portGroups = new Map(); + var mediaPath = (L.env && L.env.media) ? L.env.media : '/luci-static/design'; + var iconMap = { + 'alias': 'ethernet', + 'alias_disabled': 'ethernet_disabled', + 'bridge': 'bridge', + 'bridge_disabled': 'bridge_disabled', + 'ethernet': 'ethernet', + 'ethernet_disabled': 'ethernet_disabled', + 'switch': 'switch', + 'switch_disabled': 'switch_disabled', + 'tunnel': 'tunnel', + 'tunnel_disabled': 'tunnel_disabled', + 'vlan': 'vlan', + 'vlan_disabled': 'vlan_disabled', + 'wifi': 'wifi', + 'wifi_disabled': 'wifi_disabled', + 'signal-none': 'signal-none', + 'signal-0': 'signal-0', + 'signal-0-25': 'signal-0-25', + 'signal-25-50': 'signal-25-50', + 'signal-50-75': 'signal-50-75', + 'signal-75-100': 'signal-75-100', + 'signal-000-000': 'signal-0', + 'signal-000-025': 'signal-0-25', + 'signal-025-050': 'signal-25-50', + 'signal-050-075': 'signal-50-75', + 'signal-075-100': 'signal-75-100', + 'port_up': 'port_up', + 'port_down': 'port_down' + }; + + if (!view || document.body.getAttribute('data-page') !== 'admin-status-overview') { + return; + } + + Array.prototype.forEach.call(view.querySelectorAll('img[src*="/luci-static/resources/icons/"]'), function (img) { + var src = img.getAttribute('src') || ''; + var match = src.match(/\/icons\/([^/?#]+)\.(?:png|svg)(?:[?#].*)?$/); + var key = match ? match[1] : ''; + var mapped = iconMap[key]; + var badge = img.closest('.ifacebadge'); + + if (!mapped || !badge) { + return; + } + + var size = key.indexOf('port_') === 0 ? '18px' : + key.indexOf('signal-') === 0 ? '16px' : '20px'; + + img.setAttribute('src', mediaPath + '/images/' + mapped + '.png'); + img.style.setProperty('background-image', 'none', 'important'); + img.style.setProperty('visibility', 'visible', 'important'); + img.style.setProperty('opacity', '1', 'important'); + img.style.setProperty('display', 'inline-block', 'important'); + img.style.setProperty('width', size, 'important'); + img.style.setProperty('height', size, 'important'); + img.style.setProperty('min-width', size, 'important'); + img.style.setProperty('padding', '0', 'important'); + img.style.setProperty('margin', '0 .25rem 0 0', 'important'); + img.style.setProperty('vertical-align', 'middle', 'important'); + img.style.setProperty('object-fit', 'contain', 'important'); + }); + + Array.prototype.forEach.call(view.querySelectorAll('.cbi-section > div'), function (grid) { + var isPortGrid = grid && + grid.style && + grid.style.display === 'grid' && + grid.style.gridTemplateColumns.indexOf('minmax(70px, 1fr)') !== -1 && + grid.querySelector('img[src*="/luci-static/resources/icons/port_"]'); + + if (!isPortGrid) { + return; + } + + grid.classList.add('port-status-grid'); + + Array.prototype.forEach.call(grid.querySelectorAll('.ifacebox'), function (card) { + card.classList.add('port-status-card'); + }); + }); + + Array.prototype.forEach.call(view.querySelectorAll('img[src*="/luci-static/resources/icons/port_"]'), function (img) { + var card = img.closest('.ifacebox'); + var slot = card ? card.closest('.port-status-slot') : null; + var parent = card ? (slot ? slot.parentElement : card.parentElement) : null; + var iconType = (img.getAttribute('src') || '').indexOf('port_down.png') !== -1 ? 'down' : 'up'; + var badge = img.closest('.ifacebadge'); + + if (badge) { + img.setAttribute('src', mediaPath + '/images/port_' + iconType + '.png'); + } + + if (!card || !parent) { + return; + } + + img.setAttribute('src', mediaPath + '/images/port_' + iconType + '.png'); + img.style.setProperty('display', 'block', 'important'); + img.style.setProperty('width', '24px', 'important'); + img.style.setProperty('height', '24px', 'important'); + img.style.setProperty('padding', '0', 'important'); + img.style.setProperty('margin', '0.05rem auto', 'important'); + + if (img.nextElementSibling && img.nextElementSibling.tagName === 'BR') { + img.nextElementSibling.style.setProperty('display', 'none', 'important'); + } + + if (!portGroups.has(parent)) { + portGroups.set(parent, []); + } + + if (portGroups.get(parent).indexOf(card) === -1) { + portGroups.get(parent).push(card); + } + }); + + portGroups.forEach(function (cards, parent) { + var width = window.innerWidth || document.documentElement.clientWidth || 1280; + var columns; + + cards.forEach(function (card) { + var body = card.querySelector('.ifacebox-body'); + var slot = card.parentElement; + + if (!slot || !slot.classList.contains('port-status-slot')) { + slot = document.createElement('div'); + slot.className = 'port-status-slot'; + parent.insertBefore(slot, card); + slot.appendChild(card); + } + + card.classList.add('port-status-card'); + + slot.style.setProperty('display', 'flex', 'important'); + slot.style.setProperty('justify-content', 'center', 'important'); + slot.style.setProperty('align-items', 'stretch', 'important'); + slot.style.setProperty('min-width', '0', 'important'); + slot.style.setProperty('padding', '0.15rem 0.65rem 0.75rem', 'important'); + + card.style.setProperty('margin', '0', 'important'); + card.style.setProperty('min-width', '76px', 'important'); + card.style.setProperty('max-width', '108px', 'important'); + card.style.setProperty('width', 'fit-content', 'important'); + card.style.setProperty('text-align', 'center', 'important'); + + if (body) { + body.style.setProperty('text-align', 'center', 'important'); + body.style.setProperty('align-items', 'center', 'important'); + body.style.setProperty('justify-content', 'center', 'important'); + } + }); + + if (width <= 480) { + columns = 1; + } else if (width <= 768) { + columns = Math.min(cards.length, 2); + } else if (cards.length <= 4) { + columns = cards.length; + } else if (cards.length <= 6) { + columns = 3; + } else { + columns = 4; + } + + parent.style.setProperty('display', 'grid', 'important'); + parent.style.setProperty('grid-template-columns', 'repeat(' + columns + ', minmax(0, 1fr))', 'important'); + parent.style.setProperty('align-items', 'stretch', 'important'); + parent.style.setProperty('justify-items', 'stretch', 'important'); + parent.style.setProperty('width', '100%', 'important'); + parent.style.setProperty('max-width', '100%', 'important'); + parent.style.setProperty('padding', '0.2rem 0.6rem 0.35rem', 'important'); + parent.style.setProperty('margin', '0 auto 1rem', 'important'); + parent.style.setProperty('row-gap', '0.2rem', 'important'); + parent.style.setProperty('column-gap', '0', 'important'); + }); + } + + function initOverviewPortStatus() { + var view = document.getElementById('view'); + + if (!view || document.body.getAttribute('data-page') !== 'admin-status-overview') { + return; + } + + syncOverviewPortStatus(); + + new MutationObserver(syncOverviewPortStatus).observe(view, { + childList: true, + subtree: true + }); + } + + function syncNetworkInterfaceIcons() { + var view = document.getElementById('view'); + var mediaPath = (L.env && L.env.media) ? L.env.media : '/luci-static/design'; + var iconMap = { + 'alias': 'ethernet', + 'alias_disabled': 'ethernet_disabled', + 'bridge': 'bridge', + 'bridge_disabled': 'bridge_disabled', + 'ethernet': 'ethernet', + 'ethernet_disabled': 'ethernet_disabled', + 'switch': 'switch', + 'switch_disabled': 'switch_disabled', + 'tunnel': 'tunnel', + 'tunnel_disabled': 'tunnel_disabled', + 'vlan': 'vlan', + 'vlan_disabled': 'vlan_disabled', + 'wifi': 'wifi', + 'wifi_disabled': 'wifi_disabled' + }; + + if (!view || document.body.getAttribute('data-page') !== 'admin-network-network') { + return; + } + + Array.prototype.forEach.call(view.querySelectorAll('img[src*="/luci-static/resources/icons/"]'), function (img) { + var src = img.getAttribute('src') || ''; + var match = src.match(/\/icons\/([^/?#]+)\.(?:png|svg)(?:[?#].*)?$/); + var key = match ? match[1] : ''; + var mapped = iconMap[key]; + + if (!mapped) { + return; + } + + img.setAttribute('src', mediaPath + '/images/' + mapped + '.png'); + img.classList.add('theme-network-icon'); + img.style.setProperty('background-image', 'none', 'important'); + img.style.setProperty('object-fit', 'contain', 'important'); + }); + } + + function initNetworkInterfaceIcons() { + var view = document.getElementById('view'); + + if (!view || document.body.getAttribute('data-page') !== 'admin-network-network') { + return; + } + + syncNetworkInterfaceIcons(); + + new MutationObserver(syncNetworkInterfaceIcons).observe(view, { + childList: true, + subtree: true + }); + } + + function syncWirelessIcons() { + var mediaPath = (L.env && L.env.media) ? L.env.media : '/luci-static/design'; + var iconMap = { + 'wifi': 'wifi_big', + 'wifi_disabled': 'wifi_big_disabled', + 'signal-none': 'signal-none', + 'signal-0': 'signal-0', + 'signal-0-25': 'signal-0-25', + 'signal-25-50': 'signal-25-50', + 'signal-50-75': 'signal-50-75', + 'signal-75-100': 'signal-75-100', + 'signal-000-000': 'signal-0', + 'signal-000-025': 'signal-0-25', + 'signal-025-050': 'signal-25-50', + 'signal-050-075': 'signal-50-75', + 'signal-075-100': 'signal-75-100' + }; + + if (document.body.getAttribute('data-page') !== 'admin-network-wireless') { + return; + } + + Array.prototype.forEach.call(document.querySelectorAll('img[src*="/luci-static/resources/icons/"]'), function (img) { + var src = img.getAttribute('src') || ''; + var match = src.match(/\/icons\/([^/?#]+)\.(?:png|svg)(?:[?#].*)?$/); + var key = match ? match[1] : ''; + var inAssocList = !!img.closest('#wifi_assoclist_table'); + var mapped = iconMap[key]; + var badge = img.closest('.ifacebadge') || img.closest('.center'); + var isDeviceIcon = (key === 'wifi' || key === 'wifi_disabled'); + var size = isDeviceIcon ? '64px' : '18px'; + + if (!mapped || !badge) { + return; + } + + if (inAssocList && isDeviceIcon) { + mapped = key; + size = '16px'; + } + + img.setAttribute('src', mediaPath + '/images/' + mapped + '.png'); + img.classList.add('theme-wireless-icon'); + img.style.setProperty('background-image', 'none', 'important'); + img.style.setProperty('visibility', 'visible', 'important'); + img.style.setProperty('opacity', '1', 'important'); + img.style.setProperty('display', 'inline-block', 'important'); + img.style.setProperty('width', size, 'important'); + img.style.setProperty('height', size, 'important'); + img.style.setProperty('min-width', size, 'important'); + img.style.setProperty('padding', '0', 'important'); + img.style.setProperty('margin', (inAssocList && isDeviceIcon) ? '0 .2rem 0 0' : (isDeviceIcon ? '0 .3rem 0 0' : '0 .25rem 0 0'), 'important'); + img.style.setProperty('vertical-align', 'middle', 'important'); + img.style.setProperty('object-fit', 'contain', 'important'); + }); + } + + function initWirelessIcons() { + if (document.body.getAttribute('data-page') !== 'admin-network-wireless') { + return; + } + + syncWirelessIcons(); + + new MutationObserver(syncWirelessIcons).observe(document.body, { + childList: true, + subtree: true + }); + } + + function init() { + applySafeAreaFix(); + initIndicators(); + initOverviewPortStatus(); + initNetworkInterfaceIcons(); + initWirelessIcons(); + setHeaderShadow(); + $(window).on('resize', setHeaderShadow); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } +})(jQuery); diff --git a/luci-theme-design/htdocs/luci-static/resources/menu-design.js b/luci-theme-design/htdocs/luci-static/resources/menu-design.js index 235abf0f1..9df02b06d 100644 --- a/luci-theme-design/htdocs/luci-static/resources/menu-design.js +++ b/luci-theme-design/htdocs/luci-static/resources/menu-design.js @@ -1,17 +1,73 @@ 'use strict'; 'require baseclass'; +'require request'; 'require ui'; -// 兼容新旧版本 LuCI -var LuciCompat = { - isNewVersion: function() { - return L.env.luci_version && parseFloat(L.env.luci_version.split('.')[0]) >= 23; - } -}; - return baseclass.extend({ __init__: function() { - ui.menu.load().then(L.bind(this.render, this)); + var params = new URLSearchParams(window.location.search); + + if (params.get('menu') === 'flush') { + ui.menu.flushCache(); + params.delete('menu'); + window.history.replaceState(null, '', window.location.pathname + + (params.toString() ? '?' + params.toString() : '') + + window.location.hash); + } + + this.syncConditionalMenuCache() + .then(L.bind(function(reloading) { + if (reloading) + return; + + return ui.menu.load().then(L.bind(this.render, this)); + }, this)); + }, + + reloadPageWithMenuFlush: function() { + var url = new URL(window.location.href); + + url.searchParams.set('menu', 'flush'); + window.location.replace(url.toString()); + }, + + getConditionalMenuStamp: function() { + var googleFuMode = !!document.querySelector('.navbar a[href*="/admin/services/openclash"]'); + + return Promise.resolve(JSON.stringify({ + google_fu_mode: googleFuMode + })); + }, + + flushBackendMenuCache: function() { + ui.menu.flushCache(); + + return L.resolveDefault(request.get(L.url('admin/services/theme_menu_flush')), null).then(function() { + ui.menu.flushCache(); + }).then(function() { + ui.menu.flushCache(); + }); + }, + + syncConditionalMenuCache: function() { + var storageKey = 'design.menu.condition-stamp'; + + if (!document.body.classList.contains('logged-in')) + return Promise.resolve(); + + return this.getConditionalMenuStamp().then(L.bind(function(stamp) { + var previousStamp = window.localStorage.getItem(storageKey); + + if (previousStamp === stamp) + return false; + + window.localStorage.setItem(storageKey, stamp); + + return this.flushBackendMenuCache().then(L.bind(function() { + this.reloadPageWithMenuFlush(); + return true; + }, this)); + }, this)); }, render: function(tree) { @@ -243,4 +299,3 @@ return baseclass.extend({ } }, }); - diff --git a/luci-theme-design/luasrc/view/themes/design/footer.htm b/luci-theme-design/luasrc/view/themes/design/footer.htm deleted file mode 100644 index 7f645b7d9..000000000 --- a/luci-theme-design/luasrc/view/themes/design/footer.htm +++ /dev/null @@ -1,32 +0,0 @@ -<%# - Material is a clean HTML5 theme for LuCI. It is based on luci-theme-bootstrap and MUI - - luci-theme-material - Copyright 2015 Lutty Yang - - Have a bug? Please create an issue here on GitHub! - https://github.com/LuttyYang/luci-theme-material/issues - - luci-theme-bootstrap: - Copyright 2008 Steven Barth - Copyright 2008 Jo-Philipp Wich - Copyright 2012 David Menting - - MUI: - https://github.com/muicss/mui - - Licensed to the public under the Apache License 2.0 --%> - -<% local ver = require "luci.version" %> - - - - - - - - - diff --git a/luci-theme-design/luasrc/view/themes/design/header.htm b/luci-theme-design/luasrc/view/themes/design/header.htm deleted file mode 100644 index ba31088a7..000000000 --- a/luci-theme-design/luasrc/view/themes/design/header.htm +++ /dev/null @@ -1,130 +0,0 @@ -<%# - Material is a clean HTML5 theme for LuCI. It is based on luci-theme-bootstrap and MUI - - luci-theme-material - Copyright 2015-2017 Lutty Yang - - Have a bug? Please create an issue here on GitHub! - https://github.com/LuttyYang/luci-theme-material/issues - - luci-theme-bootstrap: - Copyright 2008 Steven Barth - Copyright 2008-2016 Jo-Philipp Wich - Copyright 2012 David Menting - - MUI: - https://github.com/muicss/mui - - Licensed to the public under the Apache License 2.0 --%> - -<% - local sys = require "luci.sys" - local util = require "luci.util" - local http = require "luci.http" - local disp = require "luci.dispatcher" - local ver = require "luci.version" - - -- 兼容新旧版本的 ubus 调用 - local boardinfo = {} - if util.ubus then - boardinfo = util.ubus("system", "board") or {} - else - -- OpenWrt 23+ 可能使用不同的方式 - local ubus = require "ubus" - local conn = ubus.connect() - if conn then - boardinfo = conn:call("system", "board", {}) or {} - conn:close() - end - end - - local node = disp.context.dispatched - local path = table.concat(disp.context.path, "-") - - http.prepare_content("text/html; charset=UTF-8") --%> - - - - - - -<%=striptags( (boardinfo.hostname or "OpenWrt") .. ( (node and node.title) and ' - ' .. translate(node.title) or '')) %> - LuCI - - - - - - - - - - - LuCI"> - - LuCI"> - - - - - - - - - - - - - <% if node and node.css then %> - - <% end -%> - - - - - <% if css then %> - - <% end -%> - - node-<%= path %><% else %>node-main-login<% end %>" data-page="<%= pcdata(path) %>"> -
- -
- - - -
-
<%:Collecting data...%>
- -
- -
-
-
- <%- if luci.sys.process.info("uid") == 0 and luci.sys.user.getuser("root") and not luci.sys.user.getpasswd("root") and path ~= "admin-system-admin-password" then -%> - <%- end -%> - - - - - \ No newline at end of file diff --git a/luci-theme-design/preview/IMG_0328.PNG b/luci-theme-design/preview/IMG_0328.PNG deleted file mode 100644 index 598e7b7ae..000000000 Binary files a/luci-theme-design/preview/IMG_0328.PNG and /dev/null differ diff --git a/luci-theme-design/preview/IMG_0329.PNG b/luci-theme-design/preview/IMG_0329.PNG deleted file mode 100644 index 86bb01ed1..000000000 Binary files a/luci-theme-design/preview/IMG_0329.PNG and /dev/null differ diff --git a/luci-theme-design/preview/home.png b/luci-theme-design/preview/home.png deleted file mode 100644 index d314957b4..000000000 Binary files a/luci-theme-design/preview/home.png and /dev/null differ diff --git a/luci-theme-design/preview/home1.png b/luci-theme-design/preview/home1.png deleted file mode 100644 index 349cbf250..000000000 Binary files a/luci-theme-design/preview/home1.png and /dev/null differ diff --git a/luci-theme-design/preview/iface.png b/luci-theme-design/preview/iface.png deleted file mode 100644 index 1f506866e..000000000 Binary files a/luci-theme-design/preview/iface.png and /dev/null differ diff --git a/luci-theme-design/preview/light.png b/luci-theme-design/preview/light.png deleted file mode 100644 index 3f10c2589..000000000 Binary files a/luci-theme-design/preview/light.png and /dev/null differ diff --git a/luci-theme-design/preview/login.png b/luci-theme-design/preview/login.png deleted file mode 100644 index aae82a761..000000000 Binary files a/luci-theme-design/preview/login.png and /dev/null differ diff --git a/luci-theme-design/preview/webapp_home.PNG b/luci-theme-design/preview/webapp_home.PNG deleted file mode 100644 index 90c0a4433..000000000 Binary files a/luci-theme-design/preview/webapp_home.PNG and /dev/null differ diff --git a/luci-theme-design/preview/webapp_vssr.PNG b/luci-theme-design/preview/webapp_vssr.PNG deleted file mode 100644 index 4214d2da3..000000000 Binary files a/luci-theme-design/preview/webapp_vssr.PNG and /dev/null differ diff --git a/luci-theme-design/preview/wifi.png b/luci-theme-design/preview/wifi.png deleted file mode 100644 index dbd969145..000000000 Binary files a/luci-theme-design/preview/wifi.png and /dev/null differ diff --git a/luci-theme-design/root/usr/share/luci/menu.d/luci-theme-design.json b/luci-theme-design/root/usr/share/luci/menu.d/luci-theme-design.json new file mode 100644 index 000000000..ebc2ee48b --- /dev/null +++ b/luci-theme-design/root/usr/share/luci/menu.d/luci-theme-design.json @@ -0,0 +1,10 @@ +{ + "admin/services/theme_menu_flush": { + "leaf": true, + "action": { + "type": "function", + "module": "luci.controller.design_theme", + "function": "menu_flush" + } + } +} diff --git a/luci-theme-design/ucode/controller/design_theme.uc b/luci-theme-design/ucode/controller/design_theme.uc new file mode 100644 index 000000000..14e895e63 --- /dev/null +++ b/luci-theme-design/ucode/controller/design_theme.uc @@ -0,0 +1,15 @@ +'use strict'; + +import { glob, unlink } from 'fs'; + +return { + menu_flush: function() { + for (let path in glob('/tmp/luci-indexcache', '/tmp/luci-indexcache.*')) + unlink(path); + + system('rm -rf /tmp/luci-modulecache 2>/dev/null'); + + http.prepare_content('application/json'); + http.write_json({ flushed: true }); + } +}; diff --git a/luci-theme-design/ucode/template/themes/design/footer.ut b/luci-theme-design/ucode/template/themes/design/footer.ut new file mode 100644 index 000000000..9127367f9 --- /dev/null +++ b/luci-theme-design/ucode/template/themes/design/footer.ut @@ -0,0 +1,18 @@ +{# + Design theme footer template for LuCI ucode runtime. +-#} + +
+ +
+
+
+ + + + + diff --git a/luci-theme-design/ucode/template/themes/design/header.ut b/luci-theme-design/ucode/template/themes/design/header.ut new file mode 100644 index 000000000..23cf3f63e --- /dev/null +++ b/luci-theme-design/ucode/template/themes/design/header.ut @@ -0,0 +1,124 @@ +{# + Design is a clean HTML5 theme for LuCI. It is based on luci-theme-material + and luci-theme-neobird. + + Copyright 2015 Lutty Yang + Copyright 2021 2smile + + Licensed to the public under the Apache License 2.0. +-#} + +{% + import { access } from 'fs'; + import { getuid, getspnam } from 'luci.core'; + + const boardinfo = ubus.call('system', 'board'); + const hostname = striptags(boardinfo?.hostname ?? 'OpenWrt'); + const dispatch_path = ctx.path ?? []; + const request_path = ctx.request_path ?? dispatch_path; + const current_path = length(dispatch_path) ? join('-', dispatch_path) : 'main-login'; + const current_page = length(request_path) ? join('-', request_path) : current_path; + const show_proxy_icon = access('/etc/config/google_fu_mode'); + const theme_rev = `${version?.luciversion ?? 'dev'}-designfix-20260418i`; + + http.prepare_content('text/html; charset=UTF-8'); +-%} + + + + + + + +{{ striptags(`${hostname}${dispatched?.title ? ` - ${_(dispatched.title)}` : ''}`) }} - LuCI + + + + + + + + + + + + + + + + + + + + + + +{% if (node?.css): %} + +{% endif %} + + +{% if (css): %} + +{% endif %} + + +
+
+
+ + {{ hostname }} +
+
+
+
+ + + +
+
{{ _('Collecting data...') }}
+ +
+ +
+
+
+ {% if (getuid() == 0 && getspnam('root')?.pwdp === '' && ctx.authsession && current_page != 'admin-system-admin' && current_page != 'admin-system-admin-password'): %} +
+

{{ _('No password set!') }}

+

{{ _('There is no password set on this router. Please configure a root password to protect the web interface.') }}

+ {% if (dispatcher.lookup('admin/system/admin')): %} + + {% endif %} +
+ {% endif %} + + {% if (boardinfo?.rootfs_type == 'initramfs'): %} +
+

{{ _('System running in recovery (initramfs) mode.') }}

+

{{ _('No changes to settings will be stored and are lost after rebooting. This mode should only be used to install a firmware upgrade') }}

+ {% if (dispatcher.lookup('admin/system/flash')): %} + + {% endif %} +
+ {% endif %} + + + + +