@deepseek-ai/dsh-client-ui-workspace 0.1.6-alpha.1 → 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 +10 -2
- package/README.zh.md +10 -2
- package/lib/client.js +403 -217
- 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 +8 -4
- package/lib/types/client/rows/Rows.d.ts +3 -1
- package/lib/types/client/rows/WorkspaceBrowser.d.ts +1 -1
- package/lib/types/client/stores.d.ts +3 -3
- package/lib/types/client/tree.d.ts +17 -9
- package/package.json +21 -20
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,7 @@ 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();
|
|
99
107
|
}
|
|
100
108
|
async unarchiveSession(sessionId) {
|
|
101
109
|
await this.workspaces.unarchiveSession(sessionId);
|
|
@@ -124,10 +132,24 @@ window.__ModuleLoader__.load({
|
|
|
124
132
|
const workspace = this.workspaces.list.getSnapshot();
|
|
125
133
|
const sessions = this.sessions.list.getSnapshot();
|
|
126
134
|
if (workspace.phase !== "ready" || sessions.phase !== "ready") return;
|
|
127
|
-
if (
|
|
135
|
+
if (this.mainReference !== void 0) {
|
|
128
136
|
initial = "done";
|
|
129
137
|
return;
|
|
130
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
|
+
}
|
|
131
153
|
const target = recentWorkspace(workspace.items, sessions.byId);
|
|
132
154
|
if (target === void 0) {
|
|
133
155
|
initial = "done";
|
|
@@ -135,8 +157,8 @@ window.__ModuleLoader__.load({
|
|
|
135
157
|
}
|
|
136
158
|
initial = "connecting";
|
|
137
159
|
this.connectWorkspace(target).then((sessionId) => {
|
|
138
|
-
if (this.
|
|
139
|
-
|
|
160
|
+
if (this.mainReference === void 0) this.openSession(sessionId);
|
|
161
|
+
}).then(() => {
|
|
140
162
|
initial = "done";
|
|
141
163
|
}, (reason) => {
|
|
142
164
|
if (this.lifetime.signal.aborted) return;
|
|
@@ -155,11 +177,43 @@ window.__ModuleLoader__.load({
|
|
|
155
177
|
}
|
|
156
178
|
/** @returns true when an archived current selection was cleared. */
|
|
157
179
|
clearArchivedCurrent() {
|
|
158
|
-
const current = this.
|
|
180
|
+
const current = this.mainReference?.sessionId;
|
|
159
181
|
if (current === void 0 || !this.workspaces.list.getSnapshot().archivedSessionIds.includes(current)) return false;
|
|
160
|
-
this.
|
|
182
|
+
this.clearMain();
|
|
161
183
|
return true;
|
|
162
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
|
+
}
|
|
163
217
|
};
|
|
164
218
|
/** Stable tie-breaking follows Host Workspace order. */
|
|
165
219
|
function recentWorkspace(workspaces, sessions) {
|
|
@@ -323,6 +377,9 @@ window.__ModuleLoader__.load({
|
|
|
323
377
|
function owningGroupKey(workspaces, sessionId) {
|
|
324
378
|
return workspaces.find((workspace) => workspace.sessionIds.includes(sessionId))?.workspaceId ?? "";
|
|
325
379
|
}
|
|
380
|
+
function mainSessionId(list) {
|
|
381
|
+
return Object.values(list.byId).find((session) => (session.retainedBy.mainView ?? 0) > 0)?.id;
|
|
382
|
+
}
|
|
326
383
|
/**
|
|
327
384
|
* Directory display label: basename of the path (both separators accepted).
|
|
328
385
|
* Ungrouped-bucket fallback for surfaces without a workspace title.
|
|
@@ -434,6 +491,7 @@ window.__ModuleLoader__.load({
|
|
|
434
491
|
* recency before that order is initialized.
|
|
435
492
|
*/
|
|
436
493
|
function groupByWorkspace(list, workspaces, archived, ungroupedOrder) {
|
|
494
|
+
const current = mainSessionId(list);
|
|
437
495
|
const groups = [];
|
|
438
496
|
const accounted = /* @__PURE__ */ new Set();
|
|
439
497
|
for (const workspace of workspaces) {
|
|
@@ -442,12 +500,12 @@ window.__ModuleLoader__.load({
|
|
|
442
500
|
const summary = list.byId[id];
|
|
443
501
|
if (summary === void 0) continue;
|
|
444
502
|
accounted.add(id);
|
|
445
|
-
if (!sessionVisible(summary,
|
|
503
|
+
if (!sessionVisible(summary, current, archived)) continue;
|
|
446
504
|
members.push(summary);
|
|
447
505
|
}
|
|
448
506
|
groups.push(buildGroup(workspace.workspaceId, workspace.workspaceId, workspace.path, Date.parse(workspace.createdAt), workspace.title, members));
|
|
449
507
|
}
|
|
450
|
-
const stray = list.ids.map((id) => list.byId[id]).filter((s) => s !== void 0 && !accounted.has(s.id) && sessionVisible(s,
|
|
508
|
+
const stray = list.ids.map((id) => list.byId[id]).filter((s) => s !== void 0 && !accounted.has(s.id) && sessionVisible(s, current, archived));
|
|
451
509
|
if (stray.length > 0) groups.push(buildGroup("", void 0, void 0, void 0, "", orderedUngrouped(stray, ungroupedOrder, list.byId)));
|
|
452
510
|
return groups;
|
|
453
511
|
}
|
|
@@ -460,15 +518,16 @@ window.__ModuleLoader__.load({
|
|
|
460
518
|
default: return;
|
|
461
519
|
}
|
|
462
520
|
}
|
|
463
|
-
function sessionNode(s, descendants,
|
|
464
|
-
const
|
|
521
|
+
function sessionNode(s, descendants, statuses) {
|
|
522
|
+
const status = statuses.get(s.id);
|
|
523
|
+
const pendingInteraction = visiblePendingKind(status?.pendingInteraction?.kind);
|
|
465
524
|
return {
|
|
466
525
|
id: s.id,
|
|
467
526
|
title: sessionTitle(s),
|
|
468
527
|
blank: s.blank,
|
|
469
|
-
running: s.running,
|
|
528
|
+
running: status?.running ?? s.running,
|
|
470
529
|
runningSubagentCount: descendants.get(s.id)?.runningCount ?? 0,
|
|
471
|
-
completed:
|
|
530
|
+
completed: status?.completionUnread === true,
|
|
472
531
|
hasActiveSchedule: hasActiveSchedule(s),
|
|
473
532
|
updatedAt: s.updatedAt,
|
|
474
533
|
...pendingInteraction === void 0 ? {} : { pendingInteraction }
|
|
@@ -482,18 +541,19 @@ window.__ModuleLoader__.load({
|
|
|
482
541
|
* provisional New Session row; archived sessions are excluded everywhere.
|
|
483
542
|
* Content search lives outside this derivation
|
|
484
543
|
* (see {@link deriveSearchResults}).
|
|
485
|
-
* @param list - sessions list snapshot (`
|
|
544
|
+
* @param list - sessions list snapshot (`mainView` retention feeds containsCurrent).
|
|
486
545
|
* @param workspaces - real Workspaces in Host group order with caller-projected Session order.
|
|
487
546
|
* @param archivedSessionIds - registry-global archive set.
|
|
488
|
-
* @param
|
|
547
|
+
* @param statuses - unified UI status by Session.
|
|
489
548
|
* @param view - local expansion arrays.
|
|
490
549
|
* @returns group sections in render order.
|
|
491
550
|
*/
|
|
492
|
-
function deriveGroups(list, workspaces, archivedSessionIds,
|
|
551
|
+
function deriveGroups(list, workspaces, archivedSessionIds, statuses, view) {
|
|
493
552
|
const archived = new Set(archivedSessionIds);
|
|
494
553
|
const expandedGroups = new Set(view.expandedGroups);
|
|
495
554
|
const descendants = indexSubagentDescendants(list.byId);
|
|
496
|
-
const
|
|
555
|
+
const current = mainSessionId(list);
|
|
556
|
+
const currentGroup = current === void 0 ? void 0 : owningGroupKey(workspaces, current);
|
|
497
557
|
const groups = [];
|
|
498
558
|
for (const g of groupByWorkspace(list, workspaces, archived, view.ungroupedOrder)) {
|
|
499
559
|
const expanded = expandedGroups.has(g.key);
|
|
@@ -506,7 +566,7 @@ window.__ModuleLoader__.load({
|
|
|
506
566
|
sessionCount: g.sessions.length,
|
|
507
567
|
expanded,
|
|
508
568
|
containsCurrent: g.key === currentGroup,
|
|
509
|
-
sessions: expanded ? g.sessions.map((session) => sessionNode(session, descendants,
|
|
569
|
+
sessions: expanded ? g.sessions.map((session) => sessionNode(session, descendants, statuses)) : []
|
|
510
570
|
});
|
|
511
571
|
}
|
|
512
572
|
return groups;
|
|
@@ -519,21 +579,22 @@ window.__ModuleLoader__.load({
|
|
|
519
579
|
*/
|
|
520
580
|
function visibleSessionIds(list, archivedSessionIds) {
|
|
521
581
|
const archived = new Set(archivedSessionIds);
|
|
582
|
+
const current = mainSessionId(list);
|
|
522
583
|
return list.ids.filter((id) => {
|
|
523
584
|
const s = list.byId[id];
|
|
524
|
-
return s !== void 0 && sessionVisible(s,
|
|
585
|
+
return s !== void 0 && sessionVisible(s, current, archived);
|
|
525
586
|
});
|
|
526
587
|
}
|
|
527
588
|
/**
|
|
528
589
|
* Derive flat rows from the browser's ordered visible Session ids.
|
|
529
590
|
* @param list - sessions list snapshot used to select the ids.
|
|
530
591
|
* @param sessionIds - known visible members in render order, including any pinned blank.
|
|
531
|
-
* @param
|
|
592
|
+
* @param statuses - unified UI status by Session.
|
|
532
593
|
* @returns flat rows in the supplied order with current status indicators.
|
|
533
594
|
*/
|
|
534
|
-
function deriveFlat(list, sessionIds,
|
|
595
|
+
function deriveFlat(list, sessionIds, statuses) {
|
|
535
596
|
const descendants = indexSubagentDescendants(list.byId);
|
|
536
|
-
return sessionIds.map((id) => sessionNode(list.byId[id], descendants,
|
|
597
|
+
return sessionIds.map((id) => sessionNode(list.byId[id], descendants, statuses));
|
|
537
598
|
}
|
|
538
599
|
/**
|
|
539
600
|
* Merge immediate title/Workspace substring matches with ranked Host content
|
|
@@ -543,12 +604,12 @@ window.__ModuleLoader__.load({
|
|
|
543
604
|
* @param workspaces - Workspace membership and display labels.
|
|
544
605
|
* @param query - caller text; surrounding whitespace is ignored.
|
|
545
606
|
* @param archivedSessionIds - registry-global archive set (members never match).
|
|
546
|
-
* @param
|
|
607
|
+
* @param statuses - unified UI status by Session.
|
|
547
608
|
* @param content - ranked Host content-search page.
|
|
548
609
|
* @param limit - protocol-owned maximum merged row count.
|
|
549
610
|
* @returns bounded deduplicated flat rows and a refine-query hint bit.
|
|
550
611
|
*/
|
|
551
|
-
function deriveSearchResults(list, workspaces, query, archivedSessionIds,
|
|
612
|
+
function deriveSearchResults(list, workspaces, query, archivedSessionIds, statuses, content, limit) {
|
|
552
613
|
const q = query.trim().toLowerCase();
|
|
553
614
|
if (q === "") return {
|
|
554
615
|
items: [],
|
|
@@ -556,6 +617,7 @@ window.__ModuleLoader__.load({
|
|
|
556
617
|
};
|
|
557
618
|
const archived = new Set(archivedSessionIds);
|
|
558
619
|
const descendants = indexSubagentDescendants(list.byId);
|
|
620
|
+
const current = mainSessionId(list);
|
|
559
621
|
const workspaceBySession = /* @__PURE__ */ new Map();
|
|
560
622
|
for (const workspace of workspaces) for (const sessionId of workspace.sessionIds) if (!workspaceBySession.has(sessionId)) workspaceBySession.set(sessionId, workspace.title);
|
|
561
623
|
const labelOf = (summary) => workspaceBySession.get(summary.id) ?? workspaceLabel(summary.cwd);
|
|
@@ -564,7 +626,7 @@ window.__ModuleLoader__.load({
|
|
|
564
626
|
const local = [];
|
|
565
627
|
for (const id of list.ids) {
|
|
566
628
|
const summary = list.byId[id];
|
|
567
|
-
if (summary === void 0 || summary.blank || !sessionVisible(summary,
|
|
629
|
+
if (summary === void 0 || summary.blank || !sessionVisible(summary, current, archived)) continue;
|
|
568
630
|
if (sessionTitle(summary).toLowerCase().includes(q) || labelOf(summary).toLowerCase().includes(q)) local.push(summary);
|
|
569
631
|
}
|
|
570
632
|
const localById = new Map(local.map((summary) => [summary.id, summary]));
|
|
@@ -579,20 +641,21 @@ window.__ModuleLoader__.load({
|
|
|
579
641
|
for (const summary of orderedLocal) include(summary);
|
|
580
642
|
for (const item of content.items) {
|
|
581
643
|
const summary = list.byId[item.sessionId];
|
|
582
|
-
if (summary !== void 0 && !summary.blank && sessionVisible(summary,
|
|
644
|
+
if (summary !== void 0 && !summary.blank && sessionVisible(summary, current, archived)) include(summary);
|
|
583
645
|
}
|
|
584
646
|
return {
|
|
585
647
|
items: ordered.slice(0, limit).map((summary) => {
|
|
586
648
|
const match = contentBySession.get(summary.id);
|
|
587
|
-
const
|
|
649
|
+
const status = statuses.get(summary.id);
|
|
650
|
+
const pendingInteraction = visiblePendingKind(status?.pendingInteraction?.kind);
|
|
588
651
|
return {
|
|
589
652
|
id: summary.id,
|
|
590
653
|
title: sessionTitle(summary),
|
|
591
654
|
workspace: labelOf(summary),
|
|
592
|
-
running: summary.running,
|
|
655
|
+
running: status?.running ?? summary.running,
|
|
593
656
|
runningSubagentCount: descendants.get(summary.id)?.runningCount ?? 0,
|
|
594
657
|
...pendingInteraction === void 0 ? {} : { pendingInteraction },
|
|
595
|
-
completed:
|
|
658
|
+
completed: status?.completionUnread === true,
|
|
596
659
|
hasActiveSchedule: hasActiveSchedule(summary),
|
|
597
660
|
...match === void 0 ? {} : { snippet: match.snippet }
|
|
598
661
|
};
|
|
@@ -600,9 +663,33 @@ window.__ModuleLoader__.load({
|
|
|
600
663
|
hasMore: content.hasMore || ordered.length > limit
|
|
601
664
|
};
|
|
602
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
|
+
}
|
|
603
690
|
//#endregion
|
|
604
691
|
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-workspace/src/client/rows/Rows.module.css.mjs
|
|
605
|
-
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}}";
|
|
606
693
|
const tagId$2 = "@deepseek-ai/dsh-client-ui-workspace/Rows.module.css";
|
|
607
694
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
|
|
608
695
|
const tag = document.createElement("style");
|
|
@@ -654,14 +741,39 @@ window.__ModuleLoader__.load({
|
|
|
654
741
|
/**
|
|
655
742
|
* Workspace browser tree row components (figma Cell set 14:3080): pure presentational —
|
|
656
743
|
* all data and callbacks arrive via props. Hover swaps (folder->chevron,
|
|
657
|
-
* time->ellipsis, action buttons) are CSS-only
|
|
658
|
-
*
|
|
659
|
-
*
|
|
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.
|
|
660
748
|
*/
|
|
661
749
|
/** Row display title: blank rows show the localized New Session label. */
|
|
662
750
|
function displayTitle(node, t) {
|
|
663
751
|
return node.blank ? t("session.new") : node.title;
|
|
664
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
|
+
}
|
|
665
777
|
/** Localized compact relative time ("刚刚"/"5分钟" in zh, "now"/"5min" in en). */
|
|
666
778
|
function timeLabel(updatedAt, now, t) {
|
|
667
779
|
const { unit, n } = (0, _deepseek_ai_dsh_client_ui_primitives.relativeTime)(updatedAt, now);
|
|
@@ -717,6 +829,7 @@ window.__ModuleLoader__.load({
|
|
|
717
829
|
* Workspace shows its hover card (the ungrouped bucket has none).
|
|
718
830
|
* `containsCurrent` arrives on the node (derivation fact, no renderer scan).
|
|
719
831
|
* @param props.group - derived group node.
|
|
832
|
+
* @param props.containsCurrentDescendant - highlight an ancestor even when its subtree is collapsed.
|
|
720
833
|
* @param props.onToggle - expand/collapse the group.
|
|
721
834
|
* @param props.onCreate - start a frontend Session inside this Workspace.
|
|
722
835
|
* @param props.drag - optional workspace-row drag wiring.
|
|
@@ -724,10 +837,10 @@ window.__ModuleLoader__.load({
|
|
|
724
837
|
* @param props.t - the browser root's locale seat.
|
|
725
838
|
* @returns the row element.
|
|
726
839
|
*/
|
|
727
|
-
function ProjectRowItem({ group, onToggle, onCreate, actions, drag, home, t }) {
|
|
840
|
+
function ProjectRowItem({ group, containsCurrentDescendant = false, onToggle, onCreate, actions, drag, home, t }) {
|
|
728
841
|
const row = group;
|
|
729
842
|
const label = row.workspaceId === void 0 ? t("group.ungrouped") : row.label;
|
|
730
|
-
const active = group.expanded && group.containsCurrent;
|
|
843
|
+
const active = containsCurrentDescendant || group.expanded && group.containsCurrent;
|
|
731
844
|
const [menuOpen, setMenuOpen] = (0, react.useState)(false);
|
|
732
845
|
const workspaceMenuItems = [{
|
|
733
846
|
id: "rename",
|
|
@@ -991,6 +1104,7 @@ window.__ModuleLoader__.load({
|
|
|
991
1104
|
const draggable = drag !== void 0 && !row.blank;
|
|
992
1105
|
const [menuOpen, setMenuOpen] = (0, react.useState)(false);
|
|
993
1106
|
const rowRef = (0, react.useRef)(null);
|
|
1107
|
+
const titleRef = (0, react.useRef)(null);
|
|
994
1108
|
(0, react.useEffect)(() => {
|
|
995
1109
|
if (onReveal === void 0) return;
|
|
996
1110
|
rowRef.current?.scrollIntoView({ block: "nearest" });
|
|
@@ -1022,6 +1136,12 @@ window.__ModuleLoader__.load({
|
|
|
1022
1136
|
onClick: () => {
|
|
1023
1137
|
onOpen(node.id);
|
|
1024
1138
|
},
|
|
1139
|
+
onPointerEnter: () => {
|
|
1140
|
+
revealClippedTitle(titleRef.current, true);
|
|
1141
|
+
},
|
|
1142
|
+
onPointerLeave: () => {
|
|
1143
|
+
revealClippedTitle(titleRef.current, false);
|
|
1144
|
+
},
|
|
1025
1145
|
draggable,
|
|
1026
1146
|
onDragStart: drag === void 0 || row.blank ? void 0 : (e) => {
|
|
1027
1147
|
e.dataTransfer.effectAllowed = "move";
|
|
@@ -1046,6 +1166,7 @@ window.__ModuleLoader__.load({
|
|
|
1046
1166
|
children: showStatus && (0, react_jsx_runtime.jsx)(SessionStatusDots, { statuses })
|
|
1047
1167
|
}),
|
|
1048
1168
|
(0, react_jsx_runtime.jsx)("span", {
|
|
1169
|
+
ref: titleRef,
|
|
1049
1170
|
className: Rows_module_css_default.title,
|
|
1050
1171
|
children: title
|
|
1051
1172
|
}),
|
|
@@ -1267,7 +1388,7 @@ window.__ModuleLoader__.load({
|
|
|
1267
1388
|
}
|
|
1268
1389
|
//#endregion
|
|
1269
1390
|
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-workspace/src/client/rows/WorkspaceBrowser.module.css.mjs
|
|
1270
|
-
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}}";
|
|
1271
1392
|
const tagId = "@deepseek-ai/dsh-client-ui-workspace/WorkspaceBrowser.module.css";
|
|
1272
1393
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
1273
1394
|
const tag = document.createElement("style");
|
|
@@ -1407,6 +1528,10 @@ window.__ModuleLoader__.load({
|
|
|
1407
1528
|
id: "workspace",
|
|
1408
1529
|
label: t("groupBy.workspace")
|
|
1409
1530
|
},
|
|
1531
|
+
{
|
|
1532
|
+
id: "workspace-tree",
|
|
1533
|
+
label: t("groupBy.workspaceTree")
|
|
1534
|
+
},
|
|
1410
1535
|
{
|
|
1411
1536
|
id: "flat",
|
|
1412
1537
|
label: t("groupBy.flat")
|
|
@@ -1431,7 +1556,7 @@ window.__ModuleLoader__.load({
|
|
|
1431
1556
|
],
|
|
1432
1557
|
selectedIds: [groupBy, orderBy],
|
|
1433
1558
|
onSelect: (id) => {
|
|
1434
|
-
if (id === "workspace" || id === "flat") onGroupPick(id);
|
|
1559
|
+
if (id === "workspace" || id === "workspace-tree" || id === "flat") onGroupPick(id);
|
|
1435
1560
|
else if (id === "manual" || id === "updated") onOrderPick(id);
|
|
1436
1561
|
setOpen(false);
|
|
1437
1562
|
},
|
|
@@ -1454,16 +1579,16 @@ window.__ModuleLoader__.load({
|
|
|
1454
1579
|
})
|
|
1455
1580
|
});
|
|
1456
1581
|
}
|
|
1457
|
-
/** Resolve an insertion side
|
|
1582
|
+
/** Resolve an insertion side across the Workspace header, descendants, and Sessions. */
|
|
1458
1583
|
function workspaceGroupHalf(e) {
|
|
1459
1584
|
const rect = e.currentTarget.getBoundingClientRect();
|
|
1460
1585
|
return e.clientY < rect.top + rect.height / 2 ? "before" : "after";
|
|
1461
1586
|
}
|
|
1462
1587
|
/** The scrolling session tree; unmounting drops the sessions subscription and expand-all state. */
|
|
1463
|
-
function SessionTree({ list,
|
|
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 }) {
|
|
1464
1589
|
const panelActive = usePanelInfo((info) => info.activePanelId !== null);
|
|
1465
|
-
const
|
|
1466
|
-
const current = panelActive ? void 0 : list.
|
|
1590
|
+
const statuses = useSessionStatus((s) => s);
|
|
1591
|
+
const current = panelActive ? void 0 : Object.values(list.byId).find((session) => (session.retainedBy.mainView ?? 0) > 0)?.id;
|
|
1467
1592
|
const revealGroup = revealSessionId === void 0 || !workspaceReady ? void 0 : owningGroupKey(workspaces, revealSessionId);
|
|
1468
1593
|
const [expandedSessionGroups, setExpandedSessionGroups] = (0, react.useState)([]);
|
|
1469
1594
|
const [drag, setDrag] = (0, react.useState)(null);
|
|
@@ -1481,23 +1606,44 @@ window.__ModuleLoader__.load({
|
|
|
1481
1606
|
setGroupExpanded,
|
|
1482
1607
|
groupExpansion
|
|
1483
1608
|
]);
|
|
1484
|
-
const
|
|
1485
|
-
|
|
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));
|
|
1626
|
+
}, [
|
|
1627
|
+
groupExpansion,
|
|
1628
|
+
parents,
|
|
1629
|
+
workspaces
|
|
1630
|
+
]);
|
|
1631
|
+
const groups = (0, react.useMemo)(() => deriveGroups(list, workspaces, archivedSessionIds, statuses, {
|
|
1486
1632
|
expandedGroups,
|
|
1487
1633
|
ungroupedOrder: ungroupedSessionIds
|
|
1488
1634
|
}), [
|
|
1489
1635
|
list,
|
|
1490
1636
|
workspaces,
|
|
1491
1637
|
archivedSessionIds,
|
|
1492
|
-
|
|
1638
|
+
statuses,
|
|
1493
1639
|
expandedGroups,
|
|
1494
1640
|
ungroupedSessionIds
|
|
1495
1641
|
]);
|
|
1496
1642
|
(0, react.useEffect)(() => {
|
|
1497
|
-
|
|
1498
|
-
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);
|
|
1499
1644
|
}, [
|
|
1500
1645
|
groupExpansion,
|
|
1646
|
+
parents,
|
|
1501
1647
|
revealGroup,
|
|
1502
1648
|
setGroupExpanded
|
|
1503
1649
|
]);
|
|
@@ -1560,18 +1706,191 @@ window.__ModuleLoader__.load({
|
|
|
1560
1706
|
if (workspaceDropCommitted.current) return;
|
|
1561
1707
|
workspaceDropCommitted.current = true;
|
|
1562
1708
|
setWorkspaceDrag(null);
|
|
1563
|
-
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);
|
|
1564
1712
|
if (rowIndex === -1) return;
|
|
1565
|
-
const anchor = over.half === "before" ? over.id :
|
|
1713
|
+
const anchor = over.half === "before" ? over.id : siblings[rowIndex + 1]?.workspaceId;
|
|
1566
1714
|
if (anchor === activeDrag.workspaceId) return;
|
|
1567
|
-
const sourceIndex =
|
|
1568
|
-
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);
|
|
1569
1717
|
if (sourceIndex !== -1 && (anchorIndex === sourceIndex || anchorIndex === sourceIndex + 1)) return;
|
|
1570
1718
|
insertWorkspaceBefore(activeDrag.workspaceId, anchor).catch((reason) => {
|
|
1571
1719
|
console.warn("workspace reorder rejected:", reason);
|
|
1572
1720
|
});
|
|
1573
1721
|
};
|
|
1574
|
-
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
|
+
};
|
|
1575
1894
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
1576
1895
|
className: clsx(WorkspaceBrowser_module_css_default.treeBody, WorkspaceBrowser_module_css_default.wide),
|
|
1577
1896
|
children: [
|
|
@@ -1586,161 +1905,25 @@ window.__ModuleLoader__.load({
|
|
|
1586
1905
|
children: [groups.length === 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
1587
1906
|
className: WorkspaceBrowser_module_css_default.empty,
|
|
1588
1907
|
children: t("empty.none")
|
|
1589
|
-
}),
|
|
1590
|
-
const workspaceId = group.workspaceId;
|
|
1591
|
-
const collapsed = collapsedSessionRows(group.sessions);
|
|
1592
|
-
const sessionsExpanded = expandedSessionGroups.includes(group.key);
|
|
1593
|
-
const workspaceMarker = workspaceId !== void 0 && workspaceDrag?.over?.id === workspaceId ? workspaceDrag.over.half : null;
|
|
1594
|
-
const workspaceDragProps = workspaceId === void 0 ? void 0 : {
|
|
1595
|
-
start: () => {
|
|
1596
|
-
workspaceDropCommitted.current = false;
|
|
1597
|
-
setWorkspaceDrag({
|
|
1598
|
-
workspaceId,
|
|
1599
|
-
over: null
|
|
1600
|
-
});
|
|
1601
|
-
},
|
|
1602
|
-
end: () => {
|
|
1603
|
-
if (workspaceDrag?.over !== null && workspaceDrag?.over !== void 0) commitWorkspaceDrag(workspaceDrag, workspaceDrag.over);
|
|
1604
|
-
else setWorkspaceDrag(null);
|
|
1605
|
-
workspaceDropCommitted.current = false;
|
|
1606
|
-
}
|
|
1607
|
-
};
|
|
1608
|
-
const hoverWorkspace = workspaceId === void 0 ? void 0 : (half) => {
|
|
1609
|
-
setWorkspaceDrag((active) => active === null ? active : {
|
|
1610
|
-
...active,
|
|
1611
|
-
over: {
|
|
1612
|
-
id: workspaceId,
|
|
1613
|
-
half
|
|
1614
|
-
}
|
|
1615
|
-
});
|
|
1616
|
-
};
|
|
1617
|
-
const dropWorkspace = workspaceId === void 0 ? void 0 : (half) => {
|
|
1618
|
-
if (workspaceDrag === null) return;
|
|
1619
|
-
commitWorkspaceDrag(workspaceDrag, {
|
|
1620
|
-
id: workspaceId,
|
|
1621
|
-
half
|
|
1622
|
-
});
|
|
1623
|
-
};
|
|
1624
|
-
return (0, react_jsx_runtime.jsxs)("div", {
|
|
1625
|
-
className: clsx(WorkspaceBrowser_module_css_default.groupSection, workspaceMarker === "before" && WorkspaceBrowser_module_css_default.workspaceDropBefore, workspaceMarker === "after" && WorkspaceBrowser_module_css_default.workspaceDropAfter),
|
|
1626
|
-
onDragOver: workspaceDrag === null || hoverWorkspace === void 0 ? void 0 : (e) => {
|
|
1627
|
-
e.preventDefault();
|
|
1628
|
-
e.dataTransfer.dropEffect = "move";
|
|
1629
|
-
hoverWorkspace(workspaceGroupHalf(e));
|
|
1630
|
-
},
|
|
1631
|
-
onDrop: workspaceDrag === null || dropWorkspace === void 0 ? void 0 : (e) => {
|
|
1632
|
-
e.preventDefault();
|
|
1633
|
-
dropWorkspace(workspaceGroupHalf(e));
|
|
1634
|
-
},
|
|
1635
|
-
children: [
|
|
1636
|
-
(0, react_jsx_runtime.jsx)(ProjectRowItem, {
|
|
1637
|
-
group,
|
|
1638
|
-
home,
|
|
1639
|
-
t,
|
|
1640
|
-
onToggle: () => {
|
|
1641
|
-
if (group.expanded) setExpandedSessionGroups((keys) => keys.filter((key) => key !== group.key));
|
|
1642
|
-
setGroupExpanded(group.key, !group.expanded);
|
|
1643
|
-
},
|
|
1644
|
-
onCreate: () => {
|
|
1645
|
-
if (group.workspaceId !== void 0) {
|
|
1646
|
-
setGroupExpanded(group.key, true);
|
|
1647
|
-
startSession(group.workspaceId);
|
|
1648
|
-
}
|
|
1649
|
-
},
|
|
1650
|
-
drag: workspaceDragProps,
|
|
1651
|
-
actions: group.workspaceId === void 0 ? void 0 : {
|
|
1652
|
-
rename: () => {
|
|
1653
|
-
/* v8 ignore next -- narrowing guard: the actions object exists only for real-workspace groups. */
|
|
1654
|
-
if (group.workspaceId !== void 0) onRenameRequest(group.workspaceId, group.label);
|
|
1655
|
-
},
|
|
1656
|
-
delete: () => {
|
|
1657
|
-
/* v8 ignore next -- narrowing guard: the actions object exists only for real-workspace groups. */
|
|
1658
|
-
if (group.workspaceId !== void 0) onDeleteRequest(group.workspaceId, group.label);
|
|
1659
|
-
}
|
|
1660
|
-
}
|
|
1661
|
-
}),
|
|
1662
|
-
(sessionsExpanded ? group.sessions : collapsed.rows).map((node) => {
|
|
1663
|
-
const sameGroupDrag = drag !== null && drag.accountKey === group.key;
|
|
1664
|
-
const normalizeHalf = (half) => node.blank ? "after" : half;
|
|
1665
|
-
const dragProps = {
|
|
1666
|
-
start: () => {
|
|
1667
|
-
sessionDropCommitted.current = false;
|
|
1668
|
-
setDrag({
|
|
1669
|
-
accountKey: group.key,
|
|
1670
|
-
sessionId: node.id,
|
|
1671
|
-
over: null
|
|
1672
|
-
});
|
|
1673
|
-
},
|
|
1674
|
-
active: sameGroupDrag,
|
|
1675
|
-
marker: sameGroupDrag && drag.over?.id === node.id ? drag.over.half : null,
|
|
1676
|
-
hover: (half) => {
|
|
1677
|
-
/* v8 ignore next -- narrowing guard: Rows gates hover on `active`, which is false while the drag state is null. */
|
|
1678
|
-
setDrag((d) => d === null ? d : {
|
|
1679
|
-
...d,
|
|
1680
|
-
over: {
|
|
1681
|
-
id: node.id,
|
|
1682
|
-
half: normalizeHalf(half)
|
|
1683
|
-
}
|
|
1684
|
-
});
|
|
1685
|
-
},
|
|
1686
|
-
drop: (half) => {
|
|
1687
|
-
/* v8 ignore next -- narrowing guard: Rows gates drop on `active`, which is false while the drag state is null. */
|
|
1688
|
-
if (drag === null) return;
|
|
1689
|
-
commitSessionDrag(drag, {
|
|
1690
|
-
id: node.id,
|
|
1691
|
-
half: normalizeHalf(half)
|
|
1692
|
-
});
|
|
1693
|
-
},
|
|
1694
|
-
end: () => {
|
|
1695
|
-
if (drag?.over !== null && drag?.over !== void 0) commitSessionDrag(drag, drag.over);
|
|
1696
|
-
else setDrag(null);
|
|
1697
|
-
sessionDropCommitted.current = false;
|
|
1698
|
-
}
|
|
1699
|
-
};
|
|
1700
|
-
return (0, react_jsx_runtime.jsx)(SessionNodeItem, {
|
|
1701
|
-
node,
|
|
1702
|
-
currentId: current,
|
|
1703
|
-
now,
|
|
1704
|
-
onOpen: open,
|
|
1705
|
-
onRename: onSessionRename,
|
|
1706
|
-
onFork: forkSession,
|
|
1707
|
-
onArchive: onSessionArchive,
|
|
1708
|
-
onReveal: node.id === revealSessionId && group.key === revealGroup ? () => {
|
|
1709
|
-
onSessionRevealed(node.id);
|
|
1710
|
-
} : void 0,
|
|
1711
|
-
drag: dragProps,
|
|
1712
|
-
t
|
|
1713
|
-
}, node.id);
|
|
1714
|
-
}),
|
|
1715
|
-
collapsed.hiddenCount > 0 && (0, react_jsx_runtime.jsx)("button", {
|
|
1716
|
-
type: "button",
|
|
1717
|
-
className: WorkspaceBrowser_module_css_default.sessionOverflowButton,
|
|
1718
|
-
"aria-expanded": sessionsExpanded,
|
|
1719
|
-
onClick: () => {
|
|
1720
|
-
setExpandedSessionGroups((keys) => toggled(keys, group.key));
|
|
1721
|
-
},
|
|
1722
|
-
children: sessionsExpanded ? t("sessions.collapse") : t("sessions.expand", { n: collapsed.hiddenCount })
|
|
1723
|
-
})
|
|
1724
|
-
]
|
|
1725
|
-
}, group.key);
|
|
1726
|
-
})]
|
|
1908
|
+
}), rootGroups.map((group) => renderGroup(group, 0))]
|
|
1727
1909
|
}),
|
|
1728
1910
|
(0, react_jsx_runtime.jsx)("span", { className: WorkspaceBrowser_module_css_default.fade })
|
|
1729
1911
|
]
|
|
1730
1912
|
});
|
|
1731
1913
|
}
|
|
1732
1914
|
/** The flat "In one list" body: every session is one draggable top-level row. */
|
|
1733
|
-
function FlatList({ list, sessionIds,
|
|
1915
|
+
function FlatList({ list, sessionIds, useSessionStatus, open, forkSession, onSessionRename, onSessionArchive, usePanelInfo, setSessionOrder, revealSessionId, onSessionRevealed, t }) {
|
|
1734
1916
|
const panelActive = usePanelInfo((info) => info.activePanelId !== null);
|
|
1735
|
-
const
|
|
1736
|
-
const rows = (0, react.useMemo)(() => deriveFlat(list, sessionIds,
|
|
1917
|
+
const statuses = useSessionStatus((s) => s);
|
|
1918
|
+
const rows = (0, react.useMemo)(() => deriveFlat(list, sessionIds, statuses), [
|
|
1737
1919
|
list,
|
|
1738
1920
|
sessionIds,
|
|
1739
|
-
|
|
1921
|
+
statuses
|
|
1740
1922
|
]);
|
|
1741
1923
|
const [drag, setDrag] = (0, react.useState)(null);
|
|
1742
1924
|
const dropCommitted = (0, react.useRef)(false);
|
|
1743
1925
|
useNativeDragAcceptance(drag !== null);
|
|
1926
|
+
const currentId = panelActive ? void 0 : Object.values(list.byId).find((session) => (session.retainedBy.mainView ?? 0) > 0)?.id;
|
|
1744
1927
|
const commitDrag = (activeDrag, over) => {
|
|
1745
1928
|
if (dropCommitted.current) return;
|
|
1746
1929
|
dropCommitted.current = true;
|
|
@@ -1773,7 +1956,7 @@ window.__ModuleLoader__.load({
|
|
|
1773
1956
|
const normalizeHalf = (half) => node.blank ? "after" : half;
|
|
1774
1957
|
return (0, react_jsx_runtime.jsx)(SessionNodeItem, {
|
|
1775
1958
|
node,
|
|
1776
|
-
currentId
|
|
1959
|
+
currentId,
|
|
1777
1960
|
now,
|
|
1778
1961
|
onOpen: open,
|
|
1779
1962
|
onRename: onSessionRename,
|
|
@@ -1822,27 +2005,28 @@ window.__ModuleLoader__.load({
|
|
|
1822
2005
|
});
|
|
1823
2006
|
}
|
|
1824
2007
|
/** Flat search body: local metadata matches plus the current Host result page. */
|
|
1825
|
-
function SearchResults({ useSessions,
|
|
2008
|
+
function SearchResults({ useSessions, useSessionStatus, open, workspaces, archivedSessionIds, query, remote, resultLimit, usePanelInfo, t }) {
|
|
1826
2009
|
const panelActive = usePanelInfo((info) => info.activePanelId !== null);
|
|
1827
2010
|
const list = useSessions((s) => s);
|
|
1828
|
-
const
|
|
2011
|
+
const statuses = useSessionStatus((s) => s);
|
|
1829
2012
|
const currentRemote = remote.query === query ? remote : {
|
|
1830
2013
|
query,
|
|
1831
2014
|
status: "loading",
|
|
1832
2015
|
items: [],
|
|
1833
2016
|
hasMore: false
|
|
1834
2017
|
};
|
|
1835
|
-
const results = (0, react.useMemo)(() => deriveSearchResults(list, workspaces, query, archivedSessionIds,
|
|
2018
|
+
const results = (0, react.useMemo)(() => deriveSearchResults(list, workspaces, query, archivedSessionIds, statuses, currentRemote, resultLimit), [
|
|
1836
2019
|
list,
|
|
1837
2020
|
workspaces,
|
|
1838
2021
|
query,
|
|
1839
2022
|
archivedSessionIds,
|
|
1840
|
-
|
|
2023
|
+
statuses,
|
|
1841
2024
|
currentRemote,
|
|
1842
2025
|
resultLimit
|
|
1843
2026
|
]);
|
|
1844
2027
|
const pending = currentRemote.status === "loading";
|
|
1845
2028
|
const failed = currentRemote.status === "error";
|
|
2029
|
+
const currentId = panelActive ? void 0 : Object.values(list.byId).find((session) => (session.retainedBy.mainView ?? 0) > 0)?.id;
|
|
1846
2030
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
1847
2031
|
className: clsx(WorkspaceBrowser_module_css_default.treeBody, WorkspaceBrowser_module_css_default.wide),
|
|
1848
2032
|
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1854,7 +2038,7 @@ window.__ModuleLoader__.load({
|
|
|
1854
2038
|
"aria-label": t("search.results.aria"),
|
|
1855
2039
|
children: results.items.map((result) => (0, react_jsx_runtime.jsx)(SearchResultItem, {
|
|
1856
2040
|
result,
|
|
1857
|
-
currentId
|
|
2041
|
+
currentId,
|
|
1858
2042
|
onOpen: open,
|
|
1859
2043
|
t
|
|
1860
2044
|
}, result.id))
|
|
@@ -1886,7 +2070,7 @@ window.__ModuleLoader__.load({
|
|
|
1886
2070
|
* @param props - composed slot props (shell owner share + store + injected actions).
|
|
1887
2071
|
* @returns the region element tree.
|
|
1888
2072
|
*/
|
|
1889
|
-
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 }) {
|
|
1890
2074
|
const home = useHostInfo((info) => info.home);
|
|
1891
2075
|
const list = useSessions((state) => state);
|
|
1892
2076
|
const workspaces = useWorkspaces((state) => state.items);
|
|
@@ -1899,7 +2083,8 @@ window.__ModuleLoader__.load({
|
|
|
1899
2083
|
const groupExpansion = useStore((s) => s.groupExpansion);
|
|
1900
2084
|
const sessionOrderByAccount = useStore((s) => s.sessionOrderByAccount);
|
|
1901
2085
|
const workspaceReady = workspacePhase === "ready" && workspaceStreamState !== "loading";
|
|
1902
|
-
const
|
|
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;
|
|
1903
2088
|
const ungroupedMemberIds = (0, react.useMemo)(() => {
|
|
1904
2089
|
const accounted = new Set(workspaces.flatMap((workspace) => workspace.sessionIds));
|
|
1905
2090
|
return list.ids.filter((id) => list.byId[id] !== void 0 && !accounted.has(id));
|
|
@@ -2329,7 +2514,7 @@ window.__ModuleLoader__.load({
|
|
|
2329
2514
|
children: wide && (normalizedQuery !== "" ? (0, react_jsx_runtime.jsx)(SearchResults, {
|
|
2330
2515
|
usePanelInfo,
|
|
2331
2516
|
useSessions,
|
|
2332
|
-
|
|
2517
|
+
useSessionStatus,
|
|
2333
2518
|
open: openSearchResult,
|
|
2334
2519
|
workspaces,
|
|
2335
2520
|
archivedSessionIds,
|
|
@@ -2341,7 +2526,7 @@ window.__ModuleLoader__.load({
|
|
|
2341
2526
|
usePanelInfo,
|
|
2342
2527
|
list,
|
|
2343
2528
|
sessionIds: orderedFlatSessionIds,
|
|
2344
|
-
|
|
2529
|
+
useSessionStatus,
|
|
2345
2530
|
open,
|
|
2346
2531
|
forkSession,
|
|
2347
2532
|
onSessionRename,
|
|
@@ -2353,13 +2538,14 @@ window.__ModuleLoader__.load({
|
|
|
2353
2538
|
}) : (0, react_jsx_runtime.jsx)(SessionTree, {
|
|
2354
2539
|
usePanelInfo,
|
|
2355
2540
|
list,
|
|
2356
|
-
|
|
2541
|
+
useSessionStatus,
|
|
2357
2542
|
onSessionRename,
|
|
2358
2543
|
onSessionArchive,
|
|
2359
2544
|
forkSession,
|
|
2360
2545
|
workspaces: orderedWorkspaces,
|
|
2361
2546
|
ungroupedSessionIds: orderedUngroupedSessionIds,
|
|
2362
2547
|
workspaceReady,
|
|
2548
|
+
nestWorkspaces: groupBy === "workspace-tree",
|
|
2363
2549
|
groupExpansion,
|
|
2364
2550
|
setGroupExpanded: actions.setGroupExpanded,
|
|
2365
2551
|
setSessionOrder: saveSessionOrder,
|
|
@@ -2537,6 +2723,7 @@ window.__ModuleLoader__.load({
|
|
|
2537
2723
|
"viewOptions.label": "视图选项",
|
|
2538
2724
|
"groupBy.label": "分组方式",
|
|
2539
2725
|
"groupBy.workspace": "按工作区",
|
|
2726
|
+
"groupBy.workspaceTree": "按工作区树",
|
|
2540
2727
|
"groupBy.flat": "单列表",
|
|
2541
2728
|
"orderBy.label": "排序方式",
|
|
2542
2729
|
"orderBy.manual": "手动排序",
|
|
@@ -2603,6 +2790,7 @@ window.__ModuleLoader__.load({
|
|
|
2603
2790
|
"viewOptions.label": "View options",
|
|
2604
2791
|
"groupBy.label": "Group by",
|
|
2605
2792
|
"groupBy.workspace": "WorkSpace",
|
|
2793
|
+
"groupBy.workspaceTree": "Workspace Tree",
|
|
2606
2794
|
"groupBy.flat": "In one list",
|
|
2607
2795
|
"orderBy.label": "Order by",
|
|
2608
2796
|
"orderBy.manual": "Manual",
|
|
@@ -2722,9 +2910,7 @@ window.__ModuleLoader__.load({
|
|
|
2722
2910
|
searchSessions,
|
|
2723
2911
|
searchResultLimit: sessions.searchResultLimit,
|
|
2724
2912
|
renameSession: async (sessionId, title) => {
|
|
2725
|
-
const
|
|
2726
|
-
if (session === void 0) throw new Error(`unknown session "${sessionId}"`);
|
|
2727
|
-
const result = await session.rename(title);
|
|
2913
|
+
const result = await sessions.using(sessionId, { source: "workspaceOperation" }, (reference) => reference.binding.session.rename(title));
|
|
2728
2914
|
if (!result.ok) throw new Error(result.error.message);
|
|
2729
2915
|
},
|
|
2730
2916
|
forkSession: (sessionId) => {
|