Skip to content

feat(workspaces): groups#153

Merged
davideweaver merged 5 commits into
mainfrom
feat/workspace-sections
Jul 1, 2026
Merged

feat(workspaces): groups#153
davideweaver merged 5 commits into
mainfrom
feat/workspace-sections

Conversation

@davideweaver

Copy link
Copy Markdown
Contributor

Summary

  • Adds named, collapsible sections to the workspaces panel for organizing open workspaces into user-defined groups
  • Sections persist across sessions (stored in the workspace index alongside the existing panel state)
  • Workspaces can be moved into/out of sections via right-click context menu; sections and workspaces within sections are reorderable by drag-and-drop
  • Also fixes a path-flicker bug when switching to/from workspaces that have a panel docked in the left-bottom slot — the column layout change caused WorkspacesPanel to remount, resetting homeDir state and briefly showing the raw absolute path instead of the tilde-substituted version

What's new

State layer (packages/extension-host/src/state/)

  • New WorkspacePanelSection type and sections/sectionOrder/workspaceSections fields on AppState
  • 8 mutation functions: createSection, renameSection, deleteSection, reorderSections, moveWorkspaceToSection, removeWorkspaceFromSection, reorderWorkspaceInSection, toggleSectionCollapsed
  • Persistence: section data round-trips through PersistedIndex / buildIndex / hydrate / doPersist
  • deleteWorkspace now cleans up section membership

UI (packages/extensions-core/src/workspaces/)

  • "New Section…" at the bottom of the add-workspace menu (uses ctx.ui.prompt)
  • Section headers: collapse/expand on click, rename/delete via right-click, draggable to reorder
  • Workspace context menu: "Move to Section ▶" (submenu per section) / "Remove from Section"
  • FrontTruncatedPath: extracted compute logic to a pure truncatePath() helper; added useLayoutEffect for synchronous before-paint measurement (eliminates the flash of the full untruncated path that useEffect alone produced)
  • homeDir() resolved once at module level via useSyncExternalStore instead of per-component useState — survives remounts

Test plan

  • "New Section…" appears at the bottom of the + menu; entering a name creates the section
  • Clicking a section header collapses/expands it
  • Right-click section header → Rename / Delete
  • Right-click workspace → "Move to Section ▶" submenu; "Remove from Section"
  • Drag sections to reorder; drop indicator (2px accent bar) appears
  • Workspace membership and collapse state persist across app restarts
  • Switching to/from a workspace with a panel in the left-bottom dock no longer causes workspace paths to flicker
  • pnpm test passes (49 new section tests + 5 new persistence-model tests)
  • pnpm --filter silo exec tsc --noEmit clean

🤖 Generated with Claude Code

davideweaver and others added 5 commits June 30, 2026 17:51
Adds user-defined sections for organizing open workspaces. Sections are
collapsible, draggable, and persist across sessions. Workspaces can be
moved into/out of sections via context menu.

- New `WorkspacePanelSection` type and `sections`/`sectionOrder`/
  `workspaceSections` fields on `AppState` with full persistence
- 8 new mutation functions: `createSection`, `renameSection`,
  `deleteSection`, `reorderSections`, `moveWorkspaceToSection`,
  `removeWorkspaceFromSection`, `reorderWorkspaceInSection`,
  `toggleSectionCollapsed`
- "New Section…" entry in the workspace add menu (via `ctx.ui.prompt`)
- Section drag-and-drop reordering; workspace DnD within sections
- Workspace context menu: "Move to Section ▶" / "Remove from Section"
- 49 unit tests covering all new state operations and persistence
- Fix path-flicker on workspace switch: `homeDir()` resolved once at
  module level so `WorkspacesPanel` remounts (caused by column layout
  changes) don't restart the async load and briefly show the raw
  absolute path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sections can now be assigned an accent color via a new Properties modal
(replaces the old Rename prompt). A colored section renders as a unified
card — tinted header background, matching border, and colored title text —
with transparency levels that adapt to light and dark themes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ingle-source membership

Pre-work cleanup before adding group drag-and-drop, so the feature lands as
small additions rather than surgery on a tangled component.

