dsh-more 0.0.3 → 0.0.5

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 (32) hide show
  1. package/CONTRIBUTING.md +1 -1
  2. package/README.md +5 -3
  3. package/README.zh-CN.md +5 -3
  4. package/dist/client.js +88 -513
  5. package/dist/index.js +154 -58
  6. package/dist/types/client/index.d.ts +5 -1
  7. package/dist/types/generated/host-settings-schema.d.ts +2 -2
  8. package/dist/types/generated/patch-catalog.d.ts +4 -4
  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/message-delete/client/index.d.ts +1 -1
  15. package/dist/types/patches/message-delete/host/message-selection.d.ts +3 -3
  16. package/dist/types/patches/message-delete/host/projection.d.ts +26 -3
  17. package/dist/types/patches/message-edit/client/index.d.ts +1 -1
  18. package/dist/types/patches/message-edit/host/edit-continuation.d.ts +3 -3
  19. package/dist/types/patches/model-reasoning-efforts/client/index.d.ts +17 -2
  20. package/dist/types/patches/session-auto-title/client/index.d.ts +2 -0
  21. package/dist/types/patches/session-auto-title/host/generate.d.ts +6 -0
  22. package/dist/types/patches/session-auto-title/shared.d.ts +2 -0
  23. package/dist/types/platform/dsh/client/message-targets.d.ts +3 -2
  24. package/dist/types/platform/dsh/client/runtime.d.ts +2 -1
  25. package/dist/types/platform/dsh/client/session-sync.d.ts +4 -2
  26. package/dist/types/platform/dsh/host/session-history.d.ts +3 -3
  27. package/dsh.plugin.json +1 -1
  28. package/package.json +65 -46
  29. package/dist/types/patches/conversation-quick-navigation/client/index.d.ts +0 -22
  30. package/dist/types/patches/conversation-quick-navigation/client/navigation.d.ts +0 -26
  31. package/dist/types/patches/conversation-quick-navigation/shared.d.ts +0 -1
  32. /package/dist/types/patches/{conversation-quick-navigation → session-auto-title}/host/index.d.ts +0 -0
package/dist/client.js CHANGED
@@ -25,8 +25,8 @@ __export(index_exports, {
25
25
  inject: () => inject
26
26
  });
27
27
  module.exports = __toCommonJS(index_exports);
28
- var import_react9 = require("react");
29
- var import_react_dom2 = require("react-dom");
28
+ var import_react8 = require("react");
29
+ var import_react_dom = require("react-dom");
30
30
 
31
31
  // src/generated/patch-catalog.ts
32
32
  var PATCH_CATALOG = [
@@ -35,7 +35,7 @@ var PATCH_CATALOG = [
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
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
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" }
38
+ { "id": "session-auto-title", "name": "\u4F1A\u8BDD\u81EA\u52A8\u547D\u540D", "description": "\u65B0\u4F1A\u8BDD\u9996\u6761\u9700\u6C42\u540E\uFF0C\u4F7F\u7528\u5F53\u524D\u6A21\u578B\u751F\u6210\u300C\u7C7B\u578B \xB7 \u5177\u4F53\u4EFB\u52A1\u300D\u6807\u9898\uFF1B\u4FDD\u7559\u624B\u52A8\u547D\u540D\uFF0C\u5931\u8D25\u65F6\u4FDD\u7559\u539F\u6709\u6807\u9898", "defaultEnabled": true, "order": 600, "clientKind": "standalone" }
39
39
  ];
40
40
  var DEFAULT_PATCH_SETTINGS = {
41
41
  "message-edit": true,
@@ -43,7 +43,7 @@ var DEFAULT_PATCH_SETTINGS = {
43
43
  "session-delete": true,
44
44
  "conversation-markdown-export": true,
45
45
  "model-reasoning-efforts": true,
46
- "conversation-quick-navigation": true
46
+ "session-auto-title": true
47
47
  };
48
48
  function decodePatchSettings(value) {
49
49
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
@@ -506,6 +506,7 @@ function installSessionDeleteMenuItems(onDelete) {
506
506
  frame = null;
507
507
  for (const [archiveButton, deleteItem] of deleteItems) {
508
508
  if (!archiveButton.isConnected) {
509
+ deleteItem.remove();
509
510
  deleteItems.delete(archiveButton);
510
511
  } else if (!deleteItem.isConnected) {
511
512
  archiveButton.removeAttribute(DELETE_SOURCE_ATTRIBUTE);
@@ -654,8 +655,7 @@ var clientPatch = {
654
655
  ctx.slots.inject("shell.overlay", () => ctx.slots.register({
655
656
  name: "shell.overlay",
656
657
  id: `${PLUGIN_NAME}-${SESSION_DELETE_PATCH_ID}`,
657
- order: 90,
658
- registrant: PLUGIN_NAME
658
+ order: 90
659
659
  }, (props) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SessionDeleteController, { ...props, ctx, activation })));
660
660
  }
661
661
  };
@@ -769,8 +769,7 @@ var clientPatch2 = {
769
769
  ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
770
770
  name: "conversation.session.header.utilities",
771
771
  id: `${PLUGIN_NAME}-${CONVERSATION_MARKDOWN_EXPORT_PATCH_ID}`,
772
- order: 100,
773
- registrant: PLUGIN_NAME
772
+ order: 100
774
773
  }, (props) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ConversationMarkdownExportButton, { ...props, activation })));
775
774
  }
776
775
  };
@@ -1066,9 +1065,14 @@ function ModelsPageEffortPortal(props) {
1066
1065
  syncButtons();
1067
1066
  const observer = new MutationObserver(schedule);
1068
1067
  observer.observe(document.body, { childList: true, subtree: true });
1068
+ document.addEventListener("input", schedule);
1069
+ document.addEventListener("change", schedule);
1069
1070
  return () => {
1070
1071
  observer.disconnect();
1072
+ document.removeEventListener("input", schedule);
1073
+ document.removeEventListener("change", schedule);
1071
1074
  if (frame !== null) window.cancelAnimationFrame(frame);
1075
+ document.querySelectorAll(`[${EFFORT_BUTTON_ATTR}]`).forEach((element) => element.remove());
1072
1076
  };
1073
1077
  }, [enabled, syncButtons]);
1074
1078
  (0, import_react5.useEffect)(() => {
@@ -1115,461 +1119,24 @@ var clientPatch3 = {
1115
1119
  getSnapshot: scope.getSnapshot.bind(scope),
1116
1120
  subscribe: scope.subscribe.bind(scope),
1117
1121
  set: scope.set.bind(scope),
1118
- unset: scope.unset.bind(scope)
1122
+ unset: scope.unset.bind(scope),
1123
+ mutate: scope.mutate.bind(scope)
1119
1124
  };
1120
1125
  ctx.slots.inject("shell.overlay", () => ctx.slots.register({
1121
1126
  name: "shell.overlay",
1122
1127
  id: `${PLUGIN_NAME}-${MODEL_REASONING_EFFORTS_PATCH_ID}`,
1123
- order: 90,
1124
- registrant: PLUGIN_NAME
1128
+ order: 90
1125
1129
  }, (props) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ModelsPageEffortPortal, { ...props, scope: boundScope, activation })));
1126
1130
  }
1127
1131
  };
1128
1132
 
1129
- // src/patches/conversation-quick-navigation/client/index.tsx
1130
- var import_react6 = require("react");
1131
- var import_react_dom = require("react-dom");
1132
- var import_dsh_client_ui_primitives6 = require("@deepseek-ai/dsh-client-ui-primitives");
1133
-
1134
- // src/kernel/message-visibility.ts
1135
- var MESSAGE_VISIBILITY_PROJECTION_KEY = "dshMoreMessageDelete";
1136
-
1137
- // src/patches/conversation-quick-navigation/shared.ts
1138
- var CONVERSATION_QUICK_NAVIGATION_PATCH_ID = "conversation-quick-navigation";
1139
-
1140
- // src/platform/dsh/client/message-content.ts
1141
- function contentText(content) {
1142
- return content.filter((block) => block.type === "text").map((block) => block.text).join(" ").replace(/\s+/g, " ").trim();
1143
- }
1144
-
1145
- // src/patches/conversation-quick-navigation/client/navigation.ts
1146
- var MAX_LABEL_CODE_POINTS = 72;
1147
- function conciseLabel(content, fallbackIndex) {
1148
- const text = contentText(content);
1149
- if (text === "") return `\u7B2C ${String(fallbackIndex)} \u8F6E \xB7 \u975E\u6587\u672C\u6D88\u606F`;
1150
- const codePoints = [...text];
1151
- if (codePoints.length <= MAX_LABEL_CODE_POINTS) return text;
1152
- return `${codePoints.slice(0, MAX_LABEL_CODE_POINTS - 1).join("")}\u2026`;
1153
- }
1154
- function turnSummary(node, key, index) {
1155
- const candidate = node;
1156
- if (candidate?.kind !== "user" && candidate?.kind !== "steering") return null;
1157
- if (!Number.isSafeInteger(candidate.data?.seq) || !Array.isArray(candidate.data?.content)) return null;
1158
- return {
1159
- key,
1160
- seq: candidate.data.seq,
1161
- label: conciseLabel(candidate.data.content, index)
1162
- };
1163
- }
1164
- function sameTurns(left, right) {
1165
- return left.length === right.length && left.every((turn, index) => {
1166
- const other = right[index];
1167
- return other !== void 0 && turn.key === other.key && turn.seq === other.seq && turn.label === other.label;
1168
- });
1169
- }
1170
- function createConversationTurnSelector() {
1171
- let previousOrder;
1172
- let previous = [];
1173
- return (snapshot) => {
1174
- if (snapshot.chat.order === previousOrder) return previous;
1175
- previousOrder = snapshot.chat.order;
1176
- const next = [];
1177
- for (const key of snapshot.chat.order) {
1178
- const summary = turnSummary(snapshot.chat.nodes.get(key), key, next.length + 1);
1179
- if (summary !== null) next.push(summary);
1180
- }
1181
- if (!sameTurns(previous, next)) previous = next;
1182
- return previous;
1183
- };
1184
- }
1185
- function historyWindowSignature(snapshot) {
1186
- const first = snapshot.chat.order[0] ?? "";
1187
- const last = snapshot.chat.order.at(-1) ?? "";
1188
- return `${first}\0${last}\0${String(snapshot.chat.order.length)}`;
1189
- }
1190
- function shouldAutoLoadOlder(state) {
1191
- return state.enabled && state.surfaceAvailable && state.hasMore && !state.loadingOlder && !state.paused;
1192
- }
1193
- function activeTurnAt(positions, readingLine) {
1194
- let active = positions[0]?.key ?? null;
1195
- for (const position of positions) {
1196
- if (position.top > readingLine) break;
1197
- active = position.key;
1198
- }
1199
- return active;
1200
- }
1201
- function scrollOffsetForRow(currentScrollTop, scrollportTop, rowTop, topInset = 20) {
1202
- return Math.max(0, currentScrollTop + rowTop - scrollportTop - topInset);
1203
- }
1133
+ // src/patches/session-auto-title/shared.ts
1134
+ var SESSION_AUTO_TITLE_PATCH_ID = "session-auto-title";
1204
1135
 
