dsh-more 0.0.1 → 0.0.3
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/CONTRIBUTING.md +6 -6
- package/README.md +21 -9
- package/README.zh-CN.md +21 -9
- package/dist/client.js +1232 -174
- package/dist/index.js +451 -25
- package/dist/types/generated/host-settings-schema.d.ts +6 -0
- package/dist/types/generated/patch-catalog.d.ts +24 -0
- package/dist/types/patches/conversation-markdown-export/client/filename.d.ts +1 -0
- package/dist/types/patches/conversation-markdown-export/client/index.d.ts +2 -0
- package/dist/types/patches/conversation-markdown-export/host/index.d.ts +2 -0
- package/dist/types/patches/conversation-markdown-export/host/transcript.d.ts +9 -0
- package/dist/types/patches/conversation-markdown-export/shared.d.ts +5 -0
- package/dist/types/patches/conversation-quick-navigation/client/index.d.ts +22 -0
- package/dist/types/patches/conversation-quick-navigation/client/navigation.d.ts +26 -0
- package/dist/types/patches/conversation-quick-navigation/host/index.d.ts +2 -0
- package/dist/types/patches/conversation-quick-navigation/shared.d.ts +1 -0
- package/dist/types/patches/model-reasoning-efforts/client/index.d.ts +39 -0
- package/dist/types/patches/model-reasoning-efforts/host/index.d.ts +2 -0
- package/dist/types/patches/model-reasoning-efforts/host/validate.d.ts +13 -0
- package/dist/types/patches/model-reasoning-efforts/shared.d.ts +51 -0
- package/dist/types/patches/session-delete/host/session-deletion.d.ts +0 -1
- package/dist/types/platform/dsh/client/api.d.ts +2 -0
- package/dist/types/platform/dsh/client/message-targets.d.ts +13 -1
- package/dist/types/platform/dsh/client/styles.d.ts +1 -1
- package/dist/types/platform/dsh/host/continuation.d.ts +4 -0
- package/dist/types/platform/dsh/host/error.d.ts +1 -1
- package/dist/types/platform/dsh/host/wire.d.ts +7 -0
- package/dsh.plugin.json +12 -2
- package/package.json +7 -4
package/dist/client.js
CHANGED
|
@@ -25,19 +25,25 @@ __export(index_exports, {
|
|
|
25
25
|
inject: () => inject
|
|
26
26
|
});
|
|
27
27
|
module.exports = __toCommonJS(index_exports);
|
|
28
|
-
var
|
|
29
|
-
var
|
|
28
|
+
var import_react9 = require("react");
|
|
29
|
+
var import_react_dom2 = require("react-dom");
|
|
30
30
|
|
|
31
31
|
// src/generated/patch-catalog.ts
|
|
32
32
|
var PATCH_CATALOG = [
|
|
33
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
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" }
|
|
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
|
+
{ "id": "conversation-markdown-export", "name": "\u5BFC\u51FA\u5B8C\u6574\u804A\u5929\u8BB0\u5F55", "description": "\u5C06\u5F53\u524D\u4F1A\u8BDD\u7684\u5B8C\u6574\u804A\u5929\u8BB0\u5F55\u4E0B\u8F7D\u4E3A Markdown \u6587\u4EF6", "defaultEnabled": true, "order": 400, "clientKind": "standalone" },
|
|
37
|
+
{ "id": "model-reasoning-efforts", "name": "\u6A21\u578B\u601D\u8003\u5F3A\u5EA6", "description": "\u5728\u6A21\u578B\u914D\u7F6E\u754C\u9762\u4E3A\u6BCF\u4E2A\u6A21\u578B\u914D\u7F6E\u652F\u6301\u7684\u601D\u8003\u5F3A\u5EA6\u5E76\u5199\u5165 settings.yaml \u7684 reasoningEfforts", "defaultEnabled": true, "order": 500, "clientKind": "standalone" },
|
|
38
|
+
{ "id": "conversation-quick-navigation", "name": "\u5BF9\u8BDD\u5FEB\u901F\u5BFC\u822A", "description": "\u81EA\u52A8\u52A0\u8F7D\u5B8C\u6574\u8BB0\u5F55\uFF0C\u5E76\u7528\u5E38\u9A7B\u76EE\u5F55\u5FEB\u901F\u5B9A\u4F4D\u5F53\u524D\u4F1A\u8BDD\u4E2D\u7684\u6BCF\u4E00\u8F6E\u63D0\u95EE", "defaultEnabled": true, "order": 600, "clientKind": "standalone" }
|
|
36
39
|
];
|
|
37
40
|
var DEFAULT_PATCH_SETTINGS = {
|
|
38
41
|
"message-edit": true,
|
|
39
42
|
"message-delete": true,
|
|
40
|
-
"session-delete": true
|
|
43
|
+
"session-delete": true,
|
|
44
|
+
"conversation-markdown-export": true,
|
|
45
|
+
"model-reasoning-efforts": true,
|
|
46
|
+
"conversation-quick-navigation": true
|
|
41
47
|
};
|
|
42
48
|
function decodePatchSettings(value) {
|
|
43
49
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
@@ -68,8 +74,9 @@ var DshMoreApiError = class extends Error {
|
|
|
68
74
|
this.code = code;
|
|
69
75
|
this.name = "DshMoreApiError";
|
|
70
76
|
}
|
|
77
|
+
code;
|
|
71
78
|
};
|
|
72
|
-
async function
|
|
79
|
+
async function requestPatch(patchId, action, payload) {
|
|
73
80
|
let response;
|
|
74
81
|
try {
|
|
75
82
|
response = await fetch(`${PLUGIN_API_PREFIX}/${encodeURIComponent(patchId)}/${encodeURIComponent(action)}`, {
|
|
@@ -83,12 +90,28 @@ async function callPatchApi(patchId, action, payload) {
|
|
|
83
90
|
} catch (error) {
|
|
84
91
|
throw new DshMoreApiError("network", error instanceof Error ? error.message : String(error));
|
|
85
92
|
}
|
|
93
|
+
return response;
|
|
94
|
+
}
|
|
95
|
+
async function responseError(response) {
|
|
86
96
|
const body = await response.json().catch(() => null);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
97
|
+
return new DshMoreApiError(body?.error?.code ?? "http", body?.error?.message ?? `HTTP ${String(response.status)}`);
|
|
98
|
+
}
|
|
99
|
+
async function callPatchApi(patchId, action, payload) {
|
|
100
|
+
const response = await requestPatch(patchId, action, payload);
|
|
101
|
+
const body = await response.json().catch(() => null);
|
|
102
|
+
if (!response.ok || body?.ok !== true || body.value === void 0) throw await responseErrorFromBody(response, body);
|
|
90
103
|
return body.value;
|
|
91
104
|
}
|
|
105
|
+
function responseErrorFromBody(response, body) {
|
|
106
|
+
return new DshMoreApiError(body?.error?.code ?? "http", body?.error?.message ?? `HTTP ${String(response.status)}`);
|
|
107
|
+
}
|
|
108
|
+
async function callPatchBlobApi(patchId, action, payload) {
|
|
109
|
+
const response = await requestPatch(patchId, action, payload);
|
|
110
|
+
if (!response.ok) throw await responseError(response);
|
|
111
|
+
const contentType = response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
|
|
112
|
+
if (contentType !== "text/markdown") throw new DshMoreApiError("unexpected-response", "\u5BFC\u51FA\u63A5\u53E3\u8FD4\u56DE\u4E86\u672A\u77E5\u5185\u5BB9\u3002");
|
|
113
|
+
return response.blob();
|
|
114
|
+
}
|
|
92
115
|
function apiErrorText(error) {
|
|
93
116
|
if (error instanceof DshMoreApiError || error instanceof Error) return error.message;
|
|
94
117
|
return String(error);
|
|
@@ -173,10 +196,16 @@ async function settleSessionRemoval(sessions, workspaces, sessionId) {
|
|
|
173
196
|
function adjacentVisibleSession(sessions, workspaces, deletingSessionId) {
|
|
174
197
|
if (sessions.current !== deletingSessionId) return void 0;
|
|
175
198
|
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
199
|
const deletingIndex = sessions.ids.indexOf(deletingSessionId);
|
|
179
|
-
|
|
200
|
+
for (let index = deletingIndex + 1; index < sessions.ids.length; index += 1) {
|
|
201
|
+
const id = sessions.ids[index];
|
|
202
|
+
if (id !== void 0 && id !== deletingSessionId && !archived.has(id)) return id;
|
|
203
|
+
}
|
|
204
|
+
for (let index = deletingIndex - 1; index >= 0; index -= 1) {
|
|
205
|
+
const id = sessions.ids[index];
|
|
206
|
+
if (id !== void 0 && id !== deletingSessionId && !archived.has(id)) return id;
|
|
207
|
+
}
|
|
208
|
+
return void 0;
|
|
180
209
|
}
|
|
181
210
|
|
|
182
211
|
// src/patches/message-edit/shared.ts
|
|
@@ -191,41 +220,28 @@ var api = {
|
|
|
191
220
|
function useMessageActions(props, enabled) {
|
|
192
221
|
const [target, setTarget] = (0, import_react.useState)(null);
|
|
193
222
|
const [text, setText] = (0, import_react.useState)("");
|
|
194
|
-
const [preview, setPreview] = (0, import_react.useState)(null);
|
|
195
223
|
const [busy, setBusy] = (0, import_react.useState)(false);
|
|
196
224
|
const [error, setError] = (0, import_react.useState)(null);
|
|
197
225
|
(0, import_react.useEffect)(() => {
|
|
198
226
|
if (enabled) return;
|
|
199
227
|
setTarget(null);
|
|
200
|
-
setPreview(null);
|
|
201
228
|
setError(null);
|
|
202
229
|
}, [enabled]);
|
|
203
230
|
const open = (next) => {
|
|
204
231
|
setTarget(next);
|
|
205
232
|
setText(next.text);
|
|
206
|
-
setPreview(null);
|
|
207
233
|
setError(null);
|
|
208
234
|
};
|
|
209
|
-
const
|
|
235
|
+
const applyChange = async () => {
|
|
210
236
|
if (target === null) return;
|
|
211
237
|
setBusy(true);
|
|
212
238
|
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
239
|
const sessions = props.ctx.sessions;
|
|
226
|
-
|
|
227
|
-
const handoff = followSessionHandoff(sessions, continuationSessionId);
|
|
240
|
+
let handoff;
|
|
228
241
|
try {
|
|
242
|
+
const preview = await api.preview({ sessionId: props.sessionId, targetSeq: target.seq, text });
|
|
243
|
+
const continuationSessionId = preview.continuationSessionId;
|
|
244
|
+
handoff = followSessionHandoff(sessions, continuationSessionId);
|
|
229
245
|
const result = await api.commit({
|
|
230
246
|
sessionId: props.sessionId,
|
|
231
247
|
targetSeq: target.seq,
|
|
@@ -236,13 +252,12 @@ function useMessageActions(props, enabled) {
|
|
|
236
252
|
await handoff.finish(result.sessionId);
|
|
237
253
|
setTarget(null);
|
|
238
254
|
} catch (caught) {
|
|
239
|
-
handoff
|
|
255
|
+
handoff?.cancel();
|
|
240
256
|
const sourceId = props.sessionId;
|
|
241
257
|
const sessionList = sessions.list.getSnapshot();
|
|
242
258
|
const sourceAvailable = sessionList.byId[sourceId] !== void 0;
|
|
243
259
|
const sourceArchived = props.ctx.workspaces.list.getSnapshot().archivedSessionIds.includes(sourceId);
|
|
244
260
|
if (sourceAvailable && !sourceArchived && sessionList.current !== sourceId) openSessionWithoutGap(sessions, sourceId);
|
|
245
|
-
setPreview(null);
|
|
246
261
|
setError(apiErrorText(caught));
|
|
247
262
|
} finally {
|
|
248
263
|
setBusy(false);
|
|
@@ -262,12 +277,9 @@ function useMessageActions(props, enabled) {
|
|
|
262
277
|
closeLabel: "\u5173\u95ED",
|
|
263
278
|
className: "dshmore-edit-modal",
|
|
264
279
|
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:
|
|
280
|
+
footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
266
281
|
/* @__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
|
|
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" })
|
|
282
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: busy || text.trim() === "", onClick: () => void applyChange(), children: busy ? "\u6B63\u5728\u4FEE\u6539\u2026" : "\u4FEE\u6539\u5E76\u91CD\u65B0\u5F00\u59CB" })
|
|
271
283
|
] }),
|
|
272
284
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dshmore-dialog-body", children: [
|
|
273
285
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
@@ -275,23 +287,15 @@ function useMessageActions(props, enabled) {
|
|
|
275
287
|
{
|
|
276
288
|
className: "dshmore-editor",
|
|
277
289
|
value: text,
|
|
278
|
-
disabled: busy
|
|
290
|
+
disabled: busy,
|
|
279
291
|
onChange: (event) => {
|
|
280
292
|
setText(event.currentTarget.value);
|
|
281
|
-
setPreview(null);
|
|
282
293
|
setError(null);
|
|
283
294
|
},
|
|
284
295
|
rows: 7,
|
|
285
296
|
autoFocus: true
|
|
286
297
|
}
|
|
287
298
|
),
|
|
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
299
|
error !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dshmore-error", children: error })
|
|
296
300
|
] })
|
|
297
301
|
}
|
|
@@ -403,6 +407,7 @@ var import_dsh_client_ui_primitives3 = require("@deepseek-ai/dsh-client-ui-primi
|
|
|
403
407
|
|
|
404
408
|
// src/platform/dsh/client/styles.ts
|
|
405
409
|
var styles = `
|
|
410
|
+
[data-dshmore-hidden] { display: none !important; }
|
|
406
411
|
.dshmore-inline-actions { display: inline-flex; align-items: center; gap: 10px; }
|
|
407
412
|
.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
413
|
.dshmore-inline-button:hover { color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-interactive-bg-hover); }
|
|
@@ -460,38 +465,71 @@ function replaceExactText(root, before, after) {
|
|
|
460
465
|
}
|
|
461
466
|
function installSessionDeleteMenuItems(onDelete) {
|
|
462
467
|
let frame = null;
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
468
|
+
const deleteItems = /* @__PURE__ */ new Map();
|
|
469
|
+
const pendingButtons = /* @__PURE__ */ new Set();
|
|
470
|
+
const syncButton = (archiveButton) => {
|
|
471
|
+
if (!archiveButton.isConnected) return;
|
|
472
|
+
const text = archiveButton.textContent?.trim();
|
|
473
|
+
const deleteLabel = text === "\u5F52\u6863\u4F1A\u8BDD" ? "\u6C38\u4E45\u5220\u9664\u4F1A\u8BDD" : text === "Archive session" ? "Permanently delete session" : void 0;
|
|
474
|
+
if (deleteLabel === void 0) return;
|
|
475
|
+
const wrapper = archiveButton.parentElement;
|
|
476
|
+
if (wrapper === null) return;
|
|
477
|
+
if (archiveButton.hasAttribute(DELETE_SOURCE_ATTRIBUTE)) {
|
|
478
|
+
if (deleteItems.get(archiveButton)?.isConnected === true) return;
|
|
479
|
+
archiveButton.removeAttribute(DELETE_SOURCE_ATTRIBUTE);
|
|
480
|
+
}
|
|
481
|
+
const clone = wrapper.cloneNode(true);
|
|
482
|
+
const deleteButton = clone.querySelector('button[role="menuitem"]');
|
|
483
|
+
if (deleteButton === null) return;
|
|
484
|
+
archiveButton.setAttribute(DELETE_SOURCE_ATTRIBUTE, "");
|
|
485
|
+
clone.setAttribute(DELETE_MENU_ATTRIBUTE, "");
|
|
486
|
+
deleteItems.set(archiveButton, clone);
|
|
487
|
+
replaceExactText(deleteButton, text, deleteLabel);
|
|
488
|
+
deleteButton.setAttribute("aria-label", deleteLabel);
|
|
489
|
+
deleteButton.classList.add("dshmore-session-delete-menu-item");
|
|
490
|
+
deleteButton.addEventListener("click", (event) => {
|
|
491
|
+
event.preventDefault();
|
|
492
|
+
event.stopImmediatePropagation();
|
|
493
|
+
onDelete(archiveButton);
|
|
494
|
+
});
|
|
495
|
+
wrapper.after(clone);
|
|
496
|
+
};
|
|
497
|
+
const collectButtons = (node) => {
|
|
498
|
+
const element = node instanceof Element ? node : node.parentElement;
|
|
499
|
+
if (element === null) return;
|
|
500
|
+
const ancestor = element.closest('button[role="menuitem"]');
|
|
501
|
+
if (ancestor !== null) pendingButtons.add(ancestor);
|
|
502
|
+
if (element.matches('button[role="menuitem"]')) pendingButtons.add(element);
|
|
503
|
+
element.querySelectorAll('button[role="menuitem"]').forEach((button) => pendingButtons.add(button));
|
|
504
|
+
};
|
|
505
|
+
const flush = () => {
|
|
506
|
+
frame = null;
|
|
507
|
+
for (const [archiveButton, deleteItem] of deleteItems) {
|
|
508
|
+
if (!archiveButton.isConnected) {
|
|
509
|
+
deleteItems.delete(archiveButton);
|
|
510
|
+
} else if (!deleteItem.isConnected) {
|
|
511
|
+
archiveButton.removeAttribute(DELETE_SOURCE_ATTRIBUTE);
|
|
512
|
+
deleteItems.delete(archiveButton);
|
|
513
|
+
pendingButtons.add(archiveButton);
|
|
514
|
+
}
|
|
484
515
|
}
|
|
516
|
+
for (const button of pendingButtons) syncButton(button);
|
|
517
|
+
pendingButtons.clear();
|
|
485
518
|
};
|
|
486
519
|
const schedule = () => {
|
|
487
520
|
if (frame !== null) return;
|
|
488
|
-
frame = window.requestAnimationFrame(
|
|
489
|
-
frame = null;
|
|
490
|
-
sync();
|
|
491
|
-
});
|
|
521
|
+
frame = window.requestAnimationFrame(flush);
|
|
492
522
|
};
|
|
493
|
-
|
|
494
|
-
|
|
523
|
+
document.querySelectorAll('button[role="menuitem"]').forEach((button) => pendingButtons.add(button));
|
|
524
|
+
flush();
|
|
525
|
+
const observer = new MutationObserver((records) => {
|
|
526
|
+
let removedKnownItem = false;
|
|
527
|
+
for (const record of records) {
|
|
528
|
+
record.addedNodes.forEach(collectButtons);
|
|
529
|
+
if (record.removedNodes.length > 0 && deleteItems.size > 0) removedKnownItem = true;
|
|
530
|
+
}
|
|
531
|
+
if (pendingButtons.size > 0 || removedKnownItem) schedule();
|
|
532
|
+
});
|
|
495
533
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
496
534
|
return () => {
|
|
497
535
|
observer.disconnect();
|
|
@@ -507,12 +545,6 @@ function installSessionDeleteMenuItems(onDelete) {
|
|
|
507
545
|
function SessionDeleteController(props) {
|
|
508
546
|
const settings = (0, import_react3.useSyncExternalStore)(props.activation.subscribe, props.activation.getSnapshot);
|
|
509
547
|
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
548
|
const [pending, setPending] = (0, import_react3.useState)(null);
|
|
517
549
|
const pendingRef = (0, import_react3.useRef)(null);
|
|
518
550
|
const deleteIntentRef = (0, import_react3.useRef)(false);
|
|
@@ -521,13 +553,18 @@ function SessionDeleteController(props) {
|
|
|
521
553
|
(0, import_react3.useEffect)(() => {
|
|
522
554
|
if (!enabled) return;
|
|
523
555
|
const workspaces = props.ctx.workspaces;
|
|
556
|
+
const sessions = props.ctx.sessions;
|
|
524
557
|
const mutable = workspaces;
|
|
525
558
|
const original = mutable.archiveSession;
|
|
526
559
|
mutable.archiveSession = (sessionId) => {
|
|
527
560
|
if (!deleteIntentRef.current) return original.call(mutable, sessionId);
|
|
528
561
|
deleteIntentRef.current = false;
|
|
529
562
|
return new Promise((resolve) => {
|
|
530
|
-
const request = {
|
|
563
|
+
const request = {
|
|
564
|
+
sessionId,
|
|
565
|
+
title: sessions.list.getSnapshot().byId[sessionId]?.displayTitle ?? String(sessionId),
|
|
566
|
+
resolve
|
|
567
|
+
};
|
|
531
568
|
pendingRef.current?.resolve();
|
|
532
569
|
pendingRef.current = request;
|
|
533
570
|
setError(null);
|
|
@@ -563,8 +600,8 @@ function SessionDeleteController(props) {
|
|
|
563
600
|
const sessions = props.ctx.sessions;
|
|
564
601
|
const workspaces = props.ctx.workspaces;
|
|
565
602
|
const fallbackSessionId = adjacentVisibleSession(
|
|
566
|
-
|
|
567
|
-
|
|
603
|
+
sessions.list.getSnapshot(),
|
|
604
|
+
workspaces.list.getSnapshot(),
|
|
568
605
|
pending.sessionId
|
|
569
606
|
);
|
|
570
607
|
if (fallbackSessionId !== void 0) openSessionWithoutGap(sessions, fallbackSessionId);
|
|
@@ -583,7 +620,7 @@ function SessionDeleteController(props) {
|
|
|
583
620
|
setBusy(false);
|
|
584
621
|
}
|
|
585
622
|
};
|
|
586
|
-
const title = pending
|
|
623
|
+
const title = pending?.title ?? "";
|
|
587
624
|
if (!enabled) return null;
|
|
588
625
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
589
626
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: styles + sessionDeleteStyles }),
|
|
@@ -623,15 +660,929 @@ var clientPatch = {
|
|
|
623
660
|
}
|
|
624
661
|
};
|
|
625
662
|
|
|
663
|
+
// src/patches/conversation-markdown-export/client/index.tsx
|
|
664
|
+
var import_react4 = require("react");
|
|
665
|
+
var import_dsh_client_ui_primitives4 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
666
|
+
|
|
667
|
+
// src/patches/conversation-markdown-export/shared.ts
|
|
668
|
+
var CONVERSATION_MARKDOWN_EXPORT_PATCH_ID = "conversation-markdown-export";
|
|
669
|
+
|
|
670
|
+
// src/patches/conversation-markdown-export/client/filename.ts
|
|
671
|
+
function safeStem(value) {
|
|
672
|
+
return [...value.normalize("NFKC").replace(/[\u0000-\u001f\u007f/\\?%*:|"<>]+/g, "-").replace(/\s+/g, " ").replace(/^[.\s-]+|[.\s-]+$/g, "")].slice(0, 80).join("").trim();
|
|
673
|
+
}
|
|
674
|
+
function markdownFilename(title, sessionId) {
|
|
675
|
+
const fallback = safeStem(`dsh-conversation-${sessionId}`) || "dsh-conversation";
|
|
676
|
+
return `${safeStem(title) || fallback}.md`;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// src/patches/conversation-markdown-export/client/index.tsx
|
|
680
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
681
|
+
var exportStyles = `
|
|
682
|
+
.dshmore-markdown-export-button { box-sizing: border-box; min-width: 128px; height: 32px; color: var(--dsw-alias-label-primary); cursor: pointer; background: transparent; border: 1px solid var(--dsw-alias-border-l2); border-radius: 18px; display: inline-flex; align-items: center; justify-content: center; gap: 5px; padding: 6px 12px; font-family: var(--dsw-font-family); font-size: 13px; line-height: 20px; }
|
|
683
|
+
.dshmore-markdown-export-button:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }
|
|
684
|
+
.dshmore-markdown-export-button:disabled { color: var(--dsw-alias-label-dimmed); cursor: wait; }
|
|
685
|
+
.dshmore-markdown-export-button > span, .dshmore-markdown-export-button > svg { flex: none; }
|
|
686
|
+
`;
|
|
687
|
+
function saveMarkdown(filename, markdown) {
|
|
688
|
+
const url = URL.createObjectURL(new Blob(["\uFEFF", markdown], { type: "text/markdown;charset=utf-8" }));
|
|
689
|
+
const anchor = document.createElement("a");
|
|
690
|
+
anchor.href = url;
|
|
691
|
+
anchor.download = filename;
|
|
692
|
+
anchor.style.display = "none";
|
|
693
|
+
document.body.appendChild(anchor);
|
|
694
|
+
anchor.click();
|
|
695
|
+
anchor.remove();
|
|
696
|
+
window.setTimeout(() => {
|
|
697
|
+
URL.revokeObjectURL(url);
|
|
698
|
+
}, 0);
|
|
699
|
+
}
|
|
700
|
+
function ConversationMarkdownExportButton(props) {
|
|
701
|
+
const settings = (0, import_react4.useSyncExternalStore)(props.activation.subscribe, props.activation.getSnapshot);
|
|
702
|
+
const enabled = settings[CONVERSATION_MARKDOWN_EXPORT_PATCH_ID];
|
|
703
|
+
const title = props.useSessions((state) => state.byId[props.sessionId]?.displayTitle ?? String(props.sessionId));
|
|
704
|
+
const [busy, setBusy] = (0, import_react4.useState)(false);
|
|
705
|
+
const [error, setError] = (0, import_react4.useState)(null);
|
|
706
|
+
const generation = (0, import_react4.useRef)(0);
|
|
707
|
+
const activeSession = (0, import_react4.useRef)(null);
|
|
708
|
+
activeSession.current = enabled ? String(props.sessionId) : null;
|
|
709
|
+
(0, import_react4.useEffect)(() => {
|
|
710
|
+
generation.current += 1;
|
|
711
|
+
setBusy(false);
|
|
712
|
+
setError(null);
|
|
713
|
+
}, [enabled, props.sessionId]);
|
|
714
|
+
const download = async () => {
|
|
715
|
+
const requestSessionId = String(props.sessionId);
|
|
716
|
+
const requestGeneration = ++generation.current;
|
|
717
|
+
setBusy(true);
|
|
718
|
+
setError(null);
|
|
719
|
+
try {
|
|
720
|
+
const markdown = await callPatchBlobApi(
|
|
721
|
+
CONVERSATION_MARKDOWN_EXPORT_PATCH_ID,
|
|
722
|
+
"render",
|
|
723
|
+
{ sessionId: props.sessionId, title }
|
|
724
|
+
);
|
|
725
|
+
if (generation.current !== requestGeneration || activeSession.current !== requestSessionId) return;
|
|
726
|
+
saveMarkdown(markdownFilename(title, String(props.sessionId)), markdown);
|
|
727
|
+
} catch (caught) {
|
|
728
|
+
if (generation.current === requestGeneration && activeSession.current === requestSessionId) setError(apiErrorText(caught));
|
|
729
|
+
} finally {
|
|
730
|
+
if (generation.current === requestGeneration && activeSession.current === requestSessionId) setBusy(false);
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
if (!enabled) return null;
|
|
734
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
735
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: exportStyles }),
|
|
736
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
737
|
+
"button",
|
|
738
|
+
{
|
|
739
|
+
type: "button",
|
|
740
|
+
className: "dshmore-markdown-export-button",
|
|
741
|
+
disabled: busy,
|
|
742
|
+
"aria-busy": busy,
|
|
743
|
+
"aria-label": "\u5BFC\u51FA\u5B8C\u6574\u804A\u5929\u8BB0\u5F55\u4E3A Markdown",
|
|
744
|
+
onClick: () => {
|
|
745
|
+
void download();
|
|
746
|
+
},
|
|
747
|
+
children: [
|
|
748
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: busy ? "\u6B63\u5728\u5BFC\u51FA\u2026" : "\u5BFC\u51FA Markdown" }),
|
|
749
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_dsh_client_ui_primitives4.IconDownloadOutline16, { size: 12 })
|
|
750
|
+
]
|
|
751
|
+
}
|
|
752
|
+
),
|
|
753
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
754
|
+
import_dsh_client_ui_primitives4.Modal,
|
|
755
|
+
{
|
|
756
|
+
open: error !== null,
|
|
757
|
+
onClose: () => setError(null),
|
|
758
|
+
title: "\u65E0\u6CD5\u5BFC\u51FA\u804A\u5929\u8BB0\u5F55",
|
|
759
|
+
closeLabel: "\u5173\u95ED",
|
|
760
|
+
description: error ?? "",
|
|
761
|
+
footer: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_dsh_client_ui_primitives4.Button, { variant: "primary", onClick: () => setError(null), children: "\u5173\u95ED" })
|
|
762
|
+
}
|
|
763
|
+
)
|
|
764
|
+
] });
|
|
765
|
+
}
|
|
766
|
+
var clientPatch2 = {
|
|
767
|
+
id: CONVERSATION_MARKDOWN_EXPORT_PATCH_ID,
|
|
768
|
+
install: (ctx, activation) => {
|
|
769
|
+
ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
|
|
770
|
+
name: "conversation.session.header.utilities",
|
|
771
|
+
id: `${PLUGIN_NAME}-${CONVERSATION_MARKDOWN_EXPORT_PATCH_ID}`,
|
|
772
|
+
order: 100,
|
|
773
|
+
registrant: PLUGIN_NAME
|
|
774
|
+
}, (props) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ConversationMarkdownExportButton, { ...props, activation })));
|
|
775
|
+
}
|
|
776
|
+
};
|
|
777
|
+
|
|
778
|
+
// src/patches/model-reasoning-efforts/client/index.tsx
|
|
779
|
+
var import_react5 = require("react");
|
|
780
|
+
var import_dsh_client_ui_primitives5 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
781
|
+
|
|
782
|
+
// src/patches/model-reasoning-efforts/shared.ts
|
|
783
|
+
var MODEL_REASONING_EFFORTS_PATCH_ID = "model-reasoning-efforts";
|
|
784
|
+
var LLM_PI_AI_SETTINGS_NAMESPACE = "llm-pi-ai";
|
|
785
|
+
var THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
786
|
+
|
|
787
|
+
// src/patches/model-reasoning-efforts/client/index.tsx
|
|
788
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
789
|
+
var EFFORT_BUTTON_ATTR = "data-dshmore-effort";
|
|
790
|
+
var LEVEL_LABELS = {
|
|
791
|
+
off: "\u4E0D\u601D\u8003",
|
|
792
|
+
minimal: "\u6781\u7B80",
|
|
793
|
+
low: "\u4F4E",
|
|
794
|
+
medium: "\u4E2D",
|
|
795
|
+
high: "\u9AD8",
|
|
796
|
+
xhigh: "\u6781\u9AD8",
|
|
797
|
+
max: "\u6700\u9AD8"
|
|
798
|
+
};
|
|
799
|
+
function emptyDraft() {
|
|
800
|
+
return { nonReasoning: false, levels: {} };
|
|
801
|
+
}
|
|
802
|
+
function draftFromServer(efforts) {
|
|
803
|
+
if (efforts === false) return { nonReasoning: true, levels: {} };
|
|
804
|
+
if (efforts !== null && typeof efforts === "object") {
|
|
805
|
+
const levels = {};
|
|
806
|
+
for (const level of THINKING_LEVELS) {
|
|
807
|
+
const wire = efforts[level];
|
|
808
|
+
if (wire !== void 0) levels[level] = { enabled: true, wire: wire ?? "" };
|
|
809
|
+
}
|
|
810
|
+
return { nonReasoning: false, levels };
|
|
811
|
+
}
|
|
812
|
+
return emptyDraft();
|
|
813
|
+
}
|
|
814
|
+
function buildDict(draft) {
|
|
815
|
+
const dict = {};
|
|
816
|
+
for (const level of THINKING_LEVELS) {
|
|
817
|
+
const entry = draft.levels[level];
|
|
818
|
+
if (entry === void 0 || !entry.enabled) continue;
|
|
819
|
+
dict[level] = level === "off" ? null : entry.wire;
|
|
820
|
+
}
|
|
821
|
+
return dict;
|
|
822
|
+
}
|
|
823
|
+
function summaryLabel(efforts) {
|
|
824
|
+
if (efforts === false) return "\u4E0D\u652F\u6301\u601D\u8003";
|
|
825
|
+
if (efforts === null || typeof efforts !== "object") return "\u672A\u914D\u7F6E";
|
|
826
|
+
const parts = THINKING_LEVELS.filter((level) => level !== "off" && efforts[level] !== void 0).map((level) => LEVEL_LABELS[level]);
|
|
827
|
+
return parts.length === 0 ? "\u672A\u914D\u7F6E" : parts.join("\u3001");
|
|
828
|
+
}
|
|
829
|
+
function findModelEntries() {
|
|
830
|
+
const entries = [];
|
|
831
|
+
const catalogs = document.querySelectorAll('[class*="modelCatalog"]');
|
|
832
|
+
for (const catalog of catalogs) {
|
|
833
|
+
for (const entry of catalog.querySelectorAll('[class*="modelEntry"]')) entries.push(entry);
|
|
834
|
+
}
|
|
835
|
+
return entries;
|
|
836
|
+
}
|
|
837
|
+
function providerRouteOf(entry) {
|
|
838
|
+
const editor = entry.closest('[class*="editor"]');
|
|
839
|
+
if (editor === null) return null;
|
|
840
|
+
const routeSpan = editor.querySelector('[class*="editorRoute"]');
|
|
841
|
+
const route = routeSpan?.textContent?.trim();
|
|
842
|
+
if (route !== void 0 && route !== "") return route;
|
|
843
|
+
const rowCard = editor.closest('[class*="rowCard"]');
|
|
844
|
+
const name = rowCard?.querySelector('[class*="rowName"]')?.textContent?.trim();
|
|
845
|
+
return name !== void 0 && name !== "" ? name : null;
|
|
846
|
+
}
|
|
847
|
+
function modelIdOf(entry) {
|
|
848
|
+
const input = entry.querySelector('[class*="modelRow"] input[type="text"]');
|
|
849
|
+
const id = input?.value.trim();
|
|
850
|
+
return id !== void 0 && id !== "" ? id : null;
|
|
851
|
+
}
|
|
852
|
+
function EffortEditorBody({ draft, setDraft, writable, busy }) {
|
|
853
|
+
const toggleLevel = (level, enabled) => {
|
|
854
|
+
setDraft((current) => {
|
|
855
|
+
const existing = current.levels[level];
|
|
856
|
+
const levels = { ...current.levels };
|
|
857
|
+
levels[level] = existing === void 0 ? { enabled, wire: level === "off" ? "" : level } : { ...existing, enabled };
|
|
858
|
+
return { ...current, levels };
|
|
859
|
+
});
|
|
860
|
+
};
|
|
861
|
+
const setWire = (level, wire) => {
|
|
862
|
+
setDraft((current) => {
|
|
863
|
+
const existing = current.levels[level];
|
|
864
|
+
if (existing === void 0) return current;
|
|
865
|
+
return { ...current, levels: { ...current.levels, [level]: { ...existing, wire } } };
|
|
866
|
+
});
|
|
867
|
+
};
|
|
868
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dshmore-effort-editor", children: [
|
|
869
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "dshmore-effort-switch", children: [
|
|
870
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
871
|
+
"input",
|
|
872
|
+
{
|
|
873
|
+
type: "checkbox",
|
|
874
|
+
role: "switch",
|
|
875
|
+
checked: draft.nonReasoning,
|
|
876
|
+
disabled: !writable || busy,
|
|
877
|
+
onChange: (event) => {
|
|
878
|
+
setDraft((current) => ({ ...current, nonReasoning: event.currentTarget.checked }));
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
),
|
|
882
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "\u8BE5\u6A21\u578B\u4E0D\u652F\u6301\u601D\u8003\uFF08reasoningEfforts: false\uFF09" })
|
|
883
|
+
] }),
|
|
884
|
+
!draft.nonReasoning && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dshmore-effort-levels", children: THINKING_LEVELS.map((level) => {
|
|
885
|
+
const entry = draft.levels[level];
|
|
886
|
+
const enabled = entry?.enabled === true;
|
|
887
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "dshmore-effort-level", children: [
|
|
888
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
889
|
+
"input",
|
|
890
|
+
{
|
|
891
|
+
type: "checkbox",
|
|
892
|
+
checked: enabled,
|
|
893
|
+
disabled: !writable || busy,
|
|
894
|
+
onChange: (event) => {
|
|
895
|
+
toggleLevel(level, event.currentTarget.checked);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
),
|
|
899
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dshmore-effort-level-label", children: LEVEL_LABELS[level] }),
|
|
900
|
+
enabled && level !== "off" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
901
|
+
"input",
|
|
902
|
+
{
|
|
903
|
+
className: "dshmore-effort-wire",
|
|
904
|
+
type: "text",
|
|
905
|
+
value: entry?.wire ?? "",
|
|
906
|
+
disabled: !writable || busy,
|
|
907
|
+
placeholder: "\u7EBF\u4E0A\u503C",
|
|
908
|
+
"aria-label": `${LEVEL_LABELS[level]} \u7684\u7EBF\u4E0A\u503C`,
|
|
909
|
+
onChange: (event) => {
|
|
910
|
+
setWire(level, event.currentTarget.value);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
)
|
|
914
|
+
] }, level);
|
|
915
|
+
}) })
|
|
916
|
+
] });
|
|
917
|
+
}
|
|
918
|
+
function EffortEditorModal({ provider, modelId, initial, writable, onClose }) {
|
|
919
|
+
const [draft, setDraft] = (0, import_react5.useState)(() => draftFromServer(initial ?? null));
|
|
920
|
+
const [busy, setBusy] = (0, import_react5.useState)(false);
|
|
921
|
+
const [error, setError] = (0, import_react5.useState)(null);
|
|
922
|
+
(0, import_react5.useEffect)(() => {
|
|
923
|
+
setDraft(draftFromServer(initial ?? null));
|
|
924
|
+
}, [initial]);
|
|
925
|
+
const known = initial !== void 0;
|
|
926
|
+
const save = async () => {
|
|
927
|
+
if (!known) return;
|
|
928
|
+
const payload = draft.nonReasoning ? false : buildDict(draft);
|
|
929
|
+
if (payload !== false && !THINKING_LEVELS.some((level) => level !== "off" && payload[level] !== void 0)) {
|
|
930
|
+
setError("\u81F3\u5C11\u9700\u8981\u5F00\u542F\u4E00\u4E2A\u975E\u300C\u4E0D\u601D\u8003\u300D\u7684\u601D\u8003\u7EA7\u522B\uFF0C\u6216\u4F7F\u7528\u300C\u6E05\u9664\u914D\u7F6E\u300D\u3002");
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
setBusy(true);
|
|
934
|
+
setError(null);
|
|
935
|
+
try {
|
|
936
|
+
await callPatchApi(MODEL_REASONING_EFFORTS_PATCH_ID, "save", {
|
|
937
|
+
provider,
|
|
938
|
+
modelId,
|
|
939
|
+
reasoningEfforts: payload
|
|
940
|
+
});
|
|
941
|
+
onClose();
|
|
942
|
+
} catch (caught) {
|
|
943
|
+
setError(apiErrorText(caught));
|
|
944
|
+
} finally {
|
|
945
|
+
setBusy(false);
|
|
946
|
+
}
|
|
947
|
+
};
|
|
948
|
+
const clear = async () => {
|
|
949
|
+
if (!known) return;
|
|
950
|
+
setBusy(true);
|
|
951
|
+
setError(null);
|
|
952
|
+
try {
|
|
953
|
+
await callPatchApi(MODEL_REASONING_EFFORTS_PATCH_ID, "save", {
|
|
954
|
+
provider,
|
|
955
|
+
modelId,
|
|
956
|
+
reasoningEfforts: null
|
|
957
|
+
});
|
|
958
|
+
onClose();
|
|
959
|
+
} catch (caught) {
|
|
960
|
+
setError(apiErrorText(caught));
|
|
961
|
+
} finally {
|
|
962
|
+
setBusy(false);
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
966
|
+
import_dsh_client_ui_primitives5.Modal,
|
|
967
|
+
{
|
|
968
|
+
open: true,
|
|
969
|
+
onClose,
|
|
970
|
+
title: `\u601D\u8003\u5F3A\u5EA6 \xB7 ${modelId}`,
|
|
971
|
+
closeLabel: "\u5173\u95ED",
|
|
972
|
+
description: `\u63D0\u4F9B\u65B9 ${provider}`,
|
|
973
|
+
footer: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
974
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_dsh_client_ui_primitives5.Button, { variant: "ghost", disabled: busy, onClick: onClose, children: "\u5173\u95ED" }),
|
|
975
|
+
known && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_dsh_client_ui_primitives5.Button, { variant: "ghost", disabled: !writable || busy, onClick: () => {
|
|
976
|
+
void clear();
|
|
977
|
+
}, children: "\u6E05\u9664\u914D\u7F6E" }),
|
|
978
|
+
known && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_dsh_client_ui_primitives5.Button, { variant: "primary", disabled: !writable || busy, onClick: () => {
|
|
979
|
+
void save();
|
|
980
|
+
}, children: busy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58" })
|
|
981
|
+
] }),
|
|
982
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dshmore-effort-dialog-body", children: [
|
|
983
|
+
!known && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "dshmore-effort-note", children: "\u8BE5\u6A21\u578B\u5C1A\u672A\u4FDD\u5B58\u5230 settings.yaml\uFF0C\u8BF7\u5148\u5728\u4E0A\u65B9\u4FDD\u5B58\u63D0\u4F9B\u65B9\u914D\u7F6E\u3002" }),
|
|
984
|
+
known && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EffortEditorBody, { draft, setDraft, writable, busy }),
|
|
985
|
+
!writable && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "dshmore-effort-warn", children: "\u5F53\u524D Settings \u5B58\u50A8\u4E3A\u53EA\u8BFB\uFF0C\u65E0\u6CD5\u4FDD\u5B58\u4FEE\u6539\u3002" }),
|
|
986
|
+
error !== null && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dshmore-effort-error", children: error })
|
|
987
|
+
] })
|
|
988
|
+
}
|
|
989
|
+
);
|
|
990
|
+
}
|
|
991
|
+
function ModelsPageEffortPortal(props) {
|
|
992
|
+
const settings = (0, import_react5.useSyncExternalStore)(props.activation.subscribe, props.activation.getSnapshot, props.activation.getSnapshot);
|
|
993
|
+
const enabled = settings[MODEL_REASONING_EFFORTS_PATCH_ID];
|
|
994
|
+
const scopeSnapshot = (0, import_react5.useSyncExternalStore)(props.scope.subscribe, props.scope.getSnapshot, props.scope.getSnapshot);
|
|
995
|
+
const [editing, setEditing] = (0, import_react5.useState)(null);
|
|
996
|
+
const [data, setData] = (0, import_react5.useState)(null);
|
|
997
|
+
const dataRef = (0, import_react5.useRef)(data);
|
|
998
|
+
dataRef.current = data;
|
|
999
|
+
const generation = (0, import_react5.useRef)(0);
|
|
1000
|
+
const fillingRef = (0, import_react5.useRef)(false);
|
|
1001
|
+
const reload = (0, import_react5.useCallback)(async () => {
|
|
1002
|
+
const current = ++generation.current;
|
|
1003
|
+
try {
|
|
1004
|
+
const snapshot = await callPatchApi(MODEL_REASONING_EFFORTS_PATCH_ID, "read", {});
|
|
1005
|
+
if (generation.current !== current) return;
|
|
1006
|
+
setData(snapshot);
|
|
1007
|
+
if (enabled && snapshot.writable && snapshot.rows.some((row) => row.reasoningEfforts === null) && !fillingRef.current) {
|
|
1008
|
+
fillingRef.current = true;
|
|
1009
|
+
void callPatchApi(MODEL_REASONING_EFFORTS_PATCH_ID, "fillDefaults", {}).catch(() => void 0).finally(() => {
|
|
1010
|
+
fillingRef.current = false;
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
} catch {
|
|
1014
|
+
}
|
|
1015
|
+
}, [enabled]);
|
|
1016
|
+
(0, import_react5.useEffect)(() => {
|
|
1017
|
+
if (enabled) void reload();
|
|
1018
|
+
}, [enabled, reload, scopeSnapshot]);
|
|
1019
|
+
const syncButtons = (0, import_react5.useCallback)(() => {
|
|
1020
|
+
if (!enabled) return;
|
|
1021
|
+
const active = /* @__PURE__ */ new Set();
|
|
1022
|
+
for (const entry of findModelEntries()) {
|
|
1023
|
+
const provider = providerRouteOf(entry);
|
|
1024
|
+
const modelId = modelIdOf(entry);
|
|
1025
|
+
if (provider === null || modelId === null) continue;
|
|
1026
|
+
active.add(entry);
|
|
1027
|
+
let button = entry.querySelector(`[${EFFORT_BUTTON_ATTR}]`);
|
|
1028
|
+
if (button === null) {
|
|
1029
|
+
button = document.createElement("button");
|
|
1030
|
+
button.type = "button";
|
|
1031
|
+
button.className = "dshmore-effort-row-button";
|
|
1032
|
+
button.setAttribute(EFFORT_BUTTON_ATTR, "");
|
|
1033
|
+
button.addEventListener("click", () => {
|
|
1034
|
+
const route = button?.dataset.provider;
|
|
1035
|
+
const model = button?.dataset.model;
|
|
1036
|
+
if (route !== void 0 && model !== void 0) setEditing({ provider: route, modelId: model });
|
|
1037
|
+
});
|
|
1038
|
+
const row = entry.querySelector('[class*="modelRow"]');
|
|
1039
|
+
if (row === null) continue;
|
|
1040
|
+
entry.insertBefore(button, row.nextSibling);
|
|
1041
|
+
}
|
|
1042
|
+
if (button.dataset.provider !== provider) button.dataset.provider = provider;
|
|
1043
|
+
if (button.dataset.model !== modelId) button.dataset.model = modelId;
|
|
1044
|
+
const rowData = dataRef.current?.rows.find((row) => row.provider === provider && row.modelId === modelId);
|
|
1045
|
+
const text = rowData === void 0 ? "\u601D\u8003\u5F3A\u5EA6" : `\u601D\u8003\u5F3A\u5EA6 \xB7 ${summaryLabel(rowData.reasoningEfforts)}`;
|
|
1046
|
+
if (button.textContent !== text) button.textContent = text;
|
|
1047
|
+
}
|
|
1048
|
+
document.querySelectorAll(`[${EFFORT_BUTTON_ATTR}]`).forEach((button) => {
|
|
1049
|
+
const host = button.parentElement;
|
|
1050
|
+
if (host === null || !active.has(host)) button.remove();
|
|
1051
|
+
});
|
|
1052
|
+
}, [enabled]);
|
|
1053
|
+
(0, import_react5.useEffect)(() => {
|
|
1054
|
+
if (!enabled) {
|
|
1055
|
+
document.querySelectorAll(`[${EFFORT_BUTTON_ATTR}]`).forEach((element) => element.remove());
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
let frame = null;
|
|
1059
|
+
const schedule = () => {
|
|
1060
|
+
if (frame !== null) return;
|
|
1061
|
+
frame = window.requestAnimationFrame(() => {
|
|
1062
|
+
frame = null;
|
|
1063
|
+
syncButtons();
|
|
1064
|
+
});
|
|
1065
|
+
};
|
|
1066
|
+
syncButtons();
|
|
1067
|
+
const observer = new MutationObserver(schedule);
|
|
1068
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
1069
|
+
return () => {
|
|
1070
|
+
observer.disconnect();
|
|
1071
|
+
if (frame !== null) window.cancelAnimationFrame(frame);
|
|
1072
|
+
};
|
|
1073
|
+
}, [enabled, syncButtons]);
|
|
1074
|
+
(0, import_react5.useEffect)(() => {
|
|
1075
|
+
if (enabled) syncButtons();
|
|
1076
|
+
}, [enabled, syncButtons, data]);
|
|
1077
|
+
const editingRow = editing === null ? void 0 : data?.rows.find((row) => row.provider === editing.provider && row.modelId === editing.modelId);
|
|
1078
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
1079
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: effortStyles }),
|
|
1080
|
+
editing !== null && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1081
|
+
EffortEditorModal,
|
|
1082
|
+
{
|
|
1083
|
+
provider: editing.provider,
|
|
1084
|
+
modelId: editing.modelId,
|
|
1085
|
+
initial: editingRow === void 0 ? void 0 : editingRow.reasoningEfforts,
|
|
1086
|
+
writable: data?.writable ?? true,
|
|
1087
|
+
onClose: () => {
|
|
1088
|
+
setEditing(null);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
)
|
|
1092
|
+
] });
|
|
1093
|
+
}
|
|
1094
|
+
var effortStyles = `
|
|
1095
|
+
.dshmore-effort-row-button { box-sizing: border-box; width: 100%; height: 28px; color: var(--dsw-alias-label-secondary); font: inherit; cursor: pointer; background: transparent; border: 1px dashed var(--dsw-alias-border-l3); border-radius: 6px; margin-top: 6px; padding: 0 10px; font-size: 12px; line-height: 18px; display: inline-flex; align-items: center; justify-content: flex-start; gap: 6px; }
|
|
1096
|
+
.dshmore-effort-row-button:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }
|
|
1097
|
+
.dshmore-effort-dialog-body { box-sizing: border-box; display: flex; flex-direction: column; gap: 12px; width: 100%; min-width: 0; overflow: hidden; }
|
|
1098
|
+
.dshmore-effort-editor { display: flex; flex-direction: column; gap: 12px; }
|
|
1099
|
+
.dshmore-effort-switch, .dshmore-effort-level { display: flex; align-items: center; gap: 8px; color: var(--dsw-alias-label-secondary); font-size: 13px; line-height: 20px; cursor: pointer; }
|
|
1100
|
+
.dshmore-effort-switch input, .dshmore-effort-level input[type="checkbox"] { accent-color: var(--dsw-alias-state-business-primary); cursor: pointer; }
|
|
1101
|
+
.dshmore-effort-levels { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px 12px; }
|
|
1102
|
+
.dshmore-effort-level-label { flex: none; }
|
|
1103
|
+
.dshmore-effort-wire { box-sizing: border-box; height: 28px; min-width: 0; width: 100%; font: inherit; color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-1); border: 1px solid var(--dsw-alias-border-l2); border-radius: 6px; padding: 0 8px; font-size: 12px; line-height: 18px; }
|
|
1104
|
+
.dshmore-effort-wire:focus { border-color: var(--dsw-alias-brand-primary); outline: none; }
|
|
1105
|
+
.dshmore-effort-wire::placeholder { color: var(--dsw-alias-label-dimmed); }
|
|
1106
|
+
.dshmore-effort-note { color: var(--dsw-alias-label-tertiary); margin: 0; font-size: 12px; line-height: 18px; }
|
|
1107
|
+
.dshmore-effort-warn { color: var(--dsw-alias-state-warn-label); margin: 0; font-size: 12px; line-height: 18px; }
|
|
1108
|
+
.dshmore-effort-error { padding: 8px 10px; border-radius: 8px; background: rgba(208,58,58,.10); color: var(--dsw-alias-state-error-primary, rgb(220,90,90)); font-size: 12px; line-height: 18px; }
|
|
1109
|
+
`;
|
|
1110
|
+
var clientPatch3 = {
|
|
1111
|
+
id: MODEL_REASONING_EFFORTS_PATCH_ID,
|
|
1112
|
+
install: (ctx, activation) => {
|
|
1113
|
+
const scope = ctx.settingsScope.bind({ namespace: LLM_PI_AI_SETTINGS_NAMESPACE });
|
|
1114
|
+
const boundScope = {
|
|
1115
|
+
getSnapshot: scope.getSnapshot.bind(scope),
|
|
1116
|
+
subscribe: scope.subscribe.bind(scope),
|
|
1117
|
+
set: scope.set.bind(scope),
|
|
1118
|
+
unset: scope.unset.bind(scope)
|
|
1119
|
+
};
|
|
1120
|
+
ctx.slots.inject("shell.overlay", () => ctx.slots.register({
|
|
1121
|
+
name: "shell.overlay",
|
|
1122
|
+
id: `${PLUGIN_NAME}-${MODEL_REASONING_EFFORTS_PATCH_ID}`,
|
|
1123
|
+
order: 90,
|
|
1124
|
+
registrant: PLUGIN_NAME
|
|
1125
|
+
}, (props) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ModelsPageEffortPortal, { ...props, scope: boundScope, activation })));
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
|
|
1129
|
+
// src/patches/conversation-quick-navigation/client/index.tsx
|
|
1130
|
+
var import_react6 = require("react");
|
|
1131
|
+
var import_react_dom = require("react-dom");
|
|
1132
|
+
var import_dsh_client_ui_primitives6 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
1133
|
+
|
|
1134
|
+
// src/kernel/message-visibility.ts
|
|
1135
|
+
var MESSAGE_VISIBILITY_PROJECTION_KEY = "dshMoreMessageDelete";
|
|
1136
|
+
|
|
1137
|
+
// src/patches/conversation-quick-navigation/shared.ts
|
|
1138
|
+
var CONVERSATION_QUICK_NAVIGATION_PATCH_ID = "conversation-quick-navigation";
|
|
1139
|
+
|
|
1140
|
+
// src/platform/dsh/client/message-content.ts
|
|
1141
|
+
function contentText(content) {
|
|
1142
|
+
return content.filter((block) => block.type === "text").map((block) => block.text).join(" ").replace(/\s+/g, " ").trim();
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
// src/patches/conversation-quick-navigation/client/navigation.ts
|
|
1146
|
+
var MAX_LABEL_CODE_POINTS = 72;
|
|
1147
|
+
function conciseLabel(content, fallbackIndex) {
|
|
1148
|
+
const text = contentText(content);
|
|
1149
|
+
if (text === "") return `\u7B2C ${String(fallbackIndex)} \u8F6E \xB7 \u975E\u6587\u672C\u6D88\u606F`;
|
|
1150
|
+
const codePoints = [...text];
|
|
1151
|
+
if (codePoints.length <= MAX_LABEL_CODE_POINTS) return text;
|
|
1152
|
+
return `${codePoints.slice(0, MAX_LABEL_CODE_POINTS - 1).join("")}\u2026`;
|
|
1153
|
+
}
|
|
1154
|
+
function turnSummary(node, key, index) {
|
|
1155
|
+
const candidate = node;
|
|
1156
|
+
if (candidate?.kind !== "user" && candidate?.kind !== "steering") return null;
|
|
1157
|
+
if (!Number.isSafeInteger(candidate.data?.seq) || !Array.isArray(candidate.data?.content)) return null;
|
|
1158
|
+
return {
|
|
1159
|
+
key,
|
|
1160
|
+
seq: candidate.data.seq,
|
|
1161
|
+
label: conciseLabel(candidate.data.content, index)
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
function sameTurns(left, right) {
|
|
1165
|
+
return left.length === right.length && left.every((turn, index) => {
|
|
1166
|
+
const other = right[index];
|
|
1167
|
+
return other !== void 0 && turn.key === other.key && turn.seq === other.seq && turn.label === other.label;
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
function createConversationTurnSelector() {
|
|
1171
|
+
let previousOrder;
|
|
1172
|
+
let previous = [];
|
|
1173
|
+
return (snapshot) => {
|
|
1174
|
+
if (snapshot.chat.order === previousOrder) return previous;
|
|
1175
|
+
previousOrder = snapshot.chat.order;
|
|
1176
|
+
const next = [];
|
|
1177
|
+
for (const key of snapshot.chat.order) {
|
|
1178
|
+
const summary = turnSummary(snapshot.chat.nodes.get(key), key, next.length + 1);
|
|
1179
|
+
if (summary !== null) next.push(summary);
|
|
1180
|
+
}
|
|
1181
|
+
if (!sameTurns(previous, next)) previous = next;
|
|
1182
|
+
return previous;
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
function historyWindowSignature(snapshot) {
|
|
1186
|
+
const first = snapshot.chat.order[0] ?? "";
|
|
1187
|
+
const last = snapshot.chat.order.at(-1) ?? "";
|
|
1188
|
+
return `${first}\0${last}\0${String(snapshot.chat.order.length)}`;
|
|
1189
|
+
}
|
|
1190
|
+
function shouldAutoLoadOlder(state) {
|
|
1191
|
+
return state.enabled && state.surfaceAvailable && state.hasMore && !state.loadingOlder && !state.paused;
|
|
1192
|
+
}
|
|
1193
|
+
function activeTurnAt(positions, readingLine) {
|
|
1194
|
+
let active = positions[0]?.key ?? null;
|
|
1195
|
+
for (const position of positions) {
|
|
1196
|
+
if (position.top > readingLine) break;
|
|
1197
|
+
active = position.key;
|
|
1198
|
+
}
|
|
1199
|
+
return active;
|
|
1200
|
+
}
|
|
1201
|
+
function scrollOffsetForRow(currentScrollTop, scrollportTop, rowTop, topInset = 20) {
|
|
1202
|
+
return Math.max(0, currentScrollTop + rowTop - scrollportTop - topInset);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
// src/patches/conversation-quick-navigation/client/index.tsx
|
|
1206
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1207
|
+
var MAX_NO_PROGRESS_ATTEMPTS = 3;
|
|
1208
|
+
var MENU_LIST_ID = "dshmore-conversation-nav-list";
|
|
1209
|
+
var navigationCollapsedPreference = false;
|
|
1210
|
+
function conversationRow(root, key) {
|
|
1211
|
+
for (const row of root.querySelectorAll("[data-chat-flow-key]")) {
|
|
1212
|
+
if (row.dataset.chatFlowKey === key) return row;
|
|
1213
|
+
}
|
|
1214
|
+
return null;
|
|
1215
|
+
}
|
|
1216
|
+
function sessionRoot(marker) {
|
|
1217
|
+
return marker?.closest("[data-phase]") ?? null;
|
|
1218
|
+
}
|
|
1219
|
+
function conversationScrollport(marker) {
|
|
1220
|
+
return sessionRoot(marker)?.querySelector("[data-conversation-scroll]") ?? null;
|
|
1221
|
+
}
|
|
1222
|
+
function nativeLoadOlderButton(scrollport) {
|
|
1223
|
+
const flow = scrollport.querySelector("[data-chat-flow]");
|
|
1224
|
+
if (flow === null) return null;
|
|
1225
|
+
for (const child of flow.children) {
|
|
1226
|
+
if (child instanceof HTMLElement && child.dataset.chatFlowKey !== void 0) break;
|
|
1227
|
+
const button = child.querySelector("button");
|
|
1228
|
+
if (button !== null) return button;
|
|
1229
|
+
}
|
|
1230
|
+
return null;
|
|
1231
|
+
}
|
|
1232
|
+
function ConversationNavigationMenu({
|
|
1233
|
+
turns,
|
|
1234
|
+
activeKey,
|
|
1235
|
+
hasMore,
|
|
1236
|
+
loadingOlder,
|
|
1237
|
+
autoLoadPaused,
|
|
1238
|
+
collapsed,
|
|
1239
|
+
onRetry,
|
|
1240
|
+
onToggleCollapsed,
|
|
1241
|
+
onJump,
|
|
1242
|
+
listRef
|
|
1243
|
+
}) {
|
|
1244
|
+
const loading = hasMore && !autoLoadPaused;
|
|
1245
|
+
const compactStatus = autoLoadPaused ? `${String(turns.length)} \u8F6E \xB7 \u5DF2\u6682\u505C` : loading ? `${String(turns.length)} \u8F6E \xB7 \u52A0\u8F7D\u4E2D` : `${String(turns.length)} \u8F6E`;
|
|
1246
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1247
|
+
"nav",
|
|
1248
|
+
{
|
|
1249
|
+
className: "dshmore-conversation-nav-menu",
|
|
1250
|
+
"data-collapsed": collapsed ? "true" : "false",
|
|
1251
|
+
"aria-label": "\u5F53\u524D\u4F1A\u8BDD\u7684\u5BF9\u8BDD\u76EE\u5F55",
|
|
1252
|
+
children: [
|
|
1253
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("header", { className: "dshmore-conversation-nav-header", children: [
|
|
1254
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dshmore-conversation-nav-heading", children: [
|
|
1255
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dshmore-conversation-nav-title-row", children: [
|
|
1256
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dshmore-conversation-nav-title", children: "\u5BF9\u8BDD\u76EE\u5F55" }),
|
|
1257
|
+
collapsed && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dshmore-conversation-nav-compact-status", role: "status", "aria-live": "polite", children: [
|
|
1258
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dshmore-conversation-nav-loading-dot", "aria-hidden": "true" }),
|
|
1259
|
+
compactStatus
|
|
1260
|
+
] })
|
|
1261
|
+
] }),
|
|
1262
|
+
!collapsed && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dshmore-conversation-nav-count", role: "status", "aria-live": "polite", children: [
|
|
1263
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dshmore-conversation-nav-loading-dot", "aria-hidden": "true" }),
|
|
1264
|
+
autoLoadPaused ? `\u5DF2\u8F7D\u5165 ${String(turns.length)} \u8F6E\uFF0C\u81EA\u52A8\u52A0\u8F7D\u5DF2\u6682\u505C` : loading ? `\u6B63\u5728\u52A0\u8F7D\u5168\u90E8\u8BB0\u5F55 \xB7 \u5DF2\u8F7D\u5165 ${String(turns.length)} \u8F6E` : `\u5171 ${String(turns.length)} \u8F6E`
|
|
1265
|
+
] })
|
|
1266
|
+
] }),
|
|
1267
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dshmore-conversation-nav-actions", children: [
|
|
1268
|
+
!collapsed && autoLoadPaused && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "dshmore-conversation-nav-retry", onClick: onRetry, children: "\u7EE7\u7EED\u52A0\u8F7D" }),
|
|
1269
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1270
|
+
"button",
|
|
1271
|
+
{
|
|
1272
|
+
type: "button",
|
|
1273
|
+
className: "dshmore-conversation-nav-toggle",
|
|
1274
|
+
"aria-label": collapsed ? "\u5C55\u5F00\u5BF9\u8BDD\u76EE\u5F55" : "\u6298\u53E0\u5BF9\u8BDD\u76EE\u5F55",
|
|
1275
|
+
"aria-controls": MENU_LIST_ID,
|
|
1276
|
+
"aria-expanded": !collapsed,
|
|
1277
|
+
onClick: onToggleCollapsed,
|
|
1278
|
+
children: collapsed ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_dsh_client_ui_primitives6.IconChevronDownOutline14, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_dsh_client_ui_primitives6.IconChevronUpOutline14, { size: 14 })
|
|
1279
|
+
}
|
|
1280
|
+
)
|
|
1281
|
+
] })
|
|
1282
|
+
] }),
|
|
1283
|
+
!collapsed && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { id: MENU_LIST_ID, ref: listRef, className: "dshmore-conversation-nav-list", children: [
|
|
1284
|
+
turns.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dshmore-conversation-nav-empty", children: loadingOlder || hasMore ? "\u6B63\u5728\u8BFB\u53D6\u5386\u53F2\u8BB0\u5F55\u2026" : "\u5F53\u524D\u6CA1\u6709\u53EF\u5B9A\u4F4D\u7684\u5BF9\u8BDD" }),
|
|
1285
|
+
turns.map((turn, index) => {
|
|
1286
|
+
const active = turn.key === activeKey;
|
|
1287
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1288
|
+
"button",
|
|
1289
|
+
{
|
|
1290
|
+
type: "button",
|
|
1291
|
+
className: "dshmore-conversation-nav-item",
|
|
1292
|
+
"aria-current": active ? "location" : void 0,
|
|
1293
|
+
onClick: () => {
|
|
1294
|
+
onJump(turn.key);
|
|
1295
|
+
},
|
|
1296
|
+
children: [
|
|
1297
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dshmore-conversation-nav-index", children: String(index + 1).padStart(2, "0") }),
|
|
1298
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dshmore-conversation-nav-label", children: turn.label })
|
|
1299
|
+
]
|
|
1300
|
+
},
|
|
1301
|
+
turn.key
|
|
1302
|
+
);
|
|
1303
|
+
})
|
|
1304
|
+
] })
|
|
1305
|
+
]
|
|
1306
|
+
}
|
|
1307
|
+
);
|
|
1308
|
+
}
|
|
1309
|
+
function ConversationQuickNavigation(props) {
|
|
1310
|
+
const settings = (0, import_react6.useSyncExternalStore)(props.activation.subscribe, props.activation.getSnapshot, props.activation.getSnapshot);
|
|
1311
|
+
const enabled = settings[CONVERSATION_QUICK_NAVIGATION_PATCH_ID];
|
|
1312
|
+
const selector = (0, import_react6.useMemo)(createConversationTurnSelector, [props.sessionId]);
|
|
1313
|
+
const allTurns = props.useSession(selector);
|
|
1314
|
+
const hasMore = props.useSession((snapshot) => snapshot.hasMore);
|
|
1315
|
+
const loadingOlder = props.useSession((snapshot) => snapshot.loadingOlder);
|
|
1316
|
+
const windowSignature = props.useSession(historyWindowSignature);
|
|
1317
|
+
const projected = props.useProjection(MESSAGE_VISIBILITY_PROJECTION_KEY);
|
|
1318
|
+
const hiddenSeqs = (0, import_react6.useMemo)(() => new Set(projected?.deletedSeqs ?? []), [projected]);
|
|
1319
|
+
const turns = (0, import_react6.useMemo)(
|
|
1320
|
+
() => allTurns.filter((turn) => !hiddenSeqs.has(turn.seq)),
|
|
1321
|
+
[allTurns, hiddenSeqs]
|
|
1322
|
+
);
|
|
1323
|
+
const markerRef = (0, import_react6.useRef)(null);
|
|
1324
|
+
const listRef = (0, import_react6.useRef)(null);
|
|
1325
|
+
const pendingLoadSignatureRef = (0, import_react6.useRef)(null);
|
|
1326
|
+
const noProgressAttemptsRef = (0, import_react6.useRef)(0);
|
|
1327
|
+
const [portalTarget, setPortalTarget] = (0, import_react6.useState)(null);
|
|
1328
|
+
const [activeKey, setActiveKey] = (0, import_react6.useState)(null);
|
|
1329
|
+
const [surfaceAvailable, setSurfaceAvailable] = (0, import_react6.useState)(false);
|
|
1330
|
+
const [composerInset, setComposerInset] = (0, import_react6.useState)(176);
|
|
1331
|
+
const [autoLoadPaused, setAutoLoadPaused] = (0, import_react6.useState)(false);
|
|
1332
|
+
const [loadTick, setLoadTick] = (0, import_react6.useState)(0);
|
|
1333
|
+
const [collapsed, setCollapsed] = (0, import_react6.useState)(() => navigationCollapsedPreference);
|
|
1334
|
+
(0, import_react6.useLayoutEffect)(() => {
|
|
1335
|
+
const root = sessionRoot(markerRef.current);
|
|
1336
|
+
setPortalTarget(root);
|
|
1337
|
+
if (root === null) return;
|
|
1338
|
+
const composer = root.querySelector("[data-composer-seat]");
|
|
1339
|
+
const updateInset = () => {
|
|
1340
|
+
setComposerInset(Math.max(96, (composer?.offsetHeight ?? 152) + 20));
|
|
1341
|
+
};
|
|
1342
|
+
updateInset();
|
|
1343
|
+
if (composer === null || typeof ResizeObserver === "undefined") return;
|
|
1344
|
+
const observer = new ResizeObserver(updateInset);
|
|
1345
|
+
observer.observe(composer);
|
|
1346
|
+
return () => {
|
|
1347
|
+
observer.disconnect();
|
|
1348
|
+
};
|
|
1349
|
+
}, [props.sessionId]);
|
|
1350
|
+
(0, import_react6.useEffect)(() => {
|
|
1351
|
+
pendingLoadSignatureRef.current = null;
|
|
1352
|
+
noProgressAttemptsRef.current = 0;
|
|
1353
|
+
setAutoLoadPaused(false);
|
|
1354
|
+
}, [enabled, props.sessionId]);
|
|
1355
|
+
(0, import_react6.useEffect)(() => {
|
|
1356
|
+
if (hasMore) return;
|
|
1357
|
+
pendingLoadSignatureRef.current = null;
|
|
1358
|
+
noProgressAttemptsRef.current = 0;
|
|
1359
|
+
setAutoLoadPaused(false);
|
|
1360
|
+
}, [hasMore]);
|
|
1361
|
+
(0, import_react6.useEffect)(() => {
|
|
1362
|
+
if (!enabled) {
|
|
1363
|
+
setActiveKey(null);
|
|
1364
|
+
setSurfaceAvailable(false);
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
1367
|
+
let frame = null;
|
|
1368
|
+
let scrollport = null;
|
|
1369
|
+
const schedule = () => {
|
|
1370
|
+
if (frame !== null) return;
|
|
1371
|
+
frame = window.requestAnimationFrame(recompute);
|
|
1372
|
+
};
|
|
1373
|
+
const bindScrollport = (next) => {
|
|
1374
|
+
if (next === scrollport) return;
|
|
1375
|
+
scrollport?.removeEventListener("scroll", schedule);
|
|
1376
|
+
scrollport = next;
|
|
1377
|
+
scrollport?.addEventListener("scroll", schedule, { passive: true });
|
|
1378
|
+
};
|
|
1379
|
+
const recompute = () => {
|
|
1380
|
+
frame = null;
|
|
1381
|
+
const nextScrollport = conversationScrollport(markerRef.current);
|
|
1382
|
+
bindScrollport(nextScrollport);
|
|
1383
|
+
const flowAvailable = nextScrollport !== null && nextScrollport.querySelector("[data-chat-flow]") !== null;
|
|
1384
|
+
setSurfaceAvailable(flowAvailable);
|
|
1385
|
+
if (nextScrollport === null || !flowAvailable) {
|
|
1386
|
+
setActiveKey(null);
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
const viewport = nextScrollport.getBoundingClientRect();
|
|
1390
|
+
const readingLine = viewport.top + Math.min(120, nextScrollport.clientHeight * 0.24);
|
|
1391
|
+
const positions = [];
|
|
1392
|
+
for (const turn of turns) {
|
|
1393
|
+
const row = conversationRow(nextScrollport, turn.key);
|
|
1394
|
+
if (row !== null) positions.push({ key: turn.key, top: row.getBoundingClientRect().top });
|
|
1395
|
+
}
|
|
1396
|
+
setActiveKey(activeTurnAt(positions, readingLine));
|
|
1397
|
+
};
|
|
1398
|
+
const observer = new MutationObserver(schedule);
|
|
1399
|
+
const root = sessionRoot(markerRef.current);
|
|
1400
|
+
if (root !== null) observer.observe(root, { childList: true, subtree: true });
|
|
1401
|
+
window.addEventListener("resize", schedule);
|
|
1402
|
+
recompute();
|
|
1403
|
+
return () => {
|
|
1404
|
+
observer.disconnect();
|
|
1405
|
+
window.removeEventListener("resize", schedule);
|
|
1406
|
+
scrollport?.removeEventListener("scroll", schedule);
|
|
1407
|
+
if (frame !== null) window.cancelAnimationFrame(frame);
|
|
1408
|
+
};
|
|
1409
|
+
}, [enabled, turns]);
|
|
1410
|
+
(0, import_react6.useEffect)(() => {
|
|
1411
|
+
if (!shouldAutoLoadOlder({ enabled, surfaceAvailable, hasMore, loadingOlder, paused: autoLoadPaused })) return;
|
|
1412
|
+
const pendingSignature = pendingLoadSignatureRef.current;
|
|
1413
|
+
let delay2 = 0;
|
|
1414
|
+
if (pendingSignature !== null) {
|
|
1415
|
+
if (pendingSignature === windowSignature) {
|
|
1416
|
+
noProgressAttemptsRef.current += 1;
|
|
1417
|
+
if (noProgressAttemptsRef.current >= MAX_NO_PROGRESS_ATTEMPTS) {
|
|
1418
|
+
pendingLoadSignatureRef.current = null;
|
|
1419
|
+
setAutoLoadPaused(true);
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
delay2 = noProgressAttemptsRef.current * 350;
|
|
1423
|
+
} else {
|
|
1424
|
+
noProgressAttemptsRef.current = 0;
|
|
1425
|
+
}
|
|
1426
|
+
pendingLoadSignatureRef.current = null;
|
|
1427
|
+
}
|
|
1428
|
+
let frame = null;
|
|
1429
|
+
let retryTimer = null;
|
|
1430
|
+
let requestTimer = null;
|
|
1431
|
+
const request = () => {
|
|
1432
|
+
frame = window.requestAnimationFrame(() => {
|
|
1433
|
+
frame = null;
|
|
1434
|
+
const scrollport = conversationScrollport(markerRef.current);
|
|
1435
|
+
const button = scrollport === null ? null : nativeLoadOlderButton(scrollport);
|
|
1436
|
+
if (button === null || button.disabled) {
|
|
1437
|
+
retryTimer = window.setTimeout(() => {
|
|
1438
|
+
setLoadTick((current) => current + 1);
|
|
1439
|
+
}, 180);
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
pendingLoadSignatureRef.current = windowSignature;
|
|
1443
|
+
button.click();
|
|
1444
|
+
retryTimer = window.setTimeout(() => {
|
|
1445
|
+
setLoadTick((current) => current + 1);
|
|
1446
|
+
}, 1600);
|
|
1447
|
+
});
|
|
1448
|
+
};
|
|
1449
|
+
if (delay2 > 0) requestTimer = window.setTimeout(request, delay2);
|
|
1450
|
+
else request();
|
|
1451
|
+
return () => {
|
|
1452
|
+
if (frame !== null) window.cancelAnimationFrame(frame);
|
|
1453
|
+
if (retryTimer !== null) window.clearTimeout(retryTimer);
|
|
1454
|
+
if (requestTimer !== null) window.clearTimeout(requestTimer);
|
|
1455
|
+
};
|
|
1456
|
+
}, [autoLoadPaused, enabled, hasMore, loadTick, loadingOlder, surfaceAvailable, windowSignature]);
|
|
1457
|
+
(0, import_react6.useLayoutEffect)(() => {
|
|
1458
|
+
if (collapsed || activeKey === null) return;
|
|
1459
|
+
const list = listRef.current;
|
|
1460
|
+
const active = list?.querySelector('[aria-current="location"]');
|
|
1461
|
+
if (list === null || active === null || active === void 0) return;
|
|
1462
|
+
const top = active.offsetTop;
|
|
1463
|
+
const bottom = top + active.offsetHeight;
|
|
1464
|
+
if (top < list.scrollTop) list.scrollTop = top;
|
|
1465
|
+
else if (bottom > list.scrollTop + list.clientHeight) list.scrollTop = bottom - list.clientHeight;
|
|
1466
|
+
}, [activeKey, collapsed]);
|
|
1467
|
+
const retryAutoLoad = () => {
|
|
1468
|
+
pendingLoadSignatureRef.current = null;
|
|
1469
|
+
noProgressAttemptsRef.current = 0;
|
|
1470
|
+
setAutoLoadPaused(false);
|
|
1471
|
+
setLoadTick((current) => current + 1);
|
|
1472
|
+
};
|
|
1473
|
+
const toggleCollapsed = () => {
|
|
1474
|
+
setCollapsed((current) => {
|
|
1475
|
+
navigationCollapsedPreference = !current;
|
|
1476
|
+
return navigationCollapsedPreference;
|
|
1477
|
+
});
|
|
1478
|
+
};
|
|
1479
|
+
const jump = (key) => {
|
|
1480
|
+
const scrollport = conversationScrollport(markerRef.current);
|
|
1481
|
+
if (scrollport === null) return;
|
|
1482
|
+
const row = conversationRow(scrollport, key);
|
|
1483
|
+
if (row === null) return;
|
|
1484
|
+
const viewport = scrollport.getBoundingClientRect();
|
|
1485
|
+
const rowRect = row.getBoundingClientRect();
|
|
1486
|
+
const top = scrollOffsetForRow(scrollport.scrollTop, viewport.top, rowRect.top);
|
|
1487
|
+
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1488
|
+
scrollport.scrollTo({ top, behavior: reducedMotion ? "auto" : "smooth" });
|
|
1489
|
+
setActiveKey(key);
|
|
1490
|
+
};
|
|
1491
|
+
const rootStyle = { "--dshmore-conversation-nav-bottom": `${String(composerInset)}px` };
|
|
1492
|
+
const surface = enabled && surfaceAvailable && portalTarget !== null ? (0, import_react_dom.createPortal)(
|
|
1493
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1494
|
+
"div",
|
|
1495
|
+
{
|
|
1496
|
+
className: "dshmore-conversation-nav-root",
|
|
1497
|
+
"data-collapsed": collapsed ? "true" : "false",
|
|
1498
|
+
style: rootStyle,
|
|
1499
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1500
|
+
ConversationNavigationMenu,
|
|
1501
|
+
{
|
|
1502
|
+
turns,
|
|
1503
|
+
activeKey,
|
|
1504
|
+
hasMore,
|
|
1505
|
+
loadingOlder,
|
|
1506
|
+
autoLoadPaused,
|
|
1507
|
+
collapsed,
|
|
1508
|
+
onRetry: retryAutoLoad,
|
|
1509
|
+
onToggleCollapsed: toggleCollapsed,
|
|
1510
|
+
onJump: jump,
|
|
1511
|
+
listRef
|
|
1512
|
+
}
|
|
1513
|
+
)
|
|
1514
|
+
}
|
|
1515
|
+
),
|
|
1516
|
+
portalTarget
|
|
1517
|
+
) : null;
|
|
1518
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1519
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { ref: markerRef, className: "dshmore-conversation-nav-marker", "aria-hidden": "true" }),
|
|
1520
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("style", { children: navigationStyles }),
|
|
1521
|
+
surface
|
|
1522
|
+
] });
|
|
1523
|
+
}
|
|
1524
|
+
var navigationStyles = `
|
|
1525
|
+
.dshmore-conversation-nav-marker { display: none; }
|
|
1526
|
+
.dshmore-conversation-nav-root { position: absolute; z-index: 20; right: 14px; top: 78px; bottom: var(--dshmore-conversation-nav-bottom, 176px); width: min(286px, calc(100% - 40px)); pointer-events: none; display: flex; align-items: flex-start; font-family: var(--dsw-font-family); }
|
|
1527
|
+
.dshmore-conversation-nav-root[data-collapsed="true"] { bottom: auto; height: 58px; }
|
|
1528
|
+
.dshmore-conversation-nav-menu { box-sizing: border-box; width: 100%; max-height: min(620px, 100%); min-height: 0; overflow: hidden; pointer-events: auto; display: flex; flex-direction: column; border: 1px solid var(--dsw-alias-border-l2); border-radius: 12px; color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-base); box-shadow: var(--dsw-shadow-lv1, var(--dsw-shadow-lv2)); animation: dshmore-conversation-nav-enter 150ms ease-out; }
|
|
1529
|
+
.dshmore-conversation-nav-menu[data-collapsed="true"] { height: 58px; max-height: 58px; }
|
|
1530
|
+
.dshmore-conversation-nav-header { box-sizing: border-box; flex: none; min-height: 58px; padding: 12px 12px 10px 14px; display: flex; align-items: center; justify-content: space-between; gap: 10px; border-bottom: 1px solid var(--dsw-alias-border-l3); }
|
|
1531
|
+
.dshmore-conversation-nav-menu[data-collapsed="true"] .dshmore-conversation-nav-header { height: 58px; min-height: 58px; padding: 0 12px 0 14px; border-bottom: 0; }
|
|
1532
|
+
.dshmore-conversation-nav-heading { min-width: 0; }
|
|
1533
|
+
.dshmore-conversation-nav-title-row { min-width: 0; display: flex; align-items: baseline; gap: 6px; }
|
|
1534
|
+
.dshmore-conversation-nav-title { font: var(--dsw-font-s-strong-14); }
|
|
1535
|
+
.dshmore-conversation-nav-compact-status { min-width: 0; overflow: hidden; color: var(--dsw-alias-label-caption); text-overflow: ellipsis; white-space: nowrap; font-size: 11px; line-height: 16px; }
|
|
1536
|
+
.dshmore-conversation-nav-count { margin-top: 2px; color: var(--dsw-alias-label-caption); font-size: 11px; line-height: 16px; text-wrap: pretty; }
|
|
1537
|
+
.dshmore-conversation-nav-loading-dot { width: 6px; height: 6px; margin-right: 6px; display: inline-block; border-radius: 999px; background: var(--dsw-alias-state-business-primary); vertical-align: 1px; animation: dshmore-conversation-nav-pulse 1.1s ease-in-out infinite; }
|
|
1538
|
+
.dshmore-conversation-nav-retry { flex: none; min-height: 28px; padding: 4px 9px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 8px; color: var(--dsw-alias-label-secondary); background: transparent; cursor: pointer; font: inherit; font-size: 11px; }
|
|
1539
|
+
.dshmore-conversation-nav-retry:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover); }
|
|
1540
|
+
.dshmore-conversation-nav-actions { flex: none; display: flex; align-items: center; gap: 6px; }
|
|
1541
|
+
.dshmore-conversation-nav-toggle { box-sizing: border-box; width: 28px; height: 28px; flex: none; padding: 0; display: grid; place-items: center; border: 0; border-radius: 999px; color: var(--dsw-alias-label-secondary); background: transparent; cursor: pointer; }
|
|
1542
|
+
.dshmore-conversation-nav-toggle:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover); }
|
|
1543
|
+
.dshmore-conversation-nav-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 6px; scrollbar-width: thin; }
|
|
1544
|
+
.dshmore-conversation-nav-empty { padding: 18px 12px; color: var(--dsw-alias-label-caption); text-align: center; font-size: 12px; line-height: 18px; text-wrap: pretty; }
|
|
1545
|
+
.dshmore-conversation-nav-item { box-sizing: border-box; width: 100%; min-height: 46px; padding: 7px 9px; display: grid; grid-template-columns: 28px minmax(0, 1fr); align-items: start; gap: 2px; border: 0; border-radius: 9px; color: var(--dsw-alias-label-secondary); background: transparent; cursor: pointer; text-align: left; font-family: inherit; transition: background-color 120ms ease, color 120ms ease; }
|
|
1546
|
+
.dshmore-conversation-nav-item:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover); }
|
|
1547
|
+
.dshmore-conversation-nav-item:focus-visible, .dshmore-conversation-nav-retry:focus-visible, .dshmore-conversation-nav-toggle:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary); outline-offset: -2px; }
|
|
1548
|
+
.dshmore-conversation-nav-item[aria-current="location"] { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover-solid); }
|
|
1549
|
+
.dshmore-conversation-nav-index { color: var(--dsw-alias-label-caption); font-size: 11px; line-height: 20px; font-variant-numeric: tabular-nums; }
|
|
1550
|
+
.dshmore-conversation-nav-item[aria-current="location"] .dshmore-conversation-nav-index { color: var(--dsw-alias-state-business-primary); font-weight: 600; }
|
|
1551
|
+
.dshmore-conversation-nav-label { min-width: 0; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; font-size: 13px; line-height: 20px; overflow-wrap: anywhere; text-wrap: pretty; }
|
|
1552
|
+
@keyframes dshmore-conversation-nav-enter { from { opacity: 0; transform: translateX(6px); } to { opacity: 1; transform: translateX(0); } }
|
|
1553
|
+
@keyframes dshmore-conversation-nav-pulse { 50% { opacity: .35; transform: scale(.82); } }
|
|
1554
|
+
@media (max-width: 720px) {
|
|
1555
|
+
.dshmore-conversation-nav-root { right: 8px; top: 74px; bottom: auto; width: calc(100% - 16px); height: min(220px, 32dvh); align-items: stretch; }
|
|
1556
|
+
.dshmore-conversation-nav-root[data-collapsed="true"] { height: 58px; }
|
|
1557
|
+
.dshmore-conversation-nav-menu { max-height: 100%; }
|
|
1558
|
+
}
|
|
1559
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1560
|
+
.dshmore-conversation-nav-menu, .dshmore-conversation-nav-loading-dot { animation: none; }
|
|
1561
|
+
.dshmore-conversation-nav-item { transition: none; }
|
|
1562
|
+
}
|
|
1563
|
+
`;
|
|
1564
|
+
var clientPatch4 = {
|
|
1565
|
+
id: CONVERSATION_QUICK_NAVIGATION_PATCH_ID,
|
|
1566
|
+
install: (ctx, activation) => {
|
|
1567
|
+
ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
|
|
1568
|
+
name: "conversation.session.header.utilities",
|
|
1569
|
+
id: `${PLUGIN_NAME}-${CONVERSATION_QUICK_NAVIGATION_PATCH_ID}`,
|
|
1570
|
+
order: 120,
|
|
1571
|
+
registrant: PLUGIN_NAME
|
|
1572
|
+
}, (props) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ConversationQuickNavigation, { ...props, activation })));
|
|
1573
|
+
}
|
|
1574
|
+
};
|
|
1575
|
+
|
|
626
1576
|
// src/generated/client-registry.ts
|
|
627
1577
|
var MESSAGE_ACTION_PATCHES = [{ id: "message-edit", order: 100, useActions: useMessageActions }, { id: "message-delete", order: 200, useActions: useMessageActions2 }];
|
|
628
|
-
var CLIENT_PATCHES = [clientPatch];
|
|
1578
|
+
var CLIENT_PATCHES = [clientPatch, clientPatch2, clientPatch3, clientPatch4];
|
|
629
1579
|
|
|
630
1580
|
// src/kernel/client/activation.ts
|
|
631
1581
|
var ClientPatchActivation = class {
|
|
632
1582
|
constructor(scope) {
|
|
633
1583
|
this.scope = scope;
|
|
634
1584
|
}
|
|
1585
|
+
scope;
|
|
635
1586
|
lastScope;
|
|
636
1587
|
lastValue = { ...DEFAULT_PATCH_SETTINGS };
|
|
637
1588
|
getSnapshot = () => {
|
|
@@ -647,19 +1598,19 @@ var ClientPatchActivation = class {
|
|
|
647
1598
|
};
|
|
648
1599
|
|
|
649
1600
|
// src/kernel/client/config-card.tsx
|
|
650
|
-
var
|
|
651
|
-
var
|
|
652
|
-
var
|
|
1601
|
+
var import_react7 = require("react");
|
|
1602
|
+
var import_dsh_client_ui_primitives7 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
1603
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
653
1604
|
function PatchConfigCard({ activation, initiallyOpen = false }) {
|
|
654
|
-
const scope = (0,
|
|
1605
|
+
const scope = (0, import_react7.useSyncExternalStore)(
|
|
655
1606
|
activation.subscribe,
|
|
656
1607
|
activation.getSettingsSnapshot,
|
|
657
1608
|
activation.getSettingsSnapshot
|
|
658
1609
|
);
|
|
659
1610
|
const settings = activation.getSnapshot();
|
|
660
|
-
const [open, setOpen] = (0,
|
|
661
|
-
const [busy, setBusy] = (0,
|
|
662
|
-
const [error, setError] = (0,
|
|
1611
|
+
const [open, setOpen] = (0, import_react7.useState)(initiallyOpen);
|
|
1612
|
+
const [busy, setBusy] = (0, import_react7.useState)(/* @__PURE__ */ new Set());
|
|
1613
|
+
const [error, setError] = (0, import_react7.useState)(null);
|
|
663
1614
|
if (scope.status === "unavailable") return null;
|
|
664
1615
|
const update = async (id, enabled) => {
|
|
665
1616
|
setBusy((current) => /* @__PURE__ */ new Set([...current, id]));
|
|
@@ -676,9 +1627,9 @@ function PatchConfigCard({ activation, initiallyOpen = false }) {
|
|
|
676
1627
|
});
|
|
677
1628
|
}
|
|
678
1629
|
};
|
|
679
|
-
return /* @__PURE__ */ (0,
|
|
680
|
-
/* @__PURE__ */ (0,
|
|
681
|
-
/* @__PURE__ */ (0,
|
|
1630
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("li", { className: `dshmore-config-card${open ? " dshmore-config-card-open" : ""}`, children: [
|
|
1631
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("style", { children: styles }),
|
|
1632
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
682
1633
|
"button",
|
|
683
1634
|
{
|
|
684
1635
|
type: "button",
|
|
@@ -689,23 +1640,23 @@ function PatchConfigCard({ activation, initiallyOpen = false }) {
|
|
|
689
1640
|
setOpen((current) => !current);
|
|
690
1641
|
},
|
|
691
1642
|
children: [
|
|
692
|
-
/* @__PURE__ */ (0,
|
|
693
|
-
/* @__PURE__ */ (0,
|
|
694
|
-
/* @__PURE__ */ (0,
|
|
1643
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "dshmore-config-heading-copy", children: [
|
|
1644
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("strong", { children: "DSH More" }),
|
|
1645
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "\u52A8\u6001\u5F00\u542F\u6216\u5173\u95ED\u72EC\u7ACB\u8865\u4E01\uFF0C\u9ED8\u8BA4\u5168\u90E8\u5F00\u542F\u3002" })
|
|
695
1646
|
] }),
|
|
696
|
-
/* @__PURE__ */ (0,
|
|
1647
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_dsh_client_ui_primitives7.IconChevronDownOutline14, { className: `dshmore-config-chevron${open ? " dshmore-config-chevron-open" : ""}` })
|
|
697
1648
|
]
|
|
698
1649
|
}
|
|
699
1650
|
),
|
|
700
|
-
open && /* @__PURE__ */ (0,
|
|
701
|
-
/* @__PURE__ */ (0,
|
|
1651
|
+
open && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "dshmore-config-body", children: [
|
|
1652
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "dshmore-config-list", children: PATCH_CATALOG.map((patch) => {
|
|
702
1653
|
const waiting = busy.has(patch.id);
|
|
703
|
-
return /* @__PURE__ */ (0,
|
|
704
|
-
/* @__PURE__ */ (0,
|
|
705
|
-
/* @__PURE__ */ (0,
|
|
706
|
-
/* @__PURE__ */ (0,
|
|
1654
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "dshmore-config-row", children: [
|
|
1655
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "dshmore-config-copy", children: [
|
|
1656
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("strong", { children: patch.name }),
|
|
1657
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: patch.description })
|
|
707
1658
|
] }),
|
|
708
|
-
/* @__PURE__ */ (0,
|
|
1659
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
709
1660
|
"input",
|
|
710
1661
|
{
|
|
711
1662
|
type: "checkbox",
|
|
@@ -720,9 +1671,9 @@ function PatchConfigCard({ activation, initiallyOpen = false }) {
|
|
|
720
1671
|
)
|
|
721
1672
|
] }, patch.id);
|
|
722
1673
|
}) }),
|
|
723
|
-
scope.status === "loading" && /* @__PURE__ */ (0,
|
|
724
|
-
scope.status === "ready" && !scope.writable && /* @__PURE__ */ (0,
|
|
725
|
-
error !== null && /* @__PURE__ */ (0,
|
|
1674
|
+
scope.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "dshmore-config-note", children: "\u6B63\u5728\u8BFB\u53D6\u63D2\u4EF6\u914D\u7F6E\u2026" }),
|
|
1675
|
+
scope.status === "ready" && !scope.writable && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "dshmore-config-note", children: "\u5F53\u524D Settings \u5B58\u50A8\u4E3A\u53EA\u8BFB\u3002" }),
|
|
1676
|
+
error !== null && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "dshmore-config-error", children: error })
|
|
726
1677
|
] })
|
|
727
1678
|
] });
|
|
728
1679
|
}
|
|
@@ -732,21 +1683,15 @@ function installPatchConfigCard(ctx, activation) {
|
|
|
732
1683
|
key: PATCH_SETTINGS_NAMESPACE,
|
|
733
1684
|
priority: 90,
|
|
734
1685
|
registrant: PLUGIN_NAME
|
|
735
|
-
}, (props) => /* @__PURE__ */ (0,
|
|
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();
|
|
1686
|
+
}, (props) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PatchConfigCard, { ...props, activation })));
|
|
747
1687
|
}
|
|
748
1688
|
|
|
749
1689
|
// src/platform/dsh/client/message-targets.tsx
|
|
1690
|
+
var import_react8 = require("react");
|
|
1691
|
+
var EMPTY_MESSAGE_SURFACE = {
|
|
1692
|
+
rows: [],
|
|
1693
|
+
byKey: /* @__PURE__ */ new Map()
|
|
1694
|
+
};
|
|
750
1695
|
function messageNodeInfo(node) {
|
|
751
1696
|
const candidate = node;
|
|
752
1697
|
if (candidate?.kind === "user" && Number.isSafeInteger(candidate.data?.seq) && Array.isArray(candidate.data?.content)) {
|
|
@@ -775,6 +1720,38 @@ function presentationSeq(node) {
|
|
|
775
1720
|
if (Number.isSafeInteger(closing?.finalNode?.seq)) return closing?.finalNode?.seq;
|
|
776
1721
|
return void 0;
|
|
777
1722
|
}
|
|
1723
|
+
function sameSurfaceRow(left, right) {
|
|
1724
|
+
return left !== void 0 && left.key === right.key && left.presentationSeq === right.presentationSeq && left.action?.seq === right.action?.seq && left.action?.kind === right.action?.kind && left.action?.text === right.action?.text;
|
|
1725
|
+
}
|
|
1726
|
+
function createMessageSurfaceSelector(active = true) {
|
|
1727
|
+
if (!active) return () => EMPTY_MESSAGE_SURFACE;
|
|
1728
|
+
const nodeCache = /* @__PURE__ */ new WeakMap();
|
|
1729
|
+
let previous = EMPTY_MESSAGE_SURFACE;
|
|
1730
|
+
let previousOrder;
|
|
1731
|
+
return (snapshot) => {
|
|
1732
|
+
if (snapshot.chat.order === previousOrder) return previous;
|
|
1733
|
+
previousOrder = snapshot.chat.order;
|
|
1734
|
+
const rows = [];
|
|
1735
|
+
for (const key of snapshot.chat.order) {
|
|
1736
|
+
const node = snapshot.chat.nodes.get(key);
|
|
1737
|
+
if (node === void 0) continue;
|
|
1738
|
+
let row = nodeCache.get(node);
|
|
1739
|
+
if (row === void 0) {
|
|
1740
|
+
row = {
|
|
1741
|
+
key,
|
|
1742
|
+
presentationSeq: presentationSeq(node),
|
|
1743
|
+
action: messageNodeInfo(node)
|
|
1744
|
+
};
|
|
1745
|
+
nodeCache.set(node, row);
|
|
1746
|
+
}
|
|
1747
|
+
const retained = previous.byKey.get(key);
|
|
1748
|
+
rows.push(sameSurfaceRow(retained, row) ? retained : row);
|
|
1749
|
+
}
|
|
1750
|
+
if (rows.length === previous.rows.length && rows.every((row, index) => row === previous.rows[index])) return previous;
|
|
1751
|
+
previous = { rows, byKey: new Map(rows.map((row) => [row.key, row])) };
|
|
1752
|
+
return previous;
|
|
1753
|
+
};
|
|
1754
|
+
}
|
|
778
1755
|
function hostForRow(row, kind) {
|
|
779
1756
|
const existing = row.querySelector(":scope [data-dshmore-message-actions]");
|
|
780
1757
|
if (kind === "assistant") {
|
|
@@ -818,11 +1795,11 @@ function hostForRow(row, kind) {
|
|
|
818
1795
|
else row.appendChild(host);
|
|
819
1796
|
return host;
|
|
820
1797
|
}
|
|
1798
|
+
function sameTarget(left, right) {
|
|
1799
|
+
return right !== void 0 && left.key === right.key && left.seq === right.seq && left.host === right.host && left.text === right.text;
|
|
1800
|
+
}
|
|
821
1801
|
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
|
-
});
|
|
1802
|
+
return left.length === right.length && left.every((target, index) => sameTarget(target, right[index]));
|
|
826
1803
|
}
|
|
827
1804
|
function trajectoryRecordKey(row) {
|
|
828
1805
|
const encoded = row.dataset.trajectoryRowKey;
|
|
@@ -843,73 +1820,153 @@ function trajectorySourceSeq(row) {
|
|
|
843
1820
|
const seq = Number(raw);
|
|
844
1821
|
return Number.isSafeInteger(seq) && seq >= 0 ? seq : void 0;
|
|
845
1822
|
}
|
|
846
|
-
function useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys) {
|
|
847
|
-
const
|
|
848
|
-
const
|
|
849
|
-
(0,
|
|
1823
|
+
function useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys, active = true) {
|
|
1824
|
+
const selector = (0, import_react8.useMemo)(() => createMessageSurfaceSelector(active), [active, props.sessionId]);
|
|
1825
|
+
const surface = props.useSession(selector);
|
|
1826
|
+
const surfaceRef = (0, import_react8.useRef)(surface);
|
|
1827
|
+
const hiddenSeqsRef = (0, import_react8.useRef)(hiddenSeqs);
|
|
1828
|
+
const hiddenTrajectoryKeysRef = (0, import_react8.useRef)(hiddenTrajectoryKeys);
|
|
1829
|
+
surfaceRef.current = surface;
|
|
1830
|
+
hiddenSeqsRef.current = hiddenSeqs;
|
|
1831
|
+
hiddenTrajectoryKeysRef.current = hiddenTrajectoryKeys;
|
|
1832
|
+
const [targets, setTargets] = (0, import_react8.useState)([]);
|
|
1833
|
+
const requestFullScanRef = (0, import_react8.useRef)(null);
|
|
1834
|
+
(0, import_react8.useLayoutEffect)(() => {
|
|
1835
|
+
if (!active) {
|
|
1836
|
+
setTargets((current) => current.length === 0 ? current : []);
|
|
1837
|
+
return;
|
|
1838
|
+
}
|
|
850
1839
|
let frame = null;
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
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) });
|
|
1840
|
+
let fullScanRequested = true;
|
|
1841
|
+
let pruneDisconnected = false;
|
|
1842
|
+
let targetsDirty = false;
|
|
1843
|
+
const pendingChatRows = /* @__PURE__ */ new Set();
|
|
1844
|
+
const pendingTrajectoryRows = /* @__PURE__ */ new Set();
|
|
1845
|
+
const mountedTargets = /* @__PURE__ */ new Map();
|
|
1846
|
+
const processChatRow = (row) => {
|
|
1847
|
+
const key = row.dataset.chatFlowKey;
|
|
1848
|
+
if (key === void 0) return;
|
|
1849
|
+
const descriptor = surfaceRef.current.byKey.get(key);
|
|
1850
|
+
if (descriptor === void 0) {
|
|
1851
|
+
if (mountedTargets.delete(key)) targetsDirty = true;
|
|
1852
|
+
row.querySelector("[data-dshmore-message-actions]")?.remove();
|
|
1853
|
+
return;
|
|
871
1854
|
}
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
1855
|
+
if (descriptor.presentationSeq !== void 0 && hiddenSeqsRef.current.has(descriptor.presentationSeq)) {
|
|
1856
|
+
row.dataset.dshmoreHidden = "";
|
|
1857
|
+
if (mountedTargets.delete(key)) targetsDirty = true;
|
|
1858
|
+
row.querySelector("[data-dshmore-message-actions]")?.remove();
|
|
1859
|
+
return;
|
|
1860
|
+
}
|
|
1861
|
+
if (row.dataset.dshmoreHidden !== void 0) delete row.dataset.dshmoreHidden;
|
|
1862
|
+
if (descriptor.action === null) {
|
|
1863
|
+
if (mountedTargets.delete(key)) targetsDirty = true;
|
|
1864
|
+
row.querySelector("[data-dshmore-message-actions]")?.remove();
|
|
1865
|
+
return;
|
|
882
1866
|
}
|
|
1867
|
+
const next = {
|
|
1868
|
+
key,
|
|
1869
|
+
...descriptor.action,
|
|
1870
|
+
host: hostForRow(row, descriptor.action.kind)
|
|
1871
|
+
};
|
|
1872
|
+
const current = mountedTargets.get(key);
|
|
1873
|
+
if (current === void 0 || !sameTarget(current, next)) {
|
|
1874
|
+
mountedTargets.set(key, next);
|
|
1875
|
+
targetsDirty = true;
|
|
1876
|
+
}
|
|
1877
|
+
};
|
|
1878
|
+
const processTrajectoryRow = (row) => {
|
|
1879
|
+
const seq = trajectorySourceSeq(row);
|
|
1880
|
+
const key = trajectoryRecordKey(row);
|
|
1881
|
+
if (seq !== void 0 && hiddenSeqsRef.current.has(seq) || key !== void 0 && hiddenTrajectoryKeysRef.current.has(key)) {
|
|
1882
|
+
row.dataset.dshmoreHidden = "";
|
|
1883
|
+
} else if (row.dataset.dshmoreHidden !== void 0) {
|
|
1884
|
+
delete row.dataset.dshmoreHidden;
|
|
1885
|
+
}
|
|
1886
|
+
};
|
|
1887
|
+
const publishTargets = () => {
|
|
1888
|
+
for (const [key, target] of mountedTargets) {
|
|
1889
|
+
if (!target.host.isConnected && mountedTargets.delete(key)) targetsDirty = true;
|
|
1890
|
+
}
|
|
1891
|
+
const next = surfaceRef.current.rows.flatMap((row) => {
|
|
1892
|
+
const target = mountedTargets.get(row.key);
|
|
1893
|
+
return target === void 0 ? [] : [target];
|
|
1894
|
+
});
|
|
883
1895
|
setTargets((current) => sameTargets(current, next) ? current : next);
|
|
1896
|
+
targetsDirty = false;
|
|
1897
|
+
};
|
|
1898
|
+
const flush = () => {
|
|
1899
|
+
frame = null;
|
|
1900
|
+
if (fullScanRequested) {
|
|
1901
|
+
fullScanRequested = false;
|
|
1902
|
+
mountedTargets.clear();
|
|
1903
|
+
targetsDirty = true;
|
|
1904
|
+
for (const row of document.querySelectorAll("[data-chat-flow-key]")) processChatRow(row);
|
|
1905
|
+
for (const row of document.querySelectorAll("[data-trajectory-row-key]")) processTrajectoryRow(row);
|
|
1906
|
+
} else {
|
|
1907
|
+
for (const row of pendingChatRows) if (row.isConnected) processChatRow(row);
|
|
1908
|
+
for (const row of pendingTrajectoryRows) if (row.isConnected) processTrajectoryRow(row);
|
|
1909
|
+
}
|
|
1910
|
+
pendingChatRows.clear();
|
|
1911
|
+
pendingTrajectoryRows.clear();
|
|
1912
|
+
const shouldPrune = pruneDisconnected;
|
|
1913
|
+
pruneDisconnected = false;
|
|
1914
|
+
if (targetsDirty || shouldPrune) publishTargets();
|
|
884
1915
|
};
|
|
885
1916
|
const schedule = () => {
|
|
886
|
-
if (frame === null) frame = window.requestAnimationFrame(
|
|
1917
|
+
if (frame === null) frame = window.requestAnimationFrame(flush);
|
|
887
1918
|
};
|
|
888
|
-
|
|
889
|
-
|
|
1919
|
+
const requestFullScan = () => {
|
|
1920
|
+
fullScanRequested = true;
|
|
1921
|
+
schedule();
|
|
1922
|
+
};
|
|
1923
|
+
const collectRows = (node) => {
|
|
1924
|
+
const element = node instanceof Element ? node : node.parentElement;
|
|
1925
|
+
if (element === null) return;
|
|
1926
|
+
const chatAncestor = element.closest("[data-chat-flow-key]");
|
|
1927
|
+
if (chatAncestor !== null) pendingChatRows.add(chatAncestor);
|
|
1928
|
+
const trajectoryAncestor = element.closest("[data-trajectory-row-key]");
|
|
1929
|
+
if (trajectoryAncestor !== null) pendingTrajectoryRows.add(trajectoryAncestor);
|
|
1930
|
+
if (element.matches("[data-chat-flow-key]")) pendingChatRows.add(element);
|
|
1931
|
+
if (element.matches("[data-trajectory-row-key]")) pendingTrajectoryRows.add(element);
|
|
1932
|
+
element.querySelectorAll("[data-chat-flow-key]").forEach((row) => pendingChatRows.add(row));
|
|
1933
|
+
element.querySelectorAll("[data-trajectory-row-key]").forEach((row) => pendingTrajectoryRows.add(row));
|
|
1934
|
+
};
|
|
1935
|
+
requestFullScanRef.current = requestFullScan;
|
|
1936
|
+
const observer = new MutationObserver((records) => {
|
|
1937
|
+
for (const record of records) {
|
|
1938
|
+
if (record.removedNodes.length > 0) pruneDisconnected = true;
|
|
1939
|
+
record.addedNodes.forEach(collectRows);
|
|
1940
|
+
}
|
|
1941
|
+
if (pendingChatRows.size > 0 || pendingTrajectoryRows.size > 0 || pruneDisconnected) schedule();
|
|
1942
|
+
});
|
|
890
1943
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
1944
|
+
flush();
|
|
891
1945
|
return () => {
|
|
892
1946
|
observer.disconnect();
|
|
893
1947
|
if (frame !== null) window.cancelAnimationFrame(frame);
|
|
1948
|
+
requestFullScanRef.current = null;
|
|
894
1949
|
for (const row of document.querySelectorAll("[data-dshmore-hidden]")) {
|
|
895
1950
|
delete row.dataset.dshmoreHidden;
|
|
896
|
-
row.style.removeProperty("display");
|
|
897
1951
|
}
|
|
898
1952
|
for (const host of document.querySelectorAll("[data-dshmore-message-actions]")) host.remove();
|
|
899
1953
|
};
|
|
900
|
-
}, [
|
|
1954
|
+
}, [active]);
|
|
1955
|
+
(0, import_react8.useLayoutEffect)(() => {
|
|
1956
|
+
requestFullScanRef.current?.();
|
|
1957
|
+
}, [surface, hiddenSeqs, hiddenTrajectoryKeys]);
|
|
901
1958
|
return targets;
|
|
902
1959
|
}
|
|
903
1960
|
|
|
904
1961
|
// src/client/index.tsx
|
|
905
|
-
var
|
|
1962
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
906
1963
|
var inject = ["slots", "sessions", "workspaces", "settingsScope"];
|
|
907
1964
|
function MessageActionPatchController({ patch, props, targets, enabled }) {
|
|
908
1965
|
const actions = patch.useActions(props, enabled);
|
|
909
1966
|
const style = { "--dshmore-action-order": patch.order };
|
|
910
|
-
return /* @__PURE__ */ (0,
|
|
911
|
-
enabled && targets.map((target) => (0,
|
|
912
|
-
/* @__PURE__ */ (0,
|
|
1967
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
1968
|
+
enabled && targets.map((target) => (0, import_react_dom2.createPortal)(
|
|
1969
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dshmore-action-contribution", style, children: actions.renderAction(target) }),
|
|
913
1970
|
target.host,
|
|
914
1971
|
`${patch.id}:${target.key}`
|
|
915
1972
|
)),
|
|
@@ -917,14 +1974,15 @@ function MessageActionPatchController({ patch, props, targets, enabled }) {
|
|
|
917
1974
|
] });
|
|
918
1975
|
}
|
|
919
1976
|
function MessageActionsController(props) {
|
|
920
|
-
const settings = (0,
|
|
1977
|
+
const settings = (0, import_react9.useSyncExternalStore)(props.activation.subscribe, props.activation.getSnapshot);
|
|
921
1978
|
const projected = props.useProjection(MESSAGE_VISIBILITY_PROJECTION_KEY);
|
|
922
|
-
const hiddenSeqs = (0,
|
|
923
|
-
const hiddenTrajectoryKeys = (0,
|
|
924
|
-
const
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1979
|
+
const hiddenSeqs = (0, import_react9.useMemo)(() => new Set(projected?.deletedSeqs ?? []), [projected]);
|
|
1980
|
+
const hiddenTrajectoryKeys = (0, import_react9.useMemo)(() => new Set(projected?.hiddenTrajectoryKeys ?? []), [projected]);
|
|
1981
|
+
const messageActionsEnabled = MESSAGE_ACTION_PATCHES.some((patch) => settings[patch.id]);
|
|
1982
|
+
const targets = useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys, messageActionsEnabled);
|
|
1983
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
1984
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("style", { children: styles }),
|
|
1985
|
+
MESSAGE_ACTION_PATCHES.map((patch) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
928
1986
|
MessageActionPatchController,
|
|
929
1987
|
{
|
|
930
1988
|
patch,
|
|
@@ -948,7 +2006,7 @@ function apply(ctx) {
|
|
|
948
2006
|
id: `${PLUGIN_NAME}-message-actions`,
|
|
949
2007
|
order: 90,
|
|
950
2008
|
registrant: PLUGIN_NAME
|
|
951
|
-
}, (props) => /* @__PURE__ */ (0,
|
|
2009
|
+
}, (props) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MessageActionsController, { ...props, ctx, activation })));
|
|
952
2010
|
for (const patch of CLIENT_PATCHES) patch.install(ctx, activation);
|
|
953
2011
|
}
|
|
954
2012
|
return module.exports; } });
|