@huanlin/dsh-plugin-ya-workspace-sidebar 0.7.0 → 0.8.1

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/lib/client.js CHANGED
@@ -1203,7 +1203,45 @@ window.__ModuleLoader__.load({
1203
1203
  * pre-split client runtime: reuse-or-create Workspace connection, the
1204
1204
  * explicit/current/recent New Session fallback, archived-current clearing,
1205
1205
  * boot auto-selection, and the directory-picking wire calls.
1206
+ *
1207
+ * dsh 0.1.7-rc.1 moved the Session selection out of the Session Controller
1208
+ * (`SessionListState.current` / `ISessions.open` / `ISessions.clear` are gone)
1209
+ * into this service: a retained `mainReference` plus a persisted selection
1210
+ * store (`dsh.sessions.current`). This port mirrors the shipped
1211
+ * `UiWorkspaceService` selection mechanics.
1206
1212
  */
1213
+ /** Persisted selection store (the official `dsh.sessions.current` key). */
1214
+ var SelectionStore = class {
1215
+ key;
1216
+ listeners = /* @__PURE__ */ new Set();
1217
+ value;
1218
+ constructor(key = "dsh.sessions.current") {
1219
+ this.key = key;
1220
+ this.value = this.read();
1221
+ }
1222
+ getSnapshot = () => this.value;
1223
+ subscribe = (listener) => {
1224
+ this.listeners.add(listener);
1225
+ return () => {
1226
+ this.listeners.delete(listener);
1227
+ };
1228
+ };
1229
+ set = (value) => {
1230
+ this.value = value;
1231
+ try {
1232
+ globalThis.localStorage?.setItem(this.key, JSON.stringify(value));
1233
+ } catch {}
1234
+ for (const listener of [...this.listeners]) listener();
1235
+ };
1236
+ read() {
1237
+ try {
1238
+ const raw = globalThis.localStorage?.getItem(this.key);
1239
+ return raw === null || raw === void 0 ? {} : JSON.parse(raw);
1240
+ } catch {
1241
+ return {};
1242
+ }
1243
+ }
1244
+ };
1207
1245
  /** Implements Workspace navigation and directory UI operations. */
1208
1246
  var YaWorkspaceNavigation = class extends _deepseek_ai_cordis.Service {
1209
1247
  directoryPicker;
@@ -1211,6 +1249,8 @@ window.__ModuleLoader__.load({
1211
1249
  sessions;
1212
1250
  connecting = /* @__PURE__ */ new Map();
1213
1251
  lifetime = new AbortController();
1252
+ selection = new SelectionStore();
1253
+ mainReference;
1214
1254
  /**
1215
1255
  * @param ctx - Client root Context.
1216
1256
  * @param directoryPicker - the directory-picking Remote namespace.
@@ -1224,61 +1264,95 @@ window.__ModuleLoader__.load({
1224
1264
  this.sessions = sessions;
1225
1265
  ctx.effect(() => this.watchNavigation(), "ya-workspace-sidebar: Workspace navigation policy");
1226
1266
  }
1267
+ /** The Session currently shown in the main view, or undefined. */
1268
+ get currentSessionId() {
1269
+ return this.mainReference?.sessionId;
1270
+ }
1271
+ /** Observe the current selection (the sidebar's blank-row filter and highlight). */
1272
+ subscribeSelection(listener) {
1273
+ return this.selection.subscribe(listener);
1274
+ }
1227
1275
  async connectWorkspace(workspaceId) {
1228
1276
  const workspace = this.workspaces.list.getSnapshot().items.find((item) => item.workspaceId === workspaceId);
1229
1277
  if (workspace === void 0) throw new Error(`ya-workspace-sidebar: unknown workspace "${workspaceId}"`);
1230
1278
  const inflight = this.connecting.get(workspaceId);
1231
1279
  if (inflight !== void 0) return inflight;
1280
+ const attempt = this.reuseOrCreateBlank(workspace).finally(() => {
1281
+ this.connecting.delete(workspaceId);
1282
+ });
1283
+ this.connecting.set(workspaceId, attempt);
1284
+ return attempt;
1285
+ }
1286
+ reuseOrCreateBlank(workspace) {
1232
1287
  const archived = this.workspaces.list.getSnapshot().archivedSessionIds;
1233
1288
  const sessions = this.sessions.list.getSnapshot();
1234
1289
  for (const id of sessions.ids) {
1235
1290
  const summary = sessions.byId[id];
1236
- if (summary !== void 0 && summary.blank && summary.cwd === workspace.path && workspace.sessionIds.includes(summary.id) && !archived.includes(summary.id)) return summary.id;
1291
+ if (summary === void 0 || !summary.blank || summary.cwd !== workspace.path || !workspace.sessionIds.includes(id) || archived.includes(id)) continue;
1292
+ return this.reuseBlank(workspace.workspaceId, id);
1237
1293
  }
1238
- const attempt = this.sessions.create({ workspaceId }).finally(() => {
1239
- this.connecting.delete(workspaceId);
1240
- });
1241
- this.connecting.set(workspaceId, attempt);
1242
- return attempt;
1294
+ return this.sessions.create({ workspaceId: workspace.workspaceId });
1243
1295
  }
1244
- openSession(sessionId) {
1245
- this.sessions.open(sessionId);
1246
- this.ctx.layout.selectPanel(null);
1296
+ async reuseBlank(workspaceId, sessionId) {
1297
+ try {
1298
+ return await this.sessions.create({
1299
+ workspaceId,
1300
+ sessionId
1301
+ });
1302
+ } catch (error) {
1303
+ if (!(error instanceof Error) || error.name !== "SessionCreateError") throw error;
1304
+ if (error.rpcError?.code !== "session/writer-held") throw error;
1305
+ return this.sessions.create({ workspaceId });
1306
+ }
1307
+ }
1308
+ openSession(target) {
1309
+ this.replaceMain(target, this.lifetime.signal, "reveal");
1247
1310
  }
1248
1311
  async openWorkspace(workspaceId, beforeOpen) {
1249
1312
  const navigation = AbortSignal.any([this.ctx.layout.beginNavigation(), this.lifetime.signal]);
1250
- const isCurrent = () => !navigation.aborted;
1251
- const sessionId = await this.connectWorkspace(workspaceId);
1252
- if (!isCurrent()) return;
1253
- beforeOpen?.(sessionId);
1254
- if (isCurrent()) this.openSession(sessionId);
1313
+ let sessionId;
1314
+ try {
1315
+ sessionId = await this.connectWorkspace(workspaceId);
1316
+ } catch (error) {
1317
+ if (!navigation.aborted) console.warn("ya-workspace-sidebar: workspace open failed:", error);
1318
+ throw error;
1319
+ }
1320
+ if (navigation.aborted) return;
1321
+ this.replaceMain(sessionId, navigation, "reveal", beforeOpen);
1255
1322
  }
1256
1323
  async forkSession(sessionId) {
1257
- const navigation = AbortSignal.any([this.ctx.layout.beginNavigation(), this.lifetime.signal]);
1258
- const childId = await this.sessions.fork({
1324
+ await this.sessions.fork({
1259
1325
  sessionId,
1260
1326
  increaseTitle: true
1261
1327
  });
1262
- if (!navigation.aborted) this.openSession(childId);
1263
1328
  }
1264
1329
  startSession(workspaceId) {
1265
1330
  const workspaces = this.workspaces.list.getSnapshot();
1266
1331
  const sessions = this.sessions.list.getSnapshot();
1267
- const current = sessions.current;
1332
+ const current = this.mainReference?.sessionId;
1268
1333
  const currentWorkspaceId = current === void 0 ? void 0 : workspaces.items.find((item) => item.sessionIds.includes(current))?.workspaceId;
1269
1334
  const recent = workspaces.phase === "ready" && sessions.phase === "ready" ? recentWorkspace(workspaces.items, sessions.byId) : void 0;
1270
1335
  const target = workspaceId ?? currentWorkspaceId ?? recent;
1271
1336
  if (target === void 0) {
1272
- this.sessions.clear();
1273
- this.ctx.layout.selectPanel(null);
1337
+ this.clearMain();
1274
1338
  return;
1275
1339
  }
1276
1340
  this.openWorkspace(target).then(() => {}, (reason) => {
1277
1341
  console.warn("ya-workspace-sidebar: new session failed:", reason);
1278
1342
  });
1279
1343
  }
1280
- async archiveSession(sessionId) {
1281
- await this.workspaces.archiveSession(sessionId);
1344
+ async archiveSession(sessionId, options = {}) {
1345
+ await this.workspaces.archiveSession(sessionId, options);
1346
+ if (this.mainReference?.sessionId === sessionId) this.clearMain();
1347
+ }
1348
+ async unarchiveSession(sessionId) {
1349
+ await this.workspaces.unarchiveSession(sessionId);
1350
+ }
1351
+ async pinSession(sessionId) {
1352
+ await this.workspaces.pinSession(sessionId);
1353
+ }
1354
+ async unpinSession(sessionId) {
1355
+ await this.workspaces.unpinSession(sessionId);
1282
1356
  }
1283
1357
  async pickDirectory() {
1284
1358
  const result = await this.directoryPicker.pick();
@@ -1304,24 +1378,17 @@ window.__ModuleLoader__.load({
1304
1378
  const workspaces = this.workspaces.list.getSnapshot();
1305
1379
  const sessions = this.sessions.list.getSnapshot();
1306
1380
  if (workspaces.phase !== "ready" || sessions.phase !== "ready") return;
1307
- if (sessions.current !== void 0) {
1308
- initial = "done";
1309
- return;
1310
- }
1311
- const target = recentWorkspace(workspaces.items, sessions.byId);
1312
- if (target === void 0) {
1381
+ if (this.mainReference !== void 0) {
1313
1382
  initial = "done";
1314
1383
  return;
1315
1384
  }
1316
1385
  initial = "connecting";
1317
- this.connectWorkspace(target).then((sessionId) => {
1318
- if (this.lifetime.signal.aborted) return;
1319
- if (this.sessions.list.getSnapshot().current === void 0) this.sessions.open(sessionId);
1386
+ this.restoreSelection(workspaces, sessions).then(() => {
1320
1387
  initial = "done";
1321
1388
  }, (reason) => {
1322
1389
  if (this.lifetime.signal.aborted) return;
1323
1390
  initial = "waiting";
1324
- console.warn("ya-workspace-sidebar: initial workspace selection failed:", reason);
1391
+ console.warn("ya-workspace-sidebar: initial Session restoration failed:", reason);
1325
1392
  });
1326
1393
  };
1327
1394
  const disposeWorkspaces = this.workspaces.list.subscribe(reconcile);
@@ -1333,13 +1400,63 @@ window.__ModuleLoader__.load({
1333
1400
  disposeWorkspaces();
1334
1401
  };
1335
1402
  }
1403
+ async restoreSelection(workspaces, sessions) {
1404
+ const saved = this.selection.getSnapshot();
1405
+ if (saved.subagentAddress !== void 0) {
1406
+ this.replaceMain(saved.subagentAddress, this.lifetime.signal, "preserve");
1407
+ return;
1408
+ }
1409
+ const summary = saved.sessionId === void 0 ? void 0 : sessions.byId[saved.sessionId];
1410
+ const workspace = summary === void 0 ? void 0 : workspaces.items.find((item) => item.sessionIds.includes(summary.id));
1411
+ if (summary !== void 0 && (!summary.blank || workspace === void 0)) {
1412
+ this.replaceMain(summary.id, this.lifetime.signal, "preserve");
1413
+ return;
1414
+ }
1415
+ const navigation = AbortSignal.any([this.ctx.layout.beginNavigation(), this.lifetime.signal]);
1416
+ let sessionId;
1417
+ if (summary !== void 0 && workspace !== void 0 && summary.cwd === workspace.path && !workspaces.archivedSessionIds.includes(summary.id)) sessionId = await this.reuseBlank(workspace.workspaceId, summary.id);
1418
+ const target = workspace?.workspaceId ?? recentWorkspace(workspaces.items, sessions.byId);
1419
+ if (sessionId === void 0 && target !== void 0) sessionId = await this.connectWorkspace(target);
1420
+ if (sessionId !== void 0 && !navigation.aborted) this.replaceMain(sessionId, navigation, "preserve");
1421
+ }
1336
1422
  /** @returns true when an archived current selection was cleared. */
1337
1423
  clearArchivedCurrent() {
1338
- const current = this.sessions.list.getSnapshot().current;
1424
+ const current = this.mainReference?.sessionId;
1339
1425
  if (current === void 0 || !this.workspaces.list.getSnapshot().archivedSessionIds.includes(current)) return false;
1340
- this.sessions.clear();
1426
+ this.clearMain();
1341
1427
  return true;
1342
1428
  }
1429
+ clearMain() {
1430
+ const previous = this.mainReference;
1431
+ this.mainReference = void 0;
1432
+ this.selection.set({});
1433
+ previous?.release();
1434
+ this.ctx.layout.selectPanel(null);
1435
+ }
1436
+ replaceMain(target, signal, panel, beforeOpen) {
1437
+ signal.throwIfAborted();
1438
+ const reference = this.sessions.retain(target, { source: "mainView" });
1439
+ try {
1440
+ signal.throwIfAborted();
1441
+ beforeOpen?.(reference.sessionId);
1442
+ if (signal.aborted) {
1443
+ reference.release();
1444
+ return;
1445
+ }
1446
+ const subagentAddress = typeof target === "string" ? this.sessions.subagentAddress(reference.sessionId) : target;
1447
+ this.selection.set({
1448
+ sessionId: reference.sessionId,
1449
+ ...subagentAddress === void 0 ? {} : { subagentAddress }
1450
+ });
1451
+ } catch (error) {
1452
+ reference.release();
1453
+ throw error;
1454
+ }
1455
+ const previous = this.mainReference;
1456
+ this.mainReference = reference;
1457
+ previous?.release();
1458
+ if (panel === "reveal") this.ctx.layout.selectPanel(null);
1459
+ }
1343
1460
  };
1344
1461
  /** Stable tie-breaking follows Host Workspace order. */
1345
1462
  function recentWorkspace(workspaces, sessions) {
@@ -1402,14 +1519,14 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
1402
1519
  .ya-workspace-row { min-height:40px; }
1403
1520
  .ya-row-main { flex:1; min-width:0; display:flex; flex-direction:column; justify-content:center; }
1404
1521
  .ya-row-line { display:flex; align-items:center; min-width:0; gap:6px; }
1405
- .ya-row-title { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:13px; line-height:18px; }
1522
+ .ya-row-title { flex:1; min-width:0; overflow:hidden; white-space:nowrap; font-size:13px; line-height:18px; -webkit-mask-image:linear-gradient(to right,#000 calc(100% - 24px),transparent 100%); mask-image:linear-gradient(to right,#000 calc(100% - 24px),transparent 100%); }
1406
1523
  .ya-row-meta { flex:none; color:var(--dsw-alias-label-tertiary); font-size:11px; white-space:nowrap; }
1407
1524
  .ya-workspace-path { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--dsw-alias-label-tertiary); font-size:11px; line-height:15px; }
1408
1525
  .ya-row-actions { flex:none; display:flex; align-items:center; gap:2px; opacity:0; pointer-events:none; transition:opacity 120ms ease-out; }
1409
1526
  .ya-row:hover .ya-row-actions, .ya-menu-open .ya-row-actions { opacity:1; pointer-events:auto; }
1410
1527
  .ya-status-slot { flex:none; width:16px; height:16px; display:inline-flex; align-items:center; justify-content:center; color:var(--dsw-alias-label-tertiary); }
1411
1528
  .ya-recent .ya-row { min-height:31px; }
1412
- .ya-search-workspace { color:var(--dsw-alias-label-tertiary); font-size:11px; line-height:15px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
1529
+ .ya-search-workspace { color:var(--dsw-alias-label-tertiary); font-size:11px; line-height:15px; overflow:hidden; white-space:nowrap; -webkit-mask-image:linear-gradient(to right,#000 calc(100% - 24px),transparent 100%); mask-image:linear-gradient(to right,#000 calc(100% - 24px),transparent 100%); }
1413
1530
  .ya-empty, .ya-status { padding:18px 10px; color:var(--dsw-alias-label-tertiary); text-align:center; font-size:13px; }
1414
1531
  .ya-warning { color:var(--dsw-alias-status-warning); }
1415
1532
  .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; }
@@ -1464,14 +1581,14 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
1464
1581
  const addEntries = flowAvailable ? [{
1465
1582
  id: ADD,
1466
1583
  label: t("addWorkspaceMenu"),
1467
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 16 }),
1584
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, { size: 16 }),
1468
1585
  disabled: flowOpen || busy
1469
1586
  }] : [];
1470
1587
  const pinnedAdd = !addOnly && snapshot.items.length > 0;
1471
1588
  const items = pinnedAdd ? snapshot.items.map((workspace) => ({
1472
1589
  id: workspace.workspaceId,
1473
1590
  label: workspace.title,
1474
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderClose16, { size: 16 }),
1591
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderCloseRegular, { size: 16 }),
1475
1592
  disabled: flowOpen || busy
1476
1593
  })) : addEntries;
1477
1594
  const settled = addOnly || snapshot.phase === "ready";
@@ -1575,7 +1692,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
1575
1692
  //#region src/client/model.ts
1576
1693
  /** Resolve the visible status-dot kind for one session, if any. */
1577
1694
  function pendingKindOf(pending, id) {
1578
- const kind = pending.get(id)?.kind;
1695
+ const kind = pending.get(id)?.pendingInteraction?.kind;
1579
1696
  return kind === "approval" || kind === "plan-review" || kind === "question" ? kind : void 0;
1580
1697
  }
1581
1698
  /** Navigation key for sessions not accounted to a real workspace. */
@@ -1591,7 +1708,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
1591
1708
  blank: summary.blank,
1592
1709
  running: summary.running,
1593
1710
  ...pendingInteraction === void 0 ? {} : { pendingInteraction },
1594
- completed: summary.completed === true,
1711
+ completed: pending.get(summary.id)?.completionUnread === true,
1595
1712
  updatedAt: summary.updatedAt,
1596
1713
  workspaceKey,
1597
1714
  workspaceTitle,
@@ -2122,7 +2239,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
2122
2239
  const title = row.blank ? t("newSession") : row.title;
2123
2240
  const isDelete = actionMode === "delete";
2124
2241
  const actionLabel = isDelete ? t("deleteSession") : t("archive");
2125
- const actionIcon = isDelete ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconArchiveOutline20, { size: 16 });
2242
+ const actionIcon = isDelete ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutlineRegular, {}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconArchiveOutlineRegular, { size: 16 });
2126
2243
  const hasCwd = row.cwd !== void 0;
2127
2244
  const pathsKnown = paths !== void 0;
2128
2245
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -2183,17 +2300,17 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
2183
2300
  {
2184
2301
  id: "rename",
2185
2302
  label: t("rename"),
2186
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, {})
2303
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutlineRegular, {})
2187
2304
  },
2188
2305
  {
2189
2306
  id: "fork",
2190
2307
  label: t("fork"),
2191
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, {})
2308
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutlineRegular, {})
2192
2309
  },
2193
2310
  {
2194
2311
  id: "unread",
2195
2312
  label: unread ? t("markRead") : t("markUnread"),
2196
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconClockOutline16, {})
2313
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconClockOutlineRegular, {})
2197
2314
  },
2198
2315
  {
2199
2316
  type: "separator",
@@ -2202,31 +2319,31 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
2202
2319
  {
2203
2320
  id: "reveal",
2204
2321
  label: t("revealInExplorer"),
2205
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenOutline16, {}),
2322
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenOutlineRegular, {}),
2206
2323
  disabled: !hasCwd
2207
2324
  },
2208
2325
  {
2209
2326
  id: "copy-path",
2210
2327
  label: t("copyPath"),
2211
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutline16, {}),
2328
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutlineRegular, {}),
2212
2329
  disabled: !hasCwd
2213
2330
  },
2214
2331
  {
2215
2332
  id: "copy-dir",
2216
2333
  label: t("copySessionDir"),
2217
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutline16, {}),
2334
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutlineRegular, {}),
2218
2335
  disabled: !pathsKnown || paths?.dir == null
