| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 0 | 15 | 19 | 0% |
| Commands | 0 | 8 | 9 | 0% |
| Section tags | 3 | 3 | 3 | 33% |
What each file covers
Sections
0 shared · 15 only in A · 19 only in B- − Android Package (Native Android App)
- − Overview
- − Package Structure
- − Key Architecture Rules
- − Transcript
- − Sync and Encryption
- − Persistence
- − Firebase / Notifications
- − Development
- − Prerequisites
- − Commands
- − Play Store screenshots and video
- − Builds, signing, and CI
- − Agent Guidance
- − Important Files
- + Electron Package
- + Development Commands
- + Testing
- + Architecture
- + Main and Renderer Processes
- + IPC Communication
- + Preload API
- + Document Service
- + Common IPC Issues
- + Data Persistence
- + Renderer State Architecture
- + Logging
- + Window State Persistence
- + Theme Support
- + File Operations
- + AI Providers
- + macOS Code Signing & Notarization
- + Git Worktree Integration
- + Analytics
Commands
0 shared · 8 only in A · 9 only in B- − npm run android:build:transcript
- − npm run android:test:unit
- − npm run android:assemble:debug
- − npm run android:assemble:release
- − npm run android:bundle:release
- − npm run android:screenshots
- − npm run android:walkthrough
- − npm run android:bundle:signed
- + npm run dev
- + npm run dev:loop
- + npm run build:mac:local
- + npm run build:mac:notarized
- + npm run dev:user2
- + npm run dev:url-handler
- + npx playwright test e2e/monaco/file-watcher-updates.spec.ts
- + npx playwright test e2e/monaco/
- + npx playwright test
Section tags
3 shared · 3 only in A · 3 only in B- − setup
- − code-style
- − architecture
- + test
- + git-pr
- + api
- build
- do-not
- agent-behaviour
Line diff
nimbalyst/nimbalyst · packages/android/CLAUDE.md
@@ −1 @@
1# Android Package (Native Android App)
2
3This package contains the native Android app for Nimbalyst. It mirrors the iOS native app architecture where practical: a pure native mobile shell with a single embedded web transcript view that renders the shared React transcript bundle.
4
5## Overview
6
7The Android app is:
8
9- **Pure native Android** using Kotlin and Jetpack Compose
10- **Room-backed** for local persistence
11- **WebSocket-synced** with CollabV3 Durable Objects
12- **End-to-end encrypted** using the same seed + user-derived key model as iOS
13- **Transcript-rendered** through a single `WebView` that loads the bundled React transcript UI
14
15Voice agent features are intentionally out of scope for Android.
16
17## Package Structure
18
19```text
20packages/android/
21 app/
22 src/main/java/com/nimbalyst/app/
23 attachments/ # Image attachment preparation/compression
24 auth/ # Auth callback parsing
25 crypto/ # AES-GCM + PBKDF2 key derivation
26 data/ # Room entities, DAOs, repository
27 notifications/ # Android notification + FCM token plumbing
28 pairing/ # QR payload parsing and persistent pairing state
29 sync/ # WebSocket sync manager and wire protocol
30 transcript/ # WebView host and JS bridge
31 ui/ # Compose screens and app shell
32 src/test/ # Unit tests
33 src/transcript/ # Shared React transcript bundle entrypoint/assets
34 scripts/ # Transcript asset sync helpers
35```
36
37## Key Architecture Rules
38
39### Transcript
40
41- The transcript UI lives in `src/transcript/main.tsx` and is bundled into Android assets.
42- `TranscriptWebView.kt` is the Android host. `TranscriptBridge.kt` is the only place JS bridge actions should be decoded and routed.
43- Keep transcript behavior aligned with iOS unless Android-specific UX requires a different path.
44
45### Sync and Encryption
46
47- `SyncManager.kt` owns the device sync lifecycle, room joins, index updates, queued prompt handling, and session control messages.
48- `CryptoManager.kt` must remain wire-compatible with iOS and desktop. Be cautious with any PBKDF2, AES-GCM, or payload format changes.
49- User routing identity and crypto identity are distinct. Do not collapse them back into a single field.
50
51### Persistence
52
53- Room is the source of truth for local Android UI state.
54- Prefer repository/DAO changes over screen-local state duplication.
55- If you add persisted fields, update schema, migrations, and any seed/demo paths together.
56
57### Firebase / Notifications
58
59- `app/google-services.json` is local environment config. Do **not** commit it. The `google-services` Gradle plugin is applied conditionally (only when the file exists), so a build without it stays green and push stays inert.
60- Client push registration lives in `notifications/NotificationManager.kt`.
61- Server push delivery lives in the collab server, which is the sibling `nimbalyst-collab` repository, not this monorepo. Clone it next to this repo at `../nimbalyst-collab` (override with `COLLAB_SERVER_PATH`); collab tests are gated by `RUN_COLLAB_TESTS=1`. See `.github/workflows/ci.yml`. Android push changes usually require coordinated client + server work.
62
63## Development
64
65### Prerequisites
66
67- Android Studio Ladybug / AGP-compatible version for this project
68- JDK 17 for Gradle builds. The project targets `JavaVersion.VERSION_17` and `jvmTarget = "17"`, and Temurin 17 matches CI. A non-17 JDK (e.g. GraalVM) can fail the AGP `jlink` step.
69- Android SDK + emulator tooling
70- Node.js 20+ for transcript bundle builds
71
72### Commands
73
74From the repository root the npm scripts wrap the Gradle tasks:
75
76```bash
77npm run android:build:transcript # build the transcript bundle
78npm run android:test:unit # ./gradlew :app:testDebugUnitTest
79npm run android:assemble:debug # ./gradlew :app:assembleDebug
80npm run android:assemble:release # ./gradlew :app:assembleRelease
81npm run android:bundle:release # ./gradlew :app:bundleRelease
82```
83
84To invoke Gradle directly, point `JAVA_HOME` at a Temurin 17 install (no hard-coded user path):
85
86```bash
87cd packages/android
88JAVA_HOME=/path/to/temurin-17 ./gradlew :app:assembleDebug
89JAVA_HOME=/path/to/temurin-17 ./gradlew :app:testDebugUnitTest
90```
91
92### Play Store screenshots and video
93
94`npm run android:screenshots` and `npm run android:walkthrough` drive an emulator against the debug-only screenshot mode in `app/src/debug/java/com/nimbalyst/app/screenshots/` (inert stub in `app/src/release/`). Never move that code into `src/main` — it seeds demo data and a fake paired state. See [ANDROID_MARKETING_SCREENSHOTS.md](../../docs/ANDROID_MARKETING_SCREENSHOTS.md).
95
96### Builds, signing, and CI
97
98- The `google-services` plugin is applied only when `app/google-services.json` is present, so a build without it succeeds and push stays inert until the file is added.
99- CI can inject Firebase config from the optional `ANDROID_GOOGLE_SERVICES_JSON_BASE64` GitHub secret by decoding it to `app/google-services.json` before the Gradle build.
100- The release `signingConfig` reads the keystore path and credentials from environment variables: `NIMBALYST_ANDROID_KEYSTORE`, `NIMBALYST_ANDROID_KEYSTORE_PASSWORD`, `NIMBALYST_ANDROID_KEY_ALIAS`, `NIMBALYST_ANDROID_KEY_PASSWORD`. When the keystore is absent the release build is simply unsigned. Minification stays off (signed is not the same as minified).
101- CI builds both the APK and Play-ready AAB via `.github/workflows/android-build.yml`, which supplies the keystore and signing secrets to produce signed release artifacts when secrets are present. CI also decodes `google-services.json` from the `ANDROID_GOOGLE_SERVICES_JSON_BASE64` secret and fails a signed build if that secret is missing, so a signed AAB never ships with push silently inert.
102- To build a signed release locally, run `npm run android:bundle:signed` (wraps `scripts/android-bundle-signed.sh`). It pulls all signing secrets from the 1Password item `Nimbalyst Android Signing` (Nimbalyst vault) at build time via `op read`: the upload keystore is fetched to a temp file deleted on exit, and passwords/alias are injected into the Gradle env only. Never commit a keystore — `*.jks`/`*.keystore` are gitignored.
103
104Open `packages/android/` in Android Studio, not the repo root.
105
106## Agent Guidance
107
108- Read the root `CLAUDE.md` before changing this package.
109- Prefer following iOS behavior and naming when implementing cross-platform mobile features.
110- Do not commit secrets or local machine config such as:
111 - `app/google-services.json`
112 - `local.properties`
113 - build outputs
114- If Android Studio reports AGP incompatibility, the correct fix is usually to update Android Studio rather than downgrade AGP/Kotlin.
115- When changing sync protocol behavior, inspect the matching iOS code paths in this repo and the collab server code paths in the sibling `nimbalyst-collab` repository before editing.
116- When changing transcript bridge behavior, update or add Android tests in `app/src/test/` where possible.
117
118## Important Files
119
120| File | Purpose |
121| --- | --- |
122| `app/src/main/java/com/nimbalyst/app/NimbalystApplication.kt` | App-level dependency setup and startup wiring |
123| `app/src/main/java/com/nimbalyst/app/MainActivity.kt` | Activity entry point and deep-link handling |
124| `app/src/main/java/com/nimbalyst/app/ui/NimbalystAndroidApp.kt` | Root Compose app shell and navigation |
125| `app/src/main/java/com/nimbalyst/app/sync/SyncManager.kt` | Core mobile sync lifecycle and message handling |
126| `app/src/main/java/com/nimbalyst/app/sync/SyncProtocol.kt` | Android wire protocol types |
127| `app/src/main/java/com/nimbalyst/app/crypto/CryptoManager.kt` | Encryption and key derivation |
128| `app/src/main/java/com/nimbalyst/app/data/NimbalystDatabase.kt` | Room database definition |
129| `app/src/main/java/com/nimbalyst/app/transcript/TranscriptWebView.kt` | WebView transcript host |
130| `app/src/main/java/com/nimbalyst/app/transcript/TranscriptBridge.kt` | JS/native bridge handler |
131| `src/transcript/main.tsx` | Shared transcript app entry point for Android |
132
nimbalyst/nimbalyst · packages/electron/CLAUDE.md
@@ +1 @@
1# Electron Package
2
3The Nimbalyst desktop app, built with Electron.
4
5## Development Commands
6
7- **Dev server**: `npm run dev` (user runs this — don't do it yourself)
8- **Dev with restart loop**: `npm run dev:loop` (enables restart button / `/restart` command)
9- **Build for Mac**: `npm run build:mac:local` or `npm run build:mac:notarized`
10- **Auth callbacks in dev**: no URL-handler setup is required, including for `npm run dev:user2`. Every sign-in flow uses a nonce-protected one-shot listener on `127.0.0.1` owned by the instance that started it.
11- **Other deep links in dev (macOS)**: `npm run dev:url-handler` (from the repo root) points non-auth `nimbalyst://` links at this checkout. The applet in `scripts/install-dev-url-handler.sh` is not part of authentication. The dev app deliberately does *not* claim the scheme itself because all development copies share Electron's `com.github.Electron` bundle id. See `src/main/utils/protocolRegistration.ts`.
12
13### Testing
14
15From the repository root:
16- Run one spec: `npx playwright test e2e/monaco/file-watcher-updates.spec.ts`
17- Run a directory: `npx playwright test e2e/monaco/`
18- Run all: `npx playwright test`
19
20**Always use `npx playwright test` directly.** Never use parallel execution — it corrupts PGLite. See [/docs/E2E_TESTING.md](/docs/E2E_TESTING.md).
21
22## Architecture
23
24### Main and Renderer Processes
25
26Electron apps split into two contexts:
27- **Main** runs Node.js, manages lifecycle, windows, menus, system interactions.
28- **Renderer** runs in a Chromium context; UI only.
29
30Renderers cannot access Node.js APIs directly — use IPC to request main-process services. For initialization rules (dynamic import in `bootstrap.ts`, lazy init for `app.getPath()` consumers, `safeHandle` / `safeOn`), and cross-platform code patterns, see [MAIN_PROCESS_INIT.md](./MAIN_PROCESS_INIT.md).
31
32## IPC Communication
33
34### Preload API
35- **Location**: `src/preload/index.ts`
36- **Exposed as**: `window.electronAPI` (NOT `window.api`)
37- **Generic methods**: `invoke`, `send`, `on` (returns an unsubscribe closure — there is no `off`, see [/docs/IPC_LISTENERS.md](/docs/IPC_LISTENERS.md))
38- Renderer services use these to talk to main-process services.
39
40### Document Service
41- Main: `ElectronDocumentService` (file scanning, metadata extraction, caching)
42- Renderer: `RendererDocumentService` (facade over IPC)
43- **Metadata**: frontmatter extraction with bounded reads (4KB)
44- **Channels**: `document-service:*`
45
46### Common IPC Issues
47- `window.api undefined` → use `window.electronAPI`
48- Empty responses → check the window has a valid workspace path
49- Service resolution is keyed off workspace path
50
51For deep IPC patterns (`safeHandle`/`safeOn`, error handling, channel structure), see [/docs/IPC_GUIDE.md](/docs/IPC_GUIDE.md).
52
53## Data Persistence
54
55The app runs over **either PGLite (PostgreSQL in WebAssembly) or better-sqlite3** — both backends are active during the in-progress migration. Code must work on either; do not assume one. **Never use `localStorage` in the renderer.** Persist via IPC to main using:
56- **app-settings store** (`src/main/utils/store.ts`) for global app settings
57- **workspace-settings store** for per-project state
58- **AppDatabase** (PGLite or SQLite, selected at init) for complex data (AI sessions, document history, trackers)
59
60The biggest divergence to remember: `data->'key'` returns a parsed object on PGLite but a JSON string on SQLite. For tables, locations, shutdown rules, timestamp handling, and the full list of backend-divergent behaviors, see [DATABASE.md](./DATABASE.md).
61
62## Renderer State Architecture
63
64The renderer uses Jotai for state that crosses component boundaries. Editors use **EditorHost** — a stable service object — for all host communication; content state lives in the editor, not parent components.
65
66| Domain | Atoms | Owner |
67| --- | --- | --- |
68| Theme | `themeAtom` | Global, IPC-synced |
69| Editors | `editorDirtyAtom(key)`, `editorProcessingAtom(key)` | EditorHost writes, Tab reads |
70| Sessions | `sessionUnreadAtom(id)`, `sessionProcessingAtom(id)` | AgenticPanel writes, UI reads |
71| File Tree | `gitStatusAtom`, `expandedDirsAtom` | WorkspaceSidebar writes, FileTree reads |
72| Trackers | `trackerCountsAtom` | TrackerService writes, UI reads |
73
74**Re-render isolation**: parents subscribe to lists of IDs; children subscribe to their own atoms. If you need `React.memo` to prevent re-renders, you have the wrong architecture.
75
76For full patterns, see [/docs/EDITOR_STATE.md](/docs/EDITOR_STATE.md) and [/docs/JOTAI.md](/docs/JOTAI.md).
77
78## Logging
79
80Three log destinations:
81
82- **Main process log**: `~/Library/Application Support/@nimbalyst/electron/logs/main.log` — main-process events, AI, sync, file ops; categories like `(MAIN)`, `(AI)`, `(API)`, `(SYNC)`.
83- **Renderer console log** (dev mode only): `~/Library/Application Support/@nimbalyst/electron/nimbalyst-debug.log` — captured via `webContents.on('console-message')` in `src/main/index.ts`.
84
85Use the agent log access tools (`get_main_process_logs`, `get_renderer_debug_logs`) instead of asking users to paste logs. See [/docs/DEBUGGING_LOGS.md](/docs/DEBUGGING_LOGS.md).
86
87## Window State Persistence
88
89- **Global session state** restores all windows on restart (bounds, focus order, dev tools state).
90- **Per-project state** restores window configuration, open file, AI panel width and collapsed state, draft inputs.
91- **Session continuity** — chat sessions persist across restarts.
92
93## Theme Support
94
95Themes: Light, Dark (#2d2d2d / #1a1a1a / #3a3a3a), Crystal Dark (Tailwind gray scale), Auto.
96
97**Critical rules:**
98- Never hardcode colors in CSS files — use CSS variables.
99- `src/renderer/index.css` is the only place theme colors are defined.
100- Apply themes by setting both the `data-theme` attribute and the CSS class on the root element.
101
102Comprehensive: [THEMING.md](./THEMING.md).
103
104## File Operations
105
106- **Drag-and-drop**: move files/folders in the Project Sidebar; hold Option/Alt to copy.
107- **Context menus**: rename, delete, open in new window.
108- **File watching**: auto-update on disk changes.
109
110## AI Providers
111
112Provider implementations live in `packages/runtime` — see `/packages/runtime/CLAUDE.md`. Electron-only pieces:
113
114- **Renderer panels**: `src/renderer/components/AIModels/panels/ClaudePanel.tsx`, `ClaudeCodePanel.tsx`
115- **Claude Code installer**: `src/renderer/components/AIModels/services/CLIInstaller.ts` (manages local installation of `@anthropic-ai/claude-agent-sdk`)
116
117## macOS Code Signing & Notarization
118
119- **Certificate**: Developer ID Application
120- **Builds**: `npm run build:mac:notarized` (notarized), `build:mac:local` (local testing)
121- **Bundled tools**: ripgrep is signed; JAR files are excluded automatically (can't be notarized)
122- **Entitlements**: hardened runtime with necessary exceptions
123
124## Git Worktree Integration
125
126Nimbalyst creates git worktrees for isolated AI coding sessions. See [/docs/WORKTREES.md](/docs/WORKTREES.md). The `worktrees` table stores metadata; `ai_sessions.worktree_id` links sessions to worktrees. IPC channels: `worktree:create`, `worktree:get-status`, `worktree:delete`, `worktree:list`, `worktree:get`.
127
128## Analytics
129
130See [/docs/ANALYTICS_GUIDE.md](/docs/ANALYTICS_GUIDE.md). **When adding, modifying, or removing PostHog events, update [/docs/POSTHOG_EVENTS.md](/docs/POSTHOG_EVENTS.md).**
131
@@ −1 +1 @@
1−# Android Package (Native Android App)
1+# Electron Package
22
3−This package contains the native Android app for Nimbalyst. It mirrors the iOS native app architecture where practical: a pure native mobile shell with a single embedded web transcript view that renders the shared React transcript bundle.
3+The Nimbalyst desktop app, built with Electron.
44
5−## Overview
5+## Development Commands
66
7−The Android app is:
7+- **Dev server**: `npm run dev` (user runs this — don't do it yourself)
8+- **Dev with restart loop**: `npm run dev:loop` (enables restart button / `/restart` command)
9+- **Build for Mac**: `npm run build:mac:local` or `npm run build:mac:notarized`
10+- **Auth callbacks in dev**: no URL-handler setup is required, including for `npm run dev:user2`. Every sign-in flow uses a nonce-protected one-shot listener on `127.0.0.1` owned by the instance that started it.
11+- **Other deep links in dev (macOS)**: `npm run dev:url-handler` (from the repo root) points non-auth `nimbalyst://` links at this checkout. The applet in `scripts/install-dev-url-handler.sh` is not part of authentication. The dev app deliberately does *not* claim the scheme itself because all development copies share Electron's `com.github.Electron` bundle id. See `src/main/utils/protocolRegistration.ts`.
812
9−- **Pure native Android** using Kotlin and Jetpack Compose
10−- **Room-backed** for local persistence
11−- **WebSocket-synced** with CollabV3 Durable Objects
12−- **End-to-end encrypted** using the same seed + user-derived key model as iOS
13−- **Transcript-rendered** through a single `WebView` that loads the bundled React transcript UI
13+### Testing
1414
15−Voice agent features are intentionally out of scope for Android.
15+From the repository root:
16+- Run one spec: `npx playwright test e2e/monaco/file-watcher-updates.spec.ts`
17+- Run a directory: `npx playwright test e2e/monaco/`
18+- Run all: `npx playwright test`
1619
17−## Package Structure
20+**Always use `npx playwright test` directly.** Never use parallel execution — it corrupts PGLite. See [/docs/E2E_TESTING.md](/docs/E2E_TESTING.md).
1821
19−```text
20−packages/android/
21− app/
22− src/main/java/com/nimbalyst/app/
23− attachments/ # Image attachment preparation/compression
24− auth/ # Auth callback parsing
25− crypto/ # AES-GCM + PBKDF2 key derivation
26− data/ # Room entities, DAOs, repository
27− notifications/ # Android notification + FCM token plumbing
28− pairing/ # QR payload parsing and persistent pairing state
29− sync/ # WebSocket sync manager and wire protocol
30− transcript/ # WebView host and JS bridge
31− ui/ # Compose screens and app shell
32− src/test/ # Unit tests
33− src/transcript/ # Shared React transcript bundle entrypoint/assets
34− scripts/ # Transcript asset sync helpers
35−```
22+## Architecture
3623
37−## Key Architecture Rules
24+### Main and Renderer Processes
3825
39−### Transcript
26+Electron apps split into two contexts:
27+- **Main** runs Node.js, manages lifecycle, windows, menus, system interactions.
28+- **Renderer** runs in a Chromium context; UI only.
4029
41−- The transcript UI lives in `src/transcript/main.tsx` and is bundled into Android assets.
42−- `TranscriptWebView.kt` is the Android host. `TranscriptBridge.kt` is the only place JS bridge actions should be decoded and routed.
43−- Keep transcript behavior aligned with iOS unless Android-specific UX requires a different path.
30+Renderers cannot access Node.js APIs directly — use IPC to request main-process services. For initialization rules (dynamic import in `bootstrap.ts`, lazy init for `app.getPath()` consumers, `safeHandle` / `safeOn`), and cross-platform code patterns, see [MAIN_PROCESS_INIT.md](./MAIN_PROCESS_INIT.md).
4431
45−### Sync and Encryption
32+## IPC Communication
4633
47−- `SyncManager.kt` owns the device sync lifecycle, room joins, index updates, queued prompt handling, and session control messages.
48−- `CryptoManager.kt` must remain wire-compatible with iOS and desktop. Be cautious with any PBKDF2, AES-GCM, or payload format changes.
49−- User routing identity and crypto identity are distinct. Do not collapse them back into a single field.
34+### Preload API
35+- **Location**: `src/preload/index.ts`
36+- **Exposed as**: `window.electronAPI` (NOT `window.api`)
37+- **Generic methods**: `invoke`, `send`, `on` (returns an unsubscribe closure — there is no `off`, see [/docs/IPC_LISTENERS.md](/docs/IPC_LISTENERS.md))
38+- Renderer services use these to talk to main-process services.
5039
51−### Persistence
40+### Document Service
41+- Main: `ElectronDocumentService` (file scanning, metadata extraction, caching)
42+- Renderer: `RendererDocumentService` (facade over IPC)
43+- **Metadata**: frontmatter extraction with bounded reads (4KB)
44+- **Channels**: `document-service:*`
5245
53−- Room is the source of truth for local Android UI state.
54−- Prefer repository/DAO changes over screen-local state duplication.
55−- If you add persisted fields, update schema, migrations, and any seed/demo paths together.
46+### Common IPC Issues
47+- `window.api undefined` → use `window.electronAPI`
48+- Empty responses → check the window has a valid workspace path
49+- Service resolution is keyed off workspace path
5650
57−### Firebase / Notifications
51+For deep IPC patterns (`safeHandle`/`safeOn`, error handling, channel structure), see [/docs/IPC_GUIDE.md](/docs/IPC_GUIDE.md).
5852
59−- `app/google-services.json` is local environment config. Do **not** commit it. The `google-services` Gradle plugin is applied conditionally (only when the file exists), so a build without it stays green and push stays inert.
60−- Client push registration lives in `notifications/NotificationManager.kt`.
61−- Server push delivery lives in the collab server, which is the sibling `nimbalyst-collab` repository, not this monorepo. Clone it next to this repo at `../nimbalyst-collab` (override with `COLLAB_SERVER_PATH`); collab tests are gated by `RUN_COLLAB_TESTS=1`. See `.github/workflows/ci.yml`. Android push changes usually require coordinated client + server work.
53+## Data Persistence
6254
63−## Development
55+The app runs over **either PGLite (PostgreSQL in WebAssembly) or better-sqlite3** — both backends are active during the in-progress migration. Code must work on either; do not assume one. **Never use `localStorage` in the renderer.** Persist via IPC to main using:
56+- **app-settings store** (`src/main/utils/store.ts`) for global app settings
57+- **workspace-settings store** for per-project state
58+- **AppDatabase** (PGLite or SQLite, selected at init) for complex data (AI sessions, document history, trackers)
6459
65−### Prerequisites
60+The biggest divergence to remember: `data->'key'` returns a parsed object on PGLite but a JSON string on SQLite. For tables, locations, shutdown rules, timestamp handling, and the full list of backend-divergent behaviors, see [DATABASE.md](./DATABASE.md).
6661
67−- Android Studio Ladybug / AGP-compatible version for this project
68−- JDK 17 for Gradle builds. The project targets `JavaVersion.VERSION_17` and `jvmTarget = "17"`, and Temurin 17 matches CI. A non-17 JDK (e.g. GraalVM) can fail the AGP `jlink` step.
69−- Android SDK + emulator tooling
70−- Node.js 20+ for transcript bundle builds
62+## Renderer State Architecture
7163
72−### Commands
64+The renderer uses Jotai for state that crosses component boundaries. Editors use **EditorHost** — a stable service object — for all host communication; content state lives in the editor, not parent components.
7365
74−From the repository root the npm scripts wrap the Gradle tasks:
66+| Domain | Atoms | Owner |
67+| --- | --- | --- |
68+| Theme | `themeAtom` | Global, IPC-synced |
69+| Editors | `editorDirtyAtom(key)`, `editorProcessingAtom(key)` | EditorHost writes, Tab reads |
70+| Sessions | `sessionUnreadAtom(id)`, `sessionProcessingAtom(id)` | AgenticPanel writes, UI reads |
71+| File Tree | `gitStatusAtom`, `expandedDirsAtom` | WorkspaceSidebar writes, FileTree reads |
72+| Trackers | `trackerCountsAtom` | TrackerService writes, UI reads |
7573
76−```bash
77−npm run android:build:transcript # build the transcript bundle
78−npm run android:test:unit # ./gradlew :app:testDebugUnitTest
79−npm run android:assemble:debug # ./gradlew :app:assembleDebug
80−npm run android:assemble:release # ./gradlew :app:assembleRelease
81−npm run android:bundle:release # ./gradlew :app:bundleRelease
82−```
74+**Re-render isolation**: parents subscribe to lists of IDs; children subscribe to their own atoms. If you need `React.memo` to prevent re-renders, you have the wrong architecture.
8375
84−To invoke Gradle directly, point `JAVA_HOME` at a Temurin 17 install (no hard-coded user path):
76+For full patterns, see [/docs/EDITOR_STATE.md](/docs/EDITOR_STATE.md) and [/docs/JOTAI.md](/docs/JOTAI.md).
8577
86−```bash
87−cd packages/android
88−JAVA_HOME=/path/to/temurin-17 ./gradlew :app:assembleDebug
89−JAVA_HOME=/path/to/temurin-17 ./gradlew :app:testDebugUnitTest
90−```
78+## Logging
9179
92−### Play Store screenshots and video
80+Three log destinations:
9381
94−`npm run android:screenshots` and `npm run android:walkthrough` drive an emulator against the debug-only screenshot mode in `app/src/debug/java/com/nimbalyst/app/screenshots/` (inert stub in `app/src/release/`). Never move that code into `src/main` — it seeds demo data and a fake paired state. See [ANDROID_MARKETING_SCREENSHOTS.md](../../docs/ANDROID_MARKETING_SCREENSHOTS.md).
82+- **Main process log**: `~/Library/Application Support/@nimbalyst/electron/logs/main.log` — main-process events, AI, sync, file ops; categories like `(MAIN)`, `(AI)`, `(API)`, `(SYNC)`.
83+- **Renderer console log** (dev mode only): `~/Library/Application Support/@nimbalyst/electron/nimbalyst-debug.log` — captured via `webContents.on('console-message')` in `src/main/index.ts`.
9584
96−### Builds, signing, and CI
85+Use the agent log access tools (`get_main_process_logs`, `get_renderer_debug_logs`) instead of asking users to paste logs. See [/docs/DEBUGGING_LOGS.md](/docs/DEBUGGING_LOGS.md).
9786
98−- The `google-services` plugin is applied only when `app/google-services.json` is present, so a build without it succeeds and push stays inert until the file is added.
99−- CI can inject Firebase config from the optional `ANDROID_GOOGLE_SERVICES_JSON_BASE64` GitHub secret by decoding it to `app/google-services.json` before the Gradle build.
100−- The release `signingConfig` reads the keystore path and credentials from environment variables: `NIMBALYST_ANDROID_KEYSTORE`, `NIMBALYST_ANDROID_KEYSTORE_PASSWORD`, `NIMBALYST_ANDROID_KEY_ALIAS`, `NIMBALYST_ANDROID_KEY_PASSWORD`. When the keystore is absent the release build is simply unsigned. Minification stays off (signed is not the same as minified).
101−- CI builds both the APK and Play-ready AAB via `.github/workflows/android-build.yml`, which supplies the keystore and signing secrets to produce signed release artifacts when secrets are present. CI also decodes `google-services.json` from the `ANDROID_GOOGLE_SERVICES_JSON_BASE64` secret and fails a signed build if that secret is missing, so a signed AAB never ships with push silently inert.
102−- To build a signed release locally, run `npm run android:bundle:signed` (wraps `scripts/android-bundle-signed.sh`). It pulls all signing secrets from the 1Password item `Nimbalyst Android Signing` (Nimbalyst vault) at build time via `op read`: the upload keystore is fetched to a temp file deleted on exit, and passwords/alias are injected into the Gradle env only. Never commit a keystore — `*.jks`/`*.keystore` are gitignored.
87+## Window State Persistence
10388
104−Open `packages/android/` in Android Studio, not the repo root.
89+- **Global session state** restores all windows on restart (bounds, focus order, dev tools state).
90+- **Per-project state** restores window configuration, open file, AI panel width and collapsed state, draft inputs.
91+- **Session continuity** — chat sessions persist across restarts.
10592
106−## Agent Guidance
93+## Theme Support
10794
108−- Read the root `CLAUDE.md` before changing this package.
109−- Prefer following iOS behavior and naming when implementing cross-platform mobile features.
110−- Do not commit secrets or local machine config such as:
111− - `app/google-services.json`
112− - `local.properties`
113− - build outputs
114−- If Android Studio reports AGP incompatibility, the correct fix is usually to update Android Studio rather than downgrade AGP/Kotlin.
115−- When changing sync protocol behavior, inspect the matching iOS code paths in this repo and the collab server code paths in the sibling `nimbalyst-collab` repository before editing.
116−- When changing transcript bridge behavior, update or add Android tests in `app/src/test/` where possible.
95+Themes: Light, Dark (#2d2d2d / #1a1a1a / #3a3a3a), Crystal Dark (Tailwind gray scale), Auto.
11796
118−## Important Files
97+**Critical rules:**
98+- Never hardcode colors in CSS files — use CSS variables.
99+- `src/renderer/index.css` is the only place theme colors are defined.
100+- Apply themes by setting both the `data-theme` attribute and the CSS class on the root element.
119101
120−| File | Purpose |
121−| --- | --- |
122−| `app/src/main/java/com/nimbalyst/app/NimbalystApplication.kt` | App-level dependency setup and startup wiring |
123−| `app/src/main/java/com/nimbalyst/app/MainActivity.kt` | Activity entry point and deep-link handling |
124−| `app/src/main/java/com/nimbalyst/app/ui/NimbalystAndroidApp.kt` | Root Compose app shell and navigation |
125−| `app/src/main/java/com/nimbalyst/app/sync/SyncManager.kt` | Core mobile sync lifecycle and message handling |
126−| `app/src/main/java/com/nimbalyst/app/sync/SyncProtocol.kt` | Android wire protocol types |
127−| `app/src/main/java/com/nimbalyst/app/crypto/CryptoManager.kt` | Encryption and key derivation |
128−| `app/src/main/java/com/nimbalyst/app/data/NimbalystDatabase.kt` | Room database definition |
129−| `app/src/main/java/com/nimbalyst/app/transcript/TranscriptWebView.kt` | WebView transcript host |
130−| `app/src/main/java/com/nimbalyst/app/transcript/TranscriptBridge.kt` | JS/native bridge handler |
131−| `src/transcript/main.tsx` | Shared transcript app entry point for Android |
102+Comprehensive: [THEMING.md](./THEMING.md).
103+
104+## File Operations
105+
106+- **Drag-and-drop**: move files/folders in the Project Sidebar; hold Option/Alt to copy.
107+- **Context menus**: rename, delete, open in new window.
108+- **File watching**: auto-update on disk changes.
109+
110+## AI Providers
111+
112+Provider implementations live in `packages/runtime` — see `/packages/runtime/CLAUDE.md`. Electron-only pieces:
113+
114+- **Renderer panels**: `src/renderer/components/AIModels/panels/ClaudePanel.tsx`, `ClaudeCodePanel.tsx`
115+- **Claude Code installer**: `src/renderer/components/AIModels/services/CLIInstaller.ts` (manages local installation of `@anthropic-ai/claude-agent-sdk`)
116+
117+## macOS Code Signing & Notarization
118+
119+- **Certificate**: Developer ID Application
120+- **Builds**: `npm run build:mac:notarized` (notarized), `build:mac:local` (local testing)
121+- **Bundled tools**: ripgrep is signed; JAR files are excluded automatically (can't be notarized)
122+- **Entitlements**: hardened runtime with necessary exceptions
123+
124+## Git Worktree Integration
125+
126+Nimbalyst creates git worktrees for isolated AI coding sessions. See [/docs/WORKTREES.md](/docs/WORKTREES.md). The `worktrees` table stores metadata; `ai_sessions.worktree_id` links sessions to worktrees. IPC channels: `worktree:create`, `worktree:get-status`, `worktree:delete`, `worktree:list`, `worktree:get`.
127+
128+## Analytics
129+
130+See [/docs/ANALYTICS_GUIDE.md](/docs/ANALYTICS_GUIDE.md). **When adding, modifying, or removing PostHog events, update [/docs/POSTHOG_EVENTS.md](/docs/POSTHOG_EVENTS.md).**
132131