1205
- // src/patches/conversation-quick-navigation/client/index.tsx
1206
- var import_jsx_runtime6 = require("react/jsx-runtime");
1207
- var MAX_NO_PROGRESS_ATTEMPTS = 3;
1208
- var MENU_LIST_ID = "dshmore-conversation-nav-list";
1209
- var navigationCollapsedPreference = false;
1210
- function conversationRow(root, key) {
1211
- for (const row of root.querySelectorAll("[data-chat-flow-key]")) {
1212
- if (row.dataset.chatFlowKey === key) return row;
1213
- }
1214
- return null;
1215
- }
1216
- function sessionRoot(marker) {
1217
- return marker?.closest("[data-phase]") ?? null;
1218
- }
1219
- function conversationScrollport(marker) {
1220
- return sessionRoot(marker)?.querySelector("[data-conversation-scroll]") ?? null;
1221
- }
1222
- function nativeLoadOlderButton(scrollport) {
1223
- const flow = scrollport.querySelector("[data-chat-flow]");
1224
- if (flow === null) return null;
1225
- for (const child of flow.children) {
1226
- if (child instanceof HTMLElement && child.dataset.chatFlowKey !== void 0) break;
1227
- const button = child.querySelector("button");
1228
- if (button !== null) return button;
1229
- }
1230
- return null;
1231
- }
1232
- function ConversationNavigationMenu({
1233
- turns,
1234
- activeKey,
1235
- hasMore,
1236
- loadingOlder,
1237
- autoLoadPaused,
1238
- collapsed,
1239
- onRetry,
1240
- onToggleCollapsed,
1241
- onJump,
1242
- listRef
1243
- }) {
1244
- const loading = hasMore && !autoLoadPaused;
1245
- const compactStatus = autoLoadPaused ? `${String(turns.length)} \u8F6E \xB7 \u5DF2\u6682\u505C` : loading ? `${String(turns.length)} \u8F6E \xB7 \u52A0\u8F7D\u4E2D` : `${String(turns.length)} \u8F6E`;
1246
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1247
- "nav",
1248
- {
1249
- className: "dshmore-conversation-nav-menu",
1250
- "data-collapsed": collapsed ? "true" : "false",
1251
- "aria-label": "\u5F53\u524D\u4F1A\u8BDD\u7684\u5BF9\u8BDD\u76EE\u5F55",
1252
- children: [
1253
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("header", { className: "dshmore-conversation-nav-header", children: [
1254
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dshmore-conversation-nav-heading", children: [
1255
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dshmore-conversation-nav-title-row", children: [
1256
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dshmore-conversation-nav-title", children: "\u5BF9\u8BDD\u76EE\u5F55" }),
1257
- collapsed && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dshmore-conversation-nav-compact-status", role: "status", "aria-live": "polite", children: [
1258
- loading && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dshmore-conversation-nav-loading-dot", "aria-hidden": "true" }),
1259
- compactStatus
1260
- ] })
1261
- ] }),
1262
- !collapsed && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dshmore-conversation-nav-count", role: "status", "aria-live": "polite", children: [
1263
- loading && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dshmore-conversation-nav-loading-dot", "aria-hidden": "true" }),
1264
- autoLoadPaused ? `\u5DF2\u8F7D\u5165 ${String(turns.length)} \u8F6E\uFF0C\u81EA\u52A8\u52A0\u8F7D\u5DF2\u6682\u505C` : loading ? `\u6B63\u5728\u52A0\u8F7D\u5168\u90E8\u8BB0\u5F55 \xB7 \u5DF2\u8F7D\u5165 ${String(turns.length)} \u8F6E` : `\u5171 ${String(turns.length)} \u8F6E`
1265
- ] })
1266
- ] }),
1267
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dshmore-conversation-nav-actions", children: [
1268
- !collapsed && autoLoadPaused && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "dshmore-conversation-nav-retry", onClick: onRetry, children: "\u7EE7\u7EED\u52A0\u8F7D" }),
1269
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1270
- "button",
1271
- {
1272
- type: "button",
1273
- className: "dshmore-conversation-nav-toggle",
1274
- "aria-label": collapsed ? "\u5C55\u5F00\u5BF9\u8BDD\u76EE\u5F55" : "\u6298\u53E0\u5BF9\u8BDD\u76EE\u5F55",
1275
- "aria-controls": MENU_LIST_ID,
1276
- "aria-expanded": !collapsed,
1277
- onClick: onToggleCollapsed,
1278
- children: collapsed ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_dsh_client_ui_primitives6.IconChevronDownOutline14, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_dsh_client_ui_primitives6.IconChevronUpOutline14, { size: 14 })
1279
- }
1280
- )
1281
- ] })
1282
- ] }),
1283
- !collapsed && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { id: MENU_LIST_ID, ref: listRef, className: "dshmore-conversation-nav-list", children: [
1284
- turns.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dshmore-conversation-nav-empty", children: loadingOlder || hasMore ? "\u6B63\u5728\u8BFB\u53D6\u5386\u53F2\u8BB0\u5F55\u2026" : "\u5F53\u524D\u6CA1\u6709\u53EF\u5B9A\u4F4D\u7684\u5BF9\u8BDD" }),
1285
- turns.map((turn, index) => {
1286
- const active = turn.key === activeKey;
1287
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1288
- "button",
1289
- {
1290
- type: "button",
1291
- className: "dshmore-conversation-nav-item",
1292
- "aria-current": active ? "location" : void 0,
1293
- onClick: () => {
1294
- onJump(turn.key);
1295
- },
1296
- children: [
1297
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dshmore-conversation-nav-index", children: String(index + 1).padStart(2, "0") }),
1298
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dshmore-conversation-nav-label", children: turn.label })
1299
- ]
1300
- },
1301
- turn.key
1302
- );
1303
- })
1304
- ] })
1305
- ]
1306
- }
1307
- );
1308
- }
1309
- function ConversationQuickNavigation(props) {
1310
- const settings = (0, import_react6.useSyncExternalStore)(props.activation.subscribe, props.activation.getSnapshot, props.activation.getSnapshot);
1311
- const enabled = settings[CONVERSATION_QUICK_NAVIGATION_PATCH_ID];
1312
- const selector = (0, import_react6.useMemo)(createConversationTurnSelector, [props.sessionId]);
1313
- const allTurns = props.useSession(selector);
1314
- const hasMore = props.useSession((snapshot) => snapshot.hasMore);
1315
- const loadingOlder = props.useSession((snapshot) => snapshot.loadingOlder);
1316
- const windowSignature = props.useSession(historyWindowSignature);
1317
- const projected = props.useProjection(MESSAGE_VISIBILITY_PROJECTION_KEY);
1318
- const hiddenSeqs = (0, import_react6.useMemo)(() => new Set(projected?.deletedSeqs ?? []), [projected]);
1319
- const turns = (0, import_react6.useMemo)(
1320
- () => allTurns.filter((turn) => !hiddenSeqs.has(turn.seq)),
1321
- [allTurns, hiddenSeqs]
1322
- );
1323
- const markerRef = (0, import_react6.useRef)(null);
1324
- const listRef = (0, import_react6.useRef)(null);
1325
- const pendingLoadSignatureRef = (0, import_react6.useRef)(null);
1326
- const noProgressAttemptsRef = (0, import_react6.useRef)(0);
1327
- const [portalTarget, setPortalTarget] = (0, import_react6.useState)(null);
1328
- const [activeKey, setActiveKey] = (0, import_react6.useState)(null);
1329
- const [surfaceAvailable, setSurfaceAvailable] = (0, import_react6.useState)(false);
1330
- const [composerInset, setComposerInset] = (0, import_react6.useState)(176);
1331
- const [autoLoadPaused, setAutoLoadPaused] = (0, import_react6.useState)(false);
1332
- const [loadTick, setLoadTick] = (0, import_react6.useState)(0);
1333
- const [collapsed, setCollapsed] = (0, import_react6.useState)(() => navigationCollapsedPreference);
1334
- (0, import_react6.useLayoutEffect)(() => {
1335
- const root = sessionRoot(markerRef.current);
1336
- setPortalTarget(root);
1337
- if (root === null) return;
1338
- const composer = root.querySelector("[data-composer-seat]");
1339
- const updateInset = () => {
1340
- setComposerInset(Math.max(96, (composer?.offsetHeight ?? 152) + 20));
1341
- };
1342
- updateInset();
1343
- if (composer === null || typeof ResizeObserver === "undefined") return;
1344
- const observer = new ResizeObserver(updateInset);
1345
- observer.observe(composer);
1346
- return () => {
1347
- observer.disconnect();
1348
- };
1349
- }, [props.sessionId]);
1350
- (0, import_react6.useEffect)(() => {
1351
- pendingLoadSignatureRef.current = null;
1352
- noProgressAttemptsRef.current = 0;
1353
- setAutoLoadPaused(false);
1354
- }, [enabled, props.sessionId]);
1355
- (0, import_react6.useEffect)(() => {
1356
- if (hasMore) return;
1357
- pendingLoadSignatureRef.current = null;
1358
- noProgressAttemptsRef.current = 0;
1359
- setAutoLoadPaused(false);
1360
- }, [hasMore]);
1361
- (0, import_react6.useEffect)(() => {
1362
- if (!enabled) {
1363
- setActiveKey(null);
1364
- setSurfaceAvailable(false);
1365
- return;
1366
- }
1367
- let frame = null;
1368
- let scrollport = null;
1369
- const schedule = () => {
1370
- if (frame !== null) return;
1371
- frame = window.requestAnimationFrame(recompute);
1372
- };
1373
- const bindScrollport = (next) => {
1374
- if (next === scrollport) return;
1375
- scrollport?.removeEventListener("scroll", schedule);
1376
- scrollport = next;
1377
- scrollport?.addEventListener("scroll", schedule, { passive: true });
1378
- };
1379
- const recompute = () => {
1380
- frame = null;
1381
- const nextScrollport = conversationScrollport(markerRef.current);
1382
- bindScrollport(nextScrollport);
1383
- const flowAvailable = nextScrollport !== null && nextScrollport.querySelector("[data-chat-flow]") !== null;
1384
- setSurfaceAvailable(flowAvailable);
1385
- if (nextScrollport === null || !flowAvailable) {
1386
- setActiveKey(null);
1387
- return;
1388
- }
1389
- const viewport = nextScrollport.getBoundingClientRect();
1390
- const readingLine = viewport.top + Math.min(120, nextScrollport.clientHeight * 0.24);
1391
- const positions = [];
1392
- for (const turn of turns) {
1393
- const row = conversationRow(nextScrollport, turn.key);
1394
- if (row !== null) positions.push({ key: turn.key, top: row.getBoundingClientRect().top });
1395
- }
1396
- setActiveKey(activeTurnAt(positions, readingLine));
1397
- };
1398
- const observer = new MutationObserver(schedule);
1399
- const root = sessionRoot(markerRef.current);
1400
- if (root !== null) observer.observe(root, { childList: true, subtree: true });
1401
- window.addEventListener("resize", schedule);
1402
- recompute();
1403
- return () => {
1404
- observer.disconnect();
1405
- window.removeEventListener("resize", schedule);
1406
- scrollport?.removeEventListener("scroll", schedule);
1407
- if (frame !== null) window.cancelAnimationFrame(frame);
1408
- };
1409
- }, [enabled, turns]);
1410
- (0, import_react6.useEffect)(() => {
1411
- if (!shouldAutoLoadOlder({ enabled, surfaceAvailable, hasMore, loadingOlder, paused: autoLoadPaused })) return;
1412
- const pendingSignature = pendingLoadSignatureRef.current;
1413
- let delay2 = 0;
1414
- if (pendingSignature !== null) {
1415
- if (pendingSignature === windowSignature) {
1416
- noProgressAttemptsRef.current += 1;
1417
- if (noProgressAttemptsRef.current >= MAX_NO_PROGRESS_ATTEMPTS) {
1418
- pendingLoadSignatureRef.current = null;
1419
- setAutoLoadPaused(true);
1420
- return;
1421
- }
1422
- delay2 = noProgressAttemptsRef.current * 350;
1423
- } else {
1424
- noProgressAttemptsRef.current = 0;
1425
- }
1426
- pendingLoadSignatureRef.current = null;
1427
- }
1428
- let frame = null;
1429
- let retryTimer = null;
1430
- let requestTimer = null;
1431
- const request = () => {
1432
- frame = window.requestAnimationFrame(() => {
1433
- frame = null;
1434
- const scrollport = conversationScrollport(markerRef.current);
1435
- const button = scrollport === null ? null : nativeLoadOlderButton(scrollport);
1436
- if (button === null || button.disabled) {
1437
- retryTimer = window.setTimeout(() => {
1438
- setLoadTick((current) => current + 1);
1439
- }, 180);
1440
- return;
1441
- }
1442
- pendingLoadSignatureRef.current = windowSignature;
1443
- button.click();
1444
- retryTimer = window.setTimeout(() => {
1445
- setLoadTick((current) => current + 1);
1446
- }, 1600);
1447
- });
1448
- };
1449
- if (delay2 > 0) requestTimer = window.setTimeout(request, delay2);
1450
- else request();
1451
- return () => {
1452
- if (frame !== null) window.cancelAnimationFrame(frame);
1453
- if (retryTimer !== null) window.clearTimeout(retryTimer);
1454
- if (requestTimer !== null) window.clearTimeout(requestTimer);
1455
- };
1456
- }, [autoLoadPaused, enabled, hasMore, loadTick, loadingOlder, surfaceAvailable, windowSignature]);
1457
- (0, import_react6.useLayoutEffect)(() => {
1458
- if (collapsed || activeKey === null) return;
1459
- const list = listRef.current;
1460
- const active = list?.querySelector('[aria-current="location"]');
1461
- if (list === null || active === null || active === void 0) return;
1462
- const top = active.offsetTop;
1463
- const bottom = top + active.offsetHeight;
1464
- if (top < list.scrollTop) list.scrollTop = top;
1465
- else if (bottom > list.scrollTop + list.clientHeight) list.scrollTop = bottom - list.clientHeight;
1466
- }, [activeKey, collapsed]);
1467
- const retryAutoLoad = () => {
1468
- pendingLoadSignatureRef.current = null;
1469
- noProgressAttemptsRef.current = 0;
1470
- setAutoLoadPaused(false);
1471
- setLoadTick((current) => current + 1);
1472
- };
1473
- const toggleCollapsed = () => {
1474
- setCollapsed((current) => {
1475
- navigationCollapsedPreference = !current;
1476
- return navigationCollapsedPreference;
1477
- });
1478
- };
1479
- const jump = (key) => {
1480
- const scrollport = conversationScrollport(markerRef.current);
1481
- if (scrollport === null) return;
1482
- const row = conversationRow(scrollport, key);
1483
- if (row === null) return;
1484
- const viewport = scrollport.getBoundingClientRect();
1485
- const rowRect = row.getBoundingClientRect();
1486
- const top = scrollOffsetForRow(scrollport.scrollTop, viewport.top, rowRect.top);
1487
- const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1488
- scrollport.scrollTo({ top, behavior: reducedMotion ? "auto" : "smooth" });
1489
- setActiveKey(key);
1490
- };
1491
- const rootStyle = { "--dshmore-conversation-nav-bottom": `${String(composerInset)}px` };
1492
- const surface = enabled && surfaceAvailable && portalTarget !== null ? (0, import_react_dom.createPortal)(
1493
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1494
- "div",
1495
- {
1496
- className: "dshmore-conversation-nav-root",
1497
- "data-collapsed": collapsed ? "true" : "false",
1498
- style: rootStyle,
1499
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1500
- ConversationNavigationMenu,
1501
- {
1502
- turns,
1503
- activeKey,
1504
- hasMore,
1505
- loadingOlder,
1506
- autoLoadPaused,
1507
- collapsed,
1508
- onRetry: retryAutoLoad,
1509
- onToggleCollapsed: toggleCollapsed,
1510
- onJump: jump,
1511
- listRef
1512
- }
1513
- )
1514
- }
1515
- ),
1516
- portalTarget
1517
- ) : null;
1518
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1519
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { ref: markerRef, className: "dshmore-conversation-nav-marker", "aria-hidden": "true" }),
1520
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("style", { children: navigationStyles }),
1521
- surface
1522
- ] });
1523
- }
1524
- var navigationStyles = `
1525
- .dshmore-conversation-nav-marker { display: none; }
1526
- .dshmore-conversation-nav-root { position: absolute; z-index: 20; right: 14px; top: 78px; bottom: var(--dshmore-conversation-nav-bottom, 176px); width: min(286px, calc(100% - 40px)); pointer-events: none; display: flex; align-items: flex-start; font-family: var(--dsw-font-family); }
1527
- .dshmore-conversation-nav-root[data-collapsed="true"] { bottom: auto; height: 58px; }
1528
- .dshmore-conversation-nav-menu { box-sizing: border-box; width: 100%; max-height: min(620px, 100%); min-height: 0; overflow: hidden; pointer-events: auto; display: flex; flex-direction: column; border: 1px solid var(--dsw-alias-border-l2); border-radius: 12px; color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-base); box-shadow: var(--dsw-shadow-lv1, var(--dsw-shadow-lv2)); animation: dshmore-conversation-nav-enter 150ms ease-out; }
1529
- .dshmore-conversation-nav-menu[data-collapsed="true"] { height: 58px; max-height: 58px; }
1530
- .dshmore-conversation-nav-header { box-sizing: border-box; flex: none; min-height: 58px; padding: 12px 12px 10px 14px; display: flex; align-items: center; justify-content: space-between; gap: 10px; border-bottom: 1px solid var(--dsw-alias-border-l3); }
1531
- .dshmore-conversation-nav-menu[data-collapsed="true"] .dshmore-conversation-nav-header { height: 58px; min-height: 58px; padding: 0 12px 0 14px; border-bottom: 0; }
1532
- .dshmore-conversation-nav-heading { min-width: 0; }
1533
- .dshmore-conversation-nav-title-row { min-width: 0; display: flex; align-items: baseline; gap: 6px; }
1534
- .dshmore-conversation-nav-title { font: var(--dsw-font-s-strong-14); }
1535
- .dshmore-conversation-nav-compact-status { min-width: 0; overflow: hidden; color: var(--dsw-alias-label-caption); text-overflow: ellipsis; white-space: nowrap; font-size: 11px; line-height: 16px; }
1536
- .dshmore-conversation-nav-count { margin-top: 2px; color: var(--dsw-alias-label-caption); font-size: 11px; line-height: 16px; text-wrap: pretty; }
1537
- .dshmore-conversation-nav-loading-dot { width: 6px; height: 6px; margin-right: 6px; display: inline-block; border-radius: 999px; background: var(--dsw-alias-state-business-primary); vertical-align: 1px; animation: dshmore-conversation-nav-pulse 1.1s ease-in-out infinite; }
1538
- .dshmore-conversation-nav-retry { flex: none; min-height: 28px; padding: 4px 9px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 8px; color: var(--dsw-alias-label-secondary); background: transparent; cursor: pointer; font: inherit; font-size: 11px; }
1539
- .dshmore-conversation-nav-retry:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover); }
1540
- .dshmore-conversation-nav-actions { flex: none; display: flex; align-items: center; gap: 6px; }
1541
- .dshmore-conversation-nav-toggle { box-sizing: border-box; width: 28px; height: 28px; flex: none; padding: 0; display: grid; place-items: center; border: 0; border-radius: 999px; color: var(--dsw-alias-label-secondary); background: transparent; cursor: pointer; }
1542
- .dshmore-conversation-nav-toggle:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover); }
1543
- .dshmore-conversation-nav-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 6px; scrollbar-width: thin; }
1544
- .dshmore-conversation-nav-empty { padding: 18px 12px; color: var(--dsw-alias-label-caption); text-align: center; font-size: 12px; line-height: 18px; text-wrap: pretty; }
1545
- .dshmore-conversation-nav-item { box-sizing: border-box; width: 100%; min-height: 46px; padding: 7px 9px; display: grid; grid-template-columns: 28px minmax(0, 1fr); align-items: start; gap: 2px; border: 0; border-radius: 9px; color: var(--dsw-alias-label-secondary); background: transparent; cursor: pointer; text-align: left; font-family: inherit; transition: background-color 120ms ease, color 120ms ease; }
1546
- .dshmore-conversation-nav-item:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover); }
1547
- .dshmore-conversation-nav-item:focus-visible, .dshmore-conversation-nav-retry:focus-visible, .dshmore-conversation-nav-toggle:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary); outline-offset: -2px; }
1548
- .dshmore-conversation-nav-item[aria-current="location"] { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover-solid); }
1549
- .dshmore-conversation-nav-index { color: var(--dsw-alias-label-caption); font-size: 11px; line-height: 20px; font-variant-numeric: tabular-nums; }
1550
- .dshmore-conversation-nav-item[aria-current="location"] .dshmore-conversation-nav-index { color: var(--dsw-alias-state-business-primary); font-weight: 600; }
1551
- .dshmore-conversation-nav-label { min-width: 0; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; font-size: 13px; line-height: 20px; overflow-wrap: anywhere; text-wrap: pretty; }
1552
- @keyframes dshmore-conversation-nav-enter { from { opacity: 0; transform: translateX(6px); } to { opacity: 1; transform: translateX(0); } }
1553
- @keyframes dshmore-conversation-nav-pulse { 50% { opacity: .35; transform: scale(.82); } }
1554
- @media (max-width: 720px) {
1555
- .dshmore-conversation-nav-root { right: 8px; top: 74px; bottom: auto; width: calc(100% - 16px); height: min(220px, 32dvh); align-items: stretch; }
1556
- .dshmore-conversation-nav-root[data-collapsed="true"] { height: 58px; }
1557
- .dshmore-conversation-nav-menu { max-height: 100%; }
1558
- }
1559
- @media (prefers-reduced-motion: reduce) {
1560
- .dshmore-conversation-nav-menu, .dshmore-conversation-nav-loading-dot { animation: none; }
1561
- .dshmore-conversation-nav-item { transition: none; }
1562
- }
1563
- `;
1136
+ // src/patches/session-auto-title/client/index.ts
1564
1137
  var clientPatch4 = {
1565
- id: CONVERSATION_QUICK_NAVIGATION_PATCH_ID,
1566
- install: (ctx, activation) => {
1567
- ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
1568
- name: "conversation.session.header.utilities",
1569
- id: `${PLUGIN_NAME}-${CONVERSATION_QUICK_NAVIGATION_PATCH_ID}`,
1570
- order: 120,
1571
- registrant: PLUGIN_NAME
1572
- }, (props) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ConversationQuickNavigation, { ...props, activation })));
1138
+ id: SESSION_AUTO_TITLE_PATCH_ID,
1139
+ install: () => {
1573
1140
  }
