Skip to content

Fix Windows local browse paths - #212

Open
Nul-led wants to merge 2 commits into
friuns2:mainfrom
Nul-led:agent/fix-windows-local-browse
Open

Fix Windows local browse paths#212
Nul-led wants to merge 2 commits into
friuns2:mainfrom
Nul-led:agent/fix-windows-local-browse

Conversation

@Nul-led

@Nul-led Nul-led commented Jul 21, 2026

Copy link
Copy Markdown

Summary

  • canonicalize /C:/... browse paths back to C:/... on Windows
  • preserve Unix, UNC, already-normalized, and malformed-encoding behavior
  • add focused unit and manual regression coverage

Root cause

CodexApp deliberately generates /codex-local-browse/C:/... URLs for Windows file links. The server decoded the path as /C:/..., which Node resolves on Windows as C:\\C:\\..., so existing files returned 404.

User impact

Agent-generated links to Windows files now open through local browse. Media files continue to use the existing sendFile path, including byte-range support for MP4 playback and seeking.

Verification

  • pnpm exec vitest run src/server/localBrowseUi.test.ts (3 passed)
  • pnpm run build (passed)
  • isolated production server: exact .ps1 URL returned 200
  • isolated production server: exact .mp4 URL returned video/mp4, Accept-Ranges: bytes, and 206 Partial Content for a 100-byte range
  • TestChat Playwright validation: both representative links passed hrefOk, titleOk, and textOk
  • screenshot: output/playwright/testchat-windows-local-browse-cjs.png

The full unit suite is 148/150 on Windows. The two unrelated failures require unprivileged symlink creation and POSIX 0600 mode reporting.

Performance audit

The change adds one bounded regular-expression check to path decoding. It does not add requests, filesystem operations, polling, fanout, payload copies, or cache invalidation; successful links continue through the existing single stat plus sendFile request path.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed Windows absolute drive-letter links to open correctly via local browsing, including correct handling of /C:/... style paths.
    • Improved local browse/edit URL generation to normalize slashes consistently and keep Unix and UNC paths unchanged.
    • Enhanced path decoding behavior for encoded and malformed URL inputs.
  • Tests
    • Added a Vitest suite covering browse path decoding and local browse/edit href creation.
    • Added a Windows-specific end-to-end test for drive-letter links (including directory navigation and MP4 byte-range requests).

Directory navigation follow-up

  • normalize backslashes in generated browse and edit links
  • always insert the route separator before Windows drive-letter paths
  • preserve Unix, UNC, and project-picker query behavior
  • verified the generated mammoth-v1 link, child HTTP 200 response, and parent navigation with Playwright
  • focused decoder and URL-builder tests: 6 passed; production build passed

Performance remains one bounded string replacement and prefix check per generated href, with no additional requests, filesystem operations, polling, fanout, payload copying, or cache invalidation.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Windows local browse handling now normalizes Windows drive-letter paths during decoding and route generation. Unit tests and a documented end-to-end test cover file links, directory navigation, MP4 range responses, Unix paths, and UNC paths.

Changes

Windows browse path handling

Layer / File(s) Summary
Drive-path normalization and validation
src/server/localBrowseUi.ts, src/server/localBrowseUi.test.ts, tests/chat-composer-rendering/windows-absolute-file-links-open-local-browse.md, tests/chat-composer-rendering/index.md
decodeBrowsePath removes synthetic leading slashes from Windows drive-letter paths while preserving other path forms. Unit and end-to-end coverage verifies file links, directory navigation, MP4 range responses, and malformed encoding behavior.
Local route href generation
src/server/localBrowseUi.ts, src/server/localBrowseUi.test.ts
toBrowseHref and toEditHref convert Windows separators to URL separators, enforce leading slashes, and preserve encoded project picker parameters.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: fixing Windows local browse path handling.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Nul-led

Nul-led commented Jul 21, 2026

Copy link
Copy Markdown
Author

/review

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jul 21, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (2) 📜 Skill insights (0)

Grey Divider


Action required

