@huanlin/dsh-plugin-ya-workspace-sidebar 0.3.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/lib/client.js +545 -53
- package/lib/client.js.map +1 -1
- package/lib/types/client/VirtualRecentList.d.ts +20 -0
- package/lib/types/client/WorkspacePicker.d.ts +2 -2
- package/lib/types/client/contract.d.ts +9 -1
- package/lib/types/client/index.d.ts +7 -3
- package/lib/types/client/locales.d.ts +2 -0
- package/lib/types/client/model.d.ts +44 -10
- package/lib/types/client/navigation.d.ts +41 -0
- package/lib/types/client/settings.d.ts +17 -0
- package/lib/types/client/styles.d.ts +1 -1
- package/lib/types/index.d.ts +1 -1
- package/package.json +59 -29
- package/lib/invariant.js +0 -10
- package/lib/types/invariant.d.ts +0 -12
package/lib/client.js
CHANGED
|
@@ -4,6 +4,7 @@ window.__ModuleLoader__.load({
|
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let _deepseek_ai_cordis = require("@deepseek-ai/cordis");
|
|
7
8
|
let react = require("react");
|
|
8
9
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
9
10
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -58,6 +59,7 @@ window.__ModuleLoader__.load({
|
|
|
58
59
|
completed: "完了",
|
|
59
60
|
collapse: "折りたたむ",
|
|
60
61
|
expand: "展開",
|
|
62
|
+
resizeRecent: "最近のセッションの高さをドラッグで調整",
|
|
61
63
|
today: "今日",
|
|
62
64
|
yesterday: "昨日",
|
|
63
65
|
date: "{m}月{d}日",
|
|
@@ -112,6 +114,7 @@ window.__ModuleLoader__.load({
|
|
|
112
114
|
completed: "Abgeschlossen",
|
|
113
115
|
collapse: "Einklappen",
|
|
114
116
|
expand: "Ausklappen",
|
|
117
|
+
resizeRecent: "Ziehen, um die Höhe des Bereichs „Letzte Sitzungen“ anzupassen",
|
|
115
118
|
today: "Heute",
|
|
116
119
|
yesterday: "Gestern",
|
|
117
120
|
date: "{d}.{m}.",
|
|
@@ -166,6 +169,7 @@ window.__ModuleLoader__.load({
|
|
|
166
169
|
completed: "Terminé",
|
|
167
170
|
collapse: "Réduire",
|
|
168
171
|
expand: "Développer",
|
|
172
|
+
resizeRecent: "Faire glisser pour redimensionner la zone des sessions récentes",
|
|
169
173
|
today: "Aujourd’hui",
|
|
170
174
|
yesterday: "Hier",
|
|
171
175
|
date: "{d}/{m}",
|
|
@@ -220,6 +224,7 @@ window.__ModuleLoader__.load({
|
|
|
220
224
|
completed: "Concluído",
|
|
221
225
|
collapse: "Recolher",
|
|
222
226
|
expand: "Expandir",
|
|
227
|
+
resizeRecent: "Arraste para redimensionar a área de sessões recentes",
|
|
223
228
|
today: "Hoje",
|
|
224
229
|
yesterday: "Ontem",
|
|
225
230
|
date: "{d}/{m}",
|
|
@@ -274,6 +279,7 @@ window.__ModuleLoader__.load({
|
|
|
274
279
|
completed: "완료됨",
|
|
275
280
|
collapse: "접기",
|
|
276
281
|
expand: "펼치기",
|
|
282
|
+
resizeRecent: "드래그하여 최근 세션 영역 높이 조절",
|
|
277
283
|
today: "오늘",
|
|
278
284
|
yesterday: "어제",
|
|
279
285
|
date: "{m}월 {d}일",
|
|
@@ -328,6 +334,7 @@ window.__ModuleLoader__.load({
|
|
|
328
334
|
completed: "مكتمل",
|
|
329
335
|
collapse: "طي",
|
|
330
336
|
expand: "توسيع",
|
|
337
|
+
resizeRecent: "اسحب لضبط ارتفاع منطقة الجلسات الأخيرة",
|
|
331
338
|
today: "اليوم",
|
|
332
339
|
yesterday: "أمس",
|
|
333
340
|
date: "{m}/{d}",
|
|
@@ -382,6 +389,7 @@ window.__ModuleLoader__.load({
|
|
|
382
389
|
completed: "पूर्ण",
|
|
383
390
|
collapse: "संक्षिप्त करें",
|
|
384
391
|
expand: "विस्तार करें",
|
|
392
|
+
resizeRecent: "हाल के सेशन क्षेत्र की ऊँचाई समायोजित करने के लिए खींचें",
|
|
385
393
|
today: "आज",
|
|
386
394
|
yesterday: "कल",
|
|
387
395
|
date: "{d}/{m}",
|
|
@@ -436,6 +444,7 @@ window.__ModuleLoader__.load({
|
|
|
436
444
|
completed: "Selesai",
|
|
437
445
|
collapse: "Ciutkan",
|
|
438
446
|
expand: "Bentangkan",
|
|
447
|
+
resizeRecent: "Seret untuk mengatur tinggi area sesi terbaru",
|
|
439
448
|
today: "Hari ini",
|
|
440
449
|
yesterday: "Kemarin",
|
|
441
450
|
date: "{d}/{m}",
|
|
@@ -490,6 +499,7 @@ window.__ModuleLoader__.load({
|
|
|
490
499
|
completed: "Tamamlandı",
|
|
491
500
|
collapse: "Daralt",
|
|
492
501
|
expand: "Genişlet",
|
|
502
|
+
resizeRecent: "Son oturumlar alanının yüksekliğini sürükleyerek ayarlayın",
|
|
493
503
|
today: "Bugün",
|
|
494
504
|
yesterday: "Dün",
|
|
495
505
|
date: "{d}.{m}",
|
|
@@ -544,6 +554,7 @@ window.__ModuleLoader__.load({
|
|
|
544
554
|
completed: "Hoàn tất",
|
|
545
555
|
collapse: "Thu gọn",
|
|
546
556
|
expand: "Mở rộng",
|
|
557
|
+
resizeRecent: "Kéo để điều chỉnh chiều cao vùng phiên gần đây",
|
|
547
558
|
today: "Hôm nay",
|
|
548
559
|
yesterday: "Hôm qua",
|
|
549
560
|
date: "{d}/{m}",
|
|
@@ -598,6 +609,7 @@ window.__ModuleLoader__.load({
|
|
|
598
609
|
completed: "เสร็จสิ้น",
|
|
599
610
|
collapse: "ย่อ",
|
|
600
611
|
expand: "ขยาย",
|
|
612
|
+
resizeRecent: "ลากเพื่อปรับความสูงของพื้นที่เซสชันล่าสุด",
|
|
601
613
|
today: "วันนี้",
|
|
602
614
|
yesterday: "เมื่อวาน",
|
|
603
615
|
date: "{d}/{m}",
|
|
@@ -652,6 +664,7 @@ window.__ModuleLoader__.load({
|
|
|
652
664
|
completed: "Завершён",
|
|
653
665
|
collapse: "Свернуть",
|
|
654
666
|
expand: "Развернуть",
|
|
667
|
+
resizeRecent: "Перетащите, чтобы изменить высоту области недавних сессий",
|
|
655
668
|
today: "Сегодня",
|
|
656
669
|
yesterday: "Вчера",
|
|
657
670
|
date: "{d}.{m}",
|
|
@@ -706,6 +719,7 @@ window.__ModuleLoader__.load({
|
|
|
706
719
|
completed: "Completata",
|
|
707
720
|
collapse: "Comprimi",
|
|
708
721
|
expand: "Espandi",
|
|
722
|
+
resizeRecent: "Trascina per ridimensionare l’area delle sessioni recenti",
|
|
709
723
|
today: "Oggi",
|
|
710
724
|
yesterday: "Ieri",
|
|
711
725
|
date: "{d}/{m}",
|
|
@@ -760,6 +774,7 @@ window.__ModuleLoader__.load({
|
|
|
760
774
|
completed: "Voltooid",
|
|
761
775
|
collapse: "Inklappen",
|
|
762
776
|
expand: "Uitklappen",
|
|
777
|
+
resizeRecent: "Sleep om de hoogte van het gebied met recente sessies aan te passen",
|
|
763
778
|
today: "Vandaag",
|
|
764
779
|
yesterday: "Gisteren",
|
|
765
780
|
date: "{d}-{m}",
|
|
@@ -814,6 +829,7 @@ window.__ModuleLoader__.load({
|
|
|
814
829
|
completed: "Slutförd",
|
|
815
830
|
collapse: "Fäll ihop",
|
|
816
831
|
expand: "Fäll ut",
|
|
832
|
+
resizeRecent: "Dra för att justera höjden på området för senaste sessioner",
|
|
817
833
|
today: "Idag",
|
|
818
834
|
yesterday: "Igår",
|
|
819
835
|
date: "{d}/{m}",
|
|
@@ -868,6 +884,7 @@ window.__ModuleLoader__.load({
|
|
|
868
884
|
completed: "Zakończono",
|
|
869
885
|
collapse: "Zwiń",
|
|
870
886
|
expand: "Rozwiń",
|
|
887
|
+
resizeRecent: "Przeciągnij, aby dostosować wysokość obszaru ostatnich sesji",
|
|
871
888
|
today: "Dzisiaj",
|
|
872
889
|
yesterday: "Wczoraj",
|
|
873
890
|
date: "{d}.{m}",
|
|
@@ -922,6 +939,7 @@ window.__ModuleLoader__.load({
|
|
|
922
939
|
completed: "已完成",
|
|
923
940
|
collapse: "摺疊",
|
|
924
941
|
expand: "展開",
|
|
942
|
+
resizeRecent: "拖動調整最近會話區域高度",
|
|
925
943
|
today: "今天",
|
|
926
944
|
yesterday: "昨天",
|
|
927
945
|
date: "{m}月{d}日",
|
|
@@ -976,6 +994,7 @@ window.__ModuleLoader__.load({
|
|
|
976
994
|
completed: "已完成",
|
|
977
995
|
collapse: "摺疊",
|
|
978
996
|
expand: "展開",
|
|
997
|
+
resizeRecent: "拖曳調整最近會話區域高度",
|
|
979
998
|
today: "今天",
|
|
980
999
|
yesterday: "昨天",
|
|
981
1000
|
date: "{m}月{d}日",
|
|
@@ -1030,6 +1049,7 @@ window.__ModuleLoader__.load({
|
|
|
1030
1049
|
completed: "已完成",
|
|
1031
1050
|
collapse: "摺疊",
|
|
1032
1051
|
expand: "展開",
|
|
1052
|
+
resizeRecent: "拖動調整最近會話區域高度",
|
|
1033
1053
|
today: "今天",
|
|
1034
1054
|
yesterday: "昨天",
|
|
1035
1055
|
date: "{m}月{d}日",
|
|
@@ -1088,6 +1108,7 @@ window.__ModuleLoader__.load({
|
|
|
1088
1108
|
completed: "已完成",
|
|
1089
1109
|
collapse: "折叠",
|
|
1090
1110
|
expand: "展开",
|
|
1111
|
+
resizeRecent: "拖动调整最近会话区域高度",
|
|
1091
1112
|
today: "今天",
|
|
1092
1113
|
yesterday: "昨天",
|
|
1093
1114
|
date: "{m}月{d}日",
|
|
@@ -1142,6 +1163,7 @@ window.__ModuleLoader__.load({
|
|
|
1142
1163
|
completed: "Completed",
|
|
1143
1164
|
collapse: "Collapse",
|
|
1144
1165
|
expand: "Expand",
|
|
1166
|
+
resizeRecent: "Drag to resize the recent sessions area",
|
|
1145
1167
|
today: "Today",
|
|
1146
1168
|
yesterday: "Yesterday",
|
|
1147
1169
|
date: "{m}/{d}",
|
|
@@ -1149,6 +1171,156 @@ window.__ModuleLoader__.load({
|
|
|
1149
1171
|
};
|
|
1150
1172
|
const NS = "ya-workspace-sidebar";
|
|
1151
1173
|
//#endregion
|
|
1174
|
+
//#region src/client/navigation.ts
|
|
1175
|
+
/**
|
|
1176
|
+
* Stand-in for the `uiWorkspace` navigation service owned by the official
|
|
1177
|
+
* ui-workspace client entry, which this bundle's patch disables.
|
|
1178
|
+
*
|
|
1179
|
+
* Since DSH v0.1.2-alpha.1 the ui-sidebar, ui-conversation, ui-agent-preset,
|
|
1180
|
+
* and the composed directory-picker client entries all inject `uiWorkspace`;
|
|
1181
|
+
* without a provider their fibers park forever and the whole WebUI boot
|
|
1182
|
+
* deadlocks. This port carries the semantics that moved here from the
|
|
1183
|
+
* pre-split client runtime: reuse-or-create Workspace connection, the
|
|
1184
|
+
* explicit/current/recent New Session fallback, archived-current clearing,
|
|
1185
|
+
* boot auto-selection, and the directory-picking wire calls.
|
|
1186
|
+
*/
|
|
1187
|
+
/** Implements Workspace navigation and directory UI operations. */
|
|
1188
|
+
var YaWorkspaceNavigation = class extends _deepseek_ai_cordis.Service {
|
|
1189
|
+
directoryPicker;
|
|
1190
|
+
workspaces;
|
|
1191
|
+
sessions;
|
|
1192
|
+
connecting = /* @__PURE__ */ new Map();
|
|
1193
|
+
/**
|
|
1194
|
+
* @param ctx - Client root Context.
|
|
1195
|
+
* @param directoryPicker - the directory-picking Remote namespace.
|
|
1196
|
+
* @param workspaces - pure Workspace Controller.
|
|
1197
|
+
* @param sessions - pure Session Controller.
|
|
1198
|
+
*/
|
|
1199
|
+
constructor(ctx, directoryPicker, workspaces, sessions) {
|
|
1200
|
+
super(ctx, "uiWorkspace");
|
|
1201
|
+
this.directoryPicker = directoryPicker;
|
|
1202
|
+
this.workspaces = workspaces;
|
|
1203
|
+
this.sessions = sessions;
|
|
1204
|
+
ctx.effect(() => this.watchNavigation(), "ya-workspace-sidebar: Workspace navigation policy");
|
|
1205
|
+
}
|
|
1206
|
+
async connectWorkspace(workspaceId) {
|
|
1207
|
+
const workspace = this.workspaces.list.getSnapshot().items.find((item) => item.workspaceId === workspaceId);
|
|
1208
|
+
if (workspace === void 0) throw new Error(`ya-workspace-sidebar: unknown workspace "${workspaceId}"`);
|
|
1209
|
+
const inflight = this.connecting.get(workspaceId);
|
|
1210
|
+
if (inflight !== void 0) return inflight;
|
|
1211
|
+
const archived = this.workspaces.list.getSnapshot().archivedSessionIds;
|
|
1212
|
+
const sessions = this.sessions.list.getSnapshot();
|
|
1213
|
+
for (const id of sessions.ids) {
|
|
1214
|
+
const summary = sessions.byId[id];
|
|
1215
|
+
if (summary !== void 0 && summary.blank && summary.cwd === workspace.path && workspace.sessionIds.includes(summary.id) && !archived.includes(summary.id)) return summary.id;
|
|
1216
|
+
}
|
|
1217
|
+
const attempt = this.sessions.create({ workspaceId }).finally(() => {
|
|
1218
|
+
this.connecting.delete(workspaceId);
|
|
1219
|
+
});
|
|
1220
|
+
this.connecting.set(workspaceId, attempt);
|
|
1221
|
+
return attempt;
|
|
1222
|
+
}
|
|
1223
|
+
startSession(workspaceId) {
|
|
1224
|
+
const workspaces = this.workspaces.list.getSnapshot();
|
|
1225
|
+
const sessions = this.sessions.list.getSnapshot();
|
|
1226
|
+
const current = sessions.current;
|
|
1227
|
+
const currentWorkspaceId = current === void 0 ? void 0 : workspaces.items.find((item) => item.sessionIds.includes(current))?.workspaceId;
|
|
1228
|
+
const recent = workspaces.phase === "ready" && sessions.phase === "ready" ? recentWorkspace(workspaces.items, sessions.byId) : void 0;
|
|
1229
|
+
const target = workspaceId ?? currentWorkspaceId ?? recent;
|
|
1230
|
+
if (target === void 0) {
|
|
1231
|
+
this.sessions.clear();
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
this.connectWorkspace(target).then((sessionId) => {
|
|
1235
|
+
this.sessions.open(sessionId);
|
|
1236
|
+
}, (reason) => {
|
|
1237
|
+
console.warn("ya-workspace-sidebar: new session failed:", reason);
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
async archiveSession(sessionId) {
|
|
1241
|
+
await this.workspaces.archiveSession(sessionId);
|
|
1242
|
+
}
|
|
1243
|
+
async pickDirectory() {
|
|
1244
|
+
const result = await this.directoryPicker.pick();
|
|
1245
|
+
if (!result.ok) throw new Error(`directory picker failed: ${result.error.message}`);
|
|
1246
|
+
return result.value;
|
|
1247
|
+
}
|
|
1248
|
+
async listDirectory(path, signal) {
|
|
1249
|
+
const result = await this.directoryPicker.list(path, signal);
|
|
1250
|
+
if (!result.ok) throw new Error(`directory browse failed: ${result.error.code}: ${result.error.message}`);
|
|
1251
|
+
return result.value;
|
|
1252
|
+
}
|
|
1253
|
+
async createDirectory(path, name) {
|
|
1254
|
+
const result = await this.directoryPicker.createDirectory(path, name);
|
|
1255
|
+
if (!result.ok) throw new Error(`directory browse failed: ${result.error.code}: ${result.error.message}`);
|
|
1256
|
+
return result.value;
|
|
1257
|
+
}
|
|
1258
|
+
watchNavigation() {
|
|
1259
|
+
let initial = "waiting";
|
|
1260
|
+
let disposed = false;
|
|
1261
|
+
const reconcile = () => {
|
|
1262
|
+
if (disposed) return;
|
|
1263
|
+
if (this.clearArchivedCurrent()) return;
|
|
1264
|
+
if (initial !== "waiting") return;
|
|
1265
|
+
const workspaces = this.workspaces.list.getSnapshot();
|
|
1266
|
+
const sessions = this.sessions.list.getSnapshot();
|
|
1267
|
+
if (workspaces.phase !== "ready" || sessions.phase !== "ready") return;
|
|
1268
|
+
if (sessions.current !== void 0) {
|
|
1269
|
+
initial = "done";
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
const target = recentWorkspace(workspaces.items, sessions.byId);
|
|
1273
|
+
if (target === void 0) {
|
|
1274
|
+
initial = "done";
|
|
1275
|
+
return;
|
|
1276
|
+
}
|
|
1277
|
+
initial = "connecting";
|
|
1278
|
+
this.connectWorkspace(target).then((sessionId) => {
|
|
1279
|
+
if (disposed) return;
|
|
1280
|
+
if (this.sessions.list.getSnapshot().current === void 0) this.sessions.open(sessionId);
|
|
1281
|
+
initial = "done";
|
|
1282
|
+
}, (reason) => {
|
|
1283
|
+
if (disposed) return;
|
|
1284
|
+
initial = "waiting";
|
|
1285
|
+
console.warn("ya-workspace-sidebar: initial workspace selection failed:", reason);
|
|
1286
|
+
});
|
|
1287
|
+
};
|
|
1288
|
+
const disposeWorkspaces = this.workspaces.list.subscribe(reconcile);
|
|
1289
|
+
const disposeSessions = this.sessions.list.subscribe(reconcile);
|
|
1290
|
+
reconcile();
|
|
1291
|
+
return () => {
|
|
1292
|
+
disposed = true;
|
|
1293
|
+
disposeSessions();
|
|
1294
|
+
disposeWorkspaces();
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
/** @returns true when an archived current selection was cleared. */
|
|
1298
|
+
clearArchivedCurrent() {
|
|
1299
|
+
const current = this.sessions.list.getSnapshot().current;
|
|
1300
|
+
if (current === void 0 || !this.workspaces.list.getSnapshot().archivedSessionIds.includes(current)) return false;
|
|
1301
|
+
this.sessions.clear();
|
|
1302
|
+
return true;
|
|
1303
|
+
}
|
|
1304
|
+
};
|
|
1305
|
+
/** Stable tie-breaking follows Host Workspace order. */
|
|
1306
|
+
function recentWorkspace(workspaces, sessions) {
|
|
1307
|
+
let selected;
|
|
1308
|
+
let selectedTime = Number.NEGATIVE_INFINITY;
|
|
1309
|
+
for (const workspace of workspaces) {
|
|
1310
|
+
let latest = Number.NEGATIVE_INFINITY;
|
|
1311
|
+
for (const sessionId of workspace.sessionIds) {
|
|
1312
|
+
const session = sessions[sessionId];
|
|
1313
|
+
if (session !== void 0) latest = Math.max(latest, session.updatedAt);
|
|
1314
|
+
}
|
|
1315
|
+
if (latest === Number.NEGATIVE_INFINITY) latest = Date.parse(workspace.createdAt);
|
|
1316
|
+
if (selected === void 0 || latest > selectedTime) {
|
|
1317
|
+
selected = workspace.workspaceId;
|
|
1318
|
+
selectedTime = latest;
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
return selected;
|
|
1322
|
+
}
|
|
1323
|
+
//#endregion
|
|
1152
1324
|
//#region src/client/styles.ts
|
|
1153
1325
|
/** One scoped stylesheet injected for the lifetime of the client activation. */
|
|
1154
1326
|
const CSS = `
|
|
@@ -1164,11 +1336,18 @@ body[data-ds-dark-theme] .ya-search { background:var(--dsw-static-neutral-bluish
|
|
|
1164
1336
|
.ya-search-input::placeholder { color:var(--dsw-alias-label-tertiary); }
|
|
1165
1337
|
.ya-search-icon { flex:none; display:inline-flex; border:0; padding:0; color:inherit; background:transparent; }
|
|
1166
1338
|
.ya-body { flex:1; min-height:0; display:flex; flex-direction:column; overflow:hidden; margin-right:calc(-1 * var(--dsh-sidebar-inline-padding)); padding-right:var(--dsh-sidebar-inline-padding); }
|
|
1167
|
-
.ya-recent { flex:none;
|
|
1168
|
-
.ya-recent-collapsed { padding-bottom:0; border-bottom-color:transparent; }
|
|
1339
|
+
.ya-recent { flex:none; }
|
|
1169
1340
|
.ya-recent-list-wrap { display:grid; grid-template-rows:1fr; transition:grid-template-rows 220ms ease-out; }
|
|
1170
1341
|
.ya-recent-collapsed .ya-recent-list-wrap { grid-template-rows:0fr; }
|
|
1171
|
-
.ya-recent-list {
|
|
1342
|
+
.ya-recent-list { position:relative; overflow-y:auto; overscroll-behavior:contain; min-height:0; max-height:min(280px, 40vh); }
|
|
1343
|
+
.ya-virtual-canvas { position:relative; width:100%; }
|
|
1344
|
+
.ya-virtual-slot { position:absolute; left:0; right:0; height:35px; box-sizing:border-box; padding:1px 0; display:flex; flex-direction:column; justify-content:center; }
|
|
1345
|
+
.ya-virtual-slot .ya-row { margin:0; min-height:33px; }
|
|
1346
|
+
.ya-recent-resizer { position:relative; flex:none; height:9px; margin:1px 0 3px; display:flex; align-items:center; justify-content:center; border-radius:5px; cursor:row-resize; touch-action:none; }
|
|
1347
|
+
.ya-recent-resizer::before { content:''; position:absolute; left:8px; right:8px; top:50%; height:1px; transform:translateY(-0.5px); background:var(--dsw-alias-border-l2); }
|
|
1348
|
+
.ya-recent-resizer::after { content:''; width:26px; height:3px; border-radius:2px; background:var(--dsw-alias-border-l2); transition:background 120ms ease-out; }
|
|
1349
|
+
.ya-recent-resizer:hover::after, .ya-recent-resizer:focus-visible::after, .ya-recent-resizer.ya-resizing::after { background:var(--dsw-alias-label-tertiary); }
|
|
1350
|
+
.ya-recent-resizer.ya-resizing::before { background:var(--dsw-alias-interactive-bg-selected); }
|
|
1172
1351
|
.ya-block-label { height:26px; display:flex; align-items:center; gap:2px; padding:0 8px; color:var(--dsw-alias-label-tertiary); font-size:12px; font-weight:600; letter-spacing:.02em; text-transform:uppercase; }
|
|
1173
1352
|
.ya-block-label-toggle { flex:none; width:20px; height:20px; margin-left:auto; border:0; border-radius:6px; padding:0; display:inline-flex; align-items:center; justify-content:center; background:transparent; color:var(--dsw-alias-label-tertiary); cursor:pointer; transition:transform 180ms ease-out; }
|
|
1174
1353
|
.ya-block-label-toggle:hover { background:var(--dsw-alias-interactive-bg-hover); color:var(--dsw-alias-label-secondary); }
|
|
@@ -1191,7 +1370,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
1191
1370
|
.ya-row:hover .ya-row-actions, .ya-menu-open .ya-row-actions { opacity:1; pointer-events:auto; }
|
|
1192
1371
|
.ya-status-slot { flex:none; width:16px; height:16px; display:inline-flex; align-items:center; justify-content:center; color:var(--dsw-alias-label-tertiary); }
|
|
1193
1372
|
.ya-recent .ya-row { min-height:31px; }
|
|
1194
|
-
.ya-search-workspace { color:var(--dsw-alias-label-tertiary); font-size:11px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
1373
|
+
.ya-search-workspace { color:var(--dsw-alias-label-tertiary); font-size:11px; line-height:15px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
1195
1374
|
.ya-empty, .ya-status { padding:18px 10px; color:var(--dsw-alias-label-tertiary); text-align:center; font-size:13px; }
|
|
1196
1375
|
.ya-warning { color:var(--dsw-alias-status-warning); }
|
|
1197
1376
|
.ya-rename-input { width:100%; height:38px; box-sizing:border-box; border:1px solid var(--dsw-alias-border-l2); border-radius:9px; padding:0 11px; background:transparent; color:var(--dsw-alias-label-primary); outline:none; }
|
|
@@ -1349,18 +1528,24 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
1349
1528
|
}
|
|
1350
1529
|
//#endregion
|
|
1351
1530
|
//#region src/client/model.ts
|
|
1531
|
+
/** Resolve the visible status-dot kind for one session, if any. */
|
|
1532
|
+
function pendingKindOf(pending, id) {
|
|
1533
|
+
const kind = pending.get(id)?.kind;
|
|
1534
|
+
return kind === "approval" || kind === "plan-review" || kind === "question" ? kind : void 0;
|
|
1535
|
+
}
|
|
1352
1536
|
/** Navigation key for sessions not accounted to a real workspace. */
|
|
1353
1537
|
const UNGROUPED = "__ya_ungrouped__";
|
|
1354
1538
|
function visible(summary, current, archived) {
|
|
1355
1539
|
return summary.origin !== "subagent" && !archived.has(summary.id) && (!summary.blank || summary.id === current);
|
|
1356
1540
|
}
|
|
1357
|
-
function rowOf(summary, workspaceKey, workspaceTitle) {
|
|
1541
|
+
function rowOf(summary, workspaceKey, workspaceTitle, pending) {
|
|
1542
|
+
const pendingInteraction = pendingKindOf(pending, summary.id);
|
|
1358
1543
|
return {
|
|
1359
1544
|
id: summary.id,
|
|
1360
1545
|
title: summary.blank ? "New Session" : summary.displayTitle,
|
|
1361
1546
|
blank: summary.blank,
|
|
1362
1547
|
running: summary.running,
|
|
1363
|
-
...
|
|
1548
|
+
...pendingInteraction === void 0 ? {} : { pendingInteraction },
|
|
1364
1549
|
completed: summary.completed === true,
|
|
1365
1550
|
updatedAt: summary.updatedAt,
|
|
1366
1551
|
workspaceKey,
|
|
@@ -1378,57 +1563,100 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
1378
1563
|
return ownerIndex(workspaces).get(sessionId)?.workspaceId ?? "__ya_ungrouped__";
|
|
1379
1564
|
}
|
|
1380
1565
|
/** Derive global recent sessions, newest first. */
|
|
1381
|
-
function deriveRecent(list, workspaces, archivedSessionIds, limit = 5) {
|
|
1566
|
+
function deriveRecent(list, workspaces, archivedSessionIds, pending, limit = 5) {
|
|
1382
1567
|
const archived = new Set(archivedSessionIds);
|
|
1383
1568
|
const owners = ownerIndex(workspaces);
|
|
1384
1569
|
return list.ids.map((id) => list.byId[id]).filter((summary) => summary !== void 0 && visible(summary, list.current, archived)).sort((a, b) => b.updatedAt - a.updatedAt || String(a.id).localeCompare(String(b.id))).slice(0, limit).map((summary) => {
|
|
1385
1570
|
const workspace = owners.get(summary.id);
|
|
1386
|
-
return rowOf(summary, workspace?.workspaceId ?? "__ya_ungrouped__", workspace?.title ?? "Ungrouped");
|
|
1571
|
+
return rowOf(summary, workspace?.workspaceId ?? "__ya_ungrouped__", workspace?.title ?? "Ungrouped", pending);
|
|
1387
1572
|
});
|
|
1388
1573
|
}
|
|
1389
|
-
/**
|
|
1574
|
+
/**
|
|
1575
|
+
* Windowing math for the recent-sessions virtual list (fixed row stride).
|
|
1576
|
+
*
|
|
1577
|
+
* Clamps `scrollTop` into the valid range, then pads the visible row range
|
|
1578
|
+
* with `overscan` rows on both edges. Before the viewport has been measured
|
|
1579
|
+
* (`viewportHeight <= 0`) it still returns a small head window so the first
|
|
1580
|
+
* paint is never blank.
|
|
1581
|
+
*/
|
|
1582
|
+
function virtualWindow(scrollTop, viewportHeight, count, stride = 35, overscan = 4) {
|
|
1583
|
+
if (count <= 0 || stride <= 0) return {
|
|
1584
|
+
start: 0,
|
|
1585
|
+
end: 0
|
|
1586
|
+
};
|
|
1587
|
+
const height = Math.max(0, viewportHeight);
|
|
1588
|
+
const top = Math.min(Math.max(0, scrollTop), Math.max(0, count * stride - height));
|
|
1589
|
+
const first = Math.floor(top / stride);
|
|
1590
|
+
const last = height > 0 ? Math.floor((top + height) / stride) : first;
|
|
1591
|
+
return {
|
|
1592
|
+
start: Math.max(0, first - overscan),
|
|
1593
|
+
end: Math.min(count, last + 1 + overscan)
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
function lastUsedOf(timestamps, fallback) {
|
|
1597
|
+
let newest;
|
|
1598
|
+
for (const ts of timestamps) if (newest === void 0 || ts > newest) newest = ts;
|
|
1599
|
+
if (newest !== void 0) return newest;
|
|
1600
|
+
if (fallback === void 0) return void 0;
|
|
1601
|
+
const created = Date.parse(fallback);
|
|
1602
|
+
return Number.isNaN(created) ? void 0 : created;
|
|
1603
|
+
}
|
|
1604
|
+
/** Derive first-level workspaces plus Ungrouped, newest session activity first. */
|
|
1390
1605
|
function deriveWorkspaces(list, workspaces, archivedSessionIds) {
|
|
1391
1606
|
const archived = new Set(archivedSessionIds);
|
|
1392
1607
|
const accounted = /* @__PURE__ */ new Set();
|
|
1393
1608
|
const result = workspaces.map((workspace) => {
|
|
1394
1609
|
let count = 0;
|
|
1610
|
+
const timestamps = [];
|
|
1395
1611
|
for (const id of workspace.sessionIds) {
|
|
1396
1612
|
accounted.add(id);
|
|
1397
1613
|
const summary = list.byId[id];
|
|
1398
|
-
if (summary !== void 0 && visible(summary, list.current, archived))
|
|
1614
|
+
if (summary !== void 0 && visible(summary, list.current, archived)) {
|
|
1615
|
+
count++;
|
|
1616
|
+
timestamps.push(summary.updatedAt);
|
|
1617
|
+
}
|
|
1399
1618
|
}
|
|
1619
|
+
const lastUsedAt = lastUsedOf(timestamps, workspace.createdAt);
|
|
1400
1620
|
return {
|
|
1401
1621
|
key: workspace.workspaceId,
|
|
1402
1622
|
title: workspace.title,
|
|
1403
1623
|
path: workspace.path,
|
|
1404
1624
|
createdAt: workspace.createdAt,
|
|
1625
|
+
...lastUsedAt === void 0 ? {} : { lastUsedAt },
|
|
1405
1626
|
count,
|
|
1406
1627
|
real: true
|
|
1407
1628
|
};
|
|
1408
1629
|
});
|
|
1409
1630
|
let ungrouped = 0;
|
|
1631
|
+
const ungroupedTimestamps = [];
|
|
1410
1632
|
for (const id of list.ids) {
|
|
1411
1633
|
const summary = list.byId[id];
|
|
1412
|
-
if (summary !== void 0 && !accounted.has(id) && visible(summary, list.current, archived))
|
|
1634
|
+
if (summary !== void 0 && !accounted.has(id) && visible(summary, list.current, archived)) {
|
|
1635
|
+
ungrouped++;
|
|
1636
|
+
ungroupedTimestamps.push(summary.updatedAt);
|
|
1637
|
+
}
|
|
1413
1638
|
}
|
|
1639
|
+
const ungroupedLastUsed = lastUsedOf(ungroupedTimestamps);
|
|
1414
1640
|
result.push({
|
|
1415
1641
|
key: UNGROUPED,
|
|
1416
1642
|
title: "Ungrouped",
|
|
1417
1643
|
count: ungrouped,
|
|
1418
|
-
real: false
|
|
1644
|
+
real: false,
|
|
1645
|
+
...ungroupedLastUsed === void 0 ? {} : { lastUsedAt: ungroupedLastUsed }
|
|
1419
1646
|
});
|
|
1647
|
+
result.sort((a, b) => (b.lastUsedAt ?? 0) - (a.lastUsedAt ?? 0) || String(a.key).localeCompare(String(b.key)));
|
|
1420
1648
|
return result;
|
|
1421
1649
|
}
|
|
1422
1650
|
/** Derive the selected workspace's sessions in its canonical order. */
|
|
1423
|
-
function deriveWorkspaceSessions(key, list, workspaces, archivedSessionIds) {
|
|
1651
|
+
function deriveWorkspaceSessions(key, list, workspaces, archivedSessionIds, pending) {
|
|
1424
1652
|
const archived = new Set(archivedSessionIds);
|
|
1425
1653
|
if (key === "__ya_ungrouped__") {
|
|
1426
1654
|
const accounted = new Set(workspaces.flatMap((workspace) => workspace.sessionIds));
|
|
1427
|
-
return list.ids.map((id) => list.byId[id]).filter((summary) => summary !== void 0 && !accounted.has(summary.id) && visible(summary, list.current, archived)).sort((a, b) => b.updatedAt - a.updatedAt || String(a.id).localeCompare(String(b.id))).map((summary) => rowOf(summary, UNGROUPED, "Ungrouped"));
|
|
1655
|
+
return list.ids.map((id) => list.byId[id]).filter((summary) => summary !== void 0 && !accounted.has(summary.id) && visible(summary, list.current, archived)).sort((a, b) => b.updatedAt - a.updatedAt || String(a.id).localeCompare(String(b.id))).map((summary) => rowOf(summary, UNGROUPED, "Ungrouped", pending));
|
|
1428
1656
|
}
|
|
1429
1657
|
const workspace = workspaces.find((item) => item.workspaceId === key);
|
|
1430
1658
|
if (workspace === void 0) return [];
|
|
1431
|
-
return workspace.sessionIds.map((id) => list.byId[id]).filter((summary) => summary !== void 0 && visible(summary, list.current, archived)).map((summary) => rowOf(summary, workspace.workspaceId, workspace.title));
|
|
1659
|
+
return workspace.sessionIds.map((id) => list.byId[id]).filter((summary) => summary !== void 0 && visible(summary, list.current, archived)).map((summary) => rowOf(summary, workspace.workspaceId, workspace.title, pending));
|
|
1432
1660
|
}
|
|
1433
1661
|
/** Format a local calendar date as `YYYY-MM-DD` (locale-agnostic, no padding surprises). */
|
|
1434
1662
|
function localDateKey(year, month, day) {
|
|
@@ -1440,21 +1668,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
1440
1668
|
const msB = Date.UTC(b.year, b.month, b.day);
|
|
1441
1669
|
return Math.round((msA - msB) / 864e5);
|
|
1442
1670
|
}
|
|
1443
|
-
|
|
1444
|
-
* Derive the selected real workspace's sessions grouped by local calendar date.
|
|
1445
|
-
*
|
|
1446
|
-
* - Only real workspaces: `Ungrouped` falls back to {@link deriveWorkspaceSessions}.
|
|
1447
|
-
* - Groups are ordered by date descending; rows within a group by `updatedAt` descending.
|
|
1448
|
-
* - {@link visible} filter is reused (archived / subagent / blank visibility).
|
|
1449
|
-
* - Future timestamps clamp to today's bucket (`dayOffset` 0).
|
|
1450
|
-
* - `now` is the reference timestamp for "today"; pass `Date.now()` in production.
|
|
1451
|
-
*/
|
|
1452
|
-
function deriveWorkspaceSessionGroups(key, list, workspaces, archivedSessionIds, now) {
|
|
1453
|
-
if (key === "__ya_ungrouped__") return [];
|
|
1454
|
-
const workspace = workspaces.find((item) => item.workspaceId === key);
|
|
1455
|
-
if (workspace === void 0) return [];
|
|
1456
|
-
const archived = new Set(archivedSessionIds);
|
|
1457
|
-
const rows = workspace.sessionIds.map((id) => list.byId[id]).filter((summary) => summary !== void 0 && visible(summary, list.current, archived)).map((summary) => rowOf(summary, workspace.workspaceId, workspace.title));
|
|
1671
|
+
function groupByLocalDate(rows, timestampOf, compare, now) {
|
|
1458
1672
|
if (rows.length === 0) return [];
|
|
1459
1673
|
const nowDate = new Date(now);
|
|
1460
1674
|
const today = {
|
|
@@ -1464,7 +1678,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
1464
1678
|
};
|
|
1465
1679
|
const buckets = /* @__PURE__ */ new Map();
|
|
1466
1680
|
for (const row of rows) {
|
|
1467
|
-
const ts = Math.min(row
|
|
1681
|
+
const ts = Math.min(timestampOf(row), now);
|
|
1468
1682
|
const d = new Date(ts);
|
|
1469
1683
|
const date = {
|
|
1470
1684
|
year: d.getFullYear(),
|
|
@@ -1484,7 +1698,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
1484
1698
|
}
|
|
1485
1699
|
const groups = [];
|
|
1486
1700
|
for (const [dateKey, bucket] of buckets) {
|
|
1487
|
-
bucket.rows.sort(
|
|
1701
|
+
bucket.rows.sort(compare);
|
|
1488
1702
|
groups.push({
|
|
1489
1703
|
dateKey,
|
|
1490
1704
|
dayOffset: bucket.dayOffset,
|
|
@@ -1494,6 +1708,36 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
1494
1708
|
groups.sort((a, b) => a.dayOffset - b.dayOffset || a.dateKey.localeCompare(b.dateKey));
|
|
1495
1709
|
return groups;
|
|
1496
1710
|
}
|
|
1711
|
+
/**
|
|
1712
|
+
* Derive the selected real workspace's sessions grouped by local calendar date.
|
|
1713
|
+
*
|
|
1714
|
+
* - Only real workspaces: `Ungrouped` falls back to {@link deriveWorkspaceSessions}.
|
|
1715
|
+
* - Groups are ordered by date descending; rows within a group by `updatedAt` descending.
|
|
1716
|
+
* - {@link visible} filter is reused (archived / subagent / blank visibility).
|
|
1717
|
+
* - Future timestamps clamp to today's bucket (`dayOffset` 0).
|
|
1718
|
+
* - `now` is the reference timestamp for "today"; pass `Date.now()` in production.
|
|
1719
|
+
*/
|
|
1720
|
+
function deriveWorkspaceSessionGroups(key, list, workspaces, archivedSessionIds, pending, now) {
|
|
1721
|
+
if (key === "__ya_ungrouped__") return [];
|
|
1722
|
+
const workspace = workspaces.find((item) => item.workspaceId === key);
|
|
1723
|
+
if (workspace === void 0) return [];
|
|
1724
|
+
const archived = new Set(archivedSessionIds);
|
|
1725
|
+
return groupByLocalDate(workspace.sessionIds.map((id) => list.byId[id]).filter((summary) => summary !== void 0 && visible(summary, list.current, archived)).map((summary) => rowOf(summary, workspace.workspaceId, workspace.title, pending)), (row) => row.updatedAt, (a, b) => b.updatedAt - a.updatedAt || String(a.id).localeCompare(String(b.id)), now);
|
|
1726
|
+
}
|
|
1727
|
+
/** Group root workspace rows by local calendar date of `lastUsedAt`; undated rows trail with empty `dateKey`. */
|
|
1728
|
+
function deriveWorkspaceGroups(rows, now) {
|
|
1729
|
+
const dated = [];
|
|
1730
|
+
const undated = [];
|
|
1731
|
+
for (const row of rows) if (row.lastUsedAt === void 0) undated.push(row);
|
|
1732
|
+
else dated.push(row);
|
|
1733
|
+
const groups = groupByLocalDate(dated, (row) => row.lastUsedAt ?? 0, (a, b) => (b.lastUsedAt ?? 0) - (a.lastUsedAt ?? 0) || String(a.key).localeCompare(String(b.key)), now);
|
|
1734
|
+
if (undated.length > 0) groups.push({
|
|
1735
|
+
dateKey: "",
|
|
1736
|
+
dayOffset: Number.POSITIVE_INFINITY,
|
|
1737
|
+
rows: [...undated]
|
|
1738
|
+
});
|
|
1739
|
+
return groups;
|
|
1740
|
+
}
|
|
1497
1741
|
/** Case-insensitive local title/workspace matching used beside Host content search. */
|
|
1498
1742
|
function localMatches(rows, query) {
|
|
1499
1743
|
const normalized = query.trim().toLocaleLowerCase();
|
|
@@ -1537,11 +1781,121 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
1537
1781
|
listeners.delete(listener);
|
|
1538
1782
|
};
|
|
1539
1783
|
}
|
|
1784
|
+
const RECENT_HEIGHT_KEY = "ya-workspace-sidebar:recent-height";
|
|
1785
|
+
const recentHeightListeners = /* @__PURE__ */ new Set();
|
|
1786
|
+
let currentRecentHeight = loadRecentHeight();
|
|
1787
|
+
function clampRecentHeight(height) {
|
|
1788
|
+
return Math.min(640, Math.max(70, Math.round(height)));
|
|
1789
|
+
}
|
|
1790
|
+
/** Read the stored height, falling back to `undefined` on any failure. */
|
|
1791
|
+
function loadRecentHeight() {
|
|
1792
|
+
try {
|
|
1793
|
+
const raw = window.localStorage.getItem(RECENT_HEIGHT_KEY);
|
|
1794
|
+
if (raw === null) return void 0;
|
|
1795
|
+
const parsed = Number(raw);
|
|
1796
|
+
return Number.isFinite(parsed) ? clampRecentHeight(parsed) : void 0;
|
|
1797
|
+
} catch {
|
|
1798
|
+
return;
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
/** Persist the height (or clear the entry for `undefined`); failures ignored. */
|
|
1802
|
+
function persistRecentHeight(height) {
|
|
1803
|
+
try {
|
|
1804
|
+
if (height === void 0) window.localStorage.removeItem(RECENT_HEIGHT_KEY);
|
|
1805
|
+
else window.localStorage.setItem(RECENT_HEIGHT_KEY, String(height));
|
|
1806
|
+
} catch {}
|
|
1807
|
+
}
|
|
1808
|
+
/** Current recent-sessions viewport height preference, if the user set one. */
|
|
1809
|
+
function getRecentViewportHeight() {
|
|
1810
|
+
return currentRecentHeight;
|
|
1811
|
+
}
|
|
1812
|
+
/** Store (or clear with `undefined`) the height preference and notify subscribers. */
|
|
1813
|
+
function setRecentViewportHeight(height) {
|
|
1814
|
+
const next = height === void 0 ? void 0 : clampRecentHeight(height);
|
|
1815
|
+
if (next === currentRecentHeight) return;
|
|
1816
|
+
currentRecentHeight = next;
|
|
1817
|
+
persistRecentHeight(next);
|
|
1818
|
+
for (const listener of [...recentHeightListeners]) listener();
|
|
1819
|
+
}
|
|
1820
|
+
/** Subscribe to recent height changes; returns an unsubscribe disposer. */
|
|
1821
|
+
function subscribeRecentViewportHeight(listener) {
|
|
1822
|
+
recentHeightListeners.add(listener);
|
|
1823
|
+
return () => {
|
|
1824
|
+
recentHeightListeners.delete(listener);
|
|
1825
|
+
};
|
|
1826
|
+
}
|
|
1827
|
+
//#endregion
|
|
1828
|
+
//#region src/client/VirtualRecentList.tsx
|
|
1829
|
+
/** Fixed-stride virtual scroller for the global recent-sessions block. */
|
|
1830
|
+
/**
|
|
1831
|
+
* Renders `rows` inside a bounded, scrollable viewport while mounting only
|
|
1832
|
+
* the visible slice (plus overscan). The row stride is fixed
|
|
1833
|
+
* ({@link RECENT_ROW_STRIDE}), so windowing is pure arithmetic — no per-row
|
|
1834
|
+
* measurement — and the scroll position survives session-list updates.
|
|
1835
|
+
*/
|
|
1836
|
+
function VirtualRecentList({ rows, renderItem, ariaLabel, viewportRef, maxHeight }) {
|
|
1837
|
+
const viewportRefInternal = (0, react.useRef)(null);
|
|
1838
|
+
const frameRef = (0, react.useRef)(0);
|
|
1839
|
+
const [scrollTop, setScrollTop] = (0, react.useState)(0);
|
|
1840
|
+
const [viewportHeight, setViewportHeight] = (0, react.useState)(0);
|
|
1841
|
+
(0, react.useLayoutEffect)(() => {
|
|
1842
|
+
const el = viewportRefInternal.current;
|
|
1843
|
+
if (el === null) return;
|
|
1844
|
+
const observer = new ResizeObserver((entries) => {
|
|
1845
|
+
const entry = entries[0];
|
|
1846
|
+
if (entry !== void 0) setViewportHeight(entry.contentRect.height);
|
|
1847
|
+
});
|
|
1848
|
+
observer.observe(el);
|
|
1849
|
+
return () => {
|
|
1850
|
+
observer.disconnect();
|
|
1851
|
+
};
|
|
1852
|
+
}, []);
|
|
1853
|
+
(0, react.useEffect)(() => () => {
|
|
1854
|
+
if (frameRef.current !== 0) window.cancelAnimationFrame(frameRef.current);
|
|
1855
|
+
}, []);
|
|
1856
|
+
const handleScroll = () => {
|
|
1857
|
+
if (frameRef.current !== 0) return;
|
|
1858
|
+
frameRef.current = window.requestAnimationFrame(() => {
|
|
1859
|
+
frameRef.current = 0;
|
|
1860
|
+
const el = viewportRefInternal.current;
|
|
1861
|
+
if (el !== null) setScrollTop(el.scrollTop);
|
|
1862
|
+
});
|
|
1863
|
+
};
|
|
1864
|
+
const { start, end } = virtualWindow(scrollTop, viewportHeight, rows.length);
|
|
1865
|
+
const slice = [];
|
|
1866
|
+
for (let index = start; index < end; index += 1) {
|
|
1867
|
+
const row = rows[index];
|
|
1868
|
+
if (row !== void 0) slice.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1869
|
+
className: "ya-virtual-slot",
|
|
1870
|
+
style: { top: index * 35 },
|
|
1871
|
+
children: renderItem(row)
|
|
1872
|
+
}, row.id));
|
|
1873
|
+
}
|
|
1874
|
+
const attachViewport = (node) => {
|
|
1875
|
+
viewportRefInternal.current = node;
|
|
1876
|
+
viewportRef?.(node);
|
|
1877
|
+
};
|
|
1878
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1879
|
+
ref: attachViewport,
|
|
1880
|
+
className: "ya-recent-list ya-virtual-viewport",
|
|
1881
|
+
role: "tree",
|
|
1882
|
+
"aria-label": ariaLabel,
|
|
1883
|
+
style: maxHeight === void 0 ? void 0 : { maxHeight: `${maxHeight}px` },
|
|
1884
|
+
onScroll: handleScroll,
|
|
1885
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1886
|
+
className: "ya-virtual-canvas",
|
|
1887
|
+
style: { height: rows.length * 35 },
|
|
1888
|
+
children: slice
|
|
1889
|
+
})
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1540
1892
|
//#endregion
|
|
1541
1893
|
//#region src/client/WorkspaceSidebar.tsx
|
|
1542
1894
|
/** Two-level workspace/session browser with a persistent global recent block. */
|
|
1543
1895
|
const SEARCH_DEBOUNCE_MS = 250;
|
|
1544
1896
|
const SEARCH_MAX = 500;
|
|
1897
|
+
/** Minimum height kept for the workspace browser below the recent block. */
|
|
1898
|
+
const MIN_WORKSPACE = 120;
|
|
1545
1899
|
function sanitized(value) {
|
|
1546
1900
|
return value.replaceAll("\0", "").slice(0, SEARCH_MAX);
|
|
1547
1901
|
}
|
|
@@ -1661,7 +2015,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
1661
2015
|
]
|
|
1662
2016
|
});
|
|
1663
2017
|
}
|
|
1664
|
-
function WorkspaceItem({ row, enter, create, rename, remove, t }) {
|
|
2018
|
+
function WorkspaceItem({ row, enter, create, rename, remove, now, t }) {
|
|
1665
2019
|
const [menuOpen, setMenuOpen] = (0, react.useState)(false);
|
|
1666
2020
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1667
2021
|
className: `ya-row ya-workspace-row${menuOpen ? " ya-menu-open" : ""}`,
|
|
@@ -1680,9 +2034,9 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
1680
2034
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1681
2035
|
className: "ya-row-title",
|
|
1682
2036
|
children: row.real ? row.title : t("ungrouped")
|
|
1683
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1684
|
-
className: "ya-row-meta",
|
|
1685
|
-
children:
|
|
2037
|
+
}), row.lastUsedAt !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2038
|
+
className: "ya-row-meta ya-row-time",
|
|
2039
|
+
children: relativeTime(row.lastUsedAt, now, t)
|
|
1686
2040
|
})]
|
|
1687
2041
|
}), row.path !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1688
2042
|
className: "ya-workspace-path",
|
|
@@ -1738,23 +2092,27 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
1738
2092
|
}
|
|
1739
2093
|
/** Fill `sidebar.workspaces` with the replacement browser. */
|
|
1740
2094
|
function WorkspaceSidebar(props) {
|
|
1741
|
-
const { wide, expandSidebar, useSessions, useWorkspaces, startSession, open, searchSessions, searchResultLimit, renameSession, forkSession, renameWorkspace, deleteWorkspace, archiveSession, createWorkspace, useDirectoryFlow, renderSlot, t } = props;
|
|
2095
|
+
const { wide, expandSidebar, useSessions, useSessionPendingInteraction, useWorkspaces, startSession, open, searchSessions, searchResultLimit, renameSession, forkSession, renameWorkspace, deleteWorkspace, archiveSession, createWorkspace, useDirectoryFlow, renderSlot, t } = props;
|
|
1742
2096
|
const sessions = useSessions((state) => state);
|
|
1743
2097
|
const workspaceState = useWorkspaces((state) => state);
|
|
1744
2098
|
const workspaces = workspaceState.items;
|
|
1745
2099
|
const archived = workspaceState.archivedSessionIds;
|
|
2100
|
+
const pendingInteractions = useSessionPendingInteraction((state) => state);
|
|
1746
2101
|
const directoryFlowAvailable = useDirectoryFlow((value) => value);
|
|
1747
|
-
const allRows = (0, react.useMemo)(() => deriveRecent(sessions, workspaces, archived, Number.MAX_SAFE_INTEGER), [
|
|
2102
|
+
const allRows = (0, react.useMemo)(() => deriveRecent(sessions, workspaces, archived, pendingInteractions, Number.MAX_SAFE_INTEGER), [
|
|
1748
2103
|
archived,
|
|
2104
|
+
pendingInteractions,
|
|
1749
2105
|
sessions,
|
|
1750
2106
|
workspaces
|
|
1751
2107
|
]);
|
|
1752
|
-
const recent = allRows
|
|
2108
|
+
const recent = allRows;
|
|
1753
2109
|
const workspaceRows = (0, react.useMemo)(() => deriveWorkspaces(sessions, workspaces, archived), [
|
|
1754
2110
|
archived,
|
|
1755
2111
|
sessions,
|
|
1756
2112
|
workspaces
|
|
1757
2113
|
]);
|
|
2114
|
+
const now = Date.now();
|
|
2115
|
+
const workspaceGroups = (0, react.useMemo)(() => deriveWorkspaceGroups(workspaceRows, now), [workspaceRows, now]);
|
|
1758
2116
|
const [selectedKey, setSelectedKey] = (0, react.useState)(null);
|
|
1759
2117
|
const [direction, setDirection] = (0, react.useState)("forward");
|
|
1760
2118
|
const [hasMounted, setHasMounted] = (0, react.useState)(false);
|
|
@@ -1776,15 +2134,15 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
1776
2134
|
if (selectedKey !== null && selectedKey !== "__ya_ungrouped__" && !workspaces.some((workspace) => workspace.workspaceId === selectedKey)) setSelectedKey(UNGROUPED);
|
|
1777
2135
|
}, [selectedKey, workspaces]);
|
|
1778
2136
|
const selectedWorkspace = selectedKey === null || selectedKey === "__ya_ungrouped__" ? void 0 : workspaces.find((workspace) => workspace.workspaceId === selectedKey);
|
|
1779
|
-
const
|
|
1780
|
-
const levelGroups = (0, react.useMemo)(() => selectedKey !== null && selectedKey !== "__ya_ungrouped__" ? deriveWorkspaceSessionGroups(selectedKey, sessions, workspaces, archived, now) : [], [
|
|
2137
|
+
const levelGroups = (0, react.useMemo)(() => selectedKey !== null && selectedKey !== "__ya_ungrouped__" ? deriveWorkspaceSessionGroups(selectedKey, sessions, workspaces, archived, pendingInteractions, now) : [], [
|
|
1781
2138
|
archived,
|
|
2139
|
+
pendingInteractions,
|
|
1782
2140
|
sessions,
|
|
1783
2141
|
workspaces,
|
|
1784
2142
|
selectedKey,
|
|
1785
2143
|
now
|
|
1786
2144
|
]);
|
|
1787
|
-
const levelRows = selectedKey === "__ya_ungrouped__" ? deriveWorkspaceSessions(UNGROUPED, sessions, workspaces, archived) : [];
|
|
2145
|
+
const levelRows = selectedKey === "__ya_ungrouped__" ? deriveWorkspaceSessions(UNGROUPED, sessions, workspaces, archived, pendingInteractions) : [];
|
|
1788
2146
|
const levelEmpty = selectedKey === "__ya_ungrouped__" ? levelRows.length === 0 : levelGroups.every((g) => g.rows.length === 0);
|
|
1789
2147
|
const [query, setQuery] = (0, react.useState)("");
|
|
1790
2148
|
const normalizedQuery = sanitized(query).trim();
|
|
@@ -1859,6 +2217,92 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
1859
2217
|
const [actionMode, setActionModeState] = (0, react.useState)(() => getActionMode());
|
|
1860
2218
|
const [sessionDeleteTarget, setSessionDeleteTarget] = (0, react.useState)(null);
|
|
1861
2219
|
(0, react.useEffect)(() => subscribeActionMode(() => setActionModeState(getActionMode())), []);
|
|
2220
|
+
const [recentHeight, setRecentHeight] = (0, react.useState)(() => getRecentViewportHeight());
|
|
2221
|
+
const [recentResizing, setRecentResizing] = (0, react.useState)(false);
|
|
2222
|
+
(0, react.useEffect)(() => subscribeRecentViewportHeight(() => {
|
|
2223
|
+
setRecentHeight(getRecentViewportHeight());
|
|
2224
|
+
}), []);
|
|
2225
|
+
const bodyRef = (0, react.useRef)(null);
|
|
2226
|
+
const recentBlockRef = (0, react.useRef)(null);
|
|
2227
|
+
const breadcrumbRef = (0, react.useRef)(null);
|
|
2228
|
+
const resizerRef = (0, react.useRef)(null);
|
|
2229
|
+
const recentViewportRef = (0, react.useRef)(null);
|
|
2230
|
+
const recentDrag = (0, react.useRef)(null);
|
|
2231
|
+
/** Effective max-height base and the largest height that keeps MIN_WORKSPACE visible. */
|
|
2232
|
+
const measureRecentBounds = () => {
|
|
2233
|
+
const viewport = recentViewportRef.current;
|
|
2234
|
+
const body = bodyRef.current;
|
|
2235
|
+
const resizer = resizerRef.current;
|
|
2236
|
+
if (viewport === null || body === null || resizer === null) return null;
|
|
2237
|
+
const computed = Number.parseFloat(window.getComputedStyle(viewport).maxHeight);
|
|
2238
|
+
const base = Number.isFinite(computed) ? computed : viewport.clientHeight;
|
|
2239
|
+
const breadcrumb = breadcrumbRef.current;
|
|
2240
|
+
const block = recentBlockRef.current;
|
|
2241
|
+
const chrome = (block !== null ? block.offsetHeight : 0) - viewport.clientHeight + (breadcrumb !== null ? breadcrumb.offsetHeight : 0) + resizer.offsetHeight;
|
|
2242
|
+
return {
|
|
2243
|
+
base,
|
|
2244
|
+
max: Math.max(70, Math.round(body.clientHeight - chrome - MIN_WORKSPACE))
|
|
2245
|
+
};
|
|
2246
|
+
};
|
|
2247
|
+
const beginRecentResize = (event) => {
|
|
2248
|
+
if (event.button !== 0 || !event.isPrimary) return;
|
|
2249
|
+
const bounds = measureRecentBounds();
|
|
2250
|
+
if (bounds === null) return;
|
|
2251
|
+
event.preventDefault();
|
|
2252
|
+
recentDrag.current = {
|
|
2253
|
+
pointerId: event.pointerId,
|
|
2254
|
+
startY: event.clientY,
|
|
2255
|
+
startHeight: bounds.base,
|
|
2256
|
+
max: bounds.max,
|
|
2257
|
+
moved: false
|
|
2258
|
+
};
|
|
2259
|
+
resizerRef.current?.setPointerCapture(event.pointerId);
|
|
2260
|
+
document.body.style.cursor = "row-resize";
|
|
2261
|
+
document.body.style.userSelect = "none";
|
|
2262
|
+
setRecentResizing(true);
|
|
2263
|
+
};
|
|
2264
|
+
const moveRecentResize = (event) => {
|
|
2265
|
+
const drag = recentDrag.current;
|
|
2266
|
+
const viewport = recentViewportRef.current;
|
|
2267
|
+
if (drag === null || viewport === null || event.pointerId !== drag.pointerId) return;
|
|
2268
|
+
drag.moved = true;
|
|
2269
|
+
const next = Math.round(Math.min(drag.max, Math.max(70, drag.startHeight + event.clientY - drag.startY)));
|
|
2270
|
+
viewport.style.maxHeight = `${next}px`;
|
|
2271
|
+
};
|
|
2272
|
+
const endRecentResize = (event) => {
|
|
2273
|
+
const drag = recentDrag.current;
|
|
2274
|
+
if (drag === null) return;
|
|
2275
|
+
recentDrag.current = null;
|
|
2276
|
+
const resizer = resizerRef.current;
|
|
2277
|
+
if (resizer !== null && resizer.hasPointerCapture(event.pointerId)) resizer.releasePointerCapture(event.pointerId);
|
|
2278
|
+
document.body.style.cursor = "";
|
|
2279
|
+
document.body.style.userSelect = "";
|
|
2280
|
+
setRecentResizing(false);
|
|
2281
|
+
if (!drag.moved) return;
|
|
2282
|
+
const viewport = recentViewportRef.current;
|
|
2283
|
+
if (viewport === null) return;
|
|
2284
|
+
const written = Number.parseFloat(viewport.style.maxHeight);
|
|
2285
|
+
if (!Number.isFinite(written)) return;
|
|
2286
|
+
const height = Math.round(written);
|
|
2287
|
+
setRecentHeight(height);
|
|
2288
|
+
setRecentViewportHeight(height);
|
|
2289
|
+
};
|
|
2290
|
+
const keyRecentResize = (event) => {
|
|
2291
|
+
if (event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
|
|
2292
|
+
event.preventDefault();
|
|
2293
|
+
const bounds = measureRecentBounds();
|
|
2294
|
+
if (bounds === null) return;
|
|
2295
|
+
const delta = event.key === "ArrowUp" ? -35 : 35;
|
|
2296
|
+
const next = Math.round(Math.min(bounds.max, Math.max(70, (recentHeight ?? bounds.base) + delta)));
|
|
2297
|
+
setRecentHeight(next);
|
|
2298
|
+
setRecentViewportHeight(next);
|
|
2299
|
+
};
|
|
2300
|
+
const resetRecentHeight = () => {
|
|
2301
|
+
const viewport = recentViewportRef.current;
|
|
2302
|
+
if (viewport !== null) viewport.style.maxHeight = "";
|
|
2303
|
+
setRecentHeight(void 0);
|
|
2304
|
+
setRecentViewportHeight(void 0);
|
|
2305
|
+
};
|
|
1862
2306
|
const beginWorkspaceRename = (row) => {
|
|
1863
2307
|
setWorkspaceRename(row);
|
|
1864
2308
|
setRenameDraft(row.title);
|
|
@@ -2035,6 +2479,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
2035
2479
|
]
|
|
2036
2480
|
}),
|
|
2037
2481
|
wide && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2482
|
+
ref: bodyRef,
|
|
2038
2483
|
className: "ya-body",
|
|
2039
2484
|
children: normalizedQuery !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2040
2485
|
className: "ya-scroll",
|
|
@@ -2057,6 +2502,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
2057
2502
|
]
|
|
2058
2503
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2059
2504
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2505
|
+
ref: recentBlockRef,
|
|
2060
2506
|
className: `ya-recent${recentCollapsed ? " ya-recent-collapsed" : ""}`,
|
|
2061
2507
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2062
2508
|
className: "ya-block-label",
|
|
@@ -2076,13 +2522,34 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
2076
2522
|
children: recent.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2077
2523
|
className: "ya-empty",
|
|
2078
2524
|
children: t("noSessions")
|
|
2079
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
2080
|
-
|
|
2081
|
-
|
|
2525
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VirtualRecentList, {
|
|
2526
|
+
rows: recent,
|
|
2527
|
+
ariaLabel: t("recent"),
|
|
2528
|
+
renderItem: (row) => sessionItem(row, true),
|
|
2529
|
+
viewportRef: (node) => {
|
|
2530
|
+
recentViewportRef.current = node;
|
|
2531
|
+
},
|
|
2532
|
+
maxHeight: recentHeight
|
|
2082
2533
|
})
|
|
2083
2534
|
})]
|
|
2084
2535
|
}),
|
|
2536
|
+
!recentCollapsed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2537
|
+
ref: resizerRef,
|
|
2538
|
+
className: `ya-recent-resizer${recentResizing ? " ya-resizing" : ""}`,
|
|
2539
|
+
role: "separator",
|
|
2540
|
+
"aria-orientation": "horizontal",
|
|
2541
|
+
"aria-label": t("resizeRecent"),
|
|
2542
|
+
"aria-valuenow": recentHeight,
|
|
2543
|
+
tabIndex: 0,
|
|
2544
|
+
onPointerDown: beginRecentResize,
|
|
2545
|
+
onPointerMove: moveRecentResize,
|
|
2546
|
+
onPointerUp: endRecentResize,
|
|
2547
|
+
onPointerCancel: endRecentResize,
|
|
2548
|
+
onDoubleClick: resetRecentHeight,
|
|
2549
|
+
onKeyDown: keyRecentResize
|
|
2550
|
+
}),
|
|
2085
2551
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2552
|
+
ref: breadcrumbRef,
|
|
2086
2553
|
className: "ya-breadcrumb",
|
|
2087
2554
|
children: selectedKey === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2088
2555
|
className: "ya-crumb",
|
|
@@ -2120,7 +2587,11 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
2120
2587
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2121
2588
|
className: hasMounted ? `ya-level-enter-${direction}` : void 0,
|
|
2122
2589
|
children: [
|
|
2123
|
-
selectedKey === null ?
|
|
2590
|
+
selectedKey === null ? workspaceGroups.flatMap((group) => [...group.dateKey === "" ? [] : [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2591
|
+
className: "ya-date-group-label",
|
|
2592
|
+
role: "separator",
|
|
2593
|
+
children: dateGroupLabel(group, now, t)
|
|
2594
|
+
}, `ws-group-${group.dateKey}`)], ...group.rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceItem, {
|
|
2124
2595
|
row,
|
|
2125
2596
|
enter: () => {
|
|
2126
2597
|
setDirection("forward");
|
|
@@ -2136,8 +2607,9 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
2136
2607
|
setDeleteTarget(row);
|
|
2137
2608
|
setRenameError(null);
|
|
2138
2609
|
},
|
|
2610
|
+
now,
|
|
2139
2611
|
t
|
|
2140
|
-
}, row.key)) : selectedKey === "__ya_ungrouped__" ? levelRows.map((row) => sessionItem(row, false)) : levelGroups.flatMap((group) => [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2612
|
+
}, row.key))]) : selectedKey === "__ya_ungrouped__" ? levelRows.map((row) => sessionItem(row, false)) : levelGroups.flatMap((group) => [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2141
2613
|
className: "ya-date-group-label",
|
|
2142
2614
|
role: "separator",
|
|
2143
2615
|
children: dateGroupLabel(group, now, t)
|
|
@@ -2246,12 +2718,17 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
2246
2718
|
}
|
|
2247
2719
|
//#endregion
|
|
2248
2720
|
//#region src/client/index.ts
|
|
2249
|
-
/**
|
|
2721
|
+
/**
|
|
2722
|
+
* Services required by the replacement client entries. `remote` and
|
|
2723
|
+
* `remote.directoryPicker` feed the uiWorkspace stand-in's directory picking.
|
|
2724
|
+
*/
|
|
2250
2725
|
const inject = [
|
|
2251
2726
|
"slots",
|
|
2252
2727
|
"sessions",
|
|
2253
2728
|
"workspaces",
|
|
2254
|
-
"locale"
|
|
2729
|
+
"locale",
|
|
2730
|
+
"remote",
|
|
2731
|
+
"remote.directoryPicker"
|
|
2255
2732
|
];
|
|
2256
2733
|
/** Register the sidebar browser and conversation hero picker. */
|
|
2257
2734
|
function apply(ctx) {
|
|
@@ -2259,8 +2736,21 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
2259
2736
|
zh,
|
|
2260
2737
|
en
|
|
2261
2738
|
}), "ya-workspace-sidebar: dictionaries");
|
|
2262
|
-
|
|
2263
|
-
|
|
2739
|
+
ctx.effect(() => {
|
|
2740
|
+
let dispose;
|
|
2741
|
+
const sync = () => {
|
|
2742
|
+
dispose?.();
|
|
2743
|
+
dispose = void 0;
|
|
2744
|
+
const store = ctx.get("betterLocale");
|
|
2745
|
+
if (store !== void 0) dispose = store.register(NS, dicts);
|
|
2746
|
+
};
|
|
2747
|
+
sync();
|
|
2748
|
+
const unsubscribe = ctx.locale.subscribe(sync);
|
|
2749
|
+
return () => {
|
|
2750
|
+
unsubscribe();
|
|
2751
|
+
dispose?.();
|
|
2752
|
+
};
|
|
2753
|
+
}, "ya-workspace-sidebar: better-locale override dicts");
|
|
2264
2754
|
ctx.effect(installStyles, "ya-workspace-sidebar: styles");
|
|
2265
2755
|
const flowSource = (name) => ({
|
|
2266
2756
|
getSnapshot: () => ctx.slots.entries(name).length > 0,
|
|
@@ -2269,6 +2759,8 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
2269
2759
|
const sidebarFlow = flowSource("sidebar.workspaces.directoryFlow");
|
|
2270
2760
|
const pickerFlow = flowSource("conversation.hero.workspace.directoryFlow");
|
|
2271
2761
|
const createWorkspace = (input) => ctx.workspaces.create(input);
|
|
2762
|
+
const navigation = new YaWorkspaceNavigation(ctx, ctx.remote.directoryPicker, ctx.workspaces, ctx.sessions);
|
|
2763
|
+
ctx.slots.provideRoot({ hooks: { workspaces: ctx.workspaces.list } });
|
|
2272
2764
|
const searchSessions = async (query, signal) => {
|
|
2273
2765
|
const result = await ctx.sessions.search(query, signal);
|
|
2274
2766
|
if (!result.ok) throw new Error(result.error.message);
|
|
@@ -2276,7 +2768,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
2276
2768
|
};
|
|
2277
2769
|
const sidebarInjected = () => ({
|
|
2278
2770
|
startSession: (workspaceId) => {
|
|
2279
|
-
|
|
2771
|
+
navigation.startSession(workspaceId);
|
|
2280
2772
|
},
|
|
2281
2773
|
open: (sessionId) => {
|
|
2282
2774
|
ctx.sessions.open(sessionId);
|
|
@@ -2304,7 +2796,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
|
|
|
2304
2796
|
await ctx.workspaces.delete(workspaceId);
|
|
2305
2797
|
},
|
|
2306
2798
|
archiveSession: async (sessionId) => {
|
|
2307
|
-
await
|
|
2799
|
+
await navigation.archiveSession(sessionId);
|
|
2308
2800
|
},
|
|
2309
2801
|
insertSessionBefore: async (workspaceId, sessionId, beforeSessionId) => {
|
|
2310
2802
|
await ctx.workspaces.insertSessionBefore(workspaceId, sessionId, beforeSessionId);
|