@deepseek-ai/dsh-client-ui-workspace 0.1.5-rc.2 → 0.1.6-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +15 -7
- package/README.zh.md +18 -10
- package/lib/client.js +609 -462
- package/lib/types/client/contract/slots.d.ts +0 -6
- package/lib/types/client/index.d.ts +5 -0
- package/lib/types/client/locales.d.ts +2 -0
- package/lib/types/client/navigation.d.ts +14 -4
- package/lib/types/client/rows/Rows.d.ts +5 -3
- package/lib/types/client/rows/WorkspaceBrowser.d.ts +1 -1
- package/lib/types/client/stores.d.ts +8 -10
- package/lib/types/client/tree.d.ts +52 -18
- package/package.json +20 -19
package/lib/client.js
CHANGED
|
@@ -28,6 +28,8 @@ window.__ModuleLoader__.load({
|
|
|
28
28
|
sessions;
|
|
29
29
|
connecting = /* @__PURE__ */ new Map();
|
|
30
30
|
lifetime = new AbortController();
|
|
31
|
+
selection = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)({}, { persist: { name: "dsh.sessions.current" } });
|
|
32
|
+
mainReference;
|
|
31
33
|
/**
|
|
32
34
|
* @param ctx - Client root Context.
|
|
33
35
|
* @param directoryPicker - the directory-picking Remote namespace.
|
|
@@ -39,7 +41,16 @@ window.__ModuleLoader__.load({
|
|
|
39
41
|
this.directoryPicker = directoryPicker;
|
|
40
42
|
this.workspaces = workspaces;
|
|
41
43
|
this.sessions = sessions;
|
|
42
|
-
ctx.effect(() =>
|
|
44
|
+
ctx.effect(() => {
|
|
45
|
+
const stop = this.watchNavigation();
|
|
46
|
+
return () => {
|
|
47
|
+
stop();
|
|
48
|
+
this.lifetime.abort();
|
|
49
|
+
const reference = this.mainReference;
|
|
50
|
+
this.mainReference = void 0;
|
|
51
|
+
reference?.release();
|
|
52
|
+
};
|
|
53
|
+
}, "ui-workspace: Workspace navigation policy");
|
|
43
54
|
}
|
|
44
55
|
async connectWorkspace(workspaceId) {
|
|
45
56
|
const workspace = this.workspaces.list.getSnapshot().items.find((item) => item.workspaceId === workspaceId);
|
|
@@ -58,17 +69,14 @@ window.__ModuleLoader__.load({
|
|
|
58
69
|
this.connecting.set(workspaceId, attempt);
|
|
59
70
|
return attempt;
|
|
60
71
|
}
|
|
61
|
-
openSession(
|
|
62
|
-
this.
|
|
63
|
-
this.ctx.layout.selectPanel(null);
|
|
72
|
+
openSession(target) {
|
|
73
|
+
this.replaceMain(target, this.lifetime.signal);
|
|
64
74
|
}
|
|
65
75
|
async openWorkspace(workspaceId, beforeOpen) {
|
|
66
76
|
const navigation = AbortSignal.any([this.ctx.layout.beginNavigation(), this.lifetime.signal]);
|
|
67
|
-
const isCurrent = () => !navigation.aborted;
|
|
68
77
|
const sessionId = await this.connectWorkspace(workspaceId);
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
if (isCurrent()) this.openSession(sessionId);
|
|
78
|
+
if (navigation.aborted) return;
|
|
79
|
+
this.replaceMain(sessionId, navigation, beforeOpen);
|
|
72
80
|
}
|
|
73
81
|
async forkSession(sessionId) {
|
|
74
82
|
const navigation = AbortSignal.any([this.ctx.layout.beginNavigation(), this.lifetime.signal]);
|
|
@@ -76,18 +84,17 @@ window.__ModuleLoader__.load({
|
|
|
76
84
|
sessionId,
|
|
77
85
|
increaseTitle: true
|
|
78
86
|
});
|
|
79
|
-
if (!navigation.aborted) this.
|
|
87
|
+
if (!navigation.aborted) this.replaceMain(childId, navigation);
|
|
80
88
|
}
|
|
81
89
|
startSession(workspaceId) {
|
|
82
90
|
const workspace = this.workspaces.list.getSnapshot();
|
|
83
91
|
const sessions = this.sessions.list.getSnapshot();
|
|
84
|
-
const current =
|
|
92
|
+
const current = this.mainReference?.sessionId;
|
|
85
93
|
const currentWorkspaceId = current === void 0 ? void 0 : workspace.items.find((item) => item.sessionIds.includes(current))?.workspaceId;
|
|
86
94
|
const recent = workspace.phase === "ready" && sessions.phase === "ready" ? recentWorkspace(workspace.items, sessions.byId) : void 0;
|
|
87
95
|
const target = workspaceId ?? currentWorkspaceId ?? recent;
|
|
88
96
|
if (target === void 0) {
|
|
89
|
-
this.
|
|
90
|
-
this.ctx.layout.selectPanel(null);
|
|
97
|
+
this.clearMain();
|
|
91
98
|
return;
|
|
92
99
|
}
|
|
93
100
|
this.openWorkspace(target).catch((reason) => {
|
|
@@ -96,6 +103,10 @@ window.__ModuleLoader__.load({
|
|
|
96
103
|
}
|
|
97
104
|
async archiveSession(sessionId) {
|
|
98
105
|
await this.workspaces.archiveSession(sessionId);
|
|
106
|
+
if (this.mainReference?.sessionId === sessionId) this.clearMain();
|
|
107
|
+
}
|
|
108
|
+
async unarchiveSession(sessionId) {
|
|
109
|
+
await this.workspaces.unarchiveSession(sessionId);
|
|
99
110
|
}
|
|
100
111
|
async pickDirectory() {
|
|
101
112
|
const result = await this.directoryPicker.pick();
|
|
@@ -121,10 +132,24 @@ window.__ModuleLoader__.load({
|
|
|
121
132
|
const workspace = this.workspaces.list.getSnapshot();
|
|
122
133
|
const sessions = this.sessions.list.getSnapshot();
|
|
123
134
|
if (workspace.phase !== "ready" || sessions.phase !== "ready") return;
|
|
124
|
-
if (
|
|
135
|
+
if (this.mainReference !== void 0) {
|
|
125
136
|
initial = "done";
|
|
126
137
|
return;
|
|
127
138
|
}
|
|
139
|
+
const saved = this.selection.getSnapshot();
|
|
140
|
+
const savedTarget = saved.subagentAddress ?? (saved.sessionId !== void 0 && sessions.byId[saved.sessionId] !== void 0 ? saved.sessionId : void 0);
|
|
141
|
+
if (savedTarget !== void 0) {
|
|
142
|
+
initial = "connecting";
|
|
143
|
+
try {
|
|
144
|
+
if (saved.subagentAddress !== void 0) this.sessions.refreshSubagents(saved.subagentAddress.parentSessionId);
|
|
145
|
+
this.openSession(savedTarget);
|
|
146
|
+
initial = "done";
|
|
147
|
+
} catch (reason) {
|
|
148
|
+
initial = "waiting";
|
|
149
|
+
console.warn("initial Session restoration failed:", reason);
|
|
150
|
+
}
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
128
153
|
const target = recentWorkspace(workspace.items, sessions.byId);
|
|
129
154
|
if (target === void 0) {
|
|
130
155
|
initial = "done";
|
|
@@ -132,8 +157,8 @@ window.__ModuleLoader__.load({
|
|
|
132
157
|
}
|
|
133
158
|
initial = "connecting";
|
|
134
159
|
this.connectWorkspace(target).then((sessionId) => {
|
|
135
|
-
if (this.
|
|
136
|
-
|
|
160
|
+
if (this.mainReference === void 0) this.openSession(sessionId);
|
|
161
|
+
}).then(() => {
|
|
137
162
|
initial = "done";
|
|
138
163
|
}, (reason) => {
|
|
139
164
|
if (this.lifetime.signal.aborted) return;
|
|
@@ -152,11 +177,43 @@ window.__ModuleLoader__.load({
|
|
|
152
177
|
}
|
|
153
178
|
/** @returns true when an archived current selection was cleared. */
|
|
154
179
|
clearArchivedCurrent() {
|
|
155
|
-
const current = this.
|
|
180
|
+
const current = this.mainReference?.sessionId;
|
|
156
181
|
if (current === void 0 || !this.workspaces.list.getSnapshot().archivedSessionIds.includes(current)) return false;
|
|
157
|
-
this.
|
|
182
|
+
this.clearMain();
|
|
158
183
|
return true;
|
|
159
184
|
}
|
|
185
|
+
clearMain() {
|
|
186
|
+
const previous = this.mainReference;
|
|
187
|
+
this.mainReference = void 0;
|
|
188
|
+
this.selection.set({});
|
|
189
|
+
previous?.release();
|
|
190
|
+
this.ctx.layout.selectPanel(null);
|
|
191
|
+
}
|
|
192
|
+
replaceMain(target, signal, beforeOpen) {
|
|
193
|
+
signal.throwIfAborted();
|
|
194
|
+
const reference = this.sessions.retain(target, { source: "mainView" });
|
|
195
|
+
try {
|
|
196
|
+
signal.throwIfAborted();
|
|
197
|
+
beforeOpen?.(reference.sessionId);
|
|
198
|
+
if (signal.aborted) {
|
|
199
|
+
reference.release();
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const subagentAddress = typeof target === "string" ? this.sessions.subagentAddress(reference.sessionId) : target;
|
|
203
|
+
this.selection.set({
|
|
204
|
+
sessionId: reference.sessionId,
|
|
205
|
+
...subagentAddress === void 0 ? {} : { subagentAddress }
|
|
206
|
+
});
|
|
207
|
+
} catch (error) {
|
|
208
|
+
reference.release();
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
const previous = this.mainReference;
|
|
212
|
+
this.mainReference = reference;
|
|
213
|
+
previous?.release();
|
|
214
|
+
this.sessions.refreshSubagents(reference.sessionId);
|
|
215
|
+
this.ctx.layout.selectPanel(null);
|
|
216
|
+
}
|
|
160
217
|
};
|
|
161
218
|
/** Stable tie-breaking follows Host Workspace order. */
|
|
162
219
|
function recentWorkspace(workspaces, sessions) {
|
|
@@ -187,6 +244,10 @@ window.__ModuleLoader__.load({
|
|
|
187
244
|
*/
|
|
188
245
|
/** Browser-local order account for the hierarchy-free flat Session list. */
|
|
189
246
|
const FLAT_SESSION_ORDER_KEY = "__flat_session_order__";
|
|
247
|
+
/** Copy read-only projections into the persisted mutable store representation. */
|
|
248
|
+
function copySessionOrders(orders) {
|
|
249
|
+
return Object.fromEntries(Object.entries(orders).map(([key, order]) => [key, [...order]]));
|
|
250
|
+
}
|
|
190
251
|
/**
|
|
191
252
|
* Create the workspace browser viewing store handle.
|
|
192
253
|
* @returns the store handle (spec + type + identity + factory in one).
|
|
@@ -197,15 +258,16 @@ window.__ModuleLoader__.load({
|
|
|
197
258
|
groupBy: "workspace",
|
|
198
259
|
orderBy: "updated",
|
|
199
260
|
groupExpansion: {},
|
|
200
|
-
sessionOrderByAccount: {}
|
|
201
|
-
sessionUpdatedAtByAccount: {}
|
|
261
|
+
sessionOrderByAccount: {}
|
|
202
262
|
}),
|
|
203
263
|
persist: "dsh.workspace.view.v5",
|
|
204
264
|
actions: {
|
|
205
265
|
setGroupBy: (d, mode) => {
|
|
206
266
|
d.groupBy = mode;
|
|
207
267
|
},
|
|
208
|
-
setOrderBy: (d, mode) => {
|
|
268
|
+
setOrderBy: (d, mode, initialOrders) => {
|
|
269
|
+
if (mode === d.orderBy) return;
|
|
270
|
+
d.sessionOrderByAccount = mode === "manual" ? copySessionOrders(initialOrders) : {};
|
|
209
271
|
d.orderBy = mode;
|
|
210
272
|
},
|
|
211
273
|
setGroupExpanded: (d, key, expanded) => {
|
|
@@ -215,14 +277,16 @@ window.__ModuleLoader__.load({
|
|
|
215
277
|
const retained = new Set(workspaceKeys);
|
|
216
278
|
d.groupExpansion = Object.fromEntries(Object.entries(d.groupExpansion).filter(([key]) => retained.has(key)));
|
|
217
279
|
d.sessionOrderByAccount = Object.fromEntries(Object.entries(d.sessionOrderByAccount).filter(([key]) => retained.has(key)));
|
|
218
|
-
|
|
280
|
+
delete d.sessionUpdatedAtByAccount;
|
|
219
281
|
},
|
|
220
|
-
|
|
221
|
-
d.
|
|
222
|
-
d.
|
|
282
|
+
syncSessionOrders: (d, orders) => {
|
|
283
|
+
if (d.orderBy !== "manual") return;
|
|
284
|
+
Object.assign(d.sessionOrderByAccount, copySessionOrders(orders));
|
|
223
285
|
},
|
|
224
|
-
setSessionOrder: (d, accountKey, order) => {
|
|
225
|
-
d.sessionOrderByAccount
|
|
286
|
+
setSessionOrder: (d, accountKey, order, initialOrders) => {
|
|
287
|
+
if (d.orderBy === "updated") d.sessionOrderByAccount = copySessionOrders(initialOrders);
|
|
288
|
+
d.orderBy = "manual";
|
|
289
|
+
d.sessionOrderByAccount[accountKey] = [...order];
|
|
226
290
|
}
|
|
227
291
|
}
|
|
228
292
|
});
|
|
@@ -313,6 +377,9 @@ window.__ModuleLoader__.load({
|
|
|
313
377
|
function owningGroupKey(workspaces, sessionId) {
|
|
314
378
|
return workspaces.find((workspace) => workspace.sessionIds.includes(sessionId))?.workspaceId ?? "";
|
|
315
379
|
}
|
|
380
|
+
function mainSessionId(list) {
|
|
381
|
+
return Object.values(list.byId).find((session) => (session.retainedBy.mainView ?? 0) > 0)?.id;
|
|
382
|
+
}
|
|
316
383
|
/**
|
|
317
384
|
* Directory display label: basename of the path (both separators accepted).
|
|
318
385
|
* Ungrouped-bucket fallback for surfaces without a workspace title.
|
|
@@ -324,10 +391,57 @@ window.__ModuleLoader__.load({
|
|
|
324
391
|
const base = workspaceTitleOf(cwd);
|
|
325
392
|
return base !== "" ? base : cwd;
|
|
326
393
|
}
|
|
327
|
-
/**
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
394
|
+
/**
|
|
395
|
+
* Project known account members by current Session recency.
|
|
396
|
+
* @param sessionIds - authoritative account membership.
|
|
397
|
+
* @param summaries - current Session summaries; members without a summary are omitted until it arrives.
|
|
398
|
+
* @returns known members newest first, with Session identity as the deterministic tie-break.
|
|
399
|
+
*/
|
|
400
|
+
function orderByRecency(sessionIds, summaries) {
|
|
401
|
+
return sessionIds.flatMap((id) => {
|
|
402
|
+
const summary = summaries[id];
|
|
403
|
+
return summary === void 0 ? [] : [{
|
|
404
|
+
id,
|
|
405
|
+
updatedAt: summary.updatedAt
|
|
406
|
+
}];
|
|
407
|
+
}).sort((a, b) => {
|
|
408
|
+
if (a.updatedAt !== b.updatedAt) return b.updatedAt - a.updatedAt;
|
|
409
|
+
return a.id < b.id ? -1 : 1;
|
|
410
|
+
}).map((member) => member.id);
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Reconcile a browser-local manual order with current account membership.
|
|
414
|
+
* @param memberIds - authoritative account membership.
|
|
415
|
+
* @param savedOrder - previously saved browser-local order.
|
|
416
|
+
* @param summaries - current Session summaries used to append newly known members by recency.
|
|
417
|
+
* @returns retained saved slots followed by newly known members; departed members and unknown new members are omitted.
|
|
418
|
+
*/
|
|
419
|
+
function reconcileManualOrder(memberIds, savedOrder, summaries) {
|
|
420
|
+
const members = new Map(memberIds.map((id) => [id, id]));
|
|
421
|
+
const included = /* @__PURE__ */ new Set();
|
|
422
|
+
const ordered = [];
|
|
423
|
+
for (const key of savedOrder ?? []) {
|
|
424
|
+
const id = members.get(key);
|
|
425
|
+
if (id === void 0 || included.has(key)) continue;
|
|
426
|
+
ordered.push(id);
|
|
427
|
+
included.add(key);
|
|
428
|
+
}
|
|
429
|
+
for (const id of orderByRecency(memberIds, summaries)) {
|
|
430
|
+
if (included.has(id)) continue;
|
|
431
|
+
ordered.push(id);
|
|
432
|
+
included.add(id);
|
|
433
|
+
}
|
|
434
|
+
return ordered;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Keep the selected provisional New Session ahead of either base order.
|
|
438
|
+
* @param order - recency or reconciled manual order.
|
|
439
|
+
* @param currentBlank - selected blank Session in this account, when present.
|
|
440
|
+
* @returns a copy with the selected blank first and no duplicate slot.
|
|
441
|
+
*/
|
|
442
|
+
function pinCurrentBlank(order, currentBlank) {
|
|
443
|
+
if (currentBlank === void 0) return [...order];
|
|
444
|
+
return [currentBlank, ...order.filter((id) => id !== currentBlank)];
|
|
331
445
|
}
|
|
332
446
|
/**
|
|
333
447
|
* Ordinary sessions are visible; among blank sessions, only the current one
|
|
@@ -351,42 +465,33 @@ window.__ModuleLoader__.load({
|
|
|
351
465
|
return (session.projectionValues?.schedule?.length ?? 0) > 0;
|
|
352
466
|
}
|
|
353
467
|
/** Build one group without projecting session lineage into presentation. */
|
|
354
|
-
function buildGroup(key, workspaceId, cwd, createdAt, label, members
|
|
355
|
-
const sessions = [...members];
|
|
356
|
-
if (order === "recency") sessions.sort(byRecency);
|
|
468
|
+
function buildGroup(key, workspaceId, cwd, createdAt, label, members) {
|
|
357
469
|
return {
|
|
358
470
|
key,
|
|
359
471
|
workspaceId,
|
|
360
472
|
cwd,
|
|
361
473
|
createdAt,
|
|
362
474
|
label,
|
|
363
|
-
sessions
|
|
475
|
+
sessions: [...members]
|
|
364
476
|
};
|
|
365
477
|
}
|
|
366
478
|
/** Apply a stored Ungrouped order and append newly loose Sessions by recency. */
|
|
367
|
-
function orderedUngrouped(members, stored) {
|
|
479
|
+
function orderedUngrouped(members, stored, summaries) {
|
|
368
480
|
const byId = new Map(members.map((session) => [session.id, session]));
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
ordered.push(session);
|
|
375
|
-
included.add(key);
|
|
376
|
-
}
|
|
377
|
-
for (const session of [...members].sort(byRecency)) {
|
|
378
|
-
if (included.has(session.id)) continue;
|
|
379
|
-
ordered.push(session);
|
|
380
|
-
}
|
|
381
|
-
return ordered;
|
|
481
|
+
return (stored === void 0 ? orderByRecency(members.map((session) => session.id), summaries) : reconcileManualOrder(members.map((session) => session.id), stored, summaries)).flatMap((id) => {
|
|
482
|
+
const session = byId.get(id);
|
|
483
|
+
/* v8 ignore next -- ids are projected exclusively from the members used to build byId. */
|
|
484
|
+
return session === void 0 ? [] : [session];
|
|
485
|
+
});
|
|
382
486
|
}
|
|
383
487
|
/**
|
|
384
|
-
* Group Sessions by
|
|
385
|
-
*
|
|
386
|
-
*
|
|
387
|
-
*
|
|
488
|
+
* Group Sessions by Workspace: one group per caller-ordered entity, with
|
|
489
|
+
* members resolved from caller-ordered sessionIds. Sessions outside every
|
|
490
|
+
* Workspace trail in the browser-local Ungrouped order, which falls back to
|
|
491
|
+
* recency before that order is initialized.
|
|
388
492
|
*/
|
|
389
493
|
function groupByWorkspace(list, workspaces, archived, ungroupedOrder) {
|
|
494
|
+
const current = mainSessionId(list);
|
|
390
495
|
const groups = [];
|
|
391
496
|
const accounted = /* @__PURE__ */ new Set();
|
|
392
497
|
for (const workspace of workspaces) {
|
|
@@ -395,13 +500,13 @@ window.__ModuleLoader__.load({
|
|
|
395
500
|
const summary = list.byId[id];
|
|
396
501
|
if (summary === void 0) continue;
|
|
397
502
|
accounted.add(id);
|
|
398
|
-
if (!sessionVisible(summary,
|
|
503
|
+
if (!sessionVisible(summary, current, archived)) continue;
|
|
399
504
|
members.push(summary);
|
|
400
505
|
}
|
|
401
|
-
groups.push(buildGroup(workspace.workspaceId, workspace.workspaceId, workspace.path, Date.parse(workspace.createdAt), workspace.title, members
|
|
506
|
+
groups.push(buildGroup(workspace.workspaceId, workspace.workspaceId, workspace.path, Date.parse(workspace.createdAt), workspace.title, members));
|
|
402
507
|
}
|
|
403
|
-
const stray = list.ids.map((id) => list.byId[id]).filter((s) => s !== void 0 && !accounted.has(s.id) && sessionVisible(s,
|
|
404
|
-
if (stray.length > 0) groups.push(buildGroup("", void 0, void 0, void 0, "",
|
|
508
|
+
const stray = list.ids.map((id) => list.byId[id]).filter((s) => s !== void 0 && !accounted.has(s.id) && sessionVisible(s, current, archived));
|
|
509
|
+
if (stray.length > 0) groups.push(buildGroup("", void 0, void 0, void 0, "", orderedUngrouped(stray, ungroupedOrder, list.byId)));
|
|
405
510
|
return groups;
|
|
406
511
|
}
|
|
407
512
|
/** Keep navigation presentation independent from domain-owned interaction objects. */
|
|
@@ -413,15 +518,16 @@ window.__ModuleLoader__.load({
|
|
|
413
518
|
default: return;
|
|
414
519
|
}
|
|
415
520
|
}
|
|
416
|
-
function sessionNode(s, descendants,
|
|
417
|
-
const
|
|
521
|
+
function sessionNode(s, descendants, statuses) {
|
|
522
|
+
const status = statuses.get(s.id);
|
|
523
|
+
const pendingInteraction = visiblePendingKind(status?.pendingInteraction?.kind);
|
|
418
524
|
return {
|
|
419
525
|
id: s.id,
|
|
420
526
|
title: sessionTitle(s),
|
|
421
527
|
blank: s.blank,
|
|
422
|
-
running: s.running,
|
|
528
|
+
running: status?.running ?? s.running,
|
|
423
529
|
runningSubagentCount: descendants.get(s.id)?.runningCount ?? 0,
|
|
424
|
-
completed:
|
|
530
|
+
completed: status?.completionUnread === true,
|
|
425
531
|
hasActiveSchedule: hasActiveSchedule(s),
|
|
426
532
|
updatedAt: s.updatedAt,
|
|
427
533
|
...pendingInteraction === void 0 ? {} : { pendingInteraction }
|
|
@@ -435,18 +541,19 @@ window.__ModuleLoader__.load({
|
|
|
435
541
|
* provisional New Session row; archived sessions are excluded everywhere.
|
|
436
542
|
* Content search lives outside this derivation
|
|
437
543
|
* (see {@link deriveSearchResults}).
|
|
438
|
-
* @param list - sessions list snapshot (`
|
|
439
|
-
* @param workspaces - real
|
|
544
|
+
* @param list - sessions list snapshot (`mainView` retention feeds containsCurrent).
|
|
545
|
+
* @param workspaces - real Workspaces in Host group order with caller-projected Session order.
|
|
440
546
|
* @param archivedSessionIds - registry-global archive set.
|
|
441
|
-
* @param
|
|
547
|
+
* @param statuses - unified UI status by Session.
|
|
442
548
|
* @param view - local expansion arrays.
|
|
443
549
|
* @returns group sections in render order.
|
|
444
550
|
*/
|
|
445
|
-
function deriveGroups(list, workspaces, archivedSessionIds,
|
|
551
|
+
function deriveGroups(list, workspaces, archivedSessionIds, statuses, view) {
|
|
446
552
|
const archived = new Set(archivedSessionIds);
|
|
447
553
|
const expandedGroups = new Set(view.expandedGroups);
|
|
448
554
|
const descendants = indexSubagentDescendants(list.byId);
|
|
449
|
-
const
|
|
555
|
+
const current = mainSessionId(list);
|
|
556
|
+
const currentGroup = current === void 0 ? void 0 : owningGroupKey(workspaces, current);
|
|
450
557
|
const groups = [];
|
|
451
558
|
for (const g of groupByWorkspace(list, workspaces, archived, view.ungroupedOrder)) {
|
|
452
559
|
const expanded = expandedGroups.has(g.key);
|
|
@@ -459,32 +566,35 @@ window.__ModuleLoader__.load({
|
|
|
459
566
|
sessionCount: g.sessions.length,
|
|
460
567
|
expanded,
|
|
461
568
|
containsCurrent: g.key === currentGroup,
|
|
462
|
-
sessions: expanded ? g.sessions.map((session) => sessionNode(session, descendants,
|
|
569
|
+
sessions: expanded ? g.sessions.map((session) => sessionNode(session, descendants, statuses)) : []
|
|
463
570
|
});
|
|
464
571
|
}
|
|
465
572
|
return groups;
|
|
466
573
|
}
|
|
467
574
|
/**
|
|
468
|
-
*
|
|
469
|
-
* children included — as a top-level row, strictly newest-first. No grouping,
|
|
470
|
-
* no parent/child adjacency. Content search lives outside this derivation
|
|
471
|
-
* (see {@link deriveSearchResults}).
|
|
575
|
+
* Select flat-list members without deriving row presentation or ordering.
|
|
472
576
|
* @param list - sessions list snapshot.
|
|
473
577
|
* @param archivedSessionIds - registry-global archive set.
|
|
474
|
-
* @
|
|
475
|
-
* @returns flat rows in render order.
|
|
578
|
+
* @returns known visible Session ids in list order, including ordinary forks and only the current blank.
|
|
476
579
|
*/
|
|
477
|
-
function
|
|
580
|
+
function visibleSessionIds(list, archivedSessionIds) {
|
|
478
581
|
const archived = new Set(archivedSessionIds);
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
for (const id of list.ids) {
|
|
582
|
+
const current = mainSessionId(list);
|
|
583
|
+
return list.ids.filter((id) => {
|
|
482
584
|
const s = list.byId[id];
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
585
|
+
return s !== void 0 && sessionVisible(s, current, archived);
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Derive flat rows from the browser's ordered visible Session ids.
|
|
590
|
+
* @param list - sessions list snapshot used to select the ids.
|
|
591
|
+
* @param sessionIds - known visible members in render order, including any pinned blank.
|
|
592
|
+
* @param statuses - unified UI status by Session.
|
|
593
|
+
* @returns flat rows in the supplied order with current status indicators.
|
|
594
|
+
*/
|
|
595
|
+
function deriveFlat(list, sessionIds, statuses) {
|
|
596
|
+
const descendants = indexSubagentDescendants(list.byId);
|
|
597
|
+
return sessionIds.map((id) => sessionNode(list.byId[id], descendants, statuses));
|
|
488
598
|
}
|
|
489
599
|
/**
|
|
490
600
|
* Merge immediate title/Workspace substring matches with ranked Host content
|
|
@@ -494,12 +604,12 @@ window.__ModuleLoader__.load({
|
|
|
494
604
|
* @param workspaces - Workspace membership and display labels.
|
|
495
605
|
* @param query - caller text; surrounding whitespace is ignored.
|
|
496
606
|
* @param archivedSessionIds - registry-global archive set (members never match).
|
|
497
|
-
* @param
|
|
607
|
+
* @param statuses - unified UI status by Session.
|
|
498
608
|
* @param content - ranked Host content-search page.
|
|
499
609
|
* @param limit - protocol-owned maximum merged row count.
|
|
500
610
|
* @returns bounded deduplicated flat rows and a refine-query hint bit.
|
|
501
611
|
*/
|
|
502
|
-
function deriveSearchResults(list, workspaces, query, archivedSessionIds,
|
|
612
|
+
function deriveSearchResults(list, workspaces, query, archivedSessionIds, statuses, content, limit) {
|
|
503
613
|
const q = query.trim().toLowerCase();
|
|
504
614
|
if (q === "") return {
|
|
505
615
|
items: [],
|
|
@@ -507,6 +617,7 @@ window.__ModuleLoader__.load({
|
|
|
507
617
|
};
|
|
508
618
|
const archived = new Set(archivedSessionIds);
|
|
509
619
|
const descendants = indexSubagentDescendants(list.byId);
|
|
620
|
+
const current = mainSessionId(list);
|
|
510
621
|
const workspaceBySession = /* @__PURE__ */ new Map();
|
|
511
622
|
for (const workspace of workspaces) for (const sessionId of workspace.sessionIds) if (!workspaceBySession.has(sessionId)) workspaceBySession.set(sessionId, workspace.title);
|
|
512
623
|
const labelOf = (summary) => workspaceBySession.get(summary.id) ?? workspaceLabel(summary.cwd);
|
|
@@ -515,10 +626,11 @@ window.__ModuleLoader__.load({
|
|
|
515
626
|
const local = [];
|
|
516
627
|
for (const id of list.ids) {
|
|
517
628
|
const summary = list.byId[id];
|
|
518
|
-
if (summary === void 0 || summary.blank || !sessionVisible(summary,
|
|
629
|
+
if (summary === void 0 || summary.blank || !sessionVisible(summary, current, archived)) continue;
|
|
519
630
|
if (sessionTitle(summary).toLowerCase().includes(q) || labelOf(summary).toLowerCase().includes(q)) local.push(summary);
|
|
520
631
|
}
|
|
521
|
-
local.
|
|
632
|
+
const localById = new Map(local.map((summary) => [summary.id, summary]));
|
|
633
|
+
const orderedLocal = orderByRecency(local.map((summary) => summary.id), list.byId).map((id) => localById.get(id));
|
|
522
634
|
const ordered = [];
|
|
523
635
|
const included = /* @__PURE__ */ new Set();
|
|
524
636
|
const include = (summary) => {
|
|
@@ -526,23 +638,24 @@ window.__ModuleLoader__.load({
|
|
|
526
638
|
included.add(summary.id);
|
|
527
639
|
ordered.push(summary);
|
|
528
640
|
};
|
|
529
|
-
for (const summary of
|
|
641
|
+
for (const summary of orderedLocal) include(summary);
|
|
530
642
|
for (const item of content.items) {
|
|
531
643
|
const summary = list.byId[item.sessionId];
|
|
532
|
-
if (summary !== void 0 && !summary.blank && sessionVisible(summary,
|
|
644
|
+
if (summary !== void 0 && !summary.blank && sessionVisible(summary, current, archived)) include(summary);
|
|
533
645
|
}
|
|
534
646
|
return {
|
|
535
647
|
items: ordered.slice(0, limit).map((summary) => {
|
|
536
648
|
const match = contentBySession.get(summary.id);
|
|
537
|
-
const
|
|
649
|
+
const status = statuses.get(summary.id);
|
|
650
|
+
const pendingInteraction = visiblePendingKind(status?.pendingInteraction?.kind);
|
|
538
651
|
return {
|
|
539
652
|
id: summary.id,
|
|
540
653
|
title: sessionTitle(summary),
|
|
541
654
|
workspace: labelOf(summary),
|
|
542
|
-
running: summary.running,
|
|
655
|
+
running: status?.running ?? summary.running,
|
|
543
656
|
runningSubagentCount: descendants.get(summary.id)?.runningCount ?? 0,
|
|
544
657
|
...pendingInteraction === void 0 ? {} : { pendingInteraction },
|
|
545
|
-
completed:
|
|
658
|
+
completed: status?.completionUnread === true,
|
|
546
659
|
hasActiveSchedule: hasActiveSchedule(summary),
|
|
547
660
|
...match === void 0 ? {} : { snippet: match.snippet }
|
|
548
661
|
};
|
|
@@ -550,9 +663,33 @@ window.__ModuleLoader__.load({
|
|
|
550
663
|
hasMore: content.hasMore || ordered.length > limit
|
|
551
664
|
};
|
|
552
665
|
}
|
|
666
|
+
/** Normalize separators for comparison without interpreting POSIX backslashes as separators. */
|
|
667
|
+
function folderPath(path) {
|
|
668
|
+
return (/^[A-Za-z]:[/\\]/.test(path) || path.startsWith("\\\\") ? path.replaceAll("\\", "/") : path).replace(/\/+$/, "");
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Find the nearest registered ancestor, excluding the Workspace directory itself.
|
|
672
|
+
* Paths use Host spelling; matching is case-sensitive, like Workspace identity.
|
|
673
|
+
* @param path - Workspace directory.
|
|
674
|
+
* @param parents - registered Workspace directory paths.
|
|
675
|
+
* @returns the owning parent path, or undefined when no parent contains the Workspace.
|
|
676
|
+
*/
|
|
677
|
+
function owningParentFolder(path, parents) {
|
|
678
|
+
const child = folderPath(path);
|
|
679
|
+
let owner;
|
|
680
|
+
let length = -1;
|
|
681
|
+
for (const parent of parents) {
|
|
682
|
+
const root = folderPath(parent);
|
|
683
|
+
if (root.length > length && child !== root && child.startsWith(`${root}/`)) {
|
|
684
|
+
owner = parent;
|
|
685
|
+
length = root.length;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return owner;
|
|
689
|
+
}
|
|
553
690
|
//#endregion
|
|
554
691
|
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-workspace/src/client/rows/Rows.module.css.mjs
|
|
555
|
-
const css$2 = ".YDXeBa_projectRow,.YDXeBa_sessionRow{cursor:pointer;user-select:none;color:var(--dsw-alias-label-primary);border-radius:8px;align-items:center;gap:6px;padding:
|
|
692
|
+
const css$2 = ".YDXeBa_projectRow,.YDXeBa_sessionRow{padding:0 8px;cursor:pointer;user-select:none;color:var(--dsw-alias-label-primary);border-radius:8px;align-items:center;gap:6px;padding-inline-start:calc(8px + var(--dsh-workspace-indent,0px));display:flex}.YDXeBa_projectRow:hover,.YDXeBa_sessionRow:hover,.YDXeBa_sessionRow.YDXeBa_selected{background:var(--dsw-alias-interactive-bg-hover)}.YDXeBa_searchResultRow{box-sizing:border-box;cursor:pointer;text-align:left;width:100%;min-height:48px;color:var(--dsw-alias-label-primary);background:0 0;border:none;border-radius:8px;flex-direction:column;align-items:stretch;padding:4px 8px;display:flex}.YDXeBa_searchResultRow:hover,.YDXeBa_searchResultRow.YDXeBa_selected{background:var(--dsw-alias-interactive-bg-hover)}.YDXeBa_searchResultHeading{align-items:center;min-width:0;display:flex}.YDXeBa_searchResultTitle{text-overflow:ellipsis;white-space:nowrap;flex:0 auto;min-width:0;margin-left:4px;font-size:14px;line-height:20px;overflow:hidden}.YDXeBa_searchResultMeta{align-items:center;gap:6px;min-width:0;margin-left:20px;display:flex}.YDXeBa_searchResultWorkspace,.YDXeBa_searchResultSnippet{text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:17px;overflow:hidden}.YDXeBa_searchResultWorkspace{max-width:40%;color:var(--dsw-alias-label-tertiary);flex:none}.YDXeBa_searchResultSnippet{min-width:0;color:var(--dsw-alias-label-secondary);flex:1}.YDXeBa_projectRow{box-sizing:border-box;align-items:center;height:34px}.YDXeBa_projectRow .YDXeBa_rowActions{height:20px}.YDXeBa_sessionRow{height:32px;animation:YDXeBa_row-in .15s var(--ds-ease-in-out);gap:0}.YDXeBa_sessionRow .YDXeBa_title{margin:0 6px 0 4px}.YDXeBa_flatSessionRowWithoutStatus .YDXeBa_title{margin-left:0}@keyframes YDXeBa_row-in{0%{opacity:0}}.YDXeBa_slot{width:16px;height:20px;color:var(--dsw-alias-label-tertiary);flex:none;justify-content:center;align-items:center;display:inline-flex}.YDXeBa_visuallyHidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}.YDXeBa_folderActive{color:var(--dsw-alias-state-business-primary)}.YDXeBa_projectRow .YDXeBa_chevron{display:none}.YDXeBa_projectRow:hover .YDXeBa_chevron{display:inline-flex}.YDXeBa_projectRow:hover .YDXeBa_folder{display:none}.YDXeBa_arrow{transition:transform .15s var(--ds-ease-in-out)}.YDXeBa_arrowOpen{transform:rotate(90deg)}.YDXeBa_projectText{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.YDXeBa_title{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:14px;line-height:20px;overflow:hidden}.YDXeBa_renameInput{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-button-elevated-fill);min-width:0;color:inherit;border-radius:4px;outline:none;padding:0 2px;font-size:14px;line-height:20px}.YDXeBa_sessionRow .YDXeBa_title{scroll-behavior:smooth;flex:1}@media (hover:hover){.YDXeBa_sessionRow:hover .YDXeBa_title{text-overflow:clip}}.YDXeBa_meta{text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:20px;overflow:hidden}.YDXeBa_time{color:var(--dsw-alias-label-tertiary);flex:none;font-size:12px;line-height:20px}.YDXeBa_scheduleIndicator{width:16px;height:20px;color:var(--dsw-alias-label-tertiary);flex:none;justify-content:center;align-items:center;margin-right:6px;display:inline-flex}.YDXeBa_searchScheduleIndicator{margin-left:4px;margin-right:0}.YDXeBa_dot{flex:none}.YDXeBa_rowActions{flex:none;align-items:center;gap:12px;display:none}.YDXeBa_projectRow:hover .YDXeBa_rowActions,.YDXeBa_sessionRow:hover .YDXeBa_rowActions,.YDXeBa_projectRow.YDXeBa_menuOpen .YDXeBa_rowActions,.YDXeBa_sessionRow.YDXeBa_menuOpen .YDXeBa_rowActions{display:inline-flex}.YDXeBa_sessionRow:hover .YDXeBa_time,.YDXeBa_sessionRow.YDXeBa_menuOpen .YDXeBa_time{display:none}.YDXeBa_projectRow.YDXeBa_menuOpen,.YDXeBa_sessionRow.YDXeBa_menuOpen{background:var(--dsw-alias-interactive-bg-hover)}.YDXeBa_sessionRow.YDXeBa_dropBefore,.YDXeBa_sessionRow.YDXeBa_dropAfter{position:relative}.YDXeBa_sessionRow.YDXeBa_dropBefore:before,.YDXeBa_sessionRow.YDXeBa_dropAfter:after{content:\"\";z-index:1;background:linear-gradient(55deg, transparent calc(50% - 1px), var(--dsw-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 0 / 5px 7px no-repeat, linear-gradient(125deg, transparent calc(50% - 1px), var(--dsw-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 5px / 5px 7px no-repeat, linear-gradient(var(--dsw-alias-state-business-primary) 0 0) 4px 5px / calc(100% - 4px) 2px no-repeat;pointer-events:none;height:12px;position:absolute;left:0;right:4px}.YDXeBa_sessionRow.YDXeBa_dropBefore:before{top:-7px}.YDXeBa_sessionRow.YDXeBa_dropAfter:after{bottom:-7px}.YDXeBa_hoverContent{flex-direction:column;gap:8px;display:flex}.YDXeBa_hoverTitle{color:#fff;overflow-wrap:break-word;font-size:14px;line-height:20px}.YDXeBa_hoverPath{color:#cfd3d6;word-break:break-all;font-size:12px;line-height:16px}.YDXeBa_hoverTime{color:#cfd3d6;font-size:12px;line-height:16px}.YDXeBa_hoverStatus{color:#adb2b8;align-items:center;gap:8px;font-size:12px;line-height:20px;display:flex}.YDXeBa_iconButton{cursor:pointer;width:16px;height:16px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:4px;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.YDXeBa_iconButton:hover{color:var(--dsw-alias-label-primary)}.YDXeBa_chevron{color:var(--dsw-alias-label-caption)}@media (prefers-reduced-motion:reduce){.YDXeBa_sessionRow,.YDXeBa_arrow{transition:none;animation:none}.YDXeBa_sessionRow .YDXeBa_title{scroll-behavior:auto}}";
|
|
556
693
|
const tagId$2 = "@deepseek-ai/dsh-client-ui-workspace/Rows.module.css";
|
|
557
694
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
|
|
558
695
|
const tag = document.createElement("style");
|
|
@@ -604,14 +741,39 @@ window.__ModuleLoader__.load({
|
|
|
604
741
|
/**
|
|
605
742
|
* Workspace browser tree row components (figma Cell set 14:3080): pure presentational —
|
|
606
743
|
* all data and callbacks arrive via props. Hover swaps (folder->chevron,
|
|
607
|
-
* time->ellipsis, action buttons) are CSS-only
|
|
608
|
-
*
|
|
609
|
-
*
|
|
744
|
+
* time->ellipsis, action buttons) are CSS-only, and a session row's clipped
|
|
745
|
+
* title is scrolled programmatically while the row is hovered. Row ... menus are
|
|
746
|
+
* visual-only except workspace Rename/Delete and session Rename/Fork/Archive; the
|
|
747
|
+
* session and workspace hover cards are suppressed while a menu is open.
|
|
610
748
|
*/
|
|
611
749
|
/** Row display title: blank rows show the localized New Session label. */
|
|
612
750
|
function displayTitle(node, t) {
|
|
613
751
|
return node.blank ? t("session.new") : node.title;
|
|
614
752
|
}
|
|
753
|
+
/**
|
|
754
|
+
* Reveal a title wider than its one-line cell while its row is hovered: the
|
|
755
|
+
* title clips its own text, so the far edge (a fork's incremented title, for
|
|
756
|
+
* example) is reachable by scrolling the element to its end. Leaving returns it
|
|
757
|
+
* to the start in one step, because the resting ellipsis and the narrowed cell
|
|
758
|
+
* would otherwise meet the text while it travelled back. A title that fits has
|
|
759
|
+
* no scroll range to move, and the stylesheet decides whether either move
|
|
760
|
+
* glides or jumps.
|
|
761
|
+
* @param title - the row's clipping title element.
|
|
762
|
+
* @param revealed - whether the pointer is on the row.
|
|
763
|
+
*/
|
|
764
|
+
function revealClippedTitle(title, revealed) {
|
|
765
|
+
/* v8 ignore next -- defensive: the title span renders unconditionally. */
|
|
766
|
+
if (title === null) return;
|
|
767
|
+
if (revealed) {
|
|
768
|
+
title.scrollLeft = title.scrollWidth - title.clientWidth;
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
if (typeof title.scrollTo === "function") title.scrollTo({
|
|
772
|
+
left: 0,
|
|
773
|
+
behavior: "instant"
|
|
774
|
+
});
|
|
775
|
+
else title.scrollLeft = 0;
|
|
776
|
+
}
|
|
615
777
|
/** Localized compact relative time ("刚刚"/"5分钟" in zh, "now"/"5min" in en). */
|
|
616
778
|
function timeLabel(updatedAt, now, t) {
|
|
617
779
|
const { unit, n } = (0, _deepseek_ai_dsh_client_ui_primitives.relativeTime)(updatedAt, now);
|
|
@@ -667,6 +829,7 @@ window.__ModuleLoader__.load({
|
|
|
667
829
|
* Workspace shows its hover card (the ungrouped bucket has none).
|
|
668
830
|
* `containsCurrent` arrives on the node (derivation fact, no renderer scan).
|
|
669
831
|
* @param props.group - derived group node.
|
|
832
|
+
* @param props.containsCurrentDescendant - highlight an ancestor even when its subtree is collapsed.
|
|
670
833
|
* @param props.onToggle - expand/collapse the group.
|
|
671
834
|
* @param props.onCreate - start a frontend Session inside this Workspace.
|
|
672
835
|
* @param props.drag - optional workspace-row drag wiring.
|
|
@@ -674,10 +837,10 @@ window.__ModuleLoader__.load({
|
|
|
674
837
|
* @param props.t - the browser root's locale seat.
|
|
675
838
|
* @returns the row element.
|
|
676
839
|
*/
|
|
677
|
-
function ProjectRowItem({ group, onToggle, onCreate, actions, drag, home, t }) {
|
|
840
|
+
function ProjectRowItem({ group, containsCurrentDescendant = false, onToggle, onCreate, actions, drag, home, t }) {
|
|
678
841
|
const row = group;
|
|
679
842
|
const label = row.workspaceId === void 0 ? t("group.ungrouped") : row.label;
|
|
680
|
-
const active = group.expanded && group.containsCurrent;
|
|
843
|
+
const active = containsCurrentDescendant || group.expanded && group.containsCurrent;
|
|
681
844
|
const [menuOpen, setMenuOpen] = (0, react.useState)(false);
|
|
682
845
|
const workspaceMenuItems = [{
|
|
683
846
|
id: "rename",
|
|
@@ -927,7 +1090,7 @@ window.__ModuleLoader__.load({
|
|
|
927
1090
|
* @param props.onFork - fork a session at its last completed turn.
|
|
928
1091
|
* @param props.onArchive - archive a session by id.
|
|
929
1092
|
* @param props.onReveal - scroll this row into view after search navigation, then acknowledge it.
|
|
930
|
-
* @param props.drag - optional
|
|
1093
|
+
* @param props.drag - optional row-drag target wiring; blank rows cannot start a drag.
|
|
931
1094
|
* @param props.flat - omit the empty status slot in the hierarchy-free flat list.
|
|
932
1095
|
* @param props.t - the browser root's locale seat.
|
|
933
1096
|
* @returns the session row.
|
|
@@ -938,8 +1101,10 @@ window.__ModuleLoader__.load({
|
|
|
938
1101
|
const selected = node.id === currentId;
|
|
939
1102
|
const statuses = sessionStatuses(node, t);
|
|
940
1103
|
const showStatus = statuses[0].state !== "done" || row.completed;
|
|
1104
|
+
const draggable = drag !== void 0 && !row.blank;
|
|
941
1105
|
const [menuOpen, setMenuOpen] = (0, react.useState)(false);
|
|
942
1106
|
const rowRef = (0, react.useRef)(null);
|
|
1107
|
+
const titleRef = (0, react.useRef)(null);
|
|
943
1108
|
(0, react.useEffect)(() => {
|
|
944
1109
|
if (onReveal === void 0) return;
|
|
945
1110
|
rowRef.current?.scrollIntoView({ block: "nearest" });
|
|
@@ -971,13 +1136,19 @@ window.__ModuleLoader__.load({
|
|
|
971
1136
|
onClick: () => {
|
|
972
1137
|
onOpen(node.id);
|
|
973
1138
|
},
|
|
974
|
-
|
|
975
|
-
|
|
1139
|
+
onPointerEnter: () => {
|
|
1140
|
+
revealClippedTitle(titleRef.current, true);
|
|
1141
|
+
},
|
|
1142
|
+
onPointerLeave: () => {
|
|
1143
|
+
revealClippedTitle(titleRef.current, false);
|
|
1144
|
+
},
|
|
1145
|
+
draggable,
|
|
1146
|
+
onDragStart: drag === void 0 || row.blank ? void 0 : (e) => {
|
|
976
1147
|
e.dataTransfer.effectAllowed = "move";
|
|
977
1148
|
e.dataTransfer.setData("text/plain", node.id);
|
|
978
1149
|
drag.start();
|
|
979
1150
|
},
|
|
980
|
-
onDragEnd: drag
|
|
1151
|
+
onDragEnd: drag === void 0 || row.blank ? void 0 : drag.end,
|
|
981
1152
|
onDragOver: drag === void 0 ? void 0 : (e) => {
|
|
982
1153
|
if (!drag.active) return;
|
|
983
1154
|
e.preventDefault();
|
|
@@ -995,6 +1166,7 @@ window.__ModuleLoader__.load({
|
|
|
995
1166
|
children: showStatus && (0, react_jsx_runtime.jsx)(SessionStatusDots, { statuses })
|
|
996
1167
|
}),
|
|
997
1168
|
(0, react_jsx_runtime.jsx)("span", {
|
|
1169
|
+
ref: titleRef,
|
|
998
1170
|
className: Rows_module_css_default.title,
|
|
999
1171
|
children: title
|
|
1000
1172
|
}),
|
|
@@ -1216,7 +1388,7 @@ window.__ModuleLoader__.load({
|
|
|
1216
1388
|
}
|
|
1217
1389
|
//#endregion
|
|
1218
1390
|
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-workspace/src/client/rows/WorkspaceBrowser.module.css.mjs
|
|
1219
|
-
const css = ".bhn1Oq_root{--dsh-session-list-edge-inset:var(--dsh-sidebar-inline-padding);--dsh-session-list-scrollbar-width:8px;--dsh-session-list-scrollbar-offset:2px;box-sizing:border-box;min-height:0;padding-right:var(--dsh-session-list-edge-inset);flex-direction:column;flex:1;display:flex}.bhn1Oq_root.bhn1Oq_rail{padding-right:0}.bhn1Oq_iconButton{corner-shape:round;cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-secondary);background:0 0;border:none;border-radius:50%;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.bhn1Oq_iconButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.bhn1Oq_sectionHeader{box-sizing:border-box;height:36px;color:var(--dsw-alias-label-tertiary);border-radius:12px;flex:none;justify-content:flex-end;align-items:center;gap:4px;margin-bottom:4px;padding-left:4px;display:flex;overflow:hidden}.bhn1Oq_root:not(.bhn1Oq_rail) .bhn1Oq_sectionHeader{margin-top:2px;margin-right:-4px}.bhn1Oq_sectionLabel{white-space:nowrap;opacity:1;visibility:visible;min-width:0;max-width:45%;transition:max-width .18s var(--ds-ease-in-out), margin-right .18s var(--ds-ease-in-out), opacity .12s var(--ds-ease-in-out), transform .18s var(--ds-ease-in-out), visibility 0s linear;flex:none;line-height:20px;overflow:hidden}.bhn1Oq_sectionLabelHidden{opacity:0;visibility:hidden;max-width:0;margin-right:-4px;transition-delay:0s,0s,0s,0s,.18s;transform:translate(-4px)}.bhn1Oq_searchSlot{box-sizing:border-box;min-width:0;max-width:28px;transition:max-width .18s var(--ds-ease-in-out), padding-left .18s var(--ds-ease-in-out);flex:1;align-items:center;margin-left:auto;padding-left:0;display:flex}.bhn1Oq_searchSlotExpanded{max-width:100%;padding-left:0}.bhn1Oq_headerActions{opacity:1;visibility:visible;max-width:60px;transition:max-width .18s var(--ds-ease-in-out), opacity .12s var(--ds-ease-in-out), transform .18s var(--ds-ease-in-out), visibility 0s linear;flex:none;align-items:center;gap:4px;display:flex;overflow:hidden}.bhn1Oq_headerActionsHidden{opacity:0;visibility:hidden;pointer-events:none;max-width:0;transition-delay:0s,0s,0s,.18s;transform:translate(4px)}.bhn1Oq_search{box-sizing:border-box;corner-shape:round;cursor:text;width:100%;height:28px;color:var(--dsw-alias-label-secondary);transition:width .18s var(--ds-ease-in-out), padding .18s var(--ds-ease-in-out), border-color .18s var(--ds-ease-in-out), background-color .18s var(--ds-ease-in-out);background:0 0;border:none;border-radius:50%;flex:none;align-items:center;gap:0;margin:0;padding:0;display:flex;overflow:hidden}.bhn1Oq_searchExpanded{border:.5px solid var(--dsw-alias-border-l4);width:calc(100% + 4px);height:30px;color:var(--dsw-alias-label-caption);background:0 0;border-radius:10px;margin-inline:-2px;padding:0 4px 0 0}.bhn1Oq_searchButton{corner-shape:round;cursor:pointer;width:28px;height:28px;color:inherit;background:0 0;border:none;border-radius:50%;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.bhn1Oq_searchExpanded .bhn1Oq_searchButton{width:28px;height:30px}.bhn1Oq_searchButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.bhn1Oq_searchExpanded .bhn1Oq_searchButton:hover{background:0 0}.bhn1Oq_searchInput{opacity:0;pointer-events:none;width:0;min-width:0;color:var(--dsw-alias-label-primary);transition:opacity .12s var(--ds-ease-in-out);background:0 0;border:none;outline:none;flex:1;font-size:13px;line-height:18px}.bhn1Oq_searchExpanded .bhn1Oq_searchInput{opacity:1;pointer-events:auto;margin-left:-2px}.bhn1Oq_searchInput::placeholder{color:var(--dsw-alias-label-tertiary)}.bhn1Oq_clearButton{corner-shape:round;cursor:pointer;width:24px;height:24px;color:var(--dsw-alias-label-secondary);background:0 0;border:none;border-radius:50%;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.bhn1Oq_clearButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.bhn1Oq_rail .bhn1Oq_sectionHeader{justify-content:flex-start;gap:0;margin-bottom:12px;padding-left:0}.bhn1Oq_rail .bhn1Oq_headerActions{max-width:none}.bhn1Oq_rail .bhn1Oq_iconButton{width:36px;height:36px;color:var(--dsw-alias-label-primary)}.bhn1Oq_rail .bhn1Oq_search{background:0 0;border-color:#0000;gap:0;width:36px;height:36px;margin:0 0 12px;padding:0}.bhn1Oq_rail .bhn1Oq_searchButton{width:36px;height:36px;color:var(--dsw-alias-label-primary)}.bhn1Oq_rail .bhn1Oq_searchButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.bhn1Oq_listArea{min-height:0;margin-left:-4px;margin-right:calc(-1 * var(--dsh-session-list-edge-inset));flex-direction:column;flex:1;padding-left:4px;display:flex;overflow:visible}.bhn1Oq_rail .bhn1Oq_listArea{margin-left:0;margin-right:0;padding-left:0}.bhn1Oq_treeBody{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}.bhn1Oq_fade{left:0;right:var(--dsh-session-list-edge-inset);background:linear-gradient(to bottom, transparent, var(--dsw-specific-sidebar-fill));pointer-events:none;height:24px;position:absolute;bottom:0}.bhn1Oq_wide{animation:bhn1Oq_wide-in .2s var(--ds-ease-in-out)}@keyframes bhn1Oq_wide-in{0%{opacity:0}}.bhn1Oq_list{min-height:0;margin-left:-4px;margin-right:var(--dsh-session-list-scrollbar-offset);padding-left:4px;padding-right:calc(var(--dsh-session-list-edge-inset) - var(--dsh-session-list-scrollbar-width) - var(--dsh-session-list-scrollbar-offset));scrollbar-gutter:stable;flex:1;padding-bottom:16px;overflow-y:auto}.bhn1Oq_flatList>*+*,.bhn1Oq_searchTree>[role=treeitem]+[role=treeitem],.bhn1Oq_groupSection>*+*{margin-top:2px}.bhn1Oq_searchStatus,.bhn1Oq_searchWarning{color:var(--dsw-alias-label-tertiary);padding:10px 12px;font-size:12px;line-height:18px}.bhn1Oq_searchWarning{color:var(--dsw-alias-label-secondary)}.bhn1Oq_groupSection{position:relative}.bhn1Oq_groupSection+.bhn1Oq_groupSection{margin-top:4px}.bhn1Oq_listTopDropIndicator,.bhn1Oq_workspaceDropBefore:before,.bhn1Oq_workspaceDropAfter:after{content:\"\";z-index:1;background:linear-gradient(55deg, transparent calc(50% - 1px), var(--dsw-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 0 / 5px 7px no-repeat, linear-gradient(125deg, transparent calc(50% - 1px), var(--dsw-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 5px / 5px 7px no-repeat, linear-gradient(var(--dsw-alias-state-business-primary) 0 0) 4px 5px / calc(100% - 4px) 2px no-repeat;pointer-events:none;height:12px;position:absolute;left:0;right:0}.bhn1Oq_listTopDropIndicator{top:-8px;left:0;right:var(--dsh-session-list-edge-inset)}.bhn1Oq_listTopDropActive>.bhn1Oq_workspaceDropBefore:first-child:before{display:none}.bhn1Oq_workspaceDropBefore:before{top:-8px}.bhn1Oq_workspaceDropAfter:after{bottom:-8px}.bhn1Oq_sessionOverflowButton{cursor:pointer;text-align:left;
|
|
1391
|
+
const css = ".bhn1Oq_root{--dsh-session-list-edge-inset:var(--dsh-sidebar-inline-padding);--dsh-session-list-scrollbar-width:8px;--dsh-session-list-scrollbar-offset:2px;box-sizing:border-box;min-height:0;padding-right:var(--dsh-session-list-edge-inset);flex-direction:column;flex:1;display:flex}.bhn1Oq_root.bhn1Oq_rail{padding-right:0}.bhn1Oq_iconButton{corner-shape:round;cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-secondary);background:0 0;border:none;border-radius:50%;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.bhn1Oq_iconButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.bhn1Oq_sectionHeader{box-sizing:border-box;height:36px;color:var(--dsw-alias-label-tertiary);border-radius:12px;flex:none;justify-content:flex-end;align-items:center;gap:4px;margin-bottom:4px;padding-left:4px;display:flex;overflow:hidden}.bhn1Oq_root:not(.bhn1Oq_rail) .bhn1Oq_sectionHeader{margin-top:2px;margin-right:-4px}.bhn1Oq_sectionLabel{white-space:nowrap;opacity:1;visibility:visible;min-width:0;max-width:45%;transition:max-width .18s var(--ds-ease-in-out), margin-right .18s var(--ds-ease-in-out), opacity .12s var(--ds-ease-in-out), transform .18s var(--ds-ease-in-out), visibility 0s linear;flex:none;line-height:20px;overflow:hidden}.bhn1Oq_sectionLabelHidden{opacity:0;visibility:hidden;max-width:0;margin-right:-4px;transition-delay:0s,0s,0s,0s,.18s;transform:translate(-4px)}.bhn1Oq_searchSlot{box-sizing:border-box;min-width:0;max-width:28px;transition:max-width .18s var(--ds-ease-in-out), padding-left .18s var(--ds-ease-in-out);flex:1;align-items:center;margin-left:auto;padding-left:0;display:flex}.bhn1Oq_searchSlotExpanded{max-width:100%;padding-left:0}.bhn1Oq_headerActions{opacity:1;visibility:visible;max-width:60px;transition:max-width .18s var(--ds-ease-in-out), opacity .12s var(--ds-ease-in-out), transform .18s var(--ds-ease-in-out), visibility 0s linear;flex:none;align-items:center;gap:4px;display:flex;overflow:hidden}.bhn1Oq_headerActionsHidden{opacity:0;visibility:hidden;pointer-events:none;max-width:0;transition-delay:0s,0s,0s,.18s;transform:translate(4px)}.bhn1Oq_search{box-sizing:border-box;corner-shape:round;cursor:text;width:100%;height:28px;color:var(--dsw-alias-label-secondary);transition:width .18s var(--ds-ease-in-out), padding .18s var(--ds-ease-in-out), border-color .18s var(--ds-ease-in-out), background-color .18s var(--ds-ease-in-out);background:0 0;border:none;border-radius:50%;flex:none;align-items:center;gap:0;margin:0;padding:0;display:flex;overflow:hidden}.bhn1Oq_searchExpanded{border:.5px solid var(--dsw-alias-border-l4);width:calc(100% + 4px);height:30px;color:var(--dsw-alias-label-caption);background:0 0;border-radius:10px;margin-inline:-2px;padding:0 4px 0 0}.bhn1Oq_searchButton{corner-shape:round;cursor:pointer;width:28px;height:28px;color:inherit;background:0 0;border:none;border-radius:50%;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.bhn1Oq_searchExpanded .bhn1Oq_searchButton{width:28px;height:30px}.bhn1Oq_searchButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.bhn1Oq_searchExpanded .bhn1Oq_searchButton:hover{background:0 0}.bhn1Oq_searchInput{opacity:0;pointer-events:none;width:0;min-width:0;color:var(--dsw-alias-label-primary);transition:opacity .12s var(--ds-ease-in-out);background:0 0;border:none;outline:none;flex:1;font-size:13px;line-height:18px}.bhn1Oq_searchExpanded .bhn1Oq_searchInput{opacity:1;pointer-events:auto;margin-left:-2px}.bhn1Oq_searchInput::placeholder{color:var(--dsw-alias-label-tertiary)}.bhn1Oq_clearButton{corner-shape:round;cursor:pointer;width:24px;height:24px;color:var(--dsw-alias-label-secondary);background:0 0;border:none;border-radius:50%;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.bhn1Oq_clearButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.bhn1Oq_rail .bhn1Oq_sectionHeader{justify-content:flex-start;gap:0;margin-bottom:12px;padding-left:0}.bhn1Oq_rail .bhn1Oq_headerActions{max-width:none}.bhn1Oq_rail .bhn1Oq_iconButton{width:36px;height:36px;color:var(--dsw-alias-label-primary);border-radius:12px}.bhn1Oq_rail .bhn1Oq_search{background:0 0;border-color:#0000;border-radius:12px;gap:0;width:36px;height:36px;margin:0 0 12px;padding:0}.bhn1Oq_rail .bhn1Oq_searchButton{width:36px;height:36px;color:var(--dsw-alias-label-primary);border-radius:12px}.bhn1Oq_rail .bhn1Oq_searchButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.bhn1Oq_listArea{min-height:0;margin-left:-4px;margin-right:calc(-1 * var(--dsh-session-list-edge-inset));flex-direction:column;flex:1;padding-left:4px;display:flex;overflow:visible}.bhn1Oq_rail .bhn1Oq_listArea{margin-left:0;margin-right:0;padding-left:0}.bhn1Oq_treeBody{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}.bhn1Oq_fade{left:0;right:var(--dsh-session-list-edge-inset);background:linear-gradient(to bottom, transparent, var(--dsw-specific-sidebar-fill));pointer-events:none;height:24px;position:absolute;bottom:0}[data-platform=darwin] .bhn1Oq_fade{display:none}.bhn1Oq_wide{animation:bhn1Oq_wide-in .2s var(--ds-ease-in-out)}@keyframes bhn1Oq_wide-in{0%{opacity:0}}.bhn1Oq_list{min-height:0;margin-left:-4px;margin-right:var(--dsh-session-list-scrollbar-offset);padding-left:4px;padding-right:calc(var(--dsh-session-list-edge-inset) - var(--dsh-session-list-scrollbar-width) - var(--dsh-session-list-scrollbar-offset));scrollbar-gutter:stable;flex:1;padding-bottom:16px;overflow-y:auto}.bhn1Oq_flatList>*+*,.bhn1Oq_searchTree>[role=treeitem]+[role=treeitem],.bhn1Oq_groupSection>*+*{margin-top:2px}.bhn1Oq_searchStatus,.bhn1Oq_searchWarning{color:var(--dsw-alias-label-tertiary);padding:10px 12px;font-size:12px;line-height:18px}.bhn1Oq_searchWarning{color:var(--dsw-alias-label-secondary)}.bhn1Oq_groupSection{position:relative}.bhn1Oq_groupSection+.bhn1Oq_groupSection{margin-top:4px}.bhn1Oq_listTopDropIndicator,.bhn1Oq_workspaceDropBefore:before,.bhn1Oq_workspaceDropAfter:after{content:\"\";z-index:1;background:linear-gradient(55deg, transparent calc(50% - 1px), var(--dsw-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 0 / 5px 7px no-repeat, linear-gradient(125deg, transparent calc(50% - 1px), var(--dsw-alias-state-business-primary) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)) 0 5px / 5px 7px no-repeat, linear-gradient(var(--dsw-alias-state-business-primary) 0 0) 4px 5px / calc(100% - 4px) 2px no-repeat;pointer-events:none;height:12px;position:absolute;left:0;right:0}.bhn1Oq_listTopDropIndicator{top:-8px;left:0;right:var(--dsh-session-list-edge-inset)}.bhn1Oq_listTopDropActive>.bhn1Oq_workspaceDropBefore:first-child:before{display:none}.bhn1Oq_workspaceDropBefore:before{top:-8px}.bhn1Oq_workspaceDropAfter:after{bottom:-8px}.bhn1Oq_sessionOverflowButton{width:100%;height:28px;padding:0 12px 0 calc(28px + var(--dsh-workspace-indent,0px));cursor:pointer;text-align:left;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:8px;font-size:12px}.bhn1Oq_groupSection>.bhn1Oq_sessionOverflowButton{margin-top:0}.bhn1Oq_sessionOverflowButton:hover{color:var(--dsw-alias-label-secondary);background:0 0}.bhn1Oq_empty{color:var(--dsw-alias-label-tertiary);padding:16px 12px;font-size:13px}.bhn1Oq_renameInput{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);width:100%;height:44px;color:var(--dsw-alias-label-primary);background:0 0;border-radius:22px;outline:none;padding:7px 14px;font-size:14px;font-weight:400;line-height:22px}.bhn1Oq_renameInput:disabled{color:var(--dsw-alias-label-dimmed)}.bhn1Oq_renameError{color:var(--dsw-alias-state-error-primary);margin-top:8px;font-size:12px;line-height:18px}.bhn1Oq_deleteAction:not(:disabled){color:var(--dsw-alias-state-error-primary)}.bhn1Oq_deleteStatus{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}@media (prefers-reduced-motion:reduce){.bhn1Oq_wide{animation:none}.bhn1Oq_search,.bhn1Oq_sectionLabel,.bhn1Oq_searchSlot,.bhn1Oq_searchInput,.bhn1Oq_headerActions{transition:none}}";
|
|
1220
1392
|
const tagId = "@deepseek-ai/dsh-client-ui-workspace/WorkspaceBrowser.module.css";
|
|
1221
1393
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
1222
1394
|
const tag = document.createElement("style");
|
|
@@ -1338,58 +1510,6 @@ window.__ModuleLoader__.load({
|
|
|
1338
1510
|
};
|
|
1339
1511
|
}, [active]);
|
|
1340
1512
|
}
|
|
1341
|
-
/** Reconcile a stored view order with the Workspace's current session account. */
|
|
1342
|
-
function reconciledSessionOrder(sessionIds, stored) {
|
|
1343
|
-
if (stored === void 0) return [...sessionIds];
|
|
1344
|
-
const byId = new Map(sessionIds.map((id) => [id, id]));
|
|
1345
|
-
const ordered = [];
|
|
1346
|
-
const included = /* @__PURE__ */ new Set();
|
|
1347
|
-
for (const key of stored) {
|
|
1348
|
-
const id = byId.get(key);
|
|
1349
|
-
if (id === void 0 || included.has(key)) continue;
|
|
1350
|
-
ordered.push(id);
|
|
1351
|
-
included.add(key);
|
|
1352
|
-
}
|
|
1353
|
-
for (const id of sessionIds) {
|
|
1354
|
-
if (included.has(id)) continue;
|
|
1355
|
-
ordered.push(id);
|
|
1356
|
-
}
|
|
1357
|
-
return ordered;
|
|
1358
|
-
}
|
|
1359
|
-
/** Newest update first with stable Session identity as the tie-break. */
|
|
1360
|
-
function compareSessionRecency(a, b, byId) {
|
|
1361
|
-
const aUpdatedAt = byId[a]?.updatedAt ?? Number.NEGATIVE_INFINITY;
|
|
1362
|
-
const bUpdatedAt = byId[b]?.updatedAt ?? Number.NEGATIVE_INFINITY;
|
|
1363
|
-
if (aUpdatedAt !== bUpdatedAt) return bUpdatedAt - aUpdatedAt;
|
|
1364
|
-
return a < b ? -1 : 1;
|
|
1365
|
-
}
|
|
1366
|
-
/** Reconcile one editable order account and apply its activity-promotion policy. */
|
|
1367
|
-
function nextSessionOrderAccount({ sessionIds, previousOrder, previousUpdatedAt, list, orderBy, sortByRecency }) {
|
|
1368
|
-
let order = reconciledSessionOrder(sessionIds, previousOrder);
|
|
1369
|
-
if (sortByRecency) order.sort((a, b) => compareSessionRecency(a, b, list.byId));
|
|
1370
|
-
else if (orderBy === "updated") {
|
|
1371
|
-
const promoted = sessionIds.filter((id) => {
|
|
1372
|
-
const session = list.byId[id];
|
|
1373
|
-
return session !== void 0 && (previousUpdatedAt[id] === void 0 || session.updatedAt > previousUpdatedAt[id]);
|
|
1374
|
-
}).sort((a, b) => compareSessionRecency(a, b, list.byId));
|
|
1375
|
-
if (promoted.length > 0) {
|
|
1376
|
-
const promotedIds = new Set(promoted);
|
|
1377
|
-
order = [...promoted, ...order.filter((id) => !promotedIds.has(id))];
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
const updatedAt = {};
|
|
1381
|
-
for (const id of sessionIds) {
|
|
1382
|
-
const session = list.byId[id];
|
|
1383
|
-
if (session !== void 0) updatedAt[id] = session.updatedAt;
|
|
1384
|
-
}
|
|
1385
|
-
const orderChanged = previousOrder === void 0 || order.length !== previousOrder.length || order.some((id, index) => id !== previousOrder[index]);
|
|
1386
|
-
const timestampsChanged = Object.keys(updatedAt).length !== Object.keys(previousUpdatedAt).length || Object.entries(updatedAt).some(([id, timestamp]) => previousUpdatedAt[id] !== timestamp);
|
|
1387
|
-
return {
|
|
1388
|
-
order,
|
|
1389
|
-
updatedAt,
|
|
1390
|
-
changed: orderChanged || timestampsChanged
|
|
1391
|
-
};
|
|
1392
|
-
}
|
|
1393
1513
|
/** Grouping and ordering menu; own open state so it resets with the wide chrome. */
|
|
1394
1514
|
function ViewOptionsMenu({ groupBy, orderBy, onGroupPick, onOrderPick, t }) {
|
|
1395
1515
|
const [open, setOpen] = (0, react.useState)(false);
|
|
@@ -1408,6 +1528,10 @@ window.__ModuleLoader__.load({
|
|
|
1408
1528
|
id: "workspace",
|
|
1409
1529
|
label: t("groupBy.workspace")
|
|
1410
1530
|
},
|
|
1531
|
+
{
|
|
1532
|
+
id: "workspace-tree",
|
|
1533
|
+
label: t("groupBy.workspaceTree")
|
|
1534
|
+
},
|
|
1411
1535
|
{
|
|
1412
1536
|
id: "flat",
|
|
1413
1537
|
label: t("groupBy.flat")
|
|
@@ -1432,7 +1556,7 @@ window.__ModuleLoader__.load({
|
|
|
1432
1556
|
],
|
|
1433
1557
|
selectedIds: [groupBy, orderBy],
|
|
1434
1558
|
onSelect: (id) => {
|
|
1435
|
-
if (id === "workspace" || id === "flat") onGroupPick(id);
|
|
1559
|
+
if (id === "workspace" || id === "workspace-tree" || id === "flat") onGroupPick(id);
|
|
1436
1560
|
else if (id === "manual" || id === "updated") onOrderPick(id);
|
|
1437
1561
|
setOpen(false);
|
|
1438
1562
|
},
|
|
@@ -1455,24 +1579,22 @@ window.__ModuleLoader__.load({
|
|
|
1455
1579
|
})
|
|
1456
1580
|
});
|
|
1457
1581
|
}
|
|
1458
|
-
/** Resolve an insertion side
|
|
1582
|
+
/** Resolve an insertion side across the Workspace header, descendants, and Sessions. */
|
|
1459
1583
|
function workspaceGroupHalf(e) {
|
|
1460
1584
|
const rect = e.currentTarget.getBoundingClientRect();
|
|
1461
1585
|
return e.clientY < rect.top + rect.height / 2 ? "before" : "after";
|
|
1462
1586
|
}
|
|
1463
1587
|
/** The scrolling session tree; unmounting drops the sessions subscription and expand-all state. */
|
|
1464
|
-
function SessionTree({
|
|
1588
|
+
function SessionTree({ list, useSessionStatus, startSession, open, forkSession, workspaces, ungroupedSessionIds, archivedSessionIds, workspaceReady, usePanelInfo, onRenameRequest, onDeleteRequest, onSessionRename, onSessionArchive, insertWorkspaceBefore, nestWorkspaces, groupExpansion, setGroupExpanded, setSessionOrder, home, t, revealSessionId, onSessionRevealed }) {
|
|
1465
1589
|
const panelActive = usePanelInfo((info) => info.activePanelId !== null);
|
|
1466
|
-
const
|
|
1467
|
-
const
|
|
1468
|
-
const current = panelActive ? void 0 : list.current;
|
|
1590
|
+
const statuses = useSessionStatus((s) => s);
|
|
1591
|
+
const current = panelActive ? void 0 : Object.values(list.byId).find((session) => (session.retainedBy.mainView ?? 0) > 0)?.id;
|
|
1469
1592
|
const revealGroup = revealSessionId === void 0 || !workspaceReady ? void 0 : owningGroupKey(workspaces, revealSessionId);
|
|
1470
1593
|
const [expandedSessionGroups, setExpandedSessionGroups] = (0, react.useState)([]);
|
|
1471
1594
|
const [drag, setDrag] = (0, react.useState)(null);
|
|
1472
1595
|
const sessionDropCommitted = (0, react.useRef)(false);
|
|
1473
1596
|
const [workspaceDrag, setWorkspaceDrag] = (0, react.useState)(null);
|
|
1474
1597
|
const workspaceDropCommitted = (0, react.useRef)(false);
|
|
1475
|
-
const previousOrderBy = (0, react.useRef)(orderBy);
|
|
1476
1598
|
useNativeDragAcceptance(drag !== null || workspaceDrag !== null);
|
|
1477
1599
|
const currentGroup = current === void 0 || !workspaceReady ? void 0 : owningGroupKey(workspaces, current);
|
|
1478
1600
|
(0, react.useEffect)(() => {
|
|
@@ -1484,70 +1606,44 @@ window.__ModuleLoader__.load({
|
|
|
1484
1606
|
setGroupExpanded,
|
|
1485
1607
|
groupExpansion
|
|
1486
1608
|
]);
|
|
1487
|
-
const
|
|
1488
|
-
|
|
1489
|
-
const
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
const previousOrder = sessionOrderByAccount[key];
|
|
1505
|
-
const next = nextSessionOrderAccount({
|
|
1506
|
-
sessionIds,
|
|
1507
|
-
previousOrder,
|
|
1508
|
-
previousUpdatedAt: sessionUpdatedAtByAccount[key] ?? {},
|
|
1509
|
-
list,
|
|
1510
|
-
orderBy,
|
|
1511
|
-
sortByRecency: orderBy === "updated" && (previousOrder === void 0 || switchedToUpdated)
|
|
1512
|
-
});
|
|
1513
|
-
if (next.changed) syncSessionOrderAccount(key, next.order.map((id) => id), next.updatedAt);
|
|
1514
|
-
}
|
|
1609
|
+
const parents = (0, react.useMemo)(() => {
|
|
1610
|
+
if (!nestWorkspaces) return /* @__PURE__ */ new Map();
|
|
1611
|
+
const keysByPath = new Map(workspaces.map((workspace) => [workspace.path, workspace.workspaceId]));
|
|
1612
|
+
const paths = [...keysByPath.keys()];
|
|
1613
|
+
return new Map(workspaces.map((workspace) => {
|
|
1614
|
+
const path = owningParentFolder(workspace.path, paths);
|
|
1615
|
+
return [workspace.workspaceId, path === void 0 ? void 0 : keysByPath.get(path)];
|
|
1616
|
+
}));
|
|
1617
|
+
}, [nestWorkspaces, workspaces]);
|
|
1618
|
+
const currentAncestors = (0, react.useMemo)(() => {
|
|
1619
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1620
|
+
for (let key = currentGroup === void 0 ? void 0 : parents.get(currentGroup); key !== void 0; key = parents.get(key)) keys.add(key);
|
|
1621
|
+
return keys;
|
|
1622
|
+
}, [currentGroup, parents]);
|
|
1623
|
+
const expandedGroups = (0, react.useMemo)(() => {
|
|
1624
|
+
const ancestorKeys = new Set(parents.values());
|
|
1625
|
+
return [...workspaces.map((workspace) => workspace.workspaceId), ""].filter((key) => groupExpansion[key] ?? ancestorKeys.has(key));
|
|
1515
1626
|
}, [
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
sessionOrderByAccount,
|
|
1519
|
-
sessionUpdatedAtByAccount,
|
|
1520
|
-
syncSessionOrderAccount,
|
|
1521
|
-
ungroupedSessionIds,
|
|
1627
|
+
groupExpansion,
|
|
1628
|
+
parents,
|
|
1522
1629
|
workspaces
|
|
1523
1630
|
]);
|
|
1524
|
-
const
|
|
1525
|
-
return workspaces.map((workspace) => {
|
|
1526
|
-
const stored = sessionOrderByAccount[workspace.workspaceId];
|
|
1527
|
-
const sessionIds = reconciledSessionOrder(workspace.sessionIds, stored);
|
|
1528
|
-
return {
|
|
1529
|
-
...workspace,
|
|
1530
|
-
sessionIds
|
|
1531
|
-
};
|
|
1532
|
-
});
|
|
1533
|
-
}, [sessionOrderByAccount, workspaces]);
|
|
1534
|
-
const orderedUngroupedSessionIds = (0, react.useMemo)(() => reconciledSessionOrder(ungroupedSessionIds, sessionOrderByAccount[""]), [sessionOrderByAccount, ungroupedSessionIds]);
|
|
1535
|
-
const groups = (0, react.useMemo)(() => deriveGroups(list, orderedWorkspaces, archivedSessionIds, pendingInteractions, {
|
|
1631
|
+
const groups = (0, react.useMemo)(() => deriveGroups(list, workspaces, archivedSessionIds, statuses, {
|
|
1536
1632
|
expandedGroups,
|
|
1537
|
-
|
|
1633
|
+
ungroupedOrder: ungroupedSessionIds
|
|
1538
1634
|
}), [
|
|
1539
1635
|
list,
|
|
1540
|
-
|
|
1636
|
+
workspaces,
|
|
1541
1637
|
archivedSessionIds,
|
|
1542
|
-
|
|
1638
|
+
statuses,
|
|
1543
1639
|
expandedGroups,
|
|
1544
|
-
|
|
1640
|
+
ungroupedSessionIds
|
|
1545
1641
|
]);
|
|
1546
1642
|
(0, react.useEffect)(() => {
|
|
1547
|
-
|
|
1548
|
-
setGroupExpanded(revealGroup, true);
|
|
1643
|
+
for (let key = revealGroup; key !== void 0; key = parents.get(key)) if (groupExpansion[key] === false || key === revealGroup && groupExpansion[key] !== true) setGroupExpanded(key, true);
|
|
1549
1644
|
}, [
|
|
1550
1645
|
groupExpansion,
|
|
1646
|
+
parents,
|
|
1551
1647
|
revealGroup,
|
|
1552
1648
|
setGroupExpanded
|
|
1553
1649
|
]);
|
|
@@ -1580,8 +1676,8 @@ window.__ModuleLoader__.load({
|
|
|
1580
1676
|
if (targetWithoutSourceIndex === -1) return;
|
|
1581
1677
|
const visibleInsertAt = over.half === "before" ? targetWithoutSourceIndex : targetWithoutSourceIndex + 1;
|
|
1582
1678
|
if (sourceIndex !== -1 && visibleInsertAt === sourceIndex) return;
|
|
1583
|
-
const accountSessionIds = activeDrag.accountKey === "" ?
|
|
1584
|
-
if (accountSessionIds === void 0) return;
|
|
1679
|
+
const accountSessionIds = activeDrag.accountKey === "" ? ungroupedSessionIds : workspaces.find((workspace) => workspace.workspaceId === activeDrag.accountKey)?.sessionIds;
|
|
1680
|
+
if (accountSessionIds === void 0 || !accountSessionIds.includes(activeDrag.sessionId)) return;
|
|
1585
1681
|
const nextOrder = accountSessionIds.filter((id) => id !== activeDrag.sessionId);
|
|
1586
1682
|
let anchor;
|
|
1587
1683
|
if (sessionsExpanded) anchor = over.half === "before" ? over.id : renderedSessions[targetIndex + 1]?.id;
|
|
@@ -1603,28 +1699,198 @@ window.__ModuleLoader__.load({
|
|
|
1603
1699
|
return node === void 0 ? [] : [node];
|
|
1604
1700
|
})).rows.some((node) => node.id === activeDrag.sessionId)) return;
|
|
1605
1701
|
}
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
insertSessionBefore(activeDrag.accountKey, activeDrag.sessionId, anchor).catch((reason) => {
|
|
1609
|
-
console.warn("session reorder rejected:", reason);
|
|
1610
|
-
});
|
|
1702
|
+
const currentBlank = group.sessions.find((node) => node.blank)?.id;
|
|
1703
|
+
setSessionOrder(activeDrag.accountKey, pinCurrentBlank(nextOrder, currentBlank));
|
|
1611
1704
|
};
|
|
1612
1705
|
const commitWorkspaceDrag = (activeDrag, over) => {
|
|
1613
1706
|
if (workspaceDropCommitted.current) return;
|
|
1614
1707
|
workspaceDropCommitted.current = true;
|
|
1615
1708
|
setWorkspaceDrag(null);
|
|
1616
|
-
const
|
|
1709
|
+
const owner = parents.get(activeDrag.workspaceId);
|
|
1710
|
+
const siblings = workspaces.filter((workspace) => parents.get(workspace.workspaceId) === owner);
|
|
1711
|
+
const rowIndex = siblings.findIndex((workspace) => workspace.workspaceId === over.id);
|
|
1617
1712
|
if (rowIndex === -1) return;
|
|
1618
|
-
const anchor = over.half === "before" ? over.id :
|
|
1713
|
+
const anchor = over.half === "before" ? over.id : siblings[rowIndex + 1]?.workspaceId;
|
|
1619
1714
|
if (anchor === activeDrag.workspaceId) return;
|
|
1620
|
-
const sourceIndex =
|
|
1621
|
-
const anchorIndex = anchor === void 0 ?
|
|
1715
|
+
const sourceIndex = siblings.findIndex((workspace) => workspace.workspaceId === activeDrag.workspaceId);
|
|
1716
|
+
const anchorIndex = anchor === void 0 ? siblings.length : siblings.findIndex((workspace) => workspace.workspaceId === anchor);
|
|
1622
1717
|
if (sourceIndex !== -1 && (anchorIndex === sourceIndex || anchorIndex === sourceIndex + 1)) return;
|
|
1623
1718
|
insertWorkspaceBefore(activeDrag.workspaceId, anchor).catch((reason) => {
|
|
1624
1719
|
console.warn("workspace reorder rejected:", reason);
|
|
1625
1720
|
});
|
|
1626
1721
|
};
|
|
1627
|
-
const
|
|
1722
|
+
const childrenByParent = (0, react.useMemo)(() => {
|
|
1723
|
+
const children = /* @__PURE__ */ new Map();
|
|
1724
|
+
for (const group of groups) {
|
|
1725
|
+
const parent = parents.get(group.key);
|
|
1726
|
+
const siblings = children.get(parent);
|
|
1727
|
+
if (siblings === void 0) children.set(parent, [group]);
|
|
1728
|
+
else siblings.push(group);
|
|
1729
|
+
}
|
|
1730
|
+
return children;
|
|
1731
|
+
}, [groups, parents]);
|
|
1732
|
+
const rootGroups = childrenByParent.get(void 0) ?? [];
|
|
1733
|
+
const workspaceDropAtListStart = rootGroups[0]?.workspaceId !== void 0 && workspaceDrag?.over?.id === rootGroups[0].workspaceId && workspaceDrag.over.half === "before";
|
|
1734
|
+
const renderGroup = (group, depth) => {
|
|
1735
|
+
const workspaceId = group.workspaceId;
|
|
1736
|
+
const children = childrenByParent.get(group.key) ?? [];
|
|
1737
|
+
const compatibleDrag = workspaceDrag !== null && parents.get(workspaceDrag.workspaceId) === parents.get(group.key);
|
|
1738
|
+
const collapsed = collapsedSessionRows(group.sessions);
|
|
1739
|
+
const sessionsExpanded = expandedSessionGroups.includes(group.key);
|
|
1740
|
+
const workspaceMarker = workspaceId !== void 0 && workspaceDrag?.over?.id === workspaceId ? workspaceDrag.over.half : null;
|
|
1741
|
+
const workspaceDragProps = workspaceId === void 0 ? void 0 : {
|
|
1742
|
+
start: () => {
|
|
1743
|
+
workspaceDropCommitted.current = false;
|
|
1744
|
+
setWorkspaceDrag({
|
|
1745
|
+
workspaceId,
|
|
1746
|
+
over: null
|
|
1747
|
+
});
|
|
1748
|
+
},
|
|
1749
|
+
end: () => {
|
|
1750
|
+
if (workspaceDrag?.over !== null && workspaceDrag?.over !== void 0) commitWorkspaceDrag(workspaceDrag, workspaceDrag.over);
|
|
1751
|
+
else setWorkspaceDrag(null);
|
|
1752
|
+
workspaceDropCommitted.current = false;
|
|
1753
|
+
}
|
|
1754
|
+
};
|
|
1755
|
+
const hoverWorkspace = workspaceId === void 0 || !compatibleDrag ? void 0 : (half) => {
|
|
1756
|
+
setWorkspaceDrag((active) => active === null ? active : {
|
|
1757
|
+
...active,
|
|
1758
|
+
over: {
|
|
1759
|
+
id: workspaceId,
|
|
1760
|
+
half
|
|
1761
|
+
}
|
|
1762
|
+
});
|
|
1763
|
+
};
|
|
1764
|
+
const dropWorkspace = workspaceId === void 0 || !compatibleDrag ? void 0 : (half) => {
|
|
1765
|
+
commitWorkspaceDrag(workspaceDrag, {
|
|
1766
|
+
id: workspaceId,
|
|
1767
|
+
half
|
|
1768
|
+
});
|
|
1769
|
+
};
|
|
1770
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
1771
|
+
style: { "--dsh-workspace-indent": `${depth * 12}px` },
|
|
1772
|
+
className: clsx(WorkspaceBrowser_module_css_default.groupSection, workspaceMarker === "before" && WorkspaceBrowser_module_css_default.workspaceDropBefore, workspaceMarker === "after" && WorkspaceBrowser_module_css_default.workspaceDropAfter),
|
|
1773
|
+
onDragOver: workspaceDrag === null ? void 0 : (e) => {
|
|
1774
|
+
e.preventDefault();
|
|
1775
|
+
if (hoverWorkspace === void 0 && parents.get(group.key) !== void 0) return;
|
|
1776
|
+
e.stopPropagation();
|
|
1777
|
+
if (hoverWorkspace === void 0) {
|
|
1778
|
+
e.dataTransfer.dropEffect = "none";
|
|
1779
|
+
if (workspaceDrag.over !== null) setWorkspaceDrag({
|
|
1780
|
+
...workspaceDrag,
|
|
1781
|
+
over: null
|
|
1782
|
+
});
|
|
1783
|
+
} else {
|
|
1784
|
+
e.dataTransfer.dropEffect = "move";
|
|
1785
|
+
hoverWorkspace(workspaceGroupHalf(e));
|
|
1786
|
+
}
|
|
1787
|
+
},
|
|
1788
|
+
onDrop: workspaceDrag === null ? void 0 : (e) => {
|
|
1789
|
+
e.preventDefault();
|
|
1790
|
+
if (dropWorkspace === void 0 && parents.get(group.key) !== void 0) return;
|
|
1791
|
+
e.stopPropagation();
|
|
1792
|
+
if (dropWorkspace === void 0) {
|
|
1793
|
+
workspaceDropCommitted.current = true;
|
|
1794
|
+
setWorkspaceDrag(null);
|
|
1795
|
+
} else dropWorkspace(workspaceGroupHalf(e));
|
|
1796
|
+
},
|
|
1797
|
+
children: [
|
|
1798
|
+
(0, react_jsx_runtime.jsx)(ProjectRowItem, {
|
|
1799
|
+
group,
|
|
1800
|
+
containsCurrentDescendant: currentAncestors.has(group.key),
|
|
1801
|
+
home,
|
|
1802
|
+
t,
|
|
1803
|
+
onToggle: () => {
|
|
1804
|
+
if (group.expanded) setExpandedSessionGroups((keys) => keys.filter((key) => key !== group.key));
|
|
1805
|
+
setGroupExpanded(group.key, !group.expanded);
|
|
1806
|
+
},
|
|
1807
|
+
onCreate: () => {
|
|
1808
|
+
if (group.workspaceId !== void 0) {
|
|
1809
|
+
setGroupExpanded(group.key, true);
|
|
1810
|
+
startSession(group.workspaceId);
|
|
1811
|
+
}
|
|
1812
|
+
},
|
|
1813
|
+
drag: workspaceDragProps,
|
|
1814
|
+
actions: group.workspaceId === void 0 ? void 0 : {
|
|
1815
|
+
rename: () => {
|
|
1816
|
+
/* v8 ignore next -- narrowing guard: the actions object exists only for real-workspace groups. */
|
|
1817
|
+
if (group.workspaceId !== void 0) onRenameRequest(group.workspaceId, group.label);
|
|
1818
|
+
},
|
|
1819
|
+
delete: () => {
|
|
1820
|
+
/* v8 ignore next -- narrowing guard: the actions object exists only for real-workspace groups. */
|
|
1821
|
+
if (group.workspaceId !== void 0) onDeleteRequest(group.workspaceId, group.label);
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
}),
|
|
1825
|
+
group.expanded && children.length > 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
1826
|
+
role: "group",
|
|
1827
|
+
children: children.map((child) => renderGroup(child, depth + 1))
|
|
1828
|
+
}),
|
|
1829
|
+
(sessionsExpanded ? group.sessions : collapsed.rows).map((node) => {
|
|
1830
|
+
const sameGroupDrag = drag !== null && drag.accountKey === group.key;
|
|
1831
|
+
const normalizeHalf = (half) => node.blank ? "after" : half;
|
|
1832
|
+
const dragProps = {
|
|
1833
|
+
start: () => {
|
|
1834
|
+
sessionDropCommitted.current = false;
|
|
1835
|
+
setDrag({
|
|
1836
|
+
accountKey: group.key,
|
|
1837
|
+
sessionId: node.id,
|
|
1838
|
+
over: null
|
|
1839
|
+
});
|
|
1840
|
+
},
|
|
1841
|
+
active: sameGroupDrag,
|
|
1842
|
+
marker: sameGroupDrag && drag.over?.id === node.id ? drag.over.half : null,
|
|
1843
|
+
hover: (half) => {
|
|
1844
|
+
/* v8 ignore next -- narrowing guard: Rows gates hover on `active`, which is false while the drag state is null. */
|
|
1845
|
+
setDrag((d) => d === null ? d : {
|
|
1846
|
+
...d,
|
|
1847
|
+
over: {
|
|
1848
|
+
id: node.id,
|
|
1849
|
+
half: normalizeHalf(half)
|
|
1850
|
+
}
|
|
1851
|
+
});
|
|
1852
|
+
},
|
|
1853
|
+
drop: (half) => {
|
|
1854
|
+
/* v8 ignore next -- narrowing guard: Rows gates drop on `active`, which is false while the drag state is null. */
|
|
1855
|
+
if (drag === null) return;
|
|
1856
|
+
commitSessionDrag(drag, {
|
|
1857
|
+
id: node.id,
|
|
1858
|
+
half: normalizeHalf(half)
|
|
1859
|
+
});
|
|
1860
|
+
},
|
|
1861
|
+
end: () => {
|
|
1862
|
+
if (drag?.over !== null && drag?.over !== void 0) commitSessionDrag(drag, drag.over);
|
|
1863
|
+
else setDrag(null);
|
|
1864
|
+
sessionDropCommitted.current = false;
|
|
1865
|
+
}
|
|
1866
|
+
};
|
|
1867
|
+
return (0, react_jsx_runtime.jsx)(SessionNodeItem, {
|
|
1868
|
+
node,
|
|
1869
|
+
currentId: current,
|
|
1870
|
+
now,
|
|
1871
|
+
onOpen: open,
|
|
1872
|
+
onRename: onSessionRename,
|
|
1873
|
+
onFork: forkSession,
|
|
1874
|
+
onArchive: onSessionArchive,
|
|
1875
|
+
onReveal: node.id === revealSessionId && group.key === revealGroup ? () => {
|
|
1876
|
+
onSessionRevealed(node.id);
|
|
1877
|
+
} : void 0,
|
|
1878
|
+
drag: dragProps,
|
|
1879
|
+
t
|
|
1880
|
+
}, node.id);
|
|
1881
|
+
}),
|
|
1882
|
+
collapsed.hiddenCount > 0 && (0, react_jsx_runtime.jsx)("button", {
|
|
1883
|
+
type: "button",
|
|
1884
|
+
className: WorkspaceBrowser_module_css_default.sessionOverflowButton,
|
|
1885
|
+
"aria-expanded": sessionsExpanded,
|
|
1886
|
+
onClick: () => {
|
|
1887
|
+
setExpandedSessionGroups((keys) => toggled(keys, group.key));
|
|
1888
|
+
},
|
|
1889
|
+
children: sessionsExpanded ? t("sessions.collapse") : t("sessions.expand", { n: collapsed.hiddenCount })
|
|
1890
|
+
})
|
|
1891
|
+
]
|
|
1892
|
+
}, group.key);
|
|
1893
|
+
};
|
|
1628
1894
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
1629
1895
|
className: clsx(WorkspaceBrowser_module_css_default.treeBody, WorkspaceBrowser_module_css_default.wide),
|
|
1630
1896
|
children: [
|
|
@@ -1639,197 +1905,25 @@ window.__ModuleLoader__.load({
|
|
|
1639
1905
|
children: [groups.length === 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
1640
1906
|
className: WorkspaceBrowser_module_css_default.empty,
|
|
1641
1907
|
children: t("empty.none")
|
|
1642
|
-
}),
|
|
1643
|
-
const workspaceId = group.workspaceId;
|
|
1644
|
-
const collapsed = collapsedSessionRows(group.sessions);
|
|
1645
|
-
const sessionsExpanded = expandedSessionGroups.includes(group.key);
|
|
1646
|
-
const workspaceMarker = workspaceId !== void 0 && workspaceDrag?.over?.id === workspaceId ? workspaceDrag.over.half : null;
|
|
1647
|
-
const workspaceDragProps = workspaceId === void 0 ? void 0 : {
|
|
1648
|
-
start: () => {
|
|
1649
|
-
workspaceDropCommitted.current = false;
|
|
1650
|
-
setWorkspaceDrag({
|
|
1651
|
-
workspaceId,
|
|
1652
|
-
over: null
|
|
1653
|
-
});
|
|
1654
|
-
},
|
|
1655
|
-
end: () => {
|
|
1656
|
-
if (workspaceDrag?.over !== null && workspaceDrag?.over !== void 0) commitWorkspaceDrag(workspaceDrag, workspaceDrag.over);
|
|
1657
|
-
else setWorkspaceDrag(null);
|
|
1658
|
-
workspaceDropCommitted.current = false;
|
|
1659
|
-
}
|
|
1660
|
-
};
|
|
1661
|
-
const hoverWorkspace = workspaceId === void 0 ? void 0 : (half) => {
|
|
1662
|
-
setWorkspaceDrag((active) => active === null ? active : {
|
|
1663
|
-
...active,
|
|
1664
|
-
over: {
|
|
1665
|
-
id: workspaceId,
|
|
1666
|
-
half
|
|
1667
|
-
}
|
|
1668
|
-
});
|
|
1669
|
-
};
|
|
1670
|
-
const dropWorkspace = workspaceId === void 0 ? void 0 : (half) => {
|
|
1671
|
-
if (workspaceDrag === null) return;
|
|
1672
|
-
commitWorkspaceDrag(workspaceDrag, {
|
|
1673
|
-
id: workspaceId,
|
|
1674
|
-
half
|
|
1675
|
-
});
|
|
1676
|
-
};
|
|
1677
|
-
return (0, react_jsx_runtime.jsxs)("div", {
|
|
1678
|
-
className: clsx(WorkspaceBrowser_module_css_default.groupSection, workspaceMarker === "before" && WorkspaceBrowser_module_css_default.workspaceDropBefore, workspaceMarker === "after" && WorkspaceBrowser_module_css_default.workspaceDropAfter),
|
|
1679
|
-
onDragOver: workspaceDrag === null || hoverWorkspace === void 0 ? void 0 : (e) => {
|
|
1680
|
-
e.preventDefault();
|
|
1681
|
-
e.dataTransfer.dropEffect = "move";
|
|
1682
|
-
hoverWorkspace(workspaceGroupHalf(e));
|
|
1683
|
-
},
|
|
1684
|
-
onDrop: workspaceDrag === null || dropWorkspace === void 0 ? void 0 : (e) => {
|
|
1685
|
-
e.preventDefault();
|
|
1686
|
-
dropWorkspace(workspaceGroupHalf(e));
|
|
1687
|
-
},
|
|
1688
|
-
children: [
|
|
1689
|
-
(0, react_jsx_runtime.jsx)(ProjectRowItem, {
|
|
1690
|
-
group,
|
|
1691
|
-
home,
|
|
1692
|
-
t,
|
|
1693
|
-
onToggle: () => {
|
|
1694
|
-
if (group.expanded) setExpandedSessionGroups((keys) => keys.filter((key) => key !== group.key));
|
|
1695
|
-
setGroupExpanded(group.key, !group.expanded);
|
|
1696
|
-
},
|
|
1697
|
-
onCreate: () => {
|
|
1698
|
-
if (group.workspaceId !== void 0) {
|
|
1699
|
-
setGroupExpanded(group.key, true);
|
|
1700
|
-
startSession(group.workspaceId);
|
|
1701
|
-
}
|
|
1702
|
-
},
|
|
1703
|
-
drag: workspaceDragProps,
|
|
1704
|
-
actions: group.workspaceId === void 0 ? void 0 : {
|
|
1705
|
-
rename: () => {
|
|
1706
|
-
/* v8 ignore next -- narrowing guard: the actions object exists only for real-workspace groups. */
|
|
1707
|
-
if (group.workspaceId !== void 0) onRenameRequest(group.workspaceId, group.label);
|
|
1708
|
-
},
|
|
1709
|
-
delete: () => {
|
|
1710
|
-
/* v8 ignore next -- narrowing guard: the actions object exists only for real-workspace groups. */
|
|
1711
|
-
if (group.workspaceId !== void 0) onDeleteRequest(group.workspaceId, group.label);
|
|
1712
|
-
}
|
|
1713
|
-
}
|
|
1714
|
-
}),
|
|
1715
|
-
(sessionsExpanded ? group.sessions : collapsed.rows).map((node) => {
|
|
1716
|
-
const sameGroupDrag = drag !== null && drag.accountKey === group.key;
|
|
1717
|
-
const dragProps = {
|
|
1718
|
-
start: () => {
|
|
1719
|
-
sessionDropCommitted.current = false;
|
|
1720
|
-
setDrag({
|
|
1721
|
-
accountKey: group.key,
|
|
1722
|
-
sessionId: node.id,
|
|
1723
|
-
over: null
|
|
1724
|
-
});
|
|
1725
|
-
},
|
|
1726
|
-
active: sameGroupDrag,
|
|
1727
|
-
marker: sameGroupDrag && drag.over?.id === node.id ? drag.over.half : null,
|
|
1728
|
-
hover: (half) => {
|
|
1729
|
-
/* v8 ignore next -- narrowing guard: Rows gates hover on `active`, which is false while the drag state is null. */
|
|
1730
|
-
setDrag((d) => d === null ? d : {
|
|
1731
|
-
...d,
|
|
1732
|
-
over: {
|
|
1733
|
-
id: node.id,
|
|
1734
|
-
half
|
|
1735
|
-
}
|
|
1736
|
-
});
|
|
1737
|
-
},
|
|
1738
|
-
drop: (half) => {
|
|
1739
|
-
/* v8 ignore next -- narrowing guard: Rows gates drop on `active`, which is false while the drag state is null. */
|
|
1740
|
-
if (drag === null) return;
|
|
1741
|
-
commitSessionDrag(drag, {
|
|
1742
|
-
id: node.id,
|
|
1743
|
-
half
|
|
1744
|
-
});
|
|
1745
|
-
},
|
|
1746
|
-
end: () => {
|
|
1747
|
-
if (drag?.over !== null && drag?.over !== void 0) commitSessionDrag(drag, drag.over);
|
|
1748
|
-
else setDrag(null);
|
|
1749
|
-
sessionDropCommitted.current = false;
|
|
1750
|
-
}
|
|
1751
|
-
};
|
|
1752
|
-
return (0, react_jsx_runtime.jsx)(SessionNodeItem, {
|
|
1753
|
-
node,
|
|
1754
|
-
currentId: current,
|
|
1755
|
-
now,
|
|
1756
|
-
onOpen: open,
|
|
1757
|
-
onRename: onSessionRename,
|
|
1758
|
-
onFork: forkSession,
|
|
1759
|
-
onArchive: onSessionArchive,
|
|
1760
|
-
onReveal: node.id === revealSessionId && group.key === revealGroup ? () => {
|
|
1761
|
-
onSessionRevealed(node.id);
|
|
1762
|
-
} : void 0,
|
|
1763
|
-
drag: dragProps,
|
|
1764
|
-
t
|
|
1765
|
-
}, node.id);
|
|
1766
|
-
}),
|
|
1767
|
-
collapsed.hiddenCount > 0 && (0, react_jsx_runtime.jsx)("button", {
|
|
1768
|
-
type: "button",
|
|
1769
|
-
className: WorkspaceBrowser_module_css_default.sessionOverflowButton,
|
|
1770
|
-
"aria-expanded": sessionsExpanded,
|
|
1771
|
-
onClick: () => {
|
|
1772
|
-
setExpandedSessionGroups((keys) => toggled(keys, group.key));
|
|
1773
|
-
},
|
|
1774
|
-
children: sessionsExpanded ? t("sessions.collapse") : t("sessions.expand", { n: collapsed.hiddenCount })
|
|
1775
|
-
})
|
|
1776
|
-
]
|
|
1777
|
-
}, group.key);
|
|
1778
|
-
})]
|
|
1908
|
+
}), rootGroups.map((group) => renderGroup(group, 0))]
|
|
1779
1909
|
}),
|
|
1780
1910
|
(0, react_jsx_runtime.jsx)("span", { className: WorkspaceBrowser_module_css_default.fade })
|
|
1781
1911
|
]
|
|
1782
1912
|
});
|
|
1783
1913
|
}
|
|
1784
1914
|
/** The flat "In one list" body: every session is one draggable top-level row. */
|
|
1785
|
-
function FlatList({
|
|
1915
|
+
function FlatList({ list, sessionIds, useSessionStatus, open, forkSession, onSessionRename, onSessionArchive, usePanelInfo, setSessionOrder, revealSessionId, onSessionRevealed, t }) {
|
|
1786
1916
|
const panelActive = usePanelInfo((info) => info.activePanelId !== null);
|
|
1787
|
-
const
|
|
1788
|
-
const
|
|
1789
|
-
const baseRows = (0, react.useMemo)(() => deriveFlat(list, archivedSessionIds, pendingInteractions), [
|
|
1790
|
-
list,
|
|
1791
|
-
archivedSessionIds,
|
|
1792
|
-
pendingInteractions
|
|
1793
|
-
]);
|
|
1794
|
-
const sessionIds = (0, react.useMemo)(() => baseRows.map((row) => row.id), [baseRows]);
|
|
1795
|
-
const previousOrderBy = (0, react.useRef)(orderBy);
|
|
1796
|
-
(0, react.useEffect)(() => {
|
|
1797
|
-
if (list.phase !== "ready") return;
|
|
1798
|
-
const previousOrder = sessionOrderByAccount[FLAT_SESSION_ORDER_KEY];
|
|
1799
|
-
const previousUpdatedAt = sessionUpdatedAtByAccount["__flat_session_order__"] ?? {};
|
|
1800
|
-
const switchedToUpdated = previousOrderBy.current !== "updated" && orderBy === "updated";
|
|
1801
|
-
previousOrderBy.current = orderBy;
|
|
1802
|
-
const next = nextSessionOrderAccount({
|
|
1803
|
-
sessionIds,
|
|
1804
|
-
previousOrder,
|
|
1805
|
-
previousUpdatedAt,
|
|
1806
|
-
list,
|
|
1807
|
-
orderBy,
|
|
1808
|
-
sortByRecency: orderBy === "updated" && (previousOrder === void 0 || switchedToUpdated)
|
|
1809
|
-
});
|
|
1810
|
-
if (next.changed) syncSessionOrderAccount(FLAT_SESSION_ORDER_KEY, next.order.map((id) => id), next.updatedAt);
|
|
1811
|
-
}, [
|
|
1917
|
+
const statuses = useSessionStatus((s) => s);
|
|
1918
|
+
const rows = (0, react.useMemo)(() => deriveFlat(list, sessionIds, statuses), [
|
|
1812
1919
|
list,
|
|
1813
|
-
orderBy,
|
|
1814
|
-
sessionOrderByAccount,
|
|
1815
|
-
sessionUpdatedAtByAccount,
|
|
1816
1920
|
sessionIds,
|
|
1817
|
-
|
|
1818
|
-
]);
|
|
1819
|
-
const rows = (0, react.useMemo)(() => {
|
|
1820
|
-
const byId = new Map(baseRows.map((row) => [row.id, row]));
|
|
1821
|
-
return reconciledSessionOrder(sessionIds, sessionOrderByAccount[FLAT_SESSION_ORDER_KEY]).flatMap((id) => {
|
|
1822
|
-
const row = byId.get(id);
|
|
1823
|
-
return row === void 0 ? [] : [row];
|
|
1824
|
-
});
|
|
1825
|
-
}, [
|
|
1826
|
-
baseRows,
|
|
1827
|
-
sessionOrderByAccount,
|
|
1828
|
-
sessionIds
|
|
1921
|
+
statuses
|
|
1829
1922
|
]);
|
|
1830
1923
|
const [drag, setDrag] = (0, react.useState)(null);
|
|
1831
1924
|
const dropCommitted = (0, react.useRef)(false);
|
|
1832
1925
|
useNativeDragAcceptance(drag !== null);
|
|
1926
|
+
const currentId = panelActive ? void 0 : Object.values(list.byId).find((session) => (session.retainedBy.mainView ?? 0) > 0)?.id;
|
|
1833
1927
|
const commitDrag = (activeDrag, over) => {
|
|
1834
1928
|
if (dropCommitted.current) return;
|
|
1835
1929
|
dropCommitted.current = true;
|
|
@@ -1844,7 +1938,8 @@ window.__ModuleLoader__.load({
|
|
|
1844
1938
|
const nextOrder = rows.map((row) => row.id).filter((id) => id !== activeDrag.sessionId);
|
|
1845
1939
|
const insertAt = anchor === void 0 ? nextOrder.length : nextOrder.indexOf(anchor);
|
|
1846
1940
|
nextOrder.splice(insertAt === -1 ? nextOrder.length : insertAt, 0, activeDrag.sessionId);
|
|
1847
|
-
|
|
1941
|
+
const currentBlank = rows.find((node) => node.blank)?.id;
|
|
1942
|
+
setSessionOrder(FLAT_SESSION_ORDER_KEY, pinCurrentBlank(nextOrder, currentBlank));
|
|
1848
1943
|
};
|
|
1849
1944
|
const now = Date.now();
|
|
1850
1945
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1858,9 +1953,10 @@ window.__ModuleLoader__.load({
|
|
|
1858
1953
|
children: t("empty.none")
|
|
1859
1954
|
}), rows.map((node) => {
|
|
1860
1955
|
const active = drag !== null;
|
|
1956
|
+
const normalizeHalf = (half) => node.blank ? "after" : half;
|
|
1861
1957
|
return (0, react_jsx_runtime.jsx)(SessionNodeItem, {
|
|
1862
1958
|
node,
|
|
1863
|
-
currentId
|
|
1959
|
+
currentId,
|
|
1864
1960
|
now,
|
|
1865
1961
|
onOpen: open,
|
|
1866
1962
|
onRename: onSessionRename,
|
|
@@ -1886,14 +1982,14 @@ window.__ModuleLoader__.load({
|
|
|
1886
1982
|
...current,
|
|
1887
1983
|
over: {
|
|
1888
1984
|
id: node.id,
|
|
1889
|
-
half
|
|
1985
|
+
half: normalizeHalf(half)
|
|
1890
1986
|
}
|
|
1891
1987
|
});
|
|
1892
1988
|
},
|
|
1893
1989
|
drop: (half) => {
|
|
1894
1990
|
if (drag !== null) commitDrag(drag, {
|
|
1895
1991
|
id: node.id,
|
|
1896
|
-
half
|
|
1992
|
+
half: normalizeHalf(half)
|
|
1897
1993
|
});
|
|
1898
1994
|
},
|
|
1899
1995
|
end: () => {
|
|
@@ -1909,27 +2005,28 @@ window.__ModuleLoader__.load({
|
|
|
1909
2005
|
});
|
|
1910
2006
|
}
|
|
1911
2007
|
/** Flat search body: local metadata matches plus the current Host result page. */
|
|
1912
|
-
function SearchResults({ useSessions,
|
|
2008
|
+
function SearchResults({ useSessions, useSessionStatus, open, workspaces, archivedSessionIds, query, remote, resultLimit, usePanelInfo, t }) {
|
|
1913
2009
|
const panelActive = usePanelInfo((info) => info.activePanelId !== null);
|
|
1914
2010
|
const list = useSessions((s) => s);
|
|
1915
|
-
const
|
|
2011
|
+
const statuses = useSessionStatus((s) => s);
|
|
1916
2012
|
const currentRemote = remote.query === query ? remote : {
|
|
1917
2013
|
query,
|
|
1918
2014
|
status: "loading",
|
|
1919
2015
|
items: [],
|
|
1920
2016
|
hasMore: false
|
|
1921
2017
|
};
|
|
1922
|
-
const results = (0, react.useMemo)(() => deriveSearchResults(list, workspaces, query, archivedSessionIds,
|
|
2018
|
+
const results = (0, react.useMemo)(() => deriveSearchResults(list, workspaces, query, archivedSessionIds, statuses, currentRemote, resultLimit), [
|
|
1923
2019
|
list,
|
|
1924
2020
|
workspaces,
|
|
1925
2021
|
query,
|
|
1926
2022
|
archivedSessionIds,
|
|
1927
|
-
|
|
2023
|
+
statuses,
|
|
1928
2024
|
currentRemote,
|
|
1929
2025
|
resultLimit
|
|
1930
2026
|
]);
|
|
1931
2027
|
const pending = currentRemote.status === "loading";
|
|
1932
2028
|
const failed = currentRemote.status === "error";
|
|
2029
|
+
const currentId = panelActive ? void 0 : Object.values(list.byId).find((session) => (session.retainedBy.mainView ?? 0) > 0)?.id;
|
|
1933
2030
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
1934
2031
|
className: clsx(WorkspaceBrowser_module_css_default.treeBody, WorkspaceBrowser_module_css_default.wide),
|
|
1935
2032
|
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1941,7 +2038,7 @@ window.__ModuleLoader__.load({
|
|
|
1941
2038
|
"aria-label": t("search.results.aria"),
|
|
1942
2039
|
children: results.items.map((result) => (0, react_jsx_runtime.jsx)(SearchResultItem, {
|
|
1943
2040
|
result,
|
|
1944
|
-
currentId
|
|
2041
|
+
currentId,
|
|
1945
2042
|
onOpen: open,
|
|
1946
2043
|
t
|
|
1947
2044
|
}, result.id))
|
|
@@ -1973,8 +2070,9 @@ window.__ModuleLoader__.load({
|
|
|
1973
2070
|
* @param props - composed slot props (shell owner share + store + injected actions).
|
|
1974
2071
|
* @returns the region element tree.
|
|
1975
2072
|
*/
|
|
1976
|
-
function WorkspaceBrowser({ wide, usePanelInfo, expandSidebar, useSessions,
|
|
2073
|
+
function WorkspaceBrowser({ wide, usePanelInfo, expandSidebar, useSessions, useSessionStatus, useWorkspaces, useStore, actions, startSession, open, renameSession, forkSession, renameWorkspace, deleteWorkspace, insertWorkspaceBefore, archiveSession, createWorkspace, searchSessions, searchResultLimit, useDirectoryFlow, useHostInfo, renderSlot, t }) {
|
|
1977
2074
|
const home = useHostInfo((info) => info.home);
|
|
2075
|
+
const list = useSessions((state) => state);
|
|
1978
2076
|
const workspaces = useWorkspaces((state) => state.items);
|
|
1979
2077
|
const workspacePhase = useWorkspaces((state) => state.phase);
|
|
1980
2078
|
const workspaceStreamState = useWorkspaces((state) => state.state);
|
|
@@ -1984,34 +2082,55 @@ window.__ModuleLoader__.load({
|
|
|
1984
2082
|
const orderBy = useStore((s) => s.orderBy);
|
|
1985
2083
|
const groupExpansion = useStore((s) => s.groupExpansion);
|
|
1986
2084
|
const sessionOrderByAccount = useStore((s) => s.sessionOrderByAccount);
|
|
1987
|
-
const
|
|
1988
|
-
const
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
(0, react.
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
promotedBlank.current = {
|
|
2002
|
-
sessionId: currentBlankSessionId,
|
|
2003
|
-
accountKey: currentBlankAccount
|
|
2085
|
+
const workspaceReady = workspacePhase === "ready" && workspaceStreamState !== "loading";
|
|
2086
|
+
const mainSessionId = Object.values(list.byId).find((session) => (session.retainedBy.mainView ?? 0) > 0)?.id;
|
|
2087
|
+
const currentBlank = mainSessionId !== void 0 && list.byId[mainSessionId]?.blank === true ? mainSessionId : void 0;
|
|
2088
|
+
const ungroupedMemberIds = (0, react.useMemo)(() => {
|
|
2089
|
+
const accounted = new Set(workspaces.flatMap((workspace) => workspace.sessionIds));
|
|
2090
|
+
return list.ids.filter((id) => list.byId[id] !== void 0 && !accounted.has(id));
|
|
2091
|
+
}, [list, workspaces]);
|
|
2092
|
+
const flatMemberIds = (0, react.useMemo)(() => visibleSessionIds(list, archivedSessionIds), [archivedSessionIds, list]);
|
|
2093
|
+
const orderedWorkspaces = (0, react.useMemo)(() => workspaces.map((workspace) => {
|
|
2094
|
+
const memberIds = workspace.sessionIds;
|
|
2095
|
+
const baseOrder = orderBy === "updated" ? orderByRecency(memberIds, list.byId) : reconcileManualOrder(memberIds, sessionOrderByAccount[workspace.workspaceId], list.byId);
|
|
2096
|
+
return {
|
|
2097
|
+
...workspace,
|
|
2098
|
+
sessionIds: pinCurrentBlank(baseOrder, currentBlank !== void 0 && memberIds.includes(currentBlank) ? currentBlank : void 0)
|
|
2004
2099
|
};
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2100
|
+
}), [
|
|
2101
|
+
currentBlank,
|
|
2102
|
+
list.byId,
|
|
2103
|
+
orderBy,
|
|
2104
|
+
sessionOrderByAccount,
|
|
2105
|
+
workspaces
|
|
2106
|
+
]);
|
|
2107
|
+
const orderedUngroupedSessionIds = (0, react.useMemo)(() => {
|
|
2108
|
+
return pinCurrentBlank(orderBy === "updated" ? orderByRecency(ungroupedMemberIds, list.byId) : reconcileManualOrder(ungroupedMemberIds, sessionOrderByAccount[""], list.byId), currentBlank !== void 0 && ungroupedMemberIds.includes(currentBlank) ? currentBlank : void 0);
|
|
2009
2109
|
}, [
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2110
|
+
currentBlank,
|
|
2111
|
+
list.byId,
|
|
2112
|
+
orderBy,
|
|
2113
|
+
sessionOrderByAccount,
|
|
2114
|
+
ungroupedMemberIds
|
|
2115
|
+
]);
|
|
2116
|
+
const orderedFlatSessionIds = (0, react.useMemo)(() => {
|
|
2117
|
+
return pinCurrentBlank(orderBy === "updated" ? orderByRecency(flatMemberIds, list.byId) : reconcileManualOrder(flatMemberIds, sessionOrderByAccount[FLAT_SESSION_ORDER_KEY], list.byId), currentBlank !== void 0 && flatMemberIds.includes(currentBlank) ? currentBlank : void 0);
|
|
2118
|
+
}, [
|
|
2119
|
+
currentBlank,
|
|
2120
|
+
flatMemberIds,
|
|
2121
|
+
list.byId,
|
|
2122
|
+
orderBy,
|
|
2013
2123
|
sessionOrderByAccount
|
|
2014
2124
|
]);
|
|
2125
|
+
const activeSessionOrders = (0, react.useMemo)(() => Object.fromEntries([
|
|
2126
|
+
...orderedWorkspaces.map((workspace) => [workspace.workspaceId, workspace.sessionIds]),
|
|
2127
|
+
["", orderedUngroupedSessionIds],
|
|
2128
|
+
[FLAT_SESSION_ORDER_KEY, orderedFlatSessionIds]
|
|
2129
|
+
]), [
|
|
2130
|
+
orderedFlatSessionIds,
|
|
2131
|
+
orderedUngroupedSessionIds,
|
|
2132
|
+
orderedWorkspaces
|
|
2133
|
+
]);
|
|
2015
2134
|
(0, react.useEffect)(() => {
|
|
2016
2135
|
if (workspacePhase !== "ready") return;
|
|
2017
2136
|
actions.retainAccountKeys([
|
|
@@ -2024,6 +2143,44 @@ window.__ModuleLoader__.load({
|
|
|
2024
2143
|
workspacePhase,
|
|
2025
2144
|
workspaces
|
|
2026
2145
|
]);
|
|
2146
|
+
(0, react.useEffect)(() => {
|
|
2147
|
+
if (list.phase !== "ready" || workspaceReady || orderBy !== "manual" || currentBlank === void 0) return;
|
|
2148
|
+
const changed = {};
|
|
2149
|
+
for (const [key, ids] of Object.entries(activeSessionOrders)) {
|
|
2150
|
+
if (key !== "__flat_session_order__" && workspacePhase !== "ready") continue;
|
|
2151
|
+
const saved = sessionOrderByAccount[key] ?? [];
|
|
2152
|
+
if (ids[0] !== currentBlank || saved[0] === currentBlank) continue;
|
|
2153
|
+
changed[key] = [currentBlank, ...saved.filter((id) => id !== currentBlank)];
|
|
2154
|
+
}
|
|
2155
|
+
if (Object.keys(changed).length > 0) actions.syncSessionOrders(changed);
|
|
2156
|
+
}, [
|
|
2157
|
+
actions.syncSessionOrders,
|
|
2158
|
+
activeSessionOrders,
|
|
2159
|
+
currentBlank,
|
|
2160
|
+
list.phase,
|
|
2161
|
+
orderBy,
|
|
2162
|
+
sessionOrderByAccount,
|
|
2163
|
+
workspacePhase,
|
|
2164
|
+
workspaceReady
|
|
2165
|
+
]);
|
|
2166
|
+
(0, react.useEffect)(() => {
|
|
2167
|
+
if (list.phase !== "ready" || !workspaceReady || orderBy !== "manual") return;
|
|
2168
|
+
const changed = Object.fromEntries(Object.entries(activeSessionOrders).filter(([key, ids]) => {
|
|
2169
|
+
const saved = sessionOrderByAccount[key];
|
|
2170
|
+
return saved === void 0 || saved.length !== ids.length || ids.some((id, index) => id !== saved[index]);
|
|
2171
|
+
}));
|
|
2172
|
+
if (Object.keys(changed).length > 0) actions.syncSessionOrders(changed);
|
|
2173
|
+
}, [
|
|
2174
|
+
actions.syncSessionOrders,
|
|
2175
|
+
activeSessionOrders,
|
|
2176
|
+
list.phase,
|
|
2177
|
+
orderBy,
|
|
2178
|
+
sessionOrderByAccount,
|
|
2179
|
+
workspaceReady
|
|
2180
|
+
]);
|
|
2181
|
+
const saveSessionOrder = (accountKey, order) => {
|
|
2182
|
+
actions.setSessionOrder(accountKey, order, activeSessionOrders);
|
|
2183
|
+
};
|
|
2027
2184
|
const [query, setQuery] = (0, react.useState)("");
|
|
2028
2185
|
const [searchExpanded, setSearchExpanded] = (0, react.useState)(false);
|
|
2029
2186
|
const [revealSessionId, setRevealSessionId] = (0, react.useState)(void 0);
|
|
@@ -2296,7 +2453,7 @@ window.__ModuleLoader__.load({
|
|
|
2296
2453
|
actions.setGroupBy(mode);
|
|
2297
2454
|
},
|
|
2298
2455
|
onOrderPick: (mode) => {
|
|
2299
|
-
actions.setOrderBy(mode);
|
|
2456
|
+
actions.setOrderBy(mode, activeSessionOrders);
|
|
2300
2457
|
},
|
|
2301
2458
|
t
|
|
2302
2459
|
}), directoryFlowAvailable && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
@@ -2357,7 +2514,7 @@ window.__ModuleLoader__.load({
|
|
|
2357
2514
|
children: wide && (normalizedQuery !== "" ? (0, react_jsx_runtime.jsx)(SearchResults, {
|
|
2358
2515
|
usePanelInfo,
|
|
2359
2516
|
useSessions,
|
|
2360
|
-
|
|
2517
|
+
useSessionStatus,
|
|
2361
2518
|
open: openSearchResult,
|
|
2362
2519
|
workspaces,
|
|
2363
2520
|
archivedSessionIds,
|
|
@@ -2367,42 +2524,35 @@ window.__ModuleLoader__.load({
|
|
|
2367
2524
|
t
|
|
2368
2525
|
}) : groupBy === "flat" ? (0, react_jsx_runtime.jsx)(FlatList, {
|
|
2369
2526
|
usePanelInfo,
|
|
2370
|
-
|
|
2371
|
-
|
|
2527
|
+
list,
|
|
2528
|
+
sessionIds: orderedFlatSessionIds,
|
|
2529
|
+
useSessionStatus,
|
|
2372
2530
|
open,
|
|
2373
2531
|
forkSession,
|
|
2374
2532
|
onSessionRename,
|
|
2375
2533
|
onSessionArchive,
|
|
2376
|
-
|
|
2377
|
-
orderBy,
|
|
2378
|
-
sessionOrderByAccount,
|
|
2379
|
-
sessionUpdatedAtByAccount,
|
|
2380
|
-
syncSessionOrderAccount: actions.syncSessionOrderAccount,
|
|
2381
|
-
setSessionOrder: actions.setSessionOrder,
|
|
2534
|
+
setSessionOrder: saveSessionOrder,
|
|
2382
2535
|
revealSessionId,
|
|
2383
2536
|
onSessionRevealed: acknowledgeSessionReveal,
|
|
2384
2537
|
t
|
|
2385
2538
|
}) : (0, react_jsx_runtime.jsx)(SessionTree, {
|
|
2386
2539
|
usePanelInfo,
|
|
2387
|
-
|
|
2388
|
-
|
|
2540
|
+
list,
|
|
2541
|
+
useSessionStatus,
|
|
2389
2542
|
onSessionRename,
|
|
2390
2543
|
onSessionArchive,
|
|
2391
2544
|
forkSession,
|
|
2392
|
-
workspaces,
|
|
2393
|
-
|
|
2545
|
+
workspaces: orderedWorkspaces,
|
|
2546
|
+
ungroupedSessionIds: orderedUngroupedSessionIds,
|
|
2547
|
+
workspaceReady,
|
|
2548
|
+
nestWorkspaces: groupBy === "workspace-tree",
|
|
2394
2549
|
groupExpansion,
|
|
2395
2550
|
setGroupExpanded: actions.setGroupExpanded,
|
|
2396
|
-
|
|
2397
|
-
sessionUpdatedAtByAccount,
|
|
2398
|
-
syncSessionOrderAccount: actions.syncSessionOrderAccount,
|
|
2399
|
-
setSessionOrder: actions.setSessionOrder,
|
|
2551
|
+
setSessionOrder: saveSessionOrder,
|
|
2400
2552
|
archivedSessionIds,
|
|
2401
2553
|
startSession,
|
|
2402
2554
|
open,
|
|
2403
2555
|
insertWorkspaceBefore,
|
|
2404
|
-
insertSessionBefore,
|
|
2405
|
-
orderBy,
|
|
2406
2556
|
revealSessionId,
|
|
2407
2557
|
onSessionRevealed: acknowledgeSessionReveal,
|
|
2408
2558
|
home,
|
|
@@ -2573,6 +2723,7 @@ window.__ModuleLoader__.load({
|
|
|
2573
2723
|
"viewOptions.label": "视图选项",
|
|
2574
2724
|
"groupBy.label": "分组方式",
|
|
2575
2725
|
"groupBy.workspace": "按工作区",
|
|
2726
|
+
"groupBy.workspaceTree": "按工作区树",
|
|
2576
2727
|
"groupBy.flat": "单列表",
|
|
2577
2728
|
"orderBy.label": "排序方式",
|
|
2578
2729
|
"orderBy.manual": "手动排序",
|
|
@@ -2639,6 +2790,7 @@ window.__ModuleLoader__.load({
|
|
|
2639
2790
|
"viewOptions.label": "View options",
|
|
2640
2791
|
"groupBy.label": "Group by",
|
|
2641
2792
|
"groupBy.workspace": "WorkSpace",
|
|
2793
|
+
"groupBy.workspaceTree": "Workspace Tree",
|
|
2642
2794
|
"groupBy.flat": "In one list",
|
|
2643
2795
|
"orderBy.label": "Order by",
|
|
2644
2796
|
"orderBy.manual": "Manual",
|
|
@@ -2758,9 +2910,7 @@ window.__ModuleLoader__.load({
|
|
|
2758
2910
|
searchSessions,
|
|
2759
2911
|
searchResultLimit: sessions.searchResultLimit,
|
|
2760
2912
|
renameSession: async (sessionId, title) => {
|
|
2761
|
-
const
|
|
2762
|
-
if (session === void 0) throw new Error(`unknown session "${sessionId}"`);
|
|
2763
|
-
const result = await session.rename(title);
|
|
2913
|
+
const result = await sessions.using(sessionId, { source: "workspaceOperation" }, (reference) => reference.binding.session.rename(title));
|
|
2764
2914
|
if (!result.ok) throw new Error(result.error.message);
|
|
2765
2915
|
},
|
|
2766
2916
|
forkSession: (sessionId) => {
|
|
@@ -2778,9 +2928,6 @@ window.__ModuleLoader__.load({
|
|
|
2778
2928
|
archiveSession: async (sessionId) => {
|
|
2779
2929
|
await uiWorkspace.archiveSession(sessionId);
|
|
2780
2930
|
},
|
|
2781
|
-
insertSessionBefore: async (workspaceId, sessionId, beforeSessionId) => {
|
|
2782
|
-
await workspaces.insertSessionBefore(workspaceId, sessionId, beforeSessionId);
|
|
2783
|
-
},
|
|
2784
2931
|
createWorkspace: (input) => workspaces.create(input),
|
|
2785
2932
|
hooks: {
|
|
2786
2933
|
directoryFlow: browserFlowSource,
|