122 lines
6.7 KiB
Markdown
122 lines
6.7 KiB
Markdown
---
|
|
status: current
|
|
system: tasks
|
|
requirements:
|
|
- REQ-TASKS-OPEN-FOLDER-001
|
|
---
|
|
|
|
# Open task folder system design
|
|
|
|
## Purpose and boundaries
|
|
|
|
Consolidate the session folder action into the editor dropdown using the existing editors service folder operation.
|
|
This extends task workspace access; it introduces no persistence or new OS integration.
|
|
See [requirements](../requirements/open-task-folder.md) and
|
|
[workspace actions](attach-workspace-sources.md).
|
|
|
|
## Requirement mapping
|
|
|
|
| Requirement | Design sections |
|
|
| --- | --- |
|
|
| REQ-TASKS-OPEN-FOLDER-001 | Components, Selection and transport, Presentation and failures |
|
|
|
|
## Components
|
|
|
|
`apps/web/components/task/task-top-bar.tsx` keeps `EditorsMenu` in
|
|
`TopbarToolsGroup`, inside the existing unarchived-task branch. The standalone
|
|
folder button is removed.
|
|
`apps/web/components/task/editors-menu.tsx` delegates the dropdown shell to
|
|
`EditorActionsDropdown` in `editor-actions-dropdown.tsx`, which renders a separated
|
|
Open folder row after the editor entries. Reuse `useTaskFolderAction` and `TaskFolderPicker`; retain
|
|
`useOpenSessionFolder` for requests, per-session duplicate suppression, loading,
|
|
and localized errors. Folder opening never changes the default editor.
|
|
|
|
The dropdown trigger requires a session, not a configured editor. With zero
|
|
editors, retain the disabled no-editors row and primary editor button while
|
|
allowing access to the folder row. Disable the folder row using the shared
|
|
folder action's capability/loading state; it must not disable editor entries.
|
|
The dropdown trigger uses the localized `task:editorActions` name, explicitly
|
|
announcing both editor and folder actions.
|
|
|
|
Close the dropdown before opening the folder picker. Follow
|
|
`WorkspaceActionsMenu` in `file-browser-toolbar.tsx`: queue selection in a ref,
|
|
activate from `onCloseAutoFocus`, and pass the persistent dropdown trigger to
|
|
`action.open`. Cancel restores that trigger, never an unmounted menu item.
|
|
Mount the picker outside dropdown content. Reuse `buildWorktreeOptions` and
|
|
`useSessionWorktrees` for labels and selected-session target resolution.
|
|
|
|
The phone Files workspace-actions menu already offers Open workspace folder.
|
|
Reuse that entry and its responsive menu treatment; route multiple-worktree choices
|
|
through `TaskFolderPicker`, using `Dialog` on desktop and `MobilePickerSheet` for
|
|
the short phone or coarse-pointer choice. The Files menu closes before opening
|
|
the picker and passes its persistent trigger for focus restoration.
|
|
Inspect `file-browser.tsx`, `file-browser-data.ts`, and the existing
|
|
`mobile-add-workspace-sources.spec.ts` as the current entry-point precedent.
|
|
|
|
## Selection and transport
|
|
|
|
Extend `openSessionFolder` in `lib/api/domains/session-api.ts` compatibly: retain
|
|
its existing second `ApiRequestOptions` argument and add an optional third payload
|
|
`{ worktree_id?: string }`. Extend the hook's `open` method to accept an optional
|
|
worktree ID. Existing no-argument callers retain their behavior.
|
|
|
|
The existing POST `/api/v1/task-sessions/:id/open-folder` already accepts
|
|
`OpenFolderRequest.WorktreeID`. `Service.OpenFolder` resolves it through
|
|
`resolveSessionPath`: a non-empty worktree ID must belong to the session or fail
|
|
with `ErrWorkspaceNotFound`. Only an empty ID permits the first-worktree and then
|
|
repository-local-path fallbacks. The browser sends identifiers, never a raw path
|
|
or shell command. Multiple choices must be explicit at the new shortcut and phone
|
|
entry point. Clear stale picker selection on session changes; closing a picker
|
|
must not issue a request.
|
|
|
|
Keep the existing backend host boundary. `OpenFolder` dispatches `open`,
|
|
`xdg-open`, or `explorer` with the resolved path as an argument. The backend also reports opener availability and rejects unavailable commands. Successful HTTP completion means the launch
|
|
request was accepted; it does not prove a native window became visible.
|
|
Remote/headless host limitations remain those of the existing action.
|
|
|
|
## Presentation and failures
|
|
|
|
Reuse translated folder copy when possible, adding a localized host-location hint
|
|
where needed. The dropdown trigger and folder row have accessible names and keyboard activation,
|
|
loading indicator, and disabled no-session/loading/unavailable states. Pending
|
|
folder launches are shared per session across all controls and clear only when
|
|
the initiating request settles; another session remains independent. Catch rejected requests
|
|
and use the existing localized toast pattern in `useOpenSessionInEditor` so callers
|
|
do not leave unhandled promise rejections. Return null after a surfaced error.
|
|
|
|
On phones, a visible Files menu opens a short temporary choice surface. The shared
|
|
mobile picker supplies one scrolling body, safe-area spacing, dismissal and focus
|
|
return. Its worktree rows have at least 44px hit areas. Keep desktop 28px control
|
|
geometry; do not inherit unconditional touch sizing into `TopbarToolsGroup`.
|
|
No layout/editor preferences or task runtime state change.
|
|
|
|
## Verification boundary
|
|
|
|
Frontend unit tests prove payload selection, stale-session handling, missing-session
|
|
behavior, failure recovery, and editor independence. Playwright proves the actual
|
|
button/menu flows and request payload on desktop and phone. Stub the native-opening
|
|
HTTP response in UI tests rather than launching file managers on CI. Existing Go
|
|
editor tests guard session/worktree resolution. A macOS smoke check proves Finder
|
|
opens the selected folder; record it separately if no macOS host is available.
|
|
|
|
## Host availability
|
|
|
|
The existing GET `/api/v1/editors` response includes `folder_opening_available`.
|
|
The editors service uses `exec.LookPath` for the platform command (`open`,
|
|
`xdg-open`, or `explorer`); unsupported platforms report false. The same command
|
|
mapping is used for opening, and the POST operation rejects unavailable openers.
|
|
The editors boot payload includes `folderOpeningAvailable` alongside its loaded
|
|
items. The shared editors store retains the capability; loaded editor items with
|
|
an unknown capability still trigger discovery. Each effect reads the live store
|
|
before claiming discovery so simultaneous consumers share one request/retry chain. Transient failure gets one delayed
|
|
retry while controls remain disabled; repeated failure settles to false. Unknown,
|
|
failed, or false discovery disables the folder row in the editor dropdown,
|
|
Files menu, and `file-actions-dropdown.tsx`, plus `useOpenSessionFolder`. Editor
|
|
entries retain their existing `editors` and `useOpenSessionInEditor` behavior.
|
|
No desktop dialog or mobile bottom sheet opens while unavailable. This checks
|
|
executable installation, like IDE discovery; launch-time failures still show errors.
|
|
|
|
## Implementation plan
|
|
|
|
[Editor dropdown folder action](../../../plans/editor-dropdown-folder/plan.md) records the implemented relocation. The original plan preserves historical validation evidence.
|