@deepseek-ai/dsh-client-ui-workspace 0.1.2-rc.1 → 0.1.5-alpha.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/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/client/ui-workspace/README.md
5
- README.md: 174d8368683f49d91dda1e35c413605b364a44e5
6
- README.zh.md: 1f589be35548374a562a56832672bc9add85b850
5
+ README.md: a819b07fecb74faeb3bebb6665aaabfdc8ecfba6
6
+ README.zh.md: f9565b12bccd596fa61d1f1436f82b090139f506
package/README.md CHANGED
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
9
9
 
10
10
  ## Summary
11
11
 
12
- `dsh-client-ui-workspace` is the shared Workspace browser and picker of the dsh web client: users browse grouped or flat Session rows in the sidebar, pick a Workspace for a new session from the Session Intent hero, and manage Workspaces and Sessions with add, rename, reorder, search, fork, and archive actions; the same Workspace menu and add flow serve both surfaces. Pending user interactions surface as amber warning dots, active Schedule projections surface as non-interactive alarm markers in ordinary and search rows, and the shared sidebar projection hides subagent-origin sessions. Distinct canonical paths remain separate id-keyed Workspaces, and adding a folder goes through a directory-flow child hole that a composed picker package's client half fills.
12
+ This package lets users browse grouped or flat Session lists, choose a Workspace for a new Session, and manage Workspaces and Sessions through add, rename, reorder, search, fork, archive, and Workspace deletion. Pending interactions appear as warning dots, active scheduled tasks as alarm markers, and subagent-origin Sessions remain hidden. Canonically distinct folder paths remain separate Workspaces. Adding a Workspace requires a composed directory picker; without one, the add action is unavailable.
13
13
 
14
14
  ## Table of Contents
15
15
 
@@ -33,7 +33,7 @@ View options combine grouping with one browser-persisted Session order per accou
33
33
 
34
34
  ### Search
35
35
 
36
- Collapsed search is one header action beside the view and add actions: activating it expands the field across the header. A non-blank query replaces either browsing mode with one flat result list — case-insensitive title and Workspace substring matches appear immediately, while a 250 ms debounced Host request adds ranked current-conversation content matches and snippets. Each new query aborts the preceding request; a failed content search leaves metadata matches visible with a warning. The list is capped at 20 and opens the selected Session without clearing the query.
36
+ Collapsed search is one header action beside the view and add actions: activating it expands the field across the header. A non-blank query replaces either browsing mode with one flat result list — case-insensitive title and Workspace substring matches appear immediately, while a 250 ms debounced Host request adds ranked current-conversation content matches and snippets. Each new query aborts the preceding request; a failed content search leaves metadata matches visible with a warning. The list is capped at 20. Choosing a result clears and collapses search, opens the Session, and scrolls its row into view in the configured browsing mode; grouped browsing also expands its Workspace and the full Session list when required.
37
37
 
38
38
  ### Managing sessions
39
39
 
package/README.zh.md CHANGED
@@ -9,7 +9,7 @@ kind: "package-reference"
9
9
 
10
10
  ## 概述
11
11
 
12
- `dsh-client-ui-workspace` 是 dsh Web 客户端的共享 Workspace 浏览器与选择器:用户在侧边栏浏览分组或扁平的 Session 行,在 Session Intent 主视觉区为新会话选择 Workspace,并可用添加、重命名、重排序、搜索、fork 与归档操作管理 Workspace 与 Session;两个界面共用同一套 Workspace 菜单与添加流程。待处理的用户交互以琥珀色警告点呈现,活动 Schedule projection 会在普通行与搜索结果中显示不可交互的闹钟,共享侧边栏投影还会隐藏 subagent 来源的会话。不同的规范化路径仍作为由 id 区分的独立 Workspace;添加文件夹走目录流子 slot,由组合的选择器包 client half 填充。
12
+ 本包让用户浏览分组或扁平的 Session 列表、为新 Session 选择 Workspace,并通过添加、重命名、重排序、搜索、fork、归档和删除 Workspace 来管理 Workspace 与 Session。待处理交互显示为警告点,活动定时任务显示为闹钟标识,subagent 来源的 Session 则保持隐藏。规范化后仍有差异的文件夹路径会保留为独立 Workspace。添加 Workspace 需要组合目录选择器;没有目录选择器时,添加操作不可用。
13
13
 
14
14
  ## 目录
15
15
 
