@huanlin/dsh-plugin-ya-workspace-sidebar 0.1.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/LICENSE +664 -0
- package/README.md +36 -0
- package/cordis.patch.yml +9 -0
- package/lib/client.js +1142 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +7 -0
- package/lib/invariant.js +10 -0
- package/package.json +97 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,1142 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@huanlin/dsh-plugin-ya-workspace-sidebar",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let react = require("react");
|
|
8
|
+
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
9
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
|
+
//#region src/client/locales.ts
|
|
11
|
+
/** Product copy for the replacement workspace browser. */
|
|
12
|
+
const zh = {
|
|
13
|
+
workspaces: "工作区",
|
|
14
|
+
sessions: "会话",
|
|
15
|
+
recent: "最近会话",
|
|
16
|
+
ungrouped: "未分组",
|
|
17
|
+
newSession: "新会话",
|
|
18
|
+
addWorkspace: "添加工作区",
|
|
19
|
+
addWorkspaceMenu: "添加工作区…",
|
|
20
|
+
search: "搜索会话",
|
|
21
|
+
searchPlaceholder: "搜索名称、关键词…",
|
|
22
|
+
clearSearch: "清除搜索",
|
|
23
|
+
searching: "正在搜索会话历史…",
|
|
24
|
+
searchUnavailable: "内容搜索暂不可用,仅显示名称匹配。",
|
|
25
|
+
noMatches: "无匹配会话",
|
|
26
|
+
noSessions: "暂无会话",
|
|
27
|
+
noWorkspaces: "暂无工作区",
|
|
28
|
+
loading: "正在加载工作区…",
|
|
29
|
+
rename: "重命名",
|
|
30
|
+
renameWorkspace: "重命名工作区",
|
|
31
|
+
renameSession: "重命名会话",
|
|
32
|
+
deleteWorkspace: "删除工作区",
|
|
33
|
+
deleteDescription: "将把“{name}”从工作区列表中移除。文件夹与会话记录会保留。",
|
|
34
|
+
fork: "分叉会话",
|
|
35
|
+
archive: "归档会话",
|
|
36
|
+
cancel: "取消",
|
|
37
|
+
confirm: "确认",
|
|
38
|
+
retry: "重新选择",
|
|
39
|
+
folderError: "无法打开文件夹",
|
|
40
|
+
workspaceName: "工作区名称",
|
|
41
|
+
sessionName: "会话名称",
|
|
42
|
+
count: "{n} 个会话",
|
|
43
|
+
now: "刚刚",
|
|
44
|
+
minutes: "{n}分钟",
|
|
45
|
+
hours: "{n}小时",
|
|
46
|
+
days: "{n}天",
|
|
47
|
+
months: "{n}个月",
|
|
48
|
+
years: "{n}年",
|
|
49
|
+
running: "进行中",
|
|
50
|
+
waiting: "等待交互",
|
|
51
|
+
completed: "已完成",
|
|
52
|
+
collapse: "折叠",
|
|
53
|
+
expand: "展开"
|
|
54
|
+
};
|
|
55
|
+
const en = {
|
|
56
|
+
workspaces: "Workspaces",
|
|
57
|
+
sessions: "Sessions",
|
|
58
|
+
recent: "Recent Sessions",
|
|
59
|
+
ungrouped: "Ungrouped",
|
|
60
|
+
newSession: "New Session",
|
|
61
|
+
addWorkspace: "Add workspace",
|
|
62
|
+
addWorkspaceMenu: "Add workspace…",
|
|
63
|
+
search: "Search sessions",
|
|
64
|
+
searchPlaceholder: "Search name, keywords...",
|
|
65
|
+
clearSearch: "Clear search",
|
|
66
|
+
searching: "Searching session history…",
|
|
67
|
+
searchUnavailable: "Content search is unavailable. Showing name matches.",
|
|
68
|
+
noMatches: "No matching sessions",
|
|
69
|
+
noSessions: "No sessions yet",
|
|
70
|
+
noWorkspaces: "No workspaces yet",
|
|
71
|
+
loading: "Loading workspaces…",
|
|
72
|
+
rename: "Rename",
|
|
73
|
+
renameWorkspace: "Rename workspace",
|
|
74
|
+
renameSession: "Rename session",
|
|
75
|
+
deleteWorkspace: "Delete workspace",
|
|
76
|
+
deleteDescription: "This removes “{name}” from the workspace list. The folder and session logs remain.",
|
|
77
|
+
fork: "Fork session",
|
|
78
|
+
archive: "Archive session",
|
|
79
|
+
cancel: "Cancel",
|
|
80
|
+
confirm: "Confirm",
|
|
81
|
+
retry: "Choose again",
|
|
82
|
+
folderError: "Couldn’t open folder",
|
|
83
|
+
workspaceName: "Workspace name",
|
|
84
|
+
sessionName: "Session name",
|
|
85
|
+
count: "{n} sessions",
|
|
86
|
+
now: "now",
|
|
87
|
+
minutes: "{n}min",
|
|
88
|
+
hours: "{n}h",
|
|
89
|
+
days: "{n}d",
|
|
90
|
+
months: "{n}mo",
|
|
91
|
+
years: "{n}y",
|
|
92
|
+
running: "Running",
|
|
93
|
+
waiting: "Waiting for interaction",
|
|
94
|
+
completed: "Completed",
|
|
95
|
+
collapse: "Collapse",
|
|
96
|
+
expand: "Expand"
|
|
97
|
+
};
|
|
98
|
+
const NS = "ya-workspace-sidebar";
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region src/client/styles.ts
|
|
101
|
+
/** One scoped stylesheet injected for the lifetime of the client activation. */
|
|
102
|
+
const CSS = `
|
|
103
|
+
[data-ya-workspace-sidebar] { flex:1; min-height:0; display:flex; flex-direction:column; box-sizing:border-box; padding-right:var(--dsh-sidebar-inline-padding); color:var(--dsw-alias-label-primary); }
|
|
104
|
+
[data-ya-workspace-sidebar].ya-rail { padding-right:0; }
|
|
105
|
+
.ya-section-header { flex:none; height:36px; display:flex; align-items:center; justify-content:flex-end; gap:4px; padding-left:12px; margin-bottom:4px; box-sizing:border-box; color:var(--dsw-alias-label-tertiary); }
|
|
106
|
+
.ya-section-title { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:14px; }
|
|
107
|
+
.ya-icon-button { flex:none; width:28px; height:28px; border:0; border-radius:50%; padding:0; display:inline-flex; align-items:center; justify-content:center; color:var(--dsw-alias-label-secondary); background:transparent; cursor:pointer; }
|
|
108
|
+
.ya-icon-button:hover { background:var(--dsw-alias-interactive-bg-hover); }
|
|
109
|
+
.ya-search { flex:none; height:38px; margin:0 2px 10px; padding:0 14px; display:flex; align-items:center; gap:8px; box-sizing:border-box; border:1px solid var(--dsw-alias-border-l2); border-radius:24px; background:var(--dsw-static-neutral-bluish-75); color:var(--dsw-alias-label-caption); }
|
|
110
|
+
body[data-ds-dark-theme] .ya-search { background:var(--dsw-static-neutral-bluish-900); }
|
|
111
|
+
.ya-search-input { flex:1; min-width:0; border:0; outline:0; background:transparent; color:var(--dsw-alias-label-primary); font:inherit; font-size:14px; }
|
|
112
|
+
.ya-search-input::placeholder { color:var(--dsw-alias-label-tertiary); }
|
|
113
|
+
.ya-search-icon { flex:none; display:inline-flex; border:0; padding:0; color:inherit; background:transparent; }
|
|
114
|
+
.ya-body { flex:1; min-height:0; display:flex; flex-direction:column; overflow:hidden; margin-right:calc(-1 * var(--dsh-sidebar-inline-padding)); padding-right:var(--dsh-sidebar-inline-padding); }
|
|
115
|
+
.ya-recent { flex:none; padding-bottom:8px; border-bottom:1px solid var(--dsw-alias-border-l2); }
|
|
116
|
+
.ya-recent-collapsed { padding-bottom:0; border-bottom-color:transparent; }
|
|
117
|
+
.ya-recent-list-wrap { display:grid; grid-template-rows:1fr; transition:grid-template-rows 220ms ease-out; }
|
|
118
|
+
.ya-recent-collapsed .ya-recent-list-wrap { grid-template-rows:0fr; }
|
|
119
|
+
.ya-recent-list { display:flex; flex-direction:column; overflow:hidden; min-height:0; }
|
|
120
|
+
.ya-block-label { height:26px; display:flex; align-items:center; gap:2px; padding:0 8px; color:var(--dsw-alias-label-tertiary); font-size:12px; font-weight:600; letter-spacing:.02em; text-transform:uppercase; }
|
|
121
|
+
.ya-block-label-toggle { flex:none; width:20px; height:20px; margin-left:auto; border:0; border-radius:6px; padding:0; display:inline-flex; align-items:center; justify-content:center; background:transparent; color:var(--dsw-alias-label-tertiary); cursor:pointer; transition:transform 180ms ease-out; }
|
|
122
|
+
.ya-block-label-toggle:hover { background:var(--dsw-alias-interactive-bg-hover); color:var(--dsw-alias-label-secondary); }
|
|
123
|
+
.ya-block-label-toggle.ya-collapsed { transform:rotate(-90deg); }
|
|
124
|
+
.ya-breadcrumb { flex:none; height:34px; display:flex; align-items:center; gap:2px; padding:0 6px; color:var(--dsw-alias-label-tertiary); font-size:13px; }
|
|
125
|
+
.ya-crumb { border:0; padding:4px 3px; border-radius:6px; background:transparent; color:inherit; font:inherit; cursor:default; min-width:0; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }
|
|
126
|
+
button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:var(--dsw-alias-label-primary); cursor:pointer; }
|
|
127
|
+
.ya-scroll { flex:1; min-height:0; overflow-y:auto; padding-bottom:12px; }
|
|
128
|
+
.ya-row { position:relative; min-height:34px; display:flex; align-items:center; gap:6px; margin:1px 0; padding:0 7px; border-radius:9px; box-sizing:border-box; color:var(--dsw-alias-label-primary); cursor:pointer; user-select:none; }
|
|
129
|
+
.ya-row:hover, .ya-row.ya-menu-open { background:var(--dsw-alias-interactive-bg-hover); }
|
|
130
|
+
.ya-row.ya-selected { background:var(--dsw-alias-interactive-bg-selected); }
|
|
131
|
+
.ya-workspace-row { min-height:40px; }
|
|
132
|
+
.ya-row-main { flex:1; min-width:0; display:flex; flex-direction:column; justify-content:center; }
|
|
133
|
+
.ya-row-line { display:flex; align-items:center; min-width:0; gap:6px; }
|
|
134
|
+
.ya-row-title { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:13px; line-height:18px; }
|
|
135
|
+
.ya-row-meta { flex:none; color:var(--dsw-alias-label-tertiary); font-size:11px; white-space:nowrap; }
|
|
136
|
+
.ya-workspace-path { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--dsw-alias-label-tertiary); font-size:11px; line-height:15px; }
|
|
137
|
+
.ya-row-actions { flex:none; display:flex; align-items:center; gap:2px; opacity:0; pointer-events:none; transition:opacity 120ms ease-out; }
|
|
138
|
+
.ya-row:hover .ya-row-actions, .ya-menu-open .ya-row-actions { opacity:1; pointer-events:auto; }
|
|
139
|
+
.ya-status-slot { flex:none; width:16px; height:16px; display:inline-flex; align-items:center; justify-content:center; color:var(--dsw-alias-label-tertiary); }
|
|
140
|
+
.ya-recent .ya-row { min-height:31px; }
|
|
141
|
+
.ya-search-workspace { color:var(--dsw-alias-label-tertiary); font-size:11px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
142
|
+
.ya-empty, .ya-status { padding:18px 10px; color:var(--dsw-alias-label-tertiary); text-align:center; font-size:13px; }
|
|
143
|
+
.ya-warning { color:var(--dsw-alias-status-warning); }
|
|
144
|
+
.ya-rename-input { width:100%; height:38px; box-sizing:border-box; border:1px solid var(--dsw-alias-border-l2); border-radius:9px; padding:0 11px; background:transparent; color:var(--dsw-alias-label-primary); outline:none; }
|
|
145
|
+
.ya-error { margin-top:8px; color:var(--dsw-alias-status-error); font-size:12px; }
|
|
146
|
+
.ya-drop-before::before, .ya-drop-after::after { content:''; position:absolute; left:8px; right:8px; height:2px; border-radius:2px; background:var(--dsw-alias-label-link); }
|
|
147
|
+
.ya-drop-before::before { top:-2px; } .ya-drop-after::after { bottom:-2px; }
|
|
148
|
+
.ya-rail .ya-section-header { padding-left:0; margin-bottom:12px; }
|
|
149
|
+
.ya-rail .ya-icon-button, .ya-rail .ya-search { width:36px; height:36px; padding:0; margin:0 0 12px; border-color:transparent; background:transparent; }
|
|
150
|
+
.ya-rail .ya-search { justify-content:center; }
|
|
151
|
+
.ya-rail .ya-search-icon { cursor:pointer; color:var(--dsw-alias-label-primary); }
|
|
152
|
+
.ya-picker-error { color:var(--dsw-alias-status-error); white-space:pre-wrap; }
|
|
153
|
+
@keyframes ya-slide-in-forward { from { opacity:0; transform:translateX(10px); } to { opacity:1; transform:translateX(0); } }
|
|
154
|
+
@keyframes ya-slide-in-backward { from { opacity:0; transform:translateX(-10px); } to { opacity:1; transform:translateX(0); } }
|
|
155
|
+
.ya-level-enter-forward { animation:ya-slide-in-forward 180ms ease-out; }
|
|
156
|
+
.ya-level-enter-backward { animation:ya-slide-in-backward 180ms ease-out; }
|
|
157
|
+
`;
|
|
158
|
+
/** Install the stylesheet and return its disposer. */
|
|
159
|
+
function installStyles() {
|
|
160
|
+
const style = document.createElement("style");
|
|
161
|
+
style.setAttribute("data-ya-workspace-sidebar-style", "");
|
|
162
|
+
style.textContent = CSS;
|
|
163
|
+
document.head.appendChild(style);
|
|
164
|
+
return () => {
|
|
165
|
+
style.remove();
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
//#endregion
|
|
169
|
+
//#region src/client/WorkspacePicker.tsx
|
|
170
|
+
const ADD = "::ya-add-workspace";
|
|
171
|
+
/** Render the workspace target menu and directory picking conversation. */
|
|
172
|
+
function WorkspacePickFlow({ t, open, anchorRef, useWorkspaces, createWorkspace, useDirectoryFlow, renderDirectoryFlow, onPick, onClose, addOnly = false, side = "bottom", selectedId }) {
|
|
173
|
+
const snapshot = useWorkspaces((state) => state);
|
|
174
|
+
const flowAvailable = useDirectoryFlow((value) => value);
|
|
175
|
+
const [flowOpen, setFlowOpen] = (0, react.useState)(false);
|
|
176
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
177
|
+
const [error, setError] = (0, react.useState)(null);
|
|
178
|
+
const getAnchorRect = (0, react.useCallback)(() => anchorRef?.current?.getBoundingClientRect() ?? null, [anchorRef]);
|
|
179
|
+
(0, react.useEffect)(() => {
|
|
180
|
+
if (flowOpen && !flowAvailable) setFlowOpen(false);
|
|
181
|
+
}, [flowAvailable, flowOpen]);
|
|
182
|
+
const openFlow = (0, react.useCallback)(() => {
|
|
183
|
+
onClose();
|
|
184
|
+
setError(null);
|
|
185
|
+
setFlowOpen(true);
|
|
186
|
+
}, [onClose]);
|
|
187
|
+
const addEntries = flowAvailable ? [{
|
|
188
|
+
id: ADD,
|
|
189
|
+
label: t("addWorkspaceMenu"),
|
|
190
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 16 }),
|
|
191
|
+
disabled: flowOpen || busy
|
|
192
|
+
}] : [];
|
|
193
|
+
const pinnedAdd = !addOnly && snapshot.items.length > 0;
|
|
194
|
+
const items = pinnedAdd ? snapshot.items.map((workspace) => ({
|
|
195
|
+
id: workspace.workspaceId,
|
|
196
|
+
label: workspace.title,
|
|
197
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderClose16, { size: 16 }),
|
|
198
|
+
disabled: flowOpen || busy
|
|
199
|
+
})) : addEntries;
|
|
200
|
+
const settled = addOnly || snapshot.phase === "ready";
|
|
201
|
+
const onlyAdd = !pinnedAdd && settled && addEntries.length === 1;
|
|
202
|
+
(0, react.useEffect)(() => {
|
|
203
|
+
if (open && onlyAdd && !flowOpen && !busy) openFlow();
|
|
204
|
+
}, [
|
|
205
|
+
busy,
|
|
206
|
+
flowOpen,
|
|
207
|
+
onlyAdd,
|
|
208
|
+
open,
|
|
209
|
+
openFlow
|
|
210
|
+
]);
|
|
211
|
+
const owner = {
|
|
212
|
+
open: flowOpen,
|
|
213
|
+
busy,
|
|
214
|
+
onPicked: (path) => {
|
|
215
|
+
setBusy(true);
|
|
216
|
+
createWorkspace({ path }).then((workspace) => {
|
|
217
|
+
setFlowOpen(false);
|
|
218
|
+
onPick(workspace.workspaceId);
|
|
219
|
+
}).catch((reason) => {
|
|
220
|
+
setFlowOpen(false);
|
|
221
|
+
setError(reason instanceof Error ? reason.message : String(reason));
|
|
222
|
+
}).finally(() => {
|
|
223
|
+
setBusy(false);
|
|
224
|
+
});
|
|
225
|
+
},
|
|
226
|
+
onCancel: () => {
|
|
227
|
+
setFlowOpen(false);
|
|
228
|
+
},
|
|
229
|
+
onError: (message) => {
|
|
230
|
+
setFlowOpen(false);
|
|
231
|
+
setError(message);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
235
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
236
|
+
open: open && !onlyAdd && items.length > 0,
|
|
237
|
+
anchor: null,
|
|
238
|
+
items,
|
|
239
|
+
...pinnedAdd ? { footer: addEntries } : {},
|
|
240
|
+
selectedId,
|
|
241
|
+
onSelect: (id) => {
|
|
242
|
+
if (id === ADD) openFlow();
|
|
243
|
+
else onPick(id);
|
|
244
|
+
},
|
|
245
|
+
onClose,
|
|
246
|
+
side,
|
|
247
|
+
portal: true,
|
|
248
|
+
getAnchorRect
|
|
249
|
+
}),
|
|
250
|
+
open && !onlyAdd && snapshot.phase === "pending" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
251
|
+
className: "ya-status",
|
|
252
|
+
children: t("loading")
|
|
253
|
+
}),
|
|
254
|
+
renderDirectoryFlow(owner),
|
|
255
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
256
|
+
open: error !== null,
|
|
257
|
+
onClose: () => {
|
|
258
|
+
setError(null);
|
|
259
|
+
},
|
|
260
|
+
closeLabel: t("cancel"),
|
|
261
|
+
title: t("folderError"),
|
|
262
|
+
footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
263
|
+
variant: "outline",
|
|
264
|
+
onClick: () => {
|
|
265
|
+
setError(null);
|
|
266
|
+
},
|
|
267
|
+
children: t("cancel")
|
|
268
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
269
|
+
variant: "primary",
|
|
270
|
+
disabled: !flowAvailable,
|
|
271
|
+
onClick: openFlow,
|
|
272
|
+
children: t("retry")
|
|
273
|
+
})] }),
|
|
274
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
275
|
+
className: "ya-picker-error",
|
|
276
|
+
role: "alert",
|
|
277
|
+
children: error
|
|
278
|
+
})
|
|
279
|
+
})
|
|
280
|
+
] });
|
|
281
|
+
}
|
|
282
|
+
/** Fill the conversation hero's workspace picker seat. */
|
|
283
|
+
function WorkspacePicker({ open, anchorRef, useWorkspaces, selectedId, onPick, onClose, createWorkspace, useDirectoryFlow, renderSlot, t }) {
|
|
284
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspacePickFlow, {
|
|
285
|
+
t,
|
|
286
|
+
open,
|
|
287
|
+
anchorRef,
|
|
288
|
+
useWorkspaces,
|
|
289
|
+
selectedId,
|
|
290
|
+
onPick,
|
|
291
|
+
onClose,
|
|
292
|
+
createWorkspace,
|
|
293
|
+
useDirectoryFlow,
|
|
294
|
+
renderDirectoryFlow: (owner) => renderSlot("conversation.hero.workspace.directoryFlow", owner)
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
//#endregion
|
|
298
|
+
//#region src/client/model.ts
|
|
299
|
+
/** Navigation key for sessions not accounted to a real workspace. */
|
|
300
|
+
const UNGROUPED = "__ya_ungrouped__";
|
|
301
|
+
function visible(summary, current, archived) {
|
|
302
|
+
return summary.origin !== "subagent" && !archived.has(summary.id) && (!summary.blank || summary.id === current);
|
|
303
|
+
}
|
|
304
|
+
function rowOf(summary, workspaceKey, workspaceTitle) {
|
|
305
|
+
return {
|
|
306
|
+
id: summary.id,
|
|
307
|
+
title: summary.blank ? "New Session" : summary.displayTitle,
|
|
308
|
+
blank: summary.blank,
|
|
309
|
+
running: summary.running,
|
|
310
|
+
...summary.pendingInteraction === void 0 ? {} : { pendingInteraction: summary.pendingInteraction },
|
|
311
|
+
completed: summary.completed === true,
|
|
312
|
+
updatedAt: summary.updatedAt,
|
|
313
|
+
workspaceKey,
|
|
314
|
+
workspaceTitle
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
function ownerIndex(workspaces) {
|
|
318
|
+
const result = /* @__PURE__ */ new Map();
|
|
319
|
+
for (const workspace of workspaces) for (const sessionId of workspace.sessionIds) result.set(sessionId, workspace);
|
|
320
|
+
return result;
|
|
321
|
+
}
|
|
322
|
+
/** Resolve the first/second-level destination for one session. */
|
|
323
|
+
function workspaceKeyForSession(sessionId, workspaces) {
|
|
324
|
+
if (sessionId === void 0) return null;
|
|
325
|
+
return ownerIndex(workspaces).get(sessionId)?.workspaceId ?? "__ya_ungrouped__";
|
|
326
|
+
}
|
|
327
|
+
/** Derive global recent sessions, newest first. */
|
|
328
|
+
function deriveRecent(list, workspaces, archivedSessionIds, limit = 5) {
|
|
329
|
+
const archived = new Set(archivedSessionIds);
|
|
330
|
+
const owners = ownerIndex(workspaces);
|
|
331
|
+
return list.ids.map((id) => list.byId[id]).filter((summary) => summary !== void 0 && visible(summary, list.current, archived)).sort((a, b) => b.updatedAt - a.updatedAt || String(a.id).localeCompare(String(b.id))).slice(0, limit).map((summary) => {
|
|
332
|
+
const workspace = owners.get(summary.id);
|
|
333
|
+
return rowOf(summary, workspace?.workspaceId ?? "__ya_ungrouped__", workspace?.title ?? "Ungrouped");
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
/** Derive first-level real workspaces plus the virtual Ungrouped row. */
|
|
337
|
+
function deriveWorkspaces(list, workspaces, archivedSessionIds) {
|
|
338
|
+
const archived = new Set(archivedSessionIds);
|
|
339
|
+
const accounted = /* @__PURE__ */ new Set();
|
|
340
|
+
const result = workspaces.map((workspace) => {
|
|
341
|
+
let count = 0;
|
|
342
|
+
for (const id of workspace.sessionIds) {
|
|
343
|
+
accounted.add(id);
|
|
344
|
+
const summary = list.byId[id];
|
|
345
|
+
if (summary !== void 0 && visible(summary, list.current, archived)) count++;
|
|
346
|
+
}
|
|
347
|
+
return {
|
|
348
|
+
key: workspace.workspaceId,
|
|
349
|
+
title: workspace.title,
|
|
350
|
+
path: workspace.path,
|
|
351
|
+
createdAt: workspace.createdAt,
|
|
352
|
+
count,
|
|
353
|
+
real: true
|
|
354
|
+
};
|
|
355
|
+
});
|
|
356
|
+
let ungrouped = 0;
|
|
357
|
+
for (const id of list.ids) {
|
|
358
|
+
const summary = list.byId[id];
|
|
359
|
+
if (summary !== void 0 && !accounted.has(id) && visible(summary, list.current, archived)) ungrouped++;
|
|
360
|
+
}
|
|
361
|
+
result.push({
|
|
362
|
+
key: UNGROUPED,
|
|
363
|
+
title: "Ungrouped",
|
|
364
|
+
count: ungrouped,
|
|
365
|
+
real: false
|
|
366
|
+
});
|
|
367
|
+
return result;
|
|
368
|
+
}
|
|
369
|
+
/** Derive the selected workspace's sessions in its canonical order. */
|
|
370
|
+
function deriveWorkspaceSessions(key, list, workspaces, archivedSessionIds) {
|
|
371
|
+
const archived = new Set(archivedSessionIds);
|
|
372
|
+
if (key === "__ya_ungrouped__") {
|
|
373
|
+
const accounted = new Set(workspaces.flatMap((workspace) => workspace.sessionIds));
|
|
374
|
+
return list.ids.map((id) => list.byId[id]).filter((summary) => summary !== void 0 && !accounted.has(summary.id) && visible(summary, list.current, archived)).sort((a, b) => b.updatedAt - a.updatedAt || String(a.id).localeCompare(String(b.id))).map((summary) => rowOf(summary, UNGROUPED, "Ungrouped"));
|
|
375
|
+
}
|
|
376
|
+
const workspace = workspaces.find((item) => item.workspaceId === key);
|
|
377
|
+
if (workspace === void 0) return [];
|
|
378
|
+
return workspace.sessionIds.map((id) => list.byId[id]).filter((summary) => summary !== void 0 && visible(summary, list.current, archived)).map((summary) => rowOf(summary, workspace.workspaceId, workspace.title));
|
|
379
|
+
}
|
|
380
|
+
/** Case-insensitive local title/workspace matching used beside Host content search. */
|
|
381
|
+
function localMatches(rows, query) {
|
|
382
|
+
const normalized = query.trim().toLocaleLowerCase();
|
|
383
|
+
if (normalized === "") return [];
|
|
384
|
+
return rows.filter((row) => `${row.title}\n${row.workspaceTitle}`.toLocaleLowerCase().includes(normalized));
|
|
385
|
+
}
|
|
386
|
+
//#endregion
|
|
387
|
+
//#region src/client/WorkspaceSidebar.tsx
|
|
388
|
+
/** Two-level workspace/session browser with a persistent global recent block. */
|
|
389
|
+
const SEARCH_DEBOUNCE_MS = 250;
|
|
390
|
+
const SEARCH_MAX = 500;
|
|
391
|
+
function sanitized(value) {
|
|
392
|
+
return value.replaceAll("\0", "").slice(0, SEARCH_MAX);
|
|
393
|
+
}
|
|
394
|
+
function relativeTime(updatedAt, now, t) {
|
|
395
|
+
const diff = Math.max(0, now - updatedAt);
|
|
396
|
+
const minute = 6e4;
|
|
397
|
+
if (diff < minute) return t("now");
|
|
398
|
+
if (diff < 60 * minute) return t("minutes", { n: Math.floor(diff / minute) });
|
|
399
|
+
if (diff < 1440 * minute) return t("hours", { n: Math.floor(diff / (60 * minute)) });
|
|
400
|
+
if (diff < 43200 * minute) return t("days", { n: Math.floor(diff / (1440 * minute)) });
|
|
401
|
+
if (diff < 525600 * minute) return t("months", { n: Math.floor(diff / (43200 * minute)) });
|
|
402
|
+
return t("years", { n: Math.floor(diff / (525600 * minute)) });
|
|
403
|
+
}
|
|
404
|
+
function SessionStatus({ row }) {
|
|
405
|
+
if (row.pendingInteraction !== void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "warning" });
|
|
406
|
+
if (row.running) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "ongoing" });
|
|
407
|
+
if (row.completed) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "done" });
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
function rowHalf(event) {
|
|
411
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
412
|
+
return event.clientY < rect.top + rect.height / 2 ? "before" : "after";
|
|
413
|
+
}
|
|
414
|
+
function SessionItem({ row, current, now, open, rename, fork, archive, t, context, drag }) {
|
|
415
|
+
const [menuOpen, setMenuOpen] = (0, react.useState)(false);
|
|
416
|
+
const title = row.blank ? t("newSession") : row.title;
|
|
417
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
418
|
+
className: `ya-row${row.id === current ? " ya-selected" : ""}${menuOpen ? " ya-menu-open" : ""}${drag?.marker === "before" ? " ya-drop-before" : ""}${drag?.marker === "after" ? " ya-drop-after" : ""}`,
|
|
419
|
+
role: "treeitem",
|
|
420
|
+
"aria-selected": row.id === current,
|
|
421
|
+
draggable: drag !== void 0,
|
|
422
|
+
onClick: () => {
|
|
423
|
+
open(row.id);
|
|
424
|
+
},
|
|
425
|
+
onDragStart: drag === void 0 ? void 0 : (event) => {
|
|
426
|
+
event.dataTransfer.effectAllowed = "move";
|
|
427
|
+
drag.start();
|
|
428
|
+
},
|
|
429
|
+
onDragEnd: drag?.end,
|
|
430
|
+
onDragOver: drag === void 0 ? void 0 : (event) => {
|
|
431
|
+
if (!drag.active) return;
|
|
432
|
+
event.preventDefault();
|
|
433
|
+
event.dataTransfer.dropEffect = "move";
|
|
434
|
+
drag.hover(rowHalf(event));
|
|
435
|
+
},
|
|
436
|
+
onDrop: drag === void 0 ? void 0 : (event) => {
|
|
437
|
+
if (!drag.active) return;
|
|
438
|
+
event.preventDefault();
|
|
439
|
+
drag.drop(rowHalf(event));
|
|
440
|
+
},
|
|
441
|
+
children: [
|
|
442
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
443
|
+
className: "ya-status-slot",
|
|
444
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionStatus, { row })
|
|
445
|
+
}),
|
|
446
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
447
|
+
className: "ya-row-main",
|
|
448
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
449
|
+
className: "ya-row-line",
|
|
450
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
451
|
+
className: "ya-row-title",
|
|
452
|
+
children: title
|
|
453
|
+
}), !row.blank && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
454
|
+
className: "ya-row-meta ya-row-time",
|
|
455
|
+
children: relativeTime(row.updatedAt, now, t)
|
|
456
|
+
})]
|
|
457
|
+
}), context === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
458
|
+
className: "ya-search-workspace",
|
|
459
|
+
children: row.workspaceTitle
|
|
460
|
+
})]
|
|
461
|
+
}),
|
|
462
|
+
!row.blank && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
463
|
+
className: "ya-row-actions",
|
|
464
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
465
|
+
open: menuOpen,
|
|
466
|
+
onClose: () => {
|
|
467
|
+
setMenuOpen(false);
|
|
468
|
+
},
|
|
469
|
+
items: [
|
|
470
|
+
{
|
|
471
|
+
id: "rename",
|
|
472
|
+
label: t("rename"),
|
|
473
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, {})
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
id: "fork",
|
|
477
|
+
label: t("fork"),
|
|
478
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, {})
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
id: "archive",
|
|
482
|
+
label: t("archive"),
|
|
483
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconArchiveOutline20, { size: 16 })
|
|
484
|
+
}
|
|
485
|
+
],
|
|
486
|
+
onSelect: (id) => {
|
|
487
|
+
setMenuOpen(false);
|
|
488
|
+
if (id === "rename") rename(row);
|
|
489
|
+
if (id === "fork") fork(row.id);
|
|
490
|
+
if (id === "archive") archive(row.id);
|
|
491
|
+
},
|
|
492
|
+
portal: true,
|
|
493
|
+
closeOnPointerLeave: true,
|
|
494
|
+
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
495
|
+
type: "button",
|
|
496
|
+
className: "ya-icon-button",
|
|
497
|
+
"aria-label": `${title} actions`,
|
|
498
|
+
onClick: (event) => {
|
|
499
|
+
event.stopPropagation();
|
|
500
|
+
setMenuOpen((value) => !value);
|
|
501
|
+
},
|
|
502
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, {})
|
|
503
|
+
})
|
|
504
|
+
})
|
|
505
|
+
})
|
|
506
|
+
]
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
function WorkspaceItem({ row, enter, create, rename, remove, t }) {
|
|
510
|
+
const [menuOpen, setMenuOpen] = (0, react.useState)(false);
|
|
511
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
512
|
+
className: `ya-row ya-workspace-row${menuOpen ? " ya-menu-open" : ""}`,
|
|
513
|
+
role: "treeitem",
|
|
514
|
+
onClick: enter,
|
|
515
|
+
title: row.path,
|
|
516
|
+
children: [
|
|
517
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
518
|
+
className: "ya-status-slot",
|
|
519
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderClose16, {})
|
|
520
|
+
}),
|
|
521
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
522
|
+
className: "ya-row-main",
|
|
523
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
524
|
+
className: "ya-row-line",
|
|
525
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
526
|
+
className: "ya-row-title",
|
|
527
|
+
children: row.real ? row.title : t("ungrouped")
|
|
528
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
529
|
+
className: "ya-row-meta",
|
|
530
|
+
children: t("count", { n: row.count })
|
|
531
|
+
})]
|
|
532
|
+
}), row.path !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
533
|
+
className: "ya-workspace-path",
|
|
534
|
+
children: row.path
|
|
535
|
+
})]
|
|
536
|
+
}),
|
|
537
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
538
|
+
className: "ya-row-actions",
|
|
539
|
+
children: [row.real && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
540
|
+
open: menuOpen,
|
|
541
|
+
onClose: () => {
|
|
542
|
+
setMenuOpen(false);
|
|
543
|
+
},
|
|
544
|
+
items: [{
|
|
545
|
+
id: "rename",
|
|
546
|
+
label: t("rename"),
|
|
547
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, {})
|
|
548
|
+
}, {
|
|
549
|
+
id: "delete",
|
|
550
|
+
label: t("deleteWorkspace"),
|
|
551
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}),
|
|
552
|
+
danger: true
|
|
553
|
+
}],
|
|
554
|
+
onSelect: (id) => {
|
|
555
|
+
setMenuOpen(false);
|
|
556
|
+
if (id === "rename") rename();
|
|
557
|
+
if (id === "delete") remove();
|
|
558
|
+
},
|
|
559
|
+
portal: true,
|
|
560
|
+
closeOnPointerLeave: true,
|
|
561
|
+
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
562
|
+
type: "button",
|
|
563
|
+
className: "ya-icon-button",
|
|
564
|
+
onClick: (event) => {
|
|
565
|
+
event.stopPropagation();
|
|
566
|
+
setMenuOpen((value) => !value);
|
|
567
|
+
},
|
|
568
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, {})
|
|
569
|
+
})
|
|
570
|
+
}), row.real && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
571
|
+
type: "button",
|
|
572
|
+
className: "ya-icon-button",
|
|
573
|
+
onClick: (event) => {
|
|
574
|
+
event.stopPropagation();
|
|
575
|
+
create();
|
|
576
|
+
},
|
|
577
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {})
|
|
578
|
+
})]
|
|
579
|
+
}),
|
|
580
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {})
|
|
581
|
+
]
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
/** Fill `sidebar.workspaces` with the replacement browser. */
|
|
585
|
+
function WorkspaceSidebar(props) {
|
|
586
|
+
const { wide, expandSidebar, useSessions, useWorkspaces, startSession, open, searchSessions, searchResultLimit, renameSession, forkSession, renameWorkspace, deleteWorkspace, archiveSession, insertSessionBefore, createWorkspace, useDirectoryFlow, renderSlot, t } = props;
|
|
587
|
+
const sessions = useSessions((state) => state);
|
|
588
|
+
const workspaceState = useWorkspaces((state) => state);
|
|
589
|
+
const workspaces = workspaceState.items;
|
|
590
|
+
const archived = workspaceState.archivedSessionIds;
|
|
591
|
+
const directoryFlowAvailable = useDirectoryFlow((value) => value);
|
|
592
|
+
const allRows = (0, react.useMemo)(() => deriveRecent(sessions, workspaces, archived, Number.MAX_SAFE_INTEGER), [
|
|
593
|
+
archived,
|
|
594
|
+
sessions,
|
|
595
|
+
workspaces
|
|
596
|
+
]);
|
|
597
|
+
const recent = allRows.slice(0, 5);
|
|
598
|
+
const workspaceRows = (0, react.useMemo)(() => deriveWorkspaces(sessions, workspaces, archived), [
|
|
599
|
+
archived,
|
|
600
|
+
sessions,
|
|
601
|
+
workspaces
|
|
602
|
+
]);
|
|
603
|
+
const [selectedKey, setSelectedKey] = (0, react.useState)(null);
|
|
604
|
+
const [direction, setDirection] = (0, react.useState)("forward");
|
|
605
|
+
const [hasMounted, setHasMounted] = (0, react.useState)(false);
|
|
606
|
+
(0, react.useEffect)(() => {
|
|
607
|
+
setHasMounted(true);
|
|
608
|
+
}, []);
|
|
609
|
+
const observedCurrent = (0, react.useRef)(void 0);
|
|
610
|
+
const initialized = (0, react.useRef)(false);
|
|
611
|
+
(0, react.useEffect)(() => {
|
|
612
|
+
if (initialized.current && observedCurrent.current === sessions.current) return;
|
|
613
|
+
initialized.current = true;
|
|
614
|
+
observedCurrent.current = sessions.current;
|
|
615
|
+
if (sessions.current !== void 0) {
|
|
616
|
+
setDirection("forward");
|
|
617
|
+
setSelectedKey(workspaceKeyForSession(sessions.current, workspaces));
|
|
618
|
+
}
|
|
619
|
+
}, [sessions.current, workspaces]);
|
|
620
|
+
(0, react.useEffect)(() => {
|
|
621
|
+
if (selectedKey !== null && selectedKey !== "__ya_ungrouped__" && !workspaces.some((workspace) => workspace.workspaceId === selectedKey)) setSelectedKey(UNGROUPED);
|
|
622
|
+
}, [selectedKey, workspaces]);
|
|
623
|
+
const selectedWorkspace = selectedKey === null || selectedKey === "__ya_ungrouped__" ? void 0 : workspaces.find((workspace) => workspace.workspaceId === selectedKey);
|
|
624
|
+
const levelRows = selectedKey === null ? [] : deriveWorkspaceSessions(selectedKey, sessions, workspaces, archived);
|
|
625
|
+
const now = Date.now();
|
|
626
|
+
const [query, setQuery] = (0, react.useState)("");
|
|
627
|
+
const normalizedQuery = sanitized(query).trim();
|
|
628
|
+
const [remote, setRemote] = (0, react.useState)({
|
|
629
|
+
query: "",
|
|
630
|
+
status: "idle",
|
|
631
|
+
items: [],
|
|
632
|
+
hasMore: false
|
|
633
|
+
});
|
|
634
|
+
(0, react.useEffect)(() => {
|
|
635
|
+
if (normalizedQuery === "") {
|
|
636
|
+
setRemote({
|
|
637
|
+
query: "",
|
|
638
|
+
status: "idle",
|
|
639
|
+
items: [],
|
|
640
|
+
hasMore: false
|
|
641
|
+
});
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
const controller = new AbortController();
|
|
645
|
+
setRemote({
|
|
646
|
+
query: normalizedQuery,
|
|
647
|
+
status: "loading",
|
|
648
|
+
items: [],
|
|
649
|
+
hasMore: false
|
|
650
|
+
});
|
|
651
|
+
const timer = window.setTimeout(() => {
|
|
652
|
+
searchSessions(normalizedQuery, controller.signal).then((result) => {
|
|
653
|
+
if (!controller.signal.aborted) setRemote({
|
|
654
|
+
query: normalizedQuery,
|
|
655
|
+
status: "ready",
|
|
656
|
+
items: result.items,
|
|
657
|
+
hasMore: result.hasMore
|
|
658
|
+
});
|
|
659
|
+
}).catch(() => {
|
|
660
|
+
if (!controller.signal.aborted) setRemote({
|
|
661
|
+
query: normalizedQuery,
|
|
662
|
+
status: "error",
|
|
663
|
+
items: [],
|
|
664
|
+
hasMore: false
|
|
665
|
+
});
|
|
666
|
+
});
|
|
667
|
+
}, SEARCH_DEBOUNCE_MS);
|
|
668
|
+
return () => {
|
|
669
|
+
window.clearTimeout(timer);
|
|
670
|
+
controller.abort();
|
|
671
|
+
};
|
|
672
|
+
}, [normalizedQuery, searchSessions]);
|
|
673
|
+
const searchRows = (0, react.useMemo)(() => {
|
|
674
|
+
if (normalizedQuery === "") return [];
|
|
675
|
+
const byId = new Map(localMatches(allRows, normalizedQuery).map((row) => [row.id, row]));
|
|
676
|
+
if (remote.query === normalizedQuery) for (const item of remote.items) {
|
|
677
|
+
const row = allRows.find((candidate) => candidate.id === item.sessionId);
|
|
678
|
+
if (row !== void 0) byId.set(row.id, row);
|
|
679
|
+
}
|
|
680
|
+
return [...byId.values()].slice(0, searchResultLimit);
|
|
681
|
+
}, [
|
|
682
|
+
allRows,
|
|
683
|
+
normalizedQuery,
|
|
684
|
+
remote,
|
|
685
|
+
searchResultLimit
|
|
686
|
+
]);
|
|
687
|
+
const [pickerOpen, setPickerOpen] = (0, react.useState)(false);
|
|
688
|
+
const pickerAnchor = (0, react.useRef)(null);
|
|
689
|
+
const [recentCollapsed, setRecentCollapsed] = (0, react.useState)(false);
|
|
690
|
+
const [workspaceRename, setWorkspaceRename] = (0, react.useState)(null);
|
|
691
|
+
const [sessionRename, setSessionRename] = (0, react.useState)(null);
|
|
692
|
+
const [renameDraft, setRenameDraft] = (0, react.useState)("");
|
|
693
|
+
const [renameError, setRenameError] = (0, react.useState)(null);
|
|
694
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
695
|
+
const [deleteTarget, setDeleteTarget] = (0, react.useState)(null);
|
|
696
|
+
const [drag, setDrag] = (0, react.useState)(null);
|
|
697
|
+
const beginWorkspaceRename = (row) => {
|
|
698
|
+
setWorkspaceRename(row);
|
|
699
|
+
setRenameDraft(row.title);
|
|
700
|
+
setRenameError(null);
|
|
701
|
+
};
|
|
702
|
+
const beginSessionRename = (row) => {
|
|
703
|
+
setSessionRename(row);
|
|
704
|
+
setRenameDraft(row.title);
|
|
705
|
+
setRenameError(null);
|
|
706
|
+
};
|
|
707
|
+
const closeRename = () => {
|
|
708
|
+
if (!busy) {
|
|
709
|
+
setWorkspaceRename(null);
|
|
710
|
+
setSessionRename(null);
|
|
711
|
+
setRenameError(null);
|
|
712
|
+
}
|
|
713
|
+
};
|
|
714
|
+
const commitRename = () => {
|
|
715
|
+
const title = renameDraft.trim();
|
|
716
|
+
if (title === "" || busy) return;
|
|
717
|
+
setBusy(true);
|
|
718
|
+
(workspaceRename !== null && workspaceRename.key !== "__ya_ungrouped__" ? renameWorkspace(workspaceRename.key, title) : sessionRename !== null ? renameSession(sessionRename.id, title) : Promise.resolve()).then(() => {
|
|
719
|
+
setWorkspaceRename(null);
|
|
720
|
+
setSessionRename(null);
|
|
721
|
+
}).catch((reason) => {
|
|
722
|
+
setRenameError(reason instanceof Error ? reason.message : String(reason));
|
|
723
|
+
}).finally(() => {
|
|
724
|
+
setBusy(false);
|
|
725
|
+
});
|
|
726
|
+
};
|
|
727
|
+
const confirmDelete = () => {
|
|
728
|
+
if (deleteTarget === null || deleteTarget.key === "__ya_ungrouped__" || busy) return;
|
|
729
|
+
setBusy(true);
|
|
730
|
+
deleteWorkspace(deleteTarget.key).then(() => {
|
|
731
|
+
setDeleteTarget(null);
|
|
732
|
+
}).catch((reason) => {
|
|
733
|
+
setRenameError(reason instanceof Error ? reason.message : String(reason));
|
|
734
|
+
}).finally(() => {
|
|
735
|
+
setBusy(false);
|
|
736
|
+
});
|
|
737
|
+
};
|
|
738
|
+
const archive = (id) => {
|
|
739
|
+
archiveSession(id).catch((reason) => {
|
|
740
|
+
console.warn("session archive rejected:", reason);
|
|
741
|
+
});
|
|
742
|
+
};
|
|
743
|
+
const fork = (id) => {
|
|
744
|
+
forkSession(id);
|
|
745
|
+
};
|
|
746
|
+
const sessionItem = (row, context = false, index) => {
|
|
747
|
+
const draggable = selectedKey !== null && selectedKey !== "__ya_ungrouped__" && index !== void 0;
|
|
748
|
+
const marker = drag?.over?.id === row.id ? drag.over.half : null;
|
|
749
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionItem, {
|
|
750
|
+
row,
|
|
751
|
+
current: sessions.current,
|
|
752
|
+
now,
|
|
753
|
+
open,
|
|
754
|
+
rename: beginSessionRename,
|
|
755
|
+
fork,
|
|
756
|
+
archive,
|
|
757
|
+
t,
|
|
758
|
+
context,
|
|
759
|
+
...draggable && selectedKey !== null && selectedKey !== "__ya_ungrouped__" ? { drag: {
|
|
760
|
+
active: drag !== null,
|
|
761
|
+
marker,
|
|
762
|
+
start: () => {
|
|
763
|
+
setDrag({
|
|
764
|
+
id: row.id,
|
|
765
|
+
over: null
|
|
766
|
+
});
|
|
767
|
+
},
|
|
768
|
+
hover: (half) => {
|
|
769
|
+
setDrag((value) => value === null ? value : {
|
|
770
|
+
...value,
|
|
771
|
+
over: {
|
|
772
|
+
id: row.id,
|
|
773
|
+
half
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
},
|
|
777
|
+
drop: (half) => {
|
|
778
|
+
if (drag === null) return;
|
|
779
|
+
const anchor = half === "before" ? row.id : levelRows[(index ?? 0) + 1]?.id;
|
|
780
|
+
const sourceIndex = levelRows.findIndex((item) => item.id === drag.id);
|
|
781
|
+
const anchorIndex = anchor === void 0 ? levelRows.length : levelRows.findIndex((item) => item.id === anchor);
|
|
782
|
+
setDrag(null);
|
|
783
|
+
if (anchor === drag.id || sourceIndex === anchorIndex || sourceIndex + 1 === anchorIndex) return;
|
|
784
|
+
insertSessionBefore(selectedKey, drag.id, anchor).catch((reason) => {
|
|
785
|
+
console.warn("session reorder rejected:", reason);
|
|
786
|
+
});
|
|
787
|
+
},
|
|
788
|
+
end: () => {
|
|
789
|
+
setDrag(null);
|
|
790
|
+
}
|
|
791
|
+
} } : {}
|
|
792
|
+
}, row.id);
|
|
793
|
+
};
|
|
794
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
795
|
+
"data-ya-workspace-sidebar": true,
|
|
796
|
+
className: wide ? "" : "ya-rail",
|
|
797
|
+
children: [
|
|
798
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
799
|
+
className: "ya-section-header",
|
|
800
|
+
children: [
|
|
801
|
+
wide && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
802
|
+
className: "ya-section-title",
|
|
803
|
+
children: t("workspaces")
|
|
804
|
+
}),
|
|
805
|
+
directoryFlowAvailable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
806
|
+
ref: pickerAnchor,
|
|
807
|
+
type: "button",
|
|
808
|
+
className: "ya-icon-button",
|
|
809
|
+
"aria-label": t("addWorkspace"),
|
|
810
|
+
onClick: () => {
|
|
811
|
+
setPickerOpen((value) => !value);
|
|
812
|
+
},
|
|
813
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconProjectAddOutline16, { size: wide ? 16 : 18 })
|
|
814
|
+
}),
|
|
815
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspacePickFlow, {
|
|
816
|
+
t,
|
|
817
|
+
open: pickerOpen,
|
|
818
|
+
anchorRef: pickerAnchor,
|
|
819
|
+
useWorkspaces,
|
|
820
|
+
createWorkspace,
|
|
821
|
+
useDirectoryFlow,
|
|
822
|
+
renderDirectoryFlow: (owner) => renderSlot("sidebar.workspaces.directoryFlow", owner),
|
|
823
|
+
addOnly: true,
|
|
824
|
+
side: "right",
|
|
825
|
+
onPick: (workspaceId) => {
|
|
826
|
+
setPickerOpen(false);
|
|
827
|
+
startSession(workspaceId);
|
|
828
|
+
},
|
|
829
|
+
onClose: () => {
|
|
830
|
+
setPickerOpen(false);
|
|
831
|
+
}
|
|
832
|
+
})
|
|
833
|
+
]
|
|
834
|
+
}),
|
|
835
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
836
|
+
className: "ya-search",
|
|
837
|
+
onClick: () => {
|
|
838
|
+
if (!wide) expandSidebar();
|
|
839
|
+
},
|
|
840
|
+
children: [
|
|
841
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
842
|
+
type: "button",
|
|
843
|
+
className: "ya-search-icon",
|
|
844
|
+
"aria-label": t("search"),
|
|
845
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, { size: wide ? 14 : 18 })
|
|
846
|
+
}),
|
|
847
|
+
wide && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
848
|
+
className: "ya-search-input",
|
|
849
|
+
value: query,
|
|
850
|
+
maxLength: SEARCH_MAX,
|
|
851
|
+
placeholder: t("searchPlaceholder"),
|
|
852
|
+
onChange: (event) => {
|
|
853
|
+
setQuery(sanitized(event.target.value));
|
|
854
|
+
}
|
|
855
|
+
}),
|
|
856
|
+
wide && query !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
857
|
+
type: "button",
|
|
858
|
+
className: "ya-icon-button",
|
|
859
|
+
"aria-label": t("clearSearch"),
|
|
860
|
+
onClick: () => {
|
|
861
|
+
setQuery("");
|
|
862
|
+
},
|
|
863
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseFill14, {})
|
|
864
|
+
})
|
|
865
|
+
]
|
|
866
|
+
}),
|
|
867
|
+
wide && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
868
|
+
className: "ya-body",
|
|
869
|
+
children: normalizedQuery !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
870
|
+
className: "ya-scroll",
|
|
871
|
+
role: "tree",
|
|
872
|
+
"aria-label": t("search"),
|
|
873
|
+
children: [
|
|
874
|
+
searchRows.map((row) => sessionItem(row, true)),
|
|
875
|
+
remote.status === "loading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
876
|
+
className: "ya-status",
|
|
877
|
+
children: t("searching")
|
|
878
|
+
}),
|
|
879
|
+
remote.status === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
880
|
+
className: "ya-status ya-warning",
|
|
881
|
+
children: t("searchUnavailable")
|
|
882
|
+
}),
|
|
883
|
+
remote.status !== "loading" && searchRows.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
884
|
+
className: "ya-empty",
|
|
885
|
+
children: t("noMatches")
|
|
886
|
+
})
|
|
887
|
+
]
|
|
888
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
889
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
890
|
+
className: `ya-recent${recentCollapsed ? " ya-recent-collapsed" : ""}`,
|
|
891
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
892
|
+
className: "ya-block-label",
|
|
893
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("recent") }), recent.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
894
|
+
type: "button",
|
|
895
|
+
className: `ya-block-label-toggle${recentCollapsed ? " ya-collapsed" : ""}`,
|
|
896
|
+
"aria-label": recentCollapsed ? t("expand") : t("collapse"),
|
|
897
|
+
"aria-expanded": !recentCollapsed,
|
|
898
|
+
onClick: (event) => {
|
|
899
|
+
event.stopPropagation();
|
|
900
|
+
setRecentCollapsed((value) => !value);
|
|
901
|
+
},
|
|
902
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {})
|
|
903
|
+
})]
|
|
904
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
905
|
+
className: "ya-recent-list-wrap",
|
|
906
|
+
children: recent.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
907
|
+
className: "ya-empty",
|
|
908
|
+
children: t("noSessions")
|
|
909
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
910
|
+
className: "ya-recent-list",
|
|
911
|
+
children: recent.map((row) => sessionItem(row, true))
|
|
912
|
+
})
|
|
913
|
+
})]
|
|
914
|
+
}),
|
|
915
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
916
|
+
className: "ya-breadcrumb",
|
|
917
|
+
children: selectedKey === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
918
|
+
className: "ya-crumb",
|
|
919
|
+
children: t("workspaces")
|
|
920
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
921
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
922
|
+
type: "button",
|
|
923
|
+
className: "ya-crumb",
|
|
924
|
+
onClick: () => {
|
|
925
|
+
setDirection("backward");
|
|
926
|
+
setSelectedKey(null);
|
|
927
|
+
},
|
|
928
|
+
children: t("workspaces")
|
|
929
|
+
}),
|
|
930
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {}),
|
|
931
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
932
|
+
className: "ya-crumb",
|
|
933
|
+
children: selectedKey === "__ya_ungrouped__" ? t("ungrouped") : selectedWorkspace?.title
|
|
934
|
+
}),
|
|
935
|
+
selectedKey !== "__ya_ungrouped__" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
936
|
+
type: "button",
|
|
937
|
+
className: "ya-icon-button",
|
|
938
|
+
"aria-label": t("newSession"),
|
|
939
|
+
onClick: () => {
|
|
940
|
+
startSession(selectedKey);
|
|
941
|
+
},
|
|
942
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {})
|
|
943
|
+
})
|
|
944
|
+
] })
|
|
945
|
+
}),
|
|
946
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
947
|
+
className: "ya-scroll",
|
|
948
|
+
role: "tree",
|
|
949
|
+
"aria-label": selectedKey === null ? t("workspaces") : t("sessions"),
|
|
950
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
951
|
+
className: hasMounted ? `ya-level-enter-${direction}` : void 0,
|
|
952
|
+
children: [
|
|
953
|
+
selectedKey === null ? workspaceRows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceItem, {
|
|
954
|
+
row,
|
|
955
|
+
enter: () => {
|
|
956
|
+
setDirection("forward");
|
|
957
|
+
setSelectedKey(row.key);
|
|
958
|
+
},
|
|
959
|
+
create: () => {
|
|
960
|
+
if (row.key !== "__ya_ungrouped__") startSession(row.key);
|
|
961
|
+
},
|
|
962
|
+
rename: () => {
|
|
963
|
+
beginWorkspaceRename(row);
|
|
964
|
+
},
|
|
965
|
+
remove: () => {
|
|
966
|
+
setDeleteTarget(row);
|
|
967
|
+
setRenameError(null);
|
|
968
|
+
},
|
|
969
|
+
t
|
|
970
|
+
}, row.key)) : levelRows.map((row, index) => sessionItem(row, false, index)),
|
|
971
|
+
selectedKey === null && workspaceRows.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
972
|
+
className: "ya-empty",
|
|
973
|
+
children: t("noWorkspaces")
|
|
974
|
+
}),
|
|
975
|
+
selectedKey !== null && levelRows.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
976
|
+
className: "ya-empty",
|
|
977
|
+
children: t("noSessions")
|
|
978
|
+
})
|
|
979
|
+
]
|
|
980
|
+
}, selectedKey ?? "root")
|
|
981
|
+
})
|
|
982
|
+
] })
|
|
983
|
+
}),
|
|
984
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
985
|
+
open: workspaceRename !== null || sessionRename !== null,
|
|
986
|
+
onClose: closeRename,
|
|
987
|
+
closeLabel: t("cancel"),
|
|
988
|
+
title: workspaceRename !== null ? t("renameWorkspace") : t("renameSession"),
|
|
989
|
+
footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
990
|
+
variant: "outline",
|
|
991
|
+
disabled: busy,
|
|
992
|
+
onClick: closeRename,
|
|
993
|
+
children: t("cancel")
|
|
994
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
995
|
+
variant: "primary",
|
|
996
|
+
disabled: busy || renameDraft.trim() === "",
|
|
997
|
+
onClick: commitRename,
|
|
998
|
+
children: t("rename")
|
|
999
|
+
})] }),
|
|
1000
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1001
|
+
className: "ya-rename-input",
|
|
1002
|
+
value: renameDraft,
|
|
1003
|
+
autoFocus: true,
|
|
1004
|
+
disabled: busy,
|
|
1005
|
+
"aria-label": workspaceRename !== null ? t("workspaceName") : t("sessionName"),
|
|
1006
|
+
onChange: (event) => {
|
|
1007
|
+
setRenameDraft(event.target.value);
|
|
1008
|
+
setRenameError(null);
|
|
1009
|
+
}
|
|
1010
|
+
}), renameError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1011
|
+
className: "ya-error",
|
|
1012
|
+
role: "alert",
|
|
1013
|
+
children: renameError
|
|
1014
|
+
})]
|
|
1015
|
+
}),
|
|
1016
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
1017
|
+
open: deleteTarget !== null,
|
|
1018
|
+
onClose: () => {
|
|
1019
|
+
if (!busy) setDeleteTarget(null);
|
|
1020
|
+
},
|
|
1021
|
+
closeLabel: t("cancel"),
|
|
1022
|
+
title: t("deleteWorkspace"),
|
|
1023
|
+
description: deleteTarget === null ? void 0 : t("deleteDescription", { name: deleteTarget.title }),
|
|
1024
|
+
footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1025
|
+
variant: "outline",
|
|
1026
|
+
disabled: busy,
|
|
1027
|
+
onClick: () => {
|
|
1028
|
+
setDeleteTarget(null);
|
|
1029
|
+
},
|
|
1030
|
+
children: t("cancel")
|
|
1031
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1032
|
+
variant: "outline",
|
|
1033
|
+
disabled: busy,
|
|
1034
|
+
onClick: confirmDelete,
|
|
1035
|
+
children: t("deleteWorkspace")
|
|
1036
|
+
})] }),
|
|
1037
|
+
children: renameError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1038
|
+
className: "ya-error",
|
|
1039
|
+
role: "alert",
|
|
1040
|
+
children: renameError
|
|
1041
|
+
})
|
|
1042
|
+
})
|
|
1043
|
+
]
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
//#endregion
|
|
1047
|
+
//#region src/client/index.ts
|
|
1048
|
+
/** Services required by both replacement client entries. */
|
|
1049
|
+
const inject = [
|
|
1050
|
+
"slots",
|
|
1051
|
+
"sessions",
|
|
1052
|
+
"workspaces",
|
|
1053
|
+
"locale"
|
|
1054
|
+
];
|
|
1055
|
+
/** Register the sidebar browser and conversation hero picker. */
|
|
1056
|
+
function apply(ctx) {
|
|
1057
|
+
ctx.effect(() => ctx.locale.register(NS, {
|
|
1058
|
+
zh,
|
|
1059
|
+
en
|
|
1060
|
+
}), "ya-workspace-sidebar: dictionaries");
|
|
1061
|
+
ctx.effect(installStyles, "ya-workspace-sidebar: styles");
|
|
1062
|
+
const flowSource = (name) => ({
|
|
1063
|
+
getSnapshot: () => ctx.slots.entries(name).length > 0,
|
|
1064
|
+
subscribe: (listener) => ctx.slots.subscribe(name, listener)
|
|
1065
|
+
});
|
|
1066
|
+
const sidebarFlow = flowSource("sidebar.workspaces.directoryFlow");
|
|
1067
|
+
const pickerFlow = flowSource("conversation.hero.workspace.directoryFlow");
|
|
1068
|
+
const createWorkspace = (input) => ctx.workspaces.create(input);
|
|
1069
|
+
const searchSessions = async (query, signal) => {
|
|
1070
|
+
const result = await ctx.sessions.search(query, signal);
|
|
1071
|
+
if (!result.ok) throw new Error(result.error.message);
|
|
1072
|
+
return result.value;
|
|
1073
|
+
};
|
|
1074
|
+
const sidebarInjected = () => ({
|
|
1075
|
+
startSession: (workspaceId) => {
|
|
1076
|
+
ctx.workspaces.startSession(workspaceId);
|
|
1077
|
+
},
|
|
1078
|
+
open: (sessionId) => {
|
|
1079
|
+
ctx.sessions.open(sessionId);
|
|
1080
|
+
},
|
|
1081
|
+
searchSessions,
|
|
1082
|
+
searchResultLimit: ctx.sessions.searchResultLimit,
|
|
1083
|
+
renameSession: async (sessionId, title) => {
|
|
1084
|
+
const session = ctx.sessions.binding(sessionId)?.session;
|
|
1085
|
+
if (session === void 0) throw new Error(`unknown session "${sessionId}"`);
|
|
1086
|
+
const result = await session.rename(title);
|
|
1087
|
+
if (!result.ok) throw new Error(result.error.message);
|
|
1088
|
+
},
|
|
1089
|
+
forkSession: (sessionId) => {
|
|
1090
|
+
ctx.sessions.fork({
|
|
1091
|
+
sessionId,
|
|
1092
|
+
increaseTitle: true
|
|
1093
|
+
}).then((childId) => {
|
|
1094
|
+
ctx.sessions.open(childId);
|
|
1095
|
+
}).catch(() => {});
|
|
1096
|
+
},
|
|
1097
|
+
renameWorkspace: async (workspaceId, title) => {
|
|
1098
|
+
await ctx.workspaces.rename(workspaceId, title);
|
|
1099
|
+
},
|
|
1100
|
+
deleteWorkspace: async (workspaceId) => {
|
|
1101
|
+
await ctx.workspaces.delete(workspaceId);
|
|
1102
|
+
},
|
|
1103
|
+
archiveSession: async (sessionId) => {
|
|
1104
|
+
await ctx.workspaces.archiveSession(sessionId);
|
|
1105
|
+
},
|
|
1106
|
+
insertSessionBefore: async (workspaceId, sessionId, beforeSessionId) => {
|
|
1107
|
+
await ctx.workspaces.insertSessionBefore(workspaceId, sessionId, beforeSessionId);
|
|
1108
|
+
},
|
|
1109
|
+
createWorkspace,
|
|
1110
|
+
hooks: { directoryFlow: sidebarFlow }
|
|
1111
|
+
});
|
|
1112
|
+
const pickerInjected = () => ({
|
|
1113
|
+
createWorkspace,
|
|
1114
|
+
hooks: { directoryFlow: pickerFlow }
|
|
1115
|
+
});
|
|
1116
|
+
ctx.slots.inject("sidebar.workspaces", () => ctx.slots.register({
|
|
1117
|
+
name: "sidebar.workspaces",
|
|
1118
|
+
children: { "sidebar.workspaces.directoryFlow": {
|
|
1119
|
+
kind: "single",
|
|
1120
|
+
scope: "root"
|
|
1121
|
+
} },
|
|
1122
|
+
inject: sidebarInjected,
|
|
1123
|
+
locale: NS
|
|
1124
|
+
}, WorkspaceSidebar));
|
|
1125
|
+
ctx.slots.inject("conversation.hero.workspace", () => ctx.slots.register({
|
|
1126
|
+
name: "conversation.hero.workspace",
|
|
1127
|
+
children: { "conversation.hero.workspace.directoryFlow": {
|
|
1128
|
+
kind: "single",
|
|
1129
|
+
scope: "root"
|
|
1130
|
+
} },
|
|
1131
|
+
inject: pickerInjected,
|
|
1132
|
+
locale: NS
|
|
1133
|
+
}, WorkspacePicker));
|
|
1134
|
+
}
|
|
1135
|
+
//#endregion
|
|
1136
|
+
exports.apply = apply;
|
|
1137
|
+
exports.inject = inject;
|
|
1138
|
+
return module.exports;
|
|
1139
|
+
}
|
|
1140
|
+
});
|
|
1141
|
+
|
|
1142
|
+
//# sourceMappingURL=client.js.map
|