dsh-more 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CONTRIBUTING.md +349 -0
  2. package/LICENSE +21 -0
  3. package/README.md +232 -0
  4. package/README.zh-CN.md +232 -0
  5. package/cordis.patch.yml +4 -0
  6. package/dist/client.js +954 -0
  7. package/dist/index.js +937 -0
  8. package/dist/types/client/index.d.ts +3 -0
  9. package/dist/types/generated/client-registry.d.ts +3 -0
  10. package/dist/types/generated/host-registry.d.ts +2 -0
  11. package/dist/types/generated/host-settings-schema.d.ts +10 -0
  12. package/dist/types/generated/patch-catalog.d.ts +30 -0
  13. package/dist/types/index.d.ts +4 -0
  14. package/dist/types/kernel/client/activation.d.ts +19 -0
  15. package/dist/types/kernel/client/config-card.d.ts +11 -0
  16. package/dist/types/kernel/client/message-actions.d.ts +14 -0
  17. package/dist/types/kernel/client/patch.d.ts +16 -0
  18. package/dist/types/kernel/host/activation.d.ts +14 -0
  19. package/dist/types/kernel/host/patch.d.ts +12 -0
  20. package/dist/types/kernel/host/settings.d.ts +5 -0
  21. package/dist/types/kernel/message-visibility.d.ts +6 -0
  22. package/dist/types/patches/message-delete/client/index.d.ts +5 -0
  23. package/dist/types/patches/message-delete/host/index.d.ts +2 -0
  24. package/dist/types/patches/message-delete/host/message-selection.d.ts +7 -0
  25. package/dist/types/patches/message-delete/host/projection.d.ts +8 -0
  26. package/dist/types/patches/message-delete/host/rebuild.d.ts +9 -0
  27. package/dist/types/patches/message-delete/shared.d.ts +6 -0
  28. package/dist/types/patches/message-edit/client/index.d.ts +5 -0
  29. package/dist/types/patches/message-edit/host/edit-continuation.d.ts +15 -0
  30. package/dist/types/patches/message-edit/host/index.d.ts +2 -0
  31. package/dist/types/patches/message-edit/shared.d.ts +7 -0
  32. package/dist/types/patches/session-delete/client/index.d.ts +4 -0
  33. package/dist/types/patches/session-delete/host/index.d.ts +2 -0
  34. package/dist/types/patches/session-delete/host/live-session-handles.d.ts +7 -0
  35. package/dist/types/patches/session-delete/host/session-deletion.d.ts +6 -0
  36. package/dist/types/patches/session-delete/shared.d.ts +1 -0
  37. package/dist/types/platform/dsh/client/api.d.ts +6 -0
  38. package/dist/types/platform/dsh/client/message-content.d.ts +2 -0
  39. package/dist/types/platform/dsh/client/message-targets.d.ts +2 -0
  40. package/dist/types/platform/dsh/client/runtime.d.ts +7 -0
  41. package/dist/types/platform/dsh/client/session-sync.d.ts +18 -0
  42. package/dist/types/platform/dsh/client/styles.d.ts +1 -0
  43. package/dist/types/platform/dsh/host/agent-session.d.ts +6 -0
  44. package/dist/types/platform/dsh/host/api-router.d.ts +3 -0
  45. package/dist/types/platform/dsh/host/confirmation.d.ts +14 -0
  46. package/dist/types/platform/dsh/host/error.d.ts +6 -0
  47. package/dist/types/platform/dsh/host/runtime-context.d.ts +4 -0
  48. package/dist/types/platform/dsh/host/session-history.d.ts +8 -0
  49. package/dist/types/platform/dsh/host/trust.d.ts +3 -0
  50. package/dist/types/platform/dsh/host/wire.d.ts +6 -0
  51. package/dist/types/platform/dsh/identity.d.ts +4 -0
  52. package/docs/assets/dsh-more-architecture.jpg +0 -0
  53. package/docs/assets/dsh-more-architecture.zh-CN.jpg +0 -0
  54. package/docs/assets/dsh-more-safe-mutation-flow.jpg +0 -0
  55. package/docs/assets/dsh-more-safe-mutation-flow.zh-CN.jpg +0 -0
  56. package/dsh.plugin.json +12 -0
  57. package/package.json +140 -0