2219
2336
  },
2220
2337
  {
2221
2338
  id: "copy-log",
2222
2339
  label: t("copyLogPath"),
2223
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutline16, {}),
2340
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutlineRegular, {}),
2224
2341
  disabled: !pathsKnown || paths?.log == null
2225
2342
  },
2226
2343
  {
2227
2344
  id: "copy-id",
2228
2345
  label: t("copySessionId"),
2229
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconLinkOutline16, {})
2346
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconLinkOutlineRegular, {})
2230
2347
  },
2231
2348
  {
2232
2349
  type: "separator",
@@ -2292,24 +2409,33 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
2292
2409
  onOpenMenu(row);
2293
2410
  setMenuOpenButton((value) => !value);
2294
2411
  },
2295
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, {})
2412
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutlineRegular, {})
2296
2413
  })
2297
2414
  })
2298
2415
  })
2299
2416
  ]
2300
2417
  });
2301
2418
  }
2302
- function WorkspaceItem({ row, enter, create, rename, remove, now, t }) {
2303
- const [menuOpen, setMenuOpen] = (0, react.useState)(false);
2419
+ function WorkspaceItem({ row, enter, create, reveal, copy, rename, remove, now, t }) {
2420
+ const [menuAt, setMenuAt] = (0, react.useState)(null);
2421
+ const hasPath = row.path !== void 0;
2304
2422
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2305
- className: `ya-row ya-workspace-row${menuOpen ? " ya-menu-open" : ""}`,
2423
+ className: `ya-row ya-workspace-row${menuAt !== null ? " ya-menu-open" : ""}`,
2306
2424
  role: "treeitem",
2307
2425
  onClick: enter,
2308
2426
  title: row.path,
2427
+ onContextMenu: (event) => {
2428
+ if (!row.real) return;
2429
+ event.preventDefault();
2430
+ setMenuAt({
2431
+ x: event.clientX,
2432
+ y: event.clientY
2433
+ });
2434
+ },
2309
2435
  children: [
2310
2436
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2311
2437
  className: "ya-status-slot",
2312
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderClose16, {})
2438
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderCloseRegular, {})
2313
2439
  }),
