Skip to content

Add Capacitor support with configurable API base URL#2163

Open
gary149 wants to merge 2 commits intomainfrom
claude/capacitor-build-readiness-tSBOx
Open

Add Capacitor support with configurable API base URL#2163
gary149 wants to merge 2 commits intomainfrom
claude/capacitor-build-readiness-tSBOx

Conversation

@gary149
Copy link
Collaborator

@gary149 gary149 commented Mar 4, 2026

Summary

This PR adds Capacitor/native app support by making the API client configurable to work with remote backends. The changes enable the chat-ui SPA to run as a native iOS/Android app via Capacitor while maintaining full compatibility with the existing web deployment.

Key Changes

  • Configurable API base URL: Added PUBLIC_API_BASE_URL environment variable to APIClient.ts and created src/lib/utils/apiBase.ts to centralize API origin configuration. This allows the app to point to a remote backend when running in Capacitor (e.g., https://myserver.com) while defaulting to relative URLs for web deployments.

  • CORS support for Capacitor: Enhanced src/lib/server/hooks/handle.ts to:

    • Handle OPTIONS preflight requests for API routes
    • Accept capacitor:// origins in addition to configured origins
    • Add Access-Control-Allow-Credentials header for cookie-based auth
  • Updated all fetch calls: Systematically updated fetch URLs across the codebase to use apiOrigin prefix:

    • src/lib/APIClient.ts - Main API client
    • src/lib/stores/settings.ts - Settings persistence
    • src/lib/stores/mcpServers.ts - MCP server management
    • src/lib/components/chat/ChatWindow.svelte - Chat operations
    • src/lib/components/chat/ModelSwitch.svelte - Model switching
    • src/lib/components/chat/UrlFetchModal.svelte - URL fetching
    • src/lib/createShareLink.ts - Share link generation
    • src/lib/utils/messageUpdates.ts - Message streaming
    • src/routes/+layout.svelte - Token validation
    • src/routes/+page.svelte - Conversation creation
    • src/routes/conversation/[id]/+page.svelte - Stop generation
    • src/routes/models/[...model]/+page.ts - Model subscription
  • Capacitor configuration: Added capacitor.config.ts with app metadata and build output configuration.

  • Build scripts: Added npm scripts for Capacitor workflow:

    • cap:build - Build static SPA and sync with Capacitor
    • cap:ios - Open iOS project
    • cap:android - Open Android project
  • Mobile viewport improvements:

    • Added viewport-fit=cover to support notch/safe areas
    • Added CSS safe area insets (env(safe-area-inset-*)) to prevent content overlap with notches and home indicators
  • Dependencies: Added @capacitor/core and @capacitor/cli packages.

  • Documentation: Added comprehensive CAPACITOR_READINESS.md assessment documenting:

    • Current readiness status (SPA foundation complete, backend required)
    • Architecture overview (hybrid: Capacitor frontend + remote Node.js backend)
    • Critical blockers and remaining work
    • Browser API compatibility issues
    • Estimated effort (1-2 weeks)

Implementation Details

The solution maintains a hybrid architecture where Capacitor wraps the static SPA frontend, which communicates with a remote Node.js backend over HTTPS. The apiOrigin utility provides a single source of truth for API URL configuration:

// Web: "" (empty string → relative URLs, current behavior)
// Capacitor: "https://myserver.com" (absolute URLs to remote backend)
export const apiOrigin: string = PUBLIC_API_BASE_URL ?? "";

Build-time configuration:

PUBLIC_API_BASE_URL=https://myserver.com ADAPTER=static vite build

The CORS handling in handle.ts now explicitly supports Capacitor's capacitor://localhost origin while maintaining security for production deployments.

https://claude.ai/code/session_01Hkh2feFJ5juwX4h2mAfYba

claude added 2 commits March 2, 2026 19:56
Comprehensive analysis of the chat-ui app's readiness for Capacitor
(iOS/Android) builds. The SPA foundation is solid thanks to the completed
"chat-ui in tauri" milestone, but a remote backend is still required.
Documents blockers, browser API compatibility issues, and remaining work.

https://claude.ai/code/session_01Hkh2feFJ5juwX4h2mAfYba
…ffolding

- Add PUBLIC_API_BASE_URL env var and apiOrigin utility for Capacitor builds
  to route API calls to a configurable remote backend instead of using
  window.location.origin (which returns capacitor://localhost in native)
- Update APIClient.ts and all 12 files with direct fetch() calls to prepend
  apiOrigin (empty string for web, full URL for Capacitor — zero behavior change)
- Add CORS support for capacitor:// origins with OPTIONS preflight handler
  and Access-Control-Allow-Credentials in handle.ts
- Add viewport-fit=cover meta tag and safe-area CSS insets for iOS notch
- Add Capacitor scaffolding: capacitor.config.ts, cap:build/ios/android scripts,
  @capacitor/core and @capacitor/cli dependencies

https://claude.ai/code/session_01Hkh2feFJ5juwX4h2mAfYba
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4f0d6241fb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

model = data.models[0].id;
}
const res = await fetch(`${base}/conversation`, {
const res = await fetch(`${apiOrigin}${base}/conversation`, {

Choose a reason for hiding this comment

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

P1 Badge Avoid routing legacy conversation POSTs cross-origin

With PUBLIC_API_BASE_URL set, this call is now cross-origin (/conversation on the backend origin), but CORS handling in src/lib/server/hooks/handle.ts is still scoped to ${base}/api/* (both preflight and response headers). Because this request is JSON POST, browsers preflight it and then block it without Access-Control-Allow-Origin, so creating a new conversation fails in Capacitor/native deployments.

Useful? React with 👍 / 👎.

const token = page.url.searchParams.get("token");

await fetch(`${base}/api/user/validate-token`, {
await fetch(`${apiOrigin}${base}/api/user/validate-token`, {

Choose a reason for hiding this comment

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

P1 Badge Include credentials on token validation cross-origin call

After adding apiOrigin, this request becomes cross-origin in native builds, but it still uses default fetch credentials (same-origin), so the session cookie is not sent or persisted. The validate-token handler depends on locals.sessionId from that cookie (src/routes/api/user/validate-token/+server.ts), so token validation will fail for Capacitor users even when the token is correct.

Useful? React with 👍 / 👎.

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