@logictan/dsh-easyrewrite 2.5.4 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -18,7 +18,7 @@
18
18
 
19
19
  Click your own message bubble to edit it in place; hit the recall button beside copy to withdraw it and everything after it. Nothing is ever really changed until you commit — the conversation, the model context, and the session log stay untouched until you press **Confirm** (rewrite) or **Send** (recall).
20
20
 
21
- > Compatible with DeepSeek Harness Web (**2.4.0 requires dsh 0.1.2-rc.1+**; users on dsh 0.1.1-rc.2 and older hosts, please stay on **2.3.1** — the final release of that line, fully functional, no further features). Built on official extension points only — no source patches.
21
+ > Compatible with DeepSeek Harness Web (**v2.6.0 fully supports both dsh 0.1.7-rc.1+ and 0.1.2-rc.1+**; users on dsh 0.1.1-rc.2 and older hosts, please stay on **2.3.1** — the final release of that line, fully functional, no further features). Built on official extension points only — no source patches.
22
22
 
23
23
  ---
24
24
 
package/README.ja.md CHANGED
@@ -18,7 +18,7 @@
18
18
 
19
19
  自分のメッセージバブルをクリックすればその場で編集、コピーキーの隣の撤回キーでそのメッセージと以降の内容をすべて撤回できます。**すべての変更はあなたが本当に確認した後にのみ発生します**——「確定」(編集)または「送信」(撤回)を押すまでは、会話・モデルコンテキスト・セッションログは一切変わりません。
20
20
 
21
- > DeepSeek Harness Web(**2.4.0 は dsh 0.1.2-rc.1+ が必要**。0.1.1-rc.2 以前のホストのユーザーは **2.3.1** に留まってください——このラインの最終版で、機能は完全に動作しますが新機能は追加されません)対応。公式拡張ポイントのみを使用し、DSH ソースコードは変更しません。
21
+ > DeepSeek Harness Web(**v2.6.0 は dsh 0.1.7-rc.1+ および 0.1.2-rc.1+ に両対応**。0.1.1-rc.2 以前のホストのユーザーは **2.3.1** に留まってください——このラインの最終版で、機能は完全に動作しますが新機能は追加されません)対応。公式拡張ポイントのみを使用し、DSH ソースコードは変更しません。
22
22
 
23
23
  ---
24
24
 
package/README.md CHANGED
@@ -12,13 +12,13 @@
12
12
 
13
13
  `#dsh` `#deepseek-harness` `#撤回` `#气泡编辑` `#重编辑` `#版本翻页器` `#i18n` `#多语言`
14
14
 
15
- </div>
16
-
17
- **在 DeepSeek Harness Web 里内联编辑与撤回你自己的消息——惰性、无痕、零丢失。**
15
+ **在 DeepSeek Harness Web 里使用撤回键和气泡编辑功能轻松编辑已发出的消息——轻量、原版风格、惰性、无痕、数据任何情况下0丢失。保护您修改过程中的每一份灵感。**
18
16
 
19
17
  单击自己的消息气泡即可原位编辑;点击复制键旁的撤回键可撤回该消息及其后的全部内容。**一切修改都发生在你真正确认之后**——在按下「确定」(编辑)或「发送」(撤回)之前,对话、模型上下文、会话日志纹丝不动。
20
18
 
21
- > 兼容 DeepSeek Harness Web(**2.4.0 要求 dsh 0.1.2-rc.1+**;0.1.1-rc.2 及更早宿主用户请停留 **2.3.1**——该线最终版,功能完整可用,仅不再追加新功能)。纯官方扩展点实现,不改 DSH 源码。
19
+ > 兼容 DeepSeek Harness Web(**v2.6.0 全面适配 dsh 0.1.7-rc.1+ 与 0.1.2-rc.1+ 双代版本**;0.1.1-rc.2 及更早宿主用户请停留 **2.3.1**——该线最终版,功能完整可用,仅不再追加新功能)。纯官方扩展点实现,不改 DSH 源码。
20
+
21
+ </div>
22
22
 
23
23
  ---
24
24
 