- Rename the collapsible-grouping concept "section" -> "group" across state,
  host, panel, CSS, and user-visible strings (New/Move to/Remove from/Delete
  Group, Group Properties). Resolves the collision with the SDK's unrelated
  WorkspaceSectionProvider (extension-contributed row content), which is left
  untouched.
- Extract all panel drag-and-drop into a pure, unit-tested model
  (workspace-dnd.ts: typed drop intents + resolvers) plus a thin
  useWorkspaceDnd hook. WorkspacesPanel becomes presentational (571 -> 429
  lines); the DnD decision logic gains 21 tests where it had none.
- Collapse the three-source membership model: a group's workspaceOrder is now
  the single source of truth; the workspace->group reverse map is derived
  (groupIdForWorkspace / workspaceGroupMap), no longer stored or persisted.
- ADR 0023: record that panel groups are host-internal (core.* only), a
  deliberate, reversible exception to the "expose via ctx" default.

Typecheck, lint, and tests green (extension-host 454, extensions-core 109).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…panel

Make the Workspaces panel a single interleaved, fully drag-reorderable list:
groups and ungrouped workspaces share one top-level order, so a group can be
dragged anywhere (including above loose workspaces), and workspaces can be
dragged into a group, out of a group, and reordered — with clear drop lines.

- State: add `panelOrder`, the interleaved top-level order (ungrouped workspace
  ids + group ids); `workspaceOrder` stays as the public-service registry order.
  New mutators `reorderPanel` / `ungroupWorkspace`; `moveWorkspaceToGroup` gains
  a positional anchor. Membership stays single-sourced on each group's
  `workspaceOrder` (reverse map derived, never stored). `reconcilePanelOrder`
  self-heals the order on load so a partial/pre-panelOrder index can't hide an
  entry.
- DnD: pure decision model (`workspace-dnd.ts`) — one shared `panelDropForEntity`
  rule behind top-level rows, three-zone group cards (edge = adjacent, middle =
  into), and a position-aware background catch-all (drop at the beginning/end).
  Thin `useWorkspaceDnd` hook holds the React state and dispatches intents;
  precedence is by DOM specificity via stopPropagation.
- Fix the real drag blocker: `enterRegionOnPointer` (click-to-enter focus) was
  `preventDefault`-ing mousedown on draggable elements, which cancels the native
  drag. It now leaves draggable elements alone.
- CSS: the panel body fills the pane and carries the end-drop zone; top padding
  gives the "before first" line room; group header uses grid (WebKit won't start
  a drag from a draggable flex container when a child is grabbed).
- ADR 0023 updated for the interleaved model.

Unit-tested throughout (pure state mutators + pure DnD resolvers +
`reconcilePanelOrder`); typecheck, lint, and tests green (extension-host 461,
extensions-core 120).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nav, panel polish

Round of refinements on the workspaces panel groups:

- Share the group properties modal between creating and editing a group, so a
  color can be picked at creation time (openNewGroup + create/edit mode via the
  pure resolveGroupProps helper); drop the plain name prompt.
- Add a second, theme-relative greyscale swatch row: neutrals mix the theme's
  ink into its background so they invert automatically (lighter on dark themes
  via a per-theme --ws-group-neutral-ink), offset one column from the hues.
- Restore arrow-key navigation across the interleaved list: the roving focus
  group now spans group headers plus every rendered workspace (extracted to the
  pure, tested buildNavItems); Enter toggles a group or activates a workspace.
- Add top spacing above an ungrouped workspace that follows a group.
- Remove the workspace-row tooltips (name/folder/close) that broke during drag.
- Drop the separator between "New workspace…" and "New Group…".
- Fix the double focus outline on the group name input (rely on the shared
  focus-visible ring) and document the rule in CLAUDE.md.

Also runs prettier over pre-existing unformatted files on the branch to fix the
failing format:check CI step.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@davideweaver davideweaver changed the title feat(workspaces): named collapsible sections feat(workspaces): groups Jul 1, 2026
@davideweaver davideweaver merged commit ec05f89 into main Jul 1, 2026
3 checks passed
@davideweaver davideweaver deleted the feat/workspace-sections branch July 1, 2026 02:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant