dsh-composer-ux 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js ADDED
@@ -0,0 +1,1626 @@
1
+ window.__ModuleLoader__.load({ id: "dsh-composer-ux", factory: (require) => {
2
+ var module = { exports: {} }; var exports = module.exports;
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name2 in all)
11
+ __defProp(target, name2, { get: all[name2], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // src/client.tsx
32
+ var client_exports = {};
33
+ __export(client_exports, {
34
+ apply: () => apply,
35
+ inject: () => inject,
36
+ name: () => name
37
+ });
38
+ module.exports = __toCommonJS(client_exports);
39
+ var import_dsh_client_store = require("@deepseek-ai/dsh-client-store");
40
+
41
+ // src/settings-contract.ts
42
+ var NAMESPACE = "composer-ux";
43
+ var ENABLED_FIELD = "enabled";
44
+ var SEND_KEY_FIELD = "sendKey";
45
+ var NEWLINE_KEY_FIELD = "newlineKey";
46
+ var PANEL_SCROLL_FIELD = "panelScroll";
47
+ var PANEL_RESIZE_FIELD = "panelResize";
48
+ var PANEL_WIDTH_FIELD = "panelWidth";
49
+ var PANEL_HEIGHT_FIELD = "panelHeight";
50
+ var MENU_FIELDS = [
51
+ "menuUndo",
52
+ "menuRedo",
53
+ "menuCut",
54
+ "menuCopy",
55
+ "menuPaste",
56
+ "menuDelete",
57
+ "menuSelectAll"
58
+ ];
59
+ var MENU_NATIVE_FIELD = "menuNative";
60
+ var HEADER_ENABLED_FIELD = "headerEnabled";
61
+ var HEADER_NAME_FIELD = "headerName";
62
+ var HEADER_VALUE_FIELD = "headerValue";
63
+ var HEADER_ROUTES_FIELD = "headerRoutes";
64
+ var HEADER_APPLIED_NAME_FIELD = "headerAppliedName";
65
+ var HEADER_APPLIED_VALUE_FIELD = "headerAppliedValue";
66
+ var HEADER_STATUS_FIELD = "headerStatus";
67
+ var DEFAULT_HEADER_NAME = "x-opencode-session";
68
+ var HEADER_VALUE_MAX = 200;
69
+ var HEADER_NAME_MAX = 64;
70
+ var DEFAULT_SETTINGS = {
71
+ enabled: true,
72
+ sendKey: "Enter",
73
+ newlineKey: "Shift+Enter",
74
+ menuUndo: true,
75
+ menuRedo: true,
76
+ menuCut: true,
77
+ menuCopy: true,
78
+ menuPaste: true,
79
+ menuDelete: true,
80
+ menuSelectAll: true,
81
+ menuNative: false,
82
+ panelScroll: true,
83
+ panelResize: true,
84
+ headerEnabled: false,
85
+ headerName: DEFAULT_HEADER_NAME,
86
+ headerValue: "",
87
+ headerRoutes: "",
88
+ headerAppliedName: "",
89
+ headerAppliedValue: "",
90
+ headerStatus: ""
91
+ };
92
+ var SEND_PRESETS = ["Enter", "Ctrl+Enter", "Alt+Enter", "Shift+Enter"];
93
+ var NEWLINE_PRESETS = ["Shift+Enter", "Enter", "Ctrl+Enter", "Alt+Enter"];
94
+ var MENU_ITEMS = [
95
+ { field: "menuUndo", label: "\u64A4\u9500", shortcut: "Ctrl+Z" },
96
+ { field: "menuRedo", label: "\u91CD\u505A", shortcut: "Ctrl+Y" },
97
+ { field: "menuCut", label: "\u526A\u5207", shortcut: "Ctrl+X" },
98
+ { field: "menuCopy", label: "\u590D\u5236", shortcut: "Ctrl+C" },
99
+ { field: "menuPaste", label: "\u7C98\u8D34", shortcut: "Ctrl+V" },
100
+ { field: "menuDelete", label: "\u5220\u9664", shortcut: "" },
101
+ { field: "menuSelectAll", label: "\u5168\u9009", shortcut: "Ctrl+A" }
102
+ ];
103
+ function newSessionId() {
104
+ const bag = globalThis;
105
+ if (typeof bag.crypto?.randomUUID === "function") return bag.crypto.randomUUID();
106
+ const hex = (count) => Array.from(
107
+ { length: count },
108
+ () => Math.floor(Math.random() * 16).toString(16)
109
+ ).join("");
110
+ return `${hex(8)}-${hex(4)}-4${hex(3)}-a${hex(3)}-${hex(12)}`;
111
+ }
112
+ function sanitizeSettings(value) {
113
+ const source = typeof value === "object" && value !== null ? value : {};
114
+ const asString = (field) => {
115
+ const v = source[field];
116
+ return typeof v === "string" && v.length > 0 && v.length <= 64 ? v : "";
117
+ };
118
+ const asText = (field, max) => {
119
+ const v = source[field];
120
+ return typeof v === "string" && v.length <= max ? v : "";
121
+ };
122
+ const asBool = (field) => {
123
+ const v = source[field];
124
+ return typeof v === "boolean" ? v : DEFAULT_SETTINGS[field];
125
+ };
126
+ const asSize = (field) => {
127
+ const v = source[field];
128
+ return typeof v === "number" && Number.isFinite(v) && v >= 320 && v <= 4e3 ? Math.round(v) : void 0;
129
+ };
130
+ return {
131
+ enabled: asBool(ENABLED_FIELD),
132
+ sendKey: asString(SEND_KEY_FIELD),
133
+ newlineKey: asString(NEWLINE_KEY_FIELD),
134
+ menuUndo: asBool("menuUndo"),
135
+ menuRedo: asBool("menuRedo"),
136
+ menuCut: asBool("menuCut"),
137
+ menuCopy: asBool("menuCopy"),
138
+ menuPaste: asBool("menuPaste"),
139
+ menuDelete: asBool("menuDelete"),
140
+ menuSelectAll: asBool("menuSelectAll"),
141
+ menuNative: asBool(MENU_NATIVE_FIELD),
142
+ panelScroll: asBool(PANEL_SCROLL_FIELD),
143
+ panelResize: asBool(PANEL_RESIZE_FIELD),
144
+ panelWidth: asSize(PANEL_WIDTH_FIELD),
145
+ panelHeight: asSize(PANEL_HEIGHT_FIELD),
146
+ headerEnabled: asBool(HEADER_ENABLED_FIELD),
147
+ // 头名留空是不合法状态(宿主半会拒绝写入并把原因写进 headerStatus),
148
+ // 故净化只做长度收窄、保留原样,让用户看得见自己输错了什么。
149
+ headerName: asText(HEADER_NAME_FIELD, HEADER_NAME_MAX),
150
+ headerValue: asText(HEADER_VALUE_FIELD, HEADER_VALUE_MAX),
151
+ headerRoutes: asText(HEADER_ROUTES_FIELD, HEADER_VALUE_MAX),
152
+ headerAppliedName: asText(HEADER_APPLIED_NAME_FIELD, HEADER_NAME_MAX),
153
+ headerAppliedValue: asText(HEADER_APPLIED_VALUE_FIELD, HEADER_VALUE_MAX),
154
+ headerStatus: asText(HEADER_STATUS_FIELD, HEADER_VALUE_MAX)
155
+ };
156
+ }
157
+
158
+ // src/client/chords.ts
159
+ var MODIFIER_KEYS = /* @__PURE__ */ new Set(["Control", "Alt", "Shift", "Meta", "AltGraph"]);
160
+ var CODE_LABELS = {
161
+ Slash: "/",
162
+ Backslash: "\\",
163
+ BracketLeft: "[",
164
+ BracketRight: "]",
165
+ Semicolon: ";",
166
+ Quote: "'",
167
+ Comma: ",",
168
+ Period: ".",
169
+ Backquote: "`",
170
+ Minus: "-",
171
+ Equal: "=",
172
+ Space: "Space"
173
+ };
174
+ function encodeChord(event) {
175
+ const parts = [];
176
+ if (event.ctrlKey) parts.push("Ctrl");
177
+ if (event.altKey) parts.push("Alt");
178
+ if (event.shiftKey) parts.push("Shift");
179
+ if (event.metaKey) parts.push("Meta");
180
+ const mainKey = normalizeMainKey(event);
181
+ if (mainKey === "") return "";
182
+ parts.push(mainKey);
183
+ return parts.join("+");
184
+ }
185
+ function normalizeMainKey(event) {
186
+ const code = event.code;
187
+ if (code.startsWith("Key")) return code.slice(3);
188
+ if (code.startsWith("Digit")) return code.slice(5);
189
+ if (CODE_LABELS[code] !== void 0) return CODE_LABELS[code];
190
+ return code !== "" ? code : event.key;
191
+ }
192
+ function isModifierOnly(event) {
193
+ return MODIFIER_KEYS.has(event.key);
194
+ }
195
+ var BARE_ALLOWED = /* @__PURE__ */ new Set([
196
+ "Enter",
197
+ "Space",
198
+ "Tab",
199
+ "Backspace",
200
+ "Delete",
201
+ "Insert",
202
+ "Home",
203
+ "End",
204
+ "PageUp",
205
+ "PageDown",
206
+ "ArrowUp",
207
+ "ArrowDown",
208
+ "ArrowLeft",
209
+ "ArrowRight"
210
+ ]);
211
+ function isBareKeyAllowed(chord) {
212
+ if (chord.includes("+")) return true;
213
+ if (BARE_ALLOWED.has(chord)) return true;
214
+ return /^F\d{1,2}$/.test(chord);
215
+ }
216
+ function chordToInit(chord) {
217
+ const parts = chord.split("+").filter(Boolean);
218
+ const init = {
219
+ ctrlKey: false,
220
+ altKey: false,
221
+ shiftKey: false,
222
+ metaKey: false,
223
+ bubbles: true,
224
+ cancelable: true
225
+ };
226
+ const key = parts.pop() ?? "";
227
+ init.key = key;
228
+ init.code = key.length === 1 ? /^[A-Z]$/.test(key) ? `Key${key}` : key : key;
229
+ for (const part of parts) {
230
+ if (part === "Ctrl") init.ctrlKey = true;
231
+ else if (part === "Alt") init.altKey = true;
232
+ else if (part === "Shift") init.shiftKey = true;
233
+ else if (part === "Meta") init.metaKey = true;
234
+ }
235
+ return init;
236
+ }
237
+ function isEnterFamily(chord) {
238
+ return chord === "Enter" || chord.endsWith("+Enter");
239
+ }
240
+ function evaluateRecordedKey(event) {
241
+ if (isModifierOnly(event)) return { kind: "ignore" };
242
+ if (event.code === "Escape" || event.key === "Escape") return { kind: "cancel" };
243
+ if (event.code === "Backspace" || event.code === "Delete") return { kind: "clear" };
244
+ const chord = encodeChord(event);
245
+ if (chord === "") return { kind: "ignore" };
246
+ if (!isBareKeyAllowed(chord)) {
247
+ return {
248
+ kind: "reject",
249
+ message: "\u5355\u4E2A\u5B57\u6BCD/\u6570\u5B57\u952E\u4F1A\u4E0E\u6253\u5B57\u51B2\u7A81\uFF0C\u8BF7\u914D\u5408 Ctrl / Alt / Shift \u4F7F\u7528\uFF0C\u6216\u9009\u62E9 Enter\u3002"
250
+ };
251
+ }
252
+ return { kind: "ok", chord };
253
+ }
254
+ function displayChord(chord) {
255
+ return chord === "" ? "\u65E0" : chord;
256
+ }
257
+
258
+ // src/client/interceptors.ts
259
+ var INPUT_SELECTOR = "[data-composer-input]";
260
+ var OVERLAY_SELECTOR = "[data-trigger-menu], [data-conversation-composer-overlay]";
261
+ function findComposerRoot(target) {
262
+ if (!(target instanceof Element)) return null;
263
+ return target.closest(INPUT_SELECTOR);
264
+ }
265
+ function overlayOpen() {
266
+ return document.querySelector(OVERLAY_SELECTOR) !== null;
267
+ }
268
+ var replaying = false;
269
+ var pasteCache = null;
270
+ function prefetchClipboard() {
271
+ pasteCache = null;
272
+ const readText = navigator.clipboard?.readText;
273
+ if (typeof readText !== "function") return;
274
+ readText.call(navigator.clipboard).then(
275
+ (text) => {
276
+ pasteCache = text;
277
+ },
278
+ () => {
279
+ pasteCache = null;
280
+ }
281
+ );
282
+ }
283
+ function dispatchKey(root, init) {
284
+ replaying = true;
285
+ try {
286
+ root.dispatchEvent(new KeyboardEvent("keydown", init));
287
+ } finally {
288
+ replaying = false;
289
+ }
290
+ }
291
+ function composing(event) {
292
+ return event.isComposing || event.keyCode === 229;
293
+ }
294
+ function installInterceptors(deps) {
295
+ const onKeyDown = (event) => {
296
+ if (replaying) return;
297
+ const root = findComposerRoot(event.target);
298
+ if (root === null) return;
299
+ if (composing(event)) return;
300
+ const chord = encodeChord(event);
301
+ if (!isEnterFamily(chord)) return;
302
+ if (overlayOpen()) return;
303
+ const settings = deps.settings();
304
+ const gesture = chord === settings.sendKey ? "send" : chord === settings.newlineKey ? "newline" : chord === "Ctrl+Enter" || chord === "Meta+Enter" ? "accelerated" : "none";
305
+ event.preventDefault();
306
+ event.stopPropagation();
307
+ if (event.repeat) return;
308
+ if (gesture === "send") {
309
+ dispatchKey(root, chordToInit("Enter"));
310
+ } else if (gesture === "newline") {
311
+ dispatchKey(root, chordToInit("Shift+Enter"));
312
+ } else if (gesture === "accelerated") {
313
+ dispatchKey(root, chordToInit("Ctrl+Enter"));
314
+ }
315
+ };
316
+ const onContextMenu = (event) => {
317
+ const root = findComposerRoot(event.target);
318
+ if (root === null) return;
319
+ if (deps.settings().menuNative) {
320
+ if (deps.menuOpen()) deps.setMenu(null);
321
+ event.stopImmediatePropagation();
322
+ event.stopPropagation();
323
+ return;
324
+ }
325
+ event.preventDefault();
326
+ event.stopPropagation();
327
+ const selection = window.getSelection();
328
+ const hasSelection = selection !== null && !selection.isCollapsed && selection.anchorNode !== null && root.contains(selection.anchorNode);
329
+ deps.setMenu({ x: event.clientX, y: event.clientY, hasSelection });
330
+ prefetchClipboard();
331
+ };
332
+ window.addEventListener("keydown", onKeyDown, true);
333
+ window.addEventListener("contextmenu", onContextMenu, true);
334
+ return () => {
335
+ window.removeEventListener("keydown", onKeyDown, true);
336
+ window.removeEventListener("contextmenu", onContextMenu, true);
337
+ };
338
+ }
339
+ async function runMenuAction(id) {
340
+ const root = document.querySelector(INPUT_SELECTOR);
341
+ if (root === null) return { ok: false, message: "\u627E\u4E0D\u5230\u8F93\u5165\u6846" };
342
+ if (!document.activeElement || !root.contains(document.activeElement)) {
343
+ if (root.isConnected) {
344
+ root.focus({ preventScroll: true });
345
+ }
346
+ }
347
+ switch (id) {
348
+ case "undo":
349
+ dispatchKey(root, chordToInit("Ctrl+Z"));
350
+ return { ok: true };
351
+ case "redo":
352
+ dispatchKey(root, chordToInit("Ctrl+Y"));
353
+ return { ok: true };
354
+ case "copy":
355
+ return { ok: document.execCommand("copy") };
356
+ case "cut":
357
+ return { ok: document.execCommand("cut") };
358
+ case "delete":
359
+ return { ok: document.execCommand("delete") };
360
+ case "selectAll":
361
+ return { ok: document.execCommand("selectAll") };
362
+ case "paste":
363
+ return pasteText(root);
364
+ }
365
+ }
366
+ function withTimeout(promise, ms) {
367
+ return new Promise((resolve, reject) => {
368
+ const timer = setTimeout(() => {
369
+ reject(new Error("clipboard-read timeout"));
370
+ }, ms);
371
+ promise.then(
372
+ (value) => {
373
+ clearTimeout(timer);
374
+ resolve(value);
375
+ },
376
+ (error) => {
377
+ clearTimeout(timer);
378
+ reject(error);
379
+ }
380
+ );
381
+ });
382
+ }
383
+ function isClipboardTimeout(error) {
384
+ return error instanceof Error && error.message === "clipboard-read timeout";
385
+ }
386
+ function isFirefox() {
387
+ return typeof navigator !== "undefined" && /Firefox\//.test(navigator.userAgent);
388
+ }
389
+ var FIREFOX_TIP = "Firefox \u6BCF\u6B21\u8BFB\u526A\u8D34\u677F\u90FD\u8981\u70B9\u300C\u7C98\u8D34(P)\u300D\u786E\u8BA4\uFF1A\u5730\u5740\u680F\u8F93\u5165 about:config \u2192 \u63A5\u53D7\u98CE\u9669\u7EE7\u7EED \u2192 \u53F3\u952E\u65B0\u5EFA\u300C\u6574\u6570\u300D\u2192 \u540D\u79F0\u586B permissions.default.clipboard-read \u2192 \u503C\u586B 1 \u2192 \u91CD\u8FDB DSH\uFF1B\u4E4B\u540E\u70B9\u4E00\u6B21\u7C98\u8D34\u5373\u6210\u529F\uFF08\u6216\u76F4\u63A5 Ctrl+V\uFF09";
390
+ async function pasteText(root) {
391
+ const insertText = (text2) => {
392
+ if (!document.activeElement || !root.contains(document.activeElement)) {
393
+ root.focus({ preventScroll: true });
394
+ }
395
+ if (text2 === "") return { ok: true };
396
+ return { ok: document.execCommand("insertText", false, text2) };
397
+ };
398
+ if (pasteCache !== null && pasteCache !== "") {
399
+ const text2 = pasteCache;
400
+ pasteCache = null;
401
+ return insertText(text2);
402
+ }
403
+ if (typeof navigator === "undefined" || navigator.clipboard === void 0 || typeof navigator.clipboard.readText !== "function") {
404
+ return { ok: false, message: "\u5F53\u524D\u6D4F\u89C8\u5668\u4E0D\u652F\u6301\u8BFB\u53D6\u526A\u8D34\u677F\uFF0C\u8BF7\u7528 Ctrl+V \u7C98\u8D34" };
405
+ }
406
+ const permissions = navigator.permissions;
407
+ if (permissions?.query) {
408
+ try {
409
+ const result = await permissions.query({ name: "clipboard-read" });
410
+ if (result.state === "denied") {
411
+ if (isFirefox()) return { ok: false, message: FIREFOX_TIP };
412
+ return {
413
+ ok: false,
414
+ message: "\u6D4F\u89C8\u5668\u5DF2\u62D2\u7EDD\u526A\u8D34\u677F\u8BFB\u53D6\uFF1A\u70B9\u51FB\u5730\u5740\u680F\u5DE6\u4FA7\u56FE\u6807 \u2192 \u7AD9\u70B9\u8BBE\u7F6E \u2192 \u526A\u8D34\u677F \u2192 \u5141\u8BB8\uFF1B\u6216\u76F4\u63A5\u6309 Ctrl+V"
415
+ };
416
+ }
417
+ } catch {
418
+ }
419
+ }
420
+ let text;
421
+ try {
422
+ text = await withTimeout(navigator.clipboard.readText(), 15e3);
423
+ } catch (error) {
424
+ if (isFirefox()) {
425
+ return { ok: false, message: FIREFOX_TIP };
426
+ }
427
+ if (isClipboardTimeout(error)) {
428
+ return {
429
+ ok: false,
430
+ message: "\u7B49\u5F85\u6D4F\u89C8\u5668\u6388\u6743\u8D85\u65F6\uFF1A\u8BF7\u5728\u5F39\u51FA\u63D0\u793A\u4E2D\u9009\u62E9\u300C\u5141\u8BB8\u300D\uFF08\u82E5\u672A\u51FA\u73B0\uFF0C\u70B9\u5730\u5740\u680F\u5DE6\u4FA7\u56FE\u6807\u5F00\u542F\uFF09\uFF0C\u6216\u6309 Ctrl+V"
431
+ };
432
+ }
433
+ return {
434
+ ok: false,
435
+ message: "\u672A\u83B7\u526A\u8D34\u677F\u8BFB\u53D6\u6388\u6743\uFF1A\u70B9\u51FB\u5730\u5740\u680F\u5DE6\u4FA7\u56FE\u6807 \u2192 \u7AD9\u70B9\u8BBE\u7F6E \u2192 \u526A\u8D34\u677F \u2192 \u5141\u8BB8\uFF1B\u6216\u76F4\u63A5\u6309 Ctrl+V \u7C98\u8D34"
436
+ };
437
+ }
438
+ if (!document.activeElement || !root.contains(document.activeElement)) {
439
+ root.focus({ preventScroll: true });
440
+ }
441
+ if (text === "") return { ok: true };
442
+ return { ok: document.execCommand("insertText", false, text) };
443
+ }
444
+
445
+ // src/client/panel.ts
446
+ var PANEL_SELECTOR = '[role="dialog"][aria-modal="true"]:has(> nav)';
447
+ var PANEL_CSS = `
448
+ html.dsh-ux-panel-scroll [role="dialog"][aria-modal="true"]:has(> nav) { min-height: 320px; }
449
+ html.dsh-ux-panel-scroll [role="dialog"][aria-modal="true"]:has(> nav) > nav {
450
+ min-height: 0;
451
+ }
452
+ html.dsh-ux-panel-scroll [role="dialog"][aria-modal="true"]:has(> nav) > nav > div:last-child {
453
+ flex: 1 1 auto;
454
+ min-height: 0;
455
+ overflow-y: auto;
456
+ overscroll-behavior: contain;
457
+ scrollbar-width: thin;
458
+ }
459
+ html.dsh-ux-panel-scroll [role="dialog"][aria-modal="true"]:has(> nav) > nav > div:last-child::-webkit-scrollbar {
460
+ width: 8px;
461
+ }
462
+ html.dsh-ux-panel-scroll [role="dialog"][aria-modal="true"]:has(> nav) > nav > div:last-child::-webkit-scrollbar-thumb {
463
+ background: var(--dsh-scrollbar-thumb, var(--dsw-alias-scrollbar-bg-l2));
464
+ border-radius: 8px;
465
+ }
466
+ html.dsh-ux-panel-scroll [role="dialog"][aria-modal="true"]:has(> nav) > nav > div:last-child::-webkit-scrollbar-thumb:hover {
467
+ background: var(--dsh-scrollbar-thumb-hover, var(--dsw-alias-scrollbar-hover-l2));
468
+ }
469
+ `;
470
+ function installPanelStyle(settings, subscribe) {
471
+ const tag = document.createElement("style");
472
+ tag.dataset.plugin = "dsh-composer-ux-panel";
473
+ tag.textContent = PANEL_CSS;
474
+ document.head.appendChild(tag);
475
+ const applyClass = () => {
476
+ document.documentElement.classList.toggle("dsh-ux-panel-scroll", settings());
477
+ };
478
+ applyClass();
479
+ const unsubscribe = subscribe(applyClass);
480
+ return () => {
481
+ unsubscribe();
482
+ tag.remove();
483
+ document.documentElement.classList.remove("dsh-ux-panel-scroll");
484
+ };
485
+ }
486
+ function findSettingsPanel() {
487
+ return document.querySelector(PANEL_SELECTOR);
488
+ }
489
+ function clampPanelWidth(width) {
490
+ return Math.max(560, Math.min(width, Math.max(800, window.innerWidth - 48)));
491
+ }
492
+ function clampPanelHeight(height) {
493
+ return Math.max(320, Math.min(height, Math.max(400, window.innerHeight - 48)));
494
+ }
495
+
496
+ // src/client/settings-style.ts
497
+ var CARD_CSS = `
498
+ .dsh-ux-card {
499
+ border: 1px solid var(--dsw-alias-border-l2);
500
+ background: var(--dsw-alias-bg-layer-3);
501
+ border-radius: 12px;
502
+ margin-bottom: 12px;
503
+ transition: border-color .16s, background .16s;
504
+ }
505
+ .dsh-ux-card:hover { border-color: var(--dsw-alias-label-dimmed); }
506
+ .dsh-ux-cardOpen {
507
+ background: var(--dsw-alias-bg-layer-2);
508
+ border-color: var(--dsw-alias-label-dimmed);
509
+ }
510
+ .dsh-ux-cardHeader {
511
+ appearance: none;
512
+ box-sizing: border-box;
513
+ width: 100%;
514
+ font: inherit;
515
+ color: inherit;
516
+ text-align: left;
517
+ cursor: pointer;
518
+ background: 0 0;
519
+ border: 0;
520
+ border-radius: 12px;
521
+ display: flex;
522
+ align-items: center;
523
+ gap: 12px;
524
+ padding: 14px 16px;
525
+ }
526
+ .dsh-ux-cardHeader:focus-visible {
527
+ outline: 2px solid var(--dsw-alias-brand-primary);
528
+ outline-offset: -2px;
529
+ }
530
+ .dsh-ux-cardHeaderStatic {
531
+ box-sizing: border-box;
532
+ display: flex;
533
+ align-items: center;
534
+ gap: 12px;
535
+ padding: 14px 16px;
536
+ }
537
+ .dsh-ux-cardHeadText {
538
+ display: flex;
539
+ flex-direction: column;
540
+ flex: 1;
541
+ gap: 4px;
542
+ min-width: 0;
543
+ }
544
+ .dsh-ux-cardName {
545
+ color: var(--dsw-alias-label-primary);
546
+ font-size: 15px;
547
+ font-weight: 600;
548
+ line-height: 1.4;
549
+ margin: 0;
550
+ }
551
+ .dsh-ux-cardDescription {
552
+ color: var(--dsw-alias-label-secondary);
553
+ font-size: 13px;
554
+ line-height: 1.5;
555
+ margin: 0;
556
+ }
557
+ .dsh-ux-cardChevron {
558
+ color: var(--dsw-alias-label-tertiary);
559
+ flex: none;
560
+ transition: transform .16s;
561
+ }
562
+ .dsh-ux-cardChevronOpen { transform: rotate(180deg); }
563
+ .dsh-ux-cardBody {
564
+ border-top: 1px solid var(--dsw-alias-border-l2);
565
+ margin: 0 16px;
566
+ padding-bottom: 8px;
567
+ }
568
+ `;
569
+ function installSettingsCardStyle() {
570
+ const tag = document.createElement("style");
571
+ tag.dataset.plugin = "dsh-composer-ux-settings";
572
+ tag.textContent = CARD_CSS;
573
+ document.head.appendChild(tag);
574
+ return () => {
575
+ tag.remove();
576
+ };
577
+ }
578
+
579
+ // src/client/ContextMenuHost.tsx
580
+ var import_react = __toESM(require("react"), 1);
581
+
582
+ // src/client/styles.ts
583
+ var description = {
584
+ color: "var(--dsw-alias-label-tertiary)",
585
+ fontSize: 12,
586
+ lineHeight: 1.6,
587
+ margin: "0 0 12px"
588
+ };
589
+ var row = {
590
+ display: "flex",
591
+ alignItems: "center",
592
+ justifyContent: "space-between",
593
+ gap: 12,
594
+ minHeight: 40,
595
+ padding: "7px 0",
596
+ borderTop: "0.5px solid var(--dsw-alias-border-l2)"
597
+ };
598
+ var rowText = {
599
+ minWidth: 0,
600
+ flex: 1
601
+ };
602
+ var rowTitle = {
603
+ color: "var(--dsw-alias-label-primary)",
604
+ fontSize: 13,
605
+ lineHeight: 1.4
606
+ };
607
+ var rowDesc = {
608
+ color: "var(--dsw-alias-label-tertiary)",
609
+ fontSize: 11,
610
+ lineHeight: 1.4,
611
+ marginTop: 2
612
+ };
613
+ var kbd = {
614
+ color: "var(--dsw-alias-label-primary)",
615
+ background: "var(--dsw-alias-bg-layer-1)",
616
+ border: "0.5px solid var(--dsw-alias-border-l2)",
617
+ borderRadius: 6,
618
+ padding: "3px 8px",
619
+ fontSize: 12,
620
+ fontFamily: "var(--dsw-font-family)",
621
+ whiteSpace: "nowrap"
622
+ };
623
+ var pill = {
624
+ background: "var(--dsw-alias-interactive-bg-hover)",
625
+ color: "var(--dsw-alias-label-secondary)",
626
+ border: "0.5px solid var(--dsw-alias-border-l2)",
627
+ borderRadius: 999,
628
+ padding: "4px 10px",
629
+ fontSize: 12,
630
+ cursor: "pointer",
631
+ whiteSpace: "nowrap"
632
+ };
633
+ var pillActive = {
634
+ ...pill,
635
+ color: "var(--dsw-alias-button-primary-fill)",
636
+ borderColor: "var(--dsw-alias-button-primary-fill)"
637
+ };
638
+ var textInput = {
639
+ background: "var(--dsw-alias-bg-layer-1)",
640
+ color: "var(--dsw-alias-label-primary)",
641
+ border: "0.5px solid var(--dsw-alias-border-l2)",
642
+ borderRadius: 8,
643
+ padding: "6px 9px",
644
+ fontSize: 12,
645
+ lineHeight: 1.4,
646
+ width: "100%",
647
+ minWidth: 0,
648
+ boxSizing: "border-box",
649
+ flex: 1
650
+ };
651
+ var hintError = {
652
+ color: "var(--dsw-alias-state-error-primary)",
653
+ fontSize: 12,
654
+ lineHeight: 1.5,
655
+ margin: "6px 0 0"
656
+ };
657
+ var hintInfo = {
658
+ color: "var(--dsw-alias-label-secondary)",
659
+ fontSize: 12,
660
+ lineHeight: 1.5,
661
+ margin: "6px 0 0"
662
+ };
663
+ var menu = {
664
+ position: "fixed",
665
+ zIndex: 9999,
666
+ minWidth: 200,
667
+ background: "var(--dsw-specific-menu)",
668
+ border: "0.5px solid var(--dsw-alias-border-l1)",
669
+ borderRadius: 10,
670
+ boxShadow: "var(--dsw-elevation-prominent)",
671
+ padding: 5,
672
+ pointerEvents: "auto",
673
+ userSelect: "none"
674
+ };
675
+ var menuItem = {
676
+ display: "flex",
677
+ alignItems: "center",
678
+ justifyContent: "space-between",
679
+ gap: 24,
680
+ padding: "6px 10px",
681
+ borderRadius: 6,
682
+ fontSize: 13,
683
+ color: "var(--dsw-alias-label-primary)",
684
+ cursor: "pointer",
685
+ background: "transparent",
686
+ border: "none",
687
+ width: "100%",
688
+ textAlign: "left"
689
+ };
690
+ var menuItemDim = {
691
+ ...menuItem,
692
+ color: "var(--dsw-alias-label-dimmed)",
693
+ cursor: "default"
694
+ };
695
+ var menuShortcut = {
696
+ color: "var(--dsw-alias-label-tertiary)",
697
+ fontSize: 12,
698
+ fontFamily: "var(--dsw-font-family)"
699
+ };
700
+ var menuSeparator = {
701
+ height: 0,
702
+ borderTop: "0.5px solid var(--dsw-alias-border-l2)",
703
+ margin: "4px 8px"
704
+ };
705
+ var menuNote = {
706
+ color: "var(--dsw-alias-state-warn-label)",
707
+ fontSize: 11,
708
+ lineHeight: 1.4,
709
+ padding: "4px 10px 2px"
710
+ };
711
+
712
+ // src/client/ContextMenuHost.tsx
713
+ var import_jsx_runtime = require("react/jsx-runtime");
714
+ var GROUPS = [
715
+ [
716
+ { label: "\u64A4\u9500", shortcut: "Ctrl+Z", field: "menuUndo", id: "undo" },
717
+ { label: "\u91CD\u505A", shortcut: "Ctrl+Y", field: "menuRedo", id: "redo" }
718
+ ],
719
+ [
720
+ { label: "\u526A\u5207", shortcut: "Ctrl+X", field: "menuCut", id: "cut" },
721
+ { label: "\u590D\u5236", shortcut: "Ctrl+C", field: "menuCopy", id: "copy" },
722
+ { label: "\u7C98\u8D34", shortcut: "Ctrl+V", field: "menuPaste", id: "paste" },
723
+ { label: "\u5220\u9664", shortcut: "", field: "menuDelete", id: "delete" }
724
+ ],
725
+ [
726
+ { label: "\u5168\u9009", shortcut: "Ctrl+A", field: "menuSelectAll", id: "selectAll" }
727
+ ]
728
+ ];
729
+ var ITEM_STYLES = {
730
+ undo: "always",
731
+ redo: "always",
732
+ cut: "selection",
733
+ copy: "selection",
734
+ paste: "always",
735
+ delete: "selection",
736
+ selectAll: "always"
737
+ };
738
+ function clamp(value, size, viewport) {
739
+ return Math.max(8, Math.min(value, viewport - size - 8));
740
+ }
741
+ function ContextMenuHost({ useMenu, useLive, actions }) {
742
+ const menuState = useMenu((item) => item);
743
+ const settings = useLive((item) => item);
744
+ const ref = (0, import_react.useRef)(null);
745
+ const [position, setPosition] = (0, import_react.useState)(null);
746
+ (0, import_react.useLayoutEffect)(() => {
747
+ if (menuState === null) {
748
+ setPosition(null);
749
+ return;
750
+ }
751
+ const el = ref.current;
752
+ if (el === null) return;
753
+ const rect = el.getBoundingClientRect();
754
+ setPosition({
755
+ x: clamp(menuState.x, rect.width, window.innerWidth),
756
+ y: clamp(menuState.y, rect.height, window.innerHeight)
757
+ });
758
+ }, [menuState?.x, menuState?.y]);
759
+ (0, import_react.useEffect)(() => {
760
+ if (menuState === null) return;
761
+ const onPointerDown = (event) => {
762
+ if (ref.current !== null && event.target instanceof Node && ref.current.contains(event.target)) return;
763
+ actions.close();
764
+ };
765
+ const onEscape = (event) => {
766
+ if (event.key !== "Escape") return;
767
+ event.preventDefault();
768
+ event.stopPropagation();
769
+ actions.close();
770
+ };
771
+ window.addEventListener("pointerdown", onPointerDown, true);
772
+ window.addEventListener("keydown", onEscape, true);
773
+ window.addEventListener("scroll", actions.close, true);
774
+ window.addEventListener("resize", actions.close);
775
+ return () => {
776
+ window.removeEventListener("pointerdown", onPointerDown, true);
777
+ window.removeEventListener("keydown", onEscape, true);
778
+ window.removeEventListener("scroll", actions.close, true);
779
+ window.removeEventListener("resize", actions.close);
780
+ };
781
+ }, [menuState === null, actions]);
782
+ (0, import_react.useEffect)(() => {
783
+ if (menuState?.note === void 0) return;
784
+ const timer = setTimeout(() => {
785
+ actions.note(void 0);
786
+ }, 3e3);
787
+ return () => {
788
+ clearTimeout(timer);
789
+ };
790
+ }, [menuState?.note]);
791
+ if (menuState === null) return null;
792
+ const onItemClick = (id) => {
793
+ if (id === "paste") actions.note("\u6B63\u5728\u8BFB\u53D6\u526A\u8D34\u677F\u2026");
794
+ void actions.run(id).then((result) => {
795
+ if (result.ok) actions.close();
796
+ else actions.note(result.message ?? "\u64CD\u4F5C\u5931\u8D25");
797
+ });
798
+ };
799
+ const groups = GROUPS.map((group) => group.filter((item) => settings[item.field] === true)).filter((group) => group.length > 0);
800
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
801
+ "div",
802
+ {
803
+ ref,
804
+ role: "menu",
805
+ style: {
806
+ ...menu,
807
+ left: position?.x ?? menuState.x,
808
+ top: position?.y ?? menuState.y
809
+ },
810
+ children: [
811
+ groups.map((group, groupIndex) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [
812
+ groupIndex > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { role: "separator", style: menuSeparator }),
813
+ group.map((item) => {
814
+ const needsSelection = ITEM_STYLES[item.id] === "selection";
815
+ const disabled = needsSelection && !menuState.hasSelection;
816
+ const style = disabled ? menuItemDim : menuItem;
817
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
818
+ "button",
819
+ {
820
+ type: "button",
821
+ role: "menuitem",
822
+ disabled,
823
+ style,
824
+ onMouseDown: (event) => {
825
+ event.preventDefault();
826
+ },
827
+ onClick: () => {
828
+ onItemClick(item.id);
829
+ },
830
+ children: [
831
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: item.label }),
832
+ item.shortcut !== "" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: menuShortcut, children: item.shortcut })
833
+ ]
834
+ },
835
+ item.field
836
+ );
837
+ })
838
+ ] }, group[0].field)),
839
+ menuState.note !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: menuNote, children: menuState.note })
840
+ ]
841
+ }
842
+ );
843
+ }
844
+
845
+ // src/client/PanelResizeHandles.tsx
846
+ var import_react2 = require("react");
847
+ var import_jsx_runtime2 = require("react/jsx-runtime");
848
+ function stripRect(edge, rect) {
849
+ const horizontal = (left, width) => ({
850
+ left,
851
+ top: rect.top + 12,
852
+ width,
853
+ height: Math.max(0, rect.height - 24),
854
+ cursor: "ew-resize"
855
+ });
856
+ const vertical = (top, height) => ({
857
+ left: rect.left + 16,
858
+ top,
859
+ width: Math.max(0, rect.width - 32),
860
+ height,
861
+ cursor: "ns-resize"
862
+ });
863
+ const corner = (left, top, horizontalCursor, verticalCursor) => ({
864
+ left,
865
+ top,
866
+ width: 18,
867
+ height: 18,
868
+ cursor: horizontalCursor
869
+ });
870
+ const corners = {
871
+ tl: "nwse-resize",
872
+ tr: "nesw-resize",
873
+ bl: "nesw-resize",
874
+ br: "nwse-resize"
875
+ };
876
+ switch (edge) {
877
+ case "left":
878
+ return horizontal(rect.left - 4, 8);
879
+ case "right":
880
+ return horizontal(rect.right - 4, 8);
881
+ case "top":
882
+ return vertical(rect.top - 4, 8);
883
+ case "bottom":
884
+ return vertical(rect.bottom - 4, 8);
885
+ case "tl":
886
+ return corner(rect.left - 6, rect.top - 6, corners.tl, "nwse");
887
+ case "tr":
888
+ return corner(rect.right - 12, rect.top - 6, corners.tr, "nesw");
889
+ case "bl":
890
+ return corner(rect.left - 6, rect.bottom - 14, corners.bl, "nesw");
891
+ case "br":
892
+ return corner(rect.right - 12, rect.bottom - 14, corners.br, "nwse");
893
+ }
894
+ }
895
+ function PanelResizeHandles({ useLive, actions }) {
896
+ const settings = useLive((value) => ({
897
+ enabled: value.enabled,
898
+ panelResize: value.panelResize,
899
+ panelWidth: value.panelWidth,
900
+ panelHeight: value.panelHeight
901
+ }));
902
+ const settingsRef = (0, import_react2.useRef)(settings);
903
+ settingsRef.current = settings;
904
+ const enabled = settings.enabled === true && settings.panelResize === true;
905
+ const [rect, setRect] = (0, import_react2.useState)(null);
906
+ const appliedRef = (0, import_react2.useRef)(false);
907
+ (0, import_react2.useEffect)(() => {
908
+ if (!enabled) {
909
+ setRect(null);
910
+ return;
911
+ }
912
+ const probe = () => {
913
+ const panel = findSettingsPanel();
914
+ if (panel === null) {
915
+ appliedRef.current = false;
916
+ setRect((current) => current === null ? null : null);
917
+ return;
918
+ }
919
+ if (!appliedRef.current) {
920
+ appliedRef.current = true;
921
+ const width = settingsRef.current.panelWidth;
922
+ const height = settingsRef.current.panelHeight;
923
+ if (width !== void 0) panel.style.width = `${clampPanelWidth(width)}px`;
924
+ if (height !== void 0) panel.style.height = `${clampPanelHeight(height)}px`;
925
+ }
926
+ setRect(panel.getBoundingClientRect());
927
+ };
928
+ probe();
929
+ const timer = setInterval(probe, 250);
930
+ const onResize = () => {
931
+ setRect((current) => current === null ? null : findSettingsPanel()?.getBoundingClientRect() ?? null);
932
+ };
933
+ window.addEventListener("resize", onResize);
934
+ return () => {
935
+ clearInterval(timer);
936
+ window.removeEventListener("resize", onResize);
937
+ };
938
+ }, [enabled]);
939
+ if (!enabled || rect === null) return null;
940
+ const onPointerDown = (edge, event) => {
941
+ event.preventDefault();
942
+ event.stopPropagation();
943
+ const panel = findSettingsPanel();
944
+ if (panel === null) return;
945
+ const start = {
946
+ edge,
947
+ startX: event.clientX,
948
+ startY: event.clientY,
949
+ startWidth: panel.clientWidth,
950
+ startHeight: panel.clientHeight
951
+ };
952
+ const onMove = (move) => {
953
+ if (dragStartRef.current !== start || dragStartRef.current === null) return;
954
+ move.preventDefault();
955
+ const panelEl = findSettingsPanel();
956
+ if (panelEl === null) return;
957
+ const dx = move.clientX - start.startX;
958
+ const dy = move.clientY - start.startY;
959
+ let width = start.startWidth;
960
+ let height = start.startHeight;
961
+ if (edge === "left" || edge === "right" || edge === "tl" || edge === "bl" || edge === "tr" || edge === "br") {
962
+ width = start.startWidth + (edge === "left" || edge === "tl" || edge === "bl" ? -dx : dx);
963
+ }
964
+ if (edge === "top" || edge === "bottom" || edge === "tl" || edge === "bl" || edge === "tr" || edge === "br") {
965
+ height = start.startHeight + (edge === "top" || edge === "tl" || edge === "tr" ? -dy : dy);
966
+ }
967
+ panelEl.style.width = `${Math.round(clampPanelWidth(width))}px`;
968
+ panelEl.style.height = `${Math.round(clampPanelHeight(height))}px`;
969
+ setRect(panelEl.getBoundingClientRect());
970
+ };
971
+ const onUp = () => {
972
+ window.removeEventListener("pointermove", onMove);
973
+ window.removeEventListener("pointerup", onUp);
974
+ dragStartRef.current = null;
975
+ const panelEl = findSettingsPanel();
976
+ if (panelEl !== null) {
977
+ actions.setField(PANEL_WIDTH_FIELD, Math.round(clampPanelWidth(panelEl.clientWidth)));
978
+ actions.setField(PANEL_HEIGHT_FIELD, Math.round(clampPanelHeight(panelEl.clientHeight)));
979
+ }
980
+ document.body.style.removeProperty("cursor");
981
+ document.body.style.removeProperty("user-select");
982
+ };
983
+ dragStartRef.current = start;
984
+ document.body.style.cursor = stripRect(edge, rect).cursor;
985
+ document.body.style.userSelect = "none";
986
+ window.addEventListener("pointermove", onMove);
987
+ window.addEventListener("pointerup", onUp);
988
+ };
989
+ const edges = ["left", "right", "top", "bottom", "tl", "tr", "bl", "br"];
990
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: edges.map((edge) => {
991
+ const pos = stripRect(edge, rect);
992
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
993
+ "div",
994
+ {
995
+ onPointerDown: (event) => {
996
+ onPointerDown(edge, event);
997
+ },
998
+ onPointerEnter: (event) => {
999
+ event.currentTarget.style.background = "var(--dsw-alias-state-business-primary)";
1000
+ event.currentTarget.style.opacity = "0.75";
1001
+ },
1002
+ onPointerLeave: (event) => {
1003
+ event.currentTarget.style.background = "transparent";
1004
+ event.currentTarget.style.opacity = "0";
1005
+ },
1006
+ style: {
1007
+ position: "fixed",
1008
+ zIndex: 9998,
1009
+ left: pos.left,
1010
+ top: pos.top,
1011
+ width: pos.width,
1012
+ height: pos.height,
1013
+ cursor: pos.cursor,
1014
+ background: "transparent",
1015
+ opacity: 0,
1016
+ borderRadius: 6,
1017
+ pointerEvents: "auto",
1018
+ touchAction: "none"
1019
+ }
1020
+ },
1021
+ edge
1022
+ );
1023
+ }) });
1024
+ }
1025
+ var dragStartRef = { current: null };
1026
+
1027
+ // src/client/SettingsSection.tsx
1028
+ var import_react3 = require("react");
1029
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1030
+ var bodyLead = { ...description, margin: "12px 0" };
1031
+ function Chevron() {
1032
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1033
+ "path",
1034
+ {
1035
+ d: "M3.25 5.5L7 9.25L10.75 5.5",
1036
+ stroke: "currentColor",
1037
+ strokeWidth: "1.4",
1038
+ strokeLinecap: "round",
1039
+ strokeLinejoin: "round"
1040
+ }
1041
+ ) });
1042
+ }
1043
+ function FoldCard(props) {
1044
+ const [open, setOpen] = (0, import_react3.useState)(false);
1045
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: open ? "dsh-ux-card dsh-ux-cardOpen" : "dsh-ux-card", children: [
1046
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1047
+ "button",
1048
+ {
1049
+ type: "button",
1050
+ className: "dsh-ux-cardHeader",
1051
+ "aria-expanded": open,
1052
+ onClick: () => {
1053
+ setOpen((current) => !current);
1054
+ },
1055
+ children: [
1056
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-ux-cardHeadText", children: [
1057
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-ux-cardName", children: props.name }),
1058
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-ux-cardDescription", children: props.summary })
1059
+ ] }),
1060
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: open ? "dsh-ux-cardChevron dsh-ux-cardChevronOpen" : "dsh-ux-cardChevron", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Chevron, {}) })
1061
+ ]
1062
+ }
1063
+ ),
1064
+ open && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-ux-cardBody", children: props.children })
1065
+ ] });
1066
+ }
1067
+ function KeyRow(props) {
1068
+ const { title, desc, value, presets, conflict, onChange, first } = props;
1069
+ const [recording, setRecording] = (0, import_react3.useState)(false);
1070
+ const [error, setError] = (0, import_react3.useState)(null);
1071
+ (0, import_react3.useEffect)(() => {
1072
+ if (!recording) return;
1073
+ const onKeyDown = (event) => {
1074
+ event.preventDefault();
1075
+ event.stopPropagation();
1076
+ if (event.isComposing) return;
1077
+ const result = evaluateRecordedKey(event);
1078
+ if (result.kind === "ignore") return;
1079
+ if (result.kind === "cancel") {
1080
+ setRecording(false);
1081
+ setError(null);
1082
+ return;
1083
+ }
1084
+ if (result.kind === "clear") {
1085
+ setRecording(false);
1086
+ setError(null);
1087
+ onChange("");
1088
+ return;
1089
+ }
1090
+ if (result.kind === "reject") {
1091
+ setError(result.message);
1092
+ return;
1093
+ }
1094
+ if (conflict(result.chord)) {
1095
+ setRecording(false);
1096
+ setError("\u53D1\u9001\u4E0E\u6362\u884C\u4E0D\u80FD\u8BBE\u4E3A\u76F8\u540C\u6309\u952E\uFF08\u53EF\u5148\u7ED9\u53E6\u4E00\u4FA7\u6362\u952E\uFF0C\u518D\u7ED1\u5B9A\u8BE5\u952E\uFF09\u3002");
1097
+ return;
1098
+ }
1099
+ setRecording(false);
1100
+ setError(null);
1101
+ onChange(result.chord);
1102
+ };
1103
+ window.addEventListener("keydown", onKeyDown, true);
1104
+ return () => {
1105
+ window.removeEventListener("keydown", onKeyDown, true);
1106
+ };
1107
+ }, [recording, conflict, onChange]);
1108
+ const base = first === true ? { ...row, borderTop: "none" } : row;
1109
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { ...base, flexDirection: "column", alignItems: "stretch", gap: 8 }, children: [
1110
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: rowText, children: [
1111
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: rowTitle, children: title }),
1112
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: rowDesc, children: desc })
1113
+ ] }),
1114
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: [
1115
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: kbd, children: displayChord(value) }),
1116
+ !recording && presets.map((preset) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1117
+ "button",
1118
+ {
1119
+ type: "button",
1120
+ style: value === preset ? pillActive : pill,
1121
+ onClick: () => {
1122
+ setError(null);
1123
+ onChange(preset);
1124
+ },
1125
+ children: displayChord(preset)
1126
+ },
1127
+ preset
1128
+ )),
1129
+ !recording && value !== "" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", style: pill, onClick: () => {
1130
+ setError(null);
1131
+ onChange("");
1132
+ }, children: "\u6E05\u9664" }),
1133
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1134
+ "button",
1135
+ {
1136
+ type: "button",
1137
+ style: recording ? pillActive : pill,
1138
+ onClick: () => {
1139
+ setRecording(true);
1140
+ setError(null);
1141
+ },
1142
+ children: recording ? "\u8BF7\u6309\u4E0B\u65B0\u7EC4\u5408\u952E\u2026" : "\u81EA\u5B9A\u4E49\u2026"
1143
+ }
1144
+ ),
1145
+ recording && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", style: pill, onClick: () => {
1146
+ setRecording(false);
1147
+ setError(null);
1148
+ }, children: "Esc \u53D6\u6D88" }),
1149
+ error !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: "var(--dsw-alias-state-error-primary)", fontSize: 12 }, children: error })
1150
+ ] })
1151
+ ] });
1152
+ }
1153
+ function ToggleRow(props) {
1154
+ const { label, desc, checked, onChange, first } = props;
1155
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: first === true ? { ...row, borderTop: "none" } : row, children: [
1156
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: rowText, children: [
1157
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: rowTitle, children: label }),
1158
+ desc !== void 0 && desc !== "" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: rowDesc, children: desc })
1159
+ ] }),
1160
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1161
+ "button",
1162
+ {
1163
+ type: "button",
1164
+ role: "switch",
1165
+ "aria-checked": checked,
1166
+ onClick: () => {
1167
+ onChange(!checked);
1168
+ },
1169
+ style: {
1170
+ width: 36,
1171
+ height: 20,
1172
+ borderRadius: 999,
1173
+ border: "0.5px solid var(--dsw-alias-border-l2)",
1174
+ cursor: "pointer",
1175
+ background: checked ? "var(--dsw-alias-button-primary-fill)" : "var(--dsw-alias-interactive-bg-hover)",
1176
+ position: "relative",
1177
+ flex: "0 0 auto",
1178
+ padding: 0
1179
+ },
1180
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1181
+ "span",
1182
+ {
1183
+ style: {
1184
+ position: "absolute",
1185
+ top: 2,
1186
+ left: checked ? 18 : 2,
1187
+ width: 14,
1188
+ height: 14,
1189
+ borderRadius: "50%",
1190
+ background: checked ? "var(--dsw-alias-label-primary-foreground)" : "var(--dsw-alias-label-secondary)",
1191
+ transition: "left 0.12s ease"
1192
+ }
1193
+ }
1194
+ )
1195
+ }
1196
+ )
1197
+ ] });
1198
+ }
1199
+ function TextFieldRow(props) {
1200
+ const { title, desc, value, placeholder, maxLength, onChange, action, first } = props;
1201
+ const [draft, setDraft] = (0, import_react3.useState)(value);
1202
+ const focused = (0, import_react3.useRef)(false);
1203
+ (0, import_react3.useEffect)(() => {
1204
+ if (!focused.current) setDraft(value);
1205
+ }, [value]);
1206
+ const commit = () => {
1207
+ if (draft === value) return;
1208
+ onChange(draft.trim());
1209
+ };
1210
+ const base = first === true ? { ...row, borderTop: "none" } : row;
1211
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { ...base, flexDirection: "column", alignItems: "stretch", gap: 8 }, children: [
1212
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: rowText, children: [
1213
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: rowTitle, children: title }),
1214
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: rowDesc, children: desc })
1215
+ ] }),
1216
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
1217
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1218
+ "input",
1219
+ {
1220
+ type: "text",
1221
+ value: draft,
1222
+ placeholder: placeholder ?? "",
1223
+ maxLength,
1224
+ spellCheck: false,
1225
+ autoCorrect: "off",
1226
+ autoCapitalize: "off",
1227
+ onFocus: () => {
1228
+ focused.current = true;
1229
+ },
1230
+ onChange: (event) => {
1231
+ setDraft(event.target.value);
1232
+ },
1233
+ onBlur: () => {
1234
+ focused.current = false;
1235
+ commit();
1236
+ },
1237
+ onKeyDown: (event) => {
1238
+ if (event.key !== "Enter") return;
1239
+ event.preventDefault();
1240
+ commit();
1241
+ event.currentTarget.blur();
1242
+ },
1243
+ style: textInput
1244
+ }
1245
+ ),
1246
+ action !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", style: pill, onClick: action.onClick, children: action.label })
1247
+ ] })
1248
+ ] });
1249
+ }
1250
+ function SettingsSection({ useLive, actions }) {
1251
+ const settings = useLive((item) => item);
1252
+ const [conflict, setConflict] = (0, import_react3.useState)(null);
1253
+ const setKey = (side, chord) => {
1254
+ const other = side === "send" ? settings.newlineKey : settings.sendKey;
1255
+ if (chord !== "" && chord === other) {
1256
+ setConflict(chord);
1257
+ return;
1258
+ }
1259
+ setConflict(null);
1260
+ actions.setField(side === "send" ? "sendKey" : "newlineKey", chord);
1261
+ };
1262
+ const conflictHint = conflict !== null ? `\u300C${displayChord(conflict)}\u300D\u5DF2\u88AB\u53E6\u4E00\u4FA7\u5360\u7528\uFF0C\u8BF7\u5148\u4E3A\u53E6\u4E00\u4FA7\u9009\u62E9\u5176\u5B83\u6309\u952E\u3002` : null;
1263
+ const menuEnabled = MENU_ITEMS.filter((item) => settings[item.field]).length;
1264
+ const keySummary = `\u53D1\u9001 ${displayChord(settings.sendKey)} \xB7 \u6362\u884C ${displayChord(settings.newlineKey)}`;
1265
+ const menuSummary = settings.menuNative ? "\u5F53\u524D\uFF1A\u7CFB\u7EDF\u539F\u751F\u83DC\u5355\uFF08\u7C98\u8D34\u514D\u6388\u6743\uFF09" : `\u5F53\u524D\uFF1A\u81EA\u5B9A\u4E49\u83DC\u5355 \xB7 ${menuEnabled} / ${MENU_ITEMS.length} \u9879\u5F00\u542F`;
1266
+ const panelSummary = `\u5BFC\u822A\u6EDA\u52A8 ${settings.panelScroll ? "\u5F00" : "\u5173"} \xB7 \u8FB9\u7F18\u7F29\u653E ${settings.panelResize ? "\u5F00" : "\u5173"} \xB7 ${settings.panelWidth}\xD7${settings.panelHeight}`;
1267
+ const headerSummary = settings.headerEnabled ? settings.headerStatus === "" ? "\u5DF2\u542F\u7528" : settings.headerStatus : "\u672A\u542F\u7528";
1268
+ const headerStatusText = settings.headerEnabled ? settings.headerStatus === "" ? "\u7B49\u5F85\u9996\u6B21\u5199\u5165\u2026" : settings.headerStatus : "\u672A\u542F\u7528\uFF08\u6253\u5F00\u4E0A\u65B9\u5F00\u5173\u5373\u5199\u5165\uFF09";
1269
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { padding: "4px 2px" }, children: [
1270
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "dsh-ux-card", children: [
1271
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-ux-cardHeaderStatic", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-ux-cardHeadText", children: [
1272
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { className: "dsh-ux-cardName", children: "\u8F93\u5165\u4F53\u9A8C" }),
1273
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dsh-ux-cardDescription", children: "dsh-composer-ux \xB7 \u4E3B\u804A\u5929\u8F93\u5165\u6846\u7684\u952E\u4F4D\u3001\u53F3\u952E\u83DC\u5355\u4E0E\u8BBE\u7F6E\u9762\u677F\uFF1B\u8BBE\u7F6E\u5373\u65F6\u751F\u6548\u5E76\u6301\u4E45\u4FDD\u5B58\u3002" })
1274
+ ] }) }),
1275
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-ux-cardBody", children: [
1276
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1277
+ ToggleRow,
1278
+ {
1279
+ first: true,
1280
+ label: "\u542F\u7528\u8F93\u5165\u589E\u5F3A",
1281
+ desc: "\u5173\u95ED\u540E\u952E\u4F4D\u3001\u53F3\u952E\u83DC\u5355\u4E0E\u8BBE\u7F6E\u9762\u677F\u6EDA\u52A8/\u7F29\u653E\u5168\u90E8\u505C\u7528\uFF08\u672C\u9875\u4FDD\u7559\u7528\u4E8E\u91CD\u65B0\u5F00\u542F\uFF09",
1282
+ checked: settings.enabled,
1283
+ onChange: (next) => {
1284
+ actions.setField(ENABLED_FIELD, next);
1285
+ }
1286
+ }
1287
+ ),
1288
+ !settings.enabled && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: hintInfo, children: "\u5DF2\u505C\u7528\uFF1A\u53D1\u9001/\u6362\u884C\u952E\u4F4D\u3001\u53F3\u952E\u83DC\u5355\u3001\u8BBE\u7F6E\u9762\u677F\u6EDA\u52A8\u4E0E\u8FB9\u7F18\u7F29\u653E\u5747\u4E0D\u518D\u751F\u6548\uFF0C\u8F93\u5165\u6846\u6062\u590D DSH \u539F\u751F\u884C\u4E3A\u3002 \u6253\u5F00\u4E0A\u65B9\u5F00\u5173\u5373\u53EF\u4E00\u952E\u6062\u590D\uFF1B\u6240\u6709\u8BBE\u7F6E\u503C\u4ECD\u4FDD\u7559\u3002" })
1289
+ ] })
1290
+ ] }),
1291
+ settings.enabled && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1292
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(FoldCard, { name: "\u952E\u4F4D", summary: keySummary, children: [
1293
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: bodyLead, children: "\u652F\u6301\u5E38\u7528\u9884\u8BBE\uFF0C\u4E5F\u53EF\u4EE5\u70B9\u51FB\u300C\u81EA\u5B9A\u4E49\u2026\u300D\u540E\u76F4\u63A5\u6309\u4E0B\u4EFB\u610F\u7EC4\u5408\u952E\u5F55\u5236\uFF08Esc \u53D6\u6D88\uFF0CBackspace \u6E05\u9664\uFF09\u3002 \u672A\u7ED1\u5B9A\u7684 Enter \u7CFB\u6309\u952E\u4E0D\u4F1A\u89E6\u53D1\u53D1\u9001\u6216\u6362\u884C\uFF1BCtrl+Enter / \u2318+Enter \u672A\u88AB\u7ED1\u5B9A\u65F6\u4FDD\u7559\u539F\u300C\u52A0\u901F\u63D0\u4EA4\u300D\u884C\u4E3A\uFF1B \u4E2D\u6587\u8F93\u5165\u6CD5\u7EC4\u5408\u671F\u95F4\u4E0D\u53D7\u5F71\u54CD\u3002" }),
1294
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1295
+ KeyRow,
1296
+ {
1297
+ first: true,
1298
+ title: "\u53D1\u9001\u952E",
1299
+ desc: "\u6309\u4E0B\u540E\u53D1\u9001\u5F53\u524D\u8F93\u5165",
1300
+ value: settings.sendKey,
1301
+ presets: SEND_PRESETS,
1302
+ conflict: (chord) => chord !== "" && chord === settings.newlineKey,
1303
+ onChange: (chord) => {
1304
+ setKey("send", chord);
1305
+ }
1306
+ }
1307
+ ),
1308
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1309
+ KeyRow,
1310
+ {
1311
+ title: "\u6362\u884C\u952E",
1312
+ desc: "\u6309\u4E0B\u540E\u5728\u8F93\u5165\u5185\u6362\u884C",
1313
+ value: settings.newlineKey,
1314
+ presets: NEWLINE_PRESETS,
1315
+ conflict: (chord) => chord !== "" && chord === settings.sendKey,
1316
+ onChange: (chord) => {
1317
+ setKey("newline", chord);
1318
+ }
1319
+ }
1320
+ ),
1321
+ conflictHint !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: hintError, children: conflictHint }),
1322
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { marginTop: 10 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1323
+ "button",
1324
+ {
1325
+ type: "button",
1326
+ style: pill,
1327
+ onClick: () => {
1328
+ setConflict(null);
1329
+ actions.resetAll();
1330
+ },
1331
+ children: "\u6062\u590D\u9ED8\u8BA4"
1332
+ }
1333
+ ) })
1334
+ ] }),
1335
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(FoldCard, { name: "\u53F3\u952E\u83DC\u5355", summary: menuSummary, children: [
1336
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: bodyLead, children: "\u53F3\u952E\u70B9\u51FB\u8F93\u5165\u6846\u65F6\u7684\u884C\u4E3A\u3002\u7CFB\u7EDF\u539F\u751F\u6A21\u5F0F\u4E0B\u7531\u6D4F\u89C8\u5668\u5F39\u51FA\u81EA\u5DF1\u7684\u83DC\u5355\uFF08\u6837\u5F0F\u968F\u6D4F\u89C8\u5668\u800C\u53D8\u5316\uFF0C \u7C98\u8D34\u514D\u6388\u6743\u3001\u96F6\u914D\u7F6E\uFF09\uFF1B\u81EA\u5B9A\u4E49\u6A21\u5F0F\u4F7F\u7528\u56FA\u5B9A\u6837\u5F0F\u83DC\u5355\uFF08\u672A\u9009\u4E2D\u6587\u672C\u65F6\u300C\u526A\u5207 / \u590D\u5236 / \u5220\u9664\u300D\u7F6E\u7070\uFF0C \u7C98\u8D34\u9700\u8981\u6D4F\u89C8\u5668\u526A\u8D34\u677F\u6388\u6743\uFF0CFirefox \u8981\u5728 about:config \u4E2D\u8BBE\u7F6E permissions.default.clipboard-read = 1 \u624D\u80FD\u514D\u5F39\u7A97\uFF09\u3002" }),
1337
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1338
+ ToggleRow,
1339
+ {
1340
+ first: true,
1341
+ label: "\u4F7F\u7528\u7CFB\u7EDF\u539F\u751F\u83DC\u5355",
1342
+ desc: "\u6253\u5F00\u540E\u53F3\u952E\u7C98\u8D34\u514D\u6388\u6743\uFF08\u70B9\u51FB\u6D4F\u89C8\u5668\u83DC\u5355\u7684\u7C98\u8D34\u76F4\u63A5\u6210\u529F\uFF09\uFF1B\u5173\u95ED\u540E\u6062\u590D\u81EA\u5B9A\u4E49\u83DC\u5355",
1343
+ checked: settings.menuNative,
1344
+ onChange: (next) => {
1345
+ actions.setField(MENU_NATIVE_FIELD, next);
1346
+ }
1347
+ }
1348
+ ),
1349
+ !settings.menuNative && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1350
+ MENU_ITEMS.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1351
+ ToggleRow,
1352
+ {
1353
+ label: item.label,
1354
+ desc: item.shortcut !== "" ? `\u5FEB\u6377\u952E ${item.shortcut}` : "",
1355
+ checked: settings[item.field],
1356
+ onChange: (next) => {
1357
+ actions.setField(item.field, next);
1358
+ }
1359
+ },
1360
+ item.field
1361
+ )),
1362
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { marginTop: 10 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1363
+ "button",
1364
+ {
1365
+ type: "button",
1366
+ style: pill,
1367
+ onClick: () => {
1368
+ for (const item of MENU_ITEMS) actions.setField(item.field, true);
1369
+ },
1370
+ children: "\u5168\u90E8\u5F00\u542F"
1371
+ }
1372
+ ) })
1373
+ ] })
1374
+ ] }),
1375
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(FoldCard, { name: "\u8BBE\u7F6E\u9762\u677F", summary: panelSummary, children: [
1376
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: bodyLead, children: "\u63D2\u4EF6\u88C5\u5F97\u591A\u65F6\u8BBE\u7F6E\u6761\u76EE\u5F88\u957F\uFF1A\u5F00\u542F\u300C\u5BFC\u822A\u53EF\u6EDA\u52A8\u300D\u540E\uFF0C\u5DE6\u4FA7\u5BFC\u822A\u5728\u6EA2\u51FA\u65F6\u4F1A\u51FA\u73B0\u6EDA\u52A8\u6761\u3002 \u5F00\u542F\u300C\u8FB9\u7F18\u8C03\u6574\u5927\u5C0F\u300D\u540E\uFF0C\u628A\u9F20\u6807\u79FB\u5230\u8BBE\u7F6E\u9762\u677F\u7684\u8FB9\u6216\u89D2\u4E0A\uFF08\u51FA\u73B0\u9AD8\u4EAE\u6216\u5149\u6807\u53D8\u5316\uFF09\u62D6\u52A8\u5373\u53EF\u6539\u53D8\u9762\u677F\u5927\u5C0F\uFF0C \u5C3A\u5BF8\u4F1A\u8BB0\u4F4F\uFF0C\u4E0B\u6B21\u6253\u5F00\u4FDD\u6301\u3002" }),
1377
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1378
+ ToggleRow,
1379
+ {
1380
+ first: true,
1381
+ label: "\u5BFC\u822A\u53EF\u6EDA\u52A8",
1382
+ desc: "\u8BBE\u7F6E\u6761\u76EE\u8D85\u51FA\u9762\u677F\u9AD8\u5EA6\u65F6\u663E\u793A\u6EDA\u52A8\u6761",
1383
+ checked: settings.panelScroll,
1384
+ onChange: (next) => {
1385
+ actions.setField(PANEL_SCROLL_FIELD, next);
1386
+ }
1387
+ }
1388
+ ),
1389
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1390
+ ToggleRow,
1391
+ {
1392
+ label: "\u8FB9\u7F18\u8C03\u6574\u5927\u5C0F",
1393
+ desc: "\u62D6\u52A8\u9762\u677F\u56DB\u8FB9 / \u56DB\u89D2\u6539\u53D8\u9762\u677F\u5C3A\u5BF8",
1394
+ checked: settings.panelResize,
1395
+ onChange: (next) => {
1396
+ actions.setField(PANEL_RESIZE_FIELD, next);
1397
+ }
1398
+ }
1399
+ ),
1400
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", marginTop: 10 }, children: [
1401
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: rowDesc, children: "\u5C3A\u5BF8\u9884\u8BBE\uFF1A" }),
1402
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1403
+ "button",
1404
+ {
1405
+ type: "button",
1406
+ style: pill,
1407
+ onClick: () => {
1408
+ actions.clearField(PANEL_WIDTH_FIELD);
1409
+ actions.clearField(PANEL_HEIGHT_FIELD);
1410
+ },
1411
+ children: "\u9ED8\u8BA4\uFF08800\uFF09"
1412
+ }
1413
+ ),
1414
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1415
+ "button",
1416
+ {
1417
+ type: "button",
1418
+ style: settings.panelWidth === 640 && settings.panelHeight === 600 ? pillActive : pill,
1419
+ onClick: () => {
1420
+ actions.setField(PANEL_WIDTH_FIELD, 640);
1421
+ actions.setField(PANEL_HEIGHT_FIELD, 600);
1422
+ },
1423
+ children: "\u7D27\u51D1\uFF08640\xD7600\uFF09"
1424
+ }
1425
+ ),
1426
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1427
+ "button",
1428
+ {
1429
+ type: "button",
1430
+ style: settings.panelWidth === 1040 && settings.panelHeight === 760 ? pillActive : pill,
1431
+ onClick: () => {
1432
+ actions.setField(PANEL_WIDTH_FIELD, 1040);
1433
+ actions.setField(PANEL_HEIGHT_FIELD, 760);
1434
+ },
1435
+ children: "\u5BBD\u655E\uFF081040\xD7760\uFF09"
1436
+ }
1437
+ )
1438
+ ] })
1439
+ ] }),
1440
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(FoldCard, { name: "OpenCode \u8BF7\u6C42\u5934", summary: headerSummary, children: [
1441
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: bodyLead, children: "OpenCode \u7684\u63A5\u53E3\u8981\u6C42\u5BA2\u6237\u7AEF\u6BCF\u6B21\u8BF7\u6C42\u90FD\u5E26\u4E0A\u4E00\u4E2A\u7A33\u5B9A\u7684\u4F1A\u8BDD ID \u8BF7\u6C42\u5934\uFF08\u5B98\u65B9 Go \u6587\u6863 \u300C\u53EF\u4EE5\u5728\u54EA\u91CC\u4F7F\u7528\uFF1F\u300D\u7B2C 3 \u6761\uFF1A\u4E3A\u6BCF\u6BB5\u5BF9\u8BDD\u5728 x-opencode-session \u4E2D\u53D1\u9001\u4F1A\u8BDD ID\uFF0C \u4EE5\u4FBF\u5176\u4F18\u5316\u8DEF\u7531\u4E0E\u63D0\u793A\u8BCD\u7F13\u5B58\uFF09\u3002DSH \u7684\u8BBE\u7F6E\u9875\u4E0D\u63D0\u4F9B\u8BF7\u6C42\u5934\u7F16\u8F91\u5668\uFF0C\u6240\u4EE5\u8FD9\u91CC\u76F4\u63A5\u628A\u5B83\u5199\u8FDB llm-pi-ai \u7684 provider \u914D\u7F6E\u2014\u2014\u4E0B\u4E00\u6B21\u6A21\u578B\u8BF7\u6C42\u5C31\u751F\u6548\uFF0C\u4E0D\u7528\u91CD\u542F\uFF0C\u4E5F\u4E0D\u7528\u624B\u5DE5\u6539 settings.yaml\u3002 \u53EA\u5BF9 llm-pi-ai \u91CC\u300C\u5DF2\u5B58\u5728\u300D\u7684 opencode \u7CFB\u8DEF\u7531\u751F\u6548\uFF0C\u4E0D\u4F1A\u51ED\u7A7A\u65B0\u5EFA provider\uFF1B \u603B\u5F00\u5173\u5173\u95ED\u6216\u672C\u680F\u76EE\u505C\u7528\u65F6\uFF0C\u5199\u5165\u7684\u5934\u4F1A\u81EA\u52A8\u64A4\u9500\u3002" }),
1442
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1443
+ ToggleRow,
1444
+ {
1445
+ first: true,
1446
+ label: "\u9644\u52A0\u8BF7\u6C42\u5934",
1447
+ desc: "\u6253\u5F00\u5373\u5199\u5165\uFF0C\u5E76\u5728\u6BCF\u6B21\u542F\u52A8\u65F6\u8865\u9F50\uFF1B\u5173\u95ED\u5373\u64A4\u9500",
1448
+ checked: settings.headerEnabled,
1449
+ onChange: (next) => {
1450
+ actions.setField(HEADER_ENABLED_FIELD, next);
1451
+ }
1452
+ }
1453
+ ),
1454
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1455
+ TextFieldRow,
1456
+ {
1457
+ title: "\u5934\u540D",
1458
+ desc: `\u9ED8\u8BA4 ${DEFAULT_HEADER_NAME}\uFF08OpenCode \u5B98\u65B9\u8981\u6C42\u7684\u90A3\u4E00\u4E2A\uFF09`,
1459
+ value: settings.headerName,
1460
+ placeholder: DEFAULT_HEADER_NAME,
1461
+ maxLength: HEADER_NAME_MAX,
1462
+ onChange: (next) => {
1463
+ actions.setField(HEADER_NAME_FIELD, next);
1464
+ }
1465
+ }
1466
+ ),
1467
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1468
+ TextFieldRow,
1469
+ {
1470
+ title: "\u503C",
1471
+ desc: "\u6240\u6709\u5BF9\u8BDD\u5171\u7528\u8FD9\u4E00\u4E2A\u503C\uFF1B\u7559\u7A7A\u65F6\u9996\u6B21\u542F\u7528\u4F1A\u81EA\u52A8\u751F\u6210\u4E00\u4E2A UUID \u5E76\u4FDD\u5B58\u6CBF\u7528",
1472
+ value: settings.headerValue,
1473
+ placeholder: "\u7559\u7A7A = \u81EA\u52A8\u751F\u6210",
1474
+ maxLength: HEADER_VALUE_MAX,
1475
+ onChange: (next) => {
1476
+ actions.setField(HEADER_VALUE_FIELD, next);
1477
+ },
1478
+ action: {
1479
+ label: "\u91CD\u65B0\u751F\u6210",
1480
+ onClick: () => {
1481
+ actions.setField(HEADER_VALUE_FIELD, newSessionId());
1482
+ }
1483
+ }
1484
+ }
1485
+ ),
1486
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1487
+ TextFieldRow,
1488
+ {
1489
+ title: "\u4F5C\u7528\u8DEF\u7531",
1490
+ desc: "\u9017\u53F7\u6216\u7A7A\u683C\u5206\u9694\uFF1B\u7559\u7A7A = \u81EA\u52A8\u5339\u914D opencode \u7CFB\u8DEF\u7531",
1491
+ value: settings.headerRoutes,
1492
+ placeholder: "\u7559\u7A7A = \u81EA\u52A8\uFF08opencode*\uFF09",
1493
+ maxLength: HEADER_VALUE_MAX,
1494
+ onChange: (next) => {
1495
+ actions.setField(HEADER_ROUTES_FIELD, next);
1496
+ }
1497
+ }
1498
+ ),
1499
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { style: hintInfo, children: [
1500
+ "\u72B6\u6001\uFF1A",
1501
+ headerStatusText
1502
+ ] })
1503
+ ] })
1504
+ ] })
1505
+ ] });
1506
+ }
1507
+
1508
+ // src/client.tsx
1509
+ var name = "composer-ux";
1510
+ var inject = ["slots", "settingsScope"];
1511
+ function apply(ctx) {
1512
+ const slots = ctx.slots;
1513
+ const scope = ctx.settingsScope.bind({ namespace: NAMESPACE });
1514
+ const live = (0, import_dsh_client_store.createSnapshotStore)({ ...DEFAULT_SETTINGS });
1515
+ const menu2 = (0, import_dsh_client_store.createSnapshotStore)(null);
1516
+ const sync = () => {
1517
+ const snapshot = scope.getSnapshot();
1518
+ live.set(snapshot.status === "ready" && snapshot.value !== void 0 ? { ...DEFAULT_SETTINGS, ...sanitizeSettings(snapshot.value) } : { ...DEFAULT_SETTINGS });
1519
+ };
1520
+ sync();
1521
+ ctx.effect(() => scope.subscribe(sync), "composer-ux: settings sync");
1522
+ const setField = (field, value) => {
1523
+ void scope.set(field, value).catch((error) => {
1524
+ console.error("[composer-ux] settings write failed", error);
1525
+ });
1526
+ };
1527
+ const clearField = (field) => {
1528
+ void scope.unset(field).catch((error) => {
1529
+ console.error("[composer-ux] settings clear failed", error);
1530
+ });
1531
+ };
1532
+ const resetAll = () => {
1533
+ void scope.mutate(
1534
+ [
1535
+ ENABLED_FIELD,
1536
+ SEND_KEY_FIELD,
1537
+ NEWLINE_KEY_FIELD,
1538
+ ...MENU_FIELDS,
1539
+ MENU_NATIVE_FIELD,
1540
+ PANEL_SCROLL_FIELD,
1541
+ PANEL_RESIZE_FIELD,
1542
+ PANEL_WIDTH_FIELD,
1543
+ PANEL_HEIGHT_FIELD,
1544
+ HEADER_ENABLED_FIELD,
1545
+ HEADER_NAME_FIELD,
1546
+ HEADER_VALUE_FIELD,
1547
+ HEADER_ROUTES_FIELD
1548
+ ].map((field) => ({ op: "unset", path: [field] }))
1549
+ ).catch((error) => {
1550
+ console.error("[composer-ux] settings reset failed", error);
1551
+ });
1552
+ };
1553
+ slots.inject("settings.section", () => slots.register({
1554
+ name: "settings.section",
1555
+ id: "composer-ux",
1556
+ order: 40,
1557
+ label: "\u8F93\u5165\u4F53\u9A8C",
1558
+ inject: () => ({
1559
+ hooks: { live },
1560
+ actions: { setField, clearField, resetAll }
1561
+ })
1562
+ }, SettingsSection));
1563
+ slots.inject("shell.overlay", () => slots.register({
1564
+ name: "shell.overlay",
1565
+ id: "composer-ux-menu",
1566
+ inject: () => ({
1567
+ hooks: { menu: menu2, live },
1568
+ actions: {
1569
+ run: runMenuAction,
1570
+ close: () => {
1571
+ menu2.set(null);
1572
+ },
1573
+ note: (message) => {
1574
+ const current = menu2.getSnapshot();
1575
+ if (current === null) return;
1576
+ menu2.set({ ...current, note: message });
1577
+ }
1578
+ }
1579
+ })
1580
+ }, ContextMenuHost));
1581
+ slots.inject("shell.overlay", () => slots.register({
1582
+ name: "shell.overlay",
1583
+ id: "composer-ux-panel-resize",
1584
+ inject: () => ({ hooks: { live }, actions: { setField } })
1585
+ }, PanelResizeHandles));
1586
+ ctx.effect(() => installSettingsCardStyle(), "composer-ux: settings card style");
1587
+ ctx.effect(
1588
+ () => installPanelStyle(
1589
+ () => {
1590
+ const settings = live.getSnapshot();
1591
+ return settings.enabled && settings.panelScroll;
1592
+ },
1593
+ (listener) => live.subscribe(listener)
1594
+ ),
1595
+ "composer-ux: panel style"
1596
+ );
1597
+ ctx.effect(() => {
1598
+ let dispose = null;
1599
+ const syncInterceptors = () => {
1600
+ const enabled = live.getSnapshot().enabled;
1601
+ if (enabled && dispose === null) {
1602
+ dispose = installInterceptors({
1603
+ settings: () => live.getSnapshot(),
1604
+ setMenu: (state) => {
1605
+ menu2.set(state);
1606
+ },
1607
+ menuOpen: () => menu2.getSnapshot() !== null
1608
+ });
1609
+ } else if (!enabled) {
1610
+ dispose?.();
1611
+ dispose = null;
1612
+ menu2.set(null);
1613
+ }
1614
+ if (live.getSnapshot().menuNative) menu2.set(null);
1615
+ };
1616
+ syncInterceptors();
1617
+ const unsubscribe = live.subscribe(syncInterceptors);
1618
+ return () => {
1619
+ unsubscribe();
1620
+ dispose?.();
1621
+ dispose = null;
1622
+ };
1623
+ }, "composer-ux: input interceptors");
1624
+ }
1625
+ return module.exports; } });
1626
+ //# sourceMappingURL=client.js.map