dsh-rewind-plugin 0.17.1 → 0.18.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.en.md +6 -6
- package/README.md +5 -5
- package/lib/client.js +272 -217
- package/lib/index.js +69 -73
- package/lib/types/client/candidates.d.ts +2 -9
- package/lib/types/client/hidden.d.ts +5 -19
- package/lib/types/client/host-rpc.d.ts +45 -0
- package/lib/types/client/index.d.ts +3 -3
- package/lib/types/client/popover.d.ts +8 -18
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/index.d.ts +2 -2
- package/lib/types/locales.d.ts +2 -10
- package/lib/types/rewind.d.ts +7 -24
- package/lib/types/snapshot.d.ts +1 -1
- package/package.json +42 -37
package/lib/client.js
CHANGED
|
@@ -94,15 +94,6 @@ function isExecutedRewindCommand(node, seq) {
|
|
|
94
94
|
const args = node.args ?? "";
|
|
95
95
|
return new RegExp(`(?:^|\\s)@${seq}(?:\\s|$)`).test(args);
|
|
96
96
|
}
|
|
97
|
-
function hasFileImpact(text) {
|
|
98
|
-
if (text === void 0) return true;
|
|
99
|
-
const match = text.match(/impact=(\d+)/);
|
|
100
|
-
if (match !== null) return Number(match[1]) > 0;
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
function hasBackupDisabled(text) {
|
|
104
|
-
return text !== void 0 && /(?:^|\n)backup=off(?:\n|$)/.test(text);
|
|
105
|
-
}
|
|
106
97
|
function isPreviewCommand(command) {
|
|
107
98
|
return (command.args ?? "").includes("preview");
|
|
108
99
|
}
|
|
@@ -238,23 +229,6 @@ function rewindCandidatesOf(snap, hidden, limit = DEFAULT_CANDIDATE_LIMIT) {
|
|
|
238
229
|
function rewindCandidatesOfChat(snap) {
|
|
239
230
|
return rewindCandidatesOf(snap, hiddenSeqsOf(snap));
|
|
240
231
|
}
|
|
241
|
-
var CANDIDATE_LIST_HEADER = "candidates=";
|
|
242
|
-
function rewindCandidatesFromHostText(text) {
|
|
243
|
-
if (!text.startsWith(CANDIDATE_LIST_HEADER)) return [];
|
|
244
|
-
const lines = text.split("\n").slice(1);
|
|
245
|
-
const candidates = [];
|
|
246
|
-
for (const line of lines) {
|
|
247
|
-
if (line === "") continue;
|
|
248
|
-
const parts = line.split(" ");
|
|
249
|
-
if (parts.length !== 3) continue;
|
|
250
|
-
const seq = Number(parts[0]);
|
|
251
|
-
const time = Number(parts[1]);
|
|
252
|
-
const preview = parts[2] ?? "";
|
|
253
|
-
if (!Number.isSafeInteger(seq) || !Number.isFinite(time)) continue;
|
|
254
|
-
candidates.push({ seq, time, preview });
|
|
255
|
-
}
|
|
256
|
-
return candidates;
|
|
257
|
-
}
|
|
258
232
|
function rewindOptionsFromCandidates(candidates, t) {
|
|
259
233
|
return candidates.map((candidate) => ({
|
|
260
234
|
id: String(candidate.seq),
|
|
@@ -263,48 +237,6 @@ function rewindOptionsFromCandidates(candidates, t) {
|
|
|
263
237
|
}));
|
|
264
238
|
}
|
|
265
239
|
|
|
266
|
-
// src/client/log.ts
|
|
267
|
-
var NS = "dsh-rewind";
|
|
268
|
-
var DEBUG_KEY = "dsh-rewind.debug";
|
|
269
|
-
var ALWAYS_ON = /* @__PURE__ */ new Set(["error", "warn"]);
|
|
270
|
-
function switchValue() {
|
|
271
|
-
try {
|
|
272
|
-
return window.localStorage.getItem(DEBUG_KEY) ?? "";
|
|
273
|
-
} catch {
|
|
274
|
-
return "";
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
function matches(value, ns) {
|
|
278
|
-
for (const entry of value.split(",")) {
|
|
279
|
-
const part = entry.trim();
|
|
280
|
-
if (part === "") continue;
|
|
281
|
-
if (part === "*" || part === `${NS}*`) return true;
|
|
282
|
-
if (part.endsWith("*")) {
|
|
283
|
-
if (ns.startsWith(part.slice(0, -1))) return true;
|
|
284
|
-
} else if (ns === part) {
|
|
285
|
-
return true;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
return false;
|
|
289
|
-
}
|
|
290
|
-
function tag(scope) {
|
|
291
|
-
return `[${NS}:${scope}]`;
|
|
292
|
-
}
|
|
293
|
-
function log(level, scope, message, data) {
|
|
294
|
-
if (ALWAYS_ON.has(level)) {
|
|
295
|
-
console[level](tag(scope), message, data);
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
if (!matches(switchValue(), `${NS}:${scope}`)) return;
|
|
299
|
-
console.info(tag(scope), message, data);
|
|
300
|
-
}
|
|
301
|
-
var rewindLog = {
|
|
302
|
-
error: (scope, message, data) => log("error", scope, message, data),
|
|
303
|
-
warn: (scope, message, data) => log("warn", scope, message, data),
|
|
304
|
-
info: (scope, message, data) => log("info", scope, message, data),
|
|
305
|
-
debug: (scope, message, data) => log("debug", scope, message, data)
|
|
306
|
-
};
|
|
307
|
-
|
|
308
240
|
// src/client/styles.ts
|
|
309
241
|
var CLASS = {
|
|
310
242
|
button: "dsh-rewind-btn",
|
|
@@ -346,44 +278,58 @@ var STYLE = `
|
|
|
346
278
|
width: calc(15px + var(--dsh-content-font-delta, 0px));
|
|
347
279
|
height: calc(15px + var(--dsh-content-font-delta, 0px));
|
|
348
280
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
281
|
+
@media (hover: hover) {
|
|
282
|
+
.dsh-rewind-btn:hover {
|
|
283
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
284
|
+
color: var(--dsw-alias-label-secondary);
|
|
285
|
+
}
|
|
352
286
|
}
|
|
353
287
|
|
|
354
288
|
.dsh-rewind-popover {
|
|
355
289
|
position: fixed;
|
|
356
|
-
z-index:
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
border
|
|
290
|
+
z-index: 1100;
|
|
291
|
+
box-sizing: border-box;
|
|
292
|
+
width: min(300px, calc(100vw - 24px));
|
|
293
|
+
padding: 16px;
|
|
294
|
+
border: 0;
|
|
295
|
+
border-radius: var(--dsw-radius-lg);
|
|
361
296
|
background: var(--dsw-specific-menu, var(--dsw-alias-bg-layer-3));
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
297
|
+
backdrop-filter: var(--dsw-menu-backdrop-filter);
|
|
298
|
+
isolation: isolate;
|
|
299
|
+
--dsw-elevation-stroke-color: var(--dsw-alias-border-l1);
|
|
300
|
+
/* Elevated surface: match the menu cards' l2 scrollbar tokens. */
|
|
301
|
+
--dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);
|
|
302
|
+
--dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);
|
|
303
|
+
box-shadow: var(--dsw-elevation-prominent);
|
|
304
|
+
font-size: 12px;
|
|
305
|
+
line-height: 18px;
|
|
365
306
|
color: var(--dsw-alias-label-primary);
|
|
366
307
|
}
|
|
367
308
|
.dsh-rewind-popover-title {
|
|
368
|
-
font-size:
|
|
369
|
-
font-weight:
|
|
370
|
-
line-height:
|
|
309
|
+
font-size: 12px;
|
|
310
|
+
font-weight: 500;
|
|
311
|
+
line-height: 18px;
|
|
371
312
|
}
|
|
372
313
|
.dsh-rewind-popover-target {
|
|
373
|
-
margin:
|
|
314
|
+
margin: 2px 0 10px;
|
|
374
315
|
font-size: 12px;
|
|
375
|
-
line-height:
|
|
316
|
+
line-height: 18px;
|
|
376
317
|
color: var(--dsw-alias-label-tertiary);
|
|
377
318
|
word-break: break-all;
|
|
319
|
+
/* The full message sits above the popover; two lines are enough context. */
|
|
320
|
+
display: -webkit-box;
|
|
321
|
+
-webkit-box-orient: vertical;
|
|
322
|
+
-webkit-line-clamp: 2;
|
|
323
|
+
overflow: hidden;
|
|
378
324
|
}
|
|
379
325
|
.dsh-rewind-popover-option {
|
|
380
326
|
display: flex;
|
|
381
327
|
flex-direction: column;
|
|
382
328
|
gap: 2px;
|
|
383
329
|
width: 100%;
|
|
384
|
-
margin: 0 0
|
|
385
|
-
padding: 8px
|
|
386
|
-
border:
|
|
330
|
+
margin: 0 0 2px;
|
|
331
|
+
padding: 6px 8px;
|
|
332
|
+
border: 0;
|
|
387
333
|
border-radius: 8px;
|
|
388
334
|
background: transparent;
|
|
389
335
|
color: inherit;
|
|
@@ -403,7 +349,7 @@ var STYLE = `
|
|
|
403
349
|
outline: none;
|
|
404
350
|
}
|
|
405
351
|
.dsh-rewind-popover-option:disabled {
|
|
406
|
-
opacity: 0.
|
|
352
|
+
opacity: 0.4;
|
|
407
353
|
cursor: default;
|
|
408
354
|
}
|
|
409
355
|
.dsh-rewind-popover-option-label {
|
|
@@ -436,15 +382,15 @@ var STYLE = `
|
|
|
436
382
|
display: flex;
|
|
437
383
|
justify-content: flex-end;
|
|
438
384
|
gap: 8px;
|
|
385
|
+
margin-top: 8px;
|
|
439
386
|
}
|
|
440
387
|
.dsh-rewind-popover-primary,
|
|
441
388
|
.dsh-rewind-popover-ghost {
|
|
442
|
-
|
|
389
|
+
height: 28px;
|
|
390
|
+
padding: 0 10px;
|
|
443
391
|
border: none;
|
|
444
392
|
border-radius: 8px;
|
|
445
393
|
font: inherit;
|
|
446
|
-
font-size: 13px;
|
|
447
|
-
line-height: 18px;
|
|
448
394
|
cursor: pointer;
|
|
449
395
|
}
|
|
450
396
|
.dsh-rewind-popover-primary {
|
|
@@ -455,7 +401,7 @@ var STYLE = `
|
|
|
455
401
|
background: var(--dsw-alias-button-primary-hover);
|
|
456
402
|
}
|
|
457
403
|
.dsh-rewind-popover-primary:disabled {
|
|
458
|
-
opacity: 0.
|
|
404
|
+
opacity: 0.4;
|
|
459
405
|
cursor: default;
|
|
460
406
|
}
|
|
461
407
|
.dsh-rewind-popover-ghost {
|
|
@@ -465,16 +411,22 @@ var STYLE = `
|
|
|
465
411
|
.dsh-rewind-popover-ghost:hover {
|
|
466
412
|
background: var(--dsw-alias-interactive-bg-hover);
|
|
467
413
|
}
|
|
414
|
+
.dsh-rewind-popover-ghost:active {
|
|
415
|
+
background: var(--dsw-alias-interactive-bg-active);
|
|
416
|
+
}
|
|
468
417
|
|
|
469
418
|
.dsh-rewind-guard-hint {
|
|
470
419
|
position: fixed;
|
|
471
420
|
z-index: 1000;
|
|
472
421
|
max-width: min(440px, calc(100vw - 24px));
|
|
473
422
|
padding: 8px 12px;
|
|
474
|
-
border:
|
|
475
|
-
border-radius:
|
|
423
|
+
border: 0;
|
|
424
|
+
border-radius: var(--dsw-radius-md);
|
|
476
425
|
background: var(--dsw-specific-menu, var(--dsw-alias-bg-layer-3));
|
|
477
|
-
|
|
426
|
+
backdrop-filter: var(--dsw-menu-backdrop-filter);
|
|
427
|
+
isolation: isolate;
|
|
428
|
+
--dsw-elevation-stroke-color: var(--dsw-alias-border-l1);
|
|
429
|
+
box-shadow: var(--dsw-elevation-panel);
|
|
478
430
|
font-size: 13px;
|
|
479
431
|
line-height: 18px;
|
|
480
432
|
color: var(--dsw-alias-label-primary);
|
|
@@ -514,12 +466,21 @@ var STYLE = `
|
|
|
514
466
|
|
|
515
467
|
// src/client/popover.ts
|
|
516
468
|
var popoverEl = null;
|
|
469
|
+
var popoverAnchor = null;
|
|
517
470
|
var disposeOutside = null;
|
|
518
471
|
var liveSessionId = null;
|
|
519
472
|
var composerFocuser = null;
|
|
520
473
|
function registerComposerFocuser(focus) {
|
|
521
474
|
composerFocuser = focus;
|
|
522
475
|
}
|
|
476
|
+
function markOpen(anchor) {
|
|
477
|
+
popoverAnchor = anchor;
|
|
478
|
+
anchor.setAttribute("aria-expanded", "true");
|
|
479
|
+
}
|
|
480
|
+
function markClosed() {
|
|
481
|
+
popoverAnchor?.setAttribute("aria-expanded", "false");
|
|
482
|
+
popoverAnchor = null;
|
|
483
|
+
}
|
|
523
484
|
function closePopover() {
|
|
524
485
|
if (popoverEl !== null) {
|
|
525
486
|
popoverEl.remove();
|
|
@@ -529,6 +490,7 @@ function closePopover() {
|
|
|
529
490
|
disposeOutside();
|
|
530
491
|
disposeOutside = null;
|
|
531
492
|
}
|
|
493
|
+
markClosed();
|
|
532
494
|
const sessionId = liveSessionId;
|
|
533
495
|
liveSessionId = null;
|
|
534
496
|
if (sessionId !== null) {
|
|
@@ -538,22 +500,6 @@ function closePopover() {
|
|
|
538
500
|
}
|
|
539
501
|
}
|
|
540
502
|
}
|
|
541
|
-
function formatTarget(t, seq, time, preview) {
|
|
542
|
-
const d = new Date(time);
|
|
543
|
-
const hh = String(d.getHours()).padStart(2, "0");
|
|
544
|
-
const mm = String(d.getMinutes()).padStart(2, "0");
|
|
545
|
-
const previewText = preview.length > 0 ? preview : t("popover.noText");
|
|
546
|
-
return `seq ${seq} \xB7 ${hh}:${mm} \xB7 ${previewText}`;
|
|
547
|
-
}
|
|
548
|
-
function parseImpactList(text) {
|
|
549
|
-
const restores = [];
|
|
550
|
-
const deletes = [];
|
|
551
|
-
for (const line of text.split("\n")) {
|
|
552
|
-
if (line.startsWith("restore:")) restores.push(line.slice("restore:".length));
|
|
553
|
-
else if (line.startsWith("delete:")) deletes.push(line.slice("delete:".length));
|
|
554
|
-
}
|
|
555
|
-
return { restores, deletes };
|
|
556
|
-
}
|
|
557
503
|
function findCommand(chat, match) {
|
|
558
504
|
if (chat === void 0) return void 0;
|
|
559
505
|
let found;
|
|
@@ -600,28 +546,12 @@ function waitForCommand(session, chatOf, match, timeoutMs = 8e3, watch) {
|
|
|
600
546
|
check();
|
|
601
547
|
});
|
|
602
548
|
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
549
|
+
var previewCall = null;
|
|
550
|
+
function registerPreviewCall(call) {
|
|
551
|
+
previewCall = call;
|
|
606
552
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
let result;
|
|
610
|
-
try {
|
|
611
|
-
result = await session.command(`/${ENGINE_COMMAND} preview @${seq} both`);
|
|
612
|
-
} catch (error) {
|
|
613
|
-
rewindLog.warn("preview", `preview command threw for @${seq}`, error);
|
|
614
|
-
return { kind: "error", text: error instanceof Error ? error.message : String(error) };
|
|
615
|
-
}
|
|
616
|
-
if (!result.ok) {
|
|
617
|
-
rewindLog.warn("preview", `preview command rejected for @${seq}`, result.error);
|
|
618
|
-
return { kind: "error", text: `${result.error.code}: ${result.error.message}` };
|
|
619
|
-
}
|
|
620
|
-
if (result.value?.matched !== true) {
|
|
621
|
-
rewindLog.warn("preview", `preview command was not matched for @${seq}`);
|
|
622
|
-
return { kind: "error", text: "the rewind command is not registered on this host" };
|
|
623
|
-
}
|
|
624
|
-
return waitForCommand(session, chatOf, (node) => isPreviewFor(node, seq) && !known.has(node.seq), 8e3, watch);
|
|
553
|
+
function previewImpact(sessionId, seq) {
|
|
554
|
+
return previewCall?.(sessionId, seq) ?? Promise.resolve({ kind: "error", text: "host RPC is unavailable" });
|
|
625
555
|
}
|
|
626
556
|
function el(tag2, className, text) {
|
|
627
557
|
const node = document.createElement(tag2);
|
|
@@ -653,7 +583,7 @@ function moveFocus(root, dir) {
|
|
|
653
583
|
const next = index === -1 ? dir === 1 ? 0 : buttons.length - 1 : (index + dir + buttons.length) % buttons.length;
|
|
654
584
|
buttons[next]?.focus();
|
|
655
585
|
}
|
|
656
|
-
function renderImpactStep(root, opts, back, cached) {
|
|
586
|
+
function renderImpactStep(root, opts, back, reposition, cached) {
|
|
657
587
|
const { session, seq, t } = opts;
|
|
658
588
|
const impact = el("div", CLASS.popoverImpact, t("popover.impact.loading"));
|
|
659
589
|
const actions = el("div", CLASS.popoverActions);
|
|
@@ -671,34 +601,32 @@ function renderImpactStep(root, opts, back, cached) {
|
|
|
671
601
|
actions.append(confirm);
|
|
672
602
|
root.replaceChildren(impact, actions);
|
|
673
603
|
focusFirst(root);
|
|
604
|
+
reposition();
|
|
674
605
|
void (async () => {
|
|
675
|
-
const outcome = cached ?? await previewImpact(session
|
|
606
|
+
const outcome = cached ?? await previewImpact(session.sessionId, seq);
|
|
676
607
|
if (outcome === null) {
|
|
677
|
-
impact.textContent = t("popover.impact.failed", { message: "preview
|
|
608
|
+
impact.textContent = t("popover.impact.failed", { message: "preview request timed out" });
|
|
678
609
|
return;
|
|
679
610
|
}
|
|
680
611
|
if (outcome.kind === "error") {
|
|
681
|
-
impact.textContent = t("popover.impact.failed", { message: outcome.text
|
|
612
|
+
impact.textContent = t("popover.impact.failed", { message: outcome.text });
|
|
682
613
|
return;
|
|
683
614
|
}
|
|
684
|
-
|
|
615
|
+
const restores = outcome.files.filter((file) => file.action === "restore").map((file) => file.path);
|
|
616
|
+
const deletes = outcome.files.filter((file) => file.action === "delete").map((file) => file.path);
|
|
617
|
+
if (restores.length === 0 && deletes.length === 0) {
|
|
685
618
|
impact.textContent = t("popover.impact.none");
|
|
686
619
|
} else {
|
|
687
|
-
const
|
|
688
|
-
|
|
689
|
-
|
|
620
|
+
const lines = [
|
|
621
|
+
...restores.map((path) => t("popover.impact.restore", { path })),
|
|
622
|
+
...deletes.map((path) => t("popover.impact.delete", { path }))
|
|
623
|
+
];
|
|
624
|
+
if (outcome.backupOff) {
|
|
625
|
+
const list = document.createElement("div");
|
|
626
|
+
list.textContent = lines.join("\n");
|
|
627
|
+
impact.replaceChildren(list, el("div", CLASS.popoverNote, t("popover.backupDisabled")));
|
|
690
628
|
} else {
|
|
691
|
-
|
|
692
|
-
...restores.map((path) => t("popover.impact.restore", { path })),
|
|
693
|
-
...deletes.map((path) => t("popover.impact.delete", { path }))
|
|
694
|
-
];
|
|
695
|
-
if (hasBackupDisabled(outcome.text)) {
|
|
696
|
-
const list = document.createElement("div");
|
|
697
|
-
list.textContent = lines.join("\n");
|
|
698
|
-
impact.replaceChildren(list, el("div", CLASS.popoverNote, t("popover.backupDisabled")));
|
|
699
|
-
} else {
|
|
700
|
-
impact.textContent = lines.join("\n");
|
|
701
|
-
}
|
|
629
|
+
impact.textContent = lines.join("\n");
|
|
702
630
|
}
|
|
703
631
|
}
|
|
704
632
|
confirm.disabled = false;
|
|
@@ -709,16 +637,44 @@ function renderImpactStep(root, opts, back, cached) {
|
|
|
709
637
|
});
|
|
710
638
|
})().catch(() => {
|
|
711
639
|
impact.textContent = t("popover.impact.failed", { message: "unexpected error" });
|
|
640
|
+
}).finally(() => {
|
|
641
|
+
reposition();
|
|
712
642
|
});
|
|
713
643
|
}
|
|
714
644
|
function mountShell(root, anchor, onKeyDown) {
|
|
645
|
+
const margin = 12;
|
|
646
|
+
const gap = 4;
|
|
647
|
+
let flippedAbove = null;
|
|
715
648
|
const position = () => {
|
|
649
|
+
if (!root.isConnected) return;
|
|
650
|
+
if (!anchor.isConnected) {
|
|
651
|
+
if (popoverEl === root) closePopover();
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
716
654
|
const rect = anchor.getBoundingClientRect();
|
|
717
|
-
const gap = 4;
|
|
718
655
|
const height = root.offsetHeight;
|
|
719
|
-
|
|
656
|
+
if (flippedAbove === null) {
|
|
657
|
+
flippedAbove = rect.bottom + gap + height > window.innerHeight - margin;
|
|
658
|
+
}
|
|
659
|
+
const top = flippedAbove ? Math.max(margin, rect.top - gap - height) : Math.min(Math.max(margin, rect.bottom + gap), Math.max(margin, window.innerHeight - margin - height));
|
|
720
660
|
root.style.top = `${Math.round(top)}px`;
|
|
721
|
-
root.style.left = `${Math.round(Math.min(rect.right, window.innerWidth -
|
|
661
|
+
root.style.left = `${Math.round(Math.max(margin, Math.min(rect.right, window.innerWidth - margin - root.offsetWidth)))}px`;
|
|
662
|
+
};
|
|
663
|
+
let frame = 0;
|
|
664
|
+
const schedule = () => {
|
|
665
|
+
if (typeof requestAnimationFrame !== "function") {
|
|
666
|
+
position();
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
if (frame !== 0) return;
|
|
670
|
+
frame = requestAnimationFrame(() => {
|
|
671
|
+
frame = 0;
|
|
672
|
+
position();
|
|
673
|
+
});
|
|
674
|
+
};
|
|
675
|
+
const onScroll = (event) => {
|
|
676
|
+
if (event.target instanceof Node && root.contains(event.target)) return;
|
|
677
|
+
schedule();
|
|
722
678
|
};
|
|
723
679
|
const onPointerDown = (event) => {
|
|
724
680
|
const target = event.target;
|
|
@@ -729,19 +685,30 @@ function mountShell(root, anchor, onKeyDown) {
|
|
|
729
685
|
document.addEventListener("pointerdown", onPointerDown);
|
|
730
686
|
document.addEventListener("keydown", onKeyDown, true);
|
|
731
687
|
}, 0);
|
|
688
|
+
document.body.append(root);
|
|
689
|
+
position();
|
|
690
|
+
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => {
|
|
691
|
+
schedule();
|
|
692
|
+
});
|
|
693
|
+
observer?.observe(root);
|
|
694
|
+
window.addEventListener("scroll", onScroll, true);
|
|
695
|
+
window.addEventListener("resize", schedule);
|
|
732
696
|
const dispose = () => {
|
|
733
697
|
clearTimeout(deferred);
|
|
734
698
|
document.removeEventListener("pointerdown", onPointerDown);
|
|
735
699
|
document.removeEventListener("keydown", onKeyDown, true);
|
|
700
|
+
window.removeEventListener("scroll", onScroll, true);
|
|
701
|
+
window.removeEventListener("resize", schedule);
|
|
702
|
+
observer?.disconnect();
|
|
703
|
+
if (frame !== 0) cancelAnimationFrame(frame);
|
|
736
704
|
};
|
|
737
|
-
document.body.append(root);
|
|
738
|
-
position();
|
|
739
705
|
return { position, dispose };
|
|
740
706
|
}
|
|
741
707
|
function openRetractPopover(opts) {
|
|
742
708
|
closePopover();
|
|
743
709
|
const { preview, anchor, t, retract, onRetract } = opts;
|
|
744
710
|
if (retract === void 0 || onRetract === void 0) return;
|
|
711
|
+
markOpen(anchor);
|
|
745
712
|
const root = el("div", CLASS.popover);
|
|
746
713
|
root.setAttribute("role", "dialog");
|
|
747
714
|
root.setAttribute("aria-label", t("popover.retract.title"));
|
|
@@ -798,21 +765,25 @@ function openPopover(opts) {
|
|
|
798
765
|
openRetractPopover(opts);
|
|
799
766
|
return;
|
|
800
767
|
}
|
|
801
|
-
const { session, seq, time, preview, anchor, t
|
|
768
|
+
const { session, seq, time, preview, anchor, t } = opts;
|
|
802
769
|
const onRewind = opts.onRewind;
|
|
803
770
|
if (seq === void 0 || time === void 0 || onRewind === void 0) return;
|
|
804
|
-
|
|
771
|
+
markOpen(anchor);
|
|
772
|
+
const durableOpts = { session, seq, time, preview, anchor, t, onRewind };
|
|
805
773
|
const root = el("div", CLASS.popover);
|
|
806
774
|
root.setAttribute("role", "dialog");
|
|
807
775
|
root.setAttribute("aria-label", t("popover.title"));
|
|
808
776
|
let bothState = { state: "loading" };
|
|
809
777
|
let impactOutcome = null;
|
|
810
778
|
let step = "modes";
|
|
779
|
+
let reposition = () => {
|
|
780
|
+
};
|
|
811
781
|
const renderModes = () => {
|
|
812
782
|
step = "modes";
|
|
783
|
+
const previewText = preview.length > 0 ? preview : t("popover.noText");
|
|
813
784
|
const children = [
|
|
814
785
|
el("div", CLASS.popoverTitle, t("popover.title")),
|
|
815
|
-
el("div", CLASS.popoverTarget,
|
|
786
|
+
el("div", CLASS.popoverTarget, previewText),
|
|
816
787
|
modeOption(t("popover.chat"), t("popover.chat.hint"), () => {
|
|
817
788
|
closePopover();
|
|
818
789
|
durableOpts.onRewind("chat");
|
|
@@ -831,20 +802,13 @@ function openPopover(opts) {
|
|
|
831
802
|
if (bothState.state === "loading") option.disabled = true;
|
|
832
803
|
children.push(option);
|
|
833
804
|
}
|
|
834
|
-
const actions = el("div", CLASS.popoverActions);
|
|
835
|
-
const cancel = document.createElement("button");
|
|
836
|
-
cancel.type = "button";
|
|
837
|
-
cancel.className = CLASS.popoverGhost;
|
|
838
|
-
cancel.textContent = t("popover.cancel");
|
|
839
|
-
cancel.addEventListener("click", closePopover);
|
|
840
|
-
actions.append(cancel);
|
|
841
|
-
children.push(actions);
|
|
842
805
|
root.replaceChildren(...children);
|
|
843
806
|
focusFirst(root);
|
|
807
|
+
reposition();
|
|
844
808
|
};
|
|
845
809
|
const renderImpact = () => {
|
|
846
810
|
step = "impact";
|
|
847
|
-
renderImpactStep(root, durableOpts, renderModes, impactOutcome);
|
|
811
|
+
renderImpactStep(root, durableOpts, renderModes, reposition, impactOutcome);
|
|
848
812
|
};
|
|
849
813
|
const onKeyDown = (event) => {
|
|
850
814
|
if (event.key === "ArrowDown") {
|
|
@@ -868,28 +832,28 @@ function openPopover(opts) {
|
|
|
868
832
|
};
|
|
869
833
|
renderModes();
|
|
870
834
|
const shell = mountShell(root, anchor, onKeyDown);
|
|
835
|
+
reposition = shell.position;
|
|
871
836
|
popoverEl = root;
|
|
872
837
|
disposeOutside = shell.dispose;
|
|
873
838
|
void (async () => {
|
|
874
|
-
const outcome = await previewImpact(session
|
|
839
|
+
const outcome = await previewImpact(session.sessionId, seq);
|
|
875
840
|
impactOutcome = outcome;
|
|
876
841
|
if (outcome !== null && outcome.kind === "success") {
|
|
877
|
-
bothState = { state:
|
|
842
|
+
bothState = { state: outcome.files.length > 0 ? "hasChanges" : "noChanges" };
|
|
878
843
|
} else {
|
|
879
|
-
bothState = { state: "error", message: outcome?.text
|
|
844
|
+
bothState = { state: "error", message: outcome?.kind === "error" ? outcome.text : "preview request timed out" };
|
|
880
845
|
}
|
|
881
846
|
renderModes();
|
|
882
|
-
shell.position();
|
|
883
847
|
})().catch(() => {
|
|
884
848
|
bothState = { state: "error", message: "unexpected error" };
|
|
885
849
|
renderModes();
|
|
886
|
-
shell.position();
|
|
887
850
|
});
|
|
888
851
|
}
|
|
889
852
|
|
|
890
853
|
// src/client/portals.tsx
|
|
891
854
|
var import_react = require("react");
|
|
892
855
|
var import_react_dom = require("react-dom");
|
|
856
|
+
var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
893
857
|
|
|
894
858
|
// src/client/pending.ts
|
|
895
859
|
var QUEUE_PREVIEW_CHARS = 200;
|
|
@@ -921,11 +885,11 @@ function matchPendingRows(rows, steering) {
|
|
|
921
885
|
for (let i = 0; i < rows.length; i++) {
|
|
922
886
|
const row = rows[i];
|
|
923
887
|
const item = steering[i];
|
|
924
|
-
matched.push(item !== void 0 &&
|
|
888
|
+
matched.push(item !== void 0 && matches(item, row) ? item.id : null);
|
|
925
889
|
}
|
|
926
890
|
return matched;
|
|
927
891
|
}
|
|
928
|
-
function
|
|
892
|
+
function matches(item, row) {
|
|
929
893
|
if (item.attachments > 0) return row.attachments === item.attachments;
|
|
930
894
|
return row.text === (item.text ?? "");
|
|
931
895
|
}
|
|
@@ -935,6 +899,48 @@ function retractSpan(steering, targetId) {
|
|
|
935
899
|
return steering.slice(index).map((item) => item.id);
|
|
936
900
|
}
|
|
937
901
|
|
|
902
|
+
// src/client/log.ts
|
|
903
|
+
var NS = "dsh-rewind";
|
|
904
|
+
var DEBUG_KEY = "dsh-rewind.debug";
|
|
905
|
+
var ALWAYS_ON = /* @__PURE__ */ new Set(["error", "warn"]);
|
|
906
|
+
function switchValue() {
|
|
907
|
+
try {
|
|
908
|
+
return window.localStorage.getItem(DEBUG_KEY) ?? "";
|
|
909
|
+
} catch {
|
|
910
|
+
return "";
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
function matches2(value, ns) {
|
|
914
|
+
for (const entry of value.split(",")) {
|
|
915
|
+
const part = entry.trim();
|
|
916
|
+
if (part === "") continue;
|
|
917
|
+
if (part === "*" || part === `${NS}*`) return true;
|
|
918
|
+
if (part.endsWith("*")) {
|
|
919
|
+
if (ns.startsWith(part.slice(0, -1))) return true;
|
|
920
|
+
} else if (ns === part) {
|
|
921
|
+
return true;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
return false;
|
|
925
|
+
}
|
|
926
|
+
function tag(scope) {
|
|
927
|
+
return `[${NS}:${scope}]`;
|
|
928
|
+
}
|
|
929
|
+
function log(level, scope, message, data) {
|
|
930
|
+
if (ALWAYS_ON.has(level)) {
|
|
931
|
+
console[level](tag(scope), message, data);
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
if (!matches2(switchValue(), `${NS}:${scope}`)) return;
|
|
935
|
+
console.info(tag(scope), message, data);
|
|
936
|
+
}
|
|
937
|
+
var rewindLog = {
|
|
938
|
+
error: (scope, message, data) => log("error", scope, message, data),
|
|
939
|
+
warn: (scope, message, data) => log("warn", scope, message, data),
|
|
940
|
+
info: (scope, message, data) => log("info", scope, message, data),
|
|
941
|
+
debug: (scope, message, data) => log("debug", scope, message, data)
|
|
942
|
+
};
|
|
943
|
+
|
|
938
944
|
// src/client/portals.tsx
|
|
939
945
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
940
946
|
function fillComposerEditable(text) {
|
|
@@ -1294,8 +1300,6 @@ function RewindPortals({ sessionId, sessionOf, chatOf, isMainViewSession, watchC
|
|
|
1294
1300
|
target,
|
|
1295
1301
|
sessionId,
|
|
1296
1302
|
sessionOf,
|
|
1297
|
-
chatOf,
|
|
1298
|
-
watchChat,
|
|
1299
1303
|
setComposerText,
|
|
1300
1304
|
t
|
|
1301
1305
|
},
|
|
@@ -1331,8 +1335,6 @@ function RewindButton({ target, sessionId, sessionOf, chatOf, watchChat, isMainV
|
|
|
1331
1335
|
if (node === void 0) return;
|
|
1332
1336
|
openPopover({
|
|
1333
1337
|
session,
|
|
1334
|
-
chatOf,
|
|
1335
|
-
watchChat,
|
|
1336
1338
|
seq: node.seq,
|
|
1337
1339
|
time: node.time,
|
|
1338
1340
|
preview: messagePreviewOf(node),
|
|
@@ -1343,17 +1345,17 @@ function RewindButton({ target, sessionId, sessionOf, chatOf, watchChat, isMainV
|
|
|
1343
1345
|
}
|
|
1344
1346
|
});
|
|
1345
1347
|
};
|
|
1346
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1348
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tooltip, { label: t("button.title"), side: "bottom", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1347
1349
|
"button",
|
|
1348
1350
|
{
|
|
1349
1351
|
type: "button",
|
|
1350
1352
|
className: CLASS.button,
|
|
1351
1353
|
"aria-label": t("button.aria"),
|
|
1352
|
-
|
|
1354
|
+
"aria-haspopup": "dialog",
|
|
1353
1355
|
onClick,
|
|
1354
1356
|
dangerouslySetInnerHTML: { __html: REWIND_ICON_SVG }
|
|
1355
1357
|
}
|
|
1356
|
-
);
|
|
1358
|
+
) });
|
|
1357
1359
|
}
|
|
1358
1360
|
function composerText() {
|
|
1359
1361
|
const surface = composerSurface();
|
|
@@ -1379,15 +1381,13 @@ async function retractPending(session, itemId, text, setComposerText) {
|
|
|
1379
1381
|
setComposerText(session.sessionId, text);
|
|
1380
1382
|
}
|
|
1381
1383
|
}
|
|
1382
|
-
function RetractButton({ target, sessionId, sessionOf,
|
|
1384
|
+
function RetractButton({ target, sessionId, sessionOf, setComposerText, t }) {
|
|
1383
1385
|
const onClick = (event) => {
|
|
1384
1386
|
event.stopPropagation();
|
|
1385
1387
|
const session = sessionOf(sessionId);
|
|
1386
1388
|
if (session === void 0) return;
|
|
1387
1389
|
openPopover({
|
|
1388
1390
|
session,
|
|
1389
|
-
chatOf,
|
|
1390
|
-
watchChat,
|
|
1391
1391
|
preview: target.preview,
|
|
1392
1392
|
anchor: event.currentTarget,
|
|
1393
1393
|
t,
|
|
@@ -1397,17 +1397,17 @@ function RetractButton({ target, sessionId, sessionOf, chatOf, watchChat, setCom
|
|
|
1397
1397
|
}
|
|
1398
1398
|
});
|
|
1399
1399
|
};
|
|
1400
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1400
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tooltip, { label: t("button.retract.title"), side: "bottom", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1401
1401
|
"button",
|
|
1402
1402
|
{
|
|
1403
1403
|
type: "button",
|
|
1404
1404
|
className: CLASS.button,
|
|
1405
1405
|
"aria-label": t("button.retract.aria"),
|
|
1406
|
-
|
|
1406
|
+
"aria-haspopup": "dialog",
|
|
1407
1407
|
onClick,
|
|
1408
1408
|
dangerouslySetInnerHTML: { __html: REWIND_ICON_SVG }
|
|
1409
1409
|
}
|
|
1410
|
-
);
|
|
1410
|
+
) });
|
|
1411
1411
|
}
|
|
1412
1412
|
function createRewindBridge(deps) {
|
|
1413
1413
|
return function RewindBridge({ sessionId }) {
|
|
@@ -1415,10 +1415,69 @@ function createRewindBridge(deps) {
|
|
|
1415
1415
|
};
|
|
1416
1416
|
}
|
|
1417
1417
|
|
|
1418
|
+
// src/client/host-rpc.ts
|
|
1419
|
+
var CHANNEL = "/dsh-rewind";
|
|
1420
|
+
var TIMEOUT_MS = 8e3;
|
|
1421
|
+
function isRecord(value) {
|
|
1422
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1423
|
+
}
|
|
1424
|
+
function isImpact(value) {
|
|
1425
|
+
return isRecord(value) && typeof value["path"] === "string" && (value["action"] === "restore" || value["action"] === "delete");
|
|
1426
|
+
}
|
|
1427
|
+
function isCandidate(value) {
|
|
1428
|
+
return isRecord(value) && Number.isSafeInteger(value["seq"]) && typeof value["time"] === "number" && Number.isFinite(value["time"]) && typeof value["preview"] === "string";
|
|
1429
|
+
}
|
|
1430
|
+
function isAbort(error) {
|
|
1431
|
+
const name2 = error?.name;
|
|
1432
|
+
return name2 === "AbortError" || name2 === "TimeoutError";
|
|
1433
|
+
}
|
|
1434
|
+
async function previewOverRpc(connection, sessionId, seq) {
|
|
1435
|
+
if (connection === void 0) return { kind: "error", text: "host RPC is unavailable" };
|
|
1436
|
+
try {
|
|
1437
|
+
const result = await connection.rpc.call(CHANNEL, "preview", { sessionId, seq }, AbortSignal.timeout(TIMEOUT_MS));
|
|
1438
|
+
if (!result.ok) {
|
|
1439
|
+
rewindLog.warn("preview", `RPC preview failed for @${seq}`, result.error);
|
|
1440
|
+
return { kind: "error", text: result.error.message };
|
|
1441
|
+
}
|
|
1442
|
+
const value = result.value;
|
|
1443
|
+
if (!isRecord(value) || typeof value["backupOff"] !== "boolean" || !Array.isArray(value["files"]) || !value["files"].every(isImpact)) {
|
|
1444
|
+
rewindLog.warn("preview", `RPC preview returned an invalid result for @${seq}`);
|
|
1445
|
+
return { kind: "error", text: "invalid preview response" };
|
|
1446
|
+
}
|
|
1447
|
+
return { kind: "success", files: value["files"], backupOff: value["backupOff"] };
|
|
1448
|
+
} catch (error) {
|
|
1449
|
+
if (isAbort(error)) {
|
|
1450
|
+
rewindLog.warn("preview", `RPC preview timed out for @${seq}`);
|
|
1451
|
+
return null;
|
|
1452
|
+
}
|
|
1453
|
+
rewindLog.warn("preview", `RPC preview threw for @${seq}`, error);
|
|
1454
|
+
return { kind: "error", text: error instanceof Error ? error.message : String(error) };
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
async function candidatesOverRpc(connection, sessionId) {
|
|
1458
|
+
if (connection === void 0) return void 0;
|
|
1459
|
+
try {
|
|
1460
|
+
const result = await connection.rpc.call(CHANNEL, "candidates", { sessionId }, AbortSignal.timeout(TIMEOUT_MS));
|
|
1461
|
+
if (!result.ok) {
|
|
1462
|
+
rewindLog.warn("preview", "RPC candidates failed", result.error);
|
|
1463
|
+
return void 0;
|
|
1464
|
+
}
|
|
1465
|
+
const value = result.value;
|
|
1466
|
+
if (!isRecord(value) || !Array.isArray(value["candidates"]) || !value["candidates"].every(isCandidate)) {
|
|
1467
|
+
rewindLog.warn("preview", "RPC candidates returned an invalid result");
|
|
1468
|
+
return void 0;
|
|
1469
|
+
}
|
|
1470
|
+
return value["candidates"];
|
|
1471
|
+
} catch (error) {
|
|
1472
|
+
rewindLog.warn("preview", "RPC candidates threw", error);
|
|
1473
|
+
return void 0;
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1418
1477
|
// src/client/build-info.ts
|
|
1419
|
-
var PLUGIN_VERSION = true ? "0.
|
|
1478
|
+
var PLUGIN_VERSION = true ? "0.18.1" : "dev";
|
|
1420
1479
|
var PLUGIN_PACKAGE = "dsh-rewind-plugin";
|
|
1421
|
-
var BUILD_HASH = true ? "
|
|
1480
|
+
var BUILD_HASH = true ? "8a3779b4" : "dev";
|
|
1422
1481
|
|
|
1423
1482
|
// src/client/locales.ts
|
|
1424
1483
|
var zh = {
|
|
@@ -1433,11 +1492,11 @@ var zh = {
|
|
|
1433
1492
|
"popover.retract.hint": "\u64A4\u56DE\u8FD9\u6761\u53CA\u4E4B\u540E\u7684\u63D2\u8BDD\u6D88\u606F\uFF0C\u4E0D\u6253\u65AD\u5F53\u524D\u751F\u6210",
|
|
1434
1493
|
"popover.retract.confirm": "\u786E\u8BA4\u56DE\u9000",
|
|
1435
1494
|
"popover.chat": "\u4EC5\u56DE\u9000\u5BF9\u8BDD",
|
|
1436
|
-
"popover.chat.hint": "\
|
|
1437
|
-
"popover.both": "\u56DE\u9000\u5BF9\u8BDD\u548C\
|
|
1438
|
-
"popover.both.hint": "\
|
|
1495
|
+
"popover.chat.hint": "\u4EC5\u56DE\u9000\u4E0A\u4E0B\u6587\uFF0C\u4E0D\u52A8\u5DE5\u4F5C\u533A\u6587\u4EF6",
|
|
1496
|
+
"popover.both": "\u56DE\u9000\u5BF9\u8BDD\u548C\u6587\u4EF6",
|
|
1497
|
+
"popover.both.hint": "\u56DE\u9000\u4E0A\u4E0B\u6587\u5E76\u8FD8\u539F\u5DE5\u4F5C\u533A\u6587\u4EF6",
|
|
1439
1498
|
"popover.checking": "\u6B63\u5728\u68C0\u67E5\u6587\u4EF6\u53D8\u66F4\u2026",
|
|
1440
|
-
"popover.noChanges": "\
|
|
1499
|
+
"popover.noChanges": "\u65E0\u53EF\u8FD8\u539F\u7684\u6587\u4EF6\u53D8\u66F4",
|
|
1441
1500
|
"popover.backupDisabled": "\u5FEB\u7167\u5907\u4EFD\u5DF2\u5173\u95ED\uFF0C\u8FD8\u539F\u53EF\u80FD\u4E0D\u5145\u5206",
|
|
1442
1501
|
"popover.cancel": "\u53D6\u6D88",
|
|
1443
1502
|
"popover.impact.loading": "\u6B63\u5728\u83B7\u53D6\u5F71\u54CD\u6E05\u5355\u2026",
|
|
@@ -1476,11 +1535,11 @@ var en = {
|
|
|
1476
1535
|
"popover.retract.hint": "Retracts this pending message and the ones after it, without interrupting the run",
|
|
1477
1536
|
"popover.retract.confirm": "Confirm rewind",
|
|
1478
1537
|
"popover.chat": "Rewind conversation only",
|
|
1479
|
-
"popover.chat.hint": "
|
|
1480
|
-
"popover.both": "Rewind conversation and
|
|
1481
|
-
"popover.both.hint": "
|
|
1538
|
+
"popover.chat.hint": "Context only; workspace untouched",
|
|
1539
|
+
"popover.both": "Rewind conversation and files",
|
|
1540
|
+
"popover.both.hint": "Context and restore workspace files",
|
|
1482
1541
|
"popover.checking": "Checking for file changes\u2026",
|
|
1483
|
-
"popover.noChanges": "No
|
|
1542
|
+
"popover.noChanges": "No restorable file changes",
|
|
1484
1543
|
"popover.backupDisabled": "Snapshots off; the restore may be incomplete",
|
|
1485
1544
|
"popover.cancel": "Cancel",
|
|
1486
1545
|
"popover.impact.loading": "Fetching impact list\u2026",
|
|
@@ -1509,7 +1568,7 @@ var en = {
|
|
|
1509
1568
|
};
|
|
1510
1569
|
|
|
1511
1570
|
// src/client/settings-card.tsx
|
|
1512
|
-
var
|
|
1571
|
+
var import_dsh_client_ui_primitives2 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
1513
1572
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1514
1573
|
var SETTINGS_ENTRY_ID = "dsh-rewind-plugin";
|
|
1515
1574
|
var DEFAULT_MAX_AGE_DAYS = 30;
|
|
@@ -1537,10 +1596,10 @@ function formLabels(t) {
|
|
|
1537
1596
|
};
|
|
1538
1597
|
}
|
|
1539
1598
|
function settingsForm(scope) {
|
|
1540
|
-
const form = new
|
|
1599
|
+
const form = new import_dsh_client_ui_primitives2.SettingsFormModel(scope, [
|
|
1541
1600
|
fileCheckpointingEnabledField(),
|
|
1542
1601
|
autoCleanupEnabledField(),
|
|
1543
|
-
(0,
|
|
1602
|
+
(0, import_dsh_client_ui_primitives2.settingsNumberField)("autoCleanupMaxAgeDays")
|
|
1544
1603
|
]);
|
|
1545
1604
|
return {
|
|
1546
1605
|
form,
|
|
@@ -1560,7 +1619,7 @@ function SettingsCard(props) {
|
|
|
1560
1619
|
const checkpointing = state.checkpointingEnabled.text === "true";
|
|
1561
1620
|
const cleanupEnabled = state.enabled.text === "true";
|
|
1562
1621
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1563
|
-
|
|
1622
|
+
import_dsh_client_ui_primitives2.SettingsForm,
|
|
1564
1623
|
{
|
|
1565
1624
|
labels: formLabels(t),
|
|
1566
1625
|
state,
|
|
@@ -1571,7 +1630,7 @@ function SettingsCard(props) {
|
|
|
1571
1630
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-rewind-settings-toggle-row", children: [
|
|
1572
1631
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-rewind-settings-toggle-label", children: t("backup.title") }),
|
|
1573
1632
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1574
|
-
|
|
1633
|
+
import_dsh_client_ui_primitives2.Switch,
|
|
1575
1634
|
{
|
|
1576
1635
|
checked: checkpointing,
|
|
1577
1636
|
label: t("backup.title"),
|
|
@@ -1589,7 +1648,7 @@ function SettingsCard(props) {
|
|
|
1589
1648
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-rewind-settings-toggle-row", children: [
|
|
1590
1649
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-rewind-settings-toggle-label", children: t("cleanup.auto") }),
|
|
1591
1650
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1592
|
-
|
|
1651
|
+
import_dsh_client_ui_primitives2.Switch,
|
|
1593
1652
|
{
|
|
1594
1653
|
checked: cleanupEnabled,
|
|
1595
1654
|
label: t("cleanup.auto"),
|
|
@@ -1603,7 +1662,7 @@ function SettingsCard(props) {
|
|
|
1603
1662
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-rewind-settings-hint", children: t(cleanupEnabled ? "cleanup.auto.on" : "cleanup.auto.off") })
|
|
1604
1663
|
] }),
|
|
1605
1664
|
cleanupEnabled ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1606
|
-
|
|
1665
|
+
import_dsh_client_ui_primitives2.SettingsValueField,
|
|
1607
1666
|
{
|
|
1608
1667
|
id: "dsh-rewind-cleanup-maxage",
|
|
1609
1668
|
label: t("cleanup.maxAge"),
|
|
@@ -1630,7 +1689,7 @@ function SettingsCard(props) {
|
|
|
1630
1689
|
|
|
1631
1690
|
// src/client/index.ts
|
|
1632
1691
|
var name = "dsh-rewind";
|
|
1633
|
-
var inject = ["slots", "sessions", "locale", "commandUi", "configForms"];
|
|
1692
|
+
var inject = ["slots", "sessions", "locale", "commandUi", "configForms", "connection"];
|
|
1634
1693
|
var NS2 = "rewind";
|
|
1635
1694
|
var HEADER_ACTIONS_SLOT = "conversation.session.header.actions";
|
|
1636
1695
|
var COMPOSER_EDITABLE_SELECTOR2 = "[data-composer-input]";
|
|
@@ -1639,6 +1698,11 @@ function apply(ctx) {
|
|
|
1639
1698
|
ctx.effect(function* () {
|
|
1640
1699
|
yield ctx.locale.register(NS2, { zh, en });
|
|
1641
1700
|
const t = ctx.locale.bind(NS2);
|
|
1701
|
+
const connection = ctx.get("connection");
|
|
1702
|
+
registerPreviewCall((sessionId, seq) => previewOverRpc(connection, sessionId, seq));
|
|
1703
|
+
yield () => {
|
|
1704
|
+
registerPreviewCall(null);
|
|
1705
|
+
};
|
|
1642
1706
|
const style = document.createElement("style");
|
|
1643
1707
|
style.dataset.plugin = PLUGIN_PACKAGE;
|
|
1644
1708
|
style.textContent = STYLE;
|
|
@@ -1777,14 +1841,7 @@ function apply(ctx) {
|
|
|
1777
1841
|
const chat = chatOf(face);
|
|
1778
1842
|
return chat !== void 0 && rewindCandidatesOfChat(chat).length > 0;
|
|
1779
1843
|
};
|
|
1780
|
-
const fetchHostCandidates = async (face
|
|
1781
|
-
const known = knownCommandSeqs(face, chatOf2, (node) => isCandidateCommand(node));
|
|
1782
|
-
const result = await face.command(`/${ENGINE_COMMAND} __candidates`);
|
|
1783
|
-
if (!result.ok || result.value?.matched !== true) return void 0;
|
|
1784
|
-
const outcome = await waitForCommand(face, chatOf2, (node) => isCandidateCommand(node) && !known.has(node.seq), 8e3, (cb) => watchChat(face.sessionId, cb));
|
|
1785
|
-
if (outcome === null || outcome.kind !== "success" || outcome.text === void 0) return void 0;
|
|
1786
|
-
return rewindCandidatesFromHostText(outcome.text);
|
|
1787
|
-
};
|
|
1844
|
+
const fetchHostCandidates = async (face) => candidatesOverRpc(connection, face.sessionId);
|
|
1788
1845
|
const hostCandidatesCache = /* @__PURE__ */ new Map();
|
|
1789
1846
|
const composerAnchor = () => {
|
|
1790
1847
|
const surface = composerSurface2();
|
|
@@ -1802,7 +1859,7 @@ function apply(ctx) {
|
|
|
1802
1859
|
options: async (session) => {
|
|
1803
1860
|
const face = sessionOf(session.sessionId);
|
|
1804
1861
|
if (face === void 0) return [];
|
|
1805
|
-
const candidates = await fetchHostCandidates(face
|
|
1862
|
+
const candidates = await fetchHostCandidates(face);
|
|
1806
1863
|
if (candidates !== void 0) hostCandidatesCache.set(session.sessionId, candidates);
|
|
1807
1864
|
return candidates === void 0 ? [] : rewindOptionsFromCandidates(candidates, t);
|
|
1808
1865
|
},
|
|
@@ -1815,8 +1872,6 @@ function apply(ctx) {
|
|
|
1815
1872
|
if (candidate === void 0) return;
|
|
1816
1873
|
openPopover({
|
|
1817
1874
|
session: face,
|
|
1818
|
-
chatOf,
|
|
1819
|
-
watchChat,
|
|
1820
1875
|
seq: candidate.seq,
|
|
1821
1876
|
time: candidate.time,
|
|
1822
1877
|
preview: candidate.preview,
|