dsh-more 0.0.2 → 0.0.4
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 +1 -1
- package/README.md +16 -3
- package/README.zh-CN.md +16 -3
- package/dist/client.js +1112 -152
- package/dist/index.js +262 -55
- package/dist/types/client/index.d.ts +5 -1
- package/dist/types/generated/host-settings-schema.d.ts +4 -0
- package/dist/types/generated/patch-catalog.d.ts +16 -0
- package/dist/types/kernel/client/activation.d.ts +2 -1
- package/dist/types/kernel/client/config-card.d.ts +2 -2
- package/dist/types/kernel/client/message-actions.d.ts +4 -0
- package/dist/types/kernel/client/patch.d.ts +1 -1
- package/dist/types/kernel/host/settings.d.ts +1 -1
- package/dist/types/patches/conversation-quick-navigation/client/index.d.ts +27 -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/message-delete/client/index.d.ts +1 -1
- package/dist/types/patches/message-delete/host/message-selection.d.ts +3 -3
- package/dist/types/patches/message-delete/host/projection.d.ts +26 -3
- package/dist/types/patches/message-edit/client/index.d.ts +1 -1
- package/dist/types/patches/message-edit/host/edit-continuation.d.ts +3 -3
- package/dist/types/patches/model-reasoning-efforts/client/index.d.ts +54 -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/platform/dsh/client/api.d.ts +2 -0
- package/dist/types/platform/dsh/client/message-targets.d.ts +14 -1
- package/dist/types/platform/dsh/client/runtime.d.ts +2 -1
- package/dist/types/platform/dsh/client/session-sync.d.ts +4 -2
- package/dist/types/platform/dsh/host/session-history.d.ts +3 -3
- package/dist/types/platform/dsh/host/wire.d.ts +6 -0
- package/dsh.plugin.json +1 -1
- package/package.json +64 -47
package/dist/client.js
CHANGED
|
@@ -25,21 +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
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" }
|
|
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" }
|
|
37
39
|
];
|
|
38
40
|
var DEFAULT_PATCH_SETTINGS = {
|
|
39
41
|
"message-edit": true,
|
|
40
42
|
"message-delete": true,
|
|
41
43
|
"session-delete": true,
|
|
42
|
-
"conversation-markdown-export": true
|
|
44
|
+
"conversation-markdown-export": true,
|
|
45
|
+
"model-reasoning-efforts": true,
|
|
46
|
+
"conversation-quick-navigation": true
|
|
43
47
|
};
|
|
44
48
|
function decodePatchSettings(value) {
|
|
45
49
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
@@ -72,7 +76,7 @@ var DshMoreApiError = class extends Error {
|
|
|
72
76
|
}
|
|
73
77
|
code;
|
|
74
78
|
};
|
|
75
|
-
async function
|
|
79
|
+
async function requestPatch(patchId, action, payload) {
|
|
76
80
|
let response;
|
|
77
81
|
try {
|
|
78
82
|
response = await fetch(`${PLUGIN_API_PREFIX}/${encodeURIComponent(patchId)}/${encodeURIComponent(action)}`, {
|
|
@@ -86,12 +90,28 @@ async function callPatchApi(patchId, action, payload) {
|
|
|
86
90
|
} catch (error) {
|
|
87
91
|
throw new DshMoreApiError("network", error instanceof Error ? error.message : String(error));
|
|
88
92
|
}
|
|
93
|
+
return response;
|
|
94
|
+
}
|
|
95
|
+
async function responseError(response) {
|
|
89
96
|
const body = await response.json().catch(() => null);
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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);
|
|
93
103
|
return body.value;
|
|
94
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
|
+
}
|
|
95
115
|
function apiErrorText(error) {
|
|
96
116
|
if (error instanceof DshMoreApiError || error instanceof Error) return error.message;
|
|
97
117
|
return String(error);
|
|
@@ -176,10 +196,16 @@ async function settleSessionRemoval(sessions, workspaces, sessionId) {
|
|
|
176
196
|
function adjacentVisibleSession(sessions, workspaces, deletingSessionId) {
|
|
177
197
|
if (sessions.current !== deletingSessionId) return void 0;
|
|
178
198
|
const archived = new Set(workspaces.archivedSessionIds);
|
|
179
|
-
const visible = sessions.ids.filter((id) => id !== deletingSessionId && !archived.has(id));
|
|
180
|
-
if (visible.length === 0) return void 0;
|
|
181
199
|
const deletingIndex = sessions.ids.indexOf(deletingSessionId);
|
|
182
|
-
|
|
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;
|
|
183
209
|
}
|
|
184
210
|
|
|
185
211
|
// src/patches/message-edit/shared.ts
|
|
@@ -439,44 +465,72 @@ function replaceExactText(root, before, after) {
|
|
|
439
465
|
}
|
|
440
466
|
function installSessionDeleteMenuItems(onDelete) {
|
|
441
467
|
let frame = null;
|
|
442
|
-
const deleteItems = /* @__PURE__ */ new
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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
|
+
deleteItem.remove();
|
|
510
|
+
deleteItems.delete(archiveButton);
|
|
511
|
+
} else if (!deleteItem.isConnected) {
|
|
452
512
|
archiveButton.removeAttribute(DELETE_SOURCE_ATTRIBUTE);
|
|
513
|
+
deleteItems.delete(archiveButton);
|
|
514
|
+
pendingButtons.add(archiveButton);
|
|
453
515
|
}
|
|
454
|
-
const clone = wrapper.cloneNode(true);
|
|
455
|
-
const deleteButton = clone.querySelector('button[role="menuitem"]');
|
|
456
|
-
if (deleteButton === null) continue;
|
|
457
|
-
archiveButton.setAttribute(DELETE_SOURCE_ATTRIBUTE, "");
|
|
458
|
-
clone.setAttribute(DELETE_MENU_ATTRIBUTE, "");
|
|
459
|
-
deleteItems.set(archiveButton, clone);
|
|
460
|
-
replaceExactText(deleteButton, text, deleteLabel);
|
|
461
|
-
deleteButton.setAttribute("aria-label", deleteLabel);
|
|
462
|
-
deleteButton.classList.add("dshmore-session-delete-menu-item");
|
|
463
|
-
deleteButton.addEventListener("click", (event) => {
|
|
464
|
-
event.preventDefault();
|
|
465
|
-
event.stopImmediatePropagation();
|
|
466
|
-
onDelete(archiveButton);
|
|
467
|
-
});
|
|
468
|
-
wrapper.after(clone);
|
|
469
516
|
}
|
|
517
|
+
for (const button of pendingButtons) syncButton(button);
|
|
518
|
+
pendingButtons.clear();
|
|
470
519
|
};
|
|
471
520
|
const schedule = () => {
|
|
472
521
|
if (frame !== null) return;
|
|
473
|
-
frame = window.requestAnimationFrame(
|
|
474
|
-
frame = null;
|
|
475
|
-
sync();
|
|
476
|
-
});
|
|
522
|
+
frame = window.requestAnimationFrame(flush);
|
|
477
523
|
};
|
|
478
|
-
|
|
479
|
-
|
|
524
|
+
document.querySelectorAll('button[role="menuitem"]').forEach((button) => pendingButtons.add(button));
|
|
525
|
+
flush();
|
|
526
|
+
const observer = new MutationObserver((records) => {
|
|
527
|
+
let removedKnownItem = false;
|
|
528
|
+
for (const record of records) {
|
|
529
|
+
record.addedNodes.forEach(collectButtons);
|
|
530
|
+
if (record.removedNodes.length > 0 && deleteItems.size > 0) removedKnownItem = true;
|
|
531
|
+
}
|
|
532
|
+
if (pendingButtons.size > 0 || removedKnownItem) schedule();
|
|
533
|
+
});
|
|
480
534
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
481
535
|
return () => {
|
|
482
536
|
observer.disconnect();
|
|
@@ -492,12 +546,6 @@ function installSessionDeleteMenuItems(onDelete) {
|
|
|
492
546
|
function SessionDeleteController(props) {
|
|
493
547
|
const settings = (0, import_react3.useSyncExternalStore)(props.activation.subscribe, props.activation.getSnapshot);
|
|
494
548
|
const enabled = settings[SESSION_DELETE_PATCH_ID];
|
|
495
|
-
const sessionList = props.useSessions((state) => state);
|
|
496
|
-
const sessionListRef = (0, import_react3.useRef)(sessionList);
|
|
497
|
-
sessionListRef.current = sessionList;
|
|
498
|
-
const workspaceList = props.useWorkspaces((state) => state);
|
|
499
|
-
const workspaceListRef = (0, import_react3.useRef)(workspaceList);
|
|
500
|
-
workspaceListRef.current = workspaceList;
|
|
501
549
|
const [pending, setPending] = (0, import_react3.useState)(null);
|
|
502
550
|
const pendingRef = (0, import_react3.useRef)(null);
|
|
503
551
|
const deleteIntentRef = (0, import_react3.useRef)(false);
|
|
@@ -506,13 +554,18 @@ function SessionDeleteController(props) {
|
|
|
506
554
|
(0, import_react3.useEffect)(() => {
|
|
507
555
|
if (!enabled) return;
|
|
508
556
|
const workspaces = props.ctx.workspaces;
|
|
557
|
+
const sessions = props.ctx.sessions;
|
|
509
558
|
const mutable = workspaces;
|
|
510
559
|
const original = mutable.archiveSession;
|
|
511
560
|
mutable.archiveSession = (sessionId) => {
|
|
512
561
|
if (!deleteIntentRef.current) return original.call(mutable, sessionId);
|
|
513
562
|
deleteIntentRef.current = false;
|
|
514
563
|
return new Promise((resolve) => {
|
|
515
|
-
const request = {
|
|
564
|
+
const request = {
|
|
565
|
+
sessionId,
|
|
566
|
+
title: sessions.list.getSnapshot().byId[sessionId]?.displayTitle ?? String(sessionId),
|
|
567
|
+
resolve
|
|
568
|
+
};
|
|
516
569
|
pendingRef.current?.resolve();
|
|
517
570
|
pendingRef.current = request;
|
|
518
571
|
setError(null);
|
|
@@ -548,8 +601,8 @@ function SessionDeleteController(props) {
|
|
|
548
601
|
const sessions = props.ctx.sessions;
|
|
549
602
|
const workspaces = props.ctx.workspaces;
|
|
550
603
|
const fallbackSessionId = adjacentVisibleSession(
|
|
551
|
-
|
|
552
|
-
|
|
604
|
+
sessions.list.getSnapshot(),
|
|
605
|
+
workspaces.list.getSnapshot(),
|
|
553
606
|
pending.sessionId
|
|
554
607
|
);
|
|
555
608
|
if (fallbackSessionId !== void 0) openSessionWithoutGap(sessions, fallbackSessionId);
|
|
@@ -568,7 +621,7 @@ function SessionDeleteController(props) {
|
|
|
568
621
|
setBusy(false);
|
|
569
622
|
}
|
|
570
623
|
};
|
|
571
|
-
const title = pending
|
|
624
|
+
const title = pending?.title ?? "";
|
|
572
625
|
if (!enabled) return null;
|
|
573
626
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
574
627
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: styles + sessionDeleteStyles }),
|
|
@@ -602,8 +655,7 @@ var clientPatch = {
|
|
|
602
655
|
ctx.slots.inject("shell.overlay", () => ctx.slots.register({
|
|
603
656
|
name: "shell.overlay",
|
|
604
657
|
id: `${PLUGIN_NAME}-${SESSION_DELETE_PATCH_ID}`,
|
|
605
|
-
order: 90
|
|
606
|
-
registrant: PLUGIN_NAME
|
|
658
|
+
order: 90
|
|
607
659
|
}, (props) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SessionDeleteController, { ...props, ctx, activation })));
|
|
608
660
|
}
|
|
609
661
|
};
|
|
@@ -665,13 +717,13 @@ function ConversationMarkdownExportButton(props) {
|
|
|
665
717
|
setBusy(true);
|
|
666
718
|
setError(null);
|
|
667
719
|
try {
|
|
668
|
-
const
|
|
720
|
+
const markdown = await callPatchBlobApi(
|
|
669
721
|
CONVERSATION_MARKDOWN_EXPORT_PATCH_ID,
|
|
670
722
|
"render",
|
|
671
723
|
{ sessionId: props.sessionId, title }
|
|
672
724
|
);
|
|
673
725
|
if (generation.current !== requestGeneration || activeSession.current !== requestSessionId) return;
|
|
674
|
-
saveMarkdown(markdownFilename(title, String(props.sessionId)),
|
|
726
|
+
saveMarkdown(markdownFilename(title, String(props.sessionId)), markdown);
|
|
675
727
|
} catch (caught) {
|
|
676
728
|
if (generation.current === requestGeneration && activeSession.current === requestSessionId) setError(apiErrorText(caught));
|
|
677
729
|
} finally {
|
|
@@ -717,15 +769,813 @@ var clientPatch2 = {
|
|
|
717
769
|
ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
|
|
718
770
|
name: "conversation.session.header.utilities",
|
|
719
771
|
id: `${PLUGIN_NAME}-${CONVERSATION_MARKDOWN_EXPORT_PATCH_ID}`,
|
|
720
|
-
order: 100
|
|
721
|
-
registrant: PLUGIN_NAME
|
|
772
|
+
order: 100
|
|
722
773
|
}, (props) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ConversationMarkdownExportButton, { ...props, activation })));
|
|
723
774
|
}
|
|
724
775
|
};
|
|
725
776
|
|
|
777
|
+
// src/patches/model-reasoning-efforts/client/index.tsx
|
|
778
|
+
var import_react5 = require("react");
|
|
779
|
+
var import_dsh_client_ui_primitives5 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
780
|
+
|
|
781
|
+
// src/patches/model-reasoning-efforts/shared.ts
|
|
782
|
+
var MODEL_REASONING_EFFORTS_PATCH_ID = "model-reasoning-efforts";
|
|
783
|
+
var LLM_PI_AI_SETTINGS_NAMESPACE = "llm-pi-ai";
|
|
784
|
+
var THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
785
|
+
|
|
786
|
+
// src/patches/model-reasoning-efforts/client/index.tsx
|
|
787
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
788
|
+
var EFFORT_BUTTON_ATTR = "data-dshmore-effort";
|
|
789
|
+
var LEVEL_LABELS = {
|
|
790
|
+
off: "\u4E0D\u601D\u8003",
|
|
791
|
+
minimal: "\u6781\u7B80",
|
|
792
|
+
low: "\u4F4E",
|
|
793
|
+
medium: "\u4E2D",
|
|
794
|
+
high: "\u9AD8",
|
|
795
|
+
xhigh: "\u6781\u9AD8",
|
|
796
|
+
max: "\u6700\u9AD8"
|
|
797
|
+
};
|
|
798
|
+
function emptyDraft() {
|
|
799
|
+
return { nonReasoning: false, levels: {} };
|
|
800
|
+
}
|
|
801
|
+
function draftFromServer(efforts) {
|
|
802
|
+
if (efforts === false) return { nonReasoning: true, levels: {} };
|
|
803
|
+
if (efforts !== null && typeof efforts === "object") {
|
|
804
|
+
const levels = {};
|
|
805
|
+
for (const level of THINKING_LEVELS) {
|
|
806
|
+
const wire = efforts[level];
|
|
807
|
+
if (wire !== void 0) levels[level] = { enabled: true, wire: wire ?? "" };
|
|
808
|
+
}
|
|
809
|
+
return { nonReasoning: false, levels };
|
|
810
|
+
}
|
|
811
|
+
return emptyDraft();
|
|
812
|
+
}
|
|
813
|
+
function buildDict(draft) {
|
|
814
|
+
const dict = {};
|
|
815
|
+
for (const level of THINKING_LEVELS) {
|
|
816
|
+
const entry = draft.levels[level];
|
|
817
|
+
if (entry === void 0 || !entry.enabled) continue;
|
|
818
|
+
dict[level] = level === "off" ? null : entry.wire;
|
|
819
|
+
}
|
|
820
|
+
return dict;
|
|
821
|
+
}
|
|
822
|
+
function summaryLabel(efforts) {
|
|
823
|
+
if (efforts === false) return "\u4E0D\u652F\u6301\u601D\u8003";
|
|
824
|
+
if (efforts === null || typeof efforts !== "object") return "\u672A\u914D\u7F6E";
|
|
825
|
+
const parts = THINKING_LEVELS.filter((level) => level !== "off" && efforts[level] !== void 0).map((level) => LEVEL_LABELS[level]);
|
|
826
|
+
return parts.length === 0 ? "\u672A\u914D\u7F6E" : parts.join("\u3001");
|
|
827
|
+
}
|
|
828
|
+
function findModelEntries() {
|
|
829
|
+
const entries = [];
|
|
830
|
+
const catalogs = document.querySelectorAll('[class*="modelCatalog"]');
|
|
831
|
+
for (const catalog of catalogs) {
|
|
832
|
+
for (const entry of catalog.querySelectorAll('[class*="modelEntry"]')) entries.push(entry);
|
|
833
|
+
}
|
|
834
|
+
return entries;
|
|
835
|
+
}
|
|
836
|
+
function providerRouteOf(entry) {
|
|
837
|
+
const editor = entry.closest('[class*="editor"]');
|
|
838
|
+
if (editor === null) return null;
|
|
839
|
+
const routeSpan = editor.querySelector('[class*="editorRoute"]');
|
|
840
|
+
const route = routeSpan?.textContent?.trim();
|
|
841
|
+
if (route !== void 0 && route !== "") return route;
|
|
842
|
+
const rowCard = editor.closest('[class*="rowCard"]');
|
|
843
|
+
const name = rowCard?.querySelector('[class*="rowName"]')?.textContent?.trim();
|
|
844
|
+
return name !== void 0 && name !== "" ? name : null;
|
|
845
|
+
}
|
|
846
|
+
function modelIdOf(entry) {
|
|
847
|
+
const input = entry.querySelector('[class*="modelRow"] input[type="text"]');
|
|
848
|
+
const id = input?.value.trim();
|
|
849
|
+
return id !== void 0 && id !== "" ? id : null;
|
|
850
|
+
}
|
|
851
|
+
function EffortEditorBody({ draft, setDraft, writable, busy }) {
|
|
852
|
+
const toggleLevel = (level, enabled) => {
|
|
853
|
+
setDraft((current) => {
|
|
854
|
+
const existing = current.levels[level];
|
|
855
|
+
const levels = { ...current.levels };
|
|
856
|
+
levels[level] = existing === void 0 ? { enabled, wire: level === "off" ? "" : level } : { ...existing, enabled };
|
|
857
|
+
return { ...current, levels };
|
|
858
|
+
});
|
|
859
|
+
};
|
|
860
|
+
const setWire = (level, wire) => {
|
|
861
|
+
setDraft((current) => {
|
|
862
|
+
const existing = current.levels[level];
|
|
863
|
+
if (existing === void 0) return current;
|
|
864
|
+
return { ...current, levels: { ...current.levels, [level]: { ...existing, wire } } };
|
|
865
|
+
});
|
|
866
|
+
};
|
|
867
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dshmore-effort-editor", children: [
|
|
868
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "dshmore-effort-switch", children: [
|
|
869
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
870
|
+
"input",
|
|
871
|
+
{
|
|
872
|
+
type: "checkbox",
|
|
873
|
+
role: "switch",
|
|
874
|
+
checked: draft.nonReasoning,
|
|
875
|
+
disabled: !writable || busy,
|
|
876
|
+
onChange: (event) => {
|
|
877
|
+
setDraft((current) => ({ ...current, nonReasoning: event.currentTarget.checked }));
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
),
|
|
881
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "\u8BE5\u6A21\u578B\u4E0D\u652F\u6301\u601D\u8003\uFF08reasoningEfforts: false\uFF09" })
|
|
882
|
+
] }),
|
|
883
|
+
!draft.nonReasoning && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dshmore-effort-levels", children: THINKING_LEVELS.map((level) => {
|
|
884
|
+
const entry = draft.levels[level];
|
|
885
|
+
const enabled = entry?.enabled === true;
|
|
886
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "dshmore-effort-level", children: [
|
|
887
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
888
|
+
"input",
|
|
889
|
+
{
|
|
890
|
+
type: "checkbox",
|
|
891
|
+
checked: enabled,
|
|
892
|
+
disabled: !writable || busy,
|
|
893
|
+
onChange: (event) => {
|
|
894
|
+
toggleLevel(level, event.currentTarget.checked);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
),
|
|
898
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dshmore-effort-level-label", children: LEVEL_LABELS[level] }),
|
|
899
|
+
enabled && level !== "off" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
900
|
+
"input",
|
|
901
|
+
{
|
|
902
|
+
className: "dshmore-effort-wire",
|
|
903
|
+
type: "text",
|
|
904
|
+
value: entry?.wire ?? "",
|
|
905
|
+
disabled: !writable || busy,
|
|
906
|
+
placeholder: "\u7EBF\u4E0A\u503C",
|
|
907
|
+
"aria-label": `${LEVEL_LABELS[level]} \u7684\u7EBF\u4E0A\u503C`,
|
|
908
|
+
onChange: (event) => {
|
|
909
|
+
setWire(level, event.currentTarget.value);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
)
|
|
913
|
+
] }, level);
|
|
914
|
+
}) })
|
|
915
|
+
] });
|
|
916
|
+
}
|
|
917
|
+
function EffortEditorModal({ provider, modelId, initial, writable, onClose }) {
|
|
918
|
+
const [draft, setDraft] = (0, import_react5.useState)(() => draftFromServer(initial ?? null));
|
|
919
|
+
const [busy, setBusy] = (0, import_react5.useState)(false);
|
|
920
|
+
const [error, setError] = (0, import_react5.useState)(null);
|
|
921
|
+
(0, import_react5.useEffect)(() => {
|
|
922
|
+
setDraft(draftFromServer(initial ?? null));
|
|
923
|
+
}, [initial]);
|
|
924
|
+
const known = initial !== void 0;
|
|
925
|
+
const save = async () => {
|
|
926
|
+
if (!known) return;
|
|
927
|
+
const payload = draft.nonReasoning ? false : buildDict(draft);
|
|
928
|
+
if (payload !== false && !THINKING_LEVELS.some((level) => level !== "off" && payload[level] !== void 0)) {
|
|
929
|
+
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");
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
setBusy(true);
|
|
933
|
+
setError(null);
|
|
934
|
+
try {
|
|
935
|
+
await callPatchApi(MODEL_REASONING_EFFORTS_PATCH_ID, "save", {
|
|
936
|
+
provider,
|
|
937
|
+
modelId,
|
|
938
|
+
reasoningEfforts: payload
|
|
939
|
+
});
|
|
940
|
+
onClose();
|
|
941
|
+
} catch (caught) {
|
|
942
|
+
setError(apiErrorText(caught));
|
|
943
|
+
} finally {
|
|
944
|
+
setBusy(false);
|
|
945
|
+
}
|
|
946
|
+
};
|
|
947
|
+
const clear = async () => {
|
|
948
|
+
if (!known) return;
|
|
949
|
+
setBusy(true);
|
|
950
|
+
setError(null);
|
|
951
|
+
try {
|
|
952
|
+
await callPatchApi(MODEL_REASONING_EFFORTS_PATCH_ID, "save", {
|
|
953
|
+
provider,
|
|
954
|
+
modelId,
|
|
955
|
+
reasoningEfforts: null
|
|
956
|
+
});
|
|
957
|
+
onClose();
|
|
958
|
+
} catch (caught) {
|
|
959
|
+
setError(apiErrorText(caught));
|
|
960
|
+
} finally {
|
|
961
|
+
setBusy(false);
|
|
962
|
+
}
|
|
963
|
+
};
|
|
964
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
965
|
+
import_dsh_client_ui_primitives5.Modal,
|
|
966
|
+
{
|
|
967
|
+
open: true,
|
|
968
|
+
onClose,
|
|
969
|
+
title: `\u601D\u8003\u5F3A\u5EA6 \xB7 ${modelId}`,
|
|
970
|
+
closeLabel: "\u5173\u95ED",
|
|
971
|
+
description: `\u63D0\u4F9B\u65B9 ${provider}`,
|
|
972
|
+
footer: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
973
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_dsh_client_ui_primitives5.Button, { variant: "ghost", disabled: busy, onClick: onClose, children: "\u5173\u95ED" }),
|
|
974
|
+
known && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_dsh_client_ui_primitives5.Button, { variant: "ghost", disabled: !writable || busy, onClick: () => {
|
|
975
|
+
void clear();
|
|
976
|
+
}, children: "\u6E05\u9664\u914D\u7F6E" }),
|
|
977
|
+
known && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_dsh_client_ui_primitives5.Button, { variant: "primary", disabled: !writable || busy, onClick: () => {
|
|
978
|
+
void save();
|
|
979
|
+
}, children: busy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58" })
|
|
980
|
+
] }),
|
|
981
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dshmore-effort-dialog-body", children: [
|
|
982
|
+
!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" }),
|
|
983
|
+
known && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EffortEditorBody, { draft, setDraft, writable, busy }),
|
|
984
|
+
!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" }),
|
|
985
|
+
error !== null && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dshmore-effort-error", children: error })
|
|
986
|
+
] })
|
|
987
|
+
}
|
|
988
|
+
);
|
|
989
|
+
}
|
|
990
|
+
function ModelsPageEffortPortal(props) {
|
|
991
|
+
const settings = (0, import_react5.useSyncExternalStore)(props.activation.subscribe, props.activation.getSnapshot, props.activation.getSnapshot);
|
|
992
|
+
const enabled = settings[MODEL_REASONING_EFFORTS_PATCH_ID];
|
|
993
|
+
const scopeSnapshot = (0, import_react5.useSyncExternalStore)(props.scope.subscribe, props.scope.getSnapshot, props.scope.getSnapshot);
|
|
994
|
+
const [editing, setEditing] = (0, import_react5.useState)(null);
|
|
995
|
+
const [data, setData] = (0, import_react5.useState)(null);
|
|
996
|
+
const dataRef = (0, import_react5.useRef)(data);
|
|
997
|
+
dataRef.current = data;
|
|
998
|
+
const generation = (0, import_react5.useRef)(0);
|
|
999
|
+
const fillingRef = (0, import_react5.useRef)(false);
|
|
1000
|
+
const reload = (0, import_react5.useCallback)(async () => {
|
|
1001
|
+
const current = ++generation.current;
|
|
1002
|
+
try {
|
|
1003
|
+
const snapshot = await callPatchApi(MODEL_REASONING_EFFORTS_PATCH_ID, "read", {});
|
|
1004
|
+
if (generation.current !== current) return;
|
|
1005
|
+
setData(snapshot);
|
|
1006
|
+
if (enabled && snapshot.writable && snapshot.rows.some((row) => row.reasoningEfforts === null) && !fillingRef.current) {
|
|
1007
|
+
fillingRef.current = true;
|
|
1008
|
+
void callPatchApi(MODEL_REASONING_EFFORTS_PATCH_ID, "fillDefaults", {}).catch(() => void 0).finally(() => {
|
|
1009
|
+
fillingRef.current = false;
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
} catch {
|
|
1013
|
+
}
|
|
1014
|
+
}, [enabled]);
|
|
1015
|
+
(0, import_react5.useEffect)(() => {
|
|
1016
|
+
if (enabled) void reload();
|
|
1017
|
+
}, [enabled, reload, scopeSnapshot]);
|
|
1018
|
+
const syncButtons = (0, import_react5.useCallback)(() => {
|
|
1019
|
+
if (!enabled) return;
|
|
1020
|
+
const active = /* @__PURE__ */ new Set();
|
|
1021
|
+
for (const entry of findModelEntries()) {
|
|
1022
|
+
const provider = providerRouteOf(entry);
|
|
1023
|
+
const modelId = modelIdOf(entry);
|
|
1024
|
+
if (provider === null || modelId === null) continue;
|
|
1025
|
+
active.add(entry);
|
|
1026
|
+
let button = entry.querySelector(`[${EFFORT_BUTTON_ATTR}]`);
|
|
1027
|
+
if (button === null) {
|
|
1028
|
+
button = document.createElement("button");
|
|
1029
|
+
button.type = "button";
|
|
1030
|
+
button.className = "dshmore-effort-row-button";
|
|
1031
|
+
button.setAttribute(EFFORT_BUTTON_ATTR, "");
|
|
1032
|
+
button.addEventListener("click", () => {
|
|
1033
|
+
const route = button?.dataset.provider;
|
|
1034
|
+
const model = button?.dataset.model;
|
|
1035
|
+
if (route !== void 0 && model !== void 0) setEditing({ provider: route, modelId: model });
|
|
1036
|
+
});
|
|
1037
|
+
const row = entry.querySelector('[class*="modelRow"]');
|
|
1038
|
+
if (row === null) continue;
|
|
1039
|
+
entry.insertBefore(button, row.nextSibling);
|
|
1040
|
+
}
|
|
1041
|
+
if (button.dataset.provider !== provider) button.dataset.provider = provider;
|
|
1042
|
+
if (button.dataset.model !== modelId) button.dataset.model = modelId;
|
|
1043
|
+
const rowData = dataRef.current?.rows.find((row) => row.provider === provider && row.modelId === modelId);
|
|
1044
|
+
const text = rowData === void 0 ? "\u601D\u8003\u5F3A\u5EA6" : `\u601D\u8003\u5F3A\u5EA6 \xB7 ${summaryLabel(rowData.reasoningEfforts)}`;
|
|
1045
|
+
if (button.textContent !== text) button.textContent = text;
|
|
1046
|
+
}
|
|
1047
|
+
document.querySelectorAll(`[${EFFORT_BUTTON_ATTR}]`).forEach((button) => {
|
|
1048
|
+
const host = button.parentElement;
|
|
1049
|
+
if (host === null || !active.has(host)) button.remove();
|
|
1050
|
+
});
|
|
1051
|
+
}, [enabled]);
|
|
1052
|
+
(0, import_react5.useEffect)(() => {
|
|
1053
|
+
if (!enabled) {
|
|
1054
|
+
document.querySelectorAll(`[${EFFORT_BUTTON_ATTR}]`).forEach((element) => element.remove());
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
let frame = null;
|
|
1058
|
+
const schedule = () => {
|
|
1059
|
+
if (frame !== null) return;
|
|
1060
|
+
frame = window.requestAnimationFrame(() => {
|
|
1061
|
+
frame = null;
|
|
1062
|
+
syncButtons();
|
|
1063
|
+
});
|
|
1064
|
+
};
|
|
1065
|
+
syncButtons();
|
|
1066
|
+
const observer = new MutationObserver(schedule);
|
|
1067
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
1068
|
+
document.addEventListener("input", schedule);
|
|
1069
|
+
document.addEventListener("change", schedule);
|
|
1070
|
+
return () => {
|
|
1071
|
+
observer.disconnect();
|
|
1072
|
+
document.removeEventListener("input", schedule);
|
|
1073
|
+
document.removeEventListener("change", schedule);
|
|
1074
|
+
if (frame !== null) window.cancelAnimationFrame(frame);
|
|
1075
|
+
document.querySelectorAll(`[${EFFORT_BUTTON_ATTR}]`).forEach((element) => element.remove());
|
|
1076
|
+
};
|
|
1077
|
+
}, [enabled, syncButtons]);
|
|
1078
|
+
(0, import_react5.useEffect)(() => {
|
|
1079
|
+
if (enabled) syncButtons();
|
|
1080
|
+
}, [enabled, syncButtons, data]);
|
|
1081
|
+
const editingRow = editing === null ? void 0 : data?.rows.find((row) => row.provider === editing.provider && row.modelId === editing.modelId);
|
|
1082
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
1083
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: effortStyles }),
|
|
1084
|
+
editing !== null && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1085
|
+
EffortEditorModal,
|
|
1086
|
+
{
|
|
1087
|
+
provider: editing.provider,
|
|
1088
|
+
modelId: editing.modelId,
|
|
1089
|
+
initial: editingRow === void 0 ? void 0 : editingRow.reasoningEfforts,
|
|
1090
|
+
writable: data?.writable ?? true,
|
|
1091
|
+
onClose: () => {
|
|
1092
|
+
setEditing(null);
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
)
|
|
1096
|
+
] });
|
|
1097
|
+
}
|
|
1098
|
+
var effortStyles = `
|
|
1099
|
+
.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; }
|
|
1100
|
+
.dshmore-effort-row-button:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }
|
|
1101
|
+
.dshmore-effort-dialog-body { box-sizing: border-box; display: flex; flex-direction: column; gap: 12px; width: 100%; min-width: 0; overflow: hidden; }
|
|
1102
|
+
.dshmore-effort-editor { display: flex; flex-direction: column; gap: 12px; }
|
|
1103
|
+
.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; }
|
|
1104
|
+
.dshmore-effort-switch input, .dshmore-effort-level input[type="checkbox"] { accent-color: var(--dsw-alias-state-business-primary); cursor: pointer; }
|
|
1105
|
+
.dshmore-effort-levels { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px 12px; }
|
|
1106
|
+
.dshmore-effort-level-label { flex: none; }
|
|
1107
|
+
.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; }
|
|
1108
|
+
.dshmore-effort-wire:focus { border-color: var(--dsw-alias-brand-primary); outline: none; }
|
|
1109
|
+
.dshmore-effort-wire::placeholder { color: var(--dsw-alias-label-dimmed); }
|
|
1110
|
+
.dshmore-effort-note { color: var(--dsw-alias-label-tertiary); margin: 0; font-size: 12px; line-height: 18px; }
|
|
1111
|
+
.dshmore-effort-warn { color: var(--dsw-alias-state-warn-label); margin: 0; font-size: 12px; line-height: 18px; }
|
|
1112
|
+
.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; }
|
|
1113
|
+
`;
|
|
1114
|
+
var clientPatch3 = {
|
|
1115
|
+
id: MODEL_REASONING_EFFORTS_PATCH_ID,
|
|
1116
|
+
install: (ctx, activation) => {
|
|
1117
|
+
const scope = ctx.settingsScope.bind({ namespace: LLM_PI_AI_SETTINGS_NAMESPACE });
|
|
1118
|
+
const boundScope = {
|
|
1119
|
+
getSnapshot: scope.getSnapshot.bind(scope),
|
|
1120
|
+
subscribe: scope.subscribe.bind(scope),
|
|
1121
|
+
set: scope.set.bind(scope),
|
|
1122
|
+
unset: scope.unset.bind(scope),
|
|
1123
|
+
mutate: scope.mutate.bind(scope)
|
|
1124
|
+
};
|
|
1125
|
+
ctx.slots.inject("shell.overlay", () => ctx.slots.register({
|
|
1126
|
+
name: "shell.overlay",
|
|
1127
|
+
id: `${PLUGIN_NAME}-${MODEL_REASONING_EFFORTS_PATCH_ID}`,
|
|
1128
|
+
order: 90
|
|
1129
|
+
}, (props) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ModelsPageEffortPortal, { ...props, scope: boundScope, activation })));
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
|
|
1133
|
+
// src/patches/conversation-quick-navigation/client/index.tsx
|
|
1134
|
+
var import_react6 = require("react");
|
|
1135
|
+
var import_react_dom = require("react-dom");
|
|
1136
|
+
var import_dsh_client_ui_primitives6 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
1137
|
+
|
|
1138
|
+
// src/kernel/message-visibility.ts
|
|
1139
|
+
var MESSAGE_VISIBILITY_PROJECTION_KEY = "dshMoreMessageDelete";
|
|
1140
|
+
|
|
1141
|
+
// src/patches/conversation-quick-navigation/shared.ts
|
|
1142
|
+
var CONVERSATION_QUICK_NAVIGATION_PATCH_ID = "conversation-quick-navigation";
|
|
1143
|
+
|
|
1144
|
+
// src/platform/dsh/client/message-content.ts
|
|
1145
|
+
function contentText(content) {
|
|
1146
|
+
return content.filter((block) => block.type === "text").map((block) => block.text).join(" ").replace(/\s+/g, " ").trim();
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
// src/patches/conversation-quick-navigation/client/navigation.ts
|
|
1150
|
+
var MAX_LABEL_CODE_POINTS = 72;
|
|
1151
|
+
function conciseLabel(content, fallbackIndex) {
|
|
1152
|
+
const text = contentText(content);
|
|
1153
|
+
if (text === "") return `\u7B2C ${String(fallbackIndex)} \u8F6E \xB7 \u975E\u6587\u672C\u6D88\u606F`;
|
|
1154
|
+
const codePoints = [...text];
|
|
1155
|
+
if (codePoints.length <= MAX_LABEL_CODE_POINTS) return text;
|
|
1156
|
+
return `${codePoints.slice(0, MAX_LABEL_CODE_POINTS - 1).join("")}\u2026`;
|
|
1157
|
+
}
|
|
1158
|
+
function turnSummary(node, key, index) {
|
|
1159
|
+
const candidate = node;
|
|
1160
|
+
if (candidate?.kind !== "user" && candidate?.kind !== "steering") return null;
|
|
1161
|
+
if (!Number.isSafeInteger(candidate.data?.seq) || !Array.isArray(candidate.data?.content)) return null;
|
|
1162
|
+
return {
|
|
1163
|
+
key,
|
|
1164
|
+
seq: candidate.data.seq,
|
|
1165
|
+
label: conciseLabel(candidate.data.content, index)
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
function sameTurns(left, right) {
|
|
1169
|
+
return left.length === right.length && left.every((turn, index) => {
|
|
1170
|
+
const other = right[index];
|
|
1171
|
+
return other !== void 0 && turn.key === other.key && turn.seq === other.seq && turn.label === other.label;
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
function createConversationTurnSelector() {
|
|
1175
|
+
let previous = [];
|
|
1176
|
+
return (snapshot) => {
|
|
1177
|
+
const next = [];
|
|
1178
|
+
for (const key of snapshot.order) {
|
|
1179
|
+
const summary = turnSummary(snapshot.nodes.get(key), key, next.length + 1);
|
|
1180
|
+
if (summary !== null) next.push(summary);
|
|
1181
|
+
}
|
|
1182
|
+
if (!sameTurns(previous, next)) previous = next;
|
|
1183
|
+
return previous;
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
function historyWindowSignature(snapshot) {
|
|
1187
|
+
const first = snapshot.order[0] ?? "";
|
|
1188
|
+
const last = snapshot.order.at(-1) ?? "";
|
|
1189
|
+
return `${first}\0${last}\0${String(snapshot.order.length)}`;
|
|
1190
|
+
}
|
|
1191
|
+
function shouldAutoLoadOlder(state) {
|
|
1192
|
+
return state.enabled && state.surfaceAvailable && state.hasMore && !state.loadingOlder && !state.paused;
|
|
1193
|
+
}
|
|
1194
|
+
function activeTurnAt(positions, readingLine) {
|
|
1195
|
+
let active = positions[0]?.key ?? null;
|
|
1196
|
+
for (const position of positions) {
|
|
1197
|
+
if (position.top > readingLine) break;
|
|
1198
|
+
active = position.key;
|
|
1199
|
+
}
|
|
1200
|
+
return active;
|
|
1201
|
+
}
|
|
1202
|
+
function scrollOffsetForRow(currentScrollTop, scrollportTop, rowTop, topInset = 20) {
|
|
1203
|
+
return Math.max(0, currentScrollTop + rowTop - scrollportTop - topInset);
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
// src/patches/conversation-quick-navigation/client/index.tsx
|
|
1207
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1208
|
+
var MAX_NO_PROGRESS_ATTEMPTS = 3;
|
|
1209
|
+
var MENU_LIST_ID = "dshmore-conversation-nav-list";
|
|
1210
|
+
var navigationCollapsedPreference = false;
|
|
1211
|
+
function conversationRow(root, key) {
|
|
1212
|
+
for (const row of root.querySelectorAll("[data-chat-flow-key]")) {
|
|
1213
|
+
if (row.dataset.chatFlowKey === key) return row;
|
|
1214
|
+
}
|
|
1215
|
+
return null;
|
|
1216
|
+
}
|
|
1217
|
+
function sessionRoot(marker) {
|
|
1218
|
+
return marker?.closest("[data-phase]") ?? null;
|
|
1219
|
+
}
|
|
1220
|
+
function conversationScrollport(marker) {
|
|
1221
|
+
return sessionRoot(marker)?.querySelector("[data-conversation-scroll]") ?? null;
|
|
1222
|
+
}
|
|
1223
|
+
function nativeLoadOlderButton(scrollport) {
|
|
1224
|
+
const flow = scrollport.querySelector("[data-chat-flow]");
|
|
1225
|
+
if (flow === null) return null;
|
|
1226
|
+
for (const child of flow.children) {
|
|
1227
|
+
if (child instanceof HTMLElement && child.dataset.chatFlowKey !== void 0) break;
|
|
1228
|
+
const button = child.querySelector("button");
|
|
1229
|
+
if (button !== null) return button;
|
|
1230
|
+
}
|
|
1231
|
+
return null;
|
|
1232
|
+
}
|
|
1233
|
+
function ConversationNavigationMenu({
|
|
1234
|
+
turns,
|
|
1235
|
+
activeKey,
|
|
1236
|
+
hasMore,
|
|
1237
|
+
loadingOlder,
|
|
1238
|
+
autoLoadPaused,
|
|
1239
|
+
collapsed,
|
|
1240
|
+
onRetry,
|
|
1241
|
+
onToggleCollapsed,
|
|
1242
|
+
onJump,
|
|
1243
|
+
listRef
|
|
1244
|
+
}) {
|
|
1245
|
+
const loading = hasMore && !autoLoadPaused;
|
|
1246
|
+
const compactStatus = autoLoadPaused ? `${String(turns.length)} \u8F6E \xB7 \u5DF2\u6682\u505C` : loading ? `${String(turns.length)} \u8F6E \xB7 \u52A0\u8F7D\u4E2D` : `${String(turns.length)} \u8F6E`;
|
|
1247
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1248
|
+
"nav",
|
|
1249
|
+
{
|
|
1250
|
+
className: "dshmore-conversation-nav-menu",
|
|
1251
|
+
"data-collapsed": collapsed ? "true" : "false",
|
|
1252
|
+
"aria-label": "\u5F53\u524D\u4F1A\u8BDD\u7684\u5BF9\u8BDD\u76EE\u5F55",
|
|
1253
|
+
children: [
|
|
1254
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("header", { className: "dshmore-conversation-nav-header", children: [
|
|
1255
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dshmore-conversation-nav-heading", children: [
|
|
1256
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dshmore-conversation-nav-title-row", children: [
|
|
1257
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dshmore-conversation-nav-title", children: "\u5BF9\u8BDD\u76EE\u5F55" }),
|
|
1258
|
+
collapsed && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dshmore-conversation-nav-compact-status", role: "status", "aria-live": "polite", children: [
|
|
1259
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dshmore-conversation-nav-loading-dot", "aria-hidden": "true" }),
|
|
1260
|
+
compactStatus
|
|
1261
|
+
] })
|
|
1262
|
+
] }),
|
|
1263
|
+
!collapsed && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dshmore-conversation-nav-count", role: "status", "aria-live": "polite", children: [
|
|
1264
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dshmore-conversation-nav-loading-dot", "aria-hidden": "true" }),
|
|
1265
|
+
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`
|
|
1266
|
+
] })
|
|
1267
|
+
] }),
|
|
1268
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dshmore-conversation-nav-actions", children: [
|
|
1269
|
+
!collapsed && autoLoadPaused && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "dshmore-conversation-nav-retry", onClick: onRetry, children: "\u7EE7\u7EED\u52A0\u8F7D" }),
|
|
1270
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1271
|
+
"button",
|
|
1272
|
+
{
|
|
1273
|
+
type: "button",
|
|
1274
|
+
className: "dshmore-conversation-nav-toggle",
|
|
1275
|
+
"aria-label": collapsed ? "\u5C55\u5F00\u5BF9\u8BDD\u76EE\u5F55" : "\u6298\u53E0\u5BF9\u8BDD\u76EE\u5F55",
|
|
1276
|
+
"aria-controls": MENU_LIST_ID,
|
|
1277
|
+
"aria-expanded": !collapsed,
|
|
1278
|
+
onClick: onToggleCollapsed,
|
|
1279
|
+
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 })
|
|
1280
|
+
}
|
|
1281
|
+
)
|
|
1282
|
+
] })
|
|
1283
|
+
] }),
|
|
1284
|
+
!collapsed && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { id: MENU_LIST_ID, ref: listRef, className: "dshmore-conversation-nav-list", children: [
|
|
1285
|
+
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" }),
|
|
1286
|
+
turns.map((turn, index) => {
|
|
1287
|
+
const active = turn.key === activeKey;
|
|
1288
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1289
|
+
"button",
|
|
1290
|
+
{
|
|
1291
|
+
type: "button",
|
|
1292
|
+
className: "dshmore-conversation-nav-item",
|
|
1293
|
+
"aria-current": active ? "location" : void 0,
|
|
1294
|
+
onClick: () => {
|
|
1295
|
+
onJump(turn.key);
|
|
1296
|
+
},
|
|
1297
|
+
children: [
|
|
1298
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dshmore-conversation-nav-index", children: String(index + 1).padStart(2, "0") }),
|
|
1299
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dshmore-conversation-nav-label", children: turn.label })
|
|
1300
|
+
]
|
|
1301
|
+
},
|
|
1302
|
+
turn.key
|
|
1303
|
+
);
|
|
1304
|
+
})
|
|
1305
|
+
] })
|
|
1306
|
+
]
|
|
1307
|
+
}
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
function ConversationQuickNavigation(props) {
|
|
1311
|
+
const settings = (0, import_react6.useSyncExternalStore)(props.activation.subscribe, props.activation.getSnapshot, props.activation.getSnapshot);
|
|
1312
|
+
const enabled = settings[CONVERSATION_QUICK_NAVIGATION_PATCH_ID];
|
|
1313
|
+
const selector = (0, import_react6.useMemo)(createConversationTurnSelector, [props.sessionId]);
|
|
1314
|
+
const allTurns = props.useChat(selector);
|
|
1315
|
+
const hasMore = props.useSession((snapshot) => snapshot.hasMore);
|
|
1316
|
+
const loadingOlder = props.useSession((snapshot) => snapshot.loadingOlder);
|
|
1317
|
+
const windowSignature = props.useChat(historyWindowSignature);
|
|
1318
|
+
const projected = props.useProjection(MESSAGE_VISIBILITY_PROJECTION_KEY);
|
|
1319
|
+
const hiddenSeqs = (0, import_react6.useMemo)(() => new Set(projected?.deletedSeqs ?? []), [projected]);
|
|
1320
|
+
const turns = (0, import_react6.useMemo)(
|
|
1321
|
+
() => allTurns.filter((turn) => !hiddenSeqs.has(turn.seq)),
|
|
1322
|
+
[allTurns, hiddenSeqs]
|
|
1323
|
+
);
|
|
1324
|
+
const markerRef = (0, import_react6.useRef)(null);
|
|
1325
|
+
const listRef = (0, import_react6.useRef)(null);
|
|
1326
|
+
const pendingLoadSignatureRef = (0, import_react6.useRef)(null);
|
|
1327
|
+
const noProgressAttemptsRef = (0, import_react6.useRef)(0);
|
|
1328
|
+
const [portalTarget, setPortalTarget] = (0, import_react6.useState)(null);
|
|
1329
|
+
const [activeKey, setActiveKey] = (0, import_react6.useState)(null);
|
|
1330
|
+
const [surfaceAvailable, setSurfaceAvailable] = (0, import_react6.useState)(false);
|
|
1331
|
+
const [composerInset, setComposerInset] = (0, import_react6.useState)(176);
|
|
1332
|
+
const [autoLoadPaused, setAutoLoadPaused] = (0, import_react6.useState)(false);
|
|
1333
|
+
const [loadTick, setLoadTick] = (0, import_react6.useState)(0);
|
|
1334
|
+
const [collapsed, setCollapsed] = (0, import_react6.useState)(() => navigationCollapsedPreference);
|
|
1335
|
+
(0, import_react6.useLayoutEffect)(() => {
|
|
1336
|
+
const root = sessionRoot(markerRef.current);
|
|
1337
|
+
setPortalTarget(root);
|
|
1338
|
+
if (root === null) return;
|
|
1339
|
+
const composer = root.querySelector("[data-composer-seat]");
|
|
1340
|
+
const updateInset = () => {
|
|
1341
|
+
setComposerInset(Math.max(96, (composer?.offsetHeight ?? 152) + 20));
|
|
1342
|
+
};
|
|
1343
|
+
updateInset();
|
|
1344
|
+
if (composer === null || typeof ResizeObserver === "undefined") return;
|
|
1345
|
+
const observer = new ResizeObserver(updateInset);
|
|
1346
|
+
observer.observe(composer);
|
|
1347
|
+
return () => {
|
|
1348
|
+
observer.disconnect();
|
|
1349
|
+
};
|
|
1350
|
+
}, [props.sessionId]);
|
|
1351
|
+
(0, import_react6.useEffect)(() => {
|
|
1352
|
+
pendingLoadSignatureRef.current = null;
|
|
1353
|
+
noProgressAttemptsRef.current = 0;
|
|
1354
|
+
setAutoLoadPaused(false);
|
|
1355
|
+
}, [enabled, props.sessionId]);
|
|
1356
|
+
(0, import_react6.useEffect)(() => {
|
|
1357
|
+
if (hasMore) return;
|
|
1358
|
+
pendingLoadSignatureRef.current = null;
|
|
1359
|
+
noProgressAttemptsRef.current = 0;
|
|
1360
|
+
setAutoLoadPaused(false);
|
|
1361
|
+
}, [hasMore]);
|
|
1362
|
+
(0, import_react6.useEffect)(() => {
|
|
1363
|
+
if (!enabled) {
|
|
1364
|
+
setActiveKey(null);
|
|
1365
|
+
setSurfaceAvailable(false);
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
let frame = null;
|
|
1369
|
+
let scrollport = null;
|
|
1370
|
+
const schedule = () => {
|
|
1371
|
+
if (frame !== null) return;
|
|
1372
|
+
frame = window.requestAnimationFrame(recompute);
|
|
1373
|
+
};
|
|
1374
|
+
const bindScrollport = (next) => {
|
|
1375
|
+
if (next === scrollport) return;
|
|
1376
|
+
scrollport?.removeEventListener("scroll", schedule);
|
|
1377
|
+
scrollport = next;
|
|
1378
|
+
scrollport?.addEventListener("scroll", schedule, { passive: true });
|
|
1379
|
+
};
|
|
1380
|
+
const recompute = () => {
|
|
1381
|
+
frame = null;
|
|
1382
|
+
const nextScrollport = conversationScrollport(markerRef.current);
|
|
1383
|
+
bindScrollport(nextScrollport);
|
|
1384
|
+
const flowAvailable = nextScrollport !== null && nextScrollport.querySelector("[data-chat-flow]") !== null;
|
|
1385
|
+
setSurfaceAvailable(flowAvailable);
|
|
1386
|
+
if (nextScrollport === null || !flowAvailable) {
|
|
1387
|
+
setActiveKey(null);
|
|
1388
|
+
return;
|
|
1389
|
+
}
|
|
1390
|
+
const viewport = nextScrollport.getBoundingClientRect();
|
|
1391
|
+
const readingLine = viewport.top + Math.min(120, nextScrollport.clientHeight * 0.24);
|
|
1392
|
+
const positions = [];
|
|
1393
|
+
for (const turn of turns) {
|
|
1394
|
+
const row = conversationRow(nextScrollport, turn.key);
|
|
1395
|
+
if (row !== null) positions.push({ key: turn.key, top: row.getBoundingClientRect().top });
|
|
1396
|
+
}
|
|
1397
|
+
setActiveKey(activeTurnAt(positions, readingLine));
|
|
1398
|
+
};
|
|
1399
|
+
const observer = new MutationObserver(schedule);
|
|
1400
|
+
const root = sessionRoot(markerRef.current);
|
|
1401
|
+
if (root !== null) observer.observe(root, { childList: true, subtree: true });
|
|
1402
|
+
window.addEventListener("resize", schedule);
|
|
1403
|
+
recompute();
|
|
1404
|
+
return () => {
|
|
1405
|
+
observer.disconnect();
|
|
1406
|
+
window.removeEventListener("resize", schedule);
|
|
1407
|
+
scrollport?.removeEventListener("scroll", schedule);
|
|
1408
|
+
if (frame !== null) window.cancelAnimationFrame(frame);
|
|
1409
|
+
};
|
|
1410
|
+
}, [enabled, turns, props.sessionId]);
|
|
1411
|
+
(0, import_react6.useEffect)(() => {
|
|
1412
|
+
if (!shouldAutoLoadOlder({ enabled, surfaceAvailable, hasMore, loadingOlder, paused: autoLoadPaused })) return;
|
|
1413
|
+
const pendingSignature = pendingLoadSignatureRef.current;
|
|
1414
|
+
let delay2 = 0;
|
|
1415
|
+
if (pendingSignature !== null) {
|
|
1416
|
+
if (pendingSignature === windowSignature) {
|
|
1417
|
+
noProgressAttemptsRef.current += 1;
|
|
1418
|
+
if (noProgressAttemptsRef.current >= MAX_NO_PROGRESS_ATTEMPTS) {
|
|
1419
|
+
pendingLoadSignatureRef.current = null;
|
|
1420
|
+
setAutoLoadPaused(true);
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
delay2 = noProgressAttemptsRef.current * 350;
|
|
1424
|
+
} else {
|
|
1425
|
+
noProgressAttemptsRef.current = 0;
|
|
1426
|
+
}
|
|
1427
|
+
pendingLoadSignatureRef.current = null;
|
|
1428
|
+
}
|
|
1429
|
+
let frame = null;
|
|
1430
|
+
let retryTimer = null;
|
|
1431
|
+
let requestTimer = null;
|
|
1432
|
+
const request = () => {
|
|
1433
|
+
frame = window.requestAnimationFrame(() => {
|
|
1434
|
+
frame = null;
|
|
1435
|
+
const scrollport = conversationScrollport(markerRef.current);
|
|
1436
|
+
const button = scrollport === null ? null : nativeLoadOlderButton(scrollport);
|
|
1437
|
+
if (button === null || button.disabled) {
|
|
1438
|
+
retryTimer = window.setTimeout(() => {
|
|
1439
|
+
setLoadTick((current) => current + 1);
|
|
1440
|
+
}, 180);
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
pendingLoadSignatureRef.current = windowSignature;
|
|
1444
|
+
button.click();
|
|
1445
|
+
retryTimer = window.setTimeout(() => {
|
|
1446
|
+
setLoadTick((current) => current + 1);
|
|
1447
|
+
}, 1600);
|
|
1448
|
+
});
|
|
1449
|
+
};
|
|
1450
|
+
if (delay2 > 0) requestTimer = window.setTimeout(request, delay2);
|
|
1451
|
+
else request();
|
|
1452
|
+
return () => {
|
|
1453
|
+
if (frame !== null) window.cancelAnimationFrame(frame);
|
|
1454
|
+
if (retryTimer !== null) window.clearTimeout(retryTimer);
|
|
1455
|
+
if (requestTimer !== null) window.clearTimeout(requestTimer);
|
|
1456
|
+
};
|
|
1457
|
+
}, [autoLoadPaused, enabled, hasMore, loadTick, loadingOlder, surfaceAvailable, windowSignature]);
|
|
1458
|
+
(0, import_react6.useLayoutEffect)(() => {
|
|
1459
|
+
if (collapsed || activeKey === null) return;
|
|
1460
|
+
const list = listRef.current;
|
|
1461
|
+
const active = list?.querySelector('[aria-current="location"]');
|
|
1462
|
+
if (list === null || active === null || active === void 0) return;
|
|
1463
|
+
const top = active.offsetTop;
|
|
1464
|
+
const bottom = top + active.offsetHeight;
|
|
1465
|
+
if (top < list.scrollTop) list.scrollTop = top;
|
|
1466
|
+
else if (bottom > list.scrollTop + list.clientHeight) list.scrollTop = bottom - list.clientHeight;
|
|
1467
|
+
}, [activeKey, collapsed]);
|
|
1468
|
+
const retryAutoLoad = () => {
|
|
1469
|
+
pendingLoadSignatureRef.current = null;
|
|
1470
|
+
noProgressAttemptsRef.current = 0;
|
|
1471
|
+
setAutoLoadPaused(false);
|
|
1472
|
+
setLoadTick((current) => current + 1);
|
|
1473
|
+
};
|
|
1474
|
+
const toggleCollapsed = () => {
|
|
1475
|
+
setCollapsed((current) => {
|
|
1476
|
+
navigationCollapsedPreference = !current;
|
|
1477
|
+
return navigationCollapsedPreference;
|
|
1478
|
+
});
|
|
1479
|
+
};
|
|
1480
|
+
const jump = (key) => {
|
|
1481
|
+
const scrollport = conversationScrollport(markerRef.current);
|
|
1482
|
+
if (scrollport === null) return;
|
|
1483
|
+
const row = conversationRow(scrollport, key);
|
|
1484
|
+
if (row === null) return;
|
|
1485
|
+
const viewport = scrollport.getBoundingClientRect();
|
|
1486
|
+
const rowRect = row.getBoundingClientRect();
|
|
1487
|
+
const top = scrollOffsetForRow(scrollport.scrollTop, viewport.top, rowRect.top);
|
|
1488
|
+
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1489
|
+
scrollport.scrollTo({ top, behavior: reducedMotion ? "auto" : "smooth" });
|
|
1490
|
+
setActiveKey(key);
|
|
1491
|
+
};
|
|
1492
|
+
const rootStyle = { "--dshmore-conversation-nav-bottom": `${String(composerInset)}px` };
|
|
1493
|
+
const surface = enabled && surfaceAvailable && portalTarget !== null ? (0, import_react_dom.createPortal)(
|
|
1494
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1495
|
+
"div",
|
|
1496
|
+
{
|
|
1497
|
+
className: "dshmore-conversation-nav-root",
|
|
1498
|
+
"data-collapsed": collapsed ? "true" : "false",
|
|
1499
|
+
style: rootStyle,
|
|
1500
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1501
|
+
ConversationNavigationMenu,
|
|
1502
|
+
{
|
|
1503
|
+
turns,
|
|
1504
|
+
activeKey,
|
|
1505
|
+
hasMore,
|
|
1506
|
+
loadingOlder,
|
|
1507
|
+
autoLoadPaused,
|
|
1508
|
+
collapsed,
|
|
1509
|
+
onRetry: retryAutoLoad,
|
|
1510
|
+
onToggleCollapsed: toggleCollapsed,
|
|
1511
|
+
onJump: jump,
|
|
1512
|
+
listRef
|
|
1513
|
+
}
|
|
1514
|
+
)
|
|
1515
|
+
}
|
|
1516
|
+
),
|
|
1517
|
+
portalTarget
|
|
1518
|
+
) : null;
|
|
1519
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1520
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { ref: markerRef, className: "dshmore-conversation-nav-marker", "aria-hidden": "true" }),
|
|
1521
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("style", { children: navigationStyles }),
|
|
1522
|
+
surface
|
|
1523
|
+
] });
|
|
1524
|
+
}
|
|
1525
|
+
var navigationStyles = `
|
|
1526
|
+
.dshmore-conversation-nav-marker { display: none; }
|
|
1527
|
+
.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); }
|
|
1528
|
+
.dshmore-conversation-nav-root[data-collapsed="true"] { bottom: auto; height: 58px; }
|
|
1529
|
+
.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; }
|
|
1530
|
+
.dshmore-conversation-nav-menu[data-collapsed="true"] { height: 58px; max-height: 58px; }
|
|
1531
|
+
.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); }
|
|
1532
|
+
.dshmore-conversation-nav-menu[data-collapsed="true"] .dshmore-conversation-nav-header { height: 58px; min-height: 58px; padding: 0 12px 0 14px; border-bottom: 0; }
|
|
1533
|
+
.dshmore-conversation-nav-heading { min-width: 0; }
|
|
1534
|
+
.dshmore-conversation-nav-title-row { min-width: 0; display: flex; align-items: baseline; gap: 6px; }
|
|
1535
|
+
.dshmore-conversation-nav-title { font: var(--dsw-font-s-strong-14); }
|
|
1536
|
+
.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; }
|
|
1537
|
+
.dshmore-conversation-nav-count { margin-top: 2px; color: var(--dsw-alias-label-caption); font-size: 11px; line-height: 16px; text-wrap: pretty; }
|
|
1538
|
+
.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; }
|
|
1539
|
+
.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; }
|
|
1540
|
+
.dshmore-conversation-nav-retry:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover); }
|
|
1541
|
+
.dshmore-conversation-nav-actions { flex: none; display: flex; align-items: center; gap: 6px; }
|
|
1542
|
+
.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; }
|
|
1543
|
+
.dshmore-conversation-nav-toggle:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover); }
|
|
1544
|
+
.dshmore-conversation-nav-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 6px; scrollbar-width: thin; }
|
|
1545
|
+
.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; }
|
|
1546
|
+
.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; }
|
|
1547
|
+
.dshmore-conversation-nav-item:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover); }
|
|
1548
|
+
.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; }
|
|
1549
|
+
.dshmore-conversation-nav-item[aria-current="location"] { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover-solid); }
|
|
1550
|
+
.dshmore-conversation-nav-index { color: var(--dsw-alias-label-caption); font-size: 11px; line-height: 20px; font-variant-numeric: tabular-nums; }
|
|
1551
|
+
.dshmore-conversation-nav-item[aria-current="location"] .dshmore-conversation-nav-index { color: var(--dsw-alias-state-business-primary); font-weight: 600; }
|
|
1552
|
+
.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; }
|
|
1553
|
+
@keyframes dshmore-conversation-nav-enter { from { opacity: 0; transform: translateX(6px); } to { opacity: 1; transform: translateX(0); } }
|
|
1554
|
+
@keyframes dshmore-conversation-nav-pulse { 50% { opacity: .35; transform: scale(.82); } }
|
|
1555
|
+
@media (max-width: 720px) {
|
|
1556
|
+
.dshmore-conversation-nav-root { right: 8px; top: 74px; bottom: auto; width: calc(100% - 16px); height: min(220px, 32dvh); align-items: stretch; }
|
|
1557
|
+
.dshmore-conversation-nav-root[data-collapsed="true"] { height: 58px; }
|
|
1558
|
+
.dshmore-conversation-nav-menu { max-height: 100%; }
|
|
1559
|
+
}
|
|
1560
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1561
|
+
.dshmore-conversation-nav-menu, .dshmore-conversation-nav-loading-dot { animation: none; }
|
|
1562
|
+
.dshmore-conversation-nav-item { transition: none; }
|
|
1563
|
+
}
|
|
1564
|
+
`;
|
|
1565
|
+
var clientPatch4 = {
|
|
1566
|
+
id: CONVERSATION_QUICK_NAVIGATION_PATCH_ID,
|
|
1567
|
+
install: (ctx, activation) => {
|
|
1568
|
+
ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
|
|
1569
|
+
name: "conversation.session.header.utilities",
|
|
1570
|
+
id: `${PLUGIN_NAME}-${CONVERSATION_QUICK_NAVIGATION_PATCH_ID}`,
|
|
1571
|
+
order: 120
|
|
1572
|
+
}, (props) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ConversationQuickNavigation, { ...props, activation })));
|
|
1573
|
+
}
|
|
1574
|
+
};
|
|
1575
|
+
|
|
726
1576
|
// src/generated/client-registry.ts
|
|
727
1577
|
var MESSAGE_ACTION_PATCHES = [{ id: "message-edit", order: 100, useActions: useMessageActions }, { id: "message-delete", order: 200, useActions: useMessageActions2 }];
|
|
728
|
-
var CLIENT_PATCHES = [clientPatch, clientPatch2];
|
|
1578
|
+
var CLIENT_PATCHES = [clientPatch, clientPatch2, clientPatch3, clientPatch4];
|
|
729
1579
|
|
|
730
1580
|
// src/kernel/client/activation.ts
|
|
731
1581
|
var ClientPatchActivation = class {
|
|
@@ -748,19 +1598,19 @@ var ClientPatchActivation = class {
|
|
|
748
1598
|
};
|
|
749
1599
|
|
|
750
1600
|
// src/kernel/client/config-card.tsx
|
|
751
|
-
var
|
|
752
|
-
var
|
|
753
|
-
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");
|
|
754
1604
|
function PatchConfigCard({ activation, initiallyOpen = false }) {
|
|
755
|
-
const scope = (0,
|
|
1605
|
+
const scope = (0, import_react7.useSyncExternalStore)(
|
|
756
1606
|
activation.subscribe,
|
|
757
1607
|
activation.getSettingsSnapshot,
|
|
758
1608
|
activation.getSettingsSnapshot
|
|
759
1609
|
);
|
|
760
1610
|
const settings = activation.getSnapshot();
|
|
761
|
-
const [open, setOpen] = (0,
|
|
762
|
-
const [busy, setBusy] = (0,
|
|
763
|
-
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);
|
|
764
1614
|
if (scope.status === "unavailable") return null;
|
|
765
1615
|
const update = async (id, enabled) => {
|
|
766
1616
|
setBusy((current) => /* @__PURE__ */ new Set([...current, id]));
|
|
@@ -777,9 +1627,9 @@ function PatchConfigCard({ activation, initiallyOpen = false }) {
|
|
|
777
1627
|
});
|
|
778
1628
|
}
|
|
779
1629
|
};
|
|
780
|
-
return /* @__PURE__ */ (0,
|
|
781
|
-
/* @__PURE__ */ (0,
|
|
782
|
-
/* @__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)(
|
|
783
1633
|
"button",
|
|
784
1634
|
{
|
|
785
1635
|
type: "button",
|
|
@@ -790,23 +1640,23 @@ function PatchConfigCard({ activation, initiallyOpen = false }) {
|
|
|
790
1640
|
setOpen((current) => !current);
|
|
791
1641
|
},
|
|
792
1642
|
children: [
|
|
793
|
-
/* @__PURE__ */ (0,
|
|
794
|
-
/* @__PURE__ */ (0,
|
|
795
|
-
/* @__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" })
|
|
796
1646
|
] }),
|
|
797
|
-
/* @__PURE__ */ (0,
|
|
1647
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_dsh_client_ui_primitives7.IconChevronDownOutline14, { className: `dshmore-config-chevron${open ? " dshmore-config-chevron-open" : ""}` })
|
|
798
1648
|
]
|
|
799
1649
|
}
|
|
800
1650
|
),
|
|
801
|
-
open && /* @__PURE__ */ (0,
|
|
802
|
-
/* @__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) => {
|
|
803
1653
|
const waiting = busy.has(patch.id);
|
|
804
|
-
return /* @__PURE__ */ (0,
|
|
805
|
-
/* @__PURE__ */ (0,
|
|
806
|
-
/* @__PURE__ */ (0,
|
|
807
|
-
/* @__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 })
|
|
808
1658
|
] }),
|
|
809
|
-
/* @__PURE__ */ (0,
|
|
1659
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
810
1660
|
"input",
|
|
811
1661
|
{
|
|
812
1662
|
type: "checkbox",
|
|
@@ -821,9 +1671,9 @@ function PatchConfigCard({ activation, initiallyOpen = false }) {
|
|
|
821
1671
|
)
|
|
822
1672
|
] }, patch.id);
|
|
823
1673
|
}) }),
|
|
824
|
-
scope.status === "loading" && /* @__PURE__ */ (0,
|
|
825
|
-
scope.status === "ready" && !scope.writable && /* @__PURE__ */ (0,
|
|
826
|
-
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 })
|
|
827
1677
|
] })
|
|
828
1678
|
] });
|
|
829
1679
|
}
|
|
@@ -831,23 +1681,16 @@ function installPatchConfigCard(ctx, activation) {
|
|
|
831
1681
|
ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
|
|
832
1682
|
name: "settings.plugin.item",
|
|
833
1683
|
key: PATCH_SETTINGS_NAMESPACE,
|
|
834
|
-
priority: 90
|
|
835
|
-
|
|
836
|
-
}, (props) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PatchConfigCard, { ...props, activation })));
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
// src/kernel/message-visibility.ts
|
|
840
|
-
var MESSAGE_VISIBILITY_PROJECTION_KEY = "dshMoreMessageDelete";
|
|
841
|
-
|
|
842
|
-
// src/platform/dsh/client/message-targets.tsx
|
|
843
|
-
var import_react6 = require("react");
|
|
844
|
-
|
|
845
|
-
// src/platform/dsh/client/message-content.ts
|
|
846
|
-
function contentText(content) {
|
|
847
|
-
return content.filter((block) => block.type === "text").map((block) => block.text).join(" ").replace(/\s+/g, " ").trim();
|
|
1684
|
+
priority: 90
|
|
1685
|
+
}, (props) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PatchConfigCard, { ...props, activation })));
|
|
848
1686
|
}
|
|
849
1687
|
|
|
850
1688
|
// src/platform/dsh/client/message-targets.tsx
|
|
1689
|
+
var import_react8 = require("react");
|
|
1690
|
+
var EMPTY_MESSAGE_SURFACE = {
|
|
1691
|
+
rows: [],
|
|
1692
|
+
byKey: /* @__PURE__ */ new Map()
|
|
1693
|
+
};
|
|
851
1694
|
function messageNodeInfo(node) {
|
|
852
1695
|
const candidate = node;
|
|
853
1696
|
if (candidate?.kind === "user" && Number.isSafeInteger(candidate.data?.seq) && Array.isArray(candidate.data?.content)) {
|
|
@@ -876,11 +1719,40 @@ function presentationSeq(node) {
|
|
|
876
1719
|
if (Number.isSafeInteger(closing?.finalNode?.seq)) return closing?.finalNode?.seq;
|
|
877
1720
|
return void 0;
|
|
878
1721
|
}
|
|
1722
|
+
function sameSurfaceRow(left, right) {
|
|
1723
|
+
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;
|
|
1724
|
+
}
|
|
1725
|
+
function createMessageSurfaceSelector(active = true) {
|
|
1726
|
+
if (!active) return () => EMPTY_MESSAGE_SURFACE;
|
|
1727
|
+
const nodeCache = /* @__PURE__ */ new WeakMap();
|
|
1728
|
+
let previous = EMPTY_MESSAGE_SURFACE;
|
|
1729
|
+
return (snapshot) => {
|
|
1730
|
+
const rows = [];
|
|
1731
|
+
for (const key of snapshot.order) {
|
|
1732
|
+
const node = snapshot.nodes.get(key);
|
|
1733
|
+
if (node === void 0) continue;
|
|
1734
|
+
let row = nodeCache.get(node);
|
|
1735
|
+
if (row === void 0) {
|
|
1736
|
+
row = {
|
|
1737
|
+
key,
|
|
1738
|
+
presentationSeq: presentationSeq(node),
|
|
1739
|
+
action: messageNodeInfo(node)
|
|
1740
|
+
};
|
|
1741
|
+
nodeCache.set(node, row);
|
|
1742
|
+
}
|
|
1743
|
+
const retained = previous.byKey.get(key);
|
|
1744
|
+
rows.push(sameSurfaceRow(retained, row) ? retained : row);
|
|
1745
|
+
}
|
|
1746
|
+
if (rows.length === previous.rows.length && rows.every((row, index) => row === previous.rows[index])) return previous;
|
|
1747
|
+
previous = { rows, byKey: new Map(rows.map((row) => [row.key, row])) };
|
|
1748
|
+
return previous;
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
879
1751
|
function hostForRow(row, kind) {
|
|
880
1752
|
const existing = row.querySelector(":scope [data-dshmore-message-actions]");
|
|
881
1753
|
if (kind === "assistant") {
|
|
882
|
-
const turnRoot = row.querySelector("[data-turn-tail][data-
|
|
883
|
-
const copyButton2 = turnRoot?.querySelector('button[aria-label="\u590D\u5236"], button[aria-label="Copy"]');
|
|
1754
|
+
const turnRoot = row.querySelector("[data-turn-tail][data-actions-reveal]");
|
|
1755
|
+
const copyButton2 = turnRoot?.querySelector('button[aria-label="\u590D\u5236"], button[aria-label="Copy"], button[aria-label="\u5DF2\u590D\u5236"], button[aria-label="Copied"]');
|
|
884
1756
|
const builtInActions2 = copyButton2?.parentElement;
|
|
885
1757
|
const placeBeforeClock = (host3) => {
|
|
886
1758
|
if (builtInActions2 === void 0 || builtInActions2 === null) return;
|
|
@@ -904,7 +1776,7 @@ function hostForRow(row, kind) {
|
|
|
904
1776
|
else row.appendChild(host2);
|
|
905
1777
|
return host2;
|
|
906
1778
|
}
|
|
907
|
-
const copyButton = row.querySelector('button[aria-label="\u590D\u5236"], button[aria-label="Copy"]');
|
|
1779
|
+
const copyButton = row.querySelector('button[aria-label="\u590D\u5236"], button[aria-label="Copy"], button[aria-label="\u5DF2\u590D\u5236"], button[aria-label="Copied"]');
|
|
908
1780
|
const builtInActions = copyButton?.parentElement;
|
|
909
1781
|
if (existing !== null) {
|
|
910
1782
|
if (builtInActions !== void 0 && builtInActions !== null && (existing.parentElement !== builtInActions || existing.nextElementSibling !== copyButton)) {
|
|
@@ -919,11 +1791,11 @@ function hostForRow(row, kind) {
|
|
|
919
1791
|
else row.appendChild(host);
|
|
920
1792
|
return host;
|
|
921
1793
|
}
|
|
1794
|
+
function sameTarget(left, right) {
|
|
1795
|
+
return right !== void 0 && left.key === right.key && left.seq === right.seq && left.host === right.host && left.text === right.text;
|
|
1796
|
+
}
|
|
922
1797
|
function sameTargets(left, right) {
|
|
923
|
-
return left.length === right.length && left.every((target, index) =>
|
|
924
|
-
const other = right[index];
|
|
925
|
-
return other !== void 0 && target.key === other.key && target.seq === other.seq && target.host === other.host && target.text === other.text;
|
|
926
|
-
});
|
|
1798
|
+
return left.length === right.length && left.every((target, index) => sameTarget(target, right[index]));
|
|
927
1799
|
}
|
|
928
1800
|
function trajectoryRecordKey(row) {
|
|
929
1801
|
const encoded = row.dataset.trajectoryRowKey;
|
|
@@ -944,68 +1816,156 @@ function trajectorySourceSeq(row) {
|
|
|
944
1816
|
const seq = Number(raw);
|
|
945
1817
|
return Number.isSafeInteger(seq) && seq >= 0 ? seq : void 0;
|
|
946
1818
|
}
|
|
947
|
-
function useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys) {
|
|
948
|
-
const
|
|
949
|
-
const
|
|
950
|
-
(0,
|
|
1819
|
+
function useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys, active = true) {
|
|
1820
|
+
const selector = (0, import_react8.useMemo)(() => createMessageSurfaceSelector(active), [active, props.sessionId]);
|
|
1821
|
+
const surface = props.useChat(selector);
|
|
1822
|
+
const surfaceRef = (0, import_react8.useRef)(surface);
|
|
1823
|
+
const hiddenSeqsRef = (0, import_react8.useRef)(hiddenSeqs);
|
|
1824
|
+
const hiddenTrajectoryKeysRef = (0, import_react8.useRef)(hiddenTrajectoryKeys);
|
|
1825
|
+
surfaceRef.current = surface;
|
|
1826
|
+
hiddenSeqsRef.current = hiddenSeqs;
|
|
1827
|
+
hiddenTrajectoryKeysRef.current = hiddenTrajectoryKeys;
|
|
1828
|
+
const [targets, setTargets] = (0, import_react8.useState)([]);
|
|
1829
|
+
const requestFullScanRef = (0, import_react8.useRef)(null);
|
|
1830
|
+
(0, import_react8.useLayoutEffect)(() => {
|
|
1831
|
+
if (!active) {
|
|
1832
|
+
setTargets((current) => current.length === 0 ? current : []);
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
951
1835
|
let frame = null;
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
delete row.dataset.dshmoreHidden;
|
|
967
|
-
}
|
|
968
|
-
const info = messageNodeInfo(node);
|
|
969
|
-
if (info !== null) next.push({ key, ...info, host: hostForRow(row, info.kind) });
|
|
1836
|
+
let fullScanRequested = true;
|
|
1837
|
+
let pruneDisconnected = false;
|
|
1838
|
+
let targetsDirty = false;
|
|
1839
|
+
const pendingChatRows = /* @__PURE__ */ new Set();
|
|
1840
|
+
const pendingTrajectoryRows = /* @__PURE__ */ new Set();
|
|
1841
|
+
const mountedTargets = /* @__PURE__ */ new Map();
|
|
1842
|
+
const processChatRow = (row) => {
|
|
1843
|
+
const key = row.dataset.chatFlowKey;
|
|
1844
|
+
if (key === void 0) return;
|
|
1845
|
+
const descriptor = surfaceRef.current.byKey.get(key);
|
|
1846
|
+
if (descriptor === void 0) {
|
|
1847
|
+
if (mountedTargets.delete(key)) targetsDirty = true;
|
|
1848
|
+
row.querySelector("[data-dshmore-message-actions]")?.remove();
|
|
1849
|
+
return;
|
|
970
1850
|
}
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1851
|
+
if (descriptor.presentationSeq !== void 0 && hiddenSeqsRef.current.has(descriptor.presentationSeq)) {
|
|
1852
|
+
row.dataset.dshmoreHidden = "";
|
|
1853
|
+
if (mountedTargets.delete(key)) targetsDirty = true;
|
|
1854
|
+
row.querySelector("[data-dshmore-message-actions]")?.remove();
|
|
1855
|
+
return;
|
|
1856
|
+
}
|
|
1857
|
+
if (row.dataset.dshmoreHidden !== void 0) delete row.dataset.dshmoreHidden;
|
|
1858
|
+
if (descriptor.action === null) {
|
|
1859
|
+
if (mountedTargets.delete(key)) targetsDirty = true;
|
|
1860
|
+
row.querySelector("[data-dshmore-message-actions]")?.remove();
|
|
1861
|
+
return;
|
|
1862
|
+
}
|
|
1863
|
+
const next = {
|
|
1864
|
+
key,
|
|
1865
|
+
...descriptor.action,
|
|
1866
|
+
host: hostForRow(row, descriptor.action.kind)
|
|
1867
|
+
};
|
|
1868
|
+
const current = mountedTargets.get(key);
|
|
1869
|
+
if (current === void 0 || !sameTarget(current, next)) {
|
|
1870
|
+
mountedTargets.set(key, next);
|
|
1871
|
+
targetsDirty = true;
|
|
1872
|
+
}
|
|
1873
|
+
};
|
|
1874
|
+
const processTrajectoryRow = (row) => {
|
|
1875
|
+
const seq = trajectorySourceSeq(row);
|
|
1876
|
+
const key = trajectoryRecordKey(row);
|
|
1877
|
+
if (seq !== void 0 && hiddenSeqsRef.current.has(seq) || key !== void 0 && hiddenTrajectoryKeysRef.current.has(key)) {
|
|
1878
|
+
row.dataset.dshmoreHidden = "";
|
|
1879
|
+
} else if (row.dataset.dshmoreHidden !== void 0) {
|
|
1880
|
+
delete row.dataset.dshmoreHidden;
|
|
1881
|
+
}
|
|
1882
|
+
};
|
|
1883
|
+
const publishTargets = () => {
|
|
1884
|
+
for (const [key, target] of mountedTargets) {
|
|
1885
|
+
if (!target.host.isConnected && mountedTargets.delete(key)) targetsDirty = true;
|
|
979
1886
|
}
|
|
1887
|
+
const next = surfaceRef.current.rows.flatMap((row) => {
|
|
1888
|
+
const target = mountedTargets.get(row.key);
|
|
1889
|
+
return target === void 0 ? [] : [target];
|
|
1890
|
+
});
|
|
980
1891
|
setTargets((current) => sameTargets(current, next) ? current : next);
|
|
1892
|
+
targetsDirty = false;
|
|
1893
|
+
};
|
|
1894
|
+
const flush = () => {
|
|
1895
|
+
frame = null;
|
|
1896
|
+
if (fullScanRequested) {
|
|
1897
|
+
fullScanRequested = false;
|
|
1898
|
+
mountedTargets.clear();
|
|
1899
|
+
targetsDirty = true;
|
|
1900
|
+
for (const row of document.querySelectorAll("[data-chat-flow-key]")) processChatRow(row);
|
|
1901
|
+
for (const row of document.querySelectorAll("[data-trajectory-row-key]")) processTrajectoryRow(row);
|
|
1902
|
+
} else {
|
|
1903
|
+
for (const row of pendingChatRows) if (row.isConnected) processChatRow(row);
|
|
1904
|
+
for (const row of pendingTrajectoryRows) if (row.isConnected) processTrajectoryRow(row);
|
|
1905
|
+
}
|
|
1906
|
+
pendingChatRows.clear();
|
|
1907
|
+
pendingTrajectoryRows.clear();
|
|
1908
|
+
const shouldPrune = pruneDisconnected;
|
|
1909
|
+
pruneDisconnected = false;
|
|
1910
|
+
if (targetsDirty || shouldPrune) publishTargets();
|
|
981
1911
|
};
|
|
982
1912
|
const schedule = () => {
|
|
983
|
-
if (frame === null) frame = window.requestAnimationFrame(
|
|
1913
|
+
if (frame === null) frame = window.requestAnimationFrame(flush);
|
|
984
1914
|
};
|
|
985
|
-
|
|
986
|
-
|
|
1915
|
+
const requestFullScan = () => {
|
|
1916
|
+
fullScanRequested = true;
|
|
1917
|
+
schedule();
|
|
1918
|
+
};
|
|
1919
|
+
const collectRows = (node) => {
|
|
1920
|
+
const element = node instanceof Element ? node : node.parentElement;
|
|
1921
|
+
if (element === null) return;
|
|
1922
|
+
const chatAncestor = element.closest("[data-chat-flow-key]");
|
|
1923
|
+
if (chatAncestor !== null) pendingChatRows.add(chatAncestor);
|
|
1924
|
+
const trajectoryAncestor = element.closest("[data-trajectory-row-key]");
|
|
1925
|
+
if (trajectoryAncestor !== null) pendingTrajectoryRows.add(trajectoryAncestor);
|
|
1926
|
+
if (element.matches("[data-chat-flow-key]")) pendingChatRows.add(element);
|
|
1927
|
+
if (element.matches("[data-trajectory-row-key]")) pendingTrajectoryRows.add(element);
|
|
1928
|
+
element.querySelectorAll("[data-chat-flow-key]").forEach((row) => pendingChatRows.add(row));
|
|
1929
|
+
element.querySelectorAll("[data-trajectory-row-key]").forEach((row) => pendingTrajectoryRows.add(row));
|
|
1930
|
+
};
|
|
1931
|
+
requestFullScanRef.current = requestFullScan;
|
|
1932
|
+
const observer = new MutationObserver((records) => {
|
|
1933
|
+
for (const record of records) {
|
|
1934
|
+
if (record.removedNodes.length > 0) {
|
|
1935
|
+
pruneDisconnected = true;
|
|
1936
|
+
collectRows(record.target);
|
|
1937
|
+
}
|
|
1938
|
+
record.addedNodes.forEach(collectRows);
|
|
1939
|
+
}
|
|
1940
|
+
if (pendingChatRows.size > 0 || pendingTrajectoryRows.size > 0 || pruneDisconnected) schedule();
|
|
1941
|
+
});
|
|
987
1942
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
1943
|
+
flush();
|
|
988
1944
|
return () => {
|
|
989
1945
|
observer.disconnect();
|
|
990
1946
|
if (frame !== null) window.cancelAnimationFrame(frame);
|
|
1947
|
+
requestFullScanRef.current = null;
|
|
991
1948
|
for (const row of document.querySelectorAll("[data-dshmore-hidden]")) {
|
|
992
1949
|
delete row.dataset.dshmoreHidden;
|
|
993
1950
|
}
|
|
994
1951
|
for (const host of document.querySelectorAll("[data-dshmore-message-actions]")) host.remove();
|
|
995
1952
|
};
|
|
996
|
-
}, [
|
|
1953
|
+
}, [active, props.sessionId]);
|
|
1954
|
+
(0, import_react8.useLayoutEffect)(() => {
|
|
1955
|
+
requestFullScanRef.current?.();
|
|
1956
|
+
}, [surface, hiddenSeqs, hiddenTrajectoryKeys]);
|
|
997
1957
|
return targets;
|
|
998
1958
|
}
|
|
999
1959
|
|
|
1000
1960
|
// src/client/index.tsx
|
|
1001
|
-
var
|
|
1961
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1002
1962
|
var inject = ["slots", "sessions", "workspaces", "settingsScope"];
|
|
1003
1963
|
function MessageActionPatchController({ patch, props, targets, enabled }) {
|
|
1004
1964
|
const actions = patch.useActions(props, enabled);
|
|
1005
1965
|
const style = { "--dshmore-action-order": patch.order };
|
|
1006
|
-
return /* @__PURE__ */ (0,
|
|
1007
|
-
enabled && targets.map((target) => (0,
|
|
1008
|
-
/* @__PURE__ */ (0,
|
|
1966
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
1967
|
+
enabled && targets.map((target) => (0, import_react_dom2.createPortal)(
|
|
1968
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dshmore-action-contribution", style, children: actions.renderAction(target) }),
|
|
1009
1969
|
target.host,
|
|
1010
1970
|
`${patch.id}:${target.key}`
|
|
1011
1971
|
)),
|
|
@@ -1013,14 +1973,15 @@ function MessageActionPatchController({ patch, props, targets, enabled }) {
|
|
|
1013
1973
|
] });
|
|
1014
1974
|
}
|
|
1015
1975
|
function MessageActionsController(props) {
|
|
1016
|
-
const settings = (0,
|
|
1976
|
+
const settings = (0, import_react9.useSyncExternalStore)(props.activation.subscribe, props.activation.getSnapshot);
|
|
1017
1977
|
const projected = props.useProjection(MESSAGE_VISIBILITY_PROJECTION_KEY);
|
|
1018
|
-
const hiddenSeqs = (0,
|
|
1019
|
-
const hiddenTrajectoryKeys = (0,
|
|
1020
|
-
const
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1978
|
+
const hiddenSeqs = (0, import_react9.useMemo)(() => new Set(projected?.deletedSeqs ?? []), [projected]);
|
|
1979
|
+
const hiddenTrajectoryKeys = (0, import_react9.useMemo)(() => new Set(projected?.hiddenTrajectoryKeys ?? []), [projected]);
|
|
1980
|
+
const messageActionsEnabled = MESSAGE_ACTION_PATCHES.some((patch) => settings[patch.id]);
|
|
1981
|
+
const targets = useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys, messageActionsEnabled);
|
|
1982
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
1983
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("style", { children: styles }),
|
|
1984
|
+
MESSAGE_ACTION_PATCHES.map((patch) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1024
1985
|
MessageActionPatchController,
|
|
1025
1986
|
{
|
|
1026
1987
|
patch,
|
|
@@ -1042,9 +2003,8 @@ function apply(ctx) {
|
|
|
1042
2003
|
ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
|
|
1043
2004
|
name: "conversation.session.header.utilities",
|
|
1044
2005
|
id: `${PLUGIN_NAME}-message-actions`,
|
|
1045
|
-
order: 90
|
|
1046
|
-
|
|
1047
|
-
}, (props) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageActionsController, { ...props, ctx, activation })));
|
|
2006
|
+
order: 90
|
|
2007
|
+
}, (props) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MessageActionsController, { ...props, ctx, activation })));
|
|
1048
2008
|
for (const patch of CLIENT_PATCHES) patch.install(ctx, activation);
|
|
1049
2009
|
}
|
|
1050
2010
|
return module.exports; } });
|