@@ -33,7 +33,7 @@ kind: "package-reference"
33
33
 
34
34
  ### 搜索
35
35
 
36
- 折叠搜索是视图和添加操作旁的一枚区头按钮:激活后输入框会扩展并占据区头。非空白查询会以单一扁平结果列表替代任一浏览模式——不区分大小写的标题和 Workspace 子串匹配项会立即显示,经 250 ms 防抖的 Host 请求则会加入经过排序的当前对话内容匹配项及其摘要片段。每次新查询都会中止前一个请求;内容搜索失败时,元数据匹配项仍会显示,同时给出警告。列表最多显示 20 条结果,打开所选 Session 时不会清除查询。
36
+ 折叠搜索是视图和添加操作旁的一枚区头按钮:激活后输入框会扩展并占据区头。非空白查询会以单一扁平结果列表替代任一浏览模式——不区分大小写的标题和 Workspace 子串匹配项会立即显示,经 250 ms 防抖的 Host 请求则会加入经过排序的当前对话内容匹配项及其摘要片段。每次新查询都会中止前一个请求;内容搜索失败时,元数据匹配项仍会显示,同时给出警告。列表最多显示 20 条结果。选择结果会清空并收起搜索、打开 Session,并在当前浏览模式中将其行滚动到可见区域;分组浏览还会按需展开所属 Workspace 和完整 Session 列表。
37
37
 
38
38
  ### 管理会话
39
39
 
