dsh-rewind-plugin 0.13.0-alpha.2 → 0.14.0-beta.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 +18 -12
- package/README.md +15 -11
- package/SECURITY.md +3 -3
- package/assets/icon.svg +27 -0
- package/assets/screenshots/cleanup-setting.png +0 -0
- package/docs/architecture.md +5 -5
- package/docs/compat/audit.md +68 -37
- package/docs/contract/client-contract.md +3 -1
- package/docs/format.md +8 -7
- package/docs/harness-reference.md +1 -1
- package/docs/release/release.md +5 -0
- package/docs/release/release.zh.md +4 -0
- package/docs/snapshot-auto-cleanup.md +3 -1
- package/docs/snapshot-auto-cleanup.zh.md +4 -2
- package/lib/client.js +177 -384
- package/lib/index.js +70 -35
- package/lib/types/client/hidden.d.ts +5 -0
- package/lib/types/client/index.d.ts +6 -1
- package/lib/types/client/portals.d.ts +56 -2
- package/lib/types/client/settings-card.d.ts +89 -177
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/index.d.ts +42 -3
- package/lib/types/locales.d.ts +25 -8
- package/lib/types/marker.d.ts +39 -0
- package/lib/types/rewind.d.ts +4 -22
- package/lib/types/snapshot-cleanup.d.ts +39 -38
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +65 -57
package/lib/client.js
CHANGED
|
@@ -78,6 +78,32 @@ function isPreviewCommand(command) {
|
|
|
78
78
|
function isCandidateCommand(command) {
|
|
79
79
|
return (command.args ?? "").includes("__candidates");
|
|
80
80
|
}
|
|
81
|
+
function coalesceCuts(spans) {
|
|
82
|
+
if (spans.length < 2) return spans;
|
|
83
|
+
const sorted = [...spans].sort((left, right) => left.start - right.start || left.end - right.end);
|
|
84
|
+
const merged = [];
|
|
85
|
+
for (const span of sorted) {
|
|
86
|
+
const last = merged[merged.length - 1];
|
|
87
|
+
if (last !== void 0 && span.start <= last.end) {
|
|
88
|
+
if (span.end > last.end) last.end = span.end;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
merged.push({ start: span.start, end: span.end });
|
|
92
|
+
}
|
|
93
|
+
return merged;
|
|
94
|
+
}
|
|
95
|
+
function cutsContain(cuts, seq) {
|
|
96
|
+
let low = 0;
|
|
97
|
+
let high = cuts.length - 1;
|
|
98
|
+
while (low <= high) {
|
|
99
|
+
const middle = low + high >> 1;
|
|
100
|
+
const cut = cuts[middle];
|
|
101
|
+
if (seq < cut.start) high = middle - 1;
|
|
102
|
+
else if (seq > cut.end) low = middle + 1;
|
|
103
|
+
else return true;
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
81
107
|
function hiddenSeqsOf(snap) {
|
|
82
108
|
const hidden = /* @__PURE__ */ new Set();
|
|
83
109
|
const spans = [];
|
|
@@ -99,13 +125,11 @@ function hiddenSeqsOf(snap) {
|
|
|
99
125
|
spans.push({ start: target, end: marker });
|
|
100
126
|
}
|
|
101
127
|
}
|
|
128
|
+
const cuts = coalesceCuts(spans);
|
|
102
129
|
for (const key of snap.order) {
|
|
103
130
|
const node = snap.nodes.get(key);
|
|
104
131
|
if (node === void 0) continue;
|
|
105
|
-
|
|
106
|
-
if (spans.some((span) => anchor >= span.start && anchor <= span.end)) {
|
|
107
|
-
hidden.add(anchor);
|
|
108
|
-
}
|
|
132
|
+
if (cutsContain(cuts, node.anchorSeq)) hidden.add(node.anchorSeq);
|
|
109
133
|
}
|
|
110
134
|
return hidden;
|
|
111
135
|
}
|
|
@@ -373,25 +397,10 @@ var STYLE = `
|
|
|
373
397
|
pointer-events: none;
|
|
374
398
|
}
|
|
375
399
|
|
|
376
|
-
/* ---- Snapshot-cleanup configuration form
|
|
377
|
-
/* The Plugins page draws the bundle's title, icon, crumb, and
|
|
378
|
-
|
|
379
|
-
|
|
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 {
|
|
384
|
-
display: flex;
|
|
385
|
-
flex-direction: column;
|
|
386
|
-
}
|
|
387
|
-
.dsh-rewind-cleanup-readonly,
|
|
388
|
-
.dsh-rewind-cleanup-unavailable {
|
|
389
|
-
margin: 0 0 12px;
|
|
390
|
-
font-size: 12px;
|
|
391
|
-
line-height: 1.5;
|
|
392
|
-
color: var(--dsw-alias-label-tertiary);
|
|
393
|
-
}
|
|
394
|
-
/* Auto-cleanup permission block: the label + Switch row, then the hint. */
|
|
400
|
+
/* ---- Snapshot-cleanup configuration form ---- */
|
|
401
|
+
/* The Plugins page draws the bundle's title, icon, crumb, description, and the
|
|
402
|
+
shared settings form frame (labels, save, read-only and failure lines); this
|
|
403
|
+
styles only the one control the card owns: the auto-cleanup toggle row. */
|
|
395
404
|
.dsh-rewind-cleanup-permission {
|
|
396
405
|
display: grid;
|
|
397
406
|
gap: 6px;
|
|
@@ -410,127 +419,12 @@ var STYLE = `
|
|
|
410
419
|
flex: 1;
|
|
411
420
|
min-width: 0;
|
|
412
421
|
}
|
|
413
|
-
.dsh-rewind-cleanup-field {
|
|
414
|
-
display: flex;
|
|
415
|
-
flex-direction: column;
|
|
416
|
-
gap: 6px;
|
|
417
|
-
padding: 12px 0;
|
|
418
|
-
}
|
|
419
|
-
.dsh-rewind-cleanup-field + .dsh-rewind-cleanup-field {
|
|
420
|
-
border-top: 0.5px solid var(--dsw-alias-border-l2);
|
|
421
|
-
}
|
|
422
|
-
.dsh-rewind-cleanup-head {
|
|
423
|
-
display: flex;
|
|
424
|
-
align-items: center;
|
|
425
|
-
gap: 8px;
|
|
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
|
-
}
|
|
434
|
-
.dsh-rewind-cleanup-label {
|
|
435
|
-
flex: 1;
|
|
436
|
-
min-width: 0;
|
|
437
|
-
font-size: 13px;
|
|
438
|
-
font-weight: 500;
|
|
439
|
-
line-height: 1.5;
|
|
440
|
-
color: var(--dsw-alias-label-primary);
|
|
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
|
-
}
|
|
466
422
|
.dsh-rewind-cleanup-hint {
|
|
467
423
|
margin: 0;
|
|
468
424
|
font-size: 12px;
|
|
469
425
|
line-height: 1.5;
|
|
470
426
|
color: var(--dsw-alias-label-tertiary);
|
|
471
427
|
}
|
|
472
|
-
.dsh-rewind-cleanup-error {
|
|
473
|
-
margin: 0;
|
|
474
|
-
font-size: 12px;
|
|
475
|
-
line-height: 1.5;
|
|
476
|
-
color: var(--dsw-alias-state-error-primary);
|
|
477
|
-
}
|
|
478
|
-
.dsh-rewind-cleanup-input {
|
|
479
|
-
height: 34px;
|
|
480
|
-
padding: 0 12px;
|
|
481
|
-
border: 0.5px solid var(--dsw-alias-border-l4);
|
|
482
|
-
border-radius: 8px;
|
|
483
|
-
background: var(--dsw-alias-bg-layer-3);
|
|
484
|
-
font: inherit;
|
|
485
|
-
font-size: 13px;
|
|
486
|
-
line-height: 1.5;
|
|
487
|
-
color: var(--dsw-alias-label-primary);
|
|
488
|
-
}
|
|
489
|
-
.dsh-rewind-cleanup-input:focus-visible {
|
|
490
|
-
outline: none;
|
|
491
|
-
border-color: var(--dsw-alias-brand-primary);
|
|
492
|
-
}
|
|
493
|
-
.dsh-rewind-cleanup-input:disabled {
|
|
494
|
-
color: var(--dsw-alias-label-tertiary);
|
|
495
|
-
cursor: default;
|
|
496
|
-
}
|
|
497
|
-
.dsh-rewind-cleanup-input[aria-invalid='true'] {
|
|
498
|
-
border-color: var(--dsw-alias-state-error-primary);
|
|
499
|
-
}
|
|
500
|
-
.dsh-rewind-cleanup-footer {
|
|
501
|
-
display: flex;
|
|
502
|
-
align-items: center;
|
|
503
|
-
gap: 8px;
|
|
504
|
-
padding-top: 16px;
|
|
505
|
-
}
|
|
506
|
-
.dsh-rewind-cleanup-failed {
|
|
507
|
-
flex: 1;
|
|
508
|
-
min-width: 0;
|
|
509
|
-
margin: 0;
|
|
510
|
-
font-size: 12px;
|
|
511
|
-
line-height: 1.5;
|
|
512
|
-
color: var(--dsw-alias-label-error);
|
|
513
|
-
}
|
|
514
|
-
.dsh-rewind-cleanup-save {
|
|
515
|
-
appearance: none;
|
|
516
|
-
border: 1px solid transparent;
|
|
517
|
-
border-radius: 8px;
|
|
518
|
-
padding: 5px 14px;
|
|
519
|
-
font: inherit;
|
|
520
|
-
font-size: 13px;
|
|
521
|
-
line-height: 1.5;
|
|
522
|
-
cursor: pointer;
|
|
523
|
-
background: var(--dsw-alias-label-primary);
|
|
524
|
-
color: var(--dsw-alias-bg-layer-3);
|
|
525
|
-
}
|
|
526
|
-
.dsh-rewind-cleanup-save:disabled {
|
|
527
|
-
opacity: 0.4;
|
|
528
|
-
cursor: default;
|
|
529
|
-
}
|
|
530
|
-
.dsh-rewind-cleanup-save:focus-visible {
|
|
531
|
-
outline: 2px solid var(--dsw-alias-brand-primary);
|
|
532
|
-
outline-offset: 1px;
|
|
533
|
-
}
|
|
534
428
|
`;
|
|
535
429
|
|
|
536
430
|
// src/client/popover.ts
|
|
@@ -1047,6 +941,14 @@ function showHint(text) {
|
|
|
1047
941
|
var COMPOSER_EDITABLE_SELECTOR = "[data-composer-input]";
|
|
1048
942
|
var USER_SEAT_SELECTOR = '[data-chat-flow-kind="user"][data-chat-anchor-key], [data-chat-flow-kind="steering"][data-chat-anchor-key]';
|
|
1049
943
|
var CHAT_SEAT_SELECTOR = "[data-chat-anchor-key]";
|
|
944
|
+
var GROUP_SHELL_SELECTOR = '[data-chat-anchor-key^="group:"]';
|
|
945
|
+
function resolveSeatAnchorSeq(element, chat) {
|
|
946
|
+
if (chat === void 0) return void 0;
|
|
947
|
+
const flowKey = element.dataset.chatAnchorKey;
|
|
948
|
+
const nodeKey = element.dataset.chatNodeKey;
|
|
949
|
+
const node = (flowKey !== void 0 ? chat.nodes.get(flowKey) : void 0) ?? (nodeKey !== void 0 ? chat.nodes.get(nodeKey) : void 0);
|
|
950
|
+
return node?.anchorSeq;
|
|
951
|
+
}
|
|
1050
952
|
var PENDING_SEAT_SELECTOR = "[data-pending-steering]";
|
|
1051
953
|
function actionsContainerOf(row) {
|
|
1052
954
|
const buttons = Array.from(row?.querySelectorAll("button") ?? []);
|
|
@@ -1055,6 +957,38 @@ function actionsContainerOf(row) {
|
|
|
1055
957
|
if (structural instanceof HTMLElement && structural.querySelector("button") !== null) return structural;
|
|
1056
958
|
return void 0;
|
|
1057
959
|
}
|
|
960
|
+
function hideWithdrawnSeats(chat, seats, hiddenSeqs, hidden) {
|
|
961
|
+
if (chat === void 0) return;
|
|
962
|
+
const shells = /* @__PURE__ */ new Map();
|
|
963
|
+
for (const seat of seats) {
|
|
964
|
+
if (seat.matches(GROUP_SHELL_SELECTOR)) continue;
|
|
965
|
+
const anchor = resolveSeatAnchorSeq(seat, chat);
|
|
966
|
+
const withdrawn = anchor !== void 0 && hiddenSeqs.has(anchor);
|
|
967
|
+
if (withdrawn) {
|
|
968
|
+
seat.style.display = "none";
|
|
969
|
+
seat.dataset.dshRewindHidden = "true";
|
|
970
|
+
hidden.add(seat);
|
|
971
|
+
} else if (hidden.has(seat)) {
|
|
972
|
+
seat.style.display = "";
|
|
973
|
+
delete seat.dataset.dshRewindHidden;
|
|
974
|
+
hidden.delete(seat);
|
|
975
|
+
}
|
|
976
|
+
const shell = seat.parentElement?.closest(GROUP_SHELL_SELECTOR);
|
|
977
|
+
if (shell === null || shell === void 0) continue;
|
|
978
|
+
shells.set(shell, (shells.get(shell) ?? true) && withdrawn);
|
|
979
|
+
}
|
|
980
|
+
for (const [shell, allWithdrawn] of shells) {
|
|
981
|
+
if (allWithdrawn) {
|
|
982
|
+
shell.style.display = "none";
|
|
983
|
+
shell.dataset.dshRewindHidden = "true";
|
|
984
|
+
hidden.add(shell);
|
|
985
|
+
} else if (hidden.has(shell)) {
|
|
986
|
+
shell.style.display = "";
|
|
987
|
+
delete shell.dataset.dshRewindHidden;
|
|
988
|
+
hidden.delete(shell);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
}
|
|
1058
992
|
function collectTargets(chat, hiddenSeqs) {
|
|
1059
993
|
const rows = /* @__PURE__ */ new Map();
|
|
1060
994
|
for (const element of document.querySelectorAll(USER_SEAT_SELECTOR)) {
|
|
@@ -1145,19 +1079,12 @@ function RewindPortals({ sessionId, sessionOf, chatOf, isMainViewSession, watchC
|
|
|
1145
1079
|
const snapshot = session.getSnapshot();
|
|
1146
1080
|
const chat = chatOf(session);
|
|
1147
1081
|
const hiddenSeqs = chat === void 0 ? /* @__PURE__ */ new Set() : hiddenSeqsOf(chat);
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
hidden.current.add(seat);
|
|
1155
|
-
} else if (hidden.current.has(seat)) {
|
|
1156
|
-
seat.style.display = "";
|
|
1157
|
-
delete seat.dataset.dshRewindHidden;
|
|
1158
|
-
hidden.current.delete(seat);
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1082
|
+
hideWithdrawnSeats(
|
|
1083
|
+
chat,
|
|
1084
|
+
chat === void 0 ? [] : document.querySelectorAll(CHAT_SEAT_SELECTOR),
|
|
1085
|
+
hiddenSeqs,
|
|
1086
|
+
hidden.current
|
|
1087
|
+
);
|
|
1161
1088
|
const durable = collectDurableTargets(snapshot, chat, hiddenSeqs);
|
|
1162
1089
|
const next = [...durable, ...collectPendingTargets(snapshot, steeringItemsOf(inboxOf(session)?.["next-step"]))];
|
|
1163
1090
|
setTargets((current) => sameTargets(current, next) ? current : next);
|
|
@@ -1306,9 +1233,9 @@ function createRewindBridge(deps) {
|
|
|
1306
1233
|
}
|
|
1307
1234
|
|
|
1308
1235
|
// src/client/build-info.ts
|
|
1309
|
-
var PLUGIN_VERSION = true ? "0.
|
|
1236
|
+
var PLUGIN_VERSION = true ? "0.14.0-beta.1" : "dev";
|
|
1310
1237
|
var PLUGIN_PACKAGE = "dsh-rewind-plugin";
|
|
1311
|
-
var BUILD_HASH = true ? "
|
|
1238
|
+
var BUILD_HASH = true ? "190a881a" : "dev";
|
|
1312
1239
|
|
|
1313
1240
|
// src/client/locales.ts
|
|
1314
1241
|
var zh = {
|
|
@@ -1391,201 +1318,100 @@ var en = {
|
|
|
1391
1318
|
};
|
|
1392
1319
|
|
|
1393
1320
|
// src/client/settings-card.tsx
|
|
1394
|
-
var import_react2 = require("react");
|
|
1395
1321
|
var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
1396
1322
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1397
|
-
var
|
|
1323
|
+
var CLEANUP_ENTRY_ID = "dsh-rewind-plugin";
|
|
1398
1324
|
var DEFAULT_MAX_AGE_DAYS = 30;
|
|
1399
|
-
function
|
|
1400
|
-
const trimmed = text.trim();
|
|
1401
|
-
if (trimmed === "") return { kind: "clear" };
|
|
1402
|
-
if (!/^\d+$/.test(trimmed)) return void 0;
|
|
1403
|
-
const days = Number(trimmed);
|
|
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) {
|
|
1325
|
+
function autoCleanupEnabledField() {
|
|
1456
1326
|
return {
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
failed: flags.failed
|
|
1327
|
+
field: "autoCleanupEnabled",
|
|
1328
|
+
format: (value) => String(value === true),
|
|
1329
|
+
parse: (text) => ({ kind: "set", value: text === "true" })
|
|
1461
1330
|
};
|
|
1462
1331
|
}
|
|
1463
|
-
function
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
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);
|
|
1480
|
-
return next;
|
|
1481
|
-
});
|
|
1482
|
-
setFailed(false);
|
|
1332
|
+
function formLabels(t) {
|
|
1333
|
+
return {
|
|
1334
|
+
unavailable: t("cleanup.unavailable"),
|
|
1335
|
+
readOnly: t("cleanup.readonly"),
|
|
1336
|
+
saveFailed: t("cleanup.saveFailed"),
|
|
1337
|
+
save: t("cleanup.save"),
|
|
1338
|
+
saving: t("cleanup.saving")
|
|
1483
1339
|
};
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
const
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
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);
|
|
1340
|
+
}
|
|
1341
|
+
function cleanupForm(scope) {
|
|
1342
|
+
const form = new import_dsh_client_ui_primitives.SettingsFormModel(scope, [
|
|
1343
|
+
autoCleanupEnabledField(),
|
|
1344
|
+
(0, import_dsh_client_ui_primitives.settingsNumberField)("autoCleanupMaxAgeDays")
|
|
1345
|
+
]);
|
|
1346
|
+
return {
|
|
1347
|
+
form,
|
|
1348
|
+
store: form.bind(() => ({
|
|
1349
|
+
...form.shell(),
|
|
1350
|
+
enabled: form.field("autoCleanupEnabled"),
|
|
1351
|
+
maxAgeDays: form.field("autoCleanupMaxAgeDays")
|
|
1352
|
+
}))
|
|
1503
1353
|
};
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
const
|
|
1507
|
-
const
|
|
1508
|
-
|
|
1509
|
-
const
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1354
|
+
}
|
|
1355
|
+
function SettingsCleanupCard(props) {
|
|
1356
|
+
const { t, useCleanupCard } = props;
|
|
1357
|
+
const state = useCleanupCard((snapshot) => snapshot);
|
|
1358
|
+
if (props.view === "summary") return null;
|
|
1359
|
+
const disabled = !state.writable || state.saving;
|
|
1360
|
+
const enabled = state.enabled.text === "true";
|
|
1361
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1362
|
+
import_dsh_client_ui_primitives.SettingsForm,
|
|
1363
|
+
{
|
|
1364
|
+
labels: formLabels(t),
|
|
1365
|
+
state,
|
|
1366
|
+
onSave: props.save,
|
|
1367
|
+
onDiscard: props.discard,
|
|
1368
|
+
children: [
|
|
1369
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-rewind-cleanup-permission", children: [
|
|
1370
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-rewind-cleanup-toggle-row", children: [
|
|
1371
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-rewind-cleanup-toggle-label", children: t("cleanup.auto") }),
|
|
1372
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1373
|
+
import_dsh_client_ui_primitives.Switch,
|
|
1374
|
+
{
|
|
1375
|
+
checked: enabled,
|
|
1376
|
+
label: t("cleanup.auto"),
|
|
1377
|
+
disabled,
|
|
1378
|
+
onChange: (next) => {
|
|
1379
|
+
props.edit("autoCleanupEnabled", String(next));
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
)
|
|
1383
|
+
] }),
|
|
1384
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-rewind-cleanup-hint", children: t(enabled ? "cleanup.auto.on" : "cleanup.auto.off") })
|
|
1385
|
+
] }),
|
|
1386
|
+
enabled ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1387
|
+
import_dsh_client_ui_primitives.SettingsValueField,
|
|
1520
1388
|
{
|
|
1521
|
-
|
|
1522
|
-
label: t("cleanup.
|
|
1389
|
+
id: "dsh-rewind-cleanup-maxage",
|
|
1390
|
+
label: t("cleanup.maxAge"),
|
|
1391
|
+
hint: t("cleanup.maxAge.hint"),
|
|
1392
|
+
overriddenLabel: t("cleanup.overridden"),
|
|
1393
|
+
resetLabel: t("cleanup.reset"),
|
|
1394
|
+
invalidLabel: t("cleanup.invalid"),
|
|
1395
|
+
placeholder: String(DEFAULT_MAX_AGE_DAYS),
|
|
1396
|
+
numeric: true,
|
|
1523
1397
|
disabled,
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
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") }),
|
|
1537
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1538
|
-
"button",
|
|
1539
|
-
{
|
|
1540
|
-
type: "button",
|
|
1541
|
-
className: "dsh-rewind-cleanup-reset",
|
|
1542
|
-
disabled,
|
|
1543
|
-
onClick: () => {
|
|
1544
|
-
stage("maxAgeDays", { text: formatMaxAge(api.base("maxAgeDays")), clear: true });
|
|
1545
|
-
},
|
|
1546
|
-
children: t("cleanup.reset")
|
|
1398
|
+
...state.maxAgeDays,
|
|
1399
|
+
onEdit: (text) => {
|
|
1400
|
+
props.edit("autoCleanupMaxAgeDays", text);
|
|
1401
|
+
},
|
|
1402
|
+
onReset: () => {
|
|
1403
|
+
props.resetField("autoCleanupMaxAgeDays");
|
|
1547
1404
|
}
|
|
1548
|
-
)
|
|
1549
|
-
] }) : null
|
|
1550
|
-
] }),
|
|
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 });
|
|
1565
1405
|
}
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
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
|
-
] })
|
|
1583
|
-
] });
|
|
1406
|
+
) : null
|
|
1407
|
+
]
|
|
1408
|
+
}
|
|
1409
|
+
);
|
|
1584
1410
|
}
|
|
1585
1411
|
|
|
1586
1412
|
// src/client/index.ts
|
|
1587
1413
|
var name = "dsh-rewind";
|
|
1588
|
-
var inject = ["slots", "sessions", "locale", "commandUi"];
|
|
1414
|
+
var inject = ["slots", "sessions", "locale", "commandUi", "configForms"];
|
|
1589
1415
|
var NS2 = "rewind";
|
|
1590
1416
|
var HEADER_ACTIONS_SLOT = "conversation.session.header.actions";
|
|
1591
1417
|
var COMPOSER_EDITABLE_SELECTOR2 = "[data-composer-input]";
|
|
@@ -1652,58 +1478,25 @@ function apply(ctx) {
|
|
|
1652
1478
|
},
|
|
1653
1479
|
createRewindBridge({ sessionOf, chatOf, isMainViewSession, watchChat, setComposerText, t, subscribeLocale })
|
|
1654
1480
|
));
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
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);
|
|
1686
|
-
},
|
|
1687
|
-
subscribe: (cb) => scope.subscribe(cb)
|
|
1688
|
-
};
|
|
1689
|
-
scoped.slots.inject("plugins.bundle.config", () => scoped.slots.register(
|
|
1690
|
-
{
|
|
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,
|
|
1695
|
-
// Match the official cards / dsh-market: locale + inject provide
|
|
1696
|
-
// the card its props through the slot renderer (the page feeds it
|
|
1697
|
-
// the `view` it asks for plus the bound api).
|
|
1698
|
-
locale: NS2,
|
|
1699
|
-
inject: () => ({ t, api: cardApi })
|
|
1700
|
-
},
|
|
1701
|
-
SettingsCleanupCard
|
|
1702
|
-
));
|
|
1703
|
-
} catch (error) {
|
|
1704
|
-
rewindLog.error("settings", "settings card register failed", error);
|
|
1705
|
-
}
|
|
1706
|
-
});
|
|
1481
|
+
try {
|
|
1482
|
+
const { store, form } = cleanupForm(
|
|
1483
|
+
ctx.configForms.get(CLEANUP_ENTRY_ID)
|
|
1484
|
+
);
|
|
1485
|
+
ctx.effect(() => () => {
|
|
1486
|
+
form.dispose();
|
|
1487
|
+
}, "dsh-rewind cleanup form");
|
|
1488
|
+
yield slots.inject("plugins.bundle.config", () => slots.register(
|
|
1489
|
+
{
|
|
1490
|
+
name: "plugins.bundle.config",
|
|
1491
|
+
key: PLUGIN_PACKAGE,
|
|
1492
|
+
locale: NS2,
|
|
1493
|
+
inject: () => ({ hooks: { cleanupCard: store }, ...form.actions() })
|
|
1494
|
+
},
|
|
1495
|
+
SettingsCleanupCard
|
|
1496
|
+
));
|
|
1497
|
+
} catch (error) {
|
|
1498
|
+
rewindLog.error("settings", "settings card register failed", error);
|
|
1499
|
+
}
|
|
1707
1500
|
const commandUi = ctx.get("commandUi");
|
|
1708
1501
|
const hasCandidates = (sessionId) => {
|
|
1709
1502
|
const face = sessionId === void 0 ? void 0 : sessionOf(sessionId);
|