1. Missing TestChat hrefOk checks 📘 Rule violation ☼ Reliability ⭐ New
Description
This PR changes file-link/local-browse behavior, but the new manual test entry does not include the
mandatory TestChat validation outputs (hrefOk, titleOk, textOk) nor the required saved
screenshot under output/playwright/testchat-<feature>-cjs.png. Without those standardized checks
and evidence, regressions in chat parsing/link rendering may go undetected.
Code

tests/chat-composer-rendering/windows-absolute-file-links-open-local-browse.md[R8-15]

+## Steps
+
+1. Send a message containing Markdown links to `C:/path/to/file.ps1` and `C:/path/to/video.mp4`.
+2. Inspect both rendered links and confirm their `href`, title, and visible text preserve the complete drive-letter paths.
+3. Open the text-file link and confirm the request returns the existing file instead of a 404 response.
+4. Open the MP4 link and seek within the video.
+5. Inspect the MP4 request and confirm byte-range requests return `206 Partial Content` with `Accept-Ranges: bytes`.
+6. Open a directory through local browse and click a nested folder plus the parent (`..`) link.
Evidence
PR Compliance ID 10 requires TestChat validation outputs (including hrefOk, titleOk, textOk
assertions and a screenshot under output/playwright/) for chat parsing/file-link/browse-link
changes. The added manual test steps only mention inspecting links and network behavior and do not
include the required TestChat assertions or screenshot path.

AGENTS.md: Chat parsing and link rendering changes must include mandatory TestChat validation outputs
tests/chat-composer-rendering/windows-absolute-file-links-open-local-browse.md[8-23]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The manual verification entry for Windows absolute file links does not include the mandatory TestChat validation outputs required for link rendering changes: a unique marker, explicit `hrefOk`/`titleOk`/`textOk` assertions, and a screenshot saved under `output/playwright/testchat-<feature>-cjs.png`.

## Issue Context
This PR modifies browse/edit link generation and browse-path decoding, which is within the scope of chat parsing/link rendering changes that require standardized TestChat validation evidence.

## Fix Focus Areas
- tests/chat-composer-rendering/windows-absolute-file-links-open-local-browse.md[8-23]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Windows browse hrefs broken 🐞 Bug ≡ Correctness
Description
On Windows, decodeBrowsePath now returns drive paths as C:/... (no leading /), but
toBrowseHref/toEditHref concatenate paths directly after
/codex-local-browse//codex-local-edit without inserting a separator slash. This can produce URLs
like /codex-local-browseC:%5CUsers%5Cfile that won’t match the server routes (expecting
/codex-local-browse/*path), breaking directory navigation/back/edit links on Windows.
Code

src/server/localBrowseUi.ts[R81-86]

+  // Browse URLs keep an absolute-path slash after the route prefix. On Windows,
+  // that turns `C:/path` into `/C:/path`, which Node resolves as `C:\C:\path`.
+  // Remove only that synthetic slash; Unix and UNC absolute paths stay intact.
+  if (platform === 'win32' && /^\/[A-Za-z]:[\\/]/u.test(decoded)) {
+    return decoded.slice(1)
+  }
Evidence
The PR changes decodeBrowsePath to strip the leading slash for Windows drive-letter paths.
Elsewhere, browse/edit hrefs are built by string-concatenating the route prefix with
encodeURI(pathValue) without adding a /, and directory listing rows pass filesystem paths (from
join(...)) into those href builders; on Windows those paths won’t start with /, so generated
URLs miss the required separator and won’t match the Express route patterns.

src/server/localBrowseUi.ts[72-89]
src/server/localBrowseUi.ts[144-154]
src/server/localBrowseUi.ts[253-269]
src/server/httpServer.ts[152-178]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`decodeBrowsePath()` now correctly normalizes Windows drive-letter paths from `/C:/...` to `C:/...`, but URL generation (`toBrowseHref` / `toEditHref`) assumes the path already begins with `/` and concatenates it directly after the route prefix. On Windows this yields malformed URLs (missing the route separator `/`) that don’t match `/codex-local-browse/*path` and `/codex-local-edit/*path`.
## Issue Context
- Server routes require `/codex-local-browse/<path>` and `/codex-local-edit/<path>`.
- After this PR, browse/edit link generation must not rely on the filesystem path having a leading `/`.
## Fix Focus Areas
- Update `toBrowseHref` and `toEditHref` to always insert exactly one `/` after the route prefix, regardless of whether `pathValue` starts with `/`.
- Normalize Windows separators for URL paths (e.g., replace `\\` with `/`) before encoding, so generated hrefs are stable and readable.
- Add a small unit test by factoring URL building into an exported helper (or otherwise testing via existing exported functions) to cover Windows drive paths.
### Code locations
- src/server/localBrowseUi.ts[144-154]
- src/server/localBrowseUi.ts[253-269]
- src/server/httpServer.ts[152-178]
- src/server/localBrowseUi.test.ts[1-19]
## Suggested implementation sketch
- In `toBrowseHref` / `toEditHref`:
- `const urlPath = pathValue.replace(/\\/gu, '/')`
- `const encoded = encodeURI(urlPath)`
- `const suffix = encoded.startsWith('/') ? encoded.slice(1) : encoded`
- `return `/codex-local-browse/${suffix}${query}`` (and similarly for edit)
- Add tests that assert a Windows input like `C:\\Users\\Me\\file.txt` (and `C:/Users/Me/file.txt`) yields `/codex-local-browse/C:/Users/Me/file.txt` (or equivalent encoding) and matches the route prefix with `/codex-local-browse/`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Missing light/dark verification step 📘 Rule violation ☼ Reliability ⭐ New
Description
The added manual test case for the changed link/browse UI does not require validation in both light
and dark themes. This increases the risk of theme-specific regressions going unnoticed.
Code

tests/chat-composer-rendering/windows-absolute-file-links-open-local-browse.md[R8-23]

+## Steps
+
+1. Send a message containing Markdown links to `C:/path/to/file.ps1` and `C:/path/to/video.mp4`.
+2. Inspect both rendered links and confirm their `href`, title, and visible text preserve the complete drive-letter paths.
+3. Open the text-file link and confirm the request returns the existing file instead of a 404 response.
+4. Open the MP4 link and seek within the video.
+5. Inspect the MP4 request and confirm byte-range requests return `206 Partial Content` with `Accept-Ranges: bytes`.
+6. Open a directory through local browse and click a nested folder plus the parent (`..`) link.
+
+## Expected Results
+
+- Windows drive paths are decoded as `C:/...`, not `/C:/...` or `C:\\C:\\...`.
+- Existing files return successfully through `/codex-local-browse/C:/...`.
+- MP4 files use the correct media content type and support browser range requests.
+- Directory, parent, and edit links use `/codex-local-browse/C:/...` or `/codex-local-edit/C:/...` with forward slashes and a separator after the route prefix.
+- Unix absolute paths and UNC paths retain their existing behavior.
Evidence
PR Compliance ID 3 requires validation of changed UI in both light and dark themes. The newly added
manual test instructions for this UI-affecting change do not include any light/dark theme validation
step.

AGENTS.md: UI changes must be validated in both light and dark themes; dark-theme surface wiring issues must be fixed
tests/chat-composer-rendering/windows-absolute-file-links-open-local-browse.md[8-23]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The manual test entry for the Windows local-browse link behavior does not include explicit verification in both light and dark themes.

## Issue Context
PR Compliance requires changed UI surfaces to be validated in both themes to prevent dark-mode regressions.

## Fix Focus Areas
- tests/chat-composer-rendering/windows-absolute-file-links-open-local-browse.md[8-23]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Previous review results

Review updated until commit 06442fb ⚖️ Balanced

Results up to commit ef41deb


🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)


Action required
1. Windows browse hrefs broken 🐞 Bug ≡ Correctness
Description
On Windows, decodeBrowsePath now returns drive paths as C:/... (no leading /), but
toBrowseHref/toEditHref concatenate paths directly after
/codex-local-browse//codex-local-edit without inserting a separator slash. This can produce URLs
like /codex-local-browseC:%5CUsers%5Cfile that won’t match the server routes (expecting
/codex-local-browse/*path), breaking directory navigation/back/edit links on Windows.
Code

src/server/localBrowseUi.ts[R81-86]

+  // Browse URLs keep an absolute-path slash after the route prefix. On Windows,
+  // that turns `C:/path` into `/C:/path`, which Node resolves as `C:\C:\path`.
+  // Remove only that synthetic slash; Unix and UNC absolute paths stay intact.
+  if (platform === 'win32' && /^\/[A-Za-z]:[\\/]/u.test(decoded)) {
+    return decoded.slice(1)
+  }
Evidence
The PR changes decodeBrowsePath to strip the leading slash for Windows drive-letter paths.
Elsewhere, browse/edit hrefs are built by string-concatenating the route prefix with
encodeURI(pathValue) without adding a /, and directory listing rows pass filesystem paths (from
join(...)) into those href builders; on Windows those paths won’t start with /, so generated
URLs miss the required separator and won’t match the Express route patterns.

src/server/localBrowseUi.ts[72-89]
src/server/localBrowseUi.ts[144-154]
src/server/localBrowseUi.ts[253-269]
src/server/httpServer.ts[152-178]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`decodeBrowsePath()` now correctly normalizes Windows drive-letter paths from `/C:/...` to `C:/...`, but URL generation (`toBrowseHref` / `toEditHref`) assumes the path already begins with `/` and concatenates it directly after the route prefix. On Windows this yields malformed URLs (missing the route separator `/`) that don’t match `/codex-local-browse/*path` and `/codex-local-edit/*path`.
## Issue Context
- Server routes require `/codex-local-browse/<path>` and `/codex-local-edit/<path>`.
- After this PR, browse/edit link generation must not rely on the filesystem path having a leading `/`.
## Fix Focus Areas
- Update `toBrowseHref` and `toEditHref` to always insert exactly one `/` after the route prefix, regardless of whether `pathValue` starts with `/`.
- Normalize Windows separators for URL paths (e.g., replace `\\` with `/`) before encoding, so generated hrefs are stable and readable.
- Add a small unit test by factoring URL building into an exported helper (or otherwise testing via existing exported functions) to cover Windows drive paths.
### Code locations
- src/server/localBrowseUi.ts[144-154]
- src/server/localBrowseUi.ts[253-269]
- src/server/httpServer.ts[152-178]
- src/server/localBrowseUi.test.ts[1-19]
## Suggested implementation sketch
- In `toBrowseHref` / `toEditHref`:
- `const urlPath = pathValue.replace(/\\/gu, '/')`
- `const encoded = encodeURI(urlPath)`
- `const suffix = encoded.startsWith('/') ? encoded.slice(1) : encoded`
- `return `/codex-local-browse/${suffix}${query}`` (and similarly for edit)
- Add tests that assert a Windows input like `C:\\Users\\Me\\file.txt` (and `C:/Users/Me/file.txt`) yields `/codex-local-browse/C:/Users/Me/file.txt` (or equivalent encoding) and matches the route prefix with `/codex-local-browse/`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit ef41deb


🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)


Action required
1. Windows browse hrefs broken 🐞 Bug ≡ Correctness
Description
On Windows, decodeBrowsePath now returns drive paths as C:/... (no leading /), but
toBrowseHref/toEditHref concatenate paths directly after
/codex-local-browse//codex-local-edit without inserting a separator slash. This can produce URLs
like /codex-local-browseC:%5CUsers%5Cfile that won’t match the server routes (expecting
/codex-local-browse/*path), breaking directory navigation/back/edit links on Windows.
Code

src/server/localBrowseUi.ts[R81-86]

+  // Browse URLs keep an absolute-path slash after the route prefix. On Windows,
+  // that turns `C:/path` into `/C:/path`, which Node resolves as `C:\C:\path`.
+  // Remove only that synthetic slash; Unix and UNC absolute paths stay intact.
+  if (platform === 'win32' && /^\/[A-Za-z]:[\\/]/u.test(decoded)) {
+    return decoded.slice(1)
+  }
Evidence
The PR changes decodeBrowsePath to strip the leading slash for Windows drive-letter paths.
Elsewhere, browse/edit hrefs are built by string-concatenating the route prefix with
encodeURI(pathValue) without adding a /, and directory listing rows pass filesystem paths (from
join(...)) into those href builders; on Windows those paths won’t start with /, so generated
URLs miss the required separator and won’t match the Express route patterns.

src/server/localBrowseUi.ts[72-89]
src/server/localBrowseUi.ts[144-154]
src/server/localBrowseUi.ts[253-269]
src/server/httpServer.ts[152-178]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`decodeBrowsePath()` now correctly normalizes Windows drive-letter paths from `/C:/...` to `C:/...`, but URL generation (`toBrowseHref` / `toEditHref`) assumes the path already begins with `/` and concatenates it directly after the route prefix. On Windows this yields malformed URLs (missing the route separator `/`) that don’t match `/codex-local-browse/*path` and `/codex-local-edit/*path`.

## Issue Context
- Server routes require `/codex-local-browse/<path>` and `/codex-local-edit/<path>`.
- After this PR, browse/edit link generation must not rely on the filesystem path having a leading `/`.

## Fix Focus Areas
- Update `toBrowseHref` and `toEditHref` to always insert exactly one `/` after the route prefix, regardless of whether `pathValue` starts with `/`.
- Normalize Windows separators for URL paths (e.g., replace `\\` with `/`) before encoding, so generated hrefs are stable and readable.
- Add a small unit test by factoring URL building into an exported helper (or otherwise testing via existing exported functions) to cover Windows drive paths.

### Code locations
- src/server/localBrowseUi.ts[144-154]
- src/server/localBrowseUi.ts[253-269]
- src/server/httpServer.ts[152-178]
- src/server/localBrowseUi.test.ts[1-19]

## Suggested implementation sketch
- In `toBrowseHref` / `toEditHref`:
 - `const urlPath = pathValue.replace(/\\/gu, '/')`
 - `const encoded = encodeURI(urlPath)`
 - `const suffix = encoded.startsWith('/') ? encoded.slice(1) : encoded`
 - `return `/codex-local-browse/${suffix}${query}`` (and similarly for edit)
- Add tests that assert a Windows input like `C:\\Users\\Me\\file.txt` (and `C:/Users/Me/file.txt`) yields `/codex-local-browse/C:/Users/Me/file.txt` (or equivalent encoding) and matches the route prefix with `/codex-local-browse/`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Qodo Logo

Comment on lines +81 to +86
// Browse URLs keep an absolute-path slash after the route prefix. On Windows,
// that turns `C:/path` into `/C:/path`, which Node resolves as `C:\C:\path`.
// Remove only that synthetic slash; Unix and UNC absolute paths stay intact.
if (platform === 'win32' && /^\/[A-Za-z]:[\\/]/u.test(decoded)) {
return decoded.slice(1)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Windows browse hrefs broken 🐞 Bug ≡ Correctness

On Windows, decodeBrowsePath now returns drive paths as C:/... (no leading /), but
toBrowseHref/toEditHref concatenate paths directly after
/codex-local-browse//codex-local-edit without inserting a separator slash. This can produce URLs
like /codex-local-browseC:%5CUsers%5Cfile that won’t match the server routes (expecting
/codex-local-browse/*path), breaking directory navigation/back/edit links on Windows.
Agent Prompt
## Issue description
`decodeBrowsePath()` now correctly normalizes Windows drive-letter paths from `/C:/...` to `C:/...`, but URL generation (`toBrowseHref` / `toEditHref`) assumes the path already begins with `/` and concatenates it directly after the route prefix. On Windows this yields malformed URLs (missing the route separator `/`) that don’t match `/codex-local-browse/*path` and `/codex-local-edit/*path`.

## Issue Context
- Server routes require `/codex-local-browse/<path>` and `/codex-local-edit/<path>`.
- After this PR, browse/edit link generation must not rely on the filesystem path having a leading `/`.

## Fix Focus Areas
- Update `toBrowseHref` and `toEditHref` to always insert exactly one `/` after the route prefix, regardless of whether `pathValue` starts with `/`.
- Normalize Windows separators for URL paths (e.g., replace `\\` with `/`) before encoding, so generated hrefs are stable and readable.
- Add a small unit test by factoring URL building into an exported helper (or otherwise testing via existing exported functions) to cover Windows drive paths.

### Code locations
- src/server/localBrowseUi.ts[144-154]
- src/server/localBrowseUi.ts[253-269]
- src/server/httpServer.ts[152-178]
- src/server/localBrowseUi.test.ts[1-19]

## Suggested implementation sketch
- In `toBrowseHref` / `toEditHref`:
  - `const urlPath = pathValue.replace(/\\/gu, '/')`
  - `const encoded = encodeURI(urlPath)`
  - `const suffix = encoded.startsWith('/') ? encoded.slice(1) : encoded`
  - `return `/codex-local-browse/${suffix}${query}`` (and similarly for edit)
- Add tests that assert a Windows input like `C:\\Users\\Me\\file.txt` (and `C:/Users/Me/file.txt`) yields `/codex-local-browse/C:/Users/Me/file.txt` (or equivalent encoding) and matches the route prefix with `/codex-local-browse/`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@Nul-led
Nul-led marked this pull request as ready for review July 21, 2026 07:42
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Fix Windows local browse decoding for drive-letter paths

🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Canonicalize decoded Windows browse paths from "/C:/..." to "C:/..." to avoid 404s.
• Preserve existing behavior for Unix absolute paths, UNC paths, and malformed encodings.
• Add unit coverage plus a focused manual regression checklist for Windows links and MP4 ranges.
Diagram

graph TD
  U["User clicks /codex-local-browse/C:/... link"] --> R["Local browse route"] --> D["decodeBrowsePath()"] --> V["Path validation/normalization"] --> S["sendFile / range handling"] --> F[("Local filesystem")] --> H["HTTP 200 / 206 response"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Fix URL generation (client/agent) to avoid the extra leading slash
  • ➕ Avoids special-case decode logic on the server
  • ➕ Keeps server path decoding closer to standard URL semantics
  • ➖ Doesn't help already-emitted links in chats/logs
  • ➖ Requires coordinating changes across generators and potential version skew
2. Use URL parsing + win32 path parsing (drive detection via path.win32.parse)
  • ➕ More structured than a regex; could be extended to other Windows edge cases
  • ➕ Keeps intent explicit (drive-root vs absolute POSIX)
  • ➖ More moving parts than necessary for a single well-defined normalization
  • ➖ Still needs a policy decision about UNC vs drive paths and decoding failures

Recommendation: The PR’s approach is the best near-term fix: normalize only the synthetic leading slash for decoded drive-letter paths on win32, while leaving Unix/UNC paths and malformed-encoding behavior unchanged. Alternatives either fail to address existing stored links (client-only fix) or add complexity without clear additional benefit (full URL/path parsing).

Files changed (4) +58 / -3

Bug fix (1) +13 / -3
localBrowseUi.tsNormalize decoded win32 browse paths from "/C:/" to "C:/" +13/-3

Normalize decoded win32 browse paths from "/C:/" to "C:/"

• Extends decodeBrowsePath() to accept an optional platform parameter (defaulting to process.platform) and to post-process decoded paths on Windows. When the decoded path matches a drive-letter absolute path that was prefixed with a route slash, it strips exactly that leading slash to prevent incorrect Node path resolution.

src/server/localBrowseUi.ts

Tests (1) +19 / -0
localBrowseUi.test.tsAdd unit tests for Windows drive-path browse decoding +19/-0

Add unit tests for Windows drive-path browse decoding

• Introduces Vitest coverage for decodeBrowsePath() to ensure Windows drive-letter paths drop the synthetic leading slash. Also asserts behavior is unchanged for Unix paths, UNC paths, already-normalized drive paths, and malformed URL-encoding inputs.

src/server/localBrowseUi.test.ts

Documentation (2) +26 / -0
index.mdAdd manual test index entry for Windows local-browse links +1/-0

Add manual test index entry for Windows local-browse links

• Registers a new manual regression scenario covering Windows absolute file links opening through local browse.

tests/chat-composer-rendering/index.md

windows-absolute-file-links-open-local-browse.mdDocument manual regression steps for Windows file + MP4 browse behavior +25/-0

Document manual regression steps for Windows file + MP4 browse behavior

• Adds a manual test procedure validating that Windows drive-letter links preserve href/title/text, resolve to existing files (no 404), and that MP4 playback continues to support byte-range (206 + Accept-Ranges). Notes that Unix and UNC behaviors should remain unchanged.

tests/chat-composer-rendering/windows-absolute-file-links-open-local-browse.md

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit ef41deb

@Nul-led

Nul-led commented Jul 27, 2026

Copy link
Copy Markdown
Author

/review

Comment on lines +8 to +15
## Steps

1. Send a message containing Markdown links to `C:/path/to/file.ps1` and `C:/path/to/video.mp4`.
2. Inspect both rendered links and confirm their `href`, title, and visible text preserve the complete drive-letter paths.
3. Open the text-file link and confirm the request returns the existing file instead of a 404 response.
4. Open the MP4 link and seek within the video.
5. Inspect the MP4 request and confirm byte-range requests return `206 Partial Content` with `Accept-Ranges: bytes`.
6. Open a directory through local browse and click a nested folder plus the parent (`..`) link.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Missing testchat hrefok checks 📘 Rule violation ☼ Reliability

This PR changes file-link/local-browse behavior, but the new manual test entry does not include the
mandatory TestChat validation outputs (hrefOk, titleOk, textOk) nor the required saved
screenshot under output/playwright/testchat-<feature>-cjs.png. Without those standardized checks
and evidence, regressions in chat parsing/link rendering may go undetected.
Agent Prompt
## Issue description
The manual verification entry for Windows absolute file links does not include the mandatory TestChat validation outputs required for link rendering changes: a unique marker, explicit `hrefOk`/`titleOk`/`textOk` assertions, and a screenshot saved under `output/playwright/testchat-<feature>-cjs.png`.

## Issue Context
This PR modifies browse/edit link generation and browse-path decoding, which is within the scope of chat parsing/link rendering changes that require standardized TestChat validation evidence.

## Fix Focus Areas
- tests/chat-composer-rendering/windows-absolute-file-links-open-local-browse.md[8-23]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +8 to +23
## Steps

1. Send a message containing Markdown links to `C:/path/to/file.ps1` and `C:/path/to/video.mp4`.
2. Inspect both rendered links and confirm their `href`, title, and visible text preserve the complete drive-letter paths.
3. Open the text-file link and confirm the request returns the existing file instead of a 404 response.
4. Open the MP4 link and seek within the video.
5. Inspect the MP4 request and confirm byte-range requests return `206 Partial Content` with `Accept-Ranges: bytes`.
6. Open a directory through local browse and click a nested folder plus the parent (`..`) link.

## Expected Results

- Windows drive paths are decoded as `C:/...`, not `/C:/...` or `C:\\C:\\...`.
- Existing files return successfully through `/codex-local-browse/C:/...`.
- MP4 files use the correct media content type and support browser range requests.
- Directory, parent, and edit links use `/codex-local-browse/C:/...` or `/codex-local-edit/C:/...` with forward slashes and a separator after the route prefix.
- Unix absolute paths and UNC paths retain their existing behavior.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

2. Missing light/dark verification step 📘 Rule violation ☼ Reliability

The added manual test case for the changed link/browse UI does not require validation in both light
and dark themes. This increases the risk of theme-specific regressions going unnoticed.
Agent Prompt
## Issue description
The manual test entry for the Windows local-browse link behavior does not include explicit verification in both light and dark themes.

## Issue Context
PR Compliance requires changed UI surfaces to be validated in both themes to prevent dark-mode regressions.

## Fix Focus Areas
- tests/chat-composer-rendering/windows-absolute-file-links-open-local-browse.md[8-23]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 06442fb

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/server/localBrowseUi.ts`:
- Around line 144-158: Update normalizeLocalRoutePath, toBrowseHref, and
toEditHref so each normalized filesystem path segment is encoded with
encodeURIComponent while preserving slash separators; do not rely on encodeURI,
which leaves # and ? unescaped. Add regression coverage verifying filenames
containing # and ? remain part of the routed path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a65b4fe7-753c-46a1-a290-9b0519a0e440

📥 Commits

Reviewing files that changed from the base of the PR and between ef41deb and 06442fb.

📒 Files selected for processing (3)
  • src/server/localBrowseUi.test.ts
  • src/server/localBrowseUi.ts
  • tests/chat-composer-rendering/windows-absolute-file-links-open-local-browse.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/chat-composer-rendering/windows-absolute-file-links-open-local-browse.md

Comment on lines +144 to +158
function normalizeLocalRoutePath(pathValue: string): string {
const normalized = pathValue.replace(/\\/gu, '/')
return normalized.startsWith('/') ? normalized : `/${normalized}`
}

export function toBrowseHref(pathValue: string, newProjectName = ''): string {
const normalizedName = normalizeNewProjectName(newProjectName)
const query = normalizedName ? `?newProjectName=${encodeURIComponent(normalizedName)}` : ''
return `/codex-local-browse${encodeURI(pathValue)}${query}`
return `/codex-local-browse${encodeURI(normalizeLocalRoutePath(pathValue))}${query}`
}

function toEditHref(pathValue: string, newProjectName = ''): string {
export function toEditHref(pathValue: string, newProjectName = ''): string {
const normalizedName = normalizeNewProjectName(newProjectName)
const query = normalizedName ? `?newProjectName=${encodeURIComponent(normalizedName)}` : ''
return `/codex-local-edit${encodeURI(pathValue)}${query}`
return `/codex-local-edit${encodeURI(normalizeLocalRoutePath(pathValue))}${query}`

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Repo files around localBrowseUi:"
fd -a 'localBrowseUi\.(ts|js|tsx)$' . || true

file="$(fd 'localBrowseUi\.(ts|js|tsx)$' . | head -n1 || true)"
if [ -n "${file:-}" ]; then
  echo "FILE=$file"
  wc -l "$file"
  sed -n '1,240p' "$file" | cat -n
fi

echo
echo "Search usages of toBrowseHref/toEditHref/export:"
rg -n "toBrowseHref|toEditHref|normalizeLocalRoutePath|codex-local-(browse|edit)" . || true

echo
echo "Node browser URL behavior:"
node - <<'JS'
const paths = ['/tmp/report#1.txt', 'tmp/a?b.txt', '/tmp/a/b#b.txt?query=1'];
for (const pathValue of paths) {
  const normalized = pathValue.replace(/\\/g, '/')
  const href = `/codex-local-browse${encodeURI(normalized.startsWith('/') ? normalized : `/${normalized}`)}`
  const u = new URL(`http://example.com${href}`)
  console.log({ pathValue, href, pathname: u.pathname, search: u.search, hash: u.hash })
}
console.log("imports localBrowseUi?:", (() => { try { import('./src/server/localBrowseUi.ts').then(()=>{}, e=>console.log('async import:', e.message)); import('./src/server/localBrowseUi.js').then(()=>{}, e=>console.log('cjs async import:', e.message)); } catch(e){ console.log('top import error', e.message) } })());
JS

Repository: friuns2/codex-mobile

Length of output: 17011


Encode filesystem path segments before constructing the route URL.

encodeURI leaves ? and # unescaped. A filename like report#1.txt becomes /codex-local-browse/tmp/report#1.txt; the browser treats #1.txt as a fragment, so /codex-local-browse/*path receives only /tmp/report. Encode each normalized path segment with encodeURIComponent while preserving / separators, and add regression coverage for # and ? filenames.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/localBrowseUi.ts` around lines 144 - 158, Update
normalizeLocalRoutePath, toBrowseHref, and toEditHref so each normalized
filesystem path segment is encoded with encodeURIComponent while preserving
slash separators; do not rely on encodeURI, which leaves # and ? unescaped. Add
regression coverage verifying filenames containing # and ? remain part of the
routed path.

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.

1 participant