refactor: rename folder/project terminology to workspace and group#91
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
ProjectItem)SidebarFolder)The work is split into three reviewable commits.
Commits
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.refactor: rename SidebarFolder internals to SidebarGroup— Types/vars/methods rename + persistence migration. Backward-compat decoders forstate.json(sidebarFolders→sidebarGroups,"folder"→"group"discriminator). One-shot UserDefaults migration for shortcut identifiersnewSidebarFolder/moveOutOfFolder→newGroup/moveOutOfGroup. Bumpsstate.jsonversion 2 → 3.refactor: rename ProjectItem internals to WorkspaceItem— Same pattern, broader scope.ProjectItem/ProjectState/ProjectTabState/ProjectPicker→WorkspaceItem/WorkspaceState/WorkspaceTabState/WorkspacePicker. Backward-compat decoders forprojects→workspacesandprojectIds→workspaceIds. Migrates the four remaining shortcut identifiers (openFolder/closeFolder/nextProject/previousProject).Persistence safety
state.jsonfiles load cleanly with no data loss.kMDItemContentType == public.folderSpotlight queries, and~/.claude/projects/(Claude Code's storage layout) are intentionally untouched.Test plan
Tests/SidebarGroupTests.swift: legacysidebarFolderskey decode, legacy"folder"discriminator decode, legacyprojectskey decode, full v2→v3 round-trip with all legacy keys at once, encoder writes only new keysTests/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 keysidebarFolders/projectIds/projectskeys), confirm sidebar shows the same groups and workspaces🤖 Generated with Claude Code