1574
1141
  };
1575
1142
 
@@ -1598,19 +1165,19 @@ var ClientPatchActivation = class {
1598
1165
  };
1599
1166
 
1600
1167
  // src/kernel/client/config-card.tsx
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");
1168
+ var import_react6 = require("react");
1169
+ var import_dsh_client_ui_primitives6 = require("@deepseek-ai/dsh-client-ui-primitives");
1170
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1604
1171
  function PatchConfigCard({ activation, initiallyOpen = false }) {
1605
- const scope = (0, import_react7.useSyncExternalStore)(
1172
+ const scope = (0, import_react6.useSyncExternalStore)(
1606
1173
  activation.subscribe,
1607
1174
  activation.getSettingsSnapshot,
1608
1175
  activation.getSettingsSnapshot
1609
1176
  );
1610
1177
  const settings = activation.getSnapshot();
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);
1178
+ const [open, setOpen] = (0, import_react6.useState)(initiallyOpen);
1179
+ const [busy, setBusy] = (0, import_react6.useState)(/* @__PURE__ */ new Set());
1180
+ const [error, setError] = (0, import_react6.useState)(null);
1614
1181
  if (scope.status === "unavailable") return null;
1615
1182
  const update = async (id, enabled) => {
1616
1183
  setBusy((current) => /* @__PURE__ */ new Set([...current, id]));
@@ -1627,9 +1194,9 @@ function PatchConfigCard({ activation, initiallyOpen = false }) {
1627
1194
  });
1628
1195
  }
