dsh-rewind-plugin 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/compat/audit.md +15 -24
- package/lib/client.js +15 -7
- package/lib/index.js +15 -7
- package/lib/types/client/portals.d.ts +22 -0
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/session-events.d.ts +34 -0
- package/package.json +1 -1
package/docs/compat/audit.md
CHANGED
|
@@ -22,30 +22,21 @@
|
|
|
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.
|
|
26
|
-
>
|
|
27
|
-
>
|
|
28
|
-
>
|
|
29
|
-
>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
> The composer refill is dual-channel the same way: on alpha the withdrawn text is
|
|
41
|
-
> written through the `conversation` service's `input` resolver's `setDraft` (the
|
|
42
|
-
> harness's own Lexical editor), else the rc.2 `<textarea>` / alpha `contenteditable`
|
|
43
|
-
> DOM write (`writeComposer` in `src/client/portals.tsx`); pinned by
|
|
44
|
-
> `tests/client-composer.test.ts`. The session-seat button DOM is dual-channel
|
|
45
|
-
> the same way: the actions (copy/branch) row was located by `[data-time-hover-root]`
|
|
46
|
-
> on ≤ 0.1.1-rc.x and by `[data-actions-reveal]` on 0.1.2-alpha.2
|
|
47
|
-
> (`ACTIONS_ROOT_SELECTOR` / `PENDING_SEAT_SELECTOR` in `src/client/portals.tsx`);
|
|
48
|
-
> pinned by `tests/client-dom.test.ts`.
|
|
25
|
+
> `0.1.2-alpha.2` … `0.1.2-alpha.4` verified-compatible. Built/tested on the rc.2
|
|
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 single
|
|
28
|
+
> `^0.1.2-alpha.2` peer member covers all three. `rc.2` (`latest`) remains the
|
|
29
|
+
> primary baseline; four versions verified: rc.2, alpha.2, alpha.3, alpha.4.
|
|
30
|
+
|
|
31
|
+
### Channeled seams (version × channel)
|
|
32
|
+
|
|
33
|
+
| Seam | rc.2 (`0.1.1-rc.2`) | alpha.2 / alpha.3 | alpha.4 |
|
|
34
|
+
| --- | --- | --- | --- |
|
|
35
|
+
| Host session log (`eventsOf`) | `Session.events` | `Session.events` | `session.snapshotEvents()` |
|
|
36
|
+
| Client chat snapshot (`chatSnapshotOf`) | session-face `chat` field | `uiConversation` `chat` view | `uiConversation` `chat` view |
|
|
37
|
+
| Client composer refill (`writeComposer`) | `<textarea>` DOM write | `conversation.input.setDraft` | `conversation.input.setDraft` |
|
|
38
|
+
| Client settings card | nested `ctx.inject(['settingsScope'])` | nested `ctx.inject(['settingsScope'])` | nested `ctx.inject(['settingsScope'])` |
|
|
39
|
+
| Client seat-button DOM (`actionsContainerOf`) | `[data-time-hover-root]` | `[data-actions-reveal]` | structural locate of the copy-`<button>` container |
|
|
49
40
|
|
|
50
41
|
## Definition of "fully compatible" (invariants)
|
|
51
42
|
|
package/lib/client.js
CHANGED
|
@@ -483,6 +483,7 @@ var STYLE = `
|
|
|
483
483
|
width: 16px;
|
|
484
484
|
height: 16px;
|
|
485
485
|
border-radius: 50%;
|
|
486
|
+
corner-shape: round;
|
|
486
487
|
background: var(--dsw-alias-label-primary-foreground);
|
|
487
488
|
transition: transform 120ms ease;
|
|
488
489
|
}
|
|
@@ -1020,7 +1021,16 @@ var COMPOSER_EDITABLE_SELECTOR = "[data-composer-input]";
|
|
|
1020
1021
|
var USER_SEAT_SELECTOR = '[data-chat-flow-kind="user"][data-chat-anchor-key], [data-chat-flow-kind="steering"][data-chat-anchor-key]';
|
|
1021
1022
|
var CHAT_SEAT_SELECTOR = "[data-chat-anchor-key]";
|
|
1022
1023
|
var ACTIONS_ROOT_SELECTOR = "[data-time-hover-root], [data-actions-reveal]";
|
|
1023
|
-
var PENDING_SEAT_SELECTOR = "[data-pending-steering]
|
|
1024
|
+
var PENDING_SEAT_SELECTOR = "[data-pending-steering]";
|
|
1025
|
+
function actionsContainerOf(row) {
|
|
1026
|
+
const root = row?.matches(ACTIONS_ROOT_SELECTOR) ? row : row?.querySelector(ACTIONS_ROOT_SELECTOR);
|
|
1027
|
+
const actions = root?.lastElementChild;
|
|
1028
|
+
if (actions instanceof HTMLElement && actions.querySelector("button") !== null) return actions;
|
|
1029
|
+
const copy = row?.querySelector("button");
|
|
1030
|
+
const structural = copy?.parentElement;
|
|
1031
|
+
if (structural instanceof HTMLElement && structural.querySelector("button") !== null) return structural;
|
|
1032
|
+
return void 0;
|
|
1033
|
+
}
|
|
1024
1034
|
function collectTargets(chat, hiddenSeqs) {
|
|
1025
1035
|
const rows = /* @__PURE__ */ new Map();
|
|
1026
1036
|
for (const element of document.querySelectorAll(USER_SEAT_SELECTOR)) {
|
|
@@ -1034,9 +1044,8 @@ function collectTargets(chat, hiddenSeqs) {
|
|
|
1034
1044
|
const user = node.data;
|
|
1035
1045
|
if (hiddenSeqs.has(node.anchorSeq ?? user.seq)) continue;
|
|
1036
1046
|
const row = rows.get(key);
|
|
1037
|
-
const
|
|
1038
|
-
|
|
1039
|
-
if (!(actions instanceof HTMLElement) || actions.querySelector("button") === null) continue;
|
|
1047
|
+
const actions = actionsContainerOf(row);
|
|
1048
|
+
if (actions === void 0) continue;
|
|
1040
1049
|
targets.push({ kind: "durable", key, container: actions, seq: user.seq, time: user.time, preview: messagePreviewOf(user) });
|
|
1041
1050
|
}
|
|
1042
1051
|
return targets;
|
|
@@ -1061,9 +1070,8 @@ function collectPendingTargets(snapshot) {
|
|
|
1061
1070
|
if (itemId === null) continue;
|
|
1062
1071
|
const row = rows[i];
|
|
1063
1072
|
if (row === void 0) continue;
|
|
1064
|
-
const
|
|
1065
|
-
|
|
1066
|
-
if (!(actions instanceof HTMLElement) || actions.querySelector("button") === null) continue;
|
|
1073
|
+
const actions = actionsContainerOf(row);
|
|
1074
|
+
if (actions === void 0) continue;
|
|
1067
1075
|
const item = steering[i];
|
|
1068
1076
|
targets.push({
|
|
1069
1077
|
kind: "pending",
|
package/lib/index.js
CHANGED
|
@@ -111,6 +111,14 @@ function translate(lang, key, params = {}) {
|
|
|
111
111
|
return text;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
// src/session-events.ts
|
|
115
|
+
function eventsOf(session) {
|
|
116
|
+
const s = session;
|
|
117
|
+
if (s.snapshotEvents !== void 0) return s.snapshotEvents();
|
|
118
|
+
if (s.events !== void 0) return s.events;
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
|
|
114
122
|
// src/settings-locale.ts
|
|
115
123
|
function readSettingsSection(provider, ns, brand) {
|
|
116
124
|
const key = brand?.(ns) ?? ns;
|
|
@@ -1516,7 +1524,7 @@ function mutationPathOf(exec) {
|
|
|
1516
1524
|
return void 0;
|
|
1517
1525
|
}
|
|
1518
1526
|
function anchorSeqOf(session, cache) {
|
|
1519
|
-
const events = session
|
|
1527
|
+
const events = eventsOf(session);
|
|
1520
1528
|
const cached = cache.get(session);
|
|
1521
1529
|
if (cached !== void 0 && cached.eventsLength === events.length) return cached.anchor;
|
|
1522
1530
|
let anchor = cached?.anchor;
|
|
@@ -1694,15 +1702,15 @@ async function executeRewind(ctx, store, fs, invocation, rawTarget, mode, inflig
|
|
|
1694
1702
|
}
|
|
1695
1703
|
let plan;
|
|
1696
1704
|
try {
|
|
1697
|
-
plan = resolveOrError(agent.session
|
|
1705
|
+
plan = resolveOrError(eventsOf(agent.session), agent.session.surface.nodes, rawTarget);
|
|
1698
1706
|
} catch (error) {
|
|
1699
1707
|
return rewindErrorResult(error);
|
|
1700
1708
|
}
|
|
1701
1709
|
const marker = buildMarker();
|
|
1702
1710
|
let event;
|
|
1703
1711
|
try {
|
|
1704
|
-
const turn = markerTurnOf(agent.session
|
|
1705
|
-
const step = markerStepOf(agent.session
|
|
1712
|
+
const turn = markerTurnOf(eventsOf(agent.session));
|
|
1713
|
+
const step = markerStepOf(eventsOf(agent.session), turn);
|
|
1706
1714
|
agent.session.append("step/start", { turn, step });
|
|
1707
1715
|
try {
|
|
1708
1716
|
event = agent.session.append("assistant/message", { turn, step, message: marker }, {
|
|
@@ -1756,7 +1764,7 @@ async function handleRewind(ctx, store, fs, invocation, inflight) {
|
|
|
1756
1764
|
const session = invocation.agent.session;
|
|
1757
1765
|
const input = invocation.rawInput.trim();
|
|
1758
1766
|
if (input === "") {
|
|
1759
|
-
const candidates = listRewindCandidates(session
|
|
1767
|
+
const candidates = listRewindCandidates(eventsOf(session), session.surface.nodes, 1);
|
|
1760
1768
|
if (candidates.length === 0) {
|
|
1761
1769
|
return { kind: "error", text: t("noUserMessages") };
|
|
1762
1770
|
}
|
|
@@ -1768,7 +1776,7 @@ async function handleRewind(ctx, store, fs, invocation, inflight) {
|
|
|
1768
1776
|
if (target2 === void 0) return { kind: "error", text: usage() };
|
|
1769
1777
|
let plan;
|
|
1770
1778
|
try {
|
|
1771
|
-
plan = resolveOrError(session
|
|
1779
|
+
plan = resolveOrError(eventsOf(session), session.surface.nodes, target2);
|
|
1772
1780
|
} catch (error) {
|
|
1773
1781
|
return rewindErrorResult(error);
|
|
1774
1782
|
}
|
|
@@ -1776,7 +1784,7 @@ async function handleRewind(ctx, store, fs, invocation, inflight) {
|
|
|
1776
1784
|
return { kind: "success", text: formatPlan(plan, impacts) };
|
|
1777
1785
|
}
|
|
1778
1786
|
if (parts[0] === "__candidates") {
|
|
1779
|
-
const candidates = listRewindCandidates(session
|
|
1787
|
+
const candidates = listRewindCandidates(eventsOf(session), session.surface.nodes);
|
|
1780
1788
|
return { kind: "success", text: formatCandidateList(candidates) };
|
|
1781
1789
|
}
|
|
1782
1790
|
const target = parts[0];
|
|
@@ -125,6 +125,28 @@ export declare function writeComposer(text: string, facade: ComposerDraftWriter
|
|
|
125
125
|
* after switching sessions or restarting dsh.
|
|
126
126
|
*/
|
|
127
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>;
|
|
128
|
+
/**
|
|
129
|
+
* Locate the actions container of a user/steering seat row — the element the
|
|
130
|
+
* ↶ button portals into (the copy/branch IconActions row).
|
|
131
|
+
*
|
|
132
|
+
* Dual channel:
|
|
133
|
+
* - rc.2 (0.1.1-rc.2) / alpha.2+ (0.1.2-alpha.2): the hover-reveal root is
|
|
134
|
+
* attribute-marked (`[data-time-hover-root]` ≤ rc.x, `[data-actions-reveal]`
|
|
135
|
+
* alpha.2+) and mounts the actions row as its LAST child. A pending row IS
|
|
136
|
+
* that root; a durable row CONTAINS it as a descendant.
|
|
137
|
+
* - alpha.4 (0.1.2-alpha.4): the marker was removed from user rows (it now
|
|
138
|
+
* lives only on the per-turn tail footer, `TurnTailNodeView`), and the user
|
|
139
|
+
* action row is revealed via CSS `:has()`. The container is instead located
|
|
140
|
+
* structurally: the direct holder of the copy `<button>` (the
|
|
141
|
+
* `MessageIconActions` container, which mounts that button as a direct
|
|
142
|
+
* child — `MessageIconActions.tsx:83,86`).
|
|
143
|
+
*
|
|
144
|
+
* Returns undefined when no qualifying container is found; the caller refuses
|
|
145
|
+
* to portal (never a crash, never a wrong attachment). Exported as a test seam
|
|
146
|
+
* (see `collectTargets`) so the dual-channel finder is exercised directly for
|
|
147
|
+
* both durable and pending row shapes without a full React portal render.
|
|
148
|
+
*/
|
|
149
|
+
export declare function actionsContainerOf(row: HTMLElement | undefined): HTMLElement | undefined;
|
|
128
150
|
/**
|
|
129
151
|
* Collect the portal targets of one session: user rows × snapshot nodes.
|
|
130
152
|
* Exported as a test seam — the DOM→targets pairing that drives the ↶ button
|
|
@@ -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 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.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";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-channel session event-log reader.
|
|
3
|
+
*
|
|
4
|
+
* Harness 0.1.1-rc.2 exposes the full log as `Session.events`
|
|
5
|
+
* (`get events(): readonly SessionEvent[]`), while 0.1.2-alpha.4 removed that
|
|
6
|
+
* member and replaced it with the on-demand APIs `snapshotEvents(from?, to?)`
|
|
7
|
+
* (half-open range, internally cached), `eventAt(seq)`, `ownEvents()` and
|
|
8
|
+
* `seq`. This module reads the full log through whichever channel the host
|
|
9
|
+
* exposes, so the plugin keeps one code path across both.
|
|
10
|
+
*
|
|
11
|
+
* `snapshotEvents()` is deliberately preferred over `ownEvents()`:
|
|
12
|
+
* `snapshotEvents()` defaults to the whole log (including any fork-inherited
|
|
13
|
+
* prefix), which is exactly what `Session.events` returned on rc.2 — the
|
|
14
|
+
* rewind semantics are preserved for fork-seeded sessions. `ownEvents()`
|
|
15
|
+
* would drop the inherited prefix and change behaviour.
|
|
16
|
+
*
|
|
17
|
+
* @module dsh-rewind/session-events
|
|
18
|
+
*/
|
|
19
|
+
import type { Session, SessionEvent } from '@deepseek-ai/dsh-session';
|
|
20
|
+
/**
|
|
21
|
+
* Read the full event log of a session, transparently across harness channels:
|
|
22
|
+
* - 0.1.1-rc.2 (`Session.events`): the live immutable log array.
|
|
23
|
+
* - 0.1.2-alpha.4 (`Session.snapshotEvents()`): a deep-frozen snapshot, cached
|
|
24
|
+
* by the session until the next append.
|
|
25
|
+
*
|
|
26
|
+
* Falls back to an empty log when the session exposes neither member (a future
|
|
27
|
+
* harness shape) rather than throwing — callers already treat the empty log as
|
|
28
|
+
* "no candidates".
|
|
29
|
+
*
|
|
30
|
+
* @param session - the session to read.
|
|
31
|
+
* @returns the full event log in log order (the same shape rc.2's `events`
|
|
32
|
+
* returned, so existing consumers are unchanged).
|
|
33
|
+
*/
|
|
34
|
+
export declare function eventsOf(session: Session): readonly SessionEvent[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-rewind-plugin",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
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",
|