dsh-rewind-plugin 0.17.0 → 0.17.1

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/lib/client.js CHANGED
@@ -36,7 +36,8 @@ module.exports = __toCommonJS(index_exports);
36
36
 
37
37
  // src/client/hidden.ts
38
38
  var ENGINE_COMMAND = "rewind-plugin";
39
- var ENGINE_COMMANDS = /* @__PURE__ */ new Set([ENGINE_COMMAND, "rewind"]);
39
+ var LEGACY_ENGINE_COMMAND = "rewind";
40
+ var ENGINE_COMMANDS = /* @__PURE__ */ new Set([ENGINE_COMMAND, LEGACY_ENGINE_COMMAND]);
40
41
  function resolveChatWatch(resolveView, sessionId, cb) {
41
42
  const view = resolveView(sessionId);
42
43
  return view?.subscribe?.(cb) ?? (() => {
@@ -167,7 +168,10 @@ function addWithdrawnTurnChrome(snap, cuts, hidden) {
167
168
  hidden.add(chrome.anchorSeq);
168
169
  }
169
170
  }
171
+ var hiddenSeqsMemo = /* @__PURE__ */ new WeakMap();
170
172
  function hiddenSeqsOf(snap) {
173
+ const memo = hiddenSeqsMemo.get(snap);
174
+ if (memo !== void 0) return new Set(memo);
171
175
  const hidden = /* @__PURE__ */ new Set();
172
176
  const spans = [];
173
177
  for (const key of snap.order) {
@@ -198,6 +202,7 @@ function hiddenSeqsOf(snap) {
198
202
  addWithdrawnTurnChrome(snap, cuts, hidden);
199
203
  } catch {
200
204
  }
205
+ hiddenSeqsMemo.set(snap, new Set(hidden));
201
206
  return hidden;
202
207
  }
203
208
 
@@ -1062,6 +1067,13 @@ function showHint(text) {
1062
1067
  var COMPOSER_EDITABLE_SELECTOR = "[data-composer-input]";
1063
1068
  var USER_SEAT_SELECTOR = '[data-chat-flow-kind="user"][data-chat-anchor-key], [data-chat-flow-kind="steering"][data-chat-anchor-key]';
1064
1069
  var CHAT_SEAT_SELECTOR = "[data-chat-anchor-key]";
1070
+ var IGNORED_PORTAL_SCOPE = [
1071
+ "[data-composer-seat]",
1072
+ "[data-composer-input]",
1073
+ "[data-sidebar-right-panel]",
1074
+ "[data-sidebar-chat]",
1075
+ "[data-plugin-panel]"
1076
+ ].join(", ");
1065
1077
  var GROUP_SHELL_SELECTOR = '[data-chat-anchor-key^="group:"]';
1066
1078
  function resolveSeatAnchorSeq(element, chat) {
1067
1079
  if (chat === void 0) return void 0;
@@ -1214,12 +1226,14 @@ function RewindPortals({ sessionId, sessionOf, chatOf, isMainViewSession, watchC
1214
1226
  const chat = chatOf(session);
1215
1227
  const hiddenSeqs = chat === void 0 ? /* @__PURE__ */ new Set() : hiddenSeqsOf(chat);
1216
1228
  for (const element of hidden.current) if (!element.isConnected) hidden.current.delete(element);
1217
- hideWithdrawnSeats(
1218
- chat,
1219
- chat === void 0 ? [] : document.querySelectorAll(CHAT_SEAT_SELECTOR),
1220
- hiddenSeqs,
1221
- hidden.current
1222
- );
1229
+ if (hiddenSeqs.size > 0 || hidden.current.size > 0) {
1230
+ hideWithdrawnSeats(
1231
+ chat,
1232
+ chat === void 0 ? [] : document.querySelectorAll(CHAT_SEAT_SELECTOR),
1233
+ hiddenSeqs,
1234
+ hidden.current
1235
+ );
1236
+ }
1223
1237
  const durable = collectDurableTargets(snapshot, chat, hiddenSeqs);
1224
1238
  const next = [...durable, ...collectPendingTargets(snapshot, steeringItemsOf(inboxOf(session)?.["next-step"]))];
1225
1239
  setTargets((current) => sameTargets(current, next) ? current : next);
@@ -1245,17 +1259,34 @@ function RewindPortals({ sessionId, sessionOf, chatOf, isMainViewSession, watchC
1245
1259
  }
1246
1260
  };
1247
1261
  refresh();
1248
- const observer = new MutationObserver(queueRefresh);
1262
+ const unsubscribeChat = watchChat(sessionId, queueRefresh);
1263
+ const observer = new MutationObserver((records) => {
1264
+ for (const record of records) {
1265
+ if (record.type === "attributes") {
1266
+ const target = record.target;
1267
+ if (target instanceof HTMLElement && hidden.current.has(target) && target.style.display !== "none") {
1268
+ queueRefresh();
1269
+ return;
1270
+ }
1271
+ continue;
1272
+ }
1273
+ const node = record.target instanceof Element ? record.target : record.target.parentElement;
1274
+ if (node !== null && node.closest(IGNORED_PORTAL_SCOPE) !== null) continue;
1275
+ queueRefresh();
1276
+ return;
1277
+ }
1278
+ });
1249
1279
  observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ["style"] });
1250
1280
  return () => {
1251
1281
  active = false;
1252
1282
  observer.disconnect();
1283
+ unsubscribeChat();
1253
1284
  showHiddenSeats(hidden.current);
1254
1285
  unsubscribeInbox?.();
1255
1286
  unsubscribeInbox = void 0;
1256
1287
  inboxOwner = void 0;
1257
1288
  };
1258
- }, [sessionId, sessionOf]);
1289
+ }, [sessionId, sessionOf, watchChat]);
1259
1290
  return targets.map((target) => (0, import_react_dom.createPortal)(
1260
1291
  target.kind === "pending" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1261
1292
  RetractButton,
@@ -1385,9 +1416,9 @@ function createRewindBridge(deps) {
1385
1416
  }
1386
1417
 
1387
1418
  // src/client/build-info.ts
1388
- var PLUGIN_VERSION = true ? "0.17.0" : "dev";
1419
+ var PLUGIN_VERSION = true ? "0.17.1" : "dev";
1389
1420
  var PLUGIN_PACKAGE = "dsh-rewind-plugin";
1390
- var BUILD_HASH = true ? "bae5081f" : "dev";
1421
+ var BUILD_HASH = true ? "0d33ec37" : "dev";
1391
1422
 
1392
1423
  // src/client/locales.ts
1393
1424
  var zh = {
@@ -11,11 +11,9 @@ import type { CommandNode } from '@deepseek-ai/dsh-client-ui-conversation/client
11
11
  * `ENGINE_COMMAND` in `src/index.ts` — the two bundles share no module.
12
12
  */
13
13
  export declare const ENGINE_COMMAND = "rewind-plugin";
14
- /**
15
- * Recognized engine command names. `rewind` is what sessions written before the
16
- * engine channel existed contain, so keeping it preserves their withdrawn
17
- * spans (forward compatibility only: an older client cannot know the new name).
18
- */
14
+ /** Legacy engine command name. */
15
+ export declare const LEGACY_ENGINE_COMMAND = "rewind";
16
+ /** Engine command names the client recognizes. */
19
17
  export declare const ENGINE_COMMANDS: ReadonlySet<string>;
20
18
  /** A chat-snapshot view node as the hiding / composer-refill logic reads it.
21
19
  * The harness's view node shape plus the `anchorSeq` the chat snapshot carries
@@ -315,13 +315,14 @@ interface RewindPortalsProps extends RewindBridgeDeps {
315
315
  /**
316
316
  * Session-scoped portal bridge: renders the ↶ button of every user message
317
317
  * row of the session the harness mounts it for. The refresh is coalesced
318
- * (one pass per mutation batch via queueMicrotask) and diffed (setState is
318
+ * (one pass per microtask window via queueMicrotask) and diffed (setState is
319
319
  * skipped when the target set is unchanged), so the plugin never runs a
320
320
  * synchronous full-transcript scan inside a commit microtask.
321
321
  *
322
- * The scan is driven by both channels its targets are made of: the DOM (rows and
323
- * their actions containers) and the session's `inbox` projection (the pending
324
- * occurrences — see `syncInboxSubscription`).
322
+ * The refresh is driven by the three inputs its targets are made of: the chat
323
+ * snapshot (`hiddenSeqsOf`), the session's `inbox` projection (the pending
324
+ * occurrences — see `syncInboxSubscription`), and the DOM (row/actions mounts,
325
+ * via the scoped observer).
325
326
  */
326
327
  export declare function RewindPortals({ sessionId, sessionOf, chatOf, isMainViewSession, watchChat, t, subscribeLocale, setComposerText, restoreImages }: RewindPortalsProps): ReactNode;
327
328
  /** The current composer draft: the contenteditable `textContent`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-rewind-plugin",
3
- "version": "0.17.0",
3
+ "version": "0.17.1",
4
4
  "description": "In-window conversation rewind with workspace file restore · 同窗口内对话回退并可还原工作区文件",
5
5
  "icon": "./assets/icon.svg",
6
6
  "keywords": [