1629
1196
  };
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)(
1197
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { className: `dshmore-config-card${open ? " dshmore-config-card-open" : ""}`, children: [
1198
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("style", { children: styles }),
1199
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1633
1200
  "button",
1634
1201
  {
1635
1202
  type: "button",
@@ -1640,23 +1207,23 @@ function PatchConfigCard({ activation, initiallyOpen = false }) {
1640
1207
  setOpen((current) => !current);
1641
1208
  },
1642
1209
  children: [
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" })
1210
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "dshmore-config-heading-copy", children: [
1211
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: "DSH More" }),
1212
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\u52A8\u6001\u5F00\u542F\u6216\u5173\u95ED\u72EC\u7ACB\u8865\u4E01\uFF0C\u9ED8\u8BA4\u5168\u90E8\u5F00\u542F\u3002" })
1646
1213
  ] }),
1647
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_dsh_client_ui_primitives7.IconChevronDownOutline14, { className: `dshmore-config-chevron${open ? " dshmore-config-chevron-open" : ""}` })
1214
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_dsh_client_ui_primitives6.IconChevronDownOutline14, { className: `dshmore-config-chevron${open ? " dshmore-config-chevron-open" : ""}` })
1648
1215
  ]
1649
1216
  }
1650
1217
  ),
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) => {
1218
+ open && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dshmore-config-body", children: [
1219
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dshmore-config-list", children: PATCH_CATALOG.map((patch) => {
1653
1220
  const waiting = busy.has(patch.id);
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 })
1221
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "dshmore-config-row", children: [
1222
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "dshmore-config-copy", children: [
1223
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: patch.name }),
1224
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: patch.description })
1658
1225
  ] }),