2314
2440
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2315
2441
  className: "ya-row-main",
@@ -2327,61 +2453,84 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
2327
2453
  children: row.path
2328
2454
  })]
2329
2455
  }),
2330
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2331
- className: "ya-row-actions",
2332
- children: [row.real && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
2333
- open: menuOpen,
2334
- onClose: () => {
2335
- setMenuOpen(false);
2456
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutlineMedium, {}),
2457
+ row.real && menuAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
2458
+ open: true,
2459
+ onClose: () => {
2460
+ setMenuAt(null);
2461
+ },
2462
+ items: [
2463
+ {
2464
+ id: "create",
2465
+ label: t("newSession"),
2466
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, {})
2467
+ },
2468
+ {
2469
+ type: "separator",
2470
+ id: "sep-ws-paths"
2471
+ },
2472
+ {
2473
+ id: "reveal",
2474
+ label: t("revealInExplorer"),
2475
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpenOutlineRegular, {}),
2476
+ disabled: !hasPath
2477
+ },
2478
+ {
2479
+ id: "copy-path",
2480
+ label: t("copyPath"),
2481
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCopyOutlineRegular, {}),
2482
+ disabled: !hasPath
2483
+ },
2484
+ {
2485
+ type: "separator",
2486
+ id: "sep-ws-manage"
2336
2487
  },
2337
- items: [{
2488
+ {
2338
2489
  id: "rename",
2339
2490
  label: t("rename"),
2340
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, {})
2341
- }, {
2491
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutlineRegular, {})
2492
+ },
2493
+ {
2342
2494
  id: "delete",
2343
2495
  label: t("deleteWorkspace"),
2344
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, {}),
2496
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutlineRegular, {}),
2345
2497
  danger: true
2346
- }],
2347
- onSelect: (id) => {
2348
- setMenuOpen(false);
2349
- if (id === "rename") rename();
2350
- if (id === "delete") remove();
2351
- },
2352
- portal: true,
2353
- closeOnPointerLeave: true,
2354
- anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2355
- type: "button",
2356
- className: "ya-icon-button",
2357
- onClick: (event) => {
2358
- event.stopPropagation();
2359
- setMenuOpen((value) => !value);
2360
- },
2361
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEllipsisOutline16, {})
2362
- })
2363
- }), row.real && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2364
- type: "button",
2365
- className: "ya-icon-button",
2366
- onClick: (event) => {
2367
- event.stopPropagation();
2368
- create();
2369
- },
2370
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {})
2371
- })]
2372
- }),
2373
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {})
2498
+ }
2499
+ ],
2500
+ onSelect: (id) => {
2501
+ setMenuAt(null);
2502
+ if (id === "create") create();
2503
+ if (id === "reveal" && row.path !== void 0) {
2504
+ reveal(row.path);
2505
+ return;
2506
+ }
2507
+ if (id === "copy-path" && row.path !== void 0) {
2508
+ copy(row.path);
2509
+ return;
2510
+ }
2511
+ if (id === "rename") rename();
2512
+ if (id === "delete") remove();
2513
+ },
2514
+ portal: true,
2515
+ getAnchorRect: () => new DOMRect(menuAt.x, menuAt.y, 0, 0),
2516
+ anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { "aria-hidden": "true" })
2517
+ })
2374
2518
  ]
