Skip to content

fix(web): keep spaced wrapped file paths clickable - #734

Open
Hugo-DDT wants to merge 2 commits into
Ark0N:masterfrom
Hugo-DDT:cursor/spaced-wrapped-file-paths-b495
Open

Hugo-DDT wants to merge 2 commits into
Ark0N:masterfrom
Hugo-DDT:cursor/spaced-wrapped-file-paths-b495

Conversation

@Hugo-DDT

Copy link
Copy Markdown
Contributor

Fixes #733

A parenthesised file path with a space in its filename ((/mnt/data/Kobo/Kobo eReader.conf), including when that name is hard-wrapped between the words) was linked as the folder prefix /mnt/data/Kobo/Kobo.

.conf is now a link extension, matching the text the guarded attachment routes already serve. Parentheses bound the whole spaced path (absolute, relative, ~/, or a bare filename), so the folder prefix and the extension-bearing tail are not linked as fragments. A hard wrap between those filename words keeps the separating space, and the clickable cells stay on the rows that hold the name. Existing URL, folder, and mid-token path joins are unchanged.

Tests: test/spaced-file-path-links.test.ts, test/file-path-link-forms.test.ts, and test/response-viewer-file-links.test.ts (these failed before the fix and pass after).

A parenthesised path with a space in its filename was linked as the
folder prefix. .conf joins the shared link extensions, parentheses
bound the whole path, and a hard wrap between filename words keeps the
separating space and the clickable cells on each row.

Fixes Ark0N#733

Co-authored-by: DDT <1786035110@qq.com>
Copilot AI balanced review requested due to automatic review settings October 28, 2026 12:28

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@Ark0N

Ark0N commented Oct 28, 2026

Copy link
Copy Markdown
Owner

Thank you for picking this up, Hugo-DDT. This PR makes a parenthesised path with a space in its name, such as (/mnt/data/Kobo/Kobo eReader.conf), link as one file, adds .conf to the link extensions, and keeps the space when such a name is hard-wrapped between its words.

The .conf addition and the absolute-path case inside parentheses are good and I want to keep them (as a bonus, Update(/home/me/proj/My Docs/notes.md) now links whole). Two other parts turn links that work on master into links to files that do not exist, so they need changes before this can merge.

1. Parentheses act as a path label, so prose inside them becomes a path (src/web/public/constants.js:2395-2418)

spacedPathBoundedByParens() falls back to re-running spacedFilePathOnLine() behind a synthesized Path: label, and then to accepting any bare word word file.ext. Neither can tell a filename word from a command or a verb:

Text master links this PR links
(node server.js) server.js node server.js
(updated package.json) package.json updated package.json
(e.g. eslint.config.js) eslint.config.js e.g. eslint.config.js
(default: settings.json) settings.json default: settings.json
(cat notes.md) notes.md cat notes.md
(CI/CD pipeline config.yml) config.yml CI/CD pipeline config.yml

Both surfaces share findFilePathLinks(), so this reaches the terminal and the response viewer. It also contradicts the documented rule that a relative spaced path needs an explicit path label (docs/architecture-invariants.md, which names CI/CD as prose that must defer, and the File-path links section of CLAUDE.md). (Kobo eReader.conf) and (node server.js) have the same shape, so more guards cannot separate them. Please keep only the direct branch (absolute paths, judged by every spacedFilePathOnLine() rule), drop the synthesized-label and bare-filename fallbacks along with their claims in the docs paragraph and the changeset, and add the rows above as negative tests in test/spaced-file-path-links.test.ts.

2. join-space also fires on mid-word cuts, so wrapped paths and URLs gain a space (src/web/public/constants.js:2669, returned at 2654, applied at 2763)

