Skip to content

fix(chat): thread path:line refs through file open - #1149

Closed
ZxlDragonDoctor wants to merge 2 commits into
vastsa:mainfrom
ZxlDragonDoctor:fix/chat-file-ref-line
Closed

ZxlDragonDoctor wants to merge 2 commits into
vastsa:mainfrom
ZxlDragonDoctor:fix/chat-file-ref-line

Conversation

@ZxlDragonDoctor

Copy link
Copy Markdown
Contributor

Problem

Chat [path:line] chips open the file but not the line (#681). parseFileRef strips :line[:col] before any opener, so FileRefChip / useOpenChatFileRef never see the position.

Solution

  • parseFileRefPosition() keeps :line[:col]; resolvePreviewTarget attaches it to the file target
  • FileRefChip and useOpenChatFileRef pass { line, column } into the host open path
  • workPanelFileRequest / WorkPanelTab carry line/column through every tab→request construction
  • Host FilesTab scrolls to the requested line
  • Plugin-view locations stay plain paths (no #L) so the bundled file-manager is unchanged

Testing

  • Command: node --test apps/desktop/test/chat-links.test.mjs apps/desktop/test/transcript-file-chips.test.mjs
  • Result: 32 passed (parse/position + chip routing contracts)

Issue alignment

  • Addresses maintainer landing conditions on closed fix(chat): preserve path:line refs through file open #810 (resubmit on current main):
    1. rebase onto main
    2. extend workPanelFileRequest plus every tab→request construction
    3. pass position through the FileRefChip path
    4. keep plugin locations free of #L
    5. update the contract tests
  • Maintainer also noted plugin-side #L jump would need a bundled plugin update; this PR keeps plugin locations plain as accepted, and the host file tab now lands on the line.

Non-goals

  • Markdown #L links (follow-up)
  • Changing the bundled file-manager plugin view

parseFileRef still strips :line[:col] for labels, but resolvePreviewTarget keeps the position on the target. FileRefChip and useOpenChatFileRef pass it to the host file request so FilesTab can scroll to the line. Plugin-view locations stay plain paths.
@muzimu217

Copy link
Copy Markdown
Contributor

The threading itself (parse → chip → hook → work-panel request → FilesTab scroll) is the right shape, and keeping plugin-view locations as plain paths is a good call. But the branch currently fails 4 of its own touched tests — the onOpen contract and the position field changed, and four older assertions were not updated to match. Same command, same machine, for the record:

Checkout node --test chat-links.test.mjs transcript-file-chips.test.mjs
merge-base (2a4b80a) 30 pass / 0 fail
your head (bde73e3) 28 pass / 4 fail

The four, all assertion-staleness rather than logic bugs:

  1. chat-links.test.mjs:92 — resolvePreviewTarget classifies urls and workspace files: the old deepEqual expects {kind, path} but the target now carries line: 10, so strict equality overflows. (Your new resolvePreviewTarget carries line/col on file chips (#681) passes — this older sibling just needs the same update.)
  2. transcript-file-chips.test.mjs — sent user-message file refs render as composer-like chips: the source-scan regex /onOpen\(path, undefined, mimeType\)/ no longer matches now that the call carries the position argument.
  3. same file — a file chip is routed by where the reference resolved, never optimistically
  4. same file — a tool row and a tool result row open a file where the message body does

(3) and (4) are the same onOpen-signature staleness as (2). Also spotted while scanning: features/chat/transcript/ChatTranscript.tsx on this branch starts with a UTF-8 BOM (EF BB BF), which lands inside the concatenated source these tests scan — unrelated to your change but worth stripping while you're in here; rustfmt/tooling flags BOMs elsewhere in this repo.

pnpm --filter @pi-desktop/desktop typecheck passes on your head. Update the four assertions to the new contract and this is ready.

@vastsa

vastsa commented Sep 28, 2026

Copy link
Copy Markdown
Owner

I reviewed the current head against the reported behavior. The issue is real, but this PR is not a complete root-cause fix yet.

  • The targeted tests currently report 28 passing and 4 failing: the existing chat-link assertion still expects the old path-only shape, and the transcript chip assertions still expect the old onOpen(path, undefined, mimeType) contract.
  • More importantly, useOpenChatFileRef still drops line and column whenever the bundled file-manager view is available: it calls openTab(fileManagerPluginTab(resolved.path)) with no position. That is the normal user path, so [path:line] still opens the file without jumping to the requested line.

The PR explicitly treats plugin locations as a non-goal, which leaves the reported failure mode intact. I am leaving this PR open and not merging it until the position is threaded through the plugin-view path (or the landing scope is changed with an explicit compatibility decision).

Review follow-up on vastsa#1149:
- fileManagerPluginTab accepts optional { line, column } and stores them on
  WorkPanelTab (same fields the host file tab already uses).
- useOpenChatFileRef passes the chat position into that plugin tab instead of
  dropping line/column whenever the bundled file view is available.
- Stale assertions updated to the new onOpen(..., { line, column }) contract and
  to resolvePreviewTarget carrying line on path:line tokens.
@ZxlDragonDoctor

Copy link
Copy Markdown
Contributor Author

Pushed follow-up for the review notes on this PR:

Stale assertions (the 4 failures)

  1. chat-links.test.mjs resolvePreviewTarget classifies urls and workspace files — src/a.ts:10 now expects { kind: "file", path: "src/a.ts", line: 10 }.
    2–4. transcript-file-chips.test.mjs — updated to the new contract:
    • onOpen(path, undefined, mimeType, { line, column })
    • fileManagerPluginTab(resolved.path, { line, column })

Root-cause gap (plugin path)

useOpenChatFileRef no longer drops position when the bundled file manager is available:

  • fileManagerPluginTab(location, { line, column }) now accepts and stores line/column on WorkPanelTab (same fields as the host file tab).
  • The plugin open path passes the chat path:line position through that tab instead of calling fileManagerPluginTab(resolved.path) only.

Host fallback openFile(..., { line, column }) is unchanged.

Please re-run:

node --test apps/desktop/test/chat-links.test.mjs apps/desktop/test/transcript-file-chips.test.mjs

Happy to follow the plugin view's remaining scroll wiring if the file-manager bundle needs a separate change outside this repo.

@muzimu217

Copy link
Copy Markdown
Contributor

Re-ran your requested command on head 73879521f (the plugin file-manager threading commit): 30/32 — great progress on the root-cause gap, the fileManagerPluginTab(location, { line, column }) threading is exactly what the maintainer's verdict asked for. Two remaining stale assertions from the same contract change, with exact fixes:

  1. transcript-file-chips.test.mjs:31 area — a file chip is routed by where the reference resolved, never optimistically asserts /openFile\(resolved\.path, mimeType\)/ against use-preview-target.ts, but the call is now openFile(resolved.path, mimeType, { line, column }) (line 178). Update the regex to /openFile\(resolved\.path, mimeType, \{ line, column \}\)/.

  2. transcript-file-chips.test.mjs:61 area — a tool row and a tool result row open a file where the message body does asserts /target\.kind === "file" \? openFileRef\(target\.path\) : openHttpUrl\(target\.url\)/, but the call is now openFileRef(target.path, undefined, undefined, { line, column }) (line 31). The ternary assertion needs the new argument shape.

After those two regex updates the pair of suites should go fully green — everything else (typecheck, the two root-cause fixes, the assertion updates you already pushed) checked out clean on my side.

@muzimu217

Copy link
Copy Markdown
Contributor

Heads-up before this lands: follow-up 73879521f (threading path:line through the plugin file-manager open) introduces a mount double-read regression.

Reproduction (same command you requested, three consecutive runs on a clean checkout of 73879521f):

node --test apps/desktop/test/chat-links.test.mjs apps/desktop/test/transcript-file-chips.test.mjs
→ 30/32 pass, 2 fail (the openFile/openFileRef signature assertions noted above)

node --test apps/desktop/test/todo-recovery.test.mjs
→ FAIL: "TodoDock recovers a failed first read when the host returns without a session switch"
  AssertionError: 2 !== 1  (h.reads === ["session-a", "session-a"] after the first render+settle)

On pristine upstream/main (373a691) the same todo-recovery suite is 5/5 green. So the regression is specific to this branch's plugin-path threading: after fileManagerPluginTab(location, { line, column }) starts carrying the position, the dock's mount path fires the session-todos read twice (recovery's initial refresh() plus a second trigger), which the test contract pins at exactly one.

Suggested directions (either works):

  1. Trace the second trigger in the plugin-open path — likely useSessionTodosRecovery's hostStatus refresh racing the dock's own mount read once fileManagerPluginTab carries position — and gate it (generation/dedupe by sessionId).
  2. Or split the landing: keep the position threading for the host file tab, land the plugin-view scroll wiring as an explicit follow-up with its own regression test.

Tested on: clean checkout of 73879521f, pnpm install --frozen-lockfile + pnpm -r build, Node 25.9.0, macOS arm64. The two signature-assertion failures above also reproduce on this head, consistent with the regex updates still being needed.

@vastsa

vastsa commented Oct 2, 2026

Copy link
Copy Markdown
Owner

Thanks for the follow-up and for carrying the line/column through the host file-tab path. I checked the current head against main (8906c88) and the reported failure still occurs on the default bundled file-manager path: useOpenChatFileRef calls fileManagerPluginTab(..., { line, column }), but WorkPanel passes only location to PluginViewTab, and the pluginViewOpen payload has no line/column fields. The host FilesTab fallback can scroll, but it is not selected when the bundled view is available, so the normal path still opens at the file without jumping to the requested line.

I also merged the PR head with current main in an isolated review worktree and resolved the chat-links.ts overlap for validation. The two targeted suites then reported 41/43 passing; the remaining failures are the two stale source-contract assertions in transcript-file-chips.test.mjs that still expect the old openFile / openFileRef signatures. The reported todo-recovery failure did not reproduce on this current-main candidate (5/5 passed).

Because the bundled-view path still does not consume the position, this does not yet remove the reported failure mode, so I am not merging it. Please wire the position into the file-manager view's actual navigation/scroll path and add a regression that verifies the rendered view lands on the requested line; then update those two assertions and rerun the affected suites. Thank you for the work on this.

@vastsa

vastsa commented Oct 3, 2026

Copy link
Copy Markdown
Owner

Thanks for the sustained work on threading the position through the chat and work-panel paths. The completed fix landed in successor PR #1349: positioned references now carry line and column to the host file tab, where the requested line is scrolled into view. Since the bundled file-manager API accepts only an opaque path and has no line-navigation contract, positioned references use that host viewer while ordinary file references keep the bundled view.

The successor passed the current-main CI and the isolated Electron line-scroll E2E. This PR is superseded by #1349 and is being closed. Thank you.

@vastsa vastsa closed this Oct 3, 2026
zszz3 pushed a commit to zszz3/PI-Desktop that referenced this pull request Oct 3, 2026
Review follow-up on vastsa#1149:
- fileManagerPluginTab accepts optional { line, column } and stores them on
  WorkPanelTab (same fields the host file tab already uses).
- useOpenChatFileRef passes the chat position into that plugin tab instead of
  dropping line/column whenever the bundled file view is available.
- Stale assertions updated to the new onOpen(..., { line, column }) contract and
  to resolvePreviewTarget carrying line on path:line tokens.

This branch was previously deployed

1 inactive deployment
Preview — 73879521 Deployed Sep 29, 2026 by vercel[bot]
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.

3 participants