Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
9565b4e
fix: address deferred CodeRabbit findings from PR #297
qnbs Jul 30, 2026
ef8c4a5
docs: Phase 0 re-verification finds Claude split -- two tracks, not one
qnbs Jul 30, 2026
1921d15
docs(adr): add ADR-0016 for native Grok provider + split Claude fix
qnbs Jul 30, 2026
2689f6d
feat(ai): Phase 1 -- native Grok provider wiring
qnbs Jul 30, 2026
c4c516b
feat(ai): Phase 2 Track A -- native Claude support on desktop
qnbs Jul 30, 2026
cb48b31
feat(ai): Phase 2 Track B -- Claude serverless proxy for web/PWA
qnbs Jul 30, 2026
c2df7ee
feat(ai): Addendum -- opt-in browser-Ollama connection for the PWA (A…
qnbs Jul 30, 2026
947ecdf
fix(tokens): avoid token-audit false-positive from #266 issue references
qnbs Jul 30, 2026
744401b
docs: address CodeRabbit quick-win findings on PR #299
qnbs Jul 30, 2026
0c53378
Merge branch 'feat/grok-native-provider' into feat/claude-track-a-des…
qnbs Jul 30, 2026
c7a2fbc
Merge branch 'feat/claude-track-a-desktop' into feat/claude-track-b-w…
qnbs Jul 30, 2026
46ed9b5
Merge branch 'feat/claude-track-b-web-proxy' into feat/ollama-browser…
qnbs Jul 30, 2026
89fb962
Merge remote-tracking branch 'origin/main' into feat/claude-track-b-w…
qnbs Jul 30, 2026
ab1ddd7
fix(docs): restore the '+' suffix scripts/sync-readme-metrics.mjs exp…
qnbs Jul 30, 2026
fde4aab
Merge branch 'feat/claude-track-b-web-proxy' into feat/ollama-browser…
qnbs Jul 30, 2026
4f93f9b
fix(api): bound the Claude proxy rate-limiter's eviction instead of c…
qnbs Jul 30, 2026
98b1bd8
Merge remote-tracking branch 'origin/feat/claude-track-b-web-proxy' i…
qnbs Jul 30, 2026
3db2347
Merge remote-tracking branch 'origin/main' into feat/ollama-browser-o…
qnbs Jul 30, 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
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ types.ts → Core shared interfaces and types

9. **Voice Full Support:** Gated behind `featureFlags.enableVoiceSupport` + `settings.voice.enabled`. Abstract engine pattern in `services/voice/voiceTypes.ts` (SttEngine, TtsEngine, VadEngine, WakeWordEngine, IntentEngine). `VoiceCommandService` singleton manages state machine (idle → listening → processing → speaking). Web Speech API fallbacks require zero downloads. Hooks: `useVoice`, `usePushToTalk` (Ctrl+Shift+V), `useVoiceDictation`.

10. **Feature Flags:** **22 flags** in `features/featureFlags/featureFlagsSlice.ts`. New installs get the **full feature set** — all default **on** except six opt-in flags that default **off**: `enableRtlLayout`, `enableVoiceSupport`, `enableProForge`, `enableVoiceWasm`, `enableGlobalCopilot`, `enableLocalFirstSync`. (`enableCodexAutoTracking` + `enableCrossProjectSearch` were promoted to permanent core; `enablePlotBoardV2` + `enableCloudSync` were retired — none remain in the slice.) See `docs/FEATURE-PARITY.md`. Do not use scattered `if (true)` hacks — all experimental features must go through a flag.
10. **Feature Flags:** **23 flags** in `features/featureFlags/featureFlagsSlice.ts`. New installs get the **full feature set** — all default **on** except seven opt-in flags that default **off**: `enableRtlLayout`, `enableVoiceSupport`, `enableProForge`, `enableVoiceWasm`, `enableGlobalCopilot`, `enableLocalFirstSync`, `enableBrowserOllama`. (`enableCodexAutoTracking` + `enableCrossProjectSearch` were promoted to permanent core; `enablePlotBoardV2` + `enableCloudSync` were retired — none remain in the slice.) See `docs/FEATURE-PARITY.md`. Do not use scattered `if (true)` hacks — all experimental features must go through a flag.

11. **Global AI Copilot (v2):** `enableGlobalCopilot` flag. `CopilotPanel` (dialog/sidebar mode), `CopilotMessageList` (markdown rendering via DOMPurify), `InlineAnnotationLayer` (badge in ManuscriptEditor). Heuristic rules: `services/copilot/heuristicEngine.ts` (8 rules). Apply-to-chapter: `services/copilot/actionApplier.ts` (offset-safe edit, redux-undo, ≥70% length gate). ProForge integration: Ask-Copilot chip on each `ReviewItemCard`. Docs: `docs/COPILOT.md`, `docs/HEURISTIC-RULES.md`.

Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ WorldScript-Studio/
│ ├── status/ # App-wide status / loading flags
│ ├── writer/ # Writer view state
│ ├── versionControl/ # Snapshots and branches
│ ├── featureFlags/ # 22 flags — full set on by default; 6 opt-in (default-off)
│ ├── featureFlags/ # 23 flags — full set on by default; 7 opt-in (default-off)
│ ├── plotBoard/ # Ephemeral viewport/draw state (NOT undo-able; localStorage)
│ ├── progressTracker/ # Writing sessions, streaks, goals
│ ├── sceneComments/ # Per-scene comments (EntityAdapter)
Expand Down Expand Up @@ -387,7 +387,7 @@ Edge builds run `scripts/build-edge.mjs` which sets `DEPLOY_TARGET=edge` and pat

### Feature Flags

- `features/featureFlags/featureFlagsSlice.ts` gates **22 flags**. New installs get the **full feature set**: all default **on** except six opt-in flags that default **off** — `enableRtlLayout`, `enableVoiceSupport`, `enableProForge`, `enableVoiceWasm`, `enableGlobalCopilot`, `enableLocalFirstSync`. (`enableCodexAutoTracking` + `enableCrossProjectSearch` were promoted to permanent core; `enablePlotBoardV2` + `enableCloudSync` were retired — none remain in the slice.) See `docs/FEATURE-PARITY.md` for the per-flag matrix.
- `features/featureFlags/featureFlagsSlice.ts` gates **23 flags**. New installs get the **full feature set**: all default **on** except seven opt-in flags that default **off** — `enableRtlLayout`, `enableVoiceSupport`, `enableProForge`, `enableVoiceWasm`, `enableGlobalCopilot`, `enableLocalFirstSync`, `enableBrowserOllama`. (`enableCodexAutoTracking` + `enableCrossProjectSearch` were promoted to permanent core; `enablePlotBoardV2` + `enableCloudSync` were retired — none remain in the slice.) See `docs/FEATURE-PARITY.md` for the per-flag matrix.
- UI: Settings → Experimental flags.
- Do not use scattered `if (true)` hacks.

Expand Down
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- **Grok (xAI) wired into the primary provider dropdown** — the backend (`streamGrok()`, BYOK key
storage, `/v1/models` connection test) already worked; it just wasn't reachable from
`AiProviderCard.tsx`'s main flow. Added the key input + model selector (`grok-3`/`grok-3-mini`),
a `providerToKind()` case for the newer Vercel-AI-SDK layer, and fixed the whole provider array's
i18n (several labels were hardcoded literals). See [ADR-0016](docs/adr/0016-native-grok-and-claude-providers.md).
- **Claude (Anthropic) now works on desktop** — `streamAnthropic()` threw unconditionally on every
platform; CORS is a browser-only restriction, so desktop now calls Anthropic directly via the
same native-HTTP escape hatch [ADR-0012](docs/adr/0012-local-server-connectivity-tauri-http.md)
established for Ollama. Desktop Settings now shows a real key input + model selector
(Opus 4.7/Sonnet 4.6/Haiku 4.5) instead of a warning-only block.
- **Claude (Anthropic) now works on the web/PWA** (Vercel, Cloudflare Pages) via a new stateless
serverless proxy (`api/claude-proxy.ts` + `functions/api/claude-proxy.ts`) — this app's first
backend dependency ever. Schema-validated, body-size-capped, same-origin-checked, rate-limited,
and timeout-bounded; never logs the API key, prompt, or response. GitHub Pages (static-only,
can't host the proxy) shows an honest "not available on this deployment" state instead of a
silent failure. This is the one provider whose BYOK key transits WorldScript's own infrastructure
in the web build — documented plainly in `docs/SECURITY-THREAT-MODEL.md` and README's privacy
framing, not folded into the general "direct browser→provider" claim every other provider gets.
- **Opt-in direct browser→Ollama connection in the web/PWA build** (`enableBrowserOllama`, default
off, Settings → Experimental) — for users who start their own Ollama server with `OLLAMA_ORIGINS`
covering the page's exact origin, matching NovelCrafter's real (non-proxy) browser-Ollama model.
Not a bypass and not the default: desktop remains the recommended, zero-config path. The Settings
UI renders the exact `OLLAMA_ORIGINS=<origin> ollama serve` command for the current deployment.
See [ADR-0017](docs/adr/0017-pwa-browser-ollama-opt-in.md) (Issue #266 follow-up).

### Fixed

- **Doc-drift gate (`scripts/check-doc-metrics.mjs`) had a blind spot for still-open checklist
Expand Down
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,9 +245,9 @@ Production uses **rolldown** (not esbuild/rollup); CI E2E runs `vite dev` — pr

### Feature Flags

Experimental features are gated behind `features/featureFlags/featureFlagsSlice.ts` (**22 flags**). New installs get the **full feature set**: all flags default **on** except six user-opt-in flags. `enableCodexAutoTracking` + `enableCrossProjectSearch` were promoted to permanent core behaviour (v1.20 / v1.8); `enablePlotBoardV2` and `enableCloudSync` were retired — none of those four remain in the slice. UI: Settings → Experimental flags (`FeatureFlagsSection.tsx`). Do not use scattered `if (true)` hacks.
Experimental features are gated behind `features/featureFlags/featureFlagsSlice.ts` (**23 flags**). New installs get the **full feature set**: all flags default **on** except seven user-opt-in flags. `enableCodexAutoTracking` + `enableCrossProjectSearch` were promoted to permanent core behaviour (v1.20 / v1.8); `enablePlotBoardV2` and `enableCloudSync` were retired — none of those four remain in the slice. UI: Settings → Experimental flags (`FeatureFlagsSection.tsx`). Do not use scattered `if (true)` hacks.

**Default on (16):** `enableStoryBibleAdvanced`, `enableBinderResearch`, `enableCompileWizard`, `enableProjectHealthScore`, `enableAppHealthPanel`, `enableDuckDbAnalytics`, `enableObjectsGroups`, `enableMindMaps`, `enableCharacterInterviews`, `enableLoraAdapters`, `enablePluginSystem`, `enableIdbAtRestEncryption` (B-1, passphrase UX complete — Settings › Privacy), `enableAdaptiveAiEngine`, `enableComputeShaders`, `enableWorkerBusV2`, `enableRustCompute`. **User opt-in — default off (6):** `enableProForge` (experimental, token-heavy 8-stage agentic pipeline — flipped to opt-in in v1.24 post-release), `enableVoiceSupport` (requires browser mic permission), `enableVoiceWasm` (B-2, ~57 MB Whisper download), `enableGlobalCopilot` (ambient AI), `enableRtlLayout` (B-5, ar/he stubs only), `enableLocalFirstSync` (shadow Yjs projection, ADR-0008; Redux stays SoT). The Settings UI groups these by catalog tier; `features/featureCatalog.ts` **derives** each flag's `defaultOn` from the slice (no hand-keyed drift). Note: `enableCloudSync` was **retired** in v1.20 (no UI shipped; `CloudSyncBackend.create()` requires explicit-consent boolean instead).
**Default on (16):** `enableStoryBibleAdvanced`, `enableBinderResearch`, `enableCompileWizard`, `enableProjectHealthScore`, `enableAppHealthPanel`, `enableDuckDbAnalytics`, `enableObjectsGroups`, `enableMindMaps`, `enableCharacterInterviews`, `enableLoraAdapters`, `enablePluginSystem`, `enableIdbAtRestEncryption` (B-1, passphrase UX complete — Settings › Privacy), `enableAdaptiveAiEngine`, `enableComputeShaders`, `enableWorkerBusV2`, `enableRustCompute`. **User opt-in — default off (7):** `enableProForge` (experimental, token-heavy 8-stage agentic pipeline — flipped to opt-in in v1.24 post-release), `enableVoiceSupport` (requires browser mic permission), `enableVoiceWasm` (B-2, ~57 MB Whisper download), `enableGlobalCopilot` (ambient AI), `enableRtlLayout` (B-5, ar/he stubs only), `enableLocalFirstSync` (shadow Yjs projection, ADR-0008; Redux stays SoT), `enableBrowserOllama` (ADR-0017, Issue #266 — direct browser→Ollama fetch in the web/PWA build, only works if the user's own server has OLLAMA_ORIGINS configured for this origin; advanced/unsupported). The Settings UI groups these by catalog tier; `features/featureCatalog.ts` **derives** each flag's `defaultOn` from the slice (no hand-keyed drift). Note: `enableCloudSync` was **retired** in v1.20 (no UI shipped; `CloudSyncBackend.create()` requires explicit-consent boolean instead).

### Command Center & shortcuts

Expand Down Expand Up @@ -336,7 +336,7 @@ All `.md` guides listed in **[`README.md`](README.md#-documentation-hub) § Docu

**useAppSelectorShallow with plotBoard:** Include `plotBoard: { activeMode: 'swimlane', snapToGrid: false, selectedConnectionId: null, isDrawingConnection: false, drawFromSectionId: null, activeSubplotFilter: null, zoom: 1, panX: 0, panY: 0 }` in mock state. Connections/subplots/tensionOverrides are in `project.present.data`. Add `// biome-ignore lint/suspicious/noExplicitAny: test mock` before `(selector: (s: any) => unknown)` lines.

**FeatureFlagsState mocks:** Always include ALL 22 flags (TypeScript strict rejects partial). Only **six** default **off**: `enableProForge`, `enableRtlLayout`, `enableVoiceSupport`, `enableVoiceWasm`, `enableGlobalCopilot`, `enableLocalFirstSync` — every other flag (including all edge-AI flags and `enableIdbAtRestEncryption`) defaults **on**. When a test needs a non-default flag state, set it explicitly in the mock — don't assume the default.
**FeatureFlagsState mocks:** Always include ALL 23 flags (TypeScript strict rejects partial). Only **seven** default **off**: `enableProForge`, `enableRtlLayout`, `enableVoiceSupport`, `enableVoiceWasm`, `enableGlobalCopilot`, `enableLocalFirstSync`, `enableBrowserOllama` — every other flag (including all edge-AI flags and `enableIdbAtRestEncryption`) defaults **on**. When a test needs a non-default flag state, set it explicitly in the mock — don't assume the default.

**ConnectionLayer test IDs:** `data-testid="connection-group"` — query by testid, not role.

Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<img src="https://img.shields.io/badge/Version-v1.24.3-6366F1" alt="v1.24.3">
<img src="https://img.shields.io/badge/Storage-IndexedDB_v8-F59E0B" alt="IndexedDB v8">
<img src="https://img.shields.io/badge/PWA-v3.0-5BB974?logo=pwa" alt="PWA v3.0">
<img src="https://img.shields.io/badge/i18n-19_locales-2857_keys-0EA5E9" alt="i18n 19 locales — 2857 keys">
<img src="https://img.shields.io/badge/i18n-19_locales-2861_keys-0EA5E9" alt="i18n 19 locales — 2861 keys">
<img src="https://img.shields.io/badge/Tests-6477%2B_%2F_532_files-22C55E" alt="6477+ tests / 532 files">
<img src="https://img.shields.io/codecov/c/github/qnbs/WorldScript-Studio?logo=codecov&label=Coverage" alt="Codecov Coverage">
<img src="https://img.shields.io/badge/License-MIT-22C55E" alt="License MIT">
Expand Down Expand Up @@ -396,7 +396,7 @@ Infrastructure-level features that keep the app fast and extensible as projects

### 🌐 Full Multi-Language Support

Shipped UI locales with **2857 i18n keys** across all 19 languages — zero hardcoded user-facing strings:
Shipped UI locales with **2861 i18n keys** across all 19 languages — zero hardcoded user-facing strings:

- 🇩🇪 **German** (Deutsch)
- 🇬🇧 **English**
Expand Down Expand Up @@ -439,7 +439,7 @@ WorldScript Studio supports **9 distinct AI execution paths**, automatically rou
| **Cloud 3** | Anthropic Claude | API key (BYOK) | Claude Opus 4.7, Sonnet 4.6, Haiku 4.5 — desktop (native) and Vercel/Cloudflare Pages (via serverless proxy); unavailable on GitHub Pages, see [privacy note](#-encryption--which-mechanism-protects-what) |
| **Cloud 4** | Grok (xAI) | API key (BYOK) | grok-3, grok-3-mini |
| **Cloud 5** | **OpenRouter** | Free or paid key | Unified gateway: DeepSeek R1, Llama 3.3 70B, Qwen 2.5 72B + 100s more; `:free` suffix = zero cost |
| **Local 1** | Ollama | Local server | Default model: Qwen3 8B; configurable URL |
| **Local 1** | Ollama | Local server | Default model: Qwen3 8B; configurable URL. Desktop app connects natively (no setup). Web/PWA is desktop-only **by default** — an opt-in `enableBrowserOllama` flag (Settings → Experimental, advanced/unsupported) lets the browser connect directly if you start your own server with `OLLAMA_ORIGINS` covering the page's origin, the same real-CORS model NovelCrafter uses; see [ADR-0017](docs/adr/0017-pwa-browser-ollama-opt-in.md). |
| **Local 2** | WebLLM (WebGPU) | GPU + browser | MLC-packaged: Llama 3.2 1B/3B, Phi-3.5 Mini, Gemma 2 2B |
| **Local 3** | ONNX Runtime Web | WASM (any device) | Fallback when no WebGPU; runs fully in-browser |
| **Local 4** | Transformers.js | WASM / WebGPU | Hugging Face–compatible models; WebGPU or WASM backend |
Expand Down Expand Up @@ -505,7 +505,7 @@ The Settings → AI panel shows a live GPU status badge with adapter details and
| **PDF Export** | jsPDF | Client-side, configurable PDF document generation |
| **Document Export** | docx + jszip | Word-compatible `.docx` generation (lazy-loaded) |
| **PWA** | Service Worker + Web App Manifest v3 | Offline support, installability, Workbox chunking |
| **i18n** | Custom React Context (`I18nContext.tsx`) | 2857 keys × 19 locales (de/en/es/fr/it + ar/he/fa RTL Beta + ja/zh/pt/el/fi/sv/hu/is/eu Beta); EN fallback; `localStorage` persistence |
| **i18n** | Custom React Context (`I18nContext.tsx`) | 2861 keys × 19 locales (de/en/es/fr/it + ar/he/fa RTL Beta + ja/zh/pt/el/fi/sv/hu/is/eu Beta); EN fallback; `localStorage` persistence |
| **Testing** | Vitest 4.x (6477+ tests / 532 files) + Playwright E2E | Unit/integration + cross-browser E2E; Stryker mutation (manual workflow) |
| **Code Quality** | Biome (lint + format) + TypeScript 7 (tsgo) strict | `--error-on-warnings` in CI; zero `any` policy |
| **Visualization** | Force-directed graph | Interactive character relationship network |
Expand Down Expand Up @@ -707,7 +707,7 @@ The main pipeline is [`.github/workflows/ci.yml`](.github/workflows/ci.yml). Opt
**Current test metrics (2026-07-30, CI-reported):**
- **6477+ unit tests** across **532 test files** — all passing
- Coverage thresholds: lines ≥ 74 · branches ≥ 60 · functions ≥ 67 · statements ≥ 72 — enforced in CI (see Codecov badge for live metrics)
- i18n: **2857 keys × 19 locales** (en/de/fr/es/it + ar/he/fa RTL Beta + ja/zh/pt/el/fi/sv/hu/is/eu Beta)
- i18n: **2861 keys × 19 locales** (en/de/fr/es/it + ar/he/fa RTL Beta + ja/zh/pt/el/fi/sv/hu/is/eu Beta)

**CI-cloud-first workflow (recommended):** On constrained hardware run **`pnpm run lint && pnpm run i18n:check && pnpm run typecheck`** locally, then push and let CI handle coverage, E2E, Lighthouse, and Stryker. Authoritative numbers come from CI artifacts (Codecov, JUnit). After CI goes green, update the README badges and `AUDIT.md` quality-gate line from the reported metrics. See **[`docs/CI.md`](docs/CI.md) § Cloud CI-first vs local development** for the full post-merge doc-update checklist.

Expand Down
26 changes: 26 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,32 @@ Status: 🔄 in progress | ⬜ open | ✅ done

---

## Native Grok + Claude + Ollama-in-PWA providers (2026-07-30)

README documented Grok and Claude as working cloud AI providers; neither was reachable from
`AiProviderCard.tsx`'s main flow. Full details + rationale in
[ADR-0016](docs/adr/0016-native-grok-and-claude-providers.md) and
[ADR-0017](docs/adr/0017-pwa-browser-ollama-opt-in.md); see `[Unreleased]` in
[CHANGELOG.md](CHANGELOG.md) for the user-facing summary. Execution plan:
[`GROK-PROVIDER-INTEGRATION-PLAN.md`](GROK-PROVIDER-INTEGRATION-PLAN.md).

- ✅ **Phase 1 — Grok**: wired into the primary provider dropdown + `providerFactory.ts`; the
backend (`streamGrok()`) already worked, this was purely a UI/wiring gap.
- ✅ **Phase 2 Track A — Claude on desktop**: `streamAnthropic()` now branches on
`isTauriRuntime()` before throwing; desktop calls Anthropic directly via the native-HTTP pattern
ADR-0012 established for Ollama.
- ✅ **Phase 2 Track B — Claude on web/PWA**: new stateless serverless proxy
(`api/claude-proxy.ts` + `functions/api/claude-proxy.ts`, Vercel + Cloudflare Pages) — this app's
first backend dependency. Mandatory abuse controls (schema validation, body-size cap, origin
check, rate limit, timeouts), never logs secrets. GitHub Pages shows an honest unavailable state.
- ✅ **Addendum — Ollama-in-PWA opt-in (Issue #266 follow-up)**: `enableBrowserOllama` flag
(default off) lets the browser attempt a direct Ollama connection when the user has separately
configured their own server's `OLLAMA_ORIGINS`; not a proxy, not a bypass, not the default.
- ⬜ Open WS-11's PR, run the CodeRabbit correction loop to quiescence, merge to `main`.
- ⬜ Comprehensive tag/release afterward, folding in everything from `[Unreleased]`.

---

## Infra — Vercel deployment failures root-caused + fixed (2026-07-29)

> **Status: Resolved same-day.** Briefly paused via `vercel.json`'s `git.deploymentEnabled: false`
Expand Down
Loading
Loading