dsh-retrace 0.4.1 → 0.4.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/README.md +1 -1
- package/README.zh.md +1 -1
- package/lib/client.bundle.js +123 -202
- package/lib/client.js +151 -233
- package/lib/dynamic-client.js +123 -202
- package/package.json +1 -1
package/lib/dynamic-client.js
CHANGED
|
@@ -83,6 +83,7 @@ return {
|
|
|
83
83
|
"timeline.open": "\u65F6\u95F4\u7EBF",
|
|
84
84
|
"timeline.openAria": "\u6253\u5F00\u4F1A\u8BDD\u65F6\u95F4\u7EBF",
|
|
85
85
|
"timeline.title": "\u7248\u672C\u65F6\u95F4\u7EBF",
|
|
86
|
+
"view.retrace": "\u7248\u672C",
|
|
86
87
|
"timeline.refresh": "\u5237\u65B0",
|
|
87
88
|
"timeline.close": "\u5173\u95ED",
|
|
88
89
|
"timeline.empty": "\u8FD8\u6CA1\u6709\u7248\u672C\u3002\u64A4\u56DE / \u7F16\u8F91 / \u91CD\u65B0\u751F\u6210\u4F1A\u5728\u6B64\u8BB0\u5F55\u7248\u672C\u3002",
|
|
@@ -115,6 +116,7 @@ return {
|
|
|
115
116
|
"timeline.cancel": "\u53D6\u6D88",
|
|
116
117
|
"timeline.busy": "\u56DE\u9000\u4E2D\u2026",
|
|
117
118
|
"timeline.detail": "\u8BE6\u60C5",
|
|
119
|
+
"timeline.trajectory": "\u8F68\u8FF9\u53F0\u8D26",
|
|
118
120
|
"timeline.jump": "\u8DF3\u8F6C",
|
|
119
121
|
"timeline.jumpFailed": "\u8BE5\u7248\u672C\u5728\u8F83\u8FDC\u7684\u8FC7\u53BB\uFF08\u8D85\u51FA\u81EA\u52A8\u52A0\u8F7D\u9884\u7B97\uFF09\uFF0C\u65E0\u6CD5\u76F4\u63A5\u5B9A\u4F4D\u3002\u8BF7\u5411\u4E0A\u6EDA\u52A8\u52A0\u8F7D\u66F4\u65E9\u6D88\u606F\u540E\u91CD\u8BD5\uFF1B\u6216\u7528\u300C\u8BE6\u60C5\u300D\u67E5\u770B\u8BE5\u7248\u672C\u5F53\u65F6\u7684\u4E8B\u4EF6\u539F\u6587\u3002",
|
|
120
122
|
"timeline.gitRepo": "git \u4ED3\u5E93",
|
|
@@ -156,6 +158,7 @@ return {
|
|
|
156
158
|
"timeline.open": "Timeline",
|
|
157
159
|
"timeline.openAria": "Open the session timeline",
|
|
158
160
|
"timeline.title": "Version timeline",
|
|
161
|
+
"view.retrace": "Versions",
|
|
159
162
|
"timeline.refresh": "Refresh",
|
|
160
163
|
"timeline.close": "Close",
|
|
161
164
|
"timeline.empty": "No versions yet. Recall / edit / regenerate record a version here.",
|
|
@@ -188,6 +191,7 @@ return {
|
|
|
188
191
|
"timeline.cancel": "Cancel",
|
|
189
192
|
"timeline.busy": "Rolling back\u2026",
|
|
190
193
|
"timeline.detail": "Details",
|
|
194
|
+
"timeline.trajectory": "Trajectory",
|
|
191
195
|
"timeline.jump": "Jump",
|
|
192
196
|
"timeline.jumpFailed": "This version lies too far back (beyond the auto-load budget) to locate directly. Scroll up to load earlier messages, or use Details to read the original event text of this version.",
|
|
193
197
|
"timeline.gitRepo": "git repository",
|
|
@@ -731,17 +735,13 @@ return {
|
|
|
731
735
|
.dsh-rt-retention-input{width:64px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-elevated);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:2px 6px;font:inherit;font-size:13px;outline:none;margin-top:1px}
|
|
732
736
|
.dsh-rt-retention-input:focus{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary)}
|
|
733
737
|
.dsh-rt-option input{accent-color:var(--dsw-alias-state-business-primary)}
|
|
734
|
-
/* ---- P1 timeline ---- */
|
|
735
|
-
.dsh-rt-
|
|
736
|
-
.dsh-rt-timeline-trigger{min-height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:transparent;border:none;border-radius:6px;display:inline-flex;align-items:center;gap:4px;padding:3px 6px;font-size:12px;line-height:18px}
|
|
737
|
-
.dsh-rt-timeline-trigger:hover,.dsh-rt-timeline-trigger:focus-visible{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover)}
|
|
738
|
-
.dsh-rt-timeline-icon{font-size:13px}
|
|
739
|
-
.dsh-rt-timeline-panel{position:fixed;top:calc(100% + 6px);right:8px;z-index:2147483000;width:min(520px,calc(100vw - 32px));max-height:min(70vh,640px);box-sizing:border-box;display:flex;flex-direction:column;gap:6px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-specific-menu);box-shadow:var(--dsw-shadow-lv3);border-radius:12px;padding:8px;overflow:hidden}
|
|
738
|
+
/* ---- P1 timeline (conversation view tab) ---- */
|
|
739
|
+
.dsh-rt-view{box-sizing:border-box;display:flex;flex-direction:column;gap:6px;height:100%;min-height:0;padding:12px 16px 0}
|
|
740
740
|
.dsh-rt-timeline-head{display:flex;align-items:center;gap:8px;flex:none}
|
|
741
741
|
.dsh-rt-timeline-title{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600;line-height:20px;flex:1}
|
|
742
742
|
.dsh-rt-timeline-git{display:flex;align-items:center;gap:6px;flex:none;border:1px dashed var(--dsw-alias-border-l2);border-radius:8px;padding:4px 8px}
|
|
743
743
|
.dsh-rt-timeline-git-text{color:var(--dsw-alias-label-caption);font-size:11px;line-height:16px}
|
|
744
|
-
.dsh-rt-timeline-list{overflow-y:auto;flex:
|
|
744
|
+
.dsh-rt-timeline-list{overflow-y:auto;flex:1;min-height:0;position:relative;--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2)}
|
|
745
745
|
.dsh-rt-timeline-empty{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;padding:12px 4px;text-align:center}
|
|
746
746
|
.dsh-rt-version{position:absolute;left:0;right:0;height:60px;box-sizing:border-box;display:flex;align-items:flex-start;gap:8px;border:1px solid transparent;border-radius:10px;padding:6px 8px}
|
|
747
747
|
.dsh-rt-version:hover{background:var(--dsw-alias-interactive-bg-hover);border-color:var(--dsw-alias-border-l2)}
|
|
@@ -811,8 +811,7 @@ return {
|
|
|
811
811
|
deleted: counts.deleted ?? 0
|
|
812
812
|
});
|
|
813
813
|
}
|
|
814
|
-
function
|
|
815
|
-
const [open, setOpen] = (0, import_react.useState)(false);
|
|
814
|
+
function RetraceView({ sessionId, useProjection, t, actions, store }) {
|
|
816
815
|
const [versions, setVersions] = (0, import_react.useState)(null);
|
|
817
816
|
const [loading, setLoading] = (0, import_react.useState)(false);
|
|
818
817
|
const [error, setError] = (0, import_react.useState)(null);
|
|
@@ -821,41 +820,11 @@ return {
|
|
|
821
820
|
const [preview, setPreview] = (0, import_react.useState)(null);
|
|
822
821
|
const [previewScope, setPreviewScope] = (0, import_react.useState)("both");
|
|
823
822
|
const [rollbackBusy, setRollbackBusy] = (0, import_react.useState)(false);
|
|
824
|
-
const [detail, setDetail] = (0, import_react.useState)(null);
|
|
825
|
-
const [jumpTarget, setJumpTarget] = (0, import_react.useState)(null);
|
|
826
|
-
const [jumpNotice, setJumpNotice] = (0, import_react.useState)(null);
|
|
827
823
|
const [scrollTop, setScrollTop] = (0, import_react.useState)(0);
|
|
828
|
-
const [panelPos, setPanelPos] = (0, import_react.useState)(null);
|
|
829
|
-
const rootRef = { current: null };
|
|
830
|
-
const triggerRef = { current: null };
|
|
831
|
-
const snapRef = { current: null };
|
|
832
824
|
const projected = typeof useProjection === "function" ? useProjection("retrace/versions") : void 0;
|
|
833
|
-
useSession((snapshot) => {
|
|
834
|
-
snapRef.current = snapshot;
|
|
835
|
-
return snapshot.chat.nodes.size;
|
|
836
|
-
});
|
|
837
825
|
(0, import_react.useEffect)(() => {
|
|
838
826
|
if (projected && Array.isArray(projected.versions)) setVersions(projected.versions);
|
|
839
827
|
}, [projected]);
|
|
840
|
-
const anchorPanel = () => {
|
|
841
|
-
const rect = triggerRef.current?.getBoundingClientRect?.();
|
|
842
|
-
if (!rect) return;
|
|
843
|
-
setPanelPos({
|
|
844
|
-
top: rect.bottom + 6,
|
|
845
|
-
right: Math.max(8, window.innerWidth - rect.right)
|
|
846
|
-
});
|
|
847
|
-
};
|
|
848
|
-
(0, import_react.useEffect)(() => {
|
|
849
|
-
if (!open) return;
|
|
850
|
-
anchorPanel();
|
|
851
|
-
const onViewportChange = () => anchorPanel();
|
|
852
|
-
window.addEventListener("resize", onViewportChange);
|
|
853
|
-
window.addEventListener("scroll", onViewportChange, true);
|
|
854
|
-
return () => {
|
|
855
|
-
window.removeEventListener("resize", onViewportChange);
|
|
856
|
-
window.removeEventListener("scroll", onViewportChange, true);
|
|
857
|
-
};
|
|
858
|
-
}, [open]);
|
|
859
828
|
const refresh = () => {
|
|
860
829
|
setLoading(true);
|
|
861
830
|
setError(null);
|
|
@@ -868,77 +837,67 @@ return {
|
|
|
868
837
|
if (getConfig().git !== true) return;
|
|
869
838
|
timelineGet(`/git/status?sessionId=${encodeURIComponent(sessionId)}`).then((result) => setGit(result?.ok === true ? result.value : null)).catch(() => setGit(null));
|
|
870
839
|
};
|
|
871
|
-
const toggle = () => {
|
|
872
|
-
const next = !open;
|
|
873
|
-
setOpen(next);
|
|
874
|
-
if (next) {
|
|
875
|
-
if (projected === void 0) refresh();
|
|
876
|
-
refreshGit();
|
|
877
|
-
}
|
|
878
|
-
};
|
|
879
840
|
(0, import_react.useEffect)(() => {
|
|
880
|
-
if (
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
};
|
|
884
|
-
document.addEventListener("pointerdown", onPointerDown);
|
|
885
|
-
return () => document.removeEventListener("pointerdown", onPointerDown);
|
|
886
|
-
}, [open]);
|
|
841
|
+
if (projected === void 0) refresh();
|
|
842
|
+
refreshGit();
|
|
843
|
+
}, []);
|
|
887
844
|
const JUMP_PAGE_BUDGET = 24;
|
|
888
845
|
const jump = async (boundarySeq) => {
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
const
|
|
893
|
-
|
|
846
|
+
actions?.setView?.("chat");
|
|
847
|
+
const target = store;
|
|
848
|
+
if (!target || typeof target.loadOlder !== "function" || typeof target.getSnapshot !== "function") return;
|
|
849
|
+
const keyOfSeq = (seq) => {
|
|
850
|
+
const nodes = target.getSnapshot()?.chat?.nodes;
|
|
851
|
+
if (!nodes) return null;
|
|
852
|
+
for (const node of nodes.values()) {
|
|
853
|
+
if (typeof node.anchorSeq === "number" && node.anchorSeq === seq) return node.key;
|
|
854
|
+
}
|
|
855
|
+
return null;
|
|
856
|
+
};
|
|
857
|
+
let key = keyOfSeq(boundarySeq);
|
|
894
858
|
let pages = 0;
|
|
895
|
-
while (pages < JUMP_PAGE_BUDGET &&
|
|
896
|
-
const before =
|
|
897
|
-
await
|
|
898
|
-
const after =
|
|
859
|
+
while (key === null && pages < JUMP_PAGE_BUDGET && target.hasMore) {
|
|
860
|
+
const before = target.getSnapshot()?.chat?.nodes?.size ?? 0;
|
|
861
|
+
await target.loadOlder();
|
|
862
|
+
const after = target.getSnapshot()?.chat?.nodes?.size ?? 0;
|
|
899
863
|
pages += 1;
|
|
900
864
|
if (after === before) break;
|
|
865
|
+
key = keyOfSeq(boundarySeq);
|
|
901
866
|
}
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
setJumpTarget(null);
|
|
867
|
+
if (key === null) {
|
|
868
|
+
await new Promise((resolve) => setTimeout(resolve, 60));
|
|
869
|
+
key = keyOfSeq(boundarySeq);
|
|
906
870
|
}
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
const nodes = snapRef.current?.chat?.nodes;
|
|
910
|
-
if (!nodes) return false;
|
|
911
|
-
for (const node of nodes.values()) {
|
|
912
|
-
if (typeof node.anchorSeq === "number" && node.anchorSeq === seq) return true;
|
|
913
|
-
}
|
|
914
|
-
return false;
|
|
915
|
-
}
|
|
916
|
-
const foundKey = useSession((snapshot) => {
|
|
917
|
-
if (jumpTarget === null) return null;
|
|
918
|
-
for (const node of snapshot.chat.nodes.values()) {
|
|
919
|
-
if (typeof node.anchorSeq === "number" && node.anchorSeq === jumpTarget) return node.key;
|
|
920
|
-
}
|
|
921
|
-
return null;
|
|
922
|
-
});
|
|
923
|
-
(0, import_react.useEffect)(() => {
|
|
924
|
-
if (foundKey === null || jumpTarget === null) return;
|
|
925
|
-
const el = document.querySelector(`[data-chat-anchor-key=${JSON.stringify(foundKey)}]`);
|
|
926
|
-
if (!el) {
|
|
927
|
-
setJumpNotice(t("timeline.jumpFailed"));
|
|
928
|
-
setJumpTarget(null);
|
|
871
|
+
if (key === null) {
|
|
872
|
+
console.warn(`[dsh-retrace] jump target seq ${boundarySeq} not reached within ${JUMP_PAGE_BUDGET} pages`);
|
|
929
873
|
return;
|
|
930
874
|
}
|
|
875
|
+
const el = await waitForElement(`[data-chat-anchor-key=${JSON.stringify(key)}]`, 90);
|
|
876
|
+
if (el === null) return;
|
|
931
877
|
el.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
878
|
+
flashKey(key);
|
|
879
|
+
};
|
|
880
|
+
function waitForElement(selector, frames) {
|
|
881
|
+
return new Promise((resolve) => {
|
|
882
|
+
let remaining = frames;
|
|
883
|
+
const probe = () => {
|
|
884
|
+
const el = document.querySelector(selector);
|
|
885
|
+
if (el !== null) return resolve(el);
|
|
886
|
+
if (remaining-- <= 0) return resolve(null);
|
|
887
|
+
requestAnimationFrame(probe);
|
|
888
|
+
};
|
|
889
|
+
requestAnimationFrame(probe);
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
function flashKey(key) {
|
|
893
|
+
const id = `dsh-rt-jump-${key.replace(/[^a-z0-9]/gi, "-")}`;
|
|
894
|
+
if (document.querySelector(`style[data-plugin-css="${id}"]`)) return;
|
|
895
|
+
const tag = document.createElement("style");
|
|
896
|
+
tag.dataset.pluginCss = id;
|
|
897
|
+
tag.textContent = `[data-chat-anchor-key=${JSON.stringify(key)}]{animation:dsh-rt-flash 1.6s ease-out 2}@keyframes dsh-rt-flash{0%,100%{background:transparent}30%,70%{background:var(--dsw-alias-state-business-primary)}55%{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 30%,transparent)}}`;
|
|
898
|
+
document.head.appendChild(tag);
|
|
899
|
+
setTimeout(() => tag.remove(), 3400);
|
|
900
|
+
}
|
|
942
901
|
const requestPreview = (record) => {
|
|
943
902
|
setPreviewScope("both");
|
|
944
903
|
setPreview({ versionId: record.versionId, kind: record.kind, boundarySeq: record.boundarySeq, data: null, error: null });
|
|
@@ -960,12 +919,6 @@ return {
|
|
|
960
919
|
refreshGit();
|
|
961
920
|
});
|
|
962
921
|
};
|
|
963
|
-
const openDetail = (record) => {
|
|
964
|
-
setDetail({ seq: record.boundarySeq, data: null, error: null });
|
|
965
|
-
timelineGet(`/event?sessionId=${encodeURIComponent(sessionId)}&seq=${record.boundarySeq}&before=2&after=2`).then((result) => {
|
|
966
|
-
setDetail((prev) => prev && prev.seq === record.boundarySeq ? { ...prev, data: result?.ok === true ? result.value : null, error: result?.ok === true ? null : result?.error?.message ?? null } : prev);
|
|
967
|
-
});
|
|
968
|
-
};
|
|
969
922
|
const initGit = () => {
|
|
970
923
|
if (!window.confirm(t("timeline.gitInitConfirm"))) return;
|
|
971
924
|
setGitBusy(true);
|
|
@@ -976,89 +929,62 @@ return {
|
|
|
976
929
|
};
|
|
977
930
|
const ROW_H = 64;
|
|
978
931
|
const list = versions ?? [];
|
|
979
|
-
const visible = list.slice(Math.max(0, Math.floor(scrollTop / ROW_H) - 2), Math.min(list.length, Math.ceil(scrollTop / ROW_H) + Math.ceil(
|
|
980
|
-
return (0, import_react.createElement)("div", { className: "dsh-rt-
|
|
981
|
-
(0, import_react.createElement)("
|
|
982
|
-
key: "
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
}, [
|
|
990
|
-
(0, import_react.createElement)("span", { key: "i", className: "dsh-rt-timeline-icon" }, "\u29C9"),
|
|
991
|
-
(0, import_react.createElement)("span", { key: "label", className: "dsh-rt-timeline-label" }, t("timeline.open"))
|
|
932
|
+
const visible = list.slice(Math.max(0, Math.floor(scrollTop / ROW_H) - 2), Math.min(list.length, Math.ceil(scrollTop / ROW_H) + Math.ceil(640 / ROW_H) + 2));
|
|
933
|
+
return (0, import_react.createElement)("div", { className: "dsh-rt-view" }, [
|
|
934
|
+
(0, import_react.createElement)("div", { key: "head", className: "dsh-rt-timeline-head" }, [
|
|
935
|
+
(0, import_react.createElement)("span", { key: "title", className: "dsh-rt-timeline-title" }, t("timeline.title")),
|
|
936
|
+
(0, import_react.createElement)("button", {
|
|
937
|
+
key: "refresh",
|
|
938
|
+
type: "button",
|
|
939
|
+
className: "dsh-rt-chip",
|
|
940
|
+
onClick: refresh
|
|
941
|
+
}, t("timeline.refresh"))
|
|
992
942
|
]),
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
943
|
+
git !== null && git !== void 0 && (0, import_react.createElement)("div", { key: "git", className: "dsh-rt-timeline-git" }, [
|
|
944
|
+
git.headHash ? [
|
|
945
|
+
(0, import_react.createElement)("span", { key: "r", className: "dsh-rt-timeline-git-text" }, `${t("timeline.gitRepo")} \xB7 ${t("timeline.gitHead", { hash: git.headHash.slice(0, 8) })}${git.dirty ? ` \xB7 ${t("timeline.gitDirty")}` : ""}`)
|
|
946
|
+
] : (0, import_react.createElement)("button", {
|
|
947
|
+
key: "init",
|
|
948
|
+
type: "button",
|
|
949
|
+
className: "dsh-rt-chip",
|
|
950
|
+
disabled: gitBusy,
|
|
951
|
+
onClick: initGit,
|
|
952
|
+
title: t("timeline.gitInitDesc")
|
|
953
|
+
}, t("timeline.gitInit"))
|
|
954
|
+
]),
|
|
955
|
+
error !== null && (0, import_react.createElement)("div", { key: "error", className: "dsh-rt-error" }, error),
|
|
956
|
+
loading && (0, import_react.createElement)("div", { key: "loading", className: "dsh-rt-timeline-empty" }, t("timeline.loading")),
|
|
957
|
+
!loading && list.length === 0 && (0, import_react.createElement)("div", { key: "empty", className: "dsh-rt-timeline-empty" }, t("timeline.empty")),
|
|
958
|
+
list.length > 0 && (0, import_react.createElement)("div", {
|
|
959
|
+
key: "list",
|
|
960
|
+
className: "dsh-rt-timeline-list",
|
|
961
|
+
onScroll: (event) => setScrollTop(event.target.scrollTop)
|
|
997
962
|
}, [
|
|
998
|
-
(0, import_react.createElement)("div", { key: "
|
|
999
|
-
(0, import_react.createElement)(
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
disabled: gitBusy,
|
|
1021
|
-
onClick: initGit,
|
|
1022
|
-
title: t("timeline.gitInitDesc")
|
|
1023
|
-
}, t("timeline.gitInit"))
|
|
1024
|
-
]),
|
|
1025
|
-
error !== null && (0, import_react.createElement)("div", { key: "error", className: "dsh-rt-error" }, error),
|
|
1026
|
-
loading && (0, import_react.createElement)("div", { key: "loading", className: "dsh-rt-timeline-empty" }, t("timeline.loading")),
|
|
1027
|
-
!loading && list.length === 0 && (0, import_react.createElement)("div", { key: "empty", className: "dsh-rt-timeline-empty" }, t("timeline.empty")),
|
|
1028
|
-
list.length > 0 && (0, import_react.createElement)("div", {
|
|
1029
|
-
key: "list",
|
|
1030
|
-
className: "dsh-rt-timeline-list",
|
|
1031
|
-
style: { height: `${Math.min(420, list.length * ROW_H)}px` },
|
|
1032
|
-
onScroll: (event) => setScrollTop(event.target.scrollTop)
|
|
1033
|
-
}, [
|
|
1034
|
-
(0, import_react.createElement)("div", { key: "spacer", style: { height: `${list.length * ROW_H}px`, position: "relative" } }, [
|
|
1035
|
-
visible.map((record) => (0, import_react.createElement)(VersionRow, {
|
|
1036
|
-
key: record.versionId,
|
|
1037
|
-
record,
|
|
1038
|
-
t,
|
|
1039
|
-
top: list.indexOf(record) * ROW_H,
|
|
1040
|
-
onPreview: () => requestPreview(record),
|
|
1041
|
-
onDetail: () => openDetail(record),
|
|
1042
|
-
onJump: () => jump(record.boundarySeq)
|
|
1043
|
-
}))
|
|
1044
|
-
])
|
|
1045
|
-
]),
|
|
1046
|
-
preview && (0, import_react.createElement)(PreviewBox, {
|
|
1047
|
-
key: "preview",
|
|
1048
|
-
preview,
|
|
1049
|
-
scope: previewScope,
|
|
1050
|
-
setScope: setPreviewScope,
|
|
1051
|
-
busy: rollbackBusy,
|
|
1052
|
-
t,
|
|
1053
|
-
onConfirm: confirmRollback,
|
|
1054
|
-
onCancel: () => setPreview(null)
|
|
1055
|
-
}),
|
|
1056
|
-
detail && (0, import_react.createElement)(DetailBox, { key: "detail", detail, t, onClose: () => setDetail(null) }),
|
|
1057
|
-
jumpNotice !== null && (0, import_react.createElement)("div", { key: "jump", className: "dsh-rt-error" }, jumpNotice)
|
|
1058
|
-
])
|
|
963
|
+
(0, import_react.createElement)("div", { key: "spacer", style: { height: `${list.length * ROW_H}px`, position: "relative" } }, [
|
|
964
|
+
visible.map((record) => (0, import_react.createElement)(VersionRow, {
|
|
965
|
+
key: record.versionId,
|
|
966
|
+
record,
|
|
967
|
+
t,
|
|
968
|
+
top: list.indexOf(record) * ROW_H,
|
|
969
|
+
onPreview: () => requestPreview(record),
|
|
970
|
+
onTrajectory: () => actions?.setView?.("trajectory"),
|
|
971
|
+
onJump: () => jump(record.boundarySeq)
|
|
972
|
+
}))
|
|
973
|
+
])
|
|
974
|
+
]),
|
|
975
|
+
preview && (0, import_react.createElement)(PreviewBox, {
|
|
976
|
+
key: "preview",
|
|
977
|
+
preview,
|
|
978
|
+
scope: previewScope,
|
|
979
|
+
setScope: setPreviewScope,
|
|
980
|
+
busy: rollbackBusy,
|
|
981
|
+
t,
|
|
982
|
+
onConfirm: confirmRollback,
|
|
983
|
+
onCancel: () => setPreview(null)
|
|
984
|
+
})
|
|
1059
985
|
]);
|
|
1060
986
|
}
|
|
1061
|
-
function VersionRow({ record, top, t, onPreview,
|
|
987
|
+
function VersionRow({ record, top, t, onPreview, onTrajectory, onJump }) {
|
|
1062
988
|
return (0, import_react.createElement)("div", { className: "dsh-rt-version", style: { top: `${top}px` } }, [
|
|
1063
989
|
(0, import_react.createElement)("span", { key: "kind", className: `dsh-rt-version-kind dsh-rt-version-kind-${record.kind}`, title: kindLabel(record.kind, t) }, KIND_ICONS[record.kind] ?? "\u2022"),
|
|
1064
990
|
(0, import_react.createElement)("div", { key: "body", className: "dsh-rt-version-body" }, [
|
|
@@ -1071,8 +997,8 @@ return {
|
|
|
1071
997
|
record.markerText ? (0, import_react.createElement)("div", { key: "text", className: "dsh-rt-version-text" }, record.markerText.length > 120 ? `${record.markerText.slice(0, 120)}\u2026` : record.markerText) : null
|
|
1072
998
|
]),
|
|
1073
999
|
(0, import_react.createElement)("span", { key: "actions", className: "dsh-rt-version-actions" }, [
|
|
1074
|
-
(0, import_react.createElement)("button", { key: "detail", type: "button", className: "dsh-rt-chip", onClick: onDetail }, t("timeline.detail")),
|
|
1075
1000
|
(0, import_react.createElement)("button", { key: "jump", type: "button", className: "dsh-rt-chip", onClick: onJump }, t("timeline.jump")),
|
|
1001
|
+
(0, import_react.createElement)("button", { key: "trajectory", type: "button", className: "dsh-rt-chip", onClick: onTrajectory, title: t("timeline.trajectory") }, t("timeline.trajectory")),
|
|
1076
1002
|
(0, import_react.createElement)("button", { key: "preview", type: "button", className: "dsh-rt-chip dsh-rt-chip-danger", onClick: onPreview }, t("timeline.preview"))
|
|
1077
1003
|
])
|
|
1078
1004
|
]);
|
|
@@ -1124,21 +1050,11 @@ return {
|
|
|
1124
1050
|
])
|
|
1125
1051
|
]);
|
|
1126
1052
|
}
|
|
1127
|
-
function DetailBox({ detail, t, onClose }) {
|
|
1128
|
-
return (0, import_react.createElement)("div", { className: "dsh-rt-modal" }, [
|
|
1129
|
-
(0, import_react.createElement)("div", { key: "title", className: "dsh-rt-modal-title" }, [
|
|
1130
|
-
`${t("timeline.detail")} \xB7 seq ${detail.seq}`,
|
|
1131
|
-
(0, import_react.createElement)("button", { key: "close", type: "button", className: "dsh-rt-chip", onClick: onClose }, t("timeline.close"))
|
|
1132
|
-
]),
|
|
1133
|
-
detail.error && (0, import_react.createElement)("div", { key: "err", className: "dsh-rt-error" }, detail.error),
|
|
1134
|
-
detail.data === null && !detail.error && (0, import_react.createElement)("div", { key: "wait", className: "dsh-rt-timeline-empty" }, t("timeline.loading")),
|
|
1135
|
-
detail.data && (0, import_react.createElement)("pre", { key: "json", className: "dsh-rt-modal-json" }, JSON.stringify(detail.data, null, 2))
|
|
1136
|
-
]);
|
|
1137
|
-
}
|
|
1138
1053
|
function apply(ctx) {
|
|
1139
1054
|
const disposeStyle = ensureStyle();
|
|
1140
1055
|
ctx.effect(() => () => disposeStyle(), "dsh-retrace: styles");
|
|
1141
1056
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), "dsh-retrace: dictionaries");
|
|
1057
|
+
const t = ctx.locale.bind(NS);
|
|
1142
1058
|
const conversationEvents = ctx.get("conversationEvents");
|
|
1143
1059
|
if (conversationEvents) {
|
|
1144
1060
|
conversationEvents.register(userActionsDefinition);
|
|
@@ -1166,12 +1082,17 @@ return {
|
|
|
1166
1082
|
key: "recall-marker",
|
|
1167
1083
|
locale: NS
|
|
1168
1084
|
}, RecallMarkerRow));
|
|
1169
|
-
ctx.slots.inject("conversation.
|
|
1170
|
-
name: "conversation.
|
|
1171
|
-
id: "retrace
|
|
1172
|
-
order:
|
|
1173
|
-
locale: NS
|
|
1174
|
-
|
|
1085
|
+
ctx.slots.inject("conversation.view", () => ctx.slots.register({
|
|
1086
|
+
name: "conversation.view",
|
|
1087
|
+
id: "retrace",
|
|
1088
|
+
order: 20,
|
|
1089
|
+
locale: NS,
|
|
1090
|
+
label: () => t("view.retrace"),
|
|
1091
|
+
inject: (sessionId, actions) => ({
|
|
1092
|
+
actions,
|
|
1093
|
+
store: ctx.get?.("sessions")?.binding?.(sessionId)?.session
|
|
1094
|
+
})
|
|
1095
|
+
}, RetraceView));
|
|
1175
1096
|
ctx.slots.inject("settings.general.item", () => ctx.slots.register({
|
|
1176
1097
|
name: "settings.general.item",
|
|
1177
1098
|
id: "retrace",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-retrace",
|
|
3
3
|
"description": "Retrace · 回溯 — Recall, edit-and-resend, regenerate, and conversation/artifact versioning (timeline, rollback, fork map) for DeepSeek Harness — Web and Desktop",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.2",
|
|
5
5
|
"packageManager": "pnpm@11.7.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "lib/index.js",
|