Skip to content

fix(desktop): link directories and paths with spaces - #57126

Open
Finn763 wants to merge 2 commits into
anomalyco:devfrom
Finn763:desktop-path-links
Open

Finn763 wants to merge 2 commits into
anomalyco:devfrom
Finn763:desktop-path-links

Conversation

@Finn763

@Finn763 Finn763 commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #55863

Type of change

  • Bug fix

What does this PR do?

File links in messages only worked for plain files that the search index knows about:

  • The existence check listed the parent directory but accepted only type === "file", so directories could never become links. A hand-written markdown link to a directory did link, then failed on click with a "File not found" toast because the click path reads the target as a file.
  • The inline-code classifier rejected any candidate containing whitespace, so paths like C:\Program Files\app\file.txt were never linkified even though the file:// route linked them.

Changes:

  • Files.kind(path) (new, next to exists) returns "file" | "directory" | undefined from the same directory listing; exists keeps its boolean contract and now accepts directories too.
  • checkFileLinkExists uses kind, and falls back to it for workspace-relative paths the search index (files only) does not cover, so directories named in a message get links.
  • The inline-code classifier no longer rejects whitespace outright; a path still only becomes a link once the host confirms it exists.
  • Clicking a directory link expands it in the file tree and reveals the side panel instead of loading it as a file.

How did you verify your code works?

  • bun test src/components/markdown-inline-code-kind.test.ts — 28 pass, including C:\Program Files\app\file.txt and my docs/notes.md; these 3 cases fail on base.
  • bun test src/file/resolve-link.test.ts — 32 pass, including absolute and workspace-relative directory links; fails on base.
  • bun test src/file/ — 53 pass; bun run typecheck clean in gui-extensions and session-ui.
  • packages/app typecheck cannot run in this Windows checkout: src/custom-elements.d.ts is a git symlink materialized as a text file, so tsc -b reports TS1128 before reaching any source. Unrelated to this change.

Screenshots / recordings

N/A.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Related: #37891, #51723, #51872

Public API: adds required kind(path) to the gui-extensions Files interface; exists() now also returns true for directories (callers unaffected, other Files implementations must add kind)

@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The linking side of the fix works where I could run it, but I couldn't check the directory click itself: it needs the desktop app.

How I checked: I ran opencode serve from source for the base and this PR. Against it I ran each checkout's real inlineCodeKind and checkFileLinkExists, with that checkout's exists/kind logic, on a project with sub/ and my folder/file.txt. With the PR, C:\Program Files\app\file.txt and …/my folder/file.txt are classified as paths, and sub, <abs>/sub and sub/ become links; on the base none of them do. The new tests pass, and typecheck is clean in gui-extensions, session-ui and app. Before/after screenshots or a short recording of clicking a nested folder link in the desktop app would help.

Public API: this adds a required kind(path): Promise<"file" | "directory" | undefined> method to the Files interface in the gui-extensions SDK (sdk/host-apis.ts), and exists() now also returns true for directories. Code that only calls Files is unaffected, but any other implementation of Files must add kind to type-check.

Concerns (details inline):

  • A folder click expands only that folder. A nested folder under collapsed parents stays hidden.
  • Every file-link click now waits for a directory listing before reading the file.
  • Every relative inline-code span the search index doesn't match now also triggers a directory listing. With whitespace now allowed, many more spans qualify.

Related: #37891, #51723, #51872


// A directory has no tab to open: reveal it in the file tree instead of loading it as a file,
// which would fail with a not-found toast.
if ((await files.kind(targetPath)) === "directory") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds a file.list round trip before every file-link click, including the common case where the target is a file. Could the directory check run only when it's needed? For example, when sync reports not-found, or by reusing what the search match already tells us (search only returns files).

This only expands the folder itself. expandDir doesn't open its parents, so for packages/app under a collapsed packages the click switches the tree to "all" and nothing visibly changes. Could this expand every parent folder too, and scroll the node into view? Also, files.absolute(targetPath) already exists on Files and covers UNC paths and backslashes; the inline regex here doesn't.

if (await findFileLink({ files: input.files, path: parsed.path, signal: input.signal })) return true

// The search index covers files only; a directory named in a message still gets a link.
return existing(parsed.path)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every relative candidate the search index doesn't match now triggers an extra directory listing (and the abort signal isn't passed to it). I measured one extra request for console.log and for git add src/foo.ts, neither of which links. With the whitespace check gone, many more inline-code spans reach this point (JSON.parse, shell commands, etc.), and the per-render checked set is the only dedupe, so it repeats on every re-render. Could the fallback be limited to paths that can actually name a directory? For example, only paths containing / or ending in /, or with no file extension.

@Finn763

Finn763 commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Addressed both points: (1) the click path now reuses the directory listing that linkified the path (a small path→kind cache per session root), so a normal file click adds no file.list round trip; (2) spaced text is only treated as a path when it starts like one (drive, leading slash, ./ or ../), so git add src/foo.ts and similar prose stay code and never reach the existence check.

This branch has not been deployed

No deployments
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.

desktop: path links skip directories and paths with spaces

1 participant