beauticode-dsh 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.zh-CN.md +48 -0
  2. package/agent.mjs +595 -0
  3. package/atmosphere.js +306 -0
  4. package/bin/beauticode-dsh +219 -0
  5. package/cli.js +219 -0
  6. package/client.js +370 -0
  7. package/console.js +303 -0
  8. package/control-client.mjs +445 -0
  9. package/cordis.patch.yml +4 -0
  10. package/host-apply.mjs +163 -0
  11. package/index.mjs +570 -0
  12. package/package.json +56 -0
  13. package/presets.mjs +73 -0
  14. package/themes/internal-beyond/NOTICE.md +9 -0
  15. package/themes/internal-beyond/bg-canvas-4k.png +0 -0
  16. package/themes/internal-beyond/bg-canvas.png +0 -0
  17. package/themes/internal-beyond/bg-infernal.jpg +0 -0
  18. package/themes/internal-beyond/bg-internal.jpg +0 -0
  19. package/ui-host.mjs +292 -0
  20. package/vendor/adapter-dsh/bridge.js +215 -0
  21. package/vendor/adapter-dsh/host-descriptor.js +15 -0
  22. package/vendor/adapter-dsh/index.js +6 -0
  23. package/vendor/adapter-dsh/injector-lock.js +27 -0
  24. package/vendor/adapter-dsh/package.json +5 -0
  25. package/vendor/adapter-dsh/session.js +527 -0
  26. package/vendor/adapter-dsh/token.js +46 -0
  27. package/vendor/adapter-dsh/tray-handoff.js +54 -0
  28. package/vendor/core/apply-transaction.js +287 -0
  29. package/vendor/core/background-store.js +1116 -0
  30. package/vendor/core/bundled-gallery.js +53 -0
  31. package/vendor/core/constants.js +47 -0
  32. package/vendor/core/error-message.js +208 -0
  33. package/vendor/core/file-lock.js +151 -0
  34. package/vendor/core/host-session.js +2 -0
  35. package/vendor/core/index.js +12 -0
  36. package/vendor/core/media-server.js +544 -0
  37. package/vendor/core/media-validation.js +244 -0
  38. package/vendor/core/package.json +5 -0
  39. package/vendor/core/paths.js +168 -0
  40. package/vendor/core/types.js +15 -0
