🏅 Sync 2026-04-19 00:24:08
No files matched your search
@@ -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
|
||||
@@ -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`
|
||||
@@ -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.
|
||||
@@ -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:=
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
<img src="./preview/light.png"/>
|
||||
|
||||
# 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端普通字体为苹果浏览器字体,只有移动端是正常显示的)
|
||||
|
||||
<details> <summary>iOS</summary>
|
||||
<img src="./preview/webapp_home.PNG"/>
|
||||
<img src="./preview/webapp_vssr.PNG"/>
|
||||
</details>
|
||||
|
||||
<details> <summary>iPadOS</summary>
|
||||
<img src="./preview/IMG_0328.PNG"/>
|
||||
<img src="./preview/IMG_0329.PNG"/>
|
||||
</details>
|
||||
|
||||
<img src="./preview/login.png"/>
|
||||
<img src="./preview/home.png"/>
|
||||
<img src="./preview/home1.png"/>
|
||||
<img src="./preview/wifi.png"/>
|
||||
<img src="./preview/iface.png"/>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
@@ -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);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -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({
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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 <lutty@wcan.in>
|
||||
|
||||
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 <steven@midlink.org>
|
||||
Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
|
||||
Copyright 2012 David Menting <david@nut-bolt.nl>
|
||||
|
||||
MUI:
|
||||
https://github.com/muicss/mui
|
||||
|
||||
Licensed to the public under the Apache License 2.0
|
||||
-%>
|
||||
|
||||
<% local ver = require "luci.version" %>
|
||||
</div>
|
||||
<footer class="mobile-hide">
|
||||
<a href="https://github.com/openwrt/luci">Powered by <%= ver.luciname %> (<%= ver.luciversion %>)</a> / <%= ver.distversion %>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">L.require('menu-design')</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -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 <lutty@wcan.in>
|
||||
|
||||
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 <steven@midlink.org>
|
||||
Copyright 2008-2016 Jo-Philipp Wich <jow@openwrt.org>
|
||||
Copyright 2012 David Menting <david@nut-bolt.nl>
|
||||
|
||||
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")
|
||||
-%>
|
||||
<!DOCTYPE html>
|
||||
<html lang="<%=luci.i18n.context.lang%>">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,viewport-fit=cover" />
|
||||
<link rel="manifest" href="<%=media%>/manifest.json">
|
||||
<title><%=striptags( (boardinfo.hostname or "OpenWrt") .. ( (node and node.title) and ' - ' .. translate(node.title) or '')) %> - LuCI</title>
|
||||
<meta name="format-detection" content="telephone=no, email=no"/>
|
||||
<meta name="x5-fullscreen" content="true">
|
||||
<meta name="full-screen" content="yes">
|
||||
<meta name="x5-page-mode" content="app">
|
||||
<meta name="browsermode" content="application">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
||||
<meta name="theme-color" media="(prefers-color-scheme: light)" content="white">
|
||||
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="black">
|
||||
<meta name="application-name" content="<%=striptags( (boardinfo.hostname or "OpenWrt") ) %> - LuCI">
|
||||
<meta name="apple-mobile-web-app-title" content="<%=striptags( (boardinfo.hostname or "OpenWrt") ) %> - LuCI">
|
||||
<meta name="msapplication-TileImage" content="<%=media%>/images/icon.png"/>
|
||||
<link rel="icon" href="<%=media%>/images/icon.png" sizes="192x192">
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="<%=media%>/images/icon.png">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-touch-fullscreen" content="yes" />
|
||||
<meta name="msapplication-TileColor" content="#09c">
|
||||
<link rel="stylesheet" href="<%=media%>/css/style.css?v=<%= ver.luciversion %>">
|
||||
<link rel="stylesheet" href="<%=media%>/css/compat-openwrt23.css?v=<%= ver.luciversion %>">
|
||||
<script src="<%=media%>/js/jquery.min.js?v=3.5.1"></script>
|
||||
<script src="<%=media%>/js/compat.js?v=<%= ver.luciversion %>"></script>
|
||||
<link rel="shortcut icon" href="<%=media%>/favicon.ico">
|
||||
<% if node and node.css then %>
|
||||
<link rel="stylesheet" href="<%=resource%>/<%=node.css%>">
|
||||
<% end -%>
|
||||
|
||||
<script src="<%=url('admin/translations', luci.i18n.context.lang)%><%# ?v=PKG_VERSION %>"></script>
|
||||
<script src="<%=resource%>/cbi.js<%= ver.luciversion and ('?v=' .. ver.luciversion) or '' %>"></script>
|
||||
<script src="<%=resource%>/luci.js<%= ver.luciversion and ('?v=' .. ver.luciversion) or '' %>"></script>
|
||||
<% if css then %>
|
||||
<style title="text/css">
|
||||
<%= css %>
|
||||
</style>
|
||||
<% end -%>
|
||||
</head>
|
||||
<body class="lang_<%=luci.i18n.context.lang%> <% if luci.dispatcher.context.authsession then %>logged-in<% end %> <% if not (path == "") then %>node-<%= path %><% else %>node-main-login<% end %>" data-page="<%= pcdata(path) %>">
|
||||
<header>
|
||||
<div class="fill">
|
||||
<div class="container">
|
||||
<span class="showSide"></span>
|
||||
<a class="brand" href="#"><%=striptags(boardinfo.hostname or "OpenWrt")%></a>
|
||||
<div class="status" id="indicators"></div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="navbar">
|
||||
<a href="/cgi-bin/luci/admin/status/overview"><img src="<%=media%>/images/home.png" /></a>
|
||||
<a href="/cgi-bin/luci/admin/services/openclash"><img src="<%=media%>/images/openclash.png" /></a>
|
||||
<a href="/cgi-bin/luci/admin/network/network"><img src="<%=media%>/images/link.png" /></a>
|
||||
<a href="/cgi-bin/luci/admin/status/realtime"><img src="<%=media%>/images/rank.png" /></a>
|
||||
<a href="/cgi-bin/luci/admin/system/admin"><img src="<%=media%>/images/user.png" /></a>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div style="" class="loading"><span><div class="loading-img"></div><%:Collecting data...%></span></div>
|
||||
<div class="main-left" id="mainmenu" style="display:none"></div>
|
||||
<div class="main-right">
|
||||
<div class="modemenu-buttons" style="display:none">
|
||||
<ul id="modemenu"></ul>
|
||||
</div>
|
||||
<div class="darkMask"></div>
|
||||
<div id="maincontent">
|
||||
<div class="container">
|
||||
<%- 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 -%>
|
||||
|
||||
<noscript>
|
||||
<div class="alert-message warning">
|
||||
<h4><%:JavaScript required!%></h4>
|
||||
<p><%:You must enable JavaScript in your browser or LuCI will not work properly.%></p>
|
||||
</div>
|
||||
</noscript>
|
||||
|
||||
<div id="tabmenu" style="display:none"></div>
|
||||
<script src="<%=media%>/js/style.js?v=<%=ver.luciversion%>"></script>
|
||||
|
Before Width: | Height: | Size: 257 KiB |
|
Before Width: | Height: | Size: 373 KiB |
|
Before Width: | Height: | Size: 310 KiB |
|
Before Width: | Height: | Size: 293 KiB |
|
Before Width: | Height: | Size: 462 KiB |
|
Before Width: | Height: | Size: 326 KiB |
|
Before Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 415 KiB |
|
Before Width: | Height: | Size: 561 KiB |
|
Before Width: | Height: | Size: 352 KiB |
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"admin/services/theme_menu_flush": {
|
||||
"leaf": true,
|
||||
"action": {
|
||||
"type": "function",
|
||||
"module": "luci.controller.design_theme",
|
||||
"function": "menu_flush"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 });
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
{#
|
||||
Design theme footer template for LuCI ucode runtime.
|
||||
-#}
|
||||
|
||||
</div>
|
||||
<footer class="mobile-hide">
|
||||
<a href="https://github.com/openwrt/luci" target="_blank" rel="noreferrer">Powered by {{ version.luciname }} ({{ version.luciversion }})</a>
|
||||
/
|
||||
{{ version.distname }} {{ version.distversion }}{% if (version.distrevision): %} ({{ version.distrevision }}){% endif %}
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">L.require('menu-design')</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -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 <lutty@wcan.in>
|
||||
Copyright 2021 2smile <thinktip@gmail.com>
|
||||
|
||||
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');
|
||||
-%}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ dispatcher.lang }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, viewport-fit=cover">
|
||||
<link rel="manifest" href="{{ media }}/manifest.json">
|
||||
<title>{{ striptags(`${hostname}${dispatched?.title ? ` - ${_(dispatched.title)}` : ''}`) }} - LuCI</title>
|
||||
<meta name="format-detection" content="telephone=no, email=no">
|
||||
<meta name="x5-fullscreen" content="true">
|
||||
<meta name="full-screen" content="yes">
|
||||
<meta name="x5-page-mode" content="app">
|
||||
<meta name="browsermode" content="application">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<meta name="theme-color" media="(prefers-color-scheme: light)" content="white">
|
||||
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="black">
|
||||
<meta name="application-name" content="{{ hostname }} - LuCI">
|
||||
<meta name="apple-mobile-web-app-title" content="{{ hostname }} - LuCI">
|
||||
<meta name="msapplication-TileImage" content="{{ media }}/images/icon.png">
|
||||
<link rel="icon" href="{{ media }}/images/icon.png" sizes="192x192">
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="{{ media }}/images/icon.png">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-touch-fullscreen" content="yes">
|
||||
<meta name="msapplication-TileColor" content="#09c">
|
||||
<link rel="stylesheet" href="{{ media }}/css/style.css?v={{ theme_rev }}">
|
||||
<script src="{{ media }}/js/jquery.min.js?v=3.5.1"></script>
|
||||
<script src="{{ media }}/js/compat.js?v={{ theme_rev }}"></script>
|
||||
<link rel="shortcut icon" href="{{ media }}/favicon.ico">
|
||||
{% if (node?.css): %}
|
||||
<link rel="stylesheet" href="{{ resource }}/{{ node.css }}">
|
||||
{% endif %}
|
||||
<script src="{{ dispatcher.build_url('admin/translations', dispatcher.lang) }}"></script>
|
||||
<script src="{{ resource }}/cbi.js?v={{ theme_rev }}"></script>
|
||||
{% if (css): %}
|
||||
<style title="text/css">{{ css }}</style>
|
||||
{% endif %}
|
||||
</head>
|
||||
<body class="lang_{{ dispatcher.lang }} {{ ctx.authsession ? 'logged-in' : '' }} {{ length(dispatch_path) ? `node-${join('-', dispatch_path)}` : 'node-main-login' }}" data-page="{{ entityencode(current_page, true) }}">
|
||||
<header>
|
||||
<div class="fill">
|
||||
<div class="container">
|
||||
<span class="showSide"></span>
|
||||
<a class="brand" href="{{ ctx.authsession ? dispatcher.build_url('admin/status/overview') : '#' }}">{{ hostname }}</a>
|
||||
<div class="status" id="indicators"></div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="navbar">
|
||||
<a href="{{ dispatcher.build_url('admin/status/overview') }}"><img src="{{ media }}/images/home.png"></a>
|
||||
{% if (show_proxy_icon): %}
|
||||
<a href="{{ dispatcher.build_url('admin/services/openclash') }}"><img src="{{ media }}/images/openclash.png"></a>
|
||||
{% endif %}
|
||||
<a href="{{ dispatcher.build_url('admin/network/network') }}"><img src="{{ media }}/images/link.png"></a>
|
||||
<a href="{{ dispatcher.build_url('admin/status/realtime') }}"><img src="{{ media }}/images/rank.png"></a>
|
||||
<a href="{{ dispatcher.build_url('admin/system/admin') }}"><img src="{{ media }}/images/user.png"></a>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="loading"><span><div class="loading-img"></div>{{ _('Collecting data...') }}</span></div>
|
||||
<div class="main-left" id="mainmenu" style="display:none"></div>
|
||||
<div class="main-right">
|
||||
<div class="modemenu-buttons" style="display:none">
|
||||
<ul id="modemenu"></ul>
|
||||
</div>
|
||||
<div class="darkMask"></div>
|
||||
<div id="maincontent">
|
||||
<div class="container">
|
||||
{% if (getuid() == 0 && getspnam('root')?.pwdp === '' && ctx.authsession && current_page != 'admin-system-admin' && current_page != 'admin-system-admin-password'): %}
|
||||
<div class="alert-message warning">
|
||||
<h4>{{ _('No password set!') }}</h4>
|
||||
<p>{{ _('There is no password set on this router. Please configure a root password to protect the web interface.') }}</p>
|
||||
{% if (dispatcher.lookup('admin/system/admin')): %}
|
||||
<div class="right"><a class="btn" href="{{ dispatcher.build_url('admin/system/admin') }}">{{ _('Go to password configuration...') }}</a></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if (boardinfo?.rootfs_type == 'initramfs'): %}
|
||||
<div class="alert-message warning">
|
||||
<h4>{{ _('System running in recovery (initramfs) mode.') }}</h4>
|
||||
<p>{{ _('No changes to settings will be stored and are lost after rebooting. This mode should only be used to install a firmware upgrade') }}</p>
|
||||
{% if (dispatcher.lookup('admin/system/flash')): %}
|
||||
<div class="right"><a class="btn" href="{{ dispatcher.build_url('admin/system/flash') }}">{{ _('Go to firmware upgrade...') }}</a></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<noscript>
|
||||
<div class="alert-message warning">
|
||||
<h4>{{ _('JavaScript required!') }}</h4>
|
||||
<p>{{ _('You must enable JavaScript in your browser or LuCI will not work properly.') }}</p>
|
||||
</div>
|
||||
</noscript>
|
||||
|
||||
<div id="tabmenu" style="display:none"></div>
|
||||
<script src="{{ media }}/js/style.js?v={{ theme_rev }}"></script>
|
||||