dsh-rewind-plugin 0.1.10 → 0.2.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/README.md +182 -174
- package/README.zh.md +214 -0
- package/assets/screenshots/guard-hint.png +0 -0
- package/assets/screenshots/impact-list.png +0 -0
- package/assets/screenshots/mode-popover.png +0 -0
- package/assets/screenshots/rewind-button.png +0 -0
- package/docs/harness-reference.md +34 -0
- package/lib/client.js +139 -95
- package/lib/index.js +237 -148
- package/lib/types/client/hidden.d.ts +33 -0
- package/lib/types/client/index.d.ts +4 -4
- package/lib/types/client/locales.d.ts +6 -2
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/index.d.ts +39 -16
- package/lib/types/session-cwd.d.ts +1 -1
- package/lib/types/snapshot.d.ts +116 -0
- package/package.json +4 -3
- package/lib/types/ledger.d.ts +0 -88
- package/scripts/build.mjs +0 -89
- package/scripts/verify-host.mjs +0 -179
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# DeepSeek Harness interface reference
|
|
2
|
+
|
|
3
|
+
> Maintainer doc: the harness subsystems this plugin depends on, and the key
|
|
4
|
+
> source files behind each interface. Local fork (if present):
|
|
5
|
+
> `<workspace>/oss/deepseek-harness/` — official repo:
|
|
6
|
+
> [github.com/deepseek-ai/deepseek-harness](https://github.com/deepseek-ai/deepseek-harness)
|
|
7
|
+
|
|
8
|
+
## Subsystem docs (`docs/subsystems/`)
|
|
9
|
+
|
|
10
|
+
- [session.md](https://github.com/deepseek-ai/deepseek-harness/blob/main/docs/subsystems/session.md) — `Session` / `SessionStore` / event model (`Session.append`, `surfaceOp`, `sourceEventSeqs`)
|
|
11
|
+
- [core.md](https://github.com/deepseek-ai/deepseek-harness/blob/main/docs/subsystems/core.md) — `Agent` (`status`, `session`) and other core types
|
|
12
|
+
- [commands.md](https://github.com/deepseek-ai/deepseek-harness/blob/main/docs/subsystems/commands.md) — command registration (`ctx.commands.register`, `CommandInvocation`, `CommandResult`)
|
|
13
|
+
- [tools.md](https://github.com/deepseek-ai/deepseek-harness/blob/main/docs/subsystems/tools.md) — tool execution seam (`tools/pre-execute` / `tools/post-execute`, `ToolExecution`)
|
|
14
|
+
- [session-query.md](https://github.com/deepseek-ai/deepseek-harness/blob/main/docs/subsystems/session-query.md) — session query / `foldSurface` read-only interfaces
|
|
15
|
+
|
|
16
|
+
Also under `docs/` at the repo root: `persistence-catalog.md` (full
|
|
17
|
+
`SessionEventMap`), `tool-catalog.md` (tool inventory), `config-catalog.md`
|
|
18
|
+
(configuration inventory).
|
|
19
|
+
|
|
20
|
+
## Key source (`packages/`)
|
|
21
|
+
|
|
22
|
+
| Interface | File |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `Session.append`, surface validation | [packages/core/session/src/index.ts](https://github.com/deepseek-ai/deepseek-harness/blob/main/packages/core/session/src/index.ts) |
|
|
25
|
+
| `foldSurface`, replacement rules | [packages/core/session/src/surface.ts](https://github.com/deepseek-ai/deepseek-harness/blob/main/packages/core/session/src/surface.ts) |
|
|
26
|
+
| `SessionEventMap`, `SurfaceOp` | [packages/core/session/src/types.ts](https://github.com/deepseek-ai/deepseek-harness/blob/main/packages/core/session/src/types.ts) |
|
|
27
|
+
| `createUserMessage`, `MessageSource` | [packages/llm/llm/src/message.ts](https://github.com/deepseek-ai/deepseek-harness/blob/main/packages/llm/llm/src/message.ts) |
|
|
28
|
+
| `CommandDefinition`, `CommandInvocation` | [packages/interaction/commands/src/index.ts](https://github.com/deepseek-ai/deepseek-harness/blob/main/packages/interaction/commands/src/index.ts) |
|
|
29
|
+
| `Agent` (`status` / `session`) | [packages/core/agent/src/runtime-types.ts](https://github.com/deepseek-ai/deepseek-harness/blob/main/packages/core/agent/src/runtime-types.ts) |
|
|
30
|
+
| `tools/pre-execute` / `execute` / `post-execute` | [packages/core/tools/src/index.ts](https://github.com/deepseek-ai/deepseek-harness/blob/main/packages/core/tools/src/index.ts) |
|
|
31
|
+
| Client DOM anchors (`data-chat-flow-kind` / `data-chat-anchor-key`) | [packages/client/ui-conversation/src/client/chat/ChatNodeSeat.tsx](https://github.com/deepseek-ai/deepseek-harness/blob/main/packages/client/ui-conversation/src/client/chat/ChatNodeSeat.tsx) |
|
|
32
|
+
| User bubble rendering | [packages/client/ui-conversation/src/client/chat/MessageItem.tsx](https://github.com/deepseek-ai/deepseek-harness/blob/main/packages/client/ui-conversation/src/client/chat/MessageItem.tsx) |
|
|
33
|
+
| Client `SessionFace` (`command` / `cancel`) | [packages/client/runtime/src/client/contract/session.ts](https://github.com/deepseek-ai/deepseek-harness/blob/main/packages/client/runtime/src/client/contract/session.ts) |
|
|
34
|
+
| Client `PendingWait` (`respond`) | [packages/client/runtime/src/client/sessions/pending.ts](https://github.com/deepseek-ai/deepseek-harness/blob/main/packages/client/runtime/src/client/sessions/pending.ts) |
|
package/lib/client.js
CHANGED
|
@@ -32,40 +32,80 @@ __export(index_exports, {
|
|
|
32
32
|
});
|
|
33
33
|
module.exports = __toCommonJS(index_exports);
|
|
34
34
|
|
|
35
|
+
// src/client/hidden.ts
|
|
36
|
+
function targetOfOutcome(text) {
|
|
37
|
+
if (text === void 0) return void 0;
|
|
38
|
+
const match = text.match(/seq (\d+)/);
|
|
39
|
+
return match !== null ? Number(match[1]) : void 0;
|
|
40
|
+
}
|
|
41
|
+
function hiddenSeqsOf(snap) {
|
|
42
|
+
const hidden = /* @__PURE__ */ new Set();
|
|
43
|
+
let minTarget = Number.POSITIVE_INFINITY;
|
|
44
|
+
let maxMarker = Number.NEGATIVE_INFINITY;
|
|
45
|
+
for (const key of snap.order) {
|
|
46
|
+
const node = snap.nodes.get(key);
|
|
47
|
+
if (node === void 0 || node.kind !== "command") continue;
|
|
48
|
+
const command = node.data;
|
|
49
|
+
if (command.name !== "rewind") continue;
|
|
50
|
+
if (command.outcome?.kind !== "success") continue;
|
|
51
|
+
if (command.outcome.sourceEventSeq === void 0) continue;
|
|
52
|
+
hidden.add(command.seq);
|
|
53
|
+
const marker = command.outcome.sourceEventSeq;
|
|
54
|
+
const target = targetOfOutcome(command.outcome.text);
|
|
55
|
+
if (target !== void 0) {
|
|
56
|
+
if (target < minTarget) minTarget = target;
|
|
57
|
+
if (marker > maxMarker) maxMarker = marker;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (Number.isFinite(minTarget)) {
|
|
61
|
+
for (const key of snap.order) {
|
|
62
|
+
const node = snap.nodes.get(key);
|
|
63
|
+
if (node === void 0) continue;
|
|
64
|
+
const anchor = node.anchorSeq;
|
|
65
|
+
if (anchor >= minTarget && anchor <= maxMarker) hidden.add(anchor);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return hidden;
|
|
69
|
+
}
|
|
70
|
+
|
|
35
71
|
// src/client/locales.ts
|
|
36
72
|
var zh = {
|
|
37
73
|
"button.aria": "\u56DE\u9000\u5230\u6B64\u6D88\u606F",
|
|
38
74
|
"button.title": "\u56DE\u9000",
|
|
39
75
|
"popover.title": "\u56DE\u9000\u5230\u8FD9\u6761\u6D88\u606F",
|
|
40
|
-
"popover.
|
|
76
|
+
"popover.noText": "\uFF08\u65E0\u6587\u672C\uFF09",
|
|
41
77
|
"popover.chat": "\u4EC5\u56DE\u9000\u5BF9\u8BDD",
|
|
42
78
|
"popover.chat.hint": "\u53EA\u56DE\u9000\u6A21\u578B\u4E0A\u4E0B\u6587\uFF0C\u4E0D\u52A8\u5DE5\u4F5C\u533A\u6587\u4EF6",
|
|
43
79
|
"popover.both": "\u56DE\u9000\u5BF9\u8BDD\u548C\u4EE3\u7801",
|
|
44
80
|
"popover.both.hint": "\u5BF9\u8BDD\u56DE\u9000\u5E76\u8FD8\u539F\u5DE5\u4F5C\u533A\u6587\u4EF6",
|
|
81
|
+
"popover.checking": "\u6B63\u5728\u68C0\u67E5\u6587\u4EF6\u53D8\u66F4\u2026",
|
|
82
|
+
"popover.noChanges": "\u6B64\u6D88\u606F\u4E4B\u540E\u6CA1\u6709\u53EF\u8FD8\u539F\u7684\u6587\u4EF6\u53D8\u66F4\uFF0C\u4EC5\u53EF\u56DE\u9000\u5BF9\u8BDD",
|
|
45
83
|
"popover.cancel": "\u53D6\u6D88",
|
|
46
84
|
"popover.impact.loading": "\u6B63\u5728\u83B7\u53D6\u5F71\u54CD\u6E05\u5355\u2026",
|
|
47
85
|
"popover.impact.failed": "\u65E0\u6CD5\u83B7\u53D6\u5F71\u54CD\u6E05\u5355\uFF1A{message}",
|
|
48
|
-
"popover.impact.none": "\u76EE\u6807\u4E4B\u540E\u6CA1\u6709\
|
|
86
|
+
"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",
|
|
49
87
|
"popover.confirm": "\u786E\u8BA4\u56DE\u9000",
|
|
50
88
|
"popover.back": "\u8FD4\u56DE",
|
|
51
|
-
"guard.hint": "/rewind \
|
|
89
|
+
"guard.hint": "/rewind \u547D\u4EE4\u4E0D\u652F\u6301\u624B\u52A8\u8F93\u5165\uFF0C\u8BF7\u4F7F\u7528\u6D88\u606F\u65C1\u7684 \u21B6 \u56DE\u9000\u6309\u94AE"
|
|
52
90
|
};
|
|
53
91
|
var en = {
|
|
54
92
|
"button.aria": "Rewind to this message",
|
|
55
93
|
"button.title": "Rewind",
|
|
56
94
|
"popover.title": "Rewind to this message",
|
|
57
|
-
"popover.
|
|
95
|
+
"popover.noText": "(no text)",
|
|
58
96
|
"popover.chat": "Rewind conversation only",
|
|
59
97
|
"popover.chat.hint": "Cut the model context only; workspace files stay untouched",
|
|
60
98
|
"popover.both": "Rewind conversation and code",
|
|
61
99
|
"popover.both.hint": "Cut the context and restore workspace files",
|
|
100
|
+
"popover.checking": "Checking for file changes\u2026",
|
|
101
|
+
"popover.noChanges": "No tracked file changes after this message; conversation-only rewind",
|
|
62
102
|
"popover.cancel": "Cancel",
|
|
63
103
|
"popover.impact.loading": "Fetching impact list\u2026",
|
|
64
104
|
"popover.impact.failed": "Could not fetch the impact list: {message}",
|
|
65
|
-
"popover.impact.none": "No
|
|
105
|
+
"popover.impact.none": "No tracked file changes after the target; nothing to restore.",
|
|
66
106
|
"popover.confirm": "Confirm rewind",
|
|
67
107
|
"popover.back": "Back",
|
|
68
|
-
"guard.hint": "/rewind
|
|
108
|
+
"guard.hint": "/rewind cannot be typed manually \u2014 use the \u21B6 button on a message to rewind."
|
|
69
109
|
};
|
|
70
110
|
|
|
71
111
|
// src/client/styles.ts
|
|
@@ -114,9 +154,10 @@ var STYLE = `
|
|
|
114
154
|
z-index: 1000;
|
|
115
155
|
width: 288px;
|
|
116
156
|
padding: 12px;
|
|
157
|
+
border: 1px solid var(--dsw-alias-border-l2);
|
|
117
158
|
border-radius: 12px;
|
|
118
|
-
background: var(--dsw-specific-
|
|
119
|
-
box-shadow:
|
|
159
|
+
background: var(--dsw-specific-menu, var(--dsw-alias-bg-layer-3));
|
|
160
|
+
box-shadow: var(--dsw-shadow-lv3);
|
|
120
161
|
font-size: 14px;
|
|
121
162
|
line-height: 20px;
|
|
122
163
|
color: var(--dsw-alias-label-primary);
|
|
@@ -151,6 +192,10 @@ var STYLE = `
|
|
|
151
192
|
.dsh-rewind-popover-option:hover {
|
|
152
193
|
background: var(--dsw-alias-interactive-bg-hover);
|
|
153
194
|
}
|
|
195
|
+
.dsh-rewind-popover-option:disabled {
|
|
196
|
+
opacity: 0.5;
|
|
197
|
+
cursor: default;
|
|
198
|
+
}
|
|
154
199
|
.dsh-rewind-popover-option-label {
|
|
155
200
|
font-weight: 500;
|
|
156
201
|
}
|
|
@@ -187,8 +232,11 @@ var STYLE = `
|
|
|
187
232
|
cursor: pointer;
|
|
188
233
|
}
|
|
189
234
|
.dsh-rewind-popover-primary {
|
|
190
|
-
background: var(--dsw-alias-
|
|
191
|
-
color: var(--dsw-alias-
|
|
235
|
+
background: var(--dsw-alias-button-primary-fill);
|
|
236
|
+
color: var(--dsw-alias-label-primary-foreground);
|
|
237
|
+
}
|
|
238
|
+
.dsh-rewind-popover-primary:hover:not(:disabled) {
|
|
239
|
+
background: var(--dsw-alias-button-primary-hover);
|
|
192
240
|
}
|
|
193
241
|
.dsh-rewind-popover-primary:disabled {
|
|
194
242
|
opacity: 0.5;
|
|
@@ -207,9 +255,10 @@ var STYLE = `
|
|
|
207
255
|
z-index: 1000;
|
|
208
256
|
max-width: min(440px, calc(100vw - 24px));
|
|
209
257
|
padding: 8px 12px;
|
|
258
|
+
border: 1px solid var(--dsw-alias-border-l2);
|
|
210
259
|
border-radius: 10px;
|
|
211
|
-
background: var(--dsw-specific-
|
|
212
|
-
box-shadow:
|
|
260
|
+
background: var(--dsw-specific-menu, var(--dsw-alias-bg-layer-3));
|
|
261
|
+
box-shadow: var(--dsw-shadow-lv3);
|
|
213
262
|
font-size: 13px;
|
|
214
263
|
line-height: 18px;
|
|
215
264
|
color: var(--dsw-alias-label-primary);
|
|
@@ -230,11 +279,11 @@ function closePopover() {
|
|
|
230
279
|
disposeOutside = null;
|
|
231
280
|
}
|
|
232
281
|
}
|
|
233
|
-
function formatTarget(seq, time, preview) {
|
|
282
|
+
function formatTarget(t, seq, time, preview) {
|
|
234
283
|
const d = new Date(time);
|
|
235
284
|
const hh = String(d.getHours()).padStart(2, "0");
|
|
236
285
|
const mm = String(d.getMinutes()).padStart(2, "0");
|
|
237
|
-
const previewText = preview.length > 0 ? preview : "
|
|
286
|
+
const previewText = preview.length > 0 ? preview : t("popover.noText");
|
|
238
287
|
return `seq ${seq} \xB7 ${hh}:${mm} \xB7 ${previewText}`;
|
|
239
288
|
}
|
|
240
289
|
function findCommand(snapshot, match) {
|
|
@@ -269,6 +318,15 @@ function waitForCommand(session, match, timeoutMs = 8e3) {
|
|
|
269
318
|
check();
|
|
270
319
|
});
|
|
271
320
|
}
|
|
321
|
+
function isPreviewFor(node, seq) {
|
|
322
|
+
const args = node.args ?? "";
|
|
323
|
+
return node.name === "rewind" && args.includes("preview") && new RegExp(`(?:^|\\s)@${seq}(?=\\s|$)`).test(args);
|
|
324
|
+
}
|
|
325
|
+
async function previewImpact(session, seq) {
|
|
326
|
+
const result = await session.command(`/rewind preview @${seq} both`);
|
|
327
|
+
if (!result.ok || result.value?.matched !== true) return null;
|
|
328
|
+
return waitForCommand(session, (node) => isPreviewFor(node, seq));
|
|
329
|
+
}
|
|
272
330
|
function el(tag, className, text) {
|
|
273
331
|
const node = document.createElement(tag);
|
|
274
332
|
node.className = className;
|
|
@@ -285,7 +343,7 @@ function modeOption(label, hint, onClick) {
|
|
|
285
343
|
button.addEventListener("click", onClick);
|
|
286
344
|
return button;
|
|
287
345
|
}
|
|
288
|
-
function renderImpactStep(root, opts, back) {
|
|
346
|
+
function renderImpactStep(root, opts, back, cached) {
|
|
289
347
|
const { session, seq, t } = opts;
|
|
290
348
|
const impact = el("div", CLASS.popoverImpact, t("popover.impact.loading"));
|
|
291
349
|
const actions = el("div", CLASS.popoverActions);
|
|
@@ -302,24 +360,10 @@ function renderImpactStep(root, opts, back) {
|
|
|
302
360
|
confirm.disabled = true;
|
|
303
361
|
actions.append(confirm);
|
|
304
362
|
root.replaceChildren(impact, actions);
|
|
305
|
-
let outcome = null;
|
|
306
363
|
void (async () => {
|
|
307
|
-
const
|
|
308
|
-
if (!result.ok || result.value?.matched !== true) {
|
|
309
|
-
impact.textContent = t("popover.impact.failed", {
|
|
310
|
-
message: result.ok ? "command not matched" : result.error?.message ?? "unknown error"
|
|
311
|
-
});
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
outcome = await waitForCommand(
|
|
315
|
-
session,
|
|
316
|
-
(node) => {
|
|
317
|
-
const args = node.args ?? "";
|
|
318
|
-
return node.name === "rewind" && args.includes("preview") && new RegExp(`(?:^|\\s)@${seq}(?=\\s|$)`).test(args);
|
|
319
|
-
}
|
|
320
|
-
);
|
|
364
|
+
const outcome = cached ?? await previewImpact(session, seq);
|
|
321
365
|
if (outcome === null) {
|
|
322
|
-
impact.textContent = t("popover.impact.failed", { message: "
|
|
366
|
+
impact.textContent = t("popover.impact.failed", { message: "preview command failed or timed out" });
|
|
323
367
|
return;
|
|
324
368
|
}
|
|
325
369
|
if (outcome.kind === "error") {
|
|
@@ -342,41 +386,65 @@ function openPopover(opts) {
|
|
|
342
386
|
const root = el("div", CLASS.popover);
|
|
343
387
|
root.setAttribute("role", "dialog");
|
|
344
388
|
root.setAttribute("aria-label", t("popover.title"));
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
el("div", CLASS.popoverTarget, formatTarget(seq, time, preview))
|
|
348
|
-
);
|
|
389
|
+
let bothState = { state: "loading" };
|
|
390
|
+
let impactOutcome = null;
|
|
349
391
|
const renderModes = () => {
|
|
350
|
-
|
|
392
|
+
const children = [
|
|
351
393
|
el("div", CLASS.popoverTitle, t("popover.title")),
|
|
352
|
-
el("div", CLASS.popoverTarget, formatTarget(seq, time, preview)),
|
|
394
|
+
el("div", CLASS.popoverTarget, formatTarget(t, seq, time, preview)),
|
|
353
395
|
modeOption(t("popover.chat"), t("popover.chat.hint"), () => {
|
|
354
396
|
closePopover();
|
|
355
397
|
void session.command(`/rewind @${seq} chat`);
|
|
356
|
-
})
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
398
|
+
})
|
|
399
|
+
];
|
|
400
|
+
if (bothState.state === "noChanges") {
|
|
401
|
+
children.push(el("div", CLASS.popoverImpact, t("popover.noChanges")));
|
|
402
|
+
} else {
|
|
403
|
+
const option = modeOption(
|
|
404
|
+
t("popover.both"),
|
|
405
|
+
bothState.state === "loading" ? t("popover.checking") : t("popover.both.hint"),
|
|
406
|
+
() => {
|
|
407
|
+
renderImpactStep(root, opts, renderModes, impactOutcome);
|
|
408
|
+
}
|
|
409
|
+
);
|
|
410
|
+
if (bothState.state === "loading") option.disabled = true;
|
|
411
|
+
children.push(option);
|
|
412
|
+
}
|
|
413
|
+
const actions = el("div", CLASS.popoverActions);
|
|
414
|
+
const cancel = document.createElement("button");
|
|
415
|
+
cancel.type = "button";
|
|
416
|
+
cancel.className = CLASS.popoverGhost;
|
|
417
|
+
cancel.textContent = t("popover.cancel");
|
|
418
|
+
cancel.addEventListener("click", closePopover);
|
|
419
|
+
actions.append(cancel);
|
|
420
|
+
children.push(actions);
|
|
421
|
+
root.replaceChildren(...children);
|
|
422
|
+
};
|
|
423
|
+
const position = () => {
|
|
424
|
+
const rect = anchor.getBoundingClientRect();
|
|
425
|
+
const gap = 4;
|
|
426
|
+
const height = root.offsetHeight;
|
|
427
|
+
const top = rect.bottom + gap + height <= window.innerHeight - 8 ? rect.bottom + gap : Math.max(8, rect.top - gap - height);
|
|
428
|
+
root.style.top = `${Math.round(top)}px`;
|
|
429
|
+
root.style.left = `${Math.round(Math.min(rect.right, window.innerWidth - 8 - root.offsetWidth))}px`;
|
|
371
430
|
};
|
|
372
431
|
renderModes();
|
|
373
432
|
document.body.append(root);
|
|
374
|
-
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
433
|
+
position();
|
|
434
|
+
const hasFileImpact = (text) => text === void 0 || text.includes("\u5C06\u5F71\u54CD");
|
|
435
|
+
void (async () => {
|
|
436
|
+
const outcome = await previewImpact(session, seq);
|
|
437
|
+
impactOutcome = outcome;
|
|
438
|
+
if (outcome !== null && outcome.kind === "success") {
|
|
439
|
+
bothState = { state: hasFileImpact(outcome.text) ? "hasChanges" : "noChanges" };
|
|
440
|
+
}
|
|
441
|
+
renderModes();
|
|
442
|
+
position();
|
|
443
|
+
})().catch(() => {
|
|
444
|
+
bothState = { state: "hasChanges" };
|
|
445
|
+
renderModes();
|
|
446
|
+
position();
|
|
447
|
+
});
|
|
380
448
|
popoverEl = root;
|
|
381
449
|
const onPointerDown = (event) => {
|
|
382
450
|
const target = event.target;
|
|
@@ -405,41 +473,9 @@ var USER_SEAT_SELECTOR = '[data-chat-flow-kind="user"]';
|
|
|
405
473
|
var CHAT_SEAT_SELECTOR = "[data-chat-anchor-key]";
|
|
406
474
|
var ACTIONS_ROOT_SELECTOR = "[data-time-hover-root]";
|
|
407
475
|
var COMPOSER_SELECTOR = "[data-input-scroll] textarea, textarea[data-phase]";
|
|
408
|
-
function targetOfOutcome(text) {
|
|
409
|
-
if (text === void 0) return void 0;
|
|
410
|
-
const match = text.match(/seq (\d+)/);
|
|
411
|
-
return match !== null ? Number(match[1]) : void 0;
|
|
412
|
-
}
|
|
413
476
|
function messagePreviewOf(node) {
|
|
414
|
-
|
|
415
|
-
}
|
|
416
|
-
function hiddenSeqsOf(session) {
|
|
417
|
-
const hidden = /* @__PURE__ */ new Set();
|
|
418
|
-
const snap = session.getSnapshot();
|
|
419
|
-
let latest = null;
|
|
420
|
-
for (const key of snap.chat.order) {
|
|
421
|
-
const node = snap.chat.nodes.get(key);
|
|
422
|
-
if (node === void 0 || node.kind !== "command") continue;
|
|
423
|
-
const command = node.data;
|
|
424
|
-
if (command.name !== "rewind") continue;
|
|
425
|
-
if (command.outcome?.kind !== "success") continue;
|
|
426
|
-
if (command.outcome.sourceEventSeq === void 0) continue;
|
|
427
|
-
hidden.add(command.seq);
|
|
428
|
-
const marker = command.outcome.sourceEventSeq;
|
|
429
|
-
const target = targetOfOutcome(command.outcome.text);
|
|
430
|
-
if (target !== void 0 && (latest === null || marker > latest.marker)) {
|
|
431
|
-
latest = { marker, target };
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
if (latest !== null) {
|
|
435
|
-
for (const key of snap.chat.order) {
|
|
436
|
-
const node = snap.chat.nodes.get(key);
|
|
437
|
-
if (node === void 0) continue;
|
|
438
|
-
const anchor = node.anchorSeq;
|
|
439
|
-
if (anchor >= latest.target && anchor <= latest.marker) hidden.add(anchor);
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
return hidden;
|
|
477
|
+
const text = node.content.map((block) => block.type === "text" && typeof block.text === "string" ? block.text : "").join("").replace(/\s+/g, " ").trim();
|
|
478
|
+
return text.length <= 80 ? text : `${text.slice(0, 79)}\u2026`;
|
|
443
479
|
}
|
|
444
480
|
function userTextAt(session, seq) {
|
|
445
481
|
const snap = session.getSnapshot();
|
|
@@ -474,6 +510,13 @@ function apply(ctx) {
|
|
|
474
510
|
const hidden = /* @__PURE__ */ new WeakSet();
|
|
475
511
|
const buttons = /* @__PURE__ */ new Map();
|
|
476
512
|
let observer = null;
|
|
513
|
+
const refreshButtonLabels = () => {
|
|
514
|
+
for (const button of buttons.values()) {
|
|
515
|
+
button.setAttribute("aria-label", t("button.aria"));
|
|
516
|
+
button.title = t("button.title");
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
const unsubscribeLocale = ctx.locale.subscribe(refreshButtonLabels);
|
|
477
520
|
const sessionFor = () => {
|
|
478
521
|
const sessionId = ctx.sessions.list.getSnapshot().current;
|
|
479
522
|
if (sessionId === void 0) return void 0;
|
|
@@ -524,7 +567,7 @@ function apply(ctx) {
|
|
|
524
567
|
if (!button.isConnected) buttons.delete(key);
|
|
525
568
|
}
|
|
526
569
|
const session = sessionFor();
|
|
527
|
-
const hiddenSeqs = session !== void 0 ? hiddenSeqsOf(session) : /* @__PURE__ */ new Set();
|
|
570
|
+
const hiddenSeqs = session !== void 0 ? hiddenSeqsOf(session.getSnapshot().chat) : /* @__PURE__ */ new Set();
|
|
528
571
|
let hiddenCount = 0;
|
|
529
572
|
for (const seat of document.querySelectorAll(CHAT_SEAT_SELECTOR)) {
|
|
530
573
|
const key = seat.dataset.chatAnchorKey;
|
|
@@ -564,11 +607,11 @@ function apply(ctx) {
|
|
|
564
607
|
if (fillComposer(text)) filled.add(target);
|
|
565
608
|
}
|
|
566
609
|
};
|
|
567
|
-
const
|
|
610
|
+
const MANUAL_REWIND = /^\s*\/rewind(?:\s|$)/i;
|
|
568
611
|
const composerTextarea = () => document.querySelector(COMPOSER_SELECTOR);
|
|
569
612
|
const hasBlockedRewindDraft = () => {
|
|
570
613
|
const textarea = composerTextarea();
|
|
571
|
-
return textarea !== null &&
|
|
614
|
+
return textarea !== null && MANUAL_REWIND.test(textarea.value);
|
|
572
615
|
};
|
|
573
616
|
let guardHintEl = null;
|
|
574
617
|
let guardHintTimer;
|
|
@@ -621,6 +664,7 @@ function apply(ctx) {
|
|
|
621
664
|
observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ["style"] });
|
|
622
665
|
scan();
|
|
623
666
|
yield () => {
|
|
667
|
+
unsubscribeLocale();
|
|
624
668
|
document.removeEventListener("keydown", onKeyDownGuard, true);
|
|
625
669
|
document.removeEventListener("click", onClickGuard, true);
|
|
626
670
|
if (guardHintEl !== null) guardHintEl.remove();
|