🎈 Sync 2025-11-29 14:51:06

This commit is contained in:
actions-user
2025-11-29 14:51:06 +08:00
parent d460bcc564
commit 38909dff19
123 changed files with 99338 additions and 500 deletions

View File

@@ -5,45 +5,39 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=daed
PKG_VERSION:=2025.11.03
DAED_VERSION:=daed-c3588a9
WING_VERSION:=wing-6df3da2
CORE_VERSION:=core-7e67e31
WING_HASH_SHORT:=$(shell echo $(WING_VERSION) | cut -d- -f2)
CORE_HASH_SHORT:=$(shell echo $(CORE_VERSION) | cut -d- -f2)
PKG_VERSION:=1.0.0
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_PROTO:=git
PKG_SOURCE_VERSION:=c3588a904c932d1fc83ee51096761776003fc25c
PKG_SOURCE_URL:=https://github.com/daeuniverse/daed.git
PKG_MIRROR_HASH:=skip
PKG_SOURCE_VERSION:=v$(PKG_VERSION)
PKG_MIRROR_HASH:=d360c45a77617281ad86bf46d8d5bc01dba0e81c64d41d4c52a76f6c1c44b247
PKG_LICENSE:=AGPL-3.0-only MIT
PKG_LICENSE_FILES:=LICENSE wing/LICENSE
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
PKG_BUILD_DIR=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)/wing
PKG_BUILD_DEPENDS:=golang/host bpf-headers node/host
PKG_BUILD_DEPENDS:=golang/host bpf-headers
PKG_BUILD_PARALLEL:=1
PKG_BUILD_FLAGS:=no-mips16
ifeq ($(ARCH),arm)
PATCH_DIR:=patches_arm
endif
GO_PKG:=github.com/daeuniverse/dae-wing
GO_PKG_LDFLAGS:= \
-s -w -X '$(GO_PKG)/db.AppDescription=$(PKG_NAME) is a integration solution of dae, API and UI.'
-X '$(GO_PKG)/db.AppDescription=$(PKG_NAME) is a integration solution of dae, API and UI.'
GO_PKG_LDFLAGS_X= \
$(GO_PKG)/db.AppName=$(PKG_NAME) \
$(GO_PKG)/db.AppVersion=$(DAED_VERSION)_$(WING_VERSION)_$(CORE_VERSION)
GO_PKG_TAGS:=embedallowed
GO_PKG_TARGET_VARS:=$(filter-out CGO_ENABLED=%,$(GO_PKG_TARGET_VARS)) \
CGO_ENABLED=1 \
GOEXPERIMENT=greenteagc
$(GO_PKG)/db.AppVersion=$(PKG_VERSION)
GO_PKG_TAGS:=embedallowed,trace
include $(INCLUDE_DIR)/package.mk
include $(INCLUDE_DIR)/bpf.mk
include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk
GO_PKG_DEFAULT_LDFLAGS:=-w -s -extldflags "-static"
TAR_CMD=$(HOST_TAR) -C $(BUILD_DIR)/ $(TAR_OPTIONS)
define Package/daed/Default
@@ -72,43 +66,26 @@ define Package/daed/conffiles
/etc/config/daed
endef
WEB_FILE:=$(PKG_NAME)-web-$(PKG_VERSION).zip
define Download/daed-web
URL:=https://github.com/daeuniverse/daed/releases/download/v$(PKG_VERSION)
URL_FILE:=web.zip
FILE:=$(WEB_FILE)
HASH:=293acce7c5013e180698bc5af899ead4e9feba9a30e39a00d59554a376a9f64d
endef
define Build/Prepare
$(call Build/Prepare/Default)
( \
$(TAR) --strip-components=1 -C $(PKG_BUILD_DIR)/../ -xzf $(DL_DIR)/$(PKG_NAME)-$(PKG_VERSION).tar.gz ; \
rm -rf $(PKG_BUILD_DIR) && git clone https://github.com/daeuniverse/dae-wing $(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)/wing && git -C $(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)/wing checkout $(WING_HASH_SHORT) ; \
rm -rf $(PKG_BUILD_DIR)/dae-core && git clone https://github.com/daeuniverse/dae $(PKG_BUILD_DIR)/dae-core && git -C $(PKG_BUILD_DIR)/dae-core checkout $(CORE_HASH_SHORT) ; \
pushd $(PKG_BUILD_DIR)/dae-core ; \
git submodule update --init ; \
wget -qO - https://github.com/daeuniverse/dae/pull/763.patch | patch -p1 ; \
wget -qO - https://github.com/daeuniverse/dae/pull/839.patch | patch -p1 ; \
wget -qO - https://github.com/daeuniverse/dae/commit/56fb759.patch | patch -p1 ; \
wget -qO - https://github.com/daeuniverse/dae/commit/af2e2c6.patch | patch -p1 ; \
wget -qO - https://github.com/daeuniverse/dae/commit/d96dc26.patch | patch -p1 ; \
wget -qO - https://github.com/daeuniverse/dae/commit/6ff101c.patch | patch -p1 ; \
wget -qO - https://github.com/daeuniverse/dae/commit/5740b94.patch | patch -p1 ; \
wget -qO - https://github.com/daeuniverse/dae/commit/cec1642.patch | patch -p1 ; \
sed -i 's/Warn("failed to write cached DNS resp")/Debug("failed to write cached DNS resp")/g' control/dns_control.go ; \
sed -i 's/Warn("failed to send DNS response")/Debug("failed to send DNS response")/g' control/dns_control.go ; \
sed -i 's/Warn("failed to send DNS reject response")/Debug("failed to send DNS reject response")/g' control/dns_control.go ; \
go mod tidy ; \
popd ; \
pushd $(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)/wing ; \
wget -qO - https://github.com/daeuniverse/dae-wing/commit/ca02961.patch | patch -p1 ; \
go mod tidy ; \
popd ; \
pushd $(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION) ; \
npm install -g pnpm ; \
pnpm install ; \
pnpm build ; \
popd ; \
mkdir -p $(PKG_BUILD_DIR)/webrender/web ; \
cp -rf $(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)/dist/* $(PKG_BUILD_DIR)/webrender/web ; \
mkdir -p $(PKG_BUILD_DIR)/webrender ; \
unzip -q -d $(PKG_BUILD_DIR)/webrender/ $(DL_DIR)/$(WEB_FILE) ; \
find $(PKG_BUILD_DIR)/webrender/web -type f -size +4k ! -name "*.gz" ! -name "*.woff" ! -name "*.woff2" -exec sh -c '\
gzip -9 -k "{}"; \
if [ "$$$$(stat -c %s {})" -lt "$$$$(stat -c %s {}.gz)" ]; then \
rm {}.gz; \
if [ "$$$$(stat -c %s "{}")" -lt "$$$$(stat -c %s "{}.gz")" ]; then \
rm "{}.gz"; \
else \
rm {}; \
rm "{}"; \
fi' \
";" ; \
)
@@ -117,7 +94,6 @@ endef
DAE_CFLAGS:= \
-O2 -Wall -Werror \
-DMAX_MATCH_SET_LEN=1024 \
-D__UNROLL_ROUTE_LOOP \
-I$(BPF_HEADERS_DIR)/tools/lib \
-I$(BPF_HEADERS_DIR)/arch/$(BPF_KARCH)/include/asm/mach-generic
@@ -128,17 +104,20 @@ endif
define Build/Compile
( \
pushd $(PKG_BUILD_DIR) ; \
$(MAKE) deps ; \
export \
$(GO_GENERAL_BUILD_CONFIG_VARS) \
$(GO_PKG_BUILD_CONFIG_VARS) \
$(GO_PKG_BUILD_VARS); \
$(GO_PKG_BUILD_VARS) ; \
go generate ./... ; \
cd dae-core ; \
export \
BPF_CLANG="$(CLANG)" \
BPF_STRIP_FLAG="-strip=$(LLVM_STRIP)" \
BPF_CFLAGS="$(DAE_CFLAGS)" \
BPF_TARGET="bpfel,bpfeb" \
BPF_TRACE_TARGET="$(GO_ARCH)" ; \
go generate control/control.go ; \
go generate trace/trace.go ; \
popd ; \
$(call GoPackage/Build/Compile) ; \
)
@@ -156,5 +135,6 @@ define Package/daed/install
$(INSTALL_BIN) $(CURDIR)/files/daed.init $(1)/etc/init.d/daed
endef
$(eval $(call Download,daed-web))
$(eval $(call GoBinPackage,daed))
$(eval $(call BuildPackage,daed))

1
daed/files/daed.init Executable file → Normal file
View File

@@ -21,6 +21,7 @@ start_service() {
config_get log_maxsize "config" "log_maxsize" "5"
procd_open_instance "$CONF"
procd_set_param env DAE_LOCATION_ASSET="/usr/share/v2ray"
procd_set_param command "$PROG" run
procd_append_param command --config "/etc/daed/"
procd_append_param command --listen "$listen_addr"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,24 @@
--- a/dae-core/trace/kern/trace.c
+++ b/dae-core/trace/kern/trace.c
@@ -228,7 +228,7 @@ KPROBE_SKB_AT(1)
KPROBE_SKB_AT(2)
KPROBE_SKB_AT(3)
KPROBE_SKB_AT(4)
-KPROBE_SKB_AT(5)
+//KPROBE_SKB_AT(5)
SEC("kprobe/skb_lifetime_termination")
int kprobe_skb_lifetime_termination(struct pt_regs *ctx)
--- a/dae-core/trace/trace.go
+++ b/dae-core/trace/trace.go
@@ -206,8 +206,8 @@ func attachBpfToTargets(objs *bpfObjects
kp, err = link.Kprobe(fn, objs.KprobeSkb3, nil)
case 4:
kp, err = link.Kprobe(fn, objs.KprobeSkb4, nil)
- case 5:
- kp, err = link.Kprobe(fn, objs.KprobeSkb5, nil)
+ //case 5:
+ // kp, err = link.Kprobe(fn, objs.KprobeSkb5, nil)
}
if err != nil {
logrus.Debugf("failed to attach kprobe to %s: %+v\n", fn, err)

View File

@@ -1,45 +0,0 @@
# SPDX-License-Identifier: GPL-3.0-only
#
# Copyright (C) 2021 ImmortalWrt.org
include $(TOPDIR)/rules.mk
PKG_NAME:=libcron
PKG_VERSION:=1.3.3
PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/PerMalmberg/libcron.git
PKG_SOURCE_VERSION:=v$(PKG_VERSION)
PKG_MIRROR_HASH:=74a81dc759bc6ff664601c9b0767120957e54571c0d8500177f7645e07287f24
PKG_LICENSE:=MIT
PKG_LICENSE_FILES:=LICENSE
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
CMAKE_INSTALL:=1
include $(INCLUDE_DIR)/package.mk
include $(INCLUDE_DIR)/cmake.mk
define Package/libcron
SECTION:=lib
CATEGORY:=Libraries
URL:=https://github.com/PerMalmberg/libcron
TITLE:=A C++ scheduling library using cron formatting
DEPENDS:=+libstdcpp
endef
define Package/libcron/description
Libcron offers an easy to use API to add callbacks with corresponding
cron-formatted strings.
endef
CMAKE_OPTIONS+= -DBUILD_SHARED_LIBS=ON
define Package/libcron/install
$(INSTALL_DIR) $(1)/usr/lib/
$(CP) $(PKG_INSTALL_DIR)/usr/lib/liblibcron.so $(1)/usr/lib/
endef
$(eval $(call BuildPackage,libcron))

View File

@@ -1,15 +1,13 @@
# SPDX-License-Identifier: Apache-2.0
#
# Copyright (C) 2023 ImmortalWrt.org
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-daed
PKG_VERSION:=1.3
PKG_RELEASE:=1
LUCI_TITLE:=LuCI Support for DAED
LUCI_DEPENDS:=+daed +curl +libcron +zoneinfo-asia +luci-compat
LUCI_TITLE:=LuCI app for dae dashboard
LUCI_DEPENDS:=+daed
LUCI_PKGARCH:=all
define Package/$(PKG_NAME)/conffiles
endef
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@@ -0,0 +1,93 @@
// SPDX-License-Identifier: Apache-2.0
'use strict';
'require form';
'require poll';
'require rpc';
'require uci';
'require view';
const callServiceList = rpc.declare({
object: 'service',
method: 'list',
params: ['name'],
expect: { '': {} }
});
function getServiceStatus() {
return L.resolveDefault(callServiceList('daed'), {}).then(function(res) {
let isRunning = false;
try {
isRunning = res['daed']['instances']['daed']['running'];
} catch (e) { }
return isRunning;
});
}
function renderStatus(isRunning, port) {
let spanTemp = '<span style="color:%s"><strong>%s %s</strong></span>';
let renderHTML;
if (isRunning) {
let button = String.format('&#160;<a class="btn cbi-button" href="http://%s:%s" target="_blank" rel="noreferrer noopener">%s</a>',
window.location.hostname, port, _('Open Web Interface'));
renderHTML = spanTemp.format('green', _('daed'), _('RUNNING')) + button;
} else {
renderHTML = spanTemp.format('red', _('daed'), _('NOT RUNNING'));
}
return renderHTML;
}
return view.extend({
load: function() {
return Promise.all([
uci.load('daed')
]);
},
render: function(data) {
let m, s, o;
let webport = (uci.get(data[0], 'config', 'address') || '0.0.0.0:2023').split(':').slice(-1)[0];
m = new form.Map('daed', _('daed'),
_('A modern dashboard for dae.'));
s = m.section(form.TypedSection);
s.anonymous = true;
s.render = function() {
poll.add(function() {
return L.resolveDefault(getServiceStatus()).then(function(res) {
let view = document.getElementById('service_status');
view.innerHTML = renderStatus(res, webport);
});
});
return E('div', { class: 'cbi-section', id: 'status_bar' }, [
E('p', { id: 'service_status' }, _('Collecting data…'))
]);
}
s = m.section(form.NamedSection, 'config', 'daed');
o = s.option(form.Flag, 'enabled', _('Enable'));
o.default = o.disabled;
o.rmempty = false;
o = s.option(form.Value, 'listen_addr', _('Listening address'));
o.datatype = 'ipaddrport(1)';
o.default = '0.0.0.0:2023';
o.rmempty = false;
o = s.option(form.Value, 'log_maxbackups', _('Max log backups'),
_('The maximum number of old log files to retain.'));
o.datatype = 'uinteger';
o.default = '1';
o = s.option(form.Value, 'log_maxsize', _('Max log size'),
_('The maximum size in megabytes of the log file before it gets rotated.'));
o.datatype = 'uinteger';
o.default = '5';
return m.render();
}
});

View File

@@ -0,0 +1,94 @@
// SPDX-License-Identifier: Apache-2.0
'use strict';
'require dom';
'require fs';
'require poll';
'require view';
return view.extend({
render() {
/* Thanks to luci-app-aria2 */
let css = ' \
#log_textarea { \
text-align: left; \
} \
#log_textarea pre { \
padding: .5rem; \
word-break: break-all; \
margin: 0; \
} \
.description { \
background-color: #33ccff; \
}';
let log_textarea = E('div', { 'id': 'log_textarea' },
E('img', {
'src': L.resource('icons/loading.svg'),
'alt': _('Loading...'),
'style': 'vertical-align:middle'
}, _('Collecting data…'))
);
poll.add(L.bind(function() {
return fs.read_direct('/var/log/daed/daed.log', 'text')
.then(function(content) {
let log = E('pre', { 'wrap': 'pre' }, [
content.trim() || _('Log is empty.')
]);
dom.content(log_textarea, log);
}).catch(function(e) {
let log;
if (e.toString().includes('NotFoundError'))
log = E('pre', { 'wrap': 'pre' }, [
_('Log file does not exist.')
]);
else
log = E('pre', { 'wrap': 'pre' }, [
_('Unknown error: %s').format(e)
]);
dom.content(log_textarea, log);
});
}));
const scrollDownButton = E('button', {
'id': 'scrollDownButton',
'class': 'cbi-button cbi-button-neutral',
}, _('Scroll to tail', 'scroll to bottom (the tail) of the log file')
);
scrollDownButton.addEventListener('click', () => {
scrollUpButton.focus();
});
const scrollUpButton = E('button', {
'id' : 'scrollUpButton',
'class': 'cbi-button cbi-button-neutral',
}, _('Scroll to head', 'scroll to top (the head) of the log file')
);
scrollUpButton.addEventListener('click', () => {
scrollDownButton.focus();
});
return E([
E('style', [ css ]),
E('h2', {}, [ _('Log') ]),
E('div', {'class': 'cbi-map'}, [
E('div', {'style': 'padding-bottom: 20px'}, [scrollDownButton]),
E('div', {'class': 'cbi-section'}, [
log_textarea,
E('div', {'style': 'text-align:right'},
E('small', {}, _('Refresh every %s seconds.').format(L.env.pollinterval))
)
]),
E('div', {'style': 'padding-bottom: 20px'}, [scrollUpButton])
])
]);
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});

View File

@@ -1,34 +0,0 @@
local sys = require "luci.sys"
local http = require "luci.http"
module("luci.controller.daed", package.seeall)
function index()
if not nixio.fs.access("/etc/config/daed") then
return
end
entry({"admin", "services", "daed"}, alias("admin", "services", "daed", "setting"),_("DAED"), 58).dependent = true
entry({"admin", "services", "daed", "setting"}, cbi("daed/basic"), _("Base Setting"), 1).leaf=true
entry({"admin", "services", "daed", "daed"}, template("daed/daed"), _("Dashboard"), 2).leaf = true
entry({"admin", "services", "daed", "log"}, cbi("daed/log"), _("Logs"), 3).leaf = true
entry({"admin", "services", "daed_status"}, call("act_status"))
entry({"admin", "services", "daed", "get_log"}, call("get_log")).leaf = true
entry({"admin", "services", "daed", "clear_log"}, call("clear_log")).leaf = true
end
function act_status()
local sys = require "luci.sys"
local e = { }
e.running = sys.call("pidof daed >/dev/null") == 0
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
function get_log()
http.write(sys.exec("cat /var/log/daed/daed.log"))
end
function clear_log()
sys.call("true > /var/log/daed/daed.log")
end

View File

@@ -1,67 +0,0 @@
local m, s ,o
m = Map("daed")
m.title = translate("DAED")
m.description = translate("DAE is a Linux high-performance transparent proxy solution based on eBPF, And DAED is a modern dashboard for dae.")
m:section(SimpleSection).template = "daed/daed_status"
s = m:section(TypedSection, "daed", translate("Global Settings"))
s.addremove = false
s.anonymous = true
o = s:option(Flag,"enabled",translate("Enable"))
o.default = 0
enable = s:option(Flag, "subscribe_auto_update", translate("Enable Auto Subscribe Update"))
enable.rmempty = false
o = s:option(Value, "daed_username", translate("Username"))
o.default = Username
o.password = true
o:depends('subscribe_auto_update', '1')
o = s:option(Value, "daed_password", translate("Password"))
o.default = Password
o.password = true
o:depends('subscribe_auto_update', '1')
o = s:option(ListValue, "subscribe_update_week_time", translate("Update Cycle"))
o:value("*", translate("Every Day"))
o:value("1", translate("Every Monday"))
o:value("2", translate("Every Tuesday"))
o:value("3", translate("Every Wednesday"))
o:value("4", translate("Every Thursday"))
o:value("5", translate("Every Friday"))
o:value("6", translate("Every Saturday"))
o:value("7", translate("Every Sunday"))
o.default = "*"
o:depends('subscribe_auto_update', '1')
update_time = s:option(ListValue, "subscribe_update_day_time", translate("Update Time (Every Day)"))
for t = 0, 23 do
update_time:value(t, t..":00")
end
update_time.default = 0
update_time:depends('subscribe_auto_update', '1')
o = s:option(Value, "log_maxbackups", translate("Logfile retention count"))
o.default = 1
o = s:option(Value, "log_maxsize", translate("Logfile Max Size (MB)"))
o.default = 5
o = s:option(Value, "listen_addr",translate("Set the DAED listen address"))
o.default = '0.0.0.0:2023'
o = s:option(Value, "dashboard_port", translate("Dashboard Access Port"))
o.placeholder = translate("Leave empty to use listen port")
o.datatype = "range(1,65535)"
o.description = translate("For reverse proxy scenarios, leave empty to use the port from listen address")
m.apply_on_parse = true
m.on_after_apply = function(self,map)
luci.sys.exec("/etc/init.d/daed restart")
end
return m

View File

@@ -1,5 +0,0 @@
m = Map("daed")
m:append(Template("daed/daed_log"))
return m

File diff suppressed because one or more lines are too long

View File

@@ -1,33 +0,0 @@
<script type="text/javascript">
//<![CDATA[
function clear_log(btn) {
XHR.get('<%=url([[admin]], [[services]], [[daed]], [[clear_log]])%>', null,
function(x, data) {
if(x && x.status == 200) {
var log_textarea = document.getElementById('log_textarea');
log_textarea.innerHTML = "";
log_textarea.scrollTop = log_textarea.scrollHeight;
}
location.reload();
}
);
}
var scrolled = false;
XHR.poll(2, '<%=url([[admin]], [[services]], [[daed]], [[get_log]])%>', null,
function(x, data) {
if(x && x.status == 200) {
var log_textarea = document.getElementById('log_textarea');
log_textarea.innerHTML = x.responseText;
if (!scrolled) {
log_textarea.scrollTop = log_textarea.scrollHeight;
scrolled = true;
}
}
}
);
//]]>
</script>
<fieldset class="cbi-section" id="_log_fieldset">
<input class="cbi-button cbi-input-remove" type="button" onclick="clear_log()" value="<%:Clear logs%>" style="margin-left: 10px; margin-top: 10px;">
<textarea id="log_textarea" class="cbi-input-textarea" style="width: calc(100% - 20px); height: 645px; margin: 10px;" data-update="change" rows="5" wrap="off" readonly="readonly"></textarea>
</fieldset>

View File

@@ -1,24 +0,0 @@
<script type="text/javascript">//<![CDATA[
XHR.poll(3, '<%=url([[admin]], [[services]], [[daed_status]])%>', null,
function(x, data) {
var tb = document.getElementById('daed_status');
if (data && tb)
{
if (data.running)
{
tb.innerHTML = '<em><b style=\"color:green\"><%:DAED%> <%:RUNNING%></b></em>';
}
else
{
tb.innerHTML = '<em><b style=\"color:red\"><%:DAED%> <%:NOT RUNNING%></b></em>';
}
}
}
);
//]]></script>
<style>.mar-10 {margin-left: 50px; margin-right: 10px;}</style>
<fieldset class="cbi-section">
<p id="daed_status">
<em><%:Collecting data...%></em>
</p>
</fieldset>

View File

@@ -0,0 +1,97 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:53
msgid "A modern dashboard for dae."
msgstr ""
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:66
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/log.js:30
msgid "Collecting data…"
msgstr ""
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:72
msgid "Enable"
msgstr ""
#: applications/luci-app-daed/root/usr/share/rpcd/acl.d/luci-app-daed.json:3
msgid "Grant access to daed configuration"
msgstr ""
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:76
msgid "Listening address"
msgstr ""
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/log.js:28
msgid "Loading..."
msgstr ""
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/log.js:77
#: applications/luci-app-daed/root/usr/share/luci/menu.d/luci-app-daed.json:22
msgid "Log"
msgstr ""
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/log.js:46
msgid "Log file does not exist."
msgstr ""
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/log.js:37
msgid "Log is empty."
msgstr ""
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:81
msgid "Max log backups"
msgstr ""
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:86
msgid "Max log size"
msgstr ""
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:35
msgid "NOT RUNNING"
msgstr ""
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:32
msgid "Open Web Interface"
msgstr ""
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:33
msgid "RUNNING"
msgstr ""
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/log.js:83
msgid "Refresh every %s seconds."
msgstr ""
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/log.js:69
msgctxt "scroll to top (the head) of the log file"
msgid "Scroll to head"
msgstr ""
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/log.js:60
msgctxt "scroll to bottom (the tail) of the log file"
msgid "Scroll to tail"
msgstr ""
#: applications/luci-app-daed/root/usr/share/luci/menu.d/luci-app-daed.json:14
msgid "Settings"
msgstr ""
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:82
msgid "The maximum number of old log files to retain."
msgstr ""
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:87
msgid "The maximum size in megabytes of the log file before it gets rotated."
msgstr ""
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/log.js:50
msgid "Unknown error: %s"
msgstr ""
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:33
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:35
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:52
#: applications/luci-app-daed/root/usr/share/luci/menu.d/luci-app-daed.json:3
msgid "daed"
msgstr ""

View File

@@ -1,68 +1,104 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Project-Id-Version: PACKAGE VERSION\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: zh-Hans\n"
"MIME-Version: 1.0\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "DAED"
msgstr "DAED"
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:53
msgid "A modern dashboard for dae."
msgstr "dae 现代化控制面板。"
msgid "DAE is a Linux high-performance transparent proxy solution based on eBPF, And DAED is a modern dashboard for dae."
msgstr "DAE是一个基于eBPF的Linux高性能透明代理解决方案,而DAED是DAE的管理面板。"
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:66
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/log.js:30
msgid "Collecting data…"
msgstr "正在收集数据中…"
msgid "Base Setting"
msgstr "基本设置"
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:72
msgid "Enable"
msgstr "启用"
msgid "Dashboard"
msgstr "仪表板"
#: applications/luci-app-daed/root/usr/share/rpcd/acl.d/luci-app-daed.json:3
msgid "Grant access to daed configuration"
msgstr "授予访问 daed 配置的权限"
msgid "Logs"
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:76
msgid "Listening address"
msgstr "监听地址"
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/log.js:28
msgid "Loading..."
msgstr "加载中..."
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/log.js:77
#: applications/luci-app-daed/root/usr/share/luci/menu.d/luci-app-daed.json:22
msgid "Log"
msgstr "日志"
msgid "Clear logs"
msgstr "清空日志"
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/log.js:46
msgid "Log file does not exist."
msgstr "日志文件不存在。"
msgid "RUNNING"
msgstr "运行中"
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/log.js:37
msgid "Log is empty."
msgstr "日志为空"
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:81
msgid "Max log backups"
msgstr "最大日志备份"
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:86
msgid "Max log size"
msgstr "最大日志大小"
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:35
msgid "NOT RUNNING"
msgstr "未运行"
msgid "Collecting data..."
msgstr "收集数据..."
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:32
msgid "Open Web Interface"
msgstr "打开 Web 界面"
msgid "Logfile retention count"
msgstr "日志文件保留数量"
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:33
msgid "RUNNING"
msgstr "运行中"
msgid "Logfile Max Size (MB)"
msgstr "日志文件大小(MB)"
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/log.js:83
msgid "Refresh every %s seconds."
msgstr "每 %s 秒刷新。"
msgid "Set the DAED listen address"
msgstr "设置监听地址"
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/log.js:69
msgctxt "scroll to top (the head) of the log file"
msgid "Scroll to head"
msgstr "滚动到顶部"
msgid "Dashboard Access Port"
msgstr "仪表板访问端口"
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/log.js:60
msgctxt "scroll to bottom (the tail) of the log file"
msgid "Scroll to tail"
msgstr "滚动到尾部"
msgid "Leave empty to use listen port"
msgstr "留空则使用监听端口"
#: applications/luci-app-daed/root/usr/share/luci/menu.d/luci-app-daed.json:14
msgid "Settings"
msgstr "设置"
msgid "For reverse proxy scenarios, leave empty to use the port from listen address"
msgstr "用于反向代理场景,留空则使用监听地址的端口"
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:82
msgid "The maximum number of old log files to retain."
msgstr "要保留的最大旧日志文件数量。"
msgid "DAED is not running"
msgstr "DAED 未运行"
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:87
msgid "The maximum size in megabytes of the log file before it gets rotated."
msgstr "要保留的最大日志大小单位MB。"
msgid "Please start the DAED service first and try again"
msgstr "请先启动 DAED 服务后重试"
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/log.js:50
msgid "Unknown error: %s"
msgstr "未知错误:%s"
msgid "Enable Auto Subscribe Update"
msgstr "启用订阅自动更新"
msgid "Update Cycle"
msgstr "更新周期"
msgid "Update Time (Every Day)"
msgstr "更新时间(每天)"
msgid "Username"
msgstr "用户名"
msgid "Password"
msgstr "密码"
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:33
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:35
#: applications/luci-app-daed/htdocs/luci-static/resources/view/daed/config.js:52
#: applications/luci-app-daed/root/usr/share/luci/menu.d/luci-app-daed.json:3
msgid "daed"
msgstr "daed"

View File