package/console.js ADDED
@@ -0,0 +1,303 @@
1
+ (() => {
2
+ "use strict";
3
+ if (window.__beauticodeConsoleLoaded) return;
4
+ window.__beauticodeConsoleLoaded = true;
5
+
6
+ const IMAGE_ACCEPT = ".jpg,.jpeg,.png,.webp,.avif,image/jpeg,image/png,image/webp,image/avif";
7
+ const VIDEO_ACCEPT = ".mp4,video/mp4";
8
+
9
+ const style = document.createElement("style");
10
+ style.dataset.beauticodeConsole = "true";
11
+ style.textContent = `
12
+ #beauticode-console{display:contents}
13
+ #beauticode-console .bc-trigger{cursor:pointer;width:calc(100% + 8px);height:34px;margin:4px -4px;padding:6px 2px 6px 10px;border:none;border-radius:12px;background:transparent;color:inherit;font:inherit;font-size:14px;line-height:22px;align-items:center;gap:8px;display:flex;overflow:hidden}
14
+ #beauticode-console .bc-trigger:hover{background:var(--dsw-alias-interactive-bg-hover)}
15
+ #beauticode-console.rail .bc-trigger{width:36px;height:36px;margin:8px 0 10px;padding:0;border-radius:50%;justify-content:center;gap:0}
16
+ #beauticode-console.rail .bc-label{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
17
+ #beauticode-console .bc-icon{flex:none;display:block}
18
+ #beauticode-console .bc-label{white-space:nowrap;overflow:hidden}
19
+ #beauticode-console-pop{position:fixed;z-index:2000;box-sizing:border-box;width:220px;padding:10px;border:1px solid rgba(255,255,255,.08);border-radius:16px;background:#2c323c;color:#e8eaed;box-shadow:var(--dsw-shadow-lv2,0 8px 24px rgba(0,0,0,.28));font:inherit;font-size:13px}
20
+ #beauticode-console-pop *{box-sizing:border-box;font-family:inherit}
21
+ body:not([data-ds-dark-theme]) #beauticode-console-pop{background:#fff;color:#1b1f24;border-color:rgba(0,0,0,.08)}
22
+ #beauticode-console-pop .bc-status{color:var(--dsw-alias-label-secondary,#9aa3ad);font-size:12px;line-height:18px;margin:0 0 8px}
23
+ #beauticode-console-pop .bc-row{display:flex;gap:6px;margin:0 0 6px}
24
+ #beauticode-console-pop .bc-btn,#beauticode-console-pop .bc-theme-toggle{cursor:pointer;flex:1;min-width:0;height:32px;padding:0 8px;border:1px solid rgba(255,255,255,.1);border-radius:12px;background:rgba(255,255,255,.06);color:inherit;font-size:13px;line-height:20px}
25
+ body:not([data-ds-dark-theme]) #beauticode-console-pop .bc-btn,
26
+ body:not([data-ds-dark-theme]) #beauticode-console-pop .bc-theme-toggle{border-color:rgba(0,0,0,.08);background:#f4f6f8}
27
+ #beauticode-console-pop .bc-btn:hover,#beauticode-console-pop .bc-theme-toggle:hover{background:rgba(255,255,255,.12)}
28
+ body:not([data-ds-dark-theme]) #beauticode-console-pop .bc-btn:hover,
29
+ body:not([data-ds-dark-theme]) #beauticode-console-pop .bc-theme-toggle:hover{background:#eceff3}
30
+ #beauticode-console-pop .bc-btn:disabled,#beauticode-console-pop .bc-theme-toggle:disabled,#beauticode-console-pop .bc-theme-item:disabled{opacity:.45;cursor:default}
31
+ #beauticode-console-pop .bc-btn.on{background:rgba(255,255,255,.16)}
32
+ #beauticode-console-pop .bc-theme-toggle{width:100%;text-align:left}
33
+ #beauticode-console-pop .bc-theme-list{margin:6px 0 0;max-height:160px;overflow:auto;border:1px solid rgba(255,255,255,.08);border-radius:12px;background:#232830;padding:4px}
34
+ body:not([data-ds-dark-theme]) #beauticode-console-pop .bc-theme-list{border-color:rgba(0,0,0,.08);background:#f4f6f8}
35
+ #beauticode-console-pop .bc-theme-item{cursor:pointer;display:block;width:100%;height:32px;padding:0 8px;border:none;border-radius:8px;background:transparent;color:inherit;font-size:13px;line-height:32px;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
36
+ #beauticode-console-pop .bc-theme-item:hover{background:rgba(255,255,255,.08)}
37
+ body:not([data-ds-dark-theme]) #beauticode-console-pop .bc-theme-item:hover{background:rgba(0,0,0,.06)}
38
+ #beauticode-console-pop .bc-msg{color:var(--dsw-alias-label-secondary,#9aa3ad);font-size:12px;line-height:16px;margin:6px 0 0;max-height:3.2em;overflow:hidden}
39
+ #beauticode-console-file{display:none !important}
40
+ `;
41
+ document.head.append(style);
42
+
43
+ const host = document.createElement("div");
44
+ host.id = "beauticode-console";
45
+ host.innerHTML =
46
+ '<button type="button" class="bc-trigger" aria-expanded="false" aria-controls="beauticode-console-pop">' +
47
+ '<svg class="bc-icon" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">' +
48
+ '<rect x="1.75" y="3.25" width="12.5" height="9.5" rx="2" stroke="currentColor" stroke-width="1.25"/>' +
49
+ '<path d="M2.5 11.25 5.6 8.2a1 1 0 0 1 1.35 0L9.2 10.4l1.05-.95a1 1 0 0 1 1.3.04L13.5 11.3" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>' +
50
+ '<circle cx="5.25" cy="6.25" r="1" fill="currentColor"/>' +
51
+ "</svg>" +
52
+ '<span class="bc-label">背景</span></button>';
53
+
54
+ const pop = document.createElement("div");
55
+ pop.id = "beauticode-console-pop";
56
+ pop.hidden = true;
57
+ pop.innerHTML =
58
+ '<p class="bc-status">未就绪</p>' +
59
+ '<div class="bc-row">' +
60
+ '<button type="button" class="bc-btn" data-act="image">图片</button>' +
61
+ '<button type="button" class="bc-btn" data-act="video">视频</button>' +
62
+ "</div>" +
63
+ '<div class="bc-row">' +
64
+ '<button type="button" class="bc-btn" data-act="clear">清除</button>' +
65
+ '<button type="button" class="bc-btn" data-act="sound">声音</button>' +
66
+ "</div>" +
67
+ '<div class="bc-themes" hidden>' +
68
+ '<button type="button" class="bc-theme-toggle" aria-expanded="false">已保存主题</button>' +
69
+ '<div class="bc-theme-list" hidden></div>' +
70
+ "</div>" +
71
+ '<p class="bc-msg" hidden></p>';
72
+
73
+ const fileInput = document.createElement("input");
74
+ fileInput.id = "beauticode-console-file";
75
+ fileInput.type = "file";
76
+
77
+ document.body.append(pop, fileInput);
78
+
79
+ const trigger = host.querySelector(".bc-trigger");
80
+ const statusEl = pop.querySelector(".bc-status");
81
+ const soundBtn = pop.querySelector('[data-act="sound"]');
82
+ const themesBox = pop.querySelector(".bc-themes");
83
+ const themeToggle = pop.querySelector(".bc-theme-toggle");
84
+ const themeList = pop.querySelector(".bc-theme-list");
85
+ const msgEl = pop.querySelector(".bc-msg");
86
+ let busy = false;
87
+ let muted = true;
88
+ let currentThemeId = "";
89
+
90
+ function findSettingsTrigger() {
91
+ const buttons = [...document.querySelectorAll('button[aria-haspopup="dialog"]')];
92
+ const candidates = buttons.filter((button) => {
93
+ if (host.contains(button) || pop.contains(button)) return false;
94
+ const rect = button.getBoundingClientRect();
95
+ return rect.width > 0 && rect.height > 0 && rect.left < 320 && rect.bottom > window.innerHeight * 0.35;
96
+ });
97
+ candidates.sort((a, b) => b.getBoundingClientRect().bottom - a.getBoundingClientRect().bottom);
98
+ return candidates[0] || null;
99
+ }
100
+
101
+ function placePop() {
102
+ const rect = trigger.getBoundingClientRect();
103
+ if (!rect.width) return;
104
+ pop.style.left = `${Math.round(rect.left)}px`;
105
+ pop.style.bottom = `${Math.round(window.innerHeight - rect.top + 8)}px`;
106
+ }
107
+
108
+ function place() {
109
+ const settings = findSettingsTrigger();
110
+ if (!settings || !settings.parentElement) {
111
+ if (host.parentElement) host.remove();
112
+ return;
113
+ }
114
+ if (host.parentElement !== settings.parentElement || host.nextElementSibling !== settings) {
115
+ settings.parentElement.insertBefore(host, settings);
116
+ }
117
+ host.classList.toggle("rail", settings.getBoundingClientRect().width <= 40);
118
+ if (!pop.hidden) placePop();
119
+ }
120
+
121
+ function setOpen(open) {
122
+ pop.hidden = !open;
123
+ trigger.setAttribute("aria-expanded", open ? "true" : "false");
124
+ if (open) {
125
+ placePop();
126
+ void refresh();
127
+ }
128
+ }
129
+
130
+ function showMessage(text) {
131
+ if (!text) {
132
+ msgEl.hidden = true;
133
+ msgEl.textContent = "";
134
+ return;
135
+ }
136
+ msgEl.hidden = false;
137
+ msgEl.textContent = text;
138
+ }
139
+
140
+ function renderStatus(data) {
141
+ if (!data?.ok) {
142
+ statusEl.textContent = data?.error || "未就绪";
143
+ return;
144
+ }
145
+ const label =
146
+ data.atmosphere === "gallery"
147
+ ? "画窗"
148
+ : data.media === "video"
149
+ ? "视频"
150
+ : data.media === "image"
151
+ ? "图片"
152
+ : "无背景";
153
+ statusEl.textContent = label;
154
+ if (data.atmosphere === "gallery") currentThemeId = "builtin-gallery";
155
+ muted = data.muted !== false;
156
+ soundBtn.classList.toggle("on", !muted);
157
+ soundBtn.textContent = muted ? "声音" : "声音开";
158
+ const themes = Array.isArray(data.themes) ? data.themes : [];
159
+ if (themes.length === 0) {
160
+ themesBox.hidden = true;
161
+ themeList.innerHTML = "";
162
+ themeList.hidden = true;
163
+ themeToggle.setAttribute("aria-expanded", "false");
164
+ return;
165
+ }
166
+ themesBox.hidden = false;
167
+ themeList.innerHTML = themes
168
+ .map(
169
+ (theme) =>
170
+ `<button type="button" class="bc-theme-item" data-theme-id="${escapeAttr(theme.id)}">${escapeText(theme.name)}</button>`,
171
+ )
172
+ .join("");
173
+ const selected = themes.find((theme) => theme.id === currentThemeId);
174
+ themeToggle.textContent = selected ? selected.name : "已保存主题";
175
+ }
176
+
177
+ function escapeText(value) {
178
+ return String(value)
179
+ .replaceAll("&", "&amp;")
180
+ .replaceAll("<", "&lt;")
181
+ .replaceAll(">", "&gt;");
182
+ }
183
+
184
+ function escapeAttr(value) {
185
+ return escapeText(value).replaceAll('"', "&quot;");
186
+ }
187
+
188
+ async function request(path, init) {
189
+ const response = await fetch(path, {
190
+ ...init,
191
+ headers: {
192
+ ...(init?.headers || {}),
193
+ },
194
+ });
195
+ const body = await response.json().catch(() => null);
196
+ if (!response.ok || body?.ok === false) {
197
+ throw new Error(body?.error || `请求失败(${response.status})`);
198
+ }
199
+ return body;
200
+ }
201
+
202
+ async function refresh() {
203
+ try {
204
+ renderStatus(await request("/__beauticode/ui/status"));
205
+ } catch (error) {
206
+ renderStatus({ ok: false, error: error instanceof Error ? error.message : String(error) });
207
+ }
208
+ }
209
+
210
+ async function run(task) {
211
+ if (busy) return;
212
+ busy = true;
213
+ for (const button of pop.querySelectorAll(".bc-btn, .bc-theme-toggle, .bc-theme-item")) button.disabled = true;
214
+ showMessage("");
215
+ try {
216
+ const result = await task();
217
+ if (result?.message) showMessage(result.message);
218
+ await refresh();
219
+ } catch (error) {
220
+ showMessage(error instanceof Error ? error.message : String(error));
221
+ } finally {
222
+ busy = false;
223
+ for (const button of pop.querySelectorAll(".bc-btn, .bc-theme-toggle, .bc-theme-item")) button.disabled = false;
224
+ }
225
+ }
226
+
227
+ trigger.addEventListener("click", (event) => {
228
+ event.stopPropagation();
229
+ setOpen(pop.hidden);
230
+ });
231
+ pop.querySelector('[data-act="image"]').addEventListener("click", () => {
232
+ fileInput.accept = IMAGE_ACCEPT;
233
+ fileInput.click();
234
+ });
235
+ pop.querySelector('[data-act="video"]').addEventListener("click", () => {
236
+ fileInput.accept = VIDEO_ACCEPT;
237
+ fileInput.click();
238
+ });
239
+ pop.querySelector('[data-act="clear"]').addEventListener("click", () => {
240
+ currentThemeId = "";
241
+ void run(() => request("/__beauticode/ui/clear", { method: "POST" }));
242
+ });
243
+ soundBtn.addEventListener("click", () => {
244
+ void run(() =>
245
+ request("/__beauticode/ui/mode", {
246
+ method: "POST",
247
+ headers: { "content-type": "application/json" },
248
+ body: JSON.stringify({ muted: !muted }),
249
+ }),
250
+ );
251
+ });
252
+ themeToggle.addEventListener("click", (event) => {
253
+ event.stopPropagation();
254
+ const open = themeList.hidden;
255
+ themeList.hidden = !open;
256
+ themeToggle.setAttribute("aria-expanded", open ? "true" : "false");
257
+ });
258
+ themeList.addEventListener("click", (event) => {
259
+ const item = event.target.closest("[data-theme-id]");
260
+ if (!item) return;
261
+ currentThemeId = item.getAttribute("data-theme-id") || "";
262
+ themeList.hidden = true;
263
+ themeToggle.setAttribute("aria-expanded", "false");
264
+ globalThis.BeauticodeAtmosphere?.setWindowMode?.(
265
+ currentThemeId === "builtin-gallery" ? "on" : "closed",
266
+ );
267
+ void run(() =>
268
+ request("/__beauticode/ui/theme/use", {
269
+ method: "POST",
270
+ headers: { "content-type": "application/json" },
271
+ body: JSON.stringify({ id: currentThemeId }),
272
+ }),
273
+ );
274
+ });
275
+ fileInput.addEventListener("change", () => {
276
+ const file = fileInput.files?.[0];
277
+ fileInput.value = "";
278
+ if (!file) return;
279
+ currentThemeId = "";
280
+ void run(() =>
281
+ request("/__beauticode/ui/import", {
282
+ method: "POST",
283
+ headers: { "x-beauticode-filename": encodeURIComponent(file.name) },
284
+ body: file,
285
+ }),
286
+ );
287
+ });
288
+
289
+ document.addEventListener("click", (event) => {
290
+ if (pop.hidden) return;
291
+ if (pop.contains(event.target) || trigger.contains(event.target)) return;
292
+ setOpen(false);
293
+ });
294
+ document.addEventListener("keydown", (event) => {
295
+ if (event.key === "Escape" && !pop.hidden) setOpen(false);
296
+ });
297
+
298
+ const observer = new MutationObserver(() => place());
299
+ observer.observe(document.documentElement, { childList: true, subtree: true });
300
+ window.addEventListener("resize", place);
301
+ setInterval(place, 500);
302
+ place();
303
+ })();