Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
2e797dd
test: capture model picker shell
quiet-node Apr 23, 2026
ce2cbc6
feat: persist active local model selection
quiet-node Apr 23, 2026
ad41ac0
hardening: add ollama tags fetch timeout and clarify startup seed
quiet-node Apr 23, 2026
b2dddc9
feat: add frontend model selection state
quiet-node Apr 23, 2026
b1d48d0
test: cover setActiveModel rejection and tighten refresh fallback
quiet-node Apr 23, 2026
936b9a3
feat: add ask bar model picker
quiet-node Apr 23, 2026
c2fb1ba
fix: close model picker popup when disabled and mark active row
quiet-node Apr 23, 2026
204971d
feat: wire model picker through app state
quiet-node Apr 23, 2026
1f72e86
refactor: stabilize model select handler and swallow rejection
quiet-node Apr 23, 2026
013ad21
docs: document local model picker and reformat tests
quiet-node Apr 23, 2026
e213d10
fix: use thuki theme tokens for model picker popup
quiet-node Apr 23, 2026
df16451
fix: expand window for model picker popup and tighten chip icon
quiet-node Apr 24, 2026
522b4e6
refactor: portal-render model picker menu via createPortal
quiet-node Apr 24, 2026
e121c83
feat: show model attribution chip under assistant messages
quiet-node Apr 24, 2026
49807f0
feat: persist model_name per assistant message
quiet-node Apr 24, 2026
f97354a
feat: thread model_name from picker to bubble and back
quiet-node Apr 24, 2026
4adac7f
docs: clarify Message.modelName semantics
quiet-node Apr 24, 2026
e032301
fix: full-width inline model picker list with tooltip trigger
quiet-node Apr 24, 2026
8c0d0c8
fix: reserve window space for picker menu and wire Tooltip
quiet-node Apr 24, 2026
04580a6
style: polish model picker chip styling in WindowControls and ChatBubble
quiet-node Apr 24, 2026
3e29e96
feat(model-picker): extract drawer panel + harden selection pipeline
quiet-node Apr 24, 2026
9def682
chore: post-rebase fixes for main integration
quiet-node Apr 25, 2026
db4b0e2
refactor(config): drop [model] available, use SQLite as active-model SOT
quiet-node Apr 25, 2026
7a3fbd1
feat(onboarding): backend gate for Ollama setup (Phase 3)
quiet-node Apr 25, 2026
b9f28b6
feat(onboarding): ModelCheckStep frontend (Phase 3)
quiet-node Apr 25, 2026
dceb61f
feat(onboarding): redesign ModelCheckStep with rail layout + verified…
quiet-node Apr 25, 2026
7462b82
feat(onboarding): rewrite IntroStep title and subtitle
quiet-node Apr 25, 2026
3a70938
feat(onboarding): read Ollama listen address from runtime config
quiet-node Apr 25, 2026
b11002f
fix(askbar): caret drift on multi-line input
quiet-node Apr 25, 2026
198bfac
feat(model-picker): vision-aware submit gate with capability labels
quiet-node Apr 26, 2026
2624f0a
fix(model-picker): unify Ollama URL source + polish capability UX
quiet-node Apr 26, 2026
4615cdb
fix(ollama): surface real HTTP error + gate single-image vision models
quiet-node Apr 26, 2026
bcc2909
feat(model-picker): gate /think against non-thinking models
quiet-node Apr 26, 2026
4a6db6f
feat(model-picker): add Browse Ollama pill with tooltip
quiet-node Apr 26, 2026
51a9b71
refactor(model-picker): polish Browse Ollama pill
quiet-node Apr 26, 2026
61c3c85
fix(model-picker): tighten Browse Ollama tooltip layout
quiet-node Apr 26, 2026
54dab33
fix(tooltip): keep multiline tooltips directly below the trigger near…
quiet-node Apr 26, 2026
adb36e3
fix(tooltip): hug short multiline content with max-width instead of f…
quiet-node Apr 26, 2026
2692bcc
fix(tooltip): update tooltip body text and adjust width for better al…
quiet-node Apr 26, 2026
4bf1a6f
fix(model-picker): refresh capabilities + models on every picker open
quiet-node Apr 26, 2026
10b291c
fix(askbar): restore slash-command highlight in the input
quiet-node Apr 26, 2026
d0bc747
refactor(model-picker): post-review hardening + cleanup
quiet-node Apr 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ Tests use **Vitest** for the frontend (React/TypeScript with React Testing Libra

**100% code coverage is mandatory.** Any new or modified code — frontend or backend — must maintain 100% coverage across lines, functions, branches, and statements. PRs that drop below 100% coverage will not be merged.

- **Frontend:** Run `bun run test:coverage` and verify all metrics are 100%.
- **Backend:** Run `bun run test:backend:coverage` to enforce 100% line coverage (identical to what CI runs). Functions excluded from coverage with `#[cfg_attr(coverage_nightly, coverage(off))]` must be thin wrappers (Tauri commands, filesystem I/O) whose logic is tested through the functions they delegate to.
**Always run `bun run test:all:coverage` (never the bare `bun run test` / `bun run test:all`).** This single command runs both Vitest with coverage and the cargo llvm-cov gate that CI enforces. If it does not exit cleanly, the task is not done. Functions excluded from coverage with `#[cfg_attr(coverage_nightly, coverage(off))]` must be thin wrappers (Tauri commands, filesystem I/O) whose logic is tested through the functions they delegate to.

## Architecture

Expand Down Expand Up @@ -143,7 +142,7 @@ When extending the system, preserve this contract: **never panic on user input**

After making any code changes and before ending your response, you must:

1. Run `bun run test` — all tests must pass
1. Run `bun run test:all:coverage` — frontend + backend tests must pass AND 100% coverage gate must hold
2. Run `bun run validate-build` — must complete with **zero warnings and zero errors**

Do not consider the task done if either step produces any warnings or errors. Fix all issues first.
Expand All @@ -152,6 +151,16 @@ Do not consider the task done if either step produces any warnings or errors. Fi

Never commit files generated by superpowers skills (design specs, implementation plans, brainstorming docs). These live under `docs/superpowers/` which is gitignored. Do not stage or commit anything under that path.

## GStack Design Tooling Fallback

When invoking GStack design skills (`/design-shotgun`, `/design-html`, `/design-review`, etc.) inside Claude Code on this project: if the design CLI fails because no OpenAI API key is configured (e.g. `setup` not run, `OPENAI_API_KEY` unset, `~/.gstack/openai.json` missing), do not block the user with a setup prompt. Automatically fall back to hand-crafted HTML wireframes that use the real Thuki design tokens read directly from the source files (`src/view/onboarding/PermissionsStep.tsx`, `src/view/onboarding/IntroStep.tsx`, `src/components/`). These wireframes are strictly more accurate to the final UI than image generation because they use the exact CSS values rather than a model's interpretation of them.

Workflow:
1. Read the relevant source files to extract the actual design tokens (colors, spacing, fonts, border radii, gradients, shadows).
2. Write the wireframes as static HTML files in `~/.gstack/projects/quiet-node-thuki/designs/<screen-name>-<date>/` so they live alongside any future image-based mockups.
3. Open the wireframes in the browser via `open file://...` for review.
4. Only mention the missing API key as a one-line aside, not as a blocker. The user can opt back into image generation later.

## Key Design Constraints

- **macOS only** — uses NSPanel, Core Graphics event taps, macOS Control key
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ Most AI tools require accounts, API keys, or subscriptions that bill you per tok

### Step 1: Set Up Your AI Engine

> **Default model:** Thuki ships with [`gemma4:e2b`](https://ollama.com/library/gemma4) by default, an effective 2B parameter edge model from Google. It runs comfortably on most modern Macs with 8 GB of RAM and delivers strong performance on reasoning, coding, and vision tasks. To use a different model, edit `~/Library/Application Support/com.quietnode.thuki/config.toml` and reorder the `[model] available` list so your preferred model is first. See [Configurations](docs/configurations.md) for the full schema.
> **Default model:** Thuki ships with [`gemma4:e2b`](https://ollama.com/library/gemma4) by default, an effective 2B parameter edge model from Google. It runs comfortably on most modern Macs with 8 GB of RAM and delivers strong performance on reasoning, coding, and vision tasks. The ask-bar model picker lists the models currently installed in your local Ollama and lets you switch the active model without leaving the overlay. To change the bootstrap default itself, edit `~/Library/Application Support/com.quietnode.thuki/config.toml` and reorder the `[model] available` list so your preferred model is first. See [Configurations](docs/configurations.md) for the full schema.

Choose one of the two options below to set up your AI engine before installing Thuki.

Expand Down Expand Up @@ -256,7 +256,6 @@ The big leap: from answering questions to taking action.
More flexibility over the model powering Thuki.

- **Native settings panel (⌘,):** a proper macOS preferences window to configure your model, Ollama endpoint, activation shortcut, slash commands, and system prompt. No config files needed.
- **In-app model switching:** swap between any Ollama model from the UI without restarting (the backend already supports multiple models via the `[model] available` list in `config.toml`; the picker UI is next)
- **Multiple provider support:** opt in to OpenAI, Anthropic, or any OpenAI-compatible endpoint as an alternative to local Ollama
- **Custom activation shortcut:** change the double-tap trigger to any key or combo you prefer

Expand Down
29 changes: 18 additions & 11 deletions docs/configurations.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,9 @@ open ~/Library/Application\ Support/com.quietnode.thuki/config.toml

```toml
[model]
# First entry is the ACTIVE model used for all inference.
# Reorder the list to switch models (requires app restart in this release).
# Run `ollama pull <model>` before adding a model you haven't used.
available = ["gemma4:e2b", "gemma4:e4b"]
# Where Thuki finds your local Ollama server. The active model itself is
# selected from the in-app picker (which lists whatever is installed in
# Ollama via /api/tags) and is stored in Thuki's local database, not here.
ollama_url = "http://127.0.0.1:11434"

[prompt]
Expand Down Expand Up @@ -81,16 +80,24 @@ Every domain below is shown as a single table that lists **all** constants Thuki

## Reference

### `[LLM models]`
### `[model]`

Which AI model Thuki uses and where to find your local Ollama server.
Where to find your local Ollama server. The active model itself is **not** a TOML setting: Thuki discovers installed models live from Ollama's `/api/tags` endpoint, lets you pick one from the in-app model picker, and stores that selection in its local SQLite database (`app_config` table). Storing the active slug in TOML would duplicate ground truth from Ollama and break the moment you remove a model with `ollama rm`, so it lives next to the conversation history instead.

| Constant | Default | Tunable? | Why not tunable | Bounds | Description |
| :----------- | :------------------------- | :------- | :-------------- | :------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `available` | `["gemma4:e2b"]` | Yes | — | non-empty list | The list of Ollama models Thuki knows about. **The first model in the list is the one Thuki actually uses.** To switch models, reorder the list. Make sure to run `ollama pull <model>` before adding a new entry here. |
| `ollama_url` | `"http://127.0.0.1:11434"` | Yes | — | non-empty URL | The web address where Thuki finds your local Ollama server. The default works if you run Ollama on this machine with its standard port. Change this only if you moved Ollama to a different port or another machine. |
| Constant | Default | Tunable? | Why not tunable | Bounds | Description |
| :----------- | :------------------------- | :------- | :-------------- | :------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ollama_url` | `"http://127.0.0.1:11434"` | Yes | — | non-empty URL | The web address where Thuki finds your local Ollama server. The default works if you run Ollama on this machine with its standard port. Change this only if you moved Ollama to a different port or another machine. |

If the active model has not been pulled, the next request surfaces a "Model not found" error with the exact `ollama pull <name>` command to run.
If the active model has been removed from Ollama between launches, Thuki silently falls back to the first installed model the next time you open the picker. If no models are installed at all, the next request surfaces a "Model not found" error with the exact `ollama pull <name>` command to run.

The table below also lists the baked-in safety limits that govern Thuki's communication with the Ollama HTTP API. None are tunable.

| Constant | Default | Tunable? | Why not tunable | Bounds | Description |
| :------------------------------------------ | :------- | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DEFAULT_OLLAMA_TAGS_REQUEST_TIMEOUT_SECS` | `5 s` | No | Protocol cap on a hung daemon to keep the UI responsive. A longer timeout would wedge the model picker; a shorter one would false-trigger on a momentarily slow daemon. | — | How long Thuki waits for Ollama's `/api/tags` endpoint to respond before giving up. If Ollama accepts the connection but never replies, this prevents the picker from stalling. |
| `DEFAULT_OLLAMA_SHOW_REQUEST_TIMEOUT_SECS` | `5 s` | No | Protocol cap on a hung daemon to keep the UI responsive. Same rationale as the tags timeout above. | — | How long Thuki waits for Ollama's `/api/show` endpoint to respond before giving up. Used when fetching capability flags (vision, thinking) for each installed model. |
| `MAX_OLLAMA_TAGS_BODY_BYTES` | `4 MiB` | No | Defense-in-depth bound on attacker-controlled response body. A misbehaving or compromised Ollama could otherwise stream an unbounded payload and exhaust memory. | — | The largest `/api/tags` response body Thuki will accept. 4 MiB fits thousands of model entries; anything larger is rejected immediately and the request returns an error. |
| `MAX_OLLAMA_SHOW_BODY_BYTES` | `4 MiB` | No | Defense-in-depth bound on attacker-controlled response body. Same rationale as `MAX_OLLAMA_TAGS_BODY_BYTES`. | — | The largest `/api/show` response body Thuki will accept. Full Modelfiles and parameters can be sizable, but 4 MiB is well above any real model; larger responses are rejected. |

### `[prompt]`

Expand Down
Loading