@@ -1,36 +0,0 @@
#!/bin/sh
USERNAME=$(uci -q get daed.config.daed_username)
PASSWORD=$(uci -q get daed.config.daed_password)
PORT=$(echo "$(uci -q get daed.config.listen_addr)" | grep -oE '[0-9]+$' | sed -n '1p')
GRAPHQL_URL="http://127.0.0.1:"$PORT"/graphql"
CRON_FILE="/etc/crontabs/root"
RANDOM_SEED=$RANDOM
RANDOM_NUM=$((RANDOM_SEED % 10 + 1))
login() {
LOGIN=$(curl -s -X POST -H "Content-Type: application/json" -d '{"query":"query Token($username: String!, $password: String!) {\n token(username: $username, password: $password)\n}","variables":{"username":"'"$USERNAME"'","password":"'"$PASSWORD"'"}}' $GRAPHQL_URL)
JSON=${LOGIN#\"}
JSON=${LOGIN%\"}
TOKEN=$(echo $JSON | sed -n 's/.*"token":"\([^"]*\)".*/\1/p')
}
update_subscription() {
SUBSCRIPTION_ID_LIST=$(curl -s -X POST -H "Authorization: $TOKEN" -d '{"query": "query Subscriptions {\n subscriptions {\nid\ntag\nstatus\nlink\ninfo\nupdatedAt\nnodes {\nedges {\nid\nname\nprotocol\nlink\n}\n}\n}\n}", "operationName": "Subscriptions"}' $GRAPHQL_URL | grep -o '"id":"[^"]*","tag"' | grep -o 'id":"[^"]*' | grep -o '[^"]*$')
echo "$SUBSCRIPTION_ID_LIST" | while read -r id; do
curl -X POST -H "Authorization: $TOKEN" -d '{"query":"mutation UpdateSubscription($id: ID!) {\n updateSubscription(id: $id) {\n id\n }\n}","variables":{"id":"'"$id"'"},"operationName":"UpdateSubscription"}' $GRAPHQL_URL
done
}
reload() {
curl -X POST -H "Authorization: $TOKEN" -d '{"query":"mutation Run($dry: Boolean!) {\n run(dry: $dry)\n}","variables":{"dry":false},"operationName":"Run"}' $GRAPHQL_URL
}
resetcron() {
touch $CRON_FILE
sed -i '/daed_sub.sh/d' $CRON_FILE 2>/dev/null
[ "$(uci -q get daed.config.subscribe_auto_update)" -eq 1 ] && echo "${RANDOM_NUM} $(uci -q get daed.config.subscribe_update_day_time) * * $(uci -q get daed.config.subscribe_update_week_time) /etc/daed/daed_sub.sh >/dev/null 2>&1" >>$CRON_FILE
crontab $CRON_FILE
}
login && update_subscription && reload && resetcron

View File

@@ -1,22 +0,0 @@
#!/bin/sh
[ "${ACTION}" = "ifup" ] || exit 0
DEVICE=$(logread | grep "link is up" | tail -n 1 | awk -F "'" '{print $2}')
DEVICE_TYPE=$(ip link show dev "$DEVICE")
case "$DEVICE_TYPE" in
*"link/ether"*)
(
LOCK_FILE="/tmp/lock/daed_hotplug_lock"
if [ -f "$LOCK_FILE" ]; then
exit 1
else
echo $$ > "$LOCK_FILE" 2>/dev/null
trap 'rm -f "$LOCK_FILE"' EXIT
sleep 60
/etc/init.d/daed restart 2>&1
fi
) &
;;
esac

View File

@@ -1,70 +0,0 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2023 Tianling Shen <cnsztl@immortalwrt.org>
USE_PROCD=0
START=98
CONF="daed"
PROG="/usr/bin/daed"
LOG="/var/log/daed/daed.log"
CRON_FILE="/etc/crontabs/root"
RANDOM_SEED=$RANDOM
RANDOM_NUM=$((RANDOM_SEED % 10 + 1))
setcron() {
touch $CRON_FILE
sed -i '/daed_sub.sh/d' $CRON_FILE 2>/dev/null
[ "$(uci -q get daed.config.subscribe_auto_update)" -eq 1 ] && echo "${RANDOM_NUM} $(uci -q get daed.config.subscribe_update_day_time) * * $(uci -q get daed.config.subscribe_update_week_time) /etc/daed/daed_sub.sh >/dev/null 2>&1" >>$CRON_FILE
crontab $CRON_FILE
}
delcron() {
sed -i '/daed_sub.sh/d' $CRON_FILE 2>/dev/null
crontab $CRON_FILE
}
setlocaluse() {
uci set dhcp.@dnsmasq[0].localuse="1"
uci commit dhcp
/etc/init.d/dnsmasq restart
}
dellocaluse() {
uci set dhcp.@dnsmasq[0].localuse="0"
uci commit dhcp
/etc/init.d/dnsmasq restart
. /lib/functions/network.sh
network_find_wan LOGICAL_WAN || exit 1
dns_list=$(ubus call network.interface.$LOGICAL_WAN status | jsonfilter -e '@["dns-server"]' | sed 's/[]["]//g' | sed 's/,/\n/g' | grep -oE '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b')
[ -z "$dns_list" ] && dns_list="119.29.29.29 180.76.76.76 223.5.5.5"
grep -v '^nameserver ' /etc/resolv.conf > /tmp/resolv.conf.new.daed 2>/dev/null
for dns in $dns_list; do
echo "nameserver $dns" >> /tmp/resolv.conf.new.daed
done
cat /tmp/resolv.conf.new.daed > /etc/resolv.conf
rm -f /tmp/resolv.conf.new.daed
}
start_service() {
[ -f "/etc/init.d/daed" ] && grep -q "DAE_LOCATION_ASSET" "/etc/init.d/daed" || sed -i '/run/i\ procd_set_param env DAE_LOCATION_ASSET="/usr/share/v2ray"' "/etc/init.d/daed"
config_load "$CONF"
local enabled
config_get_bool enabled "config" "enabled" "0"
if [ "$enabled" -eq 0 ]; then
delcron
setlocaluse
return 1
fi
setcron
dellocaluse
}
stop_service() {
delcron
setlocaluse
}
service_triggers() {
procd_add_reload_trigger "$CONF"
}

View File

@@ -0,0 +1,29 @@
{
"admin/services/daed": {
"title": "DAED",
"order": 20,
"action": {
"type": "firstchild"
},
"depends": {
"acl": [ "luci-app-daed" ],
"uci": { "daed": true }
}
},
"admin/services/daed/config": {
"title": "Settings",
"order": 10,
"action": {
"type": "view",
"path": "daed/config"
}
},
"admin/services/daed/log": {
"title": "Log",
"order": 20,
"action": {
"type": "view",
"path": "daed/log"
}
}
}

View File

@@ -1,7 +1,13 @@
{
"luci-app-daed": {
"description": "Grant UCI access for luci-app-daed",
"description": "Grant access to daed configuration",
"read": {
"file": {
"/var/log/daed/daed.log": [ "read" ]
},
"ubus": {
"service": [ "list" ]
},
"uci": [ "daed" ]
},
"write": {

View File

@@ -0,0 +1,201 @@
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.

View File

@@ -0,0 +1,35 @@
# SPDX-License-Identifier: GPL-3.0-only
#
# Copyright (C) 2021-2025 sirpdboy <herboy2008@gmail.com>
# https://github.com/sirpdboy/luci-app-kucat-config
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
NAME:=kucat-config
PKG_NAME:=luci-app-$(NAME)
PKG_VERSION:=2.1.0
PKG_RELEASE:=20251117
PKG_MAINTAINER:=sirpdboy team <herboy2008@gmail.com>
LUCI_TITLE:=LuCI support for Kucat theme setting by sirpdboy
LUCI_DEPENDS:=+curl
LUCI_PKGARCH:=all
define Build/Compile
endef
define Package/$(PKG_NAME)/postinst
#!/bin/sh
chmod +x /etc/init.d/kucat
rm -f /tmp/luci-*
endef
define Package/$(PKG_NAME)/conffiles
/etc/config/kucat
endef
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@@ -0,0 +1,91 @@
![hello](https://views.whatilearened.today/views/github/sirpdboy/deplives.svg) [![](https://img.shields.io/badge/TG群-点击加入-FFFFFF.svg)](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ)
<h1 align="center">
<br>luci-app-kucat-config<br>
</h1>
<p align="center">
<a target="_blank" href="https://github.com/sirpdboy/luci-app-kucat-config/releases">
<img src="https://img.shields.io/github/release/sirpdboy/luci-app-kucat-config.svg?style=flat-square&label=luci-app-kucat-config&colorB=green">
</a>
</p>
[中文](README_CN.md) | [English]
![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明1.jpg)
# [luci-app-kucat-config](https://github.com/sirpdboy/luci-app-kucat-config)
- Development time: December 2021
- Release time: February 2023
- Open source date: April 2023
- Main supports LEDE18.06 and official 18.06 branch open source time: April 2023 version: 1.2.6
- JS supports official website 19.07-24.10 branch latest version: May 2025 version: 2.5.9 compatible with OPENWRT 24.10 branch.
# Function Introduction
- Being cool is beauty, born from movement.
- A toolbar with 5 shortcut keys.
- Basic BUG, adapted to all plugins currently available
- We have made a lot of optimizations for mobile phones and other devices
- There are matching theme setting tools, which can adjust shortcut keys, background images, and various color schemes to choose from.
# As mentioned earlier, the development of the Cool Cat theme drew inspiration and borrowed some code from Opentopd theme, Jerryk God's Argon theme, and Thinktip God's Neobird theme. Thank you for your valuable contributions!
# Special Instructions
- When there are advanced settings and KUCAT theme setting tools in the system, the settings in the advanced settings shall prevail.
- If there are any issues with the settings, you can restore them using function 4 after logging in via SSH.
- If it is an advanced setting, to restore the default settings, you can use:/etc/init.d/advancedplus reset
- If it is the KUCAT setting tool, to restore the default settings, you can use:/etc/init.d/kucat reset
## describe
- luci-theme-kucat https://github.com/sirpdboy/luci-theme-kucat
- luci-app-advancedplus https://github.com/sirpdboy/luci-app-advancedplus
- luci-app-kucat-config https://github.com/sirpdboy/luci-app-kucat-config
## interface
![screenshots](./doc/kucat1.png)
![screenshots](./doc/kucat2.png)
![screenshots](./doc/kucat3.png)
![screenshots](./doc/kucat4.png)
![screenshots](./doc/kucatz1.png)
![screenshots](./doc/kucatz2.png)
# My other project
- Watch Dog https://github.com/sirpdboy/luci-app-watchdog
- Net Speedtest https://github.com/sirpdboy/luci-app-netspeedtest
- Task Plan : https://github.com/sirpdboy/luci-app-taskplan
- Power Off Device : https://github.com/sirpdboy/luci-app-poweroffdevice
- OpentoPD Theme : https://github.com/sirpdboy/luci-theme-opentopd
- Ku Cat Theme : https://github.com/sirpdboy/luci-theme-kucat
- Ku Cat Theme Config : https://github.com/sirpdboy/luci-app-kucat-config
- NFT Time Control : https://github.com/sirpdboy/luci-app-timecontrol
- Parent Control: https://github.com/sirpdboy/luci-theme-parentcontrol
- Eqos Plus: https://github.com/sirpdboy/luci-app-eqosplus
- Advanced : https://github.com/sirpdboy/luci-app-advanced
- ddns-go : https://github.com/sirpdboy/luci-app-ddns-go
- Advanced Plus: https://github.com/sirpdboy/luci-app-advancedplus
- Net Wizard: https://github.com/sirpdboy/luci-app-netwizard
- Part Exp: https://github.com/sirpdboy/luci-app-partexp
- Lukcy: https://github.com/sirpdboy/luci-app-lukcy
## HELP
| <img src="https://img.shields.io/badge/-Alipay-F5F5F5.svg" href="#赞助支持本项目-" height="25" alt="图飞了"/> | <img src="https://img.shields.io/badge/-WeChat-F5F5F5.svg" height="25" alt="图飞了" href="#赞助支持本项目-"/> |
| :-----------------: | :-------------: |
|![xm1](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/支付宝.png) | ![xm1](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/微信.png) |
<a href="#readme">
<img src="https://img.shields.io/badge/-TOP-orange.svg" alt="no" title="Return TOP" align="right"/>
</a>
![hello](https://visitor-badge-deno.deno.dev/sirpdboy.sirpdboy.svg) [![](https://img.shields.io/badge/TGGroup-ClickJoin-FFFFFF.svg)](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ)

View File

@@ -0,0 +1,114 @@
![hello](https://views.whatilearened.today/views/github/sirpdboy/deplives.svg) [![](https://img.shields.io/badge/TG群-点击加入-FFFFFF.svg)](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ)
<h1 align="center">
<br>luci-app-kucat-config<br>
</h1>
<p align="center">
<a target="_blank" href="https://github.com/sirpdboy/luci-app-kucat-config/releases">
<img src="https://img.shields.io/github/release/sirpdboy/luci-app-kucat-config.svg?style=flat-square&label=luci-app-kucat-config&colorB=green">
</a>
</p>
[中文] | [English](README.md)
![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明1.jpg)
# 酷猫主题链接: https://github.com/sirpdboy/luci-theme-kucat
# 进队设置系统工具-酷猫/argon/design三主题设置下载链接 [https://github.com/sirpdboy/luci-app-advancedplus](https://github.com/sirpdboy/luci-app-advancedplus/releases)
# 酷猫主题设置工具 链接: [https://github.com/sirpdboy/luci-app-kucat-config](https://github.com/sirpdboy/luci-app-kucat-config)
- 开发时间2021年12月
- 发布时间2023年2月
- 开源时间2023年4月
- 支持 支持官网openwrt 18.06-24.10分支 开源时间2023年9月 最新版本1.1.0
# 特殊说明
- 当系统中 有进阶设置又有 KUCAT主题设置工具时 以进阶设置中的设置为准。
- 如果设置上有问题可以在SSH登陆后用4号功能恢复设置。
- 如果是进阶设置,恢复默认设置,可以用:/etc/init.d/advancedplus reset
# 功能介绍
- 以酷为美,因动而生.
- 带5种快捷键的导航栏.
- 适应目前18.06到23.05的所有版本,对大多数插件等进行适配优化.
- 针对手机等做了大量优化.
- 有配套的主题设置工具,可调节快捷键、背景图片、颜色多种方案可供选择。
- 集成亮色和暗色模式。
#【说在前面的话】此次酷猫主题研发借鉴:Opentopd主题、Jerryk大神argon主题、以及thinktip大神的neobird主题的部分灵感及参考借用部分代码在这里表示感谢感谢有你们珠玉在前
#目前源码暂不开源,如果你们需要联系本人可以免费获取。
## 目前最新版本仅VIP固件中集成开源升级时间在6个月以后。感谢大家支持与理解。
# 为什么叫酷猫
## 二个原因:
## 一是这主题就是为酷而生,一切只为酷与简单!从此让你的主题不再单调!让主题活起来!按钮动起来!全面掌控主题颜色设置!
## 二是因为这主题是在九个方案版本中测试研究后决定出来正合猫有9条命另外是本人年后到现在在无数个夜班熬呀熬熬出来的都熬成夜猫子了所以定名【英文名KuCat】【中文名酷猫】。
另外要说明一下就是本人弄固件不只是编译与搬运,是开发,是创新,更是爱好!请不要将本人与一些搬运工编译的固件来比较!那样会只会显得你很无知。
当然在OPENWRT的世界里比本人更优秀更专业的人士大有人在本人能做的就是尽自己微薄浅湿的力量尽量让固件更好用和更少的BUG。
不管是本人还是别人开发的插件,这里面的每一行代码,都凝聚着开发者在后面无数辛勤汗水和智慧的付出,请大家珍重他们的劳动成果与心血付出!
也许某些功能某些要求还达不到你的预期要求,有道是一人难满百人意,有些可能是本身实现不了,有些可能是能力不足的原因实现不了.
不管如何如果有问题或者BUG请多给理解与支持【当然有能力的请给这些开发者们更多的物资支持】
在此要特别感谢VIP群里以及不在群里的所有为爱发电的好朋友们是你们给本人无尽的力量谢谢
## 界面
![screenshots](./doc/kucat1.png)
![screenshots](./doc/kucat2.png)
![screenshots](./doc/kucat3.png)
![screenshots](./doc/kucat4.png)
![screenshots](./doc/kucatz1.png)
![screenshots](./doc/kucatz2.png)
# My other project
- 路由安全看门狗 https://github.com/sirpdboy/luci-app-watchdog
- 网络速度测试 https://github.com/sirpdboy/luci-app-netspeedtest
- 计划任务插件(原定时设置) : https://github.com/sirpdboy/luci-app-taskplan
- 关机功能插件 : https://github.com/sirpdboy/luci-app-poweroffdevice
- opentopd主题 : https://github.com/sirpdboy/luci-theme-opentopd
- kucat酷猫主题: https://github.com/sirpdboy/luci-theme-kucat
- kucat酷猫主题设置工具: https://github.com/sirpdboy/luci-app-kucat-config
- NFT版上网时间控制插件: https://github.com/sirpdboy/luci-app-timecontrol
- 家长控制: https://github.com/sirpdboy/luci-theme-parentcontrol
- 定时限速: https://github.com/sirpdboy/luci-app-eqosplus
- 系统高级设置 : https://github.com/sirpdboy/luci-app-advanced
- ddns-go动态域名: https://github.com/sirpdboy/luci-app-ddns-go
- 进阶设置(系统高级设置+主题设置kucat/agron/opentopd: https://github.com/sirpdboy/luci-app-advancedplus
- 网络设置向导: https://github.com/sirpdboy/luci-app-netwizard
- 一键分区扩容: https://github.com/sirpdboy/luci-app-partexp
- lukcy大吉: https://github.com/sirpdboy/luci-app-lukcy
## 捐助
![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明3.jpg)
| <img src="https://img.shields.io/badge/-支付宝-F5F5F5.svg" href="#赞助支持本项目-" height="25" alt="图飞了"/> | <img src="https://img.shields.io/badge/-微信-F5F5F5.svg" height="25" alt="图飞了" href="#赞助支持本项目-"/> |
| :-----------------: | :-------------: |
|![xm1](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/支付宝.png) | ![xm1](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/微信.png) |
<a href="#readme">
<img src="https://img.shields.io/badge/-返回顶部-orange.svg" alt="图飞了" title="返回顶部" align="right"/>
</a>
![](https://visitor-badge-deno.deno.dev/sirpdboy.sirpdboy.svg) [![](https://img.shields.io/badge/TG群-点击加入-FFFFFF.svg)](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ)

Binary file not shown.

After

Width:  |  Height:  |  Size: 581 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 687 KiB

View File

@@ -0,0 +1,110 @@
'use strict';
'require form';
'require fs';
'require rpc';
'require uci';
'require ui';
'require view';
var opacity_sets = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20];
var ts_sets = [0, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95, 1];
return view.extend({
load: function() {
return Promise.all([
uci.load('kucat')
]);
},
render: function(data) {
var m, s, o;
m = new form.Map('kucat', _('KuCat Theme Config'),
_('You can set KuCat theme font size, color scheme, shortcut tools, and manage login and desktop background images here.'));
s = m.section(form.TypedSection, 'basic', '');
s.anonymous = true;
// Wallpaper Source
o = s.option( form.ListValue, 'background', _('Wallpaper Source'),
_('Local wallpapers need to be uploaded by oneself, and those that are automatically downloaded will only be downloaded on the first visit every day, reducing frequent remote access and making usage smoother.'));
o.value('0', _('Local wallpaper'));
o.value('1', _('Auto download Iciba wallpaper'));
o.value('2', _('Auto download unsplash wallpaper'));
o.value('3', _('Auto download Bing wallpaper'));
o.value('4', _('Auto download Bird 4K wallpaper'));
o.default = '0';
o.rmempty = false;
// Set font size
o = s.option( form.ListValue, 'fontmode', _('Set font size'));
o.rmempty = false;
o.value('0', _('Small font'));
o.value('1', _('Normal font'));
o.value('2', _('Large font'));
o.default = '0';
// Wallpaper synchronization
o = s.option( form.Flag, 'bklock', _('Wallpaper synchronization'),
_('Is the login wallpaper consistent with the desktop wallpaper? If selected, it means that the desktop wallpaper and login wallpaper are the same image.'));
o.rmempty = false;
o.default = '0';
// Expand Toolbar
o = s.option( form.Flag, 'setbar', _('Expand navigation bar'),
_('Expand or shrink the five quick navigation bars'));
o.rmempty = false;
o.default = '0';
// Refreshing mode
o = s.option( form.Flag, 'bgqs', _('Refreshing mode'));
o.rmempty = false;
o.default = '0';
// Enable Daily Word
o = s.option( form.Flag, 'dayword', _('Enable Daily Word'));
o.rmempty = false;
o.default = '0';
// Status Homekey settings
o = s.option( form.ListValue, 'gohome', _('Status Homekey settings'));
o.value('overview', _('Overview'));
o.value('processes', _('Processes'));
o.value('realtime', _('Realtime_Graphs'));
o.value('netdata', _('Netdata'));
o.default = 'overview';
o.rmempty = false;
// System Userkey settings
o = s.option( form.ListValue, 'gouser', _('System Userkey settings'));
o.value('kucat-config', _('KuCat Config'));
o.value('netwizard', _('Netwizard'));
o.value('system', _('System'));
o.value('admin', _('Administration'));
o.value('filemanager', _('File_Manager'));
o.value('ttyd', _('Terminal'));
o.value('poweroffdevice', _('PowerOff'));
o.default = 'kucat';
o.rmempty = false;
// Services Ssrkey settings
o = s.option( form.ListValue, 'gossr', _('Services Ssrkey settings'));
o.value('shadowsocksr', _('SSR'));
o.value('bypass', _('bypass'));
o.value('nikki', _('Nikki'));
o.value('passwall', _('passwall'));
o.value('passwall2', _('passwall2'));
o.value('openclash', _('OpenClash'));
o.value('homeproxy', _('HomeProxy'));
o.value('mosdns', _('MosDNS'));
o.value('smartdns', _('SmartDNS'));
o.value('AdGuardHome', _('AdGuard_Home'));
o.default = 'OpenClash';
o.rmempty = false;
return m.render();
}
});

View File

@@ -0,0 +1,95 @@
'use strict';
'require form';
'require fs';
'require rpc';
'require uci';
'require ui';
'require view';
var opacity_sets = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20];
var ts_sets = [0, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95, 1];
return view.extend({
load: function() {
return Promise.all([
uci.load('kucat')
]);
},
render: function() {
var m, s, o;
m = new form.Map('kucat', _('KuCat Theme Color Schemes List'),
_('Pre set 6 color schemes, enable wallpaper as desktop wallpaper, theme RGB values such as 255,0,0 (representing red), dark mode with wallpaper blur for better effect. No matter how many schemes are enabled, only the first scheme should be used'));
var s = m.section(form.TableSection, 'theme', '');
s.addremove = true;
s.anonymous = false;
s.sortable = true;
o = s.option(form.Value, 'remarks', _('Remarks'),
_('Give a descriptive name for this color scheme'));
o.rmempty = false;
o = s.option(form.Flag, 'use', _('Enable'),
_('Enable this color scheme'));
o.rmempty = false;
o.default = '1';
o = s.option(form.Flag, 'bkuse', _('Use wallpaper'),
_('Use desktop wallpaper'));
o.rmempty = false;
o.default = '1';
o = s.option(form.ListValue, 'mode', _('Light dark mode'),
_('Select the theme appearance mode'));
o.value('auto', _('Auto'));
o.value('light', _('Light'));
o.value('dark', _('Dark'));
o.default = 'light';
o.rmempty = false;
o = s.option(form.Value, 'primary_rgbm', _('Main color(RGB)'),
_("RGB values like '255,0,0' for red, or use preset names"));
o.value('blue', _('RoyalBlue'));
o.value('green', _('MediumSeaGreen'));
o.value('orange', _('SandyBrown'));
o.value('red', _('TomatoRed'));
o.value('black', _('Black tea eye protection gray'));
o.value('gray', _('Cool night time(gray and dark)'));
o.value('bluets', _('Cool Ocean Heart (transparent and bright)'));
o.rmempty = false;
o.default = '74,161,133';
o = s.option(form.ListValue, 'primary_rgbm_ts', _('Wallpaper transparency'),
_('Wallpaper transparency level (0: Transparent, 1: Opaque)'));
for (var i = 0; i < ts_sets.length; i++) {
o.value(ts_sets[i].toString(), ts_sets[i].toString());
}
o.rmempty = false;
o.default = '0.9';
o = s.option(form.ListValue, 'primary_opacity', _('Wallpaper blur radius'),
_('Wallpaper blur effect (0: no blur)'));
for (var i = 0; i < opacity_sets.length; i++) {
o.value(opacity_sets[i].toString(), opacity_sets[i].toString());
}
o.datatype = 'ufloat';
o.rmempty = false;
o.default = '0';
o = s.option(form.Value, 'primary_rgbs', _('Fence Color(RGB)'),
_("Fence background color in RGB (suggest dark values)"));
o.default = '225,112,88';
o.rmempty = false;
o = s.option(form.ListValue, 'primary_rgbs_ts', _('Fence color transparency'),
_('Fence background transparency (0: Transparent, 1: Opaque)'));
for (var i = 0; i < ts_sets.length; i++) {
o.value(ts_sets[i].toString(), ts_sets[i].toString());
}
o.datatype = 'ufloat';
o.rmempty = false;
o.default = '0.1';
return m.render();
}
});

View File

@@ -0,0 +1,673 @@
'use strict';
'require form';
'require fs';
'require rpc';
'require uci';
'require ui';
'require view';
var callAvailSpace = rpc.declare({
object: 'luci.kucatconfig',
method: 'space'
});
var callDelkucat = rpc.declare({
object: 'luci.kucatconfig',
method: 'deld',
params: ['filename'],
expect: { '': {} }
});
var callRenamekucat = rpc.declare({
object: 'luci.kucatconfig',
method: 'renamed',
params: ['newname'],
expect: { '': {} }
});
var callReadFile = rpc.declare({
object: 'luci.kucatconfig',
method: 'read_file',
params: ['filename'],
expect: { '': {} }
});
var bg_path = '/www/luci-static/kucat/background/';
return view.extend({
load: function() {
return Promise.all([
uci.load('kucat'),
L.resolveDefault(callAvailSpace(), {}),
L.resolveDefault(fs.list(bg_path), {})
]);
},
render: function(data) {
var m, s, o;
m = new form.Map('kucat', _('Desktop Wallpaper Upload'),
_('Kucat Theme Desktop Background Wallpaper Upload and Management.'));
// 添加样式
var style = E('style', {}, `
/* 浏览模式切换 */
.view-mode-toggle {
margin: 0 15px;
display: flex;
}
.view-mode-btn {
padding: 8px 16px;
background: #999;
cursor: pointer;
border-radius: 4px;
}
.view-mode-btn.active {
background: #007cff;
color: white;
border-color: #007cff;
}
/* 大图网格布局 */
.grid-view {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 15px;
margin: 0 15px;
}
.grid-item {
border: 1px solid #ddd;
border-radius: 8px;
padding: 10px;
background: white;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
}
.grid-item:hover {
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
transform: translateY(-2px);
}
.grid-item.selected {
border-color: #007cff;
background: #f0f8ff;
}
.grid-preview {
width: 100%;
height: 120px;
object-fit: cover;
border-radius: 4px;
margin-bottom: 8px;
}
.grid-info {
font-size: 12px;
color: #666;
}
.grid-name {
font-weight: bold;
margin-bottom: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* 选择控制 */
.selection-controls {
margin: 0 15px ;
}
.selection-info {
margin-top: 12px;
font-weight: bold;
font-size: 14px;
}
.selection-actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.selection-batch-actions {
display: flex;
gap: 2px;
align-items: center;
flex-wrap: wrap;
}
.batch-action-btn {
padding: 6px 12px;
border: 1px solid #6c757d;
background: #6c757d;
color: white;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
}
.batch-action-btn:hover {
background: #5a6268;
border-color: #545b62;
}
.batch-action-btn.select-all {
background: #28a745;
border-color: #28a745;
}
.batch-action-btn.select-all:hover {
background: #218838;
border-color: #1e7e34;
}
.batch-action-btn.invert {
background: #17a2b8;
border-color: #17a2b8;
}
.batch-action-btn.invert:hover {
background: #138496;
border-color: #117a8b;
}
/* 复选框样式 */
.file-checkbox {
margin-right: 8px;
}
.grid-checkbox {
position: absolute;
top: 5px;
left: 5px;
z-index: 2;
transform: scale(1.2);
}
/* 图片加载失败样式 */
.image-error {
width: 80px;
height: 60px;
background: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
color: #999;
font-size: 12px;
border: 1px solid #ddd;
border-radius: 3px;
align-content: center;
}
.grid-image-error {
width: 100%;
height: 120px;
background: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
color: #999;
font-size: 12px;
border-radius: 4px;
margin-bottom: 8px;
}
/* 预览模态框 */
.preview-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 10000;
}
.preview-modal-content {
max-width: 90%;
max-height: 90%;
background: white;
padding: 20px;
border-radius: 8px;
text-align: center;
}
.preview-modal-image {
max-width: 100%;
max-height: 80vh;
border-radius: 4px;
}
.preview-modal-close {
position: absolute;
top: 20px;
right: 20px;
border: none;
border-radius: 50%;
width: 40px!important;
height: 40px;
font-size: 20px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.preview-modal-info {
margin-top: 10px;
color: #333;
}
`);
var container = E('div', { 'class': 'cbi-map' });
container.appendChild(style);
container.appendChild(E('h3', _('Desktop Wallpaper Upload')));
container.appendChild(E('div', { 'class': 'cbi-section-descr' }, [
E('p', _('Available space: %1024.2mB').format(data[1].space * 1024)),
E('p', _('You can upload files in formats such as gif/jpg/png/webp to create your favorite wallpapers')),
E('p', _('Files will be uploaded to <code>%s</code>.').format(bg_path))
]));
var uploadBtn = E('button', {
'class': 'btn cbi-button cbi-button-action',
'style': 'margin: 10px 0;',
'click': ui.createHandlerFn(this, function(ev) {
var file = '/tmp/kucat_desktop_bg.tmp';
return ui.uploadFile(file, ev.target).then(function(res) {
console.log('Upload completed, res:', res);
if (!res || !res.name) {
throw new Error(_('No file selected or upload failed'));
}
return L.resolveDefault(callRenamekucat(res.name), {}).then(function(ret) {
if (ret && ret.result === 0) {
ui.addNotification(null, E('p', _('File uploaded successfully!')));
setTimeout(function() {
location.reload();
}, 1000);
} else {
var errorMsg = ret ? (ret.error || 'Unknown error') : 'No response from server';
ui.addNotification(null, E('p', _('Failed to upload file: %s').format(errorMsg)));
return L.resolveDefault(fs.remove(file), {});
}
}).catch(function(rpcError) {
console.error('RPC call error:', rpcError);
ui.addNotification(null, E('p', _('RPC call failed: %s').format(rpcError.message || rpcError)));
return L.resolveDefault(fs.remove(file), {});
});
}).catch(function(e) {
console.error('Upload error:', e);
ui.addNotification(null, E('p', _('Upload error: %s').format(e.message)));
return L.resolveDefault(fs.remove(file), {});
});
})
}, _('Upload...'));
container.appendChild(uploadBtn);
container.appendChild(E('div', { 'style': 'text-align: right; font-style: italic;' }, [
E('span', {}, [
_('© github '),
E('a', {
'href': 'https://github.com/sirpdboy',
'target': '_blank',
'style': 'text-decoration: none;'
}, 'by sirpdboy')
])
]));
var files = data[2] || [];
if (files.length > 0) {
container.appendChild(E('h3', _('Background file list')));
// 浏览模式切换
var viewModeToggle = E('div', { 'class': 'view-mode-toggle' }, [
E('button', {
'class': 'view-mode-btn active',
'data-mode': 'list',
'click': ui.createHandlerFn(this, function(ev) {
document.querySelectorAll('.view-mode-btn').forEach(btn => {
btn.classList.remove('active');
});
ev.target.classList.add('active');
listView.style.display = 'table';
gridView.style.display = 'none';
selectedFiles.clear();
})
}, _('List View')),
E('button', {
'class': 'view-mode-btn',
'data-mode': 'grid',
'click': ui.createHandlerFn(this, function(ev) {
document.querySelectorAll('.view-mode-btn').forEach(btn => {
btn.classList.remove('active');
});
ev.target.classList.add('active');
listView.style.display = 'none';
gridView.style.display = 'grid';
selectedFiles.clear();
})
}, _('Grid View'))
]);
container.appendChild(viewModeToggle);
// 存储选中的文件
var selectedFiles = new Set();
var allFileNames = files.map(file => file.name);
// 选择控制面板
var selectionControls = E('div', { 'class': 'selection-controls' }, [
E('div', { 'class': 'selection-batch-actions' }, [
E('button', {
'class': 'batch-action-btn select-all',
'click': ui.createHandlerFn(this, function() {
// 全选功能
selectedFiles.clear();
allFileNames.forEach(filename => {
selectedFiles.add(filename);
});
updateSelectionControls();
updateAllCheckboxes(true);
})
}, _('Select All')),
E('button', {
'class': 'batch-action-btn invert',
'click': ui.createHandlerFn(this, function() {
// 反选功能
allFileNames.forEach(filename => {
if (selectedFiles.has(filename)) {
selectedFiles.delete(filename);
} else {
selectedFiles.add(filename);
}
});
updateSelectionControls();
updateAllCheckboxes();
})
}, _('Invert Selection')),
E('button', {
'class': 'batch-action-btn',
'click': ui.createHandlerFn(this, function() {
// 取消全选
selectedFiles.clear();
updateSelectionControls();
updateAllCheckboxes(false);
})
}, _('Select None')),
E('button', {
'class': 'btn cbi-button cbi-button-remove',
'click': ui.createHandlerFn(this, function() {
if (selectedFiles.size === 0) {
ui.addNotification(null, E('p', _('Please select files to delete.')));
return;
}
var filenames = Array.from(selectedFiles);
var confirmMsg = _('Are you sure you want to delete %d selected files?').format(selectedFiles.size);
if (confirm(confirmMsg)) {
var deletePromises = filenames.map(function(filename) {
return L.resolveDefault(callDelkucat(filename), {});
});
Promise.all(deletePromises).then(function(results) {
var successCount = results.filter(function(ret) {
return ret && ret.result === 0;
}).length;
if (successCount === filenames.length) {
ui.addNotification(null, E('p', _('File deleted successfully!')));
} else {
ui.addNotification(null, E('p', _('Some files failed to delete.')));
}
location.reload();
}).catch(function(error) {
ui.addNotification(null, E('p', _('Delete operation failed: %s').format(error.message)));
});
}
})
}, _('Delete Selected')),
E('div', { 'class': 'selection-info' }, _('Selected: 0 files'))
])
]);
container.appendChild(selectionControls);
// 更新所有复选框状态
function updateAllCheckboxes(checked) {
if (checked === true || checked === false) {
// 设置所有复选框为指定状态
document.querySelectorAll('.file-checkbox, .grid-checkbox').forEach(checkbox => {
checkbox.checked = checked;
});
document.querySelectorAll('.grid-item').forEach(item => {
if (checked) {
item.classList.add('selected');
} else {
item.classList.remove('selected');
}
});
} else {
// 根据selectedFiles更新复选框状态
document.querySelectorAll('.file-checkbox, .grid-checkbox').forEach(checkbox => {
checkbox.checked = selectedFiles.has(checkbox.value);
});
document.querySelectorAll('.grid-item').forEach(item => {
var checkbox = item.querySelector('.grid-checkbox');
if (checkbox && selectedFiles.has(checkbox.value)) {
item.classList.add('selected');
} else {
item.classList.remove('selected');
}
});
}
}
// 更新选择控制面板
function updateSelectionControls() {
var info = selectionControls.querySelector('.selection-info');
var selectedCount = selectedFiles.size;
var totalCount = allFileNames.length;
info.textContent = _('Selected: %d of %d files').format(selectedCount, totalCount);
// 如果选择了所有文件,显示特殊提示
if (selectedCount === totalCount && totalCount > 0) {
info.innerHTML = _('Selected: <strong>All %d files</strong>').format(totalCount);
}
}
// 列表视图
var listView = E('table', {
'class': 'table cbi-section-table',
'style': 'display: table;'
}, [
E('tr', { 'class': 'tr table-titles' }, [
E('th', { 'class': 'th' }, [ _('Select') ]),
E('th', { 'class': 'th' }, [ _('Preview') ]),
E('th', { 'class': 'th' }, [ _('Filename') ]),
E('th', { 'class': 'th' }, [ _('Modified date') ]),
E('th', { 'class': 'th' }, [ _('Size') ])
])
]);
// 网格视图
var gridView = E('div', {
'class': 'grid-view',
'style': 'display: none;'
});
files.forEach(L.bind(function(file) {
var previewUrl = '/luci-static/kucat/background/' + file.name;
var timestamp = new Date().getTime();
// 列表视图行
var row = E('tr', { 'class': 'tr' });
// 选择列
var selectCell = E('td', { 'class': 'td' });
var checkbox = E('input', {
'type': 'checkbox',
'class': 'file-checkbox',
'value': file.name,
'change': ui.createHandlerFn(this, function(ev) {
if (ev.target.checked) {
selectedFiles.add(file.name);
} else {
selectedFiles.delete(file.name);
}
updateSelectionControls();
})
});
selectCell.appendChild(checkbox);
row.appendChild(selectCell);
// 预览列
var previewCell = E('td', { 'class': 'td' });
var previewContainer = E('div', { 'style': 'display: flex;justify-content: center;' });
var smallPreview = E('img', {
'class': 'file-list-preview',
'src': previewUrl + '?t=' + timestamp,
'alt': file.name,
'title': _('Click to view larger preview'),
'style': 'cursor: pointer; width: 80px; height: 60px; object-fit: cover;',
'onerror': "this.style.display='none'; this.nextElementSibling.style.display='block';",
'click': ui.createHandlerFn(this, function() {
showImagePreview(file, previewUrl);
})
});
var errorDiv = E('div', {
'class': 'image-error',
'style': 'display: none;'
}, _('Unknow file'));
previewContainer.appendChild(smallPreview);
previewContainer.appendChild(errorDiv);
previewCell.appendChild(previewContainer);
row.appendChild(previewCell);
// 文件名列
row.appendChild(E('td', { 'class': 'td' }, [ file.name ]));
// 修改日期列
row.appendChild(E('td', { 'class': 'td' }, [
new Date(file.mtime * 1000).toLocaleString()
]));
// 文件大小列
row.appendChild(E('td', { 'class': 'td' }, [
String.format('%1024.2mB', file.size)
]));
listView.appendChild(row);
// 网格视图项
var gridItem = E('div', {
'class': 'grid-item',
'click': ui.createHandlerFn(this, function(ev) {
// 如果点击的不是复选框,则显示预览
if (!ev.target.matches('input[type="checkbox"]')) {
showImagePreview(file, previewUrl);
}
})
});
var gridCheckbox = E('input', {
'type': 'checkbox',
'class': 'grid-checkbox',
'value': file.name,
'change': ui.createHandlerFn(this, function(ev) {
ev.stopPropagation();
if (ev.target.checked) {
selectedFiles.add(file.name);
gridItem.classList.add('selected');
} else {
selectedFiles.delete(file.name);
gridItem.classList.remove('selected');
}
updateSelectionControls();
})
});
var gridImageContainer = E('div', { 'style': 'position: relative;' });
var gridPreview = E('img', {
'class': 'grid-preview',
'src': previewUrl + '?t=' + timestamp,
'alt': file.name,
'onerror': "this.style.display='none'; this.nextElementSibling.style.display='flex';"
});
var gridError = E('div', {
'class': 'grid-image-error',
'style': 'display: none;'
}, _('Unknow file'));
gridImageContainer.appendChild(gridPreview);
gridImageContainer.appendChild(gridError);
var gridName = E('div', { 'class': 'grid-name' }, file.name);
var gridInfo = E('div', { 'class': 'grid-info' }, [
String.format('%1024.2mB', file.size),
E('br'),
new Date(file.mtime * 1000).toLocaleDateString()
]);
gridItem.appendChild(gridCheckbox);
gridItem.appendChild(gridImageContainer);
gridItem.appendChild(gridName);
gridItem.appendChild(gridInfo);
gridView.appendChild(gridItem);
}, this));
container.appendChild(listView);
container.appendChild(gridView);
function showImagePreview(file, previewUrl) {
var modal = E('div', { 'class': 'preview-modal' }, [
E('button', {
'class': 'preview-modal-close',
'click': function() {
modal.remove();
}
}, '×'),
E('div', { 'class': 'preview-modal-content' }, [
E('img', {
'class': 'preview-modal-image',
'src': previewUrl + '?t=' + new Date().getTime(),
'alt': file.name,
'onerror': "this.parentNode.removeChild(this);"
}),
E('div', { 'class': 'preview-modal-info' }, [
E('div', { 'style': 'font-weight: bold;' }, file.name),
E('div', { 'style': 'font-size: 14px; color: #666;' },
String.format(_('Size: %1024.2mB, Modified: %s'),
file.size, new Date(file.mtime * 1000).toLocaleString()))
])
])
]);
// 点击模态框背景关闭
modal.addEventListener('click', function(e) {
if (e.target === modal) {
modal.remove();
}
});
// ESC键关闭
document.addEventListener('keydown', function closeOnEsc(e) {
if (e.key === 'Escape') {
modal.remove();
document.removeEventListener('keydown', closeOnEsc);
}
});
document.body.appendChild(modal);
}
} else {
container.appendChild(E('h3', _('Background file list')));
container.appendChild(E('p', E('em', _('No files found.'))));
}
return container;
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});

View File

@@ -0,0 +1,672 @@
'use strict';
'require form';
'require fs';
'require rpc';
'require uci';
'require ui';
'require view';
var callAvailSpace = rpc.declare({
object: 'luci.kucatconfig',
method: 'space'
});
var callDelkucat = rpc.declare({
object: 'luci.kucatconfig',
method: 'del',
params: ['filename'],
expect: { '': {} }
});
var callRenamekucat = rpc.declare({
object: 'luci.kucatconfig',
method: 'rename',
params: ['newname'],
expect: { '': {} }
});
var callReadFile = rpc.declare({
object: 'luci.kucatconfig',
method: 'read_file',
params: ['filename'],
expect: { '': {} }
});
var bg_path = '/www/luci-static/resources/background/';
return view.extend({
load: function() {
return Promise.all([
uci.load('kucat'),
L.resolveDefault(callAvailSpace(), {}),
L.resolveDefault(fs.list(bg_path), {})
]);
},
render: function(data) {
var m, s, o;
m = new form.Map('kucat', _(''),
_('Kucat theme Login background wallpaper upload and management.'));
// 添加样式
var style = E('style', {}, `
/* 浏览模式切换 */
.view-mode-toggle {
margin: 0 15px;
display: flex;
}
.view-mode-btn {
padding: 8px 16px;
background: #999;
cursor: pointer;
border-radius: 4px;
}
.view-mode-btn.active {
background: #007cff;
color: white;
border-color: #007cff;
}
/* 大图网格布局 */
.grid-view {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 15px;
margin: 0 15px;
}
.grid-item {
border: 1px solid #ddd;
border-radius: 8px;
padding: 10px;
background: white;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
}
.grid-item:hover {
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
transform: translateY(-2px);
}
.grid-item.selected {
border-color: #007cff;
background: #f0f8ff;
}
.grid-preview {
width: 100%;
height: 120px;
object-fit: cover;
border-radius: 4px;
margin-bottom: 8px;
}
.grid-info {
font-size: 12px;
color: #666;
}
.grid-name {
font-weight: bold;
margin-bottom: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* 选择控制 */
.selection-controls {
margin: 0 15px ;
}
.selection-info {
margin-top: 12px;
font-weight: bold;
font-size: 14px;
}
.selection-actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.selection-batch-actions {
display: flex;
gap: 2px;
align-items: center;
flex-wrap: wrap;
}
.batch-action-btn {
padding: 6px 12px;
border: 1px solid #6c757d;
background: #6c757d;
color: white;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
}
.batch-action-btn:hover {
background: #5a6268;
border-color: #545b62;
}
.batch-action-btn.select-all {
background: #28a745;
border-color: #28a745;
}
.batch-action-btn.select-all:hover {
background: #218838;
border-color: #1e7e34;
}
.batch-action-btn.invert {
background: #17a2b8;
border-color: #17a2b8;
}
.batch-action-btn.invert:hover {
background: #138496;
border-color: #117a8b;
}
/* 复选框样式 */
.file-checkbox {
margin-right: 8px;
}
.grid-checkbox {
position: absolute;
top: 5px;
left: 5px;
z-index: 2;
transform: scale(1.2);
}
/* 图片加载失败样式 */
.image-error {
width: 80px;
height: 60px;
background: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
color: #999;
font-size: 12px;
border: 1px solid #ddd;
border-radius: 3px;
align-content: center;
}
.grid-image-error {
width: 100%;
height: 120px;
background: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
color: #999;
font-size: 12px;
border-radius: 4px;
margin-bottom: 8px;
}
/* 预览模态框 */
.preview-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 10000;
}
.preview-modal-content {
max-width: 90%;
max-height: 90%;
background: white;
padding: 20px;
border-radius: 8px;
text-align: center;
}
.preview-modal-image {
max-width: 100%;
max-height: 80vh;
border-radius: 4px;
}
.preview-modal-close {
position: absolute;
top: 20px;
right: 20px;
border: none;
border-radius: 50%;
width: 40px!important;
height: 40px;
font-size: 20px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.preview-modal-info {
margin-top: 10px;
color: #333;
}
`);
var container = E('div', { 'class': 'cbi-map' });
container.appendChild(style);
container.appendChild(E('h3', _('Login Wallpaper Upload')));
container.appendChild(E('div', { 'class': 'cbi-section-descr' }, [
E('p', _('Available space: %1024.2mB').format(data[1].space * 1024)),
E('p', _('You can upload files in formats such as gif/jpg/png/webp to create your favorite wallpapers')),
E('p', _('Files will be uploaded to <code>%s</code>.').format(bg_path))
]));
var uploadBtn = E('button', {
'class': 'btn cbi-button cbi-button-action',
'style': 'margin: 10px 0;',
'click': ui.createHandlerFn(this, function(ev) {
var file = '/tmp/kucat_login_bg.tmp';
return ui.uploadFile(file, ev.target).then(function(res) {
console.log('Upload completed, res:', res);
if (!res || !res.name) {
throw new Error(_('No file selected or upload failed'));
}
return L.resolveDefault(callRenamekucat(res.name), {}).then(function(ret) {
if (ret && ret.result === 0) {
ui.addNotification(null, E('p', _('File uploaded successfully!')));
setTimeout(function() {
location.reload();
}, 1000);
} else {
var errorMsg = ret ? (ret.error || 'Unknown error') : 'No response from server';
ui.addNotification(null, E('p', _('Failed to upload file: %s').format(errorMsg)));
return L.resolveDefault(fs.remove(file), {});
}
}).catch(function(rpcError) {
console.error('RPC call error:', rpcError);
ui.addNotification(null, E('p', _('RPC call failed: %s').format(rpcError.message || rpcError)));
return L.resolveDefault(fs.remove(file), {});
});
}).catch(function(e) {
console.error('Upload error:', e);
ui.addNotification(null, E('p', _('Upload error: %s').format(e.message)));
return L.resolveDefault(fs.remove(file), {});
});
})
}, _('Upload...'));
container.appendChild(uploadBtn);
container.appendChild(E('div', { 'style': 'text-align: right; font-style: italic; ' }, [
E('span', {}, [
_('© github '),
E('a', {
'href': 'https://github.com/sirpdboy',
'target': '_blank',
'style': 'text-decoration: none;'
}, 'by sirpdboy')
])
]));
var files = data[2] || [];
if (files.length > 0) {
container.appendChild(E('h3', _('Background file list')));
// 浏览模式切换
var viewModeToggle = E('div', { 'class': 'view-mode-toggle' }, [
E('button', {
'class': 'view-mode-btn active',
'data-mode': 'list',
'click': ui.createHandlerFn(this, function(ev) {
document.querySelectorAll('.view-mode-btn').forEach(btn => {
btn.classList.remove('active');
});
ev.target.classList.add('active');
listView.style.display = 'table';
gridView.style.display = 'none';
selectedFiles.clear();
})
}, _('List View')),
E('button', {
'class': 'view-mode-btn',
'data-mode': 'grid',
'click': ui.createHandlerFn(this, function(ev) {
document.querySelectorAll('.view-mode-btn').forEach(btn => {
btn.classList.remove('active');
});
ev.target.classList.add('active');
listView.style.display = 'none';
gridView.style.display = 'grid';
selectedFiles.clear();
})
}, _('Grid View'))
]);
container.appendChild(viewModeToggle);
// 存储选中的文件
var selectedFiles = new Set();
var allFileNames = files.map(file => file.name);
// 选择控制面板
var selectionControls = E('div', { 'class': 'selection-controls' }, [
E('div', { 'class': 'selection-batch-actions' }, [
E('button', {
'class': 'batch-action-btn select-all',
'click': ui.createHandlerFn(this, function() {
// 全选功能
selectedFiles.clear();
allFileNames.forEach(filename => {
selectedFiles.add(filename);
});
updateSelectionControls();
updateAllCheckboxes(true);
})
}, _('Select All')),
E('button', {
'class': 'batch-action-btn invert',
'click': ui.createHandlerFn(this, function() {
// 反选功能
allFileNames.forEach(filename => {
if (selectedFiles.has(filename)) {
selectedFiles.delete(filename);
} else {
selectedFiles.add(filename);
}
});
updateSelectionControls();
updateAllCheckboxes();
})
}, _('Invert Selection')),
E('button', {
'class': 'batch-action-btn',
'click': ui.createHandlerFn(this, function() {
// 取消全选
selectedFiles.clear();
updateSelectionControls();
updateAllCheckboxes(false);
})
}, _('Select None')),
E('button', {
'class': 'btn cbi-button cbi-button-remove',
'click': ui.createHandlerFn(this, function() {
if (selectedFiles.size === 0) {
ui.addNotification(null, E('p', _('Please select files to delete.')));
return;
}
var filenames = Array.from(selectedFiles);
var confirmMsg = _('Are you sure you want to delete %d selected files?').format(selectedFiles.size);
if (confirm(confirmMsg)) {
var deletePromises = filenames.map(function(filename) {
return L.resolveDefault(callDelkucat(filename), {});
});
Promise.all(deletePromises).then(function(results) {
var successCount = results.filter(function(ret) {
return ret && ret.result === 0;
}).length;
if (successCount === filenames.length) {
ui.addNotification(null, E('p', _('File deleted successfully!')));
} else {
ui.addNotification(null, E('p', _('Some files failed to delete.')));
}
location.reload();
}).catch(function(error) {
ui.addNotification(null, E('p', _('Delete operation failed: %s').format(error.message)));
});
}
})
}, _('Delete Selected')),
E('div', { 'class': 'selection-info' }, _('Selected: 0 files'))
])
]);
container.appendChild(selectionControls);
// 更新所有复选框状态
function updateAllCheckboxes(checked) {
if (checked === true || checked === false) {
// 设置所有复选框为指定状态
document.querySelectorAll('.file-checkbox, .grid-checkbox').forEach(checkbox => {
checkbox.checked = checked;
});
document.querySelectorAll('.grid-item').forEach(item => {
if (checked) {
item.classList.add('selected');
} else {
item.classList.remove('selected');
}
});
} else {
// 根据selectedFiles更新复选框状态
document.querySelectorAll('.file-checkbox, .grid-checkbox').forEach(checkbox => {
checkbox.checked = selectedFiles.has(checkbox.value);
});
document.querySelectorAll('.grid-item').forEach(item => {
var checkbox = item.querySelector('.grid-checkbox');
if (checkbox && selectedFiles.has(checkbox.value)) {
item.classList.add('selected');
} else {
item.classList.remove('selected');
}
});
}
}
// 更新选择控制面板
function updateSelectionControls() {
var info = selectionControls.querySelector('.selection-info');
var selectedCount = selectedFiles.size;
var totalCount = allFileNames.length;
info.textContent = _('Selected: %d of %d files').format(selectedCount, totalCount);
// 如果选择了所有文件,显示特殊提示
if (selectedCount === totalCount && totalCount > 0) {
info.innerHTML = _('Selected: <strong>All %d files</strong>').format(totalCount);
}
}
// 列表视图
var listView = E('table', {
'class': 'table cbi-section-table',
'style': 'display: table;'
}, [
E('tr', { 'class': 'tr table-titles' }, [
E('th', { 'class': 'th' }, [ _('Select') ]),
E('th', { 'class': 'th' }, [ _('Preview') ]),
E('th', { 'class': 'th' }, [ _('Filename') ]),
E('th', { 'class': 'th' }, [ _('Modified date') ]),
E('th', { 'class': 'th' }, [ _('Size') ])
])
]);
// 网格视图
var gridView = E('div', {
'class': 'grid-view',
'style': 'display: none;'
});
files.forEach(L.bind(function(file) {
var previewUrl = '/luci-static/resources/background/' + file.name;
var timestamp = new Date().getTime();
// 列表视图行
var row = E('tr', { 'class': 'tr' });
// 选择列
var selectCell = E('td', { 'class': 'td' });
var checkbox = E('input', {
'type': 'checkbox',
'class': 'file-checkbox',
'value': file.name,
'change': ui.createHandlerFn(this, function(ev) {
if (ev.target.checked) {
selectedFiles.add(file.name);
} else {
selectedFiles.delete(file.name);
}
updateSelectionControls();
})
});
selectCell.appendChild(checkbox);
row.appendChild(selectCell);
// 预览列
var previewCell = E('td', { 'class': 'td' });
var previewContainer = E('div', { 'style': 'display: flex;justify-content: center;' });
var smallPreview = E('img', {
'class': 'file-list-preview',
'src': previewUrl + '?t=' + timestamp,
'alt': file.name,
'title': _('Click to view larger preview'),
'style': 'cursor: pointer; width: 80px; height: 60px; object-fit: cover;',
'onerror': "this.style.display='none'; this.nextElementSibling.style.display='block';",
'click': ui.createHandlerFn(this, function() {
showImagePreview(file, previewUrl);
})
});
var errorDiv = E('div', {
'class': 'image-error',
'style': 'display: none;'
}, _('Unknow file'));
previewContainer.appendChild(smallPreview);
previewContainer.appendChild(errorDiv);
previewCell.appendChild(previewContainer);
row.appendChild(previewCell);
// 文件名列
row.appendChild(E('td', { 'class': 'td' }, [ file.name ]));
// 修改日期列
row.appendChild(E('td', { 'class': 'td' }, [
new Date(file.mtime * 1000).toLocaleString()
]));
// 文件大小列
row.appendChild(E('td', { 'class': 'td' }, [
String.format('%1024.2mB', file.size)
]));
listView.appendChild(row);
// 网格视图项
var gridItem = E('div', {
'class': 'grid-item',
'click': ui.createHandlerFn(this, function(ev) {
// 如果点击的不是复选框,则显示预览
if (!ev.target.matches('input[type="checkbox"]')) {
showImagePreview(file, previewUrl);
}
})
});
var gridCheckbox = E('input', {
'type': 'checkbox',
'class': 'grid-checkbox',
'value': file.name,
'change': ui.createHandlerFn(this, function(ev) {
ev.stopPropagation();
if (ev.target.checked) {
selectedFiles.add(file.name);
gridItem.classList.add('selected');
} else {
selectedFiles.delete(file.name);
gridItem.classList.remove('selected');
}
updateSelectionControls();
})
});
var gridImageContainer = E('div', { 'style': 'position: relative;' });
var gridPreview = E('img', {
'class': 'grid-preview',
'src': previewUrl + '?t=' + timestamp,
'alt': file.name,
'onerror': "this.style.display='none'; this.nextElementSibling.style.display='flex';"
});
var gridError = E('div', {
'class': 'grid-image-error',
'style': 'display: none;'
}, _('Unknow file'));
gridImageContainer.appendChild(gridPreview);
gridImageContainer.appendChild(gridError);
var gridName = E('div', { 'class': 'grid-name' }, file.name);
var gridInfo = E('div', { 'class': 'grid-info' }, [
String.format('%1024.2mB', file.size),
E('br'),
new Date(file.mtime * 1000).toLocaleDateString()
]);
gridItem.appendChild(gridCheckbox);
gridItem.appendChild(gridImageContainer);
gridItem.appendChild(gridName);
gridItem.appendChild(gridInfo);
gridView.appendChild(gridItem);
}, this));
container.appendChild(listView);
container.appendChild(gridView);
function showImagePreview(file, previewUrl) {
var modal = E('div', { 'class': 'preview-modal' }, [
E('button', {
'class': 'preview-modal-close',
'click': function() {
modal.remove();
}
}, '×'),
E('div', { 'class': 'preview-modal-content' }, [
E('img', {
'class': 'preview-modal-image',
'src': previewUrl + '?t=' + new Date().getTime(),
'alt': file.name,
'onerror': "this.style.display='none'; this.parentNode.style.display='block';this.parentNode.removeChild(this);"
}),
E('div', { 'class': 'preview-modal-info' }, [
E('div', { 'style': 'font-weight: bold;' }, file.name),
E('div', { 'style': 'font-size: 14px; color: #666;' },
String.format(_('Size: %1024.2mB, Modified: %s'),
file.size, new Date(file.mtime * 1000).toLocaleString()))
])
])
]);
// 点击模态框背景关闭
modal.addEventListener('click', function(e) {
if (e.target === modal) {
modal.remove();
}
});
// ESC键关闭
document.addEventListener('keydown', function closeOnEsc(e) {
if (e.key === 'Escape') {
modal.remove();
document.removeEventListener('keydown', closeOnEsc);
}
});
document.body.appendChild(modal);
}
} else {
container.appendChild(E('h3', _('Background file list')));
container.appendChild(E('p', E('em', _('No files found.'))));
}
return container;
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});

View File

@@ -0,0 +1 @@
zh_Hans

View File

@@ -0,0 +1,313 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: dingpengyu <jerrykuku@gmail.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh_CN\n"
"X-Generator: Poedit 2.3.1\n"
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8\n"
msgid "KuCat Theme Config"
msgstr "KuCat主题设置"
msgid "KuCat Config"
msgstr "KuCat设置"
msgid "You can set KuCat theme font size, color scheme, shortcut tools, and manage login and desktop background images here."
msgstr "您可以在这里设置KuCat主题字体大小,颜色方案,快捷工具,并管理登录背景和桌面背景图片."
msgid "Local wallpapers need to be uploaded by oneself, and those that are automatically downloaded will only be downloaded on the first visit every day, reducing frequent remote access and making usage smoother."
msgstr "本地壁纸需要自己上传,自动下载的只有每天第一次访问才会自动下载,减少频繁远程访问,使用更流畅。"
msgid "Wallpaper Source"
msgstr "壁纸来源"
msgid "Local wallpaper"
msgstr "本地壁纸库"
msgid "Auto download Bing wallpaper"
msgstr "自动下载Bing每日壁纸"
msgid "Auto download unsplash wallpaper"
msgstr "自动下载unsplash壁纸"
msgid "Auto download Iciba wallpaper"
msgstr "自动下载词霸每日壁纸"
msgid "Auto download Bird 4K wallpaper"
msgstr "自动下载小鸟4K高清壁纸(每日4千张随机选)"
msgid "Set font size"
msgstr "字体大小"
msgid "Small font"
msgstr "小号字体"
msgid "Normal font"
msgstr "中号字体"
msgid "Large font"
msgstr "大号字体"
msgid "Wallpaper synchronization"
msgstr "壁纸同步"
msgid "Is the login wallpaper consistent with the desktop wallpaper? If selected, it means that the desktop wallpaper and login wallpaper are the same image."
msgstr "登录壁纸与桌面壁纸是否一致?选择则表示桌面壁纸和登录壁纸是同一张图片。"
msgid "Refreshing mode"
msgstr "清爽模式"
msgid "Enable Daily Word"
msgstr "启用每日一言"
msgid "Care mode (large font)"
msgstr "关怀模式(大字体)"
msgid "Expand navigation bar"
msgstr "展开导航栏"
msgid "Expand or shrink the five quick navigation bars"
msgstr "展开或收缩五个快捷导航栏"
msgid "Services Ssrkey settings"
msgstr "服务相关快捷键设置"
msgid "System Userkey settings"
msgstr "系统相关快捷键设置"
msgid "Status Homekey settings"
msgstr "状态相关快捷键设置"
msgid "KuCat Theme Color Schemes List"
msgstr "KuCat主题颜色配置列表"
msgid "Pre set 6 color schemes, enable wallpaper as desktop wallpaper, theme RGB values such as 255,0,0 (representing red), dark mode with wallpaper blur for better effect. No matter how many schemes are enabled, "
"only the first scheme should be used"
msgstr "预置6种颜色方案启用壁纸是桌面壁纸主题RGB值如255,0,0(表示红色),暗色模式配合壁纸模糊开启效果更好。"
"不管启用几个方案只用最前面方案"
msgid "Color Schemes"
msgstr "颜色方案"
msgid "Remarks"
msgstr "备注"
msgid "Enable this color scheme"
msgstr "启用此配色"
msgid "Light dark mode"
msgstr "亮暗模式"
msgid "Main color(RGB)"
msgstr "主题色值(RGB)"
msgid "RGB values, such as red being '255,0,0', or using a preset scheme (which includes fence color values)"
msgstr "RGB值如红色为'255,0,0',或使用预设方案(预设方案包含栅栏色值)"
msgid "Light"
msgstr "亮色"
msgid "Auto"
msgstr "自动"
msgid "Dark"
msgstr "暗色"
msgid "RoyalBlue"
msgstr "皇家高端蓝"
msgid "MediumSeaGreen"
msgstr "中海清爽绿"
msgid "SandyBrown"
msgstr "桑迪布朗黄"
msgid "Black tea eye protection gray"
msgstr "黑茶护眼灰"
msgid "TomatoRed"
msgstr "番茄别样红"
msgid "Cool night time(gray and dark)"
msgstr "炫酷黑夜时空(灰度暗)"
msgid "Cool Ocean Heart (transparent and bright)"
msgstr "炫酷海洋之心(透明亮)"
msgid "Use wallpaper"
msgstr "使用壁纸"
msgid "Use desktop wallpaper"
msgstr "使用桌面壁纸"
msgid "Wallpaper transparency"
msgstr "壁纸透明度"
msgid "0: Transparent, 1: Opaque (Suggestion: Mild Transparent 0.9)"
msgstr "0透明1不透明建议轻度透明0.9"
msgid "Wallpaper blur radius"
msgstr "壁纸模糊半径"
msgid "The larger the value, the more blurry it becomes (0: not blurry, suggestion: 0, blurry values may cause slower reflection)"
msgstr "值越大越模糊0不模糊建议0模糊值可能导致反映变慢"
msgid "Fence Color(RGB)"
msgstr "栅栏色值(RGB)"
msgid "Fence color transparency"
msgstr "栅栏颜色透明度"
msgid "0: Transparent, 1: Opaque (Recommendation: Heavy Transparency 0.1)"
msgstr "0透明1不透明建议重度透明0.1"
msgid "RGB values, such as '255,0,0' for red, suggest dark values"
msgstr "RGB值例如红色为'255,0,0',建议深色值"
msgid "Upload..."
msgstr "上传..."
msgid ""
"You can upload files in formats such as gif/jpg/png/webp to create your favorite wallpapers"
msgstr ""
"你可以上传gif/jpg/png/webp等格式文件以创建自己喜欢壁纸。"
msgid "Background file list"
msgstr "背景文件列表"
msgid "Filename"
msgstr "文件名"
msgid "Modified date"
msgstr "修改时间"
msgid "Size"
msgstr "大小"
msgid "Action"
msgstr "操作"
msgid "Delete"
msgstr "删除"
msgid "Files will be uploaded to <code>%s</code>."
msgstr "文件将被上传至<code>%s</code>。"
msgid "Desktop Wallpaper Upload"
msgstr "桌面壁纸上传"
msgid "Login Wallpaper Upload"
msgstr "登录壁纸上传"
msgid "Kucat Theme Desktop Background Wallpaper Upload and Management."
msgstr "kucat主题桌面背景壁纸文件上传和管理。"
msgid "Kucat theme Login background wallpaper upload and management."
msgstr "kucat主题登录背景壁纸上传和管理"
msgid ""
"File uploaded successfully!"
msgstr ""
"文件上传成功!"
msgid "File deleted successfully!"
msgstr "文件删除成功!"
msgid "Failed to upload file: %s"
msgstr "文件上传失败:%s"
msgid "No file selected or upload failed"
msgstr "未选择文件或上传失败"
msgid "Available space: %1024.2mB"
msgstr "可用空间: %1024.2mB"
msgid "No files found."
msgstr "没有文件"
msgid "Basic Settings"
msgstr "基本设置"
msgid "List View"
msgstr "列表显示"
msgid "Grid View"
msgstr "网格显示"
msgid "Select All"
msgstr "全选"
msgid "Invert Selection"
msgstr "反选"
msgid "Select None"
msgstr "取消"
msgid "Delete Selected"
msgstr "删除所选"
msgid "Size: %1024.2mB, Modified: %s"
msgstr "大小: %1024.2mB, 时间: %s"
msgid "Selected: %d of %d files"
msgstr "选择: %d 个 共 %d 个文件"
msgid "Selected: <strong>All %d files</strong>"
msgstr "选择<strong>全部 %d 个文件</strong>"
msgid "Unknow file"
msgstr "未知图片"
msgid "Click to view larger preview"
msgstr "点击预览大图"
msgid "Preview"
msgstr "预览"
msgid "Select"
msgstr "选择"
msgid "Selected: 0 files"
msgstr "选择: 0 个文件"
msgid "Delete operation failed: %s"
msgstr "删除失败: %s"
msgid "Some files failed to delete."
msgstr "一些文件删除失败"
msgid "Are you sure you want to delete %d selected files?"
msgstr "你确定删除选择的 %d 个文件?"
msgid "Please select files to delete."
msgstr "请选择要删除的文件"
msgid ""
msgstr ""
msgid ""
msgstr ""
msgid ""
msgstr ""
msgid ""
msgstr ""

View File

@@ -0,0 +1,106 @@
config basic
option gouser 'admin'
option gohome 'overview'
option setbar '1'
option wizard '0'
option bgqs '1'
option gossr 'passwall'
option dayword '0'
option background '0'
option bklock '0'
option mode 'auto'
option bkuse '0'
option primary_rgbs_ts '0.1'
option primary_rgbbody '248,248,248'
option primary_opacity '0'
option primary_rgbs '28,66,188'
option primary_rgbm_ts '0.9'
option primary_rgbm '20,109,179'
option fontmode '0'
option font_d '0.92rem'
option font_z '0.875rem'
option font_x '0.8rem'
option tsoset '1'
option dhcp_domain '1'
config theme
option primary_rgbbody '33,45,60'
option primary_rgbs '76,157,69'
option remarks '隔栏经典蓝色'
option primary_opacity '0'
option primary_rgbm_ts '0.8'
option bkuse '0'
option primary_rgbs_ts '0.1'
option mode 'auto'
option use '1'
option primary_rgbm 'blue'
config theme
option primary_rgbbody '33,45,60'
option primary_rgbs '76,157,69'
option primary_rgbm_ts '0.9'
option primary_opacity '0'
option bkuse '0'
option remarks '无隔栏清爽绿'
option primary_rgbs_ts '0'
option mode 'dark'
option use '0'
option primary_rgbm 'green'
config theme
option primary_rgb_ts '0.3'
option primary_rgbm '26,131,97'
option primary_rgbs '76 157 69'
option bkuse '1'
option remarks '无隔栏绿'
option primary_rgbs_ts '0'
option primary_opacity '20'
option primary_rgbm_ts '0.7'
option mode 'dark'
option use '0'
config theme
option primary_rgbm '66,139,139'
option primary_rgbs 'rgb(55,103,128)'
option mode 'light'
option primary_opacity '0'
option primary_rgbs_ts '0.1'
option primary_rgbm_ts '1'
option remarks '隔栏无背景蓝'
option bkuse '0'
option use '0'
config theme
option primary_rgbm '50 67 77'
option primary_rgbs 'rgb(64, 68, 70)'
option primary_opacity '0'
option primary_rgbs_ts '0'
option remarks '灰无隔栏无背景'
option bkuse '0'
option primary_rgbm_ts '0.9'
option mode 'auto'
option use '0'
config theme
option bkuse '1'
option mode 'dark'
option primary_rgbm '50 66 77'
option primary_rgbs '55 80 90'
option remarks '炫灰隔栏模糊'
option primary_rgbs_ts '0'
option use '0'
option primary_rgbm_ts '0.5'
option primary_opacity '4'
config theme
option mode 'light'
option primary_rgbm '74,161,133'
option primary_rgbs '76 157 69'
option primary_rgbs_ts '0.2'
option remarks '炫绿隔栏模糊'
option primary_rgbm_ts '0.7'
option bkuse '1'
option primary_opacity '4'
option use '0'

View File

@@ -0,0 +1,247 @@
#!/bin/sh /etc/rc.common
# Copyright 2023-2024 sirpdboy team <herboy2008@gmail.com>
START=99
STOP=15
EXTRA_COMMANDS='reset'
EXTRA_HELP=" reset Reset to default settings"
reset() {
rm -rf /etc/config/kucat
cat <<EOF >/etc/config/kucat
config basic
option gouser 'admin'
option gohome 'overview'
option setbar '1'
option wizard '0'
option bgqs '1'
option gossr 'passwall'
option dayword '0'
option background '0'
option bklock '1'
option mode 'dark'
option bkuse '1'
option primary_rgbs_ts '0.1'
option primary_rgbbody '248,248,248'
option primary_opacity '0'
option primary_rgbs '28,66,188'
option primary_rgbm_ts '0.9'
option primary_rgbm '20,109,179'
option fontmode '0'
option font_d '1.1rem'
option font_z '0.92rem'
option font_x '0.875rem'
option tsoset '1'
option dhcp_domain '1'
config theme
option primary_rgbbody '33,45,60'
option primary_rgbs '76,157,69'
option remarks '隔栏经典蓝色'
option primary_opacity '0'
option primary_rgbm_ts '0.9'
option bkuse '1'
option primary_rgbs_ts '0.1'
option mode 'auto'
option use '1'
option primary_rgbm 'blue'
config theme
option primary_rgbbody '33,45,60'
option primary_rgbs '76,157,69'
option primary_rgbm_ts '0.95'
option primary_opacity '0'
option bkuse '1'
option remarks '无隔栏清爽绿'
option primary_rgbs_ts '0'
option mode 'dark'
option use '1'
option primary_rgbm 'green'
config theme
option primary_rgb_ts '0.3'
option primary_rgbm '26,131,97'
option primary_rgbs '76 157 69'
option bkuse '1'
option remarks '无隔栏绿'
option primary_rgbs_ts '0'
option primary_opacity '10'
option primary_rgbm_ts '0.5'
option mode 'dark'
option use '0'
config theme
option primary_rgbm '66,139,139'
option primary_rgbs 'rgb(55,103,128)'
option mode 'light'
option primary_opacity '0'
option primary_rgbs_ts '0.1'
option primary_rgbm_ts '1'
option remarks '隔栏无背景蓝'
option bkuse '0'
option use '0'
config theme
option primary_rgbm '50 67 77'
option primary_rgbs 'rgb(64, 68, 70)'
option primary_opacity '0'
option primary_rgbs_ts '0'
option remarks '灰无隔栏无背景'
option bkuse '0'
option primary_rgbm_ts '0.95'
option mode 'auto'
option use '0'
config theme
option bkuse '1'
option mode 'dark'
option primary_rgbm '50 66 77'
option primary_rgbs '55 80 90'
option remarks '炫灰隔栏模糊'
option primary_rgbs_ts '0'
option use '0'
option primary_rgbm_ts '0.5'
option primary_opacity '10'
config theme
option mode 'light'
option primary_rgbm '74,161,133'
option primary_rgbs '76 157 69'
option primary_rgbs_ts '0.2'
option remarks '炫绿隔栏模糊'
option primary_rgbm_ts '0.5'
option bkuse '1'
option primary_opacity '10'
option use '0'
EOF
}
kucatset(){
sum=$(grep -c 'theme' /etc/config/kucat)
useset=0
for i in $(seq 0 $((sum-1)))
do
use=$(uci -q get kucat.@theme[$i].use )
if [ "x$use" == "x1" ]; then
mode=`uci -q get kucat.@theme[$i].mode`
primary_rgbm=`uci -q get kucat.@theme[$i].primary_rgbm`
uci set kucat.@basic[0].mode=${mode}
case "$primary_rgbm" in
blue)
uci set kucat.@basic[0].primary_rgbm='20,109,179'
uci set kucat.@basic[0].bkuse=`uci -q get kucat.@theme[$i].bkuse`
uci set kucat.@basic[0].primary_rgbm_ts='0.9'
uci set kucat.@basic[0].primary_opacity='0'
uci set kucat.@basic[0].primary_rgbs_ts=`uci -q get kucat.@theme[$i].primary_rgbs_ts`
uci set kucat.@basic[0].primary_rgbs='28,66,188'
[ $mode = dark ] && uci set kucat.@basic[0].primary_rgbbody='33,45,60' || uci set kucat.@basic[0].primary_rgbbody='248,248,248'
;;
green)
uci set kucat.@basic[0].bkuse=`uci -q get kucat.@theme[$i].bkuse`
uci set kucat.@basic[0].primary_rgbm_ts='0.9'
uci set kucat.@basic[0].primary_opacity='0'
uci set kucat.@basic[0].primary_rgbm='26,131,97'
uci set kucat.@basic[0].primary_rgbs_ts=`uci -q get kucat.@theme[$i].primary_rgbs_ts`
uci set kucat.@basic[0].primary_rgbs='76,157,69'
[ $mode = dark ] && uci set kucat.@basic[0].primary_rgbbody='33,45,60' || uci set kucat.@basic[0].primary_rgbbody='248,248,248'
;;
orange)
uci set kucat.@basic[0].bkuse=`uci -q get kucat.@theme[$i].bkuse`
uci set kucat.@basic[0].primary_rgbm_ts='0.9'
uci set kucat.@basic[0].primary_opacity='0'
uci set kucat.@basic[0].primary_rgbm='163,71,6'
uci set kucat.@basic[0].primary_rgbs_ts=`uci -q get kucat.@theme[$i].primary_rgbs_ts`
uci set kucat.@basic[0].primary_rgbs='142,93,8'
;;
red)
uci set kucat.@basic[0].bkuse=`uci -q get kucat.@theme[$i].bkuse`
uci set kucat.@basic[0].primary_rgbm_ts='0.9'
uci set kucat.@basic[0].primary_opacity='0'
uci set kucat.@basic[0].primary_rgbm='143,32,13'
uci set kucat.@basic[0].primary_rgbs_ts=`uci -q get kucat.@theme[$i].primary_rgbs_ts`
uci set kucat.@basic[0].primary_rgbs='243,119,72'
;;
black)
uci set kucat.@basic[0].bkuse=`uci -q get kucat.@theme[$i].bkuse`
uci set kucat.@basic[0].primary_rgbm_ts='0.95'
uci set kucat.@basic[0].primary_opacity='0'
uci set kucat.@basic[0].primary_rgbm='25,25,26'
uci set kucat.@basic[0].primary_rgbs_ts=`uci -q get kucat.@theme[$i].primary_rgbs_ts`
uci set kucat.@basic[0].primary_rgbs='55,80,90'
;;
gray)
uci set kucat.@basic[0].bkuse=`uci -q get kucat.@theme[$i].bkuse`
uci set kucat.@basic[0].primary_rgbm_ts='0.5'
uci set kucat.@basic[0].primary_opacity='10'
uci set kucat.@basic[0].primary_rgbm='50,67,77'
uci set kucat.@basic[0].primary_rgbs_ts=`uci -q get kucat.@theme[$i].primary_rgbs_ts`
uci set kucat.@basic[0].primary_rgbs='64,68,70'
;;
bluets)
uci set kucat.@basic[0].bkuse=`uci -q get kucat.@theme[$i].bkuse`
uci set kucat.@basic[0].primary_rgbm_ts='0.5'
uci set kucat.@basic[0].primary_opacity='10'
uci set kucat.@basic[0].primary_rgbm='66,139,139'
uci set kucat.@basic[0].primary_rgbs_ts=`uci -q get kucat.@theme[$i].primary_rgbs_ts`
uci set kucat.@basic[0].primary_rgbs='55,103,128'
;;
*)
uci set kucat.@basic[0].bkuse=`uci -q get kucat.@theme[$i].bkuse`
uci set kucat.@basic[0].primary_rgbm_ts=`uci -q get kucat.@theme[$i].primary_rgbm_ts`
uci set kucat.@basic[0].primary_opacity=`uci -q get kucat.@theme[$i].primary_opacity`
uci set kucat.@basic[0].primary_rgbm=`echo $primary_rgbm | sed 's#(##; s#)##g; s#rgb##g; s#\,\ #\,#g;s#\ #\,#g;'`
uci set kucat.@basic[0].primary_rgbs=`uci -q get kucat.@theme[$i].primary_rgbs | sed 's#(##; s#)##g; s#rgb##g; s#\,\ #\,#g;s#\ #\,#g;'`
uci set kucat.@basic[0].primary_rgbs_ts=`uci -q get kucat.@theme[$i].primary_rgbs_ts`
;;
esac
useset=1
break
fi
done
if [ "x$useset" == "x0" ] ; then
uci set kucat.@basic[0].mode='auto'
uci set kucat.@basic[0].primary_rgbm='74,161,133'
uci set kucat.@basic[0].primary_rgbs_ts='0'
uci set kucat.@basic[0].primary_rgbs='225,112,88'
uci set kucat.@basic[0].bkuse='1'
uci set kucat.@basic[0].primary_rgbm_ts='0.5'
uci set kucat.@basic[0].primary_opacity='10'
fi
[ $mode == dark ] && uci set kucat.@basic[0].primary_rgbbody='33,45,60' || uci set kucat.@basic[0].primary_rgbbody='244,245,247'
case "x$(uci -q get kucat.@basic[0].fontmode)" in
x1)
uci set kucat.@basic[0].font_d='1.1rem'
uci set kucat.@basic[0].font_z='0.92rem'
uci set kucat.@basic[0].font_x='0.875rem'
;;
x2)
uci set kucat.@basic[0].font_d='1.3rem'
uci set kucat.@basic[0].font_z='1.1rem'
uci set kucat.@basic[0].font_x='0.92rem'
;;
*)
uci set kucat.@basic[0].font_d='1rem'
uci set kucat.@basic[0].font_z='0.875rem'
uci set kucat.@basic[0].font_x='0.82rem'
;;
esac
uci commit kucat
}
start() {
[ "x$(uci -q get kucat.@basic[0].mode)" = "x" ] && /etc/config/kucat reset
kucatset
rm -rf /tmp/luci-*
}
stop() {
rm -rf /tmp/luci*
}