continuationKeepsWordSpace() fires on any unclosed ( anywhere in the stitched text so far, plus a letter on both sides of the break. Claude Code hard-cuts long tokens mid-word (the verbatim BROKEN fixture in test/file-path-link-forms.test.ts is one; it still passes only because that cut falls between two digits). Through the real terminal link provider:

  • Bash(cp /home/me/proj/builds/captures/2026-10-10/ghost1b_gamepl over ay.png /tmp/) now links a folder .../ghost1b_gamepl plus a relative ay.png (master: the whole file).
  • Details (see https://example.com/reports/2026/ghost1b_game over play.png). now links the URL as .../ghost1b_game plus a relative play.png (master: the whole URL).
  • ⎿ BILD (/mnt/build/neon_getaway-mgr-hebel/builds/captures/ghost1b_game over play.png) err=0 now links .../ghost1b_game play.png (master: .../ghost1b_gameplay.png).

So "Existing URL, folder, and mid-token path joins are unchanged" does not hold for a cut between two letters after an open paren. Please scope the rule to the parenthesis that opens the broken-off path itself (not Bash( or any other ( earlier in the stitched text), never apply it to a token containing ://, and pin the Bash and URL shapes above as tests that keep joining without a space.

Even scoped that way, the third shape stays ambiguous: a parenthesised path cut between two letters looks exactly like the Kobo wrap, and both readings are valid paths. Whether fixing the wrapped Kobo case is worth that cost is an open question on my side. If the rule stays, the trade-off needs a sentence in docs/architecture-invariants.md next to the other known trade-offs and a test pinning it.

Smaller

  • src/web/public/constants.js:2392: test trimmed.length > 1000 before trimmed.includes(' '), so deeply nested parentheses with no spaces are not scanned once per level (100,000 nested pairs take 218 ms here against 2.5 ms on master).

Typecheck, lint, format and the link test files all pass locally and CI is green; the problems above are behaviours the current tests do not exercise. Once items 1 and 2 are addressed and the trade-off question is settled, this is close to ready.

Parentheses accept only an absolute spaced path. Join-space applies to the
parenthesis that opens that path, never an earlier one or a :// token.

Co-authored-by: DDT <1786035110@qq.com>
@Ark0N

Ark0N commented Oct 29, 2026

Copy link
Copy Markdown
Owner

Thank you for the quick second round, Hugo-DDT. This PR links a parenthesised absolute path with a space in its name, such as (/mnt/data/Kobo/Kobo eReader.conf), as one file, adds .conf to the link extensions, and keeps the space when such a name is hard-wrapped between its words.

The second commit covers everything from my earlier review: the synthesized-label and bare-filename fallbacks are gone and the six prose rows are pinned as negative tests, join-space only counts the path's own ( and never a :// token, the Bash( and URL shapes are pinned, the mid-letter trade-off has its sentence in docs/architecture-invariants.md and a test, and the length check runs before the space scan. Typecheck, lint, format, the frontend syntax check and the full test suite pass locally.

1. join-space also fires when the parenthesis does not close right after the name (src/web/public/constants.js:2671, in continuationKeepsWordSpace())

The space is kept whenever the path's ( is still open, but parenthesisedSpacedPaths() can only take the spaced name when the inside of the pair is exactly the path. When anything else follows the name inside the parenthesis, or the parenthesis does not close on those rows, the space goes in and the folder and relative detectors split the path. Through terminalPathLine() and findFilePathLinks(), with Saved the capture (/mnt/data/captures/2026-10-10/ghost1b_gamepl as the first row:

Second row master links this PR links
ay.png, 2.1 MB). .../ghost1b_gameplay.png folder .../ghost1b_gamepl + relative ay.png
ay.png and the log). .../ghost1b_gameplay.png folder .../ghost1b_gamepl + relative ay.png
ay.png (no closing paren) .../ghost1b_gameplay.png folder .../ghost1b_gamepl + relative ay.png

This is a different cost from the documented trade-off: there one link goes to the spaced name, here a link that works on master becomes two wrong ones, and the real Kobo wrap gains nothing in these shapes either ((/mnt/data/Kobo/Kobo over eReader.conf and more also splits). Please keep the space only when the continuation token itself closes the parenthesis right after the extension, for example right after the bare line:

if (!/^[^()]*\)[\].,;:!?]*$/.test(headText)) return false;

I ran that on a copy: the three rows above go back to master's join, while the Kobo wrap, the Bash( and URL tests and your ay.png). trade-off test keep their current results. Please add the ay.png, 2.1 MB). row to test/file-path-link-forms.test.ts as a join without a space.

Smaller

  • test/file-path-link-forms.test.ts:384: the new provider test expects openLogViewerWindow for /mnt/data/Kobo/Kobo eReader.conf, but only because the harness stubs _isExternalPreviewPath to ~/ paths (line 283). The session's workingDir is /mnt/build/neon_getaway-mgr-hebel, so in the app the real _isExternalPreviewPath() (src/web/public/panels-ui.js:4186) sends this path to openFilePreview. Please drop the two routing assertions, or put the .conf file under the session's working directory so the expectation matches the app.
  • docs/architecture-invariants.md:412: please state that the ( has to start the broken-off token or be separated from it by whitespace only, as the code comment does. Today (/mnt/data/My Photos/Kobo over eReader.conf) joins as KoboeReader.conf, and the doc sentence reads as if it keeps the space.

Open question

Whether join-space stays at all is still open on my side. A parenthesised path that a program cuts between two letters short of the right edge reads exactly like the Kobo wrap (your ay.png). test), so the rule fixes the wrapped Kobo case and gives up that one. Item 1 and the doc point are what the rule needs if it stays; the routing test applies either way. The parenthesised absolute-path detection and the .conf extension do not depend on that answer.

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.

File links truncate wrapped filenames containing spaces, including .conf files

3 participants