Skip to content

refactor: rename folder/project terminology to workspace and group#91

Merged
gi11es merged 5 commits into
masterfrom
refactor/folder-to-workspace
May 4, 2026
Merged

refactor: rename folder/project terminology to workspace and group#91
gi11es merged 5 commits into
masterfrom
refactor/folder-to-workspace

Conversation

@gi11es

@gi11es gi11es commented May 2, 2026

Copy link
Copy Markdown
Owner

Summary

The word "Folder" was overloaded across the Deckard UI for two unrelated concepts: a filesystem folder you open as a project, and a sidebar grouping that contains projects. Both meanings appeared in the same right-click menu, where four "Folder" labels referred to two different things.

This PR adopts two distinct nouns:

  • Workspace = the filesystem folder + its tabs + its sessions (was ProjectItem)
  • Group = the sidebar bucket that organises workspaces (was SidebarFolder)

The work is split into three reviewable commits.

Commits

  1. refactor: rename folder UX to workspace and group — User-facing strings only: File menu, sidebar context menus, tooltips, accessibility descriptions, picker placeholder, shortcut display labels.
  2. refactor: rename SidebarFolder internals to SidebarGroup — Types/vars/methods rename + persistence migration. Backward-compat decoders for state.json (sidebarFolderssidebarGroups, "folder""group" discriminator). One-shot UserDefaults migration for shortcut identifiers newSidebarFolder / moveOutOfFoldernewGroup / moveOutOfGroup. Bumps state.json version 2 → 3.
  3. refactor: rename ProjectItem internals to WorkspaceItem — Same pattern, broader scope. ProjectItem/ProjectState/ProjectTabState/ProjectPickerWorkspaceItem/WorkspaceState/WorkspaceTabState/WorkspacePicker. Backward-compat decoders for projectsworkspaces and projectIdsworkspaceIds. Migrates the four remaining shortcut identifiers (openFolder/closeFolder/nextProject/previousProject).