View File

@@ -0,0 +1,228 @@
#!/bin/sh
. /lib/functions.sh
. /usr/share/libubox/jshn.sh
readonly bg_path="/www/luci-static/resources/background"
readonly bg_pathd="/www/luci-static/kucat/background"
readonly tmp_path="/tmp/kucat_login_bg.tmp"
readonly tmp_pathd='/tmp/kucat_desktop_bg.tmp'
# 创建目录函数
create_dirs() {
mkdir -p "$bg_path" "$bg_pathd"
chmod 755 "$bg_path" "$bg_pathd"
}
check_filename() {
local filename="$1"
[ -z "$filename" ] && return 1
if echo "$filename" | grep -q '\.\.' || echo "$filename" | grep -q '/'; then
return 1
fi
local extension=$(echo "$filename" | awk -F. '{print tolower($NF)}')
case "$extension" in
jpg|jpeg|png|gif|webp)
return 0
;;
*)
return 1
;;
esac
}
# 检查文本文件
check_text_file() {
local filename="$1"
[ -z "$filename" ] && return 1
if echo "$filename" | grep -q '\.\.' || echo "$filename" | grep -q '/'; then
return 1
fi
local extension=$(echo "$filename" | awk -F. '{print tolower($NF)}')
case "$extension" in
md|txt|log|conf|config|sh|json|js|css|html|htm|xmll)
return 0
;;
*)
# 对于没有扩展名的文件,检查文件类型
if [ -f "$bg_pathd/$filename" ]; then
local file_type=$(file -b "$bg_pathd/$filename" 2>/dev/null)
case "$file_type" in
*"text"*|*"ASCII"*)
return 0
;;
*)
return 1
;;
esac
fi
return 1
;;
esac
}
case "$1" in
"list")
json_init
json_add_object "space"
json_close_object
json_add_object "del"
json_add_string "filename" "filename"
json_close_object
json_add_object "rename"
json_add_string "newname" "filename"
json_close_object
json_add_object "deld"
json_add_string "filename" "filename"
json_close_object
json_add_object "renamed"
json_add_string "newname" "filename"
json_close_object
json_add_object "read_file"
json_add_string "filename" "filename"
json_close_object
json_dump
json_cleanup
;;
"call")
create_dirs
case "$2" in
"space")
json_init
json_add_int "space" "$(df /www 2>/dev/null | awk 'NR==2 {print $4}')"
json_dump
json_cleanup
;;
"del")
read -r input
json_load "$input"
json_get_var filename "filename"
json_cleanup
if ! check_filename "$filename"; then
echo '{ "result": 255 }'
exit 0
fi
if rm -f "$bg_path/$filename"; then
echo '{ "result": 0 }'
else
echo '{ "result": 1 }'
fi
;;
"rename")
read -r input
json_load "$input"
json_get_var newname "newname"
json_cleanup
if ! check_filename "$newname"; then
echo '{ "result": 255 }'
exit 0
fi
# 确保临时文件存在
if [ ! -f "$tmp_path" ]; then
echo '{ "result": 1, "error": "Temporary file not found" }'
exit 0
fi
if mv "$tmp_path" "$bg_path/$newname" 2>/dev/null; then
chmod 0644 "$bg_path/$newname"
echo '{ "result": 0 }'
else
echo '{ "result": 1, "error": "Failed to move file" }'
fi
;;
"deld")
read -r input
json_load "$input"
json_get_var filename "filename"
json_cleanup
if ! check_filename "$filename"; then
echo '{ "result": 255 }'
exit 0
fi
if rm -f "$bg_pathd/$filename"; then
echo '{ "result": 0 }'
else
echo '{ "result": 1 }'
fi
;;
"renamed")
read -r input
json_load "$input"
json_get_var newname "newname"
json_cleanup
if ! check_filename "$newname"; then
echo '{ "result": 255 }'
exit 0
fi
# 确保临时文件存在
if [ ! -f "$tmp_pathd" ]; then
echo '{ "result": 1, "error": "Temporary file not found" }'
exit 0
fi
if mv "$tmp_pathd" "$bg_pathd/$newname" 2>/dev/null; then
chmod 0644 "$bg_pathd/$newname"
echo '{ "result": 0 }'
else
echo '{ "result": 1, "error": "Failed to move file" }'
fi
;;
"read_file")
read -r input
json_load "$input"
json_get_var filename "filename"
json_cleanup
# 检查文件名安全性
if [ -z "$filename" ] || echo "$filename" | grep -q '\.\.' || echo "$filename" | grep -q '/'; then
echo '{ "result": 255, "error": "Invalid filename" }'
exit 0
fi
local file_path="$bg_pathd/$filename"
# 检查文件是否存在
if [ ! -f "$file_path" ]; then
echo '{ "result": 1, "error": "File not found" }'
exit 0
fi
# 检查文件大小限制为100KB
local file_size=$(stat -c%s "$file_path" 2>/dev/null || stat -f%z "$file_path" 2>/dev/null)
if [ "$file_size" -gt 102400 ]; then
echo '{ "result": 1, "error": "File too large" }'
exit 0
fi
# 检查文件类型,只允许文本文件
if ! check_text_file "$filename"; then
echo '{ "result": 1, "error": "Not a text file" }'
exit 0
fi
# 读取文件内容
if content=$(cat "$file_path" 2>/dev/null); then
# 对内容进行JSON转义
content=$(echo "$content" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | sed 's/\//\\\//g' | sed 's/\$/\\$/g')
json_init
json_add_string "content" "$content"
json_add_string "filename" "$filename"
json_add_int "size" "$file_size"
json_add_int "result" 0
json_dump
json_cleanup
else
echo '{ "result": 1, "error": "Failed to read file" }'
fi
;;
esac
;;
esac