2375
2519
  });
2376
2520
  }
2377
2521
  /** Fill `sidebar.workspaces` with the replacement browser. */
2378
2522
  function WorkspaceSidebar(props) {
2379
- const { wide, expandSidebar, useSessions, useSessionPendingInteraction, useWorkspaces, startSession, open, searchSessions, searchResultLimit, renameSession, forkSession, renameWorkspace, deleteWorkspace, archiveSession, createWorkspace, useDirectoryFlow, renderSlot, t, resolveSessionPaths, revealInExplorer } = props;
2523
+ const { wide, expandSidebar, useSessions, useSessionStatus, useCurrentSession, useWorkspaces, startSession, open, searchSessions, searchResultLimit, renameSession, forkSession, renameWorkspace, deleteWorkspace, archiveSession, createWorkspace, useDirectoryFlow, renderSlot, t, resolveSessionPaths, revealInExplorer } = props;
2380
2524
  const sessions = useSessions((state) => state);
2525
+ const current = useCurrentSession((value) => value);
2526
+ const sessionsView = (0, react.useMemo)(() => ({
2527
+ ...sessions,
2528
+ current
2529
+ }), [sessions, current]);
2381
2530
  const workspaceState = useWorkspaces((state) => state);
2382
2531
  const workspaces = workspaceState.items;
2383
2532
  const archived = workspaceState.archivedSessionIds;
2384
- const pendingInteractions = useSessionPendingInteraction((state) => state);
2533
+ const pendingInteractions = useSessionStatus((state) => state);
2385
2534
  const directoryFlowAvailable = useDirectoryFlow((value) => value);
2386
2535
  const [pinnedOrder, setPinnedOrder] = (0, react.useState)(() => getPinnedOrder());
2387
2536
  const [unreadSet, setUnreadSet] = (0, react.useState)(() => getUnread());
@@ -2407,16 +2556,16 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
2407
2556
  setPathsTick((tick) => tick + 1);
2408
2557
  }).catch(() => {});
