dsh-folia 4.5.0 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +113 -32
- package/lib/index.js +9 -26
- package/lib/route-registry.js +115 -0
- package/package.json +3 -2
package/lib/client.js
CHANGED
|
@@ -666,6 +666,85 @@ window.__ModuleLoader__.load({
|
|
|
666
666
|
"裁切失败": "Cropping failed",
|
|
667
667
|
"读不出图片尺寸": "Could not read the image dimensions",
|
|
668
668
|
|
|
669
|
+
|
|
670
|
+
// ── 开机动画 / 桌宠(本轮新增界面)──────────────────────────────────────
|
|
671
|
+
// 这两组文案只有几十条,并进 panel 域最省;将来若继续长,再按 fonts/glass 的做法拆域。
|
|
672
|
+
"已下载:{name}": "Downloaded: {name}",
|
|
673
|
+
"已选中「{name}」,下次启动生效": "Selected \"{name}\" — takes effect on next start",
|
|
674
|
+
"已删除:{name}": "Removed: {name}",
|
|
675
|
+
"开机动画已开启": "Boot animation enabled",
|
|
676
|
+
"开机动画已关闭": "Boot animation disabled",
|
|
677
|
+
"已保存": "Saved",
|
|
678
|
+
"操作失败:{code}({detail})": "Action failed: {code} ({detail})",
|
|
679
|
+
"操作失败:{code}": "Action failed: {code}",
|
|
680
|
+
"请求失败:{msg}": "Request failed: {msg}",
|
|
681
|
+
"网络错误": "network error",
|
|
682
|
+
"🎬 开机动画": "🎬 Boot animation",
|
|
683
|
+
"启用(下次启动播放)": "Enable (plays at next start)",
|
|
684
|
+
"当前:{name}": "Current: {name}",
|
|
685
|
+
"未选择": "None selected",
|
|
686
|
+
"候选清单读不到:{msg}": "Cannot read the candidate list: {msg}",
|
|
687
|
+
"进入方式": "Enter mode",
|
|
688
|
+
"播完再进(默认)": "Play to the end (default)",
|
|
689
|
+
"界面就绪就进": "Enter once the UI is ready",
|
|
690
|
+
"点一下才进": "Enter on click",
|
|
691
|
+
"淡出时长": "Fade out",
|
|
692
|
+
"0.4 秒": "0.4s",
|
|
693
|
+
"0.6 秒": "0.6s",
|
|
694
|
+
"0.9 秒": "0.9s",
|
|
695
|
+
"1.2 秒": "1.2s",
|
|
696
|
+
"1.6 秒": "1.6s",
|
|
697
|
+
"带声音": "With sound",
|
|
698
|
+
"最短显示": "Minimum on screen",
|
|
699
|
+
"不限制": "No minimum",
|
|
700
|
+
"2 秒": "2s",
|
|
701
|
+
"最长停留": "Maximum on screen",
|
|
702
|
+
"10 秒": "10s",
|
|
703
|
+
"20 秒(默认)": "20s (default)",
|
|
704
|
+
"30 秒": "30s",
|
|
705
|
+
"60 秒": "60s",
|
|
706
|
+
"已下载 {done} / {total}": "Downloaded {done} / {total}",
|
|
707
|
+
"下载中… {s}s": "Downloading… {s}s",
|
|
708
|
+
"下载": "Download",
|
|
709
|
+
"使用中": "In use",
|
|
710
|
+
"没有可用的开机动画": "No boot animations available",
|
|
711
|
+
"读取失败:{msg}": "Load failed: {msg}",
|
|
712
|
+
"已安装并启用:{name}": "Installed and enabled: {name}",
|
|
713
|
+
"已卸载:{name}": "Uninstalled: {name}",
|
|
714
|
+
"已导入并启用:{name}": "Imported and enabled: {name}",
|
|
715
|
+
"正在读取桌宠…": "Loading pets…",
|
|
716
|
+
"🐾 桌宠": "🐾 Desktop pet",
|
|
717
|
+
"显示桌宠": "Show pet",
|
|
718
|
+
"把桌宠挪回右下角": "Move the pet back to the bottom-right corner",
|
|
719
|
+
"位置已复位(下一次出现回到右下角)": "Position reset — it returns to the bottom-right next time",
|
|
720
|
+
"复位位置": "Reset position",
|
|
721
|
+
"大小": "Size",
|
|
722
|
+
"卸载": "Uninstall",
|
|
723
|
+
"{cols} 列 · {size}": "{cols} cols · {size}",
|
|
724
|
+
"还没有安装任何桌宠 —— 从下面的站点清单里挑一只。": "No pets installed yet — pick one from the site list below.",
|
|
725
|
+
"本机可导入": "Importable from this machine",
|
|
726
|
+
"来自其它宠物插件 · {size}": "From another pet plugin · {size}",
|
|
727
|
+
"站点桌宠": "Pets on the site",
|
|
728
|
+
"安装": "Install",
|
|
729
|
+
"安装中…": "Installing…",
|
|
730
|
+
"导入": "Import",
|
|
731
|
+
"试演动作": "Try an action",
|
|
732
|
+
"闲置": "Idle",
|
|
733
|
+
"向右走": "Walk right",
|
|
734
|
+
"向左走": "Walk left",
|
|
735
|
+
"挥手": "Wave",
|
|
736
|
+
"跳一下": "Jump",
|
|
737
|
+
"失败": "Fail",
|
|
738
|
+
"发呆": "Wait",
|
|
739
|
+
"干活": "Work",
|
|
740
|
+
"完成": "Done",
|
|
741
|
+
"再大一点": "Bigger",
|
|
742
|
+
"再小一点": "Smaller",
|
|
743
|
+
"回右下角": "Back to corner",
|
|
744
|
+
"不显示它": "Hide it",
|
|
745
|
+
"站点上还没有桌宠。": "No pets on the site yet.",
|
|
746
|
+
"站点不可达": "Site unreachable",
|
|
747
|
+
"预览尺寸": "Preview-size",
|
|
669
748
|
};
|
|
670
749
|
|
|
671
750
|
// ── 内联模块:src/i18n-copy-picker.js —— 英文词表域:壁纸选择器与快捷面板(picker-* · quick-panel · sidebar-right · theme-follow)(构建期注入;勿手改本段)──
|
|
@@ -17022,18 +17101,18 @@ window.__ModuleLoader__.load({
|
|
|
17022
17101
|
const resp = await apiPostJson("/boot/" + kind, payload || {}, { parse: "always" });
|
|
17023
17102
|
const r = (resp && resp.data) || null;
|
|
17024
17103
|
if (resp && resp.ok && r && r.ok) {
|
|
17025
|
-
if (kind === "download") setNote({ ok: true, text: weT("已下载:"
|
|
17026
|
-
if (kind === "select") setNote({ ok: true, text: weT("已选中「"
|
|
17027
|
-
if (kind === "remove") setNote({ ok: true, text: weT("已删除:"
|
|
17104
|
+
if (kind === "download") setNote({ ok: true, text: weT("已下载:{name}", { name: label }) });
|
|
17105
|
+
if (kind === "select") setNote({ ok: true, text: weT("已选中「{name}」,下次启动生效", { name: label }) });
|
|
17106
|
+
if (kind === "remove") setNote({ ok: true, text: weT("已删除:{name}", { name: label }) });
|
|
17028
17107
|
if (kind === "toggle") setNote({ ok: true, text: payload.enabled ? weT("开机动画已开启") : weT("开机动画已关闭") });
|
|
17029
17108
|
if (kind === "settings") setNote({ ok: true, text: weT("已保存") });
|
|
17030
17109
|
await load(true);
|
|
17031
17110
|
} else {
|
|
17032
17111
|
const code = (r && r.error) || "failed";
|
|
17033
|
-
setNote({ ok: false, text:
|
|
17112
|
+
setNote({ ok: false, text: r && r.detail ? weT("操作失败:{code}({detail})", { code, detail: String(r.detail) }) : weT("操作失败:{code}", { code }) });
|
|
17034
17113
|
}
|
|
17035
17114
|
} catch (e) {
|
|
17036
|
-
setNote({ ok: false, text: weT("请求失败:"
|
|
17115
|
+
setNote({ ok: false, text: weT("请求失败:{msg}", { msg: (e && e.message) ? e.message : weT("网络错误") }) });
|
|
17037
17116
|
} finally {
|
|
17038
17117
|
setBusy("");
|
|
17039
17118
|
}
|
|
@@ -17047,7 +17126,7 @@ window.__ModuleLoader__.load({
|
|
|
17047
17126
|
const children = [];
|
|
17048
17127
|
|
|
17049
17128
|
children.push(React.createElement("div", { className: "we-picker__section-head", key: "head" },
|
|
17050
|
-
React.createElement("span", { className: "we-picker__section-label" }, weT("🎬 开机动画")
|
|
17129
|
+
React.createElement("span", { className: "we-picker__section-label" }, weT("🎬 开机动画")),
|
|
17051
17130
|
React.createElement("button", {
|
|
17052
17131
|
type: "button", className: "we-picker__btn", style: { marginLeft: "auto" },
|
|
17053
17132
|
disabled: Boolean(busy), onClick: () => load(true),
|
|
@@ -17066,7 +17145,7 @@ window.__ModuleLoader__.load({
|
|
|
17066
17145
|
weT("启用(下次启动播放)"),
|
|
17067
17146
|
),
|
|
17068
17147
|
React.createElement("span", { className: "we-picker__hint" },
|
|
17069
|
-
activeId ? weT("当前:"
|
|
17148
|
+
activeId ? weT("当前:{name}", { name: (clips.find((c) => c.id === activeId) || {}).title || activeId }) : weT("未选择")),
|
|
17070
17149
|
));
|
|
17071
17150
|
|
|
17072
17151
|
children.push(React.createElement("div", { key: "hint", className: "we-picker__hint", style: { margin: "2px 0 6px", opacity: 0.75 } },
|
|
@@ -17074,7 +17153,7 @@ window.__ModuleLoader__.load({
|
|
|
17074
17153
|
|
|
17075
17154
|
if (state && state.error && !clips.length) {
|
|
17076
17155
|
children.push(React.createElement("div", { key: "err", className: "we-picker__hint", style: { color: "#ff9a9a" } },
|
|
17077
|
-
weT("候选清单读不到:"
|
|
17156
|
+
weT("候选清单读不到:{msg}", { msg: String(state.error) })));
|
|
17078
17157
|
}
|
|
17079
17158
|
|
|
17080
17159
|
if (note) {
|
|
@@ -17107,7 +17186,7 @@ window.__ModuleLoader__.load({
|
|
|
17107
17186
|
React.createElement("option", { value: "click" }, weT("点一下才进")),
|
|
17108
17187
|
),
|
|
17109
17188
|
),
|
|
17110
|
-
opt(weT("淡出时长"), "fadeMs", [[400, "0.4 秒"], [600, "0.6 秒"], [900, "0.9 秒"], [1200, "1.2 秒"], [1600, "1.6 秒"]]),
|
|
17189
|
+
opt(weT("淡出时长"), "fadeMs", [[400, weT("0.4 秒")], [600, weT("0.6 秒")], [900, weT("0.9 秒")], [1200, weT("1.2 秒")], [1600, weT("1.6 秒")]]),
|
|
17111
17190
|
// 声音:默认关(带声音的自动播放会被浏览器策略拦)。打开后若仍被拦,
|
|
17112
17191
|
// 脚本会自动退回静音播放 —— 画面优先,不会黑屏。
|
|
17113
17192
|
React.createElement("label", { style: { display: "flex", gap: "6px", alignItems: "center", fontSize: "12px" } },
|
|
@@ -17119,13 +17198,13 @@ window.__ModuleLoader__.load({
|
|
|
17119
17198
|
}),
|
|
17120
17199
|
weT("带声音"),
|
|
17121
17200
|
),
|
|
17122
|
-
opt(weT("最短显示"), "minShowMs", [[0, "不限制"], [600, "0.6 秒"], [1200, "1.2 秒"], [2000, "2 秒"]]),
|
|
17123
|
-
opt(weT("最长停留"), "maxHoldMs", [[10000, "10 秒"], [20000, "20 秒(默认)"], [30000, "30 秒"], [60000, "60 秒"]]),
|
|
17201
|
+
opt(weT("最短显示"), "minShowMs", [[0, weT("不限制")], [600, weT("0.6 秒")], [1200, weT("1.2 秒")], [2000, weT("2 秒")]]),
|
|
17202
|
+
opt(weT("最长停留"), "maxHoldMs", [[10000, weT("10 秒")], [20000, weT("20 秒(默认)")], [30000, weT("30 秒")], [60000, weT("60 秒")]]),
|
|
17124
17203
|
));
|
|
17125
17204
|
}
|
|
17126
17205
|
|
|
17127
17206
|
children.push(React.createElement("div", { key: "count", className: "we-picker__hint", style: { margin: "2px 0 4px" } },
|
|
17128
|
-
weT("已下载
|
|
17207
|
+
weT("已下载 {done} / {total}", { done: installedCount, total: clips.length })));
|
|
17129
17208
|
|
|
17130
17209
|
if (clips.length) {
|
|
17131
17210
|
children.push(React.createElement("div", { key: "grid", style: BOOT_STYLE.grid },
|
|
@@ -17141,7 +17220,7 @@ window.__ModuleLoader__.load({
|
|
|
17141
17220
|
}, label);
|
|
17142
17221
|
const actions = [];
|
|
17143
17222
|
if (!c.installed) {
|
|
17144
|
-
actions.push(btn(isBusy ?
|
|
17223
|
+
actions.push(btn(isBusy ? weT("下载中… {s}s", { s: tick }) : weT("下载"), () => act("download", { id: c.id }, c.title), false, true));
|
|
17145
17224
|
} else if (!isActive) {
|
|
17146
17225
|
actions.push(btn(weT("使用"), () => act("select", { id: c.id }, c.title), false, true));
|
|
17147
17226
|
actions.push(btn(weT("删除"), () => act("remove", { id: c.id }, c.title), isBusy, false));
|
|
@@ -17227,9 +17306,9 @@ window.__ModuleLoader__.load({
|
|
|
17227
17306
|
if (imDoc && imDoc.ok) setImportable(imDoc.pets || []);
|
|
17228
17307
|
} catch { /* 无外部目录是正常情况 */ }
|
|
17229
17308
|
}
|
|
17230
|
-
else setNote({ ok: false, text: weT("读取失败:"
|
|
17309
|
+
else setNote({ ok: false, text: weT("读取失败:{msg}", { msg: (resp && resp.error) || "unknown" }) });
|
|
17231
17310
|
} catch (e) {
|
|
17232
|
-
setNote({ ok: false, text: weT("请求失败:"
|
|
17311
|
+
setNote({ ok: false, text: weT("请求失败:{msg}", { msg: (e && e.message) || weT("网络错误") }) });
|
|
17233
17312
|
}
|
|
17234
17313
|
}, []);
|
|
17235
17314
|
|
|
@@ -17242,17 +17321,17 @@ window.__ModuleLoader__.load({
|
|
|
17242
17321
|
const resp = await apiPostJson("/pets/" + kind, payload || {}, { parse: "always" });
|
|
17243
17322
|
const r = (resp && resp.data) || null;
|
|
17244
17323
|
if (resp && resp.ok && r && r.ok) {
|
|
17245
|
-
if (kind === "install") setNote({ ok: true, text: weT("已安装并启用:"
|
|
17246
|
-
if (kind === "remove") setNote({ ok: true, text: weT("已卸载:"
|
|
17247
|
-
if (kind === "import") setNote({ ok: true, text: weT("已导入并启用:"
|
|
17324
|
+
if (kind === "install") setNote({ ok: true, text: weT("已安装并启用:{name}", { name: label || "" }) });
|
|
17325
|
+
if (kind === "remove") setNote({ ok: true, text: weT("已卸载:{name}", { name: label || "" }) });
|
|
17326
|
+
if (kind === "import") setNote({ ok: true, text: weT("已导入并启用:{name}", { name: label || "" }) });
|
|
17248
17327
|
if (kind === "config") setNote({ ok: true, text: weT("已保存") });
|
|
17249
17328
|
await load(true);
|
|
17250
17329
|
} else {
|
|
17251
17330
|
const code = (r && r.error) || "failed";
|
|
17252
|
-
setNote({ ok: false, text: weT("操作失败:"
|
|
17331
|
+
setNote({ ok: false, text: weT("操作失败:{code}", { code }) });
|
|
17253
17332
|
}
|
|
17254
17333
|
} catch (e) {
|
|
17255
|
-
setNote({ ok: false, text: weT("请求失败:"
|
|
17334
|
+
setNote({ ok: false, text: weT("请求失败:{msg}", { msg: (e && e.message) || weT("网络错误") }) });
|
|
17256
17335
|
} finally {
|
|
17257
17336
|
setBusy("");
|
|
17258
17337
|
}
|
|
@@ -17272,7 +17351,7 @@ window.__ModuleLoader__.load({
|
|
|
17272
17351
|
|
|
17273
17352
|
children.push(React.createElement("div", { className: "we-picker__section-head", key: "head" },
|
|
17274
17353
|
React.createElement("span", { className: "we-picker__section-label" },
|
|
17275
|
-
weT("🐾 桌宠")
|
|
17354
|
+
weT("🐾 桌宠")),
|
|
17276
17355
|
React.createElement("button", {
|
|
17277
17356
|
type: "button", className: "we-picker__btn", style: { marginLeft: "auto" },
|
|
17278
17357
|
disabled: Boolean(busy), onClick: () => load(true),
|
|
@@ -17372,7 +17451,7 @@ window.__ModuleLoader__.load({
|
|
|
17372
17451
|
}),
|
|
17373
17452
|
React.createElement("div", { style: PET_STYLE.title }, p.name),
|
|
17374
17453
|
React.createElement("div", { style: PET_STYLE.meta },
|
|
17375
|
-
|
|
17454
|
+
weT("{cols} 列 · {size}", { cols: p.columns || 8, size: p.cell ? p.cell.width + "×" + p.cell.height : "" })),
|
|
17376
17455
|
React.createElement("div", { style: PET_STYLE.row }, actions),
|
|
17377
17456
|
);
|
|
17378
17457
|
})
|
|
@@ -17383,7 +17462,7 @@ window.__ModuleLoader__.load({
|
|
|
17383
17462
|
if (importable.length) {
|
|
17384
17463
|
children.push(React.createElement("div", { key: "impHead", className: "we-picker__section-head", style: { marginTop: "14px" } },
|
|
17385
17464
|
React.createElement("span", { className: "we-picker__section-label" },
|
|
17386
|
-
weT("本机可导入")
|
|
17465
|
+
weT("本机可导入")),
|
|
17387
17466
|
));
|
|
17388
17467
|
children.push(React.createElement("div", { key: "imp", style: PET_STYLE.grid },
|
|
17389
17468
|
importable.map((it) => {
|
|
@@ -17391,7 +17470,7 @@ window.__ModuleLoader__.load({
|
|
|
17391
17470
|
return React.createElement("div", { key: "imp:" + it.id, style: PET_STYLE.card },
|
|
17392
17471
|
React.createElement("div", { style: PET_STYLE.title }, it.name || it.id),
|
|
17393
17472
|
React.createElement("div", { style: PET_STYLE.meta },
|
|
17394
|
-
weT("来自其它宠物插件 · "
|
|
17473
|
+
weT("来自其它宠物插件 · {size}", { size: petFmtBytes(it.bytes) })),
|
|
17395
17474
|
React.createElement("div", { style: PET_STYLE.row },
|
|
17396
17475
|
btn(isBusy ? weT("导入中…") : weT("导入"), () => act("import", { id: it.id }, it.name || it.id), Boolean(busy))),
|
|
17397
17476
|
);
|
|
@@ -17403,7 +17482,7 @@ window.__ModuleLoader__.load({
|
|
|
17403
17482
|
const available = site.filter((s) => !installedIds.has(s.id));
|
|
17404
17483
|
children.push(React.createElement("div", { key: "siteHead", className: "we-picker__section-head", style: { marginTop: "14px" } },
|
|
17405
17484
|
React.createElement("span", { className: "we-picker__section-label" },
|
|
17406
|
-
weT("站点桌宠")
|
|
17485
|
+
weT("站点桌宠")),
|
|
17407
17486
|
state.siteError
|
|
17408
17487
|
? React.createElement("span", { className: "we-picker__hint", style: { marginLeft: "auto" } }, weT("站点不可达"))
|
|
17409
17488
|
: null,
|
|
@@ -17883,9 +17962,11 @@ window.__ModuleLoader__.load({
|
|
|
17883
17962
|
"background:rgba(28,32,40,0.96)", "border:1px solid rgba(255,255,255,0.16)",
|
|
17884
17963
|
"box-shadow:0 10px 30px rgba(0,0,0,0.45)", "backdrop-filter:blur(8px)",
|
|
17885
17964
|
].join(";");
|
|
17886
|
-
|
|
17965
|
+
// 九条轨道的中文名是**界面文案**(右键菜单里给用户点的)⇒ 必须走 weT;
|
|
17966
|
+
// 上面 PET_ACTIVITY_TEXTS 那几条是拿去匹配页面文字的,翻不动,登记在 i18n 的 VALUE_ALLOW。
|
|
17967
|
+
const titles = [weT("闲置"), weT("向右走"), weT("向左走"), weT("挥手"), weT("跳一下"), weT("失败"), weT("发呆"), weT("干活"), weT("完成")];
|
|
17887
17968
|
const head = document.createElement("div");
|
|
17888
|
-
head.textContent = "试演动作";
|
|
17969
|
+
head.textContent = weT("试演动作");
|
|
17889
17970
|
head.style.cssText = "padding:4px 10px 6px;opacity:0.55;font-size:11px";
|
|
17890
17971
|
menu.appendChild(head);
|
|
17891
17972
|
titles.forEach((name, r) => {
|
|
@@ -17900,15 +17981,15 @@ window.__ModuleLoader__.load({
|
|
|
17900
17981
|
const hr = document.createElement("div");
|
|
17901
17982
|
hr.style.cssText = "height:1px;margin:6px 0;background:rgba(255,255,255,0.14)";
|
|
17902
17983
|
menu.appendChild(hr);
|
|
17903
|
-
menu.appendChild(buildMenuItem("再大一点", () => { applyScale(scale + 0.2); closeMenu(); }));
|
|
17904
|
-
menu.appendChild(buildMenuItem("再小一点", () => { applyScale(Math.max(0.5, scale - 0.2)); closeMenu(); }));
|
|
17905
|
-
menu.appendChild(buildMenuItem("回右下角", () => {
|
|
17984
|
+
menu.appendChild(buildMenuItem(weT("再大一点"), () => { applyScale(scale + 0.2); closeMenu(); }));
|
|
17985
|
+
menu.appendChild(buildMenuItem(weT("再小一点"), () => { applyScale(Math.max(0.5, scale - 0.2)); closeMenu(); }));
|
|
17986
|
+
menu.appendChild(buildMenuItem(weT("回右下角"), () => {
|
|
17906
17987
|
try { localStorage.removeItem("wePetPos"); } catch { /* ignore */ }
|
|
17907
17988
|
layerEl.style.left = ""; layerEl.style.top = "";
|
|
17908
17989
|
layerEl.style.right = "24px"; layerEl.style.bottom = "16px";
|
|
17909
17990
|
closeMenu();
|
|
17910
17991
|
}));
|
|
17911
|
-
menu.appendChild(buildMenuItem("不显示它", () => {
|
|
17992
|
+
menu.appendChild(buildMenuItem(weT("不显示它"), () => {
|
|
17912
17993
|
// 只隐藏本地这一层;真正的开关在面板里(下一次轮询会被尊重)。
|
|
17913
17994
|
if (layerEl) layerEl.style.display = "none";
|
|
17914
17995
|
closeMenu();
|
|
@@ -18482,7 +18563,7 @@ window.__ModuleLoader__.load({
|
|
|
18482
18563
|
? React.createElement("span", {
|
|
18483
18564
|
className: "we-qp__card-badge",
|
|
18484
18565
|
style: { right: "auto", left: "6px", background: "rgba(255,196,120,0.9)", color: "#3a2600" },
|
|
18485
|
-
title: weT("这张图只有 "
|
|
18566
|
+
title: weT("这张图只有 {size},当壁纸会糊 —— 建议在站点上传原图", { size: w.width + "×" + w.height }),
|
|
18486
18567
|
}, weT("预览尺寸"))
|
|
18487
18568
|
: null,
|
|
18488
18569
|
w.id === sel.id && React.createElement("span", { className: "we-qp__card-badge" }, weT("当前")),
|
package/lib/index.js
CHANGED
|
@@ -77,11 +77,7 @@ import { assertSubpackagesResolvable, assertSubpackagesResolvableInHost, httpVie
|
|
|
77
77
|
// `extOf` 反向传给会话段(清单载荷的容器后缀判据 ⇒ 见该文件头)。
|
|
78
78
|
import { createHostSession } from './kernel/host-session.js';
|
|
79
79
|
export { extOf };
|
|
80
|
-
import {
|
|
81
|
-
import { registerModulesRoutes, warmModulesCache } from './routes/modules.js';
|
|
82
|
-
import { registerBootRoutes } from './routes/boot.js';
|
|
83
|
-
import { registerSiteThumbRoutes } from './routes/site-thumb.js';
|
|
84
|
-
import { registerPetsRoutes } from './routes/pets.js';
|
|
80
|
+
import { registerFeatureRoutes, warmFeatureCaches } from './route-registry.js';
|
|
85
81
|
import { installBootInjection } from './boot/index.js';
|
|
86
82
|
import { registerDiagRoutes } from './routes/diag.js';
|
|
87
83
|
// 遮挡消息桥族(上游新增域模块):hub 处理 x11 遮挡探针,路由对外暴露状态/控制面。
|
|
@@ -291,30 +287,17 @@ export function apply(ctx) {
|
|
|
291
287
|
}
|
|
292
288
|
}
|
|
293
289
|
|
|
294
|
-
// 1.
|
|
295
|
-
//
|
|
296
|
-
//
|
|
297
|
-
//
|
|
298
|
-
|
|
299
|
-
disposers, base: BASE, mediaOriginApi, buildInventory,
|
|
290
|
+
// 1. 功能族路由:**注册表驱动**(lib/route-registry.js)。
|
|
291
|
+
// 装配点不再逐个点名 —— 加一个功能只改注册表,这里永远是同一次调用。
|
|
292
|
+
// 已迁移:inventory / modules / boot / site-thumb / pets;
|
|
293
|
+
// 未迁移的族列在 INLINE_ROUTE_FAMILIES(棘轮,只许缩小),仍由下面的显式调用装载。
|
|
294
|
+
registerFeatureRoutes(webServer, {
|
|
295
|
+
disposers, base: BASE, log, mediaOriginApi, buildInventory,
|
|
296
|
+
componentProbe, ensureImagePreview,
|
|
300
297
|
});
|
|
301
|
-
// 数据模块索引(站点侧声明「有哪些数据模块」)→ lib/routes/modules.js。
|
|
302
|
-
// 与 inventory 同一套站点基址;读不到就退回内置集合,绝不让面板因为索引文件而空白。
|
|
303
|
-
registerModulesRoutes(webServer, { disposers, base: BASE });
|
|
304
|
-
|
|
305
|
-
// 开机动画 → lib/routes/boot.js(HTTP 面)+ lib/boot/index.js(注入与屏幕脚本)。
|
|
306
|
-
// 注入是「关就一行都不注」:没有选片时不产生任何脚本、不产生任何请求。
|
|
307
|
-
registerBootRoutes(webServer, { disposers, base: BASE, log });
|
|
308
|
-
|
|
309
|
-
// 站点图片缩略图代理 → lib/routes/site-thumb.js。卡片用 320px 小图,不再每张下 500 KB 原图。
|
|
310
|
-
registerSiteThumbRoutes(webServer, { disposers, base: BASE, ensureImagePreview, log });
|
|
311
298
|
|
|
312
299
|
// 启动预热(有意不阻塞):站点清单与模块索引各拉一次,让第一次打开面板不冷。
|
|
313
|
-
|
|
314
|
-
registerPetsRoutes(webServer, { disposers, base: BASE, log });
|
|
315
|
-
|
|
316
|
-
warmInventorySiteCache();
|
|
317
|
-
warmModulesCache();
|
|
300
|
+
warmFeatureCaches();
|
|
318
301
|
installBootInjection(ctx, (m) => { try { log(m); } catch { /* ignore */ } });
|
|
319
302
|
|
|
320
303
|
// 3./3a. 派生媒体族路由(源元信息 / 抽帧转码 + 进度 / 视频缩略图)→ lib/routes/media-derived.js。
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/route-registry.js — **功能路由注册表**(宿主侧)。
|
|
3
|
+
*
|
|
4
|
+
* 为什么要它:`lib/index.js` 过去逐个 `registerXxxRoutes(webServer, {…})`,加一个功能就得
|
|
5
|
+
* 在那个 400 行装配文件里挑一处插进去,并保证依赖对象拼对。客户端已经收口成注册表
|
|
6
|
+
* (`src/features.js`),宿主侧对称过来,装配点就只剩一次循环。
|
|
7
|
+
*
|
|
8
|
+
* 纪律(由 `test/verify-route-registry.mjs` 机器校验):
|
|
9
|
+
* · 本表里写出的族**必须在** `lib/routes/` 里真实存在,且 `deps` 只从传入的上下文取键;
|
|
10
|
+
* · `INLINE_ROUTE_FAMILIES` 是**尚未迁移**的族(大依赖:mediaMap / hub / 转码器等),
|
|
11
|
+
* 它是**棘轮**:只许缩小 —— 迁一个删一个,不许往回调;
|
|
12
|
+
* · 两表并集必须覆盖 `lib/routes/*.js`(不许有第三处偷偷注册的族)。
|
|
13
|
+
*
|
|
14
|
+
* 迁移一个族的步骤:把 `lib/index.js` 里那段调用删掉 → 在 `FEATURE_ROUTE_FAMILIES` 加一条 →
|
|
15
|
+
* 从 `INLINE_ROUTE_FAMILIES` 删掉 → 跑守卫。
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { registerInventoryRoutes, warmInventorySiteCache } from './routes/inventory.js';
|
|
19
|
+
import { registerModulesRoutes, warmModulesCache } from './routes/modules.js';
|
|
20
|
+
import { registerBootRoutes } from './routes/boot.js';
|
|
21
|
+
import { registerSiteThumbRoutes } from './routes/site-thumb.js';
|
|
22
|
+
import { registerPetsRoutes } from './routes/pets.js';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 已迁移的功能族。
|
|
26
|
+
*
|
|
27
|
+
* `deps(c)` 只从上下文取键 —— 这样"某个族需要什么"是**声明式**的,装配点不需要知道。
|
|
28
|
+
* 上下文由门面按需提供(见 lib/index.js 的 `registerFeatureRoutes(webServer, { … })`)。
|
|
29
|
+
*
|
|
30
|
+
* @type {{ id: string, file: string, register: Function, deps: (c: any) => any, warm?: Function }[]}
|
|
31
|
+
*/
|
|
32
|
+
export const FEATURE_ROUTE_FAMILIES = [
|
|
33
|
+
{
|
|
34
|
+
id: 'inventory',
|
|
35
|
+
file: 'lib/routes/inventory.js',
|
|
36
|
+
register: registerInventoryRoutes,
|
|
37
|
+
// 清单族需要"本机清单构建器 + 组件行状态探针 + 媒体源访问器":三者都是门面现算的,
|
|
38
|
+
// 所以只能由调用方传进来(族自己拿不到)。
|
|
39
|
+
deps: (c) => ({
|
|
40
|
+
disposers: c.disposers,
|
|
41
|
+
base: c.base,
|
|
42
|
+
mediaOriginApi: c.mediaOriginApi,
|
|
43
|
+
buildInventory: c.buildInventory,
|
|
44
|
+
componentsProbe: c.componentProbe,
|
|
45
|
+
}),
|
|
46
|
+
warm: warmInventorySiteCache,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: 'modules',
|
|
50
|
+
file: 'lib/routes/modules.js',
|
|
51
|
+
register: registerModulesRoutes,
|
|
52
|
+
deps: (c) => ({ disposers: c.disposers, base: c.base }),
|
|
53
|
+
warm: warmModulesCache,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: 'boot',
|
|
57
|
+
file: 'lib/routes/boot.js',
|
|
58
|
+
register: registerBootRoutes,
|
|
59
|
+
deps: (c) => ({ disposers: c.disposers, base: c.base, log: c.log }),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 'site-thumb',
|
|
63
|
+
file: 'lib/routes/site-thumb.js',
|
|
64
|
+
register: registerSiteThumbRoutes,
|
|
65
|
+
// 缩略图复用内核的 ffmpeg 套件(ensureImagePreview),不自己 spawn。
|
|
66
|
+
deps: (c) => ({ disposers: c.disposers, base: c.base, ensureImagePreview: c.ensureImagePreview, log: c.log }),
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: 'pets',
|
|
70
|
+
file: 'lib/routes/pets.js',
|
|
71
|
+
register: registerPetsRoutes,
|
|
72
|
+
deps: (c) => ({ disposers: c.disposers, base: c.base, log: c.log }),
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* **尚未迁移**的族(棘轮:只许缩小)。
|
|
78
|
+
*
|
|
79
|
+
* 为什么留在门面里:它们的依赖是内核的活对象(mediaMap / occlusionHub / transcodeKit /
|
|
80
|
+
* sceneFrameSlot / uploadStoreKit …),搬进本表需要把那些对象也传进来,属于另一轮改动。
|
|
81
|
+
* 这张清单存在的意义是**把"还剩多少"写下来**,守卫据此防止有人往回调。
|
|
82
|
+
*/
|
|
83
|
+
export const INLINE_ROUTE_FAMILIES = [
|
|
84
|
+
// 每条写「族 id + 门面里的注册函数名」:两者**不总同形**(presets → registerGlassPresetsRoutes),
|
|
85
|
+
// 靠名字猜会在守卫里制造假红。迁移一个族 = 在 FEATURE_ROUTE_FAMILIES 加一条 + 删这里一行。
|
|
86
|
+
{ id: 'media-bytes', fn: 'registerMediaBytesRoutes' },
|
|
87
|
+
{ id: 'media-derived', fn: 'registerMediaDerivedRoutes' },
|
|
88
|
+
{ id: 'scene-frame', fn: 'registerSceneFrameRoutes' },
|
|
89
|
+
{ id: 'scene-serve', fn: 'registerSceneServeRoutes' },
|
|
90
|
+
{ id: 'scene-media', fn: 'registerSceneMediaRoutes' },
|
|
91
|
+
{ id: 'live-frame', fn: 'registerLiveFrameRoutes' },
|
|
92
|
+
{ id: 'props', fn: 'registerPropsRoutes' },
|
|
93
|
+
{ id: 'occlusion', fn: 'registerOcclusionRoutes' },
|
|
94
|
+
{ id: 'diag', fn: 'registerDiagRoutes' },
|
|
95
|
+
{ id: 'we-assets', fn: 'registerWeAssetsRoutes' },
|
|
96
|
+
{ id: 'upload', fn: 'registerUploadRoutes' },
|
|
97
|
+
{ id: 'presets', fn: 'registerGlassPresetsRoutes' },
|
|
98
|
+
{ id: 'settings', fn: 'registerSettingsRoutes' },
|
|
99
|
+
{ id: 'cache-dir', fn: 'registerCacheDirRoutes' },
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
/** 按注册表挂载全部已迁移族;`warm` 钩子显式调用(预热是有意不阻塞的)。 */
|
|
103
|
+
export function registerFeatureRoutes(webServer, ctx) {
|
|
104
|
+
for (const fam of FEATURE_ROUTE_FAMILIES) {
|
|
105
|
+
fam.register(webServer, fam.deps(ctx));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** 启动预热(站点清单与模块索引各拉一次)。失败静默 —— 预热只是优化。 */
|
|
110
|
+
export function warmFeatureCaches() {
|
|
111
|
+
for (const fam of FEATURE_ROUTE_FAMILIES) {
|
|
112
|
+
if (typeof fam.warm !== 'function') continue;
|
|
113
|
+
try { fam.warm(); } catch { /* 预热失败不影响启动 */ }
|
|
114
|
+
}
|
|
115
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-folia",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.0",
|
|
4
4
|
"description": "Folia 壁纸引擎:把本机 Wallpaper Engine 壁纸搬进 DSH 界面(场景实时渲染 / 视频 / 网页),内置液态玻璃与 Folia 吉祥物,并可从自建站点拉取壁纸。单包形态(组件已合并)。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -156,7 +156,8 @@
|
|
|
156
156
|
"README.en.md",
|
|
157
157
|
"README.beginner.md",
|
|
158
158
|
"lib/components/",
|
|
159
|
-
"lib/features.js"
|
|
159
|
+
"lib/features.js",
|
|
160
|
+
"lib/route-registry.js"
|
|
160
161
|
],
|
|
161
162
|
"dsh": {
|
|
162
163
|
"bundle": {
|