update 2026-11-13 00:30:06

This commit is contained in:
action committed 2026-11-13 00:30:06 +08:00
1 parent 95406bc799
commit 1ed10ddea8
101 files changed
+29943 -28459

No files matched your search

+6 -6
View File
@@ -1,19 +1,19 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=clashoo
PKG_UPSTREAM_VERSION:=1.19.33
PKG_UPSTREAM_VERSION:=1.19.34
PKG_UPSTREAM_TAG:=v$(PKG_UPSTREAM_VERSION)
PKG_SHORT_SHA:=3f27ef3
PKG_SHORT_SHA:=0455d0d
PKG_VERSION:=2026.11.04.$(PKG_UPSTREAM_VERSION)
PKG_RELEASE:=3
PKG_VERSION:=2026.11.12.$(PKG_UPSTREAM_VERSION)
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-stable-$(PKG_UPSTREAM_TAG)-$(PKG_SHORT_SHA).tar.gz
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-stable-$(PKG_UPSTREAM_TAG)-$(PKG_SHORT_SHA)
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_SOURCE_SUBDIR)
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-clashoo/releases/download/mihomo-src
PKG_SOURCE_VERSION:=3f27ef39f8c96bbfbc8b0b9bb10f5c9ef60149d8
PKG_HASH:=1ef7a4087f3805bd2624a940e2e43a0bc23719a25465c9f503aa6c11572f1321
PKG_SOURCE_VERSION:=0455d0d28c4526937380505153e65a843d8612e8
PKG_HASH:=d51ffc103547e91328b3e3540e558c5b6f7be3d31e6a63dc3249d101faf9cb20
PKG_BUILD_VERSION:=$(PKG_UPSTREAM_TAG)
PKG_LICENSE:=GPL3.0+
+1 -1
View File
@@ -5,7 +5,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=honk
PKG_VERSION:=2026.11.09.1
PKG_VERSION:=2026.11.12
PKG_RELEASE:=1
# Pinned honk-core revision (upstream daeuniverse/honk main), cross-compiled by
+18 -4
View File
@@ -51,6 +51,13 @@ module_loaded() {
[ -d "/sys/module/$1" ] || grep -q "^$1 " /proc/modules
}
# 只有面板管理的 system.dae 能决定 NFQUEUE;自定义配置按开启处理。
nfqueue_enabled() {
[ "${settings_owner:-}" = system ] || return 0
_nfq=$(sed -n 's/^ nfqueue_enable: \(true\|false\)$/\1/p' /etc/honk/system.dae 2>/dev/null | head -n 1)
[ "$_nfq" != false ]
}
module_available() {
module_loaded "$1" && return 0
module_path=$(printf '%s' "$1" | tr '_' '-')
@@ -189,9 +196,14 @@ check_kernel() {
[ -d /sys/fs/bpf ] && grep -q ' /sys/fs/bpf bpf ' /proc/mounts || { kernel_error=bpffs_not_mounted; fail "bpffs 未挂载到 /sys/fs/bpf,请检查内核或手工挂载"; return 1; }
command -v ip >/dev/null 2>&1 || { kernel_error=ip_full_missing; fail "缺少 ip 命令(装 ip-full)"; return 1; }
command -v modprobe >/dev/null 2>&1 || { kernel_error=kmod_missing; fail "缺少 modprobe 命令(装 kmod)"; return 1; }
for module in sch_ingress cls_bpf veth nft_queue nfnetlink_queue; do
module_available "$module" || { kernel_error="kernel_module_missing:$module"; fail "缺少内核模块 $module,请确认 kmod-sched-core / kmod-sched-bpf / kmod-veth / kmod-nft-queue 已安装"; return 1; }
for module in sch_ingress cls_bpf veth; do
module_available "$module" || { kernel_error="kernel_module_missing:$module"; fail "缺少内核模块 $module,请确认 kmod-sched-core / kmod-sched-bpf / kmod-veth 已安装"; return 1; }
done
if nfqueue_enabled; then
for module in nft_queue nfnetlink_queue; do
module_available "$module" || { kernel_error="kernel_module_missing:$module"; fail "缺少内核模块 $module,装 kmod-nft-queue,或在 system.dae 里设 nfqueue_enable: false"; return 1; }
done
fi
}
check_conflicts() {
@@ -396,10 +408,12 @@ cleanup_initialize() {
# 交还面板管理时也用它,保证两条路径生成的 system.dae 一致。
render_runtime_system() {
_nfq=$(sed -n 's/^ nfqueue_enable: \(true\|false\)$/\1/p' /etc/honk/system.dae 2>/dev/null | head -n 1)
case "$_nfq" in true|false) ;; *) _nfq=true ;; esac
printf "global {\n wan_interface: auto\n lan_interface: '%s'\n" "$1"
printf " log_level: info\n dial_mode: domain\n allow_insecure: false\n"
printf " auto_config_kernel_parameter: true\n data_dir: '/etc/honk'\n"
printf " store_subscribe: true\n nfqueue_enable: true\n}\n\n"
printf " store_subscribe: true\n nfqueue_enable: %s\n}\n\n" "$_nfq"
printf "experimental {\n native_api {\n enabled: true\n listen: '%s:%s'\n" "$2" "$3"
printf " password_auth: true\n allow_anonymous_loopback: false\n"
printf " config_write: true\n ui: '/usr/share/doona'\n }\n"
@@ -954,7 +968,7 @@ repair() {
if [ "$initialized" = 1 ]; then system_host=$lan_ip; else system_host=127.0.0.1; fi
if [ -r /usr/share/doona/index.html ] && [ -f /etc/honk/system.dae ] && [ ! -L /etc/honk/system.dae ] && system_config_readonly && \
grep -Fqx ' wan_interface: auto' /etc/honk/system.dae && grep -Fqx " lan_interface: '$lan_device'" /etc/honk/system.dae && \
grep -Fqx " data_dir: '/etc/honk'" /etc/honk/system.dae && grep -Fqx ' nfqueue_enable: true' /etc/honk/system.dae && \
grep -Fqx " data_dir: '/etc/honk'" /etc/honk/system.dae && grep -Eqx ' nfqueue_enable: (true|false)' /etc/honk/system.dae && \
grep -Fqx " listen: '$system_host:$listen_port'" /etc/honk/system.dae && \
grep -Fqx " ui: '/usr/share/doona'" /etc/honk/system.dae && grep -Fqx ' password_auth: true' /etc/honk/system.dae && \
grep -Fqx ' allow_anonymous_loopback: false' /etc/honk/system.dae && grep -Fqx ' config_write: true' /etc/honk/system.dae && clash_system_matches; then
+11 -4
View File
@@ -598,6 +598,8 @@ validate_system_schema() {
[ "$listen_host" = "$expected_ip" ] || [ "$listen_host" = 127.0.0.1 ] || return 1
store_subscribe=$(sed -n 's/^ store_subscribe: \(true\|false\)$/\1/p' "$trusted_system")
case "$store_subscribe" in true|false) ;; *) return 1 ;; esac
nfqueue_enable=$(sed -n 's/^ nfqueue_enable: \(true\|false\)$/\1/p' "$trusted_system")
case "$nfqueue_enable" in true|false) ;; *) nfqueue_enable=true ;; esac
cat > "$temp_dir/system.expected" <<EOF
global {
wan_interface: auto
@@ -608,7 +610,7 @@ global {
auto_config_kernel_parameter: true
data_dir: '/etc/honk'
store_subscribe: $store_subscribe
nfqueue_enable: true
nfqueue_enable: $nfqueue_enable
}
experimental {
@@ -797,6 +799,9 @@ write_live_system() {
live_ip=$3
live_port=$4
live_initialized=$5
# 第 6 个参数决定 NFQUEUE:false 才关闭,其余按开启。
live_nfqueue=$6
case "$live_nfqueue" in true|false) ;; *) live_nfqueue=true ;; esac
if [ "$live_initialized" = 1 ]; then live_host=$live_ip; else live_host=127.0.0.1; fi
cat > "$live_system" <<EOF
global {
@@ -808,7 +813,7 @@ global {
auto_config_kernel_parameter: true
data_dir: '/etc/honk'
store_subscribe: true
nfqueue_enable: true
nfqueue_enable: $live_nfqueue
}
experimental {
@@ -1061,7 +1066,9 @@ restore() {
[ "$old_running" != 1 ] || [ "$restore_initialized" = 1 ] || { error running_service_requires_initialized_restore; return 1; }
[ "$(sha_of "$RESTORE")" = "$archive_hash" ] || { error restore_archive_changed; return 1; }
if [ "$(config_owner "$restore_stage/etc/honk/config.dae")" = system ]; then
write_live_system "$restore_stage/etc/honk/system.dae" "$candidate_device" "$candidate_ip" "$u_port" "$restore_initialized" || { error system_config_render_failed; return 1; }
restore_nfqueue=$(sed -n 's/^ nfqueue_enable: \(true\|false\)$/\1/p' "$restore_stage/etc/honk/system.dae" 2>/dev/null | head -n 1)
case "$restore_nfqueue" in true|false) ;; *) restore_nfqueue=true ;; esac
write_live_system "$restore_stage/etc/honk/system.dae" "$candidate_device" "$candidate_ip" "$u_port" "$restore_initialized" "$restore_nfqueue" || { error system_config_render_failed; return 1; }
chmod 0600 "$restore_stage/etc/honk/system.dae" || { error system_config_render_failed; return 1; }
fi
uci -c "$uci_dir" -t "$temp_dir/uci-tmp" set "honk.main.enabled=$old_enabled" || { error restore_profile_stage_failed; return 1; }
@@ -1155,7 +1162,7 @@ reset_data() {
reset_ip=
network_get_device reset_device "$u_network" || { error lan_network_unavailable; return 1; }
network_get_ipaddr reset_ip "$u_network" || { error lan_network_unavailable; return 1; }
write_live_system "$reset_stage/etc/honk/system.dae" "$reset_device" "$reset_ip" "$u_port" "$u_initialized" || { error system_config_render_failed; return 1; }
write_live_system "$reset_stage/etc/honk/system.dae" "$reset_device" "$reset_ip" "$u_port" "$u_initialized" true || { error system_config_render_failed; return 1; }
chmod 0600 "$reset_stage/etc/honk/system.dae" || { error system_config_render_failed; return 1; }
reset_candidate=$temp_dir/reset-candidate
mkdir -p "$reset_candidate/etc" || { error reset_stage_failed; return 1; }
+1
View File
@@ -1 +1,2 @@
scripts/** linguist-detectable=false
tests/** linguist-detectable=false
+45 -1
View File
@@ -2,22 +2,63 @@ name: build-apk
on:
push:
branches:
- '**'
tags:
- 'v*'
- 'dev-*'
pull_request:
workflow_dispatch:
permissions:
contents: write
contents: read
env:
SDK_DIR: /builder
PKG_DIR_NAME: luci-app-adguardhome-tk
jobs:
regression:
name: 回归测试
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: 检出代码
uses: actions/checkout@v7
- name: 准备 Node.js
uses: actions/setup-node@v7
with:
node-version: '24'
package-manager-cache: false
- name: 准备 Python
uses: actions/setup-python@v7
with:
python-version: '3.12'
cache: pip
cache-dependency-path: tests/browser/requirements.txt
- name: 安装测试依赖
run: |
sudo apt-get update
sudo apt-get install -y busybox
python3 -m pip install -r tests/browser/requirements.txt
python3 -m playwright install --with-deps chromium
- name: 服务与日志回归
run: bash scripts/test.sh unit
- name: 浏览器回归
run: bash scripts/test.sh browser
build:
needs: regression
if: github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
timeout-minutes: 90
permissions:
contents: write
strategy:
matrix:
sdk: [x86-64-25.12.6]
@@ -48,6 +89,9 @@ jobs:
- name: 构建包
run: runuser -u buildbot -- bash scripts/build-apk.sh
- name: 分包与安装迁移回归
run: bash scripts/test.sh sdk
- name: 整理 APK 产物
run: bash scripts/collect-apks.sh
+4 -1
View File
@@ -1,2 +1,5 @@
.DS_Store
.local
.local
.venv/
__pycache__/
*.pyc
+2 -1
View File
@@ -4,7 +4,8 @@ cd "$SDK_DIR"
mkdir -p package/${PKG_DIR_NAME}
rsync -a --delete "$GITHUB_WORKSPACE/" "package/${PKG_DIR_NAME}/" \
--exclude '.git*' --exclude '.local' --exclude '_sdk' --exclude 'dist'
--exclude '.git*' --exclude '.local' --exclude '_sdk' --exclude 'dist' \
--exclude 'tests' --exclude '.venv'
./scripts/feeds update -a
./scripts/feeds install luci-base tar
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -euo pipefail
cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.."
case "${1:-unit}" in
unit)
for script in scripts/*.sh tests/sdk/*.sh; do
bash -n "$script"
done
node --test tests/*.test.js
python3 -m unittest discover -s tests -p 'test_*.py' -v
;;
browser)
python3 tests/browser/test_verbose_checkbox.py
;;
sdk)
# 使用 SDK 构建出的工具, 避免维护额外的编译器和 apk 副本.
if [[ -n "${SDK_DIR:-}" ]]; then
export PATH="$SDK_DIR/staging_dir/hostpkg/bin:$SDK_DIR/staging_dir/host/bin:$PATH"
fi
python3 tests/sdk/test_split_packages.py
bash tests/sdk/test_apk_rename.sh
;;
*)
echo "Usage: bash scripts/test.sh [unit|browser|sdk]" >&2
exit 2
;;
esac
@@ -0,0 +1 @@
playwright==1.64.1
@@ -0,0 +1,36 @@
"""从页面提取复选框逻辑, 用真实 DOM 验证布尔属性和保存后的状态."""
from pathlib import Path
from playwright.sync_api import sync_playwright
ROOT = Path(__file__).resolve().parents[2]
source = (ROOT / 'root/www/luci-static/resources/view/adguardhome/log.js').read_text()
block = source[source.index("\t\t\tvar verboseToggle ="):source.index("\t\t\tvar message =")]
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
result = page.evaluate('''async block => {
function E(tag, attrs, children) {
const node = document.createElement(tag);
for (const key in attrs) if (attrs[key] != null) node.setAttribute(key, attrs[key]);
return node;
}
const create = new Function('E', 'm', '_', 'callSetVerboseLog', 'var toggleStyle="";'+block+';return verboseToggle;');
let saved = '0';
let fail = false;
const rpc = async enabled => { if (fail) throw Error('save failed'); saved = enabled ? '1' : '0'; return {saved:true, applied:true}; };
const render = readonly => create(E, {data:{get:()=>saved}, readonly}, x=>x, rpc);
const check = (ok,msg) => {if (!ok) throw Error(msg);};
const tick = () => new Promise(resolve => setTimeout(resolve,0));
let input = render(false); document.body.append(input);
check(!input.checked && !input.disabled, 'default off and editable');
input.click(); await tick();
check(saved === '1' && render(false).checked, 'enable survives reload');
input.click(); await tick();
check(saved === '0' && !render(false).checked, 'disable survives reload');
fail = true; input.click(); await tick();
check(!input.checked && !input.disabled && saved === '0', 'failure restores state');
check(render(true).disabled, 'readonly disabled');
saved = undefined; check(!render(false).checked, 'missing setting defaults off');
return 'PASS: default, enable/disable + reload, save failure, readonly, missing setting';
}''', block)
print(result)
browser.close()
+136
View File
@@ -0,0 +1,136 @@
/* 用模拟的 fs/uci 执行兼容 JavaScript 的 RPC 逻辑.
* 此处验证日志传输边界, OpenWrt 上的 ucode/LuCI 集成仍需实机验证.
*/
async function runLogTests(rpcSource, viewSource) {
function check(value, message) {
if (!value) throw new Error(message);
}
var limit = 262144;
var data = 'a'.repeat(limit * 20) + 'TAIL';
var inode = 1;
var readSize = 0;
var readOffset = -1;
var closed = false;
var rpcCode = rpcSource.replace(/^#!.*\n/, '').replace(/^import .*;\n/gm, '');
var api = new Function('stat', 'open', 'cursor', 'int', 'length', 'die', 'b64enc', rpcCode)(
function() { return { type: 'file', dev: { major: 0, minor: 1 }, inode: inode, size: data.length }; },
function() {
return {
seek: function(offset) { readOffset = offset; return true; },
read: function(size) { readSize = size; return data.slice(readOffset, readOffset + size); },
close: function() { closed = true; }
};
},
function() { return { get: function() { return '/tmp/test.log'; }, unload: function() {} }; },
Math.trunc, function(s) { return s.length; }, function(s) { throw new Error(s); }, btoa
)['luci.adguardhome'];
var backendRead = api.getRuntimeLog.call;
function read(request) {
var result = backendRead(request);
check(typeof result.content_b64 === 'string', 'binary-safe transport present');
check(!result.content_b64.includes('\0'), 'wire text contains no NUL');
// 模拟 rpcd 的 C 字符串传输, 检查 NUL 不会截断有效内容.
result.content = atob(result.content_b64.split('\0')[0]);
return result;
}
var initial = read({ args: { offset: 0, identity: '' } });
check(readSize === limit && readOffset === data.length - limit && closed, 'bounded tail read');
check(initial.content.length === limit && initial.content.endsWith('TAIL'), 'latest data returned');
data += 'APPEND';
var appended = read({ args: { offset: initial.offset, identity: initial.identity } });
check(appended.content === 'APPEND' && !appended.reset, 'incremental append');
data = '';
var truncated = read({ args: { offset: appended.offset, identity: appended.identity } });
check(truncated.reset && truncated.offset === 0 && truncated.content === '', 'truncate reset');
data = 'n'.repeat(appended.offset + 100);
inode++;
var rotated = read({ args: { offset: appended.offset, identity: appended.identity } });
check(rotated.reset && rotated.content.length === limit, 'replacement by larger file resets');
data += 'x'.repeat(limit * 2);
var lagged = read({ args: { offset: rotated.offset, identity: rotated.identity } });
check(lagged.reset && readSize === limit && lagged.content === 'x'.repeat(limit), 'slow reader skips old backlog');
// 复现清空日志后写入进程仍保留原偏移量的稀疏文件.
data = '\0'.repeat(120000) + 'visible after clear\n';
var sparse = read({ args: { offset: 0, identity: '' } });
check(sparse.offset === data.length, 'cursor includes sparse bytes');
check(sparse.content.endsWith('visible after clear\n'), 'text after NUL survives rpcd transport');
var unicodeBytes = new TextEncoder().encode('中文日志正常\n');
var unicodeBinary = Array.from(unicodeBytes, function(b) { return String.fromCharCode(b); }).join('');
data += unicodeBinary;
var unicode = read({ args: { offset: sparse.offset, identity: sparse.identity } });
check(unicode.offset === data.length && unicode.content === unicodeBinary, 'UTF-8 byte cursor remains correct');
var pollCallback;
var nodes = [];
var response;
var pending;
var page = new Function('view', 'rpc', 'poll', 'status', 'E', '_', 'form', 'L', viewSource)(
{ extend: function(value) { return value; } },
{ declare: function(spec) {
return function() {
if (spec.method === 'clearRuntimeLog') return Promise.resolve({ cleared: true });
return pending || Promise.resolve(response);
};
} },
{ add: function(callback) { pollCallback = callback; } },
{ render: function() { return { node: {} }; } },
function(tag, attrs, children) {
var node = Object.assign({ tag: tag, children: children, style: {}, events: {},
addEventListener: function(name, fn) { this.events[name] = fn; } }, attrs);
nodes.push(node);
return node;
},
function(text) { return text; },
{ Map: function() {
this.data = { get: function() { return '0'; } };
this.section = function() { return { option: function() { return {}; } }; };
this.render = function() {
var logFileField = { querySelector: function() { return {}; } };
var fields = { querySelector: function() { return logFileField; } };
var card = {
classList: { add: function() {} },
querySelector: function() { return fields; },
insertBefore: function() {}
};
return Promise.resolve({ querySelector: function() { return card; } });
};
} }, { resource: function(path) { return path; } }
);
await page.render(sparse);
var area = nodes.find(function(n) { return n.tag === 'textarea'; });
check(area.value.includes('visible after clear') && !area.value.includes('\0'), 'sparse log is visible in page');
response = unicode;
await pollCallback();
check(area.value.includes('中文日志正常'), 'UTF-8 log is decoded for display');
var clear = nodes.find(function(n) { return n.children === 'dellog'; });
for (var i = 0; i < 12; i++) {
response = { enabled: true, content: 'b'.repeat(limit), offset: initial.offset + (i + 1) * limit, identity: initial.identity };
await pollCallback();
check(area.value.length <= limit, 'frontend cache stays bounded');
}
response = { enabled: true, content: '', offset: 0, identity: initial.identity, reset: true };
await pollCallback();
check(area.value === '', 'empty truncated log clears display');
var resolveOld;
pending = new Promise(function(resolve) { resolveOld = resolve; });
var oldPoll = pollCallback();
clear.events.click();
await Promise.resolve();
resolveOld({ enabled: true, content: 'STALE', offset: 5, identity: initial.identity });
await oldPoll;
check(area.value === '', 'late poll cannot restore cleared content');
return 'PASS: binary-safe RPC, sparse log display, UTF-8, bounded reads, rotation and clear/poll race';
}
const test = require('node:test');
test('runtime log transport, bounded reads and clear/poll race', { timeout: 5000 }, async () => {
const fs = require('fs');
const path = require('path');
const root = path.resolve(__dirname, '..');
await runLogTests(
fs.readFileSync(path.join(root, 'root/usr/share/rpcd/ucode/luci.adguardhome'), 'utf8'),
fs.readFileSync(path.join(root, 'root/www/luci-static/resources/view/adguardhome/log.js'), 'utf8')
);
});
@@ -0,0 +1,90 @@
#!/usr/bin/env bash
set -euo pipefail
REPO_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
APK="${APK:-$(command -v apk || true)}"
if [[ ! -x "$APK" ]]; then
echo 'APK v3 is required; add it to PATH or set APK to its executable path.' >&2
exit 1
fi
APK="$(realpath "$APK")"
TEST_DIR="$(mktemp -d "${TMPDIR:-/tmp}/agh-apk-test.XXXXXX")"
export APK TEST_DIR
trap 'rm -rf -- "$TEST_DIR"' EXIT
mkdir -p "$TEST_DIR/pkgs" "$TEST_DIR/bin"
cd "$TEST_DIR"
mkdir -p old/etc/config old/usr/lib/lua/luci/i18n new/etc/config lang/usr/lib/lua/luci/i18n
printf original > old/etc/config/AdGuardHome
printf translation > old/usr/lib/lua/luci/i18n/AdGuardHome.zh-cn.lmo
cp old/etc/config/AdGuardHome new/etc/config/
cp old/usr/lib/lua/luci/i18n/* lang/usr/lib/lua/luci/i18n/
"$APK" mkpkg -I name:luci-app-adguardhome -I version:99.0-r1 -I arch:noarch -F old -o pkgs/old.apk
"$APK" mkpkg -I name:luci-i18n-adguardhome-zh-cn -I version:99.0-r1 -I arch:noarch -I depends:luci-app-adguardhome -F lang -o pkgs/old-lang.apk
"$APK" mkpkg -I name:luci-app-adguardhome-tk -I version:2.6.0-r1 -I arch:noarch -I provides:luci-app-adguardhome=2.6.0-r1 -F new -o pkgs/luci-app-adguardhome-tk-2.6.0-r1.apk
"$APK" mkpkg -I name:luci-i18n-adguardhome-tk-zh-cn -I version:2.6.0-r1 -I arch:noarch -I depends:luci-app-adguardhome-tk -I provides:luci-i18n-adguardhome-zh-cn=2.6.0-r1 -F lang -o pkgs/luci-i18n-adguardhome-tk-zh-cn-2.6.0-r1.apk
# 使用真实安装脚本和 APK 依赖求解器, 隔离网络和路由器服务操作.
sed -e 's@/etc/init.d/rpcd reload@true@' -e 's@rm -f /tmp/luci-indexcache /tmp/luci-indexcache.\* /tmp/luci-modulecache/\*@true@' "$REPO_DIR/scripts/install.sh" > install.sh
cat > bin/apk <<'SH'
#!/bin/sh
case "$1" in
add|del|upgrade) command=$1; shift; set -- "$command" --no-scripts "$@" ;;
esac
exec "$APK" --root "$TEST_ROOT" --no-network "$@"
SH
cat > bin/uclient-fetch <<'SH'
#!/bin/sh
if [ "$1" = -qO- ]; then
printf '{\n"tag_name": "v2.6.0-1",\n"assets": [\n{"name": "luci-app-adguardhome-tk-2.6.0-r1.apk"},\n{"name": "luci-i18n-adguardhome-tk-zh-cn-2.6.0-r1.apk"}\n]}\n'
else
case "$3" in *luci-i18n*) [ "${FAIL_DOWNLOAD:-0}" != 1 ] || exit 1 ;; esac
cp "$TEST_DIR/pkgs/${3##*/}" "$2"
fi
SH
chmod +x bin/*
export PATH="$TEST_DIR/bin:$PATH"
for scenario in fresh bundled split; do
export TEST_ROOT="$TEST_DIR/$scenario-root"
if [ "$scenario" = fresh ]; then
apk --allow-untrusted --initdb add
else
apk --allow-untrusted --initdb add "$TEST_DIR/pkgs/old.apk"
if [ "$scenario" = split ]; then
# 旧版拆分后的主包不再包含翻译文件, 避免构造重复文件所有权.
rm old/usr/lib/lua/luci/i18n/AdGuardHome.zh-cn.lmo
"$APK" mkpkg -I name:luci-app-adguardhome -I version:99.0-r2 -I arch:noarch -F old -o pkgs/old-split.apk
apk --allow-untrusted add "$TEST_DIR/pkgs/old-split.apk" "$TEST_DIR/pkgs/old-lang.apk"
fi
printf customized > "$TEST_ROOT/etc/config/AdGuardHome"
if FAIL_DOWNLOAD=1 sh install.sh zh-cn; then exit 1; fi
apk info | grep -qx luci-app-adguardhome
test "$(cat "$TEST_ROOT/etc/config/AdGuardHome")" = customized
fi
sh install.sh zh-cn
test "$(apk info | sort)" = "$(printf '%s\n' luci-app-adguardhome-tk luci-i18n-adguardhome-tk-zh-cn | sort)"
test "$(wc -l < "$TEST_ROOT/etc/apk/world")" = 2
if grep -Eq '^luci-app-adguardhome([<=>~@]|$)|^luci-i18n-adguardhome-zh-cn([<=>~@]|$)' "$TEST_ROOT/etc/apk/world"; then
echo "FAIL: legacy world constraints remain after $scenario migration" >&2
exit 1
fi
test -f "$TEST_ROOT/usr/lib/lua/luci/i18n/AdGuardHome.zh-cn.lmo"
if [ "$scenario" != fresh ]; then test "$(cat "$TEST_ROOT/etc/config/AdGuardHome")" = customized; fi
sh install.sh zh-cn
"$APK" mkndx --allow-untrusted --output "$TEST_DIR/pkgs/index.adb" "$TEST_DIR/pkgs/old.apk" "$TEST_DIR/pkgs/luci-app-adguardhome-tk-2.6.0-r1.apk" "$TEST_DIR/pkgs/luci-i18n-adguardhome-tk-zh-cn-2.6.0-r1.apk"
apk upgrade --available --allow-untrusted --repository "$TEST_DIR/pkgs/index.adb"
test "$(apk info | sort)" = "$(printf '%s\n' luci-app-adguardhome-tk luci-i18n-adguardhome-tk-zh-cn | sort)"
echo "PASS: $scenario migration, reinstall and upgrade with higher-version official package"
done
for scenario in fresh old-split new-chinese; do
export TEST_ROOT="$TEST_DIR/en-$scenario-root"
case "$scenario" in
fresh) apk --allow-untrusted --initdb add ;;
old-split) apk --allow-untrusted --initdb add "$TEST_DIR/pkgs/old-split.apk" "$TEST_DIR/pkgs/old-lang.apk" ;;
new-chinese) apk --allow-untrusted --initdb add "$TEST_DIR/pkgs/luci-app-adguardhome-tk-2.6.0-r1.apk" "$TEST_DIR/pkgs/luci-i18n-adguardhome-tk-zh-cn-2.6.0-r1.apk" ;;
esac
FAIL_DOWNLOAD=1 sh install.sh en
test "$(apk info)" = luci-app-adguardhome-tk
test ! -f "$TEST_ROOT/usr/lib/lua/luci/i18n/AdGuardHome.zh-cn.lmo"
test "$(wc -l < "$TEST_ROOT/etc/apk/world")" = 1
sh install.sh en
echo "PASS: English $scenario installs only main package and removes old translations"
done
@@ -0,0 +1,54 @@
import json, os, subprocess, tempfile
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
with tempfile.TemporaryDirectory() as tmp:
t=Path(tmp)
env={**os.environ, 'PATH':str(t)+':'+os.environ['PATH']}
# 仅模拟 SDK 的构建基础设施, 执行真实的安装规则和翻译编译器.
(t/'rules.mk').write_text('')
(t/'package.mk').write_text('INSTALL_DIR=mkdir -p\n')
(t/'main').mkdir(); (t/'lang').mkdir()
harness=f'TOPDIR:={t}\nINCLUDE_DIR:={t}\ninclude {ROOT}/Makefile\nall:\n\t$(call Package/luci-app-adguardhome-tk/install,{t}/main)\n\t$(call Package/luci-i18n-adguardhome-tk-zh-cn/install,{t}/lang)\n'
(t/'test.mk').write_text(harness)
subprocess.run(['make','-s','-f',str(t/'test.mk')],cwd=ROOT,env=env,check=True,stdout=subprocess.DEVNULL,timeout=30)
assert not list((t/'main').rglob('*.lmo'))
assert (t/'main/etc/init.d/AdGuardHome').stat().st_mode & 0o111
assert (t/'lang/usr/lib/lua/luci/i18n/AdGuardHome.zh-cn.lmo').stat().st_size > 0
assert len([p for p in (t/'lang').rglob('*') if p.is_file()]) == 2
print('PASS: separated payloads, translation compilation, executable permissions')
# 模拟下载和安装命令, 验证不同版本的附件选择与下载失败行为.
script=(ROOT/'scripts/install.sh').read_text().replace('/etc/init.d/rpcd reload', 'true').replace('rm -f /tmp/luci-indexcache /tmp/luci-indexcache.* /tmp/luci-modulecache/*', 'true')
(t/'install.sh').write_text(script)
(t/'uclient-fetch').write_text('''#!/bin/sh
if [ "$1" = -qO- ]; then cat "$TEST_DIR/release.json"; exit; fi
case "$3" in *luci-i18n*) [ "$FAIL_TRANSLATION" != 1 ] || exit 1 ;; esac
printf '%s' "$3" > "$2"
''')
(t/'apk').write_text('''#!/bin/sh
shift 2
for file in "$@"; do cat "$file"; echo; done > "$TEST_DIR/installed"
''')
for name in ['uclient-fetch','apk']: (t/name).chmod(0o755)
for split,fail in [(False,False),(True,False),(True,True)]:
(t/'installed').unlink(missing_ok=True)
(t/'release.json').write_text(json.dumps({'tag_name':'v2.6.0-1','assets':[{'name':'luci-i18n-adguardhome-zh-cn-2.6.0-r1.apk'}] if split else []},indent=2))
result=subprocess.run(['sh',str(t/'install.sh'),'zh-cn'],env={**env,'TEST_DIR':tmp,'FAIL_TRANSLATION':str(int(fail))},capture_output=True,text=True,timeout=30)
if fail:
assert result.returncode != 0 and not (t/'installed').exists(), result.stdout
else:
assert result.returncode == 0, result.stderr
installed=(t/'installed').read_text().splitlines()
assert len(installed)==(2 if split else 1), installed
assert all('v2.6.0-1/' in url and url.endswith('2.6.0-r1.apk') for url in installed)
print('PASS: legacy release, split release, failed translation download aborts installation')
collect=['bash',str(ROOT/'scripts/collect-apks.sh')]
(t/'sdk/bin/packages').mkdir(parents=True)
for package in ['luci-app-adguardhome-tk','luci-i18n-adguardhome-tk-zh-cn']:
(t/f'sdk/bin/packages/{package}-2.6.0-r1.apk').touch()
subprocess.run(collect,env={**env,'SDK_DIR':str(t/'sdk'),'GITHUB_WORKSPACE':tmp,'PKG_DIR_NAME':'luci-app-adguardhome-tk'},check=True,timeout=30)
assert len(list((t/'dist').glob('*.apk')))==2
(t/'sdk/bin/packages/luci-i18n-adguardhome-tk-zh-cn-2.6.0-r1.apk').unlink()
result=subprocess.run(collect,env={**env,'SDK_DIR':str(t/'sdk'),'GITHUB_WORKSPACE':tmp,'PKG_DIR_NAME':'luci-app-adguardhome-tk'},capture_output=True,timeout=30)
assert result.returncode != 0
print('PASS: collects both APKs, rejects missing translation')
@@ -0,0 +1,113 @@
const fs = require('fs');
const path = require('path');
const assert = require('assert/strict');
const test = require('node:test');
const root = path.resolve(__dirname, '..');
const source = fs.readFileSync(path.join(root, 'root/www/luci-static/resources/view/adguardhome/status.js'), 'utf8');
let now = 10000;
const storage = new Map();
let info = { mode: 'dnsmasq-upstream', enabled: true, applied_mode: 'dnsmasq-upstream', applied_enabled: true, redirected: true, busy: false, binpath: '/core' };
let running = true, fail = false, held = null, requests = 0;
function harness() {
const nodes = [], polls = [];
const module = new Function('baseclass', 'rpc', 'poll', 'E', '_', 'sessionStorage', 'L', source)(
{ extend: x => x },
{ declare: spec => () => {
if (spec.method === 'getCoreInfo') return Promise.resolve({ core_exists: true, config_exists: true, version: 'test' });
if (spec.method === 'getRedirectStatus') {
requests++;
return held || (fail ? Promise.reject(Error('offline')) : Promise.resolve({ ...info }));
}
return Promise.resolve({ AdGuardHome: { instances: { instance1: { running, command: ['/core'] } } } });
} },
{ add: (fn, interval) => polls.push({ fn, interval }) },
(tag, attrs, children) => { const n = { ...attrs, style: {}, textContent: children }; nodes.push(n); return n; },
x => x,
{ getItem: key => storage.get(key) || null, setItem: (key, value) => storage.set(key, value), removeItem: key => storage.delete(key) },
{ url: (...parts) => '/cgi-bin/luci/' + parts.join('/') }
);
return { page: module.render(), nodes, polls, redirect: () => nodes[2].textContent };
}
const flush = async () => { for (let i = 0; i < 8; i++) await Promise.resolve(); };
test('mode switching, timeout recovery and stale RPC replies', { timeout: 5000 }, async t => {
const realNow = Date.now;
Date.now = () => now;
t.after(() => { Date.now = realNow; });
let h = harness(); await flush();
assert.match(h.redirect(), /Run as dnsmasq upstream server/);
const before = requests;
now += 1000; await h.polls[0].fn();
assert.equal(requests, before, 'normal status does not query each second');
now += 2000; await h.polls[0].fn();
assert.equal(requests, before + 1);
h.page.beginSwitch({ mode: 'exchange', enabled: true });
assert.equal(h.redirect(), 'Switching');
await h.page.refresh();
assert.match(h.redirect(), /Switching/, 'old active redirect must not satisfy new mode');
info.mode = info.applied_mode = 'exchange'; info.busy = true;
await h.page.refresh();
assert.match(h.redirect(), /Switching/, 'held mode lock cannot count as success');
info.busy = false; info.redirected = false;
await h.page.refresh();
assert.match(h.redirect(), /Switching/, 'new UCI mode without redirect flag is not active');
info.redirected = true; running = false;
await h.page.refresh();
assert.match(h.redirect(), /Switching/, 'redirect flag alone cannot imply service is running');
assert.equal(h.nodes[1].textContent, 'NOT RUNNING');
running = true;
await h.page.refresh(); assert.match(h.redirect(), /Switching/, 'requires stable samples');
await h.page.refresh(); assert.match(h.redirect(), /^Use port 53 instead of dnsmasq/);
assert.equal(storage.size, 0);
h.page.beginSwitch({ mode: 'redirect', enabled: true });
now += 31000; fail = true;
await h.page.refresh(); assert.match(h.redirect(), /Unable to read switch status/);
assert.equal(h.nodes[1].textContent, 'Status unavailable');
fail = false;
h = harness(); await flush(); assert.match(h.redirect(), /not completed/, 'reload preserves pending timeout');
info.mode = info.applied_mode = 'redirect';
await h.page.refresh(); await h.page.refresh();
assert.match(h.redirect(), /^Forward port 53 requests through the firewall/);
h.page.beginSwitch({ mode: 'none', enabled: true });
info.mode = info.applied_mode = 'none'; info.redirected = false;
await h.page.refresh(); await h.page.refresh(); assert.equal(h.redirect(), 'Do not handle DNS');
h.page.beginSwitch({ mode: 'none', enabled: false });
info.enabled = info.applied_enabled = false; running = false;
await h.page.refresh(); await h.page.refresh(); assert.match(h.redirect(), /Disabled/);
let resolveOld; held = new Promise(resolve => resolveOld = resolve);
const old = h.page.refresh();
h.page.beginSwitch({ mode: 'exchange', enabled: true });
resolveOld({ ...info }); held = null; await old;
assert.match(h.redirect(), /Switching/, 'in-flight status cannot overwrite new action');
await h.page.cancelSwitch(); assert.match(h.redirect(), /Disabled/);
assert.equal(storage.size, 0);
const baseSource = fs.readFileSync(path.join(root, 'root/www/luci-static/resources/view/adguardhome/base.js'), 'utf8');
let applied = 0, cancelled = 0, target;
const base = new Function('view', 'rpc', 'ui', baseSource)(
{ extend: x => x }, { declare: () => () => Promise.resolve({}) },
{ changes: { apply: checked => { assert.equal(checked, true); applied++; } } }
);
base.redirectOption = { formvalue: () => 'exchange' };
base.enabledOption = { formvalue: () => '1' };
base.sharedStatus = { beginSwitch: value => target = value, cancelSwitch: () => cancelled++ };
base.handleSave = () => Promise.reject(Error('invalid port'));
await assert.rejects(base.handleSaveApply(null, '0'), /invalid port/);
assert.equal(applied, 0); assert.equal(cancelled, 1);
assert.deepEqual(target, { mode: 'exchange', enabled: true });
base.handleSave = () => Promise.resolve();
await base.handleSaveApply(null, '0');
assert.equal(applied, 1);
const rpcSource = fs.readFileSync(path.join(root, 'root/usr/share/rpcd/ucode/luci.adguardhome'), 'utf8').replace(/^#!.*\n/, '').replace(/^import .*;\n/gm, '');
const config = { redirect: 'exchange', enabled: '1', old_redirect: ['exchange'], old_enabled: ['1'], binpath: '/core' };
let lock = 2;
const api = new Function('cursor', 'type', 'trim', 'readfile', 'system', 'die', rpcSource)(
() => ({ get: (pkg, section, name) => config[name], unload() {} }),
x => Array.isArray(x) ? 'array' : typeof x, x => x.trim(), () => '1', () => lock,
s => { throw Error(s); }
)['luci.adguardhome'];
const result = api.getRedirectStatus.call();
assert.equal(result.applied_mode, 'exchange'); assert.equal(result.applied_enabled, true); assert.equal(result.busy, true);
lock = 0; assert.equal(api.getRedirectStatus.call().busy, false);
lock = 1; assert.throws(() => api.getRedirectStatus.call());
});
@@ -0,0 +1,201 @@
"""用隔离的 UCI 模拟器执行真实启动脚本, 不修改路由器配置."""
import json
import os
from pathlib import Path
import subprocess
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
DNS = 'dhcp.@dnsmasq[0].'
BACKUP = 'AdGuardHome.dnsmasq_backup'
class DnsmasqUpstreamTests(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory(prefix='agh-dns-test-')
self.addCleanup(self.tmp.cleanup)
self.path = Path(self.tmp.name)
self.state_file = self.path / 'uci.json'
self.env = dict(os.environ, UCI_STATE=str(self.state_file),
TEST_DIR=str(self.path), PATH=str(self.path) + ':/usr/bin:/bin')
stub = self.path / 'uci'
stub.write_text('''#!/usr/bin/python3
import json, os, sys
from pathlib import Path
p=Path(os.environ['UCI_STATE']); state=json.loads(p.read_text())
args=[x for x in sys.argv[1:] if x != '-q']; op=args[0]
arg=args[1] if len(args)>1 else ''
key,_,value=arg.partition('=')
key=key.replace('AdGuardHome.@AdGuardHome[0]', 'AdGuardHome.AdGuardHome')
if op=='export':
print(json.dumps({k:v for k,v in state.items() if k.startswith(key+'.')}, sort_keys=True)); sys.exit(0)
if op=='get':
if key not in state: sys.exit(1)
print(' '.join(state[key]) if isinstance(state[key],list) else state[key])
sys.exit(0)
if op=='set': state[key]=value
elif op=='delete':
matched=[k for k in state if k==key or k.startswith(key+'.')]
if not matched: sys.exit(1)
for k in matched: del state[k]
elif op=='add_list':
if key not in state: state[key]=[]
if not isinstance(state[key],list): state[key]=[state[key]]
state[key].append(value)
elif op=='del_list':
if key not in state: sys.exit(1)
state[key]=[x for x in state[key] if x!=value]
if not state[key]: del state[key]
elif op=='commit':
if os.environ.get('FAIL_COMMIT')==key: sys.exit(1)
elif op=='revert':
for k in list(state):
if k==key or k.startswith(key+'.'): del state[k]
else: sys.exit('unsupported UCI operation: '+op)
p.write_text(json.dumps(state))
''')
stub.chmod(0o755)
source = (ROOT / 'root/etc/init.d/AdGuardHome').read_text()
begin = source.index('save_dnsmasq_upstream_config()')
end = source.index('\ndo_redirect() {\n', source.index('_do_redirect()'))
self.functions = source[begin:end]
self.functions = self.functions.replace('/etc/init.d/dnsmasq restart', 'restart_dnsmasq')
self.functions = self.functions.replace('/var/run/AdGredir', str(self.path / 'redirect_flag'))
self.functions = self.functions.replace('/var/lock/AdGuardHome-redirect.lock', str(self.path / 'redirect.lock'))
self.functions += '''
restart_dnsmasq() { echo restart >> "$TEST_DIR/restarts"; [ "$FAIL_RESTART" != 1 ]; }
logger() { :; }
config_get() {
local value
value="$(uci -q get "AdGuardHome.$2.$3")" || value="$4"
export "$1=$value"
}
config_editor() { echo "$DNS_PORT"; }
CONFIGURATION=AdGuardHome
'''
def seed(self, **options):
self.state_file.write_text(json.dumps({DNS+k: v for k, v in options.items()}))
def state(self):
return json.loads(self.state_file.read_text())
def dns(self):
return {k[len(DNS):]: v for k, v in self.state().items() if k.startswith(DNS)}
def run_shell(self, command, expected=0, **env):
result = subprocess.run(['sh', '-c', self.functions + '\n' + command],
env=dict(self.env, **env), capture_output=True, text=True, timeout=30)
self.assertEqual(result.returncode, expected, result.stderr + result.stdout)
def test_round_trip_preserves_servers_and_custom_options(self):
original = dict(server=['9.9.9.9', '/internal/192.168.1.2', '127.0.0.1#17450'],
resolvfile='/etc/custom resolver.conf', noresolv='0', port='53')
self.seed(**original)
self.run_shell('set_forward_dnsmasq 1745')
self.assertEqual(self.dns()['server'], ['127.0.0.1#1745', '/internal/192.168.1.2'])
self.assertEqual(self.dns()['resolvfile'], original['resolvfile'])
self.assertEqual(self.dns()['noresolv'], '1')
self.run_shell('stop_forward_dnsmasq 1745')
self.assertEqual(self.dns(), original)
self.assertNotIn(BACKUP, self.state())
def test_absent_options_stay_absent(self):
self.seed(port='53')
self.run_shell('set_forward_dnsmasq 1745; stop_forward_dnsmasq 1745')
self.assertEqual(self.dns(), {'port': '53'})
def test_explicit_empty_and_noresolv_one_preserved(self):
original = dict(resolvfile='', noresolv='1', server=['1.1.1.1'])
self.seed(**original)
self.run_shell('set_forward_dnsmasq 1745; stop_forward_dnsmasq 1745')
self.assertEqual(self.dns(), original)
def test_repeated_start_and_port_change_do_not_replace_snapshot(self):
original = dict(server=['8.8.8.8'], noresolv='0')
self.seed(**original)
self.run_shell('set_forward_dnsmasq 1745')
snapshot = {k:v for k,v in self.state().items() if k.startswith(BACKUP)}
self.run_shell('set_forward_dnsmasq 1745; set_forward_dnsmasq 5353')
self.assertEqual({k:v for k,v in self.state().items() if k.startswith(BACKUP)}, snapshot)
self.assertEqual(self.dns()['server'], ['127.0.0.1#5353'])
self.assertEqual(len((self.path / 'restarts').read_text().splitlines()), 2)
self.run_shell('stop_forward_dnsmasq 5353; stop_forward_dnsmasq 5353')
self.assertEqual(self.dns(), original)
def test_domain_rules_kept_but_catch_all_removed(self):
servers = ['/#/1.1.1.1', '/example/#/8.8.8.8', '9.9.9.9',
'/internal/192.168.1.2', '/*.test/192.168.1.3', '//192.168.1.4', '/lan/']
self.seed(server=servers)
self.run_shell('set_forward_dnsmasq 1745')
self.assertEqual(self.dns()['server'], ['127.0.0.1#1745'] + servers[3:])
self.run_shell('stop_forward_dnsmasq 1745')
self.assertEqual(self.dns()['server'], servers)
def test_legacy_endpoint_removed_in_any_position_without_prefix_match(self):
self.seed(server=['9.9.9.9', '127.0.0.1#17450', '127.0.0.1#1745'], noresolv='1')
self.run_shell('stop_forward_dnsmasq 1745')
self.assertEqual(self.dns()['server'], ['9.9.9.9', '127.0.0.1#17450'])
self.run_shell('stop_forward_dnsmasq 1745')
self.assertEqual(self.dns()['server'], ['9.9.9.9', '127.0.0.1#17450'])
def test_legacy_upgrade_does_not_save_old_plugin_endpoint(self):
self.seed(server=['/internal/192.168.1.2', '127.0.0.1#1745'], noresolv='1')
self.run_shell('set_forward_dnsmasq 1745 dnsmasq-upstream 1 1745')
self.assertEqual(self.state()[BACKUP+'.server'], ['/internal/192.168.1.2'])
self.run_shell('stop_forward_dnsmasq 1745')
self.assertEqual(self.dns()['server'], ['/internal/192.168.1.2'])
self.assertNotIn('noresolv', self.dns())
self.assertIn('resolvfile', self.dns())
def test_snapshot_commit_failure_does_not_change_dns(self):
original = dict(server=['9.9.9.9'], noresolv='0')
self.seed(**original)
self.run_shell('set_forward_dnsmasq 1745', expected=1, FAIL_COMMIT='AdGuardHome')
self.assertEqual(self.dns(), original)
self.assertFalse((self.path / 'restarts').exists())
def test_restore_restart_failure_keeps_snapshot_for_retry(self):
self.seed(server=['9.9.9.9'])
self.run_shell('set_forward_dnsmasq 1745')
self.run_shell('stop_forward_dnsmasq 1745', expected=1, FAIL_RESTART='1')
self.assertEqual(self.state()[BACKUP+'.ready'], '1')
self.run_shell('stop_forward_dnsmasq 1745')
self.assertNotIn(BACKUP, self.state())
self.assertEqual(self.dns(), {'server': ['9.9.9.9']})
def test_mode_switch_and_disable_through_redirect_dispatcher(self):
self.seed(server=['9.9.9.9'], noresolv='0', port='53')
self.run_shell('uci set AdGuardHome.AdGuardHome.redirect=dnsmasq-upstream; _do_redirect 1', DNS_PORT='1745')
self.assertEqual(self.dns()['server'], ['127.0.0.1#1745'])
self.run_shell('_do_redirect 1', DNS_PORT='5353')
self.assertEqual(self.dns()['server'], ['127.0.0.1#5353'])
self.run_shell('uci set AdGuardHome.AdGuardHome.redirect=none; _do_redirect 1', DNS_PORT='5353')
self.assertEqual(self.dns(), {'server':['9.9.9.9'], 'noresolv':'0', 'port':'53'})
self.run_shell('uci set AdGuardHome.AdGuardHome.redirect=dnsmasq-upstream; _do_redirect 1', DNS_PORT='1745')
self.run_shell('_do_redirect 0', expected=1, DNS_PORT='1745')
self.assertEqual(self.dns(), {'server':['9.9.9.9'], 'noresolv':'0', 'port':'53'})
def test_dispatcher_preserves_preexisting_matching_endpoint(self):
original = {'server': ['127.0.0.1#1745', '9.9.9.9'], 'port': '53'}
self.seed(**original)
self.run_shell('uci set AdGuardHome.AdGuardHome.redirect=dnsmasq-upstream; _do_redirect 1', DNS_PORT='1745')
self.run_shell('_do_redirect 1', DNS_PORT='5353')
self.assertEqual(self.state()[BACKUP+'.server'], original['server'])
self.run_shell('_do_redirect 0', expected=1, DNS_PORT='5353')
self.assertEqual(self.dns(), original)
self.run_shell('uci set AdGuardHome.AdGuardHome.redirect=none; _do_redirect 0', expected=1, DNS_PORT='5353')
self.assertEqual(self.dns(), original)
def test_overlapping_starts_keep_original_snapshot(self):
self.seed(server=['9.9.9.9'], port='53')
self.run_shell('uci set AdGuardHome.AdGuardHome.redirect=dnsmasq-upstream; _do_redirect 1 & first=$!; _do_redirect 1 & second=$!; wait "$first" && wait "$second"', DNS_PORT='1745')
self.assertEqual(self.state()[BACKUP+'.server'], ['9.9.9.9'])
self.assertEqual(self.dns()['server'], ['127.0.0.1#1745'])
self.run_shell('_do_redirect 0', expected=1, DNS_PORT='1745')
self.assertEqual(self.dns(), {'server': ['9.9.9.9'], 'port': '53'})
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,238 @@
"""通过端口占用模拟器执行真实启动脚本, 验证服务交接顺序."""
import subprocess
import unittest
import test_dnsmasq_upstream as dns
class ServiceLifecycleTests(unittest.TestCase):
def setUp(self):
self.h = dns.DnsmasqUpstreamTests('runTest')
self.h.setUp()
self.addCleanup(self.h.doCleanups)
self.path = self.h.path
source = (dns.ROOT / 'root/etc/init.d/AdGuardHome').read_text()
lifecycle = source[source.index('start_service() {'):source.index('\nboot() {')]
lifecycle = lifecycle.replace('/var/run/', str(self.path) + '/')
lifecycle = lifecycle.replace('/lib/upgrade/keep.d/luci-app-adguardhome', str(self.path / 'keep'))
self.h.functions += '\n' + lifecycle + '''
config_load() { :; }
config_get_bool() { config_get "$@"; }
config_editor() {
if [ -n "$4" ]; then cat "$TEST_DIR/yaml-port"; return; fi
[ ! -f "$TEST_DIR/live-port" ] || { echo 'editing live config' >&2; return 1; }
printf '%s' "$2" > "$TEST_DIR/yaml-port"
echo "yaml:$2" >> "$TEST_DIR/events"
}
stop_core() {
[ "$FAIL_STOP" != 1 ] || return 1
if [ -f "$TEST_DIR/live-port" ]; then
# 模拟核心正常退出时将内存中的配置写回文件.
cat "$TEST_DIR/live-port" > "$TEST_DIR/yaml-port"
rm "$TEST_DIR/live-port"
echo stopped >> "$TEST_DIR/events"
fi
}
restart_dnsmasq() {
local port="$(uci -q get dhcp.@dnsmasq[0].port)"
if [ -f "$TEST_DIR/live-port" ] && [ "$(cat "$TEST_DIR/live-port")" = "$port" ]; then
echo 'Address in use' >&2
return 1
fi
echo "dnsmasq:$port" >> "$TEST_DIR/events"
[ "$FAIL_RESTART" != 1 ]
}
get_filesystem() { echo tmpfs; }
get_tz() { SET_TZ=''; }
procd_open_instance() { :; }
procd_set_param() { :; }
procd_close_instance() {
local port="$(cat "$TEST_DIR/yaml-port")"
[ "$port" != "$(uci -q get dhcp.@dnsmasq[0].port)" ] || return 1
printf '%s' "$port" > "$TEST_DIR/live-port"
echo "started:$port" >> "$TEST_DIR/events"
}
ubus() { echo "ubus:$3" >> "$TEST_DIR/events"; }
start() { start_service; }
'''
(self.path / 'core').touch()
(self.path / 'config.yaml').touch()
(self.path / 'yaml-port').write_text('3053')
self.h.seed(server=['9.9.9.9'], noresolv='0', port='53')
self.h.run_shell('''
uci set AdGuardHome.AdGuardHome.enabled=1
uci set AdGuardHome.AdGuardHome.configpath="$TEST_DIR/config.yaml"
uci set AdGuardHome.AdGuardHome.binpath="$TEST_DIR/core"
uci set AdGuardHome.AdGuardHome.workdir="$TEST_DIR/work"
uci set AdGuardHome.AdGuardHome.redirect=dnsmasq-upstream
_do_redirect 1 0
''')
(self.path / 'live-port').write_text('3053')
(self.path / 'events').write_text('')
def events(self):
return (self.path / 'events').read_text().splitlines()
def exchange(self):
self.h.run_shell('uci set AdGuardHome.AdGuardHome.redirect=exchange; reload_service')
self.assertEqual((self.path / 'live-port').read_text(), '53')
self.assertEqual(self.h.dns()['port'], '3053')
def test_upstream_to_exchange_releases_both_ports_before_start(self):
self.exchange()
events = self.events()
self.assertLess(events.index('stopped'), events.index('yaml:53'))
self.assertLess(events.index('yaml:53'), events.index('dnsmasq:3053'))
self.assertLess(events.index('dnsmasq:3053'), events.index('started:53'))
self.assertNotIn('ubus:event', events)
self.assertEqual(sum(e.startswith('dnsmasq:') for e in events), 1)
def test_exchange_to_upstream_releases_53_before_dnsmasq(self):
self.exchange()
(self.path / 'events').write_text('')
self.h.run_shell('uci set AdGuardHome.AdGuardHome.redirect=dnsmasq-upstream; reload_service')
events = self.events()
self.assertLess(events.index('stopped'), events.index('dnsmasq:53'))
self.assertLess(events.index('dnsmasq:53'), events.index('started:3053'))
self.assertEqual(self.h.dns()['server'], ['127.0.0.1#3053'])
self.assertNotIn('ubus:event', events)
self.assertEqual(sum(e.startswith('dnsmasq:') for e in events), 1)
def test_stopping_exchange_does_not_schedule_another_start(self):
self.exchange()
(self.path / 'events').write_text('')
self.h.run_shell('stop_service')
self.assertFalse((self.path / 'live-port').exists())
self.assertEqual(self.h.dns()['port'], '53')
self.assertNotIn('ubus:event', self.events())
def test_failed_stop_does_not_modify_ports(self):
self.h.run_shell('uci set AdGuardHome.AdGuardHome.redirect=exchange; reload_service',
expected=1, FAIL_STOP='1')
self.assertEqual((self.path / 'yaml-port').read_text(), '3053')
self.assertEqual(self.h.dns()['port'], '53')
self.assertEqual(self.events(), [])
def test_failed_dnsmasq_restart_does_not_launch_core(self):
self.h.run_shell('uci set AdGuardHome.AdGuardHome.redirect=exchange; reload_service',
expected=1, FAIL_RESTART='1')
self.assertFalse((self.path / 'live-port').exists())
self.assertFalse(any(x.startswith('started:') for x in self.events()))
def test_crashed_core_is_started_with_prepared_ports(self):
self.exchange()
(self.path / 'live-port').unlink()
(self.path / 'events').write_text('')
self.h.run_shell('reload_service')
self.assertEqual((self.path / 'live-port').read_text(), '53')
self.assertEqual(self.events(), ['started:53'])
def test_retry_after_committed_port_but_failed_handoff(self):
self.h.run_shell('uci set AdGuardHome.AdGuardHome.redirect=exchange; reload_service',
expected=1, FAIL_RESTART='1')
self.assertEqual(self.h.dns()['port'], '3053')
self.assertEqual((self.path / 'yaml-port').read_text(), '53')
self.assertEqual(self.h.state().get(dns.BACKUP + '.ready'), '1')
(self.path / 'events').write_text('')
self.h.run_shell('reload_service')
self.assertEqual(self.events(), ['dnsmasq:3053', 'started:53'])
self.assertNotIn(dns.BACKUP, self.h.state())
def test_config_watcher_queues_reload_only_after_dnsmasq_handoff(self):
self.h.run_shell('uci set AdGuardHome.AdGuardHome.redirect=exchange; _do_redirect 1')
events = self.events()
self.assertLess(events.index('dnsmasq:3053'), events.index('ubus:event'))
self.assertEqual(sum(e.startswith('dnsmasq:') for e in events), 1)
def test_repeated_round_trips_keep_ports_and_original_upstreams(self):
for _ in range(3):
self.exchange()
self.h.run_shell('uci set AdGuardHome.AdGuardHome.redirect=dnsmasq-upstream; reload_service')
self.assertEqual((self.path / 'live-port').read_text(), '3053')
self.assertEqual(self.h.dns()['port'], '53')
self.assertEqual(self.h.dns()['server'], ['127.0.0.1#3053'])
self.h.run_shell('stop_service')
self.assertEqual(self.h.dns()['server'], ['9.9.9.9'])
class ProcessHandoffTests(unittest.TestCase):
def test_stop_wait_start_does_not_reuse_a_live_jail(self):
source = (dns.ROOT / 'root/etc/init.d/AdGuardHome').read_text()
helpers = source[source.index('restart_dnsmasq()'):source.index('save_dnsmasq_upstream_config()')]
helpers = helpers.replace('/etc/init.d/dnsmasq', 'dnsmasq_command')
script = helpers + """
ubus() { echo '{}'; }
jsonfilter() { cat >/dev/null; echo 1234; }
old_jail=1
dnsmasq_command() {
echo "$1"
if [ "$1" = start ]; then [ "$old_jail" = 0 ] || return 1; fi
}
wait_for_pid_exit() { echo "wait:$1"; old_jail=0; }
wait_for_dns_port_release() { echo "port:$1"; }
restart_dnsmasq 53
"""
result = subprocess.run(['busybox', 'ash', '-c', script], capture_output=True, text=True, timeout=10)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(result.stdout.splitlines(), ['stop', 'wait:1234', 'port:53', 'start', 'port:53'])
def test_failed_jail_stop_does_not_register_a_replacement(self):
source = (dns.ROOT / 'root/etc/init.d/AdGuardHome').read_text()
helpers = source[source.index('restart_dnsmasq()'):source.index('save_dnsmasq_upstream_config()')]
helpers = helpers.replace('/etc/init.d/dnsmasq', 'dnsmasq_command')
script = helpers + """
ubus() { echo '{}'; }
jsonfilter() { cat >/dev/null; echo 1234; }
dnsmasq_command() { echo "$1"; }
wait_for_pid_exit() { return 1; }
logger() { :; }
restart_dnsmasq 53
"""
result = subprocess.run(['busybox', 'ash', '-c', script], capture_output=True, text=True, timeout=10)
self.assertEqual(result.returncode, 1, result.stderr)
self.assertEqual(result.stdout.splitlines(), ['stop'])
def test_actual_dns_socket_handoff(self):
import socket
import threading
source = (dns.ROOT / 'root/etc/init.d/AdGuardHome').read_text()
helper = source[source.index('wait_for_dns_port_release()'):source.index('restart_dnsmasq()')]
for family in [socket.AF_INET, socket.AF_INET6]:
for kind in [socket.SOCK_STREAM, socket.SOCK_DGRAM]:
with self.subTest(family=family, kind=kind):
sock = socket.socket(family, kind)
self.addCleanup(sock.close)
try:
sock.bind(('127.0.0.1' if family == socket.AF_INET else '::1', 0))
except OSError:
if family == socket.AF_INET6:
continue
raise
if kind == socket.SOCK_STREAM:
sock.listen()
port = sock.getsockname()[1]
blocked = subprocess.run(['sh', '-c', helper + f'\nsleep() {{ :; }}\nwait_for_dns_port_release {port}'], capture_output=True, text=True, timeout=10)
self.assertEqual(blocked.returncode, 1, blocked.stderr)
timer = threading.Timer(0.2, sock.close)
timer.start()
released = subprocess.run(['sh', '-c', helper + f'\nwait_for_dns_port_release {port}'], capture_output=True, text=True, timeout=5)
timer.join()
self.assertEqual(released.returncode, 0, released.stderr)
def test_stop_core_deletes_crashed_instance_without_pid(self):
source = (dns.ROOT / 'root/etc/init.d/AdGuardHome').read_text()
helpers = source[source.index('wait_for_pid_exit()'):source.index('save_dnsmasq_upstream_config()')]
core = source[source.index('stop_core()'):source.index('\nagh_reload()')]
script = helpers + core + '''
ubus() { [ "$2" = list ] && echo '{}' || echo deleted >&2; }
jsonfilter() { cat >/dev/null; case "$2" in *.instance1) echo '{"running":false}';; esac; }
stop_core
'''
result = subprocess.run(['sh','-c',script],capture_output=True,text=True,timeout=10)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIn('deleted',result.stderr)
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,107 @@
"""隔离更新器的文件和下载命令, 验证并发与失败后的锁释放."""
import os
from pathlib import Path
import signal
import subprocess
import tempfile
import time
import unittest
ROOT = Path(__file__).resolve().parents[1]
class UpdateLockTests(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory(prefix='agh-lock-test-')
self.addCleanup(self.tmp.cleanup)
self.directory = Path(self.tmp.name)
self.processes = []
self.addCleanup(self.stop_processes)
self.env = dict(os.environ, TEST_DIR=str(self.directory))
self.write_executable('uci', '''#!/bin/sh
case "$2" in
*.binpath) echo "$TEST_DIR/core";;
*.enabled) echo 0;;
esac
''')
self.write_executable('core', '#!/bin/sh\necho "AdGuardHome version v1.2.3"\n')
self.write_executable('uclient-fetch', '''#!/bin/sh
touch "$TEST_DIR/ready"
while [ ! -f "$TEST_DIR/release" ]; do sleep 0.02; done
printf '%s\n' '{"tag_name":"v1.2.3"}'
''')
source = (ROOT / 'root/usr/share/AdGuardHome/update_core.sh').read_text()
source = source.replace('PATH="/usr/sbin:/usr/bin:/sbin:/bin"',
'PATH="$TEST_DIR:/usr/bin:/bin"')
for path in ['/var/lock/AdGuardHome-update.lock', '/var/run/update_core_error',
'/var/run/update_core', '/tmp/AdGuardHome_update.log']:
source = source.replace(path, str(self.directory / Path(path).name))
self.script = self.write_executable('updater', source)
def write_executable(self, name, content):
path = self.directory / name
path.write_text(content)
path.chmod(0o755)
return path
def start(self, *args):
proc = subprocess.Popen(['sh', str(self.script), *args], env=self.env,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
start_new_session=True)
self.processes.append(proc)
return proc
def stop_processes(self):
for proc in self.processes:
if proc.poll() is None:
os.killpg(proc.pid, signal.SIGKILL)
proc.communicate(timeout=5)
def wait_ready(self):
deadline = time.monotonic() + 5
while not (self.directory / 'ready').exists():
self.assertLess(time.monotonic(), deadline, 'updater did not reach downloader')
time.sleep(0.01)
def test_normal_and_force_contenders_preserve_owner_state(self):
owner = self.start()
self.wait_ready()
log = self.directory / 'AdGuardHome_update.log'
before = log.read_bytes()
for args in [(), ('force',)]:
contender = self.start(*args)
contender.communicate(timeout=5)
self.assertEqual(contender.returncode, 2)
self.assertIsNone(owner.poll())
self.assertEqual(log.read_bytes(), before)
self.assertTrue((self.directory / 'update_core').exists())
self.assertFalse((self.directory / 'update_core_error').exists())
(self.directory / 'release').touch()
owner.communicate(timeout=5)
self.assertEqual(owner.returncode, 0)
self.assertFalse((self.directory / 'update_core').exists())
self.assertTrue((self.directory / 'AdGuardHome-update.lock').exists())
def test_killed_owner_does_not_leave_lock_held(self):
owner = self.start()
self.wait_ready()
os.killpg(owner.pid, signal.SIGKILL)
owner.communicate(timeout=5)
(self.directory / 'release').touch()
successor = self.start()
successor.communicate(timeout=5)
self.assertEqual(successor.returncode, 0)
self.assertFalse((self.directory / 'update_core').exists())
def test_failure_sets_error_and_releases_lock(self):
self.write_executable('uclient-fetch', '#!/bin/sh\nexit 1\n')
for _ in range(2):
proc = self.start()
proc.communicate(timeout=5)
self.assertEqual(proc.returncode, 1)
self.assertTrue((self.directory / 'update_core_error').exists())
self.assertFalse((self.directory / 'update_core').exists())
if __name__ == '__main__':
unittest.main()
+47
View File
@@ -0,0 +1,47 @@
name: Build
on:
push:
tags:
- v*
jobs:
build:
name: Build ${{ matrix.arch }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
arch:
- aarch64_generic
- i386_pentium-mmx
- loongarch64_generic
- mips_mips32
- mipsel_24kc
- mipsel_mips32
- x86_64
steps:
- uses: actions/checkout@main
with:
fetch-depth: 0
- name: Building packages
uses: sbwml/openwrt-gh-action-sdk@go1.24
env:
ARCH: ${{ matrix.arch }}-openwrt-24.10
FEEDNAME: packages_ci
PACKAGES: luci-app-eqosplus
NO_REFRESH_CHECK: true
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.arch }}
path: bin/packages/${{ matrix.arch }}/packages_ci/*.ipk
- name: Upload packages
uses: ncipollo/release-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
allowUpdates: true
replacesArtifacts: true
artifacts: "bin/packages/${{ matrix.arch }}/packages_ci/*.ipk"
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 sirpdboy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+39
View File
@@ -0,0 +1,39 @@
#
# Copyright (C) 2006-2017 OpenWrt.org#
# Copyright (C) 2022-2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-eqosplus
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
THEME_NAME:=eqosplus
PKG_NAME:=luci-app-$(THEME_NAME)
PKG_LICENSE:=Apache-2.0
LUCI_TITLE:=LuCI support for eqosplus.
LUCI_DESCRIPTION:=LuCI support for Easy eqosplus(Support speed limit based on IP address).
LUCI_DEPENDS:=+ip-full +tc +nftables +bc +kmod-ifb
LUCI_PKGARCH:=all
PKG_VERSION:=1.3.0
PKG_RELEASE:=20251128
PKG_MAINTAINER:=sirpdboy <herboy2008@gmail.com>
define Build/Compile
endef
define Package/$(PKG_NAME)/postinst
#!/bin/sh
rm -f /tmp/luci-*
endef
define Package/$(PKG_NAME)/conffiles
/etc/config/eqosplus
endef
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
+91
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-eqosplus<br>
</h1>
<p align="center">
<a href="https://openwrt.org"><img alt="OpenWrt" src="https://img.shields.io/badge/OpenWrt-%E2%89%A519.07-ff0000?logo=openwrt&logoColor=white"></a>
<a href="https://www.google.com/chrome/"><img alt="Chrome" src="https://img.shields.io/badge/Chrome-%E2%89%A5111-4285F3?logo=googlechrome&logoColor=white"></a>
<a href="https://www.apple.com/safari/"><img alt="Safari" src="https://img.shields.io/badge/Safari-%E2%89%A516.4-000000?logo=safari&logoColor=white"></a>
<a href="https://www.mozilla.org/firefox/"><img alt="Firefox" src="https://img.shields.io/badge/Firefox-%E2%89%A5128-FF7138?logo=firefoxbrowser&logoColor=white"></a>
<a target="_blank" href="https://github.com/sirpdboy/luci-app-eqosplus/releases"> <img alt="GitHub release" src="https://img.shields.io/github/v/release/sirpdboy/luci-app-eqosplus"></a>
<a href="https://github.com/sirpdboy/luci-app-eqosplus/releases"><img alt="Downloads" src="https://img.shields.io/github/downloads/sirpdboy/luci-app-eqosplus/total"></a>
</p>
[中文] | [English](README.md)
![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明1.jpg)
Please read this page carefully, which includes precautions and instructions on how to use it.
## Enhanced version of EQOS with timed speed limit and added features such as timed limit.
### Method for downloading source code:
```Brach
# downloading
git clone https://github.com/sirpdboy/luci-app-eqosplus package/luci-app-eqosplus
make menuconfig
```
### Configuration Menu
```Brach
make menuconfig
# find LuCI -> Applications, select luci-app-eqosplus, save and exit
```
### compile
```Brach
# compile
make package/luci-app-eqosplus/compile V=s
```
## describe
- luci-app-eqosplus : https://github.com/sirpdboy/luci-app-eqosplus
![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明2.jpg)
## interface
![screenshots](./doc/eqosplus0.png)
![screenshots](./doc/eqosplus1.png)
![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/eqosplus.png)
![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/eqosplus2.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
- KuCat Theme : https://github.com/sirpdboy/luci-theme-kucat
- KuCat 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)
+109
View File
@@ -0,0 +1,109 @@
![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-eqosplus<br>
</h1>
<p align="center">
<a target="_blank" href="https://github.com/sirpdboy/luci-app-eqosplus/releases">
<img src="https://img.shields.io/github/release/sirpdboy/luci-app-eqosplus.svg?style=flat-square&label=luci-app-eqosplus&colorB=green">
</a>
</p>
[中文] | [English](README.md)
![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明1.jpg)
请 **认真阅读完毕** 本页面,本页面包含注意事项和如何使用。
## 功能说明:
### 定时限速1.2.2版
#### 2023.7.19 定时限速1.2.2:增加更多日期:工作日和休息日,自定义日期1,2,3中间用逗号分隔;加入MAC地址限速,从此不用担心IPV6和IPV4的限速问题。
### 定时限速1.0版
#### 2022.12.24 定时限速在eqos的加强版,加入定时限制等功能。
## 编译使用方法 [![](https://img.shields.io/badge/-编译使用方法-F5F5F5.svg)](#编译使用方法-)
将luci-app-eqosplus添加至 LEDE/OpenWRT 源码的方法。
### 下载源码方法一:
编辑源码文件夹根目录feeds.conf.default并加入如下内容:
```Brach
# feeds获取源码:
src-git eqosplus https://github.com/sirpdboy/luci-app-eqosplus
```
```Brach
# 更新feeds,并安装主题:
scripts/feeds update eqosplus
scripts/feeds install luci-app-eqosplus
```
### 下载源码方法:
```Brach
# 下载源码
git clone https://github.com/sirpdboy/luci-app-eqosplus package/luci-app-eqosplus
make menuconfig
```
### 配置菜单
```Brach
make menuconfig
# 找到 LuCI -> Applications, 选择 luci-app-eqosplus, 保存后退出。
```
### 编译
```Brach
# 编译固件
make package/luci-app-eqosplus/compile V=s
```
## 说明 [![](https://img.shields.io/badge/-说明-F5F5F5.svg)](#说明-)
源码来源:https://github.com/sirpdboy/luci-app-eqosplus
- 你可以随意使用其中的源码,但请注明出处。
![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/说明2.jpg)
## 界面
![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/eqosplus.png)
![screenshots](https://raw.githubusercontent.com/sirpdboy/openwrt/master/doc/eqosplus2.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: 645 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 KiB

@@ -0,0 +1,19 @@
module("luci.controller.eqosplus", package.seeall)
-- Copyright 2022-2023 sirpdboy <herboy2008@gmail.com>
function index()
if not nixio.fs.access("/etc/config/eqosplus") then return end
entry({"admin", "control"}, firstchild(), "Control", 44).dependent = false
local e = entry({"admin", "control", "eqosplus"}, cbi("eqosplus"), _("Eqosplus"), 10)
e.dependent=false
e.acl_depends = { "luci-app-eqosplus" }
entry({"admin", "control", "eqosplus", "status"}, call("act_status")).leaf = true
end
function act_status()
local sys = require "luci.sys"
local e = {}
e.status = sys.call(" busybox ps -w | grep eqosplus | grep -v grep >/dev/null ") == 0
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
@@ -0,0 +1,190 @@
-- Copyright 2022-2023 sirpdboy <herboy2008@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local sys = require "luci.sys"
local ifaces = sys.net:devices()
local WADM = require "luci.tools.webadmin"
local ipc = require "luci.ip"
local a, t, e
a = Map("eqosplus", translate("Network speed limit"))
a.description = translate("Users can limit the network speed for uploading/downloading through MAC, IP, and IP segments (192.168.110.00-192.168.10.200). The speed unit is MB/second.")
a.template = "eqosplus/index"
t = a:section(TypedSection, "eqosplus")
t.anonymous = true
e = t:option(DummyValue, "eqosplus_status", translate("Status"))
e.template = "eqosplus/eqosplus"
e.value = translate("Collecting data...")
ipi = t:option(ListValue, "ifname", translate("Interface"), translate("Set the interface used for restriction, use pppoe-wan for dialing, use WAN hardware interface for DHCP mode (such as eth1), and use br-lan for bypass mode"))
ipi.default = "1"
ipi:value(1,translate("Automatic settings"))
ipi:value("br-lan", translate("br-lan (LAN Bridge)"))
local function get_wan_interfaces()
local result = {}
local ubus = require "ubus"
local conn = ubus.connect()
if not conn then
return result
end
local network_status = conn:call("network.interface", "dump", {})
for _, iface in ipairs(network_status.interface) do
if iface.interface:match("^wan") or iface.interface:match("^pppoe") or iface.proto == "pppoe" then
local dev = iface.l3_device or iface.device
if dev then
table.insert(result, {
name = dev,
proto = iface.proto,
logical_name = iface.interface
})
end
end
end
conn:close()
return result
end
local wan_ifaces = get_wan_interfaces()
for _, iface in ipairs(wan_ifaces) do
ipi:value(iface.name, translate(iface.name) .. (iface.proto == "pppoe" and " (PPPoE)" or " (WAN)"))
end
t = a:section(TypedSection, "device")
t.template = "cbi/tblsection"
t.anonymous = true
t.addremove = true
comment = t:option(Value, "comment", translate("Comment"))
comment.size = 8
e = t:option(Flag, "enable", translate("Enabled"))
e.rmempty = false
e.size = 4
ip = t:option(Value, "mac", translate("IP/MAC"))
ip.size = 8
local function get_devices()
local devices = {}
local seen_ips = {}
local ubus = require "ubus"
local conn = ubus.connect()
local function get_hostname(ip)
local f = io.popen("nslookup "..ip.." 2>/dev/null | grep 'name =' | cut -d'=' -f2 | sed 's/\\.$//'")
if f then
local name = f:read("*l")
f:close()
if name and name ~= "" then
return name:match("^%s*(.-)%s*$")
end
end
local leases_file = io.open("/tmp/dhcp.leases", "r")
if leases_file then
for line in leases_file:lines() do
local mac, ip_lease, _, hostname = line:match("^(%S+)%s+(%S+)%s+(%S+)%s+(%S+)")
if ip_lease == ip and hostname ~= "*" then
leases_file:close()
return hostname
end
end
leases_file:close()
end
return "unknown"
end
if conn then
local leases = conn:call("dhcp", "ipv4leases", {}) or {}
for _, lease in ipairs(leases) do
if lease.ipaddr and lease.mac then
local hostname = lease.hostname or get_hostname(lease.ipaddr)
devices[#devices+1] = {
ip = lease.ipaddr,
mac = lease.mac:upper(),
hostname = hostname,
display = string.format("%s (%s) - %s", lease.ipaddr, lease.mac:upper(), hostname)
}
seen_ips[lease.ipaddr] = true
end
end
conn:close()
end
local arp_cmd = io.popen("ip -4 neigh show dev br-lan 2>/dev/null")
if arp_cmd then
for line in arp_cmd:lines() do
local ip_addr, mac = line:match("^(%S+)%s+.+%s+(%S+)%s+")
if ip_addr and mac and mac ~= "00:00:00:00:00:00" and not seen_ips[ip_addr] then
mac = mac:upper()
local hostname = get_hostname(ip_addr)
devices[#devices+1] = {
ip = ip_addr,
mac = mac,
hostname = hostname,
display = string.format("%s (%s) - %s", ip_addr, mac, hostname)
}
seen_ips[ip_addr] = true
end
end
arp_cmd:close()
end
table.sort(devices, function(a, b) return a.ip < b.ip end)
return devices
end
local devices = get_devices()
for _, dev in ipairs(devices) do
ip:value(dev.ip, dev.display)
end
dl = t:option(Value, "download", translate("Downloads"))
dl.default = '0.1'
dl.size = 4
ul = t:option(Value, "upload", translate("Uploads"))
ul.default = '0.1'
ul.size = 4
function validate_time(self, value, section)
local hh, mm, ss
hh, mm, ss = string.match (value, "^(%d?%d):(%d%d)$")
hh = tonumber (hh)
mm = tonumber (mm)
if hh and mm and hh <= 23 and mm <= 59 then
return value
else
return nil, "Time HH:MM or space"
end
end
e = t:option(Value, "timestart", translate("Start control time"))
e.placeholder = '00:00'
e.default = '00:00'
e.validate = validate_time
e.rmempty = true
e.size = 4
e = t:option(Value, "timeend", translate("Stop control time"))
e.placeholder = '00:00'
e.default = '00:00'
e.validate = validate_time
e.rmempty = true
e.size = 4
week=t:option(Value,"week",translate("Week Day(1~7)"))
week.rmempty = true
week:value('0',translate("Everyday"))
week:value(1,translate("Monday"))
week:value(2,translate("Tuesday"))
week:value(3,translate("Wednesday"))
week:value(4,translate("Thursday"))
week:value(5,translate("Friday"))
week:value(6,translate("Saturday"))
week:value(7,translate("Sunday"))
week:value('1,2,3,4,5',translate("Workday"))
week:value('6,7',translate("Rest Day"))
week.default='0'
week.size = 6
return a
@@ -0,0 +1,3 @@
<%+cbi/valueheader%>
<font class="eqosplus_status"><%=pcdata(self:cfgvalue(section) or self.default or "")%></font>
<%+cbi/valuefooter%>
@@ -0,0 +1,12 @@
<% include("cbi/map") %>
<script type="text/javascript">//<![CDATA[
XHR.poll(2, '<%=luci.dispatcher.build_url("admin", "control", "eqosplus", "status" )%>', null,
function (x, result) {
var status = document.getElementsByClassName('eqosplus_status')[0];
status.setAttribute("style", "font-weight:bold;");
status.setAttribute("color", result.status ? "green" : "red");
status.innerHTML = result.status ? '<%=translate("RUNNING")%>' : '<%=translate("NOT RUNNING")%>';
}
)
//]]>
</script>
@@ -0,0 +1,61 @@
msgid "Eqosplus"
msgstr ""
msgid "Network speed limit"
msgstr ""
msgid "Running state"
msgstr ""
msgid "Not running"
msgstr ""
msgid "Running"
msgstr ""
msgid "Users can limit the network speed for uploading/downloading through MAC, IP, and IP segments (192.168.110.00-192.168.10.200). The speed unit is MB/second."
msgstr ""
msgid "MAC/IP"
msgstr ""
msgid "Downloads"
msgstr ""
msgid "Uploads"
msgstr ""
msgid "Comment"
msgstr ""
msgid "Upload bandwidth(Mbit/s)"
msgstr ""
msgid "Download bandwidth(Mbit/s)"
msgstr ""
msgid "Start control time"
msgstr ""
msgid "Stop control time"
msgstr ""
msgid "Week Day(1~7)"
msgstr ""
msgid "Suggested feedback:"
msgstr ""
msgid "Set the interface used for restriction, use pppoe-wan for dialing, use WAN hardware interface for DHCP mode (such as eth1), and use br-lan for bypass mode"
msgstr ""
msgid "Rest Day"
msgstr ""
msgid "Workday"
msgstr ""
msgid "Automatic settings"
msgstr ""
+61
View File
@@ -0,0 +1,61 @@
msgid "Eqosplus"
msgstr "定时限速"
msgid "Network speed limit"
msgstr "定时网速限制"
msgid "Running state"
msgstr "运行状态"
msgid "Not running"
msgstr "未运行"
msgid "Running"
msgstr "已运行"
msgid "Users can limit the network speed for uploading/downloading through MAC, IP.The speed unit is MB/second."
msgstr "可以通过MAC,IP限制用户上传/下载的网速。速度单位为<font color=\"red\"><b> MB/秒 </b></font>。"
msgid "MAC/IP"
msgstr "MAC/IP"
msgid "Downloads"
msgstr "下载速度"
msgid "Uploads"
msgstr "上传速度"
msgid "Comment"
msgstr "备注"
msgid "Upload bandwidth(Mbit/s)"
msgstr "上传总带宽(Mbit/s)"
msgid "Download bandwidth(Mbit/s)"
msgstr "下载总带宽(Mbit/s)"
msgid "Start control time"
msgstr "起控时间"
msgid "Stop control time"
msgstr "停控时间"
msgid "Week Day(1~7)"
msgstr "星期(1~7)"
msgid "Suggested feedback:"
msgstr "建议反馈:"
msgid "Set the interface used for restriction, use pppoe-wan for dialing, use WAN hardware interface for DHCP mode (such as eth1), and use br-lan for bypass mode"
msgstr "设定用来限制网络速度的接口,拨号使用pppoe-wan,DHCP模式使用WAN口的硬件接口(如:eth1),通用使用br-lan接口"
msgid "Rest Day"
msgstr "休息日"
msgid "Workday"
msgstr "工作日"
msgid "Automatic settings"
msgstr "自动设置"
+61
View File
@@ -0,0 +1,61 @@
msgid "Eqosplus"
msgstr "定时限速"
msgid "Network speed limit"
msgstr "定时网速限制"
msgid "Running state"
msgstr "运行状态"
msgid "Not running"
msgstr "未运行"
msgid "Running"
msgstr "已运行"
msgid "Users can limit the network speed for uploading/downloading through MAC, IP, and IP segments (192.168.110.00-192.168.10.200). The speed unit is MB/second."
msgstr "用户可以通过MAC、IP、IP段(192.168.10.100-192.168.10.200),限制上传/下载的网络速度。速度单位为MB/秒。。"
msgid "MAC/IP"
msgstr "MAC/IP"
msgid "Downloads"
msgstr "下载速度"
msgid "Uploads"
msgstr "上传速度"
msgid "Comment"
msgstr "备注"
msgid "Upload bandwidth(Mbit/s)"
msgstr "上传总带宽(Mbit/s)"
msgid "Download bandwidth(Mbit/s)"
msgstr "下载总带宽(Mbit/s)"
msgid "Start control time"
msgstr "起控时间"
msgid "Stop control time"
msgstr "停控时间"
msgid "Week Day(1~7)"
msgstr "星期(1~7)"
msgid "Suggested feedback:"
msgstr "建议反馈:"
msgid "Set the interface used for restriction, use pppoe-wan for dialing, use WAN hardware interface for DHCP mode (such as eth1), and use br-lan for bypass mode"
msgstr "设定用来限制网络速度的接口,拨号使用pppoe-wan,DHCP模式使用WAN口的硬件接口(如:eth1),通用使用br-lan接口"
msgid "Rest Day"
msgstr "休息日"
msgid "Workday"
msgstr "工作日"
msgid "Automatic settings"
msgstr "自动设置"
@@ -0,0 +1,25 @@
config eqosplus
option enabled '0'
option download '1000'
option upload '30'
option ifname '1'
config device
option timestart '00:00'
option week '0'
option download '1'
option timeend '23:55'
option upload '1'
option mac '192.168.10.10'
option enable '0'
config device
option mac '192.168.10.10-192.168.10.200'
option timestart '00:00'
option timeend '00:00'
option week '0'
option download '0.1'
option upload '0.1'
option enable '0'
@@ -0,0 +1,5 @@
#!/bin/sh
[ "$ACTION" = "ifup" ] && [ "$INTERFACE" = "wan" ] && {
sleep 5
/etc/init.d/eqosplus restart
}
@@ -0,0 +1,54 @@
#!/bin/sh /etc/rc.common
#
# Copyright (C) 2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-eqosplus
#
START=99
USE_PROCD=1
NAME=eqosplus
LOCK="/var/lock/$NAME.lock"
CR=/etc/crontabs/root
start_instance() {
procd_open_instance
procd_set_param command /usr/bin/eqosplusctrl
procd_set_param respawn
procd_set_param stderr 1
procd_close_instance
}
_eqosplus_start() {
if [ "$(grep -c 'option enable .1.' /etc/config/$NAME 2>/dev/null)" -gt "0" ]; then
if [ x$(uci -q get turboacc.config.sw_flow) = 'x1' ] || [ x$(uci -q get turboacc.config.sfe_flow) = 'x1' ] ;then
uci -q set turboacc.config.sw_flow='0'
uci -q set turboacc.config.sfe_flow='0'
uci -q set turboacc.config.hw_flow='0'
uci commit turboacc
/etc/init.d/turboacc restart
fi
touch $LOCK
eqosplus start
start_instance
(crontab -l ; echo "00 1 * * * /etc/init.d/eqosplus start") | sort - | uniq - | crontab -
fi
}
start_service(){
[ -f $LOCK ] && exit
stop_service
_eqosplus_start
rm -f $LOCK
}
service_triggers() {
procd_add_reload_trigger 'eqosplus'
}
stop_service(){
kill -9 $(busybox ps -w | grep 'eqosplusctrl' | grep -v 'grep' | awk '{print $1}') >/dev/null 2>&1
sed -i '/eqosplus/d' $CR >/dev/null 2>&1
rm -f $LOCK 2>/dev/null
eqosplus stop
}
@@ -0,0 +1,13 @@
#!/bin/sh
chmod +x /etc/init.d/eqosplus /usr/bin/eqosplus*
uci -q batch <<-EOF >/dev/null
delete ucitrack.@eqosplus[-1]
add ucitrack eqosplus
set ucitrack.@eqosplus[-1].init=eqosplus
commit ucitrack
EOF
[ -s /etc/config/eqosplus ] || echo "config eqosplus" > /etc/config/eqosplus
rm -f /tmp/luci-indexcache
exit 0
+379
View File
@@ -0,0 +1,379 @@
#!/bin/bash
# Copyright (C) 2006 OpenWrt.org
# Copyright 2022-2025 sirpdboy <herboy2008@gmail.com>
crrun=$1
crid=$2
NAME=eqosplus
IDLIST="/var/$NAME.idlist"
LOCK="/var/lock/$NAME.lock"
TMPID="/var/$NAME.tmpid"
LOG="/var/$NAME_log.log"
# idlist=`uci show $NAME | grep "enable='1'" | grep "device" | grep -oE '\[.*?\]' | grep -o '[0-9]' `
if [ x$(uci get $NAME.@$NAME[0].ifname 2>/dev/null) = 'x1' ]; then
ifname=$(uci -q get network.lan.ifname)
[ "x$ifname" = "x" ] && ifname="device" || ifname="ifname"
dev=$(uci -q get network.wan.$ifname)
[ ! "$dev" ] && dev=br-lan
else
dev=$(uci -q get $NAME.@$NAME[0].ifname 2>/dev/null)
[ ! "$dev" ] && dev=br-lan
fi
bin_nft=$(which nft)
bin_tc=$(which tc)
bin_ip=$(which ip)
DEBUG=1
nft_type() {
if command -v nft >/dev/null; then
nftables_ver="true"
fi
}
# Debug functions
dbg_nft() {
[ "${DEBUG:-0}" -eq 0 ] || echo "DEBUG: nft $*"
$bin_nft "$@"
}
dbg_tc() {
[ "${DEBUG:-0}" -eq 0 ] || echo "DEBUG: tc $*"
$bin_tc "$@"
}
dbg_ip() {
[ "${DEBUG:-0}" -eq 0 ] || echo "DEBUG: ip $*"
$bin_ip "$@"
}
is_macaddr() {
ret=1
if echo "$1" | grep -qE '^([0-9A-Fa-f]{2}[-:]){5}[0-9A-Fa-f]{2}$'; then
ret=0
fi
return $ret
}
# 数值比较函数
is_gt_zero() {
local value=$1
# 使用bc进行浮点数比较
echo "$value > 0" | bc -l 2>/dev/null | grep -q 1
}
# 转换为kbit
to_kbit() {
local mb=$1
# MB/s 转 kbit/s: 1 MB/s = 8192 kbit/s
echo "scale=0; $mb * 8192 / 1" | bc 2>/dev/null || echo "0"
}
# Default commands
tc="dbg_tc"
ip="dbg_ip"
nft="dbg_nft"
nft_type
stop_qos() {
for face in $(tc qdisc show 2>/dev/null | grep htb | awk '{print $5}'); do
$tc qdisc del dev $face root 2>/dev/null
done
$tc qdisc del dev ${dev} root 2>/dev/null
$tc qdisc del dev ${dev}_ifb root 2>/dev/null
$tc qdisc del dev ${dev} ingress 2>/dev/null
$ip link del dev ${dev}_ifb 2>/dev/null 2>&1
if [ -n "$nftables_ver" ]; then
$nft delete table inet ${NAME} 2>/dev/null
$nft delete table bridge ${NAME} 2>/dev/null
fi
echo "" > "$IDLIST" 2>/dev/null
}
init_qosplus() {
insmod sch_htb 2>/dev/null
insmod act_mirred 2>/dev/null
insmod ifb 2>/dev/null
$ip link del dev ${dev}_ifb 2>/dev/null
$ip link add dev ${dev}_ifb name ${dev}_ifb type ifb
$ip link set dev ${dev}_ifb up
$tc qdisc del dev ${dev} root 2>/dev/null
$tc qdisc add dev ${dev} root handle 1:0 htb default 1
$tc class add dev ${dev} parent 1:0 classid 1:1 htb rate 1000mbit ceil 1000mbit
$tc qdisc del dev ${dev}_ifb root 2>/dev/null
$tc qdisc add dev ${dev}_ifb root handle 1:0 htb default 1
$tc class add dev ${dev}_ifb parent 1:0 classid 1:1 htb rate 1000mbit ceil 1000mbit
lanipaddr=$(uci -q get network.lan.ipaddr 2>/dev/null | awk -F '.' '{print $1"."$2"."$3".0/24"}')
if [ -n "$lanipaddr" ]; then
$tc filter add dev $dev parent 1:0 protocol ip prio 1 u32 match ip src "$lanipaddr" match ip dst "$lanipaddr" flowid 1:1
$tc filter add dev ${dev}_ifb parent 1:0 protocol ip prio 1 u32 match ip src "$lanipaddr" match ip dst "$lanipaddr" flowid 1:1
fi
$tc qdisc del dev ${dev} ingress 2>/dev/null
$tc qdisc add dev ${dev} ingress
$tc filter add dev ${dev} parent ffff: protocol all prio 2 u32 match u32 0 0 flowid 1:1 action mirred egress redirect dev ${dev}_ifb
if [ -n "$nftables_ver" ]; then
$nft delete table inet ${NAME} 2>/dev/null
$nft delete table bridge ${NAME} 2>/dev/null
$nft add table inet ${NAME}
$nft add chain inet ${NAME} mark_forward { type filter hook forward priority -200\; policy accept\; }
$nft add chain inet ${NAME} mark_output { type filter hook output priority -200\; policy accept\; }
$nft add chain inet ${NAME} mark_postrouting { type filter hook postrouting priority -200\; policy accept\; }
fi
return 0
}
del_id() {
local list_id=$1
id=$((list_id * 10 + 1000))
[ "${DEBUG:-0}" -eq 0 ] || echo "D: del_id $@ --$id --$mac"
$tc filter del dev ${dev} parent 1: handle 800::$(printf "%x" $id) 2>/dev/null
$tc class del dev ${dev} parent 1:1 classid 1:$id 2>/dev/null
$tc filter del dev ${dev}_ifb parent 1: handle 800::$(printf "%x" $id) 2>/dev/null
$tc class del dev ${dev}_ifb parent 1:1 classid 1:$id 2>/dev/null
mac=$(uci -q get $NAME.@device[$list_id].mac 2>/dev/null)
if [ -n "$nftables_ver" ] && [ -n "$mac" ]; then
if is_macaddr "$mac"; then
$nft delete rule inet ${NAME} mark_forward ether saddr $mac counter 2>/dev/null
$nft delete rule inet ${NAME} mark_output ether saddr $mac counter 2>/dev/null
$nft delete rule inet ${NAME} mark_postrouting ether saddr $mac counter 2>/dev/null
else
Z=$(echo $mac | awk -F '[/]' '{print $2}')
[ -n "$Z" ] && mac=$(echo $mac | awk -F '[/]' '{print $1}') || Z=32
$nft delete rule inet ${NAME} mark_forward ip saddr $mac/$Z counter 2>/dev/null
$nft delete rule inet ${NAME} mark_output ip saddr $mac/$Z counter 2>/dev/null
$nft delete rule inet ${NAME} mark_postrouting ip saddr $mac/$Z counter 2>/dev/null
fi
fi
}
add_mac() {
local list_id=$1
id=$((list_id * 10 + 1000))
M0=$(echo $mac | cut -d : -f 1)$(echo $mac | cut -d : -f 2)
M1=$(echo $mac | cut -d : -f 3)$(echo $mac | cut -d : -f 4)
M2=$(echo $mac | cut -d : -f 5)$(echo $mac | cut -d : -f 6)
[ "${DEBUG:-0}" -eq 0 ] || echo "D: add_mac $@ --id:$id --mac:$mac M012--$M0-$M1-$M2"
if is_gt_zero "$UL"; then
if [ -n "$nftables_ver" ]; then
$nft add rule inet ${NAME} mark_forward ether saddr $mac counter meta mark set $id
$nft add rule inet ${NAME} mark_output ether saddr $mac counter meta mark set $id
$nft add rule inet ${NAME} mark_postrouting ether saddr $mac counter meta mark set $id
fi
$tc class add dev ${dev}_ifb parent 1:1 classid 1:$id htb rate "${UL}kbit" ceil "${UL}kbit" prio $id burst 15k cburst 15k
$tc qdisc add dev ${dev}_ifb parent 1:$id handle ${id}: sfq perturb 10
$tc filter add dev ${dev}_ifb parent 1: protocol ip prio $id handle $id fw flowid 1:$id
$tc filter add dev ${dev}_ifb parent 1: protocol ip prio $((id + 100)) u32 \
match u16 0x0800 0xFFFF at -2 \
match u16 0x${M2} 0xFFFF at -4 \
match u32 0x${M0}${M1} 0xFFFFFFFF at -8 \
flowid 1:$id
fi
if is_gt_zero "$DL"; then
$tc class add dev ${dev} parent 1:1 classid 1:$id htb rate "${DL}kbit" ceil "${DL}kbit" prio $id burst 15k cburst 15k
$tc qdisc add dev ${dev} parent 1:$id handle ${id}: sfq perturb 10
$tc filter add dev ${dev} parent 1: protocol ip prio $id u32 \
match u16 0x0800 0xFFFF at -2 \
match u32 0x${M1}${M2} 0xFFFFFFFF at -12 \
match u16 0x${M0} 0xFFFF at -14 \
flowid 1:$id
fi
}
add_ip() {
local list_id=$1
id=$((list_id * 10 + 1000))
[ "${DEBUG:-0}" -eq 0 ] || echo "D: add_ip $@ --$id --$mac"
Z=$(echo $mac | awk -F '[/]' '{print $2}')
[ -n "$Z" ] && mac=$(echo $mac | awk -F '[/]' '{print $1}') || Z=32
if is_gt_zero "$UL"; then
if [ -n "$nftables_ver" ]; then
$nft add rule inet ${NAME} mark_forward ip saddr $mac/$Z counter meta mark set $id
$nft add rule inet ${NAME} mark_output ip saddr $mac/$Z counter meta mark set $id
$nft add rule inet ${NAME} mark_postrouting ip saddr $mac/$Z counter meta mark set $id
fi
$tc class add dev ${dev}_ifb parent 1:1 classid 1:$id htb rate "${UL}kbit" ceil "${UL}kbit" prio $id burst 15k cburst 15k
$tc qdisc add dev ${dev}_ifb parent 1:$id handle ${id}: sfq perturb 10
$tc filter add dev ${dev}_ifb parent 1: protocol ip prio $id handle $id fw flowid 1:$id
$tc filter add dev ${dev}_ifb parent 1:0 prio $((id + 100)) protocol ip u32 \
match ip src "$mac"/"$Z" \
flowid 1:$id
fi
if is_gt_zero "$DL"; then
$tc class add dev ${dev} parent 1:1 classid 1:$id htb rate "${DL}kbit" ceil "${DL}kbit" prio $id burst 15k cburst 15k
$tc qdisc add dev ${dev} parent 1:$id handle ${id}: sfq perturb 10
$tc filter add dev ${dev} parent 1:0 prio $id protocol ip u32 \
match ip dst "$mac"/"$Z" \
flowid 1:$id
fi
}
check_time() {
local start=$1
local end=$2
local current=$(date +%H%M)
local start_min=$((10#${start:0:2}*60 + 10#${start:3:2}))
local end_min=$((10#${end:0:2}*60 + 10#${end:3:2}))
local current_min=$((10#${current:0:2}*60 + 10#${current:2:2}))
if [[ $start_min -lt $end_min ]]; then
[[ $current_min -ge $start_min && $current_min -lt $end_min ]]
else
[[ $current_min -ge $start_min || $current_min -lt $end_min ]]
fi
}
check_list() {
local i=$1
local start_time=$(uci -q get $NAME.@device[$i].timestart 2>/dev/null)
local end_time=$(uci -q get $NAME.@device[$i].timeend 2>/dev/null)
local wweek=$(uci -q get $NAME.@device[$i].week 2>/dev/null)
local current_weekday=$(date +%u)
[ -z "$start_time" ] && [ -z "$end_time" ] && [ -z "$wweek" ] && return 0
if [ -n "$wweek" ] && [ "$wweek" != "0" ]; then
local day_match=0
for day in $(echo $wweek | tr ',' ' '); do
[ "$day" -eq "$current_weekday" ] && day_match=1 && break
done
[ "$day_match" -eq 0 ] && return 1
fi
if [ -n "$start_time" ] && [ -n "$end_time" ]; then
check_time "$start_time" "$end_time" || return 1
fi
return 0
}
case "$crrun" in
"stop")
stop_qos
;;
"start")
stop_qos > /dev/null 2>&1
sleep 2
init_qosplus
idlist=$(uci show $NAME 2>/dev/null | grep "enable='1'" | grep "device" | grep -oE '\[.*?\]' | grep -o '[0-9]' | sort -nu | sed -e 's/^/!/g' -e 's/$/!/g' > $IDLIST; cat $IDLIST 2>/dev/null | sed -e 's/!//g')
if [ ! -s "$IDLIST" ]; then
return 1
fi
device_count=0
for list in $(echo $idlist | sed -e 's/!//g'); do
if check_list $list; then
mac=$(uci -q get $NAME.@device[$list].mac 2>/dev/null)
download_mb=$(uci -q get $NAME.@device[$list].download 2>/dev/null)
upload_mb=$(uci -q get $NAME.@device[$list].upload 2>/dev/null)
comment=$(uci -q get $NAME.@device[$list].comment 2>/dev/null)
# 转换为kbit
DL=$(to_kbit "$download_mb")
UL=$(to_kbit "$upload_mb")
if [ -n "$mac" ]; then
if is_macaddr "$mac"; then
add_mac $list >>$LOG
echo "✅ MAC限速: $comment ($mac) - 下载:${download_mb}MB/s(${DL}kbit/s) 上传:${upload_mb}MB/s(${UL}kbit/s)" >>$LOG
else
add_ip $list >>$LOG
echo "✅ IP限速: $comment ($mac) - 下载:${download_mb}MB/s(${DL}kbit/s) 上传:${upload_mb}MB/s(${UL}kbit/s)" >>$LOG
fi
device_count=$((device_count + 1))
fi
else
[ -f "$IDLIST" ] && [ $(grep "!${list}!" "$IDLIST" 2>/dev/null | wc -l) -gt 0 ] && {
del_id $list >>$LOG
sed -i "/!$list!/d" "$IDLIST" >/dev/null 2>&1
comment=$(uci -q get $NAME.@device[$list].comment 2>/dev/null)
}
fi
done
echo "EQOSPLUS限速服务启动完成,共配置 $device_count 个设备" >>$LOG
echo ""
echo "当前限速状态:" >>$LOG
tc -s class show dev $dev | grep "rate" | head -5 >>$LOG
tc -s class show dev ${dev}_ifb | grep "rate" | head -5 >>$LOG
;;
"add")
echo "➕ 添加限速规则..."
for list in $(echo $crid | sed -e 's/!//g' | sed 's/,/ /g'); do
mac=$(uci -q get $NAME.@device[$list].mac 2>/dev/null)
download_mb=$(uci -q get $NAME.@device[$list].download 2>/dev/null)
upload_mb=$(uci -q get $NAME.@device[$list].upload 2>/dev/null)
comment=$(uci -q get $NAME.@device[$list].comment 2>/dev/null)
DL=$(to_kbit "$download_mb")
UL=$(to_kbit "$upload_mb")
if is_macaddr $mac; then
add_mac $list
echo "✅ 添加MAC限速: $comment ($mac)" >>$LOG
else
add_ip $list
echo "✅ 添加IP限速: $comment ($mac)" >>$LOG
fi
done
;;
"del")
for list in $(echo $crid | sed -e 's/!//g' | sed 's/,/ /g'); do
comment=$(uci -q get $NAME.@device[$list].comment 2>/dev/null)
mac=$(uci -q get $NAME.@device[$list].mac 2>/dev/null)
del_id $list
done
;;
"status")
echo "EQOSPLUS限速服务状态"
echo "### 网络接口: $dev ###"
echo "# 下载队列统计 #"
tc -s qdisc show dev $dev 2>/dev/null || echo "无下载队列"
echo "# 下载类统计 #"
tc -s class show dev $dev 2>/dev/null | grep -E "class htb|rate [0-9]" || echo "无下载类"
echo "# 下载过滤器 #"
tc -s filter show dev $dev parent 1: 2>/dev/null | head -10 || echo "无下载过滤器"
echo "### 虚拟接口: ${dev}_ifb ###"
echo "# 上传队列统计 #"
tc -s qdisc show dev ${dev}_ifb 2>/dev/null || echo "无上传队列"
echo "# 上传类统计 #"
tc -s class show dev ${dev}_ifb 2>/dev/null | grep -E "class htb|rate [0-9]" || echo "无上传类"
echo "# 上传过滤器 #"
tc -s filter show dev ${dev}_ifb parent 1: 2>/dev/null | head -10 || echo "无上传过滤器"
if [ -n "$nftables_ver" ]; then
echo "### NFTables规则 ###"
nft list table inet ${NAME} 2>/dev/null || echo "没有NFTables规则"
else
echo "NFTables不可用"
fi
# 显示流量统计
echo "### 流量统计 ###"
echo "下载流量:"
tc -s class show dev $dev 2>/dev/null | grep "Sent" | head -5
echo "上传流量:"
tc -s class show dev ${dev}_ifb 2>/dev/null | grep "Sent" | head -5
;;
esac
@@ -0,0 +1,63 @@
#!/bin/sh
# Copyright (C) 2006 OpenWrt.org
# Copyright 2022-2025 sirpdboy <herboy2008@gmail.com>
NAME=eqosplus
IDLIST="/var/$NAME.idlist"
TMPID="/var/$NAME.tmpid"
idlist=`uci show $NAME | grep "enable='1'" | grep "device" | grep -oE '\[.*?\]' | grep -o '[0-9]' `
check_list() {
i=$1
checki='0'
start_time=$(uci -q get $NAME.@device[$i].timestart 2>/dev/null)
end_time=$(uci -q get $NAME.@device[$i].timeend 2>/dev/null)
wweek=`uci -q get $NAME.@device[$i].week `
current_time=$(date +%H:%M)
current_weekday=$(date +%u)
for ww in `echo $wweek | sed 's/,/ /g' `; do
if [ $current_weekday = $ww ] || [ 'x0' = x$ww ] ;then
[ "$start_time" = "$end_time" ] && checki='1' || {
if [ "$start_time" \< "$end_time" ]; then
if [ "$current_time" \> "$start_time" ] && [ "$current_time" \< "$end_time" ] ; then
checki='1'
fi
else
if [ "$start_time" \> "$end_time" ] ; then
if [ "$current_time" \< "$start_time" ] && [ "$current_time" \> "$end_time" ] ; then
checki='1'
fi
fi
fi
}
fi
done
return
}
idlistusr(){
checki='0'
[ -s $IDLIST ] || touch $IDLIST
for list in $idlist ;do
check_list $list
if [ $checki == '1' ] ; then
[ `cat $IDLIST 2>/dev/null | grep "!${list}!" | wc -l ` -gt 0 ] || {
eqosplus add $list
echo "!${list}!" >> $IDLIST ; cat $IDLIST | sort | uniq > $TMPID ;cat $TMPID >$IDLIST ;rm -rf $TMPID
}
else
[ `cat $IDLIST 2>/dev/null | grep "!${list}!" | wc -l ` -gt 0 ] && {
eqosplus del $list
sed -i "/!$list!/d" $IDLIST >/dev/null 2>&1
}
fi
done
}
while :;do
sleep 30
idlistusr
sleep 30
done
@@ -0,0 +1,11 @@
{
"luci-app-eqosplus": {
"description": "Grant UCI access for luci-app-eqosplus",
"read": {
"uci": [ "eqosplus" ]
},
"write": {
"uci": [ "eqosplus" ]
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 585 KiB

+1 -3
View File
@@ -10,10 +10,8 @@ LUCI_DEPENDS:=+fastnet +luci-lib-linkeaseauth +linkease-app-entry
# APK uses -rN; OPKG uses -N for runtime package releases.
LUCI_EXTRA_DEPENDS:=fastnet (>=0.8.9-$(if $(CONFIG_USE_APK),r)1)
LUCI_PKGARCH:=all
PKG_VERSION:=0.8.9-r1
PKG_VERSION:=0.8.9-r2
PKG_RELEASE:=
# Preserve LuCI translation version ordering when upgrading older git-* packages.
PKG_PO_VERSION:=git-26.313.00000-082
LUCI_MINIFY_CSS:=0
LUCI_MINIFY_JS:=0
+2 -2
View File
@@ -1,14 +1,14 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-ghfu
PKG_VERSION:=1.3.2
PKG_VERSION:=1.3.3
PKG_RELEASE:=1
PKG_LICENSE:=GPL-3.0-only
PKG_LICENSE_FILES:=LICENSE
PKG_MAINTAINER:=smallprogram
LUCI_TITLE:=LuCI support for GitHub Firmware Upgrade
LUCI_DEPENDS:=+luci-base +uclient-fetch +ca-bundle
LUCI_DEPENDS:=+luci-base +wget-ssl +ca-bundle
LUCI_PKGARCH:=all
ifneq ($(wildcard $(TOPDIR)/feeds/luci/luci.mk),)
+27 -10
View File
@@ -374,12 +374,12 @@ local function fetch_previous_release_with_assets(repo, timeout, accept_header,
end
fetch_github_json = function(url, timeout, accept_header)
local cmd = "uclient-fetch -T " .. tostring(timeout) .. " -H " .. util.shellquote(accept_header) .. " -qO- " .. util.shellquote(url) .. " 2>/dev/null"
local cmd = "wget -T " .. tostring(timeout) .. " --header=" .. util.shellquote(accept_header) .. " -qO- " .. util.shellquote(url) .. " 2>/dev/null"
local raw = sys.exec(cmd)
-- Fallback for uclient-fetch variants without custom-header support.
-- Fallback for wget variants without custom-header support.
if not raw or raw == "" then
local fallback = "uclient-fetch -T " .. tostring(timeout) .. " -qO- " .. util.shellquote(url) .. " 2>/dev/null"
local fallback = "wget -T " .. tostring(timeout) .. " -qO- " .. util.shellquote(url) .. " 2>/dev/null"
raw = sys.exec(fallback)
end
@@ -593,6 +593,7 @@ function action_download()
local repo = normalize_repo(http.formvalue("repo") or "")
local selected_release = trim(http.formvalue("selected_release") or "")
local keep_config = to_bool(http.formvalue("keep_config")) and "1" or "0"
local cfg = get_cfg()
local asset_url = trim(http.formvalue("asset_url") or "")
local asset_name = trim(http.formvalue("asset_name") or "firmware.bin.gz")
local asset_size = tonumber(trim(http.formvalue("asset_size") or "")) or 0
@@ -637,6 +638,12 @@ function action_download()
local fw_path = "/tmp/ghfu_" .. clean_name
local progress_log = "/tmp/ghfu-download-progress.log"
local raw_log = "/tmp/ghfu-download-raw.log"
local download_timeout = math.floor(tonumber(cfg.fetch_timeout) or 15)
if download_timeout < 120 then
download_timeout = 120
elseif download_timeout > 300 then
download_timeout = 300
end
fs.unlink(progress_log)
fs.unlink(raw_log)
@@ -645,10 +652,20 @@ function action_download()
"raw_log=", util.shellquote(raw_log), "\n",
"target=", util.shellquote(fw_path), "\n",
"total=", tostring(math.max(0, math.floor(asset_size))), "\n",
"timeout=", tostring(download_timeout), "\n",
"url=", util.shellquote(asset_url), "\n",
": > \"$progress_log\"\n",
": > \"$raw_log\"\n",
"uclient-fetch -qO \"$target\" \"$url\" >\"$raw_log\" 2>&1 &\n",
"(\n",
"if command -v wget >/dev/null 2>&1; then\n",
" wget -T \"$timeout\" -O \"$target\" \"$url\" >\"$raw_log\" 2>&1\n",
" rc=$?\n",
"else\n",
" echo \"wget is not installed\" >\"$raw_log\"\n",
" rc=127\n",
"fi\n",
"exit \"$rc\"\n",
") &\n",
"dlpid=$!\n",
"prev_size=0\n",
"prev_ts=$(date +%s)\n",
@@ -769,13 +786,13 @@ function action_download_status()
else
local status2, done_size, done_total, target2 = line:match("^DONE|([A-Z]+)|(%d+)|(%d+)|(.+)$")
if status2 then
if tonumber(done_total) and tonumber(done_total) > 0 then
logs[#logs + 1] = tr("Download finished: ") .. human_bytes(done_size) .. "/" .. human_bytes(done_total)
else
logs[#logs + 1] = tr("Download finished: ") .. human_bytes(done_size)
end
if status2 == "OK" then
if tonumber(done_total) and tonumber(done_total) > 0 then
logs[#logs + 1] = tr("Download finished: ") .. human_bytes(done_size) .. "/" .. human_bytes(done_total)
else
logs[#logs + 1] = tr("Download finished: ") .. human_bytes(done_size)
end
local name2 = tostring(target2 or ""):match("/tmp/ghfu_(.+)$")
if name2 and name2 ~= "" then
logs[#logs + 1] = name2 .. tr(" downloaded successfully")
+1 -1
View File
@@ -5,7 +5,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-honk
PKG_VERSION:=2026.11.11
PKG_VERSION:=2026.11.12.1
PKG_RELEASE:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
@@ -180,7 +180,7 @@ return view.extend({
E('div', { 'class': 'honk-actions' }, actions)
]),
E('div', { 'class': 'honk-meta' }, [
metaItem(_('Core version'), status.core_lane === 'linux66' ? status.core_version + ' · ' + _('Linux 6.6 experimental') : status.core_version),
metaItem(_('Core version'), status.core_version),
metaItem(_('Doona version'), status.doona_version),
metaItem(_('Kernel'), status.kernel)
])
@@ -30,11 +30,30 @@ if command -v apk >/dev/null 2>&1; then
}
' | sort -V | tail -1)
elif command -v opkg >/dev/null 2>&1; then
installed=$(opkg status "$PKG" 2>/dev/null | awk '
/^Version: / { version = $2 }
/^Status: .* installed$/ { installed = 1 }
END { if (installed) print version }
')
# 直接读状态文件:opkg update 占着锁时 opkg status 会失败,面板就误报未安装。
if [ -r /usr/lib/opkg/status ]; then
installed=$(awk -v p="$PKG" '
$1 == "Package:" { cur = $2; ver = ""; ok = 0 }
$1 == "Version:" { ver = $2 }
$1 == "Status:" { ok = ($NF == "installed") }
cur == p && ok && ver != "" { print ver; exit }
' /usr/lib/opkg/status)
else
installed=$(opkg status "$PKG" 2>/dev/null | awk '
/^Version: / { version = $2 }
/^Status: .* installed$/ { installed = 1 }
END { if (installed) print version }
')
fi
latest=$(
for _list in /var/opkg-lists/*; do
[ "${_list%.sig}" = "$_list" ] || continue
zcat "$_list" 2>/dev/null || cat "$_list" 2>/dev/null
done | awk -v p="$PKG" '
$1 == "Package:" { cur = $2 }
cur == p && $1 == "Version:" { print $2 }
' | sort -V | tail -1
)
fi
printf '%s\t%s\n' "$installed" "$latest"
@@ -1,6 +1,6 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-3.0-only
# Background apk index refresh for the Maintenance view. Returns at once.
# Background package index refresh for the Maintenance view. Returns at once.
LOCK=/tmp/luci-app-honk.idx.lock
@@ -10,10 +10,15 @@ if [ -f "$LOCK" ]; then
fi
: > "$LOCK"
(
if command -v apk >/dev/null 2>&1; then
flock -n /tmp/luci-app-honk.apk.lock apk update
fi
) >/dev/null 2>&1 </dev/null &
if command -v apk >/dev/null 2>&1; then
apk update >/dev/null 2>&1 </dev/null &
elif command -v opkg >/dev/null 2>&1; then
opkg update >/dev/null 2>&1 </dev/null &
else
exit 0
fi
_pid=$!
# 有一个镜像卡住就会把 opkg 锁占死,60 秒后直接掐掉。
( sleep 60; kill -TERM "$_pid" 2>/dev/null; sleep 5; kill -KILL "$_pid" 2>/dev/null ) >/dev/null 2>&1 &
exit 0
@@ -33,6 +33,23 @@ service_running() {
honk_lifecycle_managed_pid >/dev/null 2>&1
}
# 镜像卡住时 opkg update 会一直占着锁;这里给它上闸,超时直接掐掉。
pkg_update() {
_limit=$1; shift
"$@" &
_pid=$!
( sleep "$_limit"; kill -TERM "$_pid" 2>/dev/null ) &
_watch=$!
wait "$_pid" 2>/dev/null
_rc=$?
kill -TERM "$_watch" 2>/dev/null
wait "$_watch" 2>/dev/null
if [ "$_rc" != 0 ]; then
echo "note: 索引刷新超时或被中断(${_limit}s),改用现有缓存继续"
fi
return 0
}
restore_service_state() {
if [ "$was_running" = 1 ]; then
echo "--- 升级前为运行状态,重启加载新内核 ---"
@@ -91,7 +108,7 @@ fi
if command -v apk >/dev/null 2>&1; then
(
flock 9
apk update 2>&1
apk list "$PKG" 2>/dev/null | grep -q "^$PKG-" || pkg_update 60 apk update 2>&1
ver=$(apk list "$PKG" 2>/dev/null | awk -v p="$PKG" '$1 ~ "^" p "-[0-9]" { v=$1; sub("^" p "-", "", v); print v }' | sort -V | tail -1)
if [ -n "$ver" ]; then
constraint="$PKG=$ver"
@@ -121,6 +138,26 @@ fi
echo "result: $PKG is at the latest available version"
rc=0
fi
elif command -v opkg >/dev/null 2>&1; then
(
flock 9
opkg list "$PKG" 2>/dev/null | grep -q "^$PKG - " || pkg_update 60 opkg update 2>&1
opkg install "$PKG" 2>&1
exit 0
) 9>/tmp/luci-app-honk.opkg.lock
rc=$?
if [ "$rc" != 0 ]; then
:
elif ! opkg status "$PKG" 2>/dev/null | grep -q '^Status: .* installed$'; then
echo "result: $PKG is not installed"
rc=1
elif opkg list-upgradable 2>/dev/null | grep -q "^${PKG} - "; then
echo "result: $PKG still has a pending upgrade"
rc=1
else
echo "result: $PKG is at the latest available version"
rc=0
fi
else
echo "no package manager found"
exit 3
@@ -73,6 +73,8 @@ url_test_node() {
fi
sleep 2s
probeUrl=$(config_n_get @global_other[0] url_test_url https://www.google.com/generate_204)
# 预热代理链路,减轻首次请求的冷启动影响,提高部分节点的测试成功率和延迟稳定性
curl --connect-timeout 3 --max-time 5 -o /dev/null -I -sk "$@" -x "$curlx" "$probeUrl" >/dev/null 2>&1
result=$(curl --connect-timeout 3 --max-time 5 -o /dev/null -I -sk -w "%{http_code}:%{time_pretransfer}:%{time_starttransfer}" "$@" -x "$curlx" "${probeUrl}") || result=0
# 结束 SS 插件进程
if [ "$_type" != "socks" ]; then
@@ -51,6 +51,8 @@ url_test_node() {
local curlx="socks5h://127.0.0.1:${_tmp_port}"
sleep 2s
local probeUrl=$(config_n_get @global_other[0] url_test_url https://www.google.com/generate_204)
# Warm up the proxy connection to reduce cold-start effects on latency tests.
curl --connect-timeout 3 --max-time 5 -o /dev/null -I -sk -x "$curlx" "$probeUrl" >/dev/null 2>&1
result=$(curl --connect-timeout 3 --max-time 5 -o /dev/null -I -skL -w "%{http_code}:%{time_pretransfer}" -x ${curlx} "${probeUrl}")
# End the SS plugin process
local pid_file="${TMP_PATH}/url_test_${node_id}_plugin.pid"
+1 -2
View File
@@ -9,7 +9,7 @@ LUCI_TITLE:=LuCI support for quickstart
LUCI_DEPENDS:=+quickstart +luci-app-ttyd
LUCI_PKGARCH:=all
PKG_VERSION:=0.12.10-r1
PKG_VERSION:=0.17.1-rc20261028-r1
# PKG_RELEASE MUST be empty for luci.mk
PKG_RELEASE:=
@@ -19,4 +19,3 @@ LUCI_MINIFY_JS:=0
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
@@ -0,0 +1 @@
import{defineComponent as N,computed as c,openBlock as d,createBlock as y,withCtx as I,createElementVNode as e,createVNode as k,toDisplayString as l,createElementBlock as r,createTextVNode as h,createCommentVNode as g}from"/luci-static/quickstart/card-runtime-v1.js";import{N as F,_ as p,n as w}from"./app.js?v=txJtMzYh";import"./vendor.js?v=B46K-l76";const C={class:"fastnet-install","data-fastnet-install-dialog":""},_={class:"fastnet-install-product"},$={class:"fastnet-install-icon"},B={class:"fastnet-install-badge"},V={class:"fastnet-install-description",role:"status"},D={key:0,class:"fastnet-install-error",role:"alert"},U={key:1,class:"fastnet-install-note"},q={class:"fastnet-install-footer"},M={class:"fastnet-install-links"},R={href:"/cgi-bin/luci/admin/store",target:"_blank",rel:"noopener"},T=["disabled"],x={class:"fastnet-install-actions"},A=["disabled","aria-busy"],E={key:0,class:"fastnet-install-spinner","aria-hidden":"true"},P=N({__name:"FastnetInstallDialog",props:{state:{},installing:{type:Boolean},error:{}},emits:["close","install","recheck"],setup(s){const n=s,f=window.vue_lang==="en",t=(o,a)=>f?a:o,i=c(()=>n.installing||n.state==="installing"),m=c(()=>i.value?t("正在安装","Installing"):n.state==="checking"?t("检查中","Checking"):n.state==="update_required"?t("需要更新","Update needed"):n.state==="unknown"?t("待确认","Unconfirmed"):n.state==="failed"?t("安装未完成","Incomplete"):t("尚未安装","Not installed")),v=c(()=>i.value?t("正在安装 FastNet,完成后即可添加外网测速卡片。","Installing FastNet. You can add the Internet speed card once it is ready."):n.state==="checking"?t("正在确认插件和安装状态,请稍候。","Checking the plugin and installation status."):n.state==="update_required"?t("当前插件版本尚未提供可用卡片,请先升级或修复 FastNet。","Update or repair FastNet to make this card available."):n.state==="unknown"?t("暂时无法确认安装状态,请重新读取或到软件中心检查。","Reload the installation status or check FastNet in the software center."):n.state==="failed"?t("FastNet 安装未完成,请检查软件源后重试。","FastNet installation did not complete. Check the package source and retry."):t("添加外网测速模块前,需要先安装 FastNet 插件。","Install FastNet before adding the Internet speed module.")),b=c(()=>n.installing?t("正在安装","Installing"):n.state==="checking"?t("正在检查","Checking"):n.state==="installing"?t("查看安装进度","View progress"):n.state==="update_required"?t("升级或修复","Update or repair"):n.state==="failed"?t("重试安装","Retry installation"):t("安装插件","Install plugin"));return(o,a)=>(d(),y(F,{title:t("安装 FastNet 插件","Install FastNet"),onClose:a[3]||(a[3]=u=>o.$emit("close"))},{default:I(()=>[e("div",C,[e("div",_,[e("span",$,[k(p,{name:"activity"})]),e("div",null,[a[4]||(a[4]=e("strong",null,"FastNet",-1)),e("p",null,l(t("外网速率 · NAT · IPv6","Internet speed · NAT · IPv6")),1)]),e("span",B,l(m.value),1)]),e("p",V,l(v.value),1),s.error?(d(),r("div",D,[a[5]||(a[5]=e("span",{"aria-hidden":"true"},"!",-1)),e("p",null,l(s.error),1)])):(d(),r("div",U,[k(p,{name:i.value?"clock":"check"},null,8,["name"]),e("p",null,l(i.value?t("可以关闭此窗口,安装会在后台继续。","You can close this window. Installation continues in the background."):t("安装后再选择卡片位置,不会自动开始测速。","Choose a card position after installation. No speed test starts automatically.")),1)])),e("div",q,[e("div",M,[e("a",R,[h(l(t("软件中心","Software center")),1),a[6]||(a[6]=e("svg",{viewBox:"0 0 16 16","aria-hidden":"true"},[e("path",{d:"M9 3h4v4M13 3 6 10M7 3H3v10h10V9"})],-1))]),s.installing?g("",!0):(d(),r("button",{key:0,type:"button",onClick:a[0]||(a[0]=u=>o.$emit("recheck")),disabled:s.state==="checking","data-recheck-card-install":""},l(t("重新读取","Reload status")),9,T))]),e("div",x,[e("button",{type:"button",class:"fastnet-install-back",onClick:a[1]||(a[1]=u=>o.$emit("close"))},l(t("返回","Back")),1),s.state!=="unknown"?(d(),r("button",{key:0,type:"button",class:"fastnet-install-primary",disabled:s.installing||s.state==="checking","aria-busy":i.value||s.state==="checking",onClick:a[2]||(a[2]=u=>o.$emit("install")),"data-confirm-card-install":""},[i.value||s.state==="checking"?(d(),r("span",E)):g("",!0),h(l(b.value),1)],8,A)):g("",!0)])])])]),_:1},8,["title"]))}}),W=w(P,[["__scopeId","data-v-a66b8b77"]]);export{W as default};
@@ -0,0 +1,7 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["luci-static/quickstart/FastnetInstallDialog.js","luci-static/quickstart/app.js","luci-static/quickstart/vendor.js","luci-static/quickstart/installClient.js"])))=>i.map(i=>d[i]);
var kr=Object.defineProperty,wr=Object.defineProperties;var $r=Object.getOwnPropertyDescriptors;var ms=Object.getOwnPropertySymbols;var _l=Object.prototype.hasOwnProperty,yl=Object.prototype.propertyIsEnumerable;var fs=Math.pow,co=(s,t,n)=>t in s?kr(s,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):s[t]=n,ye=(s,t)=>{for(var n in t||(t={}))_l.call(t,n)&&co(s,n,t[n]);if(ms)for(var n of ms(t))yl.call(t,n)&&co(s,n,t[n]);return s},Ne=(s,t)=>wr(s,$r(t));var kl=(s,t)=>{var n={};for(var l in s)_l.call(s,l)&&t.indexOf(l)<0&&(n[l]=s[l]);if(s!=null&&ms)for(var l of ms(s))t.indexOf(l)<0&&yl.call(s,l)&&(n[l]=s[l]);return n};var Nn=(s,t,n)=>co(s,typeof t!="symbol"?t+"":t,n);var B=(s,t,n)=>new Promise((l,u)=>{var c=v=>{try{p(n.next(v))}catch(h){u(h)}},r=v=>{try{p(n.throw(v))}catch(h){u(h)}},p=v=>v.done?l(v.value):Promise.resolve(v.value).then(c,r);p((n=n.apply(s,t)).next())});import{defineComponent as X,computed as R,openBlock as i,createElementBlock as d,normalizeStyle as pt,createElementVNode as e,toDisplayString as a,unref as o,createCommentVNode as T,resolveComponent as At,createVNode as G,renderSlot as Ct,withDirectives as W,vModelCheckbox as gt,createBlock as Y,withCtx as pe,ref as x,watch as We,onMounted as nt,Fragment as V,renderList as oe,vModelSelect as Be,vModelText as Oe,createStaticVNode as Hn,shallowRef as Na,nextTick as ot,onUnmounted as lt,Teleport as Fo,withModifiers as Ge,normalizeClass as _e,withKeys as Qt,reactive as Xe,readonly as Cr,markRaw as Kn,onErrorCaptured as Sr,resolveDynamicComponent as Aa,mergeProps as Zl,inject as ba,Transition as fn,createApp as cn,createTextVNode as ue,provide as ma,TransitionGroup as xr,toRef as uo,onBeforeUnmount as ga,vShow as Bs,vModelRadio as Mt,createSlots as Pr,defineAsyncComponent as Tr,h as vo,isRef as Mr,Suspense as Er,render as Us,withAsyncContext as Ir,useCssVars as Dr,vModelDynamic as Lr}from"/luci-static/quickstart/card-runtime-v1.js";import{a as Ar,c as Nr,d as Fs,u as Yl,i as Or,b as zr,e as qr,f as Ql,g as Xl,h as Rr,j as ei,k as as,l as ta,o as ti,m as Vr,n as ss,p as Br,q as Ur,r as Fr}from"./vendor.js?v=B46K-l76";const Bn={language:void 0,numberFormat:new Intl.NumberFormat("en",{notation:"compact"})},tj=s=>B(null,null,function*(){const t=window.vue_lang||(navigator.language.toLowerCase().startsWith("zh")?"zh-cn":"en");let n={};const l=window.vue_lang_data||(t==="zh-cn"?"":`/luci-static/quickstart/i18n/${t}.json`);if(l)try{const r=yield Ar({url:l,method:"GET"});r.data&&(n=r.data)}catch(r){console.log(r)}const u=Nr({defaultLanguage:t,mutedLanguages:["zh-cn"],translations:n,setGlobalProperties:!1,provideDirective:!1,provideComponent:!1});s.use(u),Bn.language=u;const{$gettext:c}=u;window.$i18n=c;try{Bn.numberFormat=new Intl.NumberFormat(t,{notation:"compact"})}catch(r){console.error("Intl.NumberFormat unsupported lang",t,r)}}),Ae=()=>{if(Bn.language)return Bn.language;throw new Error("I18N Uninitialized!")},Hs=()=>({$gettext:(s,t,n)=>{if(Bn.language)return Bn.language.$gettext(s,t,n);throw new Error("I18N Uninitialized!")},$ngettext:(s,t,n,l,u)=>{if(Bn.language)return Bn.language.$ngettext(s,t,n,l,u);throw new Error("I18N Uninitialized!")}}),bs=s=>typeof s=="number"?Bn.numberFormat.format(s):"?",{$gettext:Hr}=Hs(),ni=(s,t)=>B(null,null,function*(){return new Promise((n,l)=>B(null,null,function*(){try{const u=yield fetch(s,t);if(Math.floor(u.status/100)!=2)throw u.status+" "+u.statusText;const c=ye({},u);c.data=yield u.json(),n(c)}catch(u){const c=u;l(Hr("网络异常:")+((c==null?void 0:c.message)||u))}}))});class Ho{constructor(t){Nn(this,"config",{baseURL:"",headers:{}});Nn(this,"useRequest",t=>t);Nn(this,"useResponse",t=>t);Nn(this,"useError",t=>t);t.baseURL&&(this.config.baseURL=t.baseURL),t.headers&&(this.config.headers=t.headers)}static create(t){return new Ho(t)}Do(t,n){return B(this,null,function*(){return new Promise((l,u)=>B(this,null,function*(){try{const c=this.useRequest({baseURL:this.config.baseURL,headers:this.config.headers});t=`${c.baseURL||""}${t}`,n.headers==null&&(n.headers={}),c.headers&&(n.headers=ye({},c.headers));const p=yield fetch(t,n),v=ye({},p);v.data=yield p.json(),l(this.useResponse(v))}catch(c){this.useError(c),u(c)}}))})}TEXT(t,n){return B(this,null,function*(){return new Promise((l,u)=>B(this,null,function*(){try{const c=this.useRequest({baseURL:this.config.baseURL,headers:this.config.headers});t=`${c.baseURL||""}${t}`,n.headers==null&&(n.headers={}),c.headers&&(n.headers=ye({},c.headers));const p=yield fetch(t,n),v=ye({},p);v.data=yield p.text(),l(v)}catch(c){this.useError(c),u(c)}}))})}interceptors(){const t=this;return{requset:{use(n){t.useRequest=n}},response:{use(n,l){t.useResponse=n,l&&(t.useError=l)}}}}}const ai=Ho.create({});ai.interceptors().requset.use((s=>s));ai.interceptors().response.use((s=>(s.data&&s.data.success==null&&s.data.success==0,s)));const{$gettext:si}=Hs(),Gr="/cgi-bin/luci/istore",jr="/cgi-bin/luci/admin";let Ms=!1;const Pe=(s,t)=>{if(!["GET","HEAD","OPTIONS"].includes((t.method||"GET").tLine truncated
`+p.value+`
`+c.value,disabled:""},null,8,pI)]),e("div",hI,[e("div",{class:"close",onClick:g,disabled:u.value},a(o(t)("关闭")),9,mI),u.value?T("",!0):(i(),d("div",{key:0,class:"next",onClick:b,disabled:u.value},a(o(t)("运行")),9,fI))])])]),_:1}))}}),gI=le(bI,[["__scopeId","data-v-3fabbd23"]]),_I={class:"actioner-container"},yI={class:"actioner-container_header"},kI={class:"tabs"},wI={class:"actioner-container_body"},$I={key:0,class:"table"},CI={class:"tr"},SI={class:"td left"},xI={class:"td left"},PI={class:"tr"},TI={class:"td left"},MI={class:"td left"},EI={class:"tr"},II={class:"td left"},DI={class:"td left"},LI=["value"],AI=["value"],NI=["value"],OI={class:"actioner-container_footer"},zI=["disabled"],qI=X({__name:"disk-info",props:{close:{type:Function,required:!0},disk:{type:Object,required:!0}},setup(s){const{$gettext:t,$ngettext:n}=Ae(),l=s,u=x(!1),c=x("info"),r=m=>{switch(c.value=m,m){case"info":break;case"attribute":g();break;case"log":h();break;case"extend":b();break}},p=()=>{u.value=!0,l.close()},v=Xe({log:"",attribute:"",extend:""}),h=()=>B(null,null,function*(){try{const m=yield we.Smart.Test.Result.POST({type:"selftest",devicePath:l.disk.path||""});if(m.data){const{result:f,error:k}=m.data;f&&f.result&&(v.log=f.result),k&&(v.log=k)}}catch(m){v.log=m}}),g=()=>B(null,null,function*(){try{const m=yield we.Smart.Attribute.Result.POST({devicePath:l.disk.path||""});if(m.data){const{result:f,error:k}=m.data;f&&f.result&&(v.attribute=f.result),k&&(v.attribute=k)}}catch(m){v.attribute=m}}),b=()=>B(null,null,function*(){try{const m=yield we.Smart.Extend.Result.POST({devicePath:l.disk.path||""});if(m.data){const{result:f,error:k}=m.data;f&&f.result&&(v.extend=f.result),k&&(v.extend=k)}}catch(m){v.extend=m}});return(m,f)=>(i(),Y(Xs,null,{default:pe(()=>[e("div",_I,[e("div",yI,[e("ul",kI,[e("li",{class:_e({"active cbi-tab":c.value=="info"}),onClick:f[0]||(f[0]=k=>r("info"))},[e("a",null,a(o(t)("设备信息")),1)],2),e("li",{class:_e({"active cbi-tab":c.value=="attribute"}),onClick:f[1]||(f[1]=k=>r("attribute"))},[e("a",null,a(o(t)("属性")),1)],2),e("li",{class:_e({"active cbi-tab":c.value=="log"}),onClick:f[2]||(f[2]=k=>r("log"))},[e("a",null,a(o(t)("自检日志")),1)],2),e("li",{class:_e({"active cbi-tab":c.value=="extend"}),onClick:f[3]||(f[3]=k=>r("extend"))},[e("a",null,a(o(t)("扩展信息")),1)],2)])]),e("div",wI,[c.value=="info"?(i(),d("table",$I,[e("tr",CI,[e("td",SI,a(o(t)("设备")),1),e("td",xI,a(s.disk.path),1)]),e("tr",PI,[e("td",TI,a(o(t)("型号")),1),e("td",MI,a(s.disk.model),1)]),e("tr",EI,[e("td",II,a(o(t)("序号")),1),e("td",DI,a(s.disk.serial),1)])])):c.value=="attribute"?(i(),d("textarea",{key:1,disabled:"",value:v.attribute},null,8,LI)):c.value=="log"?(i(),d("textarea",{key:2,disabled:"",value:v.log},null,8,AI)):c.value=="extend"?(i(),d("textarea",{key:3,disabled:"",value:v.extend},null,8,NI)):T("",!0)]),e("div",OI,[e("div",{class:"close",onClick:p,disabled:u.value},a(o(t)("关闭")),9,zI)])])]),_:1}))}}),RI=le(qI,[["__scopeId","data-v-ba5f9eb4"]]),VI=s=>{const t=document.createElement("div");document.body.appendChild(t);const n=G(CE,Ne(ye({},s),{close:()=>{l()}})),l=()=>{t.remove()};Us(n,t)},BI=s=>{const t=document.createElement("div");document.body.appendChild(t);const n=G(cI,Ne(ye({},s),{close:()=>{l()}})),l=()=>{t.remove()};Us(n,t)},UI=s=>{const t=document.createElement("div");document.body.appendChild(t);const n=G(gI,Ne(ye({},s),{close:()=>{l()}})),l=()=>{t.remove()};Us(n,t)},FI=s=>{const t=document.createElement("div");document.body.appendChild(t);const n=G(RI,Ne(ye({},s),{close:()=>{l()}})),l=()=>{t.remove()};Us(n,t)},HI={class:"cbi-section"},GI={class:"cbi-value"},jI={class:"cbi-value-title"},WI={class:"cbi-value-field"},JI={class:"cbi-checkbox"},KI=["value"],ZI={class:"cbi-value"},YI={class:"cbi-value-title"},QI={class:"cbi-value-field"},XI={class:"cbi-checkbox"},eD={value:"never"},tD={value:"sleep"},nD={value:"standby"},aD={value:"idle"},sD={class:"cbi-value-description"},oD={class:"cbi-value"},lD={class:"cbi-value-title"},iD={class:"cbi-value-field"},rD={class:"cbi-checkbox"},cD={value:0},dD=["value"],uD={class:"cbi-value-description"},vD={class:"cbi-value"},pD={class:"cbi-value-title"},hD={class:"cbi-value-field"},mD={class:"cbi-checkbox"},fD={value:0},bD=["value"],gD={class:"cbi-value-description"},_D={class:"cbi-section cbi-tblsection",id:"cbi-nfs-mount"},yD={class:"table cbi-section-table"},kD={class:"tr cbi-section-table-titles anonymous"},wD={class:"th cbi-section-table-cell","data-widget":"value"},$D={class:"th cbi-section-table-cell","data-widget":"value"},CD={class:"th cbi-section-table-cell","data-widget":"value"},SD={class:"th cbi-section-table-cell","data-widget":"value"},xD={class:"th cbi-section-table-cell","data-widget":"value"},PD={class:"th cbi-section-table-cell","data-widget":"value"},TD={class:"th cbi-section-table-cell","data-widget":"value"},MD={class:"th cbi-section-table-cell","data-widget":"value"},ED={class:"tr cbi-section-table-row"},ID={class:"td cb Line truncated
${n("设备和单设备例外不会被删除。")}`))try{const H=(yield t.DeviceMangement.deviceGroupsV2.POST({action:"delete_group",groupId:he.id,expectedVersion:p.value})).data.result;if(H!=null&&H.error)throw new Error(H.error.message);yield it()}catch(H){c.value=(H==null?void 0:H.message)||n("删除失败")}}),it=()=>B(null,null,function*(){var he,H,J;l.value=!0,c.value="";try{const[ge,ze,Fe]=yield Promise.all([t.DeviceMangement.deviceGroupsV2.GET(),t.DeviceMangement.deviceInventoryV2.GET(),t.DeviceMangement.gatewayTargetsV2.GET().catch(()=>null)]),Ee=ge.data.result;if(Ee!=null&&Ee.error)throw new Error(Ee.error.message);$.value=(Ee==null?void 0:Ee.groups)||[],$e.value=(Ee==null?void 0:Ee.globalPolicy)||null,S.value=(Ee==null?void 0:Ee.effective)||[],p.value=(Ee==null?void 0:Ee.version)||"",v.value=(Ee==null?void 0:Ee.timezone)||"",h.value=(Ee==null?void 0:Ee.nextEvaluationAt)||"",y.value=Ee==null?void 0:Ee.lastBatch,w.value=((he=ze.data.result)==null?void 0:he.devices)||[],C.value=((J=(H=Fe==null?void 0:Fe.data)==null?void 0:H.result)==null?void 0:J.targets)||[]}catch(ge){c.value=(ge==null?void 0:ge.message)||n("读取设备分组失败")}finally{l.value=!1}});return it(),(he,H)=>(i(),d("section",eR,[e("header",tR,[e("div",null,[e("h2",nR,a(o(n)("分组与计划")),1),e("p",null,a(o(n)("把需要相同安排的设备放在一起,一次设置联网、速度、路线、时段或流量额度。")),1)]),e("div",aR,[e("button",{type:"button",onClick:re},a(o(n)("默认规则")),1),e("button",{type:"button",class:"primary",onClick:j},a(o(n)("新建分组")),1)])]),l.value?(i(),Y(yt,{key:0,kind:"loading",title:o(n)("正在读取设备分组")},null,8,["title"])):c.value?(i(),Y(yt,{key:1,kind:"error",title:o(n)("设备分组读取失败"),description:c.value,"action-label":o(n)("重新加载"),onAction:it},null,8,["title","description","action-label"])):(i(),d(V,{key:2},[e("div",sR,[e("span",null,a(o(n)("按路由器时间执行"))+" · "+a(v.value||"—"),1),e("span",null,a(o(n)("下次检查"))+" "+a(q(h.value)),1)]),y.value&&y.value.status!=="unchanged"?(i(),d("div",{key:0,class:_e(["batch-result",y.value.status]),role:"status"},[e("strong",null,a(K(y.value.status)),1),e("span",null,a(o(n)("成功"))+" "+a(y.value.applied)+" · "+a(o(n)("跳过"))+" "+a(y.value.skipped)+" · "+a(o(n)("失败"))+" "+a(y.value.failed),1)],2)):T("",!0),!$.value.length&&!g.value?(i(),Y(yt,{key:1,kind:"empty",title:o(n)("还没有设备分组"),description:o(n)("例如创建“儿童设备”,设置晚间暂停联网和每月流量额度。")},null,8,["title","description"])):(i(),d("div",oR,[(i(!0),d(V,null,oe($.value,J=>(i(),d("article",{key:J.id,class:"group-card"},[e("div",lR,[e("div",null,[e("strong",null,a(J.name),1),e("span",null,a(J.members.length)+" "+a(o(n)("台设备")),1)]),e("span",iR,"P"+a(J.priority),1)]),e("p",null,a(O(J.policy)),1),e("div",rR,a(F(J.members)),1),e("div",cR,[e("button",{type:"button",onClick:ge=>De(J)},a(o(n)("编辑")),9,dR),e("button",{type:"button",class:"danger-link",onClick:ge=>Ze(J)},a(o(n)("删除")),9,uR)])]))),128))])),S.value.length?(i(),d("details",vR,[e("summary",null,[ue(a(o(n)("查看最终生效结果与来源"))+" ",1),e("small",null,a(Math.min(S.value.length,50))+" / "+a(S.value.length),1)]),e("div",pR,[(i(!0),d(V,null,oe(S.value.slice(0,50),J=>(i(),d("div",{key:J.deviceId},[e("span",null,[e("strong",null,a(z(J.deviceId)),1),e("small",null,a(ve(J)),1)]),e("em",null,a(ce(J.sources)),1)]))),128))])])):T("",!0),g.value?(i(),d("form",{key:4,class:"editor",onSubmit:Ge(at,["prevent"])},[e("div",hR,[e("div",null,[e("h3",null,a(m.value?o(n)("默认规则"):b.value?o(n)("编辑分组"):o(n)("新建分组")),1),e("p",null,a(m.value?o(n)("只为没有更具体规则的设备提供默认值。"):o(n)("单设备例外始终优先;多个分组冲突时,优先级较高的生效。")),1)]),e("button",{type:"button",class:"close",onClick:Ke,"aria-label":o(n)("关闭")},"×",8,mR)]),m.value?T("",!0):(i(),d("div",fR,[e("label",null,[e("span",null,a(o(n)("分组名称")),1),W(e("input",{"onUpdate:modelValue":H[0]||(H[0]=J=>_.name=J),maxlength:"64",required:"",placeholder:o(n)("例如:儿童设备")},null,8,bR),[[Oe,_.name,void 0,{trim:!0}]])])])),m.value?T("",!0):(i(),d("details",gR,[e("summary",null,a(o(n)("高级设置")),1),e("label",null,[e("span",null,a(o(n)("优先级")),1),W(e("input",{"onUpdate:modelValue":H[1]||(H[1]=J=>_.priority=J),type:"number",min:"0",max:"999"},null,512),[[Oe,_.priority,void 0,{number:!0}]]),e("small",null,a(o(n)("单设备例外始终优先;多个分组冲突时,优先级较高的生效。")),1)])])),m.value?T("",!0):(i(),d("fieldset",_R,[e("legend",null,[ue(a(o(n)("选择设备"))+" ",1),e("small",null,a(o(n)("已选择"))+" "+a(_.members.length)+" "+a(o(n)("台设备")),1)]),W(e("input",{"onUpdate:modelValue":H[2]||(H[2]=J=>f.value=J),class:"device-search",type:"search",placeholder:o(n)("搜索名称、IP 或 MAC")},null,8,yR),[[Oe,f.value,void 0,{trim: Line truncated
${n("影响规则")}:${Q.affectedCount}
${n("失败时将恢复")}:${Q.rollbackScope.join("、")}`))return;u.value=!0;const ce=(yield t.DeviceMangement.networkRulesV2.APPLY(F)).data.result;if(ce!=null&&ce.error)throw new Error(ce.error.message);b.value="success",g.value=n("规则已删除"),f.value=new Set,yield D()}catch(Q){b.value="error",g.value=(Q==null?void 0:Q.message)||n("删除失败,原配置已恢复")}finally{u.value=!1}}),A=()=>E([...f.value],n("确认删除所选规则?")),U=z=>z.kind==="route"&&["unreachable","unsupported"].includes(z.status),q=z=>E([z.id],n("恢复默认路线?设备重新获取地址后生效。"));return D(),(z,N)=>(i(),d("section",SV,[e("div",xV,[e("div",null,[e("h2",PV,a(o(n)("规则台账")),1),e("p",null,a(o(n)("集中检查离线、孤立或需要修复的规则;新设置请从对应设备详情进入。")),1)])]),l.value?(i(),Y(yt,{key:0,kind:"loading",title:o(n)("正在读取网络规则")},null,8,["title"])):c.value?(i(),Y(yt,{key:1,kind:"error",title:o(n)("网络规则读取失败"),description:c.value,"action-label":o(n)("重新加载"),onAction:D},null,8,["title","description","action-label"])):(i(),d(V,{key:2},[e("div",TV,[W(e("input",{"onUpdate:modelValue":N[0]||(N[0]=F=>r.value=F),type:"search",placeholder:o(n)("搜索设备、MAC、IP 或规则")},null,8,MV),[[Oe,r.value,void 0,{trim:!0}]]),W(e("select",{"onUpdate:modelValue":N[1]||(N[1]=F=>p.value=F)},[e("option",EV,a(o(n)("全部类型")),1),e("option",IV,a(o(n)("地址预留")),1),e("option",DV,a(o(n)("上网路线")),1),e("option",LV,a(o(n)("设备限速")),1),e("option",AV,a(o(n)("联网权限")),1)],512),[[Be,p.value]]),W(e("select",{"onUpdate:modelValue":N[2]||(N[2]=F=>v.value=F)},[e("option",NV,a(o(n)("全部状态")),1),e("option",OV,a(o(n)("正常")),1),e("option",zV,a(o(n)("离线或孤立")),1),e("option",qV,a(o(n)("网关不可达")),1),e("option",RV,a(o(n)("需要修复")),1)],512),[[Be,v.value]])]),f.value.size?(i(),d("div",VV,[e("span",null,a(o(n)("已选择"))+" "+a(f.value.size)+" / 128",1),e("button",{type:"button",class:"danger",disabled:u.value,onClick:A},a(u.value?o(n)("正在应用…"):o(n)("删除所选规则")),9,BV)])):T("",!0),k.value.length?(i(),d("div",UV,[e("table",null,[e("thead",null,[e("tr",null,[e("th",null,[e("input",{type:"checkbox",checked:$.value,onChange:_,"aria-label":o(n)("选择当前结果")},null,40,FV)]),e("th",null,a(o(n)("设备")),1),e("th",null,a(o(n)("规则类型")),1),e("th",null,a(o(n)("最终设置")),1),e("th",null,a(o(n)("状态")),1)])]),e("tbody",null,[(i(!0),d(V,null,oe(k.value,F=>(i(),d("tr",{key:F.id,class:_e({orphaned:F.orphaned})},[e("td",null,[e("input",{type:"checkbox",checked:f.value.has(F.id),onChange:Z=>P(F.id),"aria-label":`${o(n)("选择")} ${F.displayName}`},null,40,HV)]),e("td",null,[e("strong",null,a(F.displayName||o(n)("未命名设备")),1),e("small",null,a(F.mac||F.ip||"—"),1)]),e("td",null,[e("span",GV,a(w(F.kind)),1)]),e("td",null,[ue(a(y(F.summary)),1),F.nextAction?(i(),d("small",jV,a(C(F.nextAction)),1)):T("",!0)]),e("td",null,[e("span",{class:_e(["state-pill",F.status])},a(S(F.status)),3),U(F)?(i(),d("button",{key:0,type:"button",class:"repair",onClick:Z=>q(F)},a(o(n)("恢复默认路线")),9,WV)):T("",!0)])],2))),128))])])])):(i(),Y(yt,{key:1,kind:"empty",title:o(n)("没有匹配的网络规则"),description:o(n)("可从设备详情添加地址、路线和使用限制。")},null,8,["title","description"])),k.value.length?(i(),d("div",JV,[(i(!0),d(V,null,oe(k.value,F=>(i(),d("article",{key:`card-${F.id}`,class:_e({orphaned:F.orphaned})},[e("label",KV,[e("input",{type:"checkbox",checked:f.value.has(F.id),onChange:Z=>P(F.id),"aria-label":`${o(n)("选择")} ${F.displayName}`},null,40,ZV)]),e("div",YV,[e("strong",null,a(F.displayName||o(n)("未命名设备")),1),e("small",null,a(F.mac||F.ip||"—"),1)]),e("span",{class:_e(["state-pill",F.status])},a(S(F.status)),3),e("div",QV,[e("span",XV,a(w(F.kind)),1),e("span",null,a(y(F.summary)),1)]),F.nextAction?(i(),d("p",eB,a(C(F.nextAction)),1)):T("",!0),U(F)?(i(),d("button",{key:1,type:"button",class:"repair",onClick:Z=>q(F)},a(o(n)("恢复默认路线")),9,tB)):T("",!0)],2))),128))])):T("",!0),g.value?(i(),d("p",{key:4,class:_e(["feedback",b.value])},a(g.value),3)):T("",!0)],64))]))}}),aB=le(nB,[["__scopeId","data-v-c463c3c8"]]),sB=new Set(["available","disabled","not_installed","unsupported","error"]),oB={internetAccess:"internet_access",speedLimit:"device_speed_limit",floatGateway:"floating_gateway",trafficInsights:"traffic_insights"},So=(s,t)=>{var l,u,c,r;const n=(r=(u=(l=s==null?void 0:s.capabilities)==null?void 0:l.items)==null?void 0:u[oB[t]])!=null?r:(c=s==null?void 0:s.capabilities)==null?void 0:c[t];return n!=null&&n.state?sB.has(n.state)?n:{state:"error",reason:"capability_state_unknown"}:{state:"error",reason:"capability_missing"}},ha=s=>!!s&&typeof s=="object"&&!Array.isArray(s),Wi=s=>ha(s)&&!s.error&&(s.success===void 0||s.success===0),lB=s=>{if(!Wi(s))return;const t=s.result;if(!ha(t)||t.error||typeo Line truncated
@@ -0,0 +1,8 @@
/**
* vue v3.5.44
* (c) 2018-present Yuxi (Evan) You and Vue contributors
* @license MIT
**/var Zl,Yl;let ii,Se,pe,sn,on,zn,bt,qn,Ql,Nt,St,er;function Gn(e){let t=Object.create(null);for(let n of e.split(","))t[n]=1;return n=>n in t}let te={},Pt=[],He=()=>{},si=()=>!1,an=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||97>e.charCodeAt(2)),Jn=e=>e.startsWith("onUpdate:"),ie=Object.assign,tr=(e,t)=>{let n=e.indexOf(t);n>-1&&e.splice(n,1)},jo=Object.prototype.hasOwnProperty,re=(e,t)=>jo.call(e,t),H=Array.isArray,G=e=>typeof e=="function",ue=e=>typeof e=="string",De=e=>typeof e=="symbol",se=e=>e!==null&&typeof e=="object",nr=e=>(se(e)||G(e))&&G(e.then)&&G(e.catch),ce=Object.prototype.toString,Xn=e=>ue(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,Mt=Gn(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),Zn=e=>{let t=Object.create(null);return n=>t[n]||(t[n]=e(n))},Vo=/-\w/g,me=Zn(e=>e.replace(Vo,t=>t.slice(1).toUpperCase())),Do=/\B([A-Z])/g,Ie=Zn(e=>e.replace(Do,"-$1").toLowerCase()),un=Zn(e=>e.charAt(0).toUpperCase()+e.slice(1)),cn=Zn(e=>e?`on${un(e)}`:""),ke=(e,t)=>!Object.is(e,t),Ht=(e,...t)=>{for(let n=0;n<e.length;n++)e[n](...t)},oi=(e,t,n,l=!1)=>{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:l,value:n})},Yn=e=>{let t=parseFloat(e);return isNaN(t)?e:t},Wt=e=>{let t=ue(e)?Number(e):NaN;return isNaN(t)?e:t},Qn=()=>ii||(ii="u">typeof globalThis?globalThis:"u">typeof self?self:"u">typeof window?window:"u">typeof global?global:{}),Uo=Gn("Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,console,Error,Symbol");function pn(e){if(H(e)){let t={};for(let n=0;n<e.length;n++){let l=e[n],r=ue(l)?(function(i){let s={};return i.replace(Ho,o=>o.startsWith("/*")?"":o).split(Bo).forEach(o=>{if(o){let a=o.split($o);a.length>1&&(s[a[0].trim()]=a[1].trim())}}),s})(l):pn(l);if(r)for(let i in r)t[i]=r[i]}return t}if(ue(e)||se(e))return e}let Bo=/;(?![^(]*\))/g,$o=/:([^]+)/,Ho=/"(?:[^"\\]|\\[^])*"|'(?:[^'\\]|\\[^])*'|\\[^]|\/\*[^]*?\*\//g;function fn(e){let t="";if(ue(e))t=e;else if(H(e))for(let n=0;n<e.length;n++){let l=fn(e[n]);l&&(t+=l+" ")}else if(se(e))for(let n in e)e[n]&&(t+=n+" ");return t.trim()}function Wo(e){if(!e)return null;let{class:t,style:n}=e;return t&&!ue(t)&&(e.class=fn(t)),n&&(e.style=pn(n)),e}let Ko=Gn("itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,playsinline,readonly");function zo(e,t,n){if(e.length!==t.length)return!1;let l=!0;for(let r=0;l&&r<e.length;r++)l=qe(e[r],t[r],n);return l}function ai(e,t,n){if(e.size!==t.size)return!1;let l=Array.from(t),r=new Uint8Array(l.length);for(let i of e){let s=-1;for(let o=0;o<l.length;o++)if(!r[o]&&qe(i,l[o],n)){s=o;break}if(s<0)return!1;r[s]=1}return!0}function qo(e,t,n){let l,r,i,s,o=(l=e,ce.call(l)==="[object Map]"),a=(r=t,ce.call(r)==="[object Map]");if(o||a||(o=(i=e,ce.call(i)==="[object Set]"),a=(s=t,ce.call(s)==="[object Set]"),o||a))return!!o&&!!a&&ai(e,t,n);if(Object.keys(e).length!==Object.keys(t).length)return!1;for(let u in e){let f=e.hasOwnProperty(u),g=t.hasOwnProperty(u);if(f&&!g||!f&&g||!qe(e[u],t[u],n))return!1}return String(e)===String(t)}function ui(e,t,n,l){n||(n=[new Map,new Map]);let[r,i]=n;if(r.has(e)||i.has(t))return r.get(e)===t&&i.get(t)===e;r.set(e,t),i.set(t,e);let s=l(e,t,n);return r.delete(e),i.delete(t),s}function qe(e,t,n){let l,r;if(e===t)return!0;let i=(l=e,ce.call(l)==="[object Date]"),s=(r=t,ce.call(r)==="[object Date]");return i||s?!!i&&!!s&&e.getTime()===t.getTime():(i=De(e),s=De(t),i||s?e===t:(i=H(e),s=H(t),i||s?!!i&&!!s&&ui(e,t,n,zo):(i=se(e),s=se(t),i||s?!!i&&!!s&&ui(e,t,n,qo):String(e)===String(t))))}function el(e,t){return e.findIndex(n=>qe(n,t))}let ci=e=>!!(e&&e.__v_isRef===!0),pi=e=>ue(e)?e:e==null?"":H(e)||se(e)&&(e.toString===ce||!G(e.toString))?ci(e)?pi(e.value):JSON.stringify(e,fi,2):String(e),fi=(e,t)=>{let n;if(ci(t))return fi(e,t.value);if(n=t,ce.call(n)==="[object Map]")return{[`Map(${t.size})`]:[...t.entries()].reduce((l,[r,i],s)=>(l[lr(r,s)+" =>"]=i,l),{})};{let l;if(l=t,ce.call(l)==="[object Set]")return{[`Set(${t.size})`]:[...t.values()].map(r=>lr(r))};{if(De(t))return lr(t);let r;if(se(t)&&!H(t)&&(r=t,ce.call(r)!=="[object Object]"))return String(t)}}return t},lr=(e,t="")=>{var n;return De(e)?`Symbol(${(n=e.description)!=null?n:t})`:e};class rr{constructor(t=!1){this.detached=t,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this._warnOnRun=!0,this.__v_skip=!0,!t&&Se&&(Se.active?(this.parent=Se,this.index=(Se.scopes||(Se.scopes=[])).push(this)-1):(this._active=!1,this._warnOnRun=!1))}get active(){return this._active}pause(){if(this._active){let t,n;if(this._isPaused=!0,this.scopes){let l=this.scopes.slice();for(t=0,n=l.length;t<n;t++)l[t].pause()}for(t=0,n=this.effects.length;t<n;t++)this.effects[t].pause()}}resume(){if(this._active&&this._isPaused){let t,n;if(this._isPaused=!1,thiLine truncated
`&&(c.tagName==="PRE"||c.tagName==="TEXTAREA")&&(J=J.slice(1));let{textContent:be}=c;be!==J&&be!==J.replace(/\r\n|\r/g,`
`)&&(Cl(c,0)||Zt(),c.textContent=h.children)}if(V){if(U||Q||!E||48&K){let J=c.tagName.includes("-"),be=c.namespaceURI.includes("svg")?"svg":c.namespaceURI.includes("MathML")?"mathml":void 0;for(let xe in V)if(U&&(xe.endsWith("value")||xe==="indeterminate")||an(xe)&&!Mt(xe)||xe[0]==="."||J&&!Mt(xe)||F&&F.includes(xe)){var ee,X,Y;if(ee=c,X=xe,Y=V[xe],ja.has(X)&&ee.getAttribute(X)===(Y==null?null:`${Y}`))continue;l(c,xe,null,V[xe],be,y)}}else if(V.onClick)l(c,"onClick",null,V.onClick,void 0,y);else if(4&K&&tt(V.style))for(let J in V.style)V.style[J]}(ne=V&&V.onVnodeBeforeMount)&&Ve(ne,y,h),B&&lt(h,null,y,"beforeMount"),((ne=V&&V.onVnodeMounted)||B||ae)&&Os(()=>{ne&&Ve(ne,y,h),ae&&z.enter(c),B&&lt(h,null,y,"mounted")},T)}return c.nextSibling},v=(c,h,y,T,R,E,P)=>{P=P||!!h.dynamicChildren;let F=h.children,V=F.length,K=!1;for(let A=0;A<V;A++){let B=P?F[A]:F[A]=je(F[A]),z=B.type===kt;c?(z&&!P&&A+1<V&&je(F[A+1]).type===kt&&(a(r(c.data.slice(B.children.length)),y,i(c)),c.data=B.children),c=f(c,B,T,R,E,P)):z&&!B.children?a(B.el=r(""),y):(!K&&(K=!0,Cl(y,1)||Zt()),n(null,B,y,null,T,R,bl(y),E))}return c},_=(c,h,y,T,R,E)=>{let{slotScopeIds:P}=h;P&&(R=R?R.concat(P):P);let F=s(c),V=v(i(c),h,F,y,T,R,E);return V&&Yt(V)&&V.data==="]"?i(h.anchor=V):(Zt(),a(h.anchor=u("]"),F,V),V)},m=(c,h,y,T,R,E)=>{var P,F,V;if(P=c,F=h,Cl(P.parentElement,1)||(V=P).nodeType===1&&wr(V.getAttribute(Sl),1)||(function({props:B}){let z=B&&B[Sl];return typeof z=="string"&&wr(z,1)})(F)||Zt(),h.el=null,E){let B=x(c);for(;;){let z=i(c);if(z&&z!==B)o(z);else break}}let K=i(c),A=s(c);return o(c),n(null,h,A,K,y,T,bl(A),R),y&&(y.vnode.el=h.el,Pl(y,h.el)),K},x=(c,h="[",y="]")=>{let T=0;for(;c;)if((c=i(c))&&Yt(c)&&(c.data===h&&T++,c.data===y)){if(T===0)return i(c);T--}return c},N=(c,h,y)=>{let T=h.parentNode;T&&T.replaceChild(c,h);let R=y;for(;R;)R.vnode.el===h&&(R.vnode.el=R.subTree.el=c),R=R.parent},M=c=>c.nodeType===1&&c.tagName==="TEMPLATE";return[(c,h)=>{if(!h.hasChildNodes()){n(null,c,h),hl(),h._vnode=c;return}f(h.firstChild,c,null,null,null),hl(),h._vnode=c},f]}let ja=new Set(["src","srcset","href","poster"]),Sl="data-allow-mismatch",Va={0:"text",1:"children",2:"class",3:"style",4:"attribute"};function Cl(e,t){if(t===0||t===1)for(;e&&!e.hasAttribute(Sl);)e=e.parentElement;return wr(e&&e.getAttribute(Sl),t)}function wr(e,t){if(e==null)return!1;{if(e==="")return!0;let n=e.split(",");return!!(t===0&&n.includes("children"))||n.includes(Va[t])}}let Da=Qn().requestIdleCallback||(e=>setTimeout(e,1)),Ua=Qn().cancelIdleCallback||(e=>clearTimeout(e)),Ba=(e=1e4)=>t=>{let n=Da(t,{timeout:e});return()=>Ua(n)},$a=e=>(t,n)=>{let l=new IntersectionObserver(i=>{for(let s of i)if(s.isIntersecting){l.disconnect(),t();break}},e),r=i=>{if((function(s){let{top:o,left:a,bottom:u,right:f}=s.getBoundingClientRect(),{innerHeight:g,innerWidth:v}=window;return(o>0&&o<g||u>0&&u<g)&&(a>0&&a<v||f>0&&f<v)})(i))return t(),l.disconnect(),!1;if(getComputedStyle(i).display==="contents"){for(let s=0;s<i.children.length;s++)if(r(i.children[s])===!1)return!1}l.observe(i)};return n(i=>{if(i instanceof Element)return r(i)}),()=>l.disconnect()},Ha=e=>t=>{if(e){let n=matchMedia(e);if(!n.matches)return n.addEventListener("change",t,{once:!0}),()=>n.removeEventListener("change",t);t()}},Wa=(e=[])=>(t,n)=>{ue(e)&&(e=[e]);let l=!1,r=s=>{l||(l=!0,i(),t(),s.target.dispatchEvent(new s.constructor(s.type,s)))},i=()=>{n(s=>{for(let o of e)s.removeEventListener(o,r)})};return n(s=>{for(let o of e)s.addEventListener(o,r,{once:!0})}),i},pt=e=>!!e.type.__asyncLoader;function Ka(e){let t;G(e)&&(e={loader:e});let{loader:n,loadingComponent:l,errorComponent:r,delay:i=200,hydrate:s,timeout:o,suspensible:a=!0,onError:u}=e,f=null,g=0,v=()=>{let _;return f||(_=f=n().catch(m=>{if(m=m instanceof Error?m:Error(String(m)),u)return new Promise((x,N)=>{u(m,()=>x((g++,f=null,v())),()=>N(m),g+1)});throw m}).then(m=>_!==f&&f?f:(m&&(m.__esModule||m[Symbol.toStringTag]==="Module")&&(m=m.default),t=m,m)))};return Cr({name:"AsyncComponentWrapper",__asyncLoader:v,__asyncHydrate(_,m,x){let N=_.isConnected,M=!1;(m.bu||(m.bu=[])).push(()=>M=!0);let c=()=>{M||_.parentNode&&(!N||_.isConnected)&&x()},h=s?()=>{let y=s(c,T=>(function(R,E){if(Yt(R)&&R.data==="["){let P=1,F=R.nextSibling;for(;F;){if(F.nodeType===1){if(E(F)===!1)break}else if(Yt(F))if(F.data==="]"){if(--P==0)break}else F.data==="["&&P++;F=F.nextSibling}}else E(R)})(_,T));y&&(m.bum||(m.bum=[])).push(y)}:c;t?h():v().then(()=>!m.isUnmounted&&h())},get __asyncResolved(){return t},setup(){let _,m,x=Te;if(xr(x),t)return()=>xl(t,x);let N=y=>{f=null,It(y,x,13,!r)};if(a&&x.suspense||Ut)return v().then(y=>()=>xl(y,x)).catch(y=>(N(y),()=>r?fe(r,{error:y}):null));let M=_n(!1),c=_n(),h=_n(!!i);return Nn(()=>{_!=null&&clearTimeout(_),m!=null&&clearTimeout(m)}),i&&(m=setTimeout(()=>{x.isUnmounted||(h.value=!1)},i)),o!=null&&(_=setTimeout(()=>{if(!x.isUnmounted&&!M.value&&!c.value){let y=Error(`Async component timed out after ${o}ms.`);N(y),c.value=y}},o)),v().then(()=>{!x.isUnmounted&&(M Line truncated
`))),e[Be]=Rt(r);let i=l||r.props&&r.props.type==="number";mt(e,t?"change":"input",s=>{s.target.composing||e[Be](ei(e.value,n,i))}),(n||i)&&mt(e,"change",()=>{e.value=ei(e.value,n,i)}),t||(mt(e,"compositionstart",tc),mt(e,"compositionend",Co),mt(e,"change",Co))},mounted(e,{value:t,modifiers:{trim:n,number:l}}){let r=e[Wl];delete e[Wl],r!==void 0&&(e.type==="text"||e.type==="textarea")&&e.value!==r?e[Be](ei(e.value,n,l)):e.value=t==null?"":t},beforeUpdate(e,{value:t,oldValue:n,modifiers:{lazy:l,trim:r,number:i}},s){if(e[Be]=Rt(s),e.composing)return;let o=(i||e.type==="number")&&!/^0\d/.test(e.value)?Yn(e.value):e.value,a=t==null?"":t;if(o===a)return;let u=e.getRootNode();(u instanceof Document||u instanceof ShadowRoot)&&u.activeElement===e&&e.type!=="range"&&(l&&t===n||r&&e.value.trim()===a)||(e.value=a)}},ti={deep:!0,created(e,t,n){e[Be]=Rt(n),mt(e,"change",()=>{let l=e._modelValue,r=nn(e),i=e.checked,s=e[Be];if(H(l)){let o=el(l,r),a=o!==-1;if(i&&!a)s(l.concat(r));else if(!i&&a){let u=[...l];u.splice(o,1),s(u)}}else{let o;if(o=l,ce.call(o)==="[object Set]"){let a=new Set(l);i?a.add(r):a.delete(r),s(a)}else s(Eo(e,i))}})},mounted:xo,beforeUpdate(e,t,n){e[Be]=Rt(n),xo(e,t,n)}};function xo(e,{value:t,oldValue:n},l){let r;if(e._modelValue=t,H(t))r=el(t,l.props.value)>-1;else{let i;if(i=t,ce.call(i)==="[object Set]")r=t.has(l.props.value);else{if(t===n)return;r=qe(t,Eo(e,!0))}}e.checked!==r&&(e.checked=r)}let ni={created(e,{value:t},n){e.checked=qe(t,n.props.value),e[Be]=Rt(n),mt(e,"change",()=>{e[Be](nn(e))})},beforeUpdate(e,{value:t,oldValue:n},l){e[Be]=Rt(l),t!==n&&(e.checked=qe(t,l.props.value))}},wo={deep:!0,created(e,{value:t,modifiers:{number:n}},l){e._modelValue=t,mt(e,"change",()=>{let r,i=Array.prototype.filter.call(e.options,u=>u.selected).map(u=>n?Yn(nn(u)):nn(u)),s=e.multiple,o=s?(r=e._modelValue,ce.call(r)==="[object Set]"?new Set(i):i):i[0],a=e._pendingValue=[s,s?H(o)?i.slice():i:o];try{e[Be](o)}finally{dl(()=>{e._pendingValue===a&&(e._pendingValue=void 0)})}}),e[Be]=Rt(l)},mounted(e,{value:t}){ko(e,t)},beforeUpdate(e,{value:t},n){e._modelValue=t,e[Be]=Rt(n)},updated(e,{value:t}){let n=e._pendingValue;e._pendingValue=void 0,n&&n[0]===e.multiple&&(function(l,r,i){let s;if(!i||H(l))return qe(l,r);if(s=l,ce.call(s)==="[object Set]"){if(l.size!==r.length)return!1;for(let o of r)if(!l.has(o))return!1;return!0}return!1})(t,n[1],n[0])||ko(e,t)}};function ko(e,t){let n,l=e.multiple,r=H(t);if(!l||r||(n=t,ce.call(n)==="[object Set]")){for(let i=0,s=e.options.length;i<s;i++){let o=e.options[i],a=nn(o);if(l)if(r){let u=typeof a;u==="string"||u==="number"?o.selected=t.some(f=>String(f)===String(a)):o.selected=el(t,a)>-1}else o.selected=t.has(a);else if(qe(nn(o),t)){e.selectedIndex!==i&&(e.selectedIndex=i);return}}l||e.selectedIndex===-1||(e.selectedIndex=-1)}}function nn(e){return"_value"in e?e._value:e.value}function Eo(e,t){let n=t?"_trueValue":"_falseValue";return n in e?e[n]:t}let To={created(e,t,n){zl(e,t,n,null,"created")},mounted(e,t,n){zl(e,t,n,null,"mounted")},beforeUpdate(e,t,n,l){zl(e,t,n,l,"beforeUpdate")},updated(e,t,n,l){zl(e,t,n,l,"updated")}};function Ao(e,t){switch(e){case"SELECT":return wo;case"TEXTAREA":return Kl;default:switch(t){case"checkbox":return ti;case"radio":return ni;default:return Kl}}}function zl(e,t,n,l,r){let i=Ao(e.tagName,n.props&&n.props.type)[r];i&&i(e,t,n,l)}let nc=["ctrl","shift","alt","meta"],lc={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>nc.some(n=>e[`${n}Key`]&&!t.includes(n))},rc=(e,t)=>{if(!e)return e;let n=e._withMods||(e._withMods={}),l=t.join(".");return n[l]||(n[l]=(r,...i)=>{for(let s=0;s<t.length;s++){let o=lc[t[s]];if(o&&o(r,t))return}return e(r,...i)})},ic={esc:"escape",space:" ",up:"arrow-up",left:"arrow-left",right:"arrow-right",down:"arrow-down",delete:"backspace"},sc=(e,t)=>{let n=e._withKeys||(e._withKeys={}),l=t.join(".");return n[l]||(n[l]=r=>{if(!("key"in r))return;let i=Ie(r.key);if(t.some(s=>s===i||ic[s]===i))return e(r)})},li=ie({patchProp:ho},Js),Ro=!1;function Oo(){return Nt=Ro?Nt:ks(li),Ro=!0,Nt}let No=(...e)=>{(Nt||(Nt=Ur(li))).render(...e)},oc=(...e)=>{Oo().hydrate(...e)},ri=(...e)=>{let t=(Nt||(Nt=Ur(li))).createApp(...e),{mount:n}=t;return t.mount=l=>{let r=Fo(l);if(!r)return;let i=t._component;G(i)||i.render||i.template||(i.template=r.innerHTML),r.nodeType===1&&(r.textContent="");let s=n(r,!1,Mo(r));return r instanceof Element&&(r.removeAttribute("v-cloak"),r.setAttribute("data-v-app","")),s},t},Po=(...e)=>{let t=Oo().createApp(...e),{mount:n}=t;return t.mount=l=>{let r=Fo(l);if(r)return n(r,!0,Mo(r))},t};function Mo(e){return e instanceof SVGElement?"svg":typeof MathMLElement=="function"&&e instanceof MathMLElement?"mathml":void 0}function Fo(e){return ue(e)?document.querySelector(e):e}let Io=!1, Line truncated
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

@@ -0,0 +1,37 @@
{
"version": 2,
"license": "project-original-ai-assisted",
"reviewRequiredBeforePublicRelease": true,
"icons": [
{"key":"computer","label":"通用电脑","category":"computer","file":"computer.webp","generated":"2026-09-24"},
{"key":"phone","label":"手机","category":"phone","file":"phone.webp","generated":"2026-09-24"},
{"key":"tablet","label":"平板电脑","category":"tablet","file":"tablet.webp","generated":"2026-09-24"},
{"key":"tv","label":"电视","category":"tv","file":"tv.webp","generated":"2026-09-24"},
{"key":"network","label":"无线路由器","category":"network","file":"network.webp","generated":"2026-09-24"},
{"key":"smart-home","label":"智能家居","category":"smart-home","file":"smart-home.webp","generated":"2026-09-24"},
{"key":"camera","label":"摄像头","category":"camera","file":"camera.webp","generated":"2026-09-24"},
{"key":"gaming","label":"游戏设备","category":"gaming","file":"gaming.webp","generated":"2026-09-24"},
{"key":"storage","label":"网络存储","category":"storage","file":"storage.webp","generated":"2026-09-24"},
{"key":"printer","label":"打印机","category":"printer","file":"printer.webp","generated":"2026-09-24"},
{"key":"wearable","label":"穿戴设备","category":"wearable","file":"wearable.webp","generated":"2026-09-24"},
{"key":"unknown","label":"未知设备","category":"computer","file":"unknown.webp","generated":"2026-09-24"},
{"key":"desktop","label":"台式电脑","category":"computer","file":"desktop.webp","generated":"2026-09-25"},
{"key":"laptop","label":"笔记本电脑","category":"computer","file":"laptop.webp","generated":"2026-09-25"},
{"key":"smart-speaker","label":"智能音箱","category":"smart-home","file":"smart-speaker.webp","generated":"2026-09-25"},
{"key":"smart-bulb","label":"智能灯","category":"smart-home","file":"smart-bulb.webp","generated":"2026-09-25"},
{"key":"thermostat","label":"温控器","category":"smart-home","file":"thermostat.webp","generated":"2026-09-25"},
{"key":"sensor","label":"传感器","category":"smart-home","file":"sensor.webp","generated":"2026-09-25"},
{"key":"door-lock","label":"智能门锁","category":"smart-home","file":"door-lock.webp","generated":"2026-09-25"},
{"key":"robot-vacuum","label":"扫地机器人","category":"smart-home","file":"robot-vacuum.webp","generated":"2026-09-25"},
{"key":"air-conditioner","label":"空调","category":"smart-home","file":"air-conditioner.webp","generated":"2026-09-25"},
{"key":"projector","label":"投影仪","category":"tv","file":"projector.webp","generated":"2026-09-25"},
{"key":"set-top-box","label":"电视盒子","category":"tv","file":"set-top-box.webp","generated":"2026-09-25"},
{"key":"game-console","label":"游戏主机","category":"gaming","file":"game-console.webp","generated":"2026-09-25"},
{"key":"handheld-game","label":"掌上游戏机","category":"gaming","file":"handheld-game.webp","generated":"2026-09-25"},
{"key":"home-server","label":"家庭服务器","category":"storage","file":"home-server.webp","generated":"2026-09-25"},
{"key":"network-switch","label":"网络交换机","category":"network","file":"network-switch.webp","generated":"2026-09-25"},
{"key":"access-point","label":"无线接入点","category":"network","file":"access-point.webp","generated":"2026-09-25"},
{"key":"network-bridge","label":"网络桥接器","category":"network","file":"network-bridge.webp","generated":"2026-09-25"},
{"key":"e-reader","label":"电子阅读器","category":"tablet","file":"e-reader.webp","generated":"2026-09-25"}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

@@ -1 +1 @@
{"en":{"- -自定义- -":"- -Custom- -","(请先获取IPv4上游信息)":"(Please obtain IPv4 upstream information first)","(未格式化)":"(Not formatted)","(未挂载)":"(Not mounted)","(无DNS服务器,请之后自行填写公共DNS服务器,例如 223.5.5.5)":"(No DNS server, please fill in the public DNS server later, such as 223.5.5.5)","(系统分区)":"(System partition)","(系统盘)":"(System disk)","* 表示每天":"* Means daily","* 表示每小时":"* Means hourly","* 表示每月":"* Means monthly","%{ countdown }s后 跳转新地址":"%{ countdown }s before redirecting to new address","%{ days }天":["%{ days } d ","%{ days } d "],"%{ hours }小时":["%{ hours } h","%{ hours } h"],"%{ minutes }分":[" %{ minutes } min"," %{ minutes } min"],"%{ seconds }秒":[" %{ seconds } sec"," %{ seconds } sec"],"%{status}下载服务配置向导":"%{status} download service configuration wizard","1、点击“提交”可将变更合并到非沙箱环境":"1. Click 'Submit' to merge changes into non-sandbox environment","1. 满足上述条件以后,点击“当前 IPv4 上游信息”以刷新当前连接信息,成功以后,“自动填写”按钮将被激活。(失败可再次点击)":"1. After the above conditions are met, click 'Current IPv4 Upstream Information' to refresh the current connection information. After success, the 'Auto Fill' button will be activated. (Click again if failed)","2、点击“重置”可将沙箱恢复到初始状态":"2. Click 'Reset' to restore the sandbox to its initial state","2. 点击“自动填写”,将切换到参数页并自动填写。此时依然可以自行调整参数。":"2. Click 'Auto-fill' to switch to the parameter page and auto-fill. Parameters can still be adjusted manually.","223.5.5.5":"223.5.5.5","3、点击“退出”可退出沙箱环境,并放弃沙箱中的数据":"3. Click 'Exit' to exit the sandbox environment and abandon the data in the sandbox","阿里云":"Aliyun","安装失败":"Installation Failed","安装失败,":"Installation Failed,","安装失败或超时,请检查软件源或稍候重试":"Installation Failed or Timed Out, Please Check Software Source or Retry Later","安装中...":"Installing...","半双工":"Half Duplex","保持DHCP服务设置":"Keep DHCP Service Settings","保存":"Save","保存并应用":"Save and Apply","保存成功":"Saved Successfully","保存配置":"Save Configuration","保存中...":"Saving...","备份升级":"Backup/Flash","备用DNS地址":"Backup DNS Address","备用DNS服务器":"Backup DNS Server","备用DNS服务器地址":"Backup DNS Server Address","本向导支持自动或手动配置:":"This wizard supports automatic or manual configuration:","编辑":"Edit","编辑LAN":"Edit LAN","编辑WAN":"Edit WAN","并挂载到":"And Mount to","访客":"visitor","拨号上网":"PPPoE","不格式化,使用原文件系统":"Do Not Format, Use Original File System","不支持挂载":"Not Supported for Mounting","操作":"Operation","测试时磁盘会转动,请选择合适的模式来控制磁盘转动。":"Disk will spin during testing, please choose the appropriate mode to control disk spinning.","测速":"Speedtest","插件备份":"APP Backup","查看磁盘管理信息":"Disk Actions","查看存储服务信息":"Actions","查看高级配置":"Acrions","查看更多详情":" to check details","查看教程":"View tutorial","查看日志":"View log","查看设备信息":"Check Clients","查看网络接口信息":"NIC Info","查看系统信息":"System Utils","查看详情":"View details","常规设置":"General Settings","成员":"Member","初始化成功":"Initialized Successfully","初始化中...":"Initializing...","处理中...":"Processing...","传输时自检":"Self-Test During Transfer","窗口 %{ item }":"Window %{ item }","创建":"Create","创建成功":"Created Successfully","创建计划任务":"Create Scheduled Task","创建中...":"Creating...","创建RAID":"Create RAID","磁盘":"Disk","磁盘管理":"Disk Manager","磁盘信息":"Disk Info","磁盘阵列成员:":"RAID array members:","此操作会将会删除该分区全部数据":"This operation will delete all data on this partition","此操作会将会删除该分区全部数据,并格式化成EXT4,随后自动重启进入沙箱模式,是否继续?":"This operation will delete all data on the partition and format it to EXT4, then automatically reboot into sandbox mode. Do you want to continue?","此分区为只读状态,可能无法写入数据":"This partition is read-only, data may not be writable","此文件系统不支持Docker等应用数据,建议格式化成EXT4文件系统":"This file system does not support Docker and other application data, it is recommended to format as EXT4 file system","存储服务":"Share","错误":"Error","带宽监控":"Bandwidth Monitor","待机":"Standby","待机-处于待机和睡眠模式下不检查设备。此模式下磁盘一般不旋Line truncated
{"en":{"- -自定义- -":"- -Custom- -","(不可用)":"(not available)","(无DNS服务器,请之后自行填写公共DNS服务器,例如 223.5.5.5)":"(No DNS server, please fill in the public DNS server later, such as 223.5.5.5)","(未挂载)":"(Not mounted)","(未格式化)":"(Not formatted)","(系统分区)":"(System partition)","(系统盘)":"(System disk)","(请先获取IPv4上游信息)":"(Please obtain IPv4 upstream information first)","* 表示每天":"* Means daily","* 表示每小时":"* Means hourly","* 表示每月":"* Means monthly","%{ countdown }s后 跳转新地址":"%{ countdown }s before redirecting to new address","%{ days }天":["%{ days } d ","%{ days } d "],"%{ hours }小时":["%{ hours } h","%{ hours } h"],"%{ minutes }分":[" %{ minutes } min"," %{ minutes } min"],"%{ seconds }秒":[" %{ seconds } sec"," %{ seconds } sec"],"%{status}下载服务配置向导":"%{status} download service configuration wizard","1、点击“提交”可将变更合并到非沙箱环境":"1. Click 'Submit' to merge changes into non-sandbox environment","1. 满足上述条件以后,点击“当前 IPv4 上游信息”以刷新当前连接信息,成功以后,“自动填写”按钮将被激活。(失败可再次点击)":"1. After the above conditions are met, click 'Current IPv4 Upstream Information' to refresh the current connection information. After success, the 'Auto Fill' button will be activated. (Click again if failed)","2、点击“重置”可将沙箱恢复到初始状态":"2. Click 'Reset' to restore the sandbox to its initial state","2. 点击“自动填写”,将切换到参数页并自动填写。此时依然可以自行调整参数。":"2. Click 'Auto-fill' to switch to the parameter page and auto-fill. Parameters can still be adjusted manually.","223.5.5.5":"223.5.5.5","3、点击“退出”可退出沙箱环境,并放弃沙箱中的数据":"3. Click 'Exit' to exit the sandbox environment and abandon the data in the sandbox","Aria2高级配置":"Aria2 Adv.","CPU使用率":"CPU Usage","CPU温度":"CPU Temp.","DDNSTO":"DDNSTO","DDNSTO 是一个不需要公网IP也可以在外网访问的穿透域名服务,一个浏览器搞定内网穿透,远程访问Openwrt、远程终端、远程桌面...":"DDNSTO is a domain name penetration service that can be accessed from the Internet without a public IP. A browser can handle intranet penetration and remote access to Openwrt, remote terminal, remote desktop...","DDNSTO控制台":"DDNSTO Console","DHCP":"DHCP","DHCP 主机名(可选)":"DHCP hostname (optional)","DHCP 主机名仅支持 1~63 位英文、数字或中间连字符":"DHCP hostnames only support 1-63 digits English, numbers or middle hyphens","DHCP客户端":"DHCP Client","DHCP的IP池格式错误或超出子网范围":"DHCP IP pool format error or out of subnet range","DNS 洞察":"DNS Insights","DNS 配置方式":"DNS Configuration Method","dnspod":"dnspod","Dnspod":"Dnspod","DNS服务器":"DNS Server","DNS服务器: ":"DNS Server: ","DNS服务器地址":"DNS Server Address","DNS选项":"DNS Options","DNS配置":"DNS Settings","DNS配置已保存":"DNS Saved","DNS错误":"DNS Error","Docker根目录:":"Docker Root Directory: ","Docker迁移向导":"Docker Migration Wizard","Docker高级配置":"Docker Adv.","ID":"Id","IP":"IP","IP 地址":"IP Address","IP 地址: ":"IP Address: ","IPv4 地址":"IPv4 address","IPv4地址":"IPv4 Address","IPv4地址格式错误":"IPv4 Address Format Error","IPv4子网掩码":"IPv4 Subnet Mask","IPv4子网掩码格式错误":"IPv4 Subnet Mask Format Error","IPv6 地址策略":"IPv6 address policy","IPv6地址":"IPv6 Address","IP地址":"IP Address","IP地址版本:":"IP Version: ","IP池结束地址":"IP Pool End Address","IP池起始地址":"IP Pool Start Address","IP限速":"IP Speed Limit","iStoreOS官网":"iStoreOS","JBOD (线性)":"JBOD (Linear)","LAN 接口配置方式":"LAN Interface Configuration Method","LAN口未关联任何物理网口,可能导致路由器失联,是否继续操作?":"LAN port not associated with any physical network port, may cause router disconnection, continue?","MAC":"MAC","MAC:":"MAC:","MAC地址":"MAC address","MAC地址与IP绑定":"MAC address is tied to IP","qBittorrent高级配置":"qBittorrent Adv.","Quickstart 限速服务未就绪。请安装或启动服务后重新检查;系统不会自动改用其他方式。":"Quickstart speed limiting is not ready. Install or start the service, then check again; the system will not switch modes automatically.","RAID ( Redundant Array of Independent Disks )即独立磁盘冗余阵列,简称为「磁盘阵列」,就是用多个独立的磁盘组成在一起形成一个大的磁盘系统,从而实现比单块磁盘更好的存储性能和更高的可靠性。":"RAID (Redundant Array of Independent Disks) is a redundant array of independent disks, which uses multiple independent disks to form a large disk sysLine truncated
@@ -1,3 +1 @@
var go=Object.defineProperty,vo=Object.defineProperties;var bo=Object.getOwnPropertyDescriptors;var xa=Object.getOwnPropertySymbols;var ho=Object.prototype.hasOwnProperty,_o=Object.prototype.propertyIsEnumerable;var We=(o,n,a)=>n in o?go(o,n,{enumerable:!0,configurable:!0,writable:!0,value:a}):o[n]=a,st=(o,n)=>{for(var a in n||(n={}))ho.call(n,a)&&We(o,a,n[a]);if(xa)for(var a of xa(n))_o.call(n,a)&&We(o,a,n[a]);return o},vt=(o,n)=>vo(o,bo(n));var xo=(o,n)=>()=>(n||o((n={exports:{}}).exports,n),n.exports);var ve=(o,n,a)=>(We(o,typeof n!="symbol"?n+"":n,a),a);var L=(o,n,a)=>new Promise((l,u)=>{var d=g=>{try{k(a.next(g))}catch(p){u(p)}},c=g=>{try{k(a.throw(g))}catch(p){u(p)}},k=g=>g.done?l(g.value):Promise.resolve(g.value).then(d,c);k((a=a.apply(o,n)).next())});import{a as wo,c as ko,d as we,b as I,e as Q,u as e,o as r,f as s,g as t,t as i,n as ht,h as D,i as Y,r as _t,j as Yt,w as O,v as Rt,k as Ia,l as Z,m as V,p as E,q as $t,s as Pt,x as pt,F as U,y as tt,z as at,A as jt,B as ut,C as ct,D as ke,E as ee,T as St,G as Nt,H as lt,I as ft,J as xt,K as dt,L as gt,M as Bt,N as Xe,O as Ma,P as La,Q as Oa,R as ta,S as ea,U as Na,V as aa,W as Pe,X as yo,Y as oa,Z as ye,_ as Fo,$ as Eo,a0 as na,a1 as $o,a2 as Te,a3 as Co,a4 as Ie,a5 as Do,a6 as Bo,a7 as Yo,a8 as Ao,a9 as So,aa as zo,ab as Po,ac as To}from"./vendor.js?v=91daea62";var sT=xo(ae=>{const Io=function(){const n=document.createElement("link").relList;if(n&&n.supports&&n.supports("modulepreload"))return;for(const u of document.querySelectorAll('link[rel="modulepreload"]'))l(u);new MutationObserver(u=>{for(const d of u)if(d.type==="childList")for(const c of d.addedNodes)c.tagName==="LINK"&&c.rel==="modulepreload"&&l(c)}).observe(document,{childList:!0,subtree:!0});function a(u){const d={};return u.integrity&&(d.integrity=u.integrity),u.referrerpolicy&&(d.referrerPolicy=u.referrerpolicy),u.crossorigin==="use-credentials"?d.credentials="include":u.crossorigin==="anonymous"?d.credentials="omit":d.credentials="same-origin",d}function l(u){if(u.ep)return;u.ep=!0;const d=a(u);fetch(u.href,d)}};Io();const Gt={language:void 0,numberFormat:new Intl.NumberFormat("en",{notation:"compact"})},Mo=o=>L(ae,null,function*(){const n=window.vue_lang;new Date().getTime();let a={};try{const d=yield wo({url:window.vue_lang_data,method:"GET"});d.data&&(a=d.data)}catch(d){console.log(d)}const l=ko({defaultLanguage:n,mutedLanguages:["zh-cn"],translations:a,setGlobalProperties:!1,provideDirective:!1,provideComponent:!1});o.use(l),Gt.language=l;const{$gettext:u}=l;window.$i18n=u;try{Gt.numberFormat=new Intl.NumberFormat(n,{notation:"compact"})}catch(d){console.error("Intl.NumberFormat unsupported lang",n,d)}}),J=()=>{if(Gt.language)return Gt.language;throw new Error("I18N Uninitialized!")},Me=()=>({$gettext:(o,n,a)=>{if(Gt.language)return Gt.language.$gettext(o,n,a);throw new Error("I18N Uninitialized!")},$ngettext:(o,n,a,l,u)=>{if(Gt.language)return Gt.language.$ngettext(o,n,a,l,u);throw new Error("I18N Uninitialized!")}}),De=o=>typeof o=="number"?Gt.numberFormat.format(o):"?",{$gettext:Lo,$ngettext:uT}=Me(),Va=(o,n)=>L(ae,null,function*(){return new Promise((a,l)=>L(ae,null,function*(){try{const u=yield fetch(o,n);if(Math.floor(u.status/100)!=2)throw u.status+" "+u.statusText;const d=st({},u);d.data=yield u.json(),a(d)}catch(u){const d=u;l(Lo("\u7F51\u7EDC\u5F02\u5E38\uFF1A")+((d==null?void 0:d.message)||u))}}))});class ia{constructor(n){ve(this,"config",{baseURL:"",headers:{}});ve(this,"useRequest",n=>n);ve(this,"useResponse",n=>n);ve(this,"useError",n=>n);n.baseURL&&(this.config.baseURL=n.baseURL),n.headers&&(this.config.headers=n.headers)}static create(n){return new ia(n)}Do(n,a){return L(this,null,function*(){return new Promise((l,u)=>L(this,null,function*(){try{const d=this.useRequest({baseURL:this.config.baseURL,headers:this.config.headers});n=`${d.baseURL||""}${n}`,a.headers==null&&(a.headers={}),d.headers&&(a.headers=st({},d.headers));const k=yield fetch(n,a),g=st({},k);g.data=yield k.json(),l(this.useResponse(g))}catch(d){this.useError(d),u(d)}}))})}TEXT(n,a){return L(this,null,function*(){return new Promise((l,u)=>L(this,null,function*(){try{const d=this.useRequest({baseURL:this.config.baseURL,headers:this.config.headers});n=`${d.baseURL||""}${n}`,a.headers==null&&(a.headers={}),d.headers&&(a.headers=st({},d.headers));const k=yield fetch(n,a),g=st({},k);g.data=yield k.text(),l(g)}catch(d){this.useError(d),u(d)}}))})}interceptors(){const n=this;return{requset:{use(a){n.useRequest=a}},response:{use(a,l){n.useResponse=a,l&&(n.useError=l)}}}}}const Ga=ia.create({});Ga.interceptors().requset.use(o=>o);Ga.interceptors().response.use(o=>(o.data&&o.data.success==null&&o.data.success==0,o));const{$gettext:ja,$ngettext:cT}=Me(),Oo="/cgi-bin/luci/istore",No="/cgi-bin/luci/admin";let Ae=!1;const W=(o,n)=>(o.indexOf("//")==-1&&(o=`${Oo}${o}`),Va(o,n).then(a=>(a!=null&&a.data&&a.data.success==-1001&&a.data.error=="Forbidden"&&(Ae||(Ae=!0,alert(ja("\u767B\u5F55\u8FC7\u671F\uFF0C\u8BF7\u91CLine truncated
`+k.value+`
`+d.value,disabled:""},null,8,RF)]),t("div",WF,[t("div",{class:"close",onClick:m,disabled:u.value},i(e(a)("\u5173\u95ED")),9,HF),u.value?D("",!0):(r(),s("div",{key:0,class:"next",onClick:v,disabled:u.value},i(e(a)("\u8FD0\u884C")),9,JF))])])]),_:1}))}});var KF=N(ZF,[["__scopeId","data-v-70c3aae0"]]);const QF={class:"actioner-container"},XF={class:"actioner-container_header"},tE={class:"tabs"},eE={class:"actioner-container_body"},aE={key:0,class:"table"},oE={class:"tr"},nE={class:"td left"},iE={class:"td left"},rE={class:"tr"},sE={class:"td left"},dE={class:"td left"},lE={class:"tr"},uE={class:"td left"},cE={class:"td left"},pE=["value"],fE=["value"],mE=["value"],gE={class:"actioner-container_footer"},vE=["disabled"],bE=I({props:{close:{type:Function,required:!0},disk:{type:Object,required:!0}},setup(o){const n=o,{$gettext:a,$ngettext:l}=J(),u=E(!1),d=E("info"),c=b=>{switch(d.value=b,b){case"info":break;case"attribute":m();break;case"log":p();break;case"extend":v();break}},k=()=>{u.value=!0,n.close()},g=gt({log:"",attribute:"",extend:""}),p=()=>L(this,null,function*(){try{const b=yield G.Smart.Test.Result.POST({type:"selftest",devicePath:n.disk.path||""});if(b.data){const{result:y,error:f}=b.data;y&&y.result&&(g.log=y.result),f&&(g.log=f)}}catch(b){g.log=b}}),m=()=>L(this,null,function*(){try{const b=yield G.Smart.Attribute.Result.POST({devicePath:n.disk.path||""});if(b.data){const{result:y,error:f}=b.data;y&&y.result&&(g.attribute=y.result),f&&(g.attribute=f)}}catch(b){g.attribute=b}}),v=()=>L(this,null,function*(){try{const b=yield G.Smart.Extend.Result.POST({devicePath:n.disk.path||""});if(b.data){const{result:y,error:f}=b.data;y&&y.result&&(g.extend=y.result),f&&(g.extend=f)}}catch(b){g.extend=b}});return(b,y)=>(r(),Z(je,null,{default:V(()=>[t("div",QF,[t("div",XF,[t("ul",tE,[t("li",{class:lt({"active cbi-tab":d.value=="info"}),onClick:y[0]||(y[0]=f=>c("info"))},[t("a",null,i(e(a)("\u8BBE\u5907\u4FE1\u606F")),1)],2),t("li",{class:lt({"active cbi-tab":d.value=="attribute"}),onClick:y[1]||(y[1]=f=>c("attribute"))},[t("a",null,i(e(a)("\u5C5E\u6027")),1)],2),t("li",{class:lt({"active cbi-tab":d.value=="log"}),onClick:y[2]||(y[2]=f=>c("log"))},[t("a",null,i(e(a)("\u81EA\u68C0\u65E5\u5FD7")),1)],2),t("li",{class:lt({"active cbi-tab":d.value=="extend"}),onClick:y[3]||(y[3]=f=>c("extend"))},[t("a",null,i(e(a)("\u6269\u5C55\u4FE1\u606F")),1)],2)])]),t("div",eE,[d.value=="info"?(r(),s("table",aE,[t("tr",oE,[t("td",nE,i(e(a)("\u8BBE\u5907")),1),t("td",iE,i(o.disk.path),1)]),t("tr",rE,[t("td",sE,i(e(a)("\u578B\u53F7")),1),t("td",dE,i(o.disk.model),1)]),t("tr",lE,[t("td",uE,i(e(a)("\u5E8F\u53F7")),1),t("td",cE,i(o.disk.serial),1)])])):d.value=="attribute"?(r(),s("textarea",{key:1,disabled:"",value:e(g).attribute},null,8,pE)):d.value=="log"?(r(),s("textarea",{key:2,disabled:"",value:e(g).log},null,8,fE)):d.value=="extend"?(r(),s("textarea",{key:3,disabled:"",value:e(g).extend},null,8,mE)):D("",!0)]),t("div",gE,[t("div",{class:"close",onClick:k,disabled:u.value},i(e(a)("\u5173\u95ED")),9,vE)])])]),_:1}))}});var hE=N(bE,[["__scopeId","data-v-313197ee"]]);const _E=o=>{const n=document.createElement("div");document.body.appendChild(n);const a=Y(tF,vt(st({},o),{close:()=>{l()}})),l=()=>{n.remove()};Ie(a,n)},xE=o=>{const n=document.createElement("div");document.body.appendChild(n);const a=Y(GF,vt(st({},o),{close:()=>{l()}})),l=()=>{n.remove()};Ie(a,n)},wE=o=>{const n=document.createElement("div");document.body.appendChild(n);const a=Y(KF,vt(st({},o),{close:()=>{l()}})),l=()=>{n.remove()};Ie(a,n)},kE=o=>{const n=document.createElement("div");document.body.appendChild(n);const a=Y(hE,vt(st({},o),{close:()=>{l()}})),l=()=>{n.remove()};Ie(a,n)},yE={class:"cbi-section"},FE={class:"cbi-value"},EE={class:"cbi-value-title"},$E={class:"cbi-value-field"},CE={class:"cbi-checkbox"},DE=["value"],BE={class:"cbi-value"},YE={class:"cbi-value-title"},AE={class:"cbi-value-field"},SE={class:"cbi-checkbox"},zE={value:"never"},PE={value:"sleep"},TE={value:"standby"},IE={value:"idle"},ME={class:"cbi-value-description"},LE=t("br",null,null,-1),OE=t("br",null,null,-1),NE=t("br",null,null,-1),VE=t("br",null,null,-1),GE={class:"cbi-value"},jE={class:"cbi-value-title"},UE={class:"cbi-value-field"},qE={class:"cbi-checkbox"},RE={value:0},WE=["value"],HE={class:"cbi-value-description"},JE={class:"cbi-value"},ZE={class:"cbi-value-title"},KE={class:"cbi-value-field"},QE={class:"cbi-checkbox"},XE={value:0},t$=["value"],e$={class:"cbi-value-description"},a$={class:"cbi-section cbi-tblsection",id:"cbi-nfs-mount"},o$={class:"table cbi-section-table"},n$={class:"tr cbi-section-table-titles anonymous"},i$={class:"th cbi-section-table-cell","data-widget":"value"},r$={class:"th cbi-section-table-cell","data-widget":"value"},s$={class:"th cbi-section-table-cell","data-widget":"value"},d$={class:"th cbi-section-table-cell","data-widget":"value"},l$={class:"th cbi-section-table-cell","data-widget":"value"},u$={class:"th cbi-section-table-cell","data-widget":"value" Line truncated
import{createApp as c}from"/luci-static/quickstart/card-runtime-v1.js";import{i as a,A as p,S as m,a as l,b as d,c as u,d as f,e as g,f as v,p as S,g as h,h as y,j as w,k as b,E as O,l as A,r as L,m as P}from"./app.js?v=txJtMzYh";import{s as I}from"./vendor.js?v=B46K-l76";(function(){const s=document.createElement("link").relList;if(s&&s.supports&&s.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))r(o);new MutationObserver(o=>{for(const t of o)if(t.type==="childList")for(const n of t.addedNodes)n.tagName==="LINK"&&n.rel==="modulepreload"&&r(n)}).observe(document,{childList:!0,subtree:!0});function i(o){const t={};return o.integrity&&(t.integrity=o.integrity),o.referrerPolicy&&(t.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?t.credentials="include":o.crossOrigin==="anonymous"?t.credentials="omit":t.credentials="same-origin",t}function r(o){if(o.ep)return;o.ep=!0;const t=i(o);fetch(o.href,t)}})();a();const e=c(p);e.component("svg-menu",m);e.component("svg-system",l);e.component("svg-download",d);e.component("svg-store",u);e.component("svg-info",f);e.component("svg-disk",g);e.component("svg-nav",v);e.component("progress-item",S);e.component("svg-view-show",h);e.component("svg-view-hidden",y);e.component("article-item",w);e.component("switch-box",b);e.component("editable-select",O);e.use(A);e.use(L);e.use(I());P(e).finally(()=>e.mount("#app"));
@@ -0,0 +1 @@
var p=(m,b,s)=>new Promise((t,c)=>{var u=e=>{try{f(s.next(e))}catch(r){c(r)}},i=e=>{try{f(s.throw(e))}catch(r){c(r)}},f=e=>e.done?t(e.value):Promise.resolve(e.value).then(u,i);f((s=s.apply(m,b)).next())});import{o as y,q as E}from"./app.js?v=txJtMzYh";import"/luci-static/quickstart/card-runtime-v1.js";import"./vendor.js?v=B46K-l76";function v(m=fetch,b=y,s=t=>new Promise((c,u)=>{const i=()=>{clearTimeout(f),u(new DOMException("Aborted","AbortError"))},f=setTimeout(()=>{t==null||t.removeEventListener("abort",i),c()},3e3);t==null||t.addEventListener("abort",i,{once:!0}),t!=null&&t.aborted&&i()})){const t=(e,r)=>typeof window!="undefined"&&window.vue_lang==="en"?r:e,c=e=>{const r=E.find(n=>n.id===e);if(!r)throw Error("Unsupported card");return r.package};function u(e,r,n){return p(this,null,function*(){var l;const o=new AbortController,a=()=>o.abort(),w=setTimeout(a,8e3);n==null||n.addEventListener("abort",a,{once:!0}),n!=null&&n.aborted&&a();try{const d=yield m("/cgi-bin/luci/istore/app/"+e+"/",{method:"POST",credentials:"same-origin",redirect:"error",cache:"no-store",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:c(r)}),signal:o.signal});if(!d.ok||!((l=d.headers.get("Content-Type"))!=null&&l.includes("application/json")))throw Error([401,403].includes(d.status)?t("登录已失效或权限不足,请重新登录","Session expired or access denied. Sign in again."):t("无法读取安装状态,请稍后重试","Unable to read installation status. Try again."));const k=yield d.text();if(k.length>8192)throw Error("Invalid installation response");const h=JSON.parse(k);if(h.error||h.success!==void 0&&h.success!==0)throw Error(t("安装状态读取失败,请检查软件中心","Unable to confirm installation. Check the software center."));return h}finally{clearTimeout(w),n==null||n.removeEventListener("abort",a)}})}function i(e,r){return p(this,null,function*(){var a,w,l;const n=yield u("check",e,r),o=(a=n.result)==null?void 0:a.installation;return["idle","installing","completed","failed","unknown"].includes(o)?o==="installing"?"installing":o==="unknown"?"unknown":o==="failed"?"failed":((w=n.result)==null?void 0:w.status)==="uninstalled"?"uninstalled":((l=n.result)==null?void 0:l.status)!=="installed"?"unknown":(yield b.read(!0)).some(d=>d.id===e)?"ready":"update_required":"unknown"})}return{inspect:i,install(e,r){return p(this,null,function*(){const n=yield i(e,r);if(n!=="ready"){if(n==="unknown")throw Error(t("无法确认安装状态,请先在软件中心检查或升级 Quickstart","Installation status is unknown. Check the software center or update Quickstart."));if(n!=="installing")try{yield u("install",e,r)}catch(o){if(r!=null&&r.aborted)throw o}for(let o=0;o<60;o++){yield s(r);const a=yield i(e,r);if(a==="ready")return;if(a==="update_required")throw Error(t("插件已安装,但当前版本尚未提供可用卡片;请在软件中心升级或修复 FastNet","The installed version has no usable card. Update or repair FastNet in the software center."));if(a==="failed")throw Error(t("安装失败,请检查软件源后重试","Installation failed. Check the package source before retrying."));if(a==="unknown")throw Error(t("安装结果尚未确认,请先在软件中心检查","Installation is unconfirmed. Check the software center first."))}throw Error(t("安装结果尚未确认,请重新读取状态;后台安装可能仍在继续","Installation is unconfirmed. Reload its status; installation may still be running."))}})}}}const S=v();export{S as cardInstaller,v as createCardInstaller};
@@ -1 +1 @@
@keyframes bganimation-0698ba82{0%{background-position:0% 50%}50%{background-position:100% 50%}to{background-position:0% 50%}}@keyframes rotateEnter-0698ba82{0%{transform:rotateY(180deg)}10%{transform:rotateY(198deg)}20%{transform:rotateY(216deg)}30%{transform:rotateY(234deg)}40%{transform:rotateY(252deg)}50%{transform:rotateY(270deg)}60%{transform:rotateY(288deg)}70%{transform:rotateY(306deg)}80%{transform:rotateY(324deg)}90%{transform:rotateY(342deg)}to{transform:rotateY(360deg)}}@keyframes turns-0698ba82{0%{-webkit-transform:rotate(0deg)}25%{-webkit-transform:rotate(90deg)}50%{-webkit-transform:rotate(180deg)}75%{-webkit-transform:rotate(270deg)}to{-webkit-transform:rotate(360deg)}}.rotate-enter-active[data-v-0698ba82]{animation:rotateEnter-0698ba82 .7s;position:relative}.rotate-leave-active[data-v-0698ba82]{opacity:0;display:none;position:relative;z-index:-999}.app-container[data-v-0698ba82]{width:100%;background-color:var(--card-bg-color);box-shadow:var(--card-box-shadow);padding:10px 30px;border-radius:6px;position:relative}.disk-item-tooltip[data-v-0698ba82]{position:fixed;background:rgba(0,0,0,.7);z-index:10111;color:#fff;padding:.5rem 1rem;font-size:1em;min-width:200px;line-height:24px}.disk-item-tooltip[data-v-0698ba82]:after{content:"";position:absolute;bottom:-6px;border-color:#4c4c4c rgba(0,0,0,0) rgba(0,0,0,0);left:0;right:0;text-align:center;width:0;margin:0 auto;border-width:6px 8px 0;border-style:solid}#main .app-btn,#actioner .app-btn{min-height:36px}@keyframes bganimation-9bc295c2{0%{background-position:0% 50%}50%{background-position:100% 50%}to{background-position:0% 50%}}@keyframes rotateEnter-9bc295c2{0%{transform:rotateY(180deg)}10%{transform:rotateY(198deg)}20%{transform:rotateY(216deg)}30%{transform:rotateY(234deg)}40%{transform:rotateY(252deg)}50%{transform:rotateY(270deg)}60%{transform:rotateY(288deg)}70%{transform:rotateY(306deg)}80%{transform:rotateY(324deg)}90%{transform:rotateY(342deg)}to{transform:rotateY(360deg)}}@keyframes turns-9bc295c2{0%{-webkit-transform:rotate(0deg)}25%{-webkit-transform:rotate(90deg)}50%{-webkit-transform:rotate(180deg)}75%{-webkit-transform:rotate(270deg)}to{-webkit-transform:rotate(360deg)}}.rotate-enter-active[data-v-9bc295c2]{animation:rotateEnter-9bc295c2 .7s;position:relative}.rotate-leave-active[data-v-9bc295c2]{opacity:0;display:none;position:relative;z-index:-999}.app-container[data-v-9bc295c2]{width:100%;background-color:var(--card-bg-color);box-shadow:var(--card-box-shadow);padding:10px 30px;border-radius:6px;position:relative}#main[data-v-9bc295c2]{width:100%}#main[data-v-9bc295c2],#main[data-v-9bc295c2] *{-webkit-box-sizing:border-box;-webkit-tap-highlight-color:transparent;box-sizing:border-box;word-wrap:break-word;outline:none}@keyframes bganimation{0%{background-position:0% 50%}50%{background-position:100% 50%}to{background-position:0% 50%}}@keyframes rotateEnter{0%{transform:rotateY(180deg)}10%{transform:rotateY(198deg)}20%{transform:rotateY(216deg)}30%{transform:rotateY(234deg)}40%{transform:rotateY(252deg)}50%{transform:rotateY(270deg)}60%{transform:rotateY(288deg)}70%{transform:rotateY(306deg)}80%{transform:rotateY(324deg)}90%{transform:rotateY(342deg)}to{transform:rotateY(360deg)}}@keyframes turns{0%{-webkit-transform:rotate(0deg)}25%{-webkit-transform:rotate(90deg)}50%{-webkit-transform:rotate(180deg)}75%{-webkit-transform:rotate(270deg)}to{-webkit-transform:rotate(360deg)}}.rotate-enter-active{animation:rotateEnter .7s;position:relative}.rotate-leave-active{opacity:0;display:none;position:relative;z-index:-999}.app-container{width:100%;background-color:var(--card-bg-color);box-shadow:var(--card-box-shadow);padding:10px 30px;border-radius:6px;position:relative}:root{--flow-bg-color: #fff;--flow-span-color: rgba(0, 0, 0, .6);--card-bg-color: #fff;--card-box-shadow: 0 0 10px 1px #bfbfbf24;--app-container_title-color: #1e1e1e;--app-container_status-label_block: black;--item-label_key-span-color: #333;--item-label_value-span-color: #333;--app-container_status-label_bg: #f3f3f3;--item_btn-border: 1px solid #553afb;--item_btn-color: #553afb;--tit-color: #1e1e1e;--popup-bg-color: #fff;--tag-bg-color: #f5f5f5;--gradient-bg-color:linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,.8) 62%, rgba(255,255,255,1) 100%);--breadcrumbs-tit-color:#d6dbf8;--breadcrumbs-tit-color1:#f1f2f9;--border-color: #e5e5e5;--SystemUpdateBanner-bg-color: #eff6ff;--SystemUpdateBanner-border-color: #bedbff;--btn-border-color: #e5e5e5;--card-txt-color: #32325d;--SystemInfo-bg-color:#f9fafd}@media (prefers-color-scheme: light){:root{--flow-bg-color: #fff;--flow-span-color: rgba(0, 0, 0, .6);--card-bg-color: #fff;--card-box-shadow: 0 0 10px 1px #bfbfbf24;--app-container_title-color: #1e1e1e;--app-container_status-label_block: black;--item-label_key-span-color: #333;--item-label_value-span-color: #333;--app-container_status-label_bg: #f3f3f3;--item_btn-border: 1px solid #553afb;--item_btn-color: #553afb;--tit-color: #1e1e1e;--popup-bg-color: #fff;--tag-Line truncated
@keyframes bganimation-4cfdbcec{0%{background-position:0% 50%}50%{background-position:100% 50%}to{background-position:0% 50%}}@keyframes rotateEnter-4cfdbcec{0%{transform:rotateY(180deg)}10%{transform:rotateY(198deg)}20%{transform:rotateY(216deg)}30%{transform:rotateY(234deg)}40%{transform:rotateY(252deg)}50%{transform:rotateY(270deg)}60%{transform:rotateY(288deg)}70%{transform:rotateY(306deg)}80%{transform:rotateY(324deg)}90%{transform:rotateY(342deg)}to{transform:rotateY(360deg)}}@keyframes turns-4cfdbcec{0%{-webkit-transform:rotate(0deg)}25%{-webkit-transform:rotate(90deg)}50%{-webkit-transform:rotate(180deg)}75%{-webkit-transform:rotate(270deg)}to{-webkit-transform:rotate(360deg)}}.rotate-enter-active[data-v-4cfdbcec]{animation:rotateEnter-4cfdbcec .7s;position:relative}.rotate-leave-active[data-v-4cfdbcec]{opacity:0;display:none;position:relative;z-index:-999}.app-container[data-v-4cfdbcec]{width:100%;background-color:var(--card-bg-color);box-shadow:var(--card-box-shadow);padding:10px 30px;border-radius:6px;position:relative}.disk-item-tooltip[data-v-4cfdbcec]{position:fixed;background:#000000b3;z-index:10111;color:#fff;padding:.5rem 1rem;font-size:1em;min-width:200px;line-height:24px}.disk-item-tooltip[data-v-4cfdbcec]:after{content:"";position:absolute;bottom:-6px;border-color:#4c4c4c rgba(0,0,0,0) rgba(0,0,0,0);left:0;right:0;text-align:center;width:0;margin:0 auto;border-width:6px 8px 0;border-style:solid}#main .app-btn,#actioner .app-btn{min-height:36px}@keyframes bganimation-425e333d{0%{background-position:0% 50%}50%{background-position:100% 50%}to{background-position:0% 50%}}@keyframes rotateEnter-425e333d{0%{transform:rotateY(180deg)}10%{transform:rotateY(198deg)}20%{transform:rotateY(216deg)}30%{transform:rotateY(234deg)}40%{transform:rotateY(252deg)}50%{transform:rotateY(270deg)}60%{transform:rotateY(288deg)}70%{transform:rotateY(306deg)}80%{transform:rotateY(324deg)}90%{transform:rotateY(342deg)}to{transform:rotateY(360deg)}}@keyframes turns-425e333d{0%{-webkit-transform:rotate(0deg)}25%{-webkit-transform:rotate(90deg)}50%{-webkit-transform:rotate(180deg)}75%{-webkit-transform:rotate(270deg)}to{-webkit-transform:rotate(360deg)}}.rotate-enter-active[data-v-425e333d]{animation:rotateEnter-425e333d .7s;position:relative}.rotate-leave-active[data-v-425e333d]{opacity:0;display:none;position:relative;z-index:-999}.app-container[data-v-425e333d]{width:100%;background-color:var(--card-bg-color);box-shadow:var(--card-box-shadow);padding:10px 30px;border-radius:6px;position:relative}#main[data-v-425e333d]{width:100%}#main[data-v-425e333d],#main[data-v-425e333d] *{-webkit-box-sizing:border-box;-webkit-tap-highlight-color:transparent;box-sizing:border-box;word-wrap:break-word;outline:none}:root{--flow-bg-color: #fff;--flow-span-color: rgba(0, 0, 0, .6);--card-bg-color: #fff;--card-box-shadow: 0 0 10px 1px #bfbfbf24;--app-container_title-color: #1e1e1e;--app-container_status-label_block: black;--item-label_key-span-color: #333;--item-label_value-span-color: #333;--app-container_status-label_bg: #f3f3f3;--item_btn-border: 1px solid #553afb;--item_btn-color: #553afb;--tit-color: #1e1e1e;--popup-bg-color: #fff;--tag-bg-color: #f5f5f5;--gradient-bg-color:linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,.8) 62%, rgba(255,255,255,1) 100%);--breadcrumbs-tit-color:#d6dbf8;--breadcrumbs-tit-color1:#f1f2f9;--border-color: #e5e5e5;--SystemUpdateBanner-bg-color: #eff6ff;--SystemUpdateBanner-border-color: #bedbff;--btn-border-color: #e5e5e5;--card-txt-color: #32325d;--SystemInfo-bg-color:#f9fafd;color-scheme:light}@media(prefers-color-scheme:light){:root{--flow-bg-color: #fff;--flow-span-color: rgba(0, 0, 0, .6);--card-bg-color: #fff;--card-box-shadow: 0 0 10px 1px #bfbfbf24;--app-container_title-color: #1e1e1e;--app-container_status-label_block: black;--item-label_key-span-color: #333;--item-label_value-span-color: #333;--app-container_status-label_bg: #f3f3f3;--item_btn-border: 1px solid #553afb;--item_btn-color: #553afb;--tit-color: #1e1e1e;--popup-bg-color: #fff;--tag-bg-color: #f5f5f5;--gradient-bg-color:linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,.8) 62%, rgba(255,255,255,1) 100%);--breadcrumbs-tit-color:#d6dbf8;--breadcrumbs-tit-color1:#f1f2f9;--border-color: #e5e5e5;--SystemUpdateBanner-bg-color: #eff6ff;--SystemUpdateBanner-border-color: #bedbff;--btn-border-color: #e5e5e5;--card-txt-color: #32325d;--SystemInfo-bg-color:#f9fafd}}@media(prefers-color-scheme:dark){:root{--flow-bg-color: transparent;--flow-span-color: #cccccc;--card-bg-color: #88888822;--card-box-shadow: 0 0 .5rem 0 rgba(0, 0, 0, .35);--app-container_title-color: #cccccc;--app-container_status-label_block: #727272;--item-label_key-span-color: #cccccc;--item-label_value-span-color: #cccccc;--app-container_status-label_bg: #0000001a;--item_btn-border: 1px solid #cccccc;--item_btn-color: #cccccc;--tit-color: #cccccc;--popup-bg-color: #000;--tag-bg-color: #1e1e1e;--gradient-bg-color:linear-gradient(90deg, rgba(0,Line truncated
@@ -1,48 +1,30 @@
function wv(e,t){const r=Object.create(null),n=e.split(",");for(let i=0;i<n.length;i++)r[n[i]]=!0;return t?i=>!!r[i.toLowerCase()]:i=>!!r[i]}const zx="itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly",Hx=wv(zx);function Q0(e){return!!e||e===""}function bv(e){if(at(e)){const t={};for(let r=0;r<e.length;r++){const n=e[r],i=Zt(n)?Gx(n):bv(n);if(i)for(const a in i)t[a]=i[a]}return t}else{if(Zt(e))return e;if(Yt(e))return e}}const $x=/;(?![^(]*\))/g,Vx=/:(.+)/;function Gx(e){const t={};return e.split($x).forEach(r=>{if(r){const n=r.split(Vx);n.length>1&&(t[n[0].trim()]=n[1].trim())}}),t}function Sv(e){let t="";if(Zt(e))t=e;else if(at(e))for(let r=0;r<e.length;r++){const n=Sv(e[r]);n&&(t+=n+" ")}else if(Yt(e))for(const r in e)e[r]&&(t+=r+" ");return t.trim()}function Ux(e,t){if(e.length!==t.length)return!1;let r=!0;for(let n=0;r&&n<e.length;n++)r=wi(e[n],t[n]);return r}function wi(e,t){if(e===t)return!0;let r=cp(e),n=cp(t);if(r||n)return r&&n?e.getTime()===t.getTime():!1;if(r=at(e),n=at(t),r||n)return r&&n?Ux(e,t):!1;if(r=Yt(e),n=Yt(t),r||n){if(!r||!n)return!1;const i=Object.keys(e).length,a=Object.keys(t).length;if(i!==a)return!1;for(const o in e){const s=e.hasOwnProperty(o),l=t.hasOwnProperty(o);if(s&&!l||!s&&l||!wi(e[o],t[o]))return!1}}return String(e)===String(t)}function xv(e,t){return e.findIndex(r=>wi(r,t))}const Iz=e=>Zt(e)?e:e==null?"":at(e)||Yt(e)&&(e.toString===t_||!gt(e.toString))?JSON.stringify(e,J0,2):String(e),J0=(e,t)=>t&&t.__v_isRef?J0(e,t.value):fa(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((r,[n,i])=>(r[`${n} =>`]=i,r),{})}:Ea(t)?{[`Set(${t.size})`]:[...t.values()]}:Yt(t)&&!at(t)&&!e_(t)?String(t):t,Rt={},ua=[],fr=()=>{},Wx=()=>!1,Yx=/^on[^a-z]/,_u=e=>Yx.test(e),Tv=e=>e.startsWith("onUpdate:"),ie=Object.assign,Cv=(e,t)=>{const r=e.indexOf(t);r>-1&&e.splice(r,1)},qx=Object.prototype.hasOwnProperty,Tt=(e,t)=>qx.call(e,t),at=Array.isArray,fa=e=>wu(e)==="[object Map]",Ea=e=>wu(e)==="[object Set]",cp=e=>e instanceof Date,gt=e=>typeof e=="function",Zt=e=>typeof e=="string",Mv=e=>typeof e=="symbol",Yt=e=>e!==null&&typeof e=="object",Av=e=>Yt(e)&&gt(e.then)&&gt(e.catch),t_=Object.prototype.toString,wu=e=>t_.call(e),Xx=e=>wu(e).slice(8,-1),e_=e=>wu(e)==="[object Object]",Dv=e=>Zt(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,ml=wv(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),bu=e=>{const t=Object.create(null);return r=>t[r]||(t[r]=e(r))},jx=/-(\w)/g,Ar=bu(e=>e.replace(jx,(t,r)=>r?r.toUpperCase():"")),Kx=/\B([A-Z])/g,Mi=bu(e=>e.replace(Kx,"-$1").toLowerCase()),Su=bu(e=>e.charAt(0).toUpperCase()+e.slice(1)),hf=bu(e=>e?`on${Su(e)}`:""),$o=(e,t)=>!Object.is(e,t),yl=(e,t)=>{for(let r=0;r<e.length;r++)e[r](t)},Bl=(e,t,r)=>{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:r})},ya=e=>{const t=parseFloat(e);return isNaN(t)?e:t};let hp;const Zx=()=>hp||(hp=typeof globalThis!="undefined"?globalThis:typeof self!="undefined"?self:typeof window!="undefined"?window:typeof global!="undefined"?global:{});let Or;class r_{constructor(t=!1){this.active=!0,this.effects=[],this.cleanups=[],!t&&Or&&(this.parent=Or,this.index=(Or.scopes||(Or.scopes=[])).push(this)-1)}run(t){if(this.active)try{return Or=this,t()}finally{Or=this.parent}}on(){Or=this}off(){Or=this.parent}stop(t){if(this.active){let r,n;for(r=0,n=this.effects.length;r<n;r++)this.effects[r].stop();for(r=0,n=this.cleanups.length;r<n;r++)this.cleanups[r]();if(this.scopes)for(r=0,n=this.scopes.length;r<n;r++)this.scopes[r].stop(!0);if(this.parent&&!t){const i=this.parent.scopes.pop();i&&i!==this&&(this.parent.scopes[this.index]=i,i.index=this.index)}this.active=!1}}}function n_(e){return new r_(e)}function Qx(e,t=Or){t&&t.active&&t.effects.push(e)}const Pv=e=>{const t=new Set(e);return t.w=0,t.n=0,t},i_=e=>(e.w&Pn)>0,a_=e=>(e.n&Pn)>0,Jx=({deps:e})=>{if(e.length)for(let t=0;t<e.length;t++)e[t].w|=Pn},tT=e=>{const{deps:t}=e;if(t.length){let r=0;for(let n=0;n<t.length;n++){const i=t[n];i_(i)&&!a_(i)?i.delete(e):t[r++]=i,i.w&=~Pn,i.n&=~Pn}t.length=r}},$c=new WeakMap;let fo=0,Pn=1;const Vc=30;let xr;const pi=Symbol(""),Gc=Symbol("");class Iv{constructor(t,r=null,n){this.fn=t,this.scheduler=r,this.active=!0,this.deps=[],this.parent=void 0,Qx(this,n)}run(){if(!this.active)return this.fn();let t=xr,r=wn;for(;t;){if(t===this)return;t=t.parent}try{return this.parent=xr,xr=this,wn=!0,Pn=1<<++fo,fo<=Vc?Jx(this):vp(this),this.fn()}finally{fo<=Vc&&tT(this),Pn=1<<--fo,xr=this.parent,wn=r,this.parent=void 0}}stop(){this.active&&(vp(this),this.onStop&&this.onStop(),this.active=!1)}}function vp(e){const{deps:t}=e;if(t.length){for(let r=0;r<t.length;r++)t[r].delete(e);t.length=0}}let wn=!0;const o_=[];function La(){o_.push(wn),wn=!1}function Ra(){const e=o_.pop();wn=e===void 0?!0:e}function ke(e,t,r){if(wn&&xr){let n=$c.get(e);n||$c.set(e,n=new Map);let i=n.get(r);i||n.set(r,i=Pv()),s_(i)}}function s_(e,t){let r=!1;fo<=Vc?a_(e)||(e.n|=Pn,r=!i_(e)):r=Line truncated
`),function(s){if(a=s.indexOf(":"),n=mf.trim(s.substr(0,a)).toLowerCase(),i=mf.trim(s.substr(a+1)),n){if(r[n]&&jM.indexOf(n)>=0)return;n==="set-cookie"?r[n]=(r[n]?r[n]:[]).concat([i]):r[n]=r[n]?r[n]+", "+i:i}}),r},Kp=Fe,ZM=Kp.isStandardBrowserEnv()?function(){var t=/(msie|trident)/i.test(navigator.userAgent),r=document.createElement("a"),n;function i(a){var o=a;return t&&(r.setAttribute("href",o),o=r.href),r.setAttribute("href",o),{href:r.href,protocol:r.protocol?r.protocol.replace(/:$/,""):"",host:r.host,search:r.search?r.search.replace(/^\?/,""):"",hash:r.hash?r.hash.replace(/^#/,""):"",hostname:r.hostname,port:r.port,pathname:r.pathname.charAt(0)==="/"?r.pathname:"/"+r.pathname}}return n=i(window.location.href),function(o){var s=Kp.isString(o)?i(o):o;return s.protocol===n.protocol&&s.host===n.host}}():function(){return function(){return!0}}(),Is=Fe,QM=VM,JM=GM,tA=l1,eA=XM,rA=KM,nA=ZM,yf=f1,Zp=function(t){return new Promise(function(n,i){var a=t.data,o=t.headers,s=t.responseType;Is.isFormData(a)&&delete o["Content-Type"];var l=new XMLHttpRequest;if(t.auth){var u=t.auth.username||"",f=t.auth.password?unescape(encodeURIComponent(t.auth.password)):"";o.Authorization="Basic "+btoa(u+":"+f)}var c=eA(t.baseURL,t.url);l.open(t.method.toUpperCase(),tA(c,t.params,t.paramsSerializer),!0),l.timeout=t.timeout;function v(){if(!!l){var d="getAllResponseHeaders"in l?rA(l.getAllResponseHeaders()):null,p=!s||s==="text"||s==="json"?l.responseText:l.response,g={data:p,status:l.status,statusText:l.statusText,headers:d,config:t,request:l};QM(n,i,g),l=null}}if("onloadend"in l?l.onloadend=v:l.onreadystatechange=function(){!l||l.readyState!==4||l.status===0&&!(l.responseURL&&l.responseURL.indexOf("file:")===0)||setTimeout(v)},l.onabort=function(){!l||(i(yf("Request aborted",t,"ECONNABORTED",l)),l=null)},l.onerror=function(){i(yf("Network Error",t,null,l)),l=null},l.ontimeout=function(){var p="timeout of "+t.timeout+"ms exceeded";t.timeoutErrorMessage&&(p=t.timeoutErrorMessage),i(yf(p,t,t.transitional&&t.transitional.clarifyTimeoutError?"ETIMEDOUT":"ECONNABORTED",l)),l=null},Is.isStandardBrowserEnv()){var h=(t.withCredentials||nA(c))&&t.xsrfCookieName?JM.read(t.xsrfCookieName):void 0;h&&(o[t.xsrfHeaderName]=h)}"setRequestHeader"in l&&Is.forEach(o,function(p,g){typeof a=="undefined"&&g.toLowerCase()==="content-type"?delete o[g]:l.setRequestHeader(g,p)}),Is.isUndefined(t.withCredentials)||(l.withCredentials=!!t.withCredentials),s&&s!=="json"&&(l.responseType=t.responseType),typeof t.onDownloadProgress=="function"&&l.addEventListener("progress",t.onDownloadProgress),typeof t.onUploadProgress=="function"&&l.upload&&l.upload.addEventListener("progress",t.onUploadProgress),t.cancelToken&&t.cancelToken.promise.then(function(p){!l||(l.abort(),i(p),l=null)}),a||(a=null),l.send(a)})},ee=Fe,Qp=zM,iA=u1,aA={"Content-Type":"application/x-www-form-urlencoded"};function Jp(e,t){!ee.isUndefined(e)&&ee.isUndefined(e["Content-Type"])&&(e["Content-Type"]=t)}function oA(){var e;return(typeof XMLHttpRequest!="undefined"||typeof process!="undefined"&&Object.prototype.toString.call(process)==="[object process]")&&(e=Zp),e}function sA(e,t,r){if(ee.isString(e))try{return(t||JSON.parse)(e),ee.trim(e)}catch(n){if(n.name!=="SyntaxError")throw n}return(r||JSON.stringify)(e)}var Eu={transitional:{silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},adapter:oA(),transformRequest:[function(t,r){return Qp(r,"Accept"),Qp(r,"Content-Type"),ee.isFormData(t)||ee.isArrayBuffer(t)||ee.isBuffer(t)||ee.isStream(t)||ee.isFile(t)||ee.isBlob(t)?t:ee.isArrayBufferView(t)?t.buffer:ee.isURLSearchParams(t)?(Jp(r,"application/x-www-form-urlencoded;charset=utf-8"),t.toString()):ee.isObject(t)||r&&r["Content-Type"]==="application/json"?(Jp(r,"application/json"),sA(t)):t}],transformResponse:[function(t){var r=this.transitional,n=r&&r.silentJSONParsing,i=r&&r.forcedJSONParsing,a=!n&&this.responseType==="json";if(a||i&&ee.isString(t)&&t.length)try{return JSON.parse(t)}catch(o){if(a)throw o.name==="SyntaxError"?iA(o,this,"E_JSON_PARSE"):o}return t}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,validateStatus:function(t){return t>=200&&t<300}};Eu.headers={common:{Accept:"application/json, text/plain, */*"}};ee.forEach(["delete","get","head"],function(t){Eu.headers[t]={}});ee.forEach(["post","put","patch"],function(t){Eu.headers[t]=ee.merge(aA)});var Jv=Eu,lA=Fe,uA=Jv,fA=function(t,r,n){var i=this||uA;return lA.forEach(n,function(o){t=o.call(i,t,r)}),t},c1=function(t){return!!(t&&t.__CANCEL__)},tg=Fe,_f=fA,cA=c1,hA=Jv;function wf(e){e.cancelToken&&e.cancelToken.throwIfRequested()}var vA=function(t){wf(t),t.headers=t.headers||{},t.data=_f.call(t,t.data,t.headers,t.transformRequest),t.headers=tg.merge(t.headers.common||{},t.headers[t.method]||{},t.headers),tg.forEach(["delete","get","head","post","put","patch","common"],function(i){delete t.headers[i]});var r=t.adapter||hA.adapter;return r(t).then(function(i){retur Line truncated
* pinia v2.0.12
* (c) 2022 Eduardo San Martin Morote
* @license MIT
*/let w1;const Ru=e=>w1=e,b1=Symbol();function lh(e){return e&&typeof e=="object"&&Object.prototype.toString.call(e)==="[object Object]"&&typeof e.toJSON!="function"}var Co;(function(e){e.direct="direct",e.patchObject="patch object",e.patchFunction="patch function"})(Co||(Co={}));function Jz(){const e=n_(!0),t=e.run(()=>wa({}));let r=[],n=[];const i=_a({install(a){Ru(i),i._a=a,a.provide(b1,i),a.config.globalProperties.$pinia=i,n.forEach(o=>r.push(o)),n=[]},use(a){return!this._a&&!KA?n.push(a):r.push(a),this},_p:r,_a:null,_e:e,_s:new Map,state:t});return i}const S1=()=>{};function fg(e,t,r,n=S1){e.push(t);const i=()=>{const a=e.indexOf(t);a>-1&&(e.splice(a,1),n())};return!r&&Oa()&&Du(i),i}function Bi(e,...t){e.slice().forEach(r=>{r(...t)})}function uh(e,t){for(const r in t){const n=t[r],i=e[r];lh(i)&&lh(n)&&!Wt(n)&&!bn(n)?e[r]=uh(i,n):e[r]=n}return e}const ZA=Symbol();function QA(e){return!lh(e)||!e.hasOwnProperty(ZA)}const{assign:kr}=Object;function JA(e){return!!(Wt(e)&&e.effect)}function tD(e,t,r,n){const{state:i,actions:a,getters:o}=t,s=r.state.value[e];let l;function u(){s||(r.state.value[e]=i?i():{});const f=DT(r.state.value[e]);return kr(f,a,Object.keys(o||{}).reduce((c,v)=>(c[v]=_a(Ee(()=>{Ru(r);const h=r._s.get(e);return o[v].call(h,h)})),c),{}))}return l=x1(e,u,t,r),l.$reset=function(){const c=i?i():{};this.$patch(v=>{kr(v,c)})},l}function x1(e,t,r={},n,i){let a;const o=r.state,s=kr({actions:{}},r),l={deep:!0};let u,f,c=_a([]),v=_a([]),h;const d=n.state.value[e];!o&&!d&&(n.state.value[e]={}),wa({});function p(b){let x;u=f=!1,typeof b=="function"?(b(n.state.value[e]),x={type:Co.patchFunction,storeId:e,events:h}):(uh(n.state.value[e],b),x={type:Co.patchObject,payload:b,storeId:e,events:h}),Bv().then(()=>{u=!0}),f=!0,Bi(c,x,n.state.value[e])}const g=S1;function m(){a.stop(),c=[],v=[],n._s.delete(e)}function y(b,x){return function(){Ru(n);const T=Array.from(arguments),C=[],D=[];function P(O){C.push(O)}function A(O){D.push(O)}Bi(v,{args:T,name:b,store:w,after:P,onError:A});let L;try{L=x.apply(this&&this.$id===e?this:w,T)}catch(O){throw Bi(D,O),O}return L instanceof Promise?L.then(O=>(Bi(C,O),O)).catch(O=>(Bi(D,O),Promise.reject(O))):(Bi(C,L),L)}}const _={_p:n,$id:e,$onAction:fg.bind(null,v),$patch:p,$reset:g,$subscribe(b,x={}){const T=fg(c,b,x.detached,()=>C()),C=a.run(()=>ha(()=>n.state.value[e],D=>{(x.flush==="sync"?f:u)&&b({storeId:e,type:Co.direct,events:h},D)},kr({},l,x)));return T},$dispose:m},w=Ai(kr({},_));n._s.set(e,w);const S=n._e.run(()=>(a=n_(),a.run(()=>t())));for(const b in S){const x=S[b];if(Wt(x)&&!JA(x)||bn(x))o||(d&&QA(x)&&(Wt(x)?x.value=d[b]:uh(x,d[b])),n.state.value[e][b]=x);else if(typeof x=="function"){const T=y(b,x);S[b]=T,s.actions[b]=x}}return kr(w,S),kr(Ct(w),S),Object.defineProperty(w,"$state",{get:()=>n.state.value[e],set:b=>{p(x=>{kr(x,b)})}}),n._p.forEach(b=>{kr(w,a.run(()=>b({store:w,app:n._a,pinia:n,options:s})))}),d&&o&&r.hydrate&&r.hydrate(w.$state,d),u=!0,f=!0,w}function tH(e,t,r){let n,i;const a=typeof t=="function";typeof e=="string"?(n=e,i=a?r:t):(i=e,n=e.id);function o(s,l){const u=Oa();return s=s||u&&Je(b1),s&&Ru(s),s=w1,s._s.has(n)||(a?x1(n,t,i,s):tD(n,i,s)),s._s.get(n)}return o.$id=n,o}/*!
* vue-router v4.0.14
* (c) 2022 Eduardo San Martin Morote
* @license MIT
*/const T1=typeof Symbol=="function"&&typeof Symbol.toStringTag=="symbol",ka=e=>T1?Symbol(e):"_vr_"+e,eD=ka("rvlm"),cg=ka("rvd"),Ou=ka("r"),nd=ka("rl"),fh=ka("rvl"),ea=typeof window!="undefined";function rD(e){return e.__esModule||T1&&e[Symbol.toStringTag]==="Module"}const Lt=Object.assign;function bf(e,t){const r={};for(const n in t){const i=t[n];r[n]=Array.isArray(i)?i.map(e):e(i)}return r}const Mo=()=>{},nD=/\/$/,iD=e=>e.replace(nD,"");function Sf(e,t,r="/"){let n,i={},a="",o="";const s=t.indexOf("?"),l=t.indexOf("#",s>-1?s:0);return s>-1&&(n=t.slice(0,s),a=t.slice(s+1,l>-1?l:t.length),i=e(a)),l>-1&&(n=n||t.slice(0,l),o=t.slice(l,t.length)),n=lD(n!=null?n:t,r),{fullPath:n+(a&&"?")+a+o,path:n,query:i,hash:o}}function aD(e,t){const r=t.query?e(t.query):"";return t.path+(r&&"?")+r+(t.hash||"")}function hg(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function oD(e,t,r){const n=t.matched.length-1,i=r.matched.length-1;return n>-1&&n===i&&xa(t.matched[n],r.matched[i])&&C1(t.params,r.params)&&e(t.query)===e(r.query)&&t.hash===r.hash}function xa(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function C1(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const r in e)if(!sD(e[r],t[r]))return!1;return!0}function sD(e,t){return Array.isArray(e)?vg(e,t):Array.isArray(t)?vg(t,e):e===t}function vg(e,t){return Array.isArray(t)?e.length===t.length&&e.every((r,n)=>r===t[n]):e.length===1&&e[0]===t}function lD(e,t){if(e.startsWith("/"))return e;if(!e)return t;const r=t.split("/"),n=e.split("/");let i=r.length-1,a,o;for(a=0;a<n.length;a++)if(o=n[a],!(i===1||o==="."))if(o==="..")i--;else break;return r.slice(0,i).join("/")+"/"+n.slice(a-(a===n.length?1:0)).join("/")}var Ko;(function(e){e.pop="pop",e.push="push"})(Ko||(Ko={}));var Ao;(function(e){e.back="back",e.forward="forward",e.unknown=""})(Ao||(Ao={}));function uD(e){if(!e)if(ea){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),iD(e)}const fD=/^[^#]+#/;function cD(e,t){return e.replace(fD,"#")+t}function hD(e,t){const r=document.documentElement.getBoundingClientRect(),n=e.getBoundingClientRect();return{behavior:t.behavior,left:n.left-r.left-(t.left||0),top:n.top-r.top-(t.top||0)}}const ku=()=>({left:window.pageXOffset,top:window.pageYOffset});function vD(e){let t;if("el"in e){const r=e.el,n=typeof r=="string"&&r.startsWith("#"),i=typeof r=="string"?n?document.getElementById(r.slice(1)):document.querySelector(r):r;if(!i)return;t=hD(i,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.pageXOffset,t.top!=null?t.top:window.pageYOffset)}function dg(e,t){return(history.state?history.state.position-t:-1)+e}const ch=new Map;function dD(e,t){ch.set(e,t)}function pD(e){const t=ch.get(e);return ch.delete(e),t}let gD=()=>location.protocol+"//"+location.host;function M1(e,t){const{pathname:r,search:n,hash:i}=t,a=e.indexOf("#");if(a>-1){let s=i.includes(e.slice(a))?e.slice(a).length:1,l=i.slice(s);return l[0]!=="/"&&(l="/"+l),hg(l,"")}return hg(r,e)+n+i}function mD(e,t,r,n){let i=[],a=[],o=null;const s=({state:v})=>{const h=M1(e,location),d=r.value,p=t.value;let g=0;if(v){if(r.value=h,t.value=v,o&&o===d){o=null;return}g=p?v.position-p.position:0}else n(h);i.forEach(m=>{m(r.value,d,{delta:g,type:Ko.pop,direction:g?g>0?Ao.forward:Ao.back:Ao.unknown})})};function l(){o=r.value}function u(v){i.push(v);const h=()=>{const d=i.indexOf(v);d>-1&&i.splice(d,1)};return a.push(h),h}function f(){const{history:v}=window;!v.state||v.replaceState(Lt({},v.state,{scroll:ku()}),"")}function c(){for(const v of a)v();a=[],window.removeEventListener("popstate",s),window.removeEventListener("beforeunload",f)}return window.addEventListener("popstate",s),window.addEventListener("beforeunload",f),{pauseListeners:l,listen:u,destroy:c}}function pg(e,t,r,n=!1,i=!1){return{back:e,current:t,forward:r,replaced:n,position:window.history.length,scroll:i?ku():null}}function yD(e){const{history:t,location:r}=window,n={value:M1(e,r)},i={value:t.state};i.value||a(n.value,{back:null,current:n.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function a(l,u,f){const c=e.indexOf("#"),v=c>-1?(r.host&&document.querySelector("base")?e:e.slice(c))+l:gD()+e+l;try{t[f?"replaceState":"pushState"](u,"",v),i.value=u}catch(h){console.error(h),r[f?"replace":"assign"](v)}}function o(l,u){const f=Lt({},t.state,pg(i.value.back,l,i.value.forward,!0),u,{position:i.value.position});a(l,f,!0),n.value=l}function s(l,u){const f=Lt({},i.value,t.state,{forward:l,scroll:ku()});a(f.current,f,!0);const c=Lt({},pg(n.value,l,null),{position:f.position+1},u);a(l,c,!1),n.value=l}return{location:n,state:i,push:s,replace:o}}function eH(e){e=uD(e);const t=yD(e),r=mD(e,t.state,t.location,t.replace);function n(a,o=!0){o||r.pauseListeners(),history.go(a)}const i=Lt({location:"",base:e,go:n,createHref:cD.bind(nLine truncated
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */var vh=function(e,t){return vh=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,n){r.__proto__=n}||function(r,n){for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(r[i]=n[i])},vh(e,t)};function J(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");vh(e,t);function r(){this.constructor=e}e.prototype=t===null?Object.create(t):(r.prototype=t.prototype,new r)}var nP=function(){function e(){this.firefox=!1,this.ie=!1,this.edge=!1,this.newEdge=!1,this.weChat=!1}return e}(),iP=function(){function e(){this.browser=new nP,this.node=!1,this.wxa=!1,this.worker=!1,this.svgSupported=!1,this.touchEventsSupported=!1,this.pointerEventsSupported=!1,this.domSupported=!1,this.transformSupported=!1,this.transform3dSupported=!1,this.hasGlobalWindow=typeof window!="undefined"}return e}(),ui=new iP;typeof wx=="object"&&typeof wx.getSystemInfoSync=="function"?(ui.wxa=!0,ui.touchEventsSupported=!0):typeof document=="undefined"&&typeof self!="undefined"?ui.worker=!0:typeof navigator=="undefined"?(ui.node=!0,ui.svgSupported=!0):aP(navigator.userAgent,ui);function aP(e,t){var r=t.browser,n=e.match(/Firefox\/([\d.]+)/),i=e.match(/MSIE\s([\d.]+)/)||e.match(/Trident\/.+?rv:(([\d.]+))/),a=e.match(/Edge?\/([\d.]+)/),o=/micromessenger/i.test(e);n&&(r.firefox=!0,r.version=n[1]),i&&(r.ie=!0,r.version=i[1]),a&&(r.edge=!0,r.version=a[1],r.newEdge=+a[1].split(".")[0]>18),o&&(r.weChat=!0),t.svgSupported=typeof SVGRect!="undefined",t.touchEventsSupported="ontouchstart"in window&&!r.ie&&!r.edge,t.pointerEventsSupported="onpointerdown"in window&&(r.edge||r.ie&&+r.version>=11),t.domSupported=typeof document!="undefined";var s=document.documentElement.style;t.transform3dSupported=(r.ie&&"transition"in s||r.edge||"WebKitCSSMatrix"in window&&"m11"in new WebKitCSSMatrix||"MozPerspective"in s)&&!("OTransition"in s),t.transformSupported=t.transform3dSupported||r.ie&&+r.version>=9}var wt=ui,ad=12,oP="sans-serif",bi=ad+"px "+oP,sP=20,lP=100,uP="007LLmW'55;N0500LLLLLLLLLL00NNNLzWW\\\\WQb\\0FWLg\\bWb\\WQ\\WrWWQ000CL5LLFLL0LL**F*gLLLL5F0LF\\FFF5.5N";function fP(e){var t={};if(typeof JSON=="undefined")return t;for(var r=0;r<e.length;r++){var n=String.fromCharCode(r+32),i=(e.charCodeAt(r)-sP)/lP;t[n]=i}return t}var cP=fP(uP),Pi={createCanvas:function(){return typeof document!="undefined"&&document.createElement("canvas")},measureText:function(){var e,t;return function(r,n){if(!e){var i=Pi.createCanvas();e=i&&i.getContext("2d")}if(e)return t!==n&&(t=e.font=n||bi),e.measureText(r);r=r||"",n=n||bi;var a=/^([0-9]*?)px$/.exec(n),o=+(a&&a[1])||ad,s=0;if(n.indexOf("mono")>=0)s=o*r.length;else for(var l=0;l<r.length;l++){var u=cP[r[l]];s+=u==null?o:u*o}return{width:s}}}(),loadImage:function(e,t,r){var n=new Image;return n.onload=t,n.onerror=r,n.src=e,n}},k1=Ln(["Function","RegExp","Date","Error","CanvasGradient","CanvasPattern","Image","Canvas"],function(e,t){return e["[object "+t+"]"]=!0,e},{}),N1=Ln(["Int8","Uint8","Uint8Clamped","Int16","Uint16","Int32","Uint32","Float32","Float64"],function(e,t){return e["[object "+t+"Array]"]=!0,e},{}),ps=Object.prototype.toString,Nu=Array.prototype,hP=Nu.forEach,vP=Nu.filter,od=Nu.slice,dP=Nu.map,Cg=function(){}.constructor,Es=Cg?Cg.prototype:null,sd="__proto__",pP=2311;function B1(){return pP++}function ld(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];typeof console!="undefined"&&console.error.apply(console,e)}function xt(e){if(e==null||typeof e!="object")return e;var t=e,r=ps.call(e);if(r==="[object Array]"){if(!Po(e)){t=[];for(var n=0,i=e.length;n<i;n++)t[n]=xt(e[n])}}else if(N1[r]){if(!Po(e)){var a=e.constructor;if(a.from)t=a.from(e);else{t=new a(e.length);for(var n=0,i=e.length;n<i;n++)t[n]=e[n]}}}else if(!k1[r]&&!Po(e)&&!Yl(e)){t={};for(var o in e)e.hasOwnProperty(o)&&o!==sd&&(t[o]=xt(e[o]))}return t}function Dt(e,t,r){if(!st(t)||!st(e))return r?xt(t):e;for(var n in t)if(t.hasOwnProperty(n)&&n!==sd){var i=e[n],a=t[n];st(a)&&st(i)&&!et(a)&&!et(i)&&!Yl(a)&&!Yl(i)&&!Mg(a)&&!Mg(i)&&!Po(a)&&!Po(i)?Dt(i,a,r):(r||!(n in e))&&(e[n]=xt(t[n]))}return e}function Y(e,t){if(Object.assign)Object.assign(e,t);else for(var r in t)t.hasOwnProperty(r)&&r!==sd&&(e[r]=t[r]);return e}function Pt(e,t,r){for(var n=Gt(t),i=0;i<n.length;i++){var a=n[i];(r?t[a]!=null:e[a]==null)&&(e[a]=t[a])}return e}Pi.createCanvas;function It(e,t){if(e){if(e.indexOf)return e.indexOf(t);for(var r=0,n=e.length;r<n;r++)if(e[r]===t)return r}return-1}function gP(e,t){var r=e.prototype;function n(){}n.prototype=t.prototype,e.prototype=new n;for(var i in r)r.hasOwnProperty(i)&&(e.prototype[i]=r[i]);e.prototype.constructor=e,e.superClass=t}function Pr(e,t,r){if(e="prototype"in e?e.prototype:e,t="prototype"in t?t.prototype:t,Object.getOwnPropertyNames)for(var n=Object.getOwnPropertyNames(t),i=0;i<n.length;i++){var a=n[i];a!=="constructor"&&(r?t[a]!=null:e[a]==null)&&(e[a]=t[a])}else Pt(Line truncated
`),a=i.length;if(a===1)return Zg(i[0],t,r,n);for(var o=new Ot(0,0,0,0),s=0;s<i.length;s++){var l=Zg(i[s],t,r,n);s===0?o.copy(l):o.union(l)}return o}function po(e,t,r){return r==="right"?e-=t:r==="center"&&(e-=t/2),e}function na(e,t,r){return r==="middle"?e-=t/2:r==="bottom"&&(e-=t),e}function md(e){return Oe("\u56FD",e)}function Rn(e,t){return typeof e=="string"?e.lastIndexOf("%")>=0?parseFloat(e)/100*t:parseFloat(e):e}function aw(e,t,r){var n=t.position||"inside",i=t.distance!=null?t.distance:5,a=r.height,o=r.width,s=a/2,l=r.x,u=r.y,f="left",c="top";if(n instanceof Array)l+=Rn(n[0],r.width),u+=Rn(n[1],r.height),f=null,c=null;else switch(n){case"left":l-=i,u+=s,f="right",c="middle";break;case"right":l+=i+o,u+=s,c="middle";break;case"top":l+=o/2,u-=i,f="center",c="bottom";break;case"bottom":l+=o/2,u+=a+i,f="center";break;case"inside":l+=o/2,u+=s,f="center",c="middle";break;case"insideLeft":l+=i,u+=s,c="middle";break;case"insideRight":l+=o-i,u+=s,f="right",c="middle";break;case"insideTop":l+=o/2,u+=i,f="center";break;case"insideBottom":l+=o/2,u+=a-i,f="center",c="bottom";break;case"insideTopLeft":l+=i,u+=i;break;case"insideTopRight":l+=o-i,u+=i,f="right";break;case"insideBottomLeft":l+=i,u+=a-i,c="bottom";break;case"insideBottomRight":l+=o-i,u+=a-i,f="right",c="bottom";break}return e=e||{},e.x=l,e.y=u,e.align=f,e.verticalAlign=c,e}var $f="__zr_normal__",Vf=Jo.concat(["ignore"]),OI=Ln(Jo,function(e,t){return e[t]=!0,e},{ignore:!1}),Vi={},kI=new Ot(0,0,0,0),yd=function(){function e(t){this.id=B1(),this.animators=[],this.currentStates=[],this.states={},this._init(t)}return e.prototype._init=function(t){this.attr(t)},e.prototype.drift=function(t,r,n){switch(this.draggable){case"horizontal":r=0;break;case"vertical":t=0;break}var i=this.transform;i||(i=this.transform=[1,0,0,1,0,0]),i[4]+=t,i[5]+=r,this.decomposeTransform(),this.markRedraw()},e.prototype.beforeUpdate=function(){},e.prototype.afterUpdate=function(){},e.prototype.update=function(){this.updateTransform(),this.__dirty&&this.updateInnerText()},e.prototype.updateInnerText=function(t){var r=this._textContent;if(r&&(!r.ignore||t)){this.textConfig||(this.textConfig={});var n=this.textConfig,i=n.local,a=r.innerTransformable,o=void 0,s=void 0,l=!1;a.parent=i?this:null;var u=!1;if(a.copyTransform(r),n.position!=null){var f=kI;n.layoutRect?f.copy(n.layoutRect):f.copy(this.getBoundingRect()),i||f.applyTransform(this.transform),this.calculateTextPosition?this.calculateTextPosition(Vi,n,f):aw(Vi,n,f),a.x=Vi.x,a.y=Vi.y,o=Vi.align,s=Vi.verticalAlign;var c=n.origin;if(c&&n.rotation!=null){var v=void 0,h=void 0;c==="center"?(v=f.width*.5,h=f.height*.5):(v=Rn(c[0],f.width),h=Rn(c[1],f.height)),u=!0,a.originX=-a.x+v+(i?0:f.x),a.originY=-a.y+h+(i?0:f.y)}}n.rotation!=null&&(a.rotation=n.rotation);var d=n.offset;d&&(a.x+=d[0],a.y+=d[1],u||(a.originX=-d[0],a.originY=-d[1]));var p=n.inside==null?typeof n.position=="string"&&n.position.indexOf("inside")>=0:n.inside,g=this._innerTextDefaultStyle||(this._innerTextDefaultStyle={}),m=void 0,y=void 0,_=void 0;p&&this.canBeInsideText()?(m=n.insideFill,y=n.insideStroke,(m==null||m==="auto")&&(m=this.getInsideTextFill()),(y==null||y==="auto")&&(y=this.getInsideTextStroke(m),_=!0)):(m=n.outsideFill,y=n.outsideStroke,(m==null||m==="auto")&&(m=this.getOutsideFill()),(y==null||y==="auto")&&(y=this.getOutsideStroke(m),_=!0)),m=m||"#000",(m!==g.fill||y!==g.stroke||_!==g.autoStroke||o!==g.align||s!==g.verticalAlign)&&(l=!0,g.fill=m,g.stroke=y,g.autoStroke=_,g.align=o,g.verticalAlign=s,r.setDefaultTextStyle(g)),r.__dirty|=Le,l&&r.dirtyStyle(!0)}},e.prototype.canBeInsideText=function(){return!0},e.prototype.getInsideTextFill=function(){return"#fff"},e.prototype.getInsideTextStroke=function(t){return"#000"},e.prototype.getOutsideFill=function(){return this.__zr&&this.__zr.isDarkMode()?Eh:Ih},e.prototype.getOutsideStroke=function(t){var r=this.__zr&&this.__zr.getBackgroundColor(),n=typeof r=="string"&&$r(r);n||(n=[255,255,255,1]);for(var i=n[3],a=this.__zr.isDarkMode(),o=0;o<3;o++)n[o]=n[o]*i+(a?0:255)*(1-i);return n[3]=1,zu(n,"rgba")},e.prototype.traverse=function(t,r){},e.prototype.attrKV=function(t,r){t==="textConfig"?this.setTextConfig(r):t==="textContent"?this.setTextContent(r):t==="clipPath"?this.setClipPath(r):t==="extra"?(this.extra=this.extra||{},Y(this.extra,r)):this[t]=r},e.prototype.hide=function(){this.ignore=!0,this.markRedraw()},e.prototype.show=function(){this.ignore=!1,this.markRedraw()},e.prototype.attr=function(t,r){if(typeof t=="string")this.attrKV(t,r);else if(st(t))for(var n=t,i=Gt(n),a=0;a<i.length;a++){var o=i[a];this.attrKV(o,t[o])}return this.markRedraw(),this},e.prototype.saveCurrentToNormalState=function(t){this._innerSaveToNormal(t);for(var r=this._normalState,n=0;n<this.animators.length;n++){var i=this.animators[n],a=i.__fromStateTransition;if(!(i.getLoop()||a&&a!==$f)){var o=i.targetName,s=o?r[o]:r;i.saveTo(s)}}},e.prototype._innerSaveToNormal=function(t){var r=this._normalState;r||(r=this._normalState={}),t.textC Line truncated
* ZRender, a high performance 2d drawing library.
*
* Copyright (c) 2013, Baidu Inc.
* All rights reserved.
*
* LICENSE
* https://github.com/ecomfe/zrender/blob/master/LICENSE.txt
*/var Pl={},uw={};function HI(e){delete uw[e]}function $I(e){if(!e)return!1;if(typeof e=="string")return Jl(e,1)<Ph;if(e.colorStops){for(var t=e.colorStops,r=0,n=t.length,i=0;i<n;i++)r+=Jl(t[i].color,1);return r/=n,r<Ph}return!1}var VI=function(){function e(t,r,n){var i=this;this._sleepAfterStill=10,this._stillFrameAccum=0,this._needsRefresh=!0,this._needsRefreshHover=!0,this._darkMode=!1,n=n||{},this.dom=r,this.id=t;var a=new QP,o=n.renderer||"canvas";Pl[o]||(o=Gt(Pl)[0]),n.useDirtyRect=n.useDirtyRect==null?!1:n.useDirtyRect;var s=new Pl[o](r,a,n,t),l=n.ssr||s.ssrOnly;this.storage=a,this.painter=s;var u=!wt.node&&!wt.worker&&!l?new AI(s.getViewportRoot(),s.root):null;this.handler=new qP(a,s,u,s.root),this.animation=new _I({stage:{update:l?null:function(){return i._flush(!0)}}}),l||this.animation.start()}return e.prototype.add=function(t){!t||(this.storage.addRoot(t),t.addSelfToZr(this),this.refresh())},e.prototype.remove=function(t){!t||(this.storage.delRoot(t),t.removeSelfFromZr(this),this.refresh())},e.prototype.configLayer=function(t,r){this.painter.configLayer&&this.painter.configLayer(t,r),this.refresh()},e.prototype.setBackgroundColor=function(t){this.painter.setBackgroundColor&&this.painter.setBackgroundColor(t),this.refresh(),this._backgroundColor=t,this._darkMode=$I(t)},e.prototype.getBackgroundColor=function(){return this._backgroundColor},e.prototype.setDarkMode=function(t){this._darkMode=t},e.prototype.isDarkMode=function(){return this._darkMode},e.prototype.refreshImmediately=function(t){t||this.animation.update(!0),this._needsRefresh=!1,this.painter.refresh(),this._needsRefresh=!1},e.prototype.refresh=function(){this._needsRefresh=!0,this.animation.start()},e.prototype.flush=function(){this._flush(!1)},e.prototype._flush=function(t){var r,n=oa();this._needsRefresh&&(r=!0,this.refreshImmediately(t)),this._needsRefreshHover&&(r=!0,this.refreshHoverImmediately());var i=oa();r?(this._stillFrameAccum=0,this.trigger("rendered",{elapsedTime:i-n})):this._sleepAfterStill>0&&(this._stillFrameAccum++,this._stillFrameAccum>this._sleepAfterStill&&this.animation.stop())},e.prototype.setSleepAfterStill=function(t){this._sleepAfterStill=t},e.prototype.wakeUp=function(){this.animation.start(),this._stillFrameAccum=0},e.prototype.refreshHover=function(){this._needsRefreshHover=!0},e.prototype.refreshHoverImmediately=function(){this._needsRefreshHover=!1,this.painter.refreshHover&&this.painter.getType()==="canvas"&&this.painter.refreshHover()},e.prototype.resize=function(t){t=t||{},this.painter.resize(t.width,t.height),this.handler.resize()},e.prototype.clearAnimation=function(){this.animation.clear()},e.prototype.getWidth=function(){return this.painter.getWidth()},e.prototype.getHeight=function(){return this.painter.getHeight()},e.prototype.setCursorStyle=function(t){this.handler.setCursorStyle(t)},e.prototype.findHover=function(t,r){return this.handler.findHover(t,r)},e.prototype.on=function(t,r,n){return this.handler.on(t,r,n),this},e.prototype.off=function(t,r){this.handler.off(t,r)},e.prototype.trigger=function(t,r){this.handler.trigger(t,r)},e.prototype.clear=function(){for(var t=this.storage.getRoots(),r=0;r<t.length;r++)t[r]instanceof ye&&t[r].removeSelfFromZr(this);this.storage.delAllRoots(),this.painter.clear()},e.prototype.dispose=function(){this.animation.stop(),this.clear(),this.storage.dispose(),this.painter.dispose(),this.handler.dispose(),this.animation=this.storage=this.painter=this.handler=null,HI(this.id)},e}();function Qg(e,t){var r=new VI(B1(),e,t);return uw[r.id]=r,r}function GI(e,t){Pl[e]=t}var Jg=1e-4,fw=20;function UI(e){return e.replace(/^\s+|\s+$/g,"")}function Rh(e,t,r,n){var i=t[0],a=t[1],o=r[0],s=r[1],l=a-i,u=s-o;if(l===0)return u===0?o:(o+s)/2;if(n)if(l>0){if(e<=i)return o;if(e>=a)return s}else{if(e>=i)return o;if(e<=a)return s}else{if(e===i)return o;if(e===a)return s}return(e-i)/l*u+o}function Vt(e,t){switch(e){case"center":case"middle":e="50%";break;case"left":case"top":e="0%";break;case"right":case"bottom":e="100%";break}return it(e)?UI(e).match(/%$/)?parseFloat(e)/100*t:parseFloat(e):e==null?NaN:+e}function jt(e,t,r){return t==null&&(t=10),t=Math.min(Math.max(0,t),fw),e=(+e).toFixed(t),r?e:+e}function zr(e){if(e=+e,isNaN(e))return 0;if(e>1e-14){for(var t=1,r=0;r<15;r++,t*=10)if(Math.round(e*t)/t===e)return r}return WI(e)}function WI(e){var t=e.toString().toLowerCase(),r=t.indexOf("e"),n=r>0?+t.slice(r+1):0,i=r>0?r:t.length,a=t.indexOf("."),o=a<0?0:i-1-a;return Math.max(0,o-n)}function YI(e,t){var r=Math.log,n=Math.LN10,i=Math.floor(r(e[1]-e[0])/n),a=Math.round(r(Math.abs(t[1]-t[0]))/n),o=Math.min(Math.max(-i+a,0),20);return isFinite(o)?o:20}function qI(e,t,r){if(!e[t])return 0;var n=Ln(e,function(d,p){return d+(isNaN(p)?0:p)},0);if(n===0)return 0;for(var i=Math.pow(10,r),a=ct(e,function(d){return(isNaN(d)?0:d)/n*i*100}),o=i*100,s=ct(a,function(d){return Math.floor(d)}),l=Ln(s,function(d,p){return d+p},0),u=ct(a,function(d,p){return d-s[p]});l<o;){for(var f=Number.NEGATIVE_INFLine truncated
`);i=bw(t,r,n,i);for(var o=0,s=a.length;o<s;o++)a[o]=Sw(a[o],i);return a.join(`
`)}function bw(e,t,r,n){n=n||{};var i=Y({},n);i.font=t,r=Mt(r,"..."),i.maxIterations=Mt(n.maxIterations,2);var a=i.minChar=Mt(n.minChar,0);i.cnCharWidth=Oe("\u56FD",t);var o=i.ascCharWidth=Oe("a",t);i.placeholder=Mt(n.placeholder,"");for(var s=e=Math.max(0,e-1),l=0;l<a&&s>=o;l++)s-=o;var u=Oe(r,t);return u>s&&(r="",u=0),s=e-u,i.ellipsis=r,i.ellipsisWidth=u,i.contentWidth=s,i.containerWidth=e,i}function Sw(e,t){var r=t.containerWidth,n=t.font,i=t.contentWidth;if(!r)return"";var a=Oe(e,n);if(a<=r)return e;for(var o=0;;o++){if(a<=i||o>=t.maxIterations){e+=t.ellipsis;break}var s=o===0?ME(e,i,t.ascCharWidth,t.cnCharWidth):a>0?Math.floor(e.length*i/a):0;e=e.substr(0,s),a=Oe(e,n)}return e===""&&(e=t.placeholder),e}function ME(e,t,r,n){for(var i=0,a=0,o=e.length;a<o&&i<t;a++){var s=e.charCodeAt(a);i+=0<=s&&s<=127?r:n}return a}function AE(e,t){e!=null&&(e+="");var r=t.overflow,n=t.padding,i=t.font,a=r==="truncate",o=md(i),s=Mt(t.lineHeight,o),l=!!t.backgroundColor,u=t.lineOverflow==="truncate",f=t.width,c;f!=null&&(r==="break"||r==="breakAll")?c=e?xw(e,t.font,f,r==="breakAll",0).lines:[]:c=e?e.split(`
`):[];var v=c.length*s,h=Mt(t.height,v);if(v>h&&u){var d=Math.floor(h/s);c=c.slice(0,d)}if(e&&a&&f!=null)for(var p=bw(f,i,t.ellipsis,{minChar:t.truncateMinChar,placeholder:t.placeholder}),g=0;g<c.length;g++)c[g]=Sw(c[g],p);for(var m=h,y=0,g=0;g<c.length;g++)y=Math.max(Oe(c[g],i),y);f==null&&(f=y);var _=y;return n&&(m+=n[0]+n[2],_+=n[1]+n[3],f+=n[1]+n[3]),l&&(_=f),{lines:c,height:h,outerWidth:_,outerHeight:m,lineHeight:s,calculatedLineHeight:o,contentWidth:y,contentHeight:v,width:f}}var DE=function(){function e(){}return e}(),im=function(){function e(t){this.tokens=[],t&&(this.tokens=t)}return e}(),PE=function(){function e(){this.width=0,this.height=0,this.contentWidth=0,this.contentHeight=0,this.outerWidth=0,this.outerHeight=0,this.lines=[]}return e}();function IE(e,t){var r=new PE;if(e!=null&&(e+=""),!e)return r;for(var n=t.width,i=t.height,a=t.overflow,o=(a==="break"||a==="breakAll")&&n!=null?{width:n,accumWidth:0,breakAll:a==="breakAll"}:null,s=Yf.lastIndex=0,l;(l=Yf.exec(e))!=null;){var u=l.index;u>s&&qf(r,e.substring(s,u),t,o),qf(r,l[2],t,o,l[1]),s=Yf.lastIndex}s<e.length&&qf(r,e.substring(s,e.length),t,o);var f=[],c=0,v=0,h=t.padding,d=a==="truncate",p=t.lineOverflow==="truncate";function g(V,K,lt){V.width=K,V.lineHeight=lt,c+=lt,v=Math.max(v,K)}t:for(var m=0;m<r.lines.length;m++){for(var y=r.lines[m],_=0,w=0,S=0;S<y.tokens.length;S++){var b=y.tokens[S],x=b.styleName&&t.rich[b.styleName]||{},T=b.textPadding=x.padding,C=T?T[1]+T[3]:0,D=b.font=x.font||t.font;b.contentHeight=md(D);var P=Mt(x.height,b.contentHeight);if(b.innerHeight=P,T&&(P+=T[0]+T[2]),b.height=P,b.lineHeight=Do(x.lineHeight,t.lineHeight,P),b.align=x&&x.align||t.align,b.verticalAlign=x&&x.verticalAlign||"middle",p&&i!=null&&c+b.lineHeight>i){S>0?(y.tokens=y.tokens.slice(0,S),g(y,w,_),r.lines=r.lines.slice(0,m+1)):r.lines=r.lines.slice(0,m);break t}var A=x.width,L=A==null||A==="auto";if(typeof A=="string"&&A.charAt(A.length-1)==="%")b.percentWidth=A,f.push(b),b.contentWidth=Oe(b.text,D);else{if(L){var O=x.backgroundColor,z=O&&O.image;z&&(z=TE(z),$u(z)&&(b.width=Math.max(b.width,z.width*P/z.height)))}var N=d&&n!=null?n-w:null;N!=null&&N<b.width?!L||N<C?(b.text="",b.width=b.contentWidth=0):(b.text=CE(b.text,N-C,D,t.ellipsis,{minChar:t.truncateMinChar}),b.width=b.contentWidth=Oe(b.text,D)):b.contentWidth=Oe(b.text,D)}b.width+=C,w+=b.width,x&&(_=Math.max(_,b.lineHeight))}g(y,w,_)}r.outerWidth=r.width=Mt(n,v),r.outerHeight=r.height=Mt(i,c),r.contentHeight=c,r.contentWidth=v,h&&(r.outerWidth+=h[1]+h[3],r.outerHeight+=h[0]+h[2]);for(var m=0;m<f.length;m++){var b=f[m],tt=b.percentWidth;b.width=parseInt(tt,10)/100*r.width}return r}function qf(e,t,r,n,i){var a=t==="",o=i&&r.rich[i]||{},s=e.lines,l=o.font||r.font,u=!1,f,c;if(n){var v=o.padding,h=v?v[1]+v[3]:0;if(o.width!=null&&o.width!=="auto"){var d=Rn(o.width,n.width)+h;s.length>0&&d+n.accumWidth>n.width&&(f=t.split(`
`),u=!0),n.accumWidth=d}else{var p=xw(t,l,n.width,n.breakAll,n.accumWidth);n.accumWidth=p.accumWidth+h,c=p.linesWidths,f=p.lines}}else f=t.split(`
`);for(var g=0;g<f.length;g++){var m=f[g],y=new DE;if(y.styleName=i,y.text=m,y.isLineHolder=!m&&!a,typeof o.width=="number"?y.width=o.width:y.width=c?c[g]:Oe(m,l),!g&&!u){var _=(s[s.length-1]||(s[0]=new im)).tokens,w=_.length;w===1&&_[0].isLineHolder?_[0]=y:(m||!w||a)&&_.push(y)}else s.push(new im([y]))}}function EE(e){var t=e.charCodeAt(0);return t>=33&&t<=383}var LE=Ln(",&?/;] ".split(""),function(e,t){return e[t]=!0,e},{});function RE(e){return EE(e)?!!LE[e]:!0}function xw(e,t,r,n,i){for(var a=[],o=[],s="",l="",u=0,f=0,c=0;c<e.length;c++){var v=e.charAt(c);if(v===`
`){l&&(s+=l,f+=u),a.push(s),o.push(f),s="",l="",u=0,f=0;continue}var h=Oe(v,t),d=n?!1:!RE(v);if(a.length?f+h>r:i+f+h>r){f?(s||l)&&(d?(s||(s=l,l="",u=0,f=u),a.push(s),o.push(f-u),l+=v,u+=h,s="",f=u):(l&&(s+=l,l="",u=0),a.push(s),o.push(f),s=v,f=h)):d?(a.push(l),o.push(u),l=v,u=h):(a.push(v),o.push(h));continue}f+=h,d?(l+=v,u+=h):(l&&(s+=l,l="",u=0),s+=v)}return!a.length&&!s&&(s=e,l="",u=0),l&&(s+=l),s&&(a.push(s),o.push(f)),a.length===1&&(f+=i),{accumWidth:f,lines:a,linesWidths:o}}var Nh="__zr_style_"+Math.round(Math.random()*10),gi={shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,shadowColor:"#000",opacity:1,blend:"source-over"},Vu={style:{shadowBlur:!0,shadowOffsetX:!0,shadowOffsetY:!0,shadowColor:!0,opacity:!0}};gi[Nh]=!0;var am=["z","z2","invisible"],OE=["invisible"],kE=function(e){J(t,e);function t(r){return e.call(this,r)||this}return t.prototype._init=function(r){for(var n=Gt(r),i=0;i<n.length;i++){var a=n[i];a==="style"?this.useStyle(r[a]):e.prototype.attrKV.call(this,a,r[a])}this.style||this.useStyle({})},t.prototype.beforeBrush=function(){},t.prototype.afterBrush=function(){},t.prototype.innerBeforeBrush=function(){},t.prototype.innerAfterBrush=function(){},t.prototype.shouldBePainted=function(r,n,i,a){var o=this.transform;if(this.ignore||this.invisible||this.style.opacity===0||this.culling&&NE(this,r,n)||o&&!o[0]&&!o[3])return!1;if(i&&this.__clipPaths){for(var s=0;s<this.__clipPaths.length;++s)if(this.__clipPaths[s].isZeroArea())return!1}if(a&&this.parent)for(var l=this.parent;l;){if(l.ignore)return!1;l=l.parent}return!0},t.prototype.contain=function(r,n){return this.rectContain(r,n)},t.prototype.traverse=function(r,n){r.call(n,this)},t.prototype.rectContain=function(r,n){var i=this.transformCoordToLocal(r,n),a=this.getBoundingRect();return a.contain(i[0],i[1])},t.prototype.getPaintRect=function(){var r=this._paintRect;if(!this._paintRect||this.__dirty){var n=this.transform,i=this.getBoundingRect(),a=this.style,o=a.shadowBlur||0,s=a.shadowOffsetX||0,l=a.shadowOffsetY||0;r=this._paintRect||(this._paintRect=new Ot(0,0,0,0)),n?Ot.applyTransform(r,i,n):r.copy(i),(o||s||l)&&(r.width+=o*2+Math.abs(s),r.height+=o*2+Math.abs(l),r.x=Math.min(r.x,r.x+s-o),r.y=Math.min(r.y,r.y+l-o));var u=this.dirtyRectTolerance;r.isZero()||(r.x=Math.floor(r.x-u),r.y=Math.floor(r.y-u),r.width=Math.ceil(r.width+1+u*2),r.height=Math.ceil(r.height+1+u*2))}return r},t.prototype.setPrevPaintRect=function(r){r?(this._prevPaintRect=this._prevPaintRect||new Ot(0,0,0,0),this._prevPaintRect.copy(r)):this._prevPaintRect=null},t.prototype.getPrevPaintRect=function(){return this._prevPaintRect},t.prototype.animateStyle=function(r){return this.animate("style",r)},t.prototype.updateDuringAnimation=function(r){r==="style"?this.dirtyStyle():this.markRedraw()},t.prototype.attrKV=function(r,n){r!=="style"?e.prototype.attrKV.call(this,r,n):this.style?this.setStyle(n):this.useStyle(n)},t.prototype.setStyle=function(r,n){return typeof r=="string"?this.style[r]=n:Y(this.style,r),this.dirtyStyle(),this},t.prototype.dirtyStyle=function(r){r||this.markRedraw(),this.__dirty|=ho,this._rect&&(this._rect=null)},t.prototype.dirty=function(){this.dirtyStyle()},t.prototype.styleChanged=function(){return!!(this.__dirty&ho)},t.prototype.styleUpdated=function(){this.__dirty&=~ho},t.prototype.createStyle=function(r){return Fu(gi,r)},t.prototype.useStyle=function(r){r[Nh]||(r=this.createStyle(r)),this.__inHover?this.__hoverStyle=r:this.style=r,this.dirtyStyle()},t.prototype.isStyleObject=function(r){return r[Nh]},t.prototype._innerSaveToNormal=function(r){e.prototype._innerSaveToNormal.call(this,r);var n=this._normalState;r.style&&!n.style&&(n.style=this._mergeStyle(this.createStyle(),this.style)),this._savePrimaryToNormal(r,n,am)},t.prototype._applyStateObj=function(r,n,i,a,o,s){e.prototype._applyStateObj.call(this,r,n,i,a,o,s);var l=!(n&&a),u;if(n&&n.style?o?a?u=n.style:(u=this._mergeStyle(this.createStyle(),i.style),this._mergeStyle(u,n.style)):(u=this._mergeStyle(this.createStyle(),a?this.style:i.style),this._mergeStyle(u,n.style)):l&&(u=i.style),u)if(o){var f=this.style;if(this.style=this.createStyle(l?{}:f),l)for(var c=Gt(f),v=0;v<c.length;v++){var h=c[v];h in u&&(u[h]=u[h],this.style[h]=f[h])}for(var d=Gt(u),v=0;v<d.length;v++){var h=d[v];this.style[h]=this.style[h]}this._transitionState(r,{style:u},s,this.getAnimationStyleProps())}else this.useStyle(u);for(var p=this.__inHover?OE:am,v=0;v<p.length;v++){var h=p[v];n&&n[h]!=null?this[h]=n[h]:l&&i[h]!=null&&(this[h]=i[h])}},t.prototype._mergeStates=function(r){for(var n=e.prototype._mergeStates.call(this,r),i,a=0;a<r.length;a++){var o=r[a];o.style&&(i=i||{},this._mergeStyle(i,o.style))}return i&&(n.style=i),n},t.prototype._mergeStyle=function(r,n){return Y(r,n),r},t.prototype.getAnimationStyleProps=function(){return Vu},t.initDefaultProps=function(){var r=t.prototype;r.type="displayable",r.invisible=!1,r.z=0,r.z2=0,r.zlevel=0,r.culling=!1,r.cursor="pointer",r.rectHover=!1,r.incremental=!1,r._rect=null,r.dirty Line truncated
var uC=Object.defineProperty;var rg=Object.getOwnPropertySymbols;var fC=Object.prototype.hasOwnProperty,cC=Object.prototype.propertyIsEnumerable;var Zi=(t,e)=>(e=Symbol[t])?e:Symbol.for("Symbol."+t),hC=t=>{throw TypeError(t)};var ng=(t,e,r)=>e in t?uC(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,Yn=(t,e)=>{for(var r in e||(e={}))fC.call(e,r)&&ng(t,r,e[r]);if(rg)for(var r of rg(e))cC.call(e,r)&&ng(t,r,e[r]);return t};var ur=(t,e,r)=>new Promise((n,i)=>{var a=l=>{try{s(r.next(l))}catch(u){i(u)}},o=l=>{try{s(r.throw(l))}catch(u){i(u)}},s=l=>l.done?n(l.value):Promise.resolve(l.value).then(a,o);s((r=r.apply(t,e)).next())}),$n=function(t,e){this[0]=t,this[1]=e},$f=(t,e,r)=>{var n=(s,l,u,f)=>{try{var c=r[s](l),v=(l=c.value)instanceof $n,h=c.done;Promise.resolve(v?l[0]:l).then(d=>v?n(s==="return"?s:"next",l[1]?{done:d.done,value:d.value}:d,u,f):u({value:d,done:h})).catch(d=>n("throw",d,u,f))}catch(d){f(d)}},i=(s,l,u,f)=>o[s]=c=>(l=new Promise((v,h,d)=>(d=()=>n(s,c,v,h),a?a.then(d):d())),f=()=>a===u&&(a=0),a=u=l.then(f,f),l),a,o={};return r=r.apply(t,e),o[Zi("asyncIterator")]=()=>o,i("next"),i("throw"),i("return"),o},Xf=t=>{var e=t[Zi("asyncIterator")],r=!1,n,i={};return e==null?(e=t[Zi("iterator")](),n=a=>i[a]=o=>e[a](o)):(e=e.call(t),n=a=>i[a]=o=>{if(r){if(r=!1,a==="throw")throw o;return o}return r=!0,{done:!1,value:new $n(new Promise(s=>{var l=e[a](o);l instanceof Object||hC("Object expected"),s(l)}),1)}}),i[Zi("iterator")]=()=>i,n("next"),"throw"in e?n("throw"):i.throw=a=>{throw a},"return"in e&&n("return"),i},ig=(t,e,r)=>(e=t[Zi("asyncIterator")])?e.call(t):(t=t[Zi("iterator")](),e={},r=(n,i)=>(i=t[n])&&(e[n]=a=>new Promise((o,s,l)=>(a=i.call(t,a),l=a.done,Promise.resolve(a.value).then(u=>o({value:u,done:l}),s)))),r("next"),r("return"),e);import{defineComponent as hd,ref as Pa,onMounted as vC,computed as Bt,getCurrentInstance as dC,h as eu,inject as yr,reactive as vd,watch as dd,effectScope as e1,markRaw as t1,hasInjectionContext as pC,isRef as tu,isReactive as r1,toRaw as gC,getCurrentScope as mC,onScopeDispose as yC,nextTick as n1,toRefs as _C,onUnmounted as SC,onDeactivated as bC,onActivated as wC,shallowRef as TC,unref as Mo,shallowReactive as xC,provide as qf}from"/luci-static/quickstart/card-runtime-v1.js";function i1(t,e){return function(){return t.apply(e,arguments)}}const{toString:CC}=Object.prototype,{getPrototypeOf:en}=Object,{iterator:us,toStringTag:a1}=Symbol,tn=(({hasOwnProperty:t})=>(e,r)=>t.call(e,r))(Object.prototype),o1=t=>typeof t=="string"&&(t==="__proto__"||t==="constructor"||t==="prototype"),ef=(t,e,r)=>t===Object.prototype||!r&&e===null,AC=t=>{if(!Object.isExtensible(t))return!1;const e=Object.getOwnPropertyNames(t);return Object.getOwnPropertySymbols&&e.push(...Object.getOwnPropertySymbols(t)),e.every(r=>{if(o1(r))return!1;const n=Object.getOwnPropertyDescriptor(t,r);return!!n&&n.configurable&&n.writable===!0})},pd=(t,e)=>{let r=t;const n=[];for(;r!=null;){if(n.indexOf(r)!==-1)return!1;n.push(r);const i=en(r);if(ef(r,i,r===t))return!1;if(tn(r,e))return!0;r=i}return!1},DC=(t,e)=>t!=null&&pd(t,e)?t[e]:void 0,EC=t=>{if(t==null||typeof t!="object"&&typeof t!="function")return t;const e=en(t);if(e===null&&AC(t))return t;const r=Object.create(null),n=Object.create(null),i=[];let a=t;for(;a!=null&&i.indexOf(a)===-1;){i.push(a);const o=a===t?e:en(a);if(ef(a,o,a===t))break;const s=Object.getOwnPropertyNames(a);Object.getOwnPropertySymbols&&s.push(...Object.getOwnPropertySymbols(a));for(const l of s)o1(l)||tn(n,l)||(r[l]=t[l],n[l]=!0);a=o}return r},gd=(t=>e=>{const r=CC.call(e);return t[r]||(t[r]=r.slice(8,-1).toLowerCase())})(Object.create(null)),ir=t=>(t=t.toLowerCase(),e=>gd(e)===t),tf=t=>e=>typeof e===t,{isArray:Ii}=Array,Li=tf("undefined");function Wa(t){return t!==null&&!Li(t)&&t.constructor!==null&&!Li(t.constructor)&&Rt(t.constructor.isBuffer)&&t.constructor.isBuffer(t)}const s1=ir("ArrayBuffer");function RC(t){let e;return typeof ArrayBuffer!="undefined"&&ArrayBuffer.isView?e=ArrayBuffer.isView(t):e=t&&t.buffer&&s1(t.buffer),e}const MC=tf("string"),Rt=tf("function"),PC=tf("number"),Ya=t=>t!==null&&typeof t=="object",IC=t=>t===!0||t===!1,Cl=t=>{if(!Ya(t))return!1;const e=en(t);return(e===null||e===Object.prototype||en(e)===null)&&(ef(t,e,!0)||!tn(t,a1)&&!tn(t,us))},LC=t=>{if(!Ya(t)||Wa(t))return!1;try{return Object.keys(t).length===0&&Object.getPrototypeOf(t)===Object.prototype}catch(e){return!1}},OC=ir("Date"),kC=ir("File"),NC=t=>!!(t&&typeof t.uri!="undefined"),BC=t=>t&&typeof t.getParts!="undefined",FC=ir("Blob"),zC=ir("FileList"),HC=ir("Set"),UC=t=>Ya(t)&&Rt(t.pipe);function VC(){return typeof globalThis!="undefined"?globalThis:typeof self!="undefined"?self:typeof window!="undefined"?window:typeof global!="undefined"?global:{}}const ag=VC(),og=typeof ag.FormData!="undefined"?ag.FormData:void 0,GC=t=>{if(!t)return!1;if(og&&t instanceof og)return!0;const e=en(t);if(!e||e===Object.prototype||!Rt(t.append))return!1;const r=gd(t);return r==="formdata"||r==="object"&&Rt(tLine truncated
`).forEach(function(o){i=o.indexOf(":"),r=o.substring(0,i).trim().toLowerCase(),n=o.substring(i+1).trim();const s=E.hasOwnProp(e,r);!r||s&&E.hasOwnProp(SA,r)||(r==="set-cookie"?s?e[r].push(n):e[r]=[n]:e[r]=s?e[r]+", "+n:n)}),e};function wA(t){let e=0,r=t.length;for(;e<r;){const n=t.charCodeAt(e);if(n!==9&&n!==32)break;e+=1}for(;r>e;){const n=t.charCodeAt(r-1);if(n!==9&&n!==32)break;r-=1}return e===0&&r===t.length?t:t.slice(e,r)}const TA=new RegExp("[\\u0000-\\u0008\\u000a-\\u001f\\u007f]+","g"),xA=new RegExp("[^\\u0009\\u0020-\\u007e\\u0080-\\u00ff]+","g");function md(t,e){return E.isArray(t)?t.map(r=>md(r,e)):wA(String(t).replace(e,""))}const CA=t=>md(t,TA),AA=t=>md(t,xA);function h1(t){const e=Object.create(null);return E.forEach(t.toJSON(),(r,n)=>{e[n]=AA(r)}),e}const sg=Symbol("internals");function Za(t){return t&&String(t).trim().toLowerCase()}function Al(t){return t===!1||t==null?t:E.isArray(t)?t.map(Al):CA(String(t))}function DA(t){const e=Object.create(null),r=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let n;for(;n=r.exec(t);)e[n[1]]=n[2];return e}const EA=/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;function jf(t){let e=0,r=t.length;for(;e<r;){const n=t.charCodeAt(e);if(n!==9&&n!==32)break;e+=1}for(;r>e;){const n=t.charCodeAt(r-1);if(n!==9&&n!==32)break;r-=1}return e===0&&r===t.length?t:t.slice(e,r)}function RA(t){const e=t.length-1;if(e<1||t.charCodeAt(0)!==34||t.charCodeAt(e)!==34)return t;let r="";for(let n=1;n<e;n++){const i=t.charCodeAt(n);if(i===34||i===92&&(n+=1,n>=e))return t;r+=t[n]}return r}function MA(t){const e=Object.create(null),r=String(t);let n=0,i=!1,a=!1;function o(s){const l=jf(r.slice(n,s)),u=l.indexOf("=");if(u<1)return;const f=jf(l.slice(0,u));if(!EA.test(f))return;const c=f.toLowerCase();if(c==="__proto__"||c==="constructor"||c==="prototype")return;const v=jf(l.slice(u+1));e[c]=RA(v)}for(let s=0;s<r.length;s++){const l=r.charCodeAt(s);i?a?a=!1:l===92?a=!0:l===34&&(i=!1):l===34?i=!0:(l===44||l===59)&&(o(s),n=s+1)}return o(r.length),e}const PA=t=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(t.trim()),IA="__Proto__",lg=t=>t==="__proto__"?IA:t;function Zf(t,e,r,n,i){if(E.isFunction(n))return n.call(t,e,r);if(i&&(e=r),!!E.isString(e)){if(E.isString(n))return e.indexOf(n)!==-1;if(E.isRegExp(n))return n.test(e)}}function LA(t){return t.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(e,r,n)=>r.toUpperCase()+n)}function OA(t,e){const r=E.toCamelCase(" "+e);["get","set","has"].forEach(n=>{Object.defineProperty(t,n+r,{__proto__:null,value:function(i,a,o){return this[n].call(this,e,i,a,o)},configurable:!0})})}let je=class{constructor(e){e&&this.set(e)}set(e,r,n){const i=this;function a(s,l,u){const f=Za(l);if(!f)return;const c=E.findKey(i,f);(!c||i[c]===void 0||u===!0||u===void 0&&i[c]!==!1)&&(i[lg(c||l)]=Al(s))}const o=(s,l)=>E.forEach(s,(u,f)=>a(u,f,l));if(E.isPlainObject(e)||e instanceof this.constructor)o(e,r);else if(E.isString(e)&&(e=e.trim())&&!PA(e))o(bA(e),r);else if(E.isObject(e)&&E.isSafeIterable(e)){let s=Object.create(null),l,u;for(const f of e){if(!E.isArray(f))throw new TypeError("Object iterator must return a key-value pair");u=f[0],E.hasOwnProp(s,u)?(l=s[u],s[u]=E.isArray(l)?[...l,f[1]]:[l,f[1]]):s[u]=f[1]}o(s,r)}else e!=null&&a(r,e,n);return this}get(e,r){if(e=Za(e),e){const n=E.findKey(this,e);if(n){const i=this[n];if(!r)return i;if(r===!0)return DA(i);if(E.isFunction(r))return r.call(this,i,n);if(E.isRegExp(r))return r.exec(i);throw new TypeError("parser must be boolean|regexp|function")}}}has(e,r){if(e=Za(e),e){const n=E.findKey(this,e);return!!(n&&this[n]!==void 0&&(!r||Zf(this,this[n],n,r)))}return!1}delete(e,r){const n=this;let i=!1;function a(o){if(o=Za(o),o){const s=E.findKey(n,o);s&&(!r||Zf(n,n[s],s,r))&&(delete n[s],i=!0)}}return E.isArray(e)?e.forEach(a):a(e),i}clear(e){const r=Object.keys(this);let n=r.length,i=!1;for(;n--;){const a=r[n];(!e||Zf(this,this[a],a,e,!0))&&(delete this[a],i=!0)}return i}normalize(e){const r=this,n=Object.create(null);return E.forEach(this,(i,a)=>{const o=E.findKey(n,a);if(o){r[o]=Al(i),delete r[a];return}const s=lg(e?LA(a):String(a).trim());s!==a&&delete r[a],r[s]=Al(i),n[s]=!0}),this}concat(...e){return this.constructor.concat(this,...e)}toJSON(e){const r=Object.create(null);return E.forEach(this,(n,i)=>{n!=null&&n!==!1&&(r[i]=e&&E.isArray(n)?n.join(", "):n)}),r}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([e,r])=>e+": "+r).join(`
`)}getSetCookie(){const e=this.get("set-cookie");return E.isArray(e)?e:e==null||e===!1?[]:[e]}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(e){return e instanceof this?e:new this(e)}static parseParameters(e){return MA(e)}static concat(e,...r){const n=new this(e);return r.forEach(i=>n.set(i)),n}static accessor(e){const n=(this[sg]=this[sg]={accessors:{}}).accessors,i=this.prototype;function a(o){const s=Za(o);n[s]||(OA(i,o),n[s]=!0)}return E.isArray(e)?e.forEach(a):a(e),this}};je.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);E.reduceDescriptors(je.prototype,({value:t},e)=>{let r=e[0].toUpperCase()+e.slice(1);return{get:()=>t,set(n){this[r]=n}}});E.freezeMethods(je);const ru="[REDACTED ****]";function kA(t){if(E.hasOwnProp(t,"toJSON"))return!0;let e=Object.getPrototypeOf(t);for(;e&&e!==Object.prototype;){if(E.hasOwnProp(e,"toJSON"))return!0;e=Object.getPrototypeOf(e)}return!1}function NA(t,e){const r=new Set(e.map(a=>String(a).toLowerCase())),n=[],i=a=>{if(a===null||typeof a!="object"||E.isBuffer(a))return a;if(n.indexOf(a)!==-1)return;a instanceof je&&(a=a.toJSON()),n.push(a);let o;if(E.isArray(a))o=[],a.forEach((s,l)=>{const u=i(s);E.isUndefined(u)||(o[l]=u)});else{if(!E.isPlainObject(a)&&kA(a))return n.pop(),a;o=Object.create(null);for(const[s,l]of Object.entries(a)){const u=r.has(s.toLowerCase())?ru:i(l);E.isUndefined(u)||(o[s]=u)}}return n.pop(),o};return i(t)}function ug(t){try{return String(t)}catch(e){return""}}function BA(t){return t.errors.map(r=>{try{return r&&r.message?ug(r.message):ug(r)}catch(n){return""}}).filter(Boolean).join("; ")||t.name||"AggregateError"}const FA=`
Caused by: `,kh=Symbol("causeStack");function zA(t,e){const r=E.hasOwnProp(t,kh)&&t[kh];return r&&e.endsWith(r)?e.length-r.length:-1}function HA(t,e){try{const r=e&&e.stack;if(typeof r!="string"||!r)return;const n=t.stack;if(typeof n!="string"||!n)return;const i=FA+r;t.stack=n+i,Object.defineProperty(t,kh,{__proto__:null,value:i})}catch(r){}}let V=class v1 extends Error{static from(e,r,n,i,a,o){let s=e.message;!s&&E.isArray(e.errors)&&e.errors.length&&(s=BA(e));const l=new v1(s,r||e.code,n,i,a);return Object.defineProperty(l,"cause",{__proto__:null,value:e,writable:!0,enumerable:!1,configurable:!0}),l.name=e.name,HA(l,e),e.status!=null&&l.status==null&&(l.status=e.status),o&&Object.assign(l,o),l}constructor(e,r,n,i,a){super(e),Object.defineProperty(this,"message",{__proto__:null,value:e,enumerable:!0,writable:!0,configurable:!0}),this.name="AxiosError",this.isAxiosError=!0,r&&(this.code=r),n&&(this.config=n),i&&(this.request=i),a&&(this.response=a,this.status=a.status)}toJSON(){const e=this.config,r=e&&E.hasOwnProp(e,"redact")?e.redact:void 0,n=E.isArray(r)&&r.length>0?NA(e,r):E.toJSONObject(e);return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:n,code:this.code,status:this.status}}};V.ERR_BAD_OPTION_VALUE="ERR_BAD_OPTION_VALUE";V.ERR_BAD_OPTION="ERR_BAD_OPTION";V.ECONNABORTED="ECONNABORTED";V.ETIMEDOUT="ETIMEDOUT";V.ECONNREFUSED="ECONNREFUSED";V.ERR_NETWORK="ERR_NETWORK";V.ERR_FR_TOO_MANY_REDIRECTS="ERR_FR_TOO_MANY_REDIRECTS";V.ERR_DEPRECATED="ERR_DEPRECATED";V.ERR_BAD_RESPONSE="ERR_BAD_RESPONSE";V.ERR_BAD_REQUEST="ERR_BAD_REQUEST";V.ERR_CANCELED="ERR_CANCELED";V.ERR_NOT_SUPPORT="ERR_NOT_SUPPORT";V.ERR_INVALID_URL="ERR_INVALID_URL";V.ERR_FORM_DATA_DEPTH_EXCEEDED="ERR_FORM_DATA_DEPTH_EXCEEDED";const UA=null,d1=100;function Nh(t){return E.isPlainObject(t)||E.isArray(t)}function p1(t){return E.endsWith(t,"[]")?t.slice(0,-2):t}function Kf(t,e,r){return t?t.concat(e).map(function(i,a){return i=p1(i),!r&&a?"["+i+"]":i}).join(r?".":""):e}function VA(t){return E.isArray(t)&&!t.some(Nh)}const GA=E.toFlatObject(E,{},null,function(e){return/^is[A-Z]/.test(e)});function rf(t,e,r){if(!E.isObject(t))throw new TypeError("target must be an object");e=e||new FormData;const n=(y,_)=>{const S=E.getSafeProp(r,y);return E.isUndefined(S)?_:S},i=n("metaTokens",!0),a=n("visitor")||p,o=n("dots",!1),s=n("indexes",!1),l=n("Blob")||typeof Blob!="undefined"&&Blob,u=n("maxDepth",d1),f=l&&E.isSpecCompliantForm(e),c=[];if(!E.isFunction(a))throw new TypeError("visitor must be a function");function v(y){if(y===null)return"";if(E.isDate(y))return y.toISOString();if(E.isBoolean(y))return y.toString();if(!f&&E.isBlob(y))throw new V("Blob is not supported. Use a Buffer instead.");if(E.isArrayBuffer(y)||E.isTypedArray(y)){if(f&&typeof l=="function")return new l([y]);throw new V("Blob is not supported. Use a Buffer instead.",V.ERR_NOT_SUPPORT)}return y}function h(y){if(y>u)throw new V("Object is too deeply nested ("+y+" levels). Max depth: "+u,V.ERR_FORM_DATA_DEPTH_EXCEEDED)}function d(y,_){if(u===1/0)return JSON.stringify(y);const S=[];return JSON.stringify(y,function(w,T){if(!E.isObject(T))return T;for(;S.length&&S[S.length-1]!==this;)S.pop();return S.push(T),h(_+S.length-1),T})}function p(y,_,S){let b=y;if(E.isReactNative(e)&&E.isReactNativeBlob(y))return e.append(Kf(S,_,o),v(y)),!1;if(y&&!S&&typeof y=="object"){if(E.endsWith(_,"{}"))_=i?_:_.slice(0,-2),y=d(y,1);else if(E.isArray(y)&&VA(y)||(E.isFileList(y)||E.endsWith(_,"[]"))&&(b=E.toArray(y)))return _=p1(_),b.forEach(function(T,C){!(E.isUndefined(T)||T===null)&&e.append(s===!0?Kf([_],C,o):s===null?_:_+"[]",v(T))}),!1}return Nh(y)?!0:(e.append(Kf(S,_,o),v(y)),!1)}const m=Object.assign(GA,{defaultVisitor:p,convertValue:v,isVisitable:Nh});function g(y,_,S=0){if(!E.isUndefined(y)){if(h(S),c.indexOf(y)!==-1)throw new Error("Circular reference detected in "+_.join("."));c.push(y),E.forEach(y,function(w,T){(!(E.isUndefined(w)||w===null)&&a.call(e,w,E.isString(T)?T.trim():T,_,m))===!0&&g(w,_?_.concat(T):[T],S+1)}),c.pop()}}if(!E.isObject(t))throw new TypeError("data must be an object");return g(t),e}function fg(t){const e={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+"};return encodeURIComponent(t).replace(/[!'()~]|%20/g,function(n){return e[n]})}function yd(t,e){this._pairs=[],t&&rf(t,this,e)}const g1=yd.prototype;g1.append=function(e,r){this._pairs.push([e,r])};g1.toString=function(e){const r=e?n=>e.call(this,n,fg):fg;return this._pairs.map(function(i){return r(i[0])+"="+r(i[1])},"").join("&")};function WA(t){return encodeURIComponent(t).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+")}function m1(t,e,r){if(!e)return t;t=t||"";const n=E.isFunction(r)?{serialize:r}:r,i=E.getSafeProp(n,"encode")||WA,a=E.getSafeProp(n,"serialize");let o;if(a?o=a(e,n):o=E.isURLSearchParams(e)?e.toString():new yd(e,n).toString(i) Line truncated
`,wg=t=>String(t).replace(/\r\n|\r|\n/g,_n),Tg=t=>String(t).replace(/[\r\n"]/g,e=>e==="\r"?"%0D":e===`
`?"%0A":"%22"),Ja=(t,e)=>{let r=0;for(let n=0;n<t.length;n++){const i=t.charCodeAt(n);e&&(i===13||i===10)?(i===13&&t.charCodeAt(n+1)===10&&n++,r+=2):i<128?r++:i<2048?r+=2:i>=55296&&i<=56319&&t.charCodeAt(n+1)>=56320&&t.charCodeAt(n+1)<=57343?(r+=4,n++):r+=3}return r};class Dl extends TypeError{constructor(e,r){super(e),this.name="FormDataError",this.reason=r}}function DD(t,e,r,n,i){if(!E.isFunction(e))throw new Dl("Blob is not supported","blob");if(!E.isString(i)||!/^[A-Za-z0-9'()+_,./:=? -]{1,70}$/.test(i)||/ $/.test(i))throw new Dl("Invalid multipart boundary","boundary");const a="--"+i+"--"+_n;let o=a.length;const s=[],l=f=>{if(o+f>r)throw n()};l(0);for(const[f,c]of t){const v=E.isString(c),h=v?Ja(c,!0):c.size;if(!Number.isFinite(h)||h<0)throw new Dl("Unsupported FormData part size","part");l(h+Ja(f,!0)+(!v&&c.name?Ja(c.name,!1):0)+(!v&&c.type?Ja(c.type,!1):0));let d="--"+i+_n+'Content-Disposition: form-data; name="'+Tg(wg(f))+'"';v||(d+='; filename="'+Tg(E.isString(c.name)?c.name:"blob")+'"'+_n+"Content-Type: "+String(c.type||"application/octet-stream").replace(/[\r\n]/g,"")),d+=_n+_n;const p=Ja(d,!1)+h+_n.length;l(p),o+=p,s.push({header:d,value:c,isString:v})}const u=[];for(const f of s)u.push(f.header,f.isString?wg(f.value):f.value,_n);return u.push(a),new e(u,{type:"multipart/form-data; boundary="+i})}function ED(t,e,r,n,i){if(!E.isFunction(e))throw new V("Blob is not supported",V.ERR_NOT_SUPPORT);if(i===void 0)if(E.isFunction(Ye.generateString))i="axios-"+Ye.generateString(36,"0123456789abcdef");else{const a=E.global.crypto;if(!a||!E.isFunction(a.getRandomValues))throw new V("Random byte generation is not supported",V.ERR_NOT_SUPPORT);const o=new Uint8Array(18);a.getRandomValues(o),i="axios-"+Array.from(o,s=>("0"+s.toString(16)).slice(-2)).join("")}try{return DD(t,e,r,n,i)}catch(a){if(a instanceof Dl){const o=a.reason==="boundary"?V.ERR_BAD_OPTION_VALUE:V.ERR_BAD_REQUEST;throw new V(a.message,o)}throw a}}const xg=t=>t>=48&&t<=57||t>=65&&t<=70||t>=97&&t<=102,P1=(t,e,r)=>e+2<r&&xg(t.charCodeAt(e+1))&&xg(t.charCodeAt(e+2)),Cg=t=>t<=57?t-48:(t&223)-55,RD=t=>t>=65&&t<=90||t>=97&&t<=122||t>=48&&t<=57||t===43||t===47||t===45||t===95,MD=t=>t===9||t===10||t===12||t===13||t===32,PD=t=>{const e=Math.floor(t/4),r=t%4;return e*3+(r===2?1:r===3?2:0)},ID=t=>{const e=t.length;let r=0;return e>0&&t.charCodeAt(e-1)===61&&(r++,e>1&&t.charCodeAt(e-2)===61&&r++),Math.floor((e-r)*3/4)},LD=t=>{const e=t.length;let r=0,n=0,i=!1;for(let a=0;a<e;a++){let o=t.charCodeAt(a);if(o===37&&P1(t,a,e)&&(o=Cg(t.charCodeAt(a+1))*16+Cg(t.charCodeAt(a+2)),a+=2),!MD(o)){if(o===61){n++;continue}if(!RD(o)||n>0){i=!0;continue}r++}}return i||n>2||n>0&&(r+n)%4!==0||r%4===1?ID(t):PD(r)},OD=(t,e,r)=>{if(!t||typeof t!="string"||!/^data:/i.test(t))return 0;const n=t.indexOf(",");if(n<0)return 0;const i=t.slice(5,n),a=t.slice(n+1);if(r.test(i))return e(a);let s=0;for(let l=0,u=a.length;l<u;l++){const f=a.charCodeAt(l);if(f===37&&P1(a,l,u))s+=1,l+=2;else if(f<128)s+=1;else if(f<2048)s+=2;else if(f>=55296&&f<=56319&&l+1<u){const c=a.charCodeAt(l+1);c>=56320&&c<=57343?(s+=4,l++):s+=3}else s+=3}return s};function kD(t){const e=typeof t=="string"?t.indexOf("#"):-1;return OD(e===-1?t:t.slice(0,e),LD,/; *base64[\t\n\f\r ]*$/i)}const bd="1.21.0",Ag=64*1024,Dg={cache:"default",redirect:"follow",referrer:"about:client",referrerPolicy:"",mode:"cors",integrity:"",keepalive:!1,priority:"auto",window:null},{isFunction:Ms}=E,ND=t=>encodeURIComponent(t).replace(/%([0-9A-F]{2})/gi,(e,r)=>String.fromCharCode(parseInt(r,16))),Eg=t=>{if(!E.isString(t))return t;try{return decodeURIComponent(t)}catch(e){return t}},Ps=(t,...e)=>{try{return!!t(...e)}catch(r){return!1}},BD=t=>{const e=t.indexOf("://");let r=t;return e!==-1&&(r=r.slice(e+3)),r.includes("@")||r.includes(":")},FD=t=>{const e=E.global!==void 0&&E.global!==null?E.global:globalThis,{ReadableStream:r,TextEncoder:n}=e;t=E.merge.call({skipUndefined:!0},{Request:e.Request,Response:e.Response,Blob:e.Blob},t);const i=E.hasOwnProp(t,"fetch")?t.fetch:void 0,{Request:a,Response:o,Blob:s}=t,l=i?Ms(i):typeof fetch=="function",u=Ms(a),f=Ms(o);if(!l)return!1;const c=l&&Ms(r),v=l&&(typeof n=="function"?(_=>S=>_.encode(S))(new n):_=>ur(null,null,function*(){return new Uint8Array(yield new a(_).arrayBuffer())})),h=u&&c&&Ps(()=>{let _=!1;const S=new a(Ye.origin,{body:new r,method:"POST",get duplex(){return _=!0,"half"}}),b=S.headers.has("Content-Type");return S.body!=null&&S.body.cancel(),_&&!b}),d=f&&c&&Ps(()=>E.isReadableStream(new o("").body)),p=Object.assign(Object.create(null),Dg);u&&!Ps(()=>new a(Ye.origin,p))&&(delete p.cache,Ps(()=>new a(Ye.origin,p))||(p.cache=Dg.cache));const m={stream:d&&(_=>_.body)};l&&["text","arrayBuffer","blob","formData","stream"].forEach(_=>{!m[_]&&(m[_]=(S,b)=>{let w=S&&S[_];if(w)return w.call(S);throw new V(`Response type '${_}' is not supported`,V.ERR_NOT_SUPPORT,b)})});const g=_=>ur(null,null,function*(){if(_==null)return 0;if(E.isBlob(_))return _.size;if(E.isArrayBufferView(_)||E.isArrayBu Line truncated
`+o.map(Rg).join(`
`):" "+Rg(o[0]):"as no adapter specified";throw new V("There is no suitable adapter to dispatch the request "+s,V.ERR_NOT_SUPPORT)}return i}const L1={getAdapter:UD,adapters:wd};function tc(t){if(t.cancelToken&&t.cancelToken.throwIfRequested(),t.signal&&t.signal.aborted)throw new hs(null,t)}function Mg(t){return Math.max(0,Date.now()-t)}function rc(t){const e=E.toSafeFlatObject(t);tc(e),e.headers=je.from(E.getSafeProp(e,"headers")),e.data=Qf.call(e,e.transformRequest),["post","put","patch"].indexOf(e.method)!==-1&&e.headers.setContentType("application/x-www-form-urlencoded",!1);const r=L1.getAdapter(e.adapter||cs.adapter,e),n=Date.now();return r(e).then(function(a){tc(e),a.duration=Mg(n),e.response=a;try{a.data=Qf.call(e,e.transformResponse,a)}finally{delete e.response}return a.headers=je.from(a.headers),a},function(a){if(!x1(a)&&(tc(e),a&&a.response)){a.response.duration=Mg(n),e.response=a.response;try{a.response.data=Qf.call(e,e.transformResponse,a.response)}finally{delete e.response}a.response.headers=je.from(a.response.headers)}return Promise.reject(a)})}const nf={};["object","boolean","number","function","string","symbol"].forEach((t,e)=>{nf[t]=function(n){return typeof n===t||"a"+(e<1?"n ":" ")+t}});const Pg={};nf.transitional=function(e,r,n){function i(a,o){return"[Axios v"+bd+"] Transitional option '"+a+"'"+o+(n?". "+n:"")}return(a,o,s)=>{if(e===!1)throw new V(i(o," has been removed"+(r?" in "+r:"")),V.ERR_DEPRECATED);return r&&!Pg[o]&&(Pg[o]=!0,console.warn(i(o," has been deprecated since v"+r+" and will be removed in the near future"))),e?e(a,o,s):!0}};nf.spelling=function(e){return(r,n)=>(console.warn(`${n} is likely a misspelling of ${e}`),!0)};function VD(t,e,r){if(typeof t!="object"||t===null)throw new V("options must be an object",V.ERR_BAD_OPTION_VALUE);const n=Object.keys(t);let i=n.length;for(;i-- >0;){const a=n[i],o=Object.prototype.hasOwnProperty.call(e,a)?e[a]:void 0;if(o){const s=t[a],l=s===void 0||o(s,a,t);if(l!==!0)throw new V("option "+a+" must be "+l,V.ERR_BAD_OPTION_VALUE);continue}if(r!==!0)throw new V("Unknown option "+a,V.ERR_BAD_OPTION)}}const El={assertOptions:VD,validators:nf},Je=El.validators,nc=t=>{if(t==null||E.isPlainObject(t)||t instanceof je)return!0;try{return E.isObject(t)&&E.getSafeProp(t,Symbol.toStringTag)==="AxiosHeaders"&&["toJSON","set","get","has","delete","clear","normalize","concat","toString",Symbol.iterator].every(e=>E.isFunction(E.getSafeProp(t,e)))}catch(e){return!1}};let Ci=class{constructor(e){this.defaults=e||{},this.interceptors={request:new hg,response:new hg}}request(e,r){return ur(this,null,function*(){try{return yield this._request(e,r)}catch(n){if(n instanceof Error)try{let i={};Error.captureStackTrace?Error.captureStackTrace(i):i=new Error;const a=i.stack;let o="";if(typeof a=="string"){const s=a.indexOf(`
`);o=s===-1?"":a.slice(s+1)}if(!n.stack)n.stack=o;else if(o){const s=o.indexOf(`
`),l=s===-1?-1:o.indexOf(`
`,s+1),u=l===-1?o:o.slice(l+1),f=String(n.stack),c=zA(n,f),v=c===-1?f:f.slice(0,c),h=c===-1?"":f.slice(c);v.endsWith(u)||(n.stack=v+`
`+o+h)}}catch(i){}throw n}})}_request(e,r){typeof e=="string"?(r=r||{},r.url=e):r=e||{},r=Oi(this.defaults,r);const{transitional:n,paramsSerializer:i,headers:a}=r;n!==void 0&&El.assertOptions(n,{silentJSONParsing:Je.transitional(Je.boolean),forcedJSONParsing:Je.transitional(Je.boolean),clarifyTimeoutError:Je.transitional(Je.boolean),legacyInterceptorReqResOrdering:Je.transitional(Je.boolean),advertiseZstdAcceptEncoding:Je.transitional(Je.boolean),validateStatusUndefinedResolves:Je.transitional(Je.boolean),encodeBasicAuthUsername:Je.transitional(Je.boolean)},!1),i!=null&&(E.isFunction(i)?r.paramsSerializer={serialize:i}:El.assertOptions(i,{encode:Je.function,serialize:Je.function},!0)),r.allowAbsoluteUrls!==void 0||(E.hasOwnProp(this.defaults,"allowAbsoluteUrls")&&this.defaults.allowAbsoluteUrls!==void 0?r.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls:r.allowAbsoluteUrls=!0),El.assertOptions(r,{baseUrl:Je.spelling("baseURL"),withXsrfToken:Je.spelling("withXSRFToken")},!0),r.method=(E.getSafeProp(r,"method")||E.getSafeProp(this.defaults,"method")||"get").toLowerCase();const o=E.getSafeProp(a,"common"),s=E.getSafeProp(a,r.method),l=a&&E.merge(nc(o)?o:void 0,nc(s)?s:void 0);a&&E.forEach(T1.concat("common"),m=>{nc(E.getSafeProp(a,m))&&delete a[m]}),r.headers=je.concat(l,a);const u=[];let f=!0;this.interceptors.request.forEach(function(g){if(typeof g.runWhen=="function"&&g.runWhen(r)===!1)return;f=f&&g.synchronous;const y=r.transitional||_d;y&&y.legacyInterceptorReqResOrdering?u.unshift(g.fulfilled,g.rejected):u.push(g.fulfilled,g.rejected)});const c=[];this.interceptors.response.forEach(function(g){c.push(g.fulfilled,g.rejected)});let v,h=0,d;if(!f){const m=[rc.bind(this),void 0];for(m.unshift(...u),m.push(...c),d=m.length,v=Promise.resolve(r);h<d;)v=v.then(m[h++],m[h++]);return v}d=u.length;let p=r;for(;h<d;){const m=u[h++],g=u[h++];try{p=m?m(p):p}catch(y){if(!g){v=Promise.reject(y);break}try{const _=g.call(this,y);E.isThenable(_)&&(v=Promise.resolve(_).then(()=>rc.call(this,p)))}catch(_){v=Promise.reject(_)}break}}if(!v)try{v=rc.call(this,p)}catch(m){v=Promise.reject(m)}for(h=0,d=c.length;h<d;)v=v.then(c[h++],c[h++]);return v}getUri(e){e=Oi(this.defaults,e);const r=E1(e.baseURL,e.url,e.allowAbsoluteUrls,e);return m1(r,e.params,e.paramsSerializer)}};E.forEach(["delete","get","head","options"],function(e){Ci.prototype[e]=function(r,n){return ur(this,null,function*(){return this.request(Oi(n||{},{method:e,url:r,data:n&&E.hasOwnProp(n,"data")?n.data:void 0}))})}});E.forEach(["post","put","patch","query"],function(e){function r(n){return function(a,o,s){return ur(this,null,function*(){return this.request(Oi(s||{},{method:e,headers:n?{"Content-Type":"multipart/form-data"}:{},url:a,data:o}))})}}Ci.prototype[e]=r(),e!=="query"&&(Ci.prototype[e+"Form"]=r(!0))});let GD=class O1{constructor(e){if(typeof e!="function")throw new TypeError("executor must be a function.");let r;this.promise=new Promise(function(a){r=a});const n=this;this.promise.then(i=>{if(!n._listeners)return;let a=n._listeners.length;for(;a-- >0;)n._listeners[a](i);n._listeners=null}),this.promise.then=i=>{let a;const o=new Promise(s=>{n.subscribe(s),a=s}).then(i);return o.cancel=function(){n.unsubscribe(a)},o},e(function(a,o,s){n.reason||(n.reason=new hs(a,o,s),r(n.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(e){if(this.reason){e(this.reason);return}this._listeners?this._listeners.push(e):this._listeners=[e]}unsubscribe(e){if(!this._listeners)return;const r=this._listeners.indexOf(e);r!==-1&&this._listeners.splice(r,1)}toAbortSignal(){const e=new AbortController,r=n=>{e.abort(n)};return this.subscribe(r),e.signal.unsubscribe=()=>this.unsubscribe(r),e.signal}static source(){let e;return{token:new O1(function(i){e=i}),cancel:e}}};function WD(t){return function(r){return t.apply(null,r)}}function YD(t){return E.isObject(t)&&t.isAxiosError===!0}const Rl={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,ContentTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,UnprocessableContent:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504 Line truncated
`),a=i.length;if(a===1)return Dm(i[0],e,r,n);for(var o=new le(0,0,0,0),s=0;s<i.length;s++){var l=Dm(i[s],e,r,n);s===0?o.copy(l):o.union(l)}return o}function Ba(t,e,r,n){return r==="right"?n?t+=e:t-=e:r==="center"&&(n?t+=e/2:t-=e/2),t}function Ei(t,e,r,n){return r==="middle"?n?t+=e/2:t-=e/2:r==="bottom"&&(n?t+=e:t-=e),t}function df(t){return Ir(t).stWideCharWidth}function ki(t,e){return typeof t=="string"?t.lastIndexOf("%")>=0?parseFloat(t)/100*e:parseFloat(t):t}function FS(t,e,r){var n=e.position||"inside",i=e.distance!=null?e.distance:5,a=r.height,o=r.width,s=a/2,l=r.x,u=r.y,f="left",c="top";if(n instanceof Array)l+=ki(n[0],r.width),u+=ki(n[1],r.height),f=null,c=null;else switch(n){case"left":l-=i,u+=s,f="right",c="middle";break;case"right":l+=i+o,u+=s,c="middle";break;case"top":l+=o/2,u-=i,f="center",c="bottom";break;case"bottom":l+=o/2,u+=a+i,f="center";break;case"inside":l+=o/2,u+=s,f="center",c="middle";break;case"insideLeft":l+=i,u+=s,c="middle";break;case"insideRight":l+=o-i,u+=s,f="right",c="middle";break;case"insideTop":l+=o/2,u+=i,f="center";break;case"insideBottom":l+=o/2,u+=a-i,f="center",c="bottom";break;case"insideTopLeft":l+=i,u+=i;break;case"insideTopRight":l+=o-i,u+=i,f="right";break;case"insideBottomLeft":l+=i,u+=a-i,c="bottom";break;case"insideBottomRight":l+=o-i,u+=a-i,f="right",c="bottom";break}return t=t||{},t.x=l,t.y=u,t.align=f,t.verticalAlign=c,t}var Ec="__zr_normal__",Rc=vf.concat(["ignore"]),GM=kr(vf,function(t,e){return t[e]=!0,t},{ignore:!1}),ra={},WM=new le(0,0,0,0),Hs=[],zl=0,pf=1,gf=(function(){function t(e){this.id=Md(),this.animators=[],this.currentStates=[],this.states={},this._init(e)}return t.prototype._init=function(e){this.attr(e)},t.prototype.drift=function(e,r,n){switch(this.draggable){case"horizontal":r=0;break;case"vertical":e=0;break}var i=this.transform;i||(i=this.transform=[1,0,0,1,0,0]),i[4]+=e,i[5]+=r,this.decomposeTransform(),this.markRedraw()},t.prototype.beforeUpdate=function(){},t.prototype.afterUpdate=function(){},t.prototype.update=function(){this.updateTransform(),this.__dirty&&this.updateInnerText()},t.prototype.updateInnerText=function(e){var r=this._textContent;if(r&&(!r.ignore||e)){this.textConfig||(this.textConfig={});var n=this.textConfig,i=n.local,a=r.innerTransformable,o=void 0,s=void 0,l=!1;a.parent=i?this:null;var u=!1;a.copyTransform(r);var f=n.position!=null,c=n.autoOverflowArea,v=void 0;if((c||f)&&(v=WM,n.layoutRect?v.copy(n.layoutRect):v.copy(this.getBoundingRect()),i||v.applyTransform(this.transform)),f){this.calculateTextPosition?this.calculateTextPosition(ra,n,v):FS(ra,n,v),a.x=ra.x,a.y=ra.y,o=ra.align,s=ra.verticalAlign;var h=n.origin;if(h&&n.rotation!=null){var d=void 0,p=void 0;h==="center"?(d=v.width*.5,p=v.height*.5):(d=ki(h[0],v.width),p=ki(h[1],v.height)),u=!0,a.originX=-a.x+d+(i?0:v.x),a.originY=-a.y+p+(i?0:v.y)}}n.rotation!=null&&(a.rotation=n.rotation);var m=n.offset;m&&(a.x+=m[0],a.y+=m[1],u||(a.originX=-m[0],a.originY=-m[1]));var g=this._innerTextDefaultStyle||(this._innerTextDefaultStyle={});if(c){var y=g.overflowRect=g.overflowRect||new le(0,0,0,0);a.getLocalTransform(Hs),Hn(Hs,Hs),le.copy(y,v),y.applyTransform(Hs)}else g.overflowRect=null;var _=n.inside==null?typeof n.position=="string"&&n.position.indexOf("inside")>=0:n.inside,S=void 0,b=void 0,w=void 0;_&&this.canBeInsideText()?(S=n.insideFill,b=n.insideStroke,(S==null||S==="auto")&&(S=this.getInsideTextFill()),(b==null||b==="auto")&&(b=this.getInsideTextStroke(S),w=!0)):(S=n.outsideFill,b=n.outsideStroke,(S==null||S==="auto")&&(S=this.getOutsideFill()),(b==null||b==="auto")&&(b=this.getOutsideStroke(S),w=!0)),S=S||"#000",(S!==g.fill||b!==g.stroke||w!==g.autoStroke||o!==g.align||s!==g.verticalAlign)&&(l=!0,g.fill=S,g.stroke=b,g.autoStroke=w,g.align=o,g.verticalAlign=s,r.setDefaultTextStyle(g)),r.__dirty|=Et,l&&r.dirtyStyle(!0)}},t.prototype.canBeInsideText=function(){return!0},t.prototype.getInsideTextFill=function(){return"#fff"},t.prototype.getInsideTextStroke=function(e){return"#000"},t.prototype.getOutsideFill=function(){return this.__zr&&this.__zr.isDarkMode()?hv:cv},t.prototype.getOutsideStroke=function(e){var r=this.__zr&&this.__zr.getBackgroundColor(),n=typeof r=="string"&&Ft(r);n||(n=[255,255,255,1]);for(var i=n[3],a=this.__zr.isDarkMode(),o=0;o<3;o++)n[o]=n[o]*i+(a?0:255)*(1-i);return n[3]=1,Un(n,"rgba")},t.prototype.traverse=function(e,r){},t.prototype.attrKV=function(e,r){e==="textConfig"?this.setTextConfig(r):e==="textContent"?this.setTextContent(r):e==="clipPath"?this.setClipPath(r):e==="extra"?(this.extra=this.extra||{},G(this.extra,r)):this[e]=r},t.prototype.hide=function(){this.ignore=!0,this.markRedraw()},t.prototype.show=function(){this.ignore=!1,this.markRedraw()},t.prototype.attr=function(e,r){if(typeof e=="string")this.attrKV(e,r);else if(ie(e))for(var n=e,i=Pe(n),a=0;a<i.length;a++){var o=i[a];this.attrKV(o,e[o])}return this.markRedraw(),this},t.prototype.saveCurrentToNormalState=function(e){this._innerSaveToNormal(e);for(var r=this._normalState, Line truncated
`);a=ab(r,n,i,a);for(var s=!1,l={},u=0,f=o.length;u<f;u++)ob(l,o[u],a),o[u]=l.textLine,s=s||l.isTruncated;t.text=o.join(`
`),t.isTruncated=s}function ab(t,e,r,n){n=n||{};var i=G({},n);r=ne(r,"..."),i.maxIterations=ne(n.maxIterations,2);var a=i.minChar=ne(n.minChar,0),o=i.fontMeasureInfo=Ir(e),s=o.asciiCharWidth;i.placeholder=ne(n.placeholder,"");for(var l=t=Math.max(0,t-1),u=0;u<a&&l>=s;u++)l-=s;var f=Lr(o,r);return f>l&&(r="",f=0),l=t-f,i.ellipsis=r,i.ellipsisWidth=f,i.contentWidth=l,i.containerWidth=t,i}function ob(t,e,r){var n=r.containerWidth,i=r.contentWidth,a=r.fontMeasureInfo;if(!n){t.textLine="",t.isTruncated=!1;return}var o=Lr(a,e);if(o<=n){t.textLine=e,t.isTruncated=!1;return}for(var s=0;;s++){if(o<=i||s>=r.maxIterations){e+=r.ellipsis;break}var l=s===0?tI(e,i,a):o>0?Math.floor(e.length*i/o):0;e=e.substr(0,l),o=Lr(a,e)}e===""&&(e=r.placeholder),t.textLine=e,t.isTruncated=!0}function tI(t,e,r){for(var n=0,i=0,a=t.length;i<a&&n<e;i++)n+=NS(r,t.charCodeAt(i));return i}function rI(t,e,r,n){var i=$d(t),a=e.overflow,o=e.padding,s=o?o[1]+o[3]:0,l=o?o[0]+o[2]:0,u=e.font,f=a==="truncate",c=df(u),v=ne(e.lineHeight,c),h=e.lineOverflow==="truncate",d=!1,p=e.width;p==null&&r!=null&&(p=r-s);var m=e.height;m==null&&n!=null&&(m=n-l);var g;p!=null&&(a==="break"||a==="breakAll")?g=i?sb(i,e.font,p,a==="breakAll",0).lines:[]:g=i?i.split(`
`):[];var y=g.length*v;if(m==null&&(m=y),y>m&&h){var _=Math.floor(m/v);d=d||g.length>_,g=g.slice(0,_),y=g.length*v}if(i&&f&&p!=null)for(var S=ab(p,u,e.ellipsis,{minChar:e.truncateMinChar,placeholder:e.placeholder}),b={},w=0;w<g.length;w++)ob(b,g[w],S),g[w]=b.textLine,d=d||b.isTruncated;for(var T=m,C=0,x=Ir(u),w=0;w<g.length;w++)C=Math.max(Lr(x,g[w]),C);p==null&&(p=C);var A=p;return T+=l,A+=s,{lines:g,height:m,outerWidth:A,outerHeight:T,lineHeight:v,calculatedLineHeight:c,contentWidth:C,contentHeight:y,width:p,isTruncated:d}}var nI=(function(){function t(){}return t})(),Nm=(function(){function t(e){this.tokens=[],e&&(this.tokens=e)}return t})(),iI=(function(){function t(){this.width=0,this.height=0,this.contentWidth=0,this.contentHeight=0,this.outerWidth=0,this.outerHeight=0,this.lines=[],this.isTruncated=!1}return t})();function aI(t,e,r,n,i){var a=new iI,o=$d(t);if(!o)return a;var s=e.padding,l=s?s[1]+s[3]:0,u=s?s[0]+s[2]:0,f=e.width;f==null&&r!=null&&(f=r-l);var c=e.height;c==null&&n!=null&&(c=n-u);for(var v=e.overflow,h=(v==="break"||v==="breakAll")&&f!=null?{width:f,accumWidth:0,breakAll:v==="breakAll"}:null,d=Lc.lastIndex=0,p;(p=Lc.exec(o))!=null;){var m=p.index;m>d&&Oc(a,o.substring(d,m),e,h),Oc(a,p[2],e,h,p[1]),d=Lc.lastIndex}d<o.length&&Oc(a,o.substring(d,o.length),e,h);var g=[],y=0,_=0,S=v==="truncate",b=e.lineOverflow==="truncate",w={};function T(se,L,Y){se.width=L,se.lineHeight=Y,y+=Y,_=Math.max(_,L)}e:for(var C=0;C<a.lines.length;C++){for(var x=a.lines[C],A=0,D=0,P=0;P<x.tokens.length;P++){var M=x.tokens[P],I=M.styleName&&e.rich[M.styleName]||{},k=M.textPadding=I.padding,O=k?k[1]+k[3]:0,N=M.font=I.font||e.font;M.contentHeight=df(N);var U=ne(I.height,M.contentHeight);if(M.innerHeight=U,k&&(U+=k[0]+k[2]),M.height=U,M.lineHeight=Mn(I.lineHeight,e.lineHeight,U),M.align=I&&I.align||i,M.verticalAlign=I&&I.verticalAlign||"middle",b&&c!=null&&y+M.lineHeight>c){var W=a.lines.length;P>0?(x.tokens=x.tokens.slice(0,P),T(x,D,A),a.lines=a.lines.slice(0,C+1)):a.lines=a.lines.slice(0,C),a.isTruncated=a.isTruncated||a.lines.length<W;break e}var X=I.width,Q=X==null||X==="auto";if(typeof X=="string"&&X.charAt(X.length-1)==="%")M.percentWidth=X,g.push(M),M.contentWidth=Lr(Ir(N),M.text);else{if(Q){var J=I.backgroundColor,j=J&&J.image;j&&(j=JP(j),Sf(j)&&(M.width=Math.max(M.width,j.width*U/j.height)))}var z=S&&f!=null?f-D:null;z!=null&&z<M.width?!Q||z<O?(M.text="",M.width=M.contentWidth=0):(ib(w,M.text,z-O,N,e.ellipsis,{minChar:e.truncateMinChar}),M.text=w.text,a.isTruncated=a.isTruncated||w.isTruncated,M.width=M.contentWidth=Lr(Ir(N),M.text)):M.contentWidth=Lr(Ir(N),M.text)}M.width+=O,D+=M.width,I&&(A=Math.max(A,M.lineHeight))}T(x,D,A)}a.outerWidth=a.width=ne(f,_),a.outerHeight=a.height=ne(c,y),a.contentHeight=y,a.contentWidth=_,a.outerWidth+=l,a.outerHeight+=u;for(var C=0;C<g.length;C++){var M=g[C],re=M.percentWidth;M.width=parseInt(re,10)/100*a.width}return a}function Oc(t,e,r,n,i){var a=e==="",o=i&&r.rich[i]||{},s=t.lines,l=o.font||r.font,u=!1,f,c;if(n){var v=o.padding,h=v?v[1]+v[3]:0;if(o.width!=null&&o.width!=="auto"){var d=ki(o.width,n.width)+h;s.length>0&&d+n.accumWidth>n.width&&(f=e.split(`
`),u=!0),n.accumWidth=d}else{var p=sb(e,l,n.width,n.breakAll,n.accumWidth);n.accumWidth=p.accumWidth+h,c=p.linesWidths,f=p.lines}}f||(f=e.split(`
`));for(var m=Ir(l),g=0;g<f.length;g++){var y=f[g],_=new nI;if(_.styleName=i,_.text=y,_.isLineHolder=!y&&!a,typeof o.width=="number"?_.width=o.width:_.width=c?c[g]:Lr(m,y),!g&&!u){var S=(s[s.length-1]||(s[0]=new Nm)).tokens,b=S.length;b===1&&S[0].isLineHolder?S[0]=_:(y||!b||a)&&S.push(_)}else s.push(new Nm([_]))}}function oI(t){var e=t.charCodeAt(0);return e>=32&&e<=591||e>=880&&e<=4351||e>=4608&&e<=5119||e>=7680&&e<=8303}var sI=kr(",&?/;] ".split(""),function(t,e){return t[e]=!0,t},{});function lI(t){return oI(t)?!!sI[t]:!0}function sb(t,e,r,n,i){for(var a=[],o=[],s="",l="",u=0,f=0,c=Ir(e),v=0;v<t.length;v++){var h=t.charAt(v);if(h===`
`){l&&(s+=l,f+=u),a.push(s),o.push(f),s="",l="",u=0,f=0;continue}var d=NS(c,h.charCodeAt(0)),p=n?!1:!lI(h);if(a.length?f+d>r:i+f+d>r){f?(s||l)&&(p?(s||(s=l,l="",u=0,f=u),a.push(s),o.push(f-u),l+=h,u+=d,s="",f=u):(l&&(s+=l,l="",u=0),a.push(s),o.push(f),s=h,f=d)):p?(a.push(l),o.push(u),l=h,u=d):(a.push(h),o.push(d));continue}f+=d,p?(l+=h,u+=d):(l&&(s+=l,l="",u=0),s+=h)}return l&&(s+=l),s&&(a.push(s),o.push(f)),a.length===1&&(f+=i),{accumWidth:f,lines:a,linesWidths:o}}function Bm(t,e,r,n,i,a){if(t.baseX=r,t.baseY=n,t.outerWidth=t.outerHeight=null,!!e){var o=e.width*2,s=e.height*2;le.set(Fm,Ba(r,o,i),Ei(n,s,a),o,s),le.intersect(e,Fm,null,zm);var l=zm.outIntersectRect;t.outerWidth=l.width,t.outerHeight=l.height,t.baseX=Ba(l.x,l.width,i,!0),t.baseY=Ei(l.y,l.height,a,!0)}}var Fm=new le(0,0,0,0),zm={outIntersectRect:{},clamp:!0};function $d(t){return t!=null?t+="":t=""}function uI(t){var e=$d(t.text),r=t.font,n=Lr(Ir(r),e),i=df(r);return Sv(t,n,i,null)}function Sv(t,e,r,n){var i=new le(Ba(t.x||0,e,t.textAlign),Ei(t.y||0,r,t.textBaseline),e,r),a=n!=null?n:lb(t)?t.lineWidth:0;return a>0&&(i.x-=a/2,i.y-=a/2,i.width+=a,i.height+=a),i}function lb(t){var e=t.stroke;return e!=null&&e!=="none"&&t.lineWidth>0}var bv="__zr_style_"+Math.round(Math.random()*10),Ri={shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,shadowColor:"#000",opacity:1,blend:"source-over"},bf={style:{shadowBlur:!0,shadowOffsetX:!0,shadowOffsetY:!0,shadowColor:!0,opacity:!0}};Ri[bv]=!0;var Hm=["z","z2","invisible"],fI=["invisible"],Ss=(function(t){q(e,t);function e(r){return t.call(this,r)||this}return e.prototype._init=function(r){for(var n=Pe(r),i=0;i<n.length;i++){var a=n[i];a==="style"?this.useStyle(r[a]):t.prototype.attrKV.call(this,a,r[a])}this.style||this.useStyle({})},e.prototype.beforeBrush=function(r){},e.prototype.afterBrush=function(){},e.prototype.innerBeforeBrush=function(){},e.prototype.innerAfterBrush=function(){},e.prototype.shouldBePainted=function(r,n,i,a){var o=this.transform;if(this.ignore||this.invisible||this.style.opacity===0||this.culling&&cI(this,r,n)||o&&!o[0]&&!o[3])return!1;if(i&&this.__clipPaths&&this.__clipPaths.length){for(var s=0;s<this.__clipPaths.length;++s)if(this.__clipPaths[s].isZeroArea())return!1}if(a&&this.parent)for(var l=this.parent;l;){if(l.ignore)return!1;l=l.parent}return!0},e.prototype.contain=function(r,n){return this.rectContain(r,n)},e.prototype.traverse=function(r,n){r.call(n,this)},e.prototype.rectContain=function(r,n){var i=this.transformCoordToLocal(r,n),a=this.getBoundingRect();return a.contain(i[0],i[1])},e.prototype.getPaintRect=function(){var r=this._paintRect;if(!this._paintRect||this.__dirty){var n=this.transform,i=this.getBoundingRect(),a=this.style,o=a.shadowBlur||0,s=a.shadowOffsetX||0,l=a.shadowOffsetY||0;r=this._paintRect||(this._paintRect=new le(0,0,0,0)),n?le.applyTransform(r,i,n):r.copy(i),(o||s||l)&&(r.width+=o*2+Math.abs(s),r.height+=o*2+Math.abs(l),r.x=Math.min(r.x,r.x+s-o),r.y=Math.min(r.y,r.y+l-o));var u=this.dirtyRectTolerance;r.isZero()||(r.x=Math.floor(r.x-u),r.y=Math.floor(r.y-u),r.width=Math.ceil(r.width+1+u*2),r.height=Math.ceil(r.height+1+u*2))}return r},e.prototype.setPrevPaintRect=function(r){r?(this._prevPaintRect=this._prevPaintRect||new le(0,0,0,0),this._prevPaintRect.copy(r)):this._prevPaintRect=null},e.prototype.getPrevPaintRect=function(){return this._prevPaintRect},e.prototype.animateStyle=function(r){return this.animate("style",r)},e.prototype.updateDuringAnimation=function(r){r==="style"?this.dirtyStyle():this.markRedraw()},e.prototype.attrKV=function(r,n){r!=="style"?t.prototype.attrKV.call(this,r,n):this.style?this.setStyle(n):this.useStyle(n)},e.prototype.setStyle=function(r,n){return typeof r=="string"?this.style[r]=n:G(this.style,r),this.dirtyStyle(),this},e.prototype.dirtyStyle=function(r){r||this.markRedraw(),this.__dirty|=To,this._rect&&(this._rect=null)},e.prototype.dirty=function(){this.dirtyStyle()},e.prototype.styleChanged=function(){return!!(this.__dirty&To)},e.prototype.styleUpdated=function(){this.__dirty&=~To},e.prototype.createStyle=function(r){return ps(Ri,r)},e.prototype.useStyle=function(r){r[bv]||(r=this.createStyle(r)),this.style=r,this.dirtyStyle()},e.prototype._useHoverStyle=function(r){this.__hoverStyle=r},e.prototype.isStyleObject=function(r){return r[bv]},e.prototype._innerSaveToNormal=function(r){t.prototype._innerSaveToNormal.call(this,r);var n=this._normalState;r.style&&!n.style&&(n.style=this._mergeStyle(this.createStyle(),this.style)),this._savePrimaryToNormal(r,n,Hm)},e.prototype._applyStateObj=function(r,n,i,a,o,s){t.prototype._applyStateObj.call(this,r,n,i,a,o,s);var l=!(n&&a),u=this.__inHover===pf,f;if(n&&n.style?o?a?f=n.style:(f=this._mergeStyle(this.createStyle(),i.style),this._mergeStyle(f,n.style)):(f=this._mergeStyle(this.createStyle(),a?this.style:i.style),this._mergeStyle(f,n.style)):l&&(f=i.style),f)if(o){var c=this.style;if(this.style=this.createStyle(l?{}:c),l)for(var v=Pe(c),h=0;h<v.length;h++){var d=v[h];d in f&&(f[d]=f[d Line truncated
yyyy`);var n=ln(e),i=r?"getUTC":"get",a=n[i+"FullYear"](),o=n[i+"Month"]()+1,s=n[i+"Date"](),l=n[i+"Hours"](),u=n[i+"Minutes"](),f=n[i+"Seconds"](),c=n[i+"Milliseconds"]();return t=t.replace("MM",gt(o,2)).replace("M",o).replace("yyyy",a).replace("yy",gt(a%100+"",2)).replace("dd",gt(s,2)).replace("d",s).replace("hh",gt(l,2)).replace("h",l).replace("mm",gt(u,2)).replace("m",u).replace("ss",gt(f,2)).replace("s",f).replace("SSS",gt(c,3)),t}function R2(t){return t&&t.charAt(0).toUpperCase()+t.substr(1)}function Fi(t,e){return e=e||"transparent",te(t)?t:ie(t)&&t.colorStops&&(t.colorStops[0]||{}).color||e}function A0(t,e){if(e==="_blank"||e==="blank"){var r=window.open();r.opener=null,r.location.href=t}else window.open(t,e)}var Yl={},Jc={},Bf=(function(){function t(){this._normalMasterList=[],this._nonSeriesBoxMasterList=[]}return t.prototype.create=function(e,r){this._nonSeriesBoxMasterList=n(Yl),this._normalMasterList=n(Jc);function n(i,a){var o=[];return R(i,function(s,l){var u=s.create(e,r);o=o.concat(u||[])}),o}},t.prototype.update=function(e,r){R(this._normalMasterList,function(n){n.update&&n.update(e,r)})},t.prototype.getCoordinateSystems=function(){return this._normalMasterList.concat(this._nonSeriesBoxMasterList)},t.register=function(e,r){if(e==="matrix"||e==="calendar"){Yl[e]=r;return}Jc[e]=r},t.get=function(e){return Jc[e]||Yl[e]},t})();function M2(t){return!!Yl[t]}var P2=1,pw=2;function I2(t){gw.set(t.fullType,{getCoord2:void 0}).getCoord2=t.getCoord2}var gw=ce();function mw(t){var e=t.getShallow("coord",!0),r=P2;if(e==null){var n=gw.get(t.type);n&&n.getCoord2&&(r=pw,e=n.getCoord2(t))}return{coord:e,from:r}}var Ea=0,$l=1,L2=2;function O2(t,e){var r=t.getShallow("coordinateSystem"),n=t.getShallow("coordinateSystemUsage",!0),i=Ea;if(r){var a=t.mainType==="series";n==null&&(n=a?"data":"box"),n==="data"?(i=$l,a||(i=Ea)):n==="box"&&(i=L2,!a&&!M2(r)&&(i=Ea))}return{coordSysType:r,kind:i}}function k2(t){var e=t.targetModel,r=t.coordSysType,n=t.coordSysProvider,i=t.isDefaultDataCoordSys,a=O2(e),o=a.kind,s=a.coordSysType;if(i&&o!==$l&&(o=$l,s=r),o===Ea||s!==r)return Ea;var l=n(r,e);return l?(o===$l?e.coordinateSystem=l:e.boxCoordinateSystem=l,o):Ea}var Xl=R,N2=["left","right","top","bottom","width","height"],Qs=[["width","left","right"],["height","top","bottom"]];function bp(t,e,r,n,i){var a=0,o=0;n==null&&(n=1/0),i==null&&(i=1/0);var s=0;e.eachChild(function(l,u){var f=l.getBoundingRect(),c=e.childAt(u+1),v=c&&c.getBoundingRect(),h,d;if(t==="horizontal"){var p=f.width+(v?-v.x+f.x:0);h=a+p,h>n||l.newline?(a=0,h=p,o+=s+r,s=f.height):s=Math.max(s,f.height)}else{var m=f.height+(v?-v.y+f.y:0);d=o+m,d>i||l.newline?(a+=s+r,o=0,d=m,s=f.width):s=Math.max(s,f.width)}l.newline||(l.x=a,l.y=o,l.markRedraw(),t==="horizontal"?a=h+r:o=d+r)})}var zo=bp;Ue(bp,"vertical");Ue(bp,"horizontal");function B2(t,e){return{left:t.getShallow("left",e),top:t.getShallow("top",e),right:t.getShallow("right",e),bottom:t.getShallow("bottom",e),width:t.getShallow("width",e),height:t.getShallow("height",e)}}function F2(t,e){var r=Ff(t,e,{enableLayoutOnlyByCenter:!0}),n=t.getBoxLayoutParams(),i,a;if(r.type===Do.point)a=r.refPoint,i=sn(n,{width:e.getWidth(),height:e.getHeight()});else{var o=t.get("center"),s=K(o)?o:[o,o];i=sn(n,r.refContainer),a=r.boxCoordFrom===pw?r.refPoint:[He(s[0],i.width)+i.x,He(s[1],i.height)+i.y]}return{viewRect:i,center:a}}function z2(t,e){var r=F2(t,e),n=r.viewRect,i=r.center,a=t.get("radius");K(a)||(a=[0,a]);var o=He(n.width,e.getWidth()),s=He(n.height,e.getHeight()),l=Math.min(o,s),u=He(a[0],l/2),f=He(a[1],l/2);return{cx:i[0],cy:i[1],r0:u,r:f,viewRect:n}}function sn(t,e,r){r=Cs(r||0);var n=e.width,i=e.height,a=He(t.left,n),o=He(t.top,i),s=He(t.right,n),l=He(t.bottom,i),u=He(t.width,n),f=He(t.height,i),c=r[2]+r[0],v=r[1]+r[3],h=t.aspect;switch(isNaN(u)&&(u=n-s-v-a),isNaN(f)&&(f=i-l-c-o),h!=null&&(isNaN(u)&&isNaN(f)&&(h>n/i?u=n*.8:f=i*.8),isNaN(u)&&(u=h*f),isNaN(f)&&(f=u/h)),isNaN(a)&&(a=n-s-u-v),isNaN(o)&&(o=i-l-f-c),t.left||t.right){case"center":a=n/2-u/2-r[3];break;case"right":a=n-u-v;break}switch(t.top||t.bottom){case"middle":case"center":o=i/2-f/2-r[0];break;case"bottom":o=i-f-c;break}a=a||0,o=o||0,isNaN(u)&&(u=n-v-a-(s||0)),isNaN(f)&&(f=i-c-o-(l||0));var d=new le((e.x||0)+a+r[3],(e.y||0)+o+r[0],u,f);return d.margin=r,d}var Do={rect:1,point:2};function Ff(t,e,r){var n,i,a,o=t.boxCoordinateSystem,s;if(o){var l=mw(t),u=l.coord,f=l.from;if(o.dataToLayout){a=Do.rect,s=f;var c=o.dataToLayout(u);n=c.contentRect||c.rect}else r&&r.enableLayoutOnlyByCenter&&o.dataToPoint&&(a=Do.point,s=f,i=o.dataToPoint(u))}return a==null&&(a=Do.rect),a===Do.rect&&(n||(n={x:0,y:0,width:e.getWidth(),height:e.getHeight()}),i=[n.x+n.width/2,n.y+n.height/2]),{type:a,refContainer:n,refPoint:i,boxCoordFrom:s}}function ts(t){var e=t.layoutMode||t.constructor.layoutMode;return ie(e)?e:e?{type:e}:null}function zn(t,e,r){var n=r&&r.ignoreSize;!K(n)&&(n=[n,n]);var i=o(Qs[0],0),a=o(Qs[1],1);l(Qs[0],t,i),l(Qs[1],t,a);function o(u,f){var c={},v=0,h={},d=0 Line truncated
`,`
`,`
`];function os(e,t){return t.type=e,t}function Kh(e){return e.type==="section"}function Jb(e){return Kh(e)?vO:dO}function tS(e){if(Kh(e)){var t=0,r=e.blocks.length,n=r>1||r>0&&!e.noHeader;return I(e.blocks,function(i){var a=tS(i);a>=t&&(t=a+ +(n&&(!a||Kh(i)&&!i.noHeader)))}),t}return 0}function vO(e,t,r,n){var i=t.noHeader,a=pO(tS(t)),o=[],s=t.blocks||[];Wr(!s||et(s)),s=s||[];var l=e.orderMode;if(t.sortBlocks&&l){s=s.slice();var u={valueAsc:"asc",valueDesc:"desc"};if(Ca(u,l)){var f=new ZR(u[l],null);s.sort(function(d,p){return f.evaluate(d.sortParam,p.sortParam)})}else l==="seriesDesc"&&s.reverse()}I(s,function(d,p){var g=t.valueFormatter,m=Jb(d)(g?Y(Y({},e),{valueFormatter:g}):e,d,p>0?a.html:0,n);m!=null&&o.push(m)});var c=e.renderMode==="richText"?o.join(a.richText):Zh(o.join(""),i?r:a.html);if(i)return c;var v=qh(t.header,"ordinal",e.useUTC),h=Qb(n,e.renderMode).nameStyle;return e.renderMode==="richText"?eS(e,v,h)+a.richText+c:Zh('<div style="'+h+";"+Zb+';">'+Ke(v)+"</div>"+c,r)}function dO(e,t,r,n){var i=e.renderMode,a=t.noName,o=t.noValue,s=!t.markerType,l=t.name,u=e.useUTC,f=t.valueFormatter||e.valueFormatter||function(w){return w=et(w)?w:[w],ct(w,function(S,b){return qh(S,et(h)?h[b]:h,u)})};if(!(a&&o)){var c=s?"":e.markupStyleCreator.makeTooltipMarker(t.markerType,t.markerColor||"#333",i),v=a?"":qh(l,"ordinal",u),h=t.valueType,d=o?[]:f(t.value),p=!s||!a,g=!s&&a,m=Qb(n,i),y=m.nameStyle,_=m.valueStyle;return i==="richText"?(s?"":c)+(a?"":eS(e,v,y))+(o?"":yO(e,d,p,g,_)):Zh((s?"":c)+(a?"":gO(v,!s,y))+(o?"":mO(d,p,g,_)),r)}}function yy(e,t,r,n,i,a){if(!!e){var o=Jb(e),s={useUTC:i,renderMode:r,orderMode:n,markupStyleCreator:t,valueFormatter:e.valueFormatter};return o(s,e,0,a)}}function pO(e){return{html:cO[e],richText:hO[e]}}function Zh(e,t){var r='<div style="clear:both"></div>',n="margin: "+t+"px 0 0";return'<div style="'+n+";"+Zb+';">'+e+r+"</div>"}function gO(e,t,r){var n=t?"margin-left:2px":"";return'<span style="'+r+";"+n+'">'+Ke(e)+"</span>"}function mO(e,t,r,n){var i=r?"10px":"20px",a=t?"float:right;margin-left:"+i:"";return e=et(e)?e:[e],'<span style="'+a+";"+n+'">'+ct(e,function(o){return Ke(o)}).join("&nbsp;&nbsp;")+"</span>"}function eS(e,t,r){return e.markupStyleCreator.wrapRichTextStyle(t,r)}function yO(e,t,r,n,i){var a=[i],o=n?10:20;return r&&a.push({padding:[0,0,0,o],align:"right"}),e.markupStyleCreator.wrapRichTextStyle(et(t)?t.join(" "):t,a)}function _O(e,t){var r=e.getData().getItemVisual(t,"style"),n=r[e.visualDrawType];return Ti(n)}function rS(e,t){var r=e.get("padding");return r!=null?r:t==="richText"?[8,10]:10}var mc=function(){function e(){this.richTextStyles={},this._nextStyleNameId=vw()}return e.prototype._generateStyleName=function(){return"__EC_aUTo_"+this._nextStyleNameId++},e.prototype.makeTooltipMarker=function(t,r,n){var i=n==="richText"?this._generateStyleName():null,a=aR({color:r,type:t,renderMode:n,markerId:i});return it(a)?a:(this.richTextStyles[i]=a.style,a.content)},e.prototype.wrapRichTextStyle=function(t,r){var n={};et(r)?I(r,function(a){return Y(n,a)}):Y(n,r);var i=this._generateStyleName();return this.richTextStyles[i]=n,"{"+i+"|"+t+"}"},e}();function wO(e){var t=e.series,r=e.dataIndex,n=e.multipleSeries,i=t.getData(),a=i.mapDimensionsAll("defaultedTooltip"),o=a.length,s=t.getRawValue(r),l=et(s),u=_O(t,r),f,c,v,h;if(o>1||l&&!o){var d=bO(s,t,r,a,u);f=d.inlineValues,c=d.inlineValueTypes,v=d.blocks,h=d.inlineValues[0]}else if(o){var p=i.getDimensionInfo(a[0]);h=f=Da(i,r,a[0]),c=p.type}else h=f=l?s[0]:s;var g=wd(t),m=g&&t.name||"",y=i.getName(r),_=n?m:y;return os("section",{header:m,noHeader:n||!g,sortParam:h,blocks:[os("nameValue",{markerType:"item",markerColor:u,name:_,noName:!Tr(_),value:f,valueType:c})].concat(v||[])})}function bO(e,t,r,n,i){var a=t.getData(),o=Ln(e,function(c,v,h){var d=a.getDimensionInfo(h);return c=c||d&&d.tooltip!==!1&&d.displayName!=null},!1),s=[],l=[],u=[];n.length?I(n,function(c){f(Da(a,r,c),c)}):I(e,f);function f(c,v){var h=a.getDimensionInfo(v);!h||h.otherDims.tooltip===!1||(o?u.push(os("nameValue",{markerType:"subItem",markerColor:i,name:h.displayName,value:c,valueType:h.type})):(s.push(c),l.push(h.type)))}return{inlineValues:s,inlineValueTypes:l,blocks:u}}var an=Qt();function tl(e,t){return e.getName(t)||e.getId(t)}var SO="__universalTransitionEnabled",sf=function(e){J(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r._selectedDataIndicesMap={},r}return t.prototype.init=function(r,n,i){this.seriesIndex=this.componentIndex,this.dataTask=Bo({count:TO,reset:CO}),this.dataTask.context={model:this},this.mergeDefaultAndTheme(r,i);var a=an(this).sourceManager=new fO(this);a.prepareSource();var o=this.getInitialData(r,i);wy(o,this),this.dataTask.context.data=o,an(this).dataBeforeProcessed=o,_y(this),this._initSelectedMapFromData(o)},t.prototype.mergeDefaultAndTheme=function(r,n){var i=is(this),a=i?nf(r):{},o=this.subType;$t.hasClass(o)&&(o+="Series"),Dt(r,n.getTheme().get(this.subType)),Dt(r,this.getDefaultOption()),Oh(r Line truncated
`];function ns(t,e){return e.type=t,e}function Nv(t){return t.type==="section"}function zw(t){return Nv(t)?YO:$O}function Hw(t){if(Nv(t)){var e=0,r=t.blocks.length,n=r>1||r>0&&!t.noHeader;return R(t.blocks,function(i){var a=Hw(i);a>=e&&(e=a+ +(n&&(!a||Nv(i)&&!i.noHeader)))}),e}return 0}function YO(t,e,r,n){var i=e.noHeader,a=XO(Hw(e)),o=[],s=e.blocks||[];Nr(!s||K(s)),s=s||[];var l=t.orderMode;if(e.sortBlocks&&l){s=s.slice();var u={valueAsc:"asc",valueDesc:"desc"};if(Mt(u,l)){var f=new DO(u[l],null);s.sort(function(p,m){return f.evaluate(p.sortParam,m.sortParam)})}else l==="seriesDesc"&&s.reverse()}R(s,function(p,m){var g=e.valueFormatter,y=zw(p)(g?G(G({},t),{valueFormatter:g}):t,p,m>0?a.html:0,n);y!=null&&o.push(y)});var c=t.renderMode==="richText"?o.join(a.richText):Bv(n,o.join(""),i?r:a.html);if(i)return c;var v=Lv(e.header,"ordinal",t.useUTC),h=Fw(n,t.renderMode).nameStyle,d=Bw(n);return t.renderMode==="richText"?Uw(t,v,h)+a.richText+c:Bv(n,'<div style="'+h+";"+d+';">'+yt(v)+"</div>"+c,r)}function $O(t,e,r,n){var i=t.renderMode,a=e.noName,o=e.noValue,s=!e.markerType,l=e.name,u=t.useUTC,f=e.valueFormatter||t.valueFormatter||function(S){return S=K(S)?S:[S],oe(S,function(b,w){return Lv(b,K(h)?h[w]:h,u)})};if(!(a&&o)){var c=s?"":t.markupStyleCreator.makeTooltipMarker(e.markerType,e.markerColor||ae.color.secondary,i),v=a?"":Lv(l,"ordinal",u),h=e.valueType,d=o?[]:f(e.value,e.rawDataIndex),p=!s||!a,m=!s&&a,g=Fw(n,i),y=g.nameStyle,_=g.valueStyle;return i==="richText"?(s?"":c)+(a?"":Uw(t,v,y))+(o?"":ZO(t,d,p,m,_)):Bv(n,(s?"":c)+(a?"":qO(v,!s,y))+(o?"":jO(d,p,m,_)),r)}}function K0(t,e,r,n,i,a){if(t){var o=zw(t),s={useUTC:i,renderMode:r,orderMode:n,markupStyleCreator:e,valueFormatter:t.valueFormatter};return o(s,t,0,a)}}function XO(t){return{html:GO[t],richText:WO[t]}}function Bv(t,e,r){var n='<div style="clear:both"></div>',i="margin: "+r+"px 0 0",a=Bw(t);return'<div style="'+i+";"+a+';">'+e+n+"</div>"}function qO(t,e,r){var n=e?"margin-left:2px":"";return'<span style="'+r+";"+n+'">'+yt(t)+"</span>"}function jO(t,e,r,n){var i=r?"10px":"20px",a=e?"float:right;margin-left:"+i:"";return t=K(t)?t:[t],'<span style="'+a+";"+n+'">'+oe(t,function(o){return yt(o)}).join("&nbsp;&nbsp;")+"</span>"}function Uw(t,e,r){return t.markupStyleCreator.wrapRichTextStyle(e,r)}function ZO(t,e,r,n,i){var a=[i],o=n?10:20;return r&&a.push({padding:[0,0,0,o],align:"right"}),t.markupStyleCreator.wrapRichTextStyle(K(e)?e.join(" "):e,a)}function KO(t,e){var r=t.getData().getItemVisual(e,"style"),n=r[t.visualDrawType];return Fi(n)}function Vw(t,e){var r=t.get("padding");return r!=null?r:e==="richText"?[8,10]:10}var ih=(function(){function t(){this.richTextStyles={},this._nextStyleNameId=$S()}return t.prototype._generateStyleName=function(){return"__EC_aUTo_"+this._nextStyleNameId++},t.prototype.makeTooltipMarker=function(e,r,n){var i=n==="richText"?this._generateStyleName():null,a=dw({color:r,type:e,renderMode:n,markerId:i});return te(a)?a:(this.richTextStyles[i]=a.style,a.content)},t.prototype.wrapRichTextStyle=function(e,r){var n={};K(r)?R(r,function(a){return G(n,a)}):G(n,r);var i=this._generateStyleName();return this.richTextStyles[i]=n,"{"+i+"|"+e+"}"},t})();function QO(t){var e=t.series,r=t.dataIndex,n=t.multipleSeries,i=e.getData(),a=i.mapDimensionsAll("defaultedTooltip"),o=a.length,s=e.getRawValue(r),l=K(s),u=KO(e,r),f,c,v,h;if(o>1||l&&!o){var d=JO(s,e,r,a,u);f=d.inlineValues,c=d.inlineValueTypes,v=d.blocks,h=d.inlineValues[0]}else if(o){var p=i.getDimensionInfo(a[0]);h=f=Ha(i,r,a[0]),c=p.type}else h=f=l?s[0]:s;var m=Ud(e),g=m&&e.name||"",y=i.getName(r),_=n?g:y;return ns("section",{header:g,noHeader:n||!m,sortParam:h,blocks:[ns("nameValue",{markerType:"item",markerColor:u,name:_,noName:!dr(_),value:f,valueType:c,rawDataIndex:i.getRawIndex(r)})].concat(v||[])})}function JO(t,e,r,n,i){var a=e.getData(),o=kr(t,function(c,v,h){var d=a.getDimensionInfo(h);return c=c||d&&d.tooltip!==!1&&d.displayName!=null},!1),s=[],l=[],u=[];n.length?R(n,function(c){f(Ha(a,r,c),c)}):R(t,f);function f(c,v){var h=a.getDimensionInfo(v);!h||h.otherDims.tooltip===!1||(o?u.push(ns("nameValue",{markerType:"subItem",markerColor:i,name:h.displayName,value:c,valueType:h.type})):(s.push(c),l.push(h.type)))}return{inlineValues:s,inlineValueTypes:l,blocks:u}}var dn=we();function rl(t,e){return t.getName(e)||t.getId(e)}var ek="__universalTransitionEnabled",nr=(function(t){q(e,t);function e(){var r=t!==null&&t.apply(this,arguments)||this;return r._selectedDataIndicesMap={},r}return e.prototype.init=function(r,n,i){this.seriesIndex=this.componentIndex,this.dataTask=Ho({count:rk,reset:nk}),this.dataTask.context={model:this},this.mergeDefaultAndTheme(r,i);var a=dn(this).sourceManager=new Nw(this);a.prepareSource();var o=this.getInitialData(r,i);J0(o,this),this.dataTask.context.data=o,dn(this).dataBeforeProcessed=o,Q0(this),this._initSelectedMapFromData(o)},e.prototype.mergeDefaultAndTheme=function(r,n){var i=ts(this),a=i?As(r):{},o=this.subType;Se.hasClass(o)&&(o+="Series" Line truncated
`:"<br/>",m=c.join(g);this._showOrMove(s,function(){this._updateContentNotChangedOnAxis(r,u)?this._updatePosition(s,h,o[0],o[1],this._tooltipContent,u):this._showTooltipContent(s,m,u,Math.random()+"",o[0],o[1],h,null,v)})},t.prototype._showSeriesItemTooltip=function(r,n,i){var a=this._ecModel,o=Nt(n),s=o.seriesIndex,l=a.getSeriesByIndex(s),u=o.dataModel||l,f=o.dataIndex,c=o.dataType,v=u.getData(c),h=this._renderMode,d=r.positionDefault,p=lo([v.getItemModel(f),u,l&&(l.coordinateSystem||{}).model],this._tooltipModel,d?{position:d}:null),g=p.get("trigger");if(!(g!=null&&g!=="item")){var m=u.getDataParams(f,c),y=new mc;m.marker=y.makeTooltipMarker("item",Ti(m.color),h);var _=vy(u.formatTooltip(f,!1,c)),w=p.get("order"),S=p.get("valueFormatter"),b=_.frag,x=b?yy(S?Y({valueFormatter:S},b):b,y,h,w,a.get("useUTC"),p.get("textStyle")):_.text,T="item_"+u.name+"_"+f;this._showOrMove(p,function(){this._showTooltipContent(p,x,m,T,r.offsetX,r.offsetY,r.position,r.target,y)}),i({type:"showTip",dataIndexInside:f,dataIndex:v.getRawIndex(f),seriesIndex:s,from:this.uid})}},t.prototype._showComponentItemTooltip=function(r,n,i){var a=Nt(n),o=a.tooltipConfig,s=o.option||{};if(it(s)){var l=s;s={content:l,formatter:l}}var u=[s],f=this._ecModel.getComponent(a.componentMainType,a.componentIndex);f&&u.push(f),u.push({formatter:s.content});var c=r.positionDefault,v=lo(u,this._tooltipModel,c?{position:c}:null),h=v.get("content"),d=Math.random()+"",p=new mc;this._showOrMove(v,function(){var g=xt(v.get("formatterParams")||{});this._showTooltipContent(v,h,g,d,r.offsetX,r.offsetY,r.position,n,p)}),i({type:"showTip",from:this.uid})},t.prototype._showTooltipContent=function(r,n,i,a,o,s,l,u,f){if(this._ticket="",!(!r.get("showContent")||!r.get("show"))){var c=this._tooltipContent;c.setEnterable(r.get("enterable"));var v=r.get("formatter");l=l||r.get("position");var h=n,d=this._getNearestPoint([o,s],i,r.get("trigger"),r.get("borderColor")),p=d.color;if(v)if(it(v)){var g=r.ecModel.get("useUTC"),m=et(i)?i[0]:i,y=m&&m.axisType&&m.axisType.indexOf("time")>=0;h=v,y&&(h=Zu(m.axisValue,h,g)),h=Ib(h,i,!0)}else if(mt(v)){var _=Ft(function(w,S){w===this._ticket&&(c.setContent(S,f,r,p,l),this._updatePosition(r,l,o,s,c,i,u))},this);this._ticket=a,h=v(i,a,_)}else h=v;c.setContent(h,f,r,p,l),c.show(r,p),this._updatePosition(r,l,o,s,c,i,u)}},t.prototype._getNearestPoint=function(r,n,i,a){if(i==="axis"||et(n))return{color:a||(this._renderMode==="html"?"#fff":"none")};if(!et(n))return{color:a||n.color||n.borderColor}},t.prototype._updatePosition=function(r,n,i,a,o,s,l){var u=this._api.getWidth(),f=this._api.getHeight();n=n||r.get("position");var c=o.getSize(),v=r.get("align"),h=r.get("verticalAlign"),d=l&&l.getBoundingRect().clone();if(l&&d.applyTransform(l.transform),mt(n)&&(n=n([i,a],s,o.el,d,{viewSize:[u,f],contentSize:c.slice()})),et(n))i=Vt(n[0],u),a=Vt(n[1],f);else if(st(n)){var p=n;p.width=c[0],p.height=c[1];var g=Ma(p,{width:u,height:f});i=g.x,a=g.y,v=null,h=null}else if(it(n)&&l){var m=p5(n,d,c,r.get("borderWidth"));i=m[0],a=m[1]}else{var m=v5(i,a,o,u,f,v?null:20,h?null:20);i=m[0],a=m[1]}if(v&&(i-=B0(v)?c[0]/2:v==="right"?c[0]:0),h&&(a-=B0(h)?c[1]/2:h==="bottom"?c[1]:0),xx(r)){var m=d5(i,a,o,u,f);i=m[0],a=m[1]}o.moveTo(i,a)},t.prototype._updateContentNotChangedOnAxis=function(r,n){var i=this._lastDataByCoordSys,a=this._cbParamsList,o=!!i&&i.length===r.length;return o&&I(i,function(s,l){var u=s.dataByAxis||[],f=r[l]||{},c=f.dataByAxis||[];o=o&&u.length===c.length,o&&I(u,function(v,h){var d=c[h]||{},p=v.seriesDataIndices||[],g=d.seriesDataIndices||[];o=o&&v.value===d.value&&v.axisType===d.axisType&&v.axisId===d.axisId&&p.length===g.length,o&&I(p,function(m,y){var _=g[y];o=o&&m.seriesIndex===_.seriesIndex&&m.dataIndex===_.dataIndex}),a&&I(v.seriesDataIndices,function(m){var y=m.seriesIndex,_=n[y],w=a[y];_&&w&&w.data!==_.data&&(o=!1)})})}),this._lastDataByCoordSys=r,this._cbParamsList=n,!!o},t.prototype._hide=function(r){this._lastDataByCoordSys=null,r({type:"hideTip",from:this.uid})},t.prototype.dispose=function(r,n){wt.node||!n.getDom()||(Jh(this,"_updatePosition"),this._tooltipContent.dispose(),dv("itemTooltip",n))},t.type="tooltip",t}(qr);function lo(e,t,r){var n=t.ecModel,i;r?(i=new ce(r,n,n),i=new ce(t.option,i,n)):i=t;for(var a=e.length-1;a>=0;a--){var o=e[a];o&&(o instanceof ce&&(o=o.get("tooltip",!0)),it(o)&&(o={formatter:o}),o&&(i=new ce(o,i,n)))}return i}function N0(e,t){return e.dispatchAction||Ft(t.dispatchAction,t)}function v5(e,t,r,n,i,a,o){var s=r.getSize(),l=s[0],u=s[1];return a!=null&&(e+l+a+2>n?e-=l+a:e+=a),o!=null&&(t+u+o>i?t-=u+o:t+=o),[e,t]}function d5(e,t,r,n,i){var a=r.getSize(),o=a[0],s=a[1];return e=Math.min(e+o,n)-o,t=Math.min(t+s,i)-s,e=Math.max(e,0),t=Math.max(t,0),[e,t]}function p5(e,t,r,n){var i=r[0],a=r[1],o=Math.ceil(Math.SQRT2*n)+8,s=0,l=0,u=t.width,f=t.height;switch(e){case"inside":s=t.x+u/2-i/2,l=t.y+f/2-a/2;break;case"top":s=t.x+u/2-i/2,l=t.y-a-o;break;case"bottom":s=t.x+u/2-i/2,l=t.y+f+o;break;case"left":s=t.x-i-o,l=t Line truncated
`)){var g=new vl;g.newline=!0,u.add(g);return}var m=i.getSeriesByName(p)[0];if(!f.get(p))if(m){var y=m.getData(),_=y.getVisual("legendLineStyle")||{},w=y.getVisual("legendIcon"),S=y.getVisual("style"),b=this._createItem(m,p,d,h,n,r,_,S,w,c);b.on("click",Qi(H0,p,null,a,v)).on("mouseover",Qi(mv,m.name,null,a,v)).on("mouseout",Qi(yv,m.name,null,a,v)),f.set(p,!0)}else i.eachRawSeries(function(x){if(!f.get(p)&&x.legendVisualProvider){var T=x.legendVisualProvider;if(!T.containName(p))return;var C=T.indexOfName(p),D=T.getItemVisual(C,"style"),P=T.getItemVisual(C,"legendIcon"),A=$r(D.fill);A&&A[3]===0&&(A[3]=.2,D=Y(Y({},D),{fill:zu(A,"rgba")}));var L=this._createItem(x,p,d,h,n,r,{},D,P,c);L.on("click",Qi(H0,null,p,a,v)).on("mouseover",Qi(mv,null,p,a,v)).on("mouseout",Qi(yv,null,p,a,v)),f.set(p,!0)}},this)},this),o&&this._createSelector(o,n,a,s,l)},t.prototype._createSelector=function(r,n,i,a,o){var s=this.getSelectorGroup();gv(r,function(u){var f=u.type,c=new he({style:{x:0,y:0,align:"center",verticalAlign:"middle"},onclick:function(){i.dispatchAction({type:f==="all"?"legendAllSelect":"legendInverseSelect"})}});s.add(c);var v=n.getModel("selectorLabel"),h=n.getModel(["emphasis","selectorLabel"]);Xu(c,{normal:v,emphasis:h},{defaultText:u.title}),$h(c)})},t.prototype._createItem=function(r,n,i,a,o,s,l,u,f,c){var v=r.visualDrawType,h=o.get("itemWidth"),d=o.get("itemHeight"),p=o.isSelected(n),g=a.get("symbolRotate"),m=a.get("symbolKeepAspect"),y=a.get("icon");f=y||f||"roundRect";var _=P5(f,a,l,u,v,p),w=new vl,S=a.getModel("textStyle");if(mt(r.getLegendIcon)&&(!y||y==="inherit"))w.add(r.getLegendIcon({itemWidth:h,itemHeight:d,icon:f,iconRotate:g,itemStyle:_.itemStyle,lineStyle:_.lineStyle,symbolKeepAspect:m}));else{var b=y==="inherit"&&r.getData().getVisual("symbol")?g==="inherit"?r.getData().getVisual("symbolRotate"):g:0;w.add(I5({itemWidth:h,itemHeight:d,icon:f,iconRotate:b,itemStyle:_.itemStyle,lineStyle:_.lineStyle,symbolKeepAspect:m}))}var x=s==="left"?h+5:-5,T=s,C=o.get("formatter"),D=n;it(C)&&C?D=C.replace("{name}",n!=null?n:""):mt(C)&&(D=C(n));var P=a.get("inactiveColor");w.add(new he({style:kn(S,{text:D,x,y:d/2,fill:p?S.getTextColor():P,align:T,verticalAlign:"middle"})}));var A=new ne({shape:w.getBoundingRect(),invisible:!0}),L=a.getModel("tooltip");return L.get("show")&&Rd({el:A,componentModel:o,itemName:n,itemTooltipOption:L.option}),w.add(A),w.eachChild(function(O){O.silent=!0}),A.silent=!c,this.getContentGroup().add(w),$h(w),w.__legendDataIndex=i,w},t.prototype.layoutInner=function(r,n,i,a,o,s){var l=this.getContentGroup(),u=this.getSelectorGroup();No(r.get("orient"),l,r.get("itemGap"),i.width,i.height);var f=l.getBoundingRect(),c=[-f.x,-f.y];if(u.markRedraw(),l.markRedraw(),o){No("horizontal",u,r.get("selectorItemGap",!0));var v=u.getBoundingRect(),h=[-v.x,-v.y],d=r.get("selectorButtonGap",!0),p=r.getOrient().index,g=p===0?"width":"height",m=p===0?"height":"width",y=p===0?"y":"x";s==="end"?h[p]+=f[g]+d:c[p]+=v[g]+d,h[1-p]+=f[m]/2-v[m]/2,u.x=h[0],u.y=h[1],l.x=c[0],l.y=c[1];var _={x:0,y:0};return _[g]=f[g]+d+v[g],_[m]=Math.max(f[m],v[m]),_[y]=Math.min(0,v[y]+h[1-p]),_}else return l.x=c[0],l.y=c[1],this.group.getBoundingRect()},t.prototype.remove=function(){this.getContentGroup().removeAll(),this._isFirstRender=!0},t.type="legend.plain",t}(qr);function P5(e,t,r,n,i,a){function o(d,p){d.lineWidth==="auto"&&(d.lineWidth=p.lineWidth>0?2:0),gv(d,function(g,m){d[m]==="inherit"&&(d[m]=p[m])})}var s=t.getModel("itemStyle"),l=s.getItemStyle(),u=e.lastIndexOf("empty",0)===0?"fill":"stroke";l.decal=n.decal,l.fill==="inherit"&&(l.fill=n[i]),l.stroke==="inherit"&&(l.stroke=n[u]),l.opacity==="inherit"&&(l.opacity=(i==="fill"?n:r).opacity),o(l,n);var f=t.getModel("lineStyle"),c=f.getLineStyle();if(o(c,r),l.fill==="auto"&&(l.fill=n.fill),l.stroke==="auto"&&(l.stroke=n.fill),c.stroke==="auto"&&(c.stroke=n.fill),!a){var v=t.get("inactiveBorderWidth"),h=l[u];l.lineWidth=v==="auto"?n.lineWidth>0&&h?2:0:l.lineWidth,l.fill=t.get("inactiveColor"),l.stroke=t.get("inactiveBorderColor"),c.stroke=f.get("inactiveColor"),c.lineWidth=f.get("inactiveWidth")}return{itemStyle:l,lineStyle:c}}function I5(e){var t=e.icon||"roundRect",r=Ia(t,0,0,e.itemWidth,e.itemHeight,e.itemStyle.fill,e.symbolKeepAspect);return r.setStyle(e.itemStyle),r.rotation=(e.iconRotate||0)*Math.PI/180,r.setOrigin([e.itemWidth/2,e.itemHeight/2]),t.indexOf("empty")>-1&&(r.style.stroke=r.style.fill,r.style.fill="#fff",r.style.lineWidth=2),r}function H0(e,t,r,n){yv(e,t,r,n),r.dispatchAction({type:"legendToggleSelect",name:e!=null?e:t}),mv(e,t,r,n)}function Dx(e){for(var t=e.getZr().storage.getDisplayList(),r,n=0,i=t.length;n<i&&!(r=t[n].states.emphasis);)n++;return r&&r.hoverLayer}function mv(e,t,r,n){Dx(r)||r.dispatchAction({type:"highlight",seriesName:e,name:t,excludeSeriesId:n})}function yv(e,t,r,n){Dx(r)||r.dispatchAction({type:"downplay",seriesName:e,name:t,excludeSeriesId:n})}var Px=D5;function E5(e){var t=e.findComponents({mainType:"legend"});t&&t.length&&e.filterS Line truncated
`||u==="")){var f=o.isSelected(u);n.hasOwnProperty(u)?n[u]=n[u]&&f:n[u]=f}})}),e==="allSelect"||e==="inverseSelect"?{selected:n}:{name:t.name,selected:n}}function L5(e){e.registerAction("legendToggleSelect","legendselectchanged",Xt(uo,"toggleSelected")),e.registerAction("legendAllSelect","legendselectall",Xt(uo,"allSelect")),e.registerAction("legendInverseSelect","legendinverseselect",Xt(uo,"inverseSelect")),e.registerAction("legendSelect","legendselected",Xt(uo,"select")),e.registerAction("legendUnSelect","legendunselected",Xt(uo,"unSelect"))}function Ix(e){e.registerComponentModel(pv),e.registerComponentView(Px),e.registerProcessor(e.PRIORITY.PROCESSOR.SERIES_FILTER,E5),e.registerSubTypeDefaulter("legend",function(){return"plain"}),L5(e)}var R5=function(e){J(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type=t.type,r}return t.prototype.setScrollDataIndex=function(r){this.option.scrollDataIndex=r},t.prototype.init=function(r,n,i){var a=nf(r);e.prototype.init.call(this,r,n,i),$0(this,r,a)},t.prototype.mergeOption=function(r,n){e.prototype.mergeOption.call(this,r,n),$0(this,this.option,r)},t.type="legend.scroll",t.defaultOption=q2(pv.defaultOption,{scrollDataIndex:0,pageButtonItemGap:5,pageButtonGap:null,pageButtonPosition:"end",pageFormatter:"{current}/{total}",pageIcons:{horizontal:["M0,0L12,-10L12,10z","M0,0L-12,-10L-12,10z"],vertical:["M0,0L20,0L10,-20z","M0,0L20,0L10,20z"]},pageIconColor:"#2f4554",pageIconInactiveColor:"#aaa",pageIconSize:15,pageTextStyle:{color:"#333"},animationDurationUpdate:800}),t}(pv);function $0(e,t,r){var n=e.getOrient(),i=[1,1];i[n.index]=0,Aa(t,r,{type:"box",ignoreSize:!!i})}var O5=R5,V0=ye,Nc=["width","height"],Bc=["x","y"],k5=function(e){J(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type=t.type,r.newlineDisabled=!0,r._currentIndex=0,r}return t.prototype.init=function(){e.prototype.init.call(this),this.group.add(this._containerGroup=new V0),this._containerGroup.add(this.getContentGroup()),this.group.add(this._controllerGroup=new V0)},t.prototype.resetInner=function(){e.prototype.resetInner.call(this),this._controllerGroup.removeAll(),this._containerGroup.removeClipPath(),this._containerGroup.__rectSize=null},t.prototype.renderInner=function(r,n,i,a,o,s,l){var u=this;e.prototype.renderInner.call(this,r,n,i,a,o,s,l);var f=this._controllerGroup,c=n.get("pageIconSize",!0),v=et(c)?c:[c,c];d("pagePrev",0);var h=n.getModel("pageTextStyle");f.add(new he({name:"pageText",style:{text:"xx/xx",fill:h.getTextColor(),font:h.getFont(),verticalAlign:"middle",align:"center"},silent:!0})),d("pageNext",1);function d(p,g){var m=p+"DataIndex",y=Ld(n.get("pageIcons",!0)[n.getOrient().name][g],{onclick:Ft(u._pageGo,u,m,n,a)},{x:-v[0]/2,y:-v[1]/2,width:v[0],height:v[1]});y.name=p,f.add(y)}},t.prototype.layoutInner=function(r,n,i,a,o,s){var l=this.getSelectorGroup(),u=r.getOrient().index,f=Nc[u],c=Bc[u],v=Nc[1-u],h=Bc[1-u];o&&No("horizontal",l,r.get("selectorItemGap",!0));var d=r.get("selectorButtonGap",!0),p=l.getBoundingRect(),g=[-p.x,-p.y],m=xt(i);o&&(m[f]=i[f]-p[f]-d);var y=this._layoutContentAndController(r,a,m,u,f,v,h,c);if(o){if(s==="end")g[u]+=y[f]+d;else{var _=p[f]+d;g[u]-=_,y[c]-=_}y[f]+=p[f]+d,g[1-u]+=y[h]+y[v]/2-p[v]/2,y[v]=Math.max(y[v],p[v]),y[h]=Math.min(y[h],p[h]+g[1-u]),l.x=g[0],l.y=g[1],l.markRedraw()}return y},t.prototype._layoutContentAndController=function(r,n,i,a,o,s,l,u){var f=this.getContentGroup(),c=this._containerGroup,v=this._controllerGroup;No(r.get("orient"),f,r.get("itemGap"),a?i.width:null,a?null:i.height),No("horizontal",v,r.get("pageButtonItemGap",!0));var h=f.getBoundingRect(),d=v.getBoundingRect(),p=this._showController=h[o]>i[o],g=[-h.x,-h.y];n||(g[a]=f[u]);var m=[0,0],y=[-d.x,-d.y],_=Mt(r.get("pageButtonGap",!0),r.get("itemGap",!0));if(p){var w=r.get("pageButtonPosition",!0);w==="end"?y[a]+=i[o]-d[o]:m[a]+=d[o]+_}y[1-a]+=h[s]/2-d[s]/2,f.setPosition(g),c.setPosition(m),v.setPosition(y);var S={x:0,y:0};if(S[o]=p?i[o]:h[o],S[s]=Math.max(h[s],d[s]),S[l]=Math.min(0,d[l]+y[1-a]),c.__rectSize=i[o],p){var b={x:0,y:0};b[o]=Math.max(i[o]-d[o]-_,0),b[s]=S[s],c.setClipPath(new ne({shape:b})),c.__rectSize=b[o]}else v.eachChild(function(T){T.attr({invisible:!0,silent:!0})});var x=this._getPageInfo(r);return x.pageIndex!=null&&Dr(f,{x:x.contentPosition[0],y:x.contentPosition[1]},p?r:null),this._updatePageInfoView(r,x),S},t.prototype._pageGo=function(r,n,i){var a=this._getPageInfo(n)[r];a!=null&&i.dispatchAction({type:"legendScroll",scrollDataIndex:a,legendId:n.id})},t.prototype._updatePageInfoView=function(r,n){var i=this._controllerGroup;I(["pagePrev","pageNext"],function(f){var c=f+"DataIndex",v=n[c]!=null,h=i.childOfName(f);h&&(h.setStyle("fill",v?r.get("pageIconColor",!0):r.get("pageIconInactiveColor",!0)),h.cursor=v?"pointer":"default")});var a=i.childOfName("pageText"),o=r.get("pageFormatter"),s=n.pageIndex,l=s!=null?s+1:0,u=n.pageCount;a&&o&&a.setStyle("text",it(o)?o.replace("{current}",l==null?"":l+"").replace("{tot Line truncated
`:"<br/>",g=c.join(m);this._showOrMove(s,function(){this._updateContentNotChangedOnAxis(r,u)?this._updatePosition(s,h,o[0],o[1],this._tooltipContent,u):this._showTooltipContent(s,g,u,Math.random()+"",o[0],o[1],h,null,v)})},e.prototype._showSeriesItemTooltip=function(r,n,i){var a=this._ecModel,o=me(n),s=o.seriesIndex,l=a.getSeriesByIndex(s),u=o.dataModel||l,f=o.dataIndex,c=o.dataType,v=u.getData(c),h=this._renderMode,d=r.positionDefault,p=So([v.getItemModel(f),u,l&&(l.coordinateSystem||{}).model],this._tooltipModel,d?{position:d}:null),m=p.get("trigger");if(!(m!=null&&m!=="item")){var g=u.getDataParams(f,c),y=new ih;g.marker=y.makeTooltipMarker("item",Fi(g.color),h);var _=Y0(u.formatTooltip(f,!1,c)),S=p.get("order"),b=p.get("valueFormatter"),w=_.frag,T=w?K0(b?G({valueFormatter:b},w):w,y,h,S,a.get("useUTC"),p.get("textStyle")):_.text,C="item_"+u.name+"_"+f;this._showOrMove(p,function(){this._showTooltipContent(p,T,g,C,r.offsetX,r.offsetY,r.position,r.target,y)}),i({type:"showTip",dataIndexInside:f,dataIndex:v.getRawIndex(f),seriesIndex:s,from:this.uid})}},e.prototype._showComponentItemTooltip=function(r,n,i){var a=this._renderMode==="html",o=me(n),s=o.tooltipConfig,l=s.option||{},u=l.encodeHTMLContent;if(te(l)){var f=l;l={content:f,formatter:f},u=!0}u&&a&&l.content&&(l=de(l),l.content=yt(l.content));var c=[l],v=this._ecModel.getComponent(o.componentMainType,o.componentIndex);v&&c.push(v),c.push({formatter:l.content});var h=r.positionDefault,d=So(c,this._tooltipModel,h?{position:h}:null),p=d.get("content"),m=Math.random()+"",g=new ih;this._showOrMove(d,function(){var y=de(d.get("formatterParams")||{});this._showTooltipContent(d,p,y,m,r.offsetX,r.offsetY,r.position,n,g)}),i({type:"showTip",from:this.uid})},e.prototype._showTooltipContent=function(r,n,i,a,o,s,l,u,f){if(this._ticket="",!(!r.get("showContent")||!r.get("show"))){var c=this._tooltipContent;c.setEnterable(r.get("enterable"));var v=r.get("formatter");l=l||r.get("position");var h=n,d=this._getNearestPoint([o,s],i,r.get("trigger"),r.get("borderColor"),r.get("defaultBorderColor",!0)),p=d.color;if(v)if(te(v)){var m=r.ecModel.get("useUTC"),g=K(i)?i[0]:i,y=g&&g.axisType&&g.axisType.indexOf("time")>=0;h=v,y&&(h=xs(g.axisValue,h,m)),h=Sp(h,i,!0)}else if(fe(v)){var _=Ae(function(S,b){S===this._ticket&&(c.setContent(b,f,r,p,l),this._updatePosition(r,l,o,s,c,i,u))},this);this._ticket=a,h=v(i,a,_)}else h=v;c.setContent(h,f,r,p,l),c.show(r,p),this._updatePosition(r,l,o,s,c,i,u)}},e.prototype._getNearestPoint=function(r,n,i,a,o){if(i==="axis"||K(n))return{color:a||o};if(!K(n))return{color:a||n.color||n.borderColor}},e.prototype._updatePosition=function(r,n,i,a,o,s,l){var u=this._api.getWidth(),f=this._api.getHeight();n=n||r.get("position");var c=o.getSize(),v=r.get("align"),h=r.get("verticalAlign"),d=l&&l.getBoundingRect().clone();if(l&&d.applyTransform(l.transform),fe(n)&&(n=n([i,a],s,o.el,d,{viewSize:[u,f],contentSize:c.slice()})),K(n))i=He(n[0],u),a=He(n[1],f);else if(ie(n)){var p=n;p.width=c[0],p.height=c[1];var m=sn(p,{width:u,height:f});i=m.x,a=m.y,v=null,h=null}else if(te(n)&&l){var g=e3(n,d,c,r.get("borderWidth"));i=g[0],a=g[1]}else{var g=Qz(i,a,o,u,f,v?null:20,h?null:20);i=g[0],a=g[1]}if(v&&(i-=B_(v)?c[0]/2:v==="right"?c[0]:0),h&&(a-=B_(h)?c[1]/2:h==="bottom"?c[1]:0),Xx(r)){var g=Jz(i,a,o,u,f);i=g[0],a=g[1]}o.moveTo(i,a)},e.prototype._updateContentNotChangedOnAxis=function(r,n){var i=this._lastDataByCoordSys,a=this._cbParamsList,o=!!i&&i.length===r.length;return o&&R(i,function(s,l){var u=s.dataByAxis||[],f=r[l]||{},c=f.dataByAxis||[];o=o&&u.length===c.length,o&&R(u,function(v,h){var d=c[h]||{},p=v.seriesDataIndices||[],m=d.seriesDataIndices||[];o=o&&v.value===d.value&&v.axisType===d.axisType&&v.axisId===d.axisId&&p.length===m.length,o&&R(p,function(g,y){var _=m[y];o=o&&g.seriesIndex===_.seriesIndex&&g.dataIndex===_.dataIndex}),a&&R(v.seriesDataIndices,function(g){var y=g.seriesIndex,_=n[y],S=a[y];_&&S&&S.data!==_.data&&(o=!1)})})}),this._lastDataByCoordSys=r,this._cbParamsList=n,!!o},e.prototype._hide=function(r){this._lastDataByCoordSys=null,this._cbParamsList=null,r({type:"hideTip",from:this.uid})},e.prototype.dispose=function(r,n){ve.node||!n.getDom()||(zv(this,"_updatePosition"),this._tooltipContent.dispose(),od("itemTooltip",n),this._tooltipContent=null,this._tooltipModel=null,this._lastDataByCoordSys=null,this._cbParamsList=null)},e.type="tooltip",e})(Ct);function So(t,e,r){var n=e.ecModel,i;r?(i=new Le(r,n,n),i=new Le(e.option,i,n)):i=e;for(var a=t.length-1;a>=0;a--){var o=t[a];o&&(o instanceof Le&&(o=o.get("tooltip",!0)),te(o)&&(o={formatter:o}),o&&(i=new Le(o,i,n)))}return i}function N_(t,e){return t.dispatchAction||Ae(e.dispatchAction,e)}function Qz(t,e,r,n,i,a,o){var s=r.getSize(),l=s[0],u=s[1];return a!=null&&(t+l+a+2>n?t-=l+a:t+=a),o!=null&&(e+u+o>i?e-=u+o:e+=o),[t,e]}function Jz(t,e,r,n,i){var a=r.getSize(),o=a[0],s=a[1];return t=Math.min(t+o,n)-o,e=Math.min(e+s,i)-s,t=Math.max(t,0),e=Math.max(e,0),[t,e]}function e3(t,e,r,n){var i=r[0],a=r[ Line truncated
`)){var y=new wl;y.newline=!0,u.add(y);return}var _=i.getSeriesByName(g)[0];if(!f.get(g))if(_){var S=_.getData(),b=S.getVisual("legendLineStyle")||{},w=S.getVisual("legendIcon"),T=S.getVisual("style"),C=this._createItem(_,g,p,d,n,r,b,T,w,c,a);C.on("click",da(U_,g,null,a,h)).on("mouseover",da(ud,_.name,null,a,h)).on("mouseout",da(fd,_.name,null,a,h)),i.ssr&&C.eachChild(function(x){var A=me(x);A.seriesIndex=_.seriesIndex,A.dataIndex=p,A.ssrType="legend"}),v&&C.eachChild(function(x){m.packEventData(x,n,_,p,g)}),f.set(g,!0)}else i.eachRawSeries(function(x){var A=this;if(!f.get(g)&&x.legendVisualProvider){var D=x.legendVisualProvider;if(!D.containName(g))return;var P=D.indexOfName(g),M=D.getItemVisual(P,"style"),I=D.getItemVisual(P,"legendIcon"),k=Ft(M.fill);k&&k[3]===0&&(k[3]=.2,M=G(G({},M),{fill:Un(k,"rgba")}));var O=this._createItem(x,g,p,d,n,r,{},M,I,c,a);O.on("click",da(U_,null,g,a,h)).on("mouseover",da(ud,null,g,a,h)).on("mouseout",da(fd,null,g,a,h)),i.ssr&&O.eachChild(function(N){var U=me(N);U.seriesIndex=x.seriesIndex,U.dataIndex=p,U.ssrType="legend"}),v&&O.eachChild(function(N){A.packEventData(N,n,x,p,g)}),f.set(g,!0)}},this)},this),o&&this._createSelector(o,n,a,s,l)},e.prototype.packEventData=function(r,n,i,a,o){var s={componentType:"legend",componentIndex:n.componentIndex,dataIndex:a,value:o,seriesIndex:i.seriesIndex};me(r).eventData=s},e.prototype._createSelector=function(r,n,i,a,o){var s=this.getSelectorGroup();ld(r,function(u){var f=u.type,c=new Ke({style:{x:0,y:0,align:"center",verticalAlign:"middle"},onclick:function(){i.dispatchAction({type:f==="all"?"legendAllSelect":"legendInverseSelect",legendId:n.id})}});s.add(c);var v=n.getModel("selectorLabel"),h=n.getModel(["emphasis","selectorLabel"]);Lf(c,{normal:v,emphasis:h},{defaultText:u.title}),Au(c)})},e.prototype._createItem=function(r,n,i,a,o,s,l,u,f,c,v){var h=r.visualDrawType,d=o.get("itemWidth"),p=o.get("itemHeight"),m=o.isSelected(n),g=a.get("symbolRotate"),y=a.get("symbolKeepAspect"),_=a.get("icon");f=_||f||"roundRect";var S=u3(f,a,l,u,h,m,v),b=new wl,w=a.getModel("textStyle");if(fe(r.getLegendIcon)&&(!_||_==="inherit"))b.add(r.getLegendIcon({itemWidth:d,itemHeight:p,icon:f,iconRotate:g,itemStyle:S.itemStyle,lineStyle:S.lineStyle,symbolKeepAspect:y}));else{var T=_==="inherit"&&r.getData().getVisual("symbol")?g==="inherit"?r.getData().getVisual("symbolRotate"):g:0;b.add(f3({itemWidth:d,itemHeight:p,icon:f,iconRotate:T,itemStyle:S.itemStyle,symbolKeepAspect:y}))}var C=s==="left"?d+5:-5,x=s,A=o.get("formatter"),D=n;te(A)&&A?D=A.replace("{name}",n!=null?n:""):fe(A)&&(D=A(n));var P=m?w.getTextColor():a.get("inactiveColor");b.add(new Ke({style:on(w,{text:D,x:C,y:p/2,fill:P,align:x,verticalAlign:"middle"},{inheritColor:P})}));var M=new Ge({shape:b.getBoundingRect(),style:{fill:"transparent"}}),I=a.getModel("tooltip");return I.get("show")&&Pf({el:M,componentModel:o,itemName:n,itemTooltipOption:I.option}),b.add(M),b.eachChild(function(k){k.silent=!0}),M.silent=!c,this.getContentGroup().add(b),Au(b),b.__legendDataIndex=i,b},e.prototype.layoutInner=function(r,n,i,a,o,s){var l=this.getContentGroup(),u=this.getSelectorGroup();zo(r.get("orient"),l,r.get("itemGap"),i.width,i.height);var f=l.getBoundingRect(),c=[-f.x,-f.y];if(u.markRedraw(),l.markRedraw(),o){zo("horizontal",u,r.get("selectorItemGap",!0));var v=u.getBoundingRect(),h=[-v.x,-v.y],d=r.get("selectorButtonGap",!0),p=r.getOrient().index,m=p===0?"width":"height",g=p===0?"height":"width",y=p===0?"y":"x";s==="end"?h[p]+=f[m]+d:c[p]+=v[m]+d,h[1-p]+=f[g]/2-v[g]/2,u.x=h[0],u.y=h[1],l.x=c[0],l.y=c[1];var _={x:0,y:0};return _[m]=f[m]+d+v[m],_[g]=Math.max(f[g],v[g]),_[y]=Math.min(0,v[y]+h[1-p]),_}else return l.x=c[0],l.y=c[1],this.group.getBoundingRect()},e.prototype.remove=function(){this.getContentGroup().removeAll(),this._isFirstRender=!0},e.type="legend.plain",e})(Ct);function u3(t,e,r,n,i,a,o){function s(m,g){m.lineWidth==="auto"&&(m.lineWidth=g.lineWidth>0?2:0),ld(m,function(y,_){m[_]==="inherit"&&(m[_]=g[_])})}var l=e.getModel("itemStyle"),u=l.getItemStyle(),f=t.lastIndexOf("empty",0)===0?"fill":"stroke",c=l.getShallow("decal");u.decal=!c||c==="inherit"?n.decal:Wv(c,o),u.fill==="inherit"&&(u.fill=n[i]),u.stroke==="inherit"&&(u.stroke=n[f]),u.opacity==="inherit"&&(u.opacity=(i==="fill"?n:r).opacity),s(u,n);var v=e.getModel("lineStyle"),h=v.getLineStyle();if(s(h,r),u.fill==="auto"&&(u.fill=n.fill),u.stroke==="auto"&&(u.stroke=n.fill),h.stroke==="auto"&&(h.stroke=n.fill),!a){var d=e.get("inactiveBorderWidth"),p=u[f];u.lineWidth=d==="auto"?n.lineWidth>0&&p?2:0:u.lineWidth,u.fill=e.get("inactiveColor"),u.stroke=e.get("inactiveBorderColor"),h.stroke=v.get("inactiveColor"),h.lineWidth=v.get("inactiveWidth")}return{itemStyle:u,lineStyle:h}}function f3(t){var e=t.icon||"roundRect",r=zi(e,0,0,t.itemWidth,t.itemHeight,t.itemStyle.fill,t.symbolKeepAspect);return r.setStyle(t.itemStyle),r.rotation=(t.iconRotate||0)*Math.PI/180,r.setOrigin([t.itemWidth/2,t.itemHeight/2]),e.indexOf("empty")>-1&&(r.style.stroke=r Line truncated
`||i==="")){var a=t.isSelected(i);Mt(r,i)?r[i]=r[i]&&a:r[i]=a}}),r}function c3(t){t.registerAction("legendToggleSelect","legendselectchanged",Ue(bo,"toggleSelected")),t.registerAction("legendAllSelect","legendselectall",Ue(bo,"allSelect")),t.registerAction("legendInverseSelect","legendinverseselect",Ue(bo,"inverseSelect")),t.registerAction("legendSelect","legendselected",Ue(bo,"select")),t.registerAction("legendUnSelect","legendunselected",Ue(bo,"unSelect"))}var h3=Wd(v3);function v3(t){var e=t.findComponents({mainType:"legend"});e&&e.length&&t.filterSeries(function(r){for(var n=0;n<e.length;n++)if(!e[n].isSelected(r.name))return!1;return!0})}function Jx(t){t.registerComponentModel(sd),t.registerComponentView(Qx),t.registerProcessor(t.PRIORITY.PROCESSOR.SERIES_FILTER,h3),t.registerSubTypeDefaulter("legend",function(){return"plain"}),c3(t)}var d3=(function(t){q(e,t);function e(){var r=t!==null&&t.apply(this,arguments)||this;return r.type=e.type,r}return e.prototype.setScrollDataIndex=function(r){this.option.scrollDataIndex=r},e.prototype.init=function(r,n,i){var a=As(r);t.prototype.init.call(this,r,n,i),G_(this,r,a)},e.prototype.mergeOption=function(r,n){t.prototype.mergeOption.call(this,r,n),G_(this,this.option,r)},e.type="legend.scroll",e.defaultOption=c2(sd.defaultOption,{scrollDataIndex:0,pageButtonItemGap:5,pageButtonGap:null,pageButtonPosition:"end",pageFormatter:"{current}/{total}",pageIcons:{horizontal:["M0,0L12,-10L12,10z","M0,0L-12,-10L-12,10z"],vertical:["M0,0L20,0L10,-20z","M0,0L20,0L10,20z"]},pageIconColor:ae.color.accent50,pageIconInactiveColor:ae.color.accent10,pageIconSize:15,pageTextStyle:{color:ae.color.tertiary},animationDurationUpdate:800}),e})(sd);function G_(t,e,r){var n=t.getOrient(),i=[1,1];i[n.index]=0,zn(e,r,{type:"box",ignoreSize:!!i})}var W_=rt,Mh=["width","height"],Ph=["x","y"],p3=(function(t){q(e,t);function e(){var r=t!==null&&t.apply(this,arguments)||this;return r.type=e.type,r.newlineDisabled=!0,r._currentIndex=0,r}return e.prototype.init=function(){t.prototype.init.call(this),this.group.add(this._containerGroup=new W_),this._containerGroup.add(this.getContentGroup()),this.group.add(this._controllerGroup=new W_)},e.prototype.resetInner=function(){t.prototype.resetInner.call(this),this._controllerGroup.removeAll(),this._containerGroup.removeClipPath(),this._containerGroup.__rectSize=null},e.prototype.renderInner=function(r,n,i,a,o,s,l){var u=this;t.prototype.renderInner.call(this,r,n,i,a,o,s,l);var f=this._controllerGroup,c=n.get("pageIconSize",!0),v=K(c)?c:[c,c];d("pagePrev",0);var h=n.getModel("pageTextStyle");f.add(new Ke({name:"pageText",style:{text:"xx/xx",fill:h.getTextColor(),font:h.getFont(),verticalAlign:"middle",align:"center"},silent:!0})),d("pageNext",1);function d(p,m){var g=p+"DataIndex",y=Mf(n.get("pageIcons",!0)[n.getOrient().name][m],{onclick:Ae(u._pageGo,u,g,n,a)},{x:-v[0]/2,y:-v[1]/2,width:v[0],height:v[1]});y.name=p,f.add(y)}},e.prototype.layoutInner=function(r,n,i,a,o,s){var l=this.getSelectorGroup(),u=r.getOrient().index,f=Mh[u],c=Ph[u],v=Mh[1-u],h=Ph[1-u];o&&zo("horizontal",l,r.get("selectorItemGap",!0));var d=r.get("selectorButtonGap",!0),p=l.getBoundingRect(),m=[-p.x,-p.y],g=de(i);o&&(g[f]=i[f]-p[f]-d);var y=this._layoutContentAndController(r,a,g,u,f,v,h,c);if(o){if(s==="end")m[u]+=y[f]+d;else{var _=p[f]+d;m[u]-=_,y[c]-=_}y[f]+=p[f]+d,m[1-u]+=y[h]+y[v]/2-p[v]/2,y[v]=Math.max(y[v],p[v]),y[h]=Math.min(y[h],p[h]+m[1-u]),l.x=m[0],l.y=m[1],l.markRedraw()}return y},e.prototype._layoutContentAndController=function(r,n,i,a,o,s,l,u){var f=this.getContentGroup(),c=this._containerGroup,v=this._controllerGroup;zo(r.get("orient"),f,r.get("itemGap"),a?i.width:null,a?null:i.height),zo("horizontal",v,r.get("pageButtonItemGap",!0));var h=f.getBoundingRect(),d=v.getBoundingRect(),p=this._showController=h[o]>i[o],m=[-h.x,-h.y];n||(m[a]=f[u]);var g=[0,0],y=[-d.x,-d.y],_=ne(r.get("pageButtonGap",!0),r.get("itemGap",!0));if(p){var S=r.get("pageButtonPosition",!0);S==="end"?y[a]+=i[o]-d[o]:g[a]+=d[o]+_}y[1-a]+=h[s]/2-d[s]/2,f.setPosition(m),c.setPosition(g),v.setPosition(y);var b={x:0,y:0};if(b[o]=p?i[o]:h[o],b[s]=Math.max(h[s],d[s]),b[l]=Math.min(0,d[l]+y[1-a]),c.__rectSize=i[o],p){var w={x:0,y:0};w[o]=Math.max(i[o]-d[o]-_,0),w[s]=b[s],c.setClipPath(new Ge({shape:w})),c.__rectSize=w[o]}else v.eachChild(function(C){C.attr({invisible:!0,silent:!0})});var T=this._getPageInfo(r);return T.pageIndex!=null&&Lt(f,{x:T.contentPosition[0],y:T.contentPosition[1]},p?r:null),this._updatePageInfoView(r,T),b},e.prototype._pageGo=function(r,n,i){var a=this._getPageInfo(n)[r];a!=null&&i.dispatchAction({type:"legendScroll",scrollDataIndex:a,legendId:n.id})},e.prototype._updatePageInfoView=function(r,n){var i=this._controllerGroup;R(["pagePrev","pageNext"],function(f){var c=f+"DataIndex",v=n[c]!=null,h=i.childOfName(f);h&&(h.setStyle("fill",v?r.get("pageIconColor",!0):r.get("pageIconInactiveColor",!0)),h.cursor=v?"pointer":"default")});var a=i.childOfName("pageText"),o=r.get("pageFormatter") Line truncated
@@ -2,6 +2,7 @@
<%
local jsonc = require "luci.jsonc"
local uci = require "luci.model.uci".cursor()
local asset_version = "0.17.1-rc20261028-release-6fb1035f6307"
local features = { "_lua_force_array_" }
local configs = {}
if luci.sys.call("which ota >/dev/null 2>&1 && ota >/dev/null 2>&1") == 0 then
@@ -30,13 +31,14 @@
disable = true
}
end
configs["deviceInventoryV2"] = uci:get("quickstart", "main", "device_inventory_v2") ~= "0"
-%>
<script>
(function(){
window.token = "<%=token%>";
var vue_prefix="<%=prefix%>";
window.vue_base = vue_prefix + '/';
window.vue_lang_data = '/luci-static/quickstart/i18n/<%=lang%>.json<%# ?v=PKG_VERSION %>';
window.vue_lang_data = '/luci-static/quickstart/i18n/<%=lang%>.json?v=<%=asset_version%>';
window.vue_lang = '<%=lang%>';
window.quickstart_features = <%=jsonc.stringify(features)%>;
window.quickstart_configs = <%=jsonc.stringify(configs)%>;
@@ -51,6 +53,6 @@
</script>
<div id="app">
</div>
<script type="module" crossorigin src="/luci-static/quickstart/index.js<%# ?v=PKG_VERSION %>"></script>
<link rel="stylesheet" href="/luci-static/quickstart/style.css<%# ?v=PKG_VERSION %>">
<script type="module" crossorigin src="/luci-static/quickstart/index.js?v=<%=asset_version%>"></script>
<link rel="stylesheet" href="/luci-static/quickstart/style.css?v=<%=asset_version%>">
<%+footer%>
@@ -0,0 +1,18 @@
Quickstart device-management release notice
The Quickstart backend and device-management web application are developed in
the upstream Quickstart project under its MIT license. OpenWrt/LuCI packaging
files retain the license notices stated in those files and packages.
The 30 WebP device scene icons shipped below
/www/luci-static/quickstart/device-icons are project-original, AI-assisted
generic illustrations generated with OpenAI's image generation tool on
2026-09-24 and 2026-09-25. Generation required transparent backgrounds and no
words, letters, logos, watermarks, trademarked elements, or brand-specific
product silhouettes. Manufacturer names are rendered as ordinary text and are
not embedded in the artwork. No artwork or logo from luci-app-oui,
luci-app-bandix, a device manufacturer, or another third party is included.
The asset manifest marks product/legal review as required before a public
release. Generative creation does not guarantee non-infringement.
@@ -219,7 +219,7 @@ const FS_PSTR_DEFAULT = 20;
const PSTR = propAxis('fs-pattern-strength', 'pattern_strength', '--fs-pattern-strength', 0, 100, FS_PSTR_DEFAULT, (v) => String(v / 100));
const PINK = enumAxis('fs-pattern-ink', 'data-pattern-ink', 'original', 'theme');
/* Transparency (issue #87): the cards and the menu as glass over a wallpaper, 100% = solid. The
/* Transparency: the cards and the menu as glass over a wallpaper, 100% = solid. The
* attribute that switches the glass on is derived, not stored: set below 100% unless the OS asks
* for more contrast or forces colours, which a glass card cannot honour. Less transparency holds
* back only an opacity that came from the router: Windows' "Transparency effects" switch reaches
@@ -111,7 +111,7 @@
</a>
{#
The three chrome icons below are the same 24x24 outline differing only in path data;
the stroke is `.fs-ico` in theme/20-shell.css, not attributes, and fs-widgets.js's
the stroke is `.fs-ico` in theme/20-shell.css, not attributes, and fs-chrome.js's
svgIcon() emits the same markup client-side. aria-hidden: each sits beside its own label.
#}
<button type="button" class="fs-railtoggle" id="fs-rail-toggle" aria-label="{{ _('Collapse menu') }}" title="{{ _('Collapse menu') }}">
@@ -264,7 +264,7 @@
const solid = mq('(prefers-contrast: more)') || mq('(forced-colors: active)');
const rt = mq('(prefers-reduced-transparency: reduce)');
/* `p` written out, never built from `k`: mangle-tokens.sh renames only whole literals, so a
built name set a property the sheet no longer reads (issue #89) */
built name set a property the sheet no longer reads */
const glass = (k, p, a) => {
if (num(k, 0, 100, 100, p, '%') && !solid && !(rt && lsGet(k) === null)) attr(a, '');
};
+3 -3
View File
@@ -1,13 +1,13 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=oxidns
PKG_VERSION:=1.7.0
PKG_VERSION:=1.7.1
PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/svenshi/oxidns.git
PKG_SOURCE_DATE:=2026-11-11
PKG_SOURCE_VERSION:=a3dd95c0eebce51d3b11dc1a10b3fb5283f6228f
PKG_SOURCE_DATE:=2026-11-12
PKG_SOURCE_VERSION:=acc6155beec9c99bb77e3c792a681a84dcb7cb12
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE:=$(PKG_SOURCE_SUBDIR).tar.zst
PKG_MIRROR_HASH:=skip
Binary file not shown.
File diff suppressed because it is too large. Load diff
@@ -7,10 +7,10 @@
! Issues: https://github.com/hagezi/dns-blocklists/issues
! Disclaimer: https://github.com/hagezi/dns-blocklists/blob/main/README.md#disclaimer
! Expires: 8 hours
! Last modified: 10 Nov 2026 23:57 UTC
! Version: 2026.1110.2357.37
! Last modified: 11 Nov 2026 08:53 UTC
! Version: 2026.1111.0853.51
! Syntax: AdBlock
! Number of entries: 3570
! Number of entries: 3567
!
||ss.0001.xyz^
||ns.00dani.me^
@@ -174,9 +174,6 @@
||dns.adguard.com^
||dns-family.adguard.com^
||dns-unfiltered.adguard.com^
||dns.adguarddns.com^
||family.adguarddns.com^
||unfiltered.adguarddns.com^
||dns.adnull.com^
||dns.adrianlam.com^
||adguard.adrianofrongillo.ovh^
File diff suppressed because it is too large. Load diff
Loaded 100 of 101 files, more files were not shown because too many files have changed in this diff. Show more