1659
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1226
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1660
1227
  "input",
1661
1228
  {
1662
1229
  type: "checkbox",
@@ -1671,9 +1238,9 @@ function PatchConfigCard({ activation, initiallyOpen = false }) {
1671
1238
  )
1672
1239
  ] }, patch.id);
1673
1240
  }) }),
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 })
1241
+ scope.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "dshmore-config-note", children: "\u6B63\u5728\u8BFB\u53D6\u63D2\u4EF6\u914D\u7F6E\u2026" }),
1242
+ scope.status === "ready" && !scope.writable && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "dshmore-config-note", children: "\u5F53\u524D Settings \u5B58\u50A8\u4E3A\u53EA\u8BFB\u3002" }),
1243
+ error !== null && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "dshmore-config-error", children: error })
1677
1244
  ] })
1678
1245
  ] });
1679
1246
  }
@@ -1681,13 +1248,22 @@ function installPatchConfigCard(ctx, activation) {
1681
1248
  ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
1682
1249
  name: "settings.plugin.item",
1683
1250
  key: PATCH_SETTINGS_NAMESPACE,
1684
- priority: 90,
1685
- registrant: PLUGIN_NAME
1686
- }, (props) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PatchConfigCard, { ...props, activation })));
1251
+ priority: 90
1252
+ }, (props) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PatchConfigCard, { ...props, activation })));
1253
+ }
1254
+
1255
+ // src/kernel/message-visibility.ts
1256
+ var MESSAGE_VISIBILITY_PROJECTION_KEY = "dshMoreMessageDelete";
1257
+
1258
+ // src/platform/dsh/client/message-targets.tsx
1259
+ var import_react7 = require("react");
1260
+
1261
+ // src/platform/dsh/client/message-content.ts
1262
+ function contentText(content) {
1263
+ return content.filter((block) => block.type === "text").map((block) => block.text).join(" ").replace(/\s+/g, " ").trim();
1687
1264
  }
