Skip to content

Bundle frontend in desktop app and add server connection picker#434

Closed
skulldogged wants to merge 3 commits into
spacedriveapp:mainfrom
skulldogged:feat/desktop-instance-picker
Closed

Bundle frontend in desktop app and add server connection picker#434
skulldogged wants to merge 3 commits into
spacedriveapp:mainfrom
skulldogged:feat/desktop-instance-picker

Conversation

@skulldogged

@skulldogged skulldogged commented Mar 14, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Desktop app now bundles the frontend locally instead of loading it from a remote server, so the Tauri shell works independently of any specific backend host
  • First launch shows a connect screen where you enter the URL of a running Spacebot server (e.g. http://127.0.0.1:19898), saved to localStorage
  • Server can be changed later from a button in the top bar; disconnection banner also links to the change-server dialog
  • All API and event-stream calls use the configured backend URL on desktop, while the web UI continues using same-origin as before
  • Fixed viewport overflow caused by body.style.zoom = "1.1" Tauri hack; replaced with proper html/body/#root height constraints
  • Added beforeDevCommand to Tauri config so bun run tauri:dev starts the Vite dev server automatically

Changes

  • interface/src/lib/backend.ts — desktop detection, URL normalization, localStorage persistence, getApiBaseUrl() / assetUrl()
  • interface/src/hooks/useDesktopInstance.tsx — React context for saved backend URL and change-server dialog state
  • interface/src/components/DesktopInstanceDialog.tsx — connect gate (first run) and change-server dialog
  • interface/src/api/client.tsAPI_BASE / BASE_PATH / IS_TAURI now read from backend.ts
  • interface/src/App.tsx — gates on hasConfiguredInstance before rendering router on desktop
  • interface/src/components/TopBar.tsx — change-server button showing current host (desktop only)
  • interface/src/components/ConnectionBanner.tsx — shows backend URL when disconnected (desktop only)
  • interface/src/routes/AgentWorkers.tsx — hardcoded /api/ paths replaced with getApiBaseUrl() / assetUrl()
  • interface/src/main.tsx — removed zoom: 1.1 desktop hack
  • interface/src/ui/style/style.scss — full-height constraint on html/body/#root, hidden body overflow
  • desktop/src-tauri/tauri.conf.jsonfrontendDist points to interface/dist, added beforeDevCommand and beforeBuildCommand

Test plan

  • ./scripts/preflight.sh — pass
  • cargo clippy --all-targets — pass
  • cargo fmt --all -- --check — pass
  • bun run build (interface) — pass

Note

Desktop frontend bundling and server connection picker now integrated. The desktop app loads the UI locally and connects to a configurable Spacebot server via URL, with UI controls to switch servers and view connection status.

Written by Tembo for commit 7c61ebf. This will update automatically on new commits.

Copilot AI review requested due to automatic review settings March 14, 2026 19:32
@coderabbitai

coderabbitai Bot commented Mar 14, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Adds Tauri desktop instance management: new backend utilities for environment and URL handling, a provider hook to manage desktop instance state, UI components and dialog to configure the instance, App integration, and replacement of hard-coded paths with assetUrl/getApiBaseUrl across several routes.

Changes

Cohort / File(s) Summary
Desktop Instance Hook & Provider
interface/src/hooks/useDesktopInstance.tsx
New hook and provider exposing isDesktop, currentUrl/currentLabel, dialog state, open/close dialog, hasConfiguredInstance, and connectToInstance (persists URL and triggers reload).
Backend utilities & API client
interface/src/lib/backend.ts, interface/src/api/client.ts
New centralized backend helpers: isTauriDesktop, normalize/save/get desktop URL, assetUrl, getApiBaseUrl; client constants BASE_PATH, IS_TAURI, API_BASE now derived from these helpers.
Desktop UI components
interface/src/components/DesktopInstanceDialog.tsx, interface/src/components/DesktopInstanceGate.tsx, interface/src/components/ConnectionBanner.tsx, interface/src/components/TopBar.tsx
Added dialog, gate, and connect form for configuring desktop instance; ConnectionBanner and TopBar updated to surface currentLabel and open dialog on desktop.
App integration
interface/src/App.tsx
Wrapped application with DesktopInstanceProvider, added AppShell component that conditionally renders DesktopInstanceGate when running on desktop without a configured instance, and renders DesktopInstanceDialog within the shell.
Route & asset updates
interface/src/routes/AgentWorkers.tsx
Replaced hard-coded endpoints and asset paths with getApiBaseUrl()/assetUrl(), added manifest asset validation and updated OpenCode embed/session URL constructions.
Misc: styling & boot
interface/src/ui/style/style.scss, interface/src/components/Sidebar.tsx, interface/src/main.tsx
Added full-page height and removed body margins/overflow; tiny formatting change in Sidebar; removed WKWebView zoom-adjustment block from main.tsx.
New package/manifest entries
package.json, manifest_file
Package/manifest touched (listed in diff summary).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.04% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the two main changes: frontend bundling in the desktop app and the addition of a server connection picker interface.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, detailing the summary, specific file changes, and test results.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

Tip

CodeRabbit can generate a title for your PR based on the changes.

Add @coderabbitai placeholder anywhere in the title of your PR and CodeRabbit will replace it with a title based on the changes in the PR. You can change the placeholder by changing the reviews.auto_title_placeholder setting.

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.

Pull request overview

Updates the Tauri desktop packaging to bundle the Vite-built frontend and introduces a desktop-only “server connection picker” so the UI can point at a user-selected Spacebot backend, while keeping the web UI same-origin.

Changes:

  • Added desktop backend URL detection/normalization + localStorage persistence, and routed API/asset URL construction through this layer.
  • Introduced a desktop-only provider/dialog + first-run gate to prompt for/select the backend server.
  • Adjusted desktop build/dev flow (Tauri config hooks) and removed the WKWebView zoom hack in favor of full-height layout constraints.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
interface/src/ui/style/style.scss Makes html/body/#root full-height and hides body overflow to avoid viewport issues on desktop.
interface/src/routes/AgentWorkers.tsx Switches hardcoded /api and embed asset paths to backend-aware URL helpers.
interface/src/main.tsx Removes Tauri-only body.style.zoom hack.
interface/src/lib/backend.ts Adds desktop detection, instance URL storage/normalization, and getApiBaseUrl() / assetUrl() helpers.
interface/src/hooks/useDesktopInstance.tsx Adds React context/provider for desktop instance URL and dialog state.
interface/src/components/TopBar.tsx Adds desktop-only “change server” button showing current host label.
interface/src/components/Sidebar.tsx Removes stray whitespace line (no functional change).
interface/src/components/DesktopInstanceDialog.tsx Adds connect gate (first run) and change-server dialog UI.
interface/src/components/ConnectionBanner.tsx Desktop-only link to open the change-server dialog when disconnected.
interface/src/api/client.ts Switches BASE_PATH/IS_TAURI/API base to read from backend.ts.
interface/src/App.tsx Wraps app in desktop instance provider and gates router until configured on desktop.
desktop/src-tauri/tauri.conf.json Bundles interface/dist and adds pre-dev / pre-build commands to build/run the frontend.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread interface/src/lib/backend.ts Outdated
Comment thread interface/src/lib/backend.ts
Comment thread interface/src/components/ConnectionBanner.tsx Outdated
Comment thread interface/src/components/ConnectionBanner.tsx Outdated
Comment thread desktop/src-tauri/tauri.conf.json Outdated

@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: 2

🧹 Nitpick comments (1)
interface/src/api/client.ts (1)

3-5: Avoid freezing the backend URL at module import.

API_BASE is captured once here, but interface/src/routes/AgentWorkers.tsx now resolves getApiBaseUrl() at runtime. That splits the app between cached and live backend resolution. A shared accessor would keep every request path on the same source of truth.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@interface/src/api/client.ts` around lines 3 - 5, API_BASE is captured at
module import causing stale backend URLs; replace the frozen const with a
runtime accessor: remove the const API_BASE and export a function (e.g.,
getApiBase or getApiBaseRuntime) that returns getApiBaseUrl() when called, and
update all callers to call that accessor instead of referencing API_BASE; keep
BASE_PATH and IS_TAURI as-is but ensure any code that previously imported
API_BASE now uses the new getApiBase() so backend resolution is consistent at
runtime.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@interface/src/components/DesktopInstanceDialog.tsx`:
- Around line 41-50: The Input in DesktopInstanceDialog (id
"desktop-instance-url") lacks an accessible label and the error text isn't
associated with the field; add a visible <label> or an aria-label on the Input
(e.g., aria-label="Server URL") and mark invalid state with
aria-invalid={!!error}, and give the error <p> an id like
"desktop-instance-url-error" and set Input's aria-describedby to that id when
error exists so screen readers receive both the field name and the error
message.

In `@interface/src/lib/backend.ts`:
- Around line 12-25: normalizeDesktopInstanceUrl currently strips the full path,
search and hash and returns only url.origin which breaks reverse-proxied/subpath
deployments; change the logic in normalizeDesktopInstanceUrl to preserve the
base pathname (but strip trailing slash and clear search/hash), e.g. keep
url.pathname (treat "/" as empty), remove any trailing "/" from the pathname,
then return url.origin + pathname (so a value like "https://host/spacebot"
becomes "https://host/spacebot"); also apply the same change to the other
URL-normalization occurrence mentioned in the review to ensure consistent
behavior.

---

Nitpick comments:
In `@interface/src/api/client.ts`:
- Around line 3-5: API_BASE is captured at module import causing stale backend
URLs; replace the frozen const with a runtime accessor: remove the const
API_BASE and export a function (e.g., getApiBase or getApiBaseRuntime) that
returns getApiBaseUrl() when called, and update all callers to call that
accessor instead of referencing API_BASE; keep BASE_PATH and IS_TAURI as-is but
ensure any code that previously imported API_BASE now uses the new getApiBase()
so backend resolution is consistent at runtime.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e68ec4ef-fa64-456f-baef-48dfd462e095

📥 Commits

Reviewing files that changed from the base of the PR and between ed3aebe and 7c61ebf.

⛔ Files ignored due to path filters (1)
  • desktop/src-tauri/tauri.conf.json is excluded by !**/*.json
📒 Files selected for processing (11)
  • interface/src/App.tsx
  • interface/src/api/client.ts
  • interface/src/components/ConnectionBanner.tsx
  • interface/src/components/DesktopInstanceDialog.tsx
  • interface/src/components/Sidebar.tsx
  • interface/src/components/TopBar.tsx
  • interface/src/hooks/useDesktopInstance.tsx
  • interface/src/lib/backend.ts
  • interface/src/main.tsx
  • interface/src/routes/AgentWorkers.tsx
  • interface/src/ui/style/style.scss
💤 Files with no reviewable changes (2)
  • interface/src/main.tsx
  • interface/src/components/Sidebar.tsx

Comment thread interface/src/components/DesktopInstanceDialog.tsx Outdated
Comment thread interface/src/lib/backend.ts

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

🧹 Nitpick comments (1)
interface/src/components/ConnectionBanner.tsx (1)

25-31: Add an explicit accessible name for the server-change button.

Using only currentLabel as button text can make the action unclear to screen readers.

♿ Optional accessibility tweak
 				{isDesktop ? (
 					<button
 						type="button"
 						onClick={openDialog}
+						aria-label={`Change server (current: ${currentLabel})`}
 						className="shrink-0 text-xs text-current underline underline-offset-4 opacity-90 hover:opacity-100"
 					>
 						{currentLabel}
 					</button>
 				) : null}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@interface/src/components/ConnectionBanner.tsx` around lines 25 - 31, The
button rendering in ConnectionBanner uses only {currentLabel} which can be
ambiguous to screen readers; update the <button> (the one with
onClick={openDialog} and className containing "shrink-0 text-xs...") to include
an explicit accessible name by adding an aria-label (e.g., aria-label={`Change
server, currently ${currentLabel}`}) or by including visually hidden descriptive
text inside the button; ensure the label mentions the action ("change server" or
similar) and references currentLabel so the screen reader hears both the action
and current selection.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@interface/src/components/ConnectionBanner.tsx`:
- Around line 25-31: The button rendering in ConnectionBanner uses only
{currentLabel} which can be ambiguous to screen readers; update the <button>
(the one with onClick={openDialog} and className containing "shrink-0
text-xs...") to include an explicit accessible name by adding an aria-label
(e.g., aria-label={`Change server, currently ${currentLabel}`}) or by including
visually hidden descriptive text inside the button; ensure the label mentions
the action ("change server" or similar) and references currentLabel so the
screen reader hears both the action and current selection.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 679708a9-bed6-4ed6-af94-6172d76b0c29

📥 Commits

Reviewing files that changed from the base of the PR and between 7c61ebf and 8c973cb.

⛔ Files ignored due to path filters (1)
  • desktop/src-tauri/tauri.conf.json is excluded by !**/*.json
📒 Files selected for processing (3)
  • interface/src/components/ConnectionBanner.tsx
  • interface/src/components/DesktopInstanceDialog.tsx
  • interface/src/lib/backend.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • interface/src/components/DesktopInstanceDialog.tsx
  • interface/src/lib/backend.ts

@jamiepine

Copy link
Copy Markdown
Member

Hey @skulldogged — thank you for putting this together. The local frontend bundling, the connection picker, and the accessibility improvements were all well thought out and cleanly implemented.

Since this was opened, the desktop app work landed on main through PRs #387 and #452, which took a similar but broader direction — adding sidecar support, a ConnectionScreen gate, and a useServer hook that covers the same ground as the DesktopInstanceDialog / useDesktopInstance pattern here. The core problem you identified (desktop app needing a configurable backend URL and local frontend bundling) is now solved on main, so this PR would conflict extensively with that existing implementation.

A few things from your PR that directly influenced or improved the final result:

  • The currentLabel (host-only) display pattern for connection status — cleaner than showing full URLs
  • Accessibility improvements on the URL input (aria-invalid, aria-describedby, sr-only label)
  • Using bun run --cwd instead of sh -c in Tauri config for cross-platform reliability

Going to close this out since the functionality is covered, but your contribution is appreciated and parts of it clearly shaped what shipped. Hope to see more PRs from you!

@jamiepine jamiepine closed this Mar 21, 2026
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