dsh-wallpaper_share 26.9.10 → 26.9.13
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/README.md +74 -18
- package/README.zh-CN.md +20 -10
- package/bin/we-floater.exe +0 -0
- package/docs/encoding-and-release.md +33 -0
- package/lib/client.js +1522 -423
- package/lib/client.js.map +1 -1
- package/lib/index.js +1207 -365
- package/package.json +11 -5
- package/tools/check-package.mjs +28 -0
package/lib/client.js
CHANGED
|
@@ -6,6 +6,121 @@ window.__ModuleLoader__.load({
|
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
let react = require("react");
|
|
8
8
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
9
|
+
//#region src/client/DwpEditorCard.tsx
|
|
10
|
+
function DwpEditorCard() {
|
|
11
|
+
const [phase, setPhase] = (0, react.useState)("checking");
|
|
12
|
+
const [message, setMessage] = (0, react.useState)("");
|
|
13
|
+
const [opened, setOpened] = (0, react.useState)(false);
|
|
14
|
+
const alive = (0, react.useRef)(true);
|
|
15
|
+
const acquiring = (0, react.useRef)(false);
|
|
16
|
+
async function request(install = false) {
|
|
17
|
+
const r = await fetch("/we-sync/editor", install ? {
|
|
18
|
+
method: "POST",
|
|
19
|
+
headers: { "X-Wallpaper-Editor": "install" }
|
|
20
|
+
} : { cache: "no-store" });
|
|
21
|
+
const s = await r.json();
|
|
22
|
+
if (!r.ok) throw new Error(s.error || "无法连接编辑器服务,请刷新后重试。");
|
|
23
|
+
if (alive.current) setPhase(s.installed ? "installed" : s.installing ? "installing" : "available");
|
|
24
|
+
return s.installed;
|
|
25
|
+
}
|
|
26
|
+
(0, react.useEffect)(() => {
|
|
27
|
+
alive.current = true;
|
|
28
|
+
request().catch((e) => {
|
|
29
|
+
if (alive.current) {
|
|
30
|
+
setPhase("error");
|
|
31
|
+
setMessage(String(e.message));
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
return () => {
|
|
35
|
+
alive.current = false;
|
|
36
|
+
};
|
|
37
|
+
}, []);
|
|
38
|
+
(0, react.useEffect)(() => {
|
|
39
|
+
if (phase !== "installing" || acquiring.current) return;
|
|
40
|
+
const timer = setInterval(() => {
|
|
41
|
+
request().catch((e) => {
|
|
42
|
+
if (alive.current) {
|
|
43
|
+
setPhase("error");
|
|
44
|
+
setMessage(String(e.message));
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}, 3e3);
|
|
48
|
+
return () => clearInterval(timer);
|
|
49
|
+
}, [phase]);
|
|
50
|
+
async function acquire() {
|
|
51
|
+
if (acquiring.current) return;
|
|
52
|
+
acquiring.current = true;
|
|
53
|
+
setMessage("");
|
|
54
|
+
setPhase("installing");
|
|
55
|
+
try {
|
|
56
|
+
if (await request(true) && alive.current) setOpened(true);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
if (alive.current) {
|
|
59
|
+
setPhase("error");
|
|
60
|
+
setMessage(e instanceof Error ? e.message : "获取失败,请重试。");
|
|
61
|
+
}
|
|
62
|
+
} finally {
|
|
63
|
+
acquiring.current = false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
67
|
+
className: "wesync-card",
|
|
68
|
+
children: [
|
|
69
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
70
|
+
className: "wesync-sub",
|
|
71
|
+
children: "DWP 创作"
|
|
72
|
+
}),
|
|
73
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
74
|
+
style: {
|
|
75
|
+
display: "flex",
|
|
76
|
+
alignItems: "center",
|
|
77
|
+
gap: 12,
|
|
78
|
+
flexWrap: "wrap"
|
|
79
|
+
},
|
|
80
|
+
children: [
|
|
81
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
82
|
+
className: "wesync-btn",
|
|
83
|
+
disabled: phase === "checking" || phase === "installing",
|
|
84
|
+
onClick: () => phase === "installed" ? setOpened(true) : void acquire(),
|
|
85
|
+
children: phase === "checking" ? "检查编辑器…" : phase === "installing" ? "正在获取壁纸编辑器…" : phase === "installed" ? "打开壁纸编辑器" : "获取壁纸编辑器"
|
|
86
|
+
}),
|
|
87
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
88
|
+
style: {
|
|
89
|
+
opacity: .65,
|
|
90
|
+
fontSize: 12
|
|
91
|
+
},
|
|
92
|
+
children: phase === "installed" ? "已安装 · 可离线创作" : "24 帧创作 · 图像与视频 · 抠图 · 粒子 · 时钟"
|
|
93
|
+
}),
|
|
94
|
+
phase === "installed" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
95
|
+
className: "wesync-btn",
|
|
96
|
+
href: "/we-sync/editor/app",
|
|
97
|
+
target: "_blank",
|
|
98
|
+
rel: "noreferrer",
|
|
99
|
+
children: "独立窗口"
|
|
100
|
+
})
|
|
101
|
+
]
|
|
102
|
+
}),
|
|
103
|
+
message && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
104
|
+
role: "alert",
|
|
105
|
+
children: message
|
|
106
|
+
}),
|
|
107
|
+
opened && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("iframe", {
|
|
108
|
+
title: "壁纸编辑器",
|
|
109
|
+
src: "/we-sync/editor/app",
|
|
110
|
+
style: {
|
|
111
|
+
width: "100%",
|
|
112
|
+
height: "min(850px, 80vh)",
|
|
113
|
+
minHeight: 560,
|
|
114
|
+
border: 0,
|
|
115
|
+
marginTop: 12,
|
|
116
|
+
borderRadius: 8
|
|
117
|
+
},
|
|
118
|
+
allow: "fullscreen"
|
|
119
|
+
})
|
|
120
|
+
]
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
//#endregion
|
|
9
124
|
//#region src/client/GazeLens.ts
|
|
10
125
|
/**
|
|
11
126
|
* GazeLens —— 摄像头眼动追踪(透镜视线源)。
|
|
@@ -245,11 +360,48 @@ window.__ModuleLoader__.load({
|
|
|
245
360
|
place();
|
|
246
361
|
}
|
|
247
362
|
//#endregion
|
|
363
|
+
//#region src/market/version.ts
|
|
364
|
+
/**
|
|
365
|
+
* 版本串比较(市场用):点分数字段逐段比大小,后缀(如 `-rc` / `-T` / `+build`)忽略。
|
|
366
|
+
*
|
|
367
|
+
* 为什么需要它:市场目录是**远端数据**(`raw.githubusercontent` 有 CDN 缓存、Actions 也要跑一会儿),
|
|
368
|
+
* 所以"目录里的版本比本机已装的旧"是真会出现的状态。拿它当"更新"用,就会把新包静默装回旧版本
|
|
369
|
+
* (2026-09-10 实际发生过一次:刚发布的 1.1.0 被目录里的 1.0.0 覆盖)。比较在这里统一实现,
|
|
370
|
+
* node 半(安装护栏)与 client 半(卡片按钮状态)共用同一套语义。
|
|
371
|
+
*/
|
|
372
|
+
function compareVersion(a, b) {
|
|
373
|
+
const parse = (v) => v.trim().replace(/^v/i, "").split(".").map((seg) => {
|
|
374
|
+
const m = /^\d+/.exec(seg);
|
|
375
|
+
return m === null ? 0 : Number(m[0]);
|
|
376
|
+
});
|
|
377
|
+
const xs = parse(a);
|
|
378
|
+
const ys = parse(b);
|
|
379
|
+
const n = Math.max(xs.length, ys.length);
|
|
380
|
+
for (let i = 0; i < n; i++) {
|
|
381
|
+
const x = xs[i] ?? 0;
|
|
382
|
+
const y = ys[i] ?? 0;
|
|
383
|
+
if (x !== y) return x < y ? -1 : 1;
|
|
384
|
+
}
|
|
385
|
+
return 0;
|
|
386
|
+
}
|
|
387
|
+
//#endregion
|
|
248
388
|
//#region src/client/market-api.ts
|
|
389
|
+
/**
|
|
390
|
+
* 市场前端的纯逻辑层(client 半,浏览器安全:只用 fetch,零 node 依赖)。
|
|
391
|
+
* 与 node 半 src/market/* 解耦——这里只重声明 UI 需要的类型子集,
|
|
392
|
+
* 避免把 node:crypto / node:fs 拖进 lib/client.js 浏览器包。
|
|
393
|
+
* 全部函数注入 fetch,Node 测试喂假响应即可跑通(无需浏览器)。
|
|
394
|
+
*
|
|
395
|
+
* 范围(本轮锁定):只做免费。付费条目在 buildCards 里直接过滤掉,
|
|
396
|
+
* 不渲染、不提供购买/授权流程(付费系统暂缓实装)。
|
|
397
|
+
*/
|
|
249
398
|
const defaultFetch$1 = (url, init) => fetch(url, init);
|
|
250
|
-
/**
|
|
251
|
-
|
|
252
|
-
|
|
399
|
+
/**
|
|
400
|
+
* 拉 catalog(node 半缓存 5 分钟)。`refresh: true` = 强制重拉:node 半据此绕开自身缓存,
|
|
401
|
+
* 并给上游 URL 加 cache-buster(catalog 挂在 raw.githubusercontent 上,不加就还是旧目录)。
|
|
402
|
+
*/
|
|
403
|
+
async function fetchCatalog(fetchFn = defaultFetch$1, url = "/we-sync/dwp/market/catalog", opts) {
|
|
404
|
+
const res = await fetchFn(opts?.refresh === true ? url + (url.includes("?") ? "&" : "?") + "refresh=1" : url, { cache: "no-store" });
|
|
253
405
|
if (!res.ok) throw new Error(`catalog ${res.status}`);
|
|
254
406
|
const body = await res.json();
|
|
255
407
|
return Array.isArray(body.entries) ? body.entries : [];
|
|
@@ -261,14 +413,19 @@ window.__ModuleLoader__.load({
|
|
|
261
413
|
const body = await res.json();
|
|
262
414
|
return Array.isArray(body.installed) ? body.installed : [];
|
|
263
415
|
}
|
|
264
|
-
/**
|
|
416
|
+
/**
|
|
417
|
+
* 合并 catalog + installed → 卡片视图模型。免费 only(commercial 过滤掉)。
|
|
418
|
+
* 安装状态按版本比较得出:目录比本地新 = update;**目录比本地旧 = local-ahead**
|
|
419
|
+
* (远端目录可能因为 CDN 缓存还没刷新 —— 这种卡片不给"更新"按钮,避免把新包装回旧版本)。
|
|
420
|
+
*/
|
|
265
421
|
function buildCards(catalog, installed) {
|
|
266
422
|
const byId = new Map(installed.map((i) => [i.id, i]));
|
|
267
423
|
const cards = [];
|
|
268
424
|
for (const entry of catalog) {
|
|
269
425
|
if (entry.license.commercial !== false) continue;
|
|
270
426
|
const rec = byId.get(entry.id);
|
|
271
|
-
const
|
|
427
|
+
const next = entry.dwp.package.version;
|
|
428
|
+
const state = rec === void 0 ? "absent" : rec.version === next ? "installed" : compareVersion(next, rec.version) < 0 ? "local-ahead" : "update";
|
|
272
429
|
cards.push({
|
|
273
430
|
entry,
|
|
274
431
|
state,
|
|
@@ -484,6 +641,63 @@ window.__ModuleLoader__.load({
|
|
|
484
641
|
return (i === 0 ? String(v) : v.toFixed(1)) + " " + units[i];
|
|
485
642
|
}
|
|
486
643
|
//#endregion
|
|
644
|
+
//#region src/client/library-model.ts
|
|
645
|
+
const isApplication = (a) => a.type === "application";
|
|
646
|
+
const isLauncherApp = (a) => (a.source ?? "") === "launcher";
|
|
647
|
+
/** 应用大类切分:all 为全部应用;we 排除启动器装的;launcher 只留启动器装的。 */
|
|
648
|
+
function partitionApps(apps) {
|
|
649
|
+
const only = apps.filter(isApplication);
|
|
650
|
+
return {
|
|
651
|
+
all: only,
|
|
652
|
+
we: only.filter((a) => !isLauncherApp(a)),
|
|
653
|
+
launcher: only.filter(isLauncherApp)
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
/** 按二级分类取子集(all 返回全部应用)。 */
|
|
657
|
+
function appsForSub(apps, sub) {
|
|
658
|
+
return partitionApps(apps)[sub];
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* 目录路径取末段。scanApps 的 id 就是壁纸目录全路径(服务端 normalize 成 '/' 分隔),
|
|
662
|
+
* 启动器安装的应用目录恒为 `<安装根>/<slug>`,因此末段即 slug。
|
|
663
|
+
* 末尾有分隔符时也要正确取到(split + 过滤空段)。
|
|
664
|
+
*/
|
|
665
|
+
function slugOfDir(dir) {
|
|
666
|
+
const parts = dir.replace(/\\/g, "/").split("/").filter((s) => s !== "");
|
|
667
|
+
return parts.length === 0 ? "" : parts[parts.length - 1];
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* 扫描到的「应用」瓷砖 → 启动器安装记录(用于给出「卸载」)。
|
|
671
|
+
* 优先按目录末段匹配 slug(唯一可信的对应关系);匹配不到时**只在服务端已标记
|
|
672
|
+
* source === 'launcher' 的前提下**按标题兜底(目录被重命名过的情况)。
|
|
673
|
+
*
|
|
674
|
+
* 标题兜底必须卡在 source 上:工坊 / 自定义目录里的应用和启动器应用完全可能同名,
|
|
675
|
+
* 若放开兜底,点工坊瓷砖的「卸载」会删掉另一条启动器记录——那是真实的数据损失。
|
|
676
|
+
* 返回 null 表示"不是启动器装的":这种瓷砖只给「打开源文件」。
|
|
677
|
+
*/
|
|
678
|
+
function matchLauncherRecord(app, records) {
|
|
679
|
+
const slug = slugOfDir(app.id).toLowerCase();
|
|
680
|
+
if (slug !== "") {
|
|
681
|
+
const bySlug = records.find((r) => r.slug.toLowerCase() === slug);
|
|
682
|
+
if (bySlug !== void 0) return bySlug;
|
|
683
|
+
}
|
|
684
|
+
if (!isLauncherApp(app)) return null;
|
|
685
|
+
return records.find((r) => r.title === app.title) ?? null;
|
|
686
|
+
}
|
|
687
|
+
/** ISO 安装时间 → 本地 `YYYY-MM-DD HH:mm`(解析失败时原样回显,不吞信息)。 */
|
|
688
|
+
function formatInstalledAt(iso) {
|
|
689
|
+
const d = new Date(iso);
|
|
690
|
+
if (Number.isNaN(d.getTime())) return iso;
|
|
691
|
+
const p = (n) => String(n).padStart(2, "0");
|
|
692
|
+
return String(d.getFullYear()) + "-" + p(d.getMonth() + 1) + "-" + p(d.getDate()) + " " + p(d.getHours()) + ":" + p(d.getMinutes());
|
|
693
|
+
}
|
|
694
|
+
/** 启动器应用目录(详情里的「地址」):安装根 + slug,统一 Windows 反斜杠。 */
|
|
695
|
+
function launcherAppDir(root, slug) {
|
|
696
|
+
const r = root.trim().replace(/[\\/]+$/, "");
|
|
697
|
+
if (r === "") return slug;
|
|
698
|
+
return r.replace(/\//g, "\\") + "\\" + slug;
|
|
699
|
+
}
|
|
700
|
+
//#endregion
|
|
487
701
|
//#region src/client/WallpaperSharePanel.tsx
|
|
488
702
|
/**
|
|
489
703
|
* wallpaper_share 会话视图标签页:当前壁纸信息、同步开关、显示器选择、
|
|
@@ -515,6 +729,10 @@ window.__ModuleLoader__.load({
|
|
|
515
729
|
focusIntro: "随任务自适应调节背景效果",
|
|
516
730
|
flashFocusOn: "专注模式已开启:注视点透镜跟随鼠标(圆心清晰);可再开「眼动追踪」改为跟随视线",
|
|
517
731
|
flashFocusOff: "专注模式已关闭,恢复手动滑块",
|
|
732
|
+
floaterMode: "桌面悬浮球",
|
|
733
|
+
flashFloaterOn: "桌面悬浮球已开启:本页切到后台 / 浏览器最小化时,桌面出现环形悬浮球,单击切回本页",
|
|
734
|
+
flashFloaterOff: "桌面悬浮球已关闭",
|
|
735
|
+
floaterUnsupported: "桌面悬浮球需要 Windows 宿主且随包携带 we-floater.exe(当前不可用)",
|
|
518
736
|
renderModeTitle: "渲染模式",
|
|
519
737
|
modeEco: "预览",
|
|
520
738
|
modePerf: "捕获",
|
|
@@ -558,17 +776,38 @@ window.__ModuleLoader__.load({
|
|
|
558
776
|
openFolderFailed: "打开文件夹失败",
|
|
559
777
|
mountFailed: "挂载失败",
|
|
560
778
|
typeDwp: "dwp壁纸",
|
|
561
|
-
|
|
779
|
+
typeApps: "应用",
|
|
780
|
+
typeWeApp: "we应用",
|
|
562
781
|
typeLauncherApp: "应用",
|
|
782
|
+
appSubAll: "全部",
|
|
783
|
+
manage: "管理",
|
|
784
|
+
manageDone: "完成",
|
|
785
|
+
openSource: "打开源文件",
|
|
786
|
+
detail: "详细",
|
|
787
|
+
detailHide: "收起",
|
|
788
|
+
detailInstalledAt: "安装时间",
|
|
789
|
+
detailAddr: "地址",
|
|
790
|
+
detailExe: "exe 文件",
|
|
791
|
+
launcherManageHint: "启动与卸载到「本地 → 应用」里操作。",
|
|
792
|
+
confirmUninstallTitle: "确认卸载?",
|
|
793
|
+
confirmUninstallBody: "将删除以下内容的本地文件(dwp 壁纸可在市场重装):",
|
|
794
|
+
manageSelected: "已选",
|
|
795
|
+
bulkUninstall: "卸载选中",
|
|
796
|
+
clearSel: "清空选择",
|
|
797
|
+
manageNotDeletable: "WE 工坊内容不在此卸载(可用「打开源文件」定位)",
|
|
798
|
+
confirmBulkTitle: (n) => "确认卸载选中的 " + String(n) + " 项?",
|
|
799
|
+
flashBulkUninstalled: (n) => "已卸载 " + String(n) + " 项",
|
|
800
|
+
flashBulkPartial: (n, fail) => "已卸载 " + String(n) + " 项,失败:" + fail,
|
|
563
801
|
pageSettings: "设置",
|
|
564
802
|
pageLibrary: "壁纸库",
|
|
803
|
+
pageDwp: "dwp创作",
|
|
565
804
|
pageHint: "滚动切页 · 用力滚才翻页",
|
|
566
805
|
pageGapHint: "继续滚动翻页 · 轻滑弹回",
|
|
567
806
|
mounted: "已挂载",
|
|
568
807
|
searchPlaceholder: "搜索标题…",
|
|
569
808
|
showMore: "显示更多",
|
|
570
809
|
dwpEmpty: "还没有已安装的 DWP 壁纸,去壁纸库「市场」一栏拉取。",
|
|
571
|
-
weAppEmpty: "
|
|
810
|
+
weAppEmpty: "没有应用类内容。",
|
|
572
811
|
appsCount: (total, matched) => total === matched ? `共 ${String(total)} 个` : `共 ${String(total)} 个 · 匹配 ${String(matched)} 个`,
|
|
573
812
|
marketRefresh: "刷新",
|
|
574
813
|
marketSearch: "搜索名称 / 作者…",
|
|
@@ -578,6 +817,11 @@ window.__ModuleLoader__.load({
|
|
|
578
817
|
marketUpdate: "更新",
|
|
579
818
|
marketUninstall: "卸载",
|
|
580
819
|
marketInstalled: "已安装",
|
|
820
|
+
marketLocalAhead: "本地更新",
|
|
821
|
+
marketLocalAheadHint: "本机版本比目录里的新(远端目录可能还没刷新);不提供回退,避免误装回旧版本",
|
|
822
|
+
dwpTierLead: "DWP 壁纸 · 纹理档:",
|
|
823
|
+
dwpTierHd: "高清(增强 / 完整)",
|
|
824
|
+
dwpTierSd: "标准(预览 / 捕获)",
|
|
581
825
|
marketEmpty: "目录为空",
|
|
582
826
|
marketLoading: "加载中…",
|
|
583
827
|
marketNoMatch: "无匹配结果",
|
|
@@ -602,12 +846,12 @@ window.__ModuleLoader__.load({
|
|
|
602
846
|
launcherUrlPlaceholder: "粘贴直链(.zip/.7z/.exe)或 139 分享页链接…",
|
|
603
847
|
launcherTitlePlaceholder: "标题(留空自动取文件名)",
|
|
604
848
|
launcherPwdPlaceholder: "解压密码(加密包选填)",
|
|
605
|
-
launcherCodePlaceholder: "提取码(139
|
|
849
|
+
launcherCodePlaceholder: "提取码(139 选填)",
|
|
606
850
|
launcherPwdNeed: "压缩包已加密,请填写解压密码后重试",
|
|
607
851
|
launcherPwdWrong: "解压密码错误,或压缩包已损坏",
|
|
608
852
|
launcherAuthTitle: "139 登录态",
|
|
609
853
|
launcherAuthHint: "手动方式:登录 yun.139.com 后,F12 → 网络 → 任意请求 → 请求标头里的 Authorization,整串复制粘贴到这里",
|
|
610
|
-
launcherHelperLink: "
|
|
854
|
+
launcherHelperLink: "一键方式:安装登录态同步助手(139 网盘)",
|
|
611
855
|
launcherTutorialBeta: "该功能为测试版本",
|
|
612
856
|
launcherTutorialPrepTitle: "启动前的准备:",
|
|
613
857
|
launcherTutorialPrep1: "安装油猴(Tampermonkey 浏览器扩展)",
|
|
@@ -635,9 +879,11 @@ window.__ModuleLoader__.load({
|
|
|
635
879
|
launcherRootSaved: "✔ 安装位置已更新,之后的安装存到新位置",
|
|
636
880
|
launcherRootMoved: "✔ 已迁移应用",
|
|
637
881
|
launcherRootFail: "更改安装位置失败",
|
|
638
|
-
launcherShareCode: "
|
|
639
|
-
launcherShareCodeWrong: "
|
|
640
|
-
launcherShareFail: "
|
|
882
|
+
launcherShareCode: "该分享需要提取码:请在提取码框填入后重试",
|
|
883
|
+
launcherShareCodeWrong: "提取码错误,请核对后重试",
|
|
884
|
+
launcherShareFail: "分享解析失败(详情见括号内服务端信息)",
|
|
885
|
+
launcherShareCode139: "该 139 分享需要提取码:请在提取码框填入后重试",
|
|
886
|
+
launcherShareCode139Wrong: "139 提取码错误,请核对后重试",
|
|
641
887
|
launcherInstall: "下载安装",
|
|
642
888
|
launcherInstalling: "下载安装中…",
|
|
643
889
|
launcherEmpty: "还没有安装的应用。粘贴直链后点「下载安装」。",
|
|
@@ -688,6 +934,10 @@ window.__ModuleLoader__.load({
|
|
|
688
934
|
focusIntro: "Background adjusts adaptively to your task",
|
|
689
935
|
flashFocusOn: "Focus mode on: lens follows mouse (clear center); enable Eye Tracking to follow gaze instead",
|
|
690
936
|
flashFocusOff: "Focus mode off, manual sliders restored",
|
|
937
|
+
floaterMode: "Desktop Orb",
|
|
938
|
+
flashFloaterOn: "Desktop orb on: when this tab goes to the background or the browser minimizes, a ring button appears on your desktop; click it to return here",
|
|
939
|
+
flashFloaterOff: "Desktop orb off",
|
|
940
|
+
floaterUnsupported: "Desktop orb needs a Windows host with we-floater.exe bundled (unavailable here)",
|
|
691
941
|
renderModeTitle: "Render Mode",
|
|
692
942
|
modeEco: "Preview",
|
|
693
943
|
modePerf: "Capture",
|
|
@@ -730,18 +980,39 @@ window.__ModuleLoader__.load({
|
|
|
730
980
|
loadFailed: "Failed to load list",
|
|
731
981
|
openFolderFailed: "Failed to open folder",
|
|
732
982
|
mountFailed: "Mount failed",
|
|
733
|
-
|
|
734
|
-
typeWeApp: "WE
|
|
983
|
+
typeApps: "Apps",
|
|
984
|
+
typeWeApp: "WE app",
|
|
735
985
|
typeLauncherApp: "App",
|
|
986
|
+
appSubAll: "All",
|
|
987
|
+
manage: "Manage",
|
|
988
|
+
manageDone: "Done",
|
|
989
|
+
openSource: "Open source file",
|
|
990
|
+
detail: "Details",
|
|
991
|
+
detailHide: "Hide",
|
|
992
|
+
detailInstalledAt: "Installed",
|
|
993
|
+
detailAddr: "Location",
|
|
994
|
+
detailExe: "Executable",
|
|
995
|
+
launcherManageHint: "Launch and uninstall from Library → Apps.",
|
|
996
|
+
confirmUninstallTitle: "Uninstall?",
|
|
997
|
+
confirmUninstallBody: "This deletes the local files of (DWP wallpapers can be reinstalled from the market):",
|
|
998
|
+
manageSelected: "Selected",
|
|
999
|
+
bulkUninstall: "Uninstall selected",
|
|
1000
|
+
clearSel: "Clear",
|
|
1001
|
+
manageNotDeletable: "WE Workshop content is not uninstalled here (use \"Open source file\" to locate it)",
|
|
1002
|
+
confirmBulkTitle: (n) => "Uninstall the " + String(n) + " selected item(s)?",
|
|
1003
|
+
flashBulkUninstalled: (n) => "Uninstalled " + String(n) + " item(s)",
|
|
1004
|
+
flashBulkPartial: (n, fail) => "Uninstalled " + String(n) + ", failed: " + fail,
|
|
736
1005
|
pageSettings: "Settings",
|
|
737
1006
|
pageLibrary: "Library",
|
|
1007
|
+
pageDwp: "DWP Studio",
|
|
738
1008
|
pageHint: "Scroll to flip · keep scrolling firmly to turn the page",
|
|
739
1009
|
pageGapHint: "Keep scrolling to flip · release to bounce back",
|
|
740
1010
|
mounted: "Mounted",
|
|
741
1011
|
searchPlaceholder: "Search titles…",
|
|
742
1012
|
showMore: "Show more",
|
|
743
1013
|
dwpEmpty: "No installed DWP wallpapers yet — pull some in the library \"Market\" tab.",
|
|
744
|
-
|
|
1014
|
+
typeDwp: "DWP",
|
|
1015
|
+
weAppEmpty: "No application wallpapers yet.",
|
|
745
1016
|
appsCount: (total, matched) => total === matched ? `Total ${String(total)}` : `Total ${String(total)} · Matched ${String(matched)}`,
|
|
746
1017
|
marketRefresh: "Refresh",
|
|
747
1018
|
marketSearch: "Search name / author…",
|
|
@@ -751,6 +1022,11 @@ window.__ModuleLoader__.load({
|
|
|
751
1022
|
marketUpdate: "Update",
|
|
752
1023
|
marketUninstall: "Uninstall",
|
|
753
1024
|
marketInstalled: "Installed",
|
|
1025
|
+
marketLocalAhead: "Local is newer",
|
|
1026
|
+
marketLocalAheadHint: "The installed version is newer than the catalog (the remote catalog may not have refreshed yet); downgrading on purpose is not offered here",
|
|
1027
|
+
dwpTierLead: "DWP wallpaper · texture tier: ",
|
|
1028
|
+
dwpTierHd: "high-res (Enhanced)",
|
|
1029
|
+
dwpTierSd: "standard (Eco / Perf)",
|
|
754
1030
|
marketEmpty: "Catalog is empty",
|
|
755
1031
|
marketLoading: "Loading…",
|
|
756
1032
|
marketNoMatch: "No matches",
|
|
@@ -780,7 +1056,7 @@ window.__ModuleLoader__.load({
|
|
|
780
1056
|
launcherPwdWrong: "Wrong password, or the archive is corrupted",
|
|
781
1057
|
launcherAuthTitle: "139 Login (Authorization)",
|
|
782
1058
|
launcherAuthHint: "Manual: sign in at yun.139.com, open DevTools → Network → any request → copy the whole Authorization request header, paste it here",
|
|
783
|
-
launcherHelperLink: "One-click: install the login-sync helper",
|
|
1059
|
+
launcherHelperLink: "One-click: install the login-sync helper (139 drive)",
|
|
784
1060
|
launcherTutorialBeta: "This feature is in beta",
|
|
785
1061
|
launcherTutorialPrepTitle: "First-time setup:",
|
|
786
1062
|
launcherTutorialPrep1: "Install Tampermonkey (browser extension)",
|
|
@@ -808,9 +1084,11 @@ window.__ModuleLoader__.load({
|
|
|
808
1084
|
launcherRootSaved: "✔ Install location updated — future installs go there",
|
|
809
1085
|
launcherRootMoved: "✔ Apps moved",
|
|
810
1086
|
launcherRootFail: "Failed to update install location",
|
|
811
|
-
launcherShareCode: "This
|
|
812
|
-
launcherShareCodeWrong: "Wrong
|
|
813
|
-
launcherShareFail: "
|
|
1087
|
+
launcherShareCode: "This share needs a passcode — enter it in the passcode box and retry",
|
|
1088
|
+
launcherShareCodeWrong: "Wrong passcode — check it and retry",
|
|
1089
|
+
launcherShareFail: "Share resolve failed (see server detail in brackets)",
|
|
1090
|
+
launcherShareCode139: "This 139 share needs a passcode — enter it in the passcode box and retry",
|
|
1091
|
+
launcherShareCode139Wrong: "Wrong 139 passcode — check it and retry",
|
|
814
1092
|
launcherInstall: "Download & Install",
|
|
815
1093
|
launcherInstalling: "Downloading…",
|
|
816
1094
|
launcherEmpty: "No apps installed yet. Paste a direct link and click \"Download & Install\".",
|
|
@@ -866,6 +1144,9 @@ window.__ModuleLoader__.load({
|
|
|
866
1144
|
const [gazeError, setGazeError] = (0, react.useState)("");
|
|
867
1145
|
const [gazeSnapText, setGazeSnapText] = (0, react.useState)(store.settings.gazeSnapText);
|
|
868
1146
|
const [needsCalib, setNeedsCalib] = (0, react.useState)(false);
|
|
1147
|
+
const [floater, setFloater] = (0, react.useState)(store.settings.floater);
|
|
1148
|
+
/** 能力探测结果(GET /we-sync/floater):false = 非 Windows 或 we-floater.exe 缺失,开关置灰 */
|
|
1149
|
+
const [floaterSupported, setFloaterSupported] = (0, react.useState)(true);
|
|
869
1150
|
(0, react.useEffect)(() => onGazeStatus((s, err) => {
|
|
870
1151
|
setGazeStatus(s);
|
|
871
1152
|
setGazeError(err);
|
|
@@ -873,6 +1154,12 @@ window.__ModuleLoader__.load({
|
|
|
873
1154
|
const CHARGE_THRESHOLD = 600;
|
|
874
1155
|
const CHARGE_DECAY_MS = 250;
|
|
875
1156
|
const PULL_RATIO = .07;
|
|
1157
|
+
const PAGES = [
|
|
1158
|
+
"settings",
|
|
1159
|
+
"library",
|
|
1160
|
+
"dwp"
|
|
1161
|
+
];
|
|
1162
|
+
const pageIdx = (p) => PAGES.indexOf(p);
|
|
876
1163
|
const [page, setPage] = (0, react.useState)("settings");
|
|
877
1164
|
const appsOpen = page === "library";
|
|
878
1165
|
const panelRef = (0, react.useRef)(null);
|
|
@@ -880,9 +1167,16 @@ window.__ModuleLoader__.load({
|
|
|
880
1167
|
const trackRef = (0, react.useRef)(null);
|
|
881
1168
|
const settingsRef = (0, react.useRef)(null);
|
|
882
1169
|
const gapRef = (0, react.useRef)(null);
|
|
883
|
-
const
|
|
884
|
-
const
|
|
1170
|
+
const libRef = (0, react.useRef)(null);
|
|
1171
|
+
const gap2Ref = (0, react.useRef)(null);
|
|
1172
|
+
const dwpRef = (0, react.useRef)(null);
|
|
1173
|
+
const progRefs = [
|
|
1174
|
+
(0, react.useRef)(null),
|
|
1175
|
+
(0, react.useRef)(null),
|
|
1176
|
+
(0, react.useRef)(null)
|
|
1177
|
+
];
|
|
885
1178
|
const libLoadedRef = (0, react.useRef)(false);
|
|
1179
|
+
const capRef = (0, react.useRef)(null);
|
|
886
1180
|
const pageRef = (0, react.useRef)("settings");
|
|
887
1181
|
pageRef.current = page;
|
|
888
1182
|
const loadLibraryData = () => {
|
|
@@ -895,8 +1189,16 @@ window.__ModuleLoader__.load({
|
|
|
895
1189
|
};
|
|
896
1190
|
const geoRef = (0, react.useRef)({
|
|
897
1191
|
maxPos: 0,
|
|
898
|
-
|
|
899
|
-
|
|
1192
|
+
tops: [
|
|
1193
|
+
0,
|
|
1194
|
+
0,
|
|
1195
|
+
0
|
|
1196
|
+
],
|
|
1197
|
+
bottoms: [
|
|
1198
|
+
0,
|
|
1199
|
+
0,
|
|
1200
|
+
0
|
|
1201
|
+
]
|
|
900
1202
|
});
|
|
901
1203
|
const posRef = (0, react.useRef)(0);
|
|
902
1204
|
const velRef = (0, react.useRef)(0);
|
|
@@ -906,25 +1208,76 @@ window.__ModuleLoader__.load({
|
|
|
906
1208
|
const phaseRef = (0, react.useRef)("idle");
|
|
907
1209
|
const animRef = (0, react.useRef)(null);
|
|
908
1210
|
const decayTimerRef = (0, react.useRef)(null);
|
|
1211
|
+
/** 当前页(翻页动画期间取目标页):决定可用高度——只有 dwp创作页要避让宿主输入框 */
|
|
1212
|
+
const effectivePage = () => {
|
|
1213
|
+
const a = animRef.current;
|
|
1214
|
+
return phaseRef.current === "anim" && a !== null ? a.target : pageRef.current;
|
|
1215
|
+
};
|
|
909
1216
|
const measure = () => {
|
|
910
1217
|
const vp = viewportRef.current;
|
|
911
1218
|
const track = trackRef.current;
|
|
912
1219
|
const s = settingsRef.current;
|
|
913
1220
|
const gap = gapRef.current;
|
|
914
|
-
|
|
1221
|
+
const lib = libRef.current;
|
|
1222
|
+
const gap2 = gap2Ref.current;
|
|
1223
|
+
const dwp = dwpRef.current;
|
|
1224
|
+
if (vp === null || track === null || s === null || gap === null || lib === null || gap2 === null || dwp === null) return;
|
|
915
1225
|
const vpH = vp.clientHeight;
|
|
916
1226
|
vp.style.setProperty("--wesync-vph", Math.max(0, vpH) + "px");
|
|
917
|
-
const libTop = Math.max(0, s.offsetHeight + gap.offsetHeight);
|
|
918
1227
|
const maxPos = Math.max(0, track.scrollHeight - vpH);
|
|
1228
|
+
const hs = [
|
|
1229
|
+
s.offsetHeight,
|
|
1230
|
+
lib.offsetHeight,
|
|
1231
|
+
dwp.offsetHeight
|
|
1232
|
+
];
|
|
1233
|
+
const tops = [
|
|
1234
|
+
0,
|
|
1235
|
+
hs[0] + gap.offsetHeight,
|
|
1236
|
+
hs[0] + gap.offsetHeight + hs[1] + gap2.offsetHeight
|
|
1237
|
+
];
|
|
1238
|
+
const bottoms = tops.map((top, i) => i === PAGES.length - 1 ? maxPos : Math.max(top, Math.min(top + hs[i] - vpH, maxPos)));
|
|
919
1239
|
geoRef.current = {
|
|
920
1240
|
maxPos,
|
|
921
|
-
|
|
922
|
-
|
|
1241
|
+
tops,
|
|
1242
|
+
bottoms
|
|
923
1243
|
};
|
|
924
1244
|
};
|
|
1245
|
+
const rangeOf = (i) => {
|
|
1246
|
+
const g = geoRef.current;
|
|
1247
|
+
const k = Math.max(0, Math.min(PAGES.length - 1, i));
|
|
1248
|
+
return {
|
|
1249
|
+
top: g.tops[k],
|
|
1250
|
+
bottom: g.bottoms[k]
|
|
1251
|
+
};
|
|
1252
|
+
};
|
|
1253
|
+
/**
|
|
1254
|
+
* 可用高度:面板顶 → 底部边界,写进 --wesync-pages-max。
|
|
1255
|
+
*
|
|
1256
|
+
* 宿主输入框(composer)是 sticky + z-index 7 的常驻条,固定占着会话列底部
|
|
1257
|
+
* (实测 126px,多行输入还会变高)。压在它下面的内容点不到——elementFromPoint
|
|
1258
|
+
* 命中的是输入框的「+」按钮 / 模式行,而不是卡片按钮。
|
|
1259
|
+
* 现在「设置」「壁纸库」两页由 CSS 把输入框整体淡出并放行指针(点得到、看不见),
|
|
1260
|
+
* 只有 dwp创作页显示它,所以也只有那页需要把它的高度让出来。
|
|
1261
|
+
* 其余页用视口底部当边界——顺带修掉原来写死 32px 造成的底部溢出
|
|
1262
|
+
* (实测 1100×700 面板底边超出视口 44px,而宿主滚动被钉住、滚不回来)。
|
|
1263
|
+
*/
|
|
1264
|
+
const syncViewportCap = () => {
|
|
1265
|
+
const host = panelRef.current;
|
|
1266
|
+
if (host === null) return;
|
|
1267
|
+
const seat = effectivePage() === "dwp" ? document.querySelector("[data-composer-seat]") : null;
|
|
1268
|
+
const limit = seat !== null ? Math.min(seat.getBoundingClientRect().top, window.innerHeight - 8) : window.innerHeight - 8;
|
|
1269
|
+
let avail = Math.round(limit - host.getBoundingClientRect().top);
|
|
1270
|
+
if (avail < 240) avail = 0;
|
|
1271
|
+
if (avail > 0) host.style.setProperty("--wesync-pages-max", avail + "px");
|
|
1272
|
+
else host.style.removeProperty("--wesync-pages-max");
|
|
1273
|
+
measure();
|
|
1274
|
+
if (phaseRef.current !== "anim") {
|
|
1275
|
+
const r = rangeOf(pageIdx(pageRef.current));
|
|
1276
|
+
posRef.current = Math.max(r.top, Math.min(r.bottom, posRef.current));
|
|
1277
|
+
}
|
|
1278
|
+
};
|
|
925
1279
|
const resetProgress = () => {
|
|
926
|
-
if (
|
|
927
|
-
if (progLibRef.current !== null) progLibRef.current.style.transform = "scaleX(0)";
|
|
1280
|
+
for (const p of progRefs) if (p.current !== null) p.current.style.transform = "scaleX(0)";
|
|
928
1281
|
};
|
|
929
1282
|
const cancelCharge = () => {
|
|
930
1283
|
accRef.current = 0;
|
|
@@ -939,17 +1292,17 @@ window.__ModuleLoader__.load({
|
|
|
939
1292
|
};
|
|
940
1293
|
/** 蓄力翻页:引擎动画滚过断层到相邻页顶,到达后再切页签高亮 */
|
|
941
1294
|
const flipTo = (target) => {
|
|
942
|
-
const g = geoRef.current;
|
|
943
1295
|
phaseRef.current = "anim";
|
|
944
1296
|
cancelCharge();
|
|
945
1297
|
disarmDecay();
|
|
946
|
-
const to = target === "library" ? g.libTop : 0;
|
|
947
1298
|
animRef.current = {
|
|
948
1299
|
from: posRef.current,
|
|
949
|
-
to,
|
|
1300
|
+
to: posRef.current,
|
|
950
1301
|
start: performance.now(),
|
|
951
1302
|
target
|
|
952
1303
|
};
|
|
1304
|
+
syncViewportCap();
|
|
1305
|
+
animRef.current.to = rangeOf(pageIdx(target)).top;
|
|
953
1306
|
if (target === "library") loadLibraryData();
|
|
954
1307
|
};
|
|
955
1308
|
(0, react.useEffect)(() => {
|
|
@@ -1006,13 +1359,12 @@ window.__ModuleLoader__.load({
|
|
|
1006
1359
|
if (e.deltaMode === 1) dy *= 40;
|
|
1007
1360
|
else if (e.deltaMode === 2) dy *= 800;
|
|
1008
1361
|
if (dy === 0) return;
|
|
1009
|
-
|
|
1362
|
+
geoRef.current;
|
|
1010
1363
|
const dir = dy > 0 ? 1 : -1;
|
|
1011
|
-
const cur = pageRef.current;
|
|
1012
|
-
const
|
|
1013
|
-
const pageBottom = cur === "library" ? g.maxPos : g.boundary;
|
|
1364
|
+
const cur = pageIdx(pageRef.current);
|
|
1365
|
+
const { top: pageTop, bottom: pageBottom } = rangeOf(cur);
|
|
1014
1366
|
const atBoundary = dir === 1 && posRef.current >= pageBottom - .5 || dir === -1 && posRef.current <= pageTop + .5;
|
|
1015
|
-
const canFlip = dir === 1 && cur
|
|
1367
|
+
const canFlip = dir === 1 && cur < PAGES.length - 1 || dir === -1 && cur > 0;
|
|
1016
1368
|
if (phaseRef.current === "charge" && pullTargetRef.current !== 0 && (dir === 1 && pullTargetRef.current > 0 || dir === -1 && pullTargetRef.current < 0)) {
|
|
1017
1369
|
phaseRef.current = "idle";
|
|
1018
1370
|
cancelCharge();
|
|
@@ -1037,12 +1389,12 @@ window.__ModuleLoader__.load({
|
|
|
1037
1389
|
const ratio = Math.min(1, Math.abs(accRef.current) / CHARGE_THRESHOLD);
|
|
1038
1390
|
const maxPull = vp.clientHeight * PULL_RATIO;
|
|
1039
1391
|
pullTargetRef.current = dir * ratio * maxPull * -1;
|
|
1040
|
-
const prog =
|
|
1041
|
-
if (prog !== null) prog.style.transform = "scaleX(" + ratio.toFixed(3) + ")";
|
|
1042
|
-
const other =
|
|
1043
|
-
if (other !== null) other.style.transform = "scaleX(0)";
|
|
1392
|
+
const prog = progRefs[cur + dir];
|
|
1393
|
+
if (prog !== void 0 && prog.current !== null) prog.current.style.transform = "scaleX(" + ratio.toFixed(3) + ")";
|
|
1394
|
+
const other = progRefs[cur];
|
|
1395
|
+
if (other !== void 0 && other.current !== null) other.current.style.transform = "scaleX(0)";
|
|
1044
1396
|
if (Math.abs(accRef.current) >= CHARGE_THRESHOLD) {
|
|
1045
|
-
flipTo(
|
|
1397
|
+
flipTo(PAGES[cur + dir]);
|
|
1046
1398
|
return;
|
|
1047
1399
|
}
|
|
1048
1400
|
}
|
|
@@ -1056,26 +1408,37 @@ window.__ModuleLoader__.load({
|
|
|
1056
1408
|
const host = panelRef.current;
|
|
1057
1409
|
if (host === null) return;
|
|
1058
1410
|
host.addEventListener("wheel", onWheel, { passive: false });
|
|
1411
|
+
capRef.current = syncViewportCap;
|
|
1412
|
+
syncViewportCap();
|
|
1413
|
+
const seatEl = document.querySelector("[data-composer-seat]");
|
|
1414
|
+
const seatRo = seatEl !== null ? new ResizeObserver(syncViewportCap) : null;
|
|
1415
|
+
if (seatEl !== null && seatRo !== null) seatRo.observe(seatEl);
|
|
1059
1416
|
const ro = new ResizeObserver(() => {
|
|
1060
1417
|
measure();
|
|
1061
|
-
const
|
|
1062
|
-
posRef.current = Math.max(
|
|
1418
|
+
const r = rangeOf(pageIdx(pageRef.current));
|
|
1419
|
+
posRef.current = Math.max(r.top, Math.min(r.bottom, posRef.current));
|
|
1063
1420
|
});
|
|
1064
1421
|
if (trackRef.current !== null) ro.observe(trackRef.current);
|
|
1065
|
-
window.addEventListener("resize",
|
|
1422
|
+
window.addEventListener("resize", syncViewportCap);
|
|
1066
1423
|
return () => {
|
|
1067
1424
|
host.removeEventListener("wheel", onWheel);
|
|
1068
1425
|
ro.disconnect();
|
|
1069
|
-
|
|
1426
|
+
seatRo?.disconnect();
|
|
1427
|
+
window.removeEventListener("resize", syncViewportCap);
|
|
1428
|
+
host.style.removeProperty("--wesync-pages-max");
|
|
1429
|
+
capRef.current = null;
|
|
1070
1430
|
};
|
|
1071
1431
|
}, []);
|
|
1432
|
+
(0, react.useEffect)(() => {
|
|
1433
|
+
capRef.current?.();
|
|
1434
|
+
}, [page]);
|
|
1072
1435
|
(0, react.useEffect)(() => {
|
|
1073
1436
|
let raf = 0;
|
|
1074
1437
|
const tick = () => {
|
|
1075
1438
|
raf = requestAnimationFrame(tick);
|
|
1076
1439
|
const track = trackRef.current;
|
|
1077
1440
|
if (track === null) return;
|
|
1078
|
-
|
|
1441
|
+
geoRef.current;
|
|
1079
1442
|
const a = animRef.current;
|
|
1080
1443
|
if (phaseRef.current === "anim" && a !== null) {
|
|
1081
1444
|
const t = Math.min(1, (performance.now() - a.start) / 340);
|
|
@@ -1093,10 +1456,8 @@ window.__ModuleLoader__.load({
|
|
|
1093
1456
|
if (Math.abs(velRef.current) > .4) {
|
|
1094
1457
|
posRef.current += velRef.current;
|
|
1095
1458
|
velRef.current *= .9;
|
|
1096
|
-
const
|
|
1097
|
-
|
|
1098
|
-
const pageBottom = cur === "library" ? g.maxPos : g.boundary;
|
|
1099
|
-
posRef.current = Math.max(pageTop, Math.min(pageBottom, posRef.current));
|
|
1459
|
+
const r = rangeOf(pageIdx(pageRef.current));
|
|
1460
|
+
posRef.current = Math.max(r.top, Math.min(r.bottom, posRef.current));
|
|
1100
1461
|
} else velRef.current = 0;
|
|
1101
1462
|
pullRef.current += (pullTargetRef.current - pullRef.current) * .22;
|
|
1102
1463
|
if (Math.abs(pullRef.current) < .4 && pullTargetRef.current === 0) pullRef.current = 0;
|
|
@@ -1109,10 +1470,9 @@ window.__ModuleLoader__.load({
|
|
|
1109
1470
|
/** 页签点击:引擎动画滚到对应页顶 */
|
|
1110
1471
|
const scrollToPage = (target) => {
|
|
1111
1472
|
if (phaseRef.current === "anim") return;
|
|
1112
|
-
measure();
|
|
1113
1473
|
if (target === pageRef.current) {
|
|
1114
|
-
|
|
1115
|
-
const top = target
|
|
1474
|
+
syncViewportCap();
|
|
1475
|
+
const { top } = rangeOf(pageIdx(target));
|
|
1116
1476
|
phaseRef.current = "anim";
|
|
1117
1477
|
animRef.current = {
|
|
1118
1478
|
from: posRef.current,
|
|
@@ -1128,6 +1488,13 @@ window.__ModuleLoader__.load({
|
|
|
1128
1488
|
const [apps, setApps] = (0, react.useState)([]);
|
|
1129
1489
|
const [appsCounts, setAppsCounts] = (0, react.useState)({});
|
|
1130
1490
|
const [typeFilter, setTypeFilter] = (0, react.useState)("dwp");
|
|
1491
|
+
const [appSub, setAppSub] = (0, react.useState)("all");
|
|
1492
|
+
const [manageMode, setManageMode] = (0, react.useState)(false);
|
|
1493
|
+
const [libFlash, setLibFlash] = (0, react.useState)("");
|
|
1494
|
+
/** 管理模式的多选集合:key = 'dwp|<id>' / 'app|<启动器 slug>'(只有可卸载的项能进来) */
|
|
1495
|
+
const [sel, setSel] = (0, react.useState)({});
|
|
1496
|
+
/** 待确认的卸载目标:单卡卸载是 1 项,批量是 N 项,共用同一个确认弹层与执行路径 */
|
|
1497
|
+
const [uninstallFor, setUninstallFor] = (0, react.useState)(null);
|
|
1131
1498
|
const [search, setSearch] = (0, react.useState)("");
|
|
1132
1499
|
const [visible, setVisible] = (0, react.useState)(60);
|
|
1133
1500
|
const [appsError, setAppsError] = (0, react.useState)("");
|
|
@@ -1165,6 +1532,22 @@ window.__ModuleLoader__.load({
|
|
|
1165
1532
|
const [lEntryFor, setLEntryFor] = (0, react.useState)(null);
|
|
1166
1533
|
const [lDetailFor, setLDetailFor] = (0, react.useState)(null);
|
|
1167
1534
|
const [lChoices, setLChoices] = (0, react.useState)({});
|
|
1535
|
+
(0, react.useEffect)(() => {
|
|
1536
|
+
if (lDetailFor === null) return;
|
|
1537
|
+
const raf = requestAnimationFrame(() => {
|
|
1538
|
+
const host = panelRef.current;
|
|
1539
|
+
const vp = viewportRef.current;
|
|
1540
|
+
if (host === null || vp === null) return;
|
|
1541
|
+
const el = Array.from(host.querySelectorAll("[data-wesync-detail]")).find((x) => x.getAttribute("data-wesync-detail") === lDetailFor) ?? null;
|
|
1542
|
+
if (el === null) return;
|
|
1543
|
+
measure();
|
|
1544
|
+
const overflow = el.getBoundingClientRect().bottom - vp.getBoundingClientRect().bottom + 12;
|
|
1545
|
+
if (overflow <= 0) return;
|
|
1546
|
+
const r = rangeOf(pageIdx("library"));
|
|
1547
|
+
posRef.current = Math.max(r.top, Math.min(r.bottom, posRef.current + overflow));
|
|
1548
|
+
});
|
|
1549
|
+
return () => cancelAnimationFrame(raf);
|
|
1550
|
+
}, [lDetailFor]);
|
|
1168
1551
|
(0, react.useEffect)(() => store.subscribe(() => {
|
|
1169
1552
|
setInfo(store.info);
|
|
1170
1553
|
setEnabled(store.settings.enabled);
|
|
@@ -1176,11 +1559,26 @@ window.__ModuleLoader__.load({
|
|
|
1176
1559
|
setRenderMode(store.settings.renderMode);
|
|
1177
1560
|
setGazeEnabled(store.settings.gazeEnabled);
|
|
1178
1561
|
setGazeSnapText(store.settings.gazeSnapText);
|
|
1562
|
+
setFloater(store.settings.floater);
|
|
1179
1563
|
force((x) => x + 1);
|
|
1180
1564
|
}), []);
|
|
1181
1565
|
(0, react.useEffect)(() => {
|
|
1182
1566
|
loadDirs();
|
|
1183
1567
|
}, []);
|
|
1568
|
+
(0, react.useEffect)(() => {
|
|
1569
|
+
let dead = false;
|
|
1570
|
+
fetch("/we-sync/floater", { cache: "no-store" }).then((r) => r.json()).then((s) => {
|
|
1571
|
+
if (dead || s.supported !== false) return;
|
|
1572
|
+
setFloaterSupported(false);
|
|
1573
|
+
if (store.settings.floater) {
|
|
1574
|
+
store.settings.floater = false;
|
|
1575
|
+
store.notify();
|
|
1576
|
+
}
|
|
1577
|
+
}).catch(() => {});
|
|
1578
|
+
return () => {
|
|
1579
|
+
dead = true;
|
|
1580
|
+
};
|
|
1581
|
+
}, []);
|
|
1184
1582
|
const flash = (text) => {
|
|
1185
1583
|
setStatus(text);
|
|
1186
1584
|
window.setTimeout(() => setStatus(""), 3500);
|
|
@@ -1225,6 +1623,17 @@ window.__ModuleLoader__.load({
|
|
|
1225
1623
|
store.actions.applyBackground();
|
|
1226
1624
|
flash(next ? t.flashFocusOn : t.flashFocusOff);
|
|
1227
1625
|
};
|
|
1626
|
+
const onFloater = () => {
|
|
1627
|
+
if (!floaterSupported) {
|
|
1628
|
+
flash(t.floaterUnsupported);
|
|
1629
|
+
return;
|
|
1630
|
+
}
|
|
1631
|
+
const next = !store.settings.floater;
|
|
1632
|
+
store.settings.floater = next;
|
|
1633
|
+
setFloater(next);
|
|
1634
|
+
store.actions.syncFloater();
|
|
1635
|
+
flash(next ? t.flashFloaterOn : t.flashFloaterOff);
|
|
1636
|
+
};
|
|
1228
1637
|
const onRenderMode = (mode) => {
|
|
1229
1638
|
store.settings.renderMode = mode;
|
|
1230
1639
|
setRenderMode(mode);
|
|
@@ -1274,9 +1683,16 @@ window.__ModuleLoader__.load({
|
|
|
1274
1683
|
store.notify();
|
|
1275
1684
|
};
|
|
1276
1685
|
const loadDwp = async () => {
|
|
1686
|
+
const f = (url, init) => fetch(url, init);
|
|
1687
|
+
let installed = [];
|
|
1688
|
+
try {
|
|
1689
|
+
installed = await fetchInstalled$1(f);
|
|
1690
|
+
} catch {}
|
|
1691
|
+
let catalog = [];
|
|
1692
|
+
try {
|
|
1693
|
+
catalog = await fetchCatalog(f);
|
|
1694
|
+
} catch {}
|
|
1277
1695
|
try {
|
|
1278
|
-
const f = (url, init) => fetch(url, init);
|
|
1279
|
-
const [catalog, installed] = await Promise.all([fetchCatalog(f), fetchInstalled$1(f)]);
|
|
1280
1696
|
const byId = new Map(catalog.map((e) => [e.id, e]));
|
|
1281
1697
|
const cards = await Promise.all(installed.map(async (it) => {
|
|
1282
1698
|
const e = byId.get(it.id);
|
|
@@ -1317,12 +1733,12 @@ window.__ModuleLoader__.load({
|
|
|
1317
1733
|
if (await store.actions.mountDwp(id)) flash(t.mountHint + id);
|
|
1318
1734
|
else flash(t.mountFailed);
|
|
1319
1735
|
};
|
|
1320
|
-
const loadMarket = async () => {
|
|
1736
|
+
const loadMarket = async (refresh = false) => {
|
|
1321
1737
|
setMLoading(true);
|
|
1322
1738
|
setMError("");
|
|
1323
1739
|
try {
|
|
1324
1740
|
const f = (url, init) => fetch(url, init);
|
|
1325
|
-
const [catalog, installed] = await Promise.all([fetchCatalog(f), fetchInstalled$1(f)]);
|
|
1741
|
+
const [catalog, installed] = await Promise.all([fetchCatalog(f, void 0, { refresh }), fetchInstalled$1(f)]);
|
|
1326
1742
|
setMCards(buildCards(catalog, installed));
|
|
1327
1743
|
} catch (e) {
|
|
1328
1744
|
setMError(String(e.message ?? e));
|
|
@@ -1368,6 +1784,87 @@ window.__ModuleLoader__.load({
|
|
|
1368
1784
|
flashM(t.flashMUninstalled);
|
|
1369
1785
|
} else flashM(t.flashMFailed);
|
|
1370
1786
|
};
|
|
1787
|
+
const flashLib = (msg) => {
|
|
1788
|
+
setLibFlash(msg);
|
|
1789
|
+
window.setTimeout(() => setLibFlash(""), 3e3);
|
|
1790
|
+
};
|
|
1791
|
+
/** dwp 壁纸的「打开源文件」:node 半在资源管理器里定位 packages/<id>.dwp。 */
|
|
1792
|
+
const onRevealDwp = (id) => {
|
|
1793
|
+
fetch("/we-sync/dwp/market/reveal?id=" + encodeURIComponent(id), { cache: "no-store" }).then((res) => {
|
|
1794
|
+
if (!res.ok) flashLib(t.openFolderFailed);
|
|
1795
|
+
}).catch(() => flashLib(t.openFolderFailed));
|
|
1796
|
+
};
|
|
1797
|
+
/** 确认后的卸载:按列表逐项执行(单选=1 项,多选=N 项);dwp 正挂载先撤背景。 */
|
|
1798
|
+
const onConfirmUninstall = async () => {
|
|
1799
|
+
const targets = uninstallFor;
|
|
1800
|
+
setUninstallFor(null);
|
|
1801
|
+
if (targets === null || targets.length === 0) return;
|
|
1802
|
+
let ok = 0;
|
|
1803
|
+
const failed = [];
|
|
1804
|
+
for (const t of targets) if (t.kind === "dwp") {
|
|
1805
|
+
if (!(await uninstall((url, init) => fetch(url, init), t.id)).ok) {
|
|
1806
|
+
failed.push(t.title);
|
|
1807
|
+
continue;
|
|
1808
|
+
}
|
|
1809
|
+
if (store.settings.dwpMounted === t.id) await store.actions.unmountDwp();
|
|
1810
|
+
ok += 1;
|
|
1811
|
+
} else {
|
|
1812
|
+
if (!(await uninstallApp(t.id, (u) => fetch(u))).ok) {
|
|
1813
|
+
failed.push(t.title);
|
|
1814
|
+
continue;
|
|
1815
|
+
}
|
|
1816
|
+
ok += 1;
|
|
1817
|
+
}
|
|
1818
|
+
loadDwp();
|
|
1819
|
+
loadMarket();
|
|
1820
|
+
loadApps();
|
|
1821
|
+
loadLauncher();
|
|
1822
|
+
setSel({});
|
|
1823
|
+
flashLib(failed.length === 0 ? targets.length === 1 ? t.flashMUninstalled : t.flashBulkUninstalled(ok) : t.flashBulkPartial(ok, failed.join("、")));
|
|
1824
|
+
};
|
|
1825
|
+
const selKey = (kind, id) => kind + "|" + id;
|
|
1826
|
+
const toggleSel = (key) => {
|
|
1827
|
+
setSel((s) => {
|
|
1828
|
+
const n = { ...s };
|
|
1829
|
+
if (n[key] === true) delete n[key];
|
|
1830
|
+
else n[key] = true;
|
|
1831
|
+
return n;
|
|
1832
|
+
});
|
|
1833
|
+
};
|
|
1834
|
+
const selCount = Object.keys(sel).length;
|
|
1835
|
+
/** 选中集 → 卸载目标列表(标题与路径现查,保证与实际安装记录一致)。 */
|
|
1836
|
+
const collectSelTargets = () => {
|
|
1837
|
+
const out = [];
|
|
1838
|
+
for (const key of Object.keys(sel)) {
|
|
1839
|
+
const cut = key.indexOf("|");
|
|
1840
|
+
const kind = key.slice(0, cut);
|
|
1841
|
+
const id = key.slice(cut + 1);
|
|
1842
|
+
if (kind === "dwp") {
|
|
1843
|
+
const card = dwpCards.find((d) => d.id === id);
|
|
1844
|
+
out.push({
|
|
1845
|
+
kind: "dwp",
|
|
1846
|
+
id,
|
|
1847
|
+
title: card !== void 0 ? card.name : id,
|
|
1848
|
+
path: "packages/" + id + ".dwp"
|
|
1849
|
+
});
|
|
1850
|
+
} else {
|
|
1851
|
+
const rec = lApps.find((r) => r.id === id);
|
|
1852
|
+
out.push({
|
|
1853
|
+
kind: "app",
|
|
1854
|
+
id,
|
|
1855
|
+
title: rec !== void 0 ? rec.title : id,
|
|
1856
|
+
path: launcherAppDir(lRoot, rec !== void 0 ? rec.slug : id)
|
|
1857
|
+
});
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
return out;
|
|
1861
|
+
};
|
|
1862
|
+
(0, react.useEffect)(() => {
|
|
1863
|
+
if (!manageMode) setSel({});
|
|
1864
|
+
}, [manageMode]);
|
|
1865
|
+
(0, react.useEffect)(() => {
|
|
1866
|
+
setSel({});
|
|
1867
|
+
}, [libTab]);
|
|
1371
1868
|
const onAppOpen = (id) => {
|
|
1372
1869
|
fetch("/we-sync/apps/open?id=" + encodeURIComponent(id), { cache: "no-store" }).then((res) => {
|
|
1373
1870
|
if (!res.ok) flash(t.openFolderFailed);
|
|
@@ -1496,7 +1993,8 @@ window.__ModuleLoader__.load({
|
|
|
1496
1993
|
}
|
|
1497
1994
|
if (out.code === "share_passcode_required" || out.code === "share_passcode_wrong") {
|
|
1498
1995
|
setLCodeErr(true);
|
|
1499
|
-
|
|
1996
|
+
const req = out.code === "share_passcode_required" ? is139Share(url) ? t.launcherShareCode139 : t.launcherShareCode : is139Share(url) ? t.launcherShareCode139Wrong : t.launcherShareCodeWrong;
|
|
1997
|
+
flashLErr(t.flashLFailed + ":" + req + (out.error !== void 0 ? "(" + out.error + ")" : ""));
|
|
1500
1998
|
return;
|
|
1501
1999
|
}
|
|
1502
2000
|
if (out.code === "share_auth_required") {
|
|
@@ -1573,14 +2071,6 @@ window.__ModuleLoader__.load({
|
|
|
1573
2071
|
if (r.ok) flashL(t.flashLLaunched + ":" + rec.title);
|
|
1574
2072
|
else flashLErr(t.flashLFailed + (r.error !== void 0 ? ":" + r.error : ""));
|
|
1575
2073
|
};
|
|
1576
|
-
const onLauncherUninstall = async (rec) => {
|
|
1577
|
-
const r = await uninstallApp(rec.id, (u) => fetch(u));
|
|
1578
|
-
if (r.ok) {
|
|
1579
|
-
flashL(t.flashLUninstalled);
|
|
1580
|
-
loadLauncher();
|
|
1581
|
-
loadApps();
|
|
1582
|
-
} else flashL(t.flashLFailed + (r.error !== void 0 ? ":" + r.error : ""));
|
|
1583
|
-
};
|
|
1584
2074
|
const onUpdatePreview = async (rec) => {
|
|
1585
2075
|
const dataUrl = makeLauncherCard(rec.title);
|
|
1586
2076
|
if (dataUrl === "") {
|
|
@@ -1654,9 +2144,9 @@ window.__ModuleLoader__.load({
|
|
|
1654
2144
|
if (appsOpen) loadApps();
|
|
1655
2145
|
} catch {}
|
|
1656
2146
|
};
|
|
1657
|
-
const
|
|
2147
|
+
const appGroups = partitionApps(apps);
|
|
1658
2148
|
const kw = search.trim().toLowerCase();
|
|
1659
|
-
const filteredApps =
|
|
2149
|
+
const filteredApps = appsForSub(apps, appSub).filter((a) => kw === "" || a.title.toLowerCase().includes(kw));
|
|
1660
2150
|
const filteredDwp = dwpCards.filter((d) => kw === "" || d.name.toLowerCase().includes(kw));
|
|
1661
2151
|
const shownApps = filteredApps.slice(0, visible);
|
|
1662
2152
|
const shownDwp = filteredDwp.slice(0, visible);
|
|
@@ -1665,7 +2155,7 @@ window.__ModuleLoader__.load({
|
|
|
1665
2155
|
const wallpaper = info !== null && info.wallpaper !== null ? info.wallpaper : null;
|
|
1666
2156
|
const title = wallpaper === null ? info !== null && info.kind === "web" ? t.webNoPreview : t.noWallpaper : wallpaper.title;
|
|
1667
2157
|
const scene = info !== null && info.source.kind === "scene" ? info.scene : null;
|
|
1668
|
-
const subtitle = wallpaper === null ? t.applyHint : scene === null ? "" : renderMode === "eco" ? t.sceneEco : scene.live === true ? t.sceneExternal + " " + String(scene.status?.fps ?? "?") + "fps" : scene.model === true ? t.sceneModel : t.sceneFallback + scene.fallback;
|
|
2158
|
+
const subtitle = store.settings.dwpMounted !== null ? t.dwpTierLead + (renderMode === "enhanced" ? t.dwpTierHd : t.dwpTierSd) : wallpaper === null ? t.applyHint : scene === null ? "" : renderMode === "eco" ? t.sceneEco : scene.live === true ? t.sceneExternal + " " + String(scene.status?.fps ?? "?") + "fps" : scene.model === true ? t.sceneModel : t.sceneFallback + scene.fallback;
|
|
1669
2159
|
const monitors = info !== null && Array.isArray(info.monitors) && info.monitors.length > 1 ? info.monitors : null;
|
|
1670
2160
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1671
2161
|
className: "wesync-panel",
|
|
@@ -1767,9 +2257,15 @@ window.__ModuleLoader__.load({
|
|
|
1767
2257
|
}, m);
|
|
1768
2258
|
})
|
|
1769
2259
|
}),
|
|
1770
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
2260
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1771
2261
|
className: "wesync-actions",
|
|
1772
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
2262
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2263
|
+
className: ["wesync-btn", floater ? "wesync-focusOn" : "wesync-focusOff"].join(" "),
|
|
2264
|
+
onClick: onFloater,
|
|
2265
|
+
disabled: !floaterSupported,
|
|
2266
|
+
title: floaterSupported ? "" : t.floaterUnsupported,
|
|
2267
|
+
children: t.floaterMode
|
|
2268
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1773
2269
|
className: "wesync-focuswrap",
|
|
1774
2270
|
onMouseEnter: () => setFocusHover(true),
|
|
1775
2271
|
onMouseLeave: () => setFocusHover(false),
|
|
@@ -1815,7 +2311,7 @@ window.__ModuleLoader__.load({
|
|
|
1815
2311
|
children: t.gazeStatusLoading
|
|
1816
2312
|
}) : null)]
|
|
1817
2313
|
}) : null]
|
|
1818
|
-
})
|
|
2314
|
+
})]
|
|
1819
2315
|
}),
|
|
1820
2316
|
focus ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1821
2317
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Slider, {
|
|
@@ -1979,6 +2475,7 @@ window.__ModuleLoader__.load({
|
|
|
1979
2475
|
}),
|
|
1980
2476
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1981
2477
|
className: "wesync-page wesync-page-library",
|
|
2478
|
+
ref: libRef,
|
|
1982
2479
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1983
2480
|
className: "wesync-card",
|
|
1984
2481
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -2017,157 +2514,297 @@ window.__ModuleLoader__.load({
|
|
|
2017
2514
|
})
|
|
2018
2515
|
]
|
|
2019
2516
|
}),
|
|
2020
|
-
libTab === "local" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
children: t.appsCount(dwpCards.length, filteredDwp.length)
|
|
2059
|
-
}),
|
|
2060
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2061
|
-
className: "wesync-apps-grid",
|
|
2062
|
-
children: shownDwp.map((d) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2063
|
-
className: "wesync-app-card",
|
|
2064
|
-
title: (store.settings.dwpMounted === d.id ? t.unmountHint : t.mountHint) + d.name,
|
|
2065
|
-
onClick: () => {
|
|
2066
|
-
onToggleDwp(d.id);
|
|
2067
|
-
},
|
|
2068
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2069
|
-
className: "wesync-app-thumbwrap",
|
|
2070
|
-
children: [d.thumbnail !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
2071
|
-
className: "wesync-app-thumb",
|
|
2072
|
-
src: d.thumbnail,
|
|
2073
|
-
alt: d.name,
|
|
2074
|
-
loading: "lazy",
|
|
2075
|
-
onError: (e) => {
|
|
2076
|
-
e.currentTarget.style.visibility = "hidden";
|
|
2077
|
-
}
|
|
2078
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2079
|
-
className: "wesync-app-thumb",
|
|
2080
|
-
style: {
|
|
2081
|
-
display: "flex",
|
|
2082
|
-
alignItems: "center",
|
|
2083
|
-
justifyContent: "center"
|
|
2084
|
-
},
|
|
2085
|
-
children: t.noPreview
|
|
2086
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2087
|
-
className: "wesync-app-badge wesync-badge-" + (store.settings.dwpMounted === d.id ? "video" : "image"),
|
|
2088
|
-
children: store.settings.dwpMounted === d.id ? t.mounted : t.typeDwp
|
|
2089
|
-
})]
|
|
2090
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2091
|
-
className: "wesync-app-title",
|
|
2092
|
-
children: d.name
|
|
2093
|
-
})]
|
|
2094
|
-
}, d.id))
|
|
2095
|
-
}),
|
|
2096
|
-
filteredDwp.length > shownDwp.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2097
|
-
className: "wesync-btn wesync-show-more",
|
|
2098
|
-
onClick: () => setVisible((v) => v + 60),
|
|
2099
|
-
children: t.showMore + " (+60)"
|
|
2100
|
-
}) : null
|
|
2101
|
-
] }) : weApps.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2102
|
-
className: "wesync-app-empty",
|
|
2103
|
-
children: t.weAppEmpty
|
|
2104
|
-
}) : filteredApps.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2105
|
-
className: "wesync-app-empty",
|
|
2106
|
-
children: t.appsNoMatch
|
|
2107
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2108
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2109
|
-
className: "wesync-apps-count",
|
|
2110
|
-
children: t.appsCount(weApps.length, filteredApps.length)
|
|
2517
|
+
libTab === "local" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2518
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2519
|
+
className: "wesync-apps-filters",
|
|
2520
|
+
children: [
|
|
2521
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2522
|
+
className: ["wesync-chip", typeFilter === "dwp" ? "wesync-chip-on" : ""].join(" "),
|
|
2523
|
+
onClick: () => {
|
|
2524
|
+
setTypeFilter("dwp");
|
|
2525
|
+
setVisible(60);
|
|
2526
|
+
},
|
|
2527
|
+
children: t.typeDwp + " " + String(dwpCards.length)
|
|
2528
|
+
}),
|
|
2529
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2530
|
+
className: ["wesync-chip", typeFilter === "apps" ? "wesync-chip-on" : ""].join(" "),
|
|
2531
|
+
onClick: () => {
|
|
2532
|
+
setTypeFilter("apps");
|
|
2533
|
+
setVisible(60);
|
|
2534
|
+
},
|
|
2535
|
+
children: t.typeApps + " " + String(appGroups.all.length)
|
|
2536
|
+
}),
|
|
2537
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2538
|
+
className: "wesync-app-search",
|
|
2539
|
+
placeholder: t.searchPlaceholder,
|
|
2540
|
+
value: search,
|
|
2541
|
+
onChange: (e) => {
|
|
2542
|
+
setSearch(e.target.value);
|
|
2543
|
+
setVisible(60);
|
|
2544
|
+
}
|
|
2545
|
+
}),
|
|
2546
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2547
|
+
className: ["wesync-chip", manageMode ? "wesync-chip-on" : ""].join(" "),
|
|
2548
|
+
title: manageMode ? t.manageDone : t.manage,
|
|
2549
|
+
onClick: () => {
|
|
2550
|
+
setManageMode(!manageMode);
|
|
2551
|
+
},
|
|
2552
|
+
children: manageMode ? t.manageDone : t.manage
|
|
2553
|
+
})
|
|
2554
|
+
]
|
|
2111
2555
|
}),
|
|
2112
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
2113
|
-
className: "wesync-apps-
|
|
2114
|
-
children:
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
children: [app.hasPreview ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
2121
|
-
className: "wesync-app-thumb",
|
|
2122
|
-
src: "/we-sync/apps/preview?id=" + encodeURIComponent(app.id),
|
|
2123
|
-
alt: app.title,
|
|
2124
|
-
loading: "lazy"
|
|
2125
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2126
|
-
className: "wesync-app-thumb",
|
|
2127
|
-
style: {
|
|
2128
|
-
display: "flex",
|
|
2129
|
-
alignItems: "center",
|
|
2130
|
-
justifyContent: "center"
|
|
2131
|
-
},
|
|
2132
|
-
children: t.noPreview
|
|
2133
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2134
|
-
className: "wesync-app-badge wesync-badge-launcher",
|
|
2135
|
-
children: app.source === "launcher" ? t.typeLauncherApp : t.typeWeApp
|
|
2136
|
-
})]
|
|
2137
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2138
|
-
className: "wesync-app-title",
|
|
2139
|
-
style: {
|
|
2140
|
-
display: "flex",
|
|
2141
|
-
alignItems: "center",
|
|
2142
|
-
gap: 6
|
|
2556
|
+
typeFilter === "apps" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2557
|
+
className: "wesync-apps-filters wesync-apps-subs",
|
|
2558
|
+
children: [
|
|
2559
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2560
|
+
className: ["wesync-chip", appSub === "all" ? "wesync-chip-on" : ""].join(" "),
|
|
2561
|
+
onClick: () => {
|
|
2562
|
+
setAppSub("all");
|
|
2563
|
+
setVisible(60);
|
|
2143
2564
|
},
|
|
2144
|
-
children:
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2565
|
+
children: t.appSubAll + " " + String(appGroups.all.length)
|
|
2566
|
+
}),
|
|
2567
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2568
|
+
className: ["wesync-chip", appSub === "we" ? "wesync-chip-on" : ""].join(" "),
|
|
2569
|
+
onClick: () => {
|
|
2570
|
+
setAppSub("we");
|
|
2571
|
+
setVisible(60);
|
|
2572
|
+
},
|
|
2573
|
+
children: t.typeWeApp + " " + String(appGroups.we.length)
|
|
2574
|
+
}),
|
|
2575
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2576
|
+
className: ["wesync-chip", appSub === "launcher" ? "wesync-chip-on" : ""].join(" "),
|
|
2577
|
+
onClick: () => {
|
|
2578
|
+
setAppSub("launcher");
|
|
2579
|
+
setVisible(60);
|
|
2580
|
+
},
|
|
2581
|
+
children: t.typeLauncherApp + " " + String(appGroups.launcher.length)
|
|
2582
|
+
})
|
|
2583
|
+
]
|
|
2584
|
+
}) : null,
|
|
2585
|
+
libFlash !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2586
|
+
className: "wesync-market-flash",
|
|
2587
|
+
children: libFlash
|
|
2588
|
+
}) : null,
|
|
2589
|
+
manageMode ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2590
|
+
className: "wesync-apps-filters wesync-selbar",
|
|
2591
|
+
children: [
|
|
2592
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2593
|
+
className: "wesync-selbar-count",
|
|
2594
|
+
children: [
|
|
2595
|
+
t.manageSelected,
|
|
2596
|
+
" ",
|
|
2597
|
+
String(selCount)
|
|
2598
|
+
]
|
|
2599
|
+
}),
|
|
2600
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2601
|
+
className: "wesync-btn wesync-manage-del",
|
|
2602
|
+
disabled: selCount === 0,
|
|
2603
|
+
onClick: () => setUninstallFor(collectSelTargets()),
|
|
2604
|
+
children: t.bulkUninstall
|
|
2605
|
+
}),
|
|
2606
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2607
|
+
className: "wesync-btn",
|
|
2608
|
+
disabled: selCount === 0,
|
|
2609
|
+
onClick: () => setSel({}),
|
|
2610
|
+
children: t.clearSel
|
|
2611
|
+
})
|
|
2612
|
+
]
|
|
2613
|
+
}) : null,
|
|
2614
|
+
typeFilter === "dwp" ? dwpCards.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2615
|
+
className: "wesync-app-empty",
|
|
2616
|
+
children: t.dwpEmpty
|
|
2617
|
+
}) : filteredDwp.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2618
|
+
className: "wesync-app-empty",
|
|
2619
|
+
children: t.appsNoMatch
|
|
2620
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2621
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2622
|
+
className: "wesync-apps-count",
|
|
2623
|
+
children: t.appsCount(dwpCards.length, filteredDwp.length)
|
|
2624
|
+
}),
|
|
2625
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2626
|
+
className: "wesync-apps-grid",
|
|
2627
|
+
children: shownDwp.map((d, di) => {
|
|
2628
|
+
const key = selKey("dwp", d.id);
|
|
2629
|
+
const picked = sel[key] === true;
|
|
2630
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2631
|
+
className: [
|
|
2632
|
+
"wesync-app-card",
|
|
2633
|
+
manageMode ? "wesync-app-card-manage" : "",
|
|
2634
|
+
picked ? "wesync-app-card-selected" : ""
|
|
2635
|
+
].join(" "),
|
|
2636
|
+
style: manageMode ? { animationDelay: (-.24 * (di % 5)).toFixed(2) + "s" } : void 0,
|
|
2637
|
+
title: manageMode ? d.name : (store.settings.dwpMounted === d.id ? t.unmountHint : t.mountHint) + d.name,
|
|
2638
|
+
onClick: manageMode ? () => {
|
|
2639
|
+
toggleSel(key);
|
|
2640
|
+
} : () => {
|
|
2641
|
+
onToggleDwp(d.id);
|
|
2151
2642
|
},
|
|
2152
|
-
children:
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2643
|
+
children: [
|
|
2644
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2645
|
+
className: "wesync-app-thumbwrap",
|
|
2646
|
+
children: [
|
|
2647
|
+
d.thumbnail !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
2648
|
+
className: "wesync-app-thumb",
|
|
2649
|
+
src: d.thumbnail,
|
|
2650
|
+
alt: d.name,
|
|
2651
|
+
loading: "lazy",
|
|
2652
|
+
onError: (e) => {
|
|
2653
|
+
e.currentTarget.style.visibility = "hidden";
|
|
2654
|
+
}
|
|
2655
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2656
|
+
className: "wesync-app-thumb",
|
|
2657
|
+
style: {
|
|
2658
|
+
display: "flex",
|
|
2659
|
+
alignItems: "center",
|
|
2660
|
+
justifyContent: "center"
|
|
2661
|
+
},
|
|
2662
|
+
children: t.noPreview
|
|
2663
|
+
}),
|
|
2664
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2665
|
+
className: "wesync-app-badge wesync-badge-" + (store.settings.dwpMounted === d.id ? "video" : "image"),
|
|
2666
|
+
children: store.settings.dwpMounted === d.id ? t.mounted : t.typeDwp
|
|
2667
|
+
}),
|
|
2668
|
+
picked ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2669
|
+
className: "wesync-app-selmark",
|
|
2670
|
+
children: "✓"
|
|
2671
|
+
}) : null
|
|
2672
|
+
]
|
|
2673
|
+
}),
|
|
2674
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2675
|
+
className: "wesync-app-title",
|
|
2676
|
+
children: d.name
|
|
2677
|
+
}),
|
|
2678
|
+
manageMode ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2679
|
+
className: "wesync-manage-row",
|
|
2680
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2681
|
+
className: "wesync-btn",
|
|
2682
|
+
onClick: (e) => {
|
|
2683
|
+
e.stopPropagation();
|
|
2684
|
+
onRevealDwp(d.id);
|
|
2685
|
+
},
|
|
2686
|
+
children: t.openSource
|
|
2687
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2688
|
+
className: "wesync-btn wesync-manage-del",
|
|
2689
|
+
onClick: (e) => {
|
|
2690
|
+
e.stopPropagation();
|
|
2691
|
+
setUninstallFor([{
|
|
2692
|
+
kind: "dwp",
|
|
2693
|
+
id: d.id,
|
|
2694
|
+
title: d.name,
|
|
2695
|
+
path: "packages/" + d.id + ".dwp"
|
|
2696
|
+
}]);
|
|
2697
|
+
},
|
|
2698
|
+
children: t.marketUninstall
|
|
2699
|
+
})]
|
|
2700
|
+
}) : null
|
|
2701
|
+
]
|
|
2702
|
+
}, d.id);
|
|
2703
|
+
})
|
|
2704
|
+
}),
|
|
2705
|
+
filteredDwp.length > shownDwp.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2706
|
+
className: "wesync-btn wesync-show-more",
|
|
2707
|
+
onClick: () => setVisible((v) => v + 60),
|
|
2708
|
+
children: t.showMore + " (+60)"
|
|
2709
|
+
}) : null
|
|
2710
|
+
] }) : appGroups.all.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2711
|
+
className: "wesync-app-empty",
|
|
2712
|
+
children: t.weAppEmpty
|
|
2713
|
+
}) : filteredApps.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2714
|
+
className: "wesync-app-empty",
|
|
2715
|
+
children: t.appsNoMatch
|
|
2716
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2717
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2718
|
+
className: "wesync-apps-count",
|
|
2719
|
+
children: t.appsCount(appGroups.all.length, filteredApps.length)
|
|
2720
|
+
}),
|
|
2721
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2722
|
+
className: "wesync-apps-grid",
|
|
2723
|
+
children: shownApps.map((app, ai) => {
|
|
2724
|
+
const rec = matchLauncherRecord(app, lApps);
|
|
2725
|
+
const isLauncher = (app.source ?? "") === "launcher";
|
|
2726
|
+
const key = rec !== null ? selKey("app", rec.id) : "";
|
|
2727
|
+
const picked = rec !== null && sel[key] === true;
|
|
2728
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2729
|
+
className: [
|
|
2730
|
+
"wesync-app-card",
|
|
2731
|
+
manageMode ? "wesync-app-card-manage" : "",
|
|
2732
|
+
picked ? "wesync-app-card-selected" : ""
|
|
2733
|
+
].join(" "),
|
|
2734
|
+
style: manageMode ? { animationDelay: (-.24 * (ai % 5)).toFixed(2) + "s" } : void 0,
|
|
2735
|
+
title: manageMode ? app.title : t.launcherLaunch + app.title,
|
|
2736
|
+
onClick: manageMode ? () => {
|
|
2737
|
+
if (rec === null) flashLib(t.manageNotDeletable);
|
|
2738
|
+
else toggleSel(key);
|
|
2739
|
+
} : () => {
|
|
2158
2740
|
onLaunch(app.id, app.title, app.file, null);
|
|
2159
2741
|
},
|
|
2160
|
-
children:
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2742
|
+
children: [
|
|
2743
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2744
|
+
className: "wesync-app-thumbwrap",
|
|
2745
|
+
children: [
|
|
2746
|
+
app.hasPreview ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
2747
|
+
className: "wesync-app-thumb",
|
|
2748
|
+
src: "/we-sync/apps/preview?id=" + encodeURIComponent(app.id),
|
|
2749
|
+
alt: app.title,
|
|
2750
|
+
loading: "lazy"
|
|
2751
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2752
|
+
className: "wesync-app-thumb",
|
|
2753
|
+
style: {
|
|
2754
|
+
display: "flex",
|
|
2755
|
+
alignItems: "center",
|
|
2756
|
+
justifyContent: "center"
|
|
2757
|
+
},
|
|
2758
|
+
children: t.noPreview
|
|
2759
|
+
}),
|
|
2760
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2761
|
+
className: "wesync-app-badge " + (isLauncher ? "wesync-badge-launcher" : "wesync-badge-application"),
|
|
2762
|
+
children: isLauncher ? t.typeLauncherApp : t.typeWeApp
|
|
2763
|
+
}),
|
|
2764
|
+
picked ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2765
|
+
className: "wesync-app-selmark",
|
|
2766
|
+
children: "✓"
|
|
2767
|
+
}) : null
|
|
2768
|
+
]
|
|
2769
|
+
}),
|
|
2770
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2771
|
+
className: "wesync-app-title",
|
|
2772
|
+
children: app.title
|
|
2773
|
+
}),
|
|
2774
|
+
manageMode ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2775
|
+
className: "wesync-manage-row",
|
|
2776
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2777
|
+
className: "wesync-btn",
|
|
2778
|
+
onClick: (e) => {
|
|
2779
|
+
e.stopPropagation();
|
|
2780
|
+
onAppOpen(app.id);
|
|
2781
|
+
},
|
|
2782
|
+
children: t.openSource
|
|
2783
|
+
}), rec === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2784
|
+
className: "wesync-btn wesync-manage-del",
|
|
2785
|
+
onClick: (e) => {
|
|
2786
|
+
e.stopPropagation();
|
|
2787
|
+
setUninstallFor([{
|
|
2788
|
+
kind: "app",
|
|
2789
|
+
id: rec.id,
|
|
2790
|
+
title: app.title,
|
|
2791
|
+
path: launcherAppDir(lRoot, rec.slug)
|
|
2792
|
+
}]);
|
|
2793
|
+
},
|
|
2794
|
+
children: t.marketUninstall
|
|
2795
|
+
})]
|
|
2796
|
+
}) : null
|
|
2797
|
+
]
|
|
2798
|
+
}, app.id);
|
|
2799
|
+
})
|
|
2800
|
+
}),
|
|
2801
|
+
filteredApps.length > shownApps.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2802
|
+
className: "wesync-btn wesync-show-more",
|
|
2803
|
+
onClick: () => setVisible((v) => v + 60),
|
|
2804
|
+
children: t.showMore + " (+60)"
|
|
2805
|
+
}) : null
|
|
2806
|
+
] })
|
|
2807
|
+
] }) : libTab === "market" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2171
2808
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2172
2809
|
className: "wesync-apps-filters",
|
|
2173
2810
|
children: [
|
|
@@ -2190,7 +2827,7 @@ window.__ModuleLoader__.load({
|
|
|
2190
2827
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2191
2828
|
className: "wesync-btn",
|
|
2192
2829
|
onClick: () => {
|
|
2193
|
-
loadMarket();
|
|
2830
|
+
loadMarket(true);
|
|
2194
2831
|
},
|
|
2195
2832
|
children: t.marketRefresh
|
|
2196
2833
|
})
|
|
@@ -2251,21 +2888,38 @@ window.__ModuleLoader__.load({
|
|
|
2251
2888
|
}),
|
|
2252
2889
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2253
2890
|
className: "wesync-market-actions",
|
|
2254
|
-
children: [
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2891
|
+
children: [
|
|
2892
|
+
c.state === "absent" || c.state === "update" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2893
|
+
className: "wesync-btn wesync-market-install",
|
|
2894
|
+
disabled: busyId,
|
|
2895
|
+
onClick: () => {
|
|
2896
|
+
mInstall(c.entry.id, c.state === "update");
|
|
2897
|
+
},
|
|
2898
|
+
children: busyId ? t.marketInstalling : c.state === "update" ? t.marketUpdate : t.marketInstall
|
|
2899
|
+
}) : null,
|
|
2900
|
+
c.state === "local-ahead" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2901
|
+
className: "wesync-btn",
|
|
2902
|
+
disabled: true,
|
|
2903
|
+
title: t.marketLocalAheadHint,
|
|
2904
|
+
children: t.marketLocalAhead
|
|
2905
|
+
}) : null,
|
|
2906
|
+
c.state === "installed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2907
|
+
className: "wesync-btn wesync-market-uninstall",
|
|
2908
|
+
disabled: busyId,
|
|
2909
|
+
onClick: () => {
|
|
2910
|
+
mUninstall(c.entry.id);
|
|
2911
|
+
},
|
|
2912
|
+
children: t.marketUninstall
|
|
2913
|
+
}) : null,
|
|
2914
|
+
c.state === "local-ahead" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2915
|
+
className: "wesync-btn wesync-market-uninstall",
|
|
2916
|
+
disabled: busyId,
|
|
2917
|
+
onClick: () => {
|
|
2918
|
+
mUninstall(c.entry.id);
|
|
2919
|
+
},
|
|
2920
|
+
children: t.marketUninstall
|
|
2921
|
+
}) : null
|
|
2922
|
+
]
|
|
2269
2923
|
})
|
|
2270
2924
|
]
|
|
2271
2925
|
}, c.entry.id);
|
|
@@ -2414,7 +3068,7 @@ window.__ModuleLoader__.load({
|
|
|
2414
3068
|
"3. ",
|
|
2415
3069
|
t.launcherTutorialPrep3Lead,
|
|
2416
3070
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
2417
|
-
href: "/we-sync/
|
|
3071
|
+
href: "/we-sync/login-sync.user.js",
|
|
2418
3072
|
target: "_blank",
|
|
2419
3073
|
rel: "noreferrer",
|
|
2420
3074
|
style: {
|
|
@@ -2452,129 +3106,161 @@ window.__ModuleLoader__.load({
|
|
|
2452
3106
|
}) : (() => {
|
|
2453
3107
|
const kwL = lSearch.trim().toLowerCase();
|
|
2454
3108
|
const filteredL = lApps.filter((a) => kwL === "" || a.title.toLowerCase().includes(kwL));
|
|
2455
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2509
|
-
className: "wesync-btn",
|
|
3109
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
3110
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3111
|
+
className: "wesync-apps-filters",
|
|
3112
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3113
|
+
className: "wesync-app-search",
|
|
3114
|
+
placeholder: t.searchPlaceholder,
|
|
3115
|
+
value: lSearch,
|
|
3116
|
+
onChange: (e) => setLSearch(e.target.value)
|
|
3117
|
+
})
|
|
3118
|
+
}),
|
|
3119
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3120
|
+
className: "wesync-market-meta",
|
|
3121
|
+
children: t.launcherManageHint
|
|
3122
|
+
}),
|
|
3123
|
+
filteredL.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3124
|
+
className: "wesync-app-empty",
|
|
3125
|
+
children: t.launcherNoMatch
|
|
3126
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3127
|
+
className: "wesync-apps-grid",
|
|
3128
|
+
children: filteredL.map((rec) => {
|
|
3129
|
+
const candidates = lChoices[rec.id] ?? [];
|
|
3130
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3131
|
+
className: "wesync-app-card wesync-market-card",
|
|
3132
|
+
children: [
|
|
3133
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3134
|
+
className: "wesync-app-thumbwrap",
|
|
3135
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
3136
|
+
className: "wesync-app-thumb",
|
|
3137
|
+
src: "/we-sync/launcher/preview-file?id=" + encodeURIComponent(rec.id),
|
|
3138
|
+
alt: rec.title,
|
|
3139
|
+
loading: "lazy",
|
|
3140
|
+
onError: (e) => {
|
|
3141
|
+
e.currentTarget.style.visibility = "hidden";
|
|
3142
|
+
}
|
|
3143
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3144
|
+
className: "wesync-app-badge wesync-badge-launcher",
|
|
3145
|
+
children: t.typeLauncherApp
|
|
3146
|
+
})]
|
|
3147
|
+
}),
|
|
3148
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3149
|
+
className: "wesync-app-title",
|
|
3150
|
+
children: rec.title
|
|
3151
|
+
}),
|
|
3152
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3153
|
+
className: "wesync-market-meta",
|
|
3154
|
+
children: [
|
|
3155
|
+
humanSize(rec.size),
|
|
3156
|
+
" · ",
|
|
3157
|
+
rec.file
|
|
3158
|
+
]
|
|
3159
|
+
}),
|
|
3160
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3161
|
+
className: "wesync-market-actions",
|
|
3162
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3163
|
+
className: "wesync-btn wesync-detail-btn",
|
|
2510
3164
|
onClick: () => {
|
|
2511
3165
|
setLDetailFor(lDetailFor === rec.id ? null : rec.id);
|
|
2512
3166
|
},
|
|
2513
|
-
children:
|
|
2514
|
-
}),
|
|
2515
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2516
|
-
className: "wesync-btn wesync-market-uninstall",
|
|
2517
|
-
onClick: () => {
|
|
2518
|
-
onLauncherUninstall(rec);
|
|
2519
|
-
},
|
|
2520
|
-
children: t.launcherUninstall
|
|
3167
|
+
children: lDetailFor === rec.id ? t.detailHide : t.detail
|
|
2521
3168
|
})
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
3169
|
+
}),
|
|
3170
|
+
lDetailFor === rec.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3171
|
+
className: "wesync-market-meta",
|
|
3172
|
+
"data-wesync-detail": rec.id,
|
|
3173
|
+
style: { width: "100%" },
|
|
3174
|
+
children: [
|
|
3175
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
3176
|
+
t.detailInstalledAt,
|
|
3177
|
+
": ",
|
|
3178
|
+
formatInstalledAt(rec.installedAt)
|
|
3179
|
+
] }),
|
|
3180
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
3181
|
+
t.detailAddr,
|
|
3182
|
+
": ",
|
|
3183
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3184
|
+
style: { wordBreak: "break-all" },
|
|
3185
|
+
children: launcherAppDir(lRoot, rec.slug)
|
|
3186
|
+
})
|
|
3187
|
+
] }),
|
|
3188
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
3189
|
+
t.detailExe,
|
|
3190
|
+
": ",
|
|
3191
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3192
|
+
style: { wordBreak: "break-all" },
|
|
3193
|
+
children: rec.file
|
|
3194
|
+
})
|
|
3195
|
+
] }),
|
|
3196
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
3197
|
+
t.launcherSource,
|
|
3198
|
+
": ",
|
|
3199
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3200
|
+
style: { wordBreak: "break-all" },
|
|
3201
|
+
children: rec.sourceUrl
|
|
3202
|
+
})
|
|
3203
|
+
] }),
|
|
3204
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
3205
|
+
t.launcherSha,
|
|
3206
|
+
": ",
|
|
3207
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3208
|
+
style: { wordBreak: "break-all" },
|
|
3209
|
+
children: [rec.sha512.slice(0, 32), "…"]
|
|
3210
|
+
})
|
|
3211
|
+
] }),
|
|
3212
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3213
|
+
className: "wesync-btn",
|
|
3214
|
+
style: { marginTop: 4 },
|
|
3215
|
+
onClick: () => {
|
|
3216
|
+
onUpdatePreview(rec);
|
|
3217
|
+
},
|
|
3218
|
+
children: t.launcherUpdatePreview
|
|
3219
|
+
}),
|
|
3220
|
+
candidates.length > 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3221
|
+
style: { marginTop: 6 },
|
|
3222
|
+
children: t.launcherCandidates
|
|
3223
|
+
}), candidates.map((f) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3224
|
+
className: "wesync-btn",
|
|
3225
|
+
style: { margin: "2px 4px 0 0" },
|
|
3226
|
+
onClick: () => {
|
|
3227
|
+
onSetEntry(rec, f);
|
|
3228
|
+
},
|
|
3229
|
+
children: [
|
|
3230
|
+
f === rec.file ? "● " : "",
|
|
3231
|
+
f,
|
|
3232
|
+
f === rec.file ? "" : " → " + t.launcherSetEntry
|
|
3233
|
+
]
|
|
3234
|
+
}, f))] }) : null
|
|
3235
|
+
]
|
|
3236
|
+
}) : null
|
|
3237
|
+
]
|
|
3238
|
+
}, rec.id);
|
|
3239
|
+
})
|
|
2571
3240
|
})
|
|
2572
|
-
|
|
3241
|
+
] });
|
|
2573
3242
|
})()
|
|
2574
3243
|
] })
|
|
2575
3244
|
]
|
|
2576
3245
|
})
|
|
2577
3246
|
})
|
|
3247
|
+
}),
|
|
3248
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3249
|
+
className: "wesync-page-gap",
|
|
3250
|
+
ref: gap2Ref,
|
|
3251
|
+
children: [
|
|
3252
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "wesync-page-gap-line" }),
|
|
3253
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3254
|
+
className: "wesync-page-hint",
|
|
3255
|
+
children: t.pageGapHint
|
|
3256
|
+
}),
|
|
3257
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "wesync-page-gap-line" })
|
|
3258
|
+
]
|
|
3259
|
+
}),
|
|
3260
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3261
|
+
className: "wesync-page wesync-page-dwp",
|
|
3262
|
+
ref: dwpRef,
|
|
3263
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DwpEditorCard, {})
|
|
2578
3264
|
})
|
|
2579
3265
|
]
|
|
2580
3266
|
})
|
|
@@ -2616,31 +3302,71 @@ window.__ModuleLoader__.load({
|
|
|
2616
3302
|
]
|
|
2617
3303
|
})
|
|
2618
3304
|
}) : null,
|
|
2619
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3305
|
+
uninstallFor !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3306
|
+
className: "wesync-confirm-mask",
|
|
3307
|
+
onClick: () => setUninstallFor(null),
|
|
3308
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3309
|
+
className: "wesync-confirm",
|
|
3310
|
+
onClick: (e) => {
|
|
3311
|
+
e.stopPropagation();
|
|
3312
|
+
},
|
|
3313
|
+
children: [
|
|
3314
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3315
|
+
className: "wesync-confirm-title",
|
|
3316
|
+
children: uninstallFor.length === 1 ? t.confirmUninstallTitle : t.confirmBulkTitle(uninstallFor.length)
|
|
3317
|
+
}),
|
|
3318
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3319
|
+
className: "wesync-confirm-body",
|
|
3320
|
+
children: [
|
|
3321
|
+
t.confirmUninstallBody,
|
|
3322
|
+
uninstallFor.slice(0, 8).map((x) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
3323
|
+
className: "wesync-confirm-path",
|
|
3324
|
+
children: x.title
|
|
3325
|
+
}, x.kind + x.id)),
|
|
3326
|
+
uninstallFor.length === 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
3327
|
+
className: "wesync-confirm-path",
|
|
3328
|
+
children: uninstallFor[0].path
|
|
3329
|
+
}) : null,
|
|
3330
|
+
uninstallFor.length > 8 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3331
|
+
style: {
|
|
3332
|
+
marginTop: 4,
|
|
3333
|
+
opacity: .75
|
|
3334
|
+
},
|
|
3335
|
+
children: "… +" + String(uninstallFor.length - 8)
|
|
3336
|
+
}) : null
|
|
3337
|
+
]
|
|
3338
|
+
}),
|
|
3339
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3340
|
+
className: "wesync-confirm-actions",
|
|
3341
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3342
|
+
className: "wesync-btn",
|
|
3343
|
+
onClick: () => setUninstallFor(null),
|
|
3344
|
+
children: t.launcherConfirmCancel
|
|
3345
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3346
|
+
className: "wesync-btn wesync-market-uninstall",
|
|
3347
|
+
onClick: () => {
|
|
3348
|
+
onConfirmUninstall();
|
|
3349
|
+
},
|
|
3350
|
+
children: t.marketUninstall
|
|
3351
|
+
})]
|
|
3352
|
+
})
|
|
3353
|
+
]
|
|
3354
|
+
})
|
|
3355
|
+
}) : null,
|
|
3356
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2620
3357
|
className: "wesync-pager",
|
|
2621
|
-
children:
|
|
2622
|
-
className: ["wesync-pager-dot", page ===
|
|
2623
|
-
title: t.pageSettings,
|
|
2624
|
-
onClick: () => scrollToPage(
|
|
3358
|
+
children: PAGES.map((p, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3359
|
+
className: ["wesync-pager-dot", page === p ? "wesync-pager-dot-on" : ""].join(" "),
|
|
3360
|
+
title: p === "settings" ? t.pageSettings : p === "library" ? t.pageLibrary : t.pageDwp,
|
|
3361
|
+
onClick: () => scrollToPage(p),
|
|
2625
3362
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2626
3363
|
className: "wesync-pager-label",
|
|
2627
|
-
children: t.pageSettings
|
|
3364
|
+
children: p === "settings" ? t.pageSettings : p === "library" ? t.pageLibrary : t.pageDwp
|
|
2628
3365
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2629
3366
|
className: "wesync-pager-progress",
|
|
2630
|
-
ref:
|
|
3367
|
+
ref: progRefs[i]
|
|
2631
3368
|
})]
|
|
2632
|
-
}
|
|
2633
|
-
className: ["wesync-pager-dot", page === "library" ? "wesync-pager-dot-on" : ""].join(" "),
|
|
2634
|
-
title: t.pageLibrary,
|
|
2635
|
-
onClick: () => scrollToPage("library"),
|
|
2636
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2637
|
-
className: "wesync-pager-label",
|
|
2638
|
-
children: t.pageLibrary
|
|
2639
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2640
|
-
className: "wesync-pager-progress",
|
|
2641
|
-
ref: progLibRef
|
|
2642
|
-
})]
|
|
2643
|
-
})]
|
|
3369
|
+
}, p))
|
|
2644
3370
|
})
|
|
2645
3371
|
]
|
|
2646
3372
|
});
|
|
@@ -2707,7 +3433,11 @@ window.__ModuleLoader__.load({
|
|
|
2707
3433
|
min-width: 0;
|
|
2708
3434
|
min-height: 0;
|
|
2709
3435
|
height: 100%;
|
|
2710
|
-
|
|
3436
|
+
/* 高度上限:默认给宿主头部留 32px、再封 980px。
|
|
3437
|
+
--wesync-pages-max 由面板实测覆盖(面板顶 → 宿主输入框顶):输入框是
|
|
3438
|
+
sticky + z-index 7 的常驻条,压在它下面的内容点不到(elementFromPoint
|
|
3439
|
+
命中的是输入框),所以滚动视口必须止于输入框上沿。 */
|
|
3440
|
+
max-height: var(--wesync-pages-max, min(100vh - 32px, 980px));
|
|
2711
3441
|
overflow: hidden;
|
|
2712
3442
|
position: relative;
|
|
2713
3443
|
}
|
|
@@ -3197,6 +3927,107 @@ body[data-ds-dark-theme] .wesync-gaze-status.is-error { color: #fdba74; }
|
|
|
3197
3927
|
padding: 10px 2px;
|
|
3198
3928
|
}
|
|
3199
3929
|
|
|
3930
|
+
/* 壁纸库「本地」栏:应用大类下的二级分类行(全部 / we应用 / 应用)。
|
|
3931
|
+
与上一行筛选 chips 分开一点点间距,视觉上从属而非并列。 */
|
|
3932
|
+
.wesync-apps-subs {
|
|
3933
|
+
margin: 6px 0 8px;
|
|
3934
|
+
}
|
|
3935
|
+
|
|
3936
|
+
/* 应用启动器卡片:操作行只剩「详细」,撑满整行——点击区域大,
|
|
3937
|
+
不再是一排挤在卡片底部、字号 11px 的小按钮(原先「启动 / … / 卸载」三个)。 */
|
|
3938
|
+
.wesync-detail-btn {
|
|
3939
|
+
flex: 1;
|
|
3940
|
+
font-size: 12px;
|
|
3941
|
+
padding: 6px 10px;
|
|
3942
|
+
}
|
|
3943
|
+
|
|
3944
|
+
/* 壁纸库「本地」栏 · 管理模式:卡片上的操作行(打开源文件 / 卸载)。
|
|
3945
|
+
只在「管理」开启时渲染,平时卡片上没有任何小按钮。 */
|
|
3946
|
+
.wesync-manage-row {
|
|
3947
|
+
display: flex;
|
|
3948
|
+
gap: 6px;
|
|
3949
|
+
margin-top: 6px;
|
|
3950
|
+
}
|
|
3951
|
+
|
|
3952
|
+
.wesync-manage-row .wesync-btn {
|
|
3953
|
+
flex: 1;
|
|
3954
|
+
font-size: 11px;
|
|
3955
|
+
padding: 4px 8px;
|
|
3956
|
+
}
|
|
3957
|
+
|
|
3958
|
+
/* 卸载是破坏性操作:与「打开源文件」拉开颜色差异 */
|
|
3959
|
+
.wesync-manage-del {
|
|
3960
|
+
color: #fca5a5;
|
|
3961
|
+
}
|
|
3962
|
+
|
|
3963
|
+
/* 壁纸库「本地」栏 · 管理模式:卡片进入"编辑态"——
|
|
3964
|
+
整体轻微晃动(iOS 桌面编辑态的观感,每张卡由内联 animation-delay 错开相位),
|
|
3965
|
+
点卡片 = 多选:选中项固定不再晃,背景转蓝并打勾。
|
|
3966
|
+
只有真正可卸载的项(dwp 壁纸 / 启动器装的应用)能选中,工坊内容点了给提示。 */
|
|
3967
|
+
@keyframes wesync-jiggle {
|
|
3968
|
+
0% { transform: rotate(-0.9deg); }
|
|
3969
|
+
50% { transform: rotate(0.9deg); }
|
|
3970
|
+
100% { transform: rotate(-0.9deg); }
|
|
3971
|
+
}
|
|
3972
|
+
|
|
3973
|
+
.wesync-app-card-manage {
|
|
3974
|
+
cursor: pointer;
|
|
3975
|
+
/* 0.72s = 原 0.36s 放慢 50%(太快会显得躁,慢一点更像"编辑态待命") */
|
|
3976
|
+
animation: wesync-jiggle 0.72s ease-in-out infinite;
|
|
3977
|
+
}
|
|
3978
|
+
|
|
3979
|
+
/* 选中:停住晃动 + 蓝色高亮(用户要求的"被固定、背景变蓝") */
|
|
3980
|
+
.wesync-app-card-manage.wesync-app-card-selected {
|
|
3981
|
+
animation: none;
|
|
3982
|
+
transform: none;
|
|
3983
|
+
background: rgba(59, 130, 246, 0.26);
|
|
3984
|
+
border-color: rgba(59, 130, 246, 0.72);
|
|
3985
|
+
box-shadow: 0 0 0 1px rgba(59, 130, 246, 0.45);
|
|
3986
|
+
}
|
|
3987
|
+
|
|
3988
|
+
.wesync-app-card-selected:hover {
|
|
3989
|
+
background: rgba(59, 130, 246, 0.32);
|
|
3990
|
+
}
|
|
3991
|
+
|
|
3992
|
+
/* 选中角标:缩略图右上角的勾 */
|
|
3993
|
+
.wesync-app-selmark {
|
|
3994
|
+
position: absolute;
|
|
3995
|
+
top: 6px;
|
|
3996
|
+
right: 6px;
|
|
3997
|
+
width: 18px;
|
|
3998
|
+
height: 18px;
|
|
3999
|
+
border-radius: 50%;
|
|
4000
|
+
display: flex;
|
|
4001
|
+
align-items: center;
|
|
4002
|
+
justify-content: center;
|
|
4003
|
+
font-size: 11px;
|
|
4004
|
+
line-height: 1;
|
|
4005
|
+
color: #fff;
|
|
4006
|
+
background: rgba(59, 130, 246, 0.95);
|
|
4007
|
+
pointer-events: none;
|
|
4008
|
+
}
|
|
4009
|
+
|
|
4010
|
+
/* 管理模式的选择计数条:已选 N + 卸载选中 + 清空选择 */
|
|
4011
|
+
.wesync-selbar {
|
|
4012
|
+
margin: 6px 0 8px;
|
|
4013
|
+
}
|
|
4014
|
+
|
|
4015
|
+
.wesync-selbar-count {
|
|
4016
|
+
font-size: 12px;
|
|
4017
|
+
color: var(--dsw-alias-label-primary);
|
|
4018
|
+
margin-right: 2px;
|
|
4019
|
+
}
|
|
4020
|
+
|
|
4021
|
+
.wesync-selbar .wesync-btn {
|
|
4022
|
+
font-size: 12px;
|
|
4023
|
+
padding: 4px 10px;
|
|
4024
|
+
}
|
|
4025
|
+
|
|
4026
|
+
/* 无障碍:系统开启「减少动态效果」时不晃,选中态仍然清晰 */
|
|
4027
|
+
@media (prefers-reduced-motion: reduce) {
|
|
4028
|
+
.wesync-app-card-manage { animation: none; }
|
|
4029
|
+
}
|
|
4030
|
+
|
|
3200
4031
|
/* 壁纸库:本地 / 市场 分类行 + 类型筛选 chips + 标题搜索 */
|
|
3201
4032
|
.wesync-apps-cats {
|
|
3202
4033
|
display: flex;
|
|
@@ -3414,15 +4245,6 @@ body[data-ds-dark-theme] .wesync-gaze-status.is-error { color: #fdba74; }
|
|
|
3414
4245
|
.wesync-confirm-actions { display: flex; gap: 8px; justify-content: flex-end; }
|
|
3415
4246
|
.wesync-confirm-actions .wesync-market-install { flex: 0 0 auto; padding: 6px 18px; }
|
|
3416
4247
|
|
|
3417
|
-
/* ── 应用启动器:本地库 we应用 瓷砖的 ▶ 启动按钮 ──────────────────── */
|
|
3418
|
-
.wesync-app-launch {
|
|
3419
|
-
flex: 0 0 auto;
|
|
3420
|
-
font-size: 11px;
|
|
3421
|
-
padding: 2px 8px;
|
|
3422
|
-
border-radius: 999px;
|
|
3423
|
-
color: #fca5a5;
|
|
3424
|
-
}
|
|
3425
|
-
|
|
3426
4248
|
/* 禁用会话正文两侧的「拖拽调整宽度」把手,与轨迹页表现一致。
|
|
3427
4249
|
harness 只在检测到 composer-overlay 标记时隐藏这对把手(见
|
|
3428
4250
|
ui-conversation ConversationRoot.module.css),而本面板不接管
|
|
@@ -3433,14 +4255,18 @@ body:has(.wesync-panel) [data-width-handle] {
|
|
|
3433
4255
|
display: none;
|
|
3434
4256
|
}
|
|
3435
4257
|
|
|
3436
|
-
/* ──
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
4258
|
+
/* ── 「设置」「壁纸库」全程收起宿主输入框,只有「dwp创作」页显示 ────────
|
|
4259
|
+
两层原因:
|
|
4260
|
+
1) 观感:输入框固定占着会话列底部(实测 126px),两页的卡片底部行被它压住;
|
|
4261
|
+
2) 可点性:它是 sticky + z-index 7 的常驻条,压在它下面的控件点不到——
|
|
4262
|
+
elementFromPoint 命中的是输入框的「+」按钮 / 模式行,而不是卡片按钮
|
|
4263
|
+
(实测 1280×800:应用启动器卡片的按钮点击全被输入框吞掉)。
|
|
4264
|
+
收起姿势与沉浸模式同款(opacity + pointer-events,不改布局、seat 高度稳定):
|
|
4265
|
+
看不见,但点击穿透到面板。dwp创作页(唯一需要输入框的页)不加这条规则,
|
|
4266
|
+
其可用高度由面板实测的 --wesync-pages-max 扣掉输入框高度。
|
|
4267
|
+
:has([data-composer-card]) 只认真正的输入条:授权/提问/子代理只读条
|
|
4268
|
+
接管编辑器期间 seat 保持可见,不会把等待回答的输入框一起藏掉。 */
|
|
4269
|
+
body[data-wesync-page]:not([data-wesync-page='dwp']) [data-composer-seat]:has([data-composer-card]) {
|
|
3444
4270
|
opacity: 0 !important;
|
|
3445
4271
|
pointer-events: none !important;
|
|
3446
4272
|
transition: opacity 0.3s ease !important;
|
|
@@ -7088,6 +7914,7 @@ void main() {
|
|
|
7088
7914
|
}
|
|
7089
7915
|
ctx.drawImage(mc.canvas, -mc.originX, -mc.originY);
|
|
7090
7916
|
} else if (bmp !== null) {
|
|
7917
|
+
let src = bmp;
|
|
7091
7918
|
const ti = this.layerTexImage.get(layer.id);
|
|
7092
7919
|
let sw = ti !== void 0 ? ti[0] : bmp.width;
|
|
7093
7920
|
let sh = ti !== void 0 ? ti[1] : bmp.height;
|
|
@@ -7135,7 +7962,7 @@ void main() {
|
|
|
7135
7962
|
canvas: frameBmp
|
|
7136
7963
|
});
|
|
7137
7964
|
}
|
|
7138
|
-
|
|
7965
|
+
src = frameBmp;
|
|
7139
7966
|
sw = rw;
|
|
7140
7967
|
sh = rh;
|
|
7141
7968
|
}
|
|
@@ -7151,9 +7978,9 @@ void main() {
|
|
|
7151
7978
|
let eff = null;
|
|
7152
7979
|
if (this.wwGL !== null || WaterwavesGL.available) {
|
|
7153
7980
|
if (this.wwGL === null) this.wwGL = new WaterwavesGL();
|
|
7154
|
-
eff = this.wwGL.render(
|
|
7981
|
+
eff = this.wwGL.render(src, sw, sh, maskInfo !== void 0 ? maskInfo.bmp : null, maskInfo !== void 0 ? maskInfo.useA : false, wws, this.animTime, String(layer.id));
|
|
7155
7982
|
}
|
|
7156
|
-
if (eff === null) eff = applyWaterwaves(
|
|
7983
|
+
if (eff === null) eff = applyWaterwaves(src, sw, sh, wws, this.animTime, maskInfo !== void 0 ? maskInfo.bmp : null);
|
|
7157
7984
|
ctx.drawImage(eff, 0, 0, sw, sh, -dw / 2, -dh / 2, dw, dh);
|
|
7158
7985
|
} else if (nitros.length > 0) {
|
|
7159
7986
|
const nt = this.nitroTex.get(layer.id);
|
|
@@ -7170,9 +7997,9 @@ void main() {
|
|
|
7170
7997
|
smoothness: e.smoothness,
|
|
7171
7998
|
useMask: e.mask !== null && e.mask !== ""
|
|
7172
7999
|
}));
|
|
7173
|
-
eff = this.nitroGL.render(
|
|
8000
|
+
eff = this.nitroGL.render(src, sw, sh, nt.noise, nt.masks, params, this.animTime, String(layer.id));
|
|
7174
8001
|
}
|
|
7175
|
-
if (eff === null) ctx.drawImage(
|
|
8002
|
+
if (eff === null) ctx.drawImage(src, 0, 0, sw, sh, -dw / 2, -dh / 2, dw, dh);
|
|
7176
8003
|
else ctx.drawImage(eff, 0, 0, sw, sh, -dw / 2, -dh / 2, dw, dh);
|
|
7177
8004
|
} else if (shk !== void 0 && shk.type === "shake") {
|
|
7178
8005
|
const maskInfo2 = this.effectMasks.get(layer.id);
|
|
@@ -7181,8 +8008,8 @@ void main() {
|
|
|
7181
8008
|
const amp = shk.strength * shk.strength * effScale;
|
|
7182
8009
|
const dx = offset * amp * fd[0] * dw;
|
|
7183
8010
|
const dy = offset * amp * fd[1] * dh;
|
|
7184
|
-
ctx.drawImage(
|
|
7185
|
-
} else ctx.drawImage(
|
|
8011
|
+
ctx.drawImage(src, 0, 0, sw, sh, -dw / 2 + dx, -dh / 2 + dy, dw, dh);
|
|
8012
|
+
} else ctx.drawImage(src, 0, 0, sw, sh, -dw / 2, -dh / 2, dw, dh);
|
|
7186
8013
|
} else {
|
|
7187
8014
|
ctx.fillStyle = "rgba(120, 170, 255, 0.5)";
|
|
7188
8015
|
ctx.beginPath();
|
|
@@ -7260,7 +8087,8 @@ void main() {
|
|
|
7260
8087
|
"focus",
|
|
7261
8088
|
"renderMode",
|
|
7262
8089
|
"gazeEnabled",
|
|
7263
|
-
"gazeSnapText"
|
|
8090
|
+
"gazeSnapText",
|
|
8091
|
+
"floater"
|
|
7264
8092
|
];
|
|
7265
8093
|
const asNumber = (v, fallback, min, max) => typeof v === "number" && Number.isFinite(v) ? Math.min(max, Math.max(min, v)) : fallback;
|
|
7266
8094
|
const asBoolean = (v, fallback) => typeof v === "boolean" ? v : fallback;
|
|
@@ -7280,6 +8108,7 @@ void main() {
|
|
|
7280
8108
|
renderMode: mode === "eco" || mode === "perf" || mode === "enhanced" ? mode : d.renderMode,
|
|
7281
8109
|
gazeEnabled: focus && asBoolean(o.gazeEnabled, d.gazeEnabled),
|
|
7282
8110
|
gazeSnapText: asBoolean(o.gazeSnapText, d.gazeSnapText),
|
|
8111
|
+
floater: asBoolean(o.floater, d.floater),
|
|
7283
8112
|
taskActive: d.taskActive,
|
|
7284
8113
|
approvalPending: d.approvalPending,
|
|
7285
8114
|
immersive: d.immersive
|
|
@@ -7298,8 +8127,12 @@ void main() {
|
|
|
7298
8127
|
/**
|
|
7299
8128
|
* 生成「写即存」的设置对象:初始值来自存档,之后每次改动自动落盘。
|
|
7300
8129
|
* 滑块拖动会高频触发写入,故合并成 250ms 的尾随写;页面隐藏 / 关闭前强制补一次,避免丢最后一次改动。
|
|
8130
|
+
*
|
|
8131
|
+
* onRemoteChange:跨页签同步 —— 另一个页签改了设置(storage 事件只在其他页签触发)时,
|
|
8132
|
+
* 把新值就地写进本页面 target(绕开 Proxy 避免回写震荡)并回调,让面板镜像与悬浮球上报跟上
|
|
8133
|
+
* (否则旧页签内存里 floater=false 会一直上报错误开关值,把别人挂起的球误拆——实测过的坑)。
|
|
7301
8134
|
*/
|
|
7302
|
-
function createPersistentSettings(defaults) {
|
|
8135
|
+
function createPersistentSettings(defaults, onRemoteChange) {
|
|
7303
8136
|
const target = readStoredSettings(defaults);
|
|
7304
8137
|
let timer = null;
|
|
7305
8138
|
const flush = () => {
|
|
@@ -7321,6 +8154,22 @@ void main() {
|
|
|
7321
8154
|
document.addEventListener("visibilitychange", () => {
|
|
7322
8155
|
if (document.visibilityState === "hidden") flush();
|
|
7323
8156
|
});
|
|
8157
|
+
if (onRemoteChange !== void 0) window.addEventListener("storage", (e) => {
|
|
8158
|
+
if (e.key !== "we-sync.settings") return;
|
|
8159
|
+
let changed = false;
|
|
8160
|
+
try {
|
|
8161
|
+
const next = readStoredSettings(defaults);
|
|
8162
|
+
const rec = target;
|
|
8163
|
+
for (const key of PERSISTED_KEYS) {
|
|
8164
|
+
const k = key;
|
|
8165
|
+
if (rec[k] !== next[k]) {
|
|
8166
|
+
rec[k] = next[k];
|
|
8167
|
+
changed = true;
|
|
8168
|
+
}
|
|
8169
|
+
}
|
|
8170
|
+
} catch {}
|
|
8171
|
+
if (changed) onRemoteChange();
|
|
8172
|
+
});
|
|
7324
8173
|
return new Proxy(target, { set(obj, prop, value) {
|
|
7325
8174
|
const key = prop;
|
|
7326
8175
|
const rec = obj;
|
|
@@ -10239,6 +11088,8 @@ void main(){
|
|
|
10239
11088
|
weekday
|
|
10240
11089
|
};
|
|
10241
11090
|
}
|
|
11091
|
+
/** 1×1 RGBA、全透明、filter 0 的合法 PNG 字节。 */
|
|
11092
|
+
const TINY_PNG = Uint8Array.from(atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVR4nGNgAAIAAAUAAXpeqz8AAAAASUVORK5CYII="), (c) => c.charCodeAt(0));
|
|
10242
11093
|
//#endregion
|
|
10243
11094
|
//#region src/client/dwp-stage.ts
|
|
10244
11095
|
/**
|
|
@@ -10247,6 +11098,16 @@ void main(){
|
|
|
10247
11098
|
* 浏览器专用(依赖 DOM/canvas/createImageBitmap),不进 Node 测试网;
|
|
10248
11099
|
* 逻辑与 demo(dwp-runtime-web/demo)同源,复用同一 mount() → 像素一致。
|
|
10249
11100
|
*/
|
|
11101
|
+
/**
|
|
11102
|
+
* 低档位用 1×1 全透明 PNG 顶替高档资源(字节见 tiny-png.ts,带回归测试)。
|
|
11103
|
+
* 为什么是"顶替"而不是"不传":@dwp/web 的 loadAssets 对缺失资源直接抛错(整次挂载失败),
|
|
11104
|
+
* 所以必须给一个合法但极小的位图;这些图层在低档位 alpha 恒 0,画面上完全看不出来。
|
|
11105
|
+
*/
|
|
11106
|
+
/** 读取 scene 顶层的插件扩展字段 dsh.hdAssets(未知键 core 会忽略,这里防御式读取)。 */
|
|
11107
|
+
function hdAssetsOf(scene) {
|
|
11108
|
+
const list = scene.dsh?.hdAssets;
|
|
11109
|
+
return Array.isArray(list) ? list.filter((p) => typeof p === "string") : [];
|
|
11110
|
+
}
|
|
10250
11111
|
/** 拉取并挂载一个已装 DWP 到 canvas,返回可播放/截图/销毁的 Handle。 */
|
|
10251
11112
|
async function mountDwp(canvas, id, opts = {}) {
|
|
10252
11113
|
const fetchFn = opts.fetchFn ?? ((u, i) => fetch(u, i));
|
|
@@ -10259,9 +11120,15 @@ void main(){
|
|
|
10259
11120
|
const mres = await fetchFn(`${base}/manifest?id=${encodeURIComponent(id)}`, { cache: "no-store" });
|
|
10260
11121
|
if (mres.ok) manifest = await mres.json();
|
|
10261
11122
|
} catch {}
|
|
11123
|
+
const stub = new Set(opts.quality === "hd" ? [] : hdAssetsOf(scene));
|
|
11124
|
+
const vq = manifest?.version !== void 0 && manifest.version !== "" ? "&v=" + encodeURIComponent(manifest.version) : "";
|
|
10262
11125
|
const files = /* @__PURE__ */ new Map();
|
|
10263
11126
|
for (const ref of collectAssetRefs(scene)) {
|
|
10264
|
-
|
|
11127
|
+
if (stub.has(ref.path)) {
|
|
11128
|
+
files.set(ref.path, new Blob([TINY_PNG], { type: "image/png" }));
|
|
11129
|
+
continue;
|
|
11130
|
+
}
|
|
11131
|
+
const r = await fetchFn(`${base}/file?id=${encodeURIComponent(id)}&name=${encodeURIComponent(ref.path)}${vq}`);
|
|
10265
11132
|
if (r.ok) files.set(ref.path, await r.blob());
|
|
10266
11133
|
}
|
|
10267
11134
|
return mount(canvas, {
|
|
@@ -10286,12 +11153,36 @@ void main(){
|
|
|
10286
11153
|
handle = null;
|
|
10287
11154
|
mountingId = "";
|
|
10288
11155
|
mountedId = "";
|
|
10289
|
-
/**
|
|
10290
|
-
|
|
11156
|
+
/**
|
|
11157
|
+
* 最新实时变量(按键累积)。不同来源互不覆盖(时钟 vs 工作区脉搏),
|
|
11158
|
+
* 每次挂载成功后整表重放一次 —— 于是"切到另一张壁纸""切档位重新挂载"都能立刻拿到当前
|
|
11159
|
+
* 时间/档位,而不是干等下一次变量变化(夜间切入日夜壁纸却一直显示白天就是这么来的)。
|
|
11160
|
+
*/
|
|
11161
|
+
liveVars = {};
|
|
11162
|
+
/**
|
|
11163
|
+
* 挂载序号:每次 mount/unmount 自增,异步结果只有在序号仍是最新时才允许接管状态。
|
|
11164
|
+
* 只比 id 是不够的 —— 同一张壁纸切画质时新旧请求的 id 相同,旧(低清)结果可能后到达并覆盖新的。
|
|
11165
|
+
*/
|
|
11166
|
+
seq = 0;
|
|
11167
|
+
/** 纹理档位:'hd' = 允许拉场景声明的高档纹理,'sd' = 高档资源用占位图顶替(见 dwp-stage.ts) */
|
|
11168
|
+
quality = "sd";
|
|
10291
11169
|
/** 当前正在挂载或已挂载的 DWP id('' = 无)。 */
|
|
10292
11170
|
currentId() {
|
|
10293
11171
|
return this.mountedId !== "" ? this.mountedId : this.mountingId;
|
|
10294
11172
|
}
|
|
11173
|
+
/**
|
|
11174
|
+
* 切换纹理档位(渲染模式「增强/完整」→ hd,「预览/捕获」→ sd)。
|
|
11175
|
+
* 档位变了必须**重新挂载**:资源集合在 mount 时就定好了(低档位根本没拉 8K 纹理),
|
|
11176
|
+
* 光改变量换不出另一档的图。已挂载时这里先卸载再按新档位挂回去。
|
|
11177
|
+
*/
|
|
11178
|
+
setQuality(q) {
|
|
11179
|
+
if (q === this.quality) return;
|
|
11180
|
+
this.quality = q;
|
|
11181
|
+
const id = this.currentId();
|
|
11182
|
+
if (id === "") return;
|
|
11183
|
+
this.unmount();
|
|
11184
|
+
this.mount(id);
|
|
11185
|
+
}
|
|
10295
11186
|
/** 当前 DOM 中的舞台 canvas(mount 内部 GL→Canvas2D 降级会换新元素,按 data-dwp-stage 定位)。 */
|
|
10296
11187
|
liveCanvas() {
|
|
10297
11188
|
return document.querySelector("canvas[data-dwp-stage=\"1\"]");
|
|
@@ -10317,29 +11208,30 @@ void main(){
|
|
|
10317
11208
|
if (this.canvas.parentNode === null) document.body.appendChild(this.canvas);
|
|
10318
11209
|
return this.canvas;
|
|
10319
11210
|
}
|
|
10320
|
-
/** 挂载指定 DWP 为背景。同 id
|
|
10321
|
-
async mount(id) {
|
|
10322
|
-
if (this.mountingId === id || this.mountedId === id) return;
|
|
11211
|
+
/** 挂载指定 DWP 为背景。同 id 幂等(除非 force);换 id / 换档位先销毁旧的。异步(拉 scene+资源)。 */
|
|
11212
|
+
async mount(id, opts) {
|
|
11213
|
+
if (opts?.force !== true && (this.mountingId === id || this.mountedId === id)) return;
|
|
10323
11214
|
this.disposeHandle();
|
|
11215
|
+
const mySeq = ++this.seq;
|
|
10324
11216
|
this.mountingId = id;
|
|
10325
11217
|
const canvas = this.ensureCanvas();
|
|
10326
11218
|
try {
|
|
10327
|
-
const handle = await mountDwp(canvas, id, {
|
|
10328
|
-
|
|
10329
|
-
|
|
10330
|
-
|
|
11219
|
+
const handle = await mountDwp(canvas, id, {
|
|
11220
|
+
quality: this.quality,
|
|
11221
|
+
onDegrade: (d) => {
|
|
11222
|
+
if (d.length) console.warn("[dwp] 降级/告警:", d.join(", "));
|
|
11223
|
+
}
|
|
11224
|
+
});
|
|
11225
|
+
if (mySeq !== this.seq) {
|
|
10331
11226
|
handle.dispose();
|
|
10332
11227
|
return;
|
|
10333
11228
|
}
|
|
10334
11229
|
this.handle = handle;
|
|
10335
11230
|
this.mountedId = id;
|
|
10336
11231
|
this.mountingId = "";
|
|
10337
|
-
if (
|
|
10338
|
-
handle.setParams(this.pendingVars);
|
|
10339
|
-
this.pendingVars = null;
|
|
10340
|
-
}
|
|
11232
|
+
if (Object.keys(this.liveVars).length > 0) handle.setParams(this.liveVars);
|
|
10341
11233
|
} catch (e) {
|
|
10342
|
-
if (
|
|
11234
|
+
if (mySeq === this.seq) this.mountingId = "";
|
|
10343
11235
|
throw e;
|
|
10344
11236
|
}
|
|
10345
11237
|
}
|
|
@@ -10350,14 +11242,22 @@ void main(){
|
|
|
10350
11242
|
c.style.filter = blurPx > 0 ? "blur(" + blurPx + "px)" : "none";
|
|
10351
11243
|
c.style.transform = "scale(" + scale.toFixed(3) + ")";
|
|
10352
11244
|
}
|
|
10353
|
-
/**
|
|
11245
|
+
/**
|
|
11246
|
+
* 实时变量喂食(时钟、工作区脉搏等)。
|
|
11247
|
+
* 挂载中/未挂载时先累加到 liveVars(**按键合并**,不同来源互不覆盖),
|
|
11248
|
+
* 已挂载则把本次的键立即推给运行时(Handle.setParams 本身也是逐键覆写)。
|
|
11249
|
+
*/
|
|
10354
11250
|
setLiveVars(map) {
|
|
10355
|
-
|
|
10356
|
-
this.
|
|
10357
|
-
|
|
10358
|
-
}
|
|
11251
|
+
this.liveVars = {
|
|
11252
|
+
...this.liveVars,
|
|
11253
|
+
...map
|
|
11254
|
+
};
|
|
11255
|
+
if (this.handle === null) return;
|
|
10359
11256
|
this.handle.setParams(map);
|
|
10360
|
-
|
|
11257
|
+
}
|
|
11258
|
+
/** 当前累积的实时变量(测试/诊断用)。 */
|
|
11259
|
+
currentVars() {
|
|
11260
|
+
return { ...this.liveVars };
|
|
10361
11261
|
}
|
|
10362
11262
|
disposeHandle() {
|
|
10363
11263
|
if (this.handle !== null) {
|
|
@@ -10367,8 +11267,9 @@ void main(){
|
|
|
10367
11267
|
this.mountedId = "";
|
|
10368
11268
|
this.mountingId = "";
|
|
10369
11269
|
}
|
|
10370
|
-
/** 卸载:停渲染 +
|
|
11270
|
+
/** 卸载:停渲染 + 移除画布。序号自增 → 任何在途挂载的结果都会被丢弃。 */
|
|
10371
11271
|
unmount() {
|
|
11272
|
+
this.seq++;
|
|
10372
11273
|
this.disposeHandle();
|
|
10373
11274
|
const c = this.liveCanvas() ?? this.canvas;
|
|
10374
11275
|
if (c !== null) c.remove();
|
|
@@ -10408,6 +11309,126 @@ void main(){
|
|
|
10408
11309
|
}
|
|
10409
11310
|
return vars;
|
|
10410
11311
|
}
|
|
11312
|
+
const DAY_MINUTES = 1440;
|
|
11313
|
+
/** 一天中的分钟数(0..1439.99…,带秒的小数)→ 夜间透明度 0..1。 */
|
|
11314
|
+
function nightAlpha(minutesOfDay) {
|
|
11315
|
+
const m = (minutesOfDay % DAY_MINUTES + DAY_MINUTES) % DAY_MINUTES;
|
|
11316
|
+
const start = 1080;
|
|
11317
|
+
const nightLen = 720 % DAY_MINUTES;
|
|
11318
|
+
const rel = (m - start + DAY_MINUTES) % DAY_MINUTES;
|
|
11319
|
+
const clamp01 = (x) => x < 0 ? 0 : x > 1 ? 1 : x;
|
|
11320
|
+
if (rel <= 10) return clamp01(rel / 10);
|
|
11321
|
+
if (rel >= 710 && rel <= nightLen) return clamp01((nightLen - rel) / 10);
|
|
11322
|
+
return rel < nightLen ? 1 : 0;
|
|
11323
|
+
}
|
|
11324
|
+
/** 当前时间(+ 纹理档位)→ 变量表(值域收敛到 4 位小数,避免每次轮询都产生"变化")。 */
|
|
11325
|
+
function clockVars(now, opts = {}) {
|
|
11326
|
+
const hd = opts.hd === true;
|
|
11327
|
+
const minutes = now.getHours() * 60 + now.getMinutes() + now.getSeconds() / 60;
|
|
11328
|
+
const alpha = Number(nightAlpha(minutes).toFixed(4));
|
|
11329
|
+
return {
|
|
11330
|
+
hour: now.getHours(),
|
|
11331
|
+
night_alpha: alpha,
|
|
11332
|
+
night_on: alpha >= .9999 ? 1 : 0,
|
|
11333
|
+
day_on: alpha <= 1e-4 ? 1 : 0,
|
|
11334
|
+
hd_on: hd ? 1 : 0,
|
|
11335
|
+
night_sd: hd ? 0 : alpha,
|
|
11336
|
+
night_hd: hd ? alpha : 0
|
|
11337
|
+
};
|
|
11338
|
+
}
|
|
11339
|
+
/** 去重签名:值没变就不喂(避免无谓重绘)。 */
|
|
11340
|
+
function clockSig(vars) {
|
|
11341
|
+
return Object.keys(vars).sort().map((k) => k + "=" + String(vars[k])).join(",");
|
|
11342
|
+
}
|
|
11343
|
+
//#endregion
|
|
11344
|
+
//#region src/client/floater-report.ts
|
|
11345
|
+
const FLOATER_URL = "/we-sync/floater";
|
|
11346
|
+
const FLOATER_HEARTBEAT_MS = 5e3;
|
|
11347
|
+
function startFloaterReporter(env, isEnabled) {
|
|
11348
|
+
let lastColor = "";
|
|
11349
|
+
let blurred = false;
|
|
11350
|
+
let blurTimer = 0;
|
|
11351
|
+
let disposed = false;
|
|
11352
|
+
function current() {
|
|
11353
|
+
return env.visibilityState() === "hidden" || blurred ? "hidden" : "visible";
|
|
11354
|
+
}
|
|
11355
|
+
let lastSent = current();
|
|
11356
|
+
const send = (state, keepalive) => {
|
|
11357
|
+
try {
|
|
11358
|
+
env.send(FLOATER_URL, JSON.stringify({
|
|
11359
|
+
pageId: env.pageId(),
|
|
11360
|
+
enabled: isEnabled(),
|
|
11361
|
+
state,
|
|
11362
|
+
title: env.title(),
|
|
11363
|
+
color: lastColor !== "" ? lastColor : void 0
|
|
11364
|
+
}), keepalive);
|
|
11365
|
+
} catch {}
|
|
11366
|
+
};
|
|
11367
|
+
/** 只在状态相对上次已发送值变化时发(blur/focus 抖动、重复事件不刷请求)。 */
|
|
11368
|
+
const emit = (keepalive) => {
|
|
11369
|
+
if (disposed) return;
|
|
11370
|
+
const s = current();
|
|
11371
|
+
if (s === lastSent) return;
|
|
11372
|
+
lastSent = s;
|
|
11373
|
+
send(s, keepalive);
|
|
11374
|
+
};
|
|
11375
|
+
const cancelBlur = () => {
|
|
11376
|
+
if (blurTimer !== 0) {
|
|
11377
|
+
env.clearTimeout(blurTimer);
|
|
11378
|
+
blurTimer = 0;
|
|
11379
|
+
}
|
|
11380
|
+
};
|
|
11381
|
+
const offs = [
|
|
11382
|
+
env.onVisibilityChange(() => emit(true)),
|
|
11383
|
+
env.onPageHide(() => send("unloading", true)),
|
|
11384
|
+
env.onBlur(() => {
|
|
11385
|
+
if (blurTimer !== 0 || blurred) return;
|
|
11386
|
+
blurTimer = env.setTimeout(() => {
|
|
11387
|
+
blurTimer = 0;
|
|
11388
|
+
blurred = true;
|
|
11389
|
+
emit(true);
|
|
11390
|
+
}, 400);
|
|
11391
|
+
}),
|
|
11392
|
+
env.onFocus(() => {
|
|
11393
|
+
cancelBlur();
|
|
11394
|
+
blurred = false;
|
|
11395
|
+
emit(true);
|
|
11396
|
+
})
|
|
11397
|
+
];
|
|
11398
|
+
const heartbeat = env.setInterval(() => {
|
|
11399
|
+
if (!disposed && isEnabled() && current() === "visible") {
|
|
11400
|
+
lastSent = "visible";
|
|
11401
|
+
send("visible", true);
|
|
11402
|
+
}
|
|
11403
|
+
}, FLOATER_HEARTBEAT_MS);
|
|
11404
|
+
send(lastSent, false);
|
|
11405
|
+
return {
|
|
11406
|
+
syncNow() {
|
|
11407
|
+
const s = current();
|
|
11408
|
+
lastSent = s;
|
|
11409
|
+
send(s, false);
|
|
11410
|
+
},
|
|
11411
|
+
pingColor(color) {
|
|
11412
|
+
if (color === lastColor || !isEnabled()) return;
|
|
11413
|
+
lastColor = color;
|
|
11414
|
+
const s = current();
|
|
11415
|
+
lastSent = s;
|
|
11416
|
+
send(s, false);
|
|
11417
|
+
},
|
|
11418
|
+
get state() {
|
|
11419
|
+
return current();
|
|
11420
|
+
},
|
|
11421
|
+
get lastColor() {
|
|
11422
|
+
return lastColor;
|
|
11423
|
+
},
|
|
11424
|
+
dispose() {
|
|
11425
|
+
disposed = true;
|
|
11426
|
+
cancelBlur();
|
|
11427
|
+
for (const off of offs) off();
|
|
11428
|
+
env.clearInterval(heartbeat);
|
|
11429
|
+
}
|
|
11430
|
+
};
|
|
11431
|
+
}
|
|
10411
11432
|
//#endregion
|
|
10412
11433
|
//#region src/client/index.ts
|
|
10413
11434
|
/**
|
|
@@ -10419,7 +11440,7 @@ void main(){
|
|
|
10419
11440
|
*/
|
|
10420
11441
|
const inject = ["slots", "theme"];
|
|
10421
11442
|
/** 插件版本:构建期由 tsdown 的 define 从 package.json 注入(未注入的裸跑为 'dev')。 */
|
|
10422
|
-
const PLUGIN_VERSION = "26.9.
|
|
11443
|
+
const PLUGIN_VERSION = "26.9.13";
|
|
10423
11444
|
/** 插件 GitHub 仓库主页(版本号角标点击跳转)。 */
|
|
10424
11445
|
const PLUGIN_REPO_URL = "https://github.com/YRN-playmaker/dsh-wallpaper_share";
|
|
10425
11446
|
/** 专注模式:任务进行中(本版下调的全局值;鼠标圆内另按 FOCUS_LENS 加浓) */
|
|
@@ -10461,6 +11482,7 @@ void main(){
|
|
|
10461
11482
|
renderMode: "perf",
|
|
10462
11483
|
gazeEnabled: false,
|
|
10463
11484
|
gazeSnapText: true,
|
|
11485
|
+
floater: false,
|
|
10464
11486
|
immersive: false,
|
|
10465
11487
|
approvalPending: false,
|
|
10466
11488
|
dwpMounted: null
|
|
@@ -10475,8 +11497,13 @@ void main(){
|
|
|
10475
11497
|
/** 用户偏好(同步开关 / 渲染模式 / 显示器锁 / 透明度·模糊·阴影 / 专注·眼动)经 localStorage 持久化:
|
|
10476
11498
|
* 写即存,刷新或重启 DSH 后自动恢复。实现见 settings.ts —— 包一层 Proxy,所有
|
|
10477
11499
|
* `store.settings.x = v` 的既有赋值点无需改动即自动落盘;派生态(taskActive /
|
|
10478
|
-
* approvalPending)与临时视图态(immersive)不在落盘白名单内。
|
|
10479
|
-
|
|
11500
|
+
* approvalPending)与临时视图态(immersive)不在落盘白名单内。
|
|
11501
|
+
* 跨页签改动(storage 事件)就地同步并回调:notify 刷面板,syncFloater 让本页面立刻
|
|
11502
|
+
* 上报新的 enabled 值——不然旧页签的内存态会把别人挂起的球误拆。 */
|
|
11503
|
+
settings: createPersistentSettings(DEFAULT_SETTINGS, () => {
|
|
11504
|
+
store.notify();
|
|
11505
|
+
store.actions.syncFloater();
|
|
11506
|
+
}),
|
|
10480
11507
|
listeners: /* @__PURE__ */ new Set(),
|
|
10481
11508
|
actions: {
|
|
10482
11509
|
applyTheme: () => {},
|
|
@@ -10484,7 +11511,9 @@ void main(){
|
|
|
10484
11511
|
applyImmersive: () => {},
|
|
10485
11512
|
repoll: () => {},
|
|
10486
11513
|
mountDwp: async (_id) => false,
|
|
10487
|
-
unmountDwp: async () => {}
|
|
11514
|
+
unmountDwp: async () => {},
|
|
11515
|
+
/** 悬浮球开关变更后立即重发 sync(真实现在下方 ctx.effect 里覆盖) */
|
|
11516
|
+
syncFloater: () => {}
|
|
10488
11517
|
},
|
|
10489
11518
|
subscribe(fn) {
|
|
10490
11519
|
store.listeners.add(fn);
|
|
@@ -10607,6 +11636,55 @@ void main(){
|
|
|
10607
11636
|
running: "#3b82f6",
|
|
10608
11637
|
idle: "#22c55e"
|
|
10609
11638
|
};
|
|
11639
|
+
const floaterPageId = () => {
|
|
11640
|
+
try {
|
|
11641
|
+
let id = sessionStorage.getItem("we-sync.floater-page");
|
|
11642
|
+
if (id === null) {
|
|
11643
|
+
id = Math.random().toString(36).slice(2) + Date.now().toString(36);
|
|
11644
|
+
sessionStorage.setItem("we-sync.floater-page", id);
|
|
11645
|
+
}
|
|
11646
|
+
return id;
|
|
11647
|
+
} catch {
|
|
11648
|
+
return "legacy";
|
|
11649
|
+
}
|
|
11650
|
+
};
|
|
11651
|
+
let floaterReporter = null;
|
|
11652
|
+
try {
|
|
11653
|
+
floaterReporter = startFloaterReporter({
|
|
11654
|
+
visibilityState: () => document.visibilityState === "hidden" ? "hidden" : "visible",
|
|
11655
|
+
title: () => document.title,
|
|
11656
|
+
pageId: floaterPageId,
|
|
11657
|
+
onFocus: (fn) => {
|
|
11658
|
+
window.addEventListener("focus", fn);
|
|
11659
|
+
return () => window.removeEventListener("focus", fn);
|
|
11660
|
+
},
|
|
11661
|
+
onBlur: (fn) => {
|
|
11662
|
+
window.addEventListener("blur", fn);
|
|
11663
|
+
return () => window.removeEventListener("blur", fn);
|
|
11664
|
+
},
|
|
11665
|
+
onVisibilityChange: (fn) => {
|
|
11666
|
+
document.addEventListener("visibilitychange", fn);
|
|
11667
|
+
return () => document.removeEventListener("visibilitychange", fn);
|
|
11668
|
+
},
|
|
11669
|
+
onPageHide: (fn) => {
|
|
11670
|
+
window.addEventListener("pagehide", fn);
|
|
11671
|
+
return () => window.removeEventListener("pagehide", fn);
|
|
11672
|
+
},
|
|
11673
|
+
setInterval: (fn, ms) => window.setInterval(fn, ms),
|
|
11674
|
+
clearInterval: (handle) => window.clearInterval(handle),
|
|
11675
|
+
setTimeout: (fn, ms) => window.setTimeout(fn, ms),
|
|
11676
|
+
clearTimeout: (handle) => window.clearTimeout(handle),
|
|
11677
|
+
send: (url, body, keepalive) => {
|
|
11678
|
+
fetch(url, {
|
|
11679
|
+
method: "POST",
|
|
11680
|
+
headers: { "Content-Type": "application/json" },
|
|
11681
|
+
body,
|
|
11682
|
+
keepalive
|
|
11683
|
+
}).catch(() => {});
|
|
11684
|
+
}
|
|
11685
|
+
}, () => store.settings.floater);
|
|
11686
|
+
store.actions.syncFloater = () => floaterReporter?.syncNow();
|
|
11687
|
+
} catch {}
|
|
10610
11688
|
function syncStatus() {
|
|
10611
11689
|
const approval = document.querySelector("[data-approval-key]") !== null;
|
|
10612
11690
|
if (approval !== store.settings.approvalPending) {
|
|
@@ -10615,6 +11693,7 @@ void main(){
|
|
|
10615
11693
|
}
|
|
10616
11694
|
const color = approval ? STATUS_COLORS.approval : store.settings.taskActive ? STATUS_COLORS.running : STATUS_COLORS.idle;
|
|
10617
11695
|
orbBtn.style.borderColor = color;
|
|
11696
|
+
floaterReporter?.pingColor(color.slice(1));
|
|
10618
11697
|
orbBtn.title = approval ? "等待授权" : store.settings.taskActive ? "任务进行中" : "空闲";
|
|
10619
11698
|
const sidebarCollapsed = document.querySelector("[data-sidebar-collapsed]") !== null;
|
|
10620
11699
|
orbBtn.style.opacity = sidebarCollapsed ? "1" : "0";
|
|
@@ -10851,6 +11930,10 @@ void main(){
|
|
|
10851
11930
|
attributes: true,
|
|
10852
11931
|
attributeFilter: ["data-sidebar-collapsed"]
|
|
10853
11932
|
});
|
|
11933
|
+
/** 渲染模式 → DWP 纹理档位:「增强/完整」用高档纹理,其余(预览/捕获)用低档。 */
|
|
11934
|
+
function qualityOf(mode) {
|
|
11935
|
+
return mode === "enhanced" ? "hd" : "sd";
|
|
11936
|
+
}
|
|
10854
11937
|
function applyBackground() {
|
|
10855
11938
|
if (store.settings.dwpMounted !== null) {
|
|
10856
11939
|
const blurPx = store.settings.focus ? 0 : Math.round(effectiveVisuals().blur);
|
|
@@ -10859,6 +11942,7 @@ void main(){
|
|
|
10859
11942
|
stopSceneLayers();
|
|
10860
11943
|
setMedia(null);
|
|
10861
11944
|
styleTag.textContent = "html { background-color: #0d0e12; }body::after { content: \"\"; position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: -1; background: linear-gradient(rgba(6,8,12," + shadowAlpha.toFixed(3) + "), rgba(6,8,12," + (shadowAlpha * .85).toFixed(3) + ")); }";
|
|
11945
|
+
dwpBg.setQuality(qualityOf(store.settings.renderMode));
|
|
10862
11946
|
dwpBg.mount(store.settings.dwpMounted).then(() => dwpBg.applyVisuals(blurPx, scale)).catch((e) => {
|
|
10863
11947
|
console.error("[dwp] 背景挂载失败:", e);
|
|
10864
11948
|
});
|
|
@@ -10955,6 +12039,19 @@ void main(){
|
|
|
10955
12039
|
dwpBg.setLiveVars(map);
|
|
10956
12040
|
} catch {}
|
|
10957
12041
|
}
|
|
12042
|
+
let lastClockSig = "";
|
|
12043
|
+
function feedClockVars() {
|
|
12044
|
+
if (store.settings.dwpMounted === null) {
|
|
12045
|
+
lastClockSig = "";
|
|
12046
|
+
return;
|
|
12047
|
+
}
|
|
12048
|
+
const mounted = store.settings.dwpMounted;
|
|
12049
|
+
const vars = clockVars(/* @__PURE__ */ new Date(), { hd: qualityOf(store.settings.renderMode) === "hd" });
|
|
12050
|
+
const sig = mounted + "|" + clockSig(vars);
|
|
12051
|
+
if (sig === lastClockSig) return;
|
|
12052
|
+
lastClockSig = sig;
|
|
12053
|
+
dwpBg.setLiveVars(vars);
|
|
12054
|
+
}
|
|
10958
12055
|
async function poll() {
|
|
10959
12056
|
if (polling) return;
|
|
10960
12057
|
polling = true;
|
|
@@ -10977,6 +12074,7 @@ void main(){
|
|
|
10977
12074
|
applyBackground();
|
|
10978
12075
|
}
|
|
10979
12076
|
await pollWorkspacePulse();
|
|
12077
|
+
feedClockVars();
|
|
10980
12078
|
} catch {}
|
|
10981
12079
|
polling = false;
|
|
10982
12080
|
}
|
|
@@ -10993,7 +12091,6 @@ void main(){
|
|
|
10993
12091
|
if (store.settings.dwpMounted === null) dwpPrevEnabled = store.settings.enabled;
|
|
10994
12092
|
store.settings.dwpMounted = id;
|
|
10995
12093
|
store.settings.enabled = false;
|
|
10996
|
-
if (store.settings.renderMode === "perf") store.settings.renderMode = "enhanced";
|
|
10997
12094
|
store.notify();
|
|
10998
12095
|
applyBackground();
|
|
10999
12096
|
return true;
|
|
@@ -11010,7 +12107,6 @@ void main(){
|
|
|
11010
12107
|
dwpPrevEnabled = true;
|
|
11011
12108
|
store.settings.dwpMounted = applied.id;
|
|
11012
12109
|
store.settings.enabled = false;
|
|
11013
|
-
if (store.settings.renderMode === "perf") store.settings.renderMode = "enhanced";
|
|
11014
12110
|
store.notify();
|
|
11015
12111
|
applyBackground();
|
|
11016
12112
|
}).catch(() => {});
|
|
@@ -11019,6 +12115,9 @@ void main(){
|
|
|
11019
12115
|
panelStyleTag.remove();
|
|
11020
12116
|
immersiveStyleTag.remove();
|
|
11021
12117
|
orbBtn.remove();
|
|
12118
|
+
floaterReporter?.dispose();
|
|
12119
|
+
floaterReporter = null;
|
|
12120
|
+
store.actions.syncFloater = () => {};
|
|
11022
12121
|
destroyFocusLens();
|
|
11023
12122
|
statusObserver.disconnect();
|
|
11024
12123
|
document.removeEventListener("keydown", onImmersiveKey);
|