package/lib/client.js CHANGED
@@ -11,6 +11,31 @@ window.__ModuleLoader__.load({
11
11
  var React = require("react");
12
12
  var Primitives = require("@deepseek-ai/dsh-client-ui-primitives");
13
13
 
14
+ /**
15
+ * 取第一个可用的图标导出。
16
+ *
17
+ * dsh 0.1.7 重排了图标导出名:名字里的数字从**尺寸**改成了**字重**
18
+ * (IconCheckOutline16 → IconCheckOutlineRegular;尺寸仍由 size prop 控制,
19
+ * 各字形的默认绘制尺寸没变)。旧名在新版解析为 undefined,而
20
+ * React.createElement(undefined) 会抛 #130 —— 崩的是整个
21
+ * conversation.chat.node 槽条目,不是"少一个图标":撤回按钮会连同气泡一起消失。
22
+ *
23
+ * 所以两种写法都取:先旧名,保住 0.1.5 及以下;再新名,让 0.1.7 起恢复。
24
+ * 都取不到时退回空组件而不是 undefined —— 图标缺失只该少一个 glyph,
25
+ * 不该把整个气泡拖崩。
26
+ */
27
+ function pickIcon() {
28
+ for (var i = 0; i < arguments.length; i++) {
29
+ if (arguments[i]) return arguments[i];
30
+ }
31
+ return function EmptyIcon() { return null; };
32
+ }
33
+
34
+ var IconCheckOutline = pickIcon(Primitives.IconCheckOutline16, Primitives.IconCheckOutlineRegular);
35
+ var IconCopyOutline = pickIcon(Primitives.IconCopyOutline16, Primitives.IconCopyOutlineRegular);
36
+ var IconChevronLeftOutline = pickIcon(Primitives.IconChevronLeftOutline14, Primitives.IconChevronLeftOutlineRegular);
37
+ var IconChevronRightOutline = pickIcon(Primitives.IconChevronRightOutline14, Primitives.IconChevronRightOutlineRegular);
38
+
14
39
  var NS = "dsh-easyrewrite";
15
40
 
16
41
  /** 统一日志:默认静默(仅上报 host 落盘);调试模式(localStorage dsh-easyrewrite:debug=1)时打印控制台。 */
@@ -422,7 +447,7 @@ window.__ModuleLoader__.load({
422
447
 
423
448
  // ---------- dsh 宿主版本比较(v2.4.0) ----------
424
449
  var MIN_DSH_VERSION = "0.1.2-rc.1";
425
- var MAX_TESTED_DSH_VERSION = "0.1.2-rc.1";
450
+ var MAX_TESTED_DSH_VERSION = "0.1.7-rc.2";
426
451
  function dshVerRank(v) {
427
452
  var m = String(v || "").trim().match(/^(\d+)\.(\d+)\.(\d+)(?:-(alpha|rc)\.(\d+))?$/);
428
453
  if (!m) return null;
@@ -560,22 +585,31 @@ window.__ModuleLoader__.load({
560
585
 
561
586
  /** 安全打开会话:多重降级通道,确保可靠切换会话 */
562
587
  function safeOpenSession(targetId, props) {
588
+ if (props && typeof props.openSession === "function") {
589
+ try {
590
+ var r1 = props.openSession(targetId);
591
+ if (r1 !== false) return true;
592
+ } catch (e1) {
593
+ log("warn", "session", "props.openSession 失败,尝试降级通道", { targetId: targetId, err: String(e1 && e1.message ? e1.message : e1) });
594
+ }
595
+ }
563
596
  try {
564
- if (props && typeof props.openSession === "function") {
565
- props.openSession(targetId);
597
+ if (ctxUiWorkspaceRef && typeof ctxUiWorkspaceRef.openSession === "function") {
598
+ ctxUiWorkspaceRef.openSession(targetId);
566
599
  return true;
567
600
  }
601
+ } catch (e2) {
602
+ log("warn", "session", "ctxUiWorkspaceRef.openSession 失败", { targetId: targetId, err: String(e2 && e2.message ? e2.message : e2) });
603
+ }
604
+ try {
568
605
  if (props && props.ctxSessions && typeof props.ctxSessions.open === "function") {
569
606
  props.ctxSessions.open(targetId);
570
607
  return true;
571
608
  }
572
- if (ctxUiWorkspaceRef && typeof ctxUiWorkspaceRef.openSession === "function") {
573
- ctxUiWorkspaceRef.openSession(targetId);
574
- return true;
575
- }
576
- } catch (e) {
577
- log("error", "session", "切换会话异常", { targetId: targetId, err: String(e && e.message ? e.message : e) });
609
+ } catch (e3) {
610
+ log("warn", "session", "ctxSessions.open 失败", { targetId: targetId, err: String(e3 && e3.message ? e3.message : e3) });
578
611
  }
612
+ log("error", "session", "所有切换会话通道均不可用", { targetId: targetId });
579
613
  return false;
580
614
  }
581
615
 
@@ -1506,7 +1540,6 @@ window.__ModuleLoader__.load({
1506
1540
  zh: {
1507
1541
  title: "EasyRewrite",
1508
1542
  subtitle: "简单易用的撤回重编辑",
1509
- cardSummary: "撤回、重编辑与版本历史",
1510
1543
  expand: "展开",
1511
1544
  collapse: "收起",
1512
1545
  rewrite: "气泡框编辑(点击气泡原位修改)",
@@ -1600,7 +1633,6 @@ window.__ModuleLoader__.load({
1600
1633
  en: {
1601
1634
  title: "EasyRewrite",
1602
1635
  subtitle: "Simple & easy recall and re-edit",
1603
- cardSummary: "Recall, re-edit and version history",
1604
1636
  expand: "Expand",
1605
1637
  collapse: "Collapse",
1606
1638
  rewrite: "Bubble edit (click bubble to edit in place)",
@@ -1694,7 +1726,6 @@ window.__ModuleLoader__.load({
1694
1726
  ja: {
1695
1727
  title: "EasyRewrite",
1696
1728
  subtitle: "簡単で使いやすい撤回・再編集",
1697
- cardSummary: "撤回・再編集とバージョン履歴",
1698
1729
  expand: "展開",
1699
1730
  collapse: "折りたたむ",
1700
1731
  rewrite: "バブル編集(クリックでその場編集)",
@@ -1820,9 +1851,17 @@ window.__ModuleLoader__.load({
1820
1851
  /** 设置卡片:注册进 plugins.row.config(设置 → 插件 → 插件配置)。
1821
1852
  * 插件页对每个配置项渲染两次:view==='summary' 取行标题下的一行摘要,view==='page' 取完整表单。 */
1822
1853
  function EasyRewriteSettingsCard(props) {
1854
+ /**
1855
+ * 是否由外部容器提供卡片外壳。
1856
+ *
1857
+ * 0.1.7 的插件页(plugins.item)自己画行外壳、标题取自插槽 label,卡片再画一层头部
1858
+ * 就成了嵌套折叠;旧插槽 settings.plugin.item 没有外壳,卡片必须自画。由挂载方经
1859
+ * inject 面传入,默认 false,保持旧行为。
1860
+ */
1861
+ var embedded = !!(props && props.embedded);
1823
1862
  var L = useUILocaleDict();
1824
- if (props && props.view === "summary") return L.cardSummary;
1825
- var openState = React.useState(false);
1863
+ // embedded 时头部不渲染,但「展开时检查更新」这类 side effect 仍要照跑,故初始即展开。
1864
+ var openState = React.useState(embedded);
1826
1865
  var open = openState[0];
1827
1866
  var setOpen = openState[1];
1828
1867
  // 控件状态(初始化自 localStorage)
@@ -2084,7 +2123,7 @@ window.__ModuleLoader__.load({
2084
2123
  "aria-disabled": disabled || undefined,
2085
2124
  style: Object.assign({}, checkStyle, disabled ? { cursor: "not-allowed" } : null),
2086
2125
  onClick: function (e) { e.stopPropagation(); if (disabled) return; onChange(!value); }
2087
- }, value ? React.createElement(Primitives.IconCheckOutline16, null) : null)
2126
+ }, value ? React.createElement(IconCheckOutline, null) : null)
2088
2127
  );
2089
2128
  }
2090
2129
  // Apple 风格分段控件:灰色药丸长条 + 白色小药丸高亮当前项(滑动过渡,主题自适应)
@@ -2140,22 +2179,13 @@ window.__ModuleLoader__.load({
2140
2179
  );
2141
2180
  }
2142
2181
 
2143
- return React.createElement("li", { style: Object.assign({}, cardStyle, open ? cardOpenStyle : null), "data-dsh-easyrewrite": "settings-card" },
2144
- React.createElement("button", {
2145
- type: "button",
2146
- style: headStyle,
2147
- "aria-expanded": open,
2148
- "aria-label": (open ? L.collapse : L.expand) + ": " + L.title,
2149
- onClick: function () { setOpen(!open); }
2150
- },
2151
- React.createElement("span", { style: headTextStyle },
2152
- React.createElement("span", { style: titleStyle }, L.title),
2153
- React.createElement("span", { style: subStyle }, L.subtitle)
2154
- ),
2155
- React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", style: Object.assign({}, chevronStyle, open ? chevronOpenStyle : null) },
2156
- React.createElement("path", { d: "M9 6l6 6-6 6", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }))
2157
- ),
2158
- open ? React.createElement("div", { style: bodyStyle },
2182
+ // 配置主体:两种挂法共用。
2183
+ //
2184
+ // 0.1.7 起卡片挂进 plugins.item,页面已经给了行外壳(标题来自插槽 label、描述、
2185
+ // 展开由页面管),卡片只该交出主体。自带 <li> + 可点头部会变成页面里多出来的一层
2186
+ // 折叠 —— 列表上一个下拉,点进去还有一个。旧插槽 settings.plugin.item 没有外壳,
2187
+ // 那种挂法仍旧要卡片自画,所以分流在函数末尾按 embedded 做。
2188
+ var cardBody = React.createElement("div", { style: bodyStyle },
2159
2189
  // —— 编辑 ——
2160
2190
  React.createElement("div", { style: sectionStyle },
2161
2191
  React.createElement("span", { style: groupTitleStyle }, L.sectionEdit),
@@ -2384,7 +2414,28 @@ window.__ModuleLoader__.load({
2384
2414
  )
2385
2415
  ),
2386
2416
  React.createElement("span", { style: hintStyle }, L.instant)
2387
- ) : null
2417
+ );
2418
+ // plugins.item(0.1.7 起):页面把同一个组件按 view 渲染两次 —— summary 当列表行与
2419
+ // 详情页的一行描述,page 当配置主体。图标、标题(取自插槽 label)、启用开关与详情页
2420
+ // 外壳都由页面提供,所以这里两者都不画头部。
2421
+ if (embedded) return props.view === "summary" ? L.subtitle : cardBody;
2422
+ // settings.plugin.item(0.1.5 及以下):插槽不提供外壳,卡片自画可点头部。
2423
+ return React.createElement("li", { style: Object.assign({}, cardStyle, open ? cardOpenStyle : null), "data-dsh-easyrewrite": "settings-card" },
2424
+ React.createElement("button", {
2425
+ type: "button",
2426
+ style: headStyle,
2427
+ "aria-expanded": open,
2428
+ "aria-label": (open ? L.collapse : L.expand) + ": " + L.title,
2429
+ onClick: function () { setOpen(!open); }
2430
+ },
2431
+ React.createElement("span", { style: headTextStyle },
2432
+ React.createElement("span", { style: titleStyle }, L.title),
2433
+ React.createElement("span", { style: subStyle }, L.subtitle)
2434
+ ),
2435
+ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", style: Object.assign({}, chevronStyle, open ? chevronOpenStyle : null) },
2436
+ React.createElement("path", { d: "M9 6l6 6-6 6", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }))
2437
+ ),
2438
+ open ? cardBody : null
2388
2439
  );
2389
2440
  }
2390
2441
 
@@ -2396,7 +2447,7 @@ window.__ModuleLoader__.load({
2396
2447
  log("info", "pager", "切换版本", { from: sessionId, to: next, index: nextIndex + 1, count: fam.versions.length });
2397
2448
  // 历史版本都是归档会话(无痕替换副作用),先恢复再打开;恢复失败也照常打开(幂等)
2398
2449
  var doOpen = function () {
2399
- if (typeof props.openSession === "function") props.openSession(next);
2450
+ safeOpenSession(next, props);
2400
2451
  // 等当前会话确认为 next(轮询 current,不猜时间)后,归档家族其余版本——列表只保留目标一个。
2401
2452
  // open 未确认(超时 8s)则放弃归档,绝不误伤任何会话。
2402
2453
  if (cleanupRef.current !== null) { clearInterval(cleanupRef.current); cleanupRef.current = null; }
@@ -2604,7 +2655,7 @@ window.__ModuleLoader__.load({
2604
2655
  disabled: atFirst,
2605
2656
  "aria-label": L.pagerPrev,
2606
2657
  onClick: function () { go(-1); }
2607
- }, React.createElement(Primitives.IconChevronLeftOutline14, null)),
2658
+ }, React.createElement(IconChevronLeftOutline, null)),
2608
2659
  React.createElement("span", { style: { padding: "0 4px", fontSize: "14px", whiteSpace: "nowrap" } }, (index + 1) + "/" + count),
2609
2660
  React.createElement("button", {
2610
2661
  type: "button",
@@ -2613,7 +2664,7 @@ window.__ModuleLoader__.load({
2613
2664
  disabled: atLast,
2614
2665
  "aria-label": L.pagerNext,
2615
2666
  onClick: function () { go(1); }
2616
- }, React.createElement(Primitives.IconChevronRightOutline14, null))
2667
+ }, React.createElement(IconChevronRightOutline, null))
2617
2668
  );
2618
2669
  }
2619
2670
 
@@ -2650,8 +2701,8 @@ window.__ModuleLoader__.load({
2650
2701
  onMouseLeave: function (e) { e.currentTarget.style.background = "transparent"; },
2651
2702
  onClick: function (e) { e.stopPropagation(); copy(); }
2652
2703
  }, copied
2653
- ? React.createElement(Primitives.IconCheckOutline16, { size: 14 })
2654
- : React.createElement(Primitives.IconCopyOutline16, { size: 14 }));
2704
+ ? React.createElement(IconCheckOutline, { size: 14 })
2705
+ : React.createElement(IconCopyOutline, { size: 14 }));
2655
2706
  }
2656
2707
 
2657
2708
  /** clipboard API 不可用时的回退复制。 */
@@ -3344,8 +3395,8 @@ window.__ModuleLoader__.load({
3344
3395
  padding: "8px 14px",
3345
3396
  whiteSpace: "pre-wrap",
3346
3397
  wordBreak: "break-word",
3347
- fontSize: "14px",
3348
- lineHeight: "22px",
3398
+ fontSize: "var(--dsh-content-font-size, 14px)",
3399
+ lineHeight: "calc(22px + var(--dsh-content-font-delta, 0px))",
3349
3400
  color: "var(--dsw-alias-label-primary, inherit)"
3350
3401
  };
3351
3402
  var actionsStyle = { display: "flex", gap: "2px", alignItems: "center" };
@@ -3362,8 +3413,8 @@ window.__ModuleLoader__.load({
3362
3413
  background: "var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,0.10))",
3363
3414
  borderRadius: "14px",
3364
3415
  padding: "8px 14px",
3365
- fontSize: "14px",
3366
- lineHeight: "22px",
3416
+ fontSize: "var(--dsh-content-font-size, 14px)",
3417
+ lineHeight: "calc(22px + var(--dsh-content-font-delta, 0px))",
3367
3418
  cursor: "pointer",
3368
3419
  color: "var(--dsw-alias-label-tertiary)",
3369
3420
  whiteSpace: "pre-wrap",
@@ -3401,6 +3452,7 @@ window.__ModuleLoader__.load({
3401
3452
  showPreview ? origImages : null,
3402
3453
  showPreview ? React.createElement(
3403
3454
  "div", {
3455
+ className: "dsh-easyrewrite-bubble",
3404
3456
  style: grayBubbleStyle,
3405
3457
  title: L.collapse,
3406
3458
  onClick: togglePreview
@@ -3408,6 +3460,7 @@ window.__ModuleLoader__.load({
3408
3460
  text || L.emptyMsg
3409
3461
  ) : React.createElement(
3410
3462
  "div", {
3463
+ className: "dsh-easyrewrite-bubble",
3411
3464
  style: grayBubbleStyle,
3412
3465
  title: L.viewOriginal,
3413
3466
  onClick: togglePreview
@@ -3618,8 +3671,8 @@ window.__ModuleLoader__.load({
3618
3671
  background: "transparent",
3619
3672
  resize: "none",
3620
3673
  font: "inherit",
3621
- fontSize: "14px",
3622
- lineHeight: "22px",
3674
+ fontSize: "var(--dsh-content-font-size, 14px)",
3675
+ lineHeight: "calc(22px + var(--dsh-content-font-delta, 0px))",
3623
3676
  color: "var(--dsw-alias-label-primary)",
3624
3677
  whiteSpace: "pre-wrap",
3625
3678
  wordBreak: "break-word",
@@ -3826,7 +3879,7 @@ window.__ModuleLoader__.load({
3826
3879
  "div", { style: rowStyle, "data-dsh-easyrewrite": "user", "data-time-hover-root": true },
3827
3880
  renderMessageImagesCompat(msgImages, props),
3828
3881
  React.createElement(
3829
- "div", { style: bubbleStyle, onClick: onBubbleClick, title: L.clickEdit },
3882
+ "div", { className: "dsh-easyrewrite-bubble", style: bubbleStyle, onClick: onBubbleClick, title: L.clickEdit },
3830
3883
  text || L.emptyMsg
3831
3884
  ),
3832
3885
  confirming
@@ -3956,7 +4009,6 @@ window.__ModuleLoader__.load({
3956
4009
  var disposers = [];
3957
4010
  var styleTag = injectThemeStyle();
3958
4011
  try { if (typeof ctx.locale === "object" && ctx.locale !== null && typeof ctx.locale.register === "function") ctx.locale.register(NS, {}); } catch (e) { /* ignore */ }
3959
- log("info", "lifecycle", "client half active");
3960
4012
  // 调试 API:撤回条实时调参(调试模式门控:localStorage dsh-easyrewrite:debug=1;set/get/diagnose/export)
3961
4013
  try {
3962
4014
  var wRef = (typeof window !== "undefined") ? window : null;
@@ -4035,7 +4087,42 @@ window.__ModuleLoader__.load({
4035
4087
  } catch (eDbg) { /* ignore */ }
4036
4088
  try { ctxConversationRef = ctx.conversation; } catch (e) { ctxConversationRef = null; }
4037
4089
  try { ctxUiConversationRef = (typeof ctx.get === "function") ? (ctx.get("uiConversation") || null) : null; } catch (eUic) { ctxUiConversationRef = null; }
4038
- try { ctxUiWorkspaceRef = (typeof ctx.get === "function") ? (ctx.get("uiWorkspace") || null) : null; } catch (eUiw) { ctxUiWorkspaceRef = null; }
4090
+ try { ctxUiWorkspaceRef = ctx.uiWorkspace || ((typeof ctx.get === "function") ? (ctx.get("uiWorkspace") || null) : null); } catch (eUiw) { ctxUiWorkspaceRef = null; }
4091
+ function invokeOpenSession(id) {
4092
+ try {
4093
+ if (ctx.uiWorkspace && typeof ctx.uiWorkspace.openSession === "function") {
4094
+ ctx.uiWorkspace.openSession(id);
4095
+ return true;
4096
+ }
4097
+ } catch (e1) {}
4098
+ try {
4099
+ var uiW = (typeof ctx.get === "function") ? ctx.get("uiWorkspace") : null;
4100
+ if (uiW && typeof uiW.openSession === "function") {
4101
+ uiW.openSession(id);
4102
+ return true;
4103
+ }
4104
+ } catch (e2) {}
4105
+ try {
4106
+ if (ctxUiWorkspaceRef && typeof ctxUiWorkspaceRef.openSession === "function") {
4107
+ ctxUiWorkspaceRef.openSession(id);
4108
+ return true;
4109
+ }
4110
+ } catch (e3) {}
4111
+ try {
4112
+ if (ctx.sessions && typeof ctx.sessions.open === "function") {
4113
+ ctx.sessions.open(id);
4114
+ return true;
4115
+ }
4116
+ } catch (e4) {}
4117
+ try {
4118
+ if (ctx.workspaces && typeof ctx.workspaces.openSession === "function") {
4119
+ ctx.workspaces.openSession(id);
4120
+ return true;
4121
+ }
4122
+ } catch (e5) {}
4123
+ log("error", "session", "所有 openSession 途径均不可用", { id: id });
4124
+ return false;
4125
+ }
4039
4126
  // 陈旧版本树键清扫(review #6:lineage 已接管;旧 localStorage 键为死数据,启动时一次清掉)
4040
4127
  try {
4041
4128
  var stale = [];
@@ -4124,7 +4211,7 @@ window.__ModuleLoader__.load({
4124
4211
  }
4125
4212
  } catch (eSh2) { inputShell2 = null; }
4126
4213
  return {
4127
- openSession: function (id) { ctx.sessions.open(id); },
4214
+ openSession: invokeOpenSession,
4128
4215
  ctxWorkspaces: ctx.workspaces,
4129
4216
  ctxSessions: ctx.sessions,
4130
4217
  modelSel: modelSel,
@@ -4167,7 +4254,7 @@ window.__ModuleLoader__.load({
4167
4254
  } catch (eShell) { inputShell = null; }
4168
4255
  return {
4169
4256
  sessionId: sessionId,
4170
- openSession: function (id) { ctx.sessions.open(id); },
4257
+ openSession: invokeOpenSession,
4171
4258
  ctxWorkspaces: ctx.workspaces,
4172
4259
  ctxSessions: ctx.sessions,
4173
4260
  updateQueue: function (itemId, action) {
@@ -4203,26 +4290,38 @@ window.__ModuleLoader__.load({
4203
4290
  }, RecallBanner);
4204
4291
  });
4205
4292
  if (typeof d3 === "function") disposers.push(d3);
4206
- // 设置卡片(设置 → 插件 → 插件配置):plugins.row.config 按 `<bundle 包名>#<行 id>` 分派。
4207
- // 行 id 等于宿主半边 export const name;bundle 包名随安装形态而变——经本仓库聚合包安装时
4208
- // 是聚合包,独立安装时是本包。两个 key 都注册:未安装的那个 bundle 不会被页面分派。
4209
- var ROW_ID = "dsh-easyrewrite";
4210
- var BUNDLE_NAMES = ["@logictan/dsh-plugins-all", "@logictan/dsh-easyrewrite"];
4211
- var d4s = BUNDLE_NAMES.map(function (bundle) {
4212
- return ctx.slots.inject("plugins.row.config", function () {
4213
- return ctx.slots.register({
4214
- name: "plugins.row.config",
4215
- key: bundle + "#" + ROW_ID,
4293
+ // 设置卡片。两种挂载方式各注册一次:
4294
+ //
4295
+ // ≤0.1.5 keyed 插槽 settings.plugin.item(按命名空间分发),插槽不给外壳,
4296
+ // 卡片自画可点头部。
4297
+ // 0.1.7+ 改用插件页。第三方 bundle 的配置属于它自己那一行,走
4298
+ // plugins.row.config,key 为 `<包名>#<行 id>`;页面因此会给该行一个
4299
+ // Configure 控件,打开它自己的配置页。
4300
+ //
4301
+ // 行 id 等于宿主半边 export const name;bundle 包名随安装形态而变——经本仓库聚合包
4302
+ // 安装时是聚合包,独立安装时是本包。两个 bundle 名都注册,页面只会分派已安装的那个。
4303
+ function registerSettingsCard(slotName, kind, embedded) {
4304
+ return ctx.slots.inject(slotName, function () {
4305
+ return ctx.slots.register(Object.assign({
4306
+ name: slotName,
4216
4307
  inject: function () {
4217
4308
  return {
4218
- openSession: function (id) { ctx.sessions.open(id); },
4219
- ctxSessions: ctx.sessions
4309
+ openSession: invokeOpenSession,
4310
+ ctxSessions: ctx.sessions,
4311
+ // 页面提供行外壳的挂法,卡片只交出配置主体(见组件里的 embedded)。
4312
+ embedded: !!embedded
4220
4313
  };
4221
4314
  }
4222
- }, EasyRewriteSettingsCard);
4315
+ }, kind), EasyRewriteSettingsCard);
4223
4316
  });
4317
+ }
4318
+ var ROW_ID = "dsh-easyrewrite";
4319
+ var BUNDLE_NAMES = ["@logictan/dsh-plugins-all", "@logictan/dsh-easyrewrite"];
4320
+ var d4 = [registerSettingsCard("settings.plugin.item", { key: ROW_ID })];
4321
+ BUNDLE_NAMES.forEach(function (bundle) {
4322
+ d4.push(registerSettingsCard("plugins.row.config", { key: bundle + "#" + ROW_ID }, true));
4224
4323
  });
4225
- d4s.forEach(function (d) { if (typeof d === "function") disposers.push(d); });
4324
+ d4.forEach(function (d) { if (typeof d === "function") disposers.push(d); });
4226
4325
  // 版本翻页器 < X >:assistant 消息操作区(最后回答底部)
4227
4326
  var d5 = ctx.slots.inject("conversation.chat.assistant-actions", function () {
4228
4327
  return ctx.slots.register({
@@ -4231,7 +4330,7 @@ window.__ModuleLoader__.load({
4231
4330
  order: 10,
4232
4331
  inject: function () {
4233
4332
  return {
4234
- openSession: function (id) { ctx.sessions.open(id); },
4333
+ openSession: invokeOpenSession,
4235
4334
  ctxSessions: ctx.sessions,
4236
4335
  archiveSession: function (id) {
4237
4336
  // review #5:官方 archiveSession 幂等且 sessionKnown 接受归档会话(dsh-workspace L424/L439)——
@@ -4250,7 +4349,18 @@ window.__ModuleLoader__.load({
4250
4349
  }
4251
4350
  },
4252
4351
  currentSessionId: function () {
4253
- try { var s = ctx.sessions.list.getSnapshot(); return s ? s.current : null; } catch (e) { return null; }
4352
+ try {
4353
+ var s = ctx.sessions.list.getSnapshot();
4354
+ if (!s) return null;
4355
+ if (s.current) return s.current;
4356
+ if (s.byId) {
4357
+ var found = Object.values(s.byId).find(function (r) {
4358
+ return r && r.retainedBy && (r.retainedBy.mainView > 0);
4359
+ });
4360
+ if (found && found.id) return found.id;
4361
+ }
4362
+ return null;
4363
+ } catch (e) { return null; }
4254
4364
  },
4255
4365
  restoreSession: function (id) {
4256
4366
  return fetch("/bubble/unarchive", {
@@ -4268,6 +4378,9 @@ window.__ModuleLoader__.load({
4268
4378
  }, VersionPager);
4269
4379
  });
4270
4380
  if (typeof d5 === "function") disposers.push(d5);
4381
+ // 这行必须在全部 slots.register 之后:旧位置在注册之前,槽位渲染崩溃时它照样输出,
4382
+ // 让人误以为「日志正常 = UI 正常」。现在它只证明注册跑完,不证明槽位渲染成功。
4383
+ log("info", "lifecycle", "client half registered", { disposers: disposers.length });
4271
4384
  return function () {
4272
4385
  for (var i = 0; i < disposers.length; i++) disposers[i]();
4273
4386
  if (styleTag !== null) styleTag.remove();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logictan/dsh-easyrewrite",
3
- "version": "2.5.4",
3
+ "version": "2.6.0",
4
4
  "description": "DSH Web内目前最无感的消息撤回、重编辑插件,原版体验,兼容性强,功能简单可开关,设置丰富,现代化轻量ui框架。The most seamless message recall & re-edit plugin for DSH Web — native experience, strong compatibility, simple toggles, rich settings, modern lightweight UI. DSH Web で最もシームレスなメッセージ撤回・再編集プラグイン——ネイティブ体験、高い互換性、シンプルなトグル、充実した設定、モダンで軽量な UI。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -58,7 +58,7 @@
58
58
  ],
59
59
  "license": "MIT",
60
60
  "scripts": {
61
- "test": "node tests/smoke-host.mjs && node tests/test-log-memory.mjs && node tests/test-issue9-anti-double-submit.mjs",
61
+ "test": "node tests/smoke-host.mjs && node tests/test-log-memory.mjs && node tests/test-issue9-anti-double-submit.mjs && node tests/test-issue11-bubble-compatibility.mjs && node tests/test-dsh-0.1.7-pager.mjs",
62
62
  "build": "node build.mjs",
63
63
  "prepare": "node build.mjs",
64
64
  "prepack": "node build.mjs"