1688
1265
 
1689
1266
  // src/platform/dsh/client/message-targets.tsx
1690
- var import_react8 = require("react");
1691
1267
  var EMPTY_MESSAGE_SURFACE = {
1692
1268
  rows: [],
1693
1269
  byKey: /* @__PURE__ */ new Map()
@@ -1727,13 +1303,10 @@ function createMessageSurfaceSelector(active = true) {
1727
1303
  if (!active) return () => EMPTY_MESSAGE_SURFACE;
1728
1304
  const nodeCache = /* @__PURE__ */ new WeakMap();
1729
1305
  let previous = EMPTY_MESSAGE_SURFACE;
1730
- let previousOrder;
1731
1306
  return (snapshot) => {
1732
- if (snapshot.chat.order === previousOrder) return previous;
1733
- previousOrder = snapshot.chat.order;
1734
1307
  const rows = [];
1735
- for (const key of snapshot.chat.order) {
1736
- const node = snapshot.chat.nodes.get(key);
1308
+ for (const key of snapshot.order) {
1309
+ const node = snapshot.nodes.get(key);
1737
1310
  if (node === void 0) continue;
1738
1311
  let row = nodeCache.get(node);
1739
1312
  if (row === void 0) {
@@ -1755,8 +1328,8 @@ function createMessageSurfaceSelector(active = true) {
1755
1328
  function hostForRow(row, kind) {
1756
1329
  const existing = row.querySelector(":scope [data-dshmore-message-actions]");
1757
1330
  if (kind === "assistant") {
1758
- const turnRoot = row.querySelector("[data-turn-tail][data-time-hover-root]");
1759
- const copyButton2 = turnRoot?.querySelector('button[aria-label="\u590D\u5236"], button[aria-label="Copy"]');
1331
+ const turnRoot = row.querySelector("[data-turn-tail][data-actions-reveal]");
1332
+ const copyButton2 = turnRoot?.querySelector('button[aria-label="\u590D\u5236"], button[aria-label="Copy"], button[aria-label="\u5DF2\u590D\u5236"], button[aria-label="Copied"]');
1760
1333
  const builtInActions2 = copyButton2?.parentElement;
1761
1334
  const placeBeforeClock = (host3) => {
1762
1335
  if (builtInActions2 === void 0 || builtInActions2 === null) return;
@@ -1780,7 +1353,7 @@ function hostForRow(row, kind) {
1780
1353
  else row.appendChild(host2);
1781
1354
  return host2;
1782
1355
  }
1783
- const copyButton = row.querySelector('button[aria-label="\u590D\u5236"], button[aria-label="Copy"]');
1356
+ const copyButton = row.querySelector('button[aria-label="\u590D\u5236"], button[aria-label="Copy"], button[aria-label="\u5DF2\u590D\u5236"], button[aria-label="Copied"]');
1784
1357
  const builtInActions = copyButton?.parentElement;
1785
1358
  if (existing !== null) {
1786
1359
  if (builtInActions !== void 0 && builtInActions !== null && (existing.parentElement !== builtInActions || existing.nextElementSibling !== copyButton)) {
@@ -1821,17 +1394,17 @@ function trajectorySourceSeq(row) {
1821
1394
  return Number.isSafeInteger(seq) && seq >= 0 ? seq : void 0;
1822
1395
  }
1823
1396
  function useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys, active = true) {
1824
- const selector = (0, import_react8.useMemo)(() => createMessageSurfaceSelector(active), [active, props.sessionId]);
1825
- const surface = props.useSession(selector);
1826
- const surfaceRef = (0, import_react8.useRef)(surface);
1827
- const hiddenSeqsRef = (0, import_react8.useRef)(hiddenSeqs);
1828
- const hiddenTrajectoryKeysRef = (0, import_react8.useRef)(hiddenTrajectoryKeys);
1397
+ const selector = (0, import_react7.useMemo)(() => createMessageSurfaceSelector(active), [active, props.sessionId]);
1398
+ const surface = props.useChat(selector);
1399
+ const surfaceRef = (0, import_react7.useRef)(surface);
1400
+ const hiddenSeqsRef = (0, import_react7.useRef)(hiddenSeqs);
1401
+ const hiddenTrajectoryKeysRef = (0, import_react7.useRef)(hiddenTrajectoryKeys);
1829
1402
  surfaceRef.current = surface;
1830
1403
  hiddenSeqsRef.current = hiddenSeqs;
1831
1404
  hiddenTrajectoryKeysRef.current = hiddenTrajectoryKeys;
1832
- const [targets, setTargets] = (0, import_react8.useState)([]);
1833
- const requestFullScanRef = (0, import_react8.useRef)(null);
1834
- (0, import_react8.useLayoutEffect)(() => {
1405
+ const [targets, setTargets] = (0, import_react7.useState)([]);
1406
+ const requestFullScanRef = (0, import_react7.useRef)(null);
1407
+ (0, import_react7.useLayoutEffect)(() => {
1835
1408
  if (!active) {
1836
1409
  setTargets((current) => current.length === 0 ? current : []);
1837
1410
  return;
@@ -1935,7 +1508,10 @@ function useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys, active = tru
1935
1508
  requestFullScanRef.current = requestFullScan;
1936
1509
  const observer = new MutationObserver((records) => {
1937
1510
  for (const record of records) {
1938
- if (record.removedNodes.length > 0) pruneDisconnected = true;
1511
+ if (record.removedNodes.length > 0) {
1512
+ pruneDisconnected = true;
1513
+ collectRows(record.target);
1514
+ }
1939
1515
  record.addedNodes.forEach(collectRows);
1940
1516
  }
1941
1517
  if (pendingChatRows.size > 0 || pendingTrajectoryRows.size > 0 || pruneDisconnected) schedule();
@@ -1951,22 +1527,22 @@ function useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys, active = tru
1951
1527
  }
1952
1528
  for (const host of document.querySelectorAll("[data-dshmore-message-actions]")) host.remove();
1953
1529
  };
1954
- }, [active]);
1955
- (0, import_react8.useLayoutEffect)(() => {
1530
+ }, [active, props.sessionId]);
1531
+ (0, import_react7.useLayoutEffect)(() => {
1956
1532
  requestFullScanRef.current?.();
1957
1533
  }, [surface, hiddenSeqs, hiddenTrajectoryKeys]);
1958
1534
  return targets;
1959
1535
  }
1960
1536
 
1961
1537
  // src/client/index.tsx
1962
- var import_jsx_runtime8 = require("react/jsx-runtime");
1538
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1963
1539
  var inject = ["slots", "sessions", "workspaces", "settingsScope"];
1964
1540
  function MessageActionPatchController({ patch, props, targets, enabled }) {
1965
1541
  const actions = patch.useActions(props, enabled);
1966
1542
  const style = { "--dshmore-action-order": patch.order };
1967
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1968
- enabled && targets.map((target) => (0, import_react_dom2.createPortal)(
1969
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "dshmore-action-contribution", style, children: actions.renderAction(target) }),
1543
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1544
+ enabled && targets.map((target) => (0, import_react_dom.createPortal)(
1545
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "dshmore-action-contribution", style, children: actions.renderAction(target) }),
1970
1546
  target.host,
1971
1547
  `${patch.id}:${target.key}`
1972
1548
  )),
@@ -1974,15 +1550,15 @@ function MessageActionPatchController({ patch, props, targets, enabled }) {
1974
1550
  ] });
1975
1551
  }
1976
1552
  function MessageActionsController(props) {
1977
- const settings = (0, import_react9.useSyncExternalStore)(props.activation.subscribe, props.activation.getSnapshot);
1553
+ const settings = (0, import_react8.useSyncExternalStore)(props.activation.subscribe, props.activation.getSnapshot);
1978
1554
  const projected = props.useProjection(MESSAGE_VISIBILITY_PROJECTION_KEY);
1979
- const hiddenSeqs = (0, import_react9.useMemo)(() => new Set(projected?.deletedSeqs ?? []), [projected]);
1980
- const hiddenTrajectoryKeys = (0, import_react9.useMemo)(() => new Set(projected?.hiddenTrajectoryKeys ?? []), [projected]);
1555
+ const hiddenSeqs = (0, import_react8.useMemo)(() => new Set(projected?.deletedSeqs ?? []), [projected]);
1556
+ const hiddenTrajectoryKeys = (0, import_react8.useMemo)(() => new Set(projected?.hiddenTrajectoryKeys ?? []), [projected]);
1981
1557
  const messageActionsEnabled = MESSAGE_ACTION_PATCHES.some((patch) => settings[patch.id]);
1982
1558
  const targets = useMessageTargets(props, hiddenSeqs, hiddenTrajectoryKeys, messageActionsEnabled);
1983
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1984
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("style", { children: styles }),
1985
- MESSAGE_ACTION_PATCHES.map((patch) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1559
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1560
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("style", { children: styles }),
1561
+ MESSAGE_ACTION_PATCHES.map((patch) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1986
1562
  MessageActionPatchController,
1987
1563
  {
1988
1564
  patch,
@@ -2004,9 +1580,8 @@ function apply(ctx) {
2004
1580
  ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
2005
1581
  name: "conversation.session.header.utilities",
2006
1582
  id: `${PLUGIN_NAME}-message-actions`,
2007
- order: 90,
2008
- registrant: PLUGIN_NAME
2009
- }, (props) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MessageActionsController, { ...props, ctx, activation })));
1583
+ order: 90
1584
+ }, (props) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MessageActionsController, { ...props, ctx, activation })));
2010
1585
  for (const patch of CLIENT_PATCHES) patch.install(ctx, activation);
2011
1586
  }
2012
1587
  return module.exports; } });