package/lib/client.js CHANGED
@@ -225,10 +225,6 @@ window.__ModuleLoader__.load({
225
225
  }
226
226
  //#endregion
227
227
  //#region ../../util/workspace-path/src/index.ts
228
- /**
229
- * Browser-safe Workspace path and display helpers.
230
- * @module @deepseek-ai/dsh-util-workspace-path
231
- */
232
228
  /** Whether a path uses a Windows drive or UNC prefix. */
233
229
  function isWindowsStylePath(value) {
234
230
  return /^[A-Za-z]:[/\\]/.test(value) || value.startsWith("\\\\");
@@ -290,6 +286,15 @@ window.__ModuleLoader__.load({
290
286
  return indexed;
291
287
  }
292
288
  /**
289
+ * Resolve the Workspace browser group that owns one Session.
290
+ * @param workspaces - authoritative Workspace membership.
291
+ * @param sessionId - Session whose browser group is required.
292
+ * @returns owning Workspace id, or {@link UNGROUPED_KEY} when no Workspace accounts for it.
293
+ */
294
+ function owningGroupKey(workspaces, sessionId) {
295
+ return workspaces.find((workspace) => workspace.sessionIds.includes(sessionId))?.workspaceId ?? "";
296
+ }
297
+ /**
293
298
  * Directory display label: basename of the path (both separators accepted).
294
299
  * Ungrouped-bucket fallback for surfaces without a workspace title.
295
300
  * @param cwd - directory path, or undefined for the ungrouped bucket.
@@ -422,7 +427,7 @@ window.__ModuleLoader__.load({
422
427
  const archived = new Set(archivedSessionIds);
423
428
  const expandedGroups = new Set(view.expandedGroups);
424
429
  const descendants = indexSubagentDescendants(list.byId);
425
- const currentGroup = list.current === void 0 ? void 0 : workspaces.find((w) => w.sessionIds.includes(list.current))?.workspaceId ?? "";
430
+ const currentGroup = list.current === void 0 ? void 0 : owningGroupKey(workspaces, list.current);
426
431
  const groups = [];
427
432
  for (const g of groupByWorkspace(list, workspaces, archived, view.ungroupedOrder)) {
428
433
  const expanded = expandedGroups.has(g.key);
@@ -902,18 +907,25 @@ window.__ModuleLoader__.load({
902
907
  * @param props.onRename - open the session rename dialog (id + current title).
903
908
  * @param props.onFork - fork a session at its last completed turn.
904
909
  * @param props.onArchive - archive a session by id.
910
+ * @param props.onReveal - scroll this row into view after search navigation, then acknowledge it.
905
911
  * @param props.drag - optional draggable-row wiring.
906
912
  * @param props.flat - omit the empty status slot in the hierarchy-free flat list.
907
913
  * @param props.t - the browser root's locale seat.
908
914
  * @returns the session row.
909
915
  */
910
- function SessionNodeItem({ node, currentId, now, onOpen, onRename, onFork, onArchive, drag, flat = false, t }) {
916
+ function SessionNodeItem({ node, currentId, now, onOpen, onRename, onFork, onArchive, onReveal, drag, flat = false, t }) {
911
917
  const row = node;
912
918
  const title = displayTitle(node, t);
913
919
  const selected = node.id === currentId;
914
920
  const statuses = sessionStatuses(node, t);
915
921
  const showStatus = statuses[0].state !== "done" || row.completed;
916
922
  const [menuOpen, setMenuOpen] = (0, react.useState)(false);
923
+ const rowRef = (0, react.useRef)(null);
924
+ (0, react.useEffect)(() => {
925
+ if (onReveal === void 0) return;
926
+ rowRef.current?.scrollIntoView({ block: "nearest" });
927
+ onReveal();
928
+ }, [onReveal]);
917
929
  const sessionMenuItems = [
918
930
  {
919
931
  id: "rename",
@@ -933,6 +945,7 @@ window.__ModuleLoader__.load({
933
945
  ];
934
946
  return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.HoverCard, {
935
947
  anchor: (0, react_jsx_runtime.jsxs)("div", {
948
+ ref: rowRef,
936
949
  className: clsx(Rows_module_css_default.sessionRow, selected && Rows_module_css_default.selected, menuOpen && Rows_module_css_default.menuOpen, flat && !showStatus && Rows_module_css_default.flatSessionRowWithoutStatus, drag?.marker === "before" && Rows_module_css_default.dropBefore, drag?.marker === "after" && Rows_module_css_default.dropAfter),
937
950
  role: "treeitem",
938
951
  "aria-selected": selected,
@@ -1429,10 +1442,11 @@ window.__ModuleLoader__.load({
1429
1442
  return e.clientY < rect.top + rect.height / 2 ? "before" : "after";
1430
1443
  }
1431
1444
  /** The scrolling session tree; unmounting drops the sessions subscription and expand-all state. */
1432
- function SessionTree({ useSessions, useSessionPendingInteraction, startSession, open, forkSession, workspaces, archivedSessionIds, onRenameRequest, onDeleteRequest, onSessionRename, onSessionArchive, insertWorkspaceBefore, insertSessionBefore, orderBy, groupExpansion, setGroupExpanded, sessionOrderByAccount, sessionUpdatedAtByAccount, syncSessionOrderAccount, setSessionOrder, home, t }) {
1445
+ function SessionTree({ useSessions, useSessionPendingInteraction, startSession, open, forkSession, workspaces, archivedSessionIds, workspaceReady, onRenameRequest, onDeleteRequest, onSessionRename, onSessionArchive, insertWorkspaceBefore, insertSessionBefore, orderBy, groupExpansion, setGroupExpanded, sessionOrderByAccount, sessionUpdatedAtByAccount, syncSessionOrderAccount, setSessionOrder, home, t, revealSessionId, onSessionRevealed }) {
1433
1446
  const list = useSessions((s) => s);
1434
1447
  const pendingInteractions = useSessionPendingInteraction((s) => s);
1435
1448
  const current = list.current;
1449
+ const revealGroup = revealSessionId === void 0 || !workspaceReady ? void 0 : owningGroupKey(workspaces, revealSessionId);
1436
1450
  const [expandedSessionGroups, setExpandedSessionGroups] = (0, react.useState)([]);
1437
1451
  const [drag, setDrag] = (0, react.useState)(null);
1438
1452
  const sessionDropCommitted = (0, react.useRef)(false);
@@ -1440,7 +1454,7 @@ window.__ModuleLoader__.load({
1440
1454
  const workspaceDropCommitted = (0, react.useRef)(false);
1441
1455
  const previousOrderBy = (0, react.useRef)(orderBy);
1442
1456
  useNativeDragAcceptance(drag !== null || workspaceDrag !== null);
1443
- const currentGroup = current === void 0 ? void 0 : workspaces.find((w) => w.sessionIds.includes(current))?.workspaceId ?? "";
1457
+ const currentGroup = current === void 0 || !workspaceReady ? void 0 : owningGroupKey(workspaces, current);
1444
1458
  (0, react.useEffect)(() => {
1445
1459
  if (current === void 0 || currentGroup === void 0 || Object.hasOwn(groupExpansion, currentGroup)) return;
1446
1460
  setGroupExpanded(currentGroup, true);
@@ -1509,6 +1523,25 @@ window.__ModuleLoader__.load({
1509
1523
  expandedGroups,
1510
1524
  sessionOrderByAccount
1511
1525
  ]);
1526
+ (0, react.useEffect)(() => {
1527
+ if (revealGroup === void 0 || groupExpansion[revealGroup] === true) return;
1528
+ setGroupExpanded(revealGroup, true);
1529
+ }, [
1530
+ groupExpansion,
1531
+ revealGroup,
1532
+ setGroupExpanded
1533
+ ]);
1534
+ (0, react.useEffect)(() => {
1535
+ if (revealSessionId === void 0 || revealGroup === void 0) return;
1536
+ const group = groups.find((candidate) => candidate.key === revealGroup);
1537
+ if (group === void 0 || !group.expanded || !group.sessions.some((row) => row.id === revealSessionId)) return;
1538
+ if (collapsedSessionRows(group.sessions).rows.some((row) => row.id === revealSessionId)) return;
1539
+ setExpandedSessionGroups((keys) => keys.includes(revealGroup) ? keys : [...keys, revealGroup]);
1540
+ }, [
1541
+ groups,
1542
+ revealGroup,
1543
+ revealSessionId
1544
+ ]);
1512
1545
  const now = Date.now();
1513
1546
  const commitSessionDrag = (activeDrag, over) => {
1514
1547
  if (sessionDropCommitted.current) return;
@@ -1661,6 +1694,41 @@ window.__ModuleLoader__.load({
1661
1694
  }),
1662
1695
  (sessionsExpanded ? group.sessions : collapsed.rows).map((node) => {
1663
1696
  const sameGroupDrag = drag !== null && drag.accountKey === group.key;
1697
+ const dragProps = {
1698
+ start: () => {
1699
+ sessionDropCommitted.current = false;
1700
+ setDrag({
1701
+ accountKey: group.key,
1702
+ sessionId: node.id,
1703
+ over: null
1704
+ });
1705
+ },
1706
+ active: sameGroupDrag,
1707
+ marker: sameGroupDrag && drag.over?.id === node.id ? drag.over.half : null,
1708
+ hover: (half) => {
1709
+ /* v8 ignore next -- narrowing guard: Rows gates hover on `active`, which is false while the drag state is null. */
1710
+ setDrag((d) => d === null ? d : {
1711
+ ...d,
1712
+ over: {
1713
+ id: node.id,
1714
+ half
1715
+ }
1716
+ });
1717
+ },
1718
+ drop: (half) => {
1719
+ /* v8 ignore next -- narrowing guard: Rows gates drop on `active`, which is false while the drag state is null. */
1720
+ if (drag === null) return;
1721
+ commitSessionDrag(drag, {
1722
+ id: node.id,
1723
+ half
1724
+ });
1725
+ },
1726
+ end: () => {
1727
+ if (drag?.over !== null && drag?.over !== void 0) commitSessionDrag(drag, drag.over);
1728
+ else setDrag(null);
1729
+ sessionDropCommitted.current = false;
1730
+ }
1731
+ };
1664
1732
  return (0, react_jsx_runtime.jsx)(SessionNodeItem, {
1665
1733
  node,
1666
1734
  currentId: current,
@@ -1669,41 +1737,10 @@ window.__ModuleLoader__.load({
1669
1737
  onRename: onSessionRename,
1670
1738
  onFork: forkSession,
1671
1739
  onArchive: onSessionArchive,
1672
- drag: {
1673
- start: () => {
1674
- sessionDropCommitted.current = false;
1675
- setDrag({
1676
- accountKey: group.key,
1677
- sessionId: node.id,
1678
- over: null
1679
- });
1680
- },
1681
- active: sameGroupDrag,
1682
- marker: sameGroupDrag && drag.over?.id === node.id ? drag.over.half : null,
1683
- hover: (half) => {
1684
- /* v8 ignore next -- narrowing guard: Rows gates hover on `active`, which is false while the drag state is null. */
1685
- setDrag((d) => d === null ? d : {
1686
- ...d,
1687
- over: {
1688
- id: node.id,
1689
- half
1690
- }
1691
- });
1692
- },
1693
- drop: (half) => {
1694
- /* v8 ignore next -- narrowing guard: Rows gates drop on `active`, which is false while the drag state is null. */
1695
- if (drag === null) return;
1696
- commitSessionDrag(drag, {
1697
- id: node.id,
1698
- half
1699
- });
1700
- },
1701
- end: () => {
1702
- if (drag?.over !== null && drag?.over !== void 0) commitSessionDrag(drag, drag.over);
1703
- else setDrag(null);
1704
- sessionDropCommitted.current = false;
1705
- }
1706
- },
1740
+ onReveal: node.id === revealSessionId && group.key === revealGroup ? () => {
1741
+ onSessionRevealed(node.id);
1742
+ } : void 0,
1743
+ drag: dragProps,
1707
1744
  t
1708
1745
  }, node.id);
1709
1746
  }),
@@ -1725,7 +1762,7 @@ window.__ModuleLoader__.load({
1725
1762
  });
1726
1763
  }
1727
1764
  /** The flat "In one list" body: every session is one draggable top-level row. */
1728
- function FlatList({ useSessions, useSessionPendingInteraction, open, forkSession, onSessionRename, onSessionArchive, archivedSessionIds, orderBy, sessionOrderByAccount, sessionUpdatedAtByAccount, syncSessionOrderAccount, setSessionOrder, t }) {
1765
+ function FlatList({ useSessions, useSessionPendingInteraction, open, forkSession, onSessionRename, onSessionArchive, archivedSessionIds, orderBy, sessionOrderByAccount, sessionUpdatedAtByAccount, syncSessionOrderAccount, setSessionOrder, revealSessionId, onSessionRevealed, t }) {
1729
1766
  const list = useSessions((s) => s);
1730
1767
  const pendingInteractions = useSessionPendingInteraction((s) => s);
1731
1768
  const baseRows = (0, react.useMemo)(() => deriveFlat(list, archivedSessionIds, pendingInteractions), [
@@ -1808,6 +1845,9 @@ window.__ModuleLoader__.load({
1808
1845
  onRename: onSessionRename,
1809
1846
  onFork: forkSession,
1810
1847
  onArchive: onSessionArchive,
1848
+ onReveal: node.id === revealSessionId ? () => {
1849
+ onSessionRevealed(node.id);
1850
+ } : void 0,
1811
1851
  flat: true,
1812
1852
  drag: {
1813
1853
  start: () => {
@@ -1915,6 +1955,7 @@ window.__ModuleLoader__.load({
1915
1955
  const home = useHostInfo((info) => info.home);
1916
1956
  const workspaces = useWorkspaces((state) => state.items);
1917
1957
  const workspacePhase = useWorkspaces((state) => state.phase);
1958
+ const workspaceStreamState = useWorkspaces((state) => state.state);
1918
1959
  const archivedSessionIds = useWorkspaces((state) => state.archivedSessionIds);
1919
1960
  const directoryFlowAvailable = useDirectoryFlow((occupied) => occupied);
1920
1961
  const groupBy = useStore((s) => s.groupBy);
@@ -1926,7 +1967,7 @@ window.__ModuleLoader__.load({
1926
1967
  const current = state.current;
1927
1968
  return current !== void 0 && state.byId[current]?.blank === true ? current : void 0;
1928
1969
  });
1929
- const currentBlankAccount = currentBlankSessionId === void 0 ? void 0 : workspaces.find((workspace) => workspace.sessionIds.includes(currentBlankSessionId))?.workspaceId ?? "";
1970
+ const currentBlankAccount = currentBlankSessionId === void 0 || workspacePhase !== "ready" ? void 0 : owningGroupKey(workspaces, currentBlankSessionId);
1930
1971
  const promotedBlank = (0, react.useRef)(void 0);
1931
1972
  (0, react.useEffect)(() => {
1932
1973
  if (currentBlankSessionId === void 0 || currentBlankAccount === void 0) {
@@ -1963,6 +2004,7 @@ window.__ModuleLoader__.load({
1963
2004
  ]);
1964
2005
  const [query, setQuery] = (0, react.useState)("");
1965
2006
  const [searchExpanded, setSearchExpanded] = (0, react.useState)(false);
2007
+ const [revealSessionId, setRevealSessionId] = (0, react.useState)(void 0);
1966
2008
  const normalizedQuery = sanitizeSearchQuery(query).trim();
1967
2009
  const [remoteSearch, setRemoteSearch] = (0, react.useState)({
1968
2010
  query: "",
@@ -1975,6 +2017,18 @@ window.__ModuleLoader__.load({
1975
2017
  const [wsPickerOpen, setWsPickerOpen] = (0, react.useState)(false);
1976
2018
  const wsPlusRef = (0, react.useRef)(null);
1977
2019
  const composingRef = (0, react.useRef)(false);
2020
+ const openSearchResult = (sessionId) => {
2021
+ setRevealSessionId(sessionId);
2022
+ setQuery("");
2023
+ setSearchExpanded(false);
2024
+ open(sessionId);
2025
+ };
2026
+ const acknowledgeSessionReveal = (sessionId) => {
2027
+ setRevealSessionId((current) => current === sessionId ? void 0 : current);
2028
+ };
2029
+ (0, react.useEffect)(() => {
2030
+ if (normalizedQuery !== "") setRevealSessionId(void 0);
2031
+ }, [normalizedQuery]);
1978
2032
  const [searchOnExpand, setSearchOnExpand] = (0, react.useState)(false);
1979
2033
  (0, react.useEffect)(() => {
1980
2034
  if (wide && searchOnExpand) {
@@ -2281,7 +2335,7 @@ window.__ModuleLoader__.load({
2281
2335
  children: wide && (normalizedQuery !== "" ? (0, react_jsx_runtime.jsx)(SearchResults, {
2282
2336
  useSessions,
2283
2337
  useSessionPendingInteraction,
2284
- open,
2338
+ open: openSearchResult,
2285
2339
  workspaces,
2286
2340
  archivedSessionIds,
2287
2341
  query: normalizedQuery,
@@ -2301,6 +2355,8 @@ window.__ModuleLoader__.load({
2301
2355
  sessionUpdatedAtByAccount,
2302
2356
  syncSessionOrderAccount: actions.syncSessionOrderAccount,
2303
2357
  setSessionOrder: actions.setSessionOrder,
2358
+ revealSessionId,
2359
+ onSessionRevealed: acknowledgeSessionReveal,
2304
2360
  t
2305
2361
  }) : (0, react_jsx_runtime.jsx)(SessionTree, {
2306
2362
  useSessions,
@@ -2309,6 +2365,7 @@ window.__ModuleLoader__.load({
2309
2365
  onSessionArchive,
2310
2366
  forkSession,
2311
2367
  workspaces,
2368
+ workspaceReady: workspacePhase === "ready" && workspaceStreamState !== "loading",
2312
2369
  groupExpansion,
2313
2370
  setGroupExpanded: actions.setGroupExpanded,
2314
2371
  sessionOrderByAccount,
@@ -2321,6 +2378,8 @@ window.__ModuleLoader__.load({
2321
2378
  insertWorkspaceBefore,
2322
2379
  insertSessionBefore,
2323
2380
  orderBy,
2381
+ revealSessionId,
2382
+ onSessionRevealed: acknowledgeSessionReveal,
2324
2383
  home,
2325
2384
  t,
2326
2385
  onRenameRequest: (workspaceId, currentTitle) => {
@@ -77,12 +77,13 @@ export declare function SearchResultItem({ result, currentId, onOpen, t }: {
77
77
  * @param props.onRename - open the session rename dialog (id + current title).
78
78
  * @param props.onFork - fork a session at its last completed turn.
79
79
  * @param props.onArchive - archive a session by id.
80
+ * @param props.onReveal - scroll this row into view after search navigation, then acknowledge it.
80
81
  * @param props.drag - optional draggable-row wiring.
81
82
  * @param props.flat - omit the empty status slot in the hierarchy-free flat list.
82
83
  * @param props.t - the browser root's locale seat.
83
84
  * @returns the session row.
84
85
  */
85
- export declare function SessionNodeItem({ node, currentId, now, onOpen, onRename, onFork, onArchive, drag, flat, t }: {
86
+ export declare function SessionNodeItem({ node, currentId, now, onOpen, onRename, onFork, onArchive, onReveal, drag, flat, t, }: {
86
87
  node: SessionNode;
87
88
  currentId: string | undefined;
88
89
  now: number;
@@ -93,6 +94,8 @@ export declare function SessionNodeItem({ node, currentId, now, onOpen, onRename
93
94
  onFork: (id: SessionNode['id']) => void;
94
95
  /** Archive this session (row menu action; commits without a dialog). */
95
96
  onArchive: (id: SessionNode['id']) => void;
97
+ /** Scroll this row into view after search navigation, then acknowledge it. */
98
+ onReveal?: (() => void) | undefined;
96
99
  /** Present only on draggable rows (workspace-group sessions outside search). */
97
100
  drag?: RowDragProps | undefined;
98
101
  /** The row is rendered without a parent Workspace header. */
@@ -9,6 +9,13 @@ import type { SessionPendingInteractionBase } from '@deepseek-ai/dsh-client-ui-s
9
9
  import type { SessionId } from '@deepseek-ai/dsh-session/types';
10
10
  /** Group key for Sessions outside every Workspace. */
11
11
  export declare const UNGROUPED_KEY = "";
12
+ /**
13
+ * Resolve the Workspace browser group that owns one Session.
14
+ * @param workspaces - authoritative Workspace membership.
15
+ * @param sessionId - Session whose browser group is required.
16
+ * @returns owning Workspace id, or {@link UNGROUPED_KEY} when no Workspace accounts for it.
17
+ */
18
+ export declare function owningGroupKey(workspaces: readonly WorkspaceView[], sessionId: SessionId): string;
12
19
  /** Pending interaction kinds with dedicated Workspace-row presentation. */
13
20
  export type SessionPendingInteractionStatus = 'approval' | 'plan-review' | 'question';
14
21
  type SessionPendingInteractions = ReadonlyMap<SessionId, SessionPendingInteractionBase>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-ui-workspace",
3
3
  "description": "Workspace picker plugin: one WorkspacePicker registered into the sidebar and empty-state workspace slots",
4
- "version": "0.1.2-rc.1",
4
+ "version": "0.1.5-alpha.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -51,24 +51,24 @@
51
51
  "devDependencies": {
52
52
  "@types/react": "~18.3.1",
53
53
  "react": "^18.2.0",
54
- "@deepseek-ai/dsh-api-session-controller": "^0.1.2-rc.1",
55
- "@deepseek-ai/dsh-api-remotes": "^0.1.2-rc.1",
56
- "@deepseek-ai/dsh-api-workspace-controller": "^0.1.2-rc.1",
57
- "@deepseek-ai/dsh-client-connection": "^0.1.2-rc.1",
58
- "@deepseek-ai/dsh-client-locale": "^0.1.2-rc.1",
59
- "@deepseek-ai/dsh-client-store": "^0.1.2-rc.1",
60
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-rc.1",
61
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.2-rc.1",
62
- "@deepseek-ai/dsh-client-test-runtime": "^0.1.2-rc.1",
63
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-rc.1",
64
- "@deepseek-ai/dsh-client-ui-session": "^0.1.2-rc.1",
65
- "@deepseek-ai/dsh-client-ui-sidebar": "^0.1.2-rc.1",
66
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.2-rc.1",
67
- "@deepseek-ai/dsh-schedule": "^0.1.2-rc.1",
68
- "@deepseek-ai/dsh-typert-protocol": "^0.1.2-rc.1",
69
- "@deepseek-ai/dsh-session": "^0.1.2-rc.1",
54
+ "@deepseek-ai/dsh-api-session-controller": "^0.1.5-alpha.1",
55
+ "@deepseek-ai/dsh-api-workspace-controller": "^0.1.5-alpha.1",
56
+ "@deepseek-ai/dsh-api-remotes": "^0.1.5-alpha.1",
57
+ "@deepseek-ai/dsh-client-connection": "^0.1.5-alpha.1",
58
+ "@deepseek-ai/dsh-client-locale": "^0.1.5-alpha.1",
59
+ "@deepseek-ai/dsh-client-store": "^0.1.5-alpha.1",
60
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-alpha.1",
61
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-alpha.1",
62
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-alpha.1",
63
+ "@deepseek-ai/dsh-client-test-runtime": "^0.1.5-alpha.1",
64
+ "@deepseek-ai/dsh-client-ui-session": "^0.1.5-alpha.1",
65
+ "@deepseek-ai/dsh-client-ui-sidebar": "^0.1.5-alpha.1",
66
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-alpha.1",
67
+ "@deepseek-ai/dsh-schedule": "^0.1.5-alpha.1",
68
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.5-alpha.1",
69
+ "@deepseek-ai/dsh-session": "^0.1.5-alpha.1",
70
70
  "@deepseek-ai/cordis": "^4.0.2",
71
- "@deepseek-ai/dsh-util-workspace-path": "^0.1.2-rc.1"
71
+ "@deepseek-ai/dsh-util-workspace-path": "^0.1.5-alpha.1"
72
72
  },
73
73
  "files": [
74
74
  "lib/index.js",