dsh-rewind-plugin 0.7.3 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/docs/compat/audit.md +5 -5
- package/docs/release/release.md +3 -0
- package/docs/release/release.zh.md +3 -0
- package/lib/client.js +48 -18
- package/lib/types/client/hidden.d.ts +22 -0
- package/lib/types/client/popover.d.ts +9 -2
- package/lib/types/client/portals.d.ts +10 -3
- package/lib/types/client/styles.d.ts +1 -1
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Conversation rewind for DeepSeek Harness: **rewind the conversation to any earlier user message in one click, in the same window** — no new branch, no window switch, with optional workspace-file restore (full Claude Code `/rewind` semantics).
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/dsh-rewind-plugin)
|
|
6
|
-
[](https://www.npmjs.com/package/dsh-rewind-plugin)
|
|
7
7
|
[](https://github.com/SiriLee/dsh-rewind/actions/workflows/ci.yml)
|
|
8
8
|
|
|
9
9
|
> English | [中文](README.md)
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
DeepSeek Harness 插件:**一键就地回退对话到任意更早的用户消息**——同窗口内完成,不新建分支、不换窗口,可一并还原工作区文件(完整 Claude Code `/rewind` 语义)。
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/dsh-rewind-plugin)
|
|
6
|
-
[](https://www.npmjs.com/package/dsh-rewind-plugin)
|
|
7
7
|
[](https://github.com/SiriLee/dsh-rewind/actions/workflows/ci.yml)
|
|
8
8
|
|
|
9
9
|
> [English](README.en.md) | 中文
|
package/docs/compat/audit.md
CHANGED
|
@@ -22,15 +22,15 @@
|
|
|
22
22
|
>
|
|
23
23
|
> `0.1.2-alpha.1` was never published to npm, so the peer OR-union is declared only from `0.1.2-alpha.2`; the plugin code still supports the `alpha.1`+ client.
|
|
24
24
|
>
|
|
25
|
-
> `0.1.2-alpha.2` … `0.1.2-alpha.
|
|
25
|
+
> `0.1.2-alpha.2` … `0.1.2-alpha.5` verified-compatible. Built/tested on the rc.2
|
|
26
26
|
> baseline (the `latest` dist-tag); the same published build also drives the alpha
|
|
27
|
-
> line, and `alpha.2`/`alpha.3`/`alpha.4` share the `0.1.2` tuple, so the
|
|
28
|
-
> `^0.1.2-alpha.2` peer member covers all
|
|
29
|
-
> primary baseline;
|
|
27
|
+
> line, and `alpha.2`/`alpha.3`/`alpha.4`/`alpha.5` share the `0.1.2` tuple, so the
|
|
28
|
+
> single `^0.1.2-alpha.2` peer member covers all four. `rc.2` (`latest`) remains the
|
|
29
|
+
> primary baseline; five versions verified: rc.2, alpha.2, alpha.3, alpha.4, alpha.5.
|
|
30
30
|
|
|
31
31
|
### Channeled seams (version × channel)
|
|
32
32
|
|
|
33
|
-
| Seam | rc.2 (`0.1.1-rc.2`) | alpha.2 / alpha.3 | alpha.4 |
|
|
33
|
+
| Seam | rc.2 (`0.1.1-rc.2`) | alpha.2 / alpha.3 | alpha.4 / alpha.5 |
|
|
34
34
|
| --- | --- | --- | --- |
|
|
35
35
|
| Host session log (`eventsOf`) | `Session.events` | `Session.events` | `session.snapshotEvents()` |
|
|
36
36
|
| Client chat snapshot (`chatSnapshotOf`) | session-face `chat` field | `uiConversation` `chat` view | `uiConversation` `chat` view |
|
package/docs/release/release.md
CHANGED
|
@@ -61,6 +61,9 @@ uses an OR-union covering every published tuple series
|
|
|
61
61
|
(`0.1.1 → 0.1.2 → 0.2.x`); rc rolling within a tuple (`0.1.1-rc.2 → rc.3`)
|
|
62
62
|
needs nothing. All `@deepseek-ai/*` packages release together;
|
|
63
63
|
`npm view @deepseek-ai/dsh version` is the authoritative signal.
|
|
64
|
+
- **Exception — `@deepseek-ai/dsh-client-runtime`**: it never published a
|
|
65
|
+
`0.1.2-alpha.*` (npm `next` is `0.1.1-rc.2`) and is imported `import type`
|
|
66
|
+
only, so keep it at `^0.1.0-rc.6 || ^0.1.1-rc.2` (no `0.1.2` member).
|
|
64
67
|
- **Published-tuple check (optional)**: `node scripts/check-dsh-version.mjs`
|
|
65
68
|
compares the `latest` dist-tag version against the tuples the peers cover
|
|
66
69
|
(exit 0 = nothing to do, exit 1 = update). It reads the `latest` tag only; a
|
|
@@ -55,6 +55,9 @@ OR 并集覆盖 DSH 已发布的每个元组系列(如 `^0.1.0-rc.6 || ^0.1.1-
|
|
|
55
55
|
- **何时需要更新**:仅当 DSH 发布新元组(`0.1.1 → 0.1.2 → 0.2.x`)时;
|
|
56
56
|
同元组内 rc 滚动(`0.1.1-rc.2 → rc.3`)无需动作。DSH 所有包同版本发布,
|
|
57
57
|
`npm view @deepseek-ai/dsh version` 即权威信号。
|
|
58
|
+
- **例外 —— `@deepseek-ai/dsh-client-runtime`**:它从未发布 `0.1.2-alpha.*`
|
|
59
|
+
(npm `next` 为 `0.1.1-rc.2`)且仅被 `import type` 引用,保持
|
|
60
|
+
`^0.1.0-rc.6 || ^0.1.1-rc.2` 即可(无 `0.1.2` 元组项)。
|
|
58
61
|
- **已发布元组检查(可选)**:`node scripts/check-dsh-version.mjs` 用 npm `latest`
|
|
59
62
|
dist-tag 版本对比 peer 覆盖的元组(exit 0 无需动作,exit 1 需要)。它**只读
|
|
60
63
|
`latest` tag**;发布在其它 tag(如 `alpha`)的 pre-release 走**手动发布前检查**
|
package/lib/client.js
CHANGED
|
@@ -35,6 +35,13 @@ __export(index_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(index_exports);
|
|
36
36
|
|
|
37
37
|
// src/client/hidden.ts
|
|
38
|
+
function resolveChatWatch(resolveView, resolveFace, sessionId, cb) {
|
|
39
|
+
const view = resolveView(sessionId);
|
|
40
|
+
if (view?.subscribe !== void 0) return view.subscribe(cb);
|
|
41
|
+
const face = resolveFace(sessionId);
|
|
42
|
+
return face?.subscribe(cb) ?? (() => {
|
|
43
|
+
});
|
|
44
|
+
}
|
|
38
45
|
function chatSnapshotOf(face, chatView) {
|
|
39
46
|
const legacy = face?.getSnapshot().chat;
|
|
40
47
|
if (legacy !== void 0) return legacy;
|
|
@@ -321,6 +328,12 @@ var STYLE = `
|
|
|
321
328
|
}
|
|
322
329
|
|
|
323
330
|
/* ---- Snapshot-cleanup settings card (mirrors the harness PluginCard look) ---- */
|
|
331
|
+
/* Dual-channel note: the harness card corner/border drifted across versions \u2014
|
|
332
|
+
rc.2 uses border-radius 12px + 1px solid border-l2, while alpha uses 16px +
|
|
333
|
+
0.5px border-l4 (and adds corner-shape: round). We intentionally keep the
|
|
334
|
+
rc.2 values here for now; if the alpha look is wanted, switch these on the
|
|
335
|
+
host version like the other dual-channel surfaces (see the host-version probe
|
|
336
|
+
in src/client/index.ts). */
|
|
324
337
|
.dsh-rewind-cleanup-card {
|
|
325
338
|
list-style: none;
|
|
326
339
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
@@ -619,7 +632,7 @@ function knownCommandSeqs(session, chatOf, match) {
|
|
|
619
632
|
}
|
|
620
633
|
return known;
|
|
621
634
|
}
|
|
622
|
-
function waitForCommand(session, chatOf, match, timeoutMs = 8e3) {
|
|
635
|
+
function waitForCommand(session, chatOf, match, timeoutMs = 8e3, watch) {
|
|
623
636
|
return new Promise((resolve) => {
|
|
624
637
|
let settled = false;
|
|
625
638
|
const settle = (value) => {
|
|
@@ -635,7 +648,7 @@ function waitForCommand(session, chatOf, match, timeoutMs = 8e3) {
|
|
|
635
648
|
settle({ kind: node.outcome.kind, text: node.outcome.text });
|
|
636
649
|
}
|
|
637
650
|
};
|
|
638
|
-
const unsubscribe = session.subscribe(check);
|
|
651
|
+
const unsubscribe = (watch ?? ((cb) => session.subscribe(cb)))(check);
|
|
639
652
|
const timer = setTimeout(() => settle(null), timeoutMs);
|
|
640
653
|
check();
|
|
641
654
|
});
|
|
@@ -644,11 +657,11 @@ function isPreviewFor(node, seq) {
|
|
|
644
657
|
const args = node.args ?? "";
|
|
645
658
|
return node.name === "rewind" && args.includes("preview") && new RegExp(`(?:^|\\s)@${seq}(?=\\s|$)`).test(args);
|
|
646
659
|
}
|
|
647
|
-
async function previewImpact(session, chatOf, seq) {
|
|
660
|
+
async function previewImpact(session, chatOf, seq, watch) {
|
|
648
661
|
const known = knownCommandSeqs(session, chatOf, (node) => isPreviewFor(node, seq));
|
|
649
662
|
const result = await session.command(`/rewind preview @${seq} both`);
|
|
650
663
|
if (!result.ok || result.value?.matched !== true) return null;
|
|
651
|
-
return waitForCommand(session, chatOf, (node) => isPreviewFor(node, seq) && !known.has(node.seq));
|
|
664
|
+
return waitForCommand(session, chatOf, (node) => isPreviewFor(node, seq) && !known.has(node.seq), 8e3, watch);
|
|
652
665
|
}
|
|
653
666
|
function el(tag2, className, text) {
|
|
654
667
|
const node = document.createElement(tag2);
|
|
@@ -699,7 +712,7 @@ function renderImpactStep(root, opts, back, cached) {
|
|
|
699
712
|
root.replaceChildren(impact, actions);
|
|
700
713
|
focusFirst(root);
|
|
701
714
|
void (async () => {
|
|
702
|
-
const outcome = cached ?? await previewImpact(session, opts.chatOf, seq);
|
|
715
|
+
const outcome = cached ?? await previewImpact(session, opts.chatOf, seq, (cb) => opts.watchChat(session.sessionId, cb));
|
|
703
716
|
if (outcome === null) {
|
|
704
717
|
impact.textContent = t("popover.impact.failed", { message: "preview command failed or timed out" });
|
|
705
718
|
return;
|
|
@@ -821,7 +834,7 @@ function openPopover(opts) {
|
|
|
821
834
|
const { session, seq, time, preview, anchor, t, chatOf } = opts;
|
|
822
835
|
const onRewind = opts.onRewind;
|
|
823
836
|
if (seq === void 0 || time === void 0 || onRewind === void 0) return;
|
|
824
|
-
const durableOpts = { session, seq, time, preview, anchor, t, chatOf, onRewind };
|
|
837
|
+
const durableOpts = { session, seq, time, preview, anchor, t, chatOf, watchChat: opts.watchChat, onRewind };
|
|
825
838
|
const root = el("div", CLASS.popover);
|
|
826
839
|
root.setAttribute("role", "dialog");
|
|
827
840
|
root.setAttribute("aria-label", t("popover.title"));
|
|
@@ -891,7 +904,7 @@ function openPopover(opts) {
|
|
|
891
904
|
popoverEl = root;
|
|
892
905
|
disposeOutside = shell.dispose;
|
|
893
906
|
void (async () => {
|
|
894
|
-
const outcome = await previewImpact(session, chatOf, seq);
|
|
907
|
+
const outcome = await previewImpact(session, chatOf, seq, (cb) => opts.watchChat(session.sessionId, cb));
|
|
895
908
|
impactOutcome = outcome;
|
|
896
909
|
if (outcome !== null && outcome.kind === "success") {
|
|
897
910
|
bothState = { state: hasFileImpact(outcome.text) ? "hasChanges" : "noChanges" };
|
|
@@ -1025,7 +1038,7 @@ function userNodeOf(chat, key) {
|
|
|
1025
1038
|
if (node === void 0 || node.kind !== "user" && node.kind !== "steering") return void 0;
|
|
1026
1039
|
return node.data;
|
|
1027
1040
|
}
|
|
1028
|
-
async function runRewindAndFill(session, seq, mode, currentSessionId, chatOf, setComposerText) {
|
|
1041
|
+
async function runRewindAndFill(session, seq, mode, currentSessionId, chatOf, watchChat, setComposerText) {
|
|
1029
1042
|
const known = knownCommandSeqs(session, chatOf, (node) => isExecutedRewindCommand(node, seq));
|
|
1030
1043
|
let result;
|
|
1031
1044
|
try {
|
|
@@ -1039,7 +1052,7 @@ async function runRewindAndFill(session, seq, mode, currentSessionId, chatOf, se
|
|
|
1039
1052
|
}
|
|
1040
1053
|
let outcome;
|
|
1041
1054
|
try {
|
|
1042
|
-
outcome = await waitForCommand(session, chatOf, (node) => isExecutedRewindCommand(node, seq) && !known.has(node.seq), 2e4);
|
|
1055
|
+
outcome = await waitForCommand(session, chatOf, (node) => isExecutedRewindCommand(node, seq) && !known.has(node.seq), 2e4, (cb) => watchChat(session.sessionId, cb));
|
|
1043
1056
|
} catch (error) {
|
|
1044
1057
|
rewindLog.warn("refill", `waiting for rewind @${seq} outcome threw`, error);
|
|
1045
1058
|
return;
|
|
@@ -1169,7 +1182,7 @@ function sameTargets(left, right) {
|
|
|
1169
1182
|
return other.kind === "pending" && target.itemId === other.itemId;
|
|
1170
1183
|
});
|
|
1171
1184
|
}
|
|
1172
|
-
function RewindPortals({ sessionId, sessionOf, chatOf, currentSessionId, t, subscribeLocale, setComposerText }) {
|
|
1185
|
+
function RewindPortals({ sessionId, sessionOf, chatOf, currentSessionId, watchChat, t, subscribeLocale, setComposerText }) {
|
|
1173
1186
|
const [targets, setTargets] = (0, import_react.useState)([]);
|
|
1174
1187
|
const hidden = (0, import_react.useRef)(/* @__PURE__ */ new WeakSet());
|
|
1175
1188
|
const [, forceRender] = (0, import_react.useReducer)((count) => count + 1, 0);
|
|
@@ -1230,6 +1243,7 @@ function RewindPortals({ sessionId, sessionOf, chatOf, currentSessionId, t, subs
|
|
|
1230
1243
|
sessionId,
|
|
1231
1244
|
sessionOf,
|
|
1232
1245
|
chatOf,
|
|
1246
|
+
watchChat,
|
|
1233
1247
|
setComposerText,
|
|
1234
1248
|
t
|
|
1235
1249
|
},
|
|
@@ -1241,6 +1255,7 @@ function RewindPortals({ sessionId, sessionOf, chatOf, currentSessionId, t, subs
|
|
|
1241
1255
|
sessionId,
|
|
1242
1256
|
sessionOf,
|
|
1243
1257
|
chatOf,
|
|
1258
|
+
watchChat,
|
|
1244
1259
|
currentSessionId,
|
|
1245
1260
|
setComposerText,
|
|
1246
1261
|
t
|
|
@@ -1251,7 +1266,7 @@ function RewindPortals({ sessionId, sessionOf, chatOf, currentSessionId, t, subs
|
|
|
1251
1266
|
target.key
|
|
1252
1267
|
));
|
|
1253
1268
|
}
|
|
1254
|
-
function RewindButton({ target, sessionId, sessionOf, chatOf, currentSessionId, setComposerText, t }) {
|
|
1269
|
+
function RewindButton({ target, sessionId, sessionOf, chatOf, watchChat, currentSessionId, setComposerText, t }) {
|
|
1255
1270
|
const onClick = (event) => {
|
|
1256
1271
|
event.stopPropagation();
|
|
1257
1272
|
const session = sessionOf(sessionId);
|
|
@@ -1264,13 +1279,14 @@ function RewindButton({ target, sessionId, sessionOf, chatOf, currentSessionId,
|
|
|
1264
1279
|
openPopover({
|
|
1265
1280
|
session,
|
|
1266
1281
|
chatOf,
|
|
1282
|
+
watchChat,
|
|
1267
1283
|
seq: node.seq,
|
|
1268
1284
|
time: node.time,
|
|
1269
1285
|
preview: messagePreviewOf(node),
|
|
1270
1286
|
anchor: event.currentTarget,
|
|
1271
1287
|
t,
|
|
1272
1288
|
onRewind: (mode) => {
|
|
1273
|
-
void runRewindAndFill(session, node.seq, mode, currentSessionId, chatOf, setComposerText);
|
|
1289
|
+
void runRewindAndFill(session, node.seq, mode, currentSessionId, chatOf, watchChat, setComposerText);
|
|
1274
1290
|
}
|
|
1275
1291
|
});
|
|
1276
1292
|
};
|
|
@@ -1303,7 +1319,7 @@ async function retractPending(session, itemId, text, setComposerText) {
|
|
|
1303
1319
|
setComposerText(session.sessionId, text);
|
|
1304
1320
|
}
|
|
1305
1321
|
}
|
|
1306
|
-
function RetractButton({ target, sessionId, sessionOf, chatOf, setComposerText, t }) {
|
|
1322
|
+
function RetractButton({ target, sessionId, sessionOf, chatOf, watchChat, setComposerText, t }) {
|
|
1307
1323
|
const onClick = (event) => {
|
|
1308
1324
|
event.stopPropagation();
|
|
1309
1325
|
const session = sessionOf(sessionId);
|
|
@@ -1311,6 +1327,7 @@ function RetractButton({ target, sessionId, sessionOf, chatOf, setComposerText,
|
|
|
1311
1327
|
openPopover({
|
|
1312
1328
|
session,
|
|
1313
1329
|
chatOf,
|
|
1330
|
+
watchChat,
|
|
1314
1331
|
preview: target.preview,
|
|
1315
1332
|
anchor: event.currentTarget,
|
|
1316
1333
|
t,
|
|
@@ -1339,8 +1356,8 @@ function createRewindBridge(deps) {
|
|
|
1339
1356
|
}
|
|
1340
1357
|
|
|
1341
1358
|
// src/client/build-info.ts
|
|
1342
|
-
var PLUGIN_VERSION = true ? "0.7.
|
|
1343
|
-
var BUILD_HASH = true ? "
|
|
1359
|
+
var PLUGIN_VERSION = true ? "0.7.4" : "dev";
|
|
1360
|
+
var BUILD_HASH = true ? "73e01232" : "dev";
|
|
1344
1361
|
|
|
1345
1362
|
// src/client/locales.ts
|
|
1346
1363
|
var zh = {
|
|
@@ -1641,6 +1658,18 @@ function apply(ctx) {
|
|
|
1641
1658
|
return false;
|
|
1642
1659
|
}
|
|
1643
1660
|
};
|
|
1661
|
+
const watchChat = (sessionId, cb) => resolveChatWatch(
|
|
1662
|
+
(id) => {
|
|
1663
|
+
try {
|
|
1664
|
+
return uiConversation()?.binding(id).target(CHAT_VIEW);
|
|
1665
|
+
} catch {
|
|
1666
|
+
return void 0;
|
|
1667
|
+
}
|
|
1668
|
+
},
|
|
1669
|
+
(id) => sessionOf(id),
|
|
1670
|
+
sessionId,
|
|
1671
|
+
cb
|
|
1672
|
+
);
|
|
1644
1673
|
const slots = ctx.slots;
|
|
1645
1674
|
yield slots.inject(HEADER_ACTIONS_SLOT, () => slots.register(
|
|
1646
1675
|
{
|
|
@@ -1650,7 +1679,7 @@ function apply(ctx) {
|
|
|
1650
1679
|
id: "dsh-rewind-portals",
|
|
1651
1680
|
order: 1e3
|
|
1652
1681
|
},
|
|
1653
|
-
createRewindBridge({ sessionOf, chatOf, currentSessionId, setComposerText, t, subscribeLocale })
|
|
1682
|
+
createRewindBridge({ sessionOf, chatOf, currentSessionId, watchChat, setComposerText, t, subscribeLocale })
|
|
1654
1683
|
));
|
|
1655
1684
|
const clientCtx = ctx;
|
|
1656
1685
|
clientCtx.inject(["settingsScope"], (scoped) => {
|
|
@@ -1696,7 +1725,7 @@ function apply(ctx) {
|
|
|
1696
1725
|
const known = knownCommandSeqs(face, chatOf2, (node) => isCandidateCommand(node));
|
|
1697
1726
|
const result = await face.command("/rewind __candidates");
|
|
1698
1727
|
if (!result.ok || result.value?.matched !== true) return void 0;
|
|
1699
|
-
const outcome = await waitForCommand(face, chatOf2, (node) => isCandidateCommand(node) && !known.has(node.seq));
|
|
1728
|
+
const outcome = await waitForCommand(face, chatOf2, (node) => isCandidateCommand(node) && !known.has(node.seq), 8e3, (cb) => watchChat(face.sessionId, cb));
|
|
1700
1729
|
if (outcome === null || outcome.kind !== "success" || outcome.text === void 0) return void 0;
|
|
1701
1730
|
return rewindCandidatesFromHostText(outcome.text);
|
|
1702
1731
|
};
|
|
@@ -1731,13 +1760,14 @@ function apply(ctx) {
|
|
|
1731
1760
|
openPopover({
|
|
1732
1761
|
session: face,
|
|
1733
1762
|
chatOf,
|
|
1763
|
+
watchChat,
|
|
1734
1764
|
seq: candidate.seq,
|
|
1735
1765
|
time: candidate.time,
|
|
1736
1766
|
preview: candidate.preview,
|
|
1737
1767
|
anchor: composerAnchor(),
|
|
1738
1768
|
t,
|
|
1739
1769
|
onRewind: (mode) => {
|
|
1740
|
-
void runRewindAndFill(face, candidate.seq, mode, currentSessionId, chatOf, setComposerText);
|
|
1770
|
+
void runRewindAndFill(face, candidate.seq, mode, currentSessionId, chatOf, watchChat, setComposerText);
|
|
1741
1771
|
}
|
|
1742
1772
|
});
|
|
1743
1773
|
}
|
|
@@ -26,6 +26,28 @@ export type ChatOf = (session: {
|
|
|
26
26
|
chat?: unknown;
|
|
27
27
|
};
|
|
28
28
|
} | undefined) => HiddenChat | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Subscribe to one session's live chat-update signal, for waiting on a chat
|
|
31
|
+
* snapshot change without polling. Dual-channel, mirroring `chatOf`: the
|
|
32
|
+
* alpha.1+ `uiConversation` "chat" view when registered, else the session face
|
|
33
|
+
* (rc.2, whose snapshot still carries `chat`, so its own `subscribe` is the
|
|
34
|
+
* chat-update signal). `cb` fires whenever the chat snapshot invalidates.
|
|
35
|
+
*/
|
|
36
|
+
export type ChatWatch = (sessionId: string, cb: () => void) => () => void;
|
|
37
|
+
/**
|
|
38
|
+
* Choose the chat-update subscription for `waitForCommand`: prefer the
|
|
39
|
+
* `uiConversation` "chat" view's own `subscribe` when available (alpha.1+,
|
|
40
|
+
* where the chat-update signal lives), else the session face's `subscribe`
|
|
41
|
+
* (rc.2, whose snapshot still carries the chat, so its own subscribe is the
|
|
42
|
+
* chat-update signal). Extracted as a pure channel-selection step so the
|
|
43
|
+
* "view vs face" branch is unit-testable; the resolvers are injected by the
|
|
44
|
+
* caller (see `watchChat` in index.ts). Never throws.
|
|
45
|
+
*/
|
|
46
|
+
export declare function resolveChatWatch(resolveView: (sessionId: string) => {
|
|
47
|
+
subscribe?(cb: () => void): () => void;
|
|
48
|
+
} | undefined, resolveFace: (sessionId: string) => {
|
|
49
|
+
subscribe(cb: () => void): () => void;
|
|
50
|
+
} | undefined, sessionId: string, cb: () => void): () => void;
|
|
29
51
|
/**
|
|
30
52
|
* Resolve the chat snapshot across the two harness channels: the session-face
|
|
31
53
|
* snapshot first (rc.2 — on alpha.1+ the face no longer carries `chat`, so the
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import type { SessionFace } from '@deepseek-ai/dsh-client-runtime/client';
|
|
17
17
|
import type { CommandNode } from '@deepseek-ai/dsh-client-runtime/client';
|
|
18
|
-
import { type ChatOf } from './hidden.ts';
|
|
18
|
+
import { type ChatOf, type ChatWatch } from './hidden.ts';
|
|
19
19
|
import type { RewindKey } from './locales.ts';
|
|
20
20
|
type Translate = (key: RewindKey, params?: Record<string, unknown>) => string;
|
|
21
21
|
export interface PopoverOptions {
|
|
@@ -38,6 +38,13 @@ export interface PopoverOptions {
|
|
|
38
38
|
* Unused by the pending-retract variant.
|
|
39
39
|
*/
|
|
40
40
|
readonly chatOf: ChatOf;
|
|
41
|
+
/**
|
|
42
|
+
* Subscribe to one session's live chat-update signal, so a probe waiting on
|
|
43
|
+
* a command's chat node can be woken when the chat snapshot changes (alpha.1+
|
|
44
|
+
* the session face no longer fires on a chat update). Passed straight through
|
|
45
|
+
* to `waitForCommand`.
|
|
46
|
+
*/
|
|
47
|
+
readonly watchChat: ChatWatch;
|
|
41
48
|
/** The button that opened the popover (outside-click ignore target). */
|
|
42
49
|
readonly anchor: HTMLElement;
|
|
43
50
|
readonly t: Translate;
|
|
@@ -63,7 +70,7 @@ export declare function knownCommandSeqs(session: SessionFace, chatOf: ChatOf, m
|
|
|
63
70
|
* session snapshot (command/run + command/done land as one CommandNode).
|
|
64
71
|
* @returns the outcome text-bearing node, or null on timeout.
|
|
65
72
|
*/
|
|
66
|
-
export declare function waitForCommand(session: SessionFace, chatOf: ChatOf, match: (node: CommandNode) => boolean, timeoutMs?: number): Promise<{
|
|
73
|
+
export declare function waitForCommand(session: SessionFace, chatOf: ChatOf, match: (node: CommandNode) => boolean, timeoutMs?: number, watch?: (cb: () => void) => () => void): Promise<{
|
|
67
74
|
kind: 'success' | 'error';
|
|
68
75
|
text?: string;
|
|
69
76
|
} | null>;
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
import { type ReactNode } from 'react';
|
|
26
26
|
import type { SessionFace } from '@deepseek-ai/dsh-client-runtime/client';
|
|
27
|
-
import { type ChatOf, type HiddenChat } from './hidden.ts';
|
|
27
|
+
import { type ChatOf, type ChatWatch, type HiddenChat } from './hidden.ts';
|
|
28
28
|
import type { RewindKey } from './locales.ts';
|
|
29
29
|
type Translate = (key: RewindKey, params?: Record<string, unknown>) => string;
|
|
30
30
|
/** One portal target: the actions row of a user/steering seat + its durable node. */
|
|
@@ -58,6 +58,13 @@ export interface RewindBridgeDeps {
|
|
|
58
58
|
* `chatSnapshotOf` in hidden.ts for the channel precedence.
|
|
59
59
|
*/
|
|
60
60
|
readonly chatOf: ChatOf;
|
|
61
|
+
/**
|
|
62
|
+
* Subscribe to one session's live chat-update signal, so the composer refill
|
|
63
|
+
* waiting on an executed rewind's chat node can be woken when the chat
|
|
64
|
+
* snapshot changes (alpha.1+ the session face no longer fires on a chat
|
|
65
|
+
* update). Passed through to `waitForCommand`.
|
|
66
|
+
*/
|
|
67
|
+
readonly watchChat: ChatWatch;
|
|
61
68
|
readonly currentSessionId: () => string | undefined;
|
|
62
69
|
readonly t: Translate;
|
|
63
70
|
readonly subscribeLocale: (cb: () => void) => () => void;
|
|
@@ -124,7 +131,7 @@ export declare function writeComposer(text: string, facade: ComposerDraftWriter
|
|
|
124
131
|
* the old baseline heuristic refilled withdrawn text into the composer
|
|
125
132
|
* after switching sessions or restarting dsh.
|
|
126
133
|
*/
|
|
127
|
-
export declare function runRewindAndFill(session: SessionFace, seq: number, mode: 'chat' | 'both', currentSessionId: () => string | undefined, chatOf: ChatOf, setComposerText: (sessionId: string, text: string) => boolean): Promise<void>;
|
|
134
|
+
export declare function runRewindAndFill(session: SessionFace, seq: number, mode: 'chat' | 'both', currentSessionId: () => string | undefined, chatOf: ChatOf, watchChat: ChatWatch, setComposerText: (sessionId: string, text: string) => boolean): Promise<void>;
|
|
128
135
|
/**
|
|
129
136
|
* Locate the actions container of a user/steering seat row — the element the
|
|
130
137
|
* ↶ button portals into (the copy/branch IconActions row).
|
|
@@ -163,7 +170,7 @@ interface RewindPortalsProps extends RewindBridgeDeps {
|
|
|
163
170
|
* skipped when the target set is unchanged), so the plugin never runs a
|
|
164
171
|
* synchronous full-transcript scan inside a commit microtask.
|
|
165
172
|
*/
|
|
166
|
-
export declare function RewindPortals({ sessionId, sessionOf, chatOf, currentSessionId, t, subscribeLocale, setComposerText }: RewindPortalsProps): ReactNode;
|
|
173
|
+
export declare function RewindPortals({ sessionId, sessionOf, chatOf, currentSessionId, watchChat, t, subscribeLocale, setComposerText }: RewindPortalsProps): ReactNode;
|
|
167
174
|
/** The current composer draft, on whichever channel: rc.2 textarea `.value`,
|
|
168
175
|
* alpha.1+ contenteditable `textContent`. Empty when the composer is absent.
|
|
169
176
|
* Exported as a test seam (the empty-composer guard in `retractPending`). */
|
|
@@ -23,4 +23,4 @@ export declare const CLASS: {
|
|
|
23
23
|
/** The ↶ glyph, drawn inline so the bundle stays dependency-free. */
|
|
24
24
|
export declare const REWIND_ICON_SVG: string;
|
|
25
25
|
/** One injected stylesheet (scoped under `.dsh-rewind-*`). */
|
|
26
|
-
export declare const STYLE = "\n.dsh-rewind-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 6px;\n border: none;\n border-radius: 28px;\n background: transparent;\n color: var(--dsw-alias-label-tertiary);\n cursor: pointer;\n}\n.dsh-rewind-btn:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.dsh-rewind-popover {\n position: fixed;\n z-index: 1000;\n width: 288px;\n padding: 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n background: var(--dsw-specific-menu, var(--dsw-alias-bg-layer-3));\n box-shadow: var(--dsw-shadow-lv3);\n font-size: 14px;\n line-height: 20px;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-rewind-popover-title {\n font-size: 14px;\n font-weight: 600;\n line-height: 20px;\n}\n.dsh-rewind-popover-target {\n margin: 4px 0 10px;\n font-size: 12px;\n line-height: 16px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-all;\n}\n.dsh-rewind-popover-option {\n display: flex;\n flex-direction: column;\n gap: 2px;\n width: 100%;\n margin: 0 0 6px;\n padding: 8px 10px;\n border: 1px solid transparent;\n border-radius: 8px;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n.dsh-rewind-popover-option:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n.dsh-rewind-popover-option:disabled {\n opacity: 0.5;\n cursor: default;\n}\n.dsh-rewind-popover-option-label {\n font-weight: 500;\n}\n.dsh-rewind-popover-option-hint {\n font-size: 12px;\n line-height: 16px;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-rewind-popover-impact {\n margin: 4px 0 10px;\n padding: 8px 10px;\n border-radius: 8px;\n background: var(--dsw-alias-interactive-bg-hover);\n font-size: 12px;\n line-height: 16px;\n color: var(--dsw-alias-label-secondary);\n white-space: pre-wrap;\n max-height: 160px;\n overflow: auto;\n}\n.dsh-rewind-popover-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n}\n.dsh-rewind-popover-primary,\n.dsh-rewind-popover-ghost {\n padding: 5px 12px;\n border: none;\n border-radius: 8px;\n font: inherit;\n font-size: 13px;\n line-height: 18px;\n cursor: pointer;\n}\n.dsh-rewind-popover-primary {\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n.dsh-rewind-popover-primary:hover:not(:disabled) {\n background: var(--dsw-alias-button-primary-hover);\n}\n.dsh-rewind-popover-primary:disabled {\n opacity: 0.5;\n cursor: default;\n}\n.dsh-rewind-popover-ghost {\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-rewind-popover-ghost:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dsh-rewind-guard-hint {\n position: fixed;\n z-index: 1000;\n max-width: min(440px, calc(100vw - 24px));\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n background: var(--dsw-specific-menu, var(--dsw-alias-bg-layer-3));\n box-shadow: var(--dsw-shadow-lv3);\n font-size: 13px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n pointer-events: none;\n}\n\n/* ---- Snapshot-cleanup settings card (mirrors the harness PluginCard look) ---- */\n.dsh-rewind-cleanup-card {\n list-style: none;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n background: var(--dsw-alias-bg-layer-3);\n transition: border-color .16s, background .16s;\n}\n.dsh-rewind-cleanup-card:hover {\n border-color: var(--dsw-alias-label-dimmed);\n}\n.dsh-rewind-cleanup-card-open {\n background: var(--dsw-alias-bg-layer-2);\n border-color: var(--dsw-alias-label-dimmed);\n}\n.dsh-rewind-cleanup-header {\n width: 100%;\n appearance: none;\n border: 0;\n background: none;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n border-radius: 12px;\n}\n.dsh-rewind-cleanup-header:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n.dsh-rewind-cleanup-head-text {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.dsh-rewind-cleanup-name {\n font-size: 15px;\n font-weight: 600;\n line-height: 1.4;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-rewind-cleanup-desc {\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-rewind-cleanup-chevron {\n flex: none;\n color: var(--dsw-alias-label-tertiary);\n transition: transform .16s;\n}\n.dsh-rewind-cleanup-chevron-open {\n transform: rotate(180deg);\n}\n.dsh-rewind-cleanup-pending {\n flex: none;\n border-radius: 999px;\n padding: 1px 8px;\n font-size: 11px;\n line-height: 17px;\n font-weight: 500;\n white-space: nowrap;\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-rewind-cleanup-body {\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin: 0 16px;\n padding: 4px 0 8px;\n}\n.dsh-rewind-cleanup-readonly {\n margin: 12px 0 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-rewind-cleanup-permission {\n display: grid;\n gap: 6px;\n padding: 12px 0;\n}\n.dsh-rewind-cleanup-field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 12px 0;\n}\n.dsh-rewind-cleanup-field + .dsh-rewind-cleanup-field {\n border-top: 1px solid var(--dsw-alias-border-l2);\n}\n.dsh-rewind-cleanup-head {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.dsh-rewind-cleanup-label {\n flex: 1;\n min-width: 0;\n font-size: 13px;\n font-weight: 500;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-rewind-cleanup-hint {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-rewind-cleanup-error {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-error);\n}\n/* Switch row: label left, role=switch button right, hint below (Subagent module). */\n.dsh-rewind-cleanup-toggle-row {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 16px;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-rewind-cleanup-toggle-label {\n flex: 1;\n min-width: 0;\n}\n.dsh-rewind-cleanup-switch {\n box-sizing: border-box;\n position: relative;\n flex: 0 0 auto;\n width: 36px;\n height: 20px;\n padding: 2px;\n border: 0;\n border-radius: 10px;\n background: var(--dsw-alias-border-l3);\n cursor: pointer;\n}\n.dsh-rewind-cleanup-switch-on {\n background: var(--dsw-alias-brand-primary);\n}\n.dsh-rewind-cleanup-switch:disabled {\n cursor: default;\n opacity: 0.5;\n}\n.dsh-rewind-cleanup-switch:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 2px;\n}\n.dsh-rewind-cleanup-thumb {\n display: block;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n corner-shape: round;\n background: var(--dsw-alias-label-primary-foreground);\n transition: transform 120ms ease;\n}\n.dsh-rewind-cleanup-switch-on .dsh-rewind-cleanup-thumb {\n transform: translateX(16px);\n}\n.dsh-rewind-cleanup-input {\n box-sizing: border-box;\n height: 34px;\n padding: 0 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-3);\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-rewind-cleanup-input:focus-visible {\n outline: none;\n border-color: var(--dsw-alias-brand-primary);\n}\n.dsh-rewind-cleanup-input:disabled {\n color: var(--dsw-alias-label-tertiary);\n cursor: default;\n}\n.dsh-rewind-cleanup-input-invalid {\n border-color: var(--dsw-alias-label-error);\n}\n.dsh-rewind-cleanup-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n padding: 12px 0 4px;\n border-top: 1px solid var(--dsw-alias-border-l2);\n}\n.dsh-rewind-cleanup-failed {\n flex: 1;\n min-width: 0;\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-error);\n}\n.dsh-rewind-cleanup-discard,\n.dsh-rewind-cleanup-save {\n appearance: none;\n border: 1px solid transparent;\n border-radius: 8px;\n padding: 5px 14px;\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n cursor: pointer;\n}\n.dsh-rewind-cleanup-discard {\n border-color: var(--dsw-alias-border-l2);\n background: none;\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-rewind-cleanup-discard:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-dimmed);\n}\n.dsh-rewind-cleanup-save {\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n}\n.dsh-rewind-cleanup-discard:disabled,\n.dsh-rewind-cleanup-save:disabled {\n opacity: 0.4;\n cursor: default;\n}\n.dsh-rewind-cleanup-discard:focus-visible,\n.dsh-rewind-cleanup-save:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n";
|
|
26
|
+
export declare const STYLE = "\n.dsh-rewind-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 6px;\n border: none;\n border-radius: 28px;\n background: transparent;\n color: var(--dsw-alias-label-tertiary);\n cursor: pointer;\n}\n.dsh-rewind-btn:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.dsh-rewind-popover {\n position: fixed;\n z-index: 1000;\n width: 288px;\n padding: 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n background: var(--dsw-specific-menu, var(--dsw-alias-bg-layer-3));\n box-shadow: var(--dsw-shadow-lv3);\n font-size: 14px;\n line-height: 20px;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-rewind-popover-title {\n font-size: 14px;\n font-weight: 600;\n line-height: 20px;\n}\n.dsh-rewind-popover-target {\n margin: 4px 0 10px;\n font-size: 12px;\n line-height: 16px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-all;\n}\n.dsh-rewind-popover-option {\n display: flex;\n flex-direction: column;\n gap: 2px;\n width: 100%;\n margin: 0 0 6px;\n padding: 8px 10px;\n border: 1px solid transparent;\n border-radius: 8px;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n.dsh-rewind-popover-option:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n.dsh-rewind-popover-option:disabled {\n opacity: 0.5;\n cursor: default;\n}\n.dsh-rewind-popover-option-label {\n font-weight: 500;\n}\n.dsh-rewind-popover-option-hint {\n font-size: 12px;\n line-height: 16px;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-rewind-popover-impact {\n margin: 4px 0 10px;\n padding: 8px 10px;\n border-radius: 8px;\n background: var(--dsw-alias-interactive-bg-hover);\n font-size: 12px;\n line-height: 16px;\n color: var(--dsw-alias-label-secondary);\n white-space: pre-wrap;\n max-height: 160px;\n overflow: auto;\n}\n.dsh-rewind-popover-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n}\n.dsh-rewind-popover-primary,\n.dsh-rewind-popover-ghost {\n padding: 5px 12px;\n border: none;\n border-radius: 8px;\n font: inherit;\n font-size: 13px;\n line-height: 18px;\n cursor: pointer;\n}\n.dsh-rewind-popover-primary {\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n.dsh-rewind-popover-primary:hover:not(:disabled) {\n background: var(--dsw-alias-button-primary-hover);\n}\n.dsh-rewind-popover-primary:disabled {\n opacity: 0.5;\n cursor: default;\n}\n.dsh-rewind-popover-ghost {\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-rewind-popover-ghost:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.dsh-rewind-guard-hint {\n position: fixed;\n z-index: 1000;\n max-width: min(440px, calc(100vw - 24px));\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n background: var(--dsw-specific-menu, var(--dsw-alias-bg-layer-3));\n box-shadow: var(--dsw-shadow-lv3);\n font-size: 13px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n pointer-events: none;\n}\n\n/* ---- Snapshot-cleanup settings card (mirrors the harness PluginCard look) ---- */\n/* Dual-channel note: the harness card corner/border drifted across versions \u2014\n rc.2 uses border-radius 12px + 1px solid border-l2, while alpha uses 16px +\n 0.5px border-l4 (and adds corner-shape: round). We intentionally keep the\n rc.2 values here for now; if the alpha look is wanted, switch these on the\n host version like the other dual-channel surfaces (see the host-version probe\n in src/client/index.ts). */\n.dsh-rewind-cleanup-card {\n list-style: none;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n background: var(--dsw-alias-bg-layer-3);\n transition: border-color .16s, background .16s;\n}\n.dsh-rewind-cleanup-card:hover {\n border-color: var(--dsw-alias-label-dimmed);\n}\n.dsh-rewind-cleanup-card-open {\n background: var(--dsw-alias-bg-layer-2);\n border-color: var(--dsw-alias-label-dimmed);\n}\n.dsh-rewind-cleanup-header {\n width: 100%;\n appearance: none;\n border: 0;\n background: none;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n border-radius: 12px;\n}\n.dsh-rewind-cleanup-header:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n.dsh-rewind-cleanup-head-text {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.dsh-rewind-cleanup-name {\n font-size: 15px;\n font-weight: 600;\n line-height: 1.4;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-rewind-cleanup-desc {\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-rewind-cleanup-chevron {\n flex: none;\n color: var(--dsw-alias-label-tertiary);\n transition: transform .16s;\n}\n.dsh-rewind-cleanup-chevron-open {\n transform: rotate(180deg);\n}\n.dsh-rewind-cleanup-pending {\n flex: none;\n border-radius: 999px;\n padding: 1px 8px;\n font-size: 11px;\n line-height: 17px;\n font-weight: 500;\n white-space: nowrap;\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-rewind-cleanup-body {\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin: 0 16px;\n padding: 4px 0 8px;\n}\n.dsh-rewind-cleanup-readonly {\n margin: 12px 0 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-rewind-cleanup-permission {\n display: grid;\n gap: 6px;\n padding: 12px 0;\n}\n.dsh-rewind-cleanup-field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 12px 0;\n}\n.dsh-rewind-cleanup-field + .dsh-rewind-cleanup-field {\n border-top: 1px solid var(--dsw-alias-border-l2);\n}\n.dsh-rewind-cleanup-head {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.dsh-rewind-cleanup-label {\n flex: 1;\n min-width: 0;\n font-size: 13px;\n font-weight: 500;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-rewind-cleanup-hint {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-rewind-cleanup-error {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-error);\n}\n/* Switch row: label left, role=switch button right, hint below (Subagent module). */\n.dsh-rewind-cleanup-toggle-row {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 16px;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-rewind-cleanup-toggle-label {\n flex: 1;\n min-width: 0;\n}\n.dsh-rewind-cleanup-switch {\n box-sizing: border-box;\n position: relative;\n flex: 0 0 auto;\n width: 36px;\n height: 20px;\n padding: 2px;\n border: 0;\n border-radius: 10px;\n background: var(--dsw-alias-border-l3);\n cursor: pointer;\n}\n.dsh-rewind-cleanup-switch-on {\n background: var(--dsw-alias-brand-primary);\n}\n.dsh-rewind-cleanup-switch:disabled {\n cursor: default;\n opacity: 0.5;\n}\n.dsh-rewind-cleanup-switch:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 2px;\n}\n.dsh-rewind-cleanup-thumb {\n display: block;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n corner-shape: round;\n background: var(--dsw-alias-label-primary-foreground);\n transition: transform 120ms ease;\n}\n.dsh-rewind-cleanup-switch-on .dsh-rewind-cleanup-thumb {\n transform: translateX(16px);\n}\n.dsh-rewind-cleanup-input {\n box-sizing: border-box;\n height: 34px;\n padding: 0 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-3);\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-rewind-cleanup-input:focus-visible {\n outline: none;\n border-color: var(--dsw-alias-brand-primary);\n}\n.dsh-rewind-cleanup-input:disabled {\n color: var(--dsw-alias-label-tertiary);\n cursor: default;\n}\n.dsh-rewind-cleanup-input-invalid {\n border-color: var(--dsw-alias-label-error);\n}\n.dsh-rewind-cleanup-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n padding: 12px 0 4px;\n border-top: 1px solid var(--dsw-alias-border-l2);\n}\n.dsh-rewind-cleanup-failed {\n flex: 1;\n min-width: 0;\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-error);\n}\n.dsh-rewind-cleanup-discard,\n.dsh-rewind-cleanup-save {\n appearance: none;\n border: 1px solid transparent;\n border-radius: 8px;\n padding: 5px 14px;\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n cursor: pointer;\n}\n.dsh-rewind-cleanup-discard {\n border-color: var(--dsw-alias-border-l2);\n background: none;\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-rewind-cleanup-discard:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-dimmed);\n}\n.dsh-rewind-cleanup-save {\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n}\n.dsh-rewind-cleanup-discard:disabled,\n.dsh-rewind-cleanup-save:disabled {\n opacity: 0.4;\n cursor: default;\n}\n.dsh-rewind-cleanup-discard:focus-visible,\n.dsh-rewind-cleanup-save:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-rewind-plugin",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.4",
|
|
4
4
|
"description": "DSH 插件:真正便捷无感的同窗口内对话回退,从不新建分支;自带轻量工作区备份,可一并还原文件(完整 Claude Code /rewind 语义)。 · DSH plugin: genuinely effortless in-window conversation rewind — never forking a new session; ships a lightweight workspace backup that restores files together with the rewind (full Claude Code /rewind semantics).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|