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.
Files changed (34) hide show
  1. package/CONTRIBUTING.md +1 -1
  2. package/README.md +16 -3
  3. package/README.zh-CN.md +16 -3
  4. package/dist/client.js +1112 -152
  5. package/dist/index.js +262 -55
  6. package/dist/types/client/index.d.ts +5 -1
  7. package/dist/types/generated/host-settings-schema.d.ts +4 -0
  8. package/dist/types/generated/patch-catalog.d.ts +16 -0
  9. package/dist/types/kernel/client/activation.d.ts +2 -1
  10. package/dist/types/kernel/client/config-card.d.ts +2 -2
  11. package/dist/types/kernel/client/message-actions.d.ts +4 -0
  12. package/dist/types/kernel/client/patch.d.ts +1 -1
  13. package/dist/types/kernel/host/settings.d.ts +1 -1
  14. package/dist/types/patches/conversation-quick-navigation/client/index.d.ts +27 -0
  15. package/dist/types/patches/conversation-quick-navigation/client/navigation.d.ts +26 -0
  16. package/dist/types/patches/conversation-quick-navigation/host/index.d.ts +2 -0
  17. package/dist/types/patches/conversation-quick-navigation/shared.d.ts +1 -0
  18. package/dist/types/patches/message-delete/client/index.d.ts +1 -1
  19. package/dist/types/patches/message-delete/host/message-selection.d.ts +3 -3
  20. package/dist/types/patches/message-delete/host/projection.d.ts +26 -3
  21. package/dist/types/patches/message-edit/client/index.d.ts +1 -1
  22. package/dist/types/patches/message-edit/host/edit-continuation.d.ts +3 -3
  23. package/dist/types/patches/model-reasoning-efforts/client/index.d.ts +54 -0
  24. package/dist/types/patches/model-reasoning-efforts/host/index.d.ts +2 -0
  25. package/dist/types/patches/model-reasoning-efforts/host/validate.d.ts +13 -0
  26. package/dist/types/patches/model-reasoning-efforts/shared.d.ts +51 -0
  27. package/dist/types/platform/dsh/client/api.d.ts +2 -0
  28. package/dist/types/platform/dsh/client/message-targets.d.ts +14 -1
  29. package/dist/types/platform/dsh/client/runtime.d.ts +2 -1
  30. package/dist/types/platform/dsh/client/session-sync.d.ts +4 -2
  31. package/dist/types/platform/dsh/host/session-history.d.ts +3 -3
  32. package/dist/types/platform/dsh/host/wire.d.ts +6 -0
  33. package/dsh.plugin.json +1 -1
  34. 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 import_react7 = require("react");