package/dist/client.js ADDED
@@ -0,0 +1,954 @@
1
+ window.__ModuleLoader__.load({ id: "dsh-more", factory: (require) => { var module = { exports: {} }; var exports = module.exports;
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/client/index.tsx
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ apply: () => apply,
25
+ inject: () => inject
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+ var import_react6 = require("react");
29
+ var import_react_dom = require("react-dom");
30
+
31
+ // src/generated/patch-catalog.ts
32
+ var PATCH_CATALOG = [
33
+ { "id": "message-edit", "name": "\u7F16\u8F91\u5E76\u91CD\u65B0\u5F00\u59CB", "description": "\u7F16\u8F91\u7528\u6237\u6D88\u606F\u5E76\u4E22\u5F03\u8BE5\u6D88\u606F\u4E4B\u540E\u7684\u5386\u53F2", "defaultEnabled": true, "order": 100, "clientKind": "message-action" },
34
+ { "id": "message-delete", "name": "\u5355\u6761\u6D88\u606F\u5220\u9664", "description": "\u5220\u9664\u6307\u5B9A\u6D88\u606F\u5E76\u4FDD\u7559\u524D\u540E\u5386\u53F2\u8BB0\u5F55", "defaultEnabled": true, "order": 200, "clientKind": "message-action" },
35
+ { "id": "session-delete", "name": "\u6C38\u4E45\u5220\u9664\u4F1A\u8BDD", "description": "\u505C\u6B62\u76EE\u6807\u4F1A\u8BDD\u5E76\u4ECE\u78C1\u76D8\u6C38\u4E45\u5220\u9664\uFF0C\u4E0D\u5F71\u54CD\u539F\u751F\u5F52\u6863", "defaultEnabled": true, "order": 300, "clientKind": "standalone" }
36
+ ];
37
+ var DEFAULT_PATCH_SETTINGS = {
38
+ "message-edit": true,
39
+ "message-delete": true,
40
+ "session-delete": true
41
+ };
42
+ function decodePatchSettings(value) {
43
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
44
+ const candidate = value;
45
+ const decoded = { ...DEFAULT_PATCH_SETTINGS };
46
+ for (const patch of PATCH_CATALOG) {
47
+ const enabled = candidate[patch.id];
48
+ if (enabled !== void 0 && typeof enabled !== "boolean") return void 0;
49
+ if (typeof enabled === "boolean") decoded[patch.id] = enabled;
50
+ }
51
+ return decoded;
52
+ }
53
+
54
+ // src/patches/message-edit/client/index.tsx
55
+ var import_react = require("react");
56
+ var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
57
+
58
+ // src/platform/dsh/identity.ts
59
+ var PLUGIN_NAME = "dsh-more";
60
+ var PLUGIN_API_PREFIX = `/${PLUGIN_NAME}/api`;
61
+ var PLUGIN_MUTATION_HEADER = "x-dsh-more";
62
+ var PATCH_SETTINGS_NAMESPACE = PLUGIN_NAME;
63
+
64
+ // src/platform/dsh/client/api.ts
65
+ var DshMoreApiError = class extends Error {
66
+ constructor(code, message) {
67
+ super(message);
68
+ this.code = code;
69
+ this.name = "DshMoreApiError";
70
+ }
71
+ };
72
+ async function callPatchApi(patchId, action, payload) {
73
+ let response;
74
+ try {
75
+ response = await fetch(`${PLUGIN_API_PREFIX}/${encodeURIComponent(patchId)}/${encodeURIComponent(action)}`, {
76
+ method: "POST",
77
+ headers: {
78
+ "content-type": "application/json",
79
+ [PLUGIN_MUTATION_HEADER]: "1"
80
+ },
81
+ body: JSON.stringify(payload)
82
+ });
83
+ } catch (error) {
84
+ throw new DshMoreApiError("network", error instanceof Error ? error.message : String(error));
85
+ }
86
+ const body = await response.json().catch(() => null);
87
+ if (!response.ok || body?.ok !== true || body.value === void 0) {
88
+ throw new DshMoreApiError(body?.error?.code ?? "http", body?.error?.message ?? `HTTP ${String(response.status)}`);
89
+ }
90
+ return body.value;
91
+ }
92
+ function apiErrorText(error) {
93
+ if (error instanceof DshMoreApiError || error instanceof Error) return error.message;
94
+ return String(error);
95
+ }
96
+
97
+ // src/platform/dsh/client/session-sync.ts
98
+ var INCREMENTAL_SYNC_GRACE_MS = 180;
99
+ function openSessionWithoutGap(sessions, sessionId) {
100
+ sessions.open(sessionId);
101
+ }
102
+ function delay(milliseconds) {
103
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
104
+ }
105
+ function followSessionHandoff(sessions, targetSessionId) {
106
+ let disposed = false;
107
+ let opened = false;
108
+ let unsubscribe = () => void 0;
109
+ let resolveArrival = () => void 0;
110
+ const arrival = new Promise((resolve) => {
111
+ resolveArrival = resolve;
112
+ });
113
+ const reconcile = () => {
114
+ if (disposed || opened || sessions.list.getSnapshot().byId[targetSessionId] === void 0) return;
115
+ opened = true;
116
+ unsubscribe();
117
+ resolveArrival();
118
+ openSessionWithoutGap(sessions, targetSessionId);
119
+ };
120
+ unsubscribe = sessions.list.subscribe(reconcile);
121
+ reconcile();
122
+ return {
123
+ finish: async (returnedSessionId) => {
124
+ if (returnedSessionId !== targetSessionId) {
125
+ throw new Error("Host \u8FD4\u56DE\u7684\u65B0\u4F1A\u8BDD\u4E0E\u9884\u89C8\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u91CD\u65B0\u64CD\u4F5C\u3002");
126
+ }
127
+ if (!opened) await Promise.race([arrival, delay(INCREMENTAL_SYNC_GRACE_MS)]);
128
+ if (!opened) {
129
+ await sessions.refresh();
130
+ reconcile();
131
+ }
132
+ if (!opened) throw new Error("\u65B0\u4F1A\u8BDD\u5DF2\u521B\u5EFA\uFF0C\u4F46\u5BA2\u6237\u7AEF\u5217\u8868\u5C1A\u672A\u540C\u6B65\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
133
+ },
134
+ cancel: () => {
135
+ disposed = true;
136
+ unsubscribe();
137
+ }
138
+ };
139
+ }
140
+ function sessionRemovalSettled(sessions, workspaces, sessionId) {
141
+ return sessions.byId[sessionId] === void 0 && workspaces.items.every((workspace) => !workspace.sessionIds.includes(sessionId));
142
+ }
143
+ async function settleSessionRemoval(sessions, workspaces, sessionId) {
144
+ const settled = () => sessionRemovalSettled(
145
+ sessions.list.getSnapshot(),
146
+ workspaces.list.getSnapshot(),
147
+ sessionId
148
+ );
149
+ if (settled()) return;
150
+ await new Promise((resolve) => {
151
+ let done = false;
152
+ let timer;
153
+ const finish = () => {
154
+ if (done) return;
155
+ done = true;
156
+ clearTimeout(timer);
157
+ unsubscribeSessions();
158
+ unsubscribeWorkspaces();
159
+ resolve();
160
+ };
161
+ const reconcile = () => {
162
+ if (settled()) finish();
163
+ };
164
+ const unsubscribeSessions = sessions.list.subscribe(reconcile);
165
+ const unsubscribeWorkspaces = workspaces.list.subscribe(reconcile);
166
+ timer = setTimeout(finish, INCREMENTAL_SYNC_GRACE_MS);
167
+ reconcile();
168
+ });
169
+ if (settled()) return;
170
+ await Promise.all([sessions.refresh(), workspaces.refresh()]);
171
+ if (!settled()) throw new Error("\u4F1A\u8BDD\u5DF2\u5220\u9664\uFF0C\u4F46\u5BA2\u6237\u7AEF\u5217\u8868\u5C1A\u672A\u540C\u6B65\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u9875\u9762\u3002");
172
+ }
173
+ function adjacentVisibleSession(sessions, workspaces, deletingSessionId) {
174
+ if (sessions.current !== deletingSessionId) return void 0;
175
+ const archived = new Set(workspaces.archivedSessionIds);
176
+ const visible = sessions.ids.filter((id) => id !== deletingSessionId && !archived.has(id));
177
+ if (visible.length === 0) return void 0;
178
+ const deletingIndex = sessions.ids.indexOf(deletingSessionId);
179
+ return visible.find((id) => sessions.ids.indexOf(id) > deletingIndex) ?? visible.at(-1);
180
+ }
181
+
182
+ // src/patches/message-edit/shared.ts
183
+ var MESSAGE_EDIT_PATCH_ID = "message-edit";
184
+
185
+ // src/patches/message-edit/client/index.tsx
186
+ var import_jsx_runtime = require("react/jsx-runtime");
187
+ var api = {
188
+ preview: (payload) => callPatchApi(MESSAGE_EDIT_PATCH_ID, "preview", payload),
189
+ commit: (payload) => callPatchApi(MESSAGE_EDIT_PATCH_ID, "commit", payload)
190
+ };
191
+ function useMessageActions(props, enabled) {
192
+ const [target, setTarget] = (0, import_react.useState)(null);
193
+ const [text, setText] = (0, import_react.useState)("");
194
+ const [preview, setPreview] = (0, import_react.useState)(null);
195
+ const [busy, setBusy] = (0, import_react.useState)(false);
196
+ const [error, setError] = (0, import_react.useState)(null);
197
+ (0, import_react.useEffect)(() => {
198
+ if (enabled) return;
199
+ setTarget(null);
200
+ setPreview(null);
201
+ setError(null);
202
+ }, [enabled]);
203
+ const open = (next) => {
204
+ setTarget(next);
205
+ setText(next.text);
206
+ setPreview(null);
207
+ setError(null);
208
+ };
209
+ const previewChange = async () => {
210
+ if (target === null) return;
211
+ setBusy(true);
212
+ setError(null);
213
+ try {
214
+ setPreview(await api.preview({ sessionId: props.sessionId, targetSeq: target.seq, text }));
215
+ } catch (caught) {
216
+ setError(apiErrorText(caught));
217
+ } finally {
218
+ setBusy(false);
219
+ }
220
+ };
221
+ const commit = async () => {
222
+ if (target === null || preview === null) return;
223
+ setBusy(true);
224
+ setError(null);
225
+ const sessions = props.ctx.sessions;
226
+ const continuationSessionId = preview.continuationSessionId;
227
+ const handoff = followSessionHandoff(sessions, continuationSessionId);
228
+ try {
229
+ const result = await api.commit({
230
+ sessionId: props.sessionId,
231
+ targetSeq: target.seq,
232
+ text,
233
+ continuationSessionId,
234
+ confirmToken: preview.confirmToken
235
+ });
236
+ await handoff.finish(result.sessionId);
237
+ setTarget(null);
238
+ } catch (caught) {
239
+ handoff.cancel();
240
+ const sourceId = props.sessionId;
241
+ const sessionList = sessions.list.getSnapshot();
242
+ const sourceAvailable = sessionList.byId[sourceId] !== void 0;
243
+ const sourceArchived = props.ctx.workspaces.list.getSnapshot().archivedSessionIds.includes(sourceId);
244
+ if (sourceAvailable && !sourceArchived && sessionList.current !== sourceId) openSessionWithoutGap(sessions, sourceId);
245
+ setPreview(null);
246
+ setError(apiErrorText(caught));
247
+ } finally {
248
+ setBusy(false);
249
+ }
250
+ };
251
+ return {
252
+ renderAction: (candidate) => enabled && candidate.kind === "user" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tooltip, { label: "\u7F16\u8F91\u5E76\u4ECE\u8FD9\u91CC\u91CD\u65B0\u5F00\u59CB", side: "bottom", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dshmore-inline-button", "aria-label": "\u7F16\u8F91\u5E76\u4ECE\u8FD9\u91CC\u91CD\u65B0\u5F00\u59CB", onClick: (event) => {
253
+ event.stopPropagation();
254
+ open(candidate);
255
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconEditOutline16, { size: 16 }) }) }) : null,
256
+ overlay: enabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
257
+ import_dsh_client_ui_primitives.Modal,
258
+ {
259
+ open: target !== null,
260
+ onClose: () => !busy && setTarget(null),
261
+ title: "\u7F16\u8F91\u6D88\u606F\u5E76\u4ECE\u8FD9\u91CC\u91CD\u65B0\u5F00\u59CB",
262
+ closeLabel: "\u5173\u95ED",
263
+ className: "dshmore-edit-modal",
264
+ description: "\u4FEE\u6539\u540E\u4F1A\u521B\u5EFA\u5E72\u51C0\u7684\u524D\u7F00\u4F1A\u8BDD\uFF0C\u8FD9\u6761\u6D88\u606F\u4E4B\u540E\u7684\u5185\u5BB9\u4E0D\u4F1A\u4FDD\u7559\u3002",
265
+ footer: preview === null ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
266
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", disabled: busy, onClick: () => setTarget(null), children: "\u53D6\u6D88" }),
267
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: busy || text.trim() === "", onClick: () => void previewChange(), children: busy ? "\u6B63\u5728\u68C0\u67E5\u2026" : "\u9884\u89C8\u4FEE\u6539" })
268
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
269
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", disabled: busy, onClick: () => setPreview(null), children: "\u8FD4\u56DE\u4FEE\u6539" }),
270
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: busy, onClick: () => void commit(), children: busy ? "\u6B63\u5728\u91CD\u5EFA\u2026" : "\u786E\u8BA4\u7F16\u8F91\u5E76\u4E22\u5F03\u540E\u7EED" })
271
+ ] }),
272
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dshmore-dialog-body", children: [
273
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
274
+ "textarea",
275
+ {
276
+ className: "dshmore-editor",
277
+ value: text,
278
+ disabled: busy || preview !== null,
279
+ onChange: (event) => {
280
+ setText(event.currentTarget.value);
281
+ setPreview(null);
282
+ setError(null);
283
+ },
284
+ rows: 7,
285
+ autoFocus: true
286
+ }
287
+ ),
288
+ preview !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dshmore-warning", children: [
289
+ "\u5C06\u4ECE\u7B2C ",
290
+ preview.turn,
291
+ " \u8F6E\u4E4B\u524D\u91CD\u5EFA\u4F1A\u8BDD\uFF0C\u5E76\u4E22\u5F03\u5F53\u524D\u8F6E\u53CA\u4E4B\u540E\u5171 ",
292
+ preview.laterTurnCount,
293
+ " \u8F6E\u5185\u5BB9\u3002"
294
+ ] }),
295
+ error !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dshmore-error", children: error })
296
+ ] })
297
+ }
298
+ ) : null
299
+ };
300
+ }
301
+
302
+ // src/patches/message-delete/client/index.tsx
303
+ var import_react2 = require("react");
304
+ var import_dsh_client_ui_primitives2 = require("@deepseek-ai/dsh-client-ui-primitives");
305
+
306
+ // src/patches/message-delete/shared.ts
307
+ var MESSAGE_DELETE_PATCH_ID = "message-delete";
308
+
309
+ // src/patches/message-delete/client/index.tsx
310
+ var import_jsx_runtime2 = require("react/jsx-runtime");
311
+ var api2 = {
312
+ preview: (payload) => callPatchApi(MESSAGE_DELETE_PATCH_ID, "preview", payload),
313
+ commit: (payload) => callPatchApi(MESSAGE_DELETE_PATCH_ID, "commit", payload)
314
+ };
315
+ function useMessageActions2(props, enabled) {
316
+ const [target, setTarget] = (0, import_react2.useState)(null);
317
+ const [preview, setPreview] = (0, import_react2.useState)(null);
318
+ const [busy, setBusy] = (0, import_react2.useState)(false);
319
+ const [error, setError] = (0, import_react2.useState)(null);
320
+ (0, import_react2.useEffect)(() => {
321
+ if (enabled) return;
322
+ setTarget(null);
323
+ setPreview(null);
324
+ setError(null);
325
+ }, [enabled]);
326
+ const open = (0, import_react2.useCallback)(async (next) => {
327
+ setTarget(next);
328
+ setPreview(null);
329
+ setError(null);
330
+ setBusy(true);
331
+ try {
332
+ setPreview(await api2.preview({ sessionId: props.sessionId, targetSeq: next.seq }));
333
+ } catch (caught) {
334
+ setError(apiErrorText(caught));
335
+ } finally {
336
+ setBusy(false);
337
+ }
338
+ }, [props.sessionId]);
339
+ const commit = async () => {
340
+ if (target === null || preview === null) return;
341
+ setBusy(true);
342
+ setError(null);
343
+ const sessions = props.ctx.sessions;
344
+ const continuationSessionId = preview.continuationSessionId;
345
+ const handoff = followSessionHandoff(sessions, continuationSessionId);
346
+ try {
347
+ const result = await api2.commit({
348
+ sessionId: props.sessionId,
349
+ targetSeq: target.seq,
350
+ continuationSessionId,
351
+ confirmToken: preview.confirmToken
352
+ });
353
+ await handoff.finish(result.sessionId);
354
+ setTarget(null);
355
+ setPreview(null);
356
+ } catch (caught) {
357
+ handoff.cancel();
358
+ const sourceId = props.sessionId;
359
+ const sessionList = sessions.list.getSnapshot();
360
+ const sourceAvailable = sessionList.byId[sourceId] !== void 0;
361
+ const sourceArchived = props.ctx.workspaces.list.getSnapshot().archivedSessionIds.includes(sourceId);
362
+ if (sourceAvailable && !sourceArchived && sessionList.current !== sourceId) openSessionWithoutGap(sessions, sourceId);
363
+ setPreview(null);
364
+ setError(apiErrorText(caught));
365
+ } finally {
366
+ setBusy(false);
367
+ }
368
+ };
369
+ return {
370
+ renderAction: (candidate) => enabled ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Tooltip, { label: "\u5220\u9664\u8FD9\u6761\u6D88\u606F", side: "bottom", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dshmore-inline-button", "aria-label": "\u5220\u9664\u8FD9\u6761\u6D88\u606F", onClick: (event) => {
371
+ event.stopPropagation();
372
+ void open(candidate);
373
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.IconTrashOutline16, { size: 16 }) }) }) : null,
374
+ overlay: enabled ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
375
+ import_dsh_client_ui_primitives2.Modal,
376
+ {
377
+ open: target !== null,
378
+ onClose: () => !busy && setTarget(null),
379
+ title: "\u5220\u9664\u8FD9\u6761\u6D88\u606F\uFF1F",
380
+ closeLabel: "\u5173\u95ED",
381
+ description: "\u8FD9\u6761\u6D88\u606F\u4F1A\u7ACB\u5373\u4ECE\u5BF9\u8BDD\u9762\u677F\u548C\u6A21\u578B\u4E0A\u4E0B\u6587\u4E2D\u6D88\u5931\u3002",
382
+ footer: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
383
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "ghost", disabled: busy, onClick: () => setTarget(null), children: "\u53D6\u6D88" }),
384
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives2.Button, { variant: "primary", disabled: busy || preview === null, onClick: () => void commit(), children: busy ? "\u6B63\u5728\u68C0\u67E5\u2026" : "\u786E\u8BA4\u5220\u9664" })
385
+ ] }),
386
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dshmore-dialog-body", children: [
387
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dshmore-message-preview", children: target?.text || "\uFF08\u65E0\u6587\u672C\u5185\u5BB9\uFF09" }),
388
+ preview !== null && preview.affectedNodeCount > 1 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dshmore-warning", children: [
389
+ "\u4E3A\u4FDD\u6301\u5DE5\u5177\u8C03\u7528\u5B8C\u6574\uFF0C\u5C06\u540C\u65F6\u79FB\u9664\u5173\u8054\u7684 ",
390
+ preview.affectedNodeCount - 1,
391
+ " \u4E2A\u4E0A\u4E0B\u6587\u8282\u70B9\u3002"
392
+ ] }),
393
+ error !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dshmore-error", children: error })
394
+ ] })
395
+ }
396
+ ) : null
397
+ };
398
+ }
399
+
400
+ // src/patches/session-delete/client/index.tsx
401
+ var import_react3 = require("react");
402
+ var import_dsh_client_ui_primitives3 = require("@deepseek-ai/dsh-client-ui-primitives");
403
+
404
+ // src/platform/dsh/client/styles.ts
405
+ var styles = `
406
+ .dshmore-inline-actions { display: inline-flex; align-items: center; gap: 10px; }
407
+ .dshmore-inline-button { width: 28px; height: 28px; color: var(--dsw-alias-label-tertiary); cursor: pointer; background: transparent; border: 0; border-radius: 28px; display: inline-flex; align-items: center; justify-content: center; padding: 6px; }
408
+ .dshmore-inline-button:hover { color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-interactive-bg-hover); }
409
+ .dshmore-edit-modal { box-sizing: border-box; width: min(620px, calc(100vw - 32px)) !important; max-width: min(620px, calc(100vw - 32px)) !important; }
410
+ .dshmore-dialog-body { box-sizing: border-box; display: grid; gap: 12px; width: 100%; min-width: 0; overflow: hidden; }
411
+ .dshmore-message-preview { box-sizing: border-box; width: 100%; min-width: 0; max-height: 132px; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word; padding: 12px; border-radius: 10px; color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-bg-layer-1); font-size: 13px; line-height: 1.55; }
412
+ .dshmore-editor { box-sizing: border-box; display: block; width: 100%; min-width: 0; max-width: 100%; resize: vertical; min-height: 150px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 10px; outline: none; padding: 12px; color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-1); font: inherit; line-height: 1.55; overflow-x: hidden; overflow-wrap: anywhere; word-break: break-word; }
413
+ .dshmore-editor:focus { border-color: var(--dsw-alias-state-business-primary); }
414
+ .dshmore-warning { padding: 10px 12px; border: 1px solid rgba(220,138,40,.35); border-radius: 10px; background: rgba(220,138,40,.09); color: var(--dsw-alias-label-secondary); font-size: 12px; line-height: 1.5; }
415
+ .dshmore-danger { display: flex; gap: 8px; align-items: flex-start; padding: 10px 12px; border-radius: 10px; background: rgba(208,58,58,.10); color: var(--dsw-alias-state-error-primary, rgb(220,90,90)); font-size: 12px; line-height: 1.5; }
416
+ .dshmore-error { padding: 10px 12px; border-radius: 10px; background: rgba(208,58,58,.10); color: var(--dsw-alias-state-error-primary, rgb(220,90,90)); font-size: 12px; line-height: 1.5; }
417
+ @media (max-width: 620px) { .dshmore-edit-modal { width: calc(100vw - 24px) !important; max-width: calc(100vw - 24px) !important; } }
418
+ .dshmore-action-contribution { display: inline-flex; align-items: center; order: var(--dshmore-action-order); }
419
+ .dshmore-config-card { box-sizing: border-box; list-style: none; border: 1px solid var(--dsw-alias-border-l2); border-radius: 12px; background: var(--dsw-alias-bg-layer-3); overflow: hidden; transition: border-color .16s, background .16s; }
420
+ .dshmore-config-card:hover { border-color: var(--dsw-alias-label-dimmed); }
421
+ .dshmore-config-card-open { border-color: var(--dsw-alias-label-dimmed); background: var(--dsw-alias-bg-layer-2); }
422
+ .dshmore-config-heading { appearance: none; box-sizing: border-box; width: 100%; color: inherit; font: inherit; text-align: left; cursor: pointer; background: transparent; border: 0; border-radius: 12px; display: flex; align-items: center; gap: 12px; padding: 14px 16px; }
423
+ .dshmore-config-heading:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: -2px; }
424
+ .dshmore-config-heading-copy { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 4px; }
425
+ .dshmore-config-heading-copy strong { color: var(--dsw-alias-label-primary); font-size: 15px; line-height: 1.4; }
426
+ .dshmore-config-heading-copy > span, .dshmore-config-copy span, .dshmore-config-note { color: var(--dsw-alias-label-tertiary); font-size: 13px; line-height: 1.5; }
427
+ .dshmore-config-chevron { flex: none; color: var(--dsw-alias-label-tertiary); }
428
+ .dshmore-config-chevron-open { transform: rotate(180deg); }
429
+ .dshmore-config-body { margin: 0 16px; padding-bottom: 8px; border-top: 1px solid var(--dsw-alias-border-l2); }
430
+ .dshmore-config-list { display: grid; }
431
+ .dshmore-config-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 12px 0; cursor: pointer; }
432
+ .dshmore-config-row + .dshmore-config-row { border-top: 1px solid var(--dsw-alias-border-l1); }
433
+ .dshmore-config-row:hover { background: var(--dsw-alias-interactive-bg-hover); }
434
+ .dshmore-config-copy { min-width: 0; display: grid; gap: 2px; }
435
+ .dshmore-config-copy strong { color: var(--dsw-alias-label-primary); font-size: 13px; }
436
+ .dshmore-config-row input { width: 34px; height: 18px; flex: none; cursor: pointer; accent-color: var(--dsw-alias-state-business-primary); }
437
+ .dshmore-config-row input:disabled { cursor: default; opacity: .5; }
438
+ .dshmore-config-note, .dshmore-config-error { margin: 0; padding: 10px 0; border-top: 1px solid var(--dsw-alias-border-l1); }
439
+ .dshmore-config-error { color: var(--dsw-alias-state-error-primary, rgb(220,90,90)); font-size: 12px; }
440
+ @media (prefers-reduced-motion: no-preference) { .dshmore-config-chevron { transition: transform .16s; } }
441
+ `;
442
+
443
+ // src/patches/session-delete/shared.ts
444
+ var SESSION_DELETE_PATCH_ID = "session-delete";
445
+
446
+ // src/patches/session-delete/client/index.tsx
447
+ var import_jsx_runtime3 = require("react/jsx-runtime");
448
+ var DELETE_MENU_ATTRIBUTE = "data-dshmore-session-delete";
449
+ var DELETE_SOURCE_ATTRIBUTE = "data-dshmore-session-delete-source";
450
+ var sessionDeleteStyles = `
451
+ .dshmore-session-delete-menu-item { color: var(--dsw-alias-state-error-primary, rgb(220,90,90)) !important; }
452
+ `;
453
+ function replaceExactText(root, before, after) {
454
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
455
+ let node = walker.nextNode();
456
+ while (node !== null) {
457
+ if (node.nodeValue?.trim() === before) node.nodeValue = node.nodeValue.replace(before, after);
458
+ node = walker.nextNode();
459
+ }
460
+ }
461
+ function installSessionDeleteMenuItems(onDelete) {
462
+ let frame = null;
463
+ const sync = () => {
464
+ for (const archiveButton of document.querySelectorAll('button[role="menuitem"]')) {
465
+ const text = archiveButton.textContent?.trim();
466
+ const deleteLabel = text === "\u5F52\u6863\u4F1A\u8BDD" ? "\u6C38\u4E45\u5220\u9664\u4F1A\u8BDD" : text === "Archive session" ? "Permanently delete session" : void 0;
467
+ if (deleteLabel === void 0 || archiveButton.hasAttribute(DELETE_SOURCE_ATTRIBUTE)) continue;
468
+ const wrapper = archiveButton.parentElement;
469
+ if (wrapper === null) continue;
470
+ const clone = wrapper.cloneNode(true);
471
+ const deleteButton = clone.querySelector('button[role="menuitem"]');
472
+ if (deleteButton === null) continue;
473
+ archiveButton.setAttribute(DELETE_SOURCE_ATTRIBUTE, "");
474
+ clone.setAttribute(DELETE_MENU_ATTRIBUTE, "");
475
+ replaceExactText(deleteButton, text, deleteLabel);
476
+ deleteButton.setAttribute("aria-label", deleteLabel);
477
+ deleteButton.classList.add("dshmore-session-delete-menu-item");
478
+ deleteButton.addEventListener("click", (event) => {
479
+ event.preventDefault();
480
+ event.stopImmediatePropagation();
481
+ onDelete(archiveButton);
482
+ });
483
+ wrapper.after(clone);
484
+ }
485
+ };
486
+ const schedule = () => {
487
+ if (frame !== null) return;
488
+ frame = window.requestAnimationFrame(() => {
489
+ frame = null;
490
+ sync();
491
+ });
492
+ };
493
+ sync();
494
+ const observer = new MutationObserver(schedule);
495
+ observer.observe(document.body, { childList: true, subtree: true });
496
+ return () => {
497
+ observer.disconnect();
498
+ if (frame !== null) window.cancelAnimationFrame(frame);
499
+ document.querySelectorAll(`[${DELETE_MENU_ATTRIBUTE}]`).forEach((node) => {
500
+ node.remove();
501
+ });
502
+ document.querySelectorAll(`[${DELETE_SOURCE_ATTRIBUTE}]`).forEach((node) => {
503
+ node.removeAttribute(DELETE_SOURCE_ATTRIBUTE);
504
+ });
505
+ };
506
+ }
507
+ function SessionDeleteController(props) {
508
+ const settings = (0, import_react3.useSyncExternalStore)(props.activation.subscribe, props.activation.getSnapshot);
509
+ const enabled = settings[SESSION_DELETE_PATCH_ID];
510
+ const sessionList = props.useSessions((state) => state);
511
+ const sessionListRef = (0, import_react3.useRef)(sessionList);
512
+ sessionListRef.current = sessionList;
513
+ const workspaceList = props.useWorkspaces((state) => state);
514
+ const workspaceListRef = (0, import_react3.useRef)(workspaceList);
515
+ workspaceListRef.current = workspaceList;
516
+ const [pending, setPending] = (0, import_react3.useState)(null);
517
+ const pendingRef = (0, import_react3.useRef)(null);
518
+ const deleteIntentRef = (0, import_react3.useRef)(false);
519
+ const [busy, setBusy] = (0, import_react3.useState)(false);
520
+ const [error, setError] = (0, import_react3.useState)(null);
521
+ (0, import_react3.useEffect)(() => {
522
+ if (!enabled) return;
523
+ const workspaces = props.ctx.workspaces;
524
+ const mutable = workspaces;
525
+ const original = mutable.archiveSession;
526
+ mutable.archiveSession = (sessionId) => {
527
+ if (!deleteIntentRef.current) return original.call(mutable, sessionId);
528
+ deleteIntentRef.current = false;
529
+ return new Promise((resolve) => {
530
+ const request = { sessionId, resolve };
531
+ pendingRef.current?.resolve();
532
+ pendingRef.current = request;
533
+ setError(null);
534
+ setPending(request);
535
+ });
536
+ };
537
+ const removeDeleteItems = installSessionDeleteMenuItems((archiveButton) => {
538
+ deleteIntentRef.current = true;
539
+ archiveButton.click();
540
+ queueMicrotask(() => {
541
+ deleteIntentRef.current = false;
542
+ });
543
+ });
544
+ return () => {
545
+ mutable.archiveSession = original;
546
+ removeDeleteItems();
547
+ deleteIntentRef.current = false;
548
+ pendingRef.current?.resolve();
549
+ pendingRef.current = null;
550
+ };
551
+ }, [props.ctx, enabled]);
552
+ const close = () => {
553
+ if (busy) return;
554
+ pending?.resolve();
555
+ pendingRef.current = null;
556
+ setPending(null);
557
+ setError(null);
558
+ };
559
+ const commit = async () => {
560
+ if (pending === null) return;
561
+ setBusy(true);
562
+ setError(null);
563
+ const sessions = props.ctx.sessions;
564
+ const workspaces = props.ctx.workspaces;
565
+ const fallbackSessionId = adjacentVisibleSession(
566
+ sessionListRef.current,
567
+ workspaceListRef.current,
568
+ pending.sessionId
569
+ );
570
+ if (fallbackSessionId !== void 0) openSessionWithoutGap(sessions, fallbackSessionId);
571
+ try {
572
+ await callPatchApi(SESSION_DELETE_PATCH_ID, "commit", {
573
+ sessionId: pending.sessionId,
574
+ confirmSessionId: pending.sessionId
575
+ });
576
+ await settleSessionRemoval(sessions, workspaces, pending.sessionId);
577
+ pending.resolve();
578
+ pendingRef.current = null;
579
+ setPending(null);
580
+ } catch (caught) {
581
+ setError(apiErrorText(caught));
582
+ } finally {
583
+ setBusy(false);
584
+ }
585
+ };
586
+ const title = pending === null ? "" : sessionListRef.current.byId[pending.sessionId]?.displayTitle ?? pending.sessionId;
587
+ if (!enabled) return null;
588
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
589
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: styles + sessionDeleteStyles }),
590
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
591
+ import_dsh_client_ui_primitives3.Modal,
592
+ {
593
+ open: pending !== null,
594
+ onClose: close,
595
+ title: "\u6C38\u4E45\u5220\u9664\u4F1A\u8BDD\uFF1F",
596
+ closeLabel: "\u5173\u95ED",
597
+ description: "\u4F1A\u8BDD\u4F1A\u4ECE\u5DE6\u4FA7\u5217\u8868\u548C DSH \u6301\u4E45\u5316\u76EE\u5F55\u4E2D\u6C38\u4E45\u5220\u9664\uFF0C\u4E0D\u4F1A\u5F52\u6863\uFF0C\u4E5F\u4E0D\u4F1A\u79FB\u5165\u56DE\u6536\u76EE\u5F55\u3002",
598
+ footer: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
599
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.Button, { variant: "ghost", disabled: busy, onClick: close, children: "\u53D6\u6D88" }),
600
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.Button, { variant: "primary", disabled: busy, onClick: () => void commit(), children: busy ? "\u6B63\u5728\u5220\u9664\u2026" : "\u6C38\u4E45\u5220\u9664" })
601
+ ] }),
602
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dshmore-dialog-body", children: [
603
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dshmore-message-preview", children: title }),
604
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dshmore-danger", children: [
605
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.IconWarningOutline16, { size: 16 }),
606
+ " \u82E5\u4F1A\u8BDD\u6B63\u5728\u8FD0\u884C\uFF0C\u5C06\u5148\u505C\u6B62\u5F53\u524D\u4EFB\u52A1\u3002\u5220\u9664\u540E\u65E0\u6CD5\u6062\u590D\u3002"
607
+ ] }),
608
+ error !== null && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dshmore-error", children: error })
609
+ ] })
610
+ }
611
+ )
612
+ ] });
613
+ }
614
+ var clientPatch = {
615
+ id: SESSION_DELETE_PATCH_ID,
616
+ install: (ctx, activation) => {
617
+ ctx.slots.inject("shell.overlay", () => ctx.slots.register({
618
+ name: "shell.overlay",
619
+ id: `${PLUGIN_NAME}-${SESSION_DELETE_PATCH_ID}`,
620
+ order: 90,
621
+ registrant: PLUGIN_NAME
622
+ }, (props) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SessionDeleteController, { ...props, ctx, activation })));
623
+ }
624
+ };
625
+
626
+ // src/generated/client-registry.ts
627
+ var MESSAGE_ACTION_PATCHES = [{ id: "message-edit", order: 100, useActions: useMessageActions }, { id: "message-delete", order: 200, useActions: useMessageActions2 }];
628
+ var CLIENT_PATCHES = [clientPatch];
629
+
630
+ // src/kernel/client/activation.ts
631
+ var ClientPatchActivation = class {
632
+ constructor(scope) {
633
+ this.scope = scope;
634
+ }
635
+ lastScope;
636
+ lastValue = { ...DEFAULT_PATCH_SETTINGS };
637
+ getSnapshot = () => {
638
+ const snapshot = this.scope.getSnapshot();
639
+ if (snapshot === this.lastScope) return this.lastValue;
640
+ this.lastScope = snapshot;
641
+ this.lastValue = decodePatchSettings(snapshot.value) ?? { ...DEFAULT_PATCH_SETTINGS };
642
+ return this.lastValue;
643
+ };
644
+ getSettingsSnapshot = () => this.scope.getSnapshot();
645
+ subscribe = (listener) => this.scope.subscribe(listener);
646
+ set = (id, enabled) => this.scope.set(id, enabled);
647
+ };
648
+
649
+ // src/kernel/client/config-card.tsx
650
+ var import_react4 = require("react");
651
+ var import_dsh_client_ui_primitives4 = require("@deepseek-ai/dsh-client-ui-primitives");
652
+ var import_jsx_runtime4 = require("react/jsx-runtime");
653
+ function PatchConfigCard({ activation, initiallyOpen = false }) {
654
+ const scope = (0, import_react4.useSyncExternalStore)(
655
+ activation.subscribe,
656
+ activation.getSettingsSnapshot,
657
+ activation.getSettingsSnapshot
658
+ );
659
+ const settings = activation.getSnapshot();
660
+ const [open, setOpen] = (0, import_react4.useState)(initiallyOpen);
661
+ const [busy, setBusy] = (0, import_react4.useState)(/* @__PURE__ */ new Set());
662
+ const [error, setError] = (0, import_react4.useState)(null);
663
+ if (scope.status === "unavailable") return null;
664
+ const update = async (id, enabled) => {
665
+ setBusy((current) => /* @__PURE__ */ new Set([...current, id]));
666
+ setError(null);
667
+ try {
668
+ await activation.set(id, enabled);
669
+ } catch (caught) {
670
+ setError(caught instanceof Error ? caught.message : String(caught));
671
+ } finally {
672
+ setBusy((current) => {
673
+ const next = new Set(current);
674
+ next.delete(id);
675
+ return next;
676
+ });
677
+ }
678
+ };
679
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("li", { className: `dshmore-config-card${open ? " dshmore-config-card-open" : ""}`, children: [
680
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: styles }),
681
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
682
+ "button",
683
+ {
684
+ type: "button",
685
+ className: "dshmore-config-heading",
686
+ "aria-expanded": open,
687
+ "aria-label": `${open ? "\u6536\u8D77" : "\u5C55\u5F00"}\u8BBE\u7F6E: DSH More`,
688
+ onClick: () => {
689
+ setOpen((current) => !current);
690
+ },
691
+ children: [
692
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "dshmore-config-heading-copy", children: [
693
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: "DSH More" }),
694
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u52A8\u6001\u5F00\u542F\u6216\u5173\u95ED\u72EC\u7ACB\u8865\u4E01\uFF0C\u9ED8\u8BA4\u5168\u90E8\u5F00\u542F\u3002" })
695
+ ] }),
696
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_dsh_client_ui_primitives4.IconChevronDownOutline14, { className: `dshmore-config-chevron${open ? " dshmore-config-chevron-open" : ""}` })
697
+ ]
698
+ }
699
+ ),
700
+ open && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dshmore-config-body", children: [
701
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dshmore-config-list", children: PATCH_CATALOG.map((patch) => {
702
+ const waiting = busy.has(patch.id);
703
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "dshmore-config-row", children: [
704
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "dshmore-config-copy", children: [
705
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: patch.name }),
706
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: patch.description })
707
+ ] }),
708
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
709
+ "input",
710
+ {
711
+ type: "checkbox",
712
+ role: "switch",
713
+ "aria-label": patch.name,
714
+ checked: settings[patch.id],
715
+ disabled: scope.status !== "ready" || !scope.writable || waiting,
716
+ onChange: (event) => {
717
+ void update(patch.id, event.currentTarget.checked);
718
+ }
719
+ }
720
+ )
721
+ ] }, patch.id);
722
+ }) }),
723
+ scope.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dshmore-config-note", children: "\u6B63\u5728\u8BFB\u53D6\u63D2\u4EF6\u914D\u7F6E\u2026" }),
724
+ scope.status === "ready" && !scope.writable && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dshmore-config-note", children: "\u5F53\u524D Settings \u5B58\u50A8\u4E3A\u53EA\u8BFB\u3002" }),
725
+ error !== null && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dshmore-config-error", children: error })
726
+ ] })
727
+ ] });
728
+ }
729
+ function installPatchConfigCard(ctx, activation) {
730
+ ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
731
+ name: "settings.plugin.item",
732
+ key: PATCH_SETTINGS_NAMESPACE,
733
+ priority: 90,
734
+ registrant: PLUGIN_NAME
735
+ }, (props) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PatchConfigCard, { ...props, activation })));
736
+ }
737
+
738
+ // src/kernel/message-visibility.ts
739
+ var MESSAGE_VISIBILITY_PROJECTION_KEY = "dshMoreMessageDelete";
740
+
741
+ // src/platform/dsh/client/message-targets.tsx
742
+ var import_react5 = require("react");
743
+
744
+ // src/platform/dsh/client/message-content.ts
745
+ function contentText(content) {
746
+ return content.filter((block) => block.type === "text").map((block) => block.text).join(" ").replace(/\s+/g, " ").trim();
747
+ }
748
+
749
+ // src/platform/dsh/client/message-targets.tsx
750
+ function messageNodeInfo(node) {
751
+ const candidate = node;
752
+ if (candidate?.kind === "user" && Number.isSafeInteger(candidate.data?.seq) && Array.isArray(candidate.data?.content)) {
753
+ return {
754
+ seq: candidate.data.seq,
755
+ kind: "user",
756
+ text: contentText(candidate.data.content)
757
+ };
758
+ }
759
+ const closing = candidate?.data?.closing;
760
+ if (candidate?.kind === "turn-tail" && Number.isSafeInteger(closing?.finalNode?.seq) && Array.isArray(closing?.blocks)) {
761
+ const text = closing.blocks.filter((block) => block.kind === "text" && typeof block.text === "string").map((block) => block.text).join("");
762
+ return { seq: closing.finalNode?.seq, kind: "assistant", text };
763
+ }
764
+ return null;
765
+ }
766
+ function presentationSeq(node) {
767
+ const candidate = node;
768
+ const direct = candidate?.data?.seq;
769
+ if (Number.isSafeInteger(direct)) return direct;
770
+ const finalNode = candidate?.data?.finalNode;
771
+ if (Number.isSafeInteger(finalNode?.seq)) return finalNode?.seq;
772
+ const root = candidate?.data?.root;
773
+ if (Number.isSafeInteger(root?.seq)) return root?.seq;
774
+ const closing = candidate?.data?.closing;
775
+ if (Number.isSafeInteger(closing?.finalNode?.seq)) return closing?.finalNode?.seq;
776
+ return void 0;
777
+ }
778
+ function hostForRow(row, kind) {
779
+ const existing = row.querySelector(":scope [data-dshmore-message-actions]");
780
+ if (kind === "assistant") {
781
+ const turnRoot = row.querySelector("[data-turn-tail][data-time-hover-root]");
782
+ const copyButton2 = turnRoot?.querySelector('button[aria-label="\u590D\u5236"], button[aria-label="Copy"]');
783
+ const builtInActions2 = copyButton2?.parentElement;
784
+ const placeBeforeClock = (host3) => {
785
+ if (builtInActions2 === void 0 || builtInActions2 === null) return;
786
+ const clock = [...builtInActions2.children].findLast((child) => {
787
+ return child !== host3 && child.querySelector("button") === null && (child.textContent?.trim() ?? "") !== "";
788
+ });
789
+ if (clock === void 0) {
790
+ if (host3.parentElement !== builtInActions2 || builtInActions2.lastElementChild !== host3) builtInActions2.appendChild(host3);
791
+ } else if (host3.parentElement !== builtInActions2 || host3.nextElementSibling !== clock) {
792
+ builtInActions2.insertBefore(host3, clock);
793
+ }
794
+ };
795
+ if (existing !== null) {
796
+ placeBeforeClock(existing);
797
+ return existing;
798
+ }
799
+ const host2 = document.createElement("span");
800
+ host2.dataset.dshmoreMessageActions = "";
801
+ host2.className = "dshmore-inline-actions";
802
+ if (builtInActions2 !== void 0 && builtInActions2 !== null) placeBeforeClock(host2);
803
+ else row.appendChild(host2);
804
+ return host2;
805
+ }
806
+ const copyButton = row.querySelector('button[aria-label="\u590D\u5236"], button[aria-label="Copy"]');
807
+ const builtInActions = copyButton?.parentElement;
808
+ if (existing !== null) {
809
+ if (builtInActions !== void 0 && builtInActions !== null && (existing.parentElement !== builtInActions || existing.nextElementSibling !== copyButton)) {
810
+ builtInActions.insertBefore(existing, copyButton ?? null);
811
+ }
812
+ return existing;
813
+ }
814
+ const host = document.createElement("span");
815
+ host.dataset.dshmoreMessageActions = "";
816
+ host.className = "dshmore-inline-actions";
817
+ if (builtInActions !== void 0 && builtInActions !== null) builtInActions.insertBefore(host, copyButton ?? null);
818
+ else row.appendChild(host);
819
+ return host;
820
+ }
821
+ function sameTargets(left, right) {
822
+ return left.length === right.length && left.every((target, index) => {
823
+ const other = right[index];
824
+ return other !== void 0 && target.key === other.key && target.seq === other.seq && target.host === other.host && target.text === other.text;
825
+ });
826
+ }
827
+ function trajectoryRecordKey(row) {
828
+ const encoded = row.dataset.trajectoryRowKey;
829
+ if (encoded === void 0) return void 0;
830
+ try {
831
+ return decodeURIComponent(encoded);
832
+ } catch {
833
+ return void 0;
834
+ }
835
+ }
836
+ function trajectorySourceSeq(row) {
837
+ const decoded = trajectoryRecordKey(row);
838
+ if (decoded === void 0) return void 0;
839
+ const marker = "\0seq\0";
840
+ const at = decoded.lastIndexOf(marker);
841
+ if (at < 0) return void 0;
842
+ const raw = decoded.slice(at + marker.length).split("\0", 1)[0];
843
+ const seq = Number(raw);
844
+ return Number.isSafeInteger(seq) && seq >= 0 ? seq : void 0;
845
+ }
846
+ function useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys) {
847
+ const snapshot = props.useSession((state) => state);
848
+ const [targets, setTargets] = (0, import_react5.useState)([]);
849
+ (0, import_react5.useLayoutEffect)(() => {
850
+ let frame = null;
851
+ const scan = () => {
852
+ frame = null;
853
+ const next = [];
854
+ for (const row of document.querySelectorAll("[data-chat-flow-key]")) {
855
+ const key = row.dataset.chatFlowKey;
856
+ if (key === void 0) continue;
857
+ const node = snapshot.chat.nodes.get(key);
858
+ const seq = presentationSeq(node);
859
+ if (seq !== void 0 && hiddenSeqs.has(seq)) {
860
+ row.dataset.dshmoreHidden = "";
861
+ row.style.display = "none";
862
+ row.querySelector("[data-dshmore-message-actions]")?.remove();
863
+ continue;
864
+ }
865
+ if (row.dataset.dshmoreHidden !== void 0) {
866
+ delete row.dataset.dshmoreHidden;
867
+ row.style.removeProperty("display");
868
+ }
869
+ const info = messageNodeInfo(node);
870
+ if (info !== null) next.push({ key, ...info, host: hostForRow(row, info.kind) });
871
+ }
872
+ for (const row of document.querySelectorAll("[data-trajectory-row-key]")) {
873
+ const seq = trajectorySourceSeq(row);
874
+ const key = trajectoryRecordKey(row);
875
+ if (seq !== void 0 && hiddenSeqs.has(seq) || key !== void 0 && hiddenTrajectoryKeys.has(key)) {
876
+ row.dataset.dshmoreHidden = "";
877
+ row.style.display = "none";
878
+ } else if (row.dataset.dshmoreHidden !== void 0) {
879
+ delete row.dataset.dshmoreHidden;
880
+ row.style.removeProperty("display");
881
+ }
882
+ }
883
+ setTargets((current) => sameTargets(current, next) ? current : next);
884
+ };
885
+ const schedule = () => {
886
+ if (frame === null) frame = window.requestAnimationFrame(scan);
887
+ };
888
+ scan();
889
+ const observer = new MutationObserver(schedule);
890
+ observer.observe(document.body, { childList: true, subtree: true });
891
+ return () => {
892
+ observer.disconnect();
893
+ if (frame !== null) window.cancelAnimationFrame(frame);
894
+ for (const row of document.querySelectorAll("[data-dshmore-hidden]")) {
895
+ delete row.dataset.dshmoreHidden;
896
+ row.style.removeProperty("display");
897
+ }
898
+ for (const host of document.querySelectorAll("[data-dshmore-message-actions]")) host.remove();
899
+ };
900
+ }, [snapshot, hiddenSeqs, hiddenTrajectoryKeys]);
901
+ return targets;
902
+ }
903
+
904
+ // src/client/index.tsx
905
+ var import_jsx_runtime5 = require("react/jsx-runtime");
906
+ var inject = ["slots", "sessions", "workspaces", "settingsScope"];
907
+ function MessageActionPatchController({ patch, props, targets, enabled }) {
908
+ const actions = patch.useActions(props, enabled);
909
+ const style = { "--dshmore-action-order": patch.order };
910
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
911
+ enabled && targets.map((target) => (0, import_react_dom.createPortal)(
912
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dshmore-action-contribution", style, children: actions.renderAction(target) }),
913
+ target.host,
914
+ `${patch.id}:${target.key}`
915
+ )),
916
+ actions.overlay
917
+ ] });
918
+ }
919
+ function MessageActionsController(props) {
920
+ const settings = (0, import_react6.useSyncExternalStore)(props.activation.subscribe, props.activation.getSnapshot);
921
+ const projected = props.useProjection(MESSAGE_VISIBILITY_PROJECTION_KEY);
922
+ const hiddenSeqs = (0, import_react6.useMemo)(() => new Set(projected?.deletedSeqs ?? []), [projected]);
923
+ const hiddenTrajectoryKeys = (0, import_react6.useMemo)(() => new Set(projected?.hiddenTrajectoryKeys ?? []), [projected]);
924
+ const targets = useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys);
925
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
926
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: styles }),
927
+ MESSAGE_ACTION_PATCHES.map((patch) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
928
+ MessageActionPatchController,
929
+ {
930
+ patch,
931
+ props,
932
+ targets,
933
+ enabled: settings[patch.id]
934
+ },
935
+ patch.id
936
+ ))
937
+ ] });
938
+ }
939
+ function apply(ctx) {
940
+ const scope = ctx.settingsScope.bind({
941
+ namespace: PATCH_SETTINGS_NAMESPACE,
942
+ decode: decodePatchSettings
943
+ });
944
+ const activation = new ClientPatchActivation(scope);
945
+ installPatchConfigCard(ctx, activation);
946
+ ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
947
+ name: "conversation.session.header.utilities",
948
+ id: `${PLUGIN_NAME}-message-actions`,
949
+ order: 90,
950
+ registrant: PLUGIN_NAME
951
+ }, (props) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageActionsController, { ...props, ctx, activation })));
952
+ for (const patch of CLIENT_PATCHES) patch.install(ctx, activation);
953
+ }
954
+ return module.exports; } });