2409
2558
  };
2410
- const allRows = (0, react.useMemo)(() => deriveRecent(sessions, workspaces, archived, pendingInteractions, Number.MAX_SAFE_INTEGER), [
2559
+ const allRows = (0, react.useMemo)(() => deriveRecent(sessionsView, workspaces, archived, pendingInteractions, Number.MAX_SAFE_INTEGER), [
2411
2560
  archived,
2412
2561
  pendingInteractions,
2413
- sessions,
2562
+ sessionsView,
2414
2563
  workspaces
2415
2564
  ]);
2416
2565
  const recent = (0, react.useMemo)(() => applyPinned(allRows, pinnedOrder), [allRows, pinnedOrder]);
2417
- const workspaceRows = (0, react.useMemo)(() => deriveWorkspaces(sessions, workspaces, archived), [
2566
+ const workspaceRows = (0, react.useMemo)(() => deriveWorkspaces(sessionsView, workspaces, archived), [
2418
2567
  archived,
2419
- sessions,
2568
+ sessionsView,
2420
2569
  workspaces
2421
2570
  ]);
2422
2571
  const now = Date.now();
@@ -2430,31 +2579,31 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
2430
2579
  const observedCurrent = (0, react.useRef)(void 0);
2431
2580
  const initialized = (0, react.useRef)(false);
2432
2581
  (0, react.useEffect)(() => {
2433
- if (initialized.current && observedCurrent.current === sessions.current) return;
2582
+ if (initialized.current && observedCurrent.current === current) return;
2434
2583
  initialized.current = true;
2435
- observedCurrent.current = sessions.current;
2436
- if (sessions.current !== void 0) {
2584
+ observedCurrent.current = current;
2585
+ if (current !== void 0) {
2437
2586
  setDirection("forward");
2438
- setSelectedKey(workspaceKeyForSession(sessions.current, workspaces));
2587
+ setSelectedKey(workspaceKeyForSession(current, workspaces));
2439
2588
  }
2440
- }, [sessions.current, workspaces]);
2589
+ }, [current, workspaces]);
2441
2590
  (0, react.useEffect)(() => {
2442
2591
  if (selectedKey !== null && selectedKey !== "__ya_ungrouped__" && !workspaces.some((workspace) => workspace.workspaceId === selectedKey)) setSelectedKey(UNGROUPED);
2443
2592
  }, [selectedKey, workspaces]);
2444
2593
  const selectedWorkspace = selectedKey === null || selectedKey === "__ya_ungrouped__" ? void 0 : workspaces.find((workspace) => workspace.workspaceId === selectedKey);
2445
- const levelGroups = (0, react.useMemo)(() => selectedKey !== null && selectedKey !== "__ya_ungrouped__" ? deriveWorkspaceSessionGroups(selectedKey, sessions, workspaces, archived, pendingInteractions, now) : [], [
2594
+ const levelGroups = (0, react.useMemo)(() => selectedKey !== null && selectedKey !== "__ya_ungrouped__" ? deriveWorkspaceSessionGroups(selectedKey, sessionsView, workspaces, archived, pendingInteractions, now) : [], [
2446
2595
  archived,
2447
2596
  pendingInteractions,
2448
- sessions,
2597
+ sessionsView,
2449
2598
  workspaces,
2450
2599
  selectedKey,
2451
2600
  now
2452
2601
  ]);
2453
2602
  const levelPinned = (0, react.useMemo)(() => extractPinnedGroups(levelGroups, pinnedOrder), [levelGroups, pinnedOrder]);
2454
- const levelRows = (0, react.useMemo)(() => applyPinned(selectedKey === "__ya_ungrouped__" ? deriveWorkspaceSessions(UNGROUPED, sessions, workspaces, archived, pendingInteractions) : [], pinnedOrder), [
2603
+ const levelRows = (0, react.useMemo)(() => applyPinned(selectedKey === "__ya_ungrouped__" ? deriveWorkspaceSessions(UNGROUPED, sessionsView, workspaces, archived, pendingInteractions) : [], pinnedOrder), [
2455
2604
  archived,
2456
2605
  pendingInteractions,
2457
- sessions,
2606
+ sessionsView,
2458
2607
  workspaces,
2459
2608
  selectedKey,
2460
2609
  pinnedOrder
@@ -2709,7 +2858,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
2709
2858
  };
2710
2859
  const sessionItem = (row, context = false) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionItem, {
2711
2860
  row,
2712
- current: sessions.current,
2861
+ current,
2713
2862
  now,
2714
2863
  open: openRow,
2715
2864
  rename: beginSessionRename,
@@ -2746,7 +2895,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
2746
2895
  event.stopPropagation();
2747
2896
  toggleActionMode();
2748
2897
  },
2749
- children: actionMode === "delete" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, { size: wide ? 16 : 18 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconArchiveOutline20, { size: wide ? 16 : 18 })
2898
+ children: actionMode === "delete" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutlineRegular, { size: wide ? 16 : 18 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconArchiveOutlineRegular, { size: wide ? 16 : 18 })
2750
2899
  }),
2751
2900
  directoryFlowAvailable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2752
2901
  ref: pickerAnchor,
@@ -2756,7 +2905,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
2756
2905
  onClick: () => {
2757
2906
  setPickerOpen((value) => !value);
2758
2907
  },
2759
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconProjectAddOutline16, { size: wide ? 16 : 18 })
2908
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconProjectAddOutlineRegular, { size: wide ? 16 : 18 })
2760
2909
  }),
2761
2910
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspacePickFlow, {
2762
2911
  t,
@@ -2788,7 +2937,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
2788
2937
  type: "button",
2789
2938
  className: "ya-search-icon",
2790
2939
  "aria-label": t("search"),
2791
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, { size: wide ? 14 : 18 })
2940
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutlineRegular, { size: wide ? 14 : 18 })
2792
2941
  }),
