dsh-rewind-plugin 0.12.2 → 0.13.0-alpha.2
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/CONTRIBUTING.md +1 -1
- package/README.en.md +0 -3
- package/README.md +0 -3
- package/SECURITY.md +1 -1
- package/docs/README.md +1 -1
- package/docs/architecture.md +3 -3
- package/docs/compat/audit.md +30 -17
- package/docs/compat/diagnostics.md +1 -0
- package/docs/compat/diagnostics.zh.md +1 -0
- package/docs/contract/client-contract.md +1 -1
- package/docs/contract/client-contract.zh.md +1 -1
- package/docs/format.md +2 -2
- package/docs/harness-reference.md +6 -3
- package/docs/release/release.md +9 -9
- package/docs/release/release.zh.md +10 -10
- package/docs/rewind-fix.md +1 -1
- package/docs/rewind-fix.zh.md +1 -1
- package/lib/client.js +315 -346
- package/lib/index.js +56 -25
- package/lib/types/client/build-info.d.ts +6 -0
- package/lib/types/client/candidates.d.ts +0 -2
- package/lib/types/client/hidden.d.ts +6 -7
- package/lib/types/client/locales.d.ts +6 -14
- package/lib/types/client/pending.d.ts +57 -15
- package/lib/types/client/popover.d.ts +1 -1
- package/lib/types/client/portals.d.ts +37 -27
- package/lib/types/client/settings-card.d.ts +177 -48
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/index.d.ts +1 -1
- package/lib/types/locales.d.ts +1 -1
- package/lib/types/rewind.d.ts +1 -1
- package/lib/types/session-cwd.d.ts +8 -10
- package/lib/types/snapshot-cleanup.d.ts +6 -4
- package/lib/types/snapshot.d.ts +4 -11
- package/package.json +54 -46
package/lib/client.js
CHANGED
|
@@ -292,6 +292,14 @@ var STYLE = `
|
|
|
292
292
|
.dsh-rewind-popover-option:hover {
|
|
293
293
|
background: var(--dsw-alias-interactive-bg-hover);
|
|
294
294
|
}
|
|
295
|
+
/* Arrow navigation moves real focus, so the focused row carries the pointer's
|
|
296
|
+
fill and the browser ring is suppressed: in dark mode the UA two-tone ring
|
|
297
|
+
draws its dark half against the filled row and reads as a residue edge (the
|
|
298
|
+
harness Menu suppresses it the same way). */
|
|
299
|
+
.dsh-rewind-popover-option:focus-visible:not(:disabled) {
|
|
300
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
301
|
+
outline: none;
|
|
302
|
+
}
|
|
295
303
|
.dsh-rewind-popover-option:disabled {
|
|
296
304
|
opacity: 0.5;
|
|
297
305
|
cursor: default;
|
|
@@ -365,95 +373,43 @@ var STYLE = `
|
|
|
365
373
|
pointer-events: none;
|
|
366
374
|
}
|
|
367
375
|
|
|
368
|
-
/* ---- Snapshot-cleanup
|
|
369
|
-
/*
|
|
370
|
-
|
|
371
|
-
.
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
transition: border-color .16s, background .16s;
|
|
377
|
-
}
|
|
378
|
-
.dsh-rewind-cleanup-card:hover {
|
|
379
|
-
border-color: var(--dsw-alias-label-dimmed);
|
|
380
|
-
}
|
|
381
|
-
.dsh-rewind-cleanup-card-open {
|
|
382
|
-
background: var(--dsw-alias-bg-layer-2);
|
|
383
|
-
border-color: var(--dsw-alias-label-dimmed);
|
|
384
|
-
}
|
|
385
|
-
.dsh-rewind-cleanup-header {
|
|
386
|
-
width: 100%;
|
|
387
|
-
appearance: none;
|
|
388
|
-
border: 0;
|
|
389
|
-
background: none;
|
|
390
|
-
font: inherit;
|
|
391
|
-
color: inherit;
|
|
392
|
-
text-align: left;
|
|
393
|
-
cursor: pointer;
|
|
394
|
-
display: flex;
|
|
395
|
-
align-items: center;
|
|
396
|
-
gap: 12px;
|
|
397
|
-
padding: 14px 16px;
|
|
398
|
-
border-radius: 12px;
|
|
399
|
-
}
|
|
400
|
-
.dsh-rewind-cleanup-header:focus-visible {
|
|
401
|
-
outline: 2px solid var(--dsw-alias-brand-primary);
|
|
402
|
-
outline-offset: -2px;
|
|
403
|
-
}
|
|
404
|
-
.dsh-rewind-cleanup-head-text {
|
|
405
|
-
flex: 1;
|
|
406
|
-
min-width: 0;
|
|
376
|
+
/* ---- Snapshot-cleanup configuration form (mirrors the harness plugin form) ---- */
|
|
377
|
+
/* The Plugins page draws the bundle's title, icon, crumb, and description; this
|
|
378
|
+
is the form body only. Every value mirrors ui-settings-plugins'
|
|
379
|
+
PluginConfigForm.module.css / fields.module.css /
|
|
380
|
+
SubagentModelSelectionFields.module.css: those components are package-internal
|
|
381
|
+
(the package ships lib/** only) and cannot be imported across packages, so the
|
|
382
|
+
numbers are copied rather than the components. */
|
|
383
|
+
.dsh-rewind-cleanup-form {
|
|
407
384
|
display: flex;
|
|
408
385
|
flex-direction: column;
|
|
409
|
-
gap: 4px;
|
|
410
|
-
}
|
|
411
|
-
.dsh-rewind-cleanup-name {
|
|
412
|
-
font-size: 15px;
|
|
413
|
-
font-weight: 600;
|
|
414
|
-
line-height: 1.4;
|
|
415
|
-
color: var(--dsw-alias-label-primary);
|
|
416
|
-
}
|
|
417
|
-
.dsh-rewind-cleanup-desc {
|
|
418
|
-
font-size: 13px;
|
|
419
|
-
line-height: 1.5;
|
|
420
|
-
color: var(--dsw-alias-label-tertiary);
|
|
421
|
-
}
|
|
422
|
-
.dsh-rewind-cleanup-chevron {
|
|
423
|
-
flex: none;
|
|
424
|
-
color: var(--dsw-alias-label-tertiary);
|
|
425
|
-
transition: transform .16s;
|
|
426
386
|
}
|
|
427
|
-
.dsh-rewind-cleanup-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
.dsh-rewind-cleanup-pending {
|
|
431
|
-
flex: none;
|
|
432
|
-
border-radius: 999px;
|
|
433
|
-
padding: 1px 8px;
|
|
434
|
-
font-size: 11px;
|
|
435
|
-
line-height: 17px;
|
|
436
|
-
font-weight: 500;
|
|
437
|
-
white-space: nowrap;
|
|
438
|
-
background: var(--dsw-alias-bg-module-platform);
|
|
439
|
-
color: var(--dsw-alias-label-secondary);
|
|
440
|
-
}
|
|
441
|
-
.dsh-rewind-cleanup-body {
|
|
442
|
-
border-top: 0.5px solid var(--dsw-alias-border-l2);
|
|
443
|
-
margin: 0 16px;
|
|
444
|
-
padding: 0 0 8px;
|
|
445
|
-
}
|
|
446
|
-
.dsh-rewind-cleanup-readonly {
|
|
447
|
-
margin: 12px 0 0;
|
|
387
|
+
.dsh-rewind-cleanup-readonly,
|
|
388
|
+
.dsh-rewind-cleanup-unavailable {
|
|
389
|
+
margin: 0 0 12px;
|
|
448
390
|
font-size: 12px;
|
|
449
391
|
line-height: 1.5;
|
|
450
392
|
color: var(--dsw-alias-label-tertiary);
|
|
451
393
|
}
|
|
394
|
+
/* Auto-cleanup permission block: the label + Switch row, then the hint. */
|
|
452
395
|
.dsh-rewind-cleanup-permission {
|
|
453
396
|
display: grid;
|
|
454
397
|
gap: 6px;
|
|
455
398
|
padding: 12px 0;
|
|
456
399
|
}
|
|
400
|
+
.dsh-rewind-cleanup-toggle-row {
|
|
401
|
+
display: flex;
|
|
402
|
+
align-items: flex-start;
|
|
403
|
+
justify-content: space-between;
|
|
404
|
+
gap: 16px;
|
|
405
|
+
font-size: 13px;
|
|
406
|
+
line-height: 1.5;
|
|
407
|
+
color: var(--dsw-alias-label-primary);
|
|
408
|
+
}
|
|
409
|
+
.dsh-rewind-cleanup-toggle-label {
|
|
410
|
+
flex: 1;
|
|
411
|
+
min-width: 0;
|
|
412
|
+
}
|
|
457
413
|
.dsh-rewind-cleanup-field {
|
|
458
414
|
display: flex;
|
|
459
415
|
flex-direction: column;
|
|
@@ -463,17 +419,18 @@ var STYLE = `
|
|
|
463
419
|
.dsh-rewind-cleanup-field + .dsh-rewind-cleanup-field {
|
|
464
420
|
border-top: 0.5px solid var(--dsw-alias-border-l2);
|
|
465
421
|
}
|
|
466
|
-
/* Auto-cleanup (permission) block and the max-age field are consecutive control
|
|
467
|
-
* sections of the open card \u2014 give them the same 0.5px divider the harness gives
|
|
468
|
-
* every pair of config fields (fields.module.css field + field). */
|
|
469
|
-
.dsh-rewind-cleanup-permission + .dsh-rewind-cleanup-field {
|
|
470
|
-
border-top: 0.5px solid var(--dsw-alias-border-l2);
|
|
471
|
-
}
|
|
472
422
|
.dsh-rewind-cleanup-head {
|
|
473
423
|
display: flex;
|
|
474
424
|
align-items: center;
|
|
475
425
|
gap: 8px;
|
|
476
426
|
}
|
|
427
|
+
.dsh-rewind-cleanup-label-group {
|
|
428
|
+
display: flex;
|
|
429
|
+
align-items: center;
|
|
430
|
+
gap: 4px;
|
|
431
|
+
flex: 1;
|
|
432
|
+
min-width: 0;
|
|
433
|
+
}
|
|
477
434
|
.dsh-rewind-cleanup-label {
|
|
478
435
|
flex: 1;
|
|
479
436
|
min-width: 0;
|
|
@@ -482,6 +439,30 @@ var STYLE = `
|
|
|
482
439
|
line-height: 1.5;
|
|
483
440
|
color: var(--dsw-alias-label-primary);
|
|
484
441
|
}
|
|
442
|
+
.dsh-rewind-cleanup-label-group > .dsh-rewind-cleanup-label {
|
|
443
|
+
flex: 0 1 auto;
|
|
444
|
+
}
|
|
445
|
+
.dsh-rewind-cleanup-badges {
|
|
446
|
+
display: inline-flex;
|
|
447
|
+
align-items: center;
|
|
448
|
+
gap: 8px;
|
|
449
|
+
}
|
|
450
|
+
.dsh-rewind-cleanup-reset {
|
|
451
|
+
border: none;
|
|
452
|
+
background: none;
|
|
453
|
+
padding: 0;
|
|
454
|
+
font: inherit;
|
|
455
|
+
font-size: 12px;
|
|
456
|
+
line-height: 1.5;
|
|
457
|
+
color: var(--dsw-alias-label-secondary);
|
|
458
|
+
cursor: pointer;
|
|
459
|
+
}
|
|
460
|
+
.dsh-rewind-cleanup-reset:hover:not(:disabled) {
|
|
461
|
+
color: var(--dsw-alias-label-primary);
|
|
462
|
+
}
|
|
463
|
+
.dsh-rewind-cleanup-reset:disabled {
|
|
464
|
+
cursor: default;
|
|
465
|
+
}
|
|
485
466
|
.dsh-rewind-cleanup-hint {
|
|
486
467
|
margin: 0;
|
|
487
468
|
font-size: 12px;
|
|
@@ -492,65 +473,9 @@ var STYLE = `
|
|
|
492
473
|
margin: 0;
|
|
493
474
|
font-size: 12px;
|
|
494
475
|
line-height: 1.5;
|
|
495
|
-
color: var(--dsw-alias-
|
|
496
|
-
}
|
|
497
|
-
/* Switch row: label left, role=switch button right, hint below (Subagent module).
|
|
498
|
-
* The track mirrors the harness ui-primitives Switch (0.1.3-alpha.2 line): the
|
|
499
|
-
* on/off appearance keys off aria-checked rather than a parallel class, so the
|
|
500
|
-
* visual state cannot disagree with the state assistive technology reads, and
|
|
501
|
-
* corner-shape: round opts the capsule track out of the global superellipse
|
|
502
|
-
* (which would square the capsule ends off against the round thumb inside). */
|
|
503
|
-
.dsh-rewind-cleanup-toggle-row {
|
|
504
|
-
display: flex;
|
|
505
|
-
align-items: flex-start;
|
|
506
|
-
justify-content: space-between;
|
|
507
|
-
gap: 16px;
|
|
508
|
-
font-size: 13px;
|
|
509
|
-
line-height: 1.5;
|
|
510
|
-
color: var(--dsw-alias-label-primary);
|
|
511
|
-
}
|
|
512
|
-
.dsh-rewind-cleanup-toggle-label {
|
|
513
|
-
flex: 1;
|
|
514
|
-
min-width: 0;
|
|
515
|
-
}
|
|
516
|
-
.dsh-rewind-cleanup-switch {
|
|
517
|
-
box-sizing: border-box;
|
|
518
|
-
position: relative;
|
|
519
|
-
flex: 0 0 auto;
|
|
520
|
-
width: 36px;
|
|
521
|
-
height: 20px;
|
|
522
|
-
padding: 2px;
|
|
523
|
-
border: 0;
|
|
524
|
-
border-radius: 10px;
|
|
525
|
-
corner-shape: round;
|
|
526
|
-
background: var(--dsw-alias-border-l3);
|
|
527
|
-
cursor: pointer;
|
|
528
|
-
}
|
|
529
|
-
.dsh-rewind-cleanup-switch[aria-checked='true'] {
|
|
530
|
-
background: var(--dsw-alias-brand-primary);
|
|
531
|
-
}
|
|
532
|
-
.dsh-rewind-cleanup-switch:disabled {
|
|
533
|
-
cursor: default;
|
|
534
|
-
opacity: 0.5;
|
|
535
|
-
}
|
|
536
|
-
.dsh-rewind-cleanup-switch:focus-visible {
|
|
537
|
-
outline: 2px solid var(--dsw-alias-brand-primary);
|
|
538
|
-
outline-offset: 2px;
|
|
539
|
-
}
|
|
540
|
-
.dsh-rewind-cleanup-thumb {
|
|
541
|
-
display: block;
|
|
542
|
-
width: 16px;
|
|
543
|
-
height: 16px;
|
|
544
|
-
border-radius: 50%;
|
|
545
|
-
corner-shape: round;
|
|
546
|
-
background: var(--dsw-alias-label-primary-foreground);
|
|
547
|
-
transition: transform 120ms ease;
|
|
548
|
-
}
|
|
549
|
-
.dsh-rewind-cleanup-switch[aria-checked='true'] .dsh-rewind-cleanup-thumb {
|
|
550
|
-
transform: translateX(16px);
|
|
476
|
+
color: var(--dsw-alias-state-error-primary);
|
|
551
477
|
}
|
|
552
478
|
.dsh-rewind-cleanup-input {
|
|
553
|
-
box-sizing: border-box;
|
|
554
479
|
height: 34px;
|
|
555
480
|
padding: 0 12px;
|
|
556
481
|
border: 0.5px solid var(--dsw-alias-border-l4);
|
|
@@ -569,16 +494,14 @@ var STYLE = `
|
|
|
569
494
|
color: var(--dsw-alias-label-tertiary);
|
|
570
495
|
cursor: default;
|
|
571
496
|
}
|
|
572
|
-
.dsh-rewind-cleanup-input-invalid {
|
|
573
|
-
border-color: var(--dsw-alias-
|
|
497
|
+
.dsh-rewind-cleanup-input[aria-invalid='true'] {
|
|
498
|
+
border-color: var(--dsw-alias-state-error-primary);
|
|
574
499
|
}
|
|
575
500
|
.dsh-rewind-cleanup-footer {
|
|
576
501
|
display: flex;
|
|
577
502
|
align-items: center;
|
|
578
|
-
justify-content: flex-end;
|
|
579
503
|
gap: 8px;
|
|
580
|
-
padding:
|
|
581
|
-
border-top: 0.5px solid var(--dsw-alias-border-l2);
|
|
504
|
+
padding-top: 16px;
|
|
582
505
|
}
|
|
583
506
|
.dsh-rewind-cleanup-failed {
|
|
584
507
|
flex: 1;
|
|
@@ -588,7 +511,6 @@ var STYLE = `
|
|
|
588
511
|
line-height: 1.5;
|
|
589
512
|
color: var(--dsw-alias-label-error);
|
|
590
513
|
}
|
|
591
|
-
.dsh-rewind-cleanup-discard,
|
|
592
514
|
.dsh-rewind-cleanup-save {
|
|
593
515
|
appearance: none;
|
|
594
516
|
border: 1px solid transparent;
|
|
@@ -598,26 +520,13 @@ var STYLE = `
|
|
|
598
520
|
font-size: 13px;
|
|
599
521
|
line-height: 1.5;
|
|
600
522
|
cursor: pointer;
|
|
601
|
-
}
|
|
602
|
-
.dsh-rewind-cleanup-discard {
|
|
603
|
-
border-color: var(--dsw-alias-border-l2);
|
|
604
|
-
background: none;
|
|
605
|
-
color: var(--dsw-alias-label-secondary);
|
|
606
|
-
}
|
|
607
|
-
.dsh-rewind-cleanup-discard:hover:not(:disabled) {
|
|
608
|
-
color: var(--dsw-alias-label-primary);
|
|
609
|
-
border-color: var(--dsw-alias-label-dimmed);
|
|
610
|
-
}
|
|
611
|
-
.dsh-rewind-cleanup-save {
|
|
612
523
|
background: var(--dsw-alias-label-primary);
|
|
613
524
|
color: var(--dsw-alias-bg-layer-3);
|
|
614
525
|
}
|
|
615
|
-
.dsh-rewind-cleanup-discard:disabled,
|
|
616
526
|
.dsh-rewind-cleanup-save:disabled {
|
|
617
527
|
opacity: 0.4;
|
|
618
528
|
cursor: default;
|
|
619
529
|
}
|
|
620
|
-
.dsh-rewind-cleanup-discard:focus-visible,
|
|
621
530
|
.dsh-rewind-cleanup-save:focus-visible {
|
|
622
531
|
outline: 2px solid var(--dsw-alias-brand-primary);
|
|
623
532
|
outline-offset: 1px;
|
|
@@ -984,6 +893,23 @@ var import_react = require("react");
|
|
|
984
893
|
var import_react_dom = require("react-dom");
|
|
985
894
|
|
|
986
895
|
// src/client/pending.ts
|
|
896
|
+
var QUEUE_PREVIEW_CHARS = 200;
|
|
897
|
+
function textOf(content) {
|
|
898
|
+
if (!content.every((block) => block.type === "text")) return null;
|
|
899
|
+
return content.map((block) => block.text ?? "").join("");
|
|
900
|
+
}
|
|
901
|
+
function previewOf(content) {
|
|
902
|
+
const flat = content.filter((block) => block.type !== "image" && block.type !== "file").map((block) => block.type === "text" ? block.text ?? "" : `[${block.type}]`).join(" ").replace(/\s+/g, " ").trim();
|
|
903
|
+
const chars = Array.from(flat);
|
|
904
|
+
return chars.length > QUEUE_PREVIEW_CHARS ? `${chars.slice(0, QUEUE_PREVIEW_CHARS).join("")}\u2026` : flat;
|
|
905
|
+
}
|
|
906
|
+
function steeringItemsOf(nextStep) {
|
|
907
|
+
return (nextStep ?? []).filter((item) => item.source?.kind === "user").map((item) => ({
|
|
908
|
+
id: item.id,
|
|
909
|
+
text: textOf(item.content),
|
|
910
|
+
preview: previewOf(item.content)
|
|
911
|
+
}));
|
|
912
|
+
}
|
|
987
913
|
function matchPendingRows(rows, steering) {
|
|
988
914
|
const matched = [];
|
|
989
915
|
for (let i = 0; i < rows.length; i++) {
|
|
@@ -1045,7 +971,7 @@ function userNodeOf(chat, key) {
|
|
|
1045
971
|
if (node === void 0 || node.kind !== "user" && node.kind !== "steering") return void 0;
|
|
1046
972
|
return node.data;
|
|
1047
973
|
}
|
|
1048
|
-
async function runRewindAndFill(session, seq, mode,
|
|
974
|
+
async function runRewindAndFill(session, seq, mode, isMainViewSession, chatOf, watchChat, setComposerText) {
|
|
1049
975
|
const known = knownCommandSeqs(session, chatOf, (node) => isExecutedRewindCommand(node, seq));
|
|
1050
976
|
let result;
|
|
1051
977
|
try {
|
|
@@ -1077,7 +1003,7 @@ async function runRewindAndFill(session, seq, mode, currentSessionId, chatOf, wa
|
|
|
1077
1003
|
showHint(outcome.text ?? "rewind failed");
|
|
1078
1004
|
return;
|
|
1079
1005
|
}
|
|
1080
|
-
if (
|
|
1006
|
+
if (!isMainViewSession(session.sessionId)) {
|
|
1081
1007
|
return;
|
|
1082
1008
|
}
|
|
1083
1009
|
let text;
|
|
@@ -1155,19 +1081,21 @@ function collectDurableTargets(snapshot, chat, hiddenSeqs) {
|
|
|
1155
1081
|
if (chat === void 0 || isRewindInertSession(snapshot)) return [];
|
|
1156
1082
|
return collectTargets(chat, hiddenSeqs);
|
|
1157
1083
|
}
|
|
1084
|
+
function inboxOf(session) {
|
|
1085
|
+
return session.projections.faceOf("inbox").getSnapshot();
|
|
1086
|
+
}
|
|
1158
1087
|
function bubbleTextOf(row) {
|
|
1159
1088
|
const clone = row.cloneNode(true);
|
|
1160
1089
|
clone.lastElementChild?.remove();
|
|
1161
1090
|
return clone.textContent ?? "";
|
|
1162
1091
|
}
|
|
1163
|
-
function collectPendingTargets(snapshot) {
|
|
1092
|
+
function collectPendingTargets(snapshot, steering) {
|
|
1164
1093
|
if (snapshot.subagent !== null) return [];
|
|
1165
|
-
const steering = snapshot.queue.filter((item) => item.placement === "steering");
|
|
1166
1094
|
if (steering.length === 0) return [];
|
|
1167
1095
|
const rows = Array.from(document.querySelectorAll(PENDING_SEAT_SELECTOR));
|
|
1168
1096
|
const matched = matchPendingRows(
|
|
1169
1097
|
rows.map((row) => ({ text: bubbleTextOf(row) })),
|
|
1170
|
-
steering
|
|
1098
|
+
steering
|
|
1171
1099
|
);
|
|
1172
1100
|
const targets = [];
|
|
1173
1101
|
for (let i = 0; i < matched.length; i++) {
|
|
@@ -1197,7 +1125,7 @@ function sameTargets(left, right) {
|
|
|
1197
1125
|
return other.kind === "pending" && target.itemId === other.itemId;
|
|
1198
1126
|
});
|
|
1199
1127
|
}
|
|
1200
|
-
function RewindPortals({ sessionId, sessionOf, chatOf,
|
|
1128
|
+
function RewindPortals({ sessionId, sessionOf, chatOf, isMainViewSession, watchChat, t, subscribeLocale, setComposerText }) {
|
|
1201
1129
|
const [targets, setTargets] = (0, import_react.useState)([]);
|
|
1202
1130
|
const hidden = (0, import_react.useRef)(/* @__PURE__ */ new WeakSet());
|
|
1203
1131
|
const [, forceRender] = (0, import_react.useReducer)((count) => count + 1, 0);
|
|
@@ -1231,7 +1159,7 @@ function RewindPortals({ sessionId, sessionOf, chatOf, currentSessionId, watchCh
|
|
|
1231
1159
|
}
|
|
1232
1160
|
}
|
|
1233
1161
|
const durable = collectDurableTargets(snapshot, chat, hiddenSeqs);
|
|
1234
|
-
const next = [...durable, ...collectPendingTargets(snapshot)];
|
|
1162
|
+
const next = [...durable, ...collectPendingTargets(snapshot, steeringItemsOf(inboxOf(session)?.["next-step"]))];
|
|
1235
1163
|
setTargets((current) => sameTargets(current, next) ? current : next);
|
|
1236
1164
|
};
|
|
1237
1165
|
const queueRefresh = () => {
|
|
@@ -1271,7 +1199,7 @@ function RewindPortals({ sessionId, sessionOf, chatOf, currentSessionId, watchCh
|
|
|
1271
1199
|
sessionOf,
|
|
1272
1200
|
chatOf,
|
|
1273
1201
|
watchChat,
|
|
1274
|
-
|
|
1202
|
+
isMainViewSession,
|
|
1275
1203
|
setComposerText,
|
|
1276
1204
|
t
|
|
1277
1205
|
},
|
|
@@ -1281,7 +1209,7 @@ function RewindPortals({ sessionId, sessionOf, chatOf, currentSessionId, watchCh
|
|
|
1281
1209
|
target.key
|
|
1282
1210
|
));
|
|
1283
1211
|
}
|
|
1284
|
-
function RewindButton({ target, sessionId, sessionOf, chatOf, watchChat,
|
|
1212
|
+
function RewindButton({ target, sessionId, sessionOf, chatOf, watchChat, isMainViewSession, setComposerText, t }) {
|
|
1285
1213
|
const onClick = (event) => {
|
|
1286
1214
|
event.stopPropagation();
|
|
1287
1215
|
const session = sessionOf(sessionId);
|
|
@@ -1301,7 +1229,7 @@ function RewindButton({ target, sessionId, sessionOf, chatOf, watchChat, current
|
|
|
1301
1229
|
anchor: event.currentTarget,
|
|
1302
1230
|
t,
|
|
1303
1231
|
onRewind: (mode) => {
|
|
1304
|
-
void runRewindAndFill(session, node.seq, mode,
|
|
1232
|
+
void runRewindAndFill(session, node.seq, mode, isMainViewSession, chatOf, watchChat, setComposerText);
|
|
1305
1233
|
}
|
|
1306
1234
|
});
|
|
1307
1235
|
};
|
|
@@ -1323,8 +1251,7 @@ function composerText() {
|
|
|
1323
1251
|
return surface.textContent ?? "";
|
|
1324
1252
|
}
|
|
1325
1253
|
async function retractPending(session, itemId, text, setComposerText) {
|
|
1326
|
-
const
|
|
1327
|
-
const steering = queue.filter((item) => item.placement === "steering");
|
|
1254
|
+
const steering = steeringItemsOf(inboxOf(session)?.["next-step"]);
|
|
1328
1255
|
let removedTarget = false;
|
|
1329
1256
|
for (const id of retractSpan(steering, itemId)) {
|
|
1330
1257
|
let accepted = false;
|
|
@@ -1379,8 +1306,9 @@ function createRewindBridge(deps) {
|
|
|
1379
1306
|
}
|
|
1380
1307
|
|
|
1381
1308
|
// src/client/build-info.ts
|
|
1382
|
-
var PLUGIN_VERSION = true ? "0.
|
|
1383
|
-
var
|
|
1309
|
+
var PLUGIN_VERSION = true ? "0.13.0-alpha.2" : "dev";
|
|
1310
|
+
var PLUGIN_PACKAGE = "dsh-rewind-plugin";
|
|
1311
|
+
var BUILD_HASH = true ? "d7022b63" : "dev";
|
|
1384
1312
|
|
|
1385
1313
|
// src/client/locales.ts
|
|
1386
1314
|
var zh = {
|
|
@@ -1408,23 +1336,19 @@ var zh = {
|
|
|
1408
1336
|
"popover.impact.delete": "\u5220\u9664 {path}",
|
|
1409
1337
|
"popover.confirm": "\u786E\u8BA4\u56DE\u9000",
|
|
1410
1338
|
"popover.back": "\u8FD4\u56DE",
|
|
1411
|
-
"cleanup.title": "\u5FEB\u7167\u6E05\u7406",
|
|
1412
|
-
"cleanup.desc": "dsh-rewind \xB7 \u7BA1\u7406\u4F1A\u8BDD\u5FEB\u7167\u5907\u4EFD\u7684\u81EA\u52A8\u6E05\u7406\u7B56\u7565",
|
|
1413
|
-
"cleanup.expand": "\u5C55\u5F00",
|
|
1414
|
-
"cleanup.collapse": "\u6536\u8D77",
|
|
1415
|
-
"cleanup.unsaved": "\u672A\u4FDD\u5B58\u4FEE\u6539",
|
|
1416
1339
|
"cleanup.auto": "\u81EA\u52A8\u6E05\u7406",
|
|
1417
1340
|
"cleanup.auto.on": "\u5F00\u542F\u540E\u6309\u4E0D\u6D3B\u8DC3\u5929\u6570\u81EA\u52A8\u6E05\u7406\u8FC7\u671F\u4F1A\u8BDD\u5FEB\u7167",
|
|
1418
1341
|
"cleanup.auto.off": "\u5173\u95ED\u540E\u4FDD\u7559\u5168\u90E8\u5FEB\u7167\uFF0C\u4E0D\u81EA\u52A8\u6E05\u7406",
|
|
1419
1342
|
"cleanup.maxAge": "\u4E0D\u6D3B\u8DC3\u65F6\u95F4\uFF08\u5929\uFF09",
|
|
1420
1343
|
"cleanup.maxAge.hint": "\u8D85\u8FC7\u8BE5\u5929\u6570\u672A\u6D3B\u52A8\u7684\u4F1A\u8BDD\u5FEB\u7167\u4F1A\u88AB\u6E05\u7406",
|
|
1421
|
-
"cleanup.invalid": "\
|
|
1422
|
-
"cleanup.
|
|
1344
|
+
"cleanup.invalid": "\u8BF7\u586B\u6B63\u6574\u6570\uFF1B\u7559\u7A7A\u8868\u793A\u4F7F\u7528\u9ED8\u8BA4\u503C",
|
|
1345
|
+
"cleanup.overridden": "\u5DF2\u8986\u76D6",
|
|
1346
|
+
"cleanup.reset": "\u6062\u590D\u9ED8\u8BA4",
|
|
1423
1347
|
"cleanup.save": "\u4FDD\u5B58",
|
|
1424
1348
|
"cleanup.saving": "\u4FDD\u5B58\u4E2D\u2026",
|
|
1425
|
-
"cleanup.
|
|
1426
|
-
"cleanup.
|
|
1427
|
-
"cleanup.
|
|
1349
|
+
"cleanup.saveFailed": "\u672C\u90E8\u7F72\u6CA1\u6709\u63A5\u53D7\u8FD9\u4E9B\u503C\uFF0C\u5DF2\u4FDD\u7559\u4F9B\u4F60\u4FEE\u6539",
|
|
1350
|
+
"cleanup.readonly": "\u672C\u90E8\u7F72\u7684\u8BBE\u7F6E\u4E3A\u53EA\u8BFB",
|
|
1351
|
+
"cleanup.unavailable": "\u8BE5\u63D2\u4EF6\u5F53\u524D\u672A\u52A0\u8F7D\uFF0C\u6682\u65F6\u65E0\u6CD5\u914D\u7F6E"
|
|
1428
1352
|
};
|
|
1429
1353
|
var en = {
|
|
1430
1354
|
"button.aria": "Rewind to this message",
|
|
@@ -1451,185 +1375,211 @@ var en = {
|
|
|
1451
1375
|
"popover.impact.delete": "Delete {path}",
|
|
1452
1376
|
"popover.confirm": "Confirm rewind",
|
|
1453
1377
|
"popover.back": "Back",
|
|
1454
|
-
"cleanup.title": "Snapshot cleanup",
|
|
1455
|
-
"cleanup.desc": "dsh-rewind \xB7 Manage the auto-cleanup policy of session snapshot backups",
|
|
1456
|
-
"cleanup.expand": "Expand",
|
|
1457
|
-
"cleanup.collapse": "Collapse",
|
|
1458
|
-
"cleanup.unsaved": "Unsaved changes",
|
|
1459
1378
|
"cleanup.auto": "Auto cleanup",
|
|
1460
1379
|
"cleanup.auto.on": "When enabled, session snapshots idle past the cutoff are cleaned automatically",
|
|
1461
1380
|
"cleanup.auto.off": "When disabled, all snapshot backups are kept; nothing is cleaned automatically.",
|
|
1462
1381
|
"cleanup.maxAge": "Idle time (days)",
|
|
1463
1382
|
"cleanup.maxAge.hint": "Snapshot backups of sessions idle longer than this many days are cleaned",
|
|
1464
|
-
"cleanup.invalid": "
|
|
1465
|
-
"cleanup.
|
|
1383
|
+
"cleanup.invalid": "Enter a positive integer, or leave blank to use the default",
|
|
1384
|
+
"cleanup.overridden": "Overridden",
|
|
1385
|
+
"cleanup.reset": "Reset to default",
|
|
1466
1386
|
"cleanup.save": "Save",
|
|
1467
1387
|
"cleanup.saving": "Saving\u2026",
|
|
1468
|
-
"cleanup.
|
|
1469
|
-
"cleanup.
|
|
1470
|
-
"cleanup.
|
|
1388
|
+
"cleanup.saveFailed": "The deployment did not accept these values; they were left for you to correct",
|
|
1389
|
+
"cleanup.readonly": "This deployment stores settings read-only",
|
|
1390
|
+
"cleanup.unavailable": "This plugin is not loaded, so it cannot be configured right now"
|
|
1471
1391
|
};
|
|
1472
1392
|
|
|
1473
1393
|
// src/client/settings-card.tsx
|
|
1474
1394
|
var import_react2 = require("react");
|
|
1395
|
+
var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
1475
1396
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1476
1397
|
var CLEANUP_SETTINGS_NAMESPACE = "dsh-rewind-snapshot-cleanup";
|
|
1477
1398
|
var DEFAULT_MAX_AGE_DAYS = 30;
|
|
1478
|
-
function
|
|
1479
|
-
return { enabled: policy?.enabled ?? false, maxAgeDays: String(policy?.maxAgeDays ?? "") };
|
|
1480
|
-
}
|
|
1481
|
-
function maxAgeOf(text) {
|
|
1399
|
+
function parseMaxAge(text) {
|
|
1482
1400
|
const trimmed = text.trim();
|
|
1483
|
-
if (
|
|
1401
|
+
if (trimmed === "") return { kind: "clear" };
|
|
1402
|
+
if (!/^\d+$/.test(trimmed)) return void 0;
|
|
1484
1403
|
const days = Number(trimmed);
|
|
1485
|
-
return Number.isSafeInteger(days) && days > 0 ? days :
|
|
1486
|
-
}
|
|
1487
|
-
function
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
}
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1404
|
+
return Number.isSafeInteger(days) && days > 0 ? { kind: "set", value: days } : void 0;
|
|
1405
|
+
}
|
|
1406
|
+
function formatMaxAge(value) {
|
|
1407
|
+
return typeof value === "number" ? String(value) : "";
|
|
1408
|
+
}
|
|
1409
|
+
function formatEnabled(value) {
|
|
1410
|
+
return String(value === true);
|
|
1411
|
+
}
|
|
1412
|
+
function parseEnabled(text) {
|
|
1413
|
+
return { kind: "set", value: text === "true" };
|
|
1414
|
+
}
|
|
1415
|
+
function formatOf(field, value) {
|
|
1416
|
+
return field === "enabled" ? formatEnabled(value) : formatMaxAge(value);
|
|
1417
|
+
}
|
|
1418
|
+
function parseOf(field, text) {
|
|
1419
|
+
return field === "enabled" ? parseEnabled(text) : parseMaxAge(text);
|
|
1420
|
+
}
|
|
1421
|
+
function textOf2(field, staged, reads) {
|
|
1422
|
+
const edit = staged.get(field);
|
|
1423
|
+
return edit !== void 0 ? edit.text : formatOf(field, reads.value);
|
|
1424
|
+
}
|
|
1425
|
+
function overriddenOf(field, staged, reads) {
|
|
1426
|
+
const edit = staged.get(field);
|
|
1427
|
+
if (edit === void 0) return reads.stored;
|
|
1428
|
+
if (edit.clear) return false;
|
|
1429
|
+
return parseOf(field, edit.text)?.kind === "set";
|
|
1430
|
+
}
|
|
1431
|
+
function invalidOf(field, staged) {
|
|
1432
|
+
const edit = staged.get(field);
|
|
1433
|
+
if (edit === void 0 || edit.clear) return false;
|
|
1434
|
+
return parseOf(field, edit.text) === void 0;
|
|
1435
|
+
}
|
|
1436
|
+
function planOf(staged, reads) {
|
|
1437
|
+
const plan = [];
|
|
1438
|
+
for (const [field, edit] of staged) {
|
|
1439
|
+
const read = reads(field);
|
|
1440
|
+
if (edit.clear) {
|
|
1441
|
+
if (read.stored) plan.push({ field, kind: "clear" });
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1444
|
+
if (edit.text === formatOf(field, read.value)) continue;
|
|
1445
|
+
const write = parseOf(field, edit.text);
|
|
1446
|
+
if (write === void 0) plan.push({ field, kind: "invalid" });
|
|
1447
|
+
else if (write.kind === "clear") {
|
|
1448
|
+
if (read.stored) plan.push({ field, kind: "clear" });
|
|
1449
|
+
} else {
|
|
1450
|
+
plan.push({ field, kind: "set", value: write.value });
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
return plan;
|
|
1454
|
+
}
|
|
1455
|
+
function shellOf(plan, flags) {
|
|
1456
|
+
return {
|
|
1457
|
+
dirty: plan.length > 0,
|
|
1458
|
+
invalid: plan.some((entry) => entry.kind === "invalid"),
|
|
1459
|
+
saving: flags.saving,
|
|
1460
|
+
failed: flags.failed
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
function SettingsCleanupCard({ view, api, t }) {
|
|
1464
|
+
const [staged, setStaged] = (0, import_react2.useState)(() => /* @__PURE__ */ new Map());
|
|
1465
|
+
const [saving, setSaving] = (0, import_react2.useState)(false);
|
|
1466
|
+
const [failed, setFailed] = (0, import_react2.useState)(false);
|
|
1467
|
+
const [, setRevision] = (0, import_react2.useState)(0);
|
|
1502
1468
|
(0, import_react2.useEffect)(() => api.subscribe(() => {
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1469
|
+
setRevision((revision) => revision + 1);
|
|
1470
|
+
}), [api]);
|
|
1471
|
+
if (view === "summary") return null;
|
|
1472
|
+
const readField = (field) => ({
|
|
1473
|
+
value: api.read(field),
|
|
1474
|
+
stored: api.stored(field)
|
|
1475
|
+
});
|
|
1476
|
+
const stage = (field, edit) => {
|
|
1477
|
+
setStaged((current) => {
|
|
1478
|
+
const next = new Map(current);
|
|
1479
|
+
next.set(field, edit);
|
|
1506
1480
|
return next;
|
|
1507
1481
|
});
|
|
1508
|
-
|
|
1509
|
-
const dirty = dirtyOf(baseline, draft);
|
|
1510
|
-
const days = maxAgeOf(draft.maxAgeDays);
|
|
1511
|
-
const invalid = days === null;
|
|
1512
|
-
const disabled = busy || !writable;
|
|
1513
|
-
const edit = (patch) => {
|
|
1514
|
-
setDraft((cur) => ({ ...cur, ...patch }));
|
|
1515
|
-
setError(null);
|
|
1482
|
+
setFailed(false);
|
|
1516
1483
|
};
|
|
1484
|
+
const plan = planOf(staged, readField);
|
|
1485
|
+
const shell = shellOf(plan, { saving, failed });
|
|
1486
|
+
const writable = api.writable();
|
|
1487
|
+
const disabled = !writable || saving;
|
|
1488
|
+
const blocked = !shell.dirty || shell.invalid || shell.saving;
|
|
1517
1489
|
const save = async () => {
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
setError(t("cleanup.saveFailed", { message: e instanceof Error ? e.message : String(e) }));
|
|
1532
|
-
} finally {
|
|
1533
|
-
setBusy(false);
|
|
1534
|
-
}
|
|
1535
|
-
};
|
|
1536
|
-
const discard = () => {
|
|
1537
|
-
if (busy) return;
|
|
1538
|
-
setDraft(baseline);
|
|
1539
|
-
setError(null);
|
|
1490
|
+
const writes = plan.map((entry) => async () => {
|
|
1491
|
+
if (entry.kind === "clear") return api.unset(entry.field);
|
|
1492
|
+
if (entry.kind === "set") return api.set(entry.field, entry.value);
|
|
1493
|
+
return false;
|
|
1494
|
+
});
|
|
1495
|
+
if (plan.length === 0 || saving || plan.some((entry) => entry.kind === "invalid")) return;
|
|
1496
|
+
setSaving(true);
|
|
1497
|
+
setFailed(false);
|
|
1498
|
+
let landed = true;
|
|
1499
|
+
for (const write of writes) landed = await write() && landed;
|
|
1500
|
+
if (landed) setStaged(/* @__PURE__ */ new Map());
|
|
1501
|
+
setSaving(false);
|
|
1502
|
+
setFailed(!landed);
|
|
1540
1503
|
};
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
"aria-hidden": "true",
|
|
1564
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M4 6l4 4 4-4", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
1504
|
+
const enabledReads = readField("enabled");
|
|
1505
|
+
const enabledText = textOf2("enabled", staged, enabledReads);
|
|
1506
|
+
const enabled = enabledText === "true";
|
|
1507
|
+
const maxAgeReads = readField("maxAgeDays");
|
|
1508
|
+
const maxAgeText = textOf2("maxAgeDays", staged, maxAgeReads);
|
|
1509
|
+
const maxAgeInvalid = invalidOf("maxAgeDays", staged);
|
|
1510
|
+
if (!api.available()) {
|
|
1511
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-rewind-cleanup-unavailable", role: "status", children: t("cleanup.unavailable") });
|
|
1512
|
+
}
|
|
1513
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-rewind-cleanup-form", children: [
|
|
1514
|
+
!writable ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-rewind-cleanup-readonly", role: "status", children: t("cleanup.readonly") }) : null,
|
|
1515
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-rewind-cleanup-permission", children: [
|
|
1516
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-rewind-cleanup-toggle-row", children: [
|
|
1517
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-rewind-cleanup-toggle-label", children: t("cleanup.auto") }),
|
|
1518
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1519
|
+
import_dsh_client_ui_primitives.Switch,
|
|
1520
|
+
{
|
|
1521
|
+
checked: enabled,
|
|
1522
|
+
label: t("cleanup.auto"),
|
|
1523
|
+
disabled,
|
|
1524
|
+
onChange: (next) => {
|
|
1525
|
+
stage("enabled", { text: formatEnabled(next), clear: false });
|
|
1565
1526
|
}
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-rewind-cleanup-
|
|
1573
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.
|
|
1574
|
-
|
|
1527
|
+
}
|
|
1528
|
+
)
|
|
1529
|
+
] }),
|
|
1530
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-rewind-cleanup-hint", children: t(enabled ? "cleanup.auto.on" : "cleanup.auto.off") })
|
|
1531
|
+
] }),
|
|
1532
|
+
enabled ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-rewind-cleanup-field", children: [
|
|
1533
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-rewind-cleanup-head", children: [
|
|
1534
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-rewind-cleanup-label-group", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "dsh-rewind-cleanup-label", htmlFor: "dsh-rewind-cleanup-maxage", children: t("cleanup.maxAge") }) }),
|
|
1535
|
+
overriddenOf("maxAgeDays", staged, maxAgeReads) ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-rewind-cleanup-badges", children: [
|
|
1536
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "neutral", children: t("cleanup.overridden") }),
|
|
1575
1537
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1576
1538
|
"button",
|
|
1577
1539
|
{
|
|
1578
1540
|
type: "button",
|
|
1579
|
-
|
|
1580
|
-
className: "dsh-rewind-cleanup-switch",
|
|
1581
|
-
"aria-checked": draft.enabled,
|
|
1582
|
-
"aria-labelledby": "dsh-rewind-cleanup-enabled-label",
|
|
1541
|
+
className: "dsh-rewind-cleanup-reset",
|
|
1583
1542
|
disabled,
|
|
1584
|
-
onClick: () =>
|
|
1585
|
-
|
|
1543
|
+
onClick: () => {
|
|
1544
|
+
stage("maxAgeDays", { text: formatMaxAge(api.base("maxAgeDays")), clear: true });
|
|
1545
|
+
},
|
|
1546
|
+
children: t("cleanup.reset")
|
|
1586
1547
|
}
|
|
1587
1548
|
)
|
|
1588
|
-
] })
|
|
1589
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-rewind-cleanup-hint", children: t(draft.enabled ? "cleanup.auto.on" : "cleanup.auto.off") })
|
|
1549
|
+
] }) : null
|
|
1590
1550
|
] }),
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
"
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1551
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1552
|
+
"input",
|
|
1553
|
+
{
|
|
1554
|
+
id: "dsh-rewind-cleanup-maxage",
|
|
1555
|
+
className: "dsh-rewind-cleanup-input",
|
|
1556
|
+
type: "text",
|
|
1557
|
+
inputMode: "numeric",
|
|
1558
|
+
...maxAgeInvalid ? { "aria-invalid": true } : {},
|
|
1559
|
+
"aria-describedby": "dsh-rewind-cleanup-maxage-message",
|
|
1560
|
+
value: maxAgeText,
|
|
1561
|
+
placeholder: String(DEFAULT_MAX_AGE_DAYS),
|
|
1562
|
+
disabled,
|
|
1563
|
+
onChange: (event) => {
|
|
1564
|
+
stage("maxAgeDays", { text: event.target.value, clear: false });
|
|
1605
1565
|
}
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
"
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
)
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
{
|
|
1624
|
-
type: "button",
|
|
1625
|
-
className: "dsh-rewind-cleanup-save",
|
|
1626
|
-
disabled: !dirty || busy || !writable || invalid,
|
|
1627
|
-
onClick: save,
|
|
1628
|
-
children: busy ? t("cleanup.saving") : t("cleanup.save")
|
|
1629
|
-
}
|
|
1630
|
-
)
|
|
1631
|
-
] })
|
|
1632
|
-
] }) : null
|
|
1566
|
+
}
|
|
1567
|
+
),
|
|
1568
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1569
|
+
"p",
|
|
1570
|
+
{
|
|
1571
|
+
id: "dsh-rewind-cleanup-maxage-message",
|
|
1572
|
+
className: maxAgeInvalid ? "dsh-rewind-cleanup-error" : "dsh-rewind-cleanup-hint",
|
|
1573
|
+
children: maxAgeInvalid ? t("cleanup.invalid") : t("cleanup.maxAge.hint")
|
|
1574
|
+
}
|
|
1575
|
+
)
|
|
1576
|
+
] }) : null,
|
|
1577
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-rewind-cleanup-footer", children: [
|
|
1578
|
+
shell.failed ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-rewind-cleanup-failed", role: "status", children: t("cleanup.saveFailed") }) : null,
|
|
1579
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-rewind-cleanup-save", disabled: blocked, onClick: () => {
|
|
1580
|
+
void save();
|
|
1581
|
+
}, children: shell.saving ? t("cleanup.saving") : t("cleanup.save") })
|
|
1582
|
+
] })
|
|
1633
1583
|
] });
|
|
1634
1584
|
}
|
|
1635
1585
|
|
|
@@ -1645,11 +1595,11 @@ function apply(ctx) {
|
|
|
1645
1595
|
yield ctx.locale.register(NS2, { zh, en });
|
|
1646
1596
|
const t = ctx.locale.bind(NS2);
|
|
1647
1597
|
const style = document.createElement("style");
|
|
1648
|
-
style.dataset.plugin =
|
|
1598
|
+
style.dataset.plugin = PLUGIN_PACKAGE;
|
|
1649
1599
|
style.textContent = STYLE;
|
|
1650
1600
|
document.head.appendChild(style);
|
|
1651
1601
|
const sessionOf = (sessionId) => ctx.sessions.binding(sessionId)?.session;
|
|
1652
|
-
const
|
|
1602
|
+
const isMainViewSession = (sessionId) => (ctx.sessions.list.getSnapshot().byId[sessionId]?.retainedBy.mainView ?? 0) > 0;
|
|
1653
1603
|
const subscribeLocale = (cb) => ctx.locale.subscribe(cb);
|
|
1654
1604
|
const uiConversation = () => ctx.get("uiConversation");
|
|
1655
1605
|
const CHAT_VIEW = "chat";
|
|
@@ -1700,33 +1650,51 @@ function apply(ctx) {
|
|
|
1700
1650
|
id: "dsh-rewind-portals",
|
|
1701
1651
|
order: 1e3
|
|
1702
1652
|
},
|
|
1703
|
-
createRewindBridge({ sessionOf, chatOf,
|
|
1653
|
+
createRewindBridge({ sessionOf, chatOf, isMainViewSession, watchChat, setComposerText, t, subscribeLocale })
|
|
1704
1654
|
));
|
|
1705
1655
|
const clientCtx = ctx;
|
|
1706
1656
|
clientCtx.inject(["settingsScope"], (scoped) => {
|
|
1707
1657
|
try {
|
|
1708
1658
|
const scope = scoped.settingsScope.bind({ namespace: CLEANUP_SETTINGS_NAMESPACE });
|
|
1659
|
+
const userLayer = () => {
|
|
1660
|
+
const user = scope.getSnapshot().user;
|
|
1661
|
+
return typeof user === "object" && user !== null ? user : void 0;
|
|
1662
|
+
};
|
|
1709
1663
|
const cardApi = {
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
},
|
|
1664
|
+
// The official shell reads `status === 'ready'`; a loading namespace
|
|
1665
|
+
// is not available, so the form offers no fields yet.
|
|
1666
|
+
available: () => scope.getSnapshot().status === "ready",
|
|
1714
1667
|
writable: () => {
|
|
1715
1668
|
return scope.getSnapshot().writable === true;
|
|
1716
1669
|
},
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1670
|
+
read: (field) => scope.getSnapshot().value?.[field],
|
|
1671
|
+
base: (field) => scope.getSnapshot().base?.[field],
|
|
1672
|
+
stored: (field) => {
|
|
1673
|
+
const user = userLayer();
|
|
1674
|
+
return user !== void 0 && Object.hasOwn(user, field);
|
|
1675
|
+
},
|
|
1676
|
+
// The Host decides whether a write landed: report the read-back the
|
|
1677
|
+
// official `CardForm.store` performs.
|
|
1678
|
+
set: async (field, value) => {
|
|
1679
|
+
await scope.set(field, value);
|
|
1680
|
+
return userLayer()?.[field] === value;
|
|
1681
|
+
},
|
|
1682
|
+
unset: async (field) => {
|
|
1683
|
+
await scope.unset(field);
|
|
1684
|
+
const user = userLayer();
|
|
1685
|
+
return user === void 0 || !Object.hasOwn(user, field);
|
|
1720
1686
|
},
|
|
1721
1687
|
subscribe: (cb) => scope.subscribe(cb)
|
|
1722
1688
|
};
|
|
1723
|
-
scoped.slots.inject("
|
|
1689
|
+
scoped.slots.inject("plugins.bundle.config", () => scoped.slots.register(
|
|
1724
1690
|
{
|
|
1725
|
-
name: "
|
|
1726
|
-
|
|
1691
|
+
name: "plugins.bundle.config",
|
|
1692
|
+
// Keyed by the bundle's package name: the page renders this form on
|
|
1693
|
+
// the bundle's own page, between its description and its rows.
|
|
1694
|
+
key: PLUGIN_PACKAGE,
|
|
1727
1695
|
// Match the official cards / dsh-market: locale + inject provide
|
|
1728
|
-
// the card its props through the slot renderer (the
|
|
1729
|
-
//
|
|
1696
|
+
// the card its props through the slot renderer (the page feeds it
|
|
1697
|
+
// the `view` it asks for plus the bound api).
|
|
1730
1698
|
locale: NS2,
|
|
1731
1699
|
inject: () => ({ t, api: cardApi })
|
|
1732
1700
|
},
|
|
@@ -1789,7 +1757,7 @@ function apply(ctx) {
|
|
|
1789
1757
|
anchor: composerAnchor(),
|
|
1790
1758
|
t,
|
|
1791
1759
|
onRewind: (mode) => {
|
|
1792
|
-
void runRewindAndFill(face, candidate.seq, mode,
|
|
1760
|
+
void runRewindAndFill(face, candidate.seq, mode, isMainViewSession, chatOf, watchChat, setComposerText);
|
|
1793
1761
|
}
|
|
1794
1762
|
});
|
|
1795
1763
|
}
|
|
@@ -1800,6 +1768,7 @@ function apply(ctx) {
|
|
|
1800
1768
|
}
|
|
1801
1769
|
const composerSurface2 = () => document.querySelector(COMPOSER_EDITABLE_SELECTOR2);
|
|
1802
1770
|
yield () => {
|
|
1771
|
+
closePopover();
|
|
1803
1772
|
style.remove();
|
|
1804
1773
|
};
|
|
1805
1774
|
}, "dsh-rewind client lifecycle");
|