Persistence safety

  • All decoders accept BOTH old and new keys; encoders only write new keys. Existing state.json files load cleanly with no data loss.
  • Shortcut migration is guarded by a UserDefaults flag and is idempotent (won't re-run, won't clobber a value the user has already set on the new key).
  • Pasteboard type strings, kMDItemContentType == public.folder Spotlight queries, and ~/.claude/projects/ (Claude Code's storage layout) are intentionally untouched.

Test plan

  • Existing tests pass: 281 → 291 tests passing, 3 skipped, 0 failures (10 new tests added — see below)
  • Build succeeds in Debug
  • New tests added covering:
    • Tests/SidebarGroupTests.swift: legacy sidebarFolders key decode, legacy "folder" discriminator decode, legacy projects key decode, full v2→v3 round-trip with all legacy keys at once, encoder writes only new keys
    • Tests/ShortcutMigrationTests.swift (new file): single-identifier migration, all-identifier migration, idempotence (runs only once), no-legacy-keys is a no-op, doesn't overwrite an existing user override on the new key
  • Smoke-test dev build manually: load existing state.json (with sidebarFolders / projectIds / projects keys), confirm sidebar shows the same groups and workspaces

🤖 Generated with Claude Code

gi11es and others added 5 commits May 2, 2026 22:13
The word "Folder" was overloaded across the UI for two unrelated
concepts: a filesystem folder you open as a project, and a sidebar
grouping that contains projects. Both meanings appeared in the same
right-click menu, where four "Folder" labels referred to two different
things.

Adopt two distinct nouns: "Workspace" for the filesystem folder + its
tabs + its sessions, and "Group" for the sidebar bucket. This commit
covers user-facing strings only — File menu items, sidebar context
menus, tooltips, accessibility descriptions, picker placeholder, and
shortcut display labels in the Settings pane. Internal type names and
shortcut identifiers are unchanged in this commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Internal counterpart to the prior commit. SidebarFolder/SidebarFolderView/
SidebarFolderState become SidebarGroup/SidebarGroupView/SidebarGroupState;
the SidebarItem and SidebarOrderItem .folder cases become .group; every
folder-prefixed method/var/constant referring to the sidebar bucket gets
renamed.

Persistence migration is included so existing users don't lose state:
- DeckardState.CodingKeys reads the legacy "sidebarFolders" key as well as
  the new "sidebarGroups", and only ever writes "sidebarGroups".
- SidebarOrderItem decodes both "folder" and "group" discriminator
  strings, and only ever encodes "group". Bumps state.json version 2→3.
- One-shot DeckardShortcutMigration copies user shortcut overrides from
  the old KeyboardShortcuts identifiers (newSidebarFolder, moveOutOfFolder)
  to the new ones (newGroup, moveOutOfGroup) on first launch, then
  removes the old keys. Guarded by a UserDefaults flag.

Tests cover both decoder paths plus all migration edge cases (no-op when
new key already set, no-op on subsequent runs, multi-identifier migration).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Internal counterpart to the prior commits. ProjectItem/ProjectState/
ProjectTabState/ProjectPicker become WorkspaceItem/WorkspaceState/
WorkspaceTabState/WorkspacePicker. The matching properties and methods
across DeckardWindowController, SidebarController, TabBarController,
HookHandler, and SidebarViews follow suit (currentProject ->
currentWorkspace, openProject -> openWorkspace, projectIds ->
workspaceIds, etc.). The pasteboard drag-type *constant* is renamed to
deckardWorkspaceDragType while its underlying string value
("com.deckard.project-reorder") stays unchanged so the runtime drag
identifier remains stable.

Persistence migration:
- DeckardState.CodingKeys reads the legacy "projects" key as well as the
  new "workspaces", and only ever writes "workspaces".
- SidebarGroupState gets explicit Codable so it reads "projectIds" and
  "workspaceIds", writing only "workspaceIds".
- DeckardShortcutMigration is extended to cover four more identifier
  renames (openFolder, closeFolder, nextProject, previousProject ->
  openWorkspace, closeWorkspace, nextWorkspace, previousWorkspace) and
  a new flag key so it re-runs once on the upgrade beyond commit 2.

Tests cover the projects -> workspaces decode path, the projectIds ->
workspaceIds decode path, a full v2 -> v3 round-trip that exercises
every legacy key at once, and the expanded set of shortcut renames.

The literal "~/.claude/projects/" paths used by Claude Code's session
storage layout are intentionally untouched — those refer to the upstream
directory, not Deckard's concept of a workspace.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code-review follow-up: the prior commits left a long tail of identifiers,
local bindings, comments, and a few user-facing strings that still used
the legacy vocabulary even though their referenced types had moved to
WorkspaceItem / SidebarGroup.

User-visible:
- File menu items "Next Project", "Previous Project", and "Project 1..10"
  become "Next Workspace", "Previous Workspace", and "Workspace 1..10",
  matching what the Settings shortcut pane already shows.
- Settings help text for the default Claude/Codex args now says
  "overridden per workspace" instead of "per project".

Internal renames:
- SidebarOrderItem.project enum case -> .workspace (the on-disk
  discriminator stays "project" so existing state.json round-trips
  unchanged; this rename is Swift-side only).
- SidebarItem.project -> .workspace (runtime enum, no persistence).
- SidebarGroupView.folder property and init label -> .group.
- MoveToGroupInfo.project / .folder fields -> .workspace / .group.
- All remaining method names that meant "the workspace as a unit":
  closeProject -> closeWorkspace, selectProject -> selectWorkspace,
  selectNextProject / selectPrevProject -> selectNextWorkspace /
  selectPrevWorkspace, addTabToCurrentProject ->
  addTabToCurrentWorkspace, createTabInProject -> createTabInWorkspace,
  exploreCurrentProjectSessions -> exploreCurrentWorkspaceSessions,
  openProjectPicker -> openWorkspacePicker, openProjectClicked ->
  openWorkspaceClicked, sidebarRowToProjectIndex ->
  sidebarRowToWorkspaceIndex, recentlyClosedProjects ->
  recentlyClosedWorkspaces, nextVisibleProjectIndex ->
  nextVisibleWorkspaceIndex, collapsedProjectIds -> collapsedWorkspaceIds.
- Argument labels: projectId: -> workspaceId:, folder: -> group: on
  group-membership APIs, autoExpandFolder: -> autoExpandGroup:.
- Local var bindings: let folder -> let group, let project ->
  let workspace, projectId -> workspaceId where the binding was a
  WorkspaceItem id.
- WorkspacePicker internals: allProjects/filteredProjects ->
  allWorkspaces/filteredWorkspaces, loadRecentProjects ->
  loadRecentWorkspaces, NSUserInterfaceItemIdentifier("Project") ->
  ("Workspace").
- All // MARK: section headers and adjacent comments in the renamed
  files.
- Test class methods and XCTFail messages updated to the new vocabulary.

Pasteboard type strings (com.deckard.workspace-reorder /
com.deckard.group-reorder) are now consistent with their constants —
the prior commit's "preserve the old string for stability" comment was
mistaken since pasteboards never survive an app restart.

Untouched (intentional): the on-disk SidebarOrderItem discriminator
"project", every "~/.claude/projects/" reference (Claude Code's
upstream layout), and the ShortcutMigration history comment that names
the old "folder/project" identifiers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comprehensive cleanup that catches every greppable folder/project
identifier and comment that the prior commits missed. Previously the
rename was only partial inside the Detection and Session modules and
inside several test files; this commit unifies vocabulary across the
whole codebase.

Notable widening:
- `projectPath:` parameter label across ContextMonitor, ProcessMonitor,
  QuotaMonitor, BookmarkManager, SessionExplorerWindowController,
  DeckardWindowController, SidebarController, TabBarController and the
  matching `forProjectPath:` variants -> `workspacePath:` /
  `forWorkspacePath:`. Same for `projectName:`, `projectKey`,
  `projectPaths:`.
- All remaining `*Project*` method/local names in tab management:
  selectTabInWorkspace (formerly InProject), addTabToCurrentWorkspace
  call sites, recoverCodexSessionId(forWorkspacePath:), etc.
- SidebarController internals: shortcutForWorkspaceIndex (was
  shortcutForProjectIndex), local `groupView` (was `folderView`),
  `groupIdx`, `isInGroup`, `newGroupItem`, `currentWorkspaceId`.
- SidebarRowInfo struct fields parentGroup/childIndexInGroup/isGroup
  (were parentFolder/childIndexInFolder/isFolder).
- SidebarViews helpers acceptsGroupDrag / updateGroupDrag /
  groupView(at:) / clearGroupHighlight (were the *Folder* variants).
- AppDelegate's `projectPicker` field -> `workspacePicker`,
  `closeWorkspaceItem` local var.
- Init labels: `SidebarGroupView(group: workspaceCount:)` (was
  `folder: projectCount:`).
- Drag-handler local bindings draggedWorkspace / sourceGroup /
  targetGroup / etc.
- Comments throughout (// Sidebar groups, // Group header, // Restore
  groups, "workspaces inside groups", BookmarkManager docstrings,
  ContextMonitor docstrings, ClaudeCLIFlags Codex notes).
- ProcessMonitor diagnostic log strings "ACTIVE: workspace=" (was
  "project=").
- Test method names testWorkspace*, testWorkspaceItem*, test fixture
  data ("Test Group" / "My Group" / "Empty Group" / "Large Group" /
  /Users/test/workspace / /real-workspace / /linked-workspace /
  /my-workspace / "Workspace/Codex"), and bindings (oldWorkspaceState,
  newWorkspaceState, let workspace = WorkspaceItem(...), let group =
  SidebarGroup(...)).
- XCTFail messages corrected to reference the actual case name
  (.workspace / .group) instead of the now-renamed .project / .folder.
- Pasteboard type strings com.deckard.workspace-reorder /
  com.deckard.group-reorder (were ".project-reorder" /
  ".folder-reorder").

Untouched (intentional): on-disk SidebarOrderItem discriminator
"project", legacy CodingKey aliases (sidebarFolders / projects /
projectIds), the ShortcutMigration history comment that names the old
identifiers, every "~/.claude/projects/" path (Claude Code's upstream
storage layout), the `claudeProjectDirName` extension and the tests
that exercise it, and the `revealProjectNumbersModifiers` UserDefaults
key (renaming would lose users' current preference for no benefit).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@gi11es gi11es merged commit bd3ca6e into master May 4, 2026
3 checks passed
@gi11es gi11es deleted the refactor/folder-to-workspace branch May 4, 2026 11:37
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