View File

@@ -0,0 +1,44 @@
{
"admin/system/kucat-config": {
"title": "KuCat Config",
"order": 90,
"action": {
"type": "firstchild"
}
},
"admin/system/kucat-config/config": {
"title": "Basic Settings",
"order": 10,
"action": {
"type": "view",
"path": "kucat-config/config"
}
},
"admin/system/kucat-config/schemes": {
"title": "Color Schemes",
"order": 20,
"action": {
"type": "view",
"path": "kucat-config/schemes"
}
},
"admin/system/kucat-config/updesktop": {
"title": "Desktop Wallpaper Upload",
"order": 30,
"action": {
"type": "view",
"path": "kucat-config/updesktop"
}
},
"admin/system/kucat-config/uplogin": {
"title": "Login Wallpaper Upload",
"order": 40,
"action": {
"type": "view",
"path": "kucat-config/uplogin"
}
}
}

View File

@@ -0,0 +1,23 @@
{
"luci-app-kucat-config": {
"description": "Grant UCI access for luci-app-kucat-config",
"read": {
"file": {
"/www/luci-static/kucat/background/*": [ "list" ],
"/www/luci-static/resources/background/*": [ "list" ],
"/www/luci-static/kucat/img/*": [ "list" ],
"/etc/init.d/kucat": [ "exec" ]
},
"ubus": {
"luci.kucatconfig": [ "*" ]
},
"uci": [ "kucat" ]
},
"write": {
"file": {
"/tmp/kucat/*": [ "write" ]
},
"uci": [ "kucat" ]
}
}
}

View File

@@ -0,0 +1,4 @@
{
"config": "kucat",
"init": "kucat"
}

View File

@@ -0,0 +1,10 @@
# OpenWrt LuCI web UI address
VITE_OPENWRT_HOST=http://192.168.1.1
# Development server configuration
VITE_DEV_PORT=5173
# Specify which IP addresses the server should listen on
# Set to 0.0.0.0 to listen on all addresses, including LAN and public addresses
# Set to 127.0.0.1 to listen only on local machine
VITE_DEV_HOST=0.0.0.0

View File

@@ -0,0 +1,4 @@
{
"plugins": ["prettier-plugin-tailwindcss"]
}

View File

@@ -0,0 +1,21 @@
## v0.5.0_beta
## Changelog
### Features
- Updated theme icons (logo, spinning, refresh, refresh-off, setting) for better visuals.
- Optimized mobile display for ifacebox on Network > Interfaces page.
- Optimized modal scrolling by setting the scroll area to the modal overlay
- Improved table description row display on the Packages page.
### Fixes
- Resolved clipping issues for dropdown options by modal/header and fixed multi-select dropdown display issues.
- Corrected alert popup positioning and resolved text content overflow.
- Fixed modal content clipping.
- Fixed text and icon alignment in ifacebage.
### TODO
- Aim for optimal compatibility with OpenWrt 24.10.

View File

@@ -0,0 +1,15 @@
## v0.5.5_beta
## What's Changed
- Adapted **luci-mod-dashboard** for dark mode, PC, and mobile displays
- Optimized page scroll performance on low-performance devices
- Improved mobile display for **Network → Interfaces**
- Enhanced mobile layout for pages like **Software** and **SQM QoS** to fix button/text overflow issues
- Fixed horizontal scrollbar display bug
- Fixed overflow issue in **Network → Wireless** table
- Fixed ipk theme upgrade issue causing loss of **Aurora** option
---
🇨🇳 🎉 国庆假期最后一版,祝大家假期愉快,节后再见!

View File

@@ -0,0 +1,23 @@
## v0.6.0_beta
**Highly recommended to upgrade to the latest version**
## What's Changed
#### Features
- Support toggling Overview homepage cards (compatible with openwrt 24.10-SNAPSHOT)
- Added dotted underline indicator for Tooltip elements
- Improved visual styling for `a`, `var`, `ins`, and `del` tags
- UCI change modal now supports legend display
- Optimized mobile table cell layout (System → Software, Network → Interfaces/Wireless/Firewall, Status → Overview)
- Enhanced sorting indicators for sortable table headers
- Added fallback icon for the theme
#### Fixes
- Fixed missing first column in Network → Firewall (Port Forwards, Traffic Rules, NAT Rules)
- Fixed Tooltip being clipped when clicked or hovered
- Fixed display issues in luci-mod-dashboard
- Fixed dark mode chart background in luci-app-statistics
- … and other minor fixes

View File

@@ -0,0 +1,28 @@
## What's Changed
#### New Features
- **Theme Mode Switcher**: Now you can choose between Auto (follows system), Light, or Dark mode
- **OpenWrt 24.10-SNAPSHOT Compatibility**: Added expand/collapse buttons to support the “Show/Hide Index Cards” feature on the OpenWrt 24.10-SNAPSHOT Overview page
#### Improvements & Fixes
- **Dropdown Menus**:
- Fixed checkbox selection behavior when dropdown items contain both a checkbox and a label (Previously, clicking the checkbox triggered incorrect dropdown selection logic)
- Checkbox and label elements are now properly aligned
- Better visual feedback for options you can't select (they appear dimmed)
- Fixed dropdown menus getting cut off on mobile screens in the bridge-vlan table
- Dropdown icons now adapt their color based on background
- **Forms & Inputs**:
- Improved spacing between labels and input fields on the System → Software page
- Radio buttons and checkboxes look more polished
- **Mobile Experience**:
- Better button sizes and layout on Diagnostics page
- Fixed modal content overflowing on small screens
- Login modal is now more compact
- **Visual Polish**:
- Unified scrollbar appearance throughout the interface
- Refined theme-adaptive colors for HTML tags (`a`, `label`, `val`, `pre`)
- **Small Fixes**:
- Fixed tooltips that were flickering when hovering
- Added spacing to footer to prevent content overlap

View File

