dsh-composer-ux 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -56,13 +56,52 @@ var MENU_FIELDS = [
56
56
  "menuDelete",
57
57
  "menuSelectAll"
58
58
  ];
59
+ var MENU_MODE_FIELD = "menuMode";
59
60
  var MENU_NATIVE_FIELD = "menuNative";
61
+ var MENU_MODES = [
62
+ {
63
+ id: "official",
64
+ label: "\u5B98\u65B9",
65
+ hint: "\u672C\u63D2\u4EF6\u5B8C\u5168\u4E0D\u4ECB\u5165\uFF1ADSH \u4E0E\u5176\u5B83\u63D2\u4EF6\u81EA\u5DF1\u7684\u53F3\u952E\u5904\u7406\u539F\u6837\u751F\u6548\uFF08DSH \u5B98\u65B9\u8F93\u5165\u6846\u672C\u8EAB\u6CA1\u6709\u53F3\u952E\u83DC\u5355\uFF0C\u901A\u5E38\u770B\u5230\u7684\u5C31\u662F\u6D4F\u89C8\u5668\u7684\u83DC\u5355\uFF09"
66
+ },
67
+ {
68
+ id: "browser",
69
+ label: "\u6D4F\u89C8\u5668",
70
+ hint: "\u56FA\u5B9A\u4F7F\u7528\u6D4F\u89C8\u5668\u81EA\u5E26\u7684\u83DC\u5355\uFF0C\u5E76\u5728\u672C\u63D2\u4EF6\u8FD9\u4E00\u5C42\u6321\u4F4F\u5176\u5B83\u63D2\u4EF6\u7684\u83DC\u5355\uFF1B\u7C98\u8D34\u514D\u6388\u6743\u3001\u96F6\u914D\u7F6E"
71
+ },
72
+ {
73
+ id: "custom",
74
+ label: "\u81EA\u5B9A\u4E49",
75
+ hint: "\u4F7F\u7528\u672C\u63D2\u4EF6\u7684\u56FA\u5B9A\u6837\u5F0F\u83DC\u5355\uFF1B\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"
76
+ }
77
+ ];
78
+ function menuModeFrom(storedMode, legacyNative) {
79
+ if (storedMode === "official" || storedMode === "browser" || storedMode === "custom") return storedMode;
80
+ if (legacyNative === true) return "browser";
81
+ if (legacyNative === false) return "custom";
82
+ return DEFAULT_SETTINGS.menuMode;
83
+ }
60
84
  var QUICK_PROMPTS_FIELD = "quickPrompts";
61
85
  var OPTIMIZER_TIER_FIELD = "optimizerTier";
62
86
  var QUICK_PROMPT_MAX = 60;
87
+ var QUICK_CATEGORY_MAX = 20;
88
+ var QUICK_CATEGORY_NAME_MAX = 40;
63
89
  var QUICK_LABEL_MAX = 40;
64
- var QUICK_TEXT_MAX = 4e3;
90
+ var QUICK_TEXT_MAX = 2e5;
91
+ var QUICK_PROMPTS_API_PATH = "/composer-ux/prompts";
92
+ var QUICK_BOOK_VERSION = 2;
93
+ var DEFAULT_CATEGORY_NAME = "\u9ED8\u8BA4";
65
94
  var OPTIMIZER_API_PATH = "/composer-ux/optimize";