2793
2942
  wide && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2794
2943
  className: "ya-search-input",
@@ -2806,7 +2955,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
2806
2955
  onClick: () => {
2807
2956
  setQuery("");
2808
2957
  },
2809
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseFill14, {})
2958
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseFillMedium, {})
2810
2959
  })
2811
2960
  ]
2812
2961
  }),
@@ -2847,7 +2996,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
2847
2996
  event.stopPropagation();
2848
2997
  setRecentCollapsed((value) => !value);
2849
2998
  },
2850
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {})
2999
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutlineMedium, {})
2851
3000
  })]
2852
3001
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2853
3002
  className: "ya-recent-list-wrap",
@@ -2896,7 +3045,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
2896
3045
  },
2897
3046
  children: t("workspaces")
2898
3047
  }),
2899
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {}),
3048
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutlineMedium, {}),
2900
3049
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2901
3050
  className: "ya-crumb",
2902
3051
  children: selectedKey === "__ya_ungrouped__" ? t("ungrouped") : selectedWorkspace?.title
@@ -2908,7 +3057,7 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
2908
3057
  onClick: () => {
2909
3058
  startSession(selectedKey);
2910
3059
  },
2911
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, {})
3060
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, {})
2912
3061
  })
2913
3062
  ] })
2914
3063
  }),
