Skip to content

feat: Claude session profiles + light UI mode#2

Merged
ivandobskygithub merged 1 commit into
mainfrom
feat/profiles-and-light-theme
May 6, 2026
Merged

feat: Claude session profiles + light UI mode#2
ivandobskygithub merged 1 commit into
mainfrom
feat/profiles-and-light-theme

Conversation

@ivandobskygithub

Copy link
Copy Markdown
Owner

Summary

Adds Claude session profiles — named bundles of env vars (e.g. ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN) merged into a session's pty env at spawn time. Lets you rapidly switch inference backends (Anthropic → DeepSeek → GLM) per session without restarting the app.

Also adds a light UI mode + two light terminal themes.

Stacked on top of #1 (chore/upstream-sync). Merge that first.

Profiles

Storage: <userData>/profiles.json (atomic tmp+rename). Plain JSON — values are either literals or references like \$DEEPSEEK_API_KEY / \${DEEPSEEK_API_KEY} resolved against the host process env at spawn time. Unresolved refs are dropped, not passed through literally — so missing host env vars never silently leak fake secrets into Claude.

Selection precedence at spawn:

  1. sessionOptions.profileId if set ('none' = pass-through, ignore default)
  2. Global default profile (defaultProfileId in profiles.json)
  3. No profile

UI:

  • New-session popover now lists each profile as a launch option (default badge marked)
  • "Manage profiles…" entry opens a modal: list / create / edit / delete / set-default
  • Editor enforces env-name regex, shows live env ref vs literal indicator per value, suggests ANTHROPIC_BASE_URL + ANTHROPIC_AUTH_TOKEN for new profiles
  • Configure dialog gets a profile dropdown (Default / No profile / each saved profile)

Example profile (DeepSeek):

{
  \"id\": \"deepseek\",
  \"name\": \"DeepSeek\",
  \"env\": {
    \"ANTHROPIC_BASE_URL\": \"https://api.deepseek.com/anthropic\",
    \"ANTHROPIC_AUTH_TOKEN\": \"\$DEEPSEEK_API_KEY\",
    \"ANTHROPIC_MODEL\": \"deepseek-chat\"
  }
}

User sets DEEPSEEK_API_KEY once via setx (Windows) / shell rc (mac/linux); profile references it. Switching to GLM or anything else is just a new profile.

Light UI mode

  • Body class theme-light toggled via Global Settings → "Light UI Mode". Persisted in localStorage, applied at app load (avoids dark-flash).
  • Two new terminal themes added: Light and Solarized Light.
  • Sidebar, terminal header, dialogs, popovers, inputs, and profile UI all have light overrides.

Test plan

  • npm test → 145/145 passing (added 14 unit tests for profiles)
  • Create a DeepSeek profile, set as default, launch a session — terminal env should have correct base URL + resolved token
  • Launch with explicit profile via popover → that profile's env wins
  • Launch with "No profile" via Configure dialog → no profile env applied even though default exists
  • Set value to `$NONEXISTENT_VAR` → log shows it dropped, not passed through
  • Toggle Light UI Mode → sidebar / dialogs flip to light; terminal theme picker now offers Light / Solarized Light
  • Restart app → light mode persists

Files

File Change
profiles.js NEW — main process module: load/save/resolve/IPC
test/profiles.test.js NEW — 14 unit tests covering validation, env resolution, persistence
public/profiles-panel.js NEW — profile manager + editor modals
preload.js Expose window.api.profiles.{list,save,delete,setDefault}
main.js Wire profilesModule.init; merge resolved profile env into ptyEnv at session spawn
public/dialogs.js Profile entries in popover + dropdown in Configure dialog
public/settings-panel.js "Light UI Mode" toggle in Global Settings
public/terminal-themes.js Add light + solarizedLight terminal themes
public/style.css Profile UI styling + body.theme-light overrides
public/app.js Apply persisted light mode on load
public/index.html Load profiles-panel.js

🤖 Generated with Claude Code

Profiles are named bundles of env vars merged into a session's pty env at
spawn time. Each value is either a literal string or a reference to a host
env var ($VAR / ${VAR}); unresolved refs are dropped (not passed through
literally) so secrets never leak via missing env.

Use case: rapidly switch the inference backend per session by overriding
ANTHROPIC_BASE_URL + ANTHROPIC_AUTH_TOKEN — e.g. swap Anthropic for DeepSeek
or GLM without restarting the app.

Selection precedence:
  sessionOptions.profileId (per-session override, "none" = pass-through)
    -> global default profile (from profiles.json)
    -> no profile

UI:
- New-session popover lists each profile as a launch option (default tagged)
- "Manage profiles…" entry opens a modal: list / create / edit / delete /
  set default. Editor enforces env-name regex and previews ref vs literal.
- Configure dialog gets a profile dropdown.

Storage: <userData>/profiles.json (atomic tmp+rename). No encryption — values
are literals or refs, not raw secrets.

Light UI mode: body.theme-light CSS overrides for sidebar, header, dialogs,
inputs. Toggled in Global Settings; persisted to localStorage and applied
at app load. Two new terminal themes (Light, Solarized Light).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@ivandobskygithub ivandobskygithub changed the base branch from chore/upstream-sync to main May 6, 2026 08:08
@ivandobskygithub ivandobskygithub merged commit cfc3b3f into main May 6, 2026
@ivandobskygithub ivandobskygithub deleted the feat/profiles-and-light-theme branch May 6, 2026 08:09
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