95
+ var INSERT_MODES = [
96
+ { id: "never", label: "\u5173", hint: "\u70B9\u6761\u76EE\u53EA\u63D2\u5165\u8F93\u5165\u6846\uFF0C\u53D1\u9001\u65F6\u4E0D\u9644\u52A0" },
97
+ { id: "always", label: "\u6BCF\u6B21", hint: "\u6BCF\u6B21\u53D1\u9001\u90FD\u628A\u8FD9\u6761\u9644\u52A0\u5230\u6D88\u606F\u672B\u5C3E" },
98
+ { id: "first", label: "\u4EC5\u9996\u6B21", hint: "\u53EA\u5728\u5F53\u524D\u4F1A\u8BDD\u7684\u7B2C\u4E00\u6761\u6D88\u606F\u4E0A\u9644\u52A0" }
99
+ ];
100
+ function insertModeOf(prompt) {
101
+ if (prompt.always) return "always";
102
+ if (prompt.firstOnly) return "first";
103
+ return "never";
104
+ }
66
105
  var OPTIMIZER_TIERS = [
67
106
  { id: "basic", label: "\u666E\u901A", hint: "\u53EA\u505A\u8BED\u8A00\u5C42\u4FEE\u590D\uFF1A\u75C5\u53E5\u3001\u9519\u522B\u5B57\u3001\u6307\u4EE3\u4E0E\u542B\u7CCA\u8BCD\uFF0C\u4E0D\u65B0\u589E\u4EFB\u4F55\u9700\u6C42\uFF0C\u7BC7\u5E45\u4E0E\u539F\u6587\u76F8\u5F53\u3002" },
68
107
  { id: "advanced", label: "\u9AD8\u7EA7", hint: "\u5728\u4E0D\u52A8\u76EE\u6807\u7684\u524D\u63D0\u4E0B\uFF0C\u628A\u300C\u4F60\u663E\u7136\u60F3\u8981\u3001\u4F46\u6CA1\u8BF4\u51FA\u53E3\u300D\u7684\u5FC5\u8981\u8981\u6C42\u8865\u6210\u5BF9 AI \u7684\u8981\u6C42\uFF0C\u8BA9\u5B83\u4E00\u6B21\u505A\u5BF9\u3002" },
@@ -70,19 +109,113 @@ var OPTIMIZER_TIERS = [
70
109
  ];
71
110
  var DEFAULT_OPTIMIZER_TIER = "advanced";
72
111
  var DEFAULT_QUICK_PROMPTS = [
73
- { id: "builtin-1", label: "\u4E00\u95EE\u4E00\u7B54", prompt: "\u4F60\u4E0D\u61C2\u7684\u5C31\u95EE\u6211\uFF0C\u4E00\u95EE\u4E00\u7B54\uFF1B\u540C\u65F6\u8BF4\u6E05\u695A\u4F60\u4E3A\u4EC0\u4E48\u8981\u95EE\u8BE5\u95EE\u9898\uFF1B\u76F4\u5230\u4F60\u5BF9\u6211\u7684\u76EE\u6807\u6709\u660E\u786E\u8BA4\u77E5\u540E\u518D\u5F00\u59CB\u5E72\u6D3B\u3002", always: false },
74
- { id: "builtin-2", label: "\u4EA4\u63A5\u6587\u6863", prompt: "\u628A\u8FD9\u6B21\u4EFB\u52A1\u3001\u5DF2\u5B8C\u6210\u5185\u5BB9\u3001\u5F53\u524D\u5361\u70B9\u3001\u4E0B\u4E00\u6B65\u8BA1\u5212\u3001\u8E29\u8FC7\u7684\u5751\uFF0C\u6574\u7406\u6210\u4E00\u4EFD\u4EA4\u63A5\u6587\u6863\uFF0C\u5199\u7ED9\u65B0\u4F1A\u8BDD\u770B\u3002", always: false },
75
- { id: "builtin-3", label: "\u4EC5\u8BF4\u660E\u539F\u56E0", prompt: "\u4EC5\u8BF4\u660E\u539F\u56E0\uFF0C\u4E0D\u8981\u505A\u5176\u4ED6\u52A8\u4F5C\u3002", always: false },
76
- { id: "builtin-4", label: "\u5206\u6790\u540E\u76F4\u63A5\u5E72", prompt: "\u5206\u6790\u539F\u56E0\uFF0C\u7136\u540E\u76F4\u63A5\u5F00\u59CB\u5E72\u6D3B\uFF0C\u4E0D\u9700\u8981\u8FC7\u95EE\u6211\u3002", always: false },
77
- { id: "builtin-5", label: "\u63D0\u4EA4\u4EE3\u7801", prompt: "\u8BF7\u5E2E\u6211\u63D0\u4EA4\u4EE3\u7801\uFF1A\u68C0\u67E5\u5F53\u524D git \u53D8\u66F4\uFF0C\u751F\u6210\u89C4\u8303\u7684 commit message \u5E76\u6267\u884C\u63D0\u4EA4\u3002", always: false },
78
- { id: "builtin-6", label: "\u7ED9\u65B9\u6848", prompt: "\u8BF7\u9488\u5BF9\u4E0A\u9762\u7684\u95EE\u9898\u7ED9\u51FA\u4E00\u4E2A\u5B8C\u6574\u65B9\u6848\uFF0C\u5305\u62EC\u601D\u8DEF\u3001\u6B65\u9AA4\u3001\u6CE8\u610F\u4E8B\u9879\u548C\u98CE\u9669\u3002", always: false },
79
- { id: "builtin-7", label: "\u89E3\u91CA\u4EE3\u7801", prompt: "\u8BF7\u89E3\u91CA\u8FD9\u6BB5\u4EE3\u7801\u7684\u4F5C\u7528\u548C\u5B9E\u73B0\u601D\u8DEF\u3002", always: false },
80
- { id: "builtin-8", label: "\u5199\u6D4B\u8BD5", prompt: "\u8BF7\u4E3A\u4E0B\u9762\u7684\u4EE3\u7801\u7F16\u5199\u5355\u5143\u6D4B\u8BD5\u3002", always: false },
81
- { id: "builtin-9", label: "\u4EE3\u7801\u5BA1\u67E5", prompt: "\u8BF7\u5BF9\u4E0B\u9762\u7684\u4EE3\u7801\u8FDB\u884C\u4EE3\u7801\u5BA1\u67E5\uFF0C\u6307\u51FA\u95EE\u9898\u5E76\u7ED9\u51FA\u6539\u8FDB\u5EFA\u8BAE\u3002", always: false }
112
+ { id: "builtin-1", label: "\u4E00\u95EE\u4E00\u7B54", prompt: "\u4F60\u4E0D\u61C2\u7684\u5C31\u95EE\u6211\uFF0C\u4E00\u95EE\u4E00\u7B54\uFF1B\u540C\u65F6\u8BF4\u6E05\u695A\u4F60\u4E3A\u4EC0\u4E48\u8981\u95EE\u8BE5\u95EE\u9898\uFF1B\u76F4\u5230\u4F60\u5BF9\u6211\u7684\u76EE\u6807\u6709\u660E\u786E\u8BA4\u77E5\u540E\u518D\u5F00\u59CB\u5E72\u6D3B\u3002", always: false, firstOnly: false },
113
+ { id: "builtin-2", label: "\u4EA4\u63A5\u6587\u6863", prompt: "\u628A\u8FD9\u6B21\u4EFB\u52A1\u3001\u5DF2\u5B8C\u6210\u5185\u5BB9\u3001\u5F53\u524D\u5361\u70B9\u3001\u4E0B\u4E00\u6B65\u8BA1\u5212\u3001\u8E29\u8FC7\u7684\u5751\uFF0C\u6574\u7406\u6210\u4E00\u4EFD\u4EA4\u63A5\u6587\u6863\uFF0C\u5199\u7ED9\u65B0\u4F1A\u8BDD\u770B\u3002", always: false, firstOnly: false },
114
+ { id: "builtin-3", label: "\u4EC5\u8BF4\u660E\u539F\u56E0", prompt: "\u4EC5\u8BF4\u660E\u539F\u56E0\uFF0C\u4E0D\u8981\u505A\u5176\u4ED6\u52A8\u4F5C\u3002", always: false, firstOnly: false },
115
+ { id: "builtin-4", label: "\u5206\u6790\u540E\u76F4\u63A5\u5E72", prompt: "\u5206\u6790\u539F\u56E0\uFF0C\u7136\u540E\u76F4\u63A5\u5F00\u59CB\u5E72\u6D3B\uFF0C\u4E0D\u9700\u8981\u8FC7\u95EE\u6211\u3002", always: false, firstOnly: false },
116
+ { id: "builtin-5", label: "\u63D0\u4EA4\u4EE3\u7801", prompt: "\u8BF7\u5E2E\u6211\u63D0\u4EA4\u4EE3\u7801\uFF1A\u68C0\u67E5\u5F53\u524D git \u53D8\u66F4\uFF0C\u751F\u6210\u89C4\u8303\u7684 commit message \u5E76\u6267\u884C\u63D0\u4EA4\u3002", always: false, firstOnly: false },
117
+ { id: "builtin-6", label: "\u7ED9\u65B9\u6848", prompt: "\u8BF7\u9488\u5BF9\u4E0A\u9762\u7684\u95EE\u9898\u7ED9\u51FA\u4E00\u4E2A\u5B8C\u6574\u65B9\u6848\uFF0C\u5305\u62EC\u601D\u8DEF\u3001\u6B65\u9AA4\u3001\u6CE8\u610F\u4E8B\u9879\u548C\u98CE\u9669\u3002", always: false, firstOnly: false },
118
+ { id: "builtin-7", label: "\u89E3\u91CA\u4EE3\u7801", prompt: "\u8BF7\u89E3\u91CA\u8FD9\u6BB5\u4EE3\u7801\u7684\u4F5C\u7528\u548C\u5B9E\u73B0\u601D\u8DEF\u3002", always: false, firstOnly: false },
119
+ { id: "builtin-8", label: "\u5199\u6D4B\u8BD5", prompt: "\u8BF7\u4E3A\u4E0B\u9762\u7684\u4EE3\u7801\u7F16\u5199\u5355\u5143\u6D4B\u8BD5\u3002", always: false, firstOnly: false },
120
+ { id: "builtin-9", label: "\u4EE3\u7801\u5BA1\u67E5", prompt: "\u8BF7\u5BF9\u4E0B\u9762\u7684\u4EE3\u7801\u8FDB\u884C\u4EE3\u7801\u5BA1\u67E5\uFF0C\u6307\u51FA\u95EE\u9898\u5E76\u7ED9\u51FA\u6539\u8FDB\u5EFA\u8BAE\u3002", always: false, firstOnly: false }
82
121
  ];
83
122
  function newQuickPromptId() {
84
123
  return `qp-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
85
124
  }
125
+ function newQuickCategoryId() {
126
+ return `qc-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
127
+ }
128
+ function defaultQuickBook() {
129
+ return {
130
+ version: QUICK_BOOK_VERSION,
131
+ categories: [{ id: "cat-default", name: DEFAULT_CATEGORY_NAME, prompts: DEFAULT_QUICK_PROMPTS }]
132
+ };
133
+ }
134
+ function asPromptRow(value) {
135
+ if (typeof value !== "object" || value === null) return void 0;
136
+ const row2 = value;
137
+ const either = (first, second) => {
138
+ const picked = typeof first === "string" && first !== "" ? first : second;
139
+ return typeof picked === "string" ? picked : "";
140
+ };
141
+ const prompt = either(row2.prompt, row2.text).slice(0, QUICK_TEXT_MAX).trim();
142
+ if (prompt === "") return void 0;
143
+ const label = either(row2.label, row2.title).slice(0, QUICK_LABEL_MAX).trim();
144
+ const id = typeof row2.id === "string" && row2.id !== "" ? row2.id.slice(0, 64) : newQuickPromptId();
145
+ const order = typeof row2.order === "number" && Number.isFinite(row2.order) ? Math.max(1, Math.round(row2.order)) : void 0;
146
+ return {
147
+ prompt: {
148
+ id,
149
+ label: label === "" ? prompt.slice(0, 12) : label,
150
+ prompt,
151
+ always: row2.always === true || row2.autoSend === true,
152
+ firstOnly: row2.firstOnly === true || row2.autoSendFirst === true
153
+ },
154
+ order
155
+ };
156
+ }
157
+ function toPromptList(values) {
158
+ const rows = [];
159
+ for (const value of values) {
160
+ const row2 = asPromptRow(value);
161
+ if (row2 !== void 0) rows.push({ ...row2, index: rows.length });
162
+ }
163
+ rows.sort((a, b) => {
164
+ const left = a.order ?? Number.MAX_SAFE_INTEGER;
165
+ const right = b.order ?? Number.MAX_SAFE_INTEGER;
166
+ return left === right ? a.index - b.index : left - right;
167
+ });
168
+ const seen = /* @__PURE__ */ new Set();
169
+ const out = [];
170
+ for (const row2 of rows) {
171
+ if (out.length >= QUICK_PROMPT_MAX) break;
172
+ if (seen.has(row2.prompt.id)) continue;
173
+ seen.add(row2.prompt.id);
174
+ out.push(row2.prompt);
175
+ }
176
+ return out;
177
+ }
178
+ function sanitizeBook(value) {
179
+ if (typeof value !== "object" || value === null) return void 0;
180
+ const source = value;
181
+ const flat = Array.isArray(value) ? value : Array.isArray(source.prompts) ? source.prompts : void 0;
182
+ if (flat !== void 0) {
183
+ const prompts = toPromptList(flat);
184
+ return {
185
+ version: QUICK_BOOK_VERSION,
186
+ categories: prompts.length === 0 ? [] : [{ id: "cat-default", name: DEFAULT_CATEGORY_NAME, prompts }]
187
+ };
188
+ }
189
+ if (!Array.isArray(source.categories)) return void 0;
190
+ const seen = /* @__PURE__ */ new Set();
191
+ const categories = [];
192
+ for (const raw of source.categories) {
193
+ if (categories.length >= QUICK_CATEGORY_MAX) break;
194
+ if (typeof raw !== "object" || raw === null) continue;
195
+ const row2 = raw;
196
+ const prompts = toPromptList(Array.isArray(row2.prompts) ? row2.prompts : []);
197
+ const wanted = typeof row2.id === "string" && row2.id !== "" ? row2.id.slice(0, 64) : newQuickCategoryId();
198
+ const id = seen.has(wanted) ? newQuickCategoryId() : wanted;
199
+ seen.add(id);
200
+ const name2 = (typeof row2.name === "string" ? row2.name : "").slice(0, QUICK_CATEGORY_NAME_MAX).trim();
201
+ categories.push({ id, name: name2 === "" ? DEFAULT_CATEGORY_NAME : name2, prompts });
202
+ }
203
+ return { version: QUICK_BOOK_VERSION, categories };
204
+ }
205
+ function flattenQuickPrompts(book) {
206
+ return book.categories.flatMap((category) => category.prompts);
207
+ }
208
+ function alwaysQuickPrompts(book) {
209
+ return flattenQuickPrompts(book).filter((prompt) => insertModeOf(prompt) === "always");
210
+ }
211
+ function firstOnlyQuickPrompts(book) {
212
+ return flattenQuickPrompts(book).filter((prompt) => insertModeOf(prompt) === "first");
213
+ }
214
+ function appendBatchForSend(book, blank) {
215
+ const every = alwaysQuickPrompts(book);
216
+ if (!blank) return every;
217
+ return [...every, ...firstOnlyQuickPrompts(book)];
218
+ }
86
219
  var HEADER_ENABLED_FIELD = "headerEnabled";
87
220
  var HEADER_NAME_FIELD = "headerName";
88
221
  var HEADER_VALUE_FIELD = "headerValue";
@@ -104,7 +237,7 @@ var DEFAULT_SETTINGS = {
104
237
  menuPaste: true,
105
238
  menuDelete: true,
106
239
  menuSelectAll: true,
107
- menuNative: false,
240
+ menuMode: "official",
108
241
  panelScroll: true,
109
242
  panelResize: true,
110
243
  headerEnabled: false,
@@ -170,7 +303,13 @@ function sanitizeSettings(value) {
170
303
  const id = typeof row2.id === "string" && row2.id !== "" ? row2.id.slice(0, 64) : newQuickPromptId();
171
304
  if (seen.has(id)) continue;
172
305
  seen.add(id);
173
- out.push({ id, label: label === "" ? prompt.slice(0, 12) : label, prompt, always: row2.always === true });
306
+ out.push({
307
+ id,
308
+ label: label === "" ? prompt.slice(0, 12) : label,
309
+ prompt,
310
+ always: row2.always === true,
311
+ firstOnly: row2.firstOnly === true
312
+ });
174
313
  }
175
314
  return out;
176
315
  };
@@ -189,7 +328,7 @@ function sanitizeSettings(value) {
189
328
  menuPaste: asBool("menuPaste"),
190
329
  menuDelete: asBool("menuDelete"),
191
330
  menuSelectAll: asBool("menuSelectAll"),
192
- menuNative: asBool(MENU_NATIVE_FIELD),
331
+ menuMode: menuModeFrom(source[MENU_MODE_FIELD], source[MENU_NATIVE_FIELD]),
193
332
  panelScroll: asBool(PANEL_SCROLL_FIELD),
194
333
  panelResize: asBool(PANEL_RESIZE_FIELD),
195
334
  panelWidth: asSize(PANEL_WIDTH_FIELD),
@@ -208,6 +347,170 @@ function sanitizeSettings(value) {
208
347
  };
209
348
  }
210
349
 
350
+ // src/client/prompt-book.ts
351
+ async function requestBook(method, body) {
352
+ try {
353
+ const response = await fetch(QUICK_PROMPTS_API_PATH, {
354
+ method,
355
+ headers: body === void 0 ? void 0 : { "content-type": "application/json" },
356
+ body: body === void 0 ? void 0 : JSON.stringify(body)
357
+ });
358
+ const text = await response.text();
359
+ let parsed;
360
+ try {
361
+ parsed = JSON.parse(text);
362
+ } catch {
363
+ return { ok: false, error: `\u670D\u52A1\u7AEF\u8FD4\u56DE\u7684\u4E0D\u662F JSON\uFF08HTTP ${String(response.status)}\uFF09` };
364
+ }
365
+ const row2 = typeof parsed === "object" && parsed !== null ? parsed : {};
366
+ const book = sanitizeBook(row2.book);
367
+ const textOf = (value) => typeof value === "string" && value !== "" ? value : void 0;
368
+ return {
369
+ ok: row2.ok === true,
370
+ ...book === void 0 ? {} : { book },
371
+ ...textOf(row2.error) === void 0 ? {} : { error: textOf(row2.error) },
372
+ ...textOf(row2.file) === void 0 ? {} : { file: textOf(row2.file) },
373
+ ...textOf(row2.quarantined) === void 0 ? {} : { quarantined: textOf(row2.quarantined) }
374
+ };
375
+ } catch (error) {
376
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
377
+ }
378
+ }
379
+ function loadPromptBook() {
380
+ return requestBook("GET");
381
+ }
382
+ function savePromptBook(book) {
383
+ return requestBook("POST", { book });
384
+ }
385
+ function categoryOf(book, categoryId) {
386
+ return book.categories.find((category) => category.id === categoryId);
387
+ }
388
+ function findPrompt(book, promptId) {
389
+ for (const category of book.categories) {
390
+ const prompt = category.prompts.find((item) => item.id === promptId);
391
+ if (prompt !== void 0) return { category, prompt };
392
+ }
393
+ return void 0;
394
+ }
395
+ function bookCounts(book) {
396
+ const prompts = book.categories.flatMap((category) => category.prompts);
397
+ return {
398
+ categories: book.categories.length,
399
+ prompts: prompts.length,
400
+ always: prompts.filter((prompt) => insertModeOf(prompt) === "always").length,
401
+ first: prompts.filter((prompt) => insertModeOf(prompt) === "first").length
402
+ };
403
+ }
404
+ function mapCategory(book, categoryId, edit) {
405
+ return {
406
+ version: book.version,
407
+ categories: book.categories.map((category) => category.id === categoryId ? edit(category) : category)
408
+ };
409
+ }
410
+ function withCategoryAdded(book, name2) {
411
+ if (book.categories.length >= QUICK_CATEGORY_MAX) return book;
412
+ const clean = name2.trim().slice(0, QUICK_CATEGORY_NAME_MAX);
413
+ return {
414
+ version: book.version,
415
+ categories: [...book.categories, { id: newQuickCategoryId(), name: clean === "" ? DEFAULT_CATEGORY_NAME : clean, prompts: [] }]
416
+ };
417
+ }
418
+ function withCategoryRenamed(book, categoryId, name2) {
419
+ const clean = name2.trim().slice(0, QUICK_CATEGORY_NAME_MAX);
420
+ return mapCategory(book, categoryId, (category) => ({ ...category, name: clean === "" ? DEFAULT_CATEGORY_NAME : clean }));
421
+ }
422
+ function withCategoryRemoved(book, categoryId) {
423
+ return { version: book.version, categories: book.categories.filter((category) => category.id !== categoryId) };
424
+ }
425
+ function withCategoryMoved(book, categoryId, delta) {
426
+ const index = book.categories.findIndex((category) => category.id === categoryId);
427
+ const next = index + delta;
428
+ if (index < 0 || next < 0 || next >= book.categories.length) return book;
429
+ const categories = [...book.categories];
430
+ const [moved] = categories.splice(index, 1);
431
+ categories.splice(next, 0, moved);
432
+ return { version: book.version, categories };
433
+ }
434
+ function withPromptAdded(book, categoryId) {
435
+ return mapCategory(book, categoryId, (category) => {
436
+ if (category.prompts.length >= QUICK_PROMPT_MAX) return category;
437
+ return {
438
+ ...category,
439
+ prompts: [...category.prompts, {
440
+ id: newQuickPromptId(),
441
+ label: "\u65B0\u6307\u4EE4",
442
+ prompt: "\uFF08\u5F85\u586B\u5199\uFF09",
443
+ always: false,
444
+ firstOnly: false
445
+ }]
446
+ };
447
+ });
448
+ }
449
+ function withPromptPatched(book, categoryId, promptId, patch) {
450
+ return mapCategory(book, categoryId, (category) => ({
451
+ ...category,
452
+ prompts: category.prompts.map((prompt) => {
453
+ if (prompt.id !== promptId) return prompt;
454
+ const label = patch.label === void 0 ? prompt.label : patch.label.slice(0, QUICK_LABEL_MAX);
455
+ const text = patch.prompt === void 0 ? prompt.prompt : patch.prompt.slice(0, QUICK_TEXT_MAX);
456
+ const always = patch.always === void 0 ? prompt.always : patch.always;
457
+ const firstOnly = patch.firstOnly === void 0 ? prompt.firstOnly : patch.firstOnly;
458
+ return { id: prompt.id, label, prompt: text, always, firstOnly };
459
+ })
460
+ }));
461
+ }
462
+ function withInsertMode(book, promptId, mode) {
463
+ const hit = findPrompt(book, promptId);
464
+ if (hit === void 0) return book;
465
+ if (insertModeOf(hit.prompt) === mode) return book;
466
+ return withPromptPatched(book, hit.category.id, promptId, {
467
+ always: mode === "always",
468
+ firstOnly: mode === "first"
469
+ });
470
+ }
471
+ function withPromptRemoved(book, categoryId, promptId) {
472
+ return mapCategory(book, categoryId, (category) => ({
473
+ ...category,
474
+ prompts: category.prompts.filter((prompt) => prompt.id !== promptId)
475
+ }));
476
+ }
477
+ function withPromptMoved(book, categoryId, promptId, delta) {
478
+ const category = categoryOf(book, categoryId);
479
+ if (category === void 0) return book;
480
+ const index = category.prompts.findIndex((prompt) => prompt.id === promptId);
481
+ const next = index + delta;
482
+ if (index < 0 || next < 0 || next >= category.prompts.length) return book;
483
+ const prompts = [...category.prompts];
484
+ const [moved] = prompts.splice(index, 1);
485
+ prompts.splice(next, 0, moved);
486
+ return mapCategory(book, categoryId, (target) => ({ ...target, prompts }));
487
+ }
488
+ function promptsElsewhere(book, categoryId) {
489
+ return book.categories.filter((category) => category.id !== categoryId).flatMap((category) => category.prompts.map((prompt) => ({
490
+ categoryId: category.id,
491
+ categoryName: category.name,
492
+ prompt
493
+ })));
494
+ }
495
+ function withPromptMovedToCategory(book, promptId, toCategoryId) {
496
+ const hit = findPrompt(book, promptId);
497
+ if (hit === void 0 || hit.category.id === toCategoryId) return book;
498
+ const target = categoryOf(book, toCategoryId);
499
+ if (target === void 0 || target.prompts.length >= QUICK_PROMPT_MAX) return book;
500
+ return {
501
+ version: book.version,
502
+ categories: book.categories.map((category) => {
503
+ if (category.id === hit.category.id) {
504
+ return { ...category, prompts: category.prompts.filter((prompt) => prompt.id !== promptId) };
505
+ }
506
+ if (category.id === toCategoryId) {
507
+ return { ...category, prompts: [...category.prompts, hit.prompt] };
508
+ }
509
+ return category;
510
+ })
511
+ };
512
+ }
513
+
211
514
  // src/client/chords.ts
212
515
  var MODIFIER_KEYS = /* @__PURE__ */ new Set(["Control", "Alt", "Shift", "Meta", "AltGraph"]);
213
516
  var CODE_LABELS = {
@@ -311,17 +614,22 @@ function displayChord(chord) {
311
614
  // src/client/quick-commands.ts
312
615
  var COMPOSER_SELECTOR = "[data-composer-input]";
313
616
  var CARD_SELECTOR = "[data-composer-card]";
314
- var state = { actions: null, draft: "", sessionId: "" };
617
+ var state = { actions: null, draft: "", sessionId: "", blank: false };
315
618
  function publishInputBridge(next) {
316
619
  state.actions = next.actions;
317
620
  state.draft = next.draft;
318
621
  state.sessionId = next.sessionId;
622
+ state.blank = next.blank;
319
623
  }
320
624
  function releaseInputBridge(sessionId) {
321
625
  if (state.sessionId !== sessionId) return;
322
626
  state.actions = null;
323
627
  state.draft = "";
324
628
  state.sessionId = "";
629
+ state.blank = false;
630
+ }
631
+ function currentBlankSession() {
632
+ return state.blank === true;
325
633
  }
326
634
  function draftFromDom() {
327
635
  const el = document.querySelector(COMPOSER_SELECTOR);
@@ -331,24 +639,21 @@ function draftFromDom() {
331
639
  function currentDraft() {
332
640
  return state.actions === null ? draftFromDom() : state.draft;
333
641
  }
334
- function alwaysPrompts(prompts) {
335
- return prompts.filter((item) => item.always === true && item.prompt.trim() !== "");
336
- }
337
- function withAlwaysPrompts(draft, prompts) {
338
- const picked = alwaysPrompts(prompts);
642
+ function withPromptsAppended(draft, prompts) {
643
+ const picked = prompts.filter((item) => item.prompt.trim() !== "");
339
644
  if (picked.length === 0) return null;
340
- const base = draft.replace(/\s+$/, "");
341
- if (base === "") return null;
645
+ const base2 = draft.replace(/\s+$/, "");
646
+ if (base2 === "") return null;
342
647
  const suffix = picked.map((item) => item.prompt.trim()).join("\n\n");
343
- if (base.endsWith(suffix)) return null;
344
- return `${base}
648
+ if (base2.endsWith(suffix)) return null;
649
+ return `${base2}
345
650
 
346
651
  ${suffix}`;
347
652
  }
348
- function applyAlwaysPrompts(prompts) {
653
+ function applyPromptsForSend(prompts) {
349
654
  const actions = state.actions;
350
655
  if (actions === null) return false;
351
- const next = withAlwaysPrompts(currentDraft(), prompts);
656
+ const next = withPromptsAppended(currentDraft(), prompts);
352
657
  if (next === null) return false;
353
658
  actions.setDraft(next);
354
659
  return true;
@@ -472,7 +777,7 @@ function installInterceptors(deps) {
472
777
  event.stopPropagation();
473
778
  if (event.repeat) return;
474
779
  if (gesture === "send") {
475
- applyAlwaysPrompts(settings.quickPrompts);
780
+ applyPromptsForSend(deps.promptsForSend());
476
781
  dispatchKey(root, chordToInit("Enter"));
477
782
  } else if (gesture === "newline") {
478
783
  dispatchKey(root, chordToInit("Shift+Enter"));
@@ -483,13 +788,16 @@ function installInterceptors(deps) {
483
788
  const onContextMenu = (event) => {
484
789
  const root = findComposerRoot(event.target);
485
790
  if (root === null) return;
486
- if (deps.settings().menuNative) {
791
+ const mode = deps.settings().menuMode;
792
+ if (mode === "official") return;
793
+ if (mode === "browser") {
487
794
  if (deps.menuOpen()) deps.setMenu(null);
488
795
  event.stopImmediatePropagation();
489
796
  event.stopPropagation();
490
797
  return;
491
798
  }
492
799
  event.preventDefault();
800
+ event.stopImmediatePropagation();
493
801
  event.stopPropagation();
494
802
  const selection = window.getSelection();
495
803
  const hasSelection = selection !== null && !selection.isCollapsed && selection.anchorNode !== null && root.contains(selection.anchorNode);
@@ -500,7 +808,7 @@ function installInterceptors(deps) {
500
808
  if (replayingSendClick) return;
501
809
  const button = sendButtonOf(event.target);
502
810
  if (button === null) return;
503
- if (!applyAlwaysPrompts(deps.settings().quickPrompts)) return;
811
+ if (!applyPromptsForSend(deps.promptsForSend())) return;
504
812
  event.preventDefault();
505
813
  event.stopPropagation();
506
814
  replayingSendClick = true;
@@ -569,7 +877,7 @@ function isClipboardTimeout(error) {
569
877
  function isFirefox() {
570
878
  return typeof navigator !== "undefined" && /Firefox\//.test(navigator.userAgent);
571
879
  }
572
- 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";
880
+ var FIREFOX_TIP = "Firefox \u4E0D\u5141\u8BB8\u7F51\u9875\u9759\u9ED8\u8BFB\u526A\u8D34\u677F\uFF1A\u8BF7\u5728\u5F39\u51FA\u7684\u300C\u7C98\u8D34(P)\u300D\u5C0F\u7A97\u4E0A\u70B9\u4E00\u4E0B\uFF08\u7EA6 1 \u79D2\u540E\u624D\u53EF\u70B9\uFF0C\u8FD9\u662F Firefox \u7684\u5B89\u5168\u673A\u5236\uFF0C\u63D2\u4EF6\u5173\u4E0D\u6389\uFF09\uFF1B\u4E0D\u60F3\u591A\u8FD9\u4E00\u6B65\u5C31\u76F4\u63A5\u6309 Ctrl+V";
573
881
  async function pasteText(root) {
574
882
  const insertText = (text2) => {
575
883
  if (!document.activeElement || !root.contains(document.activeElement)) {
@@ -1093,20 +1401,6 @@ var quickItemPreview = {
1093
1401
  textOverflow: "ellipsis",
1094
1402
  whiteSpace: "nowrap"
1095
1403
  };
1096
- var quickAlwaysLabel = {
1097
- display: "inline-flex",
1098
- alignItems: "center",
1099
- gap: 4,
1100
- flex: "0 0 auto",
1101
- color: "var(--dsw-alias-label-tertiary)",
1102
- fontSize: 11,
1103
- whiteSpace: "nowrap",
1104
- cursor: "pointer"
1105
- };
1106
- var quickAlwaysBox = {
1107
- margin: 0,
1108
- cursor: "pointer"
1109
- };
1110
1404
  var quickPanelFoot = {
1111
1405
  padding: "5px 12px 7px",
1112
1406
  borderTop: "0.5px solid var(--dsw-alias-border-l2)",
@@ -1136,6 +1430,38 @@ var quickEmpty = {
1136
1430
  padding: "14px 10px",
1137
1431
  textAlign: "center"
1138
1432
  };
1433
+ var quickCategoryRow = {
1434
+ display: "flex",
1435
+ alignItems: "center",
1436
+ gap: 4,
1437
+ flexWrap: "wrap"
1438
+ };
1439
+ var quickCategoryTab = {
1440
+ border: "0.5px solid var(--dsw-alias-border-l2)",
1441
+ background: "transparent",
1442
+ color: "var(--dsw-alias-label-tertiary)",
1443
+ fontSize: 11,
1444
+ lineHeight: 1,
1445
+ padding: "4px 8px",
1446
+ borderRadius: 999,
1447
+ cursor: "pointer",
1448
+ whiteSpace: "nowrap",
1449
+ maxWidth: 160,
1450
+ overflow: "hidden",
1451
+ textOverflow: "ellipsis"
1452
+ };
1453
+ var quickCategoryTabActive = {
1454
+ ...quickCategoryTab,
1455
+ border: "0.5px solid var(--dsw-alias-brand-primary)",
1456
+ color: "var(--dsw-alias-label-primary)",
1457
+ fontWeight: 600
1458
+ };
1459
+ var quickCategoryAdd = {
1460
+ ...quickCategoryTab,
1461
+ color: "var(--dsw-alias-label-tertiary)",
1462
+ padding: "4px 7px",
1463
+ cursor: "pointer"
1464
+ };
1139
1465
 
1140
1466
  // src/client/ContextMenuHost.tsx
1141
1467
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -1284,7 +1610,7 @@ function PanelResizeHandles({ useLive, actions }) {
1284
1610
  }));
1285
1611
  const enabled = settings.enabled === true && settings.panelResize === true;
1286
1612
  const [panel, setPanel] = (0, import_react2.useState)(null);
1287
- const [active, setActive] = (0, import_react2.useState)(null);
1613
+ const [active2, setActive] = (0, import_react2.useState)(null);
1288
1614
  const dragRef = (0, import_react2.useRef)(null);
1289
1615
  (0, import_react2.useEffect)(() => {
1290
1616
  if (!enabled) {
@@ -1380,7 +1706,7 @@ function PanelResizeHandles({ useLive, actions }) {
1380
1706
  "div",
1381
1707
  {
1382
1708
  className: edge === "br" ? RESIZE_GRIP_CLASS : RESIZE_EDGE_CLASS,
1383
- "data-active": active === edge ? "true" : void 0,
1709
+ "data-active": active2 === edge ? "true" : void 0,
1384
1710
  title: edge === "br" ? "\u62D6\u52A8\u6539\u9762\u677F\u5927\u5C0F" : void 0,
1385
1711
  style: handleBox(edge),
1386
1712
  onPointerDown: (event) => {
@@ -1404,11 +1730,262 @@ function PanelResizeHandles({ useLive, actions }) {
1404
1730
  }
1405
1731
 
1406
1732
  // src/client/SettingsSection.tsx
1733
+ var import_react4 = require("react");
1734
+
1735
+ // src/client/AddPromptRow.tsx
1407
1736
  var import_react3 = require("react");
1408
1737
  var import_jsx_runtime3 = require("react/jsx-runtime");
1738
+ var rowBase = {
1739
+ display: "flex",
1740
+ alignItems: "center",
1741
+ gap: 6,
1742
+ width: "100%",
1743
+ borderRadius: 8,
1744
+ border: "0.5px dashed var(--dsw-alias-border-l2)",
1745
+ background: "transparent",
1746
+ color: "var(--dsw-alias-label-tertiary)",
1747
+ cursor: "pointer",
1748
+ textAlign: "left"
1749
+ };
1750
+ var choiceBase = {
1751
+ padding: "4px 9px",
1752
+ borderRadius: 999,
1753
+ border: "0.5px solid var(--dsw-alias-border-l2)",
1754
+ background: "transparent",
1755
+ color: "var(--dsw-alias-label-primary)",
1756
+ fontSize: 11,
1757
+ lineHeight: 1.2,
1758
+ cursor: "pointer"
1759
+ };
1760
+ var candidateBase = {
1761
+ display: "flex",
1762
+ alignItems: "center",
1763
+ gap: 6,
1764
+ width: "100%",
1765
+ padding: "4px 6px",
1766
+ borderRadius: 6,
1767
+ border: "none",
1768
+ background: "transparent",
1769
+ cursor: "pointer",
1770
+ textAlign: "left"
1771
+ };
1772
+ function AddPromptRow({
1773
+ book,
1774
+ categoryId,
1775
+ onAdd,
1776
+ onMove,
1777
+ variant = "settings"
1778
+ }) {
1779
+ const [mode, setMode] = (0, import_react3.useState)("closed");
1780
+ const ref = (0, import_react3.useRef)(null);
1781
+ (0, import_react3.useEffect)(() => {
1782
+ setMode("closed");
1783
+ }, [categoryId]);
1784
+ (0, import_react3.useEffect)(() => {
1785
+ if (mode === "closed") return;
1786
+ const onPointerDown = (event) => {
1787
+ if (ref.current !== null && event.target instanceof Node && ref.current.contains(event.target)) return;
1788
+ setMode("closed");
1789
+ };
1790
+ const onKeyDown = (event) => {
1791
+ if (event.key === "Escape") setMode("closed");
1792
+ };
1793
+ document.addEventListener("pointerdown", onPointerDown, true);
1794
+ document.addEventListener("keydown", onKeyDown, true);
1795
+ return () => {
1796
+ document.removeEventListener("pointerdown", onPointerDown, true);
1797
+ document.removeEventListener("keydown", onKeyDown, true);
1798
+ };
1799
+ }, [mode]);
1800
+ const compact = variant === "panel";
1801
+ const candidates = mode === "move" ? promptsElsewhere(book, categoryId) : [];
1802
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { ref, style: { display: "flex", flexDirection: "column", gap: 4 }, children: [
1803
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1804
+ "button",
1805
+ {
1806
+ type: "button",
1807
+ "aria-expanded": mode !== "closed",
1808
+ title: "\u65B0\u5EFA\u4E00\u6761\uFF0C\u6216\u628A\u522B\u7684\u5206\u7C7B\u91CC\u7684\u6761\u76EE\u79FB\u52A8\u5230\u8FD9\u91CC",
1809
+ style: {
1810
+ ...rowBase,
1811
+ padding: compact ? "4px 8px" : "7px 9px",
1812
+ fontSize: compact ? 11 : 12
1813
+ },
1814
+ onMouseDown: (event) => {
1815
+ event.preventDefault();
1816
+ },
1817
+ onClick: () => {
1818
+ setMode(mode === "closed" ? "choose" : "closed");
1819
+ },
1820
+ children: [
1821
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "aria-hidden": true, children: "\uFF0B" }),
1822
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: mode === "closed" ? "\u65B0\u5EFA / \u79FB\u52A8" : "\u6536\u8D77" })
1823
+ ]
1824
+ }
1825
+ ),
1826
+ mode !== "closed" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", gap: 6, flexWrap: "wrap" }, children: [
1827
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1828
+ "button",
1829
+ {
1830
+ type: "button",
1831
+ style: choiceBase,
1832
+ onMouseDown: (event) => {
1833
+ event.preventDefault();
1834
+ },
1835
+ onClick: () => {
1836
+ setMode("closed");
1837
+ onAdd();
1838
+ },
1839
+ children: "\uFF0B \u65B0\u5EFA\u4E00\u6761"
1840
+ }
1841
+ ),
1842
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1843
+ "button",
1844
+ {
1845
+ type: "button",
1846
+ style: mode === "move" ? { ...choiceBase, borderColor: "var(--dsw-alias-brand-primary)" } : choiceBase,
1847
+ onMouseDown: (event) => {
1848
+ event.preventDefault();
1849
+ },
1850
+ onClick: () => {
1851
+ setMode(mode === "move" ? "choose" : "move");
1852
+ },
1853
+ children: "\u2192 \u628A\u522B\u7684\u5206\u7C7B\u7684\u6761\u76EE\u79FB\u52A8\u5230\u8FD9\u91CC"
1854
+ }
1855
+ )
1856
+ ] }),
1857
+ mode === "move" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1858
+ "div",
1859
+ {
1860
+ style: {
1861
+ display: "flex",
1862
+ flexDirection: "column",
1863
+ gap: 2,
1864
+ maxHeight: 190,
1865
+ overflowY: "auto",
1866
+ border: "0.5px solid var(--dsw-alias-border-l2)",
1867
+ borderRadius: 8,
1868
+ padding: 4
1869
+ },
1870
+ children: [
1871
+ candidates.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: "var(--dsw-alias-label-tertiary)", fontSize: 11, padding: "4px 6px" }, children: "\u522B\u7684\u5206\u7C7B\u91CC\u8FD8\u6CA1\u6709\u6761\u76EE\u53EF\u4EE5\u79FB\u52A8\u3002" }),
1872
+ candidates.map((candidate) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1873
+ "button",
1874
+ {
1875
+ type: "button",
1876
+ title: `${candidate.categoryName} \xB7 ${candidate.prompt.prompt}`,
1877
+ style: candidateBase,
1878
+ onMouseDown: (event) => {
1879
+ event.preventDefault();
1880
+ },
1881
+ onClick: () => {
1882
+ setMode("closed");
1883
+ onMove(candidate.prompt.id);
1884
+ },
1885
+ children: [
1886
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: "var(--dsw-alias-label-tertiary)", fontSize: 11, flex: "0 0 auto" }, children: candidate.categoryName }),
1887
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: "var(--dsw-alias-label-tertiary)", flex: "0 0 auto" }, children: "\xB7" }),
1888
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1889
+ "span",
1890
+ {
1891
+ style: {
1892
+ color: "var(--dsw-alias-label-primary)",
1893
+ fontSize: 12,
1894
+ minWidth: 0,
1895
+ flex: "1 1 auto",
1896
+ overflow: "hidden",
1897
+ textOverflow: "ellipsis",
1898
+ whiteSpace: "nowrap"
1899
+ },
1900
+ children: candidate.prompt.label
1901
+ }
1902
+ ),
1903
+ candidate.prompt.always && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: "var(--dsw-alias-label-tertiary)", fontSize: 10, flex: "0 0 auto" }, children: "\u9ED8\u8BA4\u63D2\u5165" })
1904
+ ]
1905
+ },
1906
+ candidate.prompt.id
1907
+ ))
1908
+ ]
1909
+ }
1910
+ )
1911
+ ] });
1912
+ }
1913
+
1914
+ // src/client/PillChoice.tsx
1915
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1916
+ var base = {
1917
+ border: "0.5px solid var(--dsw-alias-border-l2)",
1918
+ background: "transparent",
1919
+ color: "var(--dsw-alias-label-tertiary)",
1920
+ borderRadius: 999,
1921
+ cursor: "pointer",
1922
+ lineHeight: 1,
1923
+ whiteSpace: "nowrap"
1924
+ };
1925
+ var active = {
1926
+ ...base,
1927
+ border: "0.5px solid var(--dsw-alias-brand-primary)",
1928
+ color: "var(--dsw-alias-label-primary)",
1929
+ fontWeight: 600
1930
+ };
1931
+ function PillChoice({
1932
+ items,
1933
+ value,
1934
+ onChange,
1935
+ ariaLabel,
1936
+ compact = false
1937
+ }) {
1938
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1939
+ "div",
1940
+ {
1941
+ role: "group",
1942
+ "aria-label": ariaLabel,
1943
+ style: { display: "inline-flex", alignItems: "center", gap: 2, flex: "0 0 auto" },
1944
+ children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1945
+ "button",
1946
+ {
1947
+ type: "button",
1948
+ title: item.hint,
1949
+ "aria-pressed": value === item.id,
1950
+ style: {
1951
+ ...value === item.id ? active : base,
1952
+ fontSize: compact ? 10 : 11,
1953
+ padding: compact ? "3px 5px" : "4px 7px"
1954
+ },
1955
+ onMouseDown: (event) => {
1956
+ event.preventDefault();
1957
+ },
1958
+ onClick: () => {
1959
+ onChange(item.id);
1960
+ },
1961
+ children: item.label
1962
+ },
1963
+ item.id
1964
+ ))
1965
+ }
1966
+ );
1967
+ }
1968
+
1969
+ // src/client/InsertModeControl.tsx
1970
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1971
+ function InsertModeControl({ mode, onChange, compact = false, label }) {
1972
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1973
+ PillChoice,
1974
+ {
1975
+ items: INSERT_MODES,
1976
+ value: mode,
1977
+ onChange,
1978
+ compact,
1979
+ ariaLabel: label === void 0 ? "\u53D1\u9001\u65F6\u63D2\u5165" : `\u53D1\u9001\u65F6\u63D2\u5165\uFF08${label}\uFF09`
1980
+ }
1981
+ );
1982
+ }
1983
+
1984
+ // src/client/SettingsSection.tsx
1985
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1409
1986
  var bodyLead = { ...description, margin: "12px 0" };
1410
1987
  function Chevron() {
1411
- 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)(
1988
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1412
1989
  "path",
1413
1990
  {
1414
1991
  d: "M3.25 5.5L7 9.25L10.75 5.5",
@@ -1420,9 +1997,9 @@ function Chevron() {
1420
1997
  ) });
1421
1998
  }
1422
1999
  function FoldCard(props) {
1423
- const [open, setOpen] = (0, import_react3.useState)(false);
1424
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: open ? "dsh-ux-card dsh-ux-cardOpen" : "dsh-ux-card", children: [
1425
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2000
+ const [open, setOpen] = (0, import_react4.useState)(false);
2001
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: open ? "dsh-ux-card dsh-ux-cardOpen" : "dsh-ux-card", children: [
2002
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1426
2003
  "button",
1427
2004
  {
1428
2005
  type: "button",
@@ -1432,22 +2009,22 @@ function FoldCard(props) {
1432
2009
  setOpen((current) => !current);
1433
2010
  },
1434
2011
  children: [
1435
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-ux-cardHeadText", children: [
1436
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-ux-cardName", children: props.name }),
1437
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-ux-cardDescription", children: props.summary })
2012
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "dsh-ux-cardHeadText", children: [
2013
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-ux-cardName", children: props.name }),
2014
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dsh-ux-cardDescription", children: props.summary })
1438
2015
  ] }),
1439
- /* @__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, {}) })
2016
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: open ? "dsh-ux-cardChevron dsh-ux-cardChevronOpen" : "dsh-ux-cardChevron", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Chevron, {}) })
1440
2017
  ]
1441
2018
  }
1442
2019
  ),
1443
- open && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-ux-cardBody", children: props.children })
2020
+ open && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-ux-cardBody", children: props.children })
1444
2021
  ] });
1445
2022
  }
1446
2023
  function KeyRow(props) {
1447
2024
  const { title, desc, value, presets, conflict, onChange, first } = props;
1448
- const [recording, setRecording] = (0, import_react3.useState)(false);
1449
- const [error, setError] = (0, import_react3.useState)(null);
1450
- (0, import_react3.useEffect)(() => {
2025
+ const [recording, setRecording] = (0, import_react4.useState)(false);
2026
+ const [error, setError] = (0, import_react4.useState)(null);
2027
+ (0, import_react4.useEffect)(() => {
1451
2028
  if (!recording) return;
1452
2029
  const onKeyDown = (event) => {
1453
2030
  event.preventDefault();
@@ -1484,15 +2061,15 @@ function KeyRow(props) {
1484
2061
  window.removeEventListener("keydown", onKeyDown, true);
1485
2062
  };
1486
2063
  }, [recording, conflict, onChange]);
1487
- const base = first === true ? { ...row, borderTop: "none" } : row;
1488
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { ...base, flexDirection: "column", alignItems: "stretch", gap: 8 }, children: [
1489
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: rowText, children: [
1490
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: rowTitle, children: title }),
1491
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: rowDesc, children: desc })
2064
+ const base2 = first === true ? { ...row, borderTop: "none" } : row;
2065
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { ...base2, flexDirection: "column", alignItems: "stretch", gap: 8 }, children: [
2066
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: rowText, children: [
2067
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: rowTitle, children: title }),
2068
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: rowDesc, children: desc })
1492
2069
  ] }),
1493
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: [
1494
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: kbd, children: displayChord(value) }),
1495
- !recording && presets.map((preset) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2070
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: [
2071
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: kbd, children: displayChord(value) }),
2072
+ !recording && presets.map((preset) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1496
2073
  "button",
1497
2074
  {
1498
2075
  type: "button",
@@ -1505,11 +2082,11 @@ function KeyRow(props) {
1505
2082
  },
1506
2083
  preset
1507
2084
  )),
1508
- !recording && value !== "" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", style: pill, onClick: () => {
2085
+ !recording && value !== "" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", style: pill, onClick: () => {
1509
2086
  setError(null);
1510
2087
  onChange("");
1511
2088
  }, children: "\u6E05\u9664" }),
1512
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2089
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1513
2090
  "button",
1514
2091
  {
1515
2092
  type: "button",
@@ -1521,22 +2098,22 @@ function KeyRow(props) {
1521
2098
  children: recording ? "\u8BF7\u6309\u4E0B\u65B0\u7EC4\u5408\u952E\u2026" : "\u81EA\u5B9A\u4E49\u2026"
1522
2099
  }
1523
2100
  ),
1524
- recording && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", style: pill, onClick: () => {
2101
+ recording && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", style: pill, onClick: () => {
1525
2102
  setRecording(false);
1526
2103
  setError(null);
1527
2104
  }, children: "Esc \u53D6\u6D88" }),
1528
- error !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: "var(--dsw-alias-state-error-primary)", fontSize: 12 }, children: error })
2105
+ error !== null && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { color: "var(--dsw-alias-state-error-primary)", fontSize: 12 }, children: error })
1529
2106
  ] })
1530
2107
  ] });
1531
2108
  }
1532
2109
  function ToggleRow(props) {
1533
2110
  const { label, desc, checked, onChange, first } = props;
1534
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: first === true ? { ...row, borderTop: "none" } : row, children: [
1535
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: rowText, children: [
1536
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: rowTitle, children: label }),
1537
- desc !== void 0 && desc !== "" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: rowDesc, children: desc })
2111
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: first === true ? { ...row, borderTop: "none" } : row, children: [
2112
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: rowText, children: [
2113
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: rowTitle, children: label }),
2114
+ desc !== void 0 && desc !== "" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: rowDesc, children: desc })
1538
2115
  ] }),
1539
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2116
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1540
2117
  "button",
1541
2118
  {
1542
2119
  type: "button",
@@ -1556,7 +2133,7 @@ function ToggleRow(props) {
1556
2133
  flex: "0 0 auto",
1557
2134
  padding: 0
1558
2135
  },
1559
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2136
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1560
2137
  "span",
1561
2138
  {
1562
2139
  style: {
@@ -1577,23 +2154,23 @@ function ToggleRow(props) {
1577
2154
  }
1578
2155
  function TextFieldRow(props) {
1579
2156
  const { title, desc, value, placeholder, maxLength, onChange, action, first } = props;
1580
- const [draft, setDraft] = (0, import_react3.useState)(value);
1581
- const focused = (0, import_react3.useRef)(false);
1582
- (0, import_react3.useEffect)(() => {
2157
+ const [draft, setDraft] = (0, import_react4.useState)(value);
2158
+ const focused = (0, import_react4.useRef)(false);
2159
+ (0, import_react4.useEffect)(() => {
1583
2160
  if (!focused.current) setDraft(value);
1584
2161
  }, [value]);
1585
2162
  const commit = () => {
1586
2163
  if (draft === value) return;
1587
2164
  onChange(draft.trim());
1588
2165
  };
1589
- const base = first === true ? { ...row, borderTop: "none" } : row;
1590
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { ...base, flexDirection: "column", alignItems: "stretch", gap: 8 }, children: [
1591
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: rowText, children: [
1592
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: rowTitle, children: title }),
1593
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: rowDesc, children: desc })
2166
+ const base2 = first === true ? { ...row, borderTop: "none" } : row;
2167
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { ...base2, flexDirection: "column", alignItems: "stretch", gap: 8 }, children: [
2168
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: rowText, children: [
2169
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: rowTitle, children: title }),
2170
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: rowDesc, children: desc })
1594
2171
  ] }),
1595
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
1596
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2172
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
2173
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1597
2174
  "input",
1598
2175
  {
1599
2176
  type: "text",
@@ -1622,152 +2199,250 @@ function TextFieldRow(props) {
1622
2199
  style: textInput
1623
2200
  }
1624
2201
  ),
1625
- action !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", style: pill, onClick: action.onClick, children: action.label })
2202
+ action !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", style: pill, onClick: action.onClick, children: action.label })
1626
2203
  ] })
1627
2204
  ] });
1628
2205
  }
1629
2206
  function QuickPromptsEditor(props) {
1630
- const [rows, setRows] = (0, import_react3.useState)(() => props.items.map((item) => ({ ...item })));
1631
- const [dirty, setDirty] = (0, import_react3.useState)(false);
1632
- const update = (id, patch) => {
1633
- setDirty(true);
1634
- setRows((current) => current.map((row2) => row2.id === id ? { ...row2, ...patch } : row2));
1635
- };
1636
- const remove = (id) => {
1637
- setDirty(true);
1638
- setRows((current) => current.filter((row2) => row2.id !== id));
1639
- };
1640
- const add = () => {
1641
- if (rows.length >= QUICK_PROMPT_MAX) return;
1642
- setDirty(true);
1643
- setRows((current) => current.concat([{
1644
- id: newQuickPromptId(),
1645
- label: "",
1646
- prompt: "",
1647
- always: false
1648
- }]));
1649
- };
1650
- const move = (index, delta) => {
1651
- const target = index + delta;
1652
- if (target < 0 || target >= rows.length) return;
2207
+ const [draft, setDraft] = (0, import_react4.useState)(() => props.book);
2208
+ const [activeId, setActiveId] = (0, import_react4.useState)(() => props.book.categories[0]?.id ?? "");
2209
+ const [dirty, setDirty] = (0, import_react4.useState)(false);
2210
+ const [newName, setNewName] = (0, import_react4.useState)("");
2211
+ const [note, setNote] = (0, import_react4.useState)("");
2212
+ (0, import_react4.useEffect)(() => {
2213
+ if (dirty) return;
2214
+ setDraft(props.book);
2215
+ if (!props.book.categories.some((category) => category.id === activeId)) {
2216
+ setActiveId(props.book.categories[0]?.id ?? "");
2217
+ }
2218
+ }, [props.book, dirty, activeId]);
2219
+ const edit = (next) => {
1653
2220
  setDirty(true);
1654
- setRows((current) => {
1655
- const next = [...current];
1656
- const [row2] = next.splice(index, 1);
1657
- next.splice(target, 0, row2);
1658
- return next;
1659
- });
2221
+ setDraft(next);
1660
2222
  };
1661
- const save = () => {
1662
- const cleaned = rows.map((row2) => ({
1663
- ...row2,
1664
- prompt: row2.prompt.trim(),
1665
- label: row2.label.trim() === "" ? row2.prompt.trim().slice(0, 12) : row2.label.trim()
1666
- })).filter((row2) => row2.prompt !== "");
1667
- props.onSave(cleaned);
1668
- setRows(cleaned);
1669
- setDirty(false);
2223
+ const current = draft.categories.find((category) => category.id === activeId) ?? draft.categories[0];
2224
+ const counts = bookCounts(draft);
2225
+ const saving = props.status === "saving";
2226
+ const failed = props.status !== "" && props.status !== "saving";
2227
+ const rowBox = {
2228
+ display: "flex",
2229
+ flexDirection: "column",
2230
+ gap: 6,
2231
+ padding: 8,
2232
+ borderRadius: 8,
2233
+ border: "0.5px solid var(--dsw-alias-border-l2)",
2234
+ background: "var(--dsw-alias-bg-layer-1)"
1670
2235
  };
1671
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
1672
- rows.map((row2, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1673
- "div",
1674
- {
1675
- style: {
1676
- display: "flex",
1677
- flexDirection: "column",
1678
- gap: 6,
1679
- padding: 8,
1680
- borderRadius: 8,
1681
- border: "0.5px solid var(--dsw-alias-border-l2)",
1682
- background: "var(--dsw-alias-bg-layer-1)"
2236
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
2237
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 4, flexWrap: "wrap" }, children: [
2238
+ draft.categories.map((category) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2239
+ "button",
2240
+ {
2241
+ type: "button",
2242
+ style: category.id === current?.id ? pillActive : pill,
2243
+ onClick: () => {
2244
+ setActiveId(category.id);
2245
+ },
2246
+ children: [
2247
+ category.name,
2248
+ "\uFF08",
2249
+ category.prompts.length,
2250
+ "\uFF09"
2251
+ ]
1683
2252
  },
1684
- children: [
1685
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
1686
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1687
- "input",
1688
- {
1689
- type: "text",
1690
- value: row2.label,
1691
- placeholder: "\u540D\u79F0\uFF0C\u5982\uFF1A\u4EC5\u8BF4\u660E\u539F\u56E0",
1692
- maxLength: QUICK_LABEL_MAX,
1693
- spellCheck: false,
1694
- onChange: (event) => {
1695
- update(row2.id, { label: event.target.value });
1696
- },
1697
- style: { ...textInput, flex: "0 0 150px" }
1698
- }
1699
- ),
1700
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: 4, ...rowDesc, margin: 0, flex: "0 0 auto" }, children: [
1701
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1702
- "input",
1703
- {
1704
- type: "checkbox",
1705
- checked: row2.always,
1706
- style: { margin: 0, cursor: "pointer" },
1707
- onChange: (event) => {
1708
- update(row2.id, { always: event.target.checked });
1709
- }
1710
- }
1711
- ),
1712
- "\u9ED8\u8BA4\u63D2\u5165"
1713
- ] }),
1714
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { flex: 1 } }),
1715
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", style: pill, title: "\u4E0A\u79FB", onClick: () => {
1716
- move(index, -1);
1717
- }, children: "\u2191" }),
1718
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", style: pill, title: "\u4E0B\u79FB", onClick: () => {
1719
- move(index, 1);
1720
- }, children: "\u2193" }),
1721
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", style: pill, title: "\u5220\u9664\u8FD9\u6761", onClick: () => {
1722
- remove(row2.id);
1723
- }, children: "\u2715" })
1724
- ] }),
1725
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1726
- "textarea",
2253
+ category.id
2254
+ )),
2255
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2256
+ "input",
2257
+ {
2258
+ type: "text",
2259
+ value: newName,
2260
+ placeholder: "\u65B0\u5206\u7C7B\u540D",
2261
+ maxLength: QUICK_CATEGORY_NAME_MAX,
2262
+ spellCheck: false,
2263
+ onChange: (event) => {
2264
+ setNewName(event.target.value);
2265
+ },
2266
+ style: { ...textInput, flex: "0 0 110px" }
2267
+ }
2268
+ ),
2269
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2270
+ "button",
2271
+ {
2272
+ type: "button",
2273
+ style: pill,
2274
+ disabled: draft.categories.length >= QUICK_CATEGORY_MAX,
2275
+ onClick: () => {
2276
+ edit(withCategoryAdded(draft, newName === "" ? DEFAULT_CATEGORY_NAME : newName));
2277
+ setNewName("");
2278
+ },
2279
+ children: "+ \u52A0\u5206\u7C7B"
2280
+ }
2281
+ )
2282
+ ] }),
2283
+ current === void 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: rowDesc, children: "\u8FD8\u6CA1\u6709\u5206\u7C7B\u3002\u5148\u52A0\u4E00\u4E2A\u5206\u7C7B\uFF0C\u518D\u5F80\u91CC\u653E\u6761\u76EE\u3002" }),
2284
+ current !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2285
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6, flexWrap: "wrap" }, children: [
2286
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2287
+ "input",
2288
+ {
2289
+ type: "text",
2290
+ value: current.name,
2291
+ "aria-label": "\u5206\u7C7B\u540D",
2292
+ maxLength: QUICK_CATEGORY_NAME_MAX,
2293
+ spellCheck: false,
2294
+ onChange: (event) => {
2295
+ edit(withCategoryRenamed(draft, current.id, event.target.value));
2296
+ },
2297
+ style: { ...textInput, flex: "0 0 150px" }
2298
+ }
2299
+ ),
2300
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", style: pill, title: "\u5206\u7C7B\u4E0A\u79FB", onClick: () => {
2301
+ edit(withCategoryMoved(draft, current.id, -1));
2302
+ }, children: "\u2191" }),
2303
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", style: pill, title: "\u5206\u7C7B\u4E0B\u79FB", onClick: () => {
2304
+ edit(withCategoryMoved(draft, current.id, 1));
2305
+ }, children: "\u2193" }),
2306
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2307
+ "button",
2308
+ {
2309
+ type: "button",
2310
+ style: pill,
2311
+ title: "\u5220\u9664\u8FD9\u4E2A\u5206\u7C7B\uFF08\u8FDE\u540C\u5B83\u91CC\u9762\u7684\u6761\u76EE\uFF09",
2312
+ onClick: () => {
2313
+ edit(withCategoryRemoved(draft, current.id));
2314
+ },
2315
+ children: "\u2715 \u5220\u9664\u5206\u7C7B"
2316
+ }
2317
+ ),
2318
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: rowDesc, children: [
2319
+ current.prompts.length,
2320
+ " / ",
2321
+ QUICK_PROMPT_MAX,
2322
+ " \u6761"
2323
+ ] })
2324
+ ] }),
2325
+ current.prompts.map((prompt) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: rowBox, children: [
2326
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6, flexWrap: "wrap" }, children: [
2327
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2328
+ "input",
1727
2329
  {
1728
- value: row2.prompt,
1729
- placeholder: "\u63D0\u793A\u8BCD\u6B63\u6587\uFF1A\u70B9\u51FB\u8BE5\u6761\u76EE\u65F6\u63D2\u5165\u8F93\u5165\u6846\uFF1B\u52FE\u4E86\u300C\u9ED8\u8BA4\u63D2\u5165\u300D\u5219\u5728\u53D1\u9001\u65F6\u81EA\u52A8\u9644\u52A0\u5230\u6D88\u606F\u672B\u5C3E",
1730
- maxLength: QUICK_TEXT_MAX,
2330
+ type: "text",
2331
+ value: prompt.label,
2332
+ placeholder: "\u540D\u79F0\uFF0C\u5982\uFF1A\u4EC5\u8BF4\u660E\u539F\u56E0",
2333
+ maxLength: QUICK_LABEL_MAX,
1731
2334
  spellCheck: false,
1732
- rows: Math.min(4, Math.max(2, Math.ceil(row2.prompt.length / 46))),
1733
2335
  onChange: (event) => {
1734
- update(row2.id, { prompt: event.target.value });
2336
+ edit(withPromptPatched(draft, current.id, prompt.id, { label: event.target.value }));
1735
2337
  },
1736
- style: { ...textInput, resize: "vertical", fontFamily: "inherit", lineHeight: 1.5 }
2338
+ style: { ...textInput, flex: "0 0 150px" }
1737
2339
  }
1738
- )
1739
- ]
1740
- },
1741
- row2.id
1742
- )),
1743
- rows.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: rowDesc, children: "\u8FD8\u6CA1\u6709\u6761\u76EE\u3002\u70B9\u4E0B\u9762\u7684\u300C+ \u6DFB\u52A0\u4E00\u6761\u300D\u5F00\u59CB\u5EFA\u81EA\u5DF1\u7684\u5FEB\u6377\u6307\u4EE4\u3002" }),
1744
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: [
1745
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", style: pill, onClick: add, children: "+ \u6DFB\u52A0\u4E00\u6761" }),
1746
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2340
+ ),
2341
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2342
+ InsertModeControl,
2343
+ {
2344
+ label: prompt.label,
2345
+ mode: insertModeOf(prompt),
2346
+ onChange: (mode) => {
2347
+ edit(withInsertMode(draft, prompt.id, mode));
2348
+ }
2349
+ }
2350
+ ),
2351
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { flex: 1 } }),
2352
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", style: pill, title: "\u4E0A\u79FB", onClick: () => {
2353
+ edit(withPromptMoved(draft, current.id, prompt.id, -1));
2354
+ }, children: "\u2191" }),
2355
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", style: pill, title: "\u4E0B\u79FB", onClick: () => {
2356
+ edit(withPromptMoved(draft, current.id, prompt.id, 1));
2357
+ }, children: "\u2193" }),
2358
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", style: pill, title: "\u5220\u9664\u8FD9\u6761", onClick: () => {
2359
+ edit(withPromptRemoved(draft, current.id, prompt.id));
2360
+ }, children: "\u2715" })
2361
+ ] }),
2362
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2363
+ "textarea",
2364
+ {
2365
+ value: prompt.prompt,
2366
+ placeholder: "\u63D0\u793A\u8BCD\u6B63\u6587\uFF1A\u70B9\u51FB\u8BE5\u6761\u76EE\u65F6\u63D2\u5165\u8F93\u5165\u6846\uFF1B\u52FE\u4E86\u300C\u9ED8\u8BA4\u63D2\u5165\u300D\u5219\u5728\u53D1\u9001\u65F6\u81EA\u52A8\u9644\u52A0\u5230\u6D88\u606F\u672B\u5C3E",
2367
+ maxLength: QUICK_TEXT_MAX,
2368
+ spellCheck: false,
2369
+ rows: Math.min(4, Math.max(2, Math.ceil(prompt.prompt.length / 46))),
2370
+ onChange: (event) => {
2371
+ edit(withPromptPatched(draft, current.id, prompt.id, { prompt: event.target.value }));
2372
+ },
2373
+ style: { ...textInput, resize: "vertical", fontFamily: "inherit", lineHeight: 1.5 }
2374
+ }
2375
+ )
2376
+ ] }, prompt.id)),
2377
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2378
+ AddPromptRow,
2379
+ {
2380
+ book: draft,
2381
+ categoryId: current.id,
2382
+ onAdd: () => {
2383
+ edit(withPromptAdded(draft, current.id));
2384
+ },
2385
+ onMove: (promptId) => {
2386
+ edit(withPromptMovedToCategory(draft, promptId, current.id));
2387
+ }
2388
+ }
2389
+ )
2390
+ ] }),
2391
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: [
2392
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1747
2393
  "button",
1748
2394
  {
1749
2395
  type: "button",
1750
2396
  style: dirty ? pillActive : pill,
1751
- onClick: save,
1752
- disabled: !dirty,
1753
- children: dirty ? "\u4FDD\u5B58\u4FEE\u6539" : "\u5DF2\u4FDD\u5B58"
2397
+ disabled: !dirty || saving,
2398
+ onClick: () => {
2399
+ const next = {
2400
+ version: draft.version,
2401
+ categories: draft.categories.map((category) => ({
2402
+ ...category,
2403
+ prompts: category.prompts.filter((prompt) => prompt.prompt.trim() !== "")
2404
+ }))
2405
+ };
2406
+ const skipped = counts.prompts - bookCounts(next).prompts;
2407
+ setNote(skipped > 0 ? `\u5DF2\u8DF3\u8FC7 ${String(skipped)} \u6761\u6B63\u6587\u4E3A\u7A7A\u7684\u6761\u76EE` : "");
2408
+ props.onSave(next);
2409
+ setDirty(false);
2410
+ },
2411
+ children: saving ? "\u4FDD\u5B58\u4E2D\u2026" : dirty ? "\u4FDD\u5B58\u4FEE\u6539" : "\u5DF2\u4FDD\u5B58"
1754
2412
  }
1755
2413
  ),
1756
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: rowDesc, children: [
2414
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", style: pill, onClick: () => {
2415
+ props.onReload();
2416
+ }, children: "\u91CD\u65B0\u8BFB\u53D6" }),
2417
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", style: pill, onClick: () => {
2418
+ props.onReset();
2419
+ }, children: "\u6062\u590D\u5185\u7F6E 9 \u6761" }),
2420
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: rowDesc, children: [
1757
2421
  "\u5171 ",
1758
- rows.length,
1759
- " / ",
1760
- QUICK_PROMPT_MAX,
1761
- " \u6761 \xB7 \u52FE\u4E86\u300C\u9ED8\u8BA4\u63D2\u5165\u300D\u7684\uFF1A",
1762
- rows.filter((row2) => row2.always).length,
1763
- " \u6761"
2422
+ counts.categories,
2423
+ " \u4E2A\u5206\u7C7B \xB7 ",
2424
+ counts.prompts,
2425
+ " \u6761 \xB7 \u6BCF\u6B21 ",
2426
+ counts.always,
2427
+ " \u6761 \xB7 \u4EC5\u9996\u6B21 ",
2428
+ counts.first,
2429
+ " \u6761",
2430
+ note === "" ? "" : ` \xB7 ${note}`,
2431
+ failed ? ` \xB7 ${props.status}` : ""
1764
2432
  ] })
2433
+ ] }),
2434
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { style: rowDesc, children: [
2435
+ "\u6570\u636E\u5B58\u5728 ",
2436
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { children: "~/.dsh/quick-prompts.json" }),
2437
+ "\uFF08\u4E0E\u4F1A\u8BDD\u3001\u9879\u76EE\u65E0\u5173\uFF0C\u53EF\u5355\u72EC\u5907\u4EFD\u6216\u624B\u5DE5\u7F16\u8F91\uFF0C \u6539\u5B8C\u70B9\u300C\u91CD\u65B0\u8BFB\u53D6\u300D\u5373\u53EF\u52A0\u8F7D\uFF09\u3002"
1765
2438
  ] })
1766
2439
  ] });
1767
2440
  }
1768
- function SettingsSection({ useLive, actions }) {
2441
+ function SettingsSection({ useLive, useBook, useBookStatus, actions }) {
1769
2442
  const settings = useLive((item) => item);
1770
- const [conflict, setConflict] = (0, import_react3.useState)(null);
2443
+ const book = useBook((item) => item);
2444
+ const bookStatus = useBookStatus((item) => item);
2445
+ const [conflict, setConflict] = (0, import_react4.useState)(null);
1771
2446
  const setKey = (side, chord) => {
1772
2447
  const other = side === "send" ? settings.newlineKey : settings.sendKey;
1773
2448
  if (chord !== "" && chord === other) {
@@ -1780,20 +2455,20 @@ function SettingsSection({ useLive, actions }) {
1780
2455
  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;
1781
2456
  const menuEnabled = MENU_ITEMS.filter((item) => settings[item.field]).length;
1782
2457
  const keySummary = `\u53D1\u9001 ${displayChord(settings.sendKey)} \xB7 \u6362\u884C ${displayChord(settings.newlineKey)}`;
1783
- 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`;
2458
+ const menuSummary = settings.menuMode === "official" ? "\u5F53\u524D\uFF1A\u5B98\u65B9\u4E0D\u4ECB\u5165" : settings.menuMode === "browser" ? "\u5F53\u524D\uFF1A\u6D4F\u89C8\u5668\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`;
1784
2459
  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}`;
1785
2460
  const headerSummary = settings.headerEnabled ? settings.headerStatus === "" ? "\u5DF2\u542F\u7528" : settings.headerStatus : "\u672A\u542F\u7528";
1786
- const alwaysCount = settings.quickPrompts.filter((item) => item.always).length;
1787
- const quickSummary = `${settings.quickPrompts.length} \u6761 \xB7 \u9ED8\u8BA4\u63D2\u5165 ${alwaysCount} \u6761 \xB7 \u4F18\u5316\u6863\u4F4D ${OPTIMIZER_TIERS.find((item) => item.id === settings.optimizerTier)?.label ?? "\u9AD8\u7EA7"}`;
2461
+ const quickCounts = bookCounts(book);
2462
+ const quickSummary = `${quickCounts.categories} \u4E2A\u5206\u7C7B \xB7 ${quickCounts.prompts} \u6761 \xB7 \u6BCF\u6B21 ${quickCounts.always} \xB7 \u4EC5\u9996\u6B21 ${quickCounts.first} \xB7 \u4F18\u5316\u6863\u4F4D ${OPTIMIZER_TIERS.find((item) => item.id === settings.optimizerTier)?.label ?? "\u9AD8\u7EA7"}`;
1788
2463
  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";
1789
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { padding: "4px 2px" }, children: [
1790
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "dsh-ux-card", children: [
1791
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-ux-cardHeaderStatic", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-ux-cardHeadText", children: [
1792
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { className: "dsh-ux-cardName", children: "\u8F93\u5165\u4F53\u9A8C" }),
1793
- /* @__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" })
2464
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { padding: "4px 2px" }, children: [
2465
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "dsh-ux-card", children: [
2466
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dsh-ux-cardHeaderStatic", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "dsh-ux-cardHeadText", children: [
2467
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { className: "dsh-ux-cardName", children: "\u8F93\u5165\u4F53\u9A8C" }),
2468
+ /* @__PURE__ */ (0, import_jsx_runtime6.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" })
1794
2469
  ] }) }),
1795
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-ux-cardBody", children: [
1796
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2470
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dsh-ux-cardBody", children: [
2471
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1797
2472
  ToggleRow,
1798
2473
  {
1799
2474
  first: true,
@@ -1805,13 +2480,13 @@ function SettingsSection({ useLive, actions }) {
1805
2480
  }
1806
2481
  }
1807
2482
  ),
1808
- !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" })
2483
+ !settings.enabled && /* @__PURE__ */ (0, import_jsx_runtime6.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" })
1809
2484
  ] })
1810
2485
  ] }),
1811
- settings.enabled && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1812
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(FoldCard, { name: "\u952E\u4F4D", summary: keySummary, children: [
1813
- /* @__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" }),
1814
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2486
+ settings.enabled && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2487
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(FoldCard, { name: "\u952E\u4F4D", summary: keySummary, children: [
2488
+ /* @__PURE__ */ (0, import_jsx_runtime6.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" }),
2489
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1815
2490
  KeyRow,
1816
2491
  {
1817
2492
  first: true,
@@ -1825,7 +2500,7 @@ function SettingsSection({ useLive, actions }) {
1825
2500
  }
1826
2501
  }
1827
2502
  ),
1828
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2503
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1829
2504
  KeyRow,
1830
2505
  {
1831
2506
  title: "\u6362\u884C\u952E",
@@ -1838,8 +2513,8 @@ function SettingsSection({ useLive, actions }) {
1838
2513
  }
1839
2514
  }
1840
2515
  ),
1841
- conflictHint !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: hintError, children: conflictHint }),
1842
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { marginTop: 10 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2516
+ conflictHint !== null && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: hintError, children: conflictHint }),
2517
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { marginTop: 10 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1843
2518
  "button",
1844
2519
  {
1845
2520
  type: "button",
@@ -1852,22 +2527,33 @@ function SettingsSection({ useLive, actions }) {
1852
2527
  }
1853
2528
  ) })
1854
2529
  ] }),
1855
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(FoldCard, { name: "\u53F3\u952E\u83DC\u5355", summary: menuSummary, children: [
1856
- /* @__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" }),
1857
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1858
- ToggleRow,
1859
- {
1860
- first: true,
1861
- label: "\u4F7F\u7528\u7CFB\u7EDF\u539F\u751F\u83DC\u5355",
1862
- 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",
1863
- checked: settings.menuNative,
1864
- onChange: (next) => {
1865
- actions.setField(MENU_NATIVE_FIELD, next);
2530
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(FoldCard, { name: "\u53F3\u952E\u83DC\u5355", summary: menuSummary, children: [
2531
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: bodyLead, children: "\u53F3\u952E\u70B9\u51FB\u8F93\u5165\u6846\u65F6\u5F39\u51FA\u54EA\u4E00\u79CD\u83DC\u5355\u3002\u53EA\u5F71\u54CD\u53F3\u952E\uFF0C\u4E0D\u5F71\u54CD\u952E\u4F4D\u4E0E\u5176\u5B83\u529F\u80FD\u3002" }),
2532
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { ...row, borderTop: "none", flexDirection: "column", alignItems: "stretch", gap: 8 }, children: [
2533
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: rowText, children: [
2534
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: rowTitle, children: "\u83DC\u5355\u6765\u6E90" }),
2535
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: rowDesc, children: MENU_MODES.find((item) => item.id === settings.menuMode)?.hint ?? "" })
2536
+ ] }),
2537
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2538
+ PillChoice,
2539
+ {
2540
+ items: MENU_MODES,
2541
+ value: settings.menuMode,
2542
+ onChange: (mode) => {
2543
+ actions.setField(MENU_MODE_FIELD, mode);
2544
+ },
2545
+ ariaLabel: "\u53F3\u952E\u83DC\u5355\u6765\u6E90"
1866
2546
  }
1867
- }
1868
- ),
1869
- !settings.menuNative && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1870
- MENU_ITEMS.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2547
+ )
2548
+ ] }),
2549
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: hintInfo, children: "\u5B98\u65B9\uFF1A\u672C\u63D2\u4EF6\u5B8C\u5168\u4E0D\u4ECB\u5165\uFF0CDSH \u4E0E\u5176\u5B83\u63D2\u4EF6\u81EA\u5DF1\u7684\u53F3\u952E\u5904\u7406\u539F\u6837\u751F\u6548\uFF08DSH \u5B98\u65B9\u8F93\u5165\u6846\u672C\u8EAB\u6CA1\u6709 \u53F3\u952E\u83DC\u5355\uFF0C\u6240\u4EE5\u901A\u5E38\u770B\u5230\u7684\u5C31\u662F\u6D4F\u89C8\u5668\u7684\u83DC\u5355\uFF09\u3002" }),
2550
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: hintInfo, children: "\u6D4F\u89C8\u5668\uFF1A\u56FA\u5B9A\u7528\u6D4F\u89C8\u5668\u81EA\u5E26\u7684\u83DC\u5355\uFF08\u6837\u5B50\u968F\u6D4F\u89C8\u5668\u800C\u53D8\uFF09\uFF0C\u5E76\u5728\u672C\u63D2\u4EF6\u8FD9\u4E00\u5C42\u6321\u4F4F\u5176\u5B83\u63D2\u4EF6\u7684\u83DC\u5355\uFF1B \u597D\u5904\u662F\u7C98\u8D34\u514D\u6388\u6743\u3001\u96F6\u914D\u7F6E\u3002" }),
2551
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: hintInfo, children: "\u81EA\u5B9A\u4E49\uFF1A\u7528\u672C\u63D2\u4EF6\u56FA\u5B9A\u6837\u5F0F\u7684\u83DC\u5355\uFF08\u672A\u9009\u4E2D\u6587\u672C\u65F6\u300C\u526A\u5207 / \u590D\u5236 / \u5220\u9664\u300D\u7F6E\u7070\uFF09\u3002\u8FD9\u4E00\u6863\u7684\u300C\u7C98\u8D34\u300D \u8981\u8BFB\u526A\u8D34\u677F\uFF0C\u6D4F\u89C8\u5668\u4F1A\u5148\u8981\u4E00\u6B21\u6388\u6743\u2014\u2014\u4E09\u5BB6\u6D4F\u89C8\u5668\u7684\u5904\u7406\u4E0D\u4E00\u6837\uFF1A" }),
2552
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: hintInfo, children: "\xB7 Chrome / Edge\uFF08\u8C37\u6B4C / \u5FAE\u8F6F\u6D4F\u89C8\u5668\uFF09\uFF1A\u5F39\u51FA\u540E\u70B9\u300C\u5141\u8BB8\u300D\u5373\u53EF\uFF0C\u4E4B\u540E\u4F1A\u8BB0\u4F4F\u8FD9\u4E2A\u7AD9\u70B9\u3001\u4E0D\u518D\u95EE\u3002 \u8981\u6539\u6216\u64A4\u9500\u6388\u6743\uFF1A\u70B9\u5730\u5740\u680F\u6700\u5DE6\u8FB9\u7684\u7F51\u7AD9\u56FE\u6807 \u2192 \u7F51\u7AD9\u8BBE\u7F6E\uFF08Edge \u53EB\u300C\u6B64\u7AD9\u70B9\u7684\u6743\u9650\u300D\uFF09\u2192 \u526A\u8D34\u677F\uFF1B \u4E5F\u53EF\u4EE5\u76F4\u63A5\u5728\u5730\u5740\u680F\u8F93\u5165 chrome://settings/content/clipboard\uFF08Edge \u8F93\u5165 edge://settings/content/clipboard\uFF09\u3002" }),
2553
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: hintInfo, children: "\xB7 Firefox\uFF1A\u4E0D\u5141\u8BB8\u7F51\u9875\u9759\u9ED8\u8BFB\u526A\u8D34\u677F\u2014\u2014\u4F60\u70B9\u672C\u63D2\u4EF6\u7684\u300C\u7C98\u8D34\u300D\u540E\uFF0C\u5B83\u4F1A\u5148\u5F39\u4E00\u4E2A\u53EA\u6709\u300C\u7C98\u8D34(P)\u300D \u4E00\u9879\u7684\u5C0F\u7A97\uFF08\u7EA6 1 \u79D2\u540E\u624D\u53EF\u70B9\uFF09\uFF0C\u70B9\u5B83\u624D\u5B8C\u6210\u8FD9\u6B21\u7C98\u8D34\u3002\u8FD9\u662F Firefox \u7684\u5B89\u5168\u673A\u5236\uFF0C\u63D2\u4EF6\u5173\u4E0D\u6389 \uFF08\u5B9E\u6D4B\uFF1Aabout:config \u91CC\u7684\u526A\u8D34\u677F\u9996\u9009\u9879\u5BF9\u5B83\u65E0\u6548\uFF09\u3002\u4E0D\u60F3\u591A\u8FD9\u4E00\u6B65\u5C31\u76F4\u63A5\u6309 Ctrl+V\uFF0C \u6216\u628A\u4E0A\u9762\u7684\u300C\u83DC\u5355\u6765\u6E90\u300D\u6362\u6210\u300C\u6D4F\u89C8\u5668 / \u5B98\u65B9\u300D\u6863\u2014\u2014\u90A3\u4E24\u6863\u7528\u7684\u662F\u6D4F\u89C8\u5668\u81EA\u5DF1\u7684\u7C98\u8D34\u3002" }),
2554
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: hintInfo, children: "\u6CE8\u610F\uFF1Achrome://\u3001edge:// \u8FD9\u4E9B\u5730\u5740\u6D4F\u89C8\u5668\u4E0D\u5141\u8BB8\u505A\u6210\u7F51\u9875\u91CC\u7684\u94FE\u63A5\uFF0C\u53EA\u80FD\u624B\u8F93\uFF0C \u6216\u590D\u5236\u4E0A\u9762\u90A3\u4E32\u7C98\u8D34\u5230\u5730\u5740\u680F\u3002" }),
2555
+ settings.menuMode === "custom" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2556
+ MENU_ITEMS.map((item) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1871
2557
  ToggleRow,
1872
2558
  {
1873
2559
  label: item.label,
@@ -1879,7 +2565,7 @@ function SettingsSection({ useLive, actions }) {
1879
2565
  },
1880
2566
  item.field
1881
2567
  )),
1882
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { marginTop: 10 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2568
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { marginTop: 10 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1883
2569
  "button",
1884
2570
  {
1885
2571
  type: "button",
@@ -1892,14 +2578,22 @@ function SettingsSection({ useLive, actions }) {
1892
2578
  ) })
1893
2579
  ] })
1894
2580
  ] }),
1895
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(FoldCard, { name: "\u5FEB\u6377\u6307\u4EE4", summary: quickSummary, children: [
1896
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: bodyLead, children: "\u8F93\u5165\u6846\u5DE5\u5177\u884C\u91CC\u90A3\u4E2A\u300C\u5FEB\u6377\u6307\u4EE4\u300D\u6309\u94AE\u70B9\u5F00\u5C31\u662F\u8FD9\u5F20\u6E05\u5355\uFF1A\u70B9\u6761\u76EE\u628A\u5185\u5BB9\u63D2\u5165\u8F93\u5165\u6846\uFF1B \u6761\u76EE\u53F3\u4FA7\u52FE\u4E0A\u300C\u9ED8\u8BA4\u63D2\u5165\u300D\uFF0C\u5219\u5728\u4F60**\u70B9\u53D1\u9001\u65F6**\u628A\u8FD9\u6761\u63D0\u793A\u8BCD\u81EA\u52A8\u9644\u52A0\u5230\u6D88\u606F**\u672B\u5C3E** \u4E00\u8D77\u53D1\u51FA\u53BB\uFF08\u591A\u6761\u6309\u5217\u8868\u987A\u5E8F\u62FC\u63A5\uFF0C\u8F93\u5165\u6846\u91CC\u4E0D\u63D0\u524D\u663E\u793A\uFF09\u3002 \u300C\u4F18\u5316\u63D0\u793A\u8BCD\u300D\u4F1A\u7528\u53E6\u4E00\u4E2A AI \u628A\u8F93\u5165\u6846\u91CC\u7684\u8BDD\u6574\u7406\u6210\u4E00\u6761\u80FD\u76F4\u63A5\u53D1\u51FA\u53BB\u7684\u6E05\u6670\u6307\u4EE4\uFF0C \u7ED3\u679C\u76F4\u63A5\u5199\u56DE\u8F93\u5165\u6846\uFF08Ctrl+Z \u53EF\u8FD8\u539F\uFF09\u2014\u2014\u4E0D\u4F1A\u6C61\u67D3\u5F53\u524D\u5BF9\u8BDD\uFF0C\u4E5F\u4E0D\u5360\u4F60\u7684\u5BF9\u8BDD\u8F6E\u6B21\u3002" }),
1897
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { ...row, borderTop: "none", flexDirection: "column", alignItems: "stretch", gap: 8 }, children: [
1898
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: rowText, children: [
1899
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: rowTitle, children: "\u4F18\u5316\u5F3A\u5EA6" }),
1900
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: rowDesc, children: OPTIMIZER_TIERS.find((item) => item.id === settings.optimizerTier)?.hint ?? "" })
2581
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(FoldCard, { name: "\u5FEB\u6377\u6307\u4EE4", summary: quickSummary, children: [
2582
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { style: bodyLead, children: [
2583
+ "\u8F93\u5165\u6846\u5DE5\u5177\u884C\u91CC\u90A3\u4E2A\u300C\u5FEB\u6377\u6307\u4EE4\u300D\u6309\u94AE\u70B9\u5F00\u5C31\u662F\u8FD9\u5F20\u6E05\u5355\uFF1A\u70B9\u6761\u76EE\u628A\u5185\u5BB9\u63D2\u5165\u8F93\u5165\u6846\u3002 \u6BCF\u6761\u53F3\u4FA7\u7684\u4E09\u9009\u4E00\u51B3\u5B9A",
2584
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: "\u53D1\u9001\u65F6\u600E\u4E48\u9644\u52A0" }),
2585
+ "\uFF1A \u300C\u5173\u300D= \u53EA\u63D2\u5165\u4E0D\u9644\u52A0\uFF1B\u300C\u6BCF\u6B21\u300D= \u6BCF\u6B21\u53D1\u9001\u90FD\u9644\u52A0\u5230\u6D88\u606F",
2586
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: "\u672B\u5C3E" }),
2587
+ "\uFF1B \u300C\u4EC5\u9996\u6B21\u300D= \u53EA\u5728",
2588
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: "\u8FD9\u4E2A\u4F1A\u8BDD\u7684\u7B2C\u4E00\u6761\u6D88\u606F" }),
2589
+ "\u4E0A\u9644\u52A0\uFF08\u4E4B\u540E\u4E0D\u518D\u9644\u52A0\uFF0C\u5237\u65B0\u9875\u9762\u4E5F\u4E0D\u4F1A\u91CD\u590D\uFF09\u3002 \u591A\u6761\u6309\u5217\u8868\u987A\u5E8F\u62FC\u63A5\uFF0C\u8F93\u5165\u6846\u91CC\u4E0D\u63D0\u524D\u663E\u793A\u3002 \u300C\u4F18\u5316\u63D0\u793A\u8BCD\u300D\u4F1A\u7528\u53E6\u4E00\u4E2A AI \u628A\u8F93\u5165\u6846\u91CC\u7684\u8BDD\u6574\u7406\u6210\u4E00\u6761\u80FD\u76F4\u63A5\u53D1\u51FA\u53BB\u7684\u6E05\u6670\u6307\u4EE4\uFF0C \u7ED3\u679C\u76F4\u63A5\u5199\u56DE\u8F93\u5165\u6846\uFF08Ctrl+Z \u53EF\u8FD8\u539F\uFF09\u2014\u2014\u4E0D\u4F1A\u6C61\u67D3\u5F53\u524D\u5BF9\u8BDD\uFF0C\u4E5F\u4E0D\u5360\u4F60\u7684\u5BF9\u8BDD\u8F6E\u6B21\u3002"
2590
+ ] }),
2591
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { ...row, borderTop: "none", flexDirection: "column", alignItems: "stretch", gap: 8 }, children: [
2592
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: rowText, children: [
2593
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: rowTitle, children: "\u4F18\u5316\u5F3A\u5EA6" }),
2594
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: rowDesc, children: OPTIMIZER_TIERS.find((item) => item.id === settings.optimizerTier)?.hint ?? "" })
1901
2595
  ] }),
1902
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: OPTIMIZER_TIERS.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2596
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: OPTIMIZER_TIERS.map((item) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1903
2597
  "button",
1904
2598
  {
1905
2599
  type: "button",
@@ -1913,36 +2607,32 @@ function SettingsSection({ useLive, actions }) {
1913
2607
  item.id
1914
2608
  )) })
1915
2609
  ] }),
1916
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { ...row, borderTop: "none", flexDirection: "column", alignItems: "stretch", gap: 8 }, children: [
1917
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: rowText, children: [
1918
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: rowTitle, children: "\u5FEB\u6377\u6307\u4EE4\u6E05\u5355" }),
1919
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: rowDesc, children: "\u5185\u7F6E 9 \u6761\u53EF\u4EE5\u76F4\u63A5\u7528\uFF1B\u6539\u5B8C\u8BB0\u5F97\u70B9\u300C\u4FDD\u5B58\u4FEE\u6539\u300D\u3002" })
2610
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { ...row, borderTop: "none", flexDirection: "column", alignItems: "stretch", gap: 8 }, children: [
2611
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: rowText, children: [
2612
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: rowTitle, children: "\u5FEB\u6377\u6307\u4EE4\u6E05\u5355" }),
2613
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: rowDesc, children: "\u5206\u7C7B\u5728\u8FD9\u91CC\u7BA1\u7406\uFF08\u9762\u677F\u91CC\u53EA\u505A\u5207\u6362\uFF09\uFF1B\u5185\u7F6E 9 \u6761\u53EF\u4EE5\u76F4\u63A5\u7528\uFF0C\u6539\u5B8C\u8BB0\u5F97\u70B9\u300C\u4FDD\u5B58\u4FEE\u6539\u300D\u3002" })
1920
2614
  ] }),
1921
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2615
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1922
2616
  QuickPromptsEditor,
1923
2617
  {
1924
- items: settings.quickPrompts,
2618
+ book,
2619
+ status: bookStatus,
1925
2620
  onSave: (next) => {
1926
- actions.setField(QUICK_PROMPTS_FIELD, next);
1927
- }
1928
- }
1929
- ),
1930
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { display: "flex", gap: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1931
- "button",
1932
- {
1933
- type: "button",
1934
- style: pill,
1935
- onClick: () => {
1936
- actions.clearField(QUICK_PROMPTS_FIELD);
2621
+ actions.saveBook(next);
1937
2622
  },
1938
- children: "\u6062\u590D\u5185\u7F6E 9 \u6761"
2623
+ onReload: () => {
2624
+ actions.reloadBook();
2625
+ },
2626
+ onReset: () => {
2627
+ actions.resetBook();
2628
+ }
1939
2629
  }
1940
- ) })
2630
+ )
1941
2631
  ] })
1942
2632
  ] }),
1943
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(FoldCard, { name: "\u8BBE\u7F6E\u9762\u677F", summary: panelSummary, children: [
1944
- /* @__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" }),
1945
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2633
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(FoldCard, { name: "\u8BBE\u7F6E\u9762\u677F", summary: panelSummary, children: [
2634
+ /* @__PURE__ */ (0, import_jsx_runtime6.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" }),
2635
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1946
2636
  ToggleRow,
1947
2637
  {
1948
2638
  first: true,
@@ -1954,7 +2644,7 @@ function SettingsSection({ useLive, actions }) {
1954
2644
  }
1955
2645
  }
1956
2646
  ),
1957
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2647
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1958
2648
  ToggleRow,
1959
2649
  {
1960
2650
  label: "\u8FB9\u7F18\u8C03\u6574\u5927\u5C0F",
@@ -1965,9 +2655,9 @@ function SettingsSection({ useLive, actions }) {
1965
2655
  }
1966
2656
  }
1967
2657
  ),
1968
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", marginTop: 10 }, children: [
1969
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: rowDesc, children: "\u5C3A\u5BF8\u9884\u8BBE\uFF1A" }),
1970
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2658
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", marginTop: 10 }, children: [
2659
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: rowDesc, children: "\u5C3A\u5BF8\u9884\u8BBE\uFF1A" }),
2660
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1971
2661
  "button",
1972
2662
  {
1973
2663
  type: "button",
@@ -1979,7 +2669,7 @@ function SettingsSection({ useLive, actions }) {
1979
2669
  children: "\u9ED8\u8BA4\uFF08800\uFF09"
1980
2670
  }
1981
2671
  ),
1982
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2672
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1983
2673
  "button",
1984
2674
  {
1985
2675
  type: "button",
@@ -1991,7 +2681,7 @@ function SettingsSection({ useLive, actions }) {
1991
2681
  children: "\u7D27\u51D1\uFF08640\xD7600\uFF09"
1992
2682
  }
1993
2683
  ),
1994
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2684
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1995
2685
  "button",
1996
2686
  {
1997
2687
  type: "button",
@@ -2005,9 +2695,10 @@ function SettingsSection({ useLive, actions }) {
2005
2695
  )
2006
2696
  ] })
2007
2697
  ] }),
2008
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(FoldCard, { name: "OpenCode \u8BF7\u6C42\u5934", summary: headerSummary, children: [
2009
- /* @__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" }),
2010
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2698
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(FoldCard, { name: "OpenCode \u8BF7\u6C42\u5934", summary: headerSummary, children: [
2699
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: hintError, children: "\u26A0\uFE0F \u8BF7\u4F7F\u7528\u81EA\u5B9A\u4E49\u7684 OpenCode API \u5730\u5740\uFF0C\u4E0D\u8981\u7528 DSH \u81EA\u5E26\u7684 API \u5730\u5740\u914D\u7F6E\uFF1A \u7528\u81EA\u5E26\u7684\u914D\u7F6E\u65E0\u6CD5\u4E0A\u4F20\u56FE\u7247\uFF0C\u4E00\u65E6\u53D1\u9001\u56FE\u7247\u5C31\u4F1A\u62A5\u9519\u3002" }),
2700
+ /* @__PURE__ */ (0, import_jsx_runtime6.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" }),
2701
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2011
2702
  ToggleRow,
2012
2703
  {
2013
2704
  first: true,
@@ -2019,7 +2710,7 @@ function SettingsSection({ useLive, actions }) {
2019
2710
  }
2020
2711
  }
2021
2712
  ),
2022
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2713
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2023
2714
  TextFieldRow,
2024
2715
  {
2025
2716
  title: "\u5934\u540D",
@@ -2032,7 +2723,7 @@ function SettingsSection({ useLive, actions }) {
2032
2723
  }
2033
2724
  }
2034
2725
  ),
2035
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2726
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2036
2727
  TextFieldRow,
2037
2728
  {
2038
2729
  title: "\u503C",
@@ -2051,7 +2742,7 @@ function SettingsSection({ useLive, actions }) {
2051
2742
  }
2052
2743
  }
2053
2744
  ),
2054
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2745
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2055
2746
  TextFieldRow,
2056
2747
  {
2057
2748
  title: "\u4F5C\u7528\u8DEF\u7531",
@@ -2064,7 +2755,7 @@ function SettingsSection({ useLive, actions }) {
2064
2755
  }
2065
2756
  }
2066
2757
  ),
2067
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { style: hintInfo, children: [
2758
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { style: hintInfo, children: [
2068
2759
  "\u72B6\u6001\uFF1A",
2069
2760
  headerStatusText
2070
2761
  ] })
@@ -2074,12 +2765,13 @@ function SettingsSection({ useLive, actions }) {
2074
2765
  }
2075
2766
 
2076
2767
  // src/client/QuickCommandsButton.tsx
2077
- var import_react4 = require("react");
2078
- var import_jsx_runtime4 = require("react/jsx-runtime");
2768
+ var import_react5 = require("react");
2769
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2079
2770
  function QuickCommandsButton({
2080
2771
  useInput,
2081
2772
  inputActions,
2082
2773
  sessionId,
2774
+ useSession,
2083
2775
  useLive,
2084
2776
  usePanel,
2085
2777
  actions
@@ -2087,21 +2779,23 @@ function QuickCommandsButton({
2087
2779
  const settings = useLive((item) => item);
2088
2780
  const anchor = usePanel((item) => item);
2089
2781
  const draft = useInput((state2) => state2.draft);
2090
- const ref = (0, import_react4.useRef)(null);
2091
- (0, import_react4.useEffect)(() => {
2782
+ const blank = useSession((state2) => state2?.blank === true);
2783
+ const ref = (0, import_react5.useRef)(null);
2784
+ (0, import_react5.useEffect)(() => {
2092
2785
  const sid = typeof sessionId === "string" ? sessionId : "";
2093
2786
  publishInputBridge({
2094
2787
  actions: inputActions ?? null,
2095
2788
  draft: typeof draft === "string" ? draft : "",
2096
- sessionId: sid
2789
+ sessionId: sid,
2790
+ blank: blank === true
2097
2791
  });
2098
2792
  return () => {
2099
2793
  releaseInputBridge(sid);
2100
2794
  };
2101
- }, [inputActions, draft, sessionId]);
2795
+ }, [inputActions, draft, sessionId, blank]);
2102
2796
  if (settings.enabled !== true) return null;
2103
2797
  const open = anchor !== null;
2104
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2798
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2105
2799
  "button",
2106
2800
  {
2107
2801
  ref,
@@ -2122,14 +2816,14 @@ function QuickCommandsButton({
2122
2816
  });
2123
2817
  },
2124
2818
  children: [
2125
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2819
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2126
2820
  "svg",
2127
2821
  {
2128
2822
  viewBox: "0 0 16 16",
2129
2823
  width: "12",
2130
2824
  height: "12",
2131
2825
  "aria-hidden": true,
2132
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2826
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2133
2827
  "path",
2134
2828
  {
2135
2829
  d: "M9.2 1.2 3.4 9.1h3.5l-0.9 5.7 6-8.1H8.4l0.8-5.5Z",
@@ -2138,15 +2832,15 @@ function QuickCommandsButton({
2138
2832
  )
2139
2833
  }
2140
2834
  ),
2141
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u5FEB\u6377\u6307\u4EE4" })
2835
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "\u5FEB\u6377\u6307\u4EE4" })
2142
2836
  ]
2143
2837
  }
2144
2838
  );
2145
2839
  }
2146
2840
 
2147
2841
  // src/client/QuickCommandsPanel.tsx
2148
- var import_react5 = require("react");
2149
- var import_jsx_runtime5 = require("react/jsx-runtime");
2842
+ var import_react6 = require("react");
2843
+ var import_jsx_runtime8 = require("react/jsx-runtime");
2150
2844
  var MARGIN = 8;
2151
2845
  var GAP = 8;
2152
2846
  function QuickCommandsPanel({
@@ -2154,15 +2848,23 @@ function QuickCommandsPanel({
2154
2848
  usePanel,
2155
2849
  useBusy,
2156
2850
  useNotice,
2851
+ useBook,
2852
+ useBookStatus,
2157
2853
  actions
2158
2854
  }) {
2159
2855
  const settings = useLive((item) => item);
2160
2856
  const anchor = usePanel((item) => item);
2161
2857
  const busy = useBusy((item) => item);
2162
2858
  const notice = useNotice((item) => item);
2163
- const ref = (0, import_react5.useRef)(null);
2164
- const [position, setPosition] = (0, import_react5.useState)(null);
2165
- (0, import_react5.useLayoutEffect)(() => {
2859
+ const book = useBook((item) => item);
2860
+ const status = useBookStatus((item) => item);
2861
+ const ref = (0, import_react6.useRef)(null);
2862
+ const [position, setPosition] = (0, import_react6.useState)(null);
2863
+ const [activeId, setActiveId] = (0, import_react6.useState)("");
2864
+ const counts = bookCounts(book);
2865
+ const categories = book.categories;
2866
+ const active2 = categories.find((category) => category.id === activeId) ?? categories[0];
2867
+ (0, import_react6.useLayoutEffect)(() => {
2166
2868
  if (anchor === null) {
2167
2869
  setPosition(null);
2168
2870
  return;
@@ -2174,8 +2876,8 @@ function QuickCommandsPanel({
2174
2876
  const above = anchor.bottom - height - GAP;
2175
2877
  const top = above >= MARGIN ? above : Math.min(anchor.bottom + GAP, window.innerHeight - height - MARGIN);
2176
2878
  setPosition({ left, top: Math.max(MARGIN, top) });
2177
- }, [anchor?.left, anchor?.bottom, settings.quickPrompts.length, busy]);
2178
- (0, import_react5.useEffect)(() => {
2879
+ }, [anchor?.left, anchor?.bottom, counts.prompts, categories.length, busy]);
2880
+ (0, import_react6.useEffect)(() => {
2179
2881
  if (anchor === null) return;
2180
2882
  const onPointerDown = (event) => {
2181
2883
  if (ref.current !== null && event.target instanceof Node && ref.current.contains(event.target)) return;
@@ -2198,9 +2900,10 @@ function QuickCommandsPanel({
2198
2900
  };
2199
2901
  }, [anchor === null, actions]);
2200
2902
  if (anchor === null) return null;
2201
- const items = settings.quickPrompts;
2202
2903
  const tier = settings.optimizerTier;
2203
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2904
+ const items = active2?.prompts ?? [];
2905
+ const failure = status !== "" && status !== "saving" ? status : "";
2906
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2204
2907
  "div",
2205
2908
  {
2206
2909
  ref,
@@ -2208,10 +2911,10 @@ function QuickCommandsPanel({
2208
2911
  "aria-label": "\u5FEB\u6377\u6307\u4EE4",
2209
2912
  style: { ...quickPanel, left: position?.left ?? anchor.left, top: position?.top ?? anchor.bottom },
2210
2913
  children: [
2211
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: quickPanelHead, children: [
2212
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: quickPanelTitleRow, children: [
2213
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: quickPanelTitle, children: "\u5FEB\u6377\u6307\u4EE4" }),
2214
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: quickTierRow, role: "group", "aria-label": "\u4F18\u5316\u5F3A\u5EA6", children: OPTIMIZER_TIERS.map((item) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2914
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: quickPanelHead, children: [
2915
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: quickPanelTitleRow, children: [
2916
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: quickPanelTitle, children: "\u5FEB\u6377\u6307\u4EE4" }),
2917
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: quickTierRow, role: "group", "aria-label": "\u4F18\u5316\u5F3A\u5EA6", children: OPTIMIZER_TIERS.map((item) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2215
2918
  "button",
2216
2919
  {
2217
2920
  type: "button",
@@ -2229,7 +2932,7 @@ function QuickCommandsPanel({
2229
2932
  item.id
2230
2933
  )) })
2231
2934
  ] }),
2232
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2935
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2233
2936
  "button",
2234
2937
  {
2235
2938
  type: "button",
@@ -2243,20 +2946,55 @@ function QuickCommandsPanel({
2243
2946
  actions.optimize();
2244
2947
  },
2245
2948
  children: [
2246
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "aria-hidden": true, children: "\u2728" }),
2247
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: busy ? "\u4F18\u5316\u4E2D\u2026" : "\u4F18\u5316\u63D0\u793A\u8BCD" })
2949
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { "aria-hidden": true, children: "\u2728" }),
2950
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: busy ? "\u4F18\u5316\u4E2D\u2026" : "\u4F18\u5316\u63D0\u793A\u8BCD" })
2248
2951
  ]
2249
2952
  }
2250
- )
2953
+ ),
2954
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: quickCategoryRow, children: [
2955
+ categories.map((category) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2956
+ "button",
2957
+ {
2958
+ type: "button",
2959
+ title: `${category.name}\uFF08${String(category.prompts.length)} \u6761\uFF09`,
2960
+ "aria-pressed": category.id === active2?.id,
2961
+ style: category.id === active2?.id ? quickCategoryTabActive : quickCategoryTab,
2962
+ onMouseDown: (event) => {
2963
+ event.preventDefault();
2964
+ },
2965
+ onClick: () => {
2966
+ setActiveId(category.id);
2967
+ },
2968
+ children: category.name
2969
+ },
2970
+ category.id
2971
+ )),
2972
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2973
+ "button",
2974
+ {
2975
+ type: "button",
2976
+ style: quickCategoryAdd,
2977
+ disabled: categories.length >= QUICK_CATEGORY_MAX,
2978
+ title: "\u65B0\u589E\u4E00\u4E2A\u5206\u7C7B\uFF08\u540D\u5B57\u5230\u300C\u8BBE\u7F6E \u2192 \u8F93\u5165\u4F53\u9A8C\u300D\u91CC\u6539\uFF09",
2979
+ onMouseDown: (event) => {
2980
+ event.preventDefault();
2981
+ },
2982
+ onClick: () => {
2983
+ actions.addCategory(DEFAULT_CATEGORY_NAME);
2984
+ },
2985
+ children: "\uFF0B"
2986
+ }
2987
+ )
2988
+ ] })
2251
2989
  ] }),
2252
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: quickList, children: [
2253
- items.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: quickEmpty, children: [
2254
- "\u8FD8\u6CA1\u6709\u5FEB\u6377\u6307\u4EE4\u3002",
2255
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("br", {}),
2256
- "\u5728\u300C\u8BBE\u7F6E \u2192 \u8F93\u5165\u4F53\u9A8C \u2192 \u5FEB\u6377\u6307\u4EE4\u300D\u91CC\u6DFB\u52A0\u3002"
2990
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: quickList, children: [
2991
+ items.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: quickEmpty, children: [
2992
+ "\u8FD9\u4E2A\u5206\u7C7B\u91CC\u8FD8\u6CA1\u6709\u6761\u76EE\u3002",
2993
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("br", {}),
2994
+ "\u5728\u300C\u8BBE\u7F6E \u2192 \u8F93\u5165\u4F53\u9A8C \u2192 \u5FEB\u6377\u6307\u4EE4\u6E05\u5355\u300D\u91CC\u6DFB\u52A0\u3002"
2257
2995
  ] }),
2258
- items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
2259
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2996
+ items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
2997
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2260
2998
  "button",
2261
2999
  {
2262
3000
  type: "button",
@@ -2269,30 +3007,53 @@ function QuickCommandsPanel({
2269
3007
  actions.insert(item.prompt);
2270
3008
  },
2271
3009
  children: [
2272
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: quickItemLabel, children: item.label }),
2273
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: quickItemPreview, children: item.prompt.replace(/\s+/g, " ") })
3010
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: quickItemLabel, children: item.label }),
3011
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: quickItemPreview, children: item.prompt.replace(/\s+/g, " ") })
2274
3012
  ]
2275
3013
  }
2276
3014
  ),
2277
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { style: quickAlwaysLabel, title: "\u52FE\u4E0A\u540E\uFF0C\u70B9\u53D1\u9001\u65F6\u8FD9\u6761\u63D0\u793A\u8BCD\u4F1A\u81EA\u52A8\u9644\u52A0\u5230\u4F60\u7684\u6D88\u606F\u672B\u5C3E", children: [
2278
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2279
- "input",
2280
- {
2281
- type: "checkbox",
2282
- style: quickAlwaysBox,
2283
- checked: item.always,
2284
- onChange: (event) => {
2285
- actions.setAlways(item.id, event.target.checked);
2286
- }
3015
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3016
+ InsertModeControl,
3017
+ {
3018
+ compact: true,
3019
+ label: item.label,
3020
+ mode: insertModeOf(item),
3021
+ onChange: (mode) => {
3022
+ actions.setInsertMode(item.id, mode);
2287
3023
  }
2288
- ),
2289
- "\u9ED8\u8BA4\u63D2\u5165"
2290
- ] })
2291
- ] }, item.id))
3024
+ }
3025
+ )
3026
+ ] }, item.id)),
3027
+ active2 !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3028
+ AddPromptRow,
3029
+ {
3030
+ variant: "panel",
3031
+ book,
3032
+ categoryId: active2.id,
3033
+ onAdd: () => {
3034
+ actions.addPrompt(active2.id);
3035
+ },
3036
+ onMove: (promptId) => {
3037
+ actions.movePrompt(promptId, active2.id);
3038
+ }
3039
+ }
3040
+ )
2292
3041
  ] }),
2293
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: quickPanelFoot, children: [
2294
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: quickNotice, role: "status", children: notice }),
2295
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: quickFootHint, children: "\u8BBE\u7F6E \u2192 \u8F93\u5165\u4F53\u9A8C" })
3042
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: quickPanelFoot, children: [
3043
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: quickNotice, role: "status", children: notice !== "" ? notice : failure }),
3044
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3045
+ "span",
3046
+ {
3047
+ style: quickFootHint,
3048
+ title: "\u6570\u636E\u5728 ~/.dsh/quick-prompts.json",
3049
+ children: [
3050
+ counts.categories,
3051
+ " \u5206\u7C7B \xB7 ",
3052
+ counts.prompts,
3053
+ " \u6761"
3054
+ ]
3055
+ }
3056
+ )
2296
3057
  ] })
2297
3058
  ]
2298
3059
  }
@@ -2326,6 +3087,77 @@ function apply(ctx) {
2326
3087
  console.error("[composer-ux] settings clear failed", error);
2327
3088
  });
2328
3089
  };
3090
+ const book = (0, import_dsh_client_store.createSnapshotStore)(defaultQuickBook());
3091
+ const bookStatus = (0, import_dsh_client_store.createSnapshotStore)("");
3092
+ const reloadBook = async () => {
3093
+ const reply = await loadPromptBook();
3094
+ if (reply.ok && reply.book !== void 0) {
3095
+ book.set(reply.book);
3096
+ bookStatus.set("");
3097
+ return;
3098
+ }
3099
+ bookStatus.set(reply.error ?? "\u8BFB\u53D6\u5FEB\u6377\u6307\u4EE4\u5931\u8D25");
3100
+ };
3101
+ const commitBook = (next) => {
3102
+ book.set(next);
3103
+ bookStatus.set("saving");
3104
+ void savePromptBook(next).then(
3105
+ (reply) => {
3106
+ if (reply.ok && reply.book !== void 0) {
3107
+ book.set(reply.book);
3108
+ bookStatus.set("");
3109
+ return;
3110
+ }
3111
+ bookStatus.set(reply.error ?? "\u4FDD\u5B58\u5931\u8D25");
3112
+ void reloadBook();
3113
+ },
3114
+ (error) => {
3115
+ bookStatus.set(error instanceof Error ? error.message : String(error));
3116
+ void reloadBook();
3117
+ }
3118
+ );
3119
+ };
3120
+ void reloadBook();
3121
+ const bookActions = {
3122
+ reload: () => {
3123
+ void reloadBook();
3124
+ },
3125
+ addCategory: (name2) => {
3126
+ commitBook(withCategoryAdded(book.getSnapshot(), name2));
3127
+ },
3128
+ renameCategory: (id, name2) => {
3129
+ commitBook(withCategoryRenamed(book.getSnapshot(), id, name2));
3130
+ },
3131
+ removeCategory: (id) => {
3132
+ commitBook(withCategoryRemoved(book.getSnapshot(), id));
3133
+ },
3134
+ moveCategory: (id, delta) => {
3135
+ commitBook(withCategoryMoved(book.getSnapshot(), id, delta));
3136
+ },
3137
+ addPrompt: (categoryId) => {
3138
+ commitBook(withPromptAdded(book.getSnapshot(), categoryId));
3139
+ },
3140
+ patchPrompt: (categoryId, id, patch) => {
3141
+ commitBook(withPromptPatched(book.getSnapshot(), categoryId, id, patch));
3142
+ },
3143
+ removePrompt: (categoryId, id) => {
3144
+ commitBook(withPromptRemoved(book.getSnapshot(), categoryId, id));
3145
+ },
3146
+ movePrompt: (categoryId, id, delta) => {
3147
+ commitBook(withPromptMoved(book.getSnapshot(), categoryId, id, delta));
3148
+ },
3149
+ resetBook: () => {
3150
+ commitBook(defaultQuickBook());
3151
+ },
3152
+ /** 把某条从它所在分类移到目标分类(「移动到这里」:追加到目标末尾)。 */
3153
+ movePromptToCategory: (promptId, toCategoryId) => {
3154
+ commitBook(withPromptMovedToCategory(book.getSnapshot(), promptId, toCategoryId));
3155
+ },
3156
+ /** 设置页「保存」:把编辑器里的整本一次写回(而不是每敲一个字就写盘)。 */
3157
+ saveBook: (next) => {
3158
+ commitBook(next);
3159
+ }
3160
+ };
2329
3161
  const resetAll = () => {
2330
3162
  void scope.mutate(
2331
3163
  [
@@ -2333,6 +3165,8 @@ function apply(ctx) {
2333
3165
  SEND_KEY_FIELD,
2334
3166
  NEWLINE_KEY_FIELD,
2335
3167
  ...MENU_FIELDS,
3168
+ MENU_MODE_FIELD,
3169
+ // 旧的布尔字段也一并清掉:它只是迁移线索,留着会把「恢复默认」后的档位又拉回旧值。
2336
3170
  MENU_NATIVE_FIELD,
2337
3171
  PANEL_SCROLL_FIELD,
2338
3172
  PANEL_RESIZE_FIELD,
@@ -2342,12 +3176,12 @@ function apply(ctx) {
2342
3176
  HEADER_NAME_FIELD,
2343
3177
  HEADER_VALUE_FIELD,
2344
3178
  HEADER_ROUTES_FIELD,
2345
- QUICK_PROMPTS_FIELD,
2346
3179
  OPTIMIZER_TIER_FIELD
2347
3180
  ].map((field) => ({ op: "unset", path: [field] }))
2348
3181
  ).catch((error) => {
2349
3182
  console.error("[composer-ux] settings reset failed", error);
2350
3183
  });
3184
+ bookActions.resetBook();
2351
3185
  };
2352
3186
  let noticeTimer;
2353
3187
  const note = (message, ms = 4500) => {
@@ -2361,9 +3195,6 @@ function apply(ctx) {
2361
3195
  ctx.effect(() => () => {
2362
3196
  if (noticeTimer !== void 0) clearTimeout(noticeTimer);
2363
3197
  }, "composer-ux: quick notice timer");
2364
- const writeQuickPrompts = (next) => {
2365
- setField(QUICK_PROMPTS_FIELD, next);
2366
- };
2367
3198
  const quickActions = {
2368
3199
  toggle: (anchor) => {
2369
3200
  if (panel.getSnapshot() !== null) {
@@ -2410,10 +3241,14 @@ function apply(ctx) {
2410
3241
  setTier: (tier) => {
2411
3242
  setField(OPTIMIZER_TIER_FIELD, tier);
2412
3243
  },
2413
- setAlways: (id, value) => {
2414
- writeQuickPrompts(live.getSnapshot().quickPrompts.map(
2415
- (item) => item.id === id ? { ...item, always: value } : item
2416
- ));
3244
+ /**
3245
+ * 设置某条的插入模式(关 / 每次 / 仅首次)。
3246
+ *
3247
+ * 界面上只有这一个入口:两个互斥标志由 `withInsertMode` **一次写对**,
3248
+ * 不给「又每次又仅首次」留缝。
3249
+ */
3250
+ setInsertMode: (promptId, mode) => {
3251
+ commitBook(withInsertMode(book.getSnapshot(), promptId, mode));
2417
3252
  }
2418
3253
  };
2419
3254
  slots.inject("settings.section", () => slots.register({
@@ -2422,8 +3257,15 @@ function apply(ctx) {
2422
3257
  order: 40,
2423
3258
  label: "\u8F93\u5165\u4F53\u9A8C",
2424
3259
  inject: () => ({
2425
- hooks: { live },
2426
- actions: { setField, clearField, resetAll }
3260
+ hooks: { live, book, bookStatus },
3261
+ actions: {
3262
+ setField,
3263
+ clearField,
3264
+ resetAll,
3265
+ saveBook: bookActions.saveBook,
3266
+ reloadBook: bookActions.reload,
3267
+ resetBook: bookActions.resetBook
3268
+ }
2427
3269
  })
2428
3270
  }, SettingsSection));
2429
3271
  slots.inject("shell.overlay", () => slots.register({
@@ -2463,8 +3305,22 @@ function apply(ctx) {
2463
3305
  name: "shell.overlay",
2464
3306
  id: "composer-ux-quick-panel",
2465
3307
  inject: () => ({
2466
- hooks: { live, panel, busy: optimizing, notice: panelNotice },
2467
- actions: quickActions
3308
+ hooks: { live, panel, busy: optimizing, notice: panelNotice, book, bookStatus },
3309
+ actions: {
3310
+ toggle: quickActions.toggle,
3311
+ close: quickActions.close,
3312
+ insert: quickActions.insert,
3313
+ optimize: quickActions.optimize,
3314
+ setTier: quickActions.setTier,
3315
+ setInsertMode: quickActions.setInsertMode,
3316
+ addCategory: bookActions.addCategory,
3317
+ renameCategory: bookActions.renameCategory,
3318
+ removeCategory: bookActions.removeCategory,
3319
+ moveCategory: bookActions.moveCategory,
3320
+ addPrompt: bookActions.addPrompt,
3321
+ movePrompt: bookActions.movePromptToCategory,
3322
+ reloadBook: bookActions.reload
3323
+ }
2468
3324
  })
2469
3325
  }, QuickCommandsPanel));
2470
3326
  ctx.effect(() => installSettingsCardStyle(), "composer-ux: settings card style");
@@ -2486,6 +3342,8 @@ function apply(ctx) {
2486
3342
  if (enabled && dispose === null) {
2487
3343
  dispose = installInterceptors({
2488
3344
  settings: () => live.getSnapshot(),
3345
+ // 「仅首次」由这里决定是否算进这一批:会话还是空的(blank)才带上。
3346
+ promptsForSend: () => appendBatchForSend(book.getSnapshot(), currentBlankSession()),
2489
3347
  setMenu: (state2) => {
2490
3348
  menu2.set(state2);
2491
3349
  },
@@ -2497,7 +3355,7 @@ function apply(ctx) {
2497
3355
  menu2.set(null);
2498
3356
  panel.set(null);
2499
3357
  }
2500
- if (live.getSnapshot().menuNative) menu2.set(null);
3358
+ if (live.getSnapshot().menuMode !== "custom") menu2.set(null);
2501
3359
  if (!live.getSnapshot().enabled) panel.set(null);
2502
3360
  };
2503
3361
  syncInterceptors();