dsh-group-status 0.3.0 → 0.4.0
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/CHANGELOG.md +12 -0
- package/README.en.md +6 -6
- package/README.md +6 -6
- package/lib/client.js +1122 -183
- package/lib/client.js.map +1 -1
- package/lib/types/client/contract/slots.d.ts +39 -21
- package/lib/types/client/contract/slots.d.ts.map +1 -1
- package/lib/types/client/contract/slots.js.map +1 -1
- package/lib/types/client/contract/toast.d.ts +67 -0
- package/lib/types/client/contract/toast.d.ts.map +1 -0
- package/lib/types/client/contract/toast.js +2 -0
- package/lib/types/client/contract/toast.js.map +1 -0
- package/lib/types/client/index.d.ts +6 -3
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +48 -13
- package/lib/types/client/index.js.map +1 -1
- package/lib/types/client/locales.d.ts +14 -0
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/locales.js +14 -0
- package/lib/types/client/locales.js.map +1 -1
- package/lib/types/client/navigation.d.ts +45 -12
- package/lib/types/client/navigation.d.ts.map +1 -1
- package/lib/types/client/navigation.js +102 -26
- package/lib/types/client/navigation.js.map +1 -1
- package/lib/types/client/rows/Rows.d.ts.map +1 -1
- package/lib/types/client/rows/Rows.js +4 -4
- package/lib/types/client/rows/Rows.js.map +1 -1
- package/lib/types/client/rows/WorkspaceBrowser.d.ts +1 -1
- package/lib/types/client/rows/WorkspaceBrowser.d.ts.map +1 -1
- package/lib/types/client/rows/WorkspaceBrowser.js +54 -50
- package/lib/types/client/rows/WorkspaceBrowser.js.map +1 -1
- package/lib/types/client/session-actions/ArchiveSession.js +1 -1
- package/lib/types/client/session-actions/ArchiveSession.js.map +1 -1
- package/lib/types/client/session-actions/ForkSession.d.ts +5 -4
- package/lib/types/client/session-actions/ForkSession.d.ts.map +1 -1
- package/lib/types/client/session-actions/ForkSession.js +7 -4
- package/lib/types/client/session-actions/ForkSession.js.map +1 -1
- package/lib/types/client/session-actions/PinSession.js +1 -1
- package/lib/types/client/session-actions/PinSession.js.map +1 -1
- package/lib/types/client/session-actions/RowActionToast.d.ts +5 -8
- package/lib/types/client/session-actions/RowActionToast.d.ts.map +1 -1
- package/lib/types/client/session-actions/RowActionToast.js +10 -49
- package/lib/types/client/session-actions/RowActionToast.js.map +1 -1
- package/lib/types/client/session-actions/SessionId.d.ts +8 -0
- package/lib/types/client/session-actions/SessionId.d.ts.map +1 -0
- package/lib/types/client/session-actions/SessionId.js +16 -0
- package/lib/types/client/session-actions/SessionId.js.map +1 -0
- package/lib/types/client/session-watermarks.d.ts +30 -0
- package/lib/types/client/session-watermarks.d.ts.map +1 -0
- package/lib/types/client/session-watermarks.js +54 -0
- package/lib/types/client/session-watermarks.js.map +1 -0
- package/lib/types/client/shortcuts.d.ts +2 -7
- package/lib/types/client/shortcuts.d.ts.map +1 -1
- package/lib/types/client/shortcuts.js +7 -11
- package/lib/types/client/shortcuts.js.map +1 -1
- package/lib/types/client/toast/Toast.d.ts +41 -0
- package/lib/types/client/toast/Toast.d.ts.map +1 -0
- package/lib/types/client/toast/Toast.js +44 -0
- package/lib/types/client/toast/Toast.js.map +1 -0
- package/lib/types/client/toast/register.d.ts +11 -0
- package/lib/types/client/toast/register.d.ts.map +1 -0
- package/lib/types/client/toast/register.js +162 -0
- package/lib/types/client/toast/register.js.map +1 -0
- package/package.json +33 -30
- package/lib/types/client/legacy/bridge.d.ts +0 -30
- package/lib/types/client/legacy/bridge.d.ts.map +0 -1
- package/lib/types/client/legacy/bridge.js +0 -58
- package/lib/types/client/legacy/bridge.js.map +0 -1
- package/lib/types/client/legacy/register-legacy-facets.d.ts +0 -14
- package/lib/types/client/legacy/register-legacy-facets.d.ts.map +0 -1
- package/lib/types/client/legacy/register-legacy-facets.js +0 -77
- package/lib/types/client/legacy/register-legacy-facets.js.map +0 -1
- package/lib/types/client/legacy/schedule.d.ts +0 -49
- package/lib/types/client/legacy/schedule.d.ts.map +0 -1
- package/lib/types/client/legacy/schedule.js +0 -73
- package/lib/types/client/legacy/schedule.js.map +0 -1
- package/lib/types/client/legacy/store-source.d.ts +0 -78
- package/lib/types/client/legacy/store-source.d.ts.map +0 -1
- package/lib/types/client/legacy/store-source.js +0 -82
- package/lib/types/client/legacy/store-source.js.map +0 -1
- package/lib/types/client/legacy/waiting-review.d.ts +0 -85
- package/lib/types/client/legacy/waiting-review.d.ts.map +0 -1
- package/lib/types/client/legacy/waiting-review.js +0 -179
- package/lib/types/client/legacy/waiting-review.js.map +0 -1
package/lib/client.js
CHANGED
|
@@ -5,8 +5,8 @@ window.__ModuleLoader__.load({
|
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
|
|
8
|
-
let react = require("react");
|
|
9
8
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
9
|
+
let react = require("react");
|
|
10
10
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
11
11
|
let _deepseek_ai_cordis = require("@deepseek-ai/cordis");
|
|
12
12
|
//#region src/client/contract/slots.ts
|
|
@@ -30,10 +30,8 @@ window.__ModuleLoader__.load({
|
|
|
30
30
|
searchRequest: 0,
|
|
31
31
|
addRequested: false,
|
|
32
32
|
directoryBusy: false,
|
|
33
|
-
renameTarget: null
|
|
34
|
-
forkError: null
|
|
33
|
+
renameTarget: null
|
|
35
34
|
});
|
|
36
|
-
let forkErrorSeq = 0;
|
|
37
35
|
return {
|
|
38
36
|
state,
|
|
39
37
|
search: () => {
|
|
@@ -74,22 +72,6 @@ window.__ModuleLoader__.load({
|
|
|
74
72
|
...state.getSnapshot(),
|
|
75
73
|
renameTarget: null
|
|
76
74
|
});
|
|
77
|
-
},
|
|
78
|
-
forkFailed: (reason) => {
|
|
79
|
-
forkErrorSeq += 1;
|
|
80
|
-
state.set({
|
|
81
|
-
...state.getSnapshot(),
|
|
82
|
-
forkError: {
|
|
83
|
-
reason,
|
|
84
|
-
seq: forkErrorSeq
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
},
|
|
88
|
-
dismissForkError: () => {
|
|
89
|
-
state.set({
|
|
90
|
-
...state.getSnapshot(),
|
|
91
|
-
forkError: null
|
|
92
|
-
});
|
|
93
75
|
}
|
|
94
76
|
};
|
|
95
77
|
}
|
|
@@ -100,8 +82,9 @@ window.__ModuleLoader__.load({
|
|
|
100
82
|
* @param navigation - session creation and forking from the pointer controls' navigation service.
|
|
101
83
|
* @param controls - browser-owned opening requests.
|
|
102
84
|
* @param archiveSession - shared archive action, including running-work confirmation and notices.
|
|
85
|
+
* @param reportForkFailure - publish a classified failure through the frame-wide notice channel.
|
|
103
86
|
*/
|
|
104
|
-
function installWorkspaceShortcuts(ctx, navigation, controls, archiveSession) {
|
|
87
|
+
function installWorkspaceShortcuts(ctx, navigation, controls, archiveSession, reportForkFailure) {
|
|
105
88
|
const t = ctx.locale.bind("workspace");
|
|
106
89
|
const current = () => Object.values(ctx.sessions.list.getSnapshot().byId).find((row) => (row.retainedBy.mainView ?? 0) > 0);
|
|
107
90
|
const addReason = () => ctx.slots.entries("sidebar.workspaces.directoryFlow").length === 0 ? t("shortcut.noPicker") : controls.state.getSnapshot().directoryBusy ? t("shortcut.directoryBusy") : null;
|
|
@@ -183,8 +166,8 @@ window.__ModuleLoader__.load({
|
|
|
183
166
|
status: "handled",
|
|
184
167
|
run: () => {
|
|
185
168
|
navigation.forkSession(target.id).catch((error) => {
|
|
186
|
-
const unavailable = error instanceof Error && error.name === "SessionForkError"
|
|
187
|
-
|
|
169
|
+
const unavailable = (error instanceof Error && error.name === "SessionForkError" ? error : void 0)?.rpcError.code === "session/fork-unavailable";
|
|
170
|
+
reportForkFailure(unavailable ? "unavailable" : "failed");
|
|
188
171
|
if (!unavailable) console.warn("session fork rejected:", error);
|
|
189
172
|
});
|
|
190
173
|
}
|
|
@@ -902,7 +885,838 @@ window.__ModuleLoader__.load({
|
|
|
902
885
|
});
|
|
903
886
|
}
|
|
904
887
|
//#endregion
|
|
905
|
-
//#region
|
|
888
|
+
//#region src/client/toast/Toast.tsx
|
|
889
|
+
/** Shared Workspace notice presentation and keyed business adapters. */
|
|
890
|
+
/**
|
|
891
|
+
* Render one notice using the shared Factory.
|
|
892
|
+
* @param props - keyed payload, injected display mapping, locale, and Factory renderer.
|
|
893
|
+
* @returns the shared Toast occurrence.
|
|
894
|
+
*/
|
|
895
|
+
function ToastEntry(props) {
|
|
896
|
+
const { dismissToast, display, t, renderFactorySlot, seq } = props;
|
|
897
|
+
return renderFactorySlot("workspace.toast.content", {
|
|
898
|
+
...display({ ...props }, t, dismissToast),
|
|
899
|
+
seq,
|
|
900
|
+
onDone: dismissToast
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Keep the archive filter action reactive without subscribing unrelated notices.
|
|
905
|
+
* @param props - archive notice, display mapping, and the existing viewing store.
|
|
906
|
+
* @returns the shared Toast occurrence with its current actions.
|
|
907
|
+
*/
|
|
908
|
+
function ArchiveToastEntry(props) {
|
|
909
|
+
const { dismissToast, display, filterAction, useStore, t, renderFactorySlot, seq } = props;
|
|
910
|
+
const archivedVisible = useStore((state) => (state.archivedFilter ?? "default") !== "default");
|
|
911
|
+
const fields = display({ ...props }, t, dismissToast);
|
|
912
|
+
return renderFactorySlot("workspace.toast.content", {
|
|
913
|
+
...fields,
|
|
914
|
+
actions: [...fields.actions ?? [], ...archivedVisible ? [] : [filterAction(t, dismissToast)]],
|
|
915
|
+
seq,
|
|
916
|
+
onDone: dismissToast
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* Present plain display data with the shared primitive's timing and portal.
|
|
921
|
+
* @param props - localized display fields, display identity, and dismissal.
|
|
922
|
+
* @returns one Toast, remounted only for a new display identity.
|
|
923
|
+
*/
|
|
924
|
+
function WorkspaceToast({ seq, text, tone, holdMs, actions, onDone }) {
|
|
925
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
926
|
+
text,
|
|
927
|
+
onDone,
|
|
928
|
+
...tone === "success" ? { tone: "success" } : {},
|
|
929
|
+
...tone === "warning" ? { icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutlineRegular, {}) } : {},
|
|
930
|
+
...holdMs === void 0 ? {} : { holdMs },
|
|
931
|
+
...actions === void 0 ? {} : { actions }
|
|
932
|
+
}, seq);
|
|
933
|
+
}
|
|
934
|
+
//#endregion
|
|
935
|
+
//#region src/client/toast/register.tsx
|
|
936
|
+
/** Wrap a business display mapping without making the slot component generic. */
|
|
937
|
+
function plainToast(display) {
|
|
938
|
+
return () => ({ display });
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Register notice presentations with their business-owned display fields and actions.
|
|
942
|
+
* @param ctx - workspace plugin context.
|
|
943
|
+
* @param viewStore - viewing store, read only by archive notices.
|
|
944
|
+
* @param actions - archive and source-opening callbacks.
|
|
945
|
+
*/
|
|
946
|
+
function registerToasts(ctx, viewStore, actions) {
|
|
947
|
+
const toastEntry = ToastEntry;
|
|
948
|
+
const archiveToastEntry = ArchiveToastEntry;
|
|
949
|
+
const archiveFilterAction = (t, dismiss) => ({
|
|
950
|
+
prefix: t("toast.archivedOr"),
|
|
951
|
+
label: t("toast.archivedFilter"),
|
|
952
|
+
onClick: () => {
|
|
953
|
+
dismiss();
|
|
954
|
+
actions.showArchived();
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
ctx.slots.registerFactory({
|
|
958
|
+
name: "workspace.toast.content",
|
|
959
|
+
scope: "root"
|
|
960
|
+
}, WorkspaceToast);
|
|
961
|
+
ctx.slots.inject("workspace.toast", function* () {
|
|
962
|
+
yield ctx.slots.register({
|
|
963
|
+
name: "workspace.toast",
|
|
964
|
+
key: "sessionIdCopied",
|
|
965
|
+
locale: "workspace",
|
|
966
|
+
inject: plainToast((_notice, t) => ({
|
|
967
|
+
text: t("toast.sessionIdCopied"),
|
|
968
|
+
tone: "success"
|
|
969
|
+
}))
|
|
970
|
+
}, toastEntry);
|
|
971
|
+
yield ctx.slots.register({
|
|
972
|
+
name: "workspace.toast",
|
|
973
|
+
key: "sessionIdCopyFailed",
|
|
974
|
+
locale: "workspace",
|
|
975
|
+
inject: plainToast((_notice, t) => ({
|
|
976
|
+
text: t("toast.sessionIdCopyFailed"),
|
|
977
|
+
tone: "warning"
|
|
978
|
+
}))
|
|
979
|
+
}, toastEntry);
|
|
980
|
+
yield ctx.slots.register({
|
|
981
|
+
name: "workspace.toast",
|
|
982
|
+
key: "pinFailed",
|
|
983
|
+
locale: "workspace",
|
|
984
|
+
inject: plainToast((_notice, t) => ({
|
|
985
|
+
text: t("toast.pinFailed"),
|
|
986
|
+
tone: "warning"
|
|
987
|
+
}))
|
|
988
|
+
}, toastEntry);
|
|
989
|
+
yield ctx.slots.register({
|
|
990
|
+
name: "workspace.toast",
|
|
991
|
+
key: "unpinFailed",
|
|
992
|
+
locale: "workspace",
|
|
993
|
+
inject: plainToast((_notice, t) => ({
|
|
994
|
+
text: t("toast.unpinFailed"),
|
|
995
|
+
tone: "warning"
|
|
996
|
+
}))
|
|
997
|
+
}, toastEntry);
|
|
998
|
+
yield ctx.slots.register({
|
|
999
|
+
name: "workspace.toast",
|
|
1000
|
+
key: "archivedNotOpenable",
|
|
1001
|
+
locale: "workspace",
|
|
1002
|
+
inject: plainToast((_notice, t) => ({
|
|
1003
|
+
text: t("toast.archivedNotOpenable"),
|
|
1004
|
+
tone: "warning"
|
|
1005
|
+
}))
|
|
1006
|
+
}, toastEntry);
|
|
1007
|
+
yield ctx.slots.register({
|
|
1008
|
+
name: "workspace.toast",
|
|
1009
|
+
key: "defaultWorkspaceFailed",
|
|
1010
|
+
locale: "workspace",
|
|
1011
|
+
inject: plainToast((_notice, t) => ({
|
|
1012
|
+
text: t("defaultWorkspace.failed"),
|
|
1013
|
+
tone: "warning"
|
|
1014
|
+
}))
|
|
1015
|
+
}, toastEntry);
|
|
1016
|
+
yield ctx.slots.register({
|
|
1017
|
+
name: "workspace.toast",
|
|
1018
|
+
key: "shortcutForkFailed",
|
|
1019
|
+
locale: "workspace",
|
|
1020
|
+
inject: plainToast((notice, t) => ({
|
|
1021
|
+
text: t(notice.reason === "unavailable" ? "shortcut.noCompletedTurn" : "shortcut.forkFailed"),
|
|
1022
|
+
tone: "plain"
|
|
1023
|
+
}))
|
|
1024
|
+
}, toastEntry);
|
|
1025
|
+
yield ctx.slots.register({
|
|
1026
|
+
name: "workspace.toast",
|
|
1027
|
+
key: "createFailed",
|
|
1028
|
+
locale: "workspace",
|
|
1029
|
+
inject: plainToast((notice, t) => ({
|
|
1030
|
+
text: t("toast.createFailed", { message: notice.message }),
|
|
1031
|
+
tone: "warning",
|
|
1032
|
+
holdMs: 6e3
|
|
1033
|
+
}))
|
|
1034
|
+
}, toastEntry);
|
|
1035
|
+
yield ctx.slots.register({
|
|
1036
|
+
name: "workspace.toast",
|
|
1037
|
+
key: "forkRequiresOpen",
|
|
1038
|
+
locale: "workspace",
|
|
1039
|
+
inject: plainToast((notice, t, dismiss) => ({
|
|
1040
|
+
text: t("toast.forkRequiresOpen"),
|
|
1041
|
+
tone: "warning",
|
|
1042
|
+
holdMs: 6e3,
|
|
1043
|
+
actions: [{
|
|
1044
|
+
label: t("toast.openForkSource"),
|
|
1045
|
+
onClick: () => {
|
|
1046
|
+
dismiss();
|
|
1047
|
+
actions.openForkSource(notice.sessionId);
|
|
1048
|
+
}
|
|
1049
|
+
}]
|
|
1050
|
+
}))
|
|
1051
|
+
}, toastEntry);
|
|
1052
|
+
yield ctx.slots.register({
|
|
1053
|
+
name: "workspace.toast",
|
|
1054
|
+
key: "archived",
|
|
1055
|
+
locale: "workspace",
|
|
1056
|
+
store: viewStore,
|
|
1057
|
+
inject: () => ({
|
|
1058
|
+
display: (notice, t, dismiss) => ({
|
|
1059
|
+
text: t("toast.archived"),
|
|
1060
|
+
tone: "success",
|
|
1061
|
+
holdMs: 6e3,
|
|
1062
|
+
actions: [{
|
|
1063
|
+
label: t("toast.archivedUndo"),
|
|
1064
|
+
onClick: () => {
|
|
1065
|
+
dismiss();
|
|
1066
|
+
actions.undoArchive(notice.sessionId);
|
|
1067
|
+
}
|
|
1068
|
+
}]
|
|
1069
|
+
}),
|
|
1070
|
+
filterAction: archiveFilterAction
|
|
1071
|
+
})
|
|
1072
|
+
}, archiveToastEntry);
|
|
1073
|
+
yield ctx.slots.register({
|
|
1074
|
+
name: "workspace.toast",
|
|
1075
|
+
key: "stoppedAndArchived",
|
|
1076
|
+
locale: "workspace",
|
|
1077
|
+
store: viewStore,
|
|
1078
|
+
inject: () => ({
|
|
1079
|
+
display: (notice, t, dismiss) => ({
|
|
1080
|
+
text: t("toast.stoppedAndArchived"),
|
|
1081
|
+
tone: "success",
|
|
1082
|
+
holdMs: 6e3,
|
|
1083
|
+
actions: [{
|
|
1084
|
+
label: t("toast.archivedUndo"),
|
|
1085
|
+
onClick: () => {
|
|
1086
|
+
dismiss();
|
|
1087
|
+
actions.undoArchive(notice.sessionId);
|
|
1088
|
+
}
|
|
1089
|
+
}]
|
|
1090
|
+
}),
|
|
1091
|
+
filterAction: archiveFilterAction
|
|
1092
|
+
})
|
|
1093
|
+
}, archiveToastEntry);
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
//#endregion
|
|
1097
|
+
//#region node_modules/.pnpm/@deepseek-ai+dsh-util-values@0.2.1-rc.2_@deepseek-ai+cordis@4.0.5/node_modules/@deepseek-ai/dsh-util-values/lib/index.js
|
|
1098
|
+
/**
|
|
1099
|
+
* Lazily scanned view of one JSON object's top-level fields, built from text
|
|
1100
|
+
* that may still be streaming or from an already parsed object. Nothing is
|
|
1101
|
+
* scanned until a reader asks; the view remembers every question it answered
|
|
1102
|
+
* and reports changed answers when the owner refreshes for publication.
|
|
1103
|
+
* Used for model tool-call arguments: a row reads the fields it
|
|
1104
|
+
* cares about at whatever granularity it displays, at every stage of the call.
|
|
1105
|
+
* @module @deepseek-ai/dsh-util-values/src/partial-json
|
|
1106
|
+
*/
|
|
1107
|
+
const SIMPLE_ESCAPES = {
|
|
1108
|
+
"\"": "\"",
|
|
1109
|
+
"\\": "\\",
|
|
1110
|
+
"/": "/",
|
|
1111
|
+
b: "\b",
|
|
1112
|
+
f: "\f",
|
|
1113
|
+
n: "\n",
|
|
1114
|
+
r: "\r",
|
|
1115
|
+
t: " "
|
|
1116
|
+
};
|
|
1117
|
+
const CONTENT_ESCAPE = /[\\\u0000-\u001f]/u;
|
|
1118
|
+
function isWhitespace(c) {
|
|
1119
|
+
return c === " " || c === "\n" || c === "\r" || c === " ";
|
|
1120
|
+
}
|
|
1121
|
+
function isHex(c) {
|
|
1122
|
+
return c >= "0" && c <= "9" || c >= "a" && c <= "f" || c >= "A" && c <= "F";
|
|
1123
|
+
}
|
|
1124
|
+
(class PartialArguments {
|
|
1125
|
+
/** The view of a call with no arguments available. */
|
|
1126
|
+
static EMPTY = PartialArguments.fromObject({});
|
|
1127
|
+
/**
|
|
1128
|
+
* View finished argument text without scanning it until a reader asks.
|
|
1129
|
+
* @param text - the complete argument JSON text.
|
|
1130
|
+
* @returns a sealed view.
|
|
1131
|
+
*/
|
|
1132
|
+
static fromText(text) {
|
|
1133
|
+
const view = new PartialArguments();
|
|
1134
|
+
view.append(text);
|
|
1135
|
+
view.sealed = true;
|
|
1136
|
+
return view;
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* View an already parsed argument payload, such as a PTC dispatch object.
|
|
1140
|
+
* @param value - the parsed argument value.
|
|
1141
|
+
* @returns a sealed view; a non-object payload has no fields.
|
|
1142
|
+
*/
|
|
1143
|
+
static fromObject(value) {
|
|
1144
|
+
const view = new PartialArguments();
|
|
1145
|
+
view.object = typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
1146
|
+
view.sealed = true;
|
|
1147
|
+
return view;
|
|
1148
|
+
}
|
|
1149
|
+
/**
|
|
1150
|
+
* The source: text so far or a parsed object, plus whether it can still grow.
|
|
1151
|
+
* These are the only enumerable fields, so two views over the same source
|
|
1152
|
+
* compare equal structurally however far each has been read.
|
|
1153
|
+
*/
|
|
1154
|
+
chunks = [];
|
|
1155
|
+
object;
|
|
1156
|
+
sealed = false;
|
|
1157
|
+
#ends = [];
|
|
1158
|
+
#size = 0;
|
|
1159
|
+
#consumed = 0;
|
|
1160
|
+
#mode = "root";
|
|
1161
|
+
#escape = false;
|
|
1162
|
+
#keyStart = 0;
|
|
1163
|
+
#keyEscaped = false;
|
|
1164
|
+
#key = "";
|
|
1165
|
+
#current = null;
|
|
1166
|
+
#nestedEnds = [];
|
|
1167
|
+
#nestedInString = false;
|
|
1168
|
+
#invalidAt;
|
|
1169
|
+
#invalidValue = false;
|
|
1170
|
+
#entries = /* @__PURE__ */ new Map();
|
|
1171
|
+
#order = [];
|
|
1172
|
+
#reads = /* @__PURE__ */ new Map();
|
|
1173
|
+
/** Whether this view rejects further appends; does not scan text or register reads. */
|
|
1174
|
+
get isSealed() {
|
|
1175
|
+
return this.sealed;
|
|
1176
|
+
}
|
|
1177
|
+
/** Whether indexing or a content read found invalid JSON; unread value contents are not validated. */
|
|
1178
|
+
get invalid() {
|
|
1179
|
+
this.scan();
|
|
1180
|
+
return this.#mode === "invalid" || this.#invalidValue;
|
|
1181
|
+
}
|
|
1182
|
+
/**
|
|
1183
|
+
* Retain streamed argument text without scanning or comparing observed answers.
|
|
1184
|
+
* @param fragment - the text following every fragment appended before.
|
|
1185
|
+
*/
|
|
1186
|
+
append(fragment) {
|
|
1187
|
+
if (this.sealed) throw new Error("PartialArguments: cannot append to a sealed view");
|
|
1188
|
+
if (fragment.length === 0) return;
|
|
1189
|
+
this.chunks.push(fragment);
|
|
1190
|
+
this.#size += fragment.length;
|
|
1191
|
+
this.#ends.push(this.#size);
|
|
1192
|
+
}
|
|
1193
|
+
/**
|
|
1194
|
+
* Reconcile a streamed prefix with authoritative complete text without joining the fragments.
|
|
1195
|
+
* @param text - the final argument text, which replaces missing or conflicting deltas.
|
|
1196
|
+
* @returns this view sealed with its caches retained when every character matches; otherwise a new sealed view.
|
|
1197
|
+
*/
|
|
1198
|
+
settle(text) {
|
|
1199
|
+
if (this.object !== void 0 || text.length !== this.#size) return PartialArguments.fromText(text);
|
|
1200
|
+
let offset = 0;
|
|
1201
|
+
for (const chunk of this.chunks) {
|
|
1202
|
+
if (!text.startsWith(chunk, offset)) return PartialArguments.fromText(text);
|
|
1203
|
+
offset += chunk.length;
|
|
1204
|
+
}
|
|
1205
|
+
this.chunks = text.length === 0 ? [] : [text];
|
|
1206
|
+
this.#ends = text.length === 0 ? [] : [text.length];
|
|
1207
|
+
this.sealed = true;
|
|
1208
|
+
return this;
|
|
1209
|
+
}
|
|
1210
|
+
/**
|
|
1211
|
+
* Compare observed answers and advance their publication baseline. Unread views remain unscanned.
|
|
1212
|
+
* @returns whether any observed answer changed since its first read or the preceding refresh.
|
|
1213
|
+
*/
|
|
1214
|
+
refresh() {
|
|
1215
|
+
if (this.#reads.size === 0) return false;
|
|
1216
|
+
this.scan();
|
|
1217
|
+
let changed = false;
|
|
1218
|
+
let completions = false;
|
|
1219
|
+
for (const read of this.#reads.values()) {
|
|
1220
|
+
if (read.completion) {
|
|
1221
|
+
completions = true;
|
|
1222
|
+
continue;
|
|
1223
|
+
}
|
|
1224
|
+
changed = this.refreshRead(read) || changed;
|
|
1225
|
+
}
|
|
1226
|
+
if (completions) {
|
|
1227
|
+
for (const read of this.#reads.values()) if (read.completion) changed = this.refreshRead(read) || changed;
|
|
1228
|
+
}
|
|
1229
|
+
if (this.sealed) this.#reads.clear();
|
|
1230
|
+
return changed;
|
|
1231
|
+
}
|
|
1232
|
+
refreshRead(read) {
|
|
1233
|
+
const now = read.answer();
|
|
1234
|
+
if (Object.is(now, read.last)) return false;
|
|
1235
|
+
read.last = now;
|
|
1236
|
+
return true;
|
|
1237
|
+
}
|
|
1238
|
+
/**
|
|
1239
|
+
* Check whether no further fields can arrive.
|
|
1240
|
+
* @returns whether the outer object closed, indexing failed, or the view is sealed; unread values are not validated.
|
|
1241
|
+
*/
|
|
1242
|
+
closed() {
|
|
1243
|
+
return this.remember("closed", "", () => this.closedNow());
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* List discovered fields in first-appearance order.
|
|
1247
|
+
* @returns top-level keys seen so far, in first-appearance order.
|
|
1248
|
+
*/
|
|
1249
|
+
keys() {
|
|
1250
|
+
return this.remember("keys", "", () => this.keysNow(), (keys) => keys.length);
|
|
1251
|
+
}
|
|
1252
|
+
/**
|
|
1253
|
+
* Check whether a top-level field has appeared.
|
|
1254
|
+
* @param key - argument name.
|
|
1255
|
+
* @returns whether the field has appeared (a string opened or another value began).
|
|
1256
|
+
*/
|
|
1257
|
+
has(key) {
|
|
1258
|
+
return this.remember("has", key, () => this.hasNow(key));
|
|
1259
|
+
}
|
|
1260
|
+
/**
|
|
1261
|
+
* Check whether a field's closing delimiter has arrived, without validating its contents.
|
|
1262
|
+
* @param key - argument name.
|
|
1263
|
+
* @returns whether its delimiter arrived and no content reader has reported an error for this value.
|
|
1264
|
+
*/
|
|
1265
|
+
complete(key) {
|
|
1266
|
+
return this.remember("complete", key, () => this.completeNow(key));
|
|
1267
|
+
}
|
|
1268
|
+
/**
|
|
1269
|
+
* Read string length without materializing its text.
|
|
1270
|
+
* @param key - argument name.
|
|
1271
|
+
* @param options - change granularity for a streaming string.
|
|
1272
|
+
* @returns decoded UTF-16 length of the string field so far; undefined when absent or not a string.
|
|
1273
|
+
*/
|
|
1274
|
+
stringLength(key, options) {
|
|
1275
|
+
const step = Math.max(1, Math.floor(options?.step ?? 1));
|
|
1276
|
+
const offset = options?.offset ?? 0;
|
|
1277
|
+
return this.remember(`length:${step}:${offset}`, key, () => this.lengthNow(key), (length) => length === void 0 ? void 0 : Math.ceil((length + offset) / step));
|
|
1278
|
+
}
|
|
1279
|
+
/**
|
|
1280
|
+
* Check a string against a decoded UTF-16 length limit without materializing it.
|
|
1281
|
+
* @param key - argument name.
|
|
1282
|
+
* @param maxLength - decoded UTF-16 limit, floored to at least zero.
|
|
1283
|
+
* @returns whether the string is longer than the limit; false when absent or not a string.
|
|
1284
|
+
*/
|
|
1285
|
+
stringExceeds(key, maxLength) {
|
|
1286
|
+
const limit = Math.max(0, Math.floor(maxLength));
|
|
1287
|
+
return this.remember(`exceeds:${limit}`, key, () => (this.lengthNow(key, limit + 1) ?? 0) > limit);
|
|
1288
|
+
}
|
|
1289
|
+
/**
|
|
1290
|
+
* Read a decoded string, including a streaming prefix.
|
|
1291
|
+
* @param key - argument name.
|
|
1292
|
+
* @returns the string field's decoded text so far; undefined when absent or not a string.
|
|
1293
|
+
*/
|
|
1294
|
+
text(key) {
|
|
1295
|
+
return this.remember("text", key, () => this.textNow(key));
|
|
1296
|
+
}
|
|
1297
|
+
/**
|
|
1298
|
+
* Read at most the first decoded UTF-16 units of a string.
|
|
1299
|
+
* @param key - argument name.
|
|
1300
|
+
* @param maxLength - maximum decoded UTF-16 length, floored to at least one.
|
|
1301
|
+
* @returns the bounded string prefix; undefined when absent or not a string.
|
|
1302
|
+
*/
|
|
1303
|
+
textPrefix(key, maxLength) {
|
|
1304
|
+
const limit = Math.max(1, Math.floor(maxLength));
|
|
1305
|
+
return this.remember(`prefix:${limit}`, key, () => this.textPrefixNow(key, limit));
|
|
1306
|
+
}
|
|
1307
|
+
/**
|
|
1308
|
+
* Read a completed non-string argument.
|
|
1309
|
+
* @param key - argument name.
|
|
1310
|
+
* @returns the parsed non-string value once it closed; undefined while open, absent, or a string.
|
|
1311
|
+
*/
|
|
1312
|
+
value(key) {
|
|
1313
|
+
return this.remember("value", key, () => this.valueNow(key));
|
|
1314
|
+
}
|
|
1315
|
+
/** Answer a question and, on a streaming view, remember it for change detection. */
|
|
1316
|
+
remember(kind, key, read, comparison) {
|
|
1317
|
+
this.scan();
|
|
1318
|
+
const result = read();
|
|
1319
|
+
if (!this.sealed) {
|
|
1320
|
+
const id = `${kind}/${key}`;
|
|
1321
|
+
if (!this.#reads.has(id)) this.#reads.set(id, {
|
|
1322
|
+
completion: kind === "complete",
|
|
1323
|
+
answer: comparison === void 0 ? read : () => comparison(read()),
|
|
1324
|
+
last: comparison === void 0 ? result : comparison(result)
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
return result;
|
|
1328
|
+
}
|
|
1329
|
+
closedNow() {
|
|
1330
|
+
return this.sealed || this.#mode === "closed" || this.#mode === "invalid";
|
|
1331
|
+
}
|
|
1332
|
+
keysNow() {
|
|
1333
|
+
return this.object === void 0 ? this.#order : Object.keys(this.object);
|
|
1334
|
+
}
|
|
1335
|
+
hasNow(key) {
|
|
1336
|
+
return this.object === void 0 ? this.#entries.has(key) : Object.hasOwn(this.object, key);
|
|
1337
|
+
}
|
|
1338
|
+
completeNow(key) {
|
|
1339
|
+
if (this.object !== void 0) return Object.hasOwn(this.object, key);
|
|
1340
|
+
const entry = this.#entries.get(key);
|
|
1341
|
+
return entry !== void 0 && entry.end >= 0 && (entry.kind === "string" ? entry.invalidAt === void 0 : !entry.invalid);
|
|
1342
|
+
}
|
|
1343
|
+
lengthNow(key, limit = Number.POSITIVE_INFINITY) {
|
|
1344
|
+
if (this.object !== void 0) {
|
|
1345
|
+
const field = Object.hasOwn(this.object, key) ? this.object[key] : void 0;
|
|
1346
|
+
return typeof field === "string" ? field.length : void 0;
|
|
1347
|
+
}
|
|
1348
|
+
const entry = this.#entries.get(key);
|
|
1349
|
+
if (entry?.kind !== "string") return void 0;
|
|
1350
|
+
if (entry.text !== void 0 && entry.text.at === entry.end) return entry.text.length;
|
|
1351
|
+
const read = entry.length ??= {
|
|
1352
|
+
at: entry.start,
|
|
1353
|
+
length: 0,
|
|
1354
|
+
text: ""
|
|
1355
|
+
};
|
|
1356
|
+
this.readString(entry, read, limit, false);
|
|
1357
|
+
return read.length;
|
|
1358
|
+
}
|
|
1359
|
+
textNow(key) {
|
|
1360
|
+
if (this.object !== void 0) {
|
|
1361
|
+
const field = Object.hasOwn(this.object, key) ? this.object[key] : void 0;
|
|
1362
|
+
return typeof field === "string" ? field : void 0;
|
|
1363
|
+
}
|
|
1364
|
+
const entry = this.#entries.get(key);
|
|
1365
|
+
if (entry?.kind !== "string") return void 0;
|
|
1366
|
+
if (entry.text === void 0 && entry.end >= 0 && entry.needsDecoding && entry.invalidAt === void 0) {
|
|
1367
|
+
let text;
|
|
1368
|
+
try {
|
|
1369
|
+
text = JSON.parse(`"${this.slice(entry.start, entry.end)}"`);
|
|
1370
|
+
} catch (_error) {}
|
|
1371
|
+
if (text !== void 0) entry.text = {
|
|
1372
|
+
at: entry.end,
|
|
1373
|
+
length: text.length,
|
|
1374
|
+
text
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1377
|
+
const read = entry.text ??= {
|
|
1378
|
+
at: entry.start,
|
|
1379
|
+
length: 0,
|
|
1380
|
+
text: ""
|
|
1381
|
+
};
|
|
1382
|
+
this.readString(entry, read, Number.POSITIVE_INFINITY, true);
|
|
1383
|
+
return read.text;
|
|
1384
|
+
}
|
|
1385
|
+
textPrefixNow(key, maxLength) {
|
|
1386
|
+
if (this.object !== void 0) {
|
|
1387
|
+
const field = Object.hasOwn(this.object, key) ? this.object[key] : void 0;
|
|
1388
|
+
return typeof field === "string" ? field.slice(0, maxLength) : void 0;
|
|
1389
|
+
}
|
|
1390
|
+
const entry = this.#entries.get(key);
|
|
1391
|
+
if (entry?.kind !== "string") return void 0;
|
|
1392
|
+
const prefixes = entry.prefixes ??= /* @__PURE__ */ new Map();
|
|
1393
|
+
let read = prefixes.get(maxLength);
|
|
1394
|
+
if (read === void 0) {
|
|
1395
|
+
read = {
|
|
1396
|
+
at: entry.start,
|
|
1397
|
+
length: 0,
|
|
1398
|
+
text: ""
|
|
1399
|
+
};
|
|
1400
|
+
prefixes.set(maxLength, read);
|
|
1401
|
+
}
|
|
1402
|
+
this.readString(entry, read, maxLength, true);
|
|
1403
|
+
return read.text;
|
|
1404
|
+
}
|
|
1405
|
+
valueNow(key) {
|
|
1406
|
+
if (this.object !== void 0) {
|
|
1407
|
+
if (!Object.hasOwn(this.object, key)) return void 0;
|
|
1408
|
+
const field = this.object[key];
|
|
1409
|
+
return typeof field === "string" ? void 0 : field;
|
|
1410
|
+
}
|
|
1411
|
+
const entry = this.#entries.get(key);
|
|
1412
|
+
if (entry?.kind !== "value" || entry.end < 0 || entry.invalid) return void 0;
|
|
1413
|
+
if (entry.parsed === void 0) try {
|
|
1414
|
+
entry.parsed = JSON.parse(this.slice(entry.start, entry.end));
|
|
1415
|
+
} catch (_error) {
|
|
1416
|
+
entry.invalid = true;
|
|
1417
|
+
this.#invalidValue = true;
|
|
1418
|
+
}
|
|
1419
|
+
return entry.parsed;
|
|
1420
|
+
}
|
|
1421
|
+
chunkAt(at) {
|
|
1422
|
+
let low = 0;
|
|
1423
|
+
let high = this.#ends.length;
|
|
1424
|
+
while (low < high) {
|
|
1425
|
+
const mid = low + high >>> 1;
|
|
1426
|
+
if (this.#ends[mid] <= at) low = mid + 1;
|
|
1427
|
+
else high = mid;
|
|
1428
|
+
}
|
|
1429
|
+
return low;
|
|
1430
|
+
}
|
|
1431
|
+
/** Materialize only a requested range, never the cumulative source. */
|
|
1432
|
+
slice(start, end) {
|
|
1433
|
+
if (start >= end) return "";
|
|
1434
|
+
const first = this.chunkAt(start);
|
|
1435
|
+
const last = this.chunkAt(end - 1);
|
|
1436
|
+
const base = first === 0 ? 0 : this.#ends[first - 1];
|
|
1437
|
+
if (first === last) return this.chunks[first].slice(start - base, end - base);
|
|
1438
|
+
const parts = [this.chunks[first].slice(start - base)];
|
|
1439
|
+
for (let i = first + 1; i < last; i++) parts.push(this.chunks[i]);
|
|
1440
|
+
parts.push(this.chunks[last].slice(0, end - this.#ends[last - 1]));
|
|
1441
|
+
return parts.join("");
|
|
1442
|
+
}
|
|
1443
|
+
readString(entry, read, limit, materialize) {
|
|
1444
|
+
const end = Math.min(entry.end < 0 ? this.#consumed : entry.end, entry.invalidAt ?? Number.POSITIVE_INFINITY, this.#invalidAt ?? Number.POSITIVE_INFINITY);
|
|
1445
|
+
if (!entry.needsDecoding) {
|
|
1446
|
+
const length = Math.min(end - read.at, limit - read.length);
|
|
1447
|
+
if (length <= 0) return;
|
|
1448
|
+
if (materialize) read.text += this.slice(read.at, read.at + length);
|
|
1449
|
+
read.at += length;
|
|
1450
|
+
read.length += length;
|
|
1451
|
+
return;
|
|
1452
|
+
}
|
|
1453
|
+
let chunkIndex = this.chunkAt(read.at);
|
|
1454
|
+
while (read.at < end && read.length < limit) {
|
|
1455
|
+
const base = chunkIndex === 0 ? 0 : this.#ends[chunkIndex - 1];
|
|
1456
|
+
const chunk = this.chunks[chunkIndex];
|
|
1457
|
+
const remaining = chunk.slice(read.at - base, Math.min(chunk.length, end - base));
|
|
1458
|
+
const boundary = remaining.search(CONTENT_ESCAPE);
|
|
1459
|
+
const length = Math.min(boundary < 0 ? remaining.length : boundary, limit - read.length);
|
|
1460
|
+
if (length > 0) {
|
|
1461
|
+
if (materialize) read.text += remaining.slice(0, length);
|
|
1462
|
+
read.at += length;
|
|
1463
|
+
read.length += length;
|
|
1464
|
+
if (read.at === base + chunk.length) chunkIndex++;
|
|
1465
|
+
continue;
|
|
1466
|
+
}
|
|
1467
|
+
const type = remaining.length > 1 ? remaining[1] : read.at + 1 < end ? this.chunks[chunkIndex + 1][0] : void 0;
|
|
1468
|
+
let decoded;
|
|
1469
|
+
let width = 2;
|
|
1470
|
+
if (remaining[0] === "\\" && type === void 0 && entry.end < 0) return;
|
|
1471
|
+
if (remaining[0] === "\\" && type === "u") {
|
|
1472
|
+
const hex = this.slice(read.at + 2, Math.min(end, read.at + 6));
|
|
1473
|
+
let valid = true;
|
|
1474
|
+
for (let i = 0; i < hex.length; i++) if (!isHex(hex[i])) valid = false;
|
|
1475
|
+
if (valid) {
|
|
1476
|
+
if (hex.length < 4 && entry.end < 0) return;
|
|
1477
|
+
if (hex.length === 4) decoded = String.fromCharCode(Number.parseInt(hex, 16));
|
|
1478
|
+
}
|
|
1479
|
+
width = 6;
|
|
1480
|
+
} else if (remaining[0] === "\\" && type !== void 0) decoded = SIMPLE_ESCAPES[type];
|
|
1481
|
+
if (decoded === void 0) {
|
|
1482
|
+
entry.invalidAt = read.at;
|
|
1483
|
+
this.#invalidValue = true;
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
if (materialize) read.text += decoded;
|
|
1487
|
+
read.length++;
|
|
1488
|
+
read.at += width;
|
|
1489
|
+
while (chunkIndex < this.chunks.length && read.at >= this.#ends[chunkIndex]) chunkIndex++;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
/** Locate new field ranges without decoding or parsing their contents. */
|
|
1493
|
+
scan() {
|
|
1494
|
+
if (this.object !== void 0 || this.#consumed === this.#size) return;
|
|
1495
|
+
for (let i = this.chunkAt(this.#consumed); i < this.chunks.length && this.#invalidAt === void 0; i++) {
|
|
1496
|
+
const pending = this.chunks[i];
|
|
1497
|
+
const base = i === 0 ? 0 : this.#ends[i - 1];
|
|
1498
|
+
for (let index = this.#consumed - base; index < pending.length && this.#mode !== "invalid"; index++) {
|
|
1499
|
+
if (this.#mode === "string" || this.#mode === "nested" && this.#nestedInString) {
|
|
1500
|
+
const end = this.stringBoundary(pending, index);
|
|
1501
|
+
this.#consumed += end - index;
|
|
1502
|
+
index = end;
|
|
1503
|
+
if (index === pending.length) break;
|
|
1504
|
+
}
|
|
1505
|
+
this.step(pending[index], this.#consumed);
|
|
1506
|
+
this.#consumed++;
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
/** Only raw quotes and their preceding backslash runs can terminate a string. */
|
|
1511
|
+
stringBoundary(fragment, start) {
|
|
1512
|
+
let at = start;
|
|
1513
|
+
while (true) {
|
|
1514
|
+
const quote = fragment.indexOf("\"", at);
|
|
1515
|
+
const end = quote < 0 ? fragment.length : quote;
|
|
1516
|
+
if (this.#mode === "string") {
|
|
1517
|
+
const entry = this.#current;
|
|
1518
|
+
if (!entry.needsDecoding && CONTENT_ESCAPE.test(fragment.slice(at, end))) entry.needsDecoding = true;
|
|
1519
|
+
}
|
|
1520
|
+
let slashStart = end;
|
|
1521
|
+
while (slashStart > at && fragment[slashStart - 1] === "\\") slashStart--;
|
|
1522
|
+
const escaped = (end - slashStart) % 2 === 1 !== (slashStart === at && this.#escape);
|
|
1523
|
+
this.#escape = quote < 0 && escaped;
|
|
1524
|
+
if (quote < 0 || !escaped) return end;
|
|
1525
|
+
at = quote + 1;
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
step(c, at) {
|
|
1529
|
+
switch (this.#mode) {
|
|
1530
|
+
case "root":
|
|
1531
|
+
if (isWhitespace(c)) return;
|
|
1532
|
+
if (c === "{") {
|
|
1533
|
+
this.#mode = "key-or-end";
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
this.fail();
|
|
1537
|
+
return;
|
|
1538
|
+
case "key-or-end":
|
|
1539
|
+
if (isWhitespace(c)) return;
|
|
1540
|
+
if (c === "}") {
|
|
1541
|
+
this.#mode = "closed";
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
if (c === "\"") {
|
|
1545
|
+
this.beginKey(at);
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1548
|
+
this.fail();
|
|
1549
|
+
return;
|
|
1550
|
+
case "key-only":
|
|
1551
|
+
if (isWhitespace(c)) return;
|
|
1552
|
+
if (c === "\"") {
|
|
1553
|
+
this.beginKey(at);
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
this.fail();
|
|
1557
|
+
return;
|
|
1558
|
+
case "key":
|
|
1559
|
+
this.stepKey(c, at);
|
|
1560
|
+
return;
|
|
1561
|
+
case "colon":
|
|
1562
|
+
if (isWhitespace(c)) return;
|
|
1563
|
+
if (c === ":") {
|
|
1564
|
+
this.#mode = "value";
|
|
1565
|
+
return;
|
|
1566
|
+
}
|
|
1567
|
+
this.fail();
|
|
1568
|
+
return;
|
|
1569
|
+
case "value":
|
|
1570
|
+
this.beginValue(c, at);
|
|
1571
|
+
return;
|
|
1572
|
+
case "string": {
|
|
1573
|
+
const entry = this.#current;
|
|
1574
|
+
entry.end = at;
|
|
1575
|
+
this.#current = null;
|
|
1576
|
+
this.#mode = "comma-or-end";
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
case "scalar":
|
|
1580
|
+
this.stepScalar(c, at);
|
|
1581
|
+
return;
|
|
1582
|
+
case "nested":
|
|
1583
|
+
this.stepNested(c, at);
|
|
1584
|
+
return;
|
|
1585
|
+
case "comma-or-end":
|
|
1586
|
+
if (isWhitespace(c)) return;
|
|
1587
|
+
if (c === ",") {
|
|
1588
|
+
this.#mode = "key-only";
|
|
1589
|
+
return;
|
|
1590
|
+
}
|
|
1591
|
+
if (c === "}") {
|
|
1592
|
+
this.#mode = "closed";
|
|
1593
|
+
return;
|
|
1594
|
+
}
|
|
1595
|
+
this.fail();
|
|
1596
|
+
return;
|
|
1597
|
+
case "closed":
|
|
1598
|
+
if (isWhitespace(c)) return;
|
|
1599
|
+
this.fail();
|
|
1600
|
+
return;
|
|
1601
|
+
/* v8 ignore next 2 -- scan() stops stepping once the view is invalid. */
|
|
1602
|
+
case "invalid": return;
|
|
1603
|
+
/* v8 ignore next 2 -- Every scanner mode has a handler above. */
|
|
1604
|
+
default: assertNever$1(this.#mode);
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
fail() {
|
|
1608
|
+
this.#invalidAt = this.#consumed;
|
|
1609
|
+
this.#mode = "invalid";
|
|
1610
|
+
this.#current = null;
|
|
1611
|
+
}
|
|
1612
|
+
beginKey(at) {
|
|
1613
|
+
this.#mode = "key";
|
|
1614
|
+
this.#keyStart = at + 1;
|
|
1615
|
+
this.#keyEscaped = false;
|
|
1616
|
+
this.#escape = false;
|
|
1617
|
+
}
|
|
1618
|
+
stepKey(c, at) {
|
|
1619
|
+
if (c < " ") {
|
|
1620
|
+
this.fail();
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
if (this.#escape) {
|
|
1624
|
+
this.#escape = false;
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
if (c === "\\") {
|
|
1628
|
+
this.#escape = true;
|
|
1629
|
+
this.#keyEscaped = true;
|
|
1630
|
+
return;
|
|
1631
|
+
}
|
|
1632
|
+
if (c !== "\"") return;
|
|
1633
|
+
const raw = this.slice(this.#keyStart, at);
|
|
1634
|
+
if (this.#keyEscaped) try {
|
|
1635
|
+
this.#key = JSON.parse(`"${raw}"`);
|
|
1636
|
+
} catch (_error) {
|
|
1637
|
+
this.fail();
|
|
1638
|
+
return;
|
|
1639
|
+
}
|
|
1640
|
+
else this.#key = raw;
|
|
1641
|
+
this.#mode = "colon";
|
|
1642
|
+
}
|
|
1643
|
+
open(entry) {
|
|
1644
|
+
if (!this.#entries.has(this.#key)) this.#order.push(this.#key);
|
|
1645
|
+
this.#entries.set(this.#key, entry);
|
|
1646
|
+
this.#current = entry;
|
|
1647
|
+
}
|
|
1648
|
+
beginValue(c, at) {
|
|
1649
|
+
if (isWhitespace(c)) return;
|
|
1650
|
+
if (c === "\"") {
|
|
1651
|
+
this.open({
|
|
1652
|
+
kind: "string",
|
|
1653
|
+
start: at + 1,
|
|
1654
|
+
end: -1,
|
|
1655
|
+
needsDecoding: false,
|
|
1656
|
+
invalidAt: void 0,
|
|
1657
|
+
length: void 0,
|
|
1658
|
+
text: void 0,
|
|
1659
|
+
prefixes: void 0
|
|
1660
|
+
});
|
|
1661
|
+
this.#escape = false;
|
|
1662
|
+
this.#mode = "string";
|
|
1663
|
+
return;
|
|
1664
|
+
}
|
|
1665
|
+
if (c === "}" || c === "," || c === ":" || c === "]") {
|
|
1666
|
+
this.fail();
|
|
1667
|
+
return;
|
|
1668
|
+
}
|
|
1669
|
+
this.open({
|
|
1670
|
+
kind: "value",
|
|
1671
|
+
start: at,
|
|
1672
|
+
end: -1,
|
|
1673
|
+
parsed: void 0,
|
|
1674
|
+
invalid: false
|
|
1675
|
+
});
|
|
1676
|
+
if (c === "{" || c === "[") {
|
|
1677
|
+
this.#mode = "nested";
|
|
1678
|
+
this.#nestedEnds = [c === "{" ? "}" : "]"];
|
|
1679
|
+
this.#nestedInString = false;
|
|
1680
|
+
this.#escape = false;
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
this.#mode = "scalar";
|
|
1684
|
+
}
|
|
1685
|
+
stepScalar(c, at) {
|
|
1686
|
+
if (c !== "," && c !== "}" && !isWhitespace(c)) return;
|
|
1687
|
+
this.closeValue(at);
|
|
1688
|
+
this.#mode = c === "," ? "key-only" : c === "}" ? "closed" : "comma-or-end";
|
|
1689
|
+
}
|
|
1690
|
+
stepNested(c, at) {
|
|
1691
|
+
if (this.#nestedInString) {
|
|
1692
|
+
this.#nestedInString = false;
|
|
1693
|
+
return;
|
|
1694
|
+
}
|
|
1695
|
+
if (c === "\"") {
|
|
1696
|
+
this.#nestedInString = true;
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1699
|
+
if (c === "{" || c === "[") {
|
|
1700
|
+
this.#nestedEnds.push(c === "{" ? "}" : "]");
|
|
1701
|
+
return;
|
|
1702
|
+
}
|
|
1703
|
+
if (c === "}" || c === "]") {
|
|
1704
|
+
if (this.#nestedEnds.pop() !== c) {
|
|
1705
|
+
this.fail();
|
|
1706
|
+
return;
|
|
1707
|
+
}
|
|
1708
|
+
if (this.#nestedEnds.length === 0) {
|
|
1709
|
+
this.closeValue(at + 1);
|
|
1710
|
+
this.#mode = "comma-or-end";
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
closeValue(end) {
|
|
1715
|
+
const entry = this.#current;
|
|
1716
|
+
entry.end = end;
|
|
1717
|
+
this.#current = null;
|
|
1718
|
+
}
|
|
1719
|
+
});
|
|
906
1720
|
/** Duplicate-install-safe JSON and immutable-value helpers. @module @deepseek-ai/dsh-util-values */
|
|
907
1721
|
/**
|
|
908
1722
|
* Mark an unreachable closed-union branch.
|
|
@@ -915,7 +1729,7 @@ window.__ModuleLoader__.load({
|
|
|
915
1729
|
throw new Error(`unreachable variant${context ? ` in ${context}` : ""}: ${rendered}`);
|
|
916
1730
|
}
|
|
917
1731
|
//#endregion
|
|
918
|
-
//#region node_modules/.pnpm/@deepseek-ai+dsh-util-workspace-path@0.2.
|
|
1732
|
+
//#region node_modules/.pnpm/@deepseek-ai+dsh-util-workspace-path@0.2.1-rc.2_@deepseek-ai+cordis@4.0.5/node_modules/@deepseek-ai/dsh-util-workspace-path/lib/index.js
|
|
919
1733
|
/**
|
|
920
1734
|
* Browser-safe Workspace path and display helpers.
|
|
921
1735
|
* @module @deepseek-ai/dsh-util-workspace-path
|
|
@@ -1490,46 +2304,90 @@ window.__ModuleLoader__.load({
|
|
|
1490
2304
|
};
|
|
1491
2305
|
}, "ui-workspace: Workspace navigation policy");
|
|
1492
2306
|
}
|
|
1493
|
-
|
|
2307
|
+
connectWorkspace(workspaceId, options = {}) {
|
|
2308
|
+
return this.connectBlank(workspaceId, options);
|
|
2309
|
+
}
|
|
2310
|
+
/**
|
|
2311
|
+
* @param reuse - preset for the reused or created blank; absent creates a fresh Session.
|
|
2312
|
+
*/
|
|
2313
|
+
async connectBlank(workspaceId, reuse) {
|
|
1494
2314
|
const workspace = this.workspaces.list.getSnapshot().items.find((item) => item.workspaceId === workspaceId);
|
|
1495
2315
|
if (workspace === void 0) throw new Error(`uiWorkspace.connectWorkspace: unknown workspace ${workspaceId}`);
|
|
2316
|
+
if (reuse === void 0) return this.sessions.create({ workspaceId });
|
|
1496
2317
|
const inflight = this.connecting.get(workspaceId);
|
|
1497
|
-
if (inflight !== void 0) return inflight;
|
|
1498
|
-
const attempt = this.
|
|
1499
|
-
this.connecting.delete(workspaceId);
|
|
2318
|
+
if (inflight !== void 0 && inflight.agentPreset === reuse.agentPreset) return inflight.attempt;
|
|
2319
|
+
const attempt = this.createInWorkspace(workspace, reuse).finally(() => {
|
|
2320
|
+
if (this.connecting.get(workspaceId)?.attempt === attempt) this.connecting.delete(workspaceId);
|
|
2321
|
+
});
|
|
2322
|
+
this.connecting.set(workspaceId, {
|
|
2323
|
+
agentPreset: reuse.agentPreset,
|
|
2324
|
+
attempt
|
|
1500
2325
|
});
|
|
1501
|
-
this.connecting.set(workspaceId, attempt);
|
|
1502
2326
|
return attempt;
|
|
1503
2327
|
}
|
|
1504
|
-
|
|
2328
|
+
createInWorkspace(workspace, options) {
|
|
1505
2329
|
const archived = this.workspaces.list.getSnapshot().archivedSessionIds;
|
|
1506
2330
|
const sessions = this.sessions.list.getSnapshot();
|
|
1507
|
-
|
|
2331
|
+
const blank = sessions.ids.find((id) => {
|
|
1508
2332
|
const summary = sessions.byId[id];
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
2333
|
+
return summary !== void 0 && summary.blank && summary.cwd === workspace.path && workspace.sessionIds.includes(id) && !archived.includes(id);
|
|
2334
|
+
});
|
|
2335
|
+
return this.createNamed({
|
|
2336
|
+
workspaceId: workspace.workspaceId,
|
|
2337
|
+
...blank === void 0 ? {} : { sessionId: blank },
|
|
2338
|
+
...options.agentPreset === void 0 ? {} : { agentPreset: options.agentPreset }
|
|
2339
|
+
});
|
|
2340
|
+
}
|
|
2341
|
+
/**
|
|
2342
|
+
* Create through the Host. A named blank the Host declined may still read as
|
|
2343
|
+
* blank from stale cached list metadata, which hides it from the Session tree;
|
|
2344
|
+
* its projections are reloaded so the row reflects its log.
|
|
2345
|
+
*/
|
|
2346
|
+
async createNamed(request) {
|
|
2347
|
+
const sessionId = await this.sessions.create(request);
|
|
2348
|
+
const named = request.sessionId;
|
|
2349
|
+
if (named !== void 0 && named !== sessionId) this.sessions.refreshProjections(named).catch((reason) => {
|
|
2350
|
+
console.warn("declined blank Session projection reload failed:", reason);
|
|
2351
|
+
});
|
|
2352
|
+
return sessionId;
|
|
1513
2353
|
}
|
|
1514
|
-
|
|
2354
|
+
/**
|
|
2355
|
+
* Reopen a saved blank Session under the preset its log recorded, or under
|
|
2356
|
+
* the Host default once that preset no longer activates. Listed values are
|
|
2357
|
+
* cached hints that can lag the log after an unclean Host exit, so the
|
|
2358
|
+
* Session's projections are reloaded before the preset is read; a Session
|
|
2359
|
+
* that still awaits a format migration reloads only those cached hints.
|
|
2360
|
+
*/
|
|
2361
|
+
async restoreBlank(workspaceId, sessionId) {
|
|
2362
|
+
await this.sessions.refreshProjections(sessionId);
|
|
2363
|
+
const recorded = this.sessions.list.getSnapshot().byId[sessionId]?.projectionValues?.agentPreset;
|
|
2364
|
+
const request = {
|
|
2365
|
+
workspaceId,
|
|
2366
|
+
sessionId
|
|
2367
|
+
};
|
|
2368
|
+
if (typeof recorded !== "string") return this.createNamed(request);
|
|
1515
2369
|
try {
|
|
1516
|
-
return await this.
|
|
1517
|
-
|
|
1518
|
-
|
|
2370
|
+
return await this.createNamed({
|
|
2371
|
+
...request,
|
|
2372
|
+
agentPreset: recorded
|
|
1519
2373
|
});
|
|
1520
2374
|
} catch (error) {
|
|
1521
|
-
|
|
1522
|
-
|
|
2375
|
+
const code = sessionCreateErrorOf(error)?.rpcError.code;
|
|
2376
|
+
if (code !== "agent-preset/not-found" && code !== "agent-preset/invalid") throw error;
|
|
2377
|
+
return this.createNamed(request);
|
|
1523
2378
|
}
|
|
1524
2379
|
}
|
|
1525
2380
|
openSession(target) {
|
|
1526
2381
|
this.replaceMain(target, this.lifetime.signal, "reveal");
|
|
1527
2382
|
}
|
|
1528
|
-
|
|
2383
|
+
openWorkspace(workspaceId, beforeOpen, options = {}) {
|
|
2384
|
+
return this.openWorkspaceSession(workspaceId, beforeOpen, options);
|
|
2385
|
+
}
|
|
2386
|
+
async openWorkspaceSession(workspaceId, beforeOpen, reuse) {
|
|
1529
2387
|
const navigation = AbortSignal.any([this.ctx.layout.beginNavigation(), this.lifetime.signal]);
|
|
1530
2388
|
let sessionId;
|
|
1531
2389
|
try {
|
|
1532
|
-
sessionId = await this.
|
|
2390
|
+
sessionId = await this.connectBlank(workspaceId, reuse);
|
|
1533
2391
|
} catch (error) {
|
|
1534
2392
|
if (!navigation.aborted) this.notify({
|
|
1535
2393
|
kind: "createFailed",
|
|
@@ -1540,14 +2398,18 @@ window.__ModuleLoader__.load({
|
|
|
1540
2398
|
if (navigation.aborted) return;
|
|
1541
2399
|
this.replaceMain(sessionId, navigation, "reveal", beforeOpen);
|
|
1542
2400
|
}
|
|
1543
|
-
|
|
2401
|
+
forkSession(sessionId, onCreated, options) {
|
|
1544
2402
|
return this.sessions.fork({
|
|
1545
2403
|
sessionId,
|
|
1546
2404
|
increaseTitle: true,
|
|
1547
|
-
...onCreated === void 0 ? {} : { onCreated }
|
|
2405
|
+
...onCreated === void 0 ? {} : { onCreated },
|
|
2406
|
+
...options?.allowMigration === void 0 ? {} : { allowMigration: options.allowMigration }
|
|
1548
2407
|
});
|
|
1549
2408
|
}
|
|
1550
|
-
startSession(workspaceId) {
|
|
2409
|
+
startSession(workspaceId, options) {
|
|
2410
|
+
const { agentPreset, ...draft } = options ?? {};
|
|
2411
|
+
const draftOptions = options === void 0 ? void 0 : draft;
|
|
2412
|
+
const initializeDraft = draftOptions !== void 0 && (draftOptions.prompt !== void 0 || draftOptions.clearPreviousDraft === true);
|
|
1551
2413
|
const workspace = this.workspaces.list.getSnapshot();
|
|
1552
2414
|
const sessions = this.sessions.list.getSnapshot();
|
|
1553
2415
|
const current = this.mainReference?.sessionId;
|
|
@@ -1555,13 +2417,34 @@ window.__ModuleLoader__.load({
|
|
|
1555
2417
|
const recent = workspace.phase === "ready" && sessions.phase === "ready" ? recentWorkspace(workspace.items, sessions.byId) : void 0;
|
|
1556
2418
|
const target = workspaceId ?? currentWorkspaceId ?? recent;
|
|
1557
2419
|
if (target === void 0) {
|
|
2420
|
+
if (initializeDraft) {
|
|
2421
|
+
this.notify({
|
|
2422
|
+
kind: "createFailed",
|
|
2423
|
+
message: this.ctx.locale.bind("workspace")("draft.workspaceRequired")
|
|
2424
|
+
});
|
|
2425
|
+
return;
|
|
2426
|
+
}
|
|
1558
2427
|
this.clearMain();
|
|
1559
2428
|
return;
|
|
1560
2429
|
}
|
|
1561
|
-
this.
|
|
2430
|
+
(draftOptions === void 0 ? this.openWorkspaceSession(target, void 0, void 0) : this.openWorkspace(target, initializeDraft ? (sessionId) => {
|
|
2431
|
+
const binding = this.sessions.binding(sessionId) ?? this.draftPreparationFailed();
|
|
2432
|
+
this.prepareDraft(binding, draftOptions);
|
|
2433
|
+
} : void 0, agentPreset === void 0 ? {} : { agentPreset })).catch((reason) => {
|
|
1562
2434
|
console.warn("new session failed:", reason);
|
|
1563
2435
|
});
|
|
1564
2436
|
}
|
|
2437
|
+
prepareDraft(binding, options) {
|
|
2438
|
+
if ((this.ctx.get("conversation") ?? this.draftPreparationFailed()).input.requestDraftInitialization(binding, options) === "blocked") this.draftPreparationFailed();
|
|
2439
|
+
}
|
|
2440
|
+
draftPreparationFailed() {
|
|
2441
|
+
const message = this.ctx.locale.bind("workspace")("draft.initializationFailed");
|
|
2442
|
+
this.notify({
|
|
2443
|
+
kind: "createFailed",
|
|
2444
|
+
message
|
|
2445
|
+
});
|
|
2446
|
+
throw new Error(message);
|
|
2447
|
+
}
|
|
1565
2448
|
async archiveSession(sessionId, options = {}) {
|
|
1566
2449
|
await this.workspaces.archiveSession(sessionId, options);
|
|
1567
2450
|
if (this.mainReference?.sessionId === sessionId) this.clearMain();
|
|
@@ -1640,7 +2523,7 @@ window.__ModuleLoader__.load({
|
|
|
1640
2523
|
}
|
|
1641
2524
|
const navigation = AbortSignal.any([this.ctx.layout.beginNavigation(), this.lifetime.signal]);
|
|
1642
2525
|
let sessionId;
|
|
1643
|
-
if (summary !== void 0 && workspace !== void 0 && summary.cwd === workspace.path && !workspaces.archivedSessionIds.includes(summary.id)) sessionId = await this.
|
|
2526
|
+
if (summary !== void 0 && workspace !== void 0 && summary.cwd === workspace.path && !workspaces.archivedSessionIds.includes(summary.id)) sessionId = await this.restoreBlank(workspace.workspaceId, summary.id);
|
|
1644
2527
|
let target = workspace?.workspaceId ?? recentWorkspace(workspaces.items, sessions.byId);
|
|
1645
2528
|
if (target === void 0 && workspaces.items.length === 0 && sessions.ids.length === 0) {
|
|
1646
2529
|
const prepared = await this.initializeDefaultWorkspace(navigation);
|
|
@@ -1946,6 +2829,51 @@ window.__ModuleLoader__.load({
|
|
|
1946
2829
|
liveStatus: aggregated.get(group.key) ?? EMPTY_LIVE_STATUS
|
|
1947
2830
|
}));
|
|
1948
2831
|
}
|
|
2832
|
+
/** The blank row plus the first `watermark` non-blank rows, in list order. */
|
|
2833
|
+
function watermarkRows(sessions, watermark) {
|
|
2834
|
+
let count = 0;
|
|
2835
|
+
const rows = sessions.filter((session) => {
|
|
2836
|
+
if (session.blank) return true;
|
|
2837
|
+
count += 1;
|
|
2838
|
+
return count <= watermark;
|
|
2839
|
+
});
|
|
2840
|
+
return {
|
|
2841
|
+
rows,
|
|
2842
|
+
hiddenCount: sessions.length - rows.length
|
|
2843
|
+
};
|
|
2844
|
+
}
|
|
2845
|
+
/**
|
|
2846
|
+
* Smallest watermark that covers every non-blank row within the automatic maximum that is running, has a running
|
|
2847
|
+
* child, awaits the user, carries an unread completion, or is current, and the reveal target at any position.
|
|
2848
|
+
* @param sessions - one group's ordered Session rows.
|
|
2849
|
+
* @param current - Session retained by the main view, if the sidebar is the active surface.
|
|
2850
|
+
* @param revealSessionId - Session selected from search and required to be visible.
|
|
2851
|
+
* @returns the group's baseline non-blank row count.
|
|
2852
|
+
*/
|
|
2853
|
+
function requiredWatermark(sessions, current, revealSessionId) {
|
|
2854
|
+
let index = 0;
|
|
2855
|
+
let required = 0;
|
|
2856
|
+
for (const session of sessions) {
|
|
2857
|
+
if (session.blank) continue;
|
|
2858
|
+
index += 1;
|
|
2859
|
+
if (index <= 50 && (session.running || session.runningSubagentCount > 0 || session.pendingInteraction !== void 0 || session.completed || session.id === current) || session.id === revealSessionId) required = index;
|
|
2860
|
+
}
|
|
2861
|
+
return required;
|
|
2862
|
+
}
|
|
2863
|
+
/**
|
|
2864
|
+
* Raise each listed group's watermark to its requirement; returns `marks` itself when nothing rises.
|
|
2865
|
+
* @param marks - watermarks stored by the current browser tree.
|
|
2866
|
+
* @param required - required baselines, in group order.
|
|
2867
|
+
* @returns either the unchanged input or a new object containing only raised groups.
|
|
2868
|
+
*/
|
|
2869
|
+
function raiseWatermarks(marks, required) {
|
|
2870
|
+
let raised = marks;
|
|
2871
|
+
for (const [key, watermark] of required) if (watermark > (raised[key] ?? 5)) raised = {
|
|
2872
|
+
...raised,
|
|
2873
|
+
[key]: watermark
|
|
2874
|
+
};
|
|
2875
|
+
return raised;
|
|
2876
|
+
}
|
|
1949
2877
|
//#endregion
|
|
1950
2878
|
//#region src/client/facet-visibility.ts
|
|
1951
2879
|
/**
|
|
@@ -2476,6 +3404,7 @@ window.__ModuleLoader__.load({
|
|
|
2476
3404
|
side: "bottom",
|
|
2477
3405
|
align: "end",
|
|
2478
3406
|
delayMs: 500,
|
|
3407
|
+
portal: true,
|
|
2479
3408
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2480
3409
|
type: "button",
|
|
2481
3410
|
className: Rows_module_css_default.iconButton,
|
|
@@ -2504,6 +3433,7 @@ window.__ModuleLoader__.load({
|
|
|
2504
3433
|
t
|
|
2505
3434
|
}),
|
|
2506
3435
|
openDelayMs: 800,
|
|
3436
|
+
grouped: true,
|
|
2507
3437
|
disabled: menuOpen,
|
|
2508
3438
|
copyText: row.cwd,
|
|
2509
3439
|
copyLabel: t("copy"),
|
|
@@ -2658,6 +3588,7 @@ window.__ModuleLoader__.load({
|
|
|
2658
3588
|
side: "bottom",
|
|
2659
3589
|
align: "end",
|
|
2660
3590
|
delayMs: 500,
|
|
3591
|
+
portal: true,
|
|
2661
3592
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2662
3593
|
type: "button",
|
|
2663
3594
|
className: Rows_module_css_default.iconButton,
|
|
@@ -2812,6 +3743,7 @@ window.__ModuleLoader__.load({
|
|
|
2812
3743
|
t
|
|
2813
3744
|
}),
|
|
2814
3745
|
openDelayMs: 800,
|
|
3746
|
+
grouped: true,
|
|
2815
3747
|
disabled: menuOpen || drag?.active === true,
|
|
2816
3748
|
copyText: row.blank || row.title === "" ? void 0 : row.title,
|
|
2817
3749
|
copyLabel: t("copy"),
|
|
@@ -3253,22 +4185,6 @@ window.__ModuleLoader__.load({
|
|
|
3253
4185
|
const SEARCH_DEBOUNCE_MS = 250;
|
|
3254
4186
|
/** `session.search` wire bound, measured in JavaScript UTF-16 code units. */
|
|
3255
4187
|
const SEARCH_QUERY_MAX_CODE_UNITS = 500;
|
|
3256
|
-
/** Idle Session rows visible per Workspace before the local overflow control. */
|
|
3257
|
-
const COLLAPSED_SESSION_LIMIT = 5;
|
|
3258
|
-
/** Keep provisional and running rows outside the idle-session quota, including parents with running children. */
|
|
3259
|
-
function collapsedSessionRows(sessions, limit = COLLAPSED_SESSION_LIMIT) {
|
|
3260
|
-
let idleCount = 0;
|
|
3261
|
-
const rows = sessions.filter((session) => {
|
|
3262
|
-
if (session.blank || session.running || session.runningSubagentCount > 0) return true;
|
|
3263
|
-
if (idleCount >= limit) return false;
|
|
3264
|
-
idleCount += 1;
|
|
3265
|
-
return true;
|
|
3266
|
-
});
|
|
3267
|
-
return {
|
|
3268
|
-
rows,
|
|
3269
|
-
hiddenCount: sessions.length - rows.length
|
|
3270
|
-
};
|
|
3271
|
-
}
|
|
3272
4188
|
/** Keep controlled input and RPC payload inside the session.search wire contract. */
|
|
3273
4189
|
function sanitizeSearchQuery(value) {
|
|
3274
4190
|
const withoutNul = value.replaceAll("\0", "");
|
|
@@ -3458,7 +4374,8 @@ window.__ModuleLoader__.load({
|
|
|
3458
4374
|
const facets = (0, react.useMemo)(() => visibleFacets(facetsStore.list(), badgeSettings), [badgeSettings]);
|
|
3459
4375
|
const current = panelActive ? void 0 : Object.values(list.byId).find((session) => (session.retainedBy.mainView ?? 0) > 0)?.id;
|
|
3460
4376
|
const revealGroup = revealSessionId === void 0 || !workspaceReady ? void 0 : owningGroupKey(workspaces, revealSessionId);
|
|
3461
|
-
const [
|
|
4377
|
+
const [storedWatermarks, setStoredWatermarks] = (0, react.useState)({});
|
|
4378
|
+
const [watermarkMotionKey, setWatermarkMotionKey] = (0, react.useState)(0);
|
|
3462
4379
|
const [drag, setDrag] = (0, react.useState)(null);
|
|
3463
4380
|
const sessionDropCommitted = (0, react.useRef)(false);
|
|
3464
4381
|
const [workspaceDrag, setWorkspaceDrag] = (0, react.useState)(null);
|
|
@@ -3516,20 +4433,24 @@ window.__ModuleLoader__.load({
|
|
|
3516
4433
|
revealGroup,
|
|
3517
4434
|
setGroupExpanded
|
|
3518
4435
|
]);
|
|
3519
|
-
(
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
4436
|
+
const baselines = new Map(groups.map((group) => [group.key, Math.max(5, requiredWatermark(group.expanded ? group.sessions : [], current, group.key === revealGroup ? revealSessionId : void 0))]));
|
|
4437
|
+
const foldedKeys = new Set(groups.filter((group) => !group.expanded).map((group) => group.key));
|
|
4438
|
+
const underFoldedAncestor = (key) => {
|
|
4439
|
+
for (let owner = parents.get(key); owner !== void 0; owner = parents.get(owner)) if (foldedKeys.has(owner)) return true;
|
|
4440
|
+
return false;
|
|
4441
|
+
};
|
|
4442
|
+
const requiredWatermarks = new Map([...baselines].filter(([key]) => !underFoldedAncestor(key)));
|
|
4443
|
+
const sessionWatermarks = raiseWatermarks(storedWatermarks, requiredWatermarks);
|
|
4444
|
+
if (sessionWatermarks !== storedWatermarks) setStoredWatermarks((marks) => raiseWatermarks(marks, requiredWatermarks));
|
|
4445
|
+
const dropWatermarks = (drop) => {
|
|
4446
|
+
if (!Object.keys(storedWatermarks).some(drop)) return;
|
|
4447
|
+
setStoredWatermarks((marks) => Object.fromEntries(Object.entries(marks).filter(([key]) => !drop(key))));
|
|
4448
|
+
setWatermarkMotionKey((key) => key + 1);
|
|
4449
|
+
};
|
|
4450
|
+
const withinGroup = (key, root) => {
|
|
4451
|
+
for (let owner = key; owner !== void 0; owner = parents.get(owner)) if (owner === root) return true;
|
|
4452
|
+
return false;
|
|
4453
|
+
};
|
|
3533
4454
|
const now = Date.now();
|
|
3534
4455
|
const commitSessionDrag = (activeDrag, over) => {
|
|
3535
4456
|
if (sessionDropCommitted.current) return;
|
|
@@ -3540,7 +4461,7 @@ window.__ModuleLoader__.load({
|
|
|
3540
4461
|
if (over.id === activeDrag.sessionId) return;
|
|
3541
4462
|
const accountSessionIds = activeDrag.accountKey === "" ? ungroupedSessionIds : workspaces.find((workspace) => workspace.workspaceId === activeDrag.accountKey)?.sessionIds;
|
|
3542
4463
|
if (accountSessionIds === void 0) return;
|
|
3543
|
-
const renderedSessions =
|
|
4464
|
+
const renderedSessions = watermarkRows(group.sessions, sessionWatermarks[group.key] ?? 5).rows;
|
|
3544
4465
|
const nextOrder = sessionDragOrder(accountSessionIds, renderedSessions, activeDrag, over);
|
|
3545
4466
|
if (nextOrder !== void 0) setSessionOrder(activeDrag.accountKey, nextOrder);
|
|
3546
4467
|
};
|
|
@@ -3598,8 +4519,9 @@ window.__ModuleLoader__.load({
|
|
|
3598
4519
|
const workspaceId = group.workspaceId;
|
|
3599
4520
|
const children = childrenByParent.get(group.key) ?? [];
|
|
3600
4521
|
const compatibleDrag = workspaceDrag !== null && parents.get(workspaceDrag.workspaceId) === parents.get(group.key);
|
|
3601
|
-
const
|
|
3602
|
-
const
|
|
4522
|
+
const watermark = sessionWatermarks[group.key] ?? 5;
|
|
4523
|
+
const collapsed = watermarkRows(group.sessions, baselines.get(group.key) ?? 5);
|
|
4524
|
+
const visible = watermarkRows(group.sessions, watermark);
|
|
3603
4525
|
const sessionsExpanded = visible.hiddenCount === 0;
|
|
3604
4526
|
rowKeys.push(`workspace:${group.key}`);
|
|
3605
4527
|
const childRows = group.expanded ? children.map((child) => renderGroup(child, depth + 1)) : [];
|
|
@@ -3673,10 +4595,7 @@ window.__ModuleLoader__.load({
|
|
|
3673
4595
|
now,
|
|
3674
4596
|
t,
|
|
3675
4597
|
onToggle: () => {
|
|
3676
|
-
if (group.expanded)
|
|
3677
|
-
...limits,
|
|
3678
|
-
[group.key]: COLLAPSED_SESSION_LIMIT
|
|
3679
|
-
}));
|
|
4598
|
+
if (group.expanded) dropWatermarks((key) => withinGroup(key, group.key));
|
|
3680
4599
|
setGroupExpanded(group.key, !group.expanded);
|
|
3681
4600
|
},
|
|
3682
4601
|
onCreate: () => {
|
|
@@ -3761,10 +4680,15 @@ window.__ModuleLoader__.load({
|
|
|
3761
4680
|
"data-row-key": `overflow:${group.key}`,
|
|
3762
4681
|
"aria-expanded": sessionsExpanded,
|
|
3763
4682
|
onClick: () => {
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
4683
|
+
if (sessionsExpanded) {
|
|
4684
|
+
dropWatermarks((key) => key === group.key);
|
|
4685
|
+
return;
|
|
4686
|
+
}
|
|
4687
|
+
setStoredWatermarks((marks) => ({
|
|
4688
|
+
...marks,
|
|
4689
|
+
[group.key]: visible.hiddenCount <= 5 ? Infinity : watermark + 5
|
|
3767
4690
|
}));
|
|
4691
|
+
setWatermarkMotionKey((key) => key + 1);
|
|
3768
4692
|
},
|
|
3769
4693
|
children: sessionsExpanded ? t("sessions.collapse") : t("sessions.expand", { n: visible.hiddenCount })
|
|
3770
4694
|
})
|
|
@@ -3772,7 +4696,7 @@ window.__ModuleLoader__.load({
|
|
|
3772
4696
|
}, group.key);
|
|
3773
4697
|
};
|
|
3774
4698
|
const groupRows = rootGroups.map((group) => renderGroup(group, 0));
|
|
3775
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(
|
|
4699
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.HoverCardGroup, {
|
|
3776
4700
|
className: clsx(WorkspaceBrowser_module_css_default.treeBody, WorkspaceBrowser_module_css_default.wide),
|
|
3777
4701
|
children: [
|
|
3778
4702
|
workspaceDropAtListStart && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -3784,7 +4708,7 @@ window.__ModuleLoader__.load({
|
|
|
3784
4708
|
label: t("section.sessions"),
|
|
3785
4709
|
rowKeys,
|
|
3786
4710
|
ready: list.phase === "ready" && workspaceReady && !nativeDragActive,
|
|
3787
|
-
resetKey: animationResetKey,
|
|
4711
|
+
resetKey: JSON.stringify([animationResetKey, watermarkMotionKey]),
|
|
3788
4712
|
children: [groupsWithAggregatedStatus.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EmptySessions, {
|
|
3789
4713
|
rowState,
|
|
3790
4714
|
onLeaveArchivedOnly,
|
|
@@ -3817,7 +4741,7 @@ window.__ModuleLoader__.load({
|
|
|
3817
4741
|
if (nextOrder !== void 0) setSessionOrder(FLAT_SESSION_ORDER_KEY, nextOrder);
|
|
3818
4742
|
};
|
|
3819
4743
|
const now = Date.now();
|
|
3820
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(
|
|
4744
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.HoverCardGroup, {
|
|
3821
4745
|
className: clsx(WorkspaceBrowser_module_css_default.treeBody, WorkspaceBrowser_module_css_default.wide),
|
|
3822
4746
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(AnimatedRows, {
|
|
3823
4747
|
className: clsx(WorkspaceBrowser_module_css_default.list, WorkspaceBrowser_module_css_default.flatList),
|
|
@@ -3950,7 +4874,7 @@ window.__ModuleLoader__.load({
|
|
|
3950
4874
|
* @param props - composed slot props (shell owner share + store + injected actions).
|
|
3951
4875
|
* @returns the region element tree.
|
|
3952
4876
|
*/
|
|
3953
|
-
function WorkspaceBrowser({ wide, usePanelInfo, expandSidebar, useSessions, useSessionStatus, useWorkspaces, useStore, actions, startSession, open, requestSessionRename, notifyArchivedNotOpenable, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, unarchiveSession, createWorkspace, searchSessions, searchResultLimit, useDirectoryFlow, useHostInfo, useShortcuts, useWorkspaceShortcuts, useBadgeSettings, requestSearch, requestAddWorkspace, closeAddWorkspace, setDirectoryBusy,
|
|
4877
|
+
function WorkspaceBrowser({ wide, usePanelInfo, expandSidebar, useSessions, useSessionStatus, useWorkspaces, useStore, actions, startSession, open, requestSessionRename, notifyArchivedNotOpenable, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, unarchiveSession, createWorkspace, searchSessions, searchResultLimit, useDirectoryFlow, useHostInfo, useShortcuts, useWorkspaceShortcuts, useBadgeSettings, requestSearch, requestAddWorkspace, closeAddWorkspace, setDirectoryBusy, renderSlot, t }) {
|
|
3954
4878
|
const home = useHostInfo((info) => info.home);
|
|
3955
4879
|
const shortcuts = useShortcuts((rows) => rows);
|
|
3956
4880
|
const searchShortcut = shortcuts.find((row) => row.id === "session.search");
|
|
@@ -4379,7 +5303,7 @@ window.__ModuleLoader__.load({
|
|
|
4379
5303
|
side: "right",
|
|
4380
5304
|
onPick: (workspaceId) => {
|
|
4381
5305
|
closeAddWorkspace();
|
|
4382
|
-
startSession(workspaceId);
|
|
5306
|
+
startSession(workspaceId, { clearPreviousDraft: false });
|
|
4383
5307
|
},
|
|
4384
5308
|
onClose: () => {
|
|
4385
5309
|
closeAddWorkspace();
|
|
@@ -4559,11 +5483,7 @@ window.__ModuleLoader__.load({
|
|
|
4559
5483
|
role: "alert",
|
|
4560
5484
|
children: deleteError
|
|
4561
5485
|
})]
|
|
4562
|
-
})
|
|
4563
|
-
shortcutState.forkError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
4564
|
-
text: t(shortcutState.forkError.reason === "unavailable" ? "shortcut.noCompletedTurn" : "shortcut.forkFailed"),
|
|
4565
|
-
onDone: dismissForkError
|
|
4566
|
-
}, shortcutState.forkError.seq)
|
|
5486
|
+
})
|
|
4567
5487
|
]
|
|
4568
5488
|
});
|
|
4569
5489
|
}
|
|
@@ -4608,6 +5528,7 @@ window.__ModuleLoader__.load({
|
|
|
4608
5528
|
side: "bottom",
|
|
4609
5529
|
align: "end",
|
|
4610
5530
|
delayMs: 500,
|
|
5531
|
+
portal: true,
|
|
4611
5532
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4612
5533
|
type: "button",
|
|
4613
5534
|
className: Rows_module_css_default.iconButton,
|
|
@@ -4753,13 +5674,15 @@ window.__ModuleLoader__.load({
|
|
|
4753
5674
|
/** The fork action: one `sidebar.workspaces.session.menu.item` row. */
|
|
4754
5675
|
/**
|
|
4755
5676
|
* Menu row (order 300): fork at the Session's last completed turn; the child
|
|
4756
|
-
* arrives through the Host list beside its source.
|
|
4757
|
-
*
|
|
4758
|
-
* @
|
|
5677
|
+
* arrives through the Host list beside its source. Absent when the access
|
|
5678
|
+
* snapshot excludes `session/fork` for this Session.
|
|
5679
|
+
* @param props - owner share, menu open state, access Hook, and the fork share.
|
|
5680
|
+
* @returns the row, or nothing when the caller may not fork this Session.
|
|
4759
5681
|
*/
|
|
4760
|
-
function ForkSessionMenuItem({ sessionId, useMenuOpenState, useShortcuts, forkSession, t }) {
|
|
5682
|
+
function ForkSessionMenuItem({ sessionId, useMenuOpenState, useShortcuts, useSessionAccess, forkSession, t }) {
|
|
4761
5683
|
const [, setMenuOpen] = useMenuOpenState();
|
|
4762
5684
|
const shortcut = useShortcuts((rows) => rows.find((row) => row.id === "session.fork"));
|
|
5685
|
+
if (!useSessionAccess(sessionId, "session/fork")) return null;
|
|
4763
5686
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MenuItemButton, {
|
|
4764
5687
|
shortcut,
|
|
4765
5688
|
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutlineRegular, {}),
|
|
@@ -4820,6 +5743,7 @@ window.__ModuleLoader__.load({
|
|
|
4820
5743
|
side: "bottom",
|
|
4821
5744
|
align: "end",
|
|
4822
5745
|
delayMs: 500,
|
|
5746
|
+
portal: true,
|
|
4823
5747
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4824
5748
|
type: "button",
|
|
4825
5749
|
className: Rows_module_css_default.iconButton,
|
|
@@ -4949,75 +5873,38 @@ window.__ModuleLoader__.load({
|
|
|
4949
5873
|
});
|
|
4950
5874
|
}
|
|
4951
5875
|
//#endregion
|
|
4952
|
-
//#region src/client/session-actions/
|
|
4953
|
-
/**
|
|
4954
|
-
* The `shell.overlay` entry for Workspace and Session notices.
|
|
4955
|
-
* One notice is visible at a time; a parent rerender does not extend its hold.
|
|
4956
|
-
*/
|
|
5876
|
+
//#region src/client/session-actions/SessionId.tsx
|
|
5877
|
+
/** The copy Session ID action: one `sidebar.workspaces.session.menu.item` row. */
|
|
4957
5878
|
/**
|
|
4958
|
-
*
|
|
4959
|
-
*
|
|
4960
|
-
*
|
|
5879
|
+
* Copy the complete ID after dismissing the row menu.
|
|
5880
|
+
* @param props - row identity, menu dismissal, clipboard action, and locale.
|
|
5881
|
+
* @returns the final built-in menu item.
|
|
4961
5882
|
*/
|
|
4962
|
-
|
|
5883
|
+
function SessionIdMenuItem({ sessionId, useMenuOpenState, copySessionId, t }) {
|
|
5884
|
+
const [, setMenuOpen] = useMenuOpenState();
|
|
5885
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MenuItemButton, {
|
|
5886
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutlineRegular, {}),
|
|
5887
|
+
onSelect: () => {
|
|
5888
|
+
setMenuOpen(false);
|
|
5889
|
+
copySessionId(sessionId);
|
|
5890
|
+
},
|
|
5891
|
+
children: t("menu.sessionId")
|
|
5892
|
+
});
|
|
5893
|
+
}
|
|
5894
|
+
//#endregion
|
|
5895
|
+
//#region src/client/session-actions/RowActionToast.tsx
|
|
4963
5896
|
/**
|
|
4964
|
-
*
|
|
4965
|
-
*
|
|
4966
|
-
*
|
|
4967
|
-
* reason on the same hold, or a plain warning for a failed pin, an archived
|
|
4968
|
-
* row that was clicked, or default Workspace creation.
|
|
4969
|
-
* @param props - the notice hook, the shared viewing store, the notice dismissal, the two archived-notice actions, and the locale seat.
|
|
4970
|
-
* @returns the notice on display, or null.
|
|
5897
|
+
* Dispatch the current notice without interpreting its business payload.
|
|
5898
|
+
* @param props - Current notice, dismissal, and the declared keyed renderer.
|
|
5899
|
+
* @returns the selected notice presentation, or nothing.
|
|
4971
5900
|
*/
|
|
4972
|
-
function RowActionToast({ useToast,
|
|
4973
|
-
const
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
text: t(toast.kind === "archived" ? "toast.archived" : "toast.stoppedAndArchived"),
|
|
4980
|
-
tone: "success",
|
|
4981
|
-
holdMs: LONG_TOAST_HOLD_MS,
|
|
4982
|
-
actions: [{
|
|
4983
|
-
label: t("toast.archivedUndo"),
|
|
4984
|
-
onClick: () => {
|
|
4985
|
-
dismissToast();
|
|
4986
|
-
undoArchive(sessionId);
|
|
4987
|
-
}
|
|
4988
|
-
}, ...archivedRowsVisible ? [] : [{
|
|
4989
|
-
prefix: t("toast.archivedOr"),
|
|
4990
|
-
label: t("toast.archivedFilter"),
|
|
4991
|
-
onClick: () => {
|
|
4992
|
-
dismissToast();
|
|
4993
|
-
showArchived();
|
|
4994
|
-
}
|
|
4995
|
-
}]],
|
|
4996
|
-
onDone: dismissToast
|
|
4997
|
-
}, `toast-${String(toast.seq)}`);
|
|
4998
|
-
}
|
|
4999
|
-
if (toast.kind === "createFailed") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
5000
|
-
text: t("toast.createFailed", { message: toast.message }),
|
|
5001
|
-
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutlineRegular, {}),
|
|
5002
|
-
holdMs: LONG_TOAST_HOLD_MS,
|
|
5003
|
-
onDone: dismissToast
|
|
5004
|
-
}, `toast-${String(toast.seq)}`);
|
|
5005
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
5006
|
-
text: plainNoticeText(toast, t),
|
|
5007
|
-
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutlineRegular, {}),
|
|
5008
|
-
onDone: dismissToast
|
|
5009
|
-
}, `toast-${String(toast.seq)}`);
|
|
5010
|
-
}
|
|
5011
|
-
/** The copy of one plain warning, keyed by the notice kind the union closes over. */
|
|
5012
|
-
function plainNoticeText(toast, t) {
|
|
5013
|
-
switch (toast.kind) {
|
|
5014
|
-
case "pinFailed": return t("toast.pinFailed");
|
|
5015
|
-
case "unpinFailed": return t("toast.unpinFailed");
|
|
5016
|
-
case "defaultWorkspaceFailed": return t("defaultWorkspace.failed");
|
|
5017
|
-
case "archivedNotOpenable": return t("toast.archivedNotOpenable");
|
|
5018
|
-
/* v8 ignore next 2 -- closed-union backstop; only reached if a notice kind is forged */
|
|
5019
|
-
default: return assertNever$1(toast);
|
|
5020
|
-
}
|
|
5901
|
+
function RowActionToast({ useToast, dismissToast, renderSlot }) {
|
|
5902
|
+
const notice = useToast((current) => current);
|
|
5903
|
+
if (notice === null) return null;
|
|
5904
|
+
return renderSlot("workspace.toast", {
|
|
5905
|
+
...notice,
|
|
5906
|
+
dismissToast
|
|
5907
|
+
}, { entryKey: notice.kind });
|
|
5021
5908
|
}
|
|
5022
5909
|
//#endregion
|
|
5023
5910
|
//#region src/client/locales.ts
|
|
@@ -5029,6 +5916,8 @@ window.__ModuleLoader__.load({
|
|
|
5029
5916
|
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
5030
5917
|
const zh = {
|
|
5031
5918
|
"defaultWorkspace.failed": "无法创建默认工作区,请通过“选择工作区”选择文件夹",
|
|
5919
|
+
"draft.workspaceRequired": "请先选择工作区",
|
|
5920
|
+
"draft.initializationFailed": "暂时无法填入草稿,请稍后重试",
|
|
5032
5921
|
"group.ungrouped": "未分组",
|
|
5033
5922
|
"session.new": "新会话",
|
|
5034
5923
|
"session.untitled": "未命名",
|
|
@@ -5083,6 +5972,9 @@ window.__ModuleLoader__.load({
|
|
|
5083
5972
|
"menu.unarchiveSession": "取消归档",
|
|
5084
5973
|
"menu.pinSession": "置顶会话",
|
|
5085
5974
|
"menu.unpinSession": "取消置顶",
|
|
5975
|
+
"menu.sessionId": "复制 Session ID",
|
|
5976
|
+
"toast.sessionIdCopied": "Session ID 已复制",
|
|
5977
|
+
"toast.sessionIdCopyFailed": "复制失败,请重试",
|
|
5086
5978
|
"row.archived": "已归档",
|
|
5087
5979
|
"row.pinned": "已置顶",
|
|
5088
5980
|
"toast.archivedNotOpenable": "已归档对话暂时无法查看,请取消归档后查看",
|
|
@@ -5109,6 +6001,8 @@ window.__ModuleLoader__.load({
|
|
|
5109
6001
|
"toast.pinFailed": "置顶失败,请稍后重试",
|
|
5110
6002
|
"toast.unpinFailed": "取消置顶失败,请稍后重试",
|
|
5111
6003
|
"toast.createFailed": "新建会话失败:{message}",
|
|
6004
|
+
"toast.forkRequiresOpen": "请先打开原会话完成迁移,再创建分支",
|
|
6005
|
+
"toast.openForkSource": "打开原会话",
|
|
5112
6006
|
"sessions.count.one": "{n} 个会话",
|
|
5113
6007
|
"sessions.count.other": "{n} 个会话",
|
|
5114
6008
|
"actions.workspace.aria": "工作区“{name}”的操作",
|
|
@@ -5167,6 +6061,8 @@ window.__ModuleLoader__.load({
|
|
|
5167
6061
|
/** English dictionary, checked complete against the zh key set. */
|
|
5168
6062
|
const en = {
|
|
5169
6063
|
"defaultWorkspace.failed": "Unable to create default workspace. Use Choose workspace to select a folder.",
|
|
6064
|
+
"draft.workspaceRequired": "Choose a workspace first.",
|
|
6065
|
+
"draft.initializationFailed": "Could not fill the draft. Please try again shortly.",
|
|
5170
6066
|
"group.ungrouped": "Ungrouped",
|
|
5171
6067
|
"session.new": "New Session",
|
|
5172
6068
|
"session.untitled": "Untitled",
|
|
@@ -5221,6 +6117,9 @@ window.__ModuleLoader__.load({
|
|
|
5221
6117
|
"menu.unarchiveSession": "Unarchive session",
|
|
5222
6118
|
"menu.pinSession": "Pin session",
|
|
5223
6119
|
"menu.unpinSession": "Unpin session",
|
|
6120
|
+
"menu.sessionId": "Copy Session ID",
|
|
6121
|
+
"toast.sessionIdCopied": "Session ID copied",
|
|
6122
|
+
"toast.sessionIdCopyFailed": "Copy failed. Try again.",
|
|
5224
6123
|
"row.archived": "Archived",
|
|
5225
6124
|
"row.pinned": "Pinned",
|
|
5226
6125
|
"toast.archivedNotOpenable": "Archived sessions cannot be opened. Unarchive it to view.",
|
|
@@ -5247,6 +6146,8 @@ window.__ModuleLoader__.load({
|
|
|
5247
6146
|
"toast.pinFailed": "Pin failed. Try again later.",
|
|
5248
6147
|
"toast.unpinFailed": "Unpin failed. Try again later.",
|
|
5249
6148
|
"toast.createFailed": "New session failed: {message}",
|
|
6149
|
+
"toast.forkRequiresOpen": "Open the original session to complete migration before creating a branch.",
|
|
6150
|
+
"toast.openForkSource": "Open original session",
|
|
5250
6151
|
"sessions.count.one": "{n} session",
|
|
5251
6152
|
"sessions.count.other": "{n} sessions",
|
|
5252
6153
|
"actions.workspace.aria": "Workspace actions for {name}",
|
|
@@ -5321,7 +6222,8 @@ window.__ModuleLoader__.load({
|
|
|
5321
6222
|
"remote",
|
|
5322
6223
|
"remote.directoryPicker",
|
|
5323
6224
|
"layout",
|
|
5324
|
-
"shortcuts"
|
|
6225
|
+
"shortcuts",
|
|
6226
|
+
"remoteAccess"
|
|
5325
6227
|
];
|
|
5326
6228
|
/**
|
|
5327
6229
|
* Register the browser and picker once their slot declarations are on the
|
|
@@ -5347,7 +6249,14 @@ window.__ModuleLoader__.load({
|
|
|
5347
6249
|
});
|
|
5348
6250
|
};
|
|
5349
6251
|
const uiWorkspace = new UiWorkspaceService(ctx, ctx.remote.directoryPicker, workspaces, sessions, viewInstance.actions, notify);
|
|
5350
|
-
|
|
6252
|
+
const denyWorkspaceAccess = {
|
|
6253
|
+
getSnapshot: () => false,
|
|
6254
|
+
subscribe: () => () => {}
|
|
6255
|
+
};
|
|
6256
|
+
ctx.slots.provideRoot({
|
|
6257
|
+
hooks: { workspaces: workspaces.list },
|
|
6258
|
+
keyedHooks: { workspaceAccess: (workspaceId, action) => action === void 0 ? denyWorkspaceAccess : ctx.remoteAccess.source("workspace", workspaceId, action) }
|
|
6259
|
+
});
|
|
5351
6260
|
ctx.effect(() => ctx.locale.register(NS, {
|
|
5352
6261
|
zh,
|
|
5353
6262
|
en
|
|
@@ -5446,7 +6355,12 @@ window.__ModuleLoader__.load({
|
|
|
5446
6355
|
},
|
|
5447
6356
|
unarchiveSession
|
|
5448
6357
|
});
|
|
5449
|
-
installWorkspaceShortcuts(ctx, uiWorkspace, shortcutControls, archiveInjected().archiveSession)
|
|
6358
|
+
installWorkspaceShortcuts(ctx, uiWorkspace, shortcutControls, archiveInjected().archiveSession, (reason) => {
|
|
6359
|
+
notify({
|
|
6360
|
+
kind: "shortcutForkFailed",
|
|
6361
|
+
reason
|
|
6362
|
+
});
|
|
6363
|
+
});
|
|
5450
6364
|
const archiveConfirmInjected = () => ({
|
|
5451
6365
|
hooks: { archiveRequest },
|
|
5452
6366
|
settleSessionArchive: () => {
|
|
@@ -5467,7 +6381,12 @@ window.__ModuleLoader__.load({
|
|
|
5467
6381
|
parent_session_id: sessionId,
|
|
5468
6382
|
click_position: "sidebar"
|
|
5469
6383
|
});
|
|
5470
|
-
}).catch(() => {
|
|
6384
|
+
}, { allowMigration: false }).catch((reason) => {
|
|
6385
|
+
if ((reason instanceof Error && reason.name === "SessionForkError" ? reason : void 0)?.rpcError?.code === "session/migration-required") notify({
|
|
6386
|
+
kind: "forkRequiresOpen",
|
|
6387
|
+
sessionId
|
|
6388
|
+
});
|
|
6389
|
+
});
|
|
5471
6390
|
} });
|
|
5472
6391
|
const renameInjected = () => ({ requestSessionRename });
|
|
5473
6392
|
const renameDialogInjected = () => ({
|
|
@@ -5475,19 +6394,31 @@ window.__ModuleLoader__.load({
|
|
|
5475
6394
|
settleSessionRename: shortcutControls.closeRename,
|
|
5476
6395
|
renameSession
|
|
5477
6396
|
});
|
|
6397
|
+
const sessionIdInjected = () => ({ copySessionId: async (id) => {
|
|
6398
|
+
notify({ kind: await (0, _deepseek_ai_dsh_client_ui_primitives.writeClipboard)(id) ? "sessionIdCopied" : "sessionIdCopyFailed" });
|
|
6399
|
+
} });
|
|
6400
|
+
registerToasts(ctx, viewStore, {
|
|
6401
|
+
undoArchive: unarchiveSession,
|
|
6402
|
+
showArchived: () => {
|
|
6403
|
+
viewInstance.actions.setArchivedFilter("show");
|
|
6404
|
+
},
|
|
6405
|
+
openForkSource: (id) => {
|
|
6406
|
+
if (workspaces.list.getSnapshot().archivedSessionIds.includes(id)) {
|
|
6407
|
+
notify({ kind: "archivedNotOpenable" });
|
|
6408
|
+
return;
|
|
6409
|
+
}
|
|
6410
|
+
openSession(id);
|
|
6411
|
+
}
|
|
6412
|
+
});
|
|
5478
6413
|
const rowToastInjected = () => ({
|
|
5479
6414
|
hooks: { toast: rowToast },
|
|
5480
6415
|
dismissToast: () => {
|
|
5481
6416
|
rowToast.set(null);
|
|
5482
|
-
},
|
|
5483
|
-
undoArchive: unarchiveSession,
|
|
5484
|
-
showArchived: () => {
|
|
5485
|
-
viewInstance.actions.setArchivedFilter("show");
|
|
5486
6417
|
}
|
|
5487
6418
|
});
|
|
5488
6419
|
const browserInjected = () => ({
|
|
5489
|
-
startSession: (workspaceId) => {
|
|
5490
|
-
uiWorkspace.startSession(workspaceId);
|
|
6420
|
+
startSession: (workspaceId, options) => {
|
|
6421
|
+
uiWorkspace.startSession(workspaceId, options);
|
|
5491
6422
|
},
|
|
5492
6423
|
open: openSession,
|
|
5493
6424
|
searchSessions,
|
|
@@ -5513,7 +6444,6 @@ window.__ModuleLoader__.load({
|
|
|
5513
6444
|
requestAddWorkspace: shortcutControls.add,
|
|
5514
6445
|
closeAddWorkspace: shortcutControls.closeAdd,
|
|
5515
6446
|
setDirectoryBusy: shortcutControls.directoryBusy,
|
|
5516
|
-
dismissForkError: shortcutControls.dismissForkError,
|
|
5517
6447
|
hooks: {
|
|
5518
6448
|
directoryFlow: browserFlowSource,
|
|
5519
6449
|
hostInfo,
|
|
@@ -5586,6 +6516,13 @@ window.__ModuleLoader__.load({
|
|
|
5586
6516
|
locale: NS,
|
|
5587
6517
|
inject: archiveInjected
|
|
5588
6518
|
}, ArchiveSessionMenuItem);
|
|
6519
|
+
yield ctx.slots.register({
|
|
6520
|
+
name: "sidebar.workspaces.session.menu.item",
|
|
6521
|
+
id: "session-id",
|
|
6522
|
+
order: 450,
|
|
6523
|
+
locale: NS,
|
|
6524
|
+
inject: sessionIdInjected
|
|
6525
|
+
}, SessionIdMenuItem);
|
|
5589
6526
|
});
|
|
5590
6527
|
ctx.slots.inject("sidebar.workspaces.session.row.action", function* () {
|
|
5591
6528
|
yield ctx.slots.register({
|
|
@@ -5619,9 +6556,11 @@ window.__ModuleLoader__.load({
|
|
|
5619
6556
|
yield ctx.slots.register({
|
|
5620
6557
|
name: "shell.overlay",
|
|
5621
6558
|
id: "workspace.row-toast",
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
6559
|
+
inject: rowToastInjected,
|
|
6560
|
+
children: { "workspace.toast": {
|
|
6561
|
+
kind: "keyed",
|
|
6562
|
+
scope: "root"
|
|
6563
|
+
} }
|
|
5625
6564
|
}, RowActionToast);
|
|
5626
6565
|
});
|
|
5627
6566
|
ctx.slots.inject("conversation.hero.workspace", () => ctx.slots.register({
|