Skip to content

Scribe writing companion + Parakeet STT (core seams + engine)#32

Open
alichherawalla wants to merge 72 commits into
mainfrom
feat/scribe-writing-companion
Open

Scribe writing companion + Parakeet STT (core seams + engine)#32
alichherawalla wants to merge 72 commits into
mainfrom
feat/scribe-writing-companion

Conversation

@alichherawalla

@alichherawalla alichherawalla commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Scribe + Parakeet — core seams, shell, and transcription engine

Core half of the Scribe writing companion + Parakeet STT. The pro logic lives in off-grid-ai/desktop-pro feat/scribe (#23); this PR is the open-core surface: generic seams, the inert shell, and the transcription engine layer (which sits in core beside whisper, since core RAG uses it too).

What's here

  • Extension seams (no pro logic in core): a composer-editor slot (Scribe's live-squiggle editor swaps in for the chat textarea; free builds get the plain textarea), settings-section registry rendering, and og:chat-seed for "continue in chat".
  • Inert shell: proCatalog scribe entry + proItem('scribe') nav → UpgradeScreen in free builds; the real screen registers from pro.
  • Onboarding: Scribe added to the Pro showcase grid.
  • Transcription engine (core, beside whisper):
    • ParakeetCliTranscription (sherpa-onnx offline transducer) + a whisper-fallback selector.
    • Parakeet models in the catalog (v2 English, v3 multilingual), downloadable via the existing multi-file download manager; whisper's active-model resolution hardened to map catalog id → ggml filename and never pick a Parakeet ONNX file.
    • fetch-parakeet.sh + a release.yml step stage the runtime (gated on SHERPA_ONNX_URL; no-op until pinned, with the 0.0.28-style closure/foreign-dep/minos gates).
  • Entity-quality gate + user-created entities (crm): tight isCreatableEntity at recordObservation, createEntity/linkObservationToEntity, "New" in Entities.

Verified on-device (macOS 26, arm64)

Ran the staged static sherpa-onnx v1.13.3 binary on a real wav — transcribed correctly, RTF ~0.06 (~15× realtime). Real CLI flags confirmed via --help (dropped a bogus --model-type); parseParakeetOutput locked to the verbatim JSON output as a fixture test.

Tests

Test Files  61 passed (61)
Tests  511 passed (511)

tsc (node/web/pro) clean · eslint clean on new code · electron-vite build ok · 9/9 pro E2E green.

Screenshots

Scribe live squiggles
Document review + score
Scribe settings
Parakeet model picker

Video: flows are exercised by the Playwright Electron E2E (headless temp profile) with screenshots captured from those runs; Parakeet transcription verified against the real binary on-device.

To make Parakeet ship in release builds

Pin SHERPA_ONNX_URL (repo var) to the sherpa-onnx v1.13.3 osx-arm64-static asset — CI then stages it; signing is handled by electron-builder (static binary, no extra dylibs). Until then, selecting Parakeet transparently uses whisper.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a Pro “Scribe” writing assistant entry in the sidebar and Pro catalog.
    • Added optional Parakeet speech-to-text support with model catalog download and automatic Whisper fallback.
    • Enhanced the chat composer to support externally seeded text and a pluggable composer editor (fallback to textarea).
  • Bug Fixes
    • Improved transcription engine and active-model selection behavior.
    • Strengthened macOS clipboard restore for both files and images.
  • Documentation
    • Added end-to-end plans for an offline writing assistant and for a system-wide Scribe overlay (including an AX probe approach).

User and others added 28 commits July 4, 2026 16:56
Generic extension points so pro can plug in Scribe without any pro logic in core.

- sectionRegistry: SettingsSection gains title/summary; Settings.tsx now renders all
  registered pro sections (no hardcoded pro bodies)
- slotRegistry: composerEditor slot + editorSlot.ts EditorSlotProps contract
- MemoryChat: renders the registered composer editor (Scribe's AssistedTextarea) when
  present, else the plain textarea — free builds are unaffected

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Inert shell in core: a proCatalog entry (PenNib, outcome-first copy) + proItem('scribe')
nav item. Free builds show the locked upgrade screen; the pro build routes to the real
Scribe screen. Copy follows the brand voice — outcome first, privacy as mechanism.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drives the real built app (OFFGRID_PRO=1): navigates to Scribe, types a mix of a
misspelling and a valid-but-uncommon word, and asserts via DOM enumeration that
"recieve" is underlined while "photosynthesis"/"notes" are not. Screenshot attached.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
How the OSS engines work (reference-only, built our own), the two-engine architecture,
the writing-companion vision, and the phased desktop plan.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extends the Scribe E2E to run the review pass and assert the score + issue cards
render (rule issues with no model). Screenshot attached.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ning loop, style guides

Phases 6-9:
- 6: import a file (txt/md/docx/pdf) -> rewrite/tone transform -> hand off to live edit
- 7: section-level free-text feedback from the rewrite toolbar
- 8: learning loop + a "What Scribe has learned" panel (split writing style into its
     own store; the recordFeedback->distill->styleContext loop already runs)
- 9: style-guide .md files (e.g. brand_voice.md) as the authoritative baseline, above
     learned prefs and RAG, with per-context scoping

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
E2E opens Settings, expands the Scribe section, and asserts the style-guide upload
affordance + "What Scribe has learned" panel render. Screenshot attached.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nshot

E2E opens the Import → transform menu (native picker can't be driven) and asserts its
actions render. Squiggle screenshot refreshed after the alignment fix — underlines now
sit exactly under "recieve" and "in order to".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
MemoryChat listens for an og:chat-seed CustomEvent and prefills the composer, so
Scribe's "Continue in chat" can hand a draft into a real chat thread. Generic seam;
no Scribe/pro coupling in core.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The clipboard search placeholder became "Search content or tags…" (tag-search PR #19)
but the E2E still expected the old copy. Not related to Scribe; just unbreaks the suite.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A second TranscriptionService for higher-accuracy on-device STT via NVIDIA Parakeet.
Wraps a bundled sherpa-onnx offline-transducer CLI + ONNX model (encoder/decoder/
joiner/tokens), resolved from resources like whisper, decoding through the same ffmpeg
16 kHz path. Pure buildParakeetArgs + parseParakeetOutput (JSON or text: line, escape-
safe) are unit-tested. isAvailable() gates on binary+model presence so callers can fall
back to whisper until CI stages the runtime. Whisper stays the default; this is additive.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pickTranscription(engine, services) picks whisper (default) or Parakeet, honoring
Parakeet only when installed and falling back to whisper otherwise, so a missing
Parakeet runtime never breaks transcription. Pure + unit-tested; getTranscription()
wires the real singletons.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
scripts/fetch-parakeet.sh stages the sherpa-onnx offline CLI + a Parakeet ONNX model
into resources/bin/parakeet (bundled via extraResources, signed like the other engine
binaries). Gated on SHERPA_ONNX_URL / PARAKEET_MODEL_URL repo vars — unset = no-op, so
transcription stays on whisper and a release can't break. Includes the same foreign-
dylib gate the llama engine uses.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds an `engine` field to ModelEntry and two Parakeet TDT 0.6B entries (v2 English,
v3 multilingual) as kind:'transcription', engine:'parakeet'. Each is the 4-file
sherpa-onnx transducer set (encoder/decoder/joiner/tokens) with slug-prefixed on-disk
names so multiple Parakeet models coexist in the flat models dir. They appear in the
Models screen and download through the existing multi-file download manager - no
downloader change needed. Rebuilt dist.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
parakeetModel() now resolves the active/downloaded Parakeet catalog model from the
models dir (matchParakeetFiles maps encoder/decoder/joiner/tokens by name; pure +
tested), falling back to the CI-bundled default. whisperModel()/smallWhisperModel()
now only honor the shared active-transcription pick when it's a whisper ggml file, so a
Parakeet ONNX selection can never be handed to whisper. +3 tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Opens Voice settings, switches the engine to Parakeet, and asserts the catalog models
render with a Download action. Element screenshot of the picker attached.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…g copy

whisperModel/smallWhisperModel now map the active-transcription slot (which stores a
catalog id) to the entry's primary ggml filename, so picking a whisper model actually
pins it (and Parakeet ids still resolve to null - never handed to whisper). Also drops
em dashes from the two new Parakeet catalog descriptions per the brand rule. Rebuilt dist.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…stid

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
parakeetModel() prefers the entry whose id is the active-transcription pick (the slot
stores the id for transcription), not the primary filename. Screenshot refresh from the
e2e run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fetch-parakeet.sh now preserves the sherpa-onnx prebuilt's bin/ + lib/ structure (so
the binary's @rpath finds libonnxruntime + libsherpa-onnx dylibs), copies symlinks into
real files (none in a signed .app), and gates like the llama engine: dependency-closure
(every @rpath/<name> present under lib/), foreign-dep (/opt/homebrew,/usr/local) on the
binary AND every dylib, and a minos log. parakeetBin() resolves bin/sherpa-onnx-offline.
Still gated on SHERPA_ONNX_URL (no-op until pinned); exact layout/flags need on-Mac
verification before it's called live.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a Scribe card to the onboarding Pro grid (PenNib, outcome-first copy, on-device
mechanism). Also fixes fetch-parakeet.sh to create its staging dirs only on the real
path, not on the skip no-op.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n-Mac)

Verified against the real sherpa-onnx-offline v1.13.3 --help on macOS arm64:
- buildParakeetArgs drops the bogus --model-type (sherpa infers transducer from the
  encoder/decoder/joiner trio) and adds --decoding-method=greedy_search; wav stays last.
- fetch-parakeet.sh: tar -xf (handles the .tar.bz2 release), model URL now optional
  (the in-app picker downloads models at runtime), and it stages ONLY sherpa-onnx-offline
  (the archive ships ~40 executables) - the static osx-arm64 build is self-contained
  (system libs only), so no dylibs to stage.
Tests updated (13): assert --decoding-method + guard against --model-type.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ran the staged sherpa-onnx-offline v1.13.3 on a real wav on macOS arm64 - it transcribed
correctly (RTF ~0.06). Locks in parseParakeetOutput against the verbatim stdout (JSON with
empty lang/emotion/event keys before "text"). Gitignores resources/bin/parakeet/ (CI-staged
like mflux/coreml-sd, not committed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds Parakeet transcription support and engine routing, Scribe overlay tooling and renderer wiring, expanded Pro E2E coverage, and two Writing Assistant planning docs. It also updates release staging, model metadata, and settings formatting.

Changes

Parakeet STT Runtime and Transcription Engine

Layer / File(s) Summary
CI staging and runtime ignores
.github/workflows/release.yml, .gitignore, scripts/fetch-parakeet.sh
Stages the Parakeet runtime/model in macOS release builds and ignores the staged runtime directory.
Model catalog and engine contract
packages/models/src/catalog.ts, packages/models/src/types.ts
Adds engine: 'parakeet' to model entries and inserts Parakeet catalog models with multi-file ONNX metadata.
Parakeet CLI service
src/main/transcription/parakeet-cli.ts, src/main/transcription/__tests__/parakeet-cli.test.ts
Adds Parakeet binary/model resolution, CLI argument building, stdout parsing, transcription execution, and unit coverage.
Engine selection and Whisper compatibility
src/main/transcription/select.ts, src/main/transcription/whisper-cli.ts, src/main/ipc.ts, src/main/rag/extractors.ts, src/main/transcription/__tests__/select.test.ts
Routes transcription through the active engine, maps active models to engine choices, and keeps Whisper model lookup from accepting Parakeet selections.

Scribe system-wide overlay tooling

Layer / File(s) Summary
Overlay design document
docs/SCRIBE_SYSTEMWIDE_OVERLAY.md
Describes the inline overlay architecture, AX probing, IPC contract, coordinate mapping, rollout phases, performance constraints, testing, and risks.
Overlay app and build scripts
scripts/build-scribe-overlay.sh, scripts/scribe-overlay/main.swift, scripts/scribe-overlay/run.sh, .gitignore
Adds the Swift overlay helper, its build script, run wrapper, and a local ignore for the generated binary.
Accessibility probe
scripts/scribe-ax-probe/main.swift, scripts/scribe-ax-probe/run.sh
Adds the Swift AX probe and its shell wrapper for checking per-word bounds behavior.

Scribe composer editor and navigation wiring

Layer / File(s) Summary
Composer editor slot contract
src/renderer/src/bootstrap/editorSlot.ts, src/renderer/src/bootstrap/slotRegistry.ts
Defines the composer editor slot contract and registers the composer.editor slot name.
MemoryChat composer integration
src/renderer/src/components/MemoryChat.tsx
Seeds composer text from a window event and renders the registered editor or textarea fallback.
Scribe navigation and catalog entries
src/renderer/src/App.tsx, src/renderer/src/components/Onboarding.tsx, src/renderer/src/components/pro/proCatalog.ts
Adds Scribe navigation, onboarding, and Pro catalog entries.
Settings section registry extension
src/renderer/src/bootstrap/sectionRegistry.ts
Extends settings section metadata with optional title and summary fields.

Pro E2E coverage and local ignores

Layer / File(s) Summary
Pro E2E assertions and local ignores
e2e/pro.spec.ts, .gitignore
Extends the Pro E2E suite with replay, voice, Scribe, and clipboard restore assertions, and adds local ignore rules for the PR target list and overlay binary.

Writing Assistant design documents

Layer / File(s) Summary
Writing Assistant design documents
docs/WRITING_ASSISTANT_DESKTOP_IMPLEMENTATION.md, docs/WRITING_ASSISTANT_PLAN.md
Adds two planning documents covering the Writing Assistant architecture, phases, settings, and rollout scope.

Settings.tsx formatting cleanup

Layer / File(s) Summary
Multi-line formatting pass
src/renderer/src/components/Settings.tsx
Reformats imports, props, and JSX across the settings screen without changing behavior.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant ParakeetCliTranscription
  participant ffmpeg
  participant sherpa-onnx-offline
  Caller->>ParakeetCliTranscription: transcribe(path, opts)
  ParakeetCliTranscription->>ffmpeg: convert input to 16kHz mono WAV
  ParakeetCliTranscription->>sherpa-onnx-offline: execFile with CLI args
  sherpa-onnx-offline-->>ParakeetCliTranscription: stdout text
  ParakeetCliTranscription->>ParakeetCliTranscription: parseParakeetOutput
  ParakeetCliTranscription-->>Caller: Transcript
Loading
sequenceDiagram
  participant MemoryChat
  participant getComposerEditor
  participant slotRegistry
  MemoryChat->>getComposerEditor: getComposerEditor()
  getComposerEditor->>slotRegistry: lookup SLOTS.composerEditor
  slotRegistry-->>getComposerEditor: component or undefined
  getComposerEditor-->>MemoryChat: Editor or fallback
  MemoryChat->>MemoryChat: render editor or textarea
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the two main additions: Scribe writing companion seams and Parakeet STT engine support.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/scribe-writing-companion

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add Scribe extension seams and Parakeet STT engine with whisper fallback

✨ Enhancement 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add core extension seams for Scribe (composer editor slot, settings sections, chat seeding).
• Introduce Parakeet CLI transcription engine with availability gating and whisper fallback.
• Add Parakeet models to catalog plus CI staging script/workflow and E2E+unit coverage.
Diagram

graph TD
  renderer["Renderer UI"] --> seams["Scribe seams"] --> pro{{"Pro package"}}
  renderer --> selector["Transcription select"] --> whisper["Whisper CLI"] --> catalog[("Model catalog")]
  selector --> parakeet["Parakeet CLI"] --> catalog
  ci["Release staging"] --> parakeet

  subgraph Legend
    direction LR
    _cmp["Component"] ~~~ _ext{{"External"}} ~~~ _db[("Catalog")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Engine registry (plugin-style) instead of hardcoded selector
  • ➕ Scales to more engines without editing selector code
  • ➕ Lets pro/core add engines symmetrically via registration
  • ➖ More indirection for a two-engine world
  • ➖ Harder to reason about deterministic fallback behavior
2. Native Node addon / library binding for sherpa-onnx instead of CLI
  • ➕ Avoids process spawn/argv parsing; potentially faster and more controllable
  • ➕ Simplifies dependency closure (no separate binary packaging)
  • ➖ Significantly higher build + maintenance burden across macOS targets
  • ➖ More complex release pipeline; higher risk than additive CLI approach
3. Always bundle Parakeet runtime in releases (no optional env gate)
  • ➕ Users can pick Parakeet immediately; fewer runtime states to test
  • ➕ Simpler behavior (no missing-runtime fallback)
  • ➖ Locks in a binary/model choice prematurely
  • ➖ Raises release size and increases chance of shipping a broken third-party binary

Recommendation: Current approach is the best trade-off: keep Whisper as the stable default, add Parakeet as opt-in with a strict availability check, and make CI staging optional via SHERPA_ONNX_URL/PARAKEET_MODEL_URL. This preserves release safety while allowing Parakeet to be validated and later pinned without refactoring the runtime.

Files changed (55) +2468 / -1785

Enhancement (17) +878 / -231
index.jsRegenerate models dist index (adds Parakeet catalog entries) +48/-16

Regenerate models dist index (adds Parakeet catalog entries)

• Updates the generated catalog output to include Parakeet transcription models and related runtime improvements from the rebuild.

packages/models/dist/index.js

index.mjsRegenerate models dist ESM index (adds Parakeet catalog entries) +48/-16

Regenerate models dist ESM index (adds Parakeet catalog entries)

• Updates the generated ESM catalog output to include Parakeet transcription models and related runtime improvements from the rebuild.

packages/models/dist/index.mjs

types.d.mtsRegenerate shared types dist declarations (adds transcription engine) +22/-10

Regenerate shared types dist declarations (adds transcription engine)

• Updates generated shared type declarations to include the new ModelEntry.engine field and associated rebuild output.

packages/models/dist/types.d.mts

types.d.tsRegenerate shared types dist declarations (adds transcription engine) +22/-10

Regenerate shared types dist declarations (adds transcription engine)

• Updates generated shared type declarations to include the new ModelEntry.engine field and associated rebuild output.

packages/models/dist/types.d.ts

catalog.tsAdd Parakeet transcription models to the catalog +38/-1

Add Parakeet transcription models to the catalog

• Adds Parakeet v2 (English) and v3 (multilingual) model entries as multi-file ONNX sets, tagged as engine='parakeet'. Uses slug-prefixed filenames so multiple Parakeet models can coexist in the flat models directory.

packages/models/src/catalog.ts

types.tsExtend ModelEntry with transcription engine metadata +3/-0

Extend ModelEntry with transcription engine metadata

• Adds an optional engine field for transcription models to distinguish whisper vs parakeet in catalog/resolution logic.

packages/models/src/types.ts

parakeet-cli.tsAdd Parakeet CLI transcription engine (sherpa-onnx offline transducer) +221/-0

Add Parakeet CLI transcription engine (sherpa-onnx offline transducer)

• Implements ParakeetCliTranscription with runtime/model discovery, WAV transcoding via bundled ffmpeg, deterministic argv construction, and robust transcript parsing. Resolves downloaded catalog models first, otherwise uses a CI-staged default layout.

src/main/transcription/parakeet-cli.ts

select.tsAdd transcription engine selector with whisper fallback +36/-0

Add transcription engine selector with whisper fallback

• Introduces pickTranscription/getTranscription helpers to honor Parakeet only when installed and otherwise use Whisper. Exposes a small parakeetAvailable helper for UI gating.

src/main/transcription/select.ts

index.tsExpose CRM entity create/link IPC methods to renderer +2/-0

Expose CRM entity create/link IPC methods to renderer

• Adds preload API methods for creating an entity and linking an observation to an entity via ipcRenderer.invoke.

src/preload/index.ts

App.tsxAdd Scribe to the pro nav +1/-0

Add Scribe to the pro nav

• Registers a proItem('scribe') nav entry so free builds route to upgrade and pro builds can provide the real screen.

src/renderer/src/App.tsx

editorSlot.tsDefine composer editor slot contract for Scribe AssistedTextarea +20/-0

Define composer editor slot contract for Scribe AssistedTextarea

• Adds a core-owned EditorSlotProps interface and a getComposerEditor accessor so pro can replace the chat composer textarea without core importing pro code.

src/renderer/src/bootstrap/editorSlot.ts

sectionRegistry.tsExtend settings section registry with title/summary metadata +11/-7

Extend settings section registry with title/summary metadata

• Adds optional title and summary fields to SettingsSection and retains ordered retrieval, enabling Settings UI to render registered pro sections as first-class cards.

src/renderer/src/bootstrap/sectionRegistry.ts

slotRegistry.tsAdd composerEditor slot key for editor replacement +6/-0

Add composerEditor slot key for editor replacement

• Introduces a new slot id for a replacement chat composer editor component, used by pro-side Scribe while keeping core behavior unchanged when unregistered.

src/renderer/src/bootstrap/slotRegistry.ts

MemoryChat.tsxSupport chat seeding and optional pro composer editor +40/-12

Support chat seeding and optional pro composer editor

• Adds an og:chat-seed event listener to prefill the composer from other surfaces. Uses the composerEditor slot when registered; otherwise falls back to the native textarea.

src/renderer/src/components/MemoryChat.tsx

Onboarding.tsxAdd Scribe to the Pro showcase grid +2/-0

Add Scribe to the Pro showcase grid

• Extends the onboarding Pro grid to include Scribe with PenNib icon and copy.

src/renderer/src/components/Onboarding.tsx

Settings.tsxRender registered settings sections (pro seam) +344/-159

Render registered settings sections (pro seam)

• Imports getRegisteredSettingsSections and renders each registered section as a SettingsCard with title/summary metadata. Keeps free builds inert by rendering nothing when no sections are registered.

src/renderer/src/components/Settings.tsx

proCatalog.tsAdd Scribe to the Pro feature catalog +14/-0

Add Scribe to the Pro feature catalog

• Adds a Scribe entry (route, icon, tagline, description, and highlights) so the free build surfaces the locked feature and pro can provide the actual screen.

src/renderer/src/components/pro/proCatalog.ts

Bug fix (1) +20 / -3
whisper-cli.tsHarden whisper active-model resolution against Parakeet selections +20/-3

Harden whisper active-model resolution against Parakeet selections

• Maps the active transcription choice from either a ggml filename or a catalog id to a whisper ggml filename, and returns null for Parakeet entries so ONNX files are never passed to Whisper.

src/main/transcription/whisper-cli.ts

Refactor (30) +640 / -1424
node.d.mtsRegenerate models dist typings for node adapter +2/-5

Regenerate models dist typings for node adapter

• Updates generated type declarations to match the rebuilt models package output.

packages/models/dist/adapters/node.d.mts

node.d.tsRegenerate models dist typings for node adapter +2/-5

Regenerate models dist typings for node adapter

• Updates generated type declarations to match the rebuilt models package output.

packages/models/dist/adapters/node.d.ts

node.jsRegenerate node adapter output (optional chaining cleanup) +1/-3

Regenerate node adapter output (optional chaining cleanup)

• Updates generated node adapter runtime to use modern optional chaining and align with rebuilt models artifacts.

packages/models/dist/adapters/node.js

node.mjsRegenerate node adapter ESM output (optional chaining cleanup) +1/-3

Regenerate node adapter ESM output (optional chaining cleanup)

• Updates generated ESM output for the node adapter to match the rebuilt models package artifacts.

packages/models/dist/adapters/node.mjs

catalog.d.mtsRegenerate catalog dist typings +6/-0

Regenerate catalog dist typings

• Updates generated declaration files to reflect new catalog fields/entries (including transcription engine metadata).

packages/models/dist/catalog.d.mts

catalog.d.tsRegenerate catalog dist typings +6/-0

Regenerate catalog dist typings

• Updates generated declaration files to reflect new catalog fields/entries (including transcription engine metadata).

packages/models/dist/catalog.d.ts

credibility.d.mtsRegenerate credibility dist typings +11/-0

Regenerate credibility dist typings

• Updates generated declarations as part of models package rebuild.

packages/models/dist/credibility.d.mts

credibility.d.tsRegenerate credibility dist typings +11/-0

Regenerate credibility dist typings

• Updates generated declarations as part of models package rebuild.

packages/models/dist/credibility.d.ts

download.d.mtsRegenerate download dist typings +13/-0

Regenerate download dist typings

• Updates generated declarations as part of models package rebuild.

packages/models/dist/download.d.mts

download.d.tsRegenerate download dist typings +13/-0

Regenerate download dist typings

• Updates generated declarations as part of models package rebuild.

packages/models/dist/download.d.ts

filters.d.mtsRegenerate filters dist typings +106/-0

Regenerate filters dist typings

• Updates generated declarations as part of models package rebuild.

packages/models/dist/filters.d.mts

filters.d.tsRegenerate filters dist typings +106/-0

Regenerate filters dist typings

• Updates generated declarations as part of models package rebuild.

packages/models/dist/filters.d.ts

hf.d.mtsRegenerate HuggingFace helper dist typings +56/-0

Regenerate HuggingFace helper dist typings

• Updates generated declarations as part of models package rebuild.

packages/models/dist/hf.d.mts

hf.d.tsRegenerate HuggingFace helper dist typings +56/-0

Regenerate HuggingFace helper dist typings

• Updates generated declarations as part of models package rebuild.

packages/models/dist/hf.d.ts

imagegen.d.mtsRegenerate imagegen dist typings +30/-0

Regenerate imagegen dist typings

• Updates generated declarations as part of models package rebuild.

packages/models/dist/imagegen.d.mts

imagegen.d.tsRegenerate imagegen dist typings +30/-0

Regenerate imagegen dist typings

• Updates generated declarations as part of models package rebuild.

packages/models/dist/imagegen.d.ts

index.d.mtsRegenerate models dist index typings (export shape changes) +13/-277

Regenerate models dist index typings (export shape changes)

• Updates generated type exports to reflect the rebuilt models package (including new transcription engine metadata).

packages/models/dist/index.d.mts

index.d.tsRegenerate models dist index typings (export shape changes) +13/-277

Regenerate models dist index typings (export shape changes)

• Updates generated type exports to reflect the rebuilt models package (including new transcription engine metadata).

packages/models/dist/index.d.ts

providers.d.mtsRegenerate providers dist typings +71/-0

Regenerate providers dist typings

• Updates generated declarations as part of models package rebuild.

packages/models/dist/providers.d.mts

providers.d.tsRegenerate providers dist typings +71/-0

Regenerate providers dist typings

• Updates generated declarations as part of models package rebuild.

packages/models/dist/providers.d.ts

quant.d.mtsRegenerate quant dist typings +11/-0

Regenerate quant dist typings

• Updates generated declarations as part of models package rebuild.

packages/models/dist/quant.d.mts

quant.d.tsRegenerate quant dist typings +11/-0

Regenerate quant dist typings

• Updates generated declarations as part of models package rebuild.

packages/models/dist/quant.d.ts

types-C3JiELTS.d.mtsRegenerate hashed types artifact (declaration output changes) +0/-111

Regenerate hashed types artifact (declaration output changes)

• Updates generated declaration artifacts produced by the models build pipeline.

packages/models/dist/types-C3JiELTS.d.mts

types-C3JiELTS.d.tsRegenerate hashed types artifact (declaration output changes) +0/-111

Regenerate hashed types artifact (declaration output changes)

• Updates generated declaration artifacts produced by the models build pipeline.

packages/models/dist/types-C3JiELTS.d.ts

types-Cc7yAzms.d.mtsRegenerate hashed types artifact (declaration output changes) +0/-105

Regenerate hashed types artifact (declaration output changes)

• Updates generated declaration artifacts produced by the models build pipeline.

packages/models/dist/types-Cc7yAzms.d.mts

types-Cc7yAzms.d.tsRegenerate hashed types artifact (declaration output changes) +0/-105

Regenerate hashed types artifact (declaration output changes)

• Updates generated declaration artifacts produced by the models build pipeline.

packages/models/dist/types-Cc7yAzms.d.ts

types-HSQMV1m6.d.mtsRegenerate hashed types artifact (declaration output changes) +0/-108

Regenerate hashed types artifact (declaration output changes)

• Updates generated declaration artifacts produced by the models build pipeline.

packages/models/dist/types-HSQMV1m6.d.mts

types-HSQMV1m6.d.tsRegenerate hashed types artifact (declaration output changes) +0/-108

Regenerate hashed types artifact (declaration output changes)

• Updates generated declaration artifacts produced by the models build pipeline.

packages/models/dist/types-HSQMV1m6.d.ts

types-Yh7XHwfv.d.mtsRegenerate hashed types artifact (declaration output changes) +0/-103

Regenerate hashed types artifact (declaration output changes)

• Updates generated declaration artifacts produced by the models build pipeline.

packages/models/dist/types-Yh7XHwfv.d.mts

types-Yh7XHwfv.d.tsRegenerate hashed types artifact (declaration output changes) +0/-103

Regenerate hashed types artifact (declaration output changes)

• Updates generated declaration artifacts produced by the models build pipeline.

packages/models/dist/types-Yh7XHwfv.d.ts

Tests (3) +377 / -127
pro.spec.tsExpand pro E2E coverage for Scribe and Parakeet model picker +243/-127

Expand pro E2E coverage for Scribe and Parakeet model picker

• Adds Playwright Electron E2E checks for selecting Parakeet in Voice settings and verifying Parakeet catalog models render with download actions. Adds a Scribe flow asserting misspelling underlines while valid uncommon words are not flagged, and captures screenshots.

e2e/pro.spec.ts

parakeet-cli.test.tsUnit tests for Parakeet argv building and stdout parsing +99/-0

Unit tests for Parakeet argv building and stdout parsing

• Adds Vitest coverage for Parakeet CLI arg construction, multi-format stdout transcript parsing, and model-file role matching, including a fixture for real v1.13.3 JSON output.

src/main/transcription/tests/parakeet-cli.test.ts

select.test.tsUnit tests for engine selection + fallback behavior +35/-0

Unit tests for engine selection + fallback behavior

• Adds Vitest coverage ensuring Parakeet is only used when available and otherwise falls back to Whisper, with explicit fellBack signaling.

src/main/transcription/tests/select.test.ts

Documentation (2) +447 / -0
WRITING_ASSISTANT_DESKTOP_IMPLEMENTATION.mdAdd desktop implementation notes for the writing assistant (Scribe) +250/-0

Add desktop implementation notes for the writing assistant (Scribe)

• Introduces detailed implementation documentation for the Scribe writing companion on desktop, including architecture/seams and expected behavior.

docs/WRITING_ASSISTANT_DESKTOP_IMPLEMENTATION.md

WRITING_ASSISTANT_PLAN.mdAdd plan/spec for Scribe writing assistant work +197/-0

Add plan/spec for Scribe writing assistant work

• Adds a planning/spec document outlining the Scribe feature scope, milestones, and design considerations.

docs/WRITING_ASSISTANT_PLAN.md

Other (2) +106 / -0
release.ymlOptionally stage Parakeet runtime during release builds +9/-0

Optionally stage Parakeet runtime during release builds

• Adds a gated release workflow step that runs scripts/fetch-parakeet.sh with SHERPA_ONNX_URL/PARAKEET_MODEL_URL. When unset, the step no-ops so releases continue to rely on Whisper.

.github/workflows/release.yml

fetch-parakeet.shAdd CI stager for sherpa-onnx Parakeet runtime and optional model +97/-0

Add CI stager for sherpa-onnx Parakeet runtime and optional model

• Introduces a script to stage sherpa-onnx-offline (and optionally a default model) into resources/bin/parakeet, with dylib-closure, foreign-dependency, and minos gates. Designed to be additive: absence of URLs keeps releases on Whisper.

scripts/fetch-parakeet.sh

User and others added 29 commits July 5, 2026 18:29
The card panel was at .floating while the overlay window is at .screenSaver, so
the card could be occluded. Raise the panel above the overlay level.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replicates the TextWarden/Harper pattern: rather than trying to suppress the
native spellchecker (not possible for third-party apps + browsers use their own),
integrate with it — skip spelling squiggles for words the user taught macOS via
Learn Spelling (NSSpellChecker.hasLearnedWord), so ours never contradict the OS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…+ OS guesses

CRITICAL: the selection-trick (set caret to measure bounds) makes live typing in
Electron/browser apps jumpy and corrupts input. Never move the caret while the
user types — draw live squiggles only where bounds are non-invasive (native,
direct AXBoundsForRange). Electron/browser are served by the on-demand hotkey
flow instead (a non-invasive text-marker bounds method to restore live squiggles
there is a follow-up).

Also: correction card chips are now custom layer-drawn views (emerald fill + dark
text) since NSButton ignores bezel/tint for rounded buttons; and spelling fixes
use macOS NSSpellChecker guesses (e.g. gance -> glance) instead of the small
bundled wordlist.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Card clicks did nothing: a non-activating panel swallows the first click, so add
acceptsFirstMouse + a mouseDown override on the chip so mouseUp fires. And apply
via select-then-paste (universal) when AXSelectedText doesn't take (Chromium
returns success but doesn't replace). Recolor the card to @offgrid/design tokens
(surface #141414 + border #2A2A2A + surface-light chips), not near-black.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Card now shows a category badge (SPELLING/GRAMMAR/CLARITY, emerald caps) above a
wrapping message for more detail, plus a gear chip that emits an open-settings
action.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Hotkey over a selection shows a native rewrite menu at the selection (bounds read
non-invasively from the live selection markers), emits the chosen action to the
engine, shows a Rewriting card, then pastes the result over the selection. Works
in every app (select+paste), so tone/rephrase/translate reach Slack/browser
without touching the caret. Menu/busy cards are persistent (not hover-dismissed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Every issue is now actionable: when there's no canned fix (passive voice,
wordiness, clarity), the card shows an emerald primary action (Rephrase / Fix
grammar / Make clearer) that selects the span, asks the model to rewrite it, shows
a Rewriting card, then pastes the result. Brand the card with an emerald left
accent stripe + design-token surface so it reads as Off Grid, not a system menu.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Card now mimics the app theme (light/dark palette from @offgrid/design tokens):
  accepts {cmd:theme}, defaults from macOS AppleInterfaceStyle. No longer always dark.
- Fix Rephrase no-op: the result arrives seconds after the LLM call, by which time
  the selection has collapsed, so paste inserted at the cursor instead of replacing.
  Remember the target range (pendingRange) and re-select it before pasting (both the
  hover AI-fix and the rewrite-menu paths).
- rewrite-cancel now shows a brief reason instead of vanishing silently.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Root cause of Rephrase doing nothing: style/clarity issues flag a fragment (e.g.
'was written'), and rewriting two words in isolation returns them unchanged, so
the result was (correctly) rejected as no-change. Expand style/clarity AI-fixes to
the enclosing sentence (sentenceRange), keeping grammar on the flagged span.
Verified on-device: 'The report was written by the team.' -> 'The team wrote the
report.' replaced in TextEdit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… nicer busy card

- ROOT CAUSE of 'nothing in Slack': the overlay never woke Electron/Chromium AX
  trees, so their focused field read as nil. Set AXManualAccessibility (Electron) /
  AXEnhancedUserInterface (browser) once per app pid in focusedTextElement.
- Re-enable live underlines in Slack/browser but ONLY after a real typing pause
  (>=1.2s idle) with a collapsed caret, measuring just the issue spans and restoring
  the caret - so squiggles appear shortly after you stop typing, never during it.
- Busy card: compact emerald-dot + label instead of a big empty slab.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
npm run dev only bundles JS/TS - it never built the native Swift overlay, so
main.swift edits silently didn't take effect without a manual build step. Add a
predev hook that runs build-scribe-overlay.sh, now made incremental (skips when
the binary is newer than the source), macOS/swiftc-gated, and non-fatal (a
compile error warns but never blocks the app). So 'npm run dev' just works.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ation debt)

Audit of the Scribe UI against ../brand/DESIGN_PHILOSOPHY.md plus open functional
items from on-device testing. Tracks hardcoded-hex/light-mode breakage, the
color-coding decision, a11y, Slack verification, and my own verification debt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…merald suggestions)

Match the renderer's G-2 decision - drop the rainbow for the two brand colors:
error red for spelling/grammar/punctuation, emerald for style/clarity/tone/
word-choice.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…phrase pill

Restores the select-to-rewrite entry point system-wide (lost when the hotkey was
removed for the always-on/hover model). When you select a passage (>=12 chars) in
any Scribe-active app, a small emerald 'Rephrase with Scribe' pill floats near the
cursor; clicking it opens the full rewrite menu (Improve/Shorten/Tone/Translate/…).
Own panel (doesn't fight the hover card); non-invasive selection read; gated to
allowed+active apps via the existing underline/clear signal; waits for the drag to
finish so it doesn't flicker.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The affordance floated at the cursor (far from the text). Anchor it to the
selection's bounds (selectionBoundsCocoa) so it sits just below the selected
line - same placement mechanism as the squiggle hover cards. Falls back to the
cursor only when selection bounds aren't available (Chromium).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The existing reduced-motion block only disabled the shooting-star. Add the
standard global rule collapsing all transitions/animations to ~instant so the
scale/slide/fade flourishes don't play for users who opt out (design philosophy
#7). App-wide, not just Scribe.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… status

Write up the Scribe×provit run: NativeMacActor works, failures were foreground
contention (not a Scribe bug), with ranked fixes for the provit agent (re-focus
appTarget per capture, capture-by-windowID/serialize, fix journey red-vs-emerald
copy) + repro steps. Mark G-3 (a11y) and G-4 (reduced-motion) resolved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Rephrase pill fired on the browser address bar (a URL). Now: skip search
fields / omnibox (AXSearchField subrole) entirely, and the affordance only shows
for real prose — isRephrasableProse() requires >=2 words and rejects URLs,
emails, paths, and single tokens.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…element styles)

The md preview WAS rendering (# heading → <h1>) but looked like body text because
Tailwind's reset removes default heading/bold/list styles. Add scoped .scribe-
preview prose CSS (headings, bold, em, lists, code, pre, blockquote, links, hr),
theme-aware via --og-* tokens, monospace to match the brand.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds @tiptap/react + starter-kit + pm + extension-link for a true WYSIWYG rich
editor (user chose a real library over the markdown-toolbar hack). Scoped CSS for
the ProseMirror content (prose styles Tailwind Preflight strips) + the live
squiggle decorations (wavy, brand-colored). Production build verified: TipTap
bundles clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The AXSearchField subrole alone missed Chromium's omnibox (Chrome/Arc/Edge/
Brave expose it as a plain AXTextField with no subrole), so a multi-word URL or
query selected in the address bar got a Rephrase pill. Add isAddressOrSearchField
- subrole OR descriptive metadata (role-description/description/placeholder/title
matching address/search/url markers) - and gate BOTH the squiggle path
(focusedTextElement) and the pill path (updateAffordance) on it. Also reject any
selection whose tokens are bare URLs/domains/emails in isRephrasableProse.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Remove posthog-js dependency. It was declared but never imported/initialized
  anywhere in source (zero usages) - a dangling analytics SDK that has no place
  in an app whose own onboarding promises 'no telemetry'. Gone from package.json
  + lockfile. (The PostHog *connector* in connectorCatalog is unrelated: an
  opt-in MCP integration to the user's OWN PostHog, like Slack/Figma.)
- Onboarding step 2: the Pro grid grew to 9 cards (Scribe added), which overflowed
  behind the fixed Continue/nav footer (button covered the pricing line) and left
  an orphan card in a 4-col grid. Switch to a clean 3x3 (lg:grid-cols-3) and make
  the panel scroll with bottom padding that clears the fixed footer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds jsdom + @testing-library/react/user-event so we can mount the real editor
components and drive real DOM events (select text, click Improve) in vitest.
Component-integration tests are named *.dom.test.tsx and opt into jsdom via a
per-file docblock. This is the gap that let the 'card disappears on Improve' and
'no underline' regressions reach the user - node-only unit tests can't exercise
the render/interaction path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

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