@@ -0,0 +1,15 @@
## What's Changed
### New Features
- **Redesigned Desktop Navigation**: Hover-based menus, smoother animations, unified header style
- **Form Layout Enhancements**: Horizontal layout on desktop, vertical on mobile, improved spacing
- **Enhanced Dynamic List**: Clearer buttons, drag animations, mobile-friendly, copy support
- **UCI Changes**: Added numeric badge showing pending configuration changes
### Other Improvements
- Rebuilt dropdown logic and fixed placeholder/overflow issues
- Refined `ifacebadge` display and dark mode styling
- Corrected table border-radius rendering
- For remaining changes, see the [full changelog](https://github.com/eamonxg/luci-theme-aurora/compare/v0.7.0_beta...v0.8.0_beta)

Binary file not shown.

After

Width:  |  Height:  |  Size: 754 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

View File

@@ -0,0 +1,21 @@
{
"name": "luci-theme-aurora",
"description": "Modern OpenWrt LuCI theme built with Vite and TailwindCSS",
"type": "module",
"scripts": {
"dev": "vite",
"build": "npm run clean && vite build",
"clean": "node scripts/clean.js"
},
"devDependencies": {
"@tailwindcss/vite": "^4.1.11",
"@types/node": "^22.10.5",
"prettier": "^3.6.2",
"prettier-plugin-tailwindcss": "^0.6.14",
"tailwind-scrollbar": "^4.0.2",
"tailwindcss": "^4.1.11",
"terser": "^5.43.1",
"vite": "^7.1.1"
},
"packageManager": "pnpm@10.18.0+sha512.e804f889f1cecc40d572db084eec3e4881739f8dec69c0ff10d2d1beff9a4e309383ba27b5b750059d7f4c149535b6cd0d2cb1ed3aeb739239a4284a68f40cfa"
}

1219
luci-theme-aurora/.dev/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
<svg width="550" height="550" viewBox="0 0 550 550" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M253.695 12.8086C267.545 18.1454 278.508 26.7596 289 37C289.982 37.8894 289.982 37.8894 290.984 38.7969C303.486 50.3805 313.602 69.468 317.625 85.9375C317.865 86.8953 318.105 87.853 318.352 88.8398C321.858 105.93 321.511 125.165 317 142C316.702 143.163 316.702 143.163 316.398 144.349C310.628 166.502 299.762 187.433 285 205C284.327 205.843 283.654 206.686 282.961 207.555C278.47 213.156 273.782 218.519 268.896 223.774C266.327 226.537 263.841 229.342 261.438 232.25C260.633 233.158 259.829 234.065 259 235C258.34 235 257.68 235 257 235C256.616 235.875 256.616 235.875 256.225 236.767C254.88 239.218 253.412 240.786 251.402 242.723C250.688 243.421 249.973 244.119 249.236 244.838C248.478 245.572 247.719 246.306 246.938 247.062C241.254 252.647 235.913 258.216 231 264.492C228.084 268.149 224.919 271.566 221.75 275.004C219.952 276.885 219.952 276.885 219 279C218.34 279 217.68 279 217 279C216.726 279.582 216.452 280.165 216.17 280.765C215.026 282.95 213.763 284.799 212.277 286.766C205.031 296.495 198.656 306.272 193 317C192.533 317.819 192.067 318.637 191.586 319.48C174.356 350.883 173.108 389.696 182.888 423.453C184.74 429.577 187.19 435.287 189.961 441.047C191.111 444.317 191.39 445.82 190 449C187.707 450.5 187.707 450.5 185 451C182.824 449.875 182.824 449.875 180.687 448C179.919 447.346 179.151 446.693 178.359 446.02C177.191 445.02 177.191 445.02 176 444C173.826 442.398 171.634 440.827 169.434 439.262C168.961 438.845 168.487 438.429 168 438C168 437.34 168 436.68 168 436C167.443 435.752 166.886 435.505 166.312 435.25C161.731 432.774 158.604 429.605 155.375 425.559C153.909 423.896 152.35 422.577 150.625 421.188C146.42 417.703 142.999 413.558 140 409C140 408.34 140 407.68 140 407C139.34 407 138.68 407 138 407C136.249 404.761 134.611 402.523 133 400.188C132.549 399.545 132.098 398.902 131.633 398.24C126 390.199 126 390.199 126 387C125.34 387 124.68 387 124 387C122.647 384.342 121.314 381.678 120 379C119.665 378.326 119.33 377.652 118.984 376.957C114.804 368.116 112.244 358.682 111 349C110.735 347.273 110.735 347.273 110.465 345.512C106.452 316.414 107.916 283.805 121 257C121.439 256.075 121.439 256.075 121.887 255.131C127.966 242.337 135.109 230.883 143.863 219.75C146.047 217.008 146.047 217.008 148 214C148.66 214 149.32 214 150 214C150.309 212.979 150.309 212.979 150.625 211.938C152.429 208.084 154.901 204.922 158 202C158.66 202 159.32 202 160 202C160.258 201.428 160.516 200.855 160.781 200.266C162.114 197.789 163.651 195.866 165.5 193.75C166.108 193.044 166.717 192.337 167.344 191.609C169 190 169 190 171 190C171.227 189.443 171.454 188.886 171.687 188.312C173.37 185.348 175.561 183.383 178 181C179.58 179.137 181.133 177.256 182.668 175.355C183.108 174.908 183.547 174.461 184 174C184.66 174 185.32 174 186 174C186.247 173.443 186.495 172.886 186.75 172.312C189.364 167.477 193.144 163.851 197 160C204.99 151.789 212.645 143.572 219 134C219.37 133.45 219.739 132.899 220.12 132.332C224.94 125.132 229.357 117.876 233 110C233.407 109.188 233.815 108.376 234.234 107.539C244.048 87.367 249.086 61.632 244.188 39.5625C243.939 38.3704 243.939 38.3704 243.686 37.1543C242.638 32.2952 241.295 27.6434 239.622 22.9622C238.73 20.1498 238.515 17.9094 239 15C242.749 10.3143 248.534 11.2602 253.695 12.8086Z" fill="#6DC3C5"/>
<path d="M379 127C395.79 140.378 403.626 163.462 406 184C406.279 188.609 406.334 193.196 406.312 197.813C406.308 199.036 406.304 200.26 406.3 201.521C406.186 210.698 405.417 219.136 403 228C402.733 228.999 402.466 229.998 402.191 231.027C396.077 251.272 384.609 269.828 370 285C368.742 286.431 367.492 287.868 366.25 289.312C363.615 292.339 360.838 295.162 358 298C356.559 299.622 355.122 301.247 353.688 302.875C350.306 306.662 346.824 310.088 342.922 313.332C341.026 314.978 339.263 316.721 337.493 318.5C335.869 320.132 334.238 321.755 332.606 323.378C329.18 326.786 325.757 330.197 322.335 333.61C320.866 335.074 319.396 336.537 317.925 337.999C315.784 340.129 313.645 342.261 311.508 344.395C310.86 345.038 310.212 345.681 309.544 346.343C305.702 350.184 302.053 354.139 298.51 358.258C296.49 360.589 294.366 362.818 292.25 365.062C276.497 382.391 261.694 404.04 256 427C255.653 428.146 255.306 429.292 254.949 430.473C253.52 436.248 253.617 442.081 253.625 448C253.627 449.199 253.628 450.398 253.63 451.633C253.759 461.039 254.95 469.104 258 478C258.289 478.938 258.578 479.877 258.875 480.844C262.139 490.753 267.295 500.041 272.262 509.184C272.626 509.856 272.991 510.529 273.366 511.222C274.062 512.498 274.764 513.771 275.474 515.04C277.271 518.323 278.696 521.24 279 525C277.438 527.375 277.438 527.375 275 529C270.511 529.098 267.863 527.569 264.57 524.586C261.547 521.532 258.762 518.29 256 515C254.732 513.515 254.732 513.515 253.438 512C251.084 509.103 248.839 506.153 246.621 503.152C244.297 500.066 241.858 497.073 239.418 494.078C238.002 492.003 237.426 490.455 237 488C236.34 488 235.68 488 235 488C234.625 487.287 234.25 486.574 233.863 485.84C230.394 479.325 230.394 479.325 226.313 473.188C224.128 470.177 222.58 467.357 221 464C220.188 462.417 219.375 460.833 218.563 459.25C209.592 441.201 203.713 421.162 203 401C202.959 399.844 202.918 398.687 202.875 397.496C202.619 383.189 205.305 370.388 210 357C210.409 355.797 210.817 354.595 211.238 353.355C216.61 338.73 225.158 325.259 234.574 312.898C236.047 311.056 236.047 311.056 237 309C237.66 309 238.32 309 239 309C239.238 308.453 239.477 307.907 239.723 307.344C241.317 304.418 243.3 302.055 245.438 299.5C248.653 295.659 248.653 295.659 251.66 291.656C252.102 291.11 252.544 290.563 253 290C253.66 290 254.32 290 255 290C255.393 289.132 255.393 289.132 255.793 288.246C258.892 282.479 264.184 277.403 269 273C269.66 273 270.32 273 271 273C271.66 271.68 272.32 270.36 273 269C273.66 269 274.32 269 275 269C275.236 268.437 275.473 267.874 275.716 267.294C277.298 264.467 279.229 262.564 281.543 260.297C282.452 259.398 283.362 258.5 284.299 257.574C285.261 256.632 286.224 255.691 287.188 254.75C293.371 248.682 299.376 242.591 305 236C307.447 233.291 309.91 230.598 312.375 227.906C313.241 226.947 314.108 225.988 315 225C315.536 224.408 316.071 223.816 316.623 223.207C321.082 218.206 325.118 212.975 329.063 207.563C329.941 206.37 329.941 206.37 330.838 205.154C337.701 195.792 343.637 186.303 349 176C349.37 175.301 349.74 174.603 350.121 173.883C351.126 171.941 352.068 169.978 353 168C353.692 166.585 353.692 166.585 354.398 165.141C358.159 157.009 360.399 149.049 362.176 140.284C363.301 134.982 364.568 131.236 368 127C372.15 124.379 374.688 124.844 379 127Z" fill="#6DB5CB"/>
<path d="M405 315C409.619 317.511 411.532 321.081 413 326C413.414 329.24 413.429 332.424 413.375 335.688C413.365 336.576 413.355 337.464 413.344 338.38C413.182 345.286 412.287 351.456 410 358C409.785 358.629 409.571 359.258 409.35 359.906C404.93 372.741 399.204 383.134 391 394C390.386 394.833 389.773 395.665 389.141 396.523C385.758 401.029 382.43 404.533 378 408C377.258 408.743 376.515 409.485 375.75 410.25C367.423 418.577 358.315 425.833 349 433C348.428 433.44 347.856 433.88 347.266 434.334C345.595 435.619 343.922 436.903 342.25 438.188C334.038 444.495 326.004 450.872 318.359 457.863C316.318 459.712 314.236 461.483 312.125 463.25C302.932 471.294 295.155 480.759 287.863 490.516C285.287 493.95 285.287 493.95 281.75 494.563C278 494 278 494 276.461 492.32C269.594 481.357 270.732 467.305 273.536 455.055C275.659 446.728 278.264 438.756 282 431C282.293 430.376 282.586 429.753 282.887 429.11C286.927 420.529 291.219 412.558 297 405C297.739 403.997 298.477 402.994 299.238 401.961C328.917 362.028 328.917 362.028 344 357C344.33 356.01 344.66 355.02 345 354C346.977 352.195 346.977 352.195 349.625 350.125C353.595 346.963 357.47 343.741 361.275 340.382C367.342 335.031 373.536 329.885 379.938 324.938C380.623 324.402 381.308 323.866 382.014 323.313C389.033 317.958 395.855 313.304 405 315Z" fill="#6FADCB"/>
<path d="M379 127C395.79 140.378 403.626 163.462 406 184C406.279 188.609 406.334 193.196 406.312 197.813C406.308 199.036 406.304 200.26 406.3 201.521C406.186 210.698 405.417 219.136 403 228C402.733 228.999 402.466 229.998 402.191 231.027C396.077 251.272 384.609 269.828 370 285C368.742 286.431 367.492 287.868 366.25 289.312C363.615 292.339 360.838 295.162 358 298C356.559 299.622 355.122 301.247 353.688 302.875C350.306 306.662 346.824 310.088 342.922 313.332C341.026 314.978 339.263 316.721 337.493 318.5C335.869 320.132 334.238 321.755 332.606 323.378C329.18 326.786 325.757 330.197 322.335 333.61C320.866 335.074 319.396 336.537 317.925 337.999C315.784 340.129 313.645 342.261 311.508 344.395C310.86 345.038 310.212 345.681 309.544 346.343C305.702 350.184 302.053 354.139 298.51 358.258C296.49 360.589 294.366 362.818 292.25 365.062C276.497 382.391 261.694 404.04 256 427C255.653 428.146 255.306 429.292 254.949 430.473C253.52 436.248 253.617 442.081 253.625 448C253.627 449.199 253.628 450.398 253.63 451.633C253.759 461.039 254.95 469.104 258 478C258.289 478.938 258.577 479.877 258.875 480.844C262.139 490.753 267.295 500.041 272.262 509.184C272.626 509.856 272.991 510.529 273.366 511.222C274.062 512.498 274.764 513.771 275.474 515.04C277.271 518.323 278.696 521.24 279 525C277.438 527.375 277.438 527.375 275 529C270.511 529.098 267.863 527.569 264.57 524.586C261.547 521.532 258.762 518.29 256 515C255.154 514.01 254.309 513.02 253.438 512C251.084 509.103 248.839 506.153 246.621 503.152C244.297 500.066 241.858 497.073 239.418 494.078C238.002 492.003 237.426 490.455 237 488C236.34 488 235.68 488 235 488C233.97 486.214 232.951 484.421 231.938 482.625C231.369 481.627 230.801 480.63 230.215 479.602C229.814 478.743 229.413 477.885 229 477C229.33 476.34 229.66 475.68 230 475C230.377 475.579 230.754 476.157 231.142 476.753C237.996 487.239 244.75 497.553 253 507C254.015 508.195 255.028 509.392 256.039 510.59C263.229 520.575 263.229 520.575 272.812 526.312C273.534 526.209 274.256 526.106 275 526C275.853 524.354 275.853 524.354 276 522C274.951 519.512 273.854 517.27 272.555 514.918C272.188 514.227 271.821 513.535 271.443 512.823C270.265 510.605 269.072 508.396 267.875 506.188C254.731 481.937 245.986 455.142 252.977 427.438C264.038 390.283 291.134 361.565 318.105 334.85C320.04 332.933 321.97 331.013 323.9 329.091C329.376 323.639 334.859 318.193 340.345 312.75C343.729 309.394 347.107 306.033 350.482 302.668C351.754 301.403 353.028 300.139 354.303 298.877C361.688 291.574 368.605 284.202 375 276C375.844 274.954 376.687 273.909 377.531 272.863C398.146 246.153 407.42 214.223 403.312 180.625C400.227 162.671 392.942 142.529 378.953 130.195C375.89 128.321 373.492 128.556 370 129C367.312 131.821 366.363 134.563 365.422 138.297C365.152 139.33 364.882 140.364 364.604 141.429C364.322 142.525 364.04 143.621 363.75 144.75C359.738 159.674 354.199 173.826 346 187C345.01 187.495 345.01 187.495 344 188C345.669 182.311 347.644 177.93 351 173C351.747 171.083 352.489 169.163 353.16 167.219C354.071 164.813 355.21 162.606 356.375 160.313C359.356 153.958 360.726 147.281 362.125 140.437C363.271 135.09 364.532 131.269 368 127C372.15 124.379 374.688 124.844 379 127Z" fill="#F6FAFB"/>
<path d="M405 315C409.619 317.511 411.532 321.081 413 326C413.414 329.24 413.429 332.424 413.375 335.688C413.365 336.576 413.355 337.464 413.344 338.38C413.182 345.286 412.287 351.456 410 358C409.785 358.629 409.571 359.258 409.35 359.906C404.93 372.741 399.204 383.134 391 394C390.386 394.833 389.773 395.665 389.141 396.523C385.758 401.029 382.43 404.533 378 408C377.258 408.743 376.515 409.485 375.75 410.25C367.423 418.577 358.315 425.833 349 433C348.428 433.44 347.856 433.88 347.266 434.334C345.595 435.619 343.922 436.903 342.25 438.188C334.038 444.495 326.004 450.872 318.359 457.863C316.318 459.712 314.236 461.483 312.125 463.25C302.932 471.294 295.155 480.759 287.863 490.516C285.287 493.95 285.287 493.95 281.75 494.563C278 494 278 494 276.461 492.32C269.594 481.357 270.732 467.305 273.536 455.055C275.659 446.728 278.264 438.756 282 431C282.293 430.376 282.586 429.753 282.887 429.11C286.927 420.529 291.219 412.558 297 405C297.739 403.997 298.477 402.994 299.238 401.961C328.917 362.028 328.917 362.028 344 357C344.33 356.01 344.66 355.02 345 354C346.977 352.195 346.977 352.195 349.625 350.125C353.595 346.963 357.47 343.741 361.275 340.382C367.342 335.031 373.536 329.885 379.938 324.938C380.623 324.402 381.308 323.866 382.014 323.313C389.033 317.958 395.855 313.304 405 315ZM366.453 338.793C363.91 341.081 361.29 343.231 358.625 345.375C353.478 349.539 348.617 353.951 343.766 358.449C341.559 360.484 339.334 362.476 337.063 364.438C307.999 389.77 277.338 423.613 274 464C272.925 478.143 272.925 478.143 278 491C279.792 493.242 279.792 493.242 282.227 493.098C286.538 491.391 288.617 488.059 291.375 484.438C292.536 482.963 293.697 481.489 294.859 480.016C295.682 478.961 295.682 478.961 296.521 477.885C305.387 466.583 315.755 456.884 327 448C328.156 447.066 329.311 446.132 330.465 445.195C336.227 440.533 342.027 435.947 347.991 431.545C378.576 408.968 404.096 383.494 411.582 345.008C412.951 335.154 412.943 326.915 407.375 318.563C394.097 309.827 375.428 330.528 366.453 338.793Z" fill="#F6FAFB"/>
<path d="M247 49C247.33 49 247.66 49 248 49C249.09 83.1458 238.058 114.068 217 141C216.261 141.949 215.523 142.897 214.762 143.875C209.986 149.847 204.889 155.495 199.687 161.094C197.257 163.722 194.874 166.384 192.5 169.062C189.239 172.704 185.851 176.19 182.375 179.625C178.733 183.232 175.258 186.887 171.934 190.785C169.901 193.113 167.807 195.371 165.697 197.628C136.542 228.861 112.198 266.666 111.762 310.742C111.754 311.55 111.746 312.358 111.737 313.19C111.724 314.893 111.713 316.595 111.705 318.298C111.693 319.998 111.673 321.698 111.643 323.398C111.365 339.354 113.379 354.721 119.187 369.688C119.628 370.847 119.628 370.847 120.076 372.029C124.572 383.445 130.501 393.904 138.566 403.18C140 405 140 405 140 407C139.34 407 138.68 407 138 407C136.249 404.761 134.611 402.523 133 400.188C132.549 399.545 132.098 398.902 131.633 398.24C126 390.199 126 390.199 126 387C125.34 387 124.68 387 124 387C122.647 384.342 121.314 381.678 120 379C119.665 378.326 119.33 377.652 118.984 376.957C114.804 368.116 112.244 358.682 111 349C110.735 347.273 110.735 347.273 110.465 345.512C106.452 316.414 107.916 283.805 121 257C121.439 256.075 121.439 256.075 121.887 255.131C127.966 242.337 135.109 230.883 143.863 219.75C146.047 217.008 146.047 217.008 148 214C148.66 214 149.32 214 150 214C150.309 212.979 150.309 212.979 150.625 211.938C152.429 208.084 154.901 204.922 158 202C158.66 202 159.32 202 160 202C160.258 201.428 160.516 200.855 160.781 200.266C162.114 197.789 163.651 195.866 165.5 193.75C166.108 193.044 166.717 192.337 167.344 191.609C169 190 169 190 171 190C171.227 189.443 171.454 188.886 171.687 188.312C173.37 185.348 175.561 183.383 178 181C179.58 179.137 181.133 177.256 182.668 175.355C183.327 174.685 183.327 174.685 184 174C184.66 174 185.32 174 186 174C186.247 173.443 186.495 172.886 186.75 172.312C189.364 167.477 193.144 163.851 197 160C204.99 151.789 212.645 143.572 219 134C219.37 133.45 219.739 132.899 220.12 132.332C224.94 125.132 229.357 117.876 233 110C233.407 109.188 233.815 108.376 234.234 107.539C242.38 90.7954 246.253 71.4274 246.012 52.8203C246.008 51.8896 246.004 50.9589 246 50C246.33 49.67 246.66 49.34 247 49Z" fill="#F4F9FA"/>
<path d="M343 188C343.33 188.66 343.66 189.32 344 190C331.853 208.813 317.84 225.954 302.568 242.319C301.475 243.491 300.384 244.665 299.295 245.841C290.177 255.689 280.826 265.269 271.27 274.693C266.842 279.077 262.573 283.514 258.518 288.246C256.428 290.661 254.208 292.943 252 295.25C240.348 307.775 230.152 321.964 222 337C221.649 337.644 221.297 338.288 220.935 338.951C205.758 367.095 200.951 398.966 210 430C211.277 433.944 212.64 437.852 214.157 441.711C216.097 446.708 216.097 446.708 215 450C211.833 444.09 209.667 438.328 207.875 431.875C207.616 430.946 207.358 430.018 207.091 429.061C200.493 404.726 201.688 380.701 210 357C210.409 355.797 210.817 354.595 211.238 353.355C216.61 338.73 225.158 325.259 234.574 312.898C236.047 311.056 236.047 311.056 237 309C237.66 309 238.32 309 239 309C239.238 308.453 239.477 307.907 239.723 307.344C241.317 304.418 243.3 302.055 245.438 299.5C248.653 295.659 248.653 295.659 251.66 291.656C252.102 291.11 252.544 290.563 253 290C253.66 290 254.32 290 255 290C255.262 289.421 255.523 288.842 255.793 288.246C258.892 282.479 264.184 277.403 269 273C269.66 273 270.32 273 271 273C271.99 271.02 271.99 271.02 273 269C273.66 269 274.32 269 275 269C275.236 268.437 275.473 267.874 275.716 267.294C277.298 264.467 279.229 262.564 281.543 260.297C282.907 258.949 282.907 258.949 284.299 257.574C285.261 256.632 286.224 255.691 287.188 254.75C293.371 248.682 299.376 242.591 305 236C307.447 233.291 309.91 230.598 312.375 227.906C313.241 226.947 314.108 225.988 315 225C315.803 224.112 315.803 224.112 316.623 223.207C321.082 218.206 325.118 212.975 329.063 207.562C329.648 206.768 330.234 205.973 330.838 205.154C334.302 200.429 337.625 195.633 340.834 190.73C342 189 342 189 343 188Z" fill="#F3F9FA"/>
<path d="M253.695 12.8086C267.545 18.1454 278.508 26.7596 289 37C289.655 37.593 290.31 38.1859 290.984 38.7969C303.486 50.3805 313.602 69.468 317.625 85.9375C317.865 86.8953 318.105 87.853 318.352 88.8398C321.858 105.93 321.511 125.165 317 142C316.801 142.775 316.603 143.55 316.398 144.349C312.242 160.307 305.651 173.799 297.188 187.875C296.719 188.655 296.251 189.435 295.769 190.239C295.323 190.962 294.877 191.685 294.418 192.43C294.024 193.071 293.63 193.713 293.223 194.375C292 196 292 196 289 198C290.298 194.269 292.111 191.135 294.274 187.839C304.69 171.957 311.608 155.475 316 137C316.274 135.859 316.274 135.859 316.553 134.696C317.119 131.28 317.201 127.907 317.238 124.453C317.25 123.359 317.25 123.359 317.263 122.243C317.277 120.696 317.287 119.15 317.295 117.603C317.307 116.056 317.327 114.508 317.357 112.961C317.903 84.5806 306.918 59.1528 287.536 38.5361C277.941 28.9411 262.328 16.5497 248.602 14.6719C245.001 14.4847 245.001 14.4847 242 16C241.551 21.2299 242.887 25.3691 244.5 30.25C246.54 36.7257 247.543 42.1537 247 49C244.898 45.4192 244.295 41.8735 243.563 37.8125C242.744 33.4129 241.838 29.155 240.313 24.9375C239.093 21.4941 238.386 18.6867 239 15C242.749 10.3143 248.534 11.2602 253.695 12.8086Z" fill="#F4F9FA"/>
<path d="M195 309C195.495 309.99 195.495 309.99 196 311C194.895 313.323 193.73 315.556 192.5 317.812C174.962 351.43 172.673 389.68 183.555 425.656C185.252 430.992 187.537 436.007 189.961 441.047C191.111 444.317 191.39 445.82 190 449C187.707 450.5 187.707 450.5 185 451C182.824 449.875 182.824 449.875 180.688 448C179.919 447.346 179.151 446.693 178.359 446.02C177.191 445.02 177.191 445.02 176 444C173.826 442.398 171.634 440.827 169.434 439.262C168.961 438.845 168.487 438.429 168 438C168 437.34 168 436.68 168 436C167.165 435.629 167.165 435.629 166.312 435.25C161.731 432.774 158.604 429.605 155.375 425.559C153.909 423.896 152.35 422.577 150.625 421.188C146.376 417.686 143.235 413.424 140 409C140.495 408.01 140.495 408.01 141 407C141.382 407.412 141.764 407.825 142.158 408.25C151.431 418.248 160.618 428.136 171 437C172.097 437.96 173.193 438.921 174.289 439.883C175.463 440.901 176.638 441.92 177.812 442.938C178.376 443.435 178.94 443.932 179.52 444.445C182.779 447.377 182.779 447.377 187 448C188.172 444.483 187.807 443.854 186.453 440.535C186.104 439.659 185.755 438.782 185.395 437.879C185.017 436.95 184.639 436.02 184.25 435.062C177.374 417.544 174.599 400.612 174.625 381.812C174.626 381.142 174.626 380.472 174.627 379.781C174.67 368.119 175.794 357.221 179 346C179.214 345.234 179.429 344.469 179.649 343.68C183.146 331.525 187.958 319.563 195 309Z" fill="#F6FAFB"/>
<path d="M288 198C288.495 198.99 288.495 198.99 289 200C281.973 209.266 274.548 217.918 266.529 226.335C264.35 228.708 262.316 231.196 260.29 233.699C259.864 234.129 259.439 234.558 259 235C258.34 235 257.68 235 257 235C256.616 235.875 256.616 235.875 256.225 236.767C254.88 239.218 253.412 240.786 251.402 242.723C250.33 243.77 250.33 243.77 249.236 244.838C248.478 245.572 247.719 246.306 246.938 247.062C241.254 252.647 235.913 258.216 231 264.492C228.084 268.149 224.919 271.566 221.75 275.004C219.952 276.885 219.952 276.885 219 279C218.34 279 217.68 279 217 279C218.679 274.989 221.428 271.93 224.25 268.688C224.776 268.079 225.302 267.471 225.843 266.845C234.467 256.943 243.484 247.425 252.828 238.203C255.47 235.523 257.919 232.711 260.363 229.852C261.964 228.041 263.637 226.366 265.375 224.688C269.086 221.035 272.314 217.052 275.578 213C278.207 209.743 280.878 206.524 283.562 203.312C284.004 202.783 284.446 202.254 284.902 201.708C285.934 200.472 286.967 199.236 288 198Z" fill="#F9FBFC"/>
<path d="M305 239C305 242 305 242 303.687 243.605C303.131 244.128 302.574 244.65 302 245.188C301.443 245.717 300.886 246.247 300.312 246.793C299.879 247.191 299.446 247.59 299 248C299.99 248.66 300.98 249.32 302 250C300.857 253.43 299.716 254.661 297 257C294.25 258.25 294.25 258.25 292 259C291.629 255.257 291.648 253.507 293.828 250.367C295.027 249.134 295.027 249.134 296.25 247.875C297.039 247.047 297.828 246.22 298.641 245.367C299.419 244.586 300.198 243.805 301 243C301.758 242.239 302.516 241.479 303.297 240.695C304.14 239.856 304.14 239.856 305 239Z" fill="#65B1C6"/>
<path d="M355 298C355.66 298.33 356.32 298.66 357 299C352.536 304.223 347.926 309.209 343 314C342.01 313.67 341.02 313.34 340 313C344.95 308.05 349.9 303.1 355 298Z" fill="#EFF7F8"/>
<path d="M216 450C219.324 454.259 221.733 458.861 224.25 463.625C224.722 464.496 225.194 465.368 225.68 466.266C226.121 467.098 226.561 467.931 227.016 468.789C227.421 469.549 227.826 470.309 228.243 471.092C228.493 471.722 228.743 472.351 229 473C228.67 473.66 228.34 474.32 228 475C225.018 471.504 222.957 468.148 221 464C220.572 463.187 220.144 462.373 219.703 461.535C219.244 460.657 218.785 459.779 218.312 458.875C217.876 458.047 217.439 457.22 216.988 456.367C216 454 216 454 216 450Z" fill="#F4F9FA"/>
<path d="M398 232C399.523 236.618 397.706 239.729 395.75 243.938C395.437 244.641 395.124 245.344 394.801 246.068C393.085 249.868 391.237 253.472 389 257C388.67 256.34 388.34 255.68 388 255C388.33 254.01 388.66 253.02 389 252C389 251.01 389 250.02 389 249C389.603 248.417 390.207 247.835 390.828 247.234C393.526 244.459 394.444 241.877 395.75 238.25C396.178 237.08 396.606 235.909 397.047 234.703C397.361 233.811 397.676 232.919 398 232Z" fill="#4E96BB"/>
<path d="M401 215C401.66 215 402.32 215 403 215C402.221 226.522 402.221 226.522 400 232C399.005 228.8 399.005 226.257 399.375 222.938C399.465 222.08 399.555 221.223 399.648 220.34C400 218 400 218 401 215Z" fill="#5B9CC2"/>
<path d="M355 168C355.33 168.99 355.66 169.98 356 171C356.66 171 357.32 171 358 171C357.67 172.32 357.34 173.64 357 175C356.34 175 355.68 175 355 175C354.67 175.66 354.34 176.32 354 177C352.354 178.687 350.688 180.355 349 182C350.492 177.028 352.605 172.582 355 168Z" fill="#61A4C4"/>
<path d="M388 395C388 399.023 386.676 400.043 384 403C382.051 404.745 380.049 406.37 378 408C377.34 407.67 376.68 407.34 376 407C379.96 403.04 383.92 399.08 388 395Z" fill="#F0F6F8"/>
<path d="M282 201C282.99 201 283.98 201 285 201C283.636 203.992 282.051 206.29 279.875 208.75C279.067 209.67 279.067 209.67 278.242 210.609C277.832 211.068 277.422 211.527 277 212C276.67 211.01 276.34 210.02 276 209C276.99 208.34 277.98 207.68 279 207C279.372 205.677 279.706 204.343 280 203C280.66 202.34 281.32 201.68 282 201Z" fill="#59ACC3"/>
<path d="M113 287C113.99 287 114.98 287 116 287C115.67 289.97 115.34 292.94 115 296C114.01 296 113.02 296 112 296C112.33 293.03 112.66 290.06 113 287Z" fill="#71CCBD"/>
<path d="M234 256C234 259 234 259 232.469 260.605C231.819 261.128 231.169 261.65 230.5 262.188C229.85 262.717 229.201 263.247 228.531 263.793C228.026 264.191 227.521 264.59 227 265C226.67 263.35 226.34 261.7 226 260C226.763 259.897 227.526 259.794 228.312 259.688C231.171 258.956 232.157 258.23 234 256Z" fill="#57AFC0"/>
<path d="M355 348C355.66 348.33 356.32 348.66 357 349C355.02 351.97 355.02 351.97 353 355C351.68 354.67 350.36 354.34 349 354C350.98 352.02 352.96 350.04 355 348Z" fill="#5C9FC2"/>
<path d="M209 375C209.054 376.458 209.093 377.916 209.125 379.375C209.148 380.187 209.171 380.999 209.195 381.836C209 384 209 384 207 386C206.67 382.7 206.34 379.4 206 376C208 375 208 375 209 375Z" fill="#77C4C9"/>
<path d="M390 248C390.66 248 391.32 248 392 248C391.385 253.538 391.385 253.538 389.438 255.875C388.963 256.246 388.489 256.618 388 257C388.165 256.237 388.33 255.474 388.5 254.688C389.091 251.964 389.091 251.964 389 249C389.33 248.67 389.66 248.34 390 248Z" fill="#5E9FC4"/>
<path d="M186 174C184.509 177.795 182.243 179.61 179 182C181.811 174 181.811 174 186 174Z" fill="#FAFDFD"/>
<path d="M275 485C277 488 279 491 281 494C279.188 493.875 279.188 493.875 277 493C275.188 490.062 275.188 490.062 274 487C274.33 486.34 274.66 485.68 275 485Z" fill="#F0F8F8"/>
<path d="M368 130C368.955 132.17 369.144 133.563 368.398 135.824C367.641 137.568 366.824 139.286 366 141C365.67 141 365.34 141 365 141C365.75 133.375 365.75 133.375 368 130Z" fill="#579CBF"/>
<path d="M369 126C372.125 126.375 372.125 126.375 375 127C375 127.33 375 127.66 375 128C374.072 128.268 373.144 128.536 372.188 128.812C369.238 129.911 367.944 130.624 366 133C366.75 128.25 366.75 128.25 369 126Z" fill="#EAF5F5"/>
<path d="M136 234C136.75 235.75 136.75 235.75 137 238C135.062 240.25 135.062 240.25 133 242C132.67 241.34 132.34 240.68 132 240C133.32 238.02 134.64 236.04 136 234Z" fill="#5EC4B4"/>
<path d="M175 187C175.66 187.33 176.32 187.66 177 188C176.67 189.98 176.34 191.96 176 194C175.01 193.34 174.02 192.68 173 192C173.875 188.125 173.875 188.125 175 187Z" fill="#68C6BC"/>
</svg>

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,48 @@
#!/usr/bin/env node
import fs from "node:fs";
import path from "node:path";
function removeAllChildren(targetDir) {
if (!fs.existsSync(targetDir)) return;
for (const item of fs.readdirSync(targetDir)) {
const itemPath = path.join(targetDir, item);
fs.rmSync(itemPath, { recursive: true, force: true });
}
}
function cleanBuildOutput() {
const projectRoot = path.resolve("..");
const auroraDir = path.join(projectRoot, "htdocs", "luci-static", "aurora");
const resourcesDir = path.join(
projectRoot,
"htdocs",
"luci-static",
"resources",
);
console.log("🧹 start clean build output...");
if (fs.existsSync(auroraDir)) {
for (const item of fs.readdirSync(auroraDir)) {
if (item !== "public") {
const itemPath = path.join(auroraDir, item);
console.log(` remove: ${itemPath}`);
fs.rmSync(itemPath, { recursive: true, force: true });
} else {
console.log(` keep: ${path.join(auroraDir, "public")}`);
}
}
}
if (!fs.existsSync(resourcesDir)) {
fs.mkdirSync(resourcesDir, { recursive: true });
} else {
console.log(` clean: ${resourcesDir}/*`);
removeAllChildren(resourcesDir);
}
console.log("✅ clean build output done!");
}
cleanBuildOutput();

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#f1f5f9" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="6 9 12 15 18 9"/>
</svg>

After

Width:  |  Height:  |  Size: 223 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#0f172b" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="6 9 12 15 18 9"/>
</svg>

After

Width:  |  Height:  |  Size: 223 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 18L15 12L9 6" stroke="#0F162B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 212 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1760890864667" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="18898" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M918.8 83.7c-43.5-30.3-104-19.5-134.6 24.1l-425.8 606-127-135.7c-36.4-38.9-98.1-41.1-137-4.8-39 36.2-41.1 97.7-4.7 136.6l206.5 220.7c4.7 5 9.8 9.4 15.1 13.1 0.7 0.5 1.4 1 2.2 1.5 43.5 30.3 104 19.5 134.6-24.1l494-703.2c30.7-43.5 20.1-103.9-23.3-134.2z" fill="#0f162b" p-id="18899"></path></svg>

After

Width:  |  Height:  |  Size: 628 B

View File

@@ -0,0 +1,3 @@
<svg width="800" height="800" viewBox="0 0 800 800" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M99.5149 622.549C93.2144 633.653 93.4349 634 107.075 634H651.912C665.537 634 665.757 633.622 659.473 622.549L385.196 138.236C378.99 127.258 379.998 127.258 373.792 138.236L99.5149 622.534V622.549ZM440.01 107.161L714.303 591.475C744.577 644.978 713.373 697 651.912 697H107.075C45.5668 697 14.4423 644.946 44.7005 591.506L318.977 107.192C349.314 53.5947 409.705 53.6105 440.026 107.192L440.01 107.161ZM379.494 586.75C383.631 586.75 387.728 585.936 391.551 584.353C395.373 582.77 398.847 580.45 401.773 577.525C404.698 574.6 407.019 571.127 408.603 567.305C410.186 563.483 411.001 559.387 411.001 555.25C411.001 551.112 410.186 547.016 408.603 543.194C407.019 539.372 404.698 535.899 401.773 532.974C398.847 530.049 395.373 527.729 391.551 526.146C387.728 524.563 383.631 523.749 379.494 523.749C371.14 523.751 363.128 527.07 357.221 532.977C351.314 538.885 347.996 546.896 347.996 555.25C347.996 563.603 351.314 571.614 357.221 577.522C363.128 583.429 371.14 586.748 379.494 586.75ZM347.991 303.249V460.749C347.991 469.104 351.31 477.116 357.218 483.023C363.126 488.931 371.139 492.249 379.494 492.249C387.849 492.249 395.862 488.931 401.769 483.023C407.677 477.116 410.996 469.104 410.996 460.749V303.249C410.996 294.894 407.677 286.882 401.769 280.975C395.862 275.067 387.849 271.749 379.494 271.749C371.139 271.749 363.126 275.067 357.218 280.975C351.31 286.882 347.991 294.894 347.991 303.249Z" fill="#0F162B"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<circle cx="12" cy="12" r="6"/>
</svg>

After

Width:  |  Height:  |  Size: 122 B

View File

@@ -0,0 +1,3 @@
<svg width="800" height="800" viewBox="0 0 800 800" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M571.418 595.528C580.7 609.514 576.962 628.288 563.018 637.696C533.911 657.268 510.181 670.456 491.868 677.302C456.716 690.382 419.502 697.054 381.995 697C354.694 697 328.234 693.514 302.949 687.004L335.962 629.8C396.485 641.014 459.864 629.8 513.289 597.124L530.089 586.708C536.769 582.478 544.84 581.032 552.573 582.681C560.306 584.329 567.086 588.941 571.46 595.528H571.418ZM509.803 94.006C529.123 102.574 547.477 113.074 564.53 125.212L250.869 668.482C231.733 659.714 213.526 649.048 196.52 636.646L509.761 94.006H509.803ZM381.995 67C409.295 67 435.839 70.486 461.082 76.996L427.985 134.284L422.063 133.234C371.12 125.021 318.883 132.645 272.412 155.075C225.941 177.505 187.476 213.659 162.214 258.653C136.953 303.647 126.112 355.311 131.158 406.664C136.204 458.017 156.893 506.583 190.43 545.8L157.459 602.92C113.918 558.666 84.4251 502.532 72.6843 441.57C60.9436 380.609 67.4788 317.537 91.469 260.277C115.459 203.018 155.835 154.123 207.524 119.736C259.213 85.349 319.912 67.0021 381.995 67ZM606.572 161.08C664.623 219.944 697.117 299.327 697 382C697.058 427.158 687.374 471.797 668.607 512.872C664.536 521.456 660.05 529.838 655.167 537.988C653.67 540.539 651.526 542.649 648.952 544.106C646.378 545.563 643.465 546.315 640.508 546.285C637.55 546.255 634.653 545.445 632.109 543.936C629.564 542.428 627.464 540.274 626.019 537.694L609.932 508.714L553.357 406.948C551.939 404.39 551.212 401.505 551.251 398.581C551.289 395.656 552.09 392.791 553.575 390.271C555.06 387.751 557.177 385.662 559.717 384.212C562.258 382.761 565.132 381.998 568.058 382H633.999C634.02 321.959 612.568 263.891 573.518 218.284L606.572 161.122V161.08Z" fill="#0F162B"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,3 @@
<svg width="800" height="800" viewBox="0 0 800 800" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M201.032 123.921C202.67 122.913 204.182 121.936 205.537 121.023C257.614 85.7368 319.094 66.9154 382 67.0003C555.974 67.0003 697 208.026 697 382C697.086 440.93 680.568 498.691 649.34 548.666C648.405 550.159 647.097 551.383 645.545 552.216C643.993 553.05 642.25 553.465 640.489 553.42C638.728 553.376 637.009 552.873 635.501 551.962C633.993 551.051 632.748 549.763 631.889 548.225L552.509 405.404C551.176 403.007 550.492 400.303 550.525 397.56C550.558 394.817 551.307 392.13 552.698 389.765C554.089 387.401 556.073 385.44 558.454 384.078C560.836 382.716 563.531 382 566.275 382H634C634.01 335.412 621.106 289.734 596.72 250.038C572.334 210.343 537.422 178.184 495.861 157.135C454.3 136.085 407.718 126.968 361.288 130.797C314.858 134.626 270.398 151.251 232.847 178.825C229.627 181.188 225.94 182.839 222.032 183.668C218.124 184.497 214.085 184.485 210.182 183.633C206.279 182.781 202.602 181.108 199.395 178.726C196.189 176.344 193.525 173.307 191.582 169.816L189.818 166.666C185.825 159.468 184.763 151.007 186.852 143.044C188.941 135.082 194.02 128.232 201.032 123.921ZM558.715 642.82C506.586 678.213 445.009 697.092 382 697C208.025 697 67 555.974 67 382C67 322.969 83.254 267.718 111.478 220.5C112.696 218.456 114.432 216.77 116.509 215.61C118.587 214.45 120.933 213.858 123.312 213.893C125.691 213.928 128.019 214.589 130.061 215.81C132.103 217.03 133.788 218.768 134.945 220.846L211.49 358.595C212.824 360.993 213.508 363.697 213.474 366.44C213.441 369.183 212.692 371.87 211.302 374.235C209.911 376.599 207.926 378.56 205.545 379.922C203.164 381.284 200.468 382 197.725 382H130C129.999 428.603 142.921 474.294 167.33 513.993C191.739 553.693 226.679 585.846 268.265 606.879C309.852 627.913 356.457 637.002 402.9 633.137C449.342 629.272 493.803 612.604 531.341 584.986C534.645 582.52 538.43 580.775 542.451 579.865C546.472 578.954 550.64 578.898 554.684 579.7C558.728 580.501 562.558 582.143 565.928 584.519C569.297 586.894 572.131 589.951 574.244 593.491C578.821 601.138 580.227 610.272 578.161 618.941C576.095 627.61 570.721 635.129 563.188 639.89C561.702 640.833 560.232 641.799 558.778 642.788L558.715 642.82Z" fill="#0F162B"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1758476543825" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17352" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M439.594667 85.333333h144.810666a70.4 70.4 0 0 1 70.101334 60.970667l5.802666 44.8a17.066667 17.066667 0 0 0 8.192 12.458667l38.058667 22.528a17.066667 17.066667 0 0 0 15.146667 1.109333l46.848-19.2a71.082667 71.082667 0 0 1 88.021333 29.226667l72.405333 122.069333c18.218667 30.72 10.069333 69.973333-18.688 91.221333l-45.184 33.408a17.066667 17.066667 0 0 0-6.912 13.696v28.757334a17.066667 17.066667 0 0 0 6.912 13.696l45.226667 33.408c28.714667 21.205333 36.864 60.458667 18.645333 91.221333l-72.405333 122.026667a71.082667 71.082667 0 0 1-88.021333 29.226666l-46.848-19.2a17.066667 17.066667 0 0 0-15.146667 1.152l-38.058667 22.528a17.066667 17.066667 0 0 0-8.192 12.501334l-5.802666 44.8A70.4 70.4 0 0 1 584.405333 938.666667h-144.810666a70.4 70.4 0 0 1-70.101334-60.970667l-5.802666-44.8a17.066667 17.066667 0 0 0-8.192-12.458667l-38.058667-22.528a17.066667 17.066667 0 0 0-15.146667-1.109333l-46.848 19.2a71.082667 71.082667 0 0 1-88.021333-29.226667l-72.405333-122.069333a69.290667 69.290667 0 0 1 18.688-91.221333l45.184-33.408a17.066667 17.066667 0 0 0 6.912-13.696v-28.757334a17.066667 17.066667 0 0 0-6.912-13.696l-45.226667-33.408a69.290667 69.290667 0 0 1-18.645333-91.221333l72.405333-122.026667a71.082667 71.082667 0 0 1 88.021333-29.226666l46.848 19.2a17.066667 17.066667 0 0 0 15.146667-1.152l38.058667-22.528a17.066667 17.066667 0 0 0 8.192-12.501334l5.802666-44.8A70.4 70.4 0 0 1 439.594667 85.333333z m1.024 130.688a75.818667 75.818667 0 0 1-36.821334 55.466667l-51.712 30.464a76.970667 76.970667 0 0 1-67.925333 4.906667l-35.328-14.336a17.066667 17.066667 0 0 0-21.077333 7.04L178.645333 381.738667a17.066667 17.066667 0 0 0 4.565334 22.528l33.066666 24.277333c19.541333 14.293333 31.018667 36.906667 31.018667 61.013333v44.885334c0 24.106667-11.52 46.72-31.018667 61.013333l-33.066666 24.277333a17.066667 17.066667 0 0 0-4.565334 22.528l49.066667 82.176a17.066667 17.066667 0 0 0 21.12 7.04l35.328-14.336a76.970667 76.970667 0 0 1 67.925333 4.906667l51.712 30.421333c20.224 11.946667 33.792 32.384 36.821334 55.509334l4.010666 30.506666a17.066667 17.066667 0 0 0 16.896 14.848h100.949334a17.066667 17.066667 0 0 0 16.896-14.848l4.010666-30.506666c2.986667-23.125333 16.597333-43.605333 36.821334-55.466667l51.712-30.464a76.970667 76.970667 0 0 1 67.925333-4.906667l35.328 14.336a17.066667 17.066667 0 0 0 21.077333-7.04l49.109334-82.176a17.066667 17.066667 0 0 0-4.565334-22.528l-33.066666-24.277333a75.648 75.648 0 0 1-31.018667-61.013333v-44.885334c0-24.106667 11.52-46.72 31.018667-61.013333l33.066666-24.277333a17.066667 17.066667 0 0 0 4.565334-22.528l-49.066667-82.176a17.066667 17.066667 0 0 0-21.12-7.04l-35.328 14.336c-22.186667 9.045333-47.317333 7.210667-67.925333-4.906667l-51.712-30.421333a75.818667 75.818667 0 0 1-36.821334-55.509334l-4.010666-30.506666A17.066667 17.066667 0 0 0 562.474667 170.666667h-100.949334a17.066667 17.066667 0 0 0-16.896 14.848l-4.010666 30.506666zM682.666667 512a42.666667 42.666667 0 0 1-85.333334 0 85.333333 85.333333 0 1 0-85.333333 85.333333 42.666667 42.666667 0 0 1 0 85.333334 170.666667 170.666667 0 1 1 170.666667-170.666667z" fill="#0f162b" p-id="17353"></path></svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1758357645269" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7610" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 85.333333c235.648 0 426.666667 191.018667 426.666667 426.666667s-191.018667 426.666667-426.666667 426.666667S85.333333 747.648 85.333333 512 276.352 85.333333 512 85.333333z m0 128a298.666667 298.666667 0 1 0 0 597.333334 298.666667 298.666667 0 0 0 0-597.333334z" fill="#000000" fill-opacity=".05" p-id="7611"></path><path d="M813.696 813.696c166.613333-166.613333 166.613333-436.778667 0-603.392-166.613333-166.613333-436.778667-166.613333-603.392 0A64 64 0 0 0 300.8 300.8a298.666667 298.666667 0 1 1 422.4 422.4 64 64 0 0 0 90.496 90.496z" fill="#000000" p-id="7612"></path></svg>

After

Width:  |  Height:  |  Size: 922 B

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,405 @@
"use strict";
"require baseclass";
"require ui";
return baseclass.extend({
__init__() {
ui.menu.load().then((tree) => this.render(tree));
this.initMobileMenu();
this.initUciIndicator();
},
initUciIndicator() {
const original = ui.changes?.setIndicator;
if (!original) return;
ui.changes.setIndicator = function (n) {
original.call(this, n);
document
.querySelector('[data-indicator="uci-changes"]')
?.setAttribute("data-count", n || 0);
};
},
initMobileMenu() {
const overlay = document.querySelector("#mobile-menu-overlay");
const menuToggle = document.querySelector("#mobile-menu-btn");
const closeBtn = document.querySelector("#mobile-nav-close");
if (!menuToggle || !overlay) return;
menuToggle.addEventListener("click", (e) => {
e.stopPropagation();
const isOpen = overlay.classList.contains("mobile-menu-open");
overlay.classList.toggle("mobile-menu-open", !isOpen);
menuToggle.classList.toggle("active", !isOpen);
menuToggle.setAttribute("aria-expanded", !isOpen);
document.body.style.overflow = isOpen ? "" : "hidden";
if (isOpen) {
document
.querySelectorAll(".mobile-nav-item.submenu-expanded")
.forEach((item) => {
item.classList.remove("submenu-expanded");
const submenu = item.querySelector(".mobile-nav-submenu");
if (submenu) {
submenu.style.maxHeight = "0";
submenu.style.opacity = "0";
}
});
}
});
if (closeBtn) {
closeBtn.addEventListener("click", () => menuToggle.click());
}
overlay.addEventListener("click", (e) => {
if (e.target === overlay) menuToggle.click();
});
document.addEventListener("keydown", (e) => {
if (
e.key === "Escape" &&
overlay.classList.contains("mobile-menu-open")
) {
menuToggle.click();
}
});
document.addEventListener("click", (e) => {
const link = e.target.closest(".mobile-nav-link");
if (!link) return;
const item = link.closest(".mobile-nav-item");
const submenu = item?.querySelector(".mobile-nav-submenu");
if (submenu) {
e.preventDefault();
e.stopPropagation();
const isExpanded = item.classList.contains("submenu-expanded");
document
.querySelectorAll(".mobile-nav-item.submenu-expanded")
.forEach((i) => {
if (i !== item) {
i.classList.remove("submenu-expanded");
const s = i.querySelector(".mobile-nav-submenu");
if (s) {
s.style.maxHeight = "0";
s.style.opacity = "0";
}
}
});
item.classList.toggle("submenu-expanded", !isExpanded);
submenu.style.maxHeight = isExpanded
? "0"
: `${submenu.scrollHeight}px`;
submenu.style.opacity = isExpanded ? "0" : "1";
}
});
},
renderMobileMenu(tree, url) {
const list = document.querySelector("#mobile-nav-list");
const children = ui.menu.getChildren(tree);
if (!list || !children.length) return;
list.innerHTML = "";
children.forEach((child) => {
const submenu = ui.menu.getChildren(child);
const hasSubmenu = submenu.length > 0;
const li = E("li", { class: "mobile-nav-item" }, [
E(
"a",
{
class: "mobile-nav-link",
href: hasSubmenu ? "#" : L.url(url, child.name),
},
[_(child.title)],
),
]);
if (hasSubmenu) {
const ul = E("ul", {
class: "mobile-nav-submenu",
style: "max-height: 0; opacity: 0;",
});
submenu.forEach((item) => {
ul.appendChild(
E("li", { class: "mobile-nav-subitem" }, [
E(
"a",
{
class: "mobile-nav-sublink",
href: L.url(url, child.name, item.name),
},
[_(item.title)],
),
]),
);
});
li.appendChild(ul);
}
list.appendChild(li);
});
},
render(tree) {
this.renderModeMenu(tree);
if (L.env.dispatchpath.length >= 3) {
let node = tree;
let url = "";
for (let i = 0; i < 3 && node; i++) {
const segment = L.env.dispatchpath[i];
node = node.children?.[segment];
url += (url ? "/" : "") + segment;
}
if (node) this.renderTabMenu(node, url);
}
},
renderTabMenu(tree, url, level = 0) {
const container = document.querySelector("#tabmenu");
const ul = E("ul", { class: "tabs" });
const children = ui.menu.getChildren(tree);
let activeNode = null;
children.forEach((child) => {
const isActive = L.env.dispatchpath[3 + level] === child.name;
ul.appendChild(
E(
"li",
{
class: `tabmenu-item-${child.name}${isActive ? " active" : ""}`,
},
[E("a", { href: L.url(url, child.name) }, [_(child.title)])],
),
);
if (isActive) activeNode = child;
});
if (!ul.children.length) return E([]);
container.appendChild(ul);
container.style.display = "";
if (activeNode) {
this.renderTabMenu(activeNode, `${url}/${activeNode.name}`, level + 1);
}
return ul;
},
renderMainMenu(tree, url, level = 0) {
const ul = level
? E("ul", { class: "desktop-nav-list" })
: document.querySelector("#topmenu");
const children = ui.menu.getChildren(tree);
if (!children.length || level > 1) return E([]);
if (level === 0) {
const container = document.querySelector(".desktop-menu-container");
const overlay = document.querySelector(".desktop-menu-overlay");
const header = document.querySelector("header");
let showTimer = null;
let hideTimer = null;
children.forEach((child) => {
const submenu = ui.menu.getChildren(child);
const hasSubmenu = submenu.length > 0;
const li = E(
"li",
{
class: hasSubmenu ? "has-desktop-nav" : "",
},
[
E(
"a",
{
class: "menu",
href: hasSubmenu ? "#" : L.url(url, child.name),
},
[_(child.title)],
),
],
);
ul.appendChild(li);
if (hasSubmenu) {
const nav = E(
"div",
{
class: "desktop-nav",
},
[this.renderMainMenu(child, `${url}/${child.name}`, level + 1)],
);
li.appendChild(nav);
const menuLink = li.querySelector("a");
li.addEventListener("mouseenter", () => {
if (hideTimer) {
clearTimeout(hideTimer);
hideTimer = null;
}
showTimer = setTimeout(() => {
const wasActive = nav.classList.contains("active");
document.querySelectorAll(".desktop-nav").forEach((n) => {
if (n !== nav) n.classList.remove("active");
});
document.querySelectorAll("#topmenu a").forEach((a) => {
if (a !== menuLink) a.classList.remove("menu-active");
});
if (wasActive) return;
menuLink.classList.add("menu-active");
header.classList.add("has-desktop-nav");
requestAnimationFrame(() => {
const navHeight = nav.scrollHeight;
const headerHeight =
header.querySelector(".header-content")?.offsetHeight || 56;
const totalHeight = headerHeight + navHeight;
if (container) {
container.style.height = `${totalHeight}px`;
}
requestAnimationFrame(() => {
nav.classList.add("active");
overlay.classList.add("active");
if (container) {
container.classList.add("active");
}
});
});
}, 100);
});
li.addEventListener("mouseleave", () => {
if (showTimer) {
clearTimeout(showTimer);
showTimer = null;
}
});
menuLink.addEventListener("click", (e) => {
e.preventDefault();
});
}
});
if (header && overlay) {
const hideMenu = () => {
if (showTimer) {
clearTimeout(showTimer);
showTimer = null;
}
hideTimer = setTimeout(() => {
this.hideDesktopNav();
}, 150);
};
header.addEventListener("mouseleave", hideMenu);
overlay.addEventListener("mouseenter", hideMenu);
header.addEventListener("mouseenter", () => {
if (hideTimer) {
clearTimeout(hideTimer);
hideTimer = null;
}
});
overlay.addEventListener("click", () => this.hideDesktopNav());
}
} else {
children.forEach((child) => {
ul.appendChild(
E("li", {}, [
E("a", { href: L.url(url, child.name) }, [_(child.title)]),
]),
);
});
}
ul.style.display = "";
return ul;
},
hideDesktopNav() {
document
.querySelectorAll(".desktop-nav")
.forEach((nav) => nav.classList.remove("active"));
document.querySelector("header")?.classList.remove("has-desktop-nav");
const container = document.querySelector(".desktop-menu-container");
if (container) {
container.classList.remove("active");
container.style.height = "";
}
document.querySelector(".desktop-menu-overlay")?.classList.remove("active");
document
.querySelectorAll("#topmenu a")
.forEach((a) => a.classList.remove("menu-active"));
},
renderModeMenu(tree) {
const ul = document.querySelector("#modemenu");
const children = ui.menu.getChildren(tree);
let activeChild = null;
children.forEach((child, index) => {
const isActive = L.env.requestpath.length
? child.name === L.env.requestpath[0]
: index === 0;
ul.appendChild(
E(
"li",
{
class: isActive ? "active" : "",
},
[E("a", { href: L.url(child.name) }, [_(child.title)])],
),
);
if (isActive) activeChild = child;
});
if (activeChild) {
this.renderMainMenu(activeChild, activeChild.name);
this.renderMobileMenu(activeChild, activeChild.name);
}
if (ul.children.length > 1) {
ul.style.display = "";
}
},
});

View File

@@ -0,0 +1,35 @@
"use strict";
"require ui";
"require view";
return view.extend({
render() {
const form = document.querySelector("form");
const btn = document.querySelector("button");
const dlg = ui.showModal(
_("Authorization Required"),
Array.from(document.querySelectorAll("section > *")),
"login",
);
form.addEventListener("keypress", (ev) => {
if (ev.key === "Enter") btn.click();
});
btn.addEventListener("click", () => {
dlg.querySelectorAll("*").forEach((node) => {
node.style.display = "none";
});
dlg.appendChild(E("div", { class: "spinning" }, _("Logging in…")));
form.submit();
});
document.querySelector('input[type="password"]').focus();
return "";
},
addFooter() {},
});

View File

@@ -0,0 +1,308 @@
/**
* Copyright (C) 2025 eamonxg <eamonxiong@gmail.com>
* Licensed under the Apache License, Version 2.0.
*/
import tailwindcss from "@tailwindcss/vite";
import { mkdir, readdir, readFile, writeFile } from "fs/promises";
import { dirname, join, relative, resolve } from "path";
import { minify as terserMinify } from "terser";
import { defineConfig, loadEnv, Plugin, ResolvedConfig } from "vite";
const CURRENT_DIR = process.cwd();
const PROJECT_ROOT = resolve(CURRENT_DIR, "..");
const BUILD_OUTPUT = resolve(PROJECT_ROOT, "htdocs/luci-static");
async function scanFiles(
dir: string,
extensions: string[] = [],
): Promise<string[]> {
const entries = await readdir(dir, { withFileTypes: true });
const files: string[] = [];
for (const entry of entries) {
const fullPath = join(dir, entry.name);
if (entry.isDirectory()) {
files.push(...(await scanFiles(fullPath, extensions)));
} else if (
entry.isFile() &&
(!extensions.length || extensions.some((ext) => fullPath.endsWith(ext)))
) {
files.push(fullPath);
}
}
return files;
}
function createLuciJsCompressPlugin(): Plugin {
let outDir: string;
let jsFiles: string[] = [];
return {
name: "luci-js-compress",
apply: "build",
configResolved(config: ResolvedConfig) {
outDir = config.build.outDir;
},
async buildStart() {
const srcDir = resolve(CURRENT_DIR, "src/resource");
jsFiles = await scanFiles(srcDir, [".js"]);
},
async generateBundle() {
for (const filePath of jsFiles) {
try {
const sourceCode = await readFile(filePath, "utf-8");
const compressed = await terserMinify(sourceCode, {
parse: { bare_returns: true },
compress: false,
mangle: false,
format: { comments: false, beautify: false },
});
const relativePath = relative(
resolve(CURRENT_DIR, "src/resource"),
filePath,
).replace(/\\/g, "/");
const outputPath = join(outDir, "resources", relativePath);
await mkdir(dirname(outputPath), { recursive: true });
await writeFile(outputPath, compressed.code || sourceCode, "utf-8");
} catch (error: any) {
console.error(`JS compress failed: ${filePath}`, error?.message);
}
}
},
};
}
interface RouteConfig {
routes: Record<string, string>;
shouldRewrite: boolean;
hmrMessage: string;
}
interface ResourceConfig {
css: RouteConfig;
js: RouteConfig;
}
function createLocalServePlugin(): Plugin {
const resourceConfig: ResourceConfig = {
css: {
routes: {
"/luci-static/aurora/main.css": "/src/media/main.css",
},
shouldRewrite: true,
hmrMessage: "CSS file changed",
},
js: {
routes: {
"/luci-static/resources/view/aurora/sysauth.js":
"src/resource/view/aurora/sysauth.js",
"/luci-static/resources/menu-aurora.js": "src/resource/menu-aurora.js",
},
shouldRewrite: false,
hmrMessage: "JS file changed",
},
};
const buildHmrMap = (routes: Record<string, string>, isVitePath: boolean) => {
const map: Record<string, string> = {};
Object.entries(routes).forEach(([publicPath, sourcePath]) => {
const filePath = isVitePath
? resolve(CURRENT_DIR, sourcePath.replace(/^\//, ""))
: resolve(CURRENT_DIR, sourcePath);
map[filePath.replace(/\\/g, "/")] = publicPath;
});
return map;
};
const cssHmrMap = buildHmrMap(resourceConfig.css.routes, true);
const jsHmrMap = buildHmrMap(resourceConfig.js.routes, false);
return {
name: "local-serve-plugin",
apply: "serve",
enforce: "pre",
configureServer(server) {
server.middlewares.use(async (req, res, next) => {
if (!req.url) return next();
const [pathname, search] = req.url.split("?");
const cssTarget = resourceConfig.css.routes[pathname];
if (cssTarget) {
req.url = cssTarget + (search ? `?${search}` : "");
return next();
}
const jsPath = resourceConfig.js.routes[pathname];
if (jsPath) {
try {
const file = resolve(CURRENT_DIR, jsPath);
const code = await readFile(file, "utf-8");
res.setHeader("Content-Type", "text/javascript");
res.setHeader("Cache-Control", "no-store");
res.statusCode = 200;
res.end(code);
return;
} catch (err) {
console.error(`[JS Error] Failed to read ${jsPath}:`, err);
}
}
next();
});
},
handleHotUpdate({ file, server }) {
const normalizedFile = file.replace(/\\/g, "/");
const resources = [
{ map: cssHmrMap, config: resourceConfig.css },
{ map: jsHmrMap, config: resourceConfig.js },
];
for (const { map, config } of resources) {
const publicPath = map[normalizedFile];
if (publicPath) {
console.log(`[HMR] ${config.hmrMessage}: ${publicPath} (tracked: ${normalizedFile})`);
server.ws.send({ type: "full-reload", path: "*" });
return [];
}
}
},
};
}
function createRedirectPlugin(): Plugin {
return {
name: "redirect-plugin",
apply: "serve",
configureServer(server) {
server.middlewares.use((req, res, next) => {
if (req.url === "/" || req.url === "/index.html") {
res.writeHead(302, { Location: "/cgi-bin/luci" });
res.end();
return;
}
next();
});
},
};
}
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, CURRENT_DIR, "");
const OPENWRT_HOST = env.VITE_OPENWRT_HOST || "http://192.168.1.1:80";
const DEV_HOST = env.VITE_DEV_HOST || "127.0.0.1";
const DEV_PORT = Number(env.VITE_DEV_PORT) || 5173;
const proxyConfig = {
"/luci-static": {
target: OPENWRT_HOST,
changeOrigin: true,
secure: false,
},
"/cgi-bin": {
target: OPENWRT_HOST,
changeOrigin: true,
secure: false,
configure: (proxy: any) => {
proxy.on("proxyRes", (proxyRes: any, req: any, res: any) => {
const contentType = proxyRes.headers["content-type"] || "";
if (contentType.includes("text/html")) {
const chunks: Buffer[] = [];
proxyRes.on("data", (chunk: Buffer) => {
chunks.push(chunk);
});
proxyRes.on("end", () => {
let html = Buffer.concat(chunks).toString("utf-8");
const viteClient = `<script type="module" src="/@vite/client"></script>`;
if (html.includes("</head>") && !html.includes("/@vite/client")) {
html = html.replace("</head>", `${viteClient}\n\t</head>`);
console.log("[HMR] Injected Vite client into proxied HTML");
}
res.removeAllListeners("end");
res.setHeader("Content-Length", Buffer.byteLength(html));
res.end(html);
});
proxyRes.pipe = () => proxyRes;
}
});
},
},
} as const;
const aliasConfig = {
"@": resolve(CURRENT_DIR, "src"),
"@assets": resolve(CURRENT_DIR, "src/assets"),
} as const;
return {
plugins: [
tailwindcss(),
createRedirectPlugin(),
createLocalServePlugin(),
createLuciJsCompressPlugin(),
],
css: {
postcss: {
plugins: [
{
postcssPlugin: "remove-layers",
Once(root) {
function removeLayers(node: any) {
node.walkAtRules("layer", (rule: any) => {
removeLayers(rule);
rule.replaceWith(rule.nodes);
});
}
removeLayers(root);
},
},
],
},
},
build: {
outDir: BUILD_OUTPUT,
emptyOutDir: false,
cssMinify: "lightningcss",
rollupOptions: {
input: {
main: resolve(CURRENT_DIR, "src/media/main.css"),
},
output: {
assetFileNames: "aurora/[name].[ext]",
},
},
},
server: {
host: DEV_HOST,
port: DEV_PORT,
proxy: proxyConfig,
headers: {
"Cache-Control": "no-store",
},
},
resolve: {
alias: aliasConfig,
},
};
});

