You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add workspace-row extension slots and a workspace-level folded state (workspace suspend)
#11996
Session rows expose two additive slots (sidebar.workspaces.session.menu.item and sidebar.workspaces.session.row.action), and a community plugin can already implement session-level suspend with them. Workspace group rows have no extension slot at all, and neither the Client workspace services nor the Host Workspace Controller expose a workspace-level archived / folded state, so a plugin cannot implement "workspace suspend": fold a whole workspace group into a restore group and bring it back later.
I built a working session-level prototype first (linked below) and hit this wall when extending it to workspaces.
What exists today (dsh 0.2.1-rc.5)
sidebar.workspaces is declared by ui-sidebar and registered by ui-workspace (single, replaceRisk: shadows-shipped-ui). Its declared children are all Session-row oriented: sidebar.workspaces.session.menu.item, sidebar.workspaces.session.row.action, sidebar.workspaces.directoryFlow, sidebar.session.row.leading, sidebar.session.row.hover.
The workspace group row draws its own "…" menu inline (rename, delete), a "new session" hover button and its collapse interaction inside ui-workspace. No slot is exposed.
Replacing sidebar.workspaces is not a workable option: it drops search, grouping modes, drag ordering, the waterline, the archive filter, the workspace tree and the directory flow.
/** Owner share of one Workspace row action occurrence. */exportinterfaceWorkspaceRowOwnerProps{workspaceId: WorkspaceId/** Row display title: stored title, falling back to the directory name. */displayTitle: string/** Workspace directory. */path: string/** Sessions in the workspace (archived included), for a plugin's own action copy. */sessionCount: number}
sidebar.workspaces.workspace.menu.item — rows of one workspace's "…" menu, with slotInject: { hooks: { menuOpenState: SlotHookFactory<…, UseMenuOpenState> } } so an entry can dismiss the menu.
sidebar.workspaces.workspace.row.action — the hover buttons at the end of one workspace row, after the "…" trigger.
Same rules as the session slots: an entry renders one role="menuitem" button (or one icon button), decides its own visibility, owns its overlays, and localizes through its own locale namespace. Keep built-in cells addressable by id so plugin rows can be placed by order — suggestion for the menu: new-session(100) / rename(200) / delete(300); the row today only has a "new session" button (100).
Optional: sidebar.workspace.row.leading — the 16px leading cell, so a suspended workspace can carry a marker, mirroring sidebar.session.row.leading.
2. Workspace-level folded state (required for a real fold; pick one)
2a. Host state (preferred).WorkspaceView gains a persisted suspendedWorkspaceIds: readonly WorkspaceId[], delivered through follow() like archived / pinned, plus Client suspendWorkspace(workspaceId, { stopActivity? }) / resumeWorkspace(workspaceId) and a standard hook. Under the default archive filter a suspended workspace renders only its group header (or is skipped entirely), not its member rows. An archive-style refusal while work is running (workspace/workspace-active, details naming turn / subagent / job / schedule) would match the existing session semantics.
2b. Client-only (minimal). Expose the list view operations (setWorkspaceExpanded, read expansion state) so a plugin can keep the suspended set locally and fold the group itself.
Without one of these, slot-only support enables just an approximation: archive every session of the workspace. That still renders the empty group header (the host keeps headers for empty groups) and leaks a UI fold into real archive state, which then shows up in the archive filter.
Compatibility
Both slots are additive with replaceRisk: none; with no registrant, rendering is unchanged.
suspendedWorkspaceIds defaults to [], so upgrades change no behavior.
Acceptance: the slots show up in the client slot catalog; a one-row registration appears in the workspace "…" menu and executes, dismisses, keyboard-walks and follows locale; behavior is identical in group-by-workspace, workspace-tree, flat-list and every archive-filter view; with 2a, suspend survives reload, the default filter hides member rows, and restore keeps the member order.
Prototype
dsh-session-suspend — a session-level suspend plugin implemented entirely through the existing session-row slots (suspend → bottom "Suspended N" restore group, toasts, host-style tooltips; theme tokens only, no shipped UI replaced):
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Summary
Session rows expose two additive slots (
sidebar.workspaces.session.menu.itemandsidebar.workspaces.session.row.action), and a community plugin can already implement session-level suspend with them. Workspace group rows have no extension slot at all, and neither the Client workspace services nor the Host Workspace Controller expose a workspace-level archived / folded state, so a plugin cannot implement "workspace suspend": fold a whole workspace group into a restore group and bring it back later.I built a working session-level prototype first (linked below) and hit this wall when extending it to workspaces.
What exists today (dsh 0.2.1-rc.5)
sidebar.workspacesis declared byui-sidebarand registered byui-workspace(single,replaceRisk: shadows-shipped-ui). Its declared children are all Session-row oriented:sidebar.workspaces.session.menu.item,sidebar.workspaces.session.row.action,sidebar.workspaces.directoryFlow,sidebar.session.row.leading,sidebar.session.row.hover.rename,delete), a "new session" hover button and its collapse interaction insideui-workspace. No slot is exposed.workspaces=create/rename/delete/archiveSession/unarchiveSession/insertSessionBefore;uiWorkspacelikewise goes only to session granularity.sidebar.workspacesis not a workable option: it drops search, grouping modes, drag ordering, the waterline, the archive filter, the workspace tree and the directory flow.Proposal
1. Two additive list slots (required)
Mirror the session-row pair —
kind: list,scope: root,replaceRisk: none:sidebar.workspaces.workspace.menu.item— rows of one workspace's "…" menu, withslotInject: { hooks: { menuOpenState: SlotHookFactory<…, UseMenuOpenState> } }so an entry can dismiss the menu.sidebar.workspaces.workspace.row.action— the hover buttons at the end of one workspace row, after the "…" trigger.Same rules as the session slots: an entry renders one
role="menuitem"button (or one icon button), decides its own visibility, owns its overlays, and localizes through its own locale namespace. Keep built-in cells addressable by id so plugin rows can be placed byorder— suggestion for the menu:new-session(100) /rename(200) /delete(300); the row today only has a "new session" button (100).Optional:
sidebar.workspace.row.leading— the 16px leading cell, so a suspended workspace can carry a marker, mirroringsidebar.session.row.leading.2. Workspace-level folded state (required for a real fold; pick one)
2a. Host state (preferred).
WorkspaceViewgains a persistedsuspendedWorkspaceIds: readonly WorkspaceId[], delivered throughfollow()likearchived/pinned, plus ClientsuspendWorkspace(workspaceId, { stopActivity? })/resumeWorkspace(workspaceId)and a standard hook. Under the default archive filter a suspended workspace renders only its group header (or is skipped entirely), not its member rows. An archive-style refusal while work is running (workspace/workspace-active, details namingturn/subagent/job/schedule) would match the existing session semantics.2b. Client-only (minimal). Expose the list view operations (
setWorkspaceExpanded, read expansion state) so a plugin can keep the suspended set locally and fold the group itself.Without one of these, slot-only support enables just an approximation: archive every session of the workspace. That still renders the empty group header (the host keeps headers for empty groups) and leaks a UI fold into real archive state, which then shows up in the archive filter.
Compatibility
replaceRisk: none; with no registrant, rendering is unchanged.suspendedWorkspaceIdsdefaults to[], so upgrades change no behavior.Prototype
dsh-session-suspend— a session-level suspend plugin implemented entirely through the existing session-row slots (suspend → bottom "Suspended N" restore group, toasts, host-style tooltips; theme tokens only, no shipped UI replaced):docs/workspace-row-slots-request.mdin that repo.The workspace entry needs roughly 60–80 lines on top of this once the slots exist.
中文摘要
Session 行有
sidebar.workspaces.session.menu.item/.row.action两个增量槽(社区插件已经用它实现了会话级挂起:https://github.com/Lqg97/dsh-session-suspend ),但工作区分组行没有任何插槽,Host 也没有工作区级归档 / 收起状态,所以「工作区挂起(整组收起、可整组恢复)」做不了。建议:① 新增sidebar.workspaces.workspace.menu.item/.row.action(ownerProps 给workspaceId/displayTitle/path/sessionCount,菜单槽带menuOpenState);②(推荐)Workspace Controller 增加suspendWorkspace/resumeWorkspace与持久化的suspendedWorkspaceIds(follow 增量帧同archived/pinned,默认筛选下只留组头),或退而求其次暴露setWorkspaceExpanded等视图操作。两类槽都是 additive,未注册时行为不变。Related
All reactions