💋 Sync 2026-10-04 04:16:09

This commit is contained in:
github-actions[bot] committed 2026-10-04 04:16:09 +08:00
1 parent f6b2df73ec
commit f62992fd80
745 files changed
+212416 -1173

No files matched your search

@@ -0,0 +1,241 @@
.fluent-dashboard {
--fluent-dashboard-border: var(--fluent-card-border, var(--inputborder-color, color-mix(in srgb, currentColor 16%, transparent)));
--fluent-dashboard-muted: var(--fluent-text-secondary, color-mix(in srgb, currentColor 68%, transparent));
--fluent-dashboard-surface: var(--fluent-card-bg, rgba(var(--primary-rgbbody, 255, 255, 255), 0.7));
--fluent-dashboard-shadow: var(--fluent-card-shadow, 0 .1rem .3rem var(--input-boxcolor, color-mix(in srgb, CanvasText 8%, transparent)));
color: var(--fluent-text, CanvasText);
container-type: inline-size;
}
.fluent-dashboard, .fluent-dashboard * {
box-sizing: border-box;
}
.fluent-dashboard__header {
color: highlighttext;
background: linear-gradient(120deg, Highlight, color-mix(in srgb, Highlight 68%, CanvasText));
border-radius: .75rem;
justify-content: space-between;
align-items: end;
gap: 1rem;
margin-block-end: 1rem;
padding: 1.25rem;
display: flex;
z-index: 0;
overflow: hidden;
position: relative;
}
.fluent-dashboard__header h1, .fluent-dashboard__header p {
color: inherit;
margin: 0;
}
.fluent-dashboard__header h1 {
font-size: clamp(1.5rem, 4vw, 2.25rem);
line-height: 1.1;
}
.fluent-dashboard__header h1 + p {
opacity: .82;
margin-block-start: .4rem;
}
.fluent-dashboard__updated {
opacity: .86;
flex: none;
font-size: .85rem;
}
.fluent-dashboard__grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 1rem;
display: grid;
}
.fluent-dashboard__card {
min-width: 0;
color: inherit;
background: var(--fluent-dashboard-surface);
border: 1px solid var(--fluent-dashboard-border);
box-shadow: var(--fluent-dashboard-shadow);
border-radius: .75rem;
overflow: hidden;
}
.fluent-dashboard__card--wide {
grid-column: 1 / -1;
}
.fluent-dashboard__card-title {
color: inherit;
border-block-end: 1px solid var(--fluent-dashboard-border);
margin: 0;
padding: 1rem 1.1rem .85rem 1.35rem;
font-size: 1rem;
font-weight: 650;
line-height: 1.25;
position: relative;
}
.fluent-dashboard__card-title:before {
content: "";
background: highlight;
border-radius: 1rem;
width: .2rem;
position: absolute;
inset: 1rem auto .85rem .7rem;
}
.fluent-dashboard__card-content {
padding: 1rem 1.1rem;
}
.fluent-dashboard__details {
grid-template-columns: minmax(8rem, 36%) minmax(0, 1fr);
gap: .6rem 1rem;
margin: 0;
display: grid;
}
.fluent-dashboard__details-label {
color: var(--fluent-dashboard-muted);
}
.fluent-dashboard__details-value {
overflow-wrap: anywhere;
min-width: 0;
font-weight: 550;
}
.fluent-dashboard__meter + .fluent-dashboard__meter {
margin-block-start: 1.1rem;
}
.fluent-dashboard__meter-label {
justify-content: space-between;
gap: 1rem;
margin-block-end: .45rem;
font-size: .85rem;
display: flex;
}
.fluent-dashboard__meter-label span:last-child {
color: var(--fluent-dashboard-muted);
font-variant-numeric: tabular-nums;
}
.fluent-dashboard__meter-track {
background: color-mix(in srgb, currentColor 12%, transparent);
border-radius: 1rem;
height: .5rem;
overflow: hidden;
}
.fluent-dashboard__meter-value {
border-radius: inherit;
background: highlight;
height: 100%;
transition: width .2s;
display: block;
}
.fluent-dashboard__table-wrap {
max-width: 100%;
overflow-x: auto;
}
.fluent-dashboard__table {
width: 100%;
color: inherit;
border-collapse: collapse;
margin: 0;
}
.fluent-dashboard__table th, .fluent-dashboard__table td {
text-align: start;
vertical-align: middle;
border-block-end: 1px solid var(--fluent-dashboard-border);
padding: .7rem .65rem;
}
.fluent-dashboard__table th {
color: var(--fluent-dashboard-muted);
letter-spacing: .02em;
text-transform: uppercase;
font-size: .78rem;
font-weight: 650;
}
.fluent-dashboard__table tbody tr:last-child td {
border-block-end: 0;
}
.fluent-dashboard__status {
white-space: nowrap;
align-items: center;
gap: .35rem;
font-size: .82rem;
font-weight: 600;
display: inline-flex;
}
.fluent-dashboard__status:before {
content: "";
background: currentColor;
border-radius: 50%;
width: .5rem;
height: .5rem;
}
.fluent-dashboard__status--up {
color: color-mix(in srgb, LinkText 75%, Highlight);
}
.fluent-dashboard__status--down {
color: color-mix(in srgb, currentColor 58%, transparent);
}
.fluent-dashboard__table .fluent-dashboard__empty {
color: var(--fluent-dashboard-muted);
text-align: center;
padding-block: 1.5rem;
font-style: italic;
}
@container (width <= 44rem) {
.fluent-dashboard__grid {
grid-template-columns: minmax(0, 1fr);
}
.fluent-dashboard__card--wide {
grid-column: auto;
}
}
@media (max-width: 575px) {
.fluent-dashboard__header {
flex-direction: column;
align-items: start;
padding: 1rem;
}
.fluent-dashboard__details {
grid-template-columns: minmax(0, 1fr);
gap: .2rem;
}
.fluent-dashboard__details-value + .fluent-dashboard__details-label {
margin-block-start: .45rem;
}
.fluent-dashboard__card-content {
padding: .85rem;
}
}
@media (prefers-reduced-motion: reduce) {
.fluent-dashboard__meter-value {
transition: none;
}
}
@@ -0,0 +1,654 @@
"use strict";
"require view";
"require dom";
"require poll";
"require rpc";
"require network";
// WARN: generated by rsbuild - do not edit
// The require scope
var __webpack_require__ = {};
// webpack/runtime/define_property_getters
(() => {
__webpack_require__.d = (exports, getters, values) => {
var define = (defs, kind) => {
for(var key in defs) {
if(__webpack_require__.o(defs, key) && !__webpack_require__.o(exports, key)) {
Object.defineProperty(exports, key, { enumerable: true, [kind]: defs[key] });
}
}
};
define(getters, "get");
define(values, "value");
};
})();
// webpack/runtime/has_own_property
(() => {
__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
})();
// webpack/runtime/make_namespace_object
(() => {
// define __esModule on exports
__webpack_require__.r = (exports) => {
if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};
})();
var __webpack_exports__ = {};
// ESM COMPAT FLAG
__webpack_require__.r(__webpack_exports__);
// EXPORTS
__webpack_require__.d(__webpack_exports__, {
main: () => (/* binding */ main)
});
;// CONCATENATED MODULE: ../node_modules/.pnpm/@lazulikao+luci-types@https_685a81b9ead15f36368ce6e6ebcf4a27/node_modules/@lazulikao/luci-types/src/jsx/jsx-factory.ts
const Fragment = Symbol.for("jsx.fragment");
function jsx_factory_e(e, t) {
let { children: n, ...r } = null === t || "object" != typeof t || Array.isArray(t) ? {} : t, o = function e(t, n = []) {
for (let r of t)null != r && "boolean" != typeof r && (Array.isArray(r) ? e(r, n) : n.push(r instanceof Node ? r : String(r)));
return n;
}(null == n ? [] : Array.isArray(n) ? n : [
n
]);
if (e === Fragment) {
let e = document.createDocumentFragment();
return e.append(...o), e;
}
if ("function" == typeof e) {
let t = Reflect.apply(e, void 0, [
{
...r,
children: o
}
]);
if (!(t instanceof Node)) throw TypeError("JSX components must return a DOM Node");
return t;
}
if ("string" != typeof e) throw TypeError("JSX element types must be tag names or component functions");
let f = {}, l = {
...r
};
for (let [e, t] of Object.entries(l))e.startsWith("on") && "function" == typeof t ? (f[e] = t, delete l[e]) : "boolean" == typeof t && (t ? l[e] = e : delete l[e]);
let s = Object.keys(l).length > 0 ? o.length > 1 ? E(e, l, o) : E(e, l, o[0]) : o.length > 1 ? E(e, {}, o) : E(e, {}, o[0]);
for (let [e, t] of Object.entries(f)){
let n = e.slice(2).toLowerCase();
s.addEventListener(n, t);
}
return s;
}
function jsx(t, n) {
return jsx_factory_e(t, n);
}
function jsxs(t, n) {
return jsx_factory_e(t, n);
}
function jsxDEV(t, n) {
return jsx_factory_e(t, n);
}
;// CONCATENATED MODULE: ../node_modules/.pnpm/@lazulikao+luci-types@https_685a81b9ead15f36368ce6e6ebcf4a27/node_modules/@lazulikao/luci-types/src/jsx/jsx-runtime.ts
;// CONCATENATED MODULE: ./web/fluentdashboard/model.ts
function model_t(t) {
return null === t || "object" != typeof t || Array.isArray(t) ? {} : t;
}
function model_e(t) {
return "string" == typeof t ? t : "";
}
function model_r(t) {
return "number" == typeof t && Number.isFinite(t) ? t : 0;
}
function model_o(e, i = "free") {
let n = model_t(e);
return {
total: model_r(n.total),
free: model_r(n[i] ?? n.free)
};
}
function normalizeSystemSnapshot(i, n, a) {
let l = model_t(i), m = model_t(n), u = model_t(l.release), f = model_t(a), s = Array.isArray(m.load) ? m.load.map(model_r) : [];
return {
hostname: model_e(l.hostname),
model: model_e(l.model),
architecture: model_e(l.system),
target: model_e(u.target),
firmware: model_e(u.description),
kernel: model_e(l.kernel),
luciVersion: [
model_e(f.branch),
model_e(f.revision)
].filter(Boolean).join(" "),
uptime: model_r(m.uptime),
localtime: model_r(m.localtime),
load: s,
memory: model_o(m.memory, "available"),
swap: model_o(m.swap),
root: model_o(m.root),
tmp: model_o(m.tmp)
};
}
function usedPercent(t) {
return t.total <= 0 ? 0 : Math.min(100, Math.max(0, (t.total - t.free) / t.total * 100));
}
function formatBytes(t) {
if (!Number.isFinite(t) || t <= 0) return "0 B";
let e = [
"B",
"KiB",
"MiB",
"GiB",
"TiB"
], r = Math.min(Math.floor(Math.log(t) / Math.log(1024)), e.length - 1), o = t / 1024 ** r;
return `${o >= 10 || 0 === r ? o.toFixed(0) : o.toFixed(1)} ${e[r]}`;
}
function formatDuration(t) {
if (!Number.isFinite(t) || t <= 0) return "-";
let e = Math.floor(t / 86400), r = Math.floor(t % 86400 / 3600), o = Math.floor(t % 3600 / 60);
return [
e > 0 ? `${e}d` : "",
`${r}h`,
`${o}m`
].filter(Boolean).join(" ");
}
function formatLoad(t) {
return 0 === t.length ? "-" : t.slice(0, 3).map((t)=>(t / 65535).toFixed(2)).join(" / ");
}
function formatLocalTime(t) {
return !Number.isFinite(t) || t <= 0 ? "-" : new Intl.DateTimeFormat(void 0, {
dateStyle: "medium",
timeStyle: "medium",
timeZone: "UTC"
}).format(new Date(1000 * t));
}
;// CONCATENATED MODULE: ./web/fluentdashboard/index.tsx
let fluentdashboard_l = rpc.declare({
object: "system",
method: "board"
}), fluentdashboard_o = rpc.declare({
object: "system",
method: "info"
}), c = rpc.declare({
object: "luci",
method: "getVersion"
}), fluentdashboard_u = rpc.declare({
object: "luci-rpc",
method: "getDHCPLeases",
expect: {
"": {}
}
});
function fluentdashboard_h(e) {
return null === e || "object" != typeof e || Array.isArray(e) ? {} : e;
}
function fluentdashboard_m(e) {
return "string" == typeof e ? e : "";
}
function p(e) {
return "number" == typeof e && Number.isFinite(e) ? e : 0;
}
async function fluentdashboard_f(e, t) {
try {
return await e;
} catch {
return t;
}
}
function y(e, t = !1) {
let r = e.getL3Device() ?? e.getDevice();
return {
name: e.getName(),
protocol: e.getI18n(),
up: e.isUp(),
addresses: t ? e.getIP6Addrs() : e.getIPAddrs(),
gateway: t ? e.getGateway6Addr() : e.getGatewayAddr(),
uptime: e.getUptime(),
device: r?.getName() ?? "",
rx: r?.getRXBytes() ?? 0,
tx: r?.getTXBytes() ?? 0
};
}
async function g() {
await fluentdashboard_f(network.flushCache(), {});
let [e, t, r, a, s, n, d, u] = await Promise.all([
fluentdashboard_f(fluentdashboard_l(), {}),
fluentdashboard_f(fluentdashboard_o(), {}),
fluentdashboard_f(c(), {}),
fluentdashboard_f(network.getWANNetworks(), []),
fluentdashboard_f(network.getWAN6Networks(), []),
fluentdashboard_f(network.getNetworks(), []),
fluentdashboard_f(network.getWifiDevices(), []),
fluentdashboard_f(network.getWifiNetworks(), [])
]), h = u.map((e)=>{
let t = d.find((t)=>t.getName() === e.getWifiDeviceName());
return {
radio: t?.getName() ?? e.getWifiDeviceName() ?? "",
hardware: t?.getI18n() ?? "",
up: e.isUp() && !t?.isDisabled(),
ssid: e.getActiveSSID() || e.getSSID() || e.getMeshID() || "",
mode: e.getActiveModeI18n() || e.getMode(),
channel: e.getChannel(),
frequency: e.getFrequency() ?? "",
encryption: e.getActiveEncryption()
};
});
for (let e of d)h.some((t)=>t.radio === e.getName()) || h.push({
radio: e.getName(),
hardware: e.getI18n(),
up: e.isUp() && !e.isDisabled(),
ssid: "",
mode: "",
channel: null,
frequency: "",
encryption: ""
});
return {
system: normalizeSystemSnapshot(e, t, r),
wan4: a.map((e)=>y(e)),
wan6: s.map((e)=>y(e, !0)),
interfaces: n.filter((e)=>"loopback" !== e.getName()).map((e)=>y(e)),
wireless: h,
updatedAt: new Date()
};
}
async function w() {
let [e, t, r] = await Promise.all([
fluentdashboard_f(fluentdashboard_u(), {}),
fluentdashboard_f(network.getWifiNetworks(), []),
fluentdashboard_f(network.getHostHints(), null)
]), a = await Promise.all(t.map(async (e)=>({
wifi: e,
peers: await fluentdashboard_f(e.getAssocList(), [])
}))), s = [];
for (let { wifi: e, peers: t } of a)for (let a of t){
let t = r?.getHostnameByMACAddr(a.mac) || r?.getIPAddrByMACAddr(a.mac) || r?.getIP6AddrByMACAddr(a.mac) || "";
s.push({
network: e.getActiveSSID() || e.getSSID() || e.getName(),
hostname: t,
mac: a.mac,
signal: a.signal,
noise: a.noise ?? null,
rxRate: a.rx?.rate ?? 0,
txRate: a.tx?.rate ?? 0
});
}
return {
leases: function(e) {
let t = fluentdashboard_h(e), r = [];
if (Array.isArray(t.dhcp_leases)) for (let e of t.dhcp_leases){
let t = fluentdashboard_h(e);
r.push({
family: "IPv4",
hostname: fluentdashboard_m(t.hostname),
address: fluentdashboard_m(t.ipaddr),
identifier: fluentdashboard_m(t.macaddr),
expires: !1 !== t.expires && p(t.expires)
});
}
if (Array.isArray(t.dhcp6_leases)) for (let e of t.dhcp6_leases){
let t = fluentdashboard_h(e), a = Array.isArray(t.ip6addrs) ? t.ip6addrs.filter((e)=>"string" == typeof e) : [];
r.push({
family: "IPv6",
hostname: fluentdashboard_m(t.hostname),
address: a.join(", ") || fluentdashboard_m(t.ip6addr),
identifier: fluentdashboard_m(t.duid),
expires: !1 !== t.expires && p(t.expires)
});
}
return r;
}(e),
clients: s
};
}
function b(e) {
return null == e || "" === e ? "-" : String(e);
}
function v(t) {
return jsx("span", {
class: `fluent-dashboard__status fluent-dashboard__status--${t ? "up" : "down"}`,
children: t ? _("Online") : _("Offline")
});
}
function A(r, a, s, n = !1) {
return jsxs("div", {
class: `fluent-dashboard__card${n ? " fluent-dashboard__card--wide" : ""}`,
"data-dashboard-section": r,
children: [
jsx("h2", {
class: "fluent-dashboard__card-title",
children: a
}),
jsx("div", {
class: "fluent-dashboard__card-content",
children: s
})
]
});
}
function S(r, a, s) {
return jsx("div", {
class: "fluent-dashboard__table-wrap",
children: jsxs("table", {
class: "fluent-dashboard__table",
children: [
jsx("thead", {
children: jsx("tr", {
children: r.map((t)=>jsx("th", {
children: t
}))
})
}),
jsx("tbody", {
children: a.length ? a.map((t)=>jsx("tr", {
children: t.map((t)=>jsx("td", {
children: t instanceof Node ? t : b(t)
}))
})) : jsx("tr", {
children: jsx("td", {
class: "fluent-dashboard__empty",
colSpan: r.length,
children: s
})
})
})
]
})
});
}
function x(t) {
return A("system", _("System"), jsx("div", {
class: "fluent-dashboard__details",
children: [
[
_("Hostname"),
t.hostname
],
[
_("Model"),
t.model
],
[
_("Architecture"),
t.architecture
],
[
_("Target Platform"),
t.target
],
[
_("Firmware Version"),
t.firmware
],
[
_("Kernel Version"),
t.kernel
],
[
_("LuCI Version"),
t.luciVersion
],
[
_("Local Time"),
formatLocalTime(t.localtime)
],
[
_("Uptime"),
formatDuration(t.uptime)
],
[
_("Load Average"),
formatLoad(t.load)
]
].flatMap(([t, r])=>[
jsx("div", {
class: "fluent-dashboard__details-label",
children: t
}),
jsx("div", {
class: "fluent-dashboard__details-value",
children: r instanceof Node ? r : b(r)
})
])
}));
}
function I(a) {
let s = [
[
_("Memory"),
a.memory
],
[
_("Swap"),
a.swap
],
[
_("Root storage"),
a.root
],
[
_("Temporary storage"),
a.tmp
]
];
return A("resources", _("Resources"), s.map(([a, s])=>{
let n, i;
return n = Math.max(0, s.total - s.free), i = usedPercent(s), jsxs("div", {
class: "fluent-dashboard__meter",
children: [
jsxs("div", {
class: "fluent-dashboard__meter-label",
children: [
jsx("span", {
children: a
}),
jsx("span", {
children: `${formatBytes(n)} / ${formatBytes(s.total)}`
})
]
}),
jsx("div", {
class: "fluent-dashboard__meter-track",
role: "progressbar",
"aria-valuemin": 0,
"aria-valuemax": 100,
"aria-valuenow": Math.round(i),
children: jsx("span", {
class: "fluent-dashboard__meter-value",
style: `width:${i.toFixed(1)}%`
})
})
]
});
}));
}
function N(e) {
let t = [
...e.wan4.map((e)=>[
"IPv4",
e
]),
...e.wan6.map((e)=>[
"IPv6",
e
])
];
return A("internet", _("Internet"), S([
_("Family"),
_("Interface"),
_("Status"),
_("Protocol"),
_("Address"),
_("Gateway"),
_("Uptime")
], t.map(([e, t])=>[
e,
t.name,
v(t.up),
t.protocol,
t.addresses.join(", "),
t.gateway,
formatDuration(t.uptime)
]), _("No Internet connection information is available.")), !0);
}
function D(e) {
return A("interfaces", _("Interfaces"), S([
_("Interface"),
_("Device"),
_("Status"),
_("Address"),
_("Received"),
_("Transmitted")
], e.map((e)=>[
e.name,
e.device,
v(e.up),
e.addresses.join(", "),
formatBytes(e.rx),
formatBytes(e.tx)
]), _("No network interfaces were found.")), !0);
}
function k(t) {
return A("wireless", _("Wireless"), S([
_("Radio"),
_("SSID"),
_("Status"),
_("Mode"),
_("Channel"),
_("Encryption")
], t.map((t)=>[
jsx("span", {
title: t.hardware,
children: b(t.radio)
}),
t.ssid,
v(t.up),
t.mode,
null === t.channel ? "-" : t.frequency ? `${t.channel} (${t.frequency} GHz)` : t.channel,
t.encryption
]), _("No wireless radios were found.")), !0);
}
function C(e) {
return A("leases", _("Active DHCP Leases"), S([
_("Family"),
_("Hostname"),
_("Address"),
_("MAC / DUID"),
_("Expires")
], e.map((e)=>{
var t;
return [
e.family,
e.hostname,
e.address,
e.identifier,
!1 === (t = e.expires) ? _("Unlimited") : t <= 0 ? _("Expired") : formatDuration(t)
];
}), _("There are no active DHCP leases.")), !0);
}
function P(e) {
return e > 0 ? `${(e / 1000).toFixed(1)} ${_("Mbit/s")}` : "-";
}
function M(e) {
return A("clients", _("Wireless Clients"), S([
_("Network"),
_("Host"),
_("MAC address"),
_("Signal"),
_("RX / TX Rate")
], e.map((e)=>[
e.network,
e.hostname,
e.mac,
null === e.noise ? `${e.signal} dBm` : `${e.signal} / ${e.noise} dBm`,
`${P(e.rxRate)} / ${P(e.txRate)}`
]), _("No wireless clients are connected.")), !0);
}
class $ extends L.view {
handleSave = null;
handleSaveApply = null;
handleReset = null;
root = null;
load() {
return Promise.all([
g(),
w()
]);
}
replaceSection(e, t) {
this.root?.querySelector(`[data-dashboard-section="${e}"]`)?.replaceWith(t);
}
async refreshSummary() {
if (!this.root?.isConnected) return;
let e = await g();
if (!this.root?.isConnected) return;
this.replaceSection("system", x(e.system)), this.replaceSection("resources", I(e.system)), this.replaceSection("internet", N(e)), this.replaceSection("interfaces", D(e.interfaces)), this.replaceSection("wireless", k(e.wireless));
let t = this.root.querySelector("[data-dashboard-updated]");
t && (t.textContent = e.updatedAt.toLocaleTimeString());
}
async refreshDetails() {
if (!this.root?.isConnected) return;
let e = await w();
this.root?.isConnected && (this.replaceSection("leases", C(e.leases)), this.replaceSection("clients", M(e.clients)));
}
render([r, a]) {
return document.getElementById("fluent-dashboard-styles") || document.head.appendChild(jsx("link", {
id: "fluent-dashboard-styles",
rel: "stylesheet",
href: L.resource("view/fluentdashboard/index.css")
})), this.root = jsxs("div", {
class: "fluent-dashboard",
children: [
jsxs("div", {
class: "fluent-dashboard__header",
children: [
jsxs("div", {
children: [
jsx("h1", {
children: _("Fluent Dashboard")
}),
jsx("p", {
children: _("A live overview of this OpenWrt device.")
})
]
}),
jsxs("p", {
class: "fluent-dashboard__updated",
children: [
_("Updated"),
" ",
jsx("time", {
"data-dashboard-updated": "",
children: r.updatedAt.toLocaleTimeString()
})
]
})
]
}),
jsxs("div", {
class: "fluent-dashboard__grid",
children: [
x(r.system),
I(r.system),
N(r),
D(r.interfaces),
k(r.wireless),
C(a.leases),
M(a.clients)
]
})
]
}), poll.add(()=>this.refreshSummary(), 5), poll.add(()=>this.refreshDetails(), 15), this.root;
}
}
const main = $;
return main;