19
luci-theme-aurora/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,19 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll": "explicit",
"source.organizeImports": "explicit"
},
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"prettier.requireConfig": false,
"prettier.useEditorConfig": true
}

201
luci-theme-aurora/LICENSE Normal file
View File

@@ -0,0 +1,201 @@
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.

View File

@@ -0,0 +1,20 @@
#
# Copyright (C) 2025 eamonxg <eamonxiong@gmail.com>
# Licensed under the Apache License, Version 2.0.
#
include $(TOPDIR)/rules.mk
LUCI_TITLE:=Aurora Theme (A modern browser theme built with Vite and Tailwind CSS)
LUCI_DEPENDS:=+luci-base
PKG_VERSION:=0.8.6_beta
PKG_RELEASE:=20251128
PKG_LICENSE:=Apache-2.0
LUCI_MINIFY_CSS:=
CONFIG_LUCI_CSSTIDY:=
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@@ -0,0 +1,55 @@
# LuCI Theme Aurora
**English** | [简体中文](README_zh.md)
## Introduction
**luci-theme-aurora** is an OpenWrt theme designed specifically for modern browsers.
The design of the theme is inspired by **Aurora** — in Tromsø, northern Norway, the daytime view is of pure white snow-capped mountains, while at night the sky comes alive with dancing auroras.
Similarly, in this theme, the light mode reflects the purity of snowy mountains, and the dark mode showcases the enchanting beauty of the aurora.
> ⚠️ **Note**
> The theme is in Beta, and versions are updated frequently. The released version may not always be the latest.
> If you have a build environment, you can compile it yourself; otherwise, you can fork the repository and use the [GitHub Actions workflow](https://github.com/eamonxg/luci-theme-aurora/actions/workflows/build-and-release-aurora.yml) to build the theme automatically.
> Bugs are welcome—please feel free to submit an Issue 🙏💖.
## Compatibility
- **OpenWrt**: The theme uses ucode templates and LuCI JavaScript APIs, requiring OpenWrt 23.05.0 or later.
- **Browsers**: The theme is built with **TailwindCSS v4**, please use the following modern browsers:
- **Chrome/Edge 111** _(released March 2023)_
- **Safari 16.4** _(released March 2023)_
- **Firefox 128** _(released July 2024)_
## Preview
### Desktop
![light](./.dev/docs/preview/light.png)
![dark](./.dev/docs/preview/dark.png)
### Mobile
![mobile](./.dev/docs/preview/mobile.png)
## About Development
As we step into 2025, the development of OpenWrt themes in the market remains fairly primitive, while the frontend toolchain has already become mature and advanced.
Embracing the modern frontend ecosystem is therefore the core philosophy of the Aurora theme.
Aurora uses **Vite** as the build tool.
- During development, you can freely choose and integrate any CSS toolchain.
- In production, multiple strategies are available to optimize bundled assets.
- Most importantly, with the help of a local proxy server, style changes can be previewed in real time!
## License & Credits
The project structure for the production build is based on the official OpenWrt theme
[luci-theme-bootstrap](https://github.com/openwrt/luci/tree/master/themes/luci-theme-bootstrap).
Visual style and some implementations are influenced by
[Apple](https://www.apple.com/) and [Vercel](https://vercel.com/docs/dashboard-features).
The project is developed using a modern front-end stack, including
[Vite](https://vitejs.dev/) and [Tailwind CSS](https://tailwindcss.com/).

View File

@@ -0,0 +1,53 @@
# LuCI Theme Aurora
[English](README.md) | **简体中文**
## 简介
**luci-theme-aurora** 是一款专为现代浏览器设计的 OpenWrt 主题。
主题的设计灵感源自 **Aurora极光** —— 在挪威北部特罗姆瑟,白天映入眼帘的是洁白纯净的雪山,夜幕降临则是天际舞动的极光。
同样在主题中,我希望白天模式呈现雪山般纯净,暗夜模式展现极光般迷人。
> ⚠️ **提示**
> 主题处于 Beta 阶段,版本更新频繁,发布的版本可能不是最新的。
> 有编译环境可自行编译,无编译环境可 fork 仓库使用 [GitHub Actions 工作流](https://github.com/eamonxg/luci-theme-aurora/actions/workflows/build-and-release-aurora.yml) 自动生成主题安装包。
> 遇到 Bug 欢迎提交 Issue 🙏💖。
## 兼容性
- **OpenWrt**:主题使用 ucode 模板以及 luci JavaScript APIs需要 **OpenWrt 23.05.0** 或更高版本。
- **浏览器**:主题基于 **TailwindCSS v4** 开发,请使用以下现代浏览器:
- **Chrome/Edge 111** _(2023 年 3 月发布)_
- **Safari 16.4** _(2023 年 3 月发布)_
- **Firefox 128** _(2024 年 7 月发布)_
## 预览
### 桌面端
![light](./.dev/docs/preview/light.png)
![dark](./.dev/docs/preview/dark.png)
### 移动端
![mobile](./.dev/docs/preview/mobile.png)
## 关于开发
步入 2025 年,市面上的 OpenWrt 主题开发方式仍然比较原始,而前端工具链已相当成熟且先进。
所以全面拥抱现代前端生态,是 Aurora 主题开发的核心理念。
Aurora 选择 Vite 作为构建工具,
在开发阶段,可以自由选择并使用 CSS 任何工具链;
在生产阶段,可以选择多种手段优化打包资源;
最重要的是,通过本地代理服务器,可以做到样式修改实时预览!
## 版权与致谢
本项目生产目录结构基于 OpenWrt 官方主题
[luci-theme-bootstrap](https://github.com/openwrt/luci/tree/master/themes/luci-theme-bootstrap)。
视觉风格及部分实现借鉴自
[Apple](https://www.apple.com/) 和 [Vercel](https://vercel.com/docs/dashboard-features)。
项目使用现代前端技术栈开发,包括
[Vite](https://vitejs.dev/) 和 [Tailwind CSS](https://tailwindcss.com/)。

View File

@@ -0,0 +1,31 @@
<svg width="550" height="550" viewBox="0 0 550 550" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M253.695 12.8086C267.545 18.1454 278.508 26.7596 289 37C289.982 37.8894 289.982 37.8894 290.984 38.7969C303.486 50.3805 313.602 69.468 317.625 85.9375C317.865 86.8953 318.105 87.853 318.352 88.8398C321.858 105.93 321.511 125.165 317 142C316.702 143.163 316.702 143.163 316.398 144.349C310.628 166.502 299.762 187.433 285 205C284.327 205.843 283.654 206.686 282.961 207.555C278.47 213.156 273.782 218.519 268.896 223.774C266.327 226.537 263.841 229.342 261.438 232.25C260.633 233.158 259.829 234.065 259 235C258.34 235 257.68 235 257 235C256.616 235.875 256.616 235.875 256.225 236.767C254.88 239.218 253.412 240.786 251.402 242.723C250.688 243.421 249.973 244.119 249.236 244.838C248.478 245.572 247.719 246.306 246.938 247.062C241.254 252.647 235.913 258.216 231 264.492C228.084 268.149 224.919 271.566 221.75 275.004C219.952 276.885 219.952 276.885 219 279C218.34 279 217.68 279 217 279C216.726 279.582 216.452 280.165 216.17 280.765C215.026 282.95 213.763 284.799 212.277 286.766C205.031 296.495 198.656 306.272 193 317C192.533 317.819 192.067 318.637 191.586 319.48C174.356 350.883 173.108 389.696 182.888 423.453C184.74 429.577 187.19 435.287 189.961 441.047C191.111 444.317 191.39 445.82 190 449C187.707 450.5 187.707 450.5 185 451C182.824 449.875 182.824 449.875 180.687 448C179.919 447.346 179.151 446.693 178.359 446.02C177.191 445.02 177.191 445.02 176 444C173.826 442.398 171.634 440.827 169.434 439.262C168.961 438.845 168.487 438.429 168 438C168 437.34 168 436.68 168 436C167.443 435.752 166.886 435.505 166.312 435.25C161.731 432.774 158.604 429.605 155.375 425.559C153.909 423.896 152.35 422.577 150.625 421.188C146.42 417.703 142.999 413.558 140 409C140 408.34 140 407.68 140 407C139.34 407 138.68 407 138 407C136.249 404.761 134.611 402.523 133 400.188C132.549 399.545 132.098 398.902 131.633 398.24C126 390.199 126 390.199 126 387C125.34 387 124.68 387 124 387C122.647 384.342 121.314 381.678 120 379C119.665 378.326 119.33 377.652 118.984 376.957C114.804 368.116 112.244 358.682 111 349C110.735 347.273 110.735 347.273 110.465 345.512C106.452 316.414 107.916 283.805 121 257C121.439 256.075 121.439 256.075 121.887 255.131C127.966 242.337 135.109 230.883 143.863 219.75C146.047 217.008 146.047 217.008 148 214C148.66 214 149.32 214 150 214C150.309 212.979 150.309 212.979 150.625 211.938C152.429 208.084 154.901 204.922 158 202C158.66 202 159.32 202 160 202C160.258 201.428 160.516 200.855 160.781 200.266C162.114 197.789 163.651 195.866 165.5 193.75C166.108 193.044 166.717 192.337 167.344 191.609C169 190 169 190 171 190C171.227 189.443 171.454 188.886 171.687 188.312C173.37 185.348 175.561 183.383 178 181C179.58 179.137 181.133 177.256 182.668 175.355C183.108 174.908 183.547 174.461 184 174C184.66 174 185.32 174 186 174C186.247 173.443 186.495 172.886 186.75 172.312C189.364 167.477 193.144 163.851 197 160C204.99 151.789 212.645 143.572 219 134C219.37 133.45 219.739 132.899 220.12 132.332C224.94 125.132 229.357 117.876 233 110C233.407 109.188 233.815 108.376 234.234 107.539C244.048 87.367 249.086 61.632 244.188 39.5625C243.939 38.3704 243.939 38.3704 243.686 37.1543C242.638 32.2952 241.295 27.6434 239.622 22.9622C238.73 20.1498 238.515 17.9094 239 15C242.749 10.3143 248.534 11.2602 253.695 12.8086Z" fill="#6DC3C5"/>
<path d="M379 127C395.79 140.378 403.626 163.462 406 184C406.279 188.609 406.334 193.196 406.312 197.813C406.308 199.036 406.304 200.26 406.3 201.521C406.186 210.698 405.417 219.136 403 228C402.733 228.999 402.466 229.998 402.191 231.027C396.077 251.272 384.609 269.828 370 285C368.742 286.431 367.492 287.868 366.25 289.312C363.615 292.339 360.838 295.162 358 298C356.559 299.622 355.122 301.247 353.688 302.875C350.306 306.662 346.824 310.088 342.922 313.332C341.026 314.978 339.263 316.721 337.493 318.5C335.869 320.132 334.238 321.755 332.606 323.378C329.18 326.786 325.757 330.197 322.335 333.61C320.866 335.074 319.396 336.537 317.925 337.999C315.784 340.129 313.645 342.261 311.508 344.395C310.86 345.038 310.212 345.681 309.544 346.343C305.702 350.184 302.053 354.139 298.51 358.258C296.49 360.589 294.366 362.818 292.25 365.062C276.497 382.391 261.694 404.04 256 427C255.653 428.146 255.306 429.292 254.949 430.473C253.52 436.248 253.617 442.081 253.625 448C253.627 449.199 253.628 450.398 253.63 451.633C253.759 461.039 254.95 469.104 258 478C258.289 478.938 258.578 479.877 258.875 480.844C262.139 490.753 267.295 500.041 272.262 509.184C272.626 509.856 272.991 510.529 273.366 511.222C274.062 512.498 274.764 513.771 275.474 515.04C277.271 518.323 278.696 521.24 279 525C277.438 527.375 277.438 527.375 275 529C270.511 529.098 267.863 527.569 264.57 524.586C261.547 521.532 258.762 518.29 256 515C254.732 513.515 254.732 513.515 253.438 512C251.084 509.103 248.839 506.153 246.621 503.152C244.297 500.066 241.858 497.073 239.418 494.078C238.002 492.003 237.426 490.455 237 488C236.34 488 235.68 488 235 488C234.625 487.287 234.25 486.574 233.863 485.84C230.394 479.325 230.394 479.325 226.313 473.188C224.128 470.177 222.58 467.357 221 464C220.188 462.417 219.375 460.833 218.563 459.25C209.592 441.201 203.713 421.162 203 401C202.959 399.844 202.918 398.687 202.875 397.496C202.619 383.189 205.305 370.388 210 357C210.409 355.797 210.817 354.595 211.238 353.355C216.61 338.73 225.158 325.259 234.574 312.898C236.047 311.056 236.047 311.056 237 309C237.66 309 238.32 309 239 309C239.238 308.453 239.477 307.907 239.723 307.344C241.317 304.418 243.3 302.055 245.438 299.5C248.653 295.659 248.653 295.659 251.66 291.656C252.102 291.11 252.544 290.563 253 290C253.66 290 254.32 290 255 290C255.393 289.132 255.393 289.132 255.793 288.246C258.892 282.479 264.184 277.403 269 273C269.66 273 270.32 273 271 273C271.66 271.68 272.32 270.36 273 269C273.66 269 274.32 269 275 269C275.236 268.437 275.473 267.874 275.716 267.294C277.298 264.467 279.229 262.564 281.543 260.297C282.452 259.398 283.362 258.5 284.299 257.574C285.261 256.632 286.224 255.691 287.188 254.75C293.371 248.682 299.376 242.591 305 236C307.447 233.291 309.91 230.598 312.375 227.906C313.241 226.947 314.108 225.988 315 225C315.536 224.408 316.071 223.816 316.623 223.207C321.082 218.206 325.118 212.975 329.063 207.563C329.941 206.37 329.941 206.37 330.838 205.154C337.701 195.792 343.637 186.303 349 176C349.37 175.301 349.74 174.603 350.121 173.883C351.126 171.941 352.068 169.978 353 168C353.692 166.585 353.692 166.585 354.398 165.141C358.159 157.009 360.399 149.049 362.176 140.284C363.301 134.982 364.568 131.236 368 127C372.15 124.379 374.688 124.844 379 127Z" fill="#6DB5CB"/>
<path d="M405 315C409.619 317.511 411.532 321.081 413 326C413.414 329.24 413.429 332.424 413.375 335.688C413.365 336.576 413.355 337.464 413.344 338.38C413.182 345.286 412.287 351.456 410 358C409.785 358.629 409.571 359.258 409.35 359.906C404.93 372.741 399.204 383.134 391 394C390.386 394.833 389.773 395.665 389.141 396.523C385.758 401.029 382.43 404.533 378 408C377.258 408.743 376.515 409.485 375.75 410.25C367.423 418.577 358.315 425.833 349 433C348.428 433.44 347.856 433.88 347.266 434.334C345.595 435.619 343.922 436.903 342.25 438.188C334.038 444.495 326.004 450.872 318.359 457.863C316.318 459.712 314.236 461.483 312.125 463.25C302.932 471.294 295.155 480.759 287.863 490.516C285.287 493.95 285.287 493.95 281.75 494.563C278 494 278 494 276.461 492.32C269.594 481.357 270.732 467.305 273.536 455.055C275.659 446.728 278.264 438.756 282 431C282.293 430.376 282.586 429.753 282.887 429.11C286.927 420.529 291.219 412.558 297 405C297.739 403.997 298.477 402.994 299.238 401.961C328.917 362.028 328.917 362.028 344 357C344.33 356.01 344.66 355.02 345 354C346.977 352.195 346.977 352.195 349.625 350.125C353.595 346.963 357.47 343.741 361.275 340.382C367.342 335.031 373.536 329.885 379.938 324.938C380.623 324.402 381.308 323.866 382.014 323.313C389.033 317.958 395.855 313.304 405 315Z" fill="#6FADCB"/>
<path d="M379 127C395.79 140.378 403.626 163.462 406 184C406.279 188.609 406.334 193.196 406.312 197.813C406.308 199.036 406.304 200.26 406.3 201.521C406.186 210.698 405.417 219.136 403 228C402.733 228.999 402.466 229.998 402.191 231.027C396.077 251.272 384.609 269.828 370 285C368.742 286.431 367.492 287.868 366.25 289.312C363.615 292.339 360.838 295.162 358 298C356.559 299.622 355.122 301.247 353.688 302.875C350.306 306.662 346.824 310.088 342.922 313.332C341.026 314.978 339.263 316.721 337.493 318.5C335.869 320.132 334.238 321.755 332.606 323.378C329.18 326.786 325.757 330.197 322.335 333.61C320.866 335.074 319.396 336.537 317.925 337.999C315.784 340.129 313.645 342.261 311.508 344.395C310.86 345.038 310.212 345.681 309.544 346.343C305.702 350.184 302.053 354.139 298.51 358.258C296.49 360.589 294.366 362.818 292.25 365.062C276.497 382.391 261.694 404.04 256 427C255.653 428.146 255.306 429.292 254.949 430.473C253.52 436.248 253.617 442.081 253.625 448C253.627 449.199 253.628 450.398 253.63 451.633C253.759 461.039 254.95 469.104 258 478C258.289 478.938 258.577 479.877 258.875 480.844C262.139 490.753 267.295 500.041 272.262 509.184C272.626 509.856 272.991 510.529 273.366 511.222C274.062 512.498 274.764 513.771 275.474 515.04C277.271 518.323 278.696 521.24 279 525C277.438 527.375 277.438 527.375 275 529C270.511 529.098 267.863 527.569 264.57 524.586C261.547 521.532 258.762 518.29 256 515C255.154 514.01 254.309 513.02 253.438 512C251.084 509.103 248.839 506.153 246.621 503.152C244.297 500.066 241.858 497.073 239.418 494.078C238.002 492.003 237.426 490.455 237 488C236.34 488 235.68 488 235 488C233.97 486.214 232.951 484.421 231.938 482.625C231.369 481.627 230.801 480.63 230.215 479.602C229.814 478.743 229.413 477.885 229 477C229.33 476.34 229.66 475.68 230 475C230.377 475.579 230.754 476.157 231.142 476.753C237.996 487.239 244.75 497.553 253 507C254.015 508.195 255.028 509.392 256.039 510.59C263.229 520.575 263.229 520.575 272.812 526.312C273.534 526.209 274.256 526.106 275 526C275.853 524.354 275.853 524.354 276 522C274.951 519.512 273.854 517.27 272.555 514.918C272.188 514.227 271.821 513.535 271.443 512.823C270.265 510.605 269.072 508.396 267.875 506.188C254.731 481.937 245.986 455.142 252.977 427.438C264.038 390.283 291.134 361.565 318.105 334.85C320.04 332.933 321.97 331.013 323.9 329.091C329.376 323.639 334.859 318.193 340.345 312.75C343.729 309.394 347.107 306.033 350.482 302.668C351.754 301.403 353.028 300.139 354.303 298.877C361.688 291.574 368.605 284.202 375 276C375.844 274.954 376.687 273.909 377.531 272.863C398.146 246.153 407.42 214.223 403.312 180.625C400.227 162.671 392.942 142.529 378.953 130.195C375.89 128.321 373.492 128.556 370 129C367.312 131.821 366.363 134.563 365.422 138.297C365.152 139.33 364.882 140.364 364.604 141.429C364.322 142.525 364.04 143.621 363.75 144.75C359.738 159.674 354.199 173.826 346 187C345.01 187.495 345.01 187.495 344 188C345.669 182.311 347.644 177.93 351 173C351.747 171.083 352.489 169.163 353.16 167.219C354.071 164.813 355.21 162.606 356.375 160.313C359.356 153.958 360.726 147.281 362.125 140.437C363.271 135.09 364.532 131.269 368 127C372.15 124.379 374.688 124.844 379 127Z" fill="#F6FAFB"/>
<path d="M405 315C409.619 317.511 411.532 321.081 413 326C413.414 329.24 413.429 332.424 413.375 335.688C413.365 336.576 413.355 337.464 413.344 338.38C413.182 345.286 412.287 351.456 410 358C409.785 358.629 409.571 359.258 409.35 359.906C404.93 372.741 399.204 383.134 391 394C390.386 394.833 389.773 395.665 389.141 396.523C385.758 401.029 382.43 404.533 378 408C377.258 408.743 376.515 409.485 375.75 410.25C367.423 418.577 358.315 425.833 349 433C348.428 433.44 347.856 433.88 347.266 434.334C345.595 435.619 343.922 436.903 342.25 438.188C334.038 444.495 326.004 450.872 318.359 457.863C316.318 459.712 314.236 461.483 312.125 463.25C302.932 471.294 295.155 480.759 287.863 490.516C285.287 493.95 285.287 493.95 281.75 494.563C278 494 278 494 276.461 492.32C269.594 481.357 270.732 467.305 273.536 455.055C275.659 446.728 278.264 438.756 282 431C282.293 430.376 282.586 429.753 282.887 429.11C286.927 420.529 291.219 412.558 297 405C297.739 403.997 298.477 402.994 299.238 401.961C328.917 362.028 328.917 362.028 344 357C344.33 356.01 344.66 355.02 345 354C346.977 352.195 346.977 352.195 349.625 350.125C353.595 346.963 357.47 343.741 361.275 340.382C367.342 335.031 373.536 329.885 379.938 324.938C380.623 324.402 381.308 323.866 382.014 323.313C389.033 317.958 395.855 313.304 405 315ZM366.453 338.793C363.91 341.081 361.29 343.231 358.625 345.375C353.478 349.539 348.617 353.951 343.766 358.449C341.559 360.484 339.334 362.476 337.063 364.438C307.999 389.77 277.338 423.613 274 464C272.925 478.143 272.925 478.143 278 491C279.792 493.242 279.792 493.242 282.227 493.098C286.538 491.391 288.617 488.059 291.375 484.438C292.536 482.963 293.697 481.489 294.859 480.016C295.682 478.961 295.682 478.961 296.521 477.885C305.387 466.583 315.755 456.884 327 448C328.156 447.066 329.311 446.132 330.465 445.195C336.227 440.533 342.027 435.947 347.991 431.545C378.576 408.968 404.096 383.494 411.582 345.008C412.951 335.154 412.943 326.915 407.375 318.563C394.097 309.827 375.428 330.528 366.453 338.793Z" fill="#F6FAFB"/>
<path d="M247 49C247.33 49 247.66 49 248 49C249.09 83.1458 238.058 114.068 217 141C216.261 141.949 215.523 142.897 214.762 143.875C209.986 149.847 204.889 155.495 199.687 161.094C197.257 163.722 194.874 166.384 192.5 169.062C189.239 172.704 185.851 176.19 182.375 179.625C178.733 183.232 175.258 186.887 171.934 190.785C169.901 193.113 167.807 195.371 165.697 197.628C136.542 228.861 112.198 266.666 111.762 310.742C111.754 311.55 111.746 312.358 111.737 313.19C111.724 314.893 111.713 316.595 111.705 318.298C111.693 319.998 111.673 321.698 111.643 323.398C111.365 339.354 113.379 354.721 119.187 369.688C119.628 370.847 119.628 370.847 120.076 372.029C124.572 383.445 130.501 393.904 138.566 403.18C140 405 140 405 140 407C139.34 407 138.68 407 138 407C136.249 404.761 134.611 402.523 133 400.188C132.549 399.545 132.098 398.902 131.633 398.24C126 390.199 126 390.199 126 387C125.34 387 124.68 387 124 387C122.647 384.342 121.314 381.678 120 379C119.665 378.326 119.33 377.652 118.984 376.957C114.804 368.116 112.244 358.682 111 349C110.735 347.273 110.735 347.273 110.465 345.512C106.452 316.414 107.916 283.805 121 257C121.439 256.075 121.439 256.075 121.887 255.131C127.966 242.337 135.109 230.883 143.863 219.75C146.047 217.008 146.047 217.008 148 214C148.66 214 149.32 214 150 214C150.309 212.979 150.309 212.979 150.625 211.938C152.429 208.084 154.901 204.922 158 202C158.66 202 159.32 202 160 202C160.258 201.428 160.516 200.855 160.781 200.266C162.114 197.789 163.651 195.866 165.5 193.75C166.108 193.044 166.717 192.337 167.344 191.609C169 190 169 190 171 190C171.227 189.443 171.454 188.886 171.687 188.312C173.37 185.348 175.561 183.383 178 181C179.58 179.137 181.133 177.256 182.668 175.355C183.327 174.685 183.327 174.685 184 174C184.66 174 185.32 174 186 174C186.247 173.443 186.495 172.886 186.75 172.312C189.364 167.477 193.144 163.851 197 160C204.99 151.789 212.645 143.572 219 134C219.37 133.45 219.739 132.899 220.12 132.332C224.94 125.132 229.357 117.876 233 110C233.407 109.188 233.815 108.376 234.234 107.539C242.38 90.7954 246.253 71.4274 246.012 52.8203C246.008 51.8896 246.004 50.9589 246 50C246.33 49.67 246.66 49.34 247 49Z" fill="#F4F9FA"/>
<path d="M343 188C343.33 188.66 343.66 189.32 344 190C331.853 208.813 317.84 225.954 302.568 242.319C301.475 243.491 300.384 244.665 299.295 245.841C290.177 255.689 280.826 265.269 271.27 274.693C266.842 279.077 262.573 283.514 258.518 288.246C256.428 290.661 254.208 292.943 252 295.25C240.348 307.775 230.152 321.964 222 337C221.649 337.644 221.297 338.288 220.935 338.951C205.758 367.095 200.951 398.966 210 430C211.277 433.944 212.64 437.852 214.157 441.711C216.097 446.708 216.097 446.708 215 450C211.833 444.09 209.667 438.328 207.875 431.875C207.616 430.946 207.358 430.018 207.091 429.061C200.493 404.726 201.688 380.701 210 357C210.409 355.797 210.817 354.595 211.238 353.355C216.61 338.73 225.158 325.259 234.574 312.898C236.047 311.056 236.047 311.056 237 309C237.66 309 238.32 309 239 309C239.238 308.453 239.477 307.907 239.723 307.344C241.317 304.418 243.3 302.055 245.438 299.5C248.653 295.659 248.653 295.659 251.66 291.656C252.102 291.11 252.544 290.563 253 290C253.66 290 254.32 290 255 290C255.262 289.421 255.523 288.842 255.793 288.246C258.892 282.479 264.184 277.403 269 273C269.66 273 270.32 273 271 273C271.99 271.02 271.99 271.02 273 269C273.66 269 274.32 269 275 269C275.236 268.437 275.473 267.874 275.716 267.294C277.298 264.467 279.229 262.564 281.543 260.297C282.907 258.949 282.907 258.949 284.299 257.574C285.261 256.632 286.224 255.691 287.188 254.75C293.371 248.682 299.376 242.591 305 236C307.447 233.291 309.91 230.598 312.375 227.906C313.241 226.947 314.108 225.988 315 225C315.803 224.112 315.803 224.112 316.623 223.207C321.082 218.206 325.118 212.975 329.063 207.562C329.648 206.768 330.234 205.973 330.838 205.154C334.302 200.429 337.625 195.633 340.834 190.73C342 189 342 189 343 188Z" fill="#F3F9FA"/>
<path d="M253.695 12.8086C267.545 18.1454 278.508 26.7596 289 37C289.655 37.593 290.31 38.1859 290.984 38.7969C303.486 50.3805 313.602 69.468 317.625 85.9375C317.865 86.8953 318.105 87.853 318.352 88.8398C321.858 105.93 321.511 125.165 317 142C316.801 142.775 316.603 143.55 316.398 144.349C312.242 160.307 305.651 173.799 297.188 187.875C296.719 188.655 296.251 189.435 295.769 190.239C295.323 190.962 294.877 191.685 294.418 192.43C294.024 193.071 293.63 193.713 293.223 194.375C292 196 292 196 289 198C290.298 194.269 292.111 191.135 294.274 187.839C304.69 171.957 311.608 155.475 316 137C316.274 135.859 316.274 135.859 316.553 134.696C317.119 131.28 317.201 127.907 317.238 124.453C317.25 123.359 317.25 123.359 317.263 122.243C317.277 120.696 317.287 119.15 317.295 117.603C317.307 116.056 317.327 114.508 317.357 112.961C317.903 84.5806 306.918 59.1528 287.536 38.5361C277.941 28.9411 262.328 16.5497 248.602 14.6719C245.001 14.4847 245.001 14.4847 242 16C241.551 21.2299 242.887 25.3691 244.5 30.25C246.54 36.7257 247.543 42.1537 247 49C244.898 45.4192 244.295 41.8735 243.563 37.8125C242.744 33.4129 241.838 29.155 240.313 24.9375C239.093 21.4941 238.386 18.6867 239 15C242.749 10.3143 248.534 11.2602 253.695 12.8086Z" fill="#F4F9FA"/>
<path d="M195 309C195.495 309.99 195.495 309.99 196 311C194.895 313.323 193.73 315.556 192.5 317.812C174.962 351.43 172.673 389.68 183.555 425.656C185.252 430.992 187.537 436.007 189.961 441.047C191.111 444.317 191.39 445.82 190 449C187.707 450.5 187.707 450.5 185 451C182.824 449.875 182.824 449.875 180.688 448C179.919 447.346 179.151 446.693 178.359 446.02C177.191 445.02 177.191 445.02 176 444C173.826 442.398 171.634 440.827 169.434 439.262C168.961 438.845 168.487 438.429 168 438C168 437.34 168 436.68 168 436C167.165 435.629 167.165 435.629 166.312 435.25C161.731 432.774 158.604 429.605 155.375 425.559C153.909 423.896 152.35 422.577 150.625 421.188C146.376 417.686 143.235 413.424 140 409C140.495 408.01 140.495 408.01 141 407C141.382 407.412 141.764 407.825 142.158 408.25C151.431 418.248 160.618 428.136 171 437C172.097 437.96 173.193 438.921 174.289 439.883C175.463 440.901 176.638 441.92 177.812 442.938C178.376 443.435 178.94 443.932 179.52 444.445C182.779 447.377 182.779 447.377 187 448C188.172 444.483 187.807 443.854 186.453 440.535C186.104 439.659 185.755 438.782 185.395 437.879C185.017 436.95 184.639 436.02 184.25 435.062C177.374 417.544 174.599 400.612 174.625 381.812C174.626 381.142 174.626 380.472 174.627 379.781C174.67 368.119 175.794 357.221 179 346C179.214 345.234 179.429 344.469 179.649 343.68C183.146 331.525 187.958 319.563 195 309Z" fill="#F6FAFB"/>
<path d="M288 198C288.495 198.99 288.495 198.99 289 200C281.973 209.266 274.548 217.918 266.529 226.335C264.35 228.708 262.316 231.196 260.29 233.699C259.864 234.129 259.439 234.558 259 235C258.34 235 257.68 235 257 235C256.616 235.875 256.616 235.875 256.225 236.767C254.88 239.218 253.412 240.786 251.402 242.723C250.33 243.77 250.33 243.77 249.236 244.838C248.478 245.572 247.719 246.306 246.938 247.062C241.254 252.647 235.913 258.216 231 264.492C228.084 268.149 224.919 271.566 221.75 275.004C219.952 276.885 219.952 276.885 219 279C218.34 279 217.68 279 217 279C218.679 274.989 221.428 271.93 224.25 268.688C224.776 268.079 225.302 267.471 225.843 266.845C234.467 256.943 243.484 247.425 252.828 238.203C255.47 235.523 257.919 232.711 260.363 229.852C261.964 228.041 263.637 226.366 265.375 224.688C269.086 221.035 272.314 217.052 275.578 213C278.207 209.743 280.878 206.524 283.562 203.312C284.004 202.783 284.446 202.254 284.902 201.708C285.934 200.472 286.967 199.236 288 198Z" fill="#F9FBFC"/>
<path d="M305 239C305 242 305 242 303.687 243.605C303.131 244.128 302.574 244.65 302 245.188C301.443 245.717 300.886 246.247 300.312 246.793C299.879 247.191 299.446 247.59 299 248C299.99 248.66 300.98 249.32 302 250C300.857 253.43 299.716 254.661 297 257C294.25 258.25 294.25 258.25 292 259C291.629 255.257 291.648 253.507 293.828 250.367C295.027 249.134 295.027 249.134 296.25 247.875C297.039 247.047 297.828 246.22 298.641 245.367C299.419 244.586 300.198 243.805 301 243C301.758 242.239 302.516 241.479 303.297 240.695C304.14 239.856 304.14 239.856 305 239Z" fill="#65B1C6"/>
<path d="M355 298C355.66 298.33 356.32 298.66 357 299C352.536 304.223 347.926 309.209 343 314C342.01 313.67 341.02 313.34 340 313C344.95 308.05 349.9 303.1 355 298Z" fill="#EFF7F8"/>
<path d="M216 450C219.324 454.259 221.733 458.861 224.25 463.625C224.722 464.496 225.194 465.368 225.68 466.266C226.121 467.098 226.561 467.931 227.016 468.789C227.421 469.549 227.826 470.309 228.243 471.092C228.493 471.722 228.743 472.351 229 473C228.67 473.66 228.34 474.32 228 475C225.018 471.504 222.957 468.148 221 464C220.572 463.187 220.144 462.373 219.703 461.535C219.244 460.657 218.785 459.779 218.312 458.875C217.876 458.047 217.439 457.22 216.988 456.367C216 454 216 454 216 450Z" fill="#F4F9FA"/>
<path d="M398 232C399.523 236.618 397.706 239.729 395.75 243.938C395.437 244.641 395.124 245.344 394.801 246.068C393.085 249.868 391.237 253.472 389 257C388.67 256.34 388.34 255.68 388 255C388.33 254.01 388.66 253.02 389 252C389 251.01 389 250.02 389 249C389.603 248.417 390.207 247.835 390.828 247.234C393.526 244.459 394.444 241.877 395.75 238.25C396.178 237.08 396.606 235.909 397.047 234.703C397.361 233.811 397.676 232.919 398 232Z" fill="#4E96BB"/>
<path d="M401 215C401.66 215 402.32 215 403 215C402.221 226.522 402.221 226.522 400 232C399.005 228.8 399.005 226.257 399.375 222.938C399.465 222.08 399.555 221.223 399.648 220.34C400 218 400 218 401 215Z" fill="#5B9CC2"/>
<path d="M355 168C355.33 168.99 355.66 169.98 356 171C356.66 171 357.32 171 358 171C357.67 172.32 357.34 173.64 357 175C356.34 175 355.68 175 355 175C354.67 175.66 354.34 176.32 354 177C352.354 178.687 350.688 180.355 349 182C350.492 177.028 352.605 172.582 355 168Z" fill="#61A4C4"/>
<path d="M388 395C388 399.023 386.676 400.043 384 403C382.051 404.745 380.049 406.37 378 408C377.34 407.67 376.68 407.34 376 407C379.96 403.04 383.92 399.08 388 395Z" fill="#F0F6F8"/>
<path d="M282 201C282.99 201 283.98 201 285 201C283.636 203.992 282.051 206.29 279.875 208.75C279.067 209.67 279.067 209.67 278.242 210.609C277.832 211.068 277.422 211.527 277 212C276.67 211.01 276.34 210.02 276 209C276.99 208.34 277.98 207.68 279 207C279.372 205.677 279.706 204.343 280 203C280.66 202.34 281.32 201.68 282 201Z" fill="#59ACC3"/>
<path d="M113 287C113.99 287 114.98 287 116 287C115.67 289.97 115.34 292.94 115 296C114.01 296 113.02 296 112 296C112.33 293.03 112.66 290.06 113 287Z" fill="#71CCBD"/>
<path d="M234 256C234 259 234 259 232.469 260.605C231.819 261.128 231.169 261.65 230.5 262.188C229.85 262.717 229.201 263.247 228.531 263.793C228.026 264.191 227.521 264.59 227 265C226.67 263.35 226.34 261.7 226 260C226.763 259.897 227.526 259.794 228.312 259.688C231.171 258.956 232.157 258.23 234 256Z" fill="#57AFC0"/>
<path d="M355 348C355.66 348.33 356.32 348.66 357 349C355.02 351.97 355.02 351.97 353 355C351.68 354.67 350.36 354.34 349 354C350.98 352.02 352.96 350.04 355 348Z" fill="#5C9FC2"/>
<path d="M209 375C209.054 376.458 209.093 377.916 209.125 379.375C209.148 380.187 209.171 380.999 209.195 381.836C209 384 209 384 207 386C206.67 382.7 206.34 379.4 206 376C208 375 208 375 209 375Z" fill="#77C4C9"/>
<path d="M390 248C390.66 248 391.32 248 392 248C391.385 253.538 391.385 253.538 389.438 255.875C388.963 256.246 388.489 256.618 388 257C388.165 256.237 388.33 255.474 388.5 254.688C389.091 251.964 389.091 251.964 389 249C389.33 248.67 389.66 248.34 390 248Z" fill="#5E9FC4"/>
<path d="M186 174C184.509 177.795 182.243 179.61 179 182C181.811 174 181.811 174 186 174Z" fill="#FAFDFD"/>
<path d="M275 485C277 488 279 491 281 494C279.188 493.875 279.188 493.875 277 493C275.188 490.062 275.188 490.062 274 487C274.33 486.34 274.66 485.68 275 485Z" fill="#F0F8F8"/>
<path d="M368 130C368.955 132.17 369.144 133.563 368.398 135.824C367.641 137.568 366.824 139.286 366 141C365.67 141 365.34 141 365 141C365.75 133.375 365.75 133.375 368 130Z" fill="#579CBF"/>
<path d="M369 126C372.125 126.375 372.125 126.375 375 127C375 127.33 375 127.66 375 128C374.072 128.268 373.144 128.536 372.188 128.812C369.238 129.911 367.944 130.624 366 133C366.75 128.25 366.75 128.25 369 126Z" fill="#EAF5F5"/>
<path d="M136 234C136.75 235.75 136.75 235.75 137 238C135.062 240.25 135.062 240.25 133 242C132.67 241.34 132.34 240.68 132 240C133.32 238.02 134.64 236.04 136 234Z" fill="#5EC4B4"/>
<path d="M175 187C175.66 187.33 176.32 187.66 177 188C176.67 189.98 176.34 191.96 176 194C175.01 193.34 174.02 192.68 173 192C173.875 188.125 173.875 188.125 175 187Z" fill="#68C6BC"/>
</svg>

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
"use strict";"require ui";"require view";return view.extend({render(){const form=document.querySelector("form");const btn=document.querySelector("button");const dlg=ui.showModal(_("Authorization Required"),Array.from(document.querySelectorAll("section > *")),"login");form.addEventListener("keypress",ev=>{if(ev.key==="Enter")btn.click()});btn.addEventListener("click",()=>{dlg.querySelectorAll("*").forEach(node=>{node.style.display="none"});dlg.appendChild(E("div",{class:"spinning"},_("Logging in…")));form.submit()});document.querySelector('input[type="password"]').focus();return""},addFooter(){}});

View File

@@ -0,0 +1,12 @@
#!/bin/sh
if [ "$PKG_UPGRADE" != 1 ]; then
uci get luci.themes.Aurora >/dev/null 2>&1 || \
uci batch <<-EOF
set luci.themes.Aurora=/luci-static/aurora
set luci.main.mediaurlbase=/luci-static/aurora
commit luci
EOF
fi
exit 0

View File

@@ -0,0 +1,40 @@
{% if (!blank_page): %}
<footer>
<span>
Powered by
<a href="https://github.com/openwrt/luci" target="_blank" rel="noreferrer">
{{ version.luciname }} ({{ version.luciversion }})</a>
/
<a href="{{ entityencode(version.disturl ?? '#', true) }}" target="_blank" rel="noreferrer">
{{ version.distname }} {{ version.distversion }} ({{ version.distrevision }})</a>
{% if (lua_active): %}
/ {{ _('Lua compatibility mode active') }}
{% endif %}
</span>
<ul class="breadcrumb pull-right" id="modemenu" style="display:none"></ul>
<div class="theme-switcher">
<label class="theme-option active" data-theme="device">
<input type="radio" name="theme" value="device" checked>
<span class="theme-icon">
<svg height="16" stroke-linejoin="round" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" clip-rule="evenodd" d="M0.5 3.25C0.5 1.45507 1.95507 0 3.75 0H8.25C10.0449 0 11.5 1.45507 11.5 3.25V11.25V12H10.75H1.25H0.5V11.25V3.25ZM3.75 1.5C2.7835 1.5 2 2.2835 2 3.25V10.5H10V3.25C10 2.2835 9.2165 1.5 8.25 1.5H3.75ZM3 3.5C3 2.94772 3.44772 2.5 4 2.5H8C8.55228 2.5 9 2.94772 9 3.5V7H3V3.5ZM6.5 9.5H9V8H6.5V9.5Z" fill="currentColor" transform="translate(2.5, 2)"></path></svg>
</span>
</label>
<label class="theme-option" data-theme="light">
<input type="radio" name="theme" value="light">
<span class="theme-icon">
<svg height="16" stroke-linejoin="round" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.75 1V0.25H6.25V1V1.25V2H7.75V1.25V1ZM7 9C8.10457 9 9 8.10457 9 7C9 5.89543 8.10457 5 7 5C5.89543 5 5 5.89543 5 7C5 8.10457 5.89543 9 7 9ZM7 10.5C8.933 10.5 10.5 8.933 10.5 7C10.5 5.067 8.933 3.5 7 3.5C5.067 3.5 3.5 5.067 3.5 7C3.5 8.933 5.067 10.5 7 10.5ZM7.75 12V12.75V13V13.75H6.25V13V12.75V12H7.75ZM12 6.25H12.75H13H13.75V7.75H13H12.75H12V6.25ZM1 6.25H0.25V7.75H1H1.25H2V6.25H1.25H1ZM10.0052 2.93414L10.5355 2.40381L10.7123 2.22703L11.2426 1.6967L12.3033 2.75736L11.773 3.28769L11.5962 3.46447L11.0659 3.9948L10.0052 2.93414ZM2.22703 10.7123L1.6967 11.2426L2.75736 12.3033L3.28769 11.773L3.46447 11.5962L3.9948 11.0659L2.93414 10.0052L2.40381 10.5355L2.22703 10.7123ZM2.93414 3.9948L2.40381 3.46447L2.22703 3.28769L1.6967 2.75736L2.75736 1.6967L3.28769 2.22703L3.46447 2.40381L3.9948 2.93414L2.93414 3.9948ZM10.7123 11.773L11.2426 12.3033L12.3033 11.2426L11.773 10.7123L11.5962 10.5355L11.0659 10.0052L10.0052 11.0659L10.5355 11.5962L10.7123 11.773Z" fill="currentColor" transform="translate(1.25, 1.25)"></path></svg>
</span>
</label>
<label class="theme-option" data-theme="dark">
<input type="radio" name="theme" value="dark">
<span class="theme-icon">
<svg height="16" stroke-linejoin="round" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.5 0.25V1V1.5H11L11.75 1.5V3H11H10.5V3.5V4.25H9V3.5V3H8.5H7.75V1.5H8.5H9V1V0.25H10.5ZM3.25514 2.75496C2.33413 3.53491 1.75 4.69972 1.75 6C1.75 8.34721 3.65279 10.25 6 10.25C7.30029 10.25 8.4651 9.66587 9.24505 8.74485C9.16377 8.74827 9.08207 8.74999 9 8.74999C5.82436 8.74999 3.25 6.17563 3.25 2.99999C3.25 2.91792 3.25172 2.83623 3.25514 2.75496ZM0.25 6C0.25 3.51072 1.83142 1.39271 4.042 0.592193L5.00256 1.55275C4.83933 2.00347 4.75 2.49047 4.75 2.99999C4.75 5.3472 6.65279 7.24999 9 7.24999C9.50953 7.24999 9.99653 7.16065 10.4473 6.99743L11.4078 7.95798C10.6073 10.1686 8.48929 11.75 6 11.75C2.82436 11.75 0.25 9.17564 0.25 6Z" fill="currentColor" transform="translate(2.25, 2.25)"></path></svg>
</span>
</label>
</div>
</footer>
</div>
<script>L.require('menu-aurora')</script>
{% endif %}
</body>
</html>

View File

@@ -0,0 +1,163 @@
{#
luci-theme-aurora:
Copyright 2025 eamonxg <eamonxiong@gmail.com>
This theme is derived from luci-theme-bootstrap UT template.
luci-theme-bootstrap:
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2012 David Menting <david@nut-bolt.nl>
Copyright 2008-2022 Jo-Philipp Wich <jo@mein.io>
Licensed to the public under the Apache License 2.0.
-#}
{%
import { getuid, getspnam } from 'luci.core';
const boardinfo = ubus.call('system', 'board');
http.prepare_content('text/html; charset=UTF-8');
-%}
<!DOCTYPE html>
<html lang="{{ dispatcher.lang }}">
<head>
<meta charset="utf-8">
<title>{{ striptags(`${boardinfo.hostname ?? '?'}${node ? ` - ${node.title}` : ''}`) }} - LuCI</title>
<script>
const setTheme = (theme) => {
const current = ['light', 'dark', 'device'].includes(theme) ? theme : 'device';
const isDark = current === 'dark' || (current === 'device' && window.matchMedia('(prefers-color-scheme: dark)').matches);
document.documentElement.setAttribute('data-darkmode', isDark);
localStorage.setItem('aurora.theme', current);
};
const syncSwitchers = () => {
const current = localStorage.getItem('aurora.theme') || 'device';
document.querySelectorAll('.theme-switcher .theme-option').forEach(opt => {
const active = opt.dataset.theme === current;
opt.classList.toggle('active', active);
opt.querySelector('input[type="radio"]')?.setAttribute('checked', active);
});
};
setTheme(localStorage.getItem('aurora.theme'));
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
const theme = localStorage.getItem('aurora.theme') || 'device';
if (theme === 'device') setTheme('device'), syncSwitchers();
});
document.addEventListener('DOMContentLoaded', () => {
syncSwitchers();
document.querySelectorAll('.theme-switcher .theme-option').forEach(opt => {
opt.addEventListener('click', () => (setTheme(opt.dataset.theme), syncSwitchers()));
});
});
</script>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta name="theme-color" content="#f8fafc" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#0f172a" media="(prefers-color-scheme: dark)">
<meta name="color-scheme" content="light dark">
<link rel="stylesheet" href="{{ media }}/main.css">
<link rel="icon" href="{{ media }}/images/logo_48.png" sizes="48x48">
<link rel="icon" href="{{ media }}/images/logo.svg" sizes="any">
{% if (node?.css): %}
<link rel="stylesheet" href="{{ resource }}/{{ node.css }}">
{% endif %}
{% if (css): %}
<style title="text/css">{{ css }}</style>
{% endif %}
<script src="{{ dispatcher.build_url('admin/translations', dispatcher.lang) }}"></script>
<script src="{{ resource }}/cbi.js"></script>
</head>
<body class="lang_{{ dispatcher.lang }} {{ entityencode(striptags(node?.title ?? ''), true) }}" data-page="{{ entityencode(join('-', ctx.request_path), true) }}">
{% if (!blank_page): %}
<header>
<div class="header-content">
<a class="brand" href="/">{{ striptags(boardinfo.hostname ?? '?') }}</a>
<ul class="nav" id="topmenu" style="display:none"></ul>
<div class="mobile-controls">
<div class="mobile-menu-toggle" id="mobile-menu-btn" title="{{ _('Menu') }}">
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path d="M195.75 162H767.25C771.41 161.987 775.532 162.99 779.38 164.954C783.227 166.917 786.726 169.802 789.674 173.442C792.623 177.083 794.964 181.409 796.564 186.172C798.164 190.935 798.992 196.043 799 201.204V208.667C798.992 213.333 798.164 218 796.564 222.667C794.964 227.333 792.623 231.067 789.674 234.707C786.726 238.347 783.227 241.24 779.38 243.2C775.532 245.16 771.41 246 767.25 246H195.75C191.59 246 187.468 245.16 183.62 243.2C179.773 241.24 176.275 238.347 173.326 234.707C170.378 231.067 168.036 227.333 166.436 222.667C164.836 218 164.008 213.333 164 208.667V201.204C164.008 196.043 164.836 190.935 166.436 186.172C168.036 181.409 170.378 177.083 173.326 173.442C176.275 169.802 179.773 166.917 183.62 164.954C187.468 162.99 191.59 161.987 195.75 162Z" fill="currentColor"/>
<path d="M354.733 458H767.267C771.424 457.987 775.544 459.001 779.39 460.987C783.235 462.972 786.732 465.888 789.679 469.57C792.626 473.25 794.966 477.624 796.565 482.441C798.164 487.256 798.992 492.421 799 497.639V504.252C798.992 508.971 798.164 513.689 796.565 518.408C794.966 523.126 792.626 526.901 789.679 530.581C786.732 534.262 783.235 537.187 779.39 539.169C775.544 541.151 771.424 542 767.267 542H354.733C350.577 542 346.459 541.151 342.615 539.169C338.771 537.187 335.276 534.262 332.328 530.581C329.382 526.901 327.041 523.126 325.441 518.408C323.841 513.689 323.011 508.971 323 504.252V497.639C323.008 492.421 323.836 487.256 325.435 482.441C327.034 477.624 329.375 473.25 332.321 469.57C335.269 465.888 338.765 462.972 342.61 460.987C346.456 459.001 350.576 457.987 354.733 458Z" fill="currentColor"/>
<path d="M195.75 758H767.25C771.41 758 775.534 758.933 779.381 760.941C783.229 762.907 786.727 765.793 789.675 769.436C792.624 773.079 794.964 777.407 796.564 782.171C798.164 786.936 798.992 792.045 799 797.207V804.667C798.989 809.333 798.16 814 796.558 818.667C794.957 823.333 792.615 827.067 789.667 830.707C786.718 834.347 783.221 837.24 779.375 839.2C775.529 841.067 771.409 842 767.25 842H195.75C191.59 842 187.468 841.067 183.62 839.2C179.773 837.24 176.275 834.347 173.326 830.707C170.378 827.067 168.036 823.333 166.436 818.667C164.836 814 164.008 809.333 164 804.667V797.207C164.008 792.047 164.836 786.939 166.436 782.175C168.036 777.411 170.378 773.086 173.326 769.445C176.275 765.805 179.773 762.921 183.62 760.957C187.468 758.994 191.59 758 195.75 758Z" fill="currentColor"/>
</svg>
</div>
</div>
<div id="indicators" class="pull-right"></div>
</div>
<div class="desktop-menu-container"></div>
</header>
<div class="desktop-menu-overlay"></div>
<div class="mobile-menu-overlay" id="mobile-menu-overlay">
<nav class="mobile-nav">
<div class="mobile-nav-header">
<div class="theme-switcher mobile">
<label class="theme-option active" data-theme="device">
<input type="radio" name="mobile-theme" value="device" checked>
<span class="theme-icon">
<svg height="16" stroke-linejoin="round" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" clip-rule="evenodd" d="M0.5 3.25C0.5 1.45507 1.95507 0 3.75 0H8.25C10.0449 0 11.5 1.45507 11.5 3.25V11.25V12H10.75H1.25H0.5V11.25V3.25ZM3.75 1.5C2.7835 1.5 2 2.2835 2 3.25V10.5H10V3.25C10 2.2835 9.2165 1.5 8.25 1.5H3.75ZM3 3.5C3 2.94772 3.44772 2.5 4 2.5H8C8.55228 2.5 9 2.94772 9 3.5V7H3V3.5ZM6.5 9.5H9V8H6.5V9.5Z" fill="currentColor" transform="translate(2.5, 2)"></path></svg>
</span>
</label>
<label class="theme-option" data-theme="light">
<input type="radio" name="mobile-theme" value="light">
<span class="theme-icon">
<svg height="16" stroke-linejoin="round" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.75 1V0.25H6.25V1V1.25V2H7.75V1.25V1ZM7 9C8.10457 9 9 8.10457 9 7C9 5.89543 8.10457 5 7 5C5.89543 5 5 5.89543 5 7C5 8.10457 5.89543 9 7 9ZM7 10.5C8.933 10.5 10.5 8.933 10.5 7C10.5 5.067 8.933 3.5 7 3.5C5.067 3.5 3.5 5.067 3.5 7C3.5 8.933 5.067 10.5 7 10.5ZM7.75 12V12.75V13V13.75H6.25V13V12.75V12H7.75ZM12 6.25H12.75H13H13.75V7.75H13H12.75H12V6.25ZM1 6.25H0.25V7.75H1H1.25H2V6.25H1.25H1ZM10.0052 2.93414L10.5355 2.40381L10.7123 2.22703L11.2426 1.6967L12.3033 2.75736L11.773 3.28769L11.5962 3.46447L11.0659 3.9948L10.0052 2.93414ZM2.22703 10.7123L1.6967 11.2426L2.75736 12.3033L3.28769 11.773L3.46447 11.5962L3.9948 11.0659L2.93414 10.0052L2.40381 10.5355L2.22703 10.7123ZM2.93414 3.9948L2.40381 3.46447L2.22703 3.28769L1.6967 2.75736L2.75736 1.6967L3.28769 2.22703L3.46447 2.40381L3.9948 2.93414L2.93414 3.9948ZM10.7123 11.773L11.2426 12.3033L12.3033 11.2426L11.773 10.7123L11.5962 10.5355L11.0659 10.0052L10.0052 11.0659L10.5355 11.5962L10.7123 11.773Z" fill="currentColor" transform="translate(1.25, 1.25)"></path></svg>
</span>
</label>
<label class="theme-option" data-theme="dark">
<input type="radio" name="mobile-theme" value="dark">
<span class="theme-icon">
<svg height="16" stroke-linejoin="round" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.5 0.25V1V1.5H11L11.75 1.5V3H11H10.5V3.5V4.25H9V3.5V3H8.5H7.75V1.5H8.5H9V1V0.25H10.5ZM3.25514 2.75496C2.33413 3.53491 1.75 4.69972 1.75 6C1.75 8.34721 3.65279 10.25 6 10.25C7.30029 10.25 8.4651 9.66587 9.24505 8.74485C9.16377 8.74827 9.08207 8.74999 9 8.74999C5.82436 8.74999 3.25 6.17563 3.25 2.99999C3.25 2.91792 3.25172 2.83623 3.25514 2.75496ZM0.25 6C0.25 3.51072 1.83142 1.39271 4.042 0.592193L5.00256 1.55275C4.83933 2.00347 4.75 2.49047 4.75 2.99999C4.75 5.3472 6.65279 7.24999 9 7.24999C9.50953 7.24999 9.99653 7.16065 10.4473 6.99743L11.4078 7.95798C10.6073 10.1686 8.48929 11.75 6 11.75C2.82436 11.75 0.25 9.17564 0.25 6Z" fill="currentColor" transform="translate(2.25, 2.25)"></path></svg>
</span>
</label>
</div>
<div class="mobile-nav-close" id="mobile-nav-close">
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path d="M788.425 758H210.575C193.137 758 179 774.691 179 795.281V804.719C179 825.309 193.137 842 210.575 842H788.425C805.863 842 820 825.309 820 804.719V795.281C820 774.691 805.863 758 788.425 758Z" fill="currentColor"/>
<path d="M474.439 458H210.561C193.131 458 179 474.552 179 494.969V505.031C179 525.448 193.131 542 210.561 542H474.439C491.869 542 506 525.448 506 505.031V494.969C506 474.552 491.869 458 474.439 458Z" fill="currentColor"/>
<path d="M474.439 162H210.561C193.131 162 179 178.529 179 198.918V209.082C179 229.471 193.131 246 210.561 246H474.439C491.869 246 506 229.471 506 209.082V198.918C506 178.529 491.869 162 474.439 162Z" fill="currentColor"/>
<path d="M869.193 312.212L713.513 135.652C695.095 114.783 665.231 114.783 646.813 135.652C628.396 156.521 628.396 190.413 646.813 211.282L769.096 350L646.813 488.718C628.396 509.64 628.396 543.479 646.813 564.348C665.231 585.217 695.095 585.217 713.513 564.348L869.145 387.842C887.61 366.973 887.61 333.081 869.193 312.212Z" fill="currentColor"/>
</svg>
</div>
</div>
<ul class="mobile-nav-list" id="mobile-nav-list">
</ul>
</nav>
</div>
<div id="maincontent">
{% if (getuid() == 0 && getspnam('root')?.pwdp === ''): %}
<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>
{% endif %}

View File

@@ -0,0 +1,51 @@
{#
luci-theme-aurora:
Copyright 2025 eamonxg <eamonxiong@gmail.com>
This theme is derived from luci-theme-bootstrap UT template.
luci-theme-bootstrap:
Copyright 2022 Jo-Philipp Wich <jo@mein.io>
Licensed to the public under the Apache License 2.0.
-#}
{% include('header', { blank_page: true }) %}
<section hidden>
<form method="post" class="cbi-map">
<div class="cbi-section">
<div class="cbi-section-node">
<div class="cbi-value">
<label class="cbi-value-title" for="luci_username">{{ _('Username') }}</label>
<div class="cbi-value-field">
<input name="luci_username" id="luci_username" type="text" autocomplete="username" value="{{ entityencode(duser, true) }}">
</div>
</div>
<div class="cbi-value">
<label class="cbi-value-title" for="luci_password">{{ _('Password') }}</label>
<div class="cbi-value-field">
<input name="luci_password" id="luci_password" type="password" autocomplete="current-password">
</div>
</div>
{% if (fuser): %}
<div class="alert-message error">
{{ _('Invalid username and/or password! Please try again.') }}
</div>
{% endif %}
</div>
</div>
</form>
<hr>
<button class="btn cbi-button-positive important">{{ _('Log in') }}</button>
</section>
<div id="view">
<div class="spinning">{{ _('Loading view…') }}</div>
<script>
L.require('ui').then(function(ui) {
ui.instantiateView('aurora.sysauth');
});
</script>
</div>
{% include('footer', { blank_page: true }) %}

201
luci-theme-kucat/LICENSE Normal file
View File

@@ -0,0 +1,201 @@
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.

23
luci-theme-kucat/Makefile Normal file
View File

@@ -0,0 +1,23 @@
#
# Copyright (C) 2019-2025 The Sirpdboy Team <herboy2008@gmail.com>
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
THEME_NAME:=kucat
THEME_TITLE:=Kucat Theme
PKG_NAME:=luci-theme-$(THEME_NAME)
LUCI_TITLE:=Kucat Theme by sirpdboy
LUCI_DEPENDS:=+wget +curl +jsonfilter
PKG_VERSION:=3.1.4
PKG_RELEASE:=20251126
define Package/luci-theme-$(THEME_NAME)/conffiles
/www/luci-static/resources/background/
/www/luci-static/kucat/background/
endef
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

153
luci-theme-kucat/README.md Normal file
View File

@@ -0,0 +1,153 @@
<!-- markdownlint-configure-file {
"MD013": {
"code_blocks": false,
"tables": false,
"line_length":200
},
"MD033": false,
"MD041": false
} -->
[lede]: https://github.com/coolsnowwolf/lede
[official]: https://github.com/openwrt/openwrt
[immortalwrt]: https://github.com/immortalwrt/immortalwrt
![hello](https://views.whatilearened.today/views/github/sirpdboy/deplives.svg) [![](https://img.shields.io/badge/TG群-点击加入-FFFFFF.svg)](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ)
# KuCat Theme Config Plugin
You can set KuCat theme font size, color scheme, shortcut tools,
and manage login and desktop background images here.
<p align="center">
<a target="_blank" href="https://github.com/sirpdboy/luci-theme-kucat/releases">
<img src="https://img.shields.io/github/release/sirpdboy/luci-theme-kucat.svg?style=flat-square&label=luci-theme-kucat&colorB=green">
</a>
</p>
[中文](README_CN.md) | [English]
![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明1.jpg)
# [luci-theme-kucat](https://github.com/sirpdboy/luci-theme-kucat)
- Development time: December 2021
- Release time: February 2023
- Open source date: April 2023
- Main supports LEDE18.06 and official 18.06 branch open source time: April 2023 version: 1.2.6
- JS supports official website 19.07-24.10 branch latest version: May 2025 version: 2.5.9 compatible with OPENWRT 24.10 branch.
- UCode version supports official website branch 19.07-24.10 latest version: November 2025 version: 3.1.2 compatible with OPENWRT latest version.
- The new version no longer supports the advancedplus plugin. Please use the matching Luci app ucat config dedicated JS version plugin.
# Function Introduction
- Being cool is beauty, born from movement.
- A toolbar with 5 shortcut keys.
- Basic BUG, adapted to all plugins currently available
- We have made a lot of optimizations for mobile phones and other devices
- There are matching theme setting tools, which can adjust shortcut keys, background images, and various color schemes to choose from.
# As mentioned earlier, the development of the Cool Cat theme drew inspiration and borrowed some code from Opentopd theme, Jerryk God's Argon theme, and Thinktip God's Neobird theme. Thank you for your valuable contributions!
# Special Instructions
- When there are advanced settings and KUCAT theme setting tools in the system, the settings in the advanced settings shall prevail.
- If there are any issues with the settings, you can restore them using function 4 after logging in via SSH.
- If it is an advanced setting, to restore the default settings, you can use:/etc/init.d/advancedplus reset
- If it is the KUCAT setting tool, to restore the default settings, you can use:/etc/init.d/kucat reset
## describe
- luci-theme-kucat https://github.com/sirpdboy/luci-theme-kucat
- luci-app-kucat-config https://github.com/sirpdboy/luci-app-kucat-config
## Branch Introduction
There are currently two main branches that are adapted to different versions of the **OpenWrt** source code.
The table below will provide a detailed introduction:
| Branch | Version | Description | Matching source |
| ------ | ------- | ---------------------------------- | --------------------------------------------------------- |
| master | v3.x.x | Support the latest version of LuCI | [Official OpenWrt][official] • [ImmortalWrt][immortalwrt] |
| 23.05 | v2.x.x | Support the 23.05 version of LuCI | [Official OpenWrt][official] • [ImmortalWrt][immortalwrt] |
| 18.06 | v1.x.x | Support the 18.06 version of LuCI | [Lean's LEDE][lede] |
## Getting started
### Build for Lean's LEDE project
```bash
rm -rf luci-app-kucat-config # if have
git clone -b 18.06 https://github.com/sirpdboy/luci-theme-kucat.git luci-theme-kucat
make menuconfig #choose LUCI->Theme->luci-theme-kucat
make -j1 V=s
```
### Build for OpenWrt official SnapShots and ImmortalWrt
```bash
cd openwrt/package
git clone https://github.com/sirpdboy/luci-theme-kucat.git
make menuconfig #choose LUCI->Theme->Luci-theme-kucat
make -j1 V=s
```bash
### Install for LuCI 18.06 ( Lean's LEDE )
```bash
wget --no-check-certificate https://github.com/sirpdboy/luci-theme-kucat/releases/download/v1.5.6/luci-theme-kucat_1.5.6-20240302_all.ipk
opkg install luci-theme-kucat*.ipk
```
### Install for OpenWrt official SnapShots and ImmortalWrt
```bash
wget --no-check-certificate https://github.com/sirpdboy/luci-theme-kucat/releases/download/v2.6.18/luci-theme-kucat_2.6.18-r20251018_all.ipk
opkg install luci-theme-kucat*.ipk
```
### Install luci-app-kucat-config
```bash
wget --no-check-certificate -O luci-theme-kucat_2.6.10-r20250720_all.ipk https://github.com/sirpdboy/luci-app-kucat-config/releases/download/v1.1.0/luci-theme-kucat_2.6.10-r20250720_all.ipk
opkg install luci-app-kucat-config*.ipk
```
## interface
![screenshots](./doc/pc.jpg)
![screenshots](./doc/mob.jpg)
# My other project
- Watch Dog https://github.com/sirpdboy/luci-app-watchdog
- Net Speedtest https://github.com/sirpdboy/luci-app-netspeedtest
- Task Plan : https://github.com/sirpdboy/luci-app-taskplan
- Power Off Device : https://github.com/sirpdboy/luci-app-poweroffdevice
- OpentoPD Theme : https://github.com/sirpdboy/luci-theme-opentopd
- Ku Cat Theme : https://github.com/sirpdboy/luci-theme-kucat
- Ku Cat Theme Config : https://github.com/sirpdboy/luci-app-kucat-config
- NFT Time Control : https://github.com/sirpdboy/luci-app-timecontrol
- Parent Control: https://github.com/sirpdboy/luci-theme-parentcontrol
- Eqos Plus: https://github.com/sirpdboy/luci-app-eqosplus
- Advanced : https://github.com/sirpdboy/luci-app-advanced
- ddns-go : https://github.com/sirpdboy/luci-app-ddns-go
- Advanced Plus: https://github.com/sirpdboy/luci-app-advancedplus
- Net Wizard: https://github.com/sirpdboy/luci-app-netwizard
- Part Exp: https://github.com/sirpdboy/luci-app-partexp
- Lukcy: https://github.com/sirpdboy/luci-app-lukcy
## HELP
| <img src="https://img.shields.io/badge/-Alipay-F5F5F5.svg" href="#赞助支持本项目-" height="25" alt="图飞了"/> | <img src="https://img.shields.io/badge/-WeChat-F5F5F5.svg" height="25" alt="图飞了" href="#赞助支持本项目-"/> |
| :-----------------: | :-------------: |
|![xm1](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/支付宝.png) | ![xm1](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/微信.png) |
<a href="#readme">
<img src="https://img.shields.io/badge/-TOP-orange.svg" alt="no" title="Return TOP" align="right"/>
</a>
![hello](https://visitor-badge-deno.deno.dev/sirpdboy.sirpdboy.svg) [![](https://img.shields.io/badge/TGGroup-ClickJoin-FFFFFF.svg)](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ)

View File

@@ -0,0 +1,156 @@
![hello](https://views.whatilearened.today/views/github/sirpdboy/deplives.svg) [![](https://img.shields.io/badge/TG群-点击加入-FFFFFF.svg)](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ)
<h1 align="center">
<br>luci-theme-kucat<br>
</h1>
<p align="center">
<a target="_blank" href="https://github.com/sirpdboy/luci-theme-kucat/releases">
<img src="https://img.shields.io/github/release/sirpdboy/luci-theme-kucat.svg?style=flat-square&label=luci-theme-kucat&colorB=green">
</a>
</p>
[中文] | [English](README.md)
![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明1.jpg)
# 酷猫主题链接: https://github.com/sirpdboy/luci-theme-kucat
# 进队设置-酷猫主题设置下载链接: [https://github.com/sirpdboy/luci-app-kucat-config](https://github.com/sirpdboy/luci-app-kucat-config/releases)
- 开发时间2021年12月
- 发布时间2023年2月
- 开源时间2023年4月
- main 支持LEDE18.06和官方18.06分支 开源时间2023年4月 版本1.2.6
- JS 支持官网19.07-24.10分支 最新版本2025年5月 版本2.5.9 适配OPENWRT 24.10分支。
- ucode版支持官网19.07-24.10分支 最新版本2025年11月 版本3.1.2 适配OPENWRT 最新版本。 新版本不再支持advancedplus插件请用配套luci-app-kucat-config 专用JS版插件。
# 功能介绍
- 以酷为美,因动而生。
- 带5种快捷键的工具栏。
- 基本BUG适应目前已经所有插件.
- 针对手机等做了大量优化.
- 有配套的主题设置工具,可调节快捷键、背景图片、颜色多种方案可供选择。
#【说在前面的话】此次酷猫主题研发借鉴:Opentopd主题、Jerryk大神argon主题、以及thinktip大神的neobird主题的部分灵感及参考借用部分代码在这里表示感谢感谢有你们珠玉在前
#目前源码暂不开源,如果你们需要联系本人可以免费获取。
# 特殊说明
- 当系统中 有进阶设置又有 KUCAT主题设置工具时 以进阶设置中的设置为准。
- 如果设置上有问题可以在SSH登陆后用4号功能恢复设置。
- 如果是进阶设置,恢复默认设置,可以用:/etc/init.d/kucat reset
-
# 为什么叫酷猫
## 二个原因:
## 一是这主题就是为酷而生,一切只为酷与简单!从此让你的主题不再单调!让主题活起来!按钮动起来!全面掌控主题颜色设置!
## 二是因为这主题是在九个方案版本中测试研究后决定出来正合猫有9条命另外是本人年后到现在在无数个夜班熬呀熬熬出来的都熬成夜猫子了所以定名【英文名KuCat】【中文名酷猫】。
另外要说明一下就是本人弄固件不只是编译与搬运,是开发,是创新,更是爱好!请不要将本人与一些搬运工编译的固件来比较!那样会只会显得你很无知。
当然在OPENWRT的世界里比本人更优秀更专业的人士大有人在本人能做的就是尽自己微薄浅湿的力量尽量让固件更好用和更少的BUG。
不管是本人还是别人开发的插件,这里面的每一行代码,都凝聚着开发者在后面无数辛勤汗水和智慧的付出,请大家珍重他们的劳动成果与心血付出!
也许某些功能某些要求还达不到你的预期要求,有道是一人难满百人意,有些可能是本身实现不了,有些可能是能力不足的原因实现不了.
不管如何如果有问题或者BUG请多给理解与支持【当然有能力的请给这些开发者们更多的物资支持】
在此要特别感谢VIP群里以及不在群里的所有为爱发电的好朋友们是你们给本人无尽的力量谢谢
| 分支 | 版本 | 目标 | 源码 |
| ------ | ------- | ---------------------------------- | --------------------------------------------------------- |
| master | v3.x.x | Support the latest version of LuCI | [Official OpenWrt][official] • [ImmortalWrt][immortalwrt] |
| 23.05 | v2.x.x | Support the 23.05 version of LuCI | [Official OpenWrt][official] • [ImmortalWrt][immortalwrt] |
| 18.06 | v1.x.x | Support the 18.06 version of LuCI | [Lean's LEDE][lede] |
### 建立 Lean's LEDE 项目
```bash
rm -rf luci-app-kucat-config # if have
git clone -b 18.06 https://github.com/sirpdboy/luci-theme-kucat.git luci-theme-kucat
make menuconfig #choose LUCI->Theme->luci-theme-kucat
make -j1 V=s
```
### 建立官方 OpenWrt 和ImmortalWrt
```bash
cd openwrt/package
git clone https://github.com/sirpdboy/luci-theme-kucat.git
make menuconfig #choose LUCI->Theme->Luci-theme-kucat
make -j1 V=s
```bash
### 安装 LuCI 18.06 ( Lean's LEDE )
```bash
wget --no-check-certificate https://github.com/sirpdboy/luci-theme-kucat/releases/download/v1.5.6/luci-theme-kucat_1.5.6-20240302_all.ipk
opkg install luci-theme-kucat*.ipk
```
### 安装 官方openwrt和ImmortalWrt
```bash
wget --no-check-certificate https://github.com/sirpdboy/luci-theme-kucat/releases/download/v2.6.18/luci-theme-kucat_2.6.18-r20251018_all.ipk
opkg install luci-theme-kucat*.ipk
```
### 安装 luci-app-kucat-config
```bash
wget --no-check-certificate -O luci-theme-kucat_2.6.10-r20250720_all.ipk https://github.com/sirpdboy/luci-app-kucat-config/releases/download/v1.1.0/luci-theme-kucat_2.6.10-r20250720_all.ipk
opkg install luci-app-kucat-config*.ipk
```
## 界面
![screenshots](./doc/pc.jpg)
![screenshots](./doc/mob.jpg)
# My other project
- 路由安全看门狗 https://github.com/sirpdboy/luci-app-watchdog
- 网络速度测试 https://github.com/sirpdboy/luci-app-netspeedtest
- 计划任务插件(原定时设置) : https://github.com/sirpdboy/luci-app-taskplan
- 关机功能插件 : https://github.com/sirpdboy/luci-app-poweroffdevice
- opentopd主题 : https://github.com/sirpdboy/luci-theme-opentopd
- kucat酷猫主题: https://github.com/sirpdboy/luci-theme-kucat
- kucat酷猫主题设置工具: https://github.com/sirpdboy/luci-app-kucat-config
- NFT版上网时间控制插件: https://github.com/sirpdboy/luci-app-timecontrol
- 家长控制: https://github.com/sirpdboy/luci-theme-parentcontrol
- 定时限速: https://github.com/sirpdboy/luci-app-eqosplus
- 系统高级设置 : https://github.com/sirpdboy/luci-app-advanced
- ddns-go动态域名: https://github.com/sirpdboy/luci-app-ddns-go
- 进阶设置(系统高级设置+主题设置kucat/agron/opentopd: https://github.com/sirpdboy/luci-app-advancedplus
- 网络设置向导: https://github.com/sirpdboy/luci-app-netwizard
- 一键分区扩容: https://github.com/sirpdboy/luci-app-partexp
- lukcy大吉: https://github.com/sirpdboy/luci-app-lukcy
## 捐助
![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明3.jpg)
| <img src="https://img.shields.io/badge/-支付宝-F5F5F5.svg" href="#赞助支持本项目-" height="25" alt="图飞了"/> | <img src="https://img.shields.io/badge/-微信-F5F5F5.svg" height="25" alt="图飞了" href="#赞助支持本项目-"/> |
| :-----------------: | :-------------: |
|![xm1](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/支付宝.png) | ![xm1](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/微信.png) |
<a href="#readme">
<img src="https://img.shields.io/badge/-返回顶部-orange.svg" alt="图飞了" title="返回顶部" align="right"/>
</a>
![](https://visitor-badge-deno.deno.dev/sirpdboy.sirpdboy.svg) [![](https://img.shields.io/badge/TG群-点击加入-FFFFFF.svg)](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ)

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 KiB

BIN
luci-theme-kucat/doc/pc.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 KiB

View File

@@ -0,0 +1,2 @@
Drop background here!
accept jpg png gif mp4 webm

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square70x70logo src="/luci-static/kucat/img/logo70.png"/>
<square150x150logo src="/luci-static/kucat/img/logo150.png"/>
<wide310x150logo src="/luci-static/kucat/img/logow310x150.png"/>
<square310x310logo src="/luci-static/kucat/img/logo310.png"/>
<square512x512logo src="/luci-static/kucat/img/logo512.png"/>
<TileColor>#1a1a1a</TileColor>
<TileImage src="logo.png"/>
</tile>
<notification>
<polling-uri src="/live-tile-notifications"/>
<frequency>30</frequency>
</notification>
</msapplication>
</browserconfig>

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Some files were not shown because too many files have changed in this diff Show More