29
- var import_react_dom = require("react-dom");
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 callPatchApi(patchId, action, payload) {
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
- if (!response.ok || body?.ok !== true || body.value === void 0) {
91
- throw new DshMoreApiError(body?.error?.code ?? "http", body?.error?.message ?? `HTTP ${String(response.status)}`);
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
- return visible.find((id) => sessions.ids.indexOf(id) > deletingIndex) ?? visible.at(-1);
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 WeakMap();
443
- const sync = () => {
444
- for (const archiveButton of document.querySelectorAll('button[role="menuitem"]')) {
445
- const text = archiveButton.textContent?.trim();
446
- const deleteLabel = text === "\u5F52\u6863\u4F1A\u8BDD" ? "\u6C38\u4E45\u5220\u9664\u4F1A\u8BDD" : text === "Archive session" ? "Permanently delete session" : void 0;
447
- if (deleteLabel === void 0) continue;
448
- const wrapper = archiveButton.parentElement;
449
- if (wrapper === null) continue;
450
- if (archiveButton.hasAttribute(DELETE_SOURCE_ATTRIBUTE)) {
451
- if (deleteItems.get(archiveButton)?.isConnected === true) continue;
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
- sync();
479
- const observer = new MutationObserver(schedule);
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 = { sessionId, resolve };
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
- sessionListRef.current,
552
- workspaceListRef.current,
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 === null ? "" : sessionListRef.current.byId[pending.sessionId]?.displayTitle ?? pending.sessionId;
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 result = await callPatchApi(
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)), result.markdown);
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 import_react5 = require("react");
752
- var import_dsh_client_ui_primitives5 = require("@deepseek-ai/dsh-client-ui-primitives");
753
- var import_jsx_runtime5 = require("react/jsx-runtime");
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, import_react5.useSyncExternalStore)(
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, import_react5.useState)(initiallyOpen);
762
- const [busy, setBusy] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
763
- const [error, setError] = (0, import_react5.useState)(null);
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, import_jsx_runtime5.jsxs)("li", { className: `dshmore-config-card${open ? " dshmore-config-card-open" : ""}`, children: [
781
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: styles }),
782
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
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, import_jsx_runtime5.jsxs)("span", { className: "dshmore-config-heading-copy", children: [
794
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { children: "DSH More" }),
795
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "\u52A8\u6001\u5F00\u542F\u6216\u5173\u95ED\u72EC\u7ACB\u8865\u4E01\uFF0C\u9ED8\u8BA4\u5168\u90E8\u5F00\u542F\u3002" })
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, import_jsx_runtime5.jsx)(import_dsh_client_ui_primitives5.IconChevronDownOutline14, { className: `dshmore-config-chevron${open ? " dshmore-config-chevron-open" : ""}` })
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, import_jsx_runtime5.jsxs)("div", { className: "dshmore-config-body", children: [
802
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dshmore-config-list", children: PATCH_CATALOG.map((patch) => {
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, import_jsx_runtime5.jsxs)("label", { className: "dshmore-config-row", children: [
805
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "dshmore-config-copy", children: [
806
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { children: patch.name }),
807
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: patch.description })
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, import_jsx_runtime5.jsx)(
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, import_jsx_runtime5.jsx)("p", { className: "dshmore-config-note", children: "\u6B63\u5728\u8BFB\u53D6\u63D2\u4EF6\u914D\u7F6E\u2026" }),
825
- scope.status === "ready" && !scope.writable && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "dshmore-config-note", children: "\u5F53\u524D Settings \u5B58\u50A8\u4E3A\u53EA\u8BFB\u3002" }),
826
- error !== null && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "dshmore-config-error", children: error })
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
- registrant: PLUGIN_NAME
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-time-hover-root]");
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 snapshot = props.useSession((state) => state);
949
- const [targets, setTargets] = (0, import_react6.useState)([]);
950
- (0, import_react6.useLayoutEffect)(() => {
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
- const scan = () => {
953
- frame = null;
954
- const next = [];
955
- for (const row of document.querySelectorAll("[data-chat-flow-key]")) {
956
- const key = row.dataset.chatFlowKey;
957
- if (key === void 0) continue;
958
- const node = snapshot.chat.nodes.get(key);
959
- const seq = presentationSeq(node);
960
- if (seq !== void 0 && hiddenSeqs.has(seq)) {
961
- row.dataset.dshmoreHidden = "";
962
- row.querySelector("[data-dshmore-message-actions]")?.remove();
963
- continue;
964
- }
965
- if (row.dataset.dshmoreHidden !== void 0) {
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
- for (const row of document.querySelectorAll("[data-trajectory-row-key]")) {
972
- const seq = trajectorySourceSeq(row);
973
- const key = trajectoryRecordKey(row);
974
- if (seq !== void 0 && hiddenSeqs.has(seq) || key !== void 0 && hiddenTrajectoryKeys.has(key)) {
975
- row.dataset.dshmoreHidden = "";
976
- } else if (row.dataset.dshmoreHidden !== void 0) {
977
- delete row.dataset.dshmoreHidden;
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(scan);
1913
+ if (frame === null) frame = window.requestAnimationFrame(flush);
984
1914
  };
985
- scan();
986
- const observer = new MutationObserver(schedule);
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
- }, [snapshot, hiddenSeqs, hiddenTrajectoryKeys]);
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 import_jsx_runtime6 = require("react/jsx-runtime");
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, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1007
- enabled && targets.map((target) => (0, import_react_dom.createPortal)(
1008
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dshmore-action-contribution", style, children: actions.renderAction(target) }),
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, import_react7.useSyncExternalStore)(props.activation.subscribe, props.activation.getSnapshot);
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, import_react7.useMemo)(() => new Set(projected?.deletedSeqs ?? []), [projected]);
1019
- const hiddenTrajectoryKeys = (0, import_react7.useMemo)(() => new Set(projected?.hiddenTrajectoryKeys ?? []), [projected]);
1020
- const targets = useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys);
1021
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1022
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("style", { children: styles }),
1023
- MESSAGE_ACTION_PATCHES.map((patch) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
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
- registrant: PLUGIN_NAME
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; } });