@@ -2932,6 +3081,8 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
2932
3081
  create: () => {
2933
3082
  if (row.key !== "__ya_ungrouped__") startSession(row.key);
2934
3083
  },
3084
+ reveal,
3085
+ copy,
2935
3086
  rename: () => {
2936
3087
  beginWorkspaceRename(row);
2937
3088
  },
@@ -3102,6 +3253,10 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
3102
3253
  const createWorkspace = (input) => ctx.workspaces.create(input);
3103
3254
  const navigation = new YaWorkspaceNavigation(ctx, ctx.remote.directoryPicker, ctx.workspaces, ctx.sessions);
3104
3255
  ctx.slots.provideRoot({ hooks: { workspaces: ctx.workspaces.list } });
3256
+ const currentSession = {
3257
+ getSnapshot: () => navigation.currentSessionId,
3258
+ subscribe: (listener) => navigation.subscribeSelection(listener)
3259
+ };
3105
3260
  const searchSessions = async (query, signal) => {
3106
3261
  const result = await ctx.sessions.search(query, signal);
3107
3262
  if (!result.ok) throw new Error(result.error.message);
@@ -3168,7 +3323,10 @@ button.ya-crumb:hover { background:var(--dsw-alias-interactive-bg-hover); color:
3168
3323
  console.warn("reveal request rejected:", reason);
3169
3324
  }
3170
3325
  },
3171
- hooks: { directoryFlow: sidebarFlow }
3326
+ hooks: {
3327
+ directoryFlow: sidebarFlow,
3328
+ currentSession
3329
+ }
3172
3330
  });
3173
3331
  const pickerInjected = () => ({
3174
3332
  createWorkspace,