dsh-rewind-plugin 0.20.0-alpha.4 → 0.21.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/lib/client.js CHANGED
@@ -171,7 +171,7 @@ function rewindCutsOf(nodes) {
171
171
 
172
172
  // src/client/styles.ts
173
173
  var CLASS = {
174
- button: "dsh-rewind-btn",
174
+ action: "dsh-rewind-action",
175
175
  popover: "dsh-rewind-popover",
176
176
  popoverTitle: "dsh-rewind-popover-title",
177
177
  popoverTarget: "dsh-rewind-popover-target",
@@ -185,18 +185,14 @@ var CLASS = {
185
185
  popoverGhost: "dsh-rewind-popover-ghost",
186
186
  guardHint: "dsh-rewind-guard-hint"
187
187
  };
188
- var REWIND_ICON_SVG = [
189
- '<svg width="15" height="15" viewBox="0 0 16 16" fill="none" aria-hidden="true">',
190
- ' <path d="M6.5 2.5 2.5 6.5l4 4" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>',
191
- ' <path d="M2.5 6.5h7a4 4 0 0 1 4 4v1.5" stroke="currentColor" stroke-width="1.25" stroke-linecap="round"/>',
192
- "</svg>"
193
- ].join("");
194
188
  var STYLE = `
195
- .dsh-rewind-btn {
189
+ /* The official action chrome, value for value: the action is Copy's peer in the
190
+ same 28px row, so a different box, glyph scale, or resting tier would read as
191
+ a different kind of control. */
192
+ .dsh-rewind-action {
196
193
  display: inline-flex;
197
194
  align-items: center;
198
195
  justify-content: center;
199
- /* The neighbouring action buttons scale by this delta too. */
200
196
  width: calc(28px + var(--dsh-content-font-delta, 0px));
201
197
  height: calc(28px + var(--dsh-content-font-delta, 0px));
202
198
  padding: 6px;
@@ -206,12 +202,12 @@ var STYLE = `
206
202
  color: var(--dsw-alias-label-tertiary);
207
203
  cursor: pointer;
208
204
  }
209
- .dsh-rewind-btn svg {
205
+ .dsh-rewind-action svg {
210
206
  width: calc(15px + var(--dsh-content-font-delta, 0px));
211
207
  height: calc(15px + var(--dsh-content-font-delta, 0px));
212
208
  }
213
209
  @media (hover: hover) {
214
- .dsh-rewind-btn:hover {
210
+ .dsh-rewind-action:hover {
215
211
  background: var(--dsw-alias-interactive-bg-hover);
216
212
  color: var(--dsw-alias-label-secondary);
217
213
  }
@@ -513,6 +509,13 @@ function moveFocus(root, dir) {
513
509
  const next = index === -1 ? dir === 1 ? 0 : buttons.length - 1 : (index + dir + buttons.length) % buttons.length;
514
510
  buttons[next]?.focus();
515
511
  }
512
+ function moveFocusByKey(root, event) {
513
+ if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return false;
514
+ event.preventDefault();
515
+ event.stopPropagation();
516
+ moveFocus(root, event.key === "ArrowDown" ? 1 : -1);
517
+ return true;
518
+ }
516
519
  function renderImpactStep(root, opts, back, reposition, cached) {
517
520
  const { session, seq, t } = opts;
518
521
  const impact = el("div", CLASS.popoverImpact, t("popover.impact.loading"));
@@ -635,7 +638,6 @@ function mountShell(root, anchor, onKeyDown) {
635
638
  return { position, dispose };
636
639
  }
637
640
  function openRetractPopover(opts) {
638
- closePopover();
639
641
  const { preview, anchor, t, retract, onRetract } = opts;
640
642
  if (retract === void 0 || onRetract === void 0) return;
641
643
  markOpen(anchor);
@@ -643,18 +645,7 @@ function openRetractPopover(opts) {
643
645
  root.setAttribute("role", "dialog");
644
646
  root.setAttribute("aria-label", t("popover.retract.title"));
645
647
  const onKeyDown = (event) => {
646
- if (event.key === "ArrowDown") {
647
- event.preventDefault();
648
- event.stopPropagation();
649
- moveFocus(root, 1);
650
- return;
651
- }
652
- if (event.key === "ArrowUp") {
653
- event.preventDefault();
654
- event.stopPropagation();
655
- moveFocus(root, -1);
656
- return;
657
- }
648
+ if (moveFocusByKey(root, event)) return;
658
649
  if (event.key === "Escape") {
659
650
  event.preventDefault();
660
651
  event.stopPropagation();
@@ -666,7 +657,7 @@ function openRetractPopover(opts) {
666
657
  const confirm = document.createElement("button");
667
658
  confirm.type = "button";
668
659
  confirm.className = CLASS.popoverPrimary;
669
- confirm.textContent = t("popover.retract.confirm");
660
+ confirm.textContent = t("popover.confirm");
670
661
  confirm.addEventListener("click", () => {
671
662
  closePopover();
672
663
  onRetract();
@@ -741,18 +732,7 @@ function openPopover(opts) {
741
732
  renderImpactStep(root, durableOpts, renderModes, reposition, impactOutcome);
742
733
  };
743
734
  const onKeyDown = (event) => {
744
- if (event.key === "ArrowDown") {
745
- event.preventDefault();
746
- event.stopPropagation();
747
- moveFocus(root, 1);
748
- return;
749
- }
750
- if (event.key === "ArrowUp") {
751
- event.preventDefault();
752
- event.stopPropagation();
753
- moveFocus(root, -1);
754
- return;
755
- }
735
+ if (moveFocusByKey(root, event)) return;
756
736
  if (event.key === "Escape") {
757
737
  event.preventDefault();
758
738
  event.stopPropagation();
@@ -1067,13 +1047,142 @@ function changed(input) {
1067
1047
  return input.changedTurns === void 0 ? {} : { changedTurns: input.changedTurns };
1068
1048
  }
1069
1049
 
1070
- // src/client/portals.tsx
1050
+ // src/client/mount.tsx
1071
1051
  var import_react = require("react");
1072
- var import_react_dom = require("react-dom");
1052
+ var import_jsx_runtime = require("react/jsx-runtime");
1053
+ var CHAT_NODE_SLOT = "conversation.chat.node";
1054
+ var ROW_KEYS = /* @__PURE__ */ new Set(["user", "steering"]);
1055
+ var TICK_KEY = "dsh-rewind:mount";
1056
+ function rendererOf(component) {
1057
+ if (typeof component === "function") return component;
1058
+ if (component === null || typeof component !== "object") return void 0;
1059
+ const type = component.type;
1060
+ return typeof type === "function" ? type : void 0;
1061
+ }
1062
+ function targetOf(props) {
1063
+ const sessionId = props["sessionId"];
1064
+ const data = props["node"]?.data;
1065
+ if (typeof sessionId !== "string" || typeof data?.seq !== "number" || typeof data.time !== "number") return void 0;
1066
+ return { sessionId, seq: data.seq, time: data.time };
1067
+ }
1068
+ function rowWithAction(official, render) {
1069
+ return function RowWithAction(props) {
1070
+ const row = official(props);
1071
+ const target = targetOf(props);
1072
+ if (target === void 0 || !(0, import_react.isValidElement)(row)) return row;
1073
+ const actions = row.props.actions;
1074
+ if (typeof actions !== "function") return row;
1075
+ const buildActions = actions;
1076
+ const withAction = (text) => {
1077
+ const actionsRow = buildActions(text);
1078
+ if (!(0, import_react.isValidElement)(actionsRow)) return actionsRow;
1079
+ try {
1080
+ const element = render({ ...target, text });
1081
+ if (element === null || element === void 0) return actionsRow;
1082
+ const existing = actionsRow.props.extraActions;
1083
+ return (0, import_react.cloneElement)(actionsRow, {
1084
+ extraActions: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.Fragment, { children: [
1085
+ existing,
1086
+ element
1087
+ ] })
1088
+ });
1089
+ } catch {
1090
+ return actionsRow;
1091
+ }
1092
+ };
1093
+ return (0, import_react.cloneElement)(row, { actions: withAction });
1094
+ };
1095
+ }
1096
+ function installRowAction({ slots, render }) {
1097
+ if (typeof slots.entries !== "function") return () => {
1098
+ };
1099
+ const readEntries = (key) => slots.entries(key);
1100
+ const taken = /* @__PURE__ */ new Map();
1101
+ const takeOver = (entry) => {
1102
+ if (taken.has(entry)) return false;
1103
+ const official = rendererOf(entry.component);
1104
+ if (official === void 0) return false;
1105
+ const replacement = rowWithAction(official, render);
1106
+ taken.set(entry, { original: entry.component, replacement });
1107
+ entry.component = replacement;
1108
+ return true;
1109
+ };
1110
+ const tick = () => {
1111
+ if (typeof slots.register !== "function") return;
1112
+ try {
1113
+ slots.register({ name: CHAT_NODE_SLOT, key: TICK_KEY }, () => null)();
1114
+ } catch {
1115
+ }
1116
+ };
1117
+ const sync = () => {
1118
+ let changed2 = false;
1119
+ for (const entry of readEntries(CHAT_NODE_SLOT)) {
1120
+ const key = entry.options.key;
1121
+ if (key === void 0 || !ROW_KEYS.has(key)) continue;
1122
+ changed2 = takeOver(entry) || changed2;
1123
+ }
1124
+ if (changed2) tick();
1125
+ };
1126
+ sync();
1127
+ const unsubscribe = slots.subscribe?.(CHAT_NODE_SLOT, sync);
1128
+ return () => {
1129
+ unsubscribe?.();
1130
+ for (const [entry, record] of taken) {
1131
+ if (entry.component === record.replacement) entry.component = record.original;
1132
+ }
1133
+ taken.clear();
1134
+ };
1135
+ }
1136
+
1137
+ // src/client/action.tsx
1073
1138
  var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
1139
+ var import_jsx_runtime2 = require("react/jsx-runtime");
1140
+ var ICON = /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "15", height: "15", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
1141
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M6.5 2.5 2.5 6.5l4 4", stroke: "currentColor", strokeWidth: "1.25", strokeLinecap: "round", strokeLinejoin: "round" }),
1142
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2.5 6.5h7a4 4 0 0 1 4 4v1.5", stroke: "currentColor", strokeWidth: "1.25", strokeLinecap: "round" })
1143
+ ] });
1144
+ function RewindActionButton({ t, titleKey, ariaKey, pending = false, onOpen }) {
1145
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives.Tooltip, { label: t(titleKey), side: "bottom", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1146
+ "button",
1147
+ {
1148
+ type: "button",
1149
+ className: CLASS.action,
1150
+ "aria-label": t(ariaKey),
1151
+ "aria-haspopup": "dialog",
1152
+ "data-pending-retract": pending || void 0,
1153
+ onClick: (event) => {
1154
+ event.stopPropagation();
1155
+ onOpen(event.currentTarget);
1156
+ },
1157
+ children: ICON
1158
+ }
1159
+ ) });
1160
+ }
1161
+ function rewindActionRenderer(t, eligible, open) {
1162
+ return (row) => eligible(row.sessionId) ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1163
+ RewindActionButton,
1164
+ {
1165
+ t,
1166
+ titleKey: "button.title",
1167
+ ariaKey: "button.aria",
1168
+ onOpen: (anchor) => open(row, anchor)
1169
+ }
1170
+ ) : null;
1171
+ }
1172
+
1173
+ // src/client/pending-mount.tsx
1174
+ var import_react2 = require("react");
1175
+ var import_react_dom = require("react-dom");
1074
1176
 
1075
1177
  // src/client/pending.ts
1076
- var QUEUE_PREVIEW_CHARS = 200;
1178
+ var INBOX_PROJECTION = "inbox";
1179
+ function inboxOf(session) {
1180
+ try {
1181
+ return session.projections.faceOf(INBOX_PROJECTION).getSnapshot();
1182
+ } catch {
1183
+ return void 0;
1184
+ }
1185
+ }
1077
1186
  function textOf(content) {
1078
1187
  const texts = content.filter((block) => block.type === "text");
1079
1188
  if (texts.length === 0) return null;
@@ -1082,10 +1191,6 @@ function textOf(content) {
1082
1191
  function attachmentsCountOf(content) {
1083
1192
  return content.filter((block) => block.type !== "text").length;
1084
1193
  }
1085
- function previewOf(text) {
1086
- const chars = Array.from((text ?? "").replace(/\s+/g, " ").trim());
1087
- return chars.length > QUEUE_PREVIEW_CHARS ? `${chars.slice(0, QUEUE_PREVIEW_CHARS).join("")}\u2026` : chars.join("");
1088
- }
1089
1194
  function steeringItemsOf(nextStep) {
1090
1195
  return (nextStep ?? []).filter((item) => item.source?.kind === "user").map((item) => {
1091
1196
  const text = textOf(item.content);
@@ -1093,23 +1198,18 @@ function steeringItemsOf(nextStep) {
1093
1198
  id: item.id,
1094
1199
  text,
1095
1200
  attachments: attachmentsCountOf(item.content),
1096
- images: imageRefsOf(item.content),
1097
- preview: previewOf(text)
1201
+ images: imageRefsOf(item.content)
1098
1202
  };
1099
1203
  });
1100
1204
  }
1101
1205
  function matchPendingRows(rows, steering) {
1102
- const matched = [];
1103
- for (let i = 0; i < rows.length; i++) {
1104
- const row = rows[i];
1105
- const item = steering[i];
1106
- matched.push(item !== void 0 && matches2(item, row) ? item.id : null);
1107
- }
1108
- return matched;
1206
+ return rows.map((row, index) => {
1207
+ const item = steering[index];
1208
+ return item !== void 0 && matches2(item, row) ? item.id : null;
1209
+ });
1109
1210
  }
1110
1211
  function matches2(item, row) {
1111
- if (item.attachments > 0) return row.attachments === item.attachments;
1112
- return row.text === (item.text ?? "");
1212
+ return item.attachments > 0 ? row.attachments === item.attachments : row.text === (item.text ?? "");
1113
1213
  }
1114
1214
  function retractSpan(steering, targetId) {
1115
1215
  const index = steering.findIndex((item) => item.id === targetId);
@@ -1117,8 +1217,8 @@ function retractSpan(steering, targetId) {
1117
1217
  return steering.slice(index).map((item) => item.id);
1118
1218
  }
1119
1219
 
1120
- // src/client/portals.tsx
1121
- var import_jsx_runtime = require("react/jsx-runtime");
1220
+ // src/client/rewind-flow.ts
1221
+ var COMPOSER_EDITABLE_SELECTOR = "[data-composer-input]";
1122
1222
  function fillComposerEditable(text) {
1123
1223
  const editable = document.querySelector(COMPOSER_EDITABLE_SELECTOR);
1124
1224
  if (editable === null) return false;
@@ -1158,11 +1258,6 @@ function writeComposer(text, facade) {
1158
1258
  }
1159
1259
  return fillComposer(text);
1160
1260
  }
1161
- function userNodeOf(chat, key) {
1162
- const node = chat?.nodes.get(key);
1163
- if (node === void 0 || node.kind !== "user" && node.kind !== "steering") return void 0;
1164
- return node.data;
1165
- }
1166
1261
  async function runRewindAndFill(session, seq, mode, isMainViewSession, chatOf, watchChat, setComposerText, restoreImages) {
1167
1262
  const known = knownCommandSeqs(session, chatOf, (node) => isExecutedRewindCommand(node, seq));
1168
1263
  let result;
@@ -1246,229 +1341,16 @@ function showHint(text) {
1246
1341
  }
1247
1342
  window.setTimeout(() => hint.remove(), 3200);
1248
1343
  }
1249
- var COMPOSER_EDITABLE_SELECTOR = "[data-composer-input]";
1250
- var USER_SEAT_SELECTOR = '[data-chat-flow-kind="user"][data-chat-anchor-key], [data-chat-flow-kind="steering"][data-chat-anchor-key]';
1251
- var IGNORED_PORTAL_SCOPE = [
1252
- "[data-composer-seat]",
1253
- "[data-composer-input]",
1254
- "[data-sidebar-right-panel]",
1255
- "[data-sidebar-chat]",
1256
- "[data-plugin-panel]"
1257
- ].join(", ");
1258
- var PENDING_SEAT_SELECTOR = "[data-pending-steering]";
1259
- var MESSAGE_ATTACHMENT_SELECTOR = "[data-message-attachments]";
1260
- function actionsContainerOf(row) {
1261
- const buttons = Array.from(row?.querySelectorAll("button") ?? []);
1262
- const lastButton = buttons.filter((button) => !button.classList.contains(CLASS.button)).at(-1);
1263
- const structural = lastButton?.parentElement;
1264
- if (structural instanceof HTMLElement && structural.querySelector("button") !== null) return structural;
1265
- return void 0;
1266
- }
1267
- function collectTargets(chat) {
1268
- const rows = /* @__PURE__ */ new Map();
1269
- for (const element of document.querySelectorAll(USER_SEAT_SELECTOR)) {
1270
- const key = element.dataset.chatAnchorKey;
1271
- if (key !== void 0) rows.set(key, element);
1272
- }
1273
- const targets = [];
1274
- for (const key of chat.order) {
1275
- const node = chat.nodes.get(key);
1276
- if (node === void 0 || node.kind !== "user" && node.kind !== "steering") continue;
1277
- const user = node.data;
1278
- const row = rows.get(key);
1279
- const actions = actionsContainerOf(row);
1280
- if (actions === void 0) continue;
1281
- targets.push({ kind: "durable", key, container: actions, seq: user.seq, time: user.time, preview: messagePreviewOf(user) });
1282
- }
1283
- return targets;
1284
- }
1285
- function isRewindInertSession(snapshot) {
1286
- return snapshot.subagent !== null && snapshot.subagent !== void 0;
1287
- }
1288
- function collectDurableTargets(snapshot, chat) {
1289
- if (chat === void 0 || isRewindInertSession(snapshot)) return [];
1290
- return collectTargets(chat);
1291
- }
1292
- function inboxOf(session) {
1293
- return session.projections.faceOf(INBOX_PROJECTION).getSnapshot();
1294
- }
1295
- var INBOX_PROJECTION = "inbox";
1296
- function bubbleTextOf(row) {
1297
- const clone = row.cloneNode(true);
1298
- clone.lastElementChild?.remove();
1299
- const originals = row.querySelectorAll(MESSAGE_ATTACHMENT_SELECTOR);
1300
- const copies = clone.querySelectorAll(MESSAGE_ATTACHMENT_SELECTOR);
1301
- for (let index = 0; index < copies.length; index++) copies[index]?.remove();
1302
- return { text: clone.textContent ?? "", attachments: originals.length };
1303
- }
1304
- function collectPendingTargets(snapshot, steering) {
1305
- if (snapshot.subagent !== null) return [];
1306
- if (steering.length === 0) return [];
1307
- const rows = Array.from(document.querySelectorAll(PENDING_SEAT_SELECTOR));
1308
- const matched = matchPendingRows(rows.map(bubbleTextOf), steering);
1309
- const targets = [];
1310
- for (let i = 0; i < matched.length; i++) {
1311
- const itemId = matched[i];
1312
- if (itemId === null) continue;
1313
- const row = rows[i];
1314
- if (row === void 0) continue;
1315
- const actions = actionsContainerOf(row);
1316
- if (actions === void 0) continue;
1317
- const item = steering[i];
1318
- targets.push({
1319
- kind: "pending",
1320
- key: `pending:${itemId}`,
1321
- container: actions,
1322
- itemId,
1323
- text: item.text,
1324
- preview: item.preview
1325
- });
1326
- }
1327
- return targets;
1328
- }
1329
- function sameTargets(left, right) {
1330
- return left.length === right.length && left.every((target, index) => {
1331
- const other = right[index];
1332
- if (other === void 0 || target.key !== other.key || target.container !== other.container) return false;
1333
- if (target.kind === "durable") return other.kind === "durable" && target.seq === other.seq;
1334
- return other.kind === "pending" && target.itemId === other.itemId;
1335
- });
1336
- }
1337
- function RewindPortals({ sessionId, sessionOf, chatOf, isMainViewSession, watchChat, t, subscribeLocale, setComposerText, restoreImages }) {
1338
- const [targets, setTargets] = (0, import_react.useState)([]);
1339
- const [, forceRender] = (0, import_react.useReducer)((count) => count + 1, 0);
1340
- (0, import_react.useEffect)(() => subscribeLocale(() => {
1341
- forceRender();
1342
- }), [subscribeLocale]);
1343
- (0, import_react.useLayoutEffect)(() => {
1344
- let active = true;
1345
- let queued = false;
1346
- let unsubscribeInbox;
1347
- let inboxOwner;
1348
- const refresh = () => {
1349
- if (!active) return;
1350
- const session = sessionOf(sessionId);
1351
- syncInboxSubscription(session);
1352
- if (session === void 0) {
1353
- setTargets([]);
1354
- return;
1355
- }
1356
- const snapshot = session.getSnapshot();
1357
- const chat = chatOf(session);
1358
- const durable = collectDurableTargets(snapshot, chat);
1359
- const next = [...durable, ...collectPendingTargets(snapshot, steeringItemsOf(inboxOf(session)?.["next-step"]))];
1360
- setTargets((current) => sameTargets(current, next) ? current : next);
1361
- };
1362
- const queueRefresh = () => {
1363
- if (queued || !active) return;
1364
- queued = true;
1365
- queueMicrotask(() => {
1366
- queued = false;
1367
- refresh();
1368
- });
1369
- };
1370
- const syncInboxSubscription = (session) => {
1371
- if (session === inboxOwner) return;
1372
- unsubscribeInbox?.();
1373
- unsubscribeInbox = void 0;
1374
- inboxOwner = session;
1375
- if (session === void 0) return;
1376
- try {
1377
- unsubscribeInbox = session.projections.faceOf(INBOX_PROJECTION).subscribe(queueRefresh);
1378
- } catch (error) {
1379
- rewindLog.warn("portals", "inbox projection subscribe failed", error);
1380
- }
1381
- };
1382
- refresh();
1383
- const unsubscribeChat = watchChat(sessionId, queueRefresh);
1384
- const observer = new MutationObserver((records) => {
1385
- for (const record of records) {
1386
- const node = record.target instanceof Element ? record.target : record.target.parentElement;
1387
- if (node !== null && node.closest(IGNORED_PORTAL_SCOPE) !== null) continue;
1388
- queueRefresh();
1389
- return;
1390
- }
1391
- });
1392
- observer.observe(document.body, { childList: true, subtree: true });
1393
- return () => {
1394
- active = false;
1395
- observer.disconnect();
1396
- unsubscribeChat();
1397
- unsubscribeInbox?.();
1398
- unsubscribeInbox = void 0;
1399
- inboxOwner = void 0;
1400
- };
1401
- }, [sessionId, sessionOf, watchChat]);
1402
- return targets.map((target) => (0, import_react_dom.createPortal)(
1403
- target.kind === "pending" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1404
- RetractButton,
1405
- {
1406
- target,
1407
- sessionId,
1408
- sessionOf,
1409
- setComposerText,
1410
- restoreImages,
1411
- t
1412
- },
1413
- target.key
1414
- ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1415
- RewindButton,
1416
- {
1417
- target,
1418
- sessionId,
1419
- sessionOf,
1420
- chatOf,
1421
- watchChat,
1422
- isMainViewSession,
1423
- setComposerText,
1424
- restoreImages,
1425
- t
1426
- },
1427
- target.key
1428
- ),
1429
- target.container,
1430
- target.key
1431
- ));
1432
- }
1433
- function RewindButton({ target, sessionId, sessionOf, chatOf, watchChat, isMainViewSession, setComposerText, restoreImages, t }) {
1434
- const onClick = (event) => {
1435
- event.stopPropagation();
1436
- const session = sessionOf(sessionId);
1437
- if (session === void 0) {
1438
- rewindLog.warn("portals", "rewind button clicked with no session binding");
1439
- return;
1440
- }
1441
- const node = userNodeOf(chatOf(session), target.key);
1442
- if (node === void 0) return;
1443
- openPopover({
1444
- session,
1445
- seq: node.seq,
1446
- time: node.time,
1447
- preview: messagePreviewOf(node),
1448
- anchor: event.currentTarget,
1449
- t,
1450
- onRewind: (mode) => {
1451
- void runRewindAndFill(session, node.seq, mode, isMainViewSession, chatOf, watchChat, setComposerText, restoreImages);
1452
- }
1453
- });
1454
- };
1455
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tooltip, { label: t("button.title"), side: "bottom", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1456
- "button",
1457
- {
1458
- type: "button",
1459
- className: CLASS.button,
1460
- "aria-label": t("button.aria"),
1461
- "aria-haspopup": "dialog",
1462
- onClick,
1463
- dangerouslySetInnerHTML: { __html: REWIND_ICON_SVG }
1464
- }
1465
- ) });
1466
- }
1467
1344
  function composerText() {
1468
1345
  const surface = composerSurface();
1469
1346
  if (surface === null) return "";
1470
1347
  return surface.textContent ?? "";
1471
1348
  }
1349
+ function isRewindInertSession(snapshot) {
1350
+ return snapshot.subagent !== null && snapshot.subagent !== void 0;
1351
+ }
1352
+
1353
+ // src/client/retract.ts
1472
1354
  async function retractPending(session, itemId, text, setComposerText, restoreImages) {
1473
1355
  const steering = steeringItemsOf(inboxOf(session)?.["next-step"]);
1474
1356
  const images = steering.find((item) => item.id === itemId)?.images ?? [];
@@ -1497,37 +1379,177 @@ async function retractPending(session, itemId, text, setComposerText, restoreIma
1497
1379
  setComposerText(session.sessionId, text);
1498
1380
  }
1499
1381
  }
1500
- function RetractButton({ target, sessionId, sessionOf, setComposerText, restoreImages, t }) {
1501
- const onClick = (event) => {
1502
- event.stopPropagation();
1503
- const session = sessionOf(sessionId);
1504
- if (session === void 0) return;
1382
+
1383
+ // src/client/pending-mount.tsx
1384
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1385
+ var PENDING_SEAT_SELECTOR = "[data-pending-steering]";
1386
+ var MESSAGE_ATTACHMENT_SELECTOR = "[data-message-attachments]";
1387
+ var SESSION_CONTENT_SELECTOR = "[data-conversation-session]";
1388
+ var NO_TARGETS = [];
1389
+ function bubbleTextOf(row) {
1390
+ const clone = row.cloneNode(true);
1391
+ clone.lastElementChild?.remove();
1392
+ const attachments = row.querySelectorAll(MESSAGE_ATTACHMENT_SELECTOR).length;
1393
+ for (const block of clone.querySelectorAll(MESSAGE_ATTACHMENT_SELECTOR)) block.remove();
1394
+ return { text: clone.textContent ?? "", attachments };
1395
+ }
1396
+ function actionsContainerOf(row) {
1397
+ const buttons = [...row.querySelectorAll("button")].filter((button) => button.dataset["pendingRetract"] === void 0);
1398
+ const structural = buttons.at(-1)?.parentElement;
1399
+ return structural instanceof HTMLElement && structural.querySelector("button") !== null ? structural : void 0;
1400
+ }
1401
+ function collectTargets(roots, steering) {
1402
+ const targets = [];
1403
+ for (const root of roots) {
1404
+ const rows = [...root.querySelectorAll(PENDING_SEAT_SELECTOR)];
1405
+ const matched = matchPendingRows(rows.map(bubbleTextOf), steering);
1406
+ for (const [index, row] of rows.entries()) {
1407
+ const itemId = matched[index];
1408
+ const item = steering[index];
1409
+ const container = actionsContainerOf(row);
1410
+ if (itemId === null || itemId === void 0 || item === void 0 || container === void 0) continue;
1411
+ targets.push({ container, itemId, text: item.text });
1412
+ }
1413
+ }
1414
+ return targets;
1415
+ }
1416
+ function sameTargets(left, right) {
1417
+ return left.length === right.length && left.every((target, index) => {
1418
+ const other = right[index];
1419
+ return other !== void 0 && target.container === other.container && target.itemId === other.itemId;
1420
+ });
1421
+ }
1422
+ function contentRootsOf(sessionId) {
1423
+ return [...document.querySelectorAll(SESSION_CONTENT_SELECTOR)].filter((element) => element.dataset["conversationSession"] === sessionId);
1424
+ }
1425
+ function involvesPending(record) {
1426
+ const target = record.target;
1427
+ if (target instanceof Element && target.closest(PENDING_SEAT_SELECTOR) !== null) return true;
1428
+ for (const node of [...record.addedNodes, ...record.removedNodes]) {
1429
+ if (node instanceof Element && (node.matches(PENDING_SEAT_SELECTOR) || node.querySelector(PENDING_SEAT_SELECTOR) !== null)) {
1430
+ return true;
1431
+ }
1432
+ }
1433
+ return false;
1434
+ }
1435
+ function resolveSession(deps, sessionId) {
1436
+ try {
1437
+ return deps.sessionOf(sessionId);
1438
+ } catch {
1439
+ return void 0;
1440
+ }
1441
+ }
1442
+ function inboxFaceOf(session) {
1443
+ try {
1444
+ return session.projections.faceOf("inbox");
1445
+ } catch {
1446
+ return void 0;
1447
+ }
1448
+ }
1449
+ function RetractButton({ target, sessionId, deps }) {
1450
+ const onOpen = (anchor) => {
1451
+ const session = resolveSession(deps, sessionId);
1452
+ if (session === void 0) {
1453
+ rewindLog.warn("retract", "pending retract clicked with no session binding");
1454
+ return;
1455
+ }
1505
1456
  openPopover({
1506
1457
  session,
1507
- preview: target.preview,
1508
- anchor: event.currentTarget,
1509
- t,
1458
+ preview: target.text ?? "",
1459
+ anchor,
1460
+ t: deps.t,
1510
1461
  retract: { itemId: target.itemId, text: target.text },
1511
1462
  onRetract: () => {
1512
- void retractPending(session, target.itemId, target.text, setComposerText, restoreImages);
1463
+ void retractPending(session, target.itemId, target.text, deps.setComposerText, deps.restoreImages);
1513
1464
  }
1514
1465
  });
1515
1466
  };
1516
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tooltip, { label: t("button.retract.title"), side: "bottom", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1517
- "button",
1467
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1468
+ RewindActionButton,
1518
1469
  {
1519
- type: "button",
1520
- className: CLASS.button,
1521
- "aria-label": t("button.retract.aria"),
1522
- "aria-haspopup": "dialog",
1523
- onClick,
1524
- dangerouslySetInnerHTML: { __html: REWIND_ICON_SVG }
1470
+ t: deps.t,
1471
+ titleKey: "button.title",
1472
+ ariaKey: "button.retract.aria",
1473
+ pending: true,
1474
+ onOpen
1525
1475
  }
1526
- ) });
1476
+ );
1527
1477
  }
1528
- function createRewindBridge(deps) {
1529
- return function RewindBridge({ sessionId }) {
1530
- return (0, import_react.createElement)(RewindPortals, { sessionId, ...deps });
1478
+ function PendingRetractLayer({ sessionId, deps }) {
1479
+ const [targets, setTargets] = (0, import_react2.useState)(NO_TARGETS);
1480
+ const [, forceRender] = (0, import_react2.useReducer)((count) => count + 1, 0);
1481
+ (0, import_react2.useLayoutEffect)(() => deps.subscribeLocale(() => {
1482
+ forceRender();
1483
+ }), [deps]);
1484
+ (0, import_react2.useLayoutEffect)(() => {
1485
+ let stopped = false;
1486
+ let queued = false;
1487
+ let observer = null;
1488
+ let observed = [];
1489
+ const stopObserver = () => {
1490
+ observer?.disconnect();
1491
+ observer = null;
1492
+ observed = [];
1493
+ };
1494
+ const observe = (roots) => {
1495
+ if (observer !== null && observed.length === roots.length && observed.every((root, index) => root === roots[index])) return;
1496
+ stopObserver();
1497
+ observed = roots;
1498
+ observer = new MutationObserver((records) => {
1499
+ if (stopped) return;
1500
+ if (observed.some((root) => !root.isConnected)) {
1501
+ refresh();
1502
+ return;
1503
+ }
1504
+ if (records.some(involvesPending)) schedule();
1505
+ });
1506
+ for (const root of observed) observer.observe(root, { childList: true, subtree: true });
1507
+ };
1508
+ const refresh = () => {
1509
+ if (stopped) return;
1510
+ const session2 = resolveSession(deps, sessionId);
1511
+ const steering = session2 === void 0 ? [] : steeringItemsOf(inboxOf(session2)?.["next-step"]);
1512
+ if (session2 === void 0 || steering.length === 0 || isRewindInertSession(session2.getSnapshot())) {
1513
+ setTargets(NO_TARGETS);
1514
+ stopObserver();
1515
+ return;
1516
+ }
1517
+ const roots = contentRootsOf(sessionId);
1518
+ if (roots.length === 0) {
1519
+ setTargets(NO_TARGETS);
1520
+ stopObserver();
1521
+ return;
1522
+ }
1523
+ observe(roots);
1524
+ const next = collectTargets(roots, steering);
1525
+ setTargets((current) => sameTargets(current, next) ? current : next);
1526
+ };
1527
+ const schedule = () => {
1528
+ if (queued || stopped) return;
1529
+ queued = true;
1530
+ queueMicrotask(() => {
1531
+ queued = false;
1532
+ refresh();
1533
+ });
1534
+ };
1535
+ refresh();
1536
+ const session = resolveSession(deps, sessionId);
1537
+ const unsubscribe = session === void 0 ? void 0 : inboxFaceOf(session)?.subscribe?.(schedule);
1538
+ return () => {
1539
+ stopped = true;
1540
+ unsubscribe?.();
1541
+ stopObserver();
1542
+ };
1543
+ }, [sessionId, deps]);
1544
+ return targets.map((target) => (0, import_react_dom.createPortal)(
1545
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RetractButton, { target, sessionId, deps }, target.itemId),
1546
+ target.container,
1547
+ target.itemId
1548
+ ));
1549
+ }
1550
+ function createPendingBridge(deps) {
1551
+ return function PendingBridge({ sessionId }) {
1552
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PendingRetractLayer, { sessionId, deps });
1531
1553
  };
1532
1554
  }
1533
1555
 
@@ -1611,22 +1633,17 @@ async function candidatesOverApi(sessionId) {
1611
1633
  }
1612
1634
 
1613
1635
  // src/client/build-info.ts
1614
- var PLUGIN_VERSION = true ? "0.20.0-alpha.4" : "dev";
1636
+ var PLUGIN_VERSION = true ? "0.21.0" : "dev";
1615
1637
  var PLUGIN_PACKAGE = "dsh-rewind-plugin";
1616
- var BUILD_HASH = true ? "c59b80ee" : "dev";
1638
+ var BUILD_HASH = true ? "5ab4d025" : "dev";
1617
1639
 
1618
1640
  // src/client/locales.ts
1619
1641
  var en = {
1620
- "button.aria": "Rewind to this message",
1621
1642
  "button.title": "Rewind",
1643
+ "button.aria": "Rewind to this message",
1622
1644
  "button.retract.aria": "Rewind to this pending message",
1623
- "button.retract.title": "Rewind",
1624
1645
  "popover.title": "Rewind to this message",
1625
1646
  "popover.noText": "(no text)",
1626
- "popover.retract.title": "Rewind to this pending message",
1627
- "popover.retract.target": "Pending \xB7 {preview}",
1628
- "popover.retract.hint": "Retracts this pending message and the ones after it, without interrupting the run",
1629
- "popover.retract.confirm": "Confirm rewind",
1630
1647
  "popover.chat": "Rewind conversation only",
1631
1648
  "popover.chat.hint": "Context only; workspace untouched",
1632
1649
  "popover.both": "Rewind conversation and files",
@@ -1634,7 +1651,6 @@ var en = {
1634
1651
  "popover.checking": "Checking for file changes\u2026",
1635
1652
  "popover.noChanges": "No restorable file changes",
1636
1653
  "popover.backupDisabled": "Snapshots off; the restore may be incomplete",
1637
- "popover.cancel": "Cancel",
1638
1654
  "popover.impact.loading": "Fetching impact list\u2026",
1639
1655
  "popover.impact.failed": "Could not fetch the impact list: {message}",
1640
1656
  "popover.impact.none": "No tracked file changes after the target; nothing to restore.",
@@ -1642,6 +1658,10 @@ var en = {
1642
1658
  "popover.impact.delete": "Delete {path}",
1643
1659
  "popover.confirm": "Confirm rewind",
1644
1660
  "popover.back": "Back",
1661
+ "popover.retract.title": "Rewind to this pending message",
1662
+ "popover.retract.target": "Pending \xB7 {preview}",
1663
+ "popover.retract.hint": "Retracts this pending message and the ones after it, without interrupting the run",
1664
+ "popover.cancel": "Cancel",
1645
1665
  "backup.title": "Snapshot backups",
1646
1666
  "backup.on": "When on, file snapshots are created automatically for file restore.",
1647
1667
  "backup.off": "When off, no file snapshots are created; file changes made while off cannot be restored.",
@@ -1660,16 +1680,11 @@ var en = {
1660
1680
  "cleanup.unavailable": "This plugin is not loaded, so it cannot be configured right now"
1661
1681
  };
1662
1682
  var zh = {
1663
- "button.aria": "\u56DE\u9000\u5230\u6B64\u6D88\u606F",
1664
1683
  "button.title": "\u56DE\u9000",
1684
+ "button.aria": "\u56DE\u9000\u5230\u6B64\u6D88\u606F",
1665
1685
  "button.retract.aria": "\u56DE\u9000\u5230\u6B64\u63D2\u8BDD\u6D88\u606F",
1666
- "button.retract.title": "\u56DE\u9000",
1667
1686
  "popover.title": "\u56DE\u9000\u5230\u8FD9\u6761\u6D88\u606F",
1668
1687
  "popover.noText": "\uFF08\u65E0\u6587\u672C\uFF09",
1669
- "popover.retract.title": "\u56DE\u9000\u5230\u8FD9\u6761\u63D2\u8BDD\u6D88\u606F",
1670
- "popover.retract.target": "\u63D2\u8BDD\u4E2D \xB7 {preview}",
1671
- "popover.retract.hint": "\u64A4\u56DE\u8FD9\u6761\u53CA\u4E4B\u540E\u7684\u63D2\u8BDD\u6D88\u606F\uFF0C\u4E0D\u6253\u65AD\u5F53\u524D\u751F\u6210",
1672
- "popover.retract.confirm": "\u786E\u8BA4\u56DE\u9000",
1673
1688
  "popover.chat": "\u4EC5\u56DE\u9000\u5BF9\u8BDD",
1674
1689
  "popover.chat.hint": "\u4EC5\u56DE\u9000\u4E0A\u4E0B\u6587\uFF0C\u4E0D\u52A8\u5DE5\u4F5C\u533A\u6587\u4EF6",
1675
1690
  "popover.both": "\u56DE\u9000\u5BF9\u8BDD\u548C\u6587\u4EF6",
@@ -1677,7 +1692,6 @@ var zh = {
1677
1692
  "popover.checking": "\u6B63\u5728\u68C0\u67E5\u6587\u4EF6\u53D8\u66F4\u2026",
1678
1693
  "popover.noChanges": "\u65E0\u53EF\u8FD8\u539F\u7684\u6587\u4EF6\u53D8\u66F4",
1679
1694
  "popover.backupDisabled": "\u5FEB\u7167\u5907\u4EFD\u5DF2\u5173\u95ED\uFF0C\u8FD8\u539F\u53EF\u80FD\u4E0D\u5145\u5206",
1680
- "popover.cancel": "\u53D6\u6D88",
1681
1695
  "popover.impact.loading": "\u6B63\u5728\u83B7\u53D6\u5F71\u54CD\u6E05\u5355\u2026",
1682
1696
  "popover.impact.failed": "\u65E0\u6CD5\u83B7\u53D6\u5F71\u54CD\u6E05\u5355\uFF1A{message}",
1683
1697
  "popover.impact.none": "\u76EE\u6807\u4E4B\u540E\u6CA1\u6709\u8DDF\u8E2A\u5230\u7684\u5199\u7C7B\u53D8\u66F4\uFF0C\u65E0\u9700\u8FD8\u539F\u6587\u4EF6\u3002",
@@ -1685,6 +1699,10 @@ var zh = {
1685
1699
  "popover.impact.delete": "\u5220\u9664 {path}",
1686
1700
  "popover.confirm": "\u786E\u8BA4\u56DE\u9000",
1687
1701
  "popover.back": "\u8FD4\u56DE",
1702
+ "popover.retract.title": "\u56DE\u9000\u5230\u8FD9\u6761\u63D2\u8BDD\u6D88\u606F",
1703
+ "popover.retract.target": "\u63D2\u8BDD\u4E2D \xB7 {preview}",
1704
+ "popover.retract.hint": "\u64A4\u56DE\u8FD9\u6761\u53CA\u4E4B\u540E\u7684\u63D2\u8BDD\u6D88\u606F\uFF0C\u4E0D\u6253\u65AD\u5F53\u524D\u751F\u6210",
1705
+ "popover.cancel": "\u53D6\u6D88",
1688
1706
  "backup.title": "\u5FEB\u7167\u5907\u4EFD",
1689
1707
  "backup.on": "\u5F00\u542F\u540E\u81EA\u52A8\u521B\u5EFA\u6587\u4EF6\u5FEB\u7167\uFF0C\u7528\u4E8E\u6587\u4EF6\u8FD8\u539F\u3002",
1690
1708
  "backup.off": "\u5173\u95ED\u540E\u4E0D\u518D\u521B\u5EFA\u6587\u4EF6\u5FEB\u7167\uFF1B\u5173\u95ED\u671F\u95F4\u7684\u6587\u4EF6\u6539\u52A8\u65E0\u6CD5\u8FD8\u539F\u3002",
@@ -1703,16 +1721,11 @@ var zh = {
1703
1721
  "cleanup.unavailable": "\u8BE5\u63D2\u4EF6\u5F53\u524D\u672A\u52A0\u8F7D\uFF0C\u6682\u65F6\u65E0\u6CD5\u914D\u7F6E"
1704
1722
  };
1705
1723
  var ja = {
1706
- "button.aria": "\u3053\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u307E\u3067\u5DFB\u304D\u623B\u3059",
1707
1724
  "button.title": "\u5DFB\u304D\u623B\u3057",
1725
+ "button.aria": "\u3053\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u307E\u3067\u5DFB\u304D\u623B\u3059",
1708
1726
  "button.retract.aria": "\u3053\u306E\u4FDD\u7559\u4E2D\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u307E\u3067\u5DFB\u304D\u623B\u3059",
1709
- "button.retract.title": "\u5DFB\u304D\u623B\u3057",
1710
1727
  "popover.title": "\u3053\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u307E\u3067\u5DFB\u304D\u623B\u3059",
1711
1728
  "popover.noText": "\uFF08\u30C6\u30AD\u30B9\u30C8\u306A\u3057\uFF09",
1712
- "popover.retract.title": "\u3053\u306E\u4FDD\u7559\u4E2D\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u307E\u3067\u5DFB\u304D\u623B\u3059",
1713
- "popover.retract.target": "\u4FDD\u7559\u4E2D \xB7 {preview}",
1714
- "popover.retract.hint": "\u3053\u306E\u4FDD\u7559\u4E2D\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u3068\u305D\u308C\u4EE5\u964D\u3092\u64A4\u56DE\u3057\u307E\u3059\u3002\u73FE\u5728\u306E\u751F\u6210\u306F\u4E2D\u65AD\u3057\u307E\u305B\u3093",
1715
- "popover.retract.confirm": "\u5DFB\u304D\u623B\u3057\u3092\u5B9F\u884C",
1716
1729
  "popover.chat": "\u4F1A\u8A71\u306E\u307F\u5DFB\u304D\u623B\u3059",
1717
1730
  "popover.chat.hint": "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u306E\u307F\u5BFE\u8C61\u3067\u3001\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u306F\u5909\u66F4\u3057\u307E\u305B\u3093",
1718
1731
  "popover.both": "\u4F1A\u8A71\u3068\u30D5\u30A1\u30A4\u30EB\u3092\u5DFB\u304D\u623B\u3059",
@@ -1720,7 +1733,6 @@ var ja = {
1720
1733
  "popover.checking": "\u30D5\u30A1\u30A4\u30EB\u306E\u5909\u66F4\u3092\u78BA\u8A8D\u4E2D\u2026",
1721
1734
  "popover.noChanges": "\u5FA9\u5143\u3067\u304D\u308B\u30D5\u30A1\u30A4\u30EB\u306E\u5909\u66F4\u306F\u3042\u308A\u307E\u305B\u3093",
1722
1735
  "popover.backupDisabled": "\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u304C\u7121\u52B9\u306A\u305F\u3081\u3001\u5FA9\u5143\u304C\u4E0D\u5B8C\u5168\u306B\u306A\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059",
1723
- "popover.cancel": "\u30AD\u30E3\u30F3\u30BB\u30EB",
1724
1736
  "popover.impact.loading": "\u5F71\u97FF\u30EA\u30B9\u30C8\u3092\u53D6\u5F97\u4E2D\u2026",
1725
1737
  "popover.impact.failed": "\u5F71\u97FF\u30EA\u30B9\u30C8\u3092\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\uFF1A{message}",
1726
1738
  "popover.impact.none": "\u5BFE\u8C61\u3088\u308A\u5F8C\u306B\u8FFD\u8DE1\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB\u5909\u66F4\u306F\u3042\u308A\u307E\u305B\u3093\u3002\u5FA9\u5143\u3059\u308B\u3082\u306E\u306F\u3042\u308A\u307E\u305B\u3093\u3002",
@@ -1728,6 +1740,10 @@ var ja = {
1728
1740
  "popover.impact.delete": "{path} \u3092\u524A\u9664",
1729
1741
  "popover.confirm": "\u5DFB\u304D\u623B\u3057\u3092\u5B9F\u884C",
1730
1742
  "popover.back": "\u623B\u308B",
1743
+ "popover.retract.title": "\u3053\u306E\u4FDD\u7559\u4E2D\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u307E\u3067\u5DFB\u304D\u623B\u3059",
1744
+ "popover.retract.target": "\u4FDD\u7559\u4E2D \xB7 {preview}",
1745
+ "popover.retract.hint": "\u3053\u306E\u4FDD\u7559\u4E2D\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u3068\u305D\u308C\u4EE5\u964D\u3092\u64A4\u56DE\u3057\u307E\u3059\u3002\u73FE\u5728\u306E\u751F\u6210\u306F\u4E2D\u65AD\u3057\u307E\u305B\u3093",
1746
+ "popover.cancel": "\u30AD\u30E3\u30F3\u30BB\u30EB",
1731
1747
  "backup.title": "\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u306E\u30D0\u30C3\u30AF\u30A2\u30C3\u30D7",
1732
1748
  "backup.on": "\u6709\u52B9\u306B\u3059\u308B\u3068\u3001\u30D5\u30A1\u30A4\u30EB\u5FA9\u5143\u7528\u306E\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u304C\u81EA\u52D5\u3067\u4F5C\u6210\u3055\u308C\u307E\u3059\u3002",
1733
1749
  "backup.off": "\u7121\u52B9\u306B\u3059\u308B\u3068\u3001\u30D5\u30A1\u30A4\u30EB\u306E\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u306F\u4F5C\u6210\u3055\u308C\u307E\u305B\u3093\u3002\u7121\u52B9\u306A\u9593\u306E\u30D5\u30A1\u30A4\u30EB\u5909\u66F4\u306F\u5FA9\u5143\u3067\u304D\u307E\u305B\u3093\u3002",
@@ -1746,16 +1762,11 @@ var ja = {
1746
1762
  "cleanup.unavailable": "\u3053\u306E\u30D7\u30E9\u30B0\u30A4\u30F3\u306F\u73FE\u5728\u8AAD\u307F\u8FBC\u307E\u308C\u3066\u3044\u306A\u3044\u305F\u3081\u3001\u8A2D\u5B9A\u3067\u304D\u307E\u305B\u3093"
1747
1763
  };
1748
1764
  var es = {
1749
- "button.aria": "Rebobinar hasta este mensaje",
1750
1765
  "button.title": "Rebobinar",
1766
+ "button.aria": "Rebobinar hasta este mensaje",
1751
1767
  "button.retract.aria": "Rebobinar hasta este mensaje pendiente",
1752
- "button.retract.title": "Rebobinar",
1753
1768
  "popover.title": "Rebobinar hasta este mensaje",
1754
1769
  "popover.noText": "(sin texto)",
1755
- "popover.retract.title": "Rebobinar hasta este mensaje pendiente",
1756
- "popover.retract.target": "Pendiente \xB7 {preview}",
1757
- "popover.retract.hint": "Retira este mensaje pendiente y los siguientes, sin interrumpir la ejecuci\xF3n",
1758
- "popover.retract.confirm": "Confirmar rebobinado",
1759
1770
  "popover.chat": "Rebobinar solo la conversaci\xF3n",
1760
1771
  "popover.chat.hint": "Solo el contexto; el espacio de trabajo no se modifica",
1761
1772
  "popover.both": "Rebobinar conversaci\xF3n y archivos",
@@ -1763,7 +1774,6 @@ var es = {
1763
1774
  "popover.checking": "Comprobando cambios en los archivos\u2026",
1764
1775
  "popover.noChanges": "No hay cambios de archivo restaurables",
1765
1776
  "popover.backupDisabled": "Instant\xE1neas desactivadas; la restauraci\xF3n puede ser incompleta",
1766
- "popover.cancel": "Cancelar",
1767
1777
  "popover.impact.loading": "Obteniendo la lista de impacto\u2026",
1768
1778
  "popover.impact.failed": "No se pudo obtener la lista de impacto: {message}",
1769
1779
  "popover.impact.none": "No hay cambios de archivo rastreados despu\xE9s del destino; no hay nada que restaurar.",
@@ -1771,6 +1781,10 @@ var es = {
1771
1781
  "popover.impact.delete": "Eliminar {path}",
1772
1782
  "popover.confirm": "Confirmar rebobinado",
1773
1783
  "popover.back": "Atr\xE1s",
1784
+ "popover.retract.title": "Rebobinar hasta este mensaje pendiente",
1785
+ "popover.retract.target": "Pendiente \xB7 {preview}",
1786
+ "popover.retract.hint": "Retira este mensaje pendiente y los siguientes, sin interrumpir la ejecuci\xF3n",
1787
+ "popover.cancel": "Cancelar",
1774
1788
  "backup.title": "Copias de seguridad de instant\xE1neas",
1775
1789
  "backup.on": "Si est\xE1 activado, se crean instant\xE1neas de archivos autom\xE1ticamente para la restauraci\xF3n.",
1776
1790
  "backup.off": "Si est\xE1 desactivado, no se crean instant\xE1neas de archivos; los cambios hechos mientras est\xE1 desactivado no se pueden restaurar.",
@@ -1789,16 +1803,11 @@ var es = {
1789
1803
  "cleanup.unavailable": "Este plugin no est\xE1 cargado, por lo que no se puede configurar ahora"
1790
1804
  };
1791
1805
  var fr = {
1792
- "button.aria": "Rembobiner jusqu\u2019\xE0 ce message",
1793
1806
  "button.title": "Rembobiner",
1807
+ "button.aria": "Rembobiner jusqu\u2019\xE0 ce message",
1794
1808
  "button.retract.aria": "Rembobiner jusqu\u2019\xE0 ce message en attente",
1795
- "button.retract.title": "Rembobiner",
1796
1809
  "popover.title": "Rembobiner jusqu\u2019\xE0 ce message",
1797
1810
  "popover.noText": "(sans texte)",
1798
- "popover.retract.title": "Rembobiner jusqu\u2019\xE0 ce message en attente",
1799
- "popover.retract.target": "En attente \xB7 {preview}",
1800
- "popover.retract.hint": "Retire ce message en attente et les suivants, sans interrompre l\u2019ex\xE9cution",
1801
- "popover.retract.confirm": "Confirmer le rembobinage",
1802
1811
  "popover.chat": "Rembobiner la conversation uniquement",
1803
1812
  "popover.chat.hint": "Contexte uniquement ; l\u2019espace de travail reste intact",
1804
1813
  "popover.both": "Rembobiner la conversation et les fichiers",
@@ -1806,7 +1815,6 @@ var fr = {
1806
1815
  "popover.checking": "V\xE9rification des modifications de fichiers\u2026",
1807
1816
  "popover.noChanges": "Aucune modification de fichier restaurable",
1808
1817
  "popover.backupDisabled": "Instantan\xE9s d\xE9sactiv\xE9s ; la restauration peut \xEAtre incompl\xE8te",
1809
- "popover.cancel": "Annuler",
1810
1818
  "popover.impact.loading": "R\xE9cup\xE9ration de la liste d\u2019impact\u2026",
1811
1819
  "popover.impact.failed": "Impossible de r\xE9cup\xE9rer la liste d\u2019impact : {message}",
1812
1820
  "popover.impact.none": "Aucune modification de fichier suivie apr\xE8s la cible ; rien \xE0 restaurer.",
@@ -1814,6 +1822,10 @@ var fr = {
1814
1822
  "popover.impact.delete": "Supprimer {path}",
1815
1823
  "popover.confirm": "Confirmer le rembobinage",
1816
1824
  "popover.back": "Retour",
1825
+ "popover.retract.title": "Rembobiner jusqu\u2019\xE0 ce message en attente",
1826
+ "popover.retract.target": "En attente \xB7 {preview}",
1827
+ "popover.retract.hint": "Retire ce message en attente et les suivants, sans interrompre l\u2019ex\xE9cution",
1828
+ "popover.cancel": "Annuler",
1817
1829
  "backup.title": "Sauvegardes d\u2019instantan\xE9s",
1818
1830
  "backup.on": "Lorsque cette option est activ\xE9e, des instantan\xE9s de fichiers sont cr\xE9\xE9s automatiquement pour la restauration.",
1819
1831
  "backup.off": "Lorsque cette option est d\xE9sactiv\xE9e, aucun instantan\xE9 de fichier n\u2019est cr\xE9\xE9 ; les modifications effectu\xE9es pendant cette p\xE9riode ne peuvent pas \xEAtre restaur\xE9es.",
@@ -1832,16 +1844,11 @@ var fr = {
1832
1844
  "cleanup.unavailable": "Ce plugin n\u2019est pas charg\xE9, il ne peut donc pas \xEAtre configur\xE9 pour le moment"
1833
1845
  };
1834
1846
  var de = {
1835
- "button.aria": "Bis zu dieser Nachricht zur\xFCckspulen",
1836
1847
  "button.title": "Zur\xFCckspulen",
1848
+ "button.aria": "Bis zu dieser Nachricht zur\xFCckspulen",
1837
1849
  "button.retract.aria": "Bis zu dieser ausstehenden Nachricht zur\xFCckspulen",
1838
- "button.retract.title": "Zur\xFCckspulen",
1839
1850
  "popover.title": "Bis zu dieser Nachricht zur\xFCckspulen",
1840
1851
  "popover.noText": "(kein Text)",
1841
- "popover.retract.title": "Bis zu dieser ausstehenden Nachricht zur\xFCckspulen",
1842
- "popover.retract.target": "Ausstehend \xB7 {preview}",
1843
- "popover.retract.hint": "Zieht diese ausstehende Nachricht und alle folgenden zur\xFCck, ohne den Lauf zu unterbrechen",
1844
- "popover.retract.confirm": "Zur\xFCckspulen best\xE4tigen",
1845
1852
  "popover.chat": "Nur Unterhaltung zur\xFCckspulen",
1846
1853
  "popover.chat.hint": "Nur Kontext; Arbeitsbereich bleibt unver\xE4ndert",
1847
1854
  "popover.both": "Unterhaltung und Dateien zur\xFCckspulen",
@@ -1849,7 +1856,6 @@ var de = {
1849
1856
  "popover.checking": "Datei\xE4nderungen werden gepr\xFCft\u2026",
1850
1857
  "popover.noChanges": "Keine wiederherstellbaren Datei\xE4nderungen",
1851
1858
  "popover.backupDisabled": "Snapshots deaktiviert; die Wiederherstellung kann unvollst\xE4ndig sein",
1852
- "popover.cancel": "Abbrechen",
1853
1859
  "popover.impact.loading": "Auswirkungsliste wird abgerufen\u2026",
1854
1860
  "popover.impact.failed": "Auswirkungsliste konnte nicht abgerufen werden: {message}",
1855
1861
  "popover.impact.none": "Keine erfassten Datei\xE4nderungen nach dem Ziel; nichts wiederherzustellen.",
@@ -1857,6 +1863,10 @@ var de = {
1857
1863
  "popover.impact.delete": "{path} l\xF6schen",
1858
1864
  "popover.confirm": "Zur\xFCckspulen best\xE4tigen",
1859
1865
  "popover.back": "Zur\xFCck",
1866
+ "popover.retract.title": "Bis zu dieser ausstehenden Nachricht zur\xFCckspulen",
1867
+ "popover.retract.target": "Ausstehend \xB7 {preview}",
1868
+ "popover.retract.hint": "Zieht diese ausstehende Nachricht und alle folgenden zur\xFCck, ohne den Lauf zu unterbrechen",
1869
+ "popover.cancel": "Abbrechen",
1860
1870
  "backup.title": "Snapshot-Sicherungen",
1861
1871
  "backup.on": "Wenn aktiviert, werden automatisch Datei-Snapshots f\xFCr die Wiederherstellung erstellt.",
1862
1872
  "backup.off": "Wenn deaktiviert, werden keine Datei-Snapshots erstellt; \xC4nderungen aus dieser Zeit k\xF6nnen nicht wiederhergestellt werden.",
@@ -1875,16 +1885,11 @@ var de = {
1875
1885
  "cleanup.unavailable": "Dieses Plugin ist nicht geladen und kann daher gerade nicht konfiguriert werden"
1876
1886
  };
1877
1887
  var ru = {
1878
- "button.aria": "\u041E\u0442\u043A\u0430\u0442\u0438\u0442\u044C \u043A \u044D\u0442\u043E\u043C\u0443 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044E",
1879
1888
  "button.title": "\u041E\u0442\u043A\u0430\u0442\u0438\u0442\u044C",
1889
+ "button.aria": "\u041E\u0442\u043A\u0430\u0442\u0438\u0442\u044C \u043A \u044D\u0442\u043E\u043C\u0443 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044E",
1880
1890
  "button.retract.aria": "\u041E\u0442\u043A\u0430\u0442\u0438\u0442\u044C \u043A \u044D\u0442\u043E\u043C\u0443 \u043E\u0436\u0438\u0434\u0430\u044E\u0449\u0435\u043C\u0443 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044E",
1881
- "button.retract.title": "\u041E\u0442\u043A\u0430\u0442\u0438\u0442\u044C",
1882
1891
  "popover.title": "\u041E\u0442\u043A\u0430\u0442\u0438\u0442\u044C \u043A \u044D\u0442\u043E\u043C\u0443 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044E",
1883
1892
  "popover.noText": "(\u043D\u0435\u0442 \u0442\u0435\u043A\u0441\u0442\u0430)",
1884
- "popover.retract.title": "\u041E\u0442\u043A\u0430\u0442\u0438\u0442\u044C \u043A \u044D\u0442\u043E\u043C\u0443 \u043E\u0436\u0438\u0434\u0430\u044E\u0449\u0435\u043C\u0443 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044E",
1885
- "popover.retract.target": "\u041E\u0436\u0438\u0434\u0430\u0435\u0442 \xB7 {preview}",
1886
- "popover.retract.hint": "\u041E\u0442\u0437\u044B\u0432\u0430\u0435\u0442 \u044D\u0442\u043E \u043E\u0436\u0438\u0434\u0430\u044E\u0449\u0435\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0438 \u043F\u043E\u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0435, \u043D\u0435 \u043F\u0440\u0435\u0440\u044B\u0432\u0430\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u0435",
1887
- "popover.retract.confirm": "\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C \u043E\u0442\u043A\u0430\u0442",
1888
1893
  "popover.chat": "\u041E\u0442\u043A\u0430\u0442\u0438\u0442\u044C \u0442\u043E\u043B\u044C\u043A\u043E \u0431\u0435\u0441\u0435\u0434\u0443",
1889
1894
  "popover.chat.hint": "\u0422\u043E\u043B\u044C\u043A\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442; \u0440\u0430\u0431\u043E\u0447\u0430\u044F \u043E\u0431\u043B\u0430\u0441\u0442\u044C \u043D\u0435 \u0438\u0437\u043C\u0435\u043D\u044F\u0435\u0442\u0441\u044F",
1890
1895
  "popover.both": "\u041E\u0442\u043A\u0430\u0442\u0438\u0442\u044C \u0431\u0435\u0441\u0435\u0434\u0443 \u0438 \u0444\u0430\u0439\u043B\u044B",
@@ -1892,7 +1897,6 @@ var ru = {
1892
1897
  "popover.checking": "\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439 \u0444\u0430\u0439\u043B\u043E\u0432\u2026",
1893
1898
  "popover.noChanges": "\u041D\u0435\u0442 \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u043C\u044B\u0445 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439 \u0444\u0430\u0439\u043B\u043E\u0432",
1894
1899
  "popover.backupDisabled": "\u0421\u043D\u0438\u043C\u043A\u0438 \u043E\u0442\u043A\u043B\u044E\u0447\u0435\u043D\u044B; \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043D\u0435\u043F\u043E\u043B\u043D\u044B\u043C",
1895
- "popover.cancel": "\u041E\u0442\u043C\u0435\u043D\u0430",
1896
1900
  "popover.impact.loading": "\u041F\u043E\u043B\u0443\u0447\u0435\u043D\u0438\u0435 \u0441\u043F\u0438\u0441\u043A\u0430 \u0432\u043B\u0438\u044F\u043D\u0438\u044F\u2026",
1897
1901
  "popover.impact.failed": "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u0441\u043F\u0438\u0441\u043E\u043A \u0432\u043B\u0438\u044F\u043D\u0438\u044F: {message}",
1898
1902
  "popover.impact.none": "\u041F\u043E\u0441\u043B\u0435 \u0446\u0435\u043B\u0438 \u043D\u0435\u0442 \u043E\u0442\u0441\u043B\u0435\u0436\u0438\u0432\u0430\u0435\u043C\u044B\u0445 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439 \u0444\u0430\u0439\u043B\u043E\u0432; \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E.",
@@ -1900,6 +1904,10 @@ var ru = {
1900
1904
  "popover.impact.delete": "\u0423\u0434\u0430\u043B\u0438\u0442\u044C {path}",
1901
1905
  "popover.confirm": "\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C \u043E\u0442\u043A\u0430\u0442",
1902
1906
  "popover.back": "\u041D\u0430\u0437\u0430\u0434",
1907
+ "popover.retract.title": "\u041E\u0442\u043A\u0430\u0442\u0438\u0442\u044C \u043A \u044D\u0442\u043E\u043C\u0443 \u043E\u0436\u0438\u0434\u0430\u044E\u0449\u0435\u043C\u0443 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044E",
1908
+ "popover.retract.target": "\u041E\u0436\u0438\u0434\u0430\u0435\u0442 \xB7 {preview}",
1909
+ "popover.retract.hint": "\u041E\u0442\u0437\u044B\u0432\u0430\u0435\u0442 \u044D\u0442\u043E \u043E\u0436\u0438\u0434\u0430\u044E\u0449\u0435\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0438 \u043F\u043E\u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0435, \u043D\u0435 \u043F\u0440\u0435\u0440\u044B\u0432\u0430\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u0435",
1910
+ "popover.cancel": "\u041E\u0442\u043C\u0435\u043D\u0430",
1903
1911
  "backup.title": "\u0420\u0435\u0437\u0435\u0440\u0432\u043D\u044B\u0435 \u043A\u043E\u043F\u0438\u0438 \u0441\u043D\u0438\u043C\u043A\u043E\u0432",
1904
1912
  "backup.on": "\u0415\u0441\u043B\u0438 \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u043E, \u0441\u043D\u0438\u043C\u043A\u0438 \u0444\u0430\u0439\u043B\u043E\u0432 \u0441\u043E\u0437\u0434\u0430\u044E\u0442\u0441\u044F \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438 \u0434\u043B\u044F \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F.",
1905
1913
  "backup.off": "\u0415\u0441\u043B\u0438 \u043E\u0442\u043A\u043B\u044E\u0447\u0435\u043D\u043E, \u0441\u043D\u0438\u043C\u043A\u0438 \u0444\u0430\u0439\u043B\u043E\u0432 \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u044E\u0442\u0441\u044F; \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F, \u0441\u0434\u0435\u043B\u0430\u043D\u043D\u044B\u0435 \u0432 \u044D\u0442\u043E \u0432\u0440\u0435\u043C\u044F, \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F.",
@@ -1918,16 +1926,11 @@ var ru = {
1918
1926
  "cleanup.unavailable": "\u042D\u0442\u043E\u0442 \u043F\u043B\u0430\u0433\u0438\u043D \u043D\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u0441\u0435\u0439\u0447\u0430\u0441 \u0435\u0433\u043E \u043D\u0435\u043B\u044C\u0437\u044F \u043D\u0430\u0441\u0442\u0440\u043E\u0438\u0442\u044C"
1919
1927
  };
1920
1928
  var pt = {
1921
- "button.aria": "Rebobinar at\xE9 esta mensagem",
1922
1929
  "button.title": "Rebobinar",
1930
+ "button.aria": "Rebobinar at\xE9 esta mensagem",
1923
1931
  "button.retract.aria": "Rebobinar at\xE9 esta mensagem pendente",
1924
- "button.retract.title": "Rebobinar",
1925
1932
  "popover.title": "Rebobinar at\xE9 esta mensagem",
1926
1933
  "popover.noText": "(sem texto)",
1927
- "popover.retract.title": "Rebobinar at\xE9 esta mensagem pendente",
1928
- "popover.retract.target": "Pendente \xB7 {preview}",
1929
- "popover.retract.hint": "Retira esta mensagem pendente e as seguintes, sem interromper a execu\xE7\xE3o",
1930
- "popover.retract.confirm": "Confirmar rebobinamento",
1931
1934
  "popover.chat": "Rebobinar apenas a conversa",
1932
1935
  "popover.chat.hint": "Apenas o contexto; o espa\xE7o de trabalho n\xE3o \xE9 alterado",
1933
1936
  "popover.both": "Rebobinar conversa e arquivos",
@@ -1935,7 +1938,6 @@ var pt = {
1935
1938
  "popover.checking": "Verificando altera\xE7\xF5es nos arquivos\u2026",
1936
1939
  "popover.noChanges": "Nenhuma altera\xE7\xE3o de arquivo restaur\xE1vel",
1937
1940
  "popover.backupDisabled": "Snapshots desativados; a restaura\xE7\xE3o pode ficar incompleta",
1938
- "popover.cancel": "Cancelar",
1939
1941
  "popover.impact.loading": "Obtendo a lista de impacto\u2026",
1940
1942
  "popover.impact.failed": "N\xE3o foi poss\xEDvel obter a lista de impacto: {message}",
1941
1943
  "popover.impact.none": "Nenhuma altera\xE7\xE3o de arquivo rastreada ap\xF3s o destino; nada a restaurar.",
@@ -1943,6 +1945,10 @@ var pt = {
1943
1945
  "popover.impact.delete": "Excluir {path}",
1944
1946
  "popover.confirm": "Confirmar rebobinamento",
1945
1947
  "popover.back": "Voltar",
1948
+ "popover.retract.title": "Rebobinar at\xE9 esta mensagem pendente",
1949
+ "popover.retract.target": "Pendente \xB7 {preview}",
1950
+ "popover.retract.hint": "Retira esta mensagem pendente e as seguintes, sem interromper a execu\xE7\xE3o",
1951
+ "popover.cancel": "Cancelar",
1946
1952
  "backup.title": "Backups de snapshots",
1947
1953
  "backup.on": "Quando ativado, snapshots de arquivos s\xE3o criados automaticamente para a restaura\xE7\xE3o.",
1948
1954
  "backup.off": "Quando desativado, nenhum snapshot de arquivo \xE9 criado; altera\xE7\xF5es feitas enquanto desativado n\xE3o podem ser restauradas.",
@@ -1964,7 +1970,7 @@ var CLIENT_DICTS = { en, zh, ja, es, fr, de, ru, pt };
1964
1970
 
1965
1971
  // src/client/settings-card.tsx
1966
1972
  var import_dsh_client_ui_primitives2 = require("@deepseek-ai/dsh-client-ui-primitives");
1967
- var import_jsx_runtime2 = require("react/jsx-runtime");
1973
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1968
1974
  var SETTINGS_ENTRY_ID = "dsh-rewind-plugin";
1969
1975
  var DEFAULT_MAX_AGE_DAYS = 30;
1970
1976
  function autoCleanupEnabledField() {
@@ -2013,7 +2019,7 @@ function SettingsCard(props) {
2013
2019
  const disabled = !state.writable || state.saving;
2014
2020
  const checkpointing = state.checkpointingEnabled.text === "true";
2015
2021
  const cleanupEnabled = state.enabled.text === "true";
2016
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2022
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2017
2023
  import_dsh_client_ui_primitives2.SettingsForm,
2018
2024
  {
2019
2025
  labels: formLabels(t),
@@ -2021,10 +2027,10 @@ function SettingsCard(props) {
2021
2027
  onSave: props.save,
2022
2028
  onDiscard: props.discard,
2023
2029
  children: [
2024
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-rewind-settings-section", children: [
2025
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-rewind-settings-toggle-row", children: [
2026
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-rewind-settings-toggle-label", children: t("backup.title") }),
2027
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2030
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dsh-rewind-settings-section", children: [
2031
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dsh-rewind-settings-toggle-row", children: [
2032
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dsh-rewind-settings-toggle-label", children: t("backup.title") }),
2033
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2028
2034
  import_dsh_client_ui_primitives2.Switch,
2029
2035
  {
2030
2036
  checked: checkpointing,
@@ -2036,13 +2042,13 @@ function SettingsCard(props) {
2036
2042
  }
2037
2043
  )
2038
2044
  ] }),
2039
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-rewind-settings-hint", children: t(checkpointing ? "backup.on" : "backup.off") })
2045
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dsh-rewind-settings-hint", children: t(checkpointing ? "backup.on" : "backup.off") })
2040
2046
  ] }),
2041
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-rewind-settings-divider" }),
2042
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-rewind-settings-section", children: [
2043
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-rewind-settings-toggle-row", children: [
2044
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-rewind-settings-toggle-label", children: t("cleanup.auto") }),
2045
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2047
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dsh-rewind-settings-divider" }),
2048
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dsh-rewind-settings-section", children: [
2049
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dsh-rewind-settings-toggle-row", children: [
2050
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dsh-rewind-settings-toggle-label", children: t("cleanup.auto") }),
2051
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2046
2052
  import_dsh_client_ui_primitives2.Switch,
2047
2053
  {
2048
2054
  checked: cleanupEnabled,
@@ -2054,9 +2060,9 @@ function SettingsCard(props) {
2054
2060
  }
2055
2061
  )
2056
2062
  ] }),
2057
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-rewind-settings-hint", children: t(cleanupEnabled ? "cleanup.auto.on" : "cleanup.auto.off") })
2063
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "dsh-rewind-settings-hint", children: t(cleanupEnabled ? "cleanup.auto.on" : "cleanup.auto.off") })
2058
2064
  ] }),
2059
- cleanupEnabled ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2065
+ cleanupEnabled ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2060
2066
  import_dsh_client_ui_primitives2.SettingsValueField,
2061
2067
  {
2062
2068
  id: "dsh-rewind-cleanup-maxage",
@@ -2094,9 +2100,6 @@ function apply(ctx) {
2094
2100
  yield ctx.locale.register(NS2, CLIENT_DICTS);
2095
2101
  const t = ctx.locale.bind(NS2);
2096
2102
  registerPreviewCall(previewOverApi);
2097
- yield () => {
2098
- registerPreviewCall(null);
2099
- };
2100
2103
  const style = document.createElement("style");
2101
2104
  style.dataset.plugin = PLUGIN_PACKAGE;
2102
2105
  style.textContent = STYLE;
@@ -2211,16 +2214,6 @@ function apply(ctx) {
2211
2214
  cb
2212
2215
  );
2213
2216
  const slots = ctx.slots;
2214
- yield slots.inject(HEADER_ACTIONS_SLOT, () => slots.register(
2215
- {
2216
- name: HEADER_ACTIONS_SLOT,
2217
- // A distinct list-entry id keeps the bridge from shadowing any other
2218
- // header action; the entry renders portals only, never header UI.
2219
- id: "dsh-rewind-portals",
2220
- order: 1e3
2221
- },
2222
- createRewindBridge({ sessionOf, chatOf, isMainViewSession, watchChat, setComposerText, restoreImages, t, subscribeLocale })
2223
- ));
2224
2217
  try {
2225
2218
  const { store, form } = settingsForm(
2226
2219
  ctx.configForms.get(SETTINGS_ENTRY_ID)
@@ -2293,10 +2286,47 @@ function apply(ctx) {
2293
2286
  for (const name2 of ["rewind", "undo"]) {
2294
2287
  yield commandUi.decorate({ name: name2, ...rewindPopupSpec });
2295
2288
  }
2289
+ const canRewindIn = (sessionId) => {
2290
+ const face = sessionOf(sessionId);
2291
+ return face !== void 0 && !isRewindInertSession(face.getSnapshot());
2292
+ };
2293
+ const openRewindFor = (row, anchor) => {
2294
+ const face = sessionOf(row.sessionId);
2295
+ if (face === void 0) {
2296
+ rewindLog.warn("action", "rewind action clicked with no session binding");
2297
+ return;
2298
+ }
2299
+ openPopover({
2300
+ session: face,
2301
+ seq: row.seq,
2302
+ time: row.time,
2303
+ preview: row.text,
2304
+ anchor,
2305
+ t,
2306
+ onRewind: (mode) => {
2307
+ void runRewindAndFill(face, row.seq, mode, isMainViewSession, chatOf, watchChat, setComposerText, restoreImages);
2308
+ }
2309
+ });
2310
+ };
2311
+ try {
2312
+ yield installRowAction({
2313
+ slots: ctx.slots,
2314
+ render: rewindActionRenderer(t, canRewindIn, openRewindFor)
2315
+ });
2316
+ } catch (error) {
2317
+ rewindLog.error("action", "rewind action mount failed", error);
2318
+ }
2319
+ try {
2320
+ yield slots.inject(HEADER_ACTIONS_SLOT, () => slots.register(
2321
+ { name: HEADER_ACTIONS_SLOT, id: "dsh-rewind-pending", order: 1e3 },
2322
+ createPendingBridge({ sessionOf, setComposerText, restoreImages, t, subscribeLocale })
2323
+ ));
2324
+ } catch (error) {
2325
+ rewindLog.error("retract", "pending retract mount failed", error);
2326
+ }
2296
2327
  const composerSurface2 = () => document.querySelector(COMPOSER_EDITABLE_SELECTOR2);
2297
2328
  yield () => {
2298
2329
  closePopover();
2299
- style.remove();
2300
2330
  };
2301
2331
  }, "dsh-rewind client lifecycle");
2302
2332
  }