Skip to content

Add in-app folder picker for web and mobile clients#492

Merged
BunsDev merged 4 commits intoOpenKnots:mainfrom
jbrahy:feat/server-folder-picker
Apr 25, 2026
Merged

Add in-app folder picker for web and mobile clients#492
BunsDev merged 4 commits intoOpenKnots:mainfrom
jbrahy:feat/server-folder-picker

Conversation

@jbrahy
Copy link
Copy Markdown
Contributor

@jbrahy jbrahy commented Apr 22, 2026

Summary

  • Adds a new WS method, projects.browseDirectory, that lists immediate children of any directory the server process can stat. Scoped to the authenticated WS transport.
  • Adds ServerFolderPickerDialog — an in-app folder picker that consumes the new method over WebSocket, used as a fallback from the Sidebar "pick folder" flow whenever the native OS picker is unavailable (i.e. when the server is running headless — osascript / zenity / kdialog return null).
  • Electron desktop users continue to see the native OS dialog; the new fallback fires only for non-Electron clients (web, mobile) when the server-side native picker produces no selection.
  • 9 unit tests cover the handler: sort order, hidden-file filtering, non-absolute rejection, non-directory rejection, broken-symlink partial flag, directory-kind symlinks, filesystem-root parentPath omission, and the default-to-home behavior.

Security posture (documented on the handler)

The new endpoint is gated by the same auth token as every other WS method. Within that gate, it performs no path allowlisting — any caller holding a valid token can enumerate the full filesystem of the user the server process runs as. That is intentional for a filesystem picker, but operators sharing the auth token widely should treat filesystem enumeration as within scope of that token. See the docstring on browseFileSystemDirectory for details.

Test plan

  • bun run fmt:check
  • bun run lint
  • bun run typecheck
  • cd apps/server && bun run test -- fileSystemBrowser (9/9 passing)
  • Manual: with server on headless host + auth token, open web client, "Add project" → native picker returns null → in-app dialog opens rooted at $HOME; navigate up/down via dirent click and the ../ entry; confirm project is added at chosen path
  • Manual: Electron desktop still sees the native OS folder picker (no fallback dialog)

🤖 Generated with Claude Code

When the native folder picker is unavailable — typically on a headless
remote host running the okcode server, where osascript / zenity / kdialog
return null — the web client currently has no way to select a project
directory short of typing an absolute path by hand.

Add a WS method, projects.browseDirectory, that walks any directory the
server process can stat and returns its immediate children. The Sidebar
"pick folder" flow falls back to a new in-app dialog that consumes this
method when running in a non-Electron client and the native picker
returns null.

Security posture documented on the handler: the method is gated by the
same auth token as every other WS route, but within that gate it does no
path allowlisting — holders of a valid token can enumerate the full
filesystem of the service user, which is the point of a filesystem
picker.
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 22, 2026

@jbrahy is attempting to deploy a commit to the 0xBuns Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions Bot added size:L vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Apr 22, 2026
jbrahy added 3 commits April 22, 2026 02:41
The previous listing split entries into directories-first / files-second
with no sort affordance, which was awkward at a glance and gave the user
no way to change order.

- Single flat list sorted case-insensitively by filename using locale
  rules (sensitivity: "base"), so "Endgame-..." and "endgame-..." collate
  adjacently.
- Column header "Name" with an A→Z / Z→A arrow icon; clicking toggles
  sort direction. Default is ascending.
- Files keep their muted, non-interactive styling and now render with a
  file icon rather than empty spacer so the visual hierarchy is clear
  when directories and files are intermixed in the same sorted list.
…orts

- Replace the fixed 320px list height with max-h:320px + min-h:80px so
  small directories no longer show a huge empty panel below the last
  entry, while long listings still scroll.
- Bump the column header to text-xs with foreground/70 so it is actually
  legible instead of sinking into the container background at 10px.
- Add min-w-0 to the path input so a long /deep/nested/path no longer
  forces the flex row to overflow the dialog panel on narrow viewports.
- Switch the loading spinner centering from h-full (which collapsed to
  zero now that the parent is no longer fixed-height) to its own
  min-h:80px so the spinner has room to breathe.
Previous revision nested DialogPanel inside DialogPopup with a manual
width override (w-[min(560px,95vw)]), which was wider than
DialogPopup's max-w-lg container and caused the path input, list rows,
and footer buttons to spill past the card's right edge.

Rewrite to follow the convention used by CloneRepositoryDialog /
WorktreeCleanupDialog:

- DialogPopup (max-w-xl) is the outer card; DialogHeader, DialogPanel,
  and DialogFooter are its direct children.
- Drop the manual width on DialogPanel; let DialogPopup's max-w-xl and
  the panel's own padding (px-6 pb-6) drive layout.
- Replace the raw overflow-auto list with ScrollArea (max-h-[280px]).
- Replace Loader2Icon with Spinner from ./ui/spinner.
- Switch nav buttons to size="icon-sm" for consistency with the system.
- Switch Cancel/Use-this-folder to size="sm" so they match other
  footer buttons.
- text-red-500 → text-destructive for the error state.
- Group the sort header and "Some entries skipped" hint in a single
  toolbar row above the list instead of a separate paragraph below.
@BunsDev BunsDev merged commit 8878afc into OpenKnots:main Apr 25, 2026
8 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants