Skip to content

impl(web): configurable Connection abstraction (tauri port phase 2)#75

Draft
andykais-claude wants to merge 1 commit into
cursor/tauri-server-phase1-1e82from
cursor/tauri-frontend-phase2-1e82
Draft

impl(web): configurable Connection abstraction (tauri port phase 2)#75
andykais-claude wants to merge 1 commit into
cursor/tauri-server-phase1-1e82from
cursor/tauri-frontend-phase2-1e82

Conversation

@andykais-claude

Copy link
Copy Markdown
Collaborator

Phase 2 of the Tauri port plan. Stacked on top of phase 1 — please review/merge phase 1 first, then this. (Base branch is intentionally cursor/tauri-server-phase1-1e82; rebase to main once phase 1 lands.)

The frontend used to hardcode same-origin when constructing the RPC client and /files/* URLs. With Tauri (and a "point my browser at a remote forager" use-case for the web build) on the horizon, both need to come from a runtime Connection.

Walkthrough

forager_phase2_connection_smoketest.mp4

Browser smoke test of the resolver: thumbnails, RPC, image preview, and video playback (HTTP Range) all working through the new controller.media_url / controller.thumbnail_url helpers.

Same-origin default — thumbnail src is http://127.0.0.1:8000/files/thumbnail/...
?api=... query string accepted; SPA boots and renders
Cronch video streaming via the new media_url helper

What changed

New: packages/web/src/lib/connection.ts

The Connection abstraction the design doc described in §5. resolve_connection() picks a base URL in priority order:

  1. window.__FORAGER_CONNECTION__ — injected by the Tauri shell before the SPA boots.
  2. ?api=https://example.com query string — handy for the browser-served build pointing at a different backend.
  3. Same origin — current default.

Trailing slashes stripped, result cached per session, SSR/test path returns an empty placeholder so module load doesn't blow up.

New: packages/web/test/connection.test.ts

8 unit tests covering every branch, plus caching and the SSR placeholder. Run with deno test packages/web/test/connection.test.ts. These nail down the resolver behaviour without needing a browser, which the in-page smoke test couldn't do reliably (CORS not in scope until phase 4).

BaseController (packages/web/src/lib/base_controller.ts)

  • New connection: Connection field, populated from a constructor arg (defaults to resolve_connection()).
  • The RPC client now builds its URL from connection.base_url instead of window.location.protocol + host (this also incidentally fixes a missing // in the old string concatenation).
  • New media_url(id) and thumbnail_url(id, index?) helpers so the rest of the SPA never builds those URLs by hand.

Rune (packages/web/src/lib/runes/rune.ts)

Same treatment — accepts an optional Connection (default resolve_connection()) and exposes the same two URL helpers as protected methods. MediaViewRune.preview_thumbnail is the consumer today; the helpers are there for any future rune that needs to build asset URLs.

Replaced: every hardcoded /files/* literal in the frontend

Site Before After
MediaViewRune.preview_thumbnail `/files/thumbnail/${...?.id}` this.thumbnail_url(thumbnail.id)
MediaGroupRune.preview_thumbnail `/files/thumbnail/${...id}` this.thumbnail_url(...)
MediaView.svelte media_url derivable `/files/media_file/${ref.id}` controller.media_url(ref.id)
MediaView.svelte filmstrip <img src> /files/thumbnail/{id}?index={i} controller.thumbnail_url(id, i)
MediaView.svelte audio thumbnail <img src> /files/thumbnail/{id} controller.thumbnail_url(id)

grep -rn '/files/(media_file\|thumbnail)' packages/web/src now only matches the two helper bodies (base_controller.ts, rune.ts).

Why default-resolve in the constructor instead of plumbing through +page.svelte?

Tauri injects window.__FORAGER_CONNECTION__ before any SPA code runs, and the browser-served build either picks up ?api= from the URL or falls back to same-origin. In every case resolve_connection() is correct by the time a controller is constructed. Plumbing through layout/page data would just be ceremony with no runtime effect, and the design doc didn't call for it. Both the constructor and Rune still accept an explicit Connection for testability and for any future case that wants to override.

Testing

  • deno test packages/web/test/connection.test.ts — 8 / 8 pass.
  • deno task --cwd packages/web build:local — clean.
  • deno task compile:local — full CLI binary builds.
  • deno task --cwd packages/cli test — 2 / 2 pass.
  • ✅ Bundle inspection: grep -oE '/files/(media_file|thumbnail)[^"\]{0,80}' .../chunks/*.jsshows only/files/media_file/${t}and/files/thumbnail/${t}template-literal placeholders — i.e. the helpers concatenateconnection.base_url + /files/...` at runtime as intended.
  • ✅ Manual browser smoke test (recorded above):
    • Same-origin: thumbnail src resolves to http://127.0.0.1:8000/files/thumbnail/....
    • ?api=http://127.0.0.1:8000: SPA still boots, thumbnails render, no console errors.
    • Image preview + video playback (Range requests) work end-to-end.

⚠️ deno check against the SvelteKit source surfaces 91 pre-existing errors ($app/navigation resolution, Deno globals not typed when checked outside deno task) — same count and same files on phase 1's tip, verified by git stash + recheck. Build is the actual gate and it's clean.

Follow-ups (not in this PR)

  • Phase 3 — adapter-static switch; collapse the custom Deno SvelteKit adapter once the SPA is fully decoupled.
  • Phase 4 — web.server.cors_allow_origins so a browser-served ?api= against a different host actually works (today both ends would be same-origin, so the SPA loads fine but a true cross-origin remote would hit CORS).

To show artifacts inline, enable in settings.

Open in Web Open in Cursor 

Phase 2 of the Tauri port plan. The frontend used to hardcode same-origin
when constructing the RPC client and /files/* URLs. With Tauri (and a
'point my browser at a remote forager' use-case for the web build) on the
horizon, both need to be resolved from a runtime Connection.

new: packages/web/src/lib/connection.ts
  - resolve_connection() picks a base_url in this priority order:
      1. window.__FORAGER_CONNECTION__ (Tauri injects this at startup)
      2. ?api=https://example.com query string
      3. Same origin (current default)
  - Result is cached per session.
  - Trailing slashes stripped for predictable URL building.

new: packages/web/test/connection.test.ts
  - 8 unit tests covering each branch + caching + SSR placeholder fallback.

BaseController and Rune
  - Both accept an optional Connection (default = resolve_connection()).
  - Both expose media_url(id) and thumbnail_url(id, index?) helpers so the
    rest of the SPA never builds those URLs by hand.
  - BaseController's RPC client URL now derives from connection.base_url.

callers
  - lib/runes/media_view_rune.svelte.ts: preview_thumbnail now uses
    this.thumbnail_url(...) on both MediaViewRune and MediaGroupRune.
  - routes/browse/components/MediaView.svelte: media_url, audio thumbnail
    src, and the filmstrip thumbnails all go through controller.{media,
    thumbnail}_url(...).

Browser experience is identical for the same-origin default path. The two
new branches are exercised by the unit tests; visual smoke-tested in a
Chromium webview.

Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
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.

2 participants