tandem-editor 0.13.0 → 0.13.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,39 @@ All notable changes to Tandem will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),\
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ## [0.13.5] - 2026-05-29
11
+
12
+ ### Added
13
+
14
+ - **Customizable keyboard shortcuts (ADR-041)** — the ~17 App-level discrete shortcuts (Save, Save As, Settings, command palette, New Scratchpad, Close/Open/Reopen tab, toggle mode/authorship/panels, next/previous annotation, comment on selection, select block) are now user-remappable in Settings → Shortcuts via click-to-record. Remaps layer over the matcher (override-first), so users who don't customize see byte-identical behavior; text-formatting / Tiptap keymaps and family shortcuts (`Ctrl+1..9`, find, accept/dismiss, `?`) stay fixed. Conflict detection blocks a remap onto any shortcut already in use — including the fixed matcher branches it derives live from the matcher itself, so loose branches like `Ctrl+Shift+/` (help), `Ctrl+Alt+F` (find), and `Ctrl+Shift+3` (jump-to-tab) can't be silently shadowed. Overrides are validated on load/merge (junk, non-bindable, fixed-colliding, and duplicate-chord entries are dropped). The Help modal reflects effective bindings.
15
+
16
+ ### Changed
17
+
18
+ - **Design system re-skin — annotation selection (QA follow-up)** — two pre-existing umbrella behaviors (not master re-sync regressions; sibling class to #837/#838/#840). **Empty selection is now a valid resting state.** The lifted `useAnnotationReview` auto-set effect previously force-selected the first pending annotation the instant the active id went null (vestigial bulk-review model — there's no dedicated review mode anymore), so a card was *always* highlighted. It now early-returns on null and only AUTO-ADVANCES when the currently-active annotation stops being live (resolved/deleted), landing on empty after the last one. A document opens with nothing selected. **Three deselect gestures** were added: `Escape`, clicking empty rail background (the annotation list container, via `e.target === e.currentTarget` so card / "resolved"-summary clicks pass through), and clicking editor text that isn't an annotation (new `onClearAnnotation` on `Editor`). Keyboard accept/dismiss falls back to the first pending review target when nothing is selected, via a shared `activeOrFirstPending` helper used by BOTH the `Ctrl+Enter` shortcut and the command-palette accept/dismiss commands (the two had silently diverged — palette no-op'd while the shortcut worked). Escape uses the `e.defaultPrevented` protocol; the reply-thread overlay, selection popup, and Help modal were made to consume Escape (capture-phase + `stopPropagation` for the two window-level ones) so closing them no longer also clears the selection. **Selected-card glow fixed** — `.is-review-target` used `box-shadow: 0 0 0 3px var(--tandem-accent-bg)`, the same color as the card fill, so once the one-shot `tandem-annotation-flash` faded the selected state read as a flat hard-edged stroke. Replaced with a contrasting accent-border edge + a soft accent glow (per the bundle's two-layer focus-ring idiom). Orphaned `getActiveReviewAnn` removed from the review hook. No `src/server/` changes; no annotation data-model, coordinate, or MCP-write paths touched; every testid preserved (`aria-current` is simply absent when nothing is selected). Per-text Claude authorship for wholesale Claude-generated documents is tracked separately as a feature (out of scope here).
19
+ - **Design system re-skin — empty states (3.11 D5, #896)** — `EmptyState.svelte` adopts the bundle's D5 recipe for its two real-triggered states: **A (no document open)** — stacked-docs-with-plus SVG, "Nothing open yet", and an **Open file…** pill wired to the file-open dialog; **C (server unavailable)** — broken-link SVG with a `--tandem-error` X (error conveyed by geometry *and* color, not color alone), "Server unavailable", a **Retry** pill wired to `yjsSync.reconnect()`, and an **Open settings** ghost link wired to the settings modal (bundle's "primary + ghost link, never two primaries"). The headline moves from production's `--tandem-font-serif` 17px to the bundle's **sans 15px/600** — an empty-state headline is UI chrome, not reading content, so it matches the rest of the re-skinned chrome (Bryan's sign-off). The disconnect-debounce `$effect` is preserved verbatim (no prop-in-cleanup hazard), and production's `connected && !claudeActive` Claude/MCP positioning line is kept on state A (carries information the bundle omits). Bundle state B (no production trigger — `EmptyState` only renders with no doc open) and the four extended 44×44 surfaces (no consumers) are intentionally not built; the app-window chrome framing is demo-only. New testids `empty-state-{open-file,retry,open-settings}`; decorative SVGs are `aria-hidden`. No `src/server/` changes, no annotation/coordinate paths. svelte-migration-reviewer CLEAR (pre-code plan pass).
20
+ - **Design system re-skin — annotation conversation (3.3)** — five surfaces in the annotation-card family align to a coherent recipe without touching the shipped-1.5 cardTint taxonomy or any coordinate path (card surface is range-free by design). **`AnnotationCardActions`** lifts every inline-style on the 5 button variants into a scoped `<style>` block with `.aca-btn--accept`/`--reject`/`--ghost`/`--send` classes, gains `:hover`/`:focus-visible` states (especially the `--send` accent-invert) that inline styles can't express, and respects dual-mechanism reduced-motion. **`AnnotationCardHeader`** lifts all 6 inline-style attributes into `.ach-*` classes; the per-variant `badgeBg`/`badgeFg`/`dotColor` tokens stay inline (they vary per variant prop) but the status-color ternary collapses into `class:is-accepted` / `class:is-rejected` directives and the `.ach-edit-btn` gains proper hover/focus states. **`AnnotationCard`** Expand-thread button becomes a `.tandem-expand-thread` pill consistent with the rest of the floating-chrome family; the sacred 6-branch `cardTint` (lines 80–88) is preserved verbatim — derived-spec §3.3's 2-tint anti-pattern stays superseded by 1.5's full-taxonomy tint, locked per Conflict #8. **`CommentThread`** swaps reply-author colors from `--tandem-accent` / `--tandem-fg-muted` to the semantic `--tandem-author-claude` / `--tandem-author-user` tokens (cluster 3.3 decision #6) and adds 4px author dots before each name so the rail family rhymes with the header's 6px dot at a smaller weight. **`ReplyThreadOverlay`** quoted-source block gains `font-family: var(--tandem-font-serif)` + `font-style: italic` (cluster 3.3 decision #7) so the quoted material reads as voiced text, not UI chrome. No `src/server/` changes, no range/coordinate paths touched, no MCP write paths touched, 33+ testids preserved, all `$state`/`$effect`/`$derived`/`bind:this` reactivity surfaces unchanged. crdt + annotation-model + svelte-migration reviewers CLEAR.
21
+ - **Design system re-skin — cowork trio (3.7 PR-C)** — final of three thin PRs for the combined 3.7+3.8 cluster. All three Cowork-integration surfaces lift their const-string `style` declarations + inline `style="..."` attributes into scoped `<style>` blocks with `.cos-*` / `.cs-*` / `.cad-*` classes. **`CoworkOnboardingStep`** drops the two const-string `primaryBtnStyle`/`secondaryBtnStyle`; gains `.cos-btn--primary`/`--ghost` with proper `:hover` and `:disabled` states (inline ternaries couldn't express these). **`CoworkSettings`** drops six const-string style declarations (`sectionLabelStyle`, `helpTextStyle`, `infoBannerStyle`, `errorBannerStyle`, `primaryBtnStyle`, `secondaryBtnStyle`); the `workspaceRowStyle(ws)` runtime computation is intentionally preserved as inline style because the per-row border/bg/fg vary by status family (success/warning/error) and the `STATUS_TOKENS` map is the source of truth — the new `.cs-workspace-row` class provides static display/radius/font-size and the inline overrides handle the family-specific tokens. The `cursor: wait` busy state moves to a `class:is-busy={busy}` directive driven by `$state`. **`CoworkAdminDeclinedModal`** aligns to the cluster-3.2 modal family — backdrop → `color-mix(in srgb, var(--tandem-bg) 70%, transparent)`, dialog → `--tandem-r-5` (error-border preserved as the surface's own identity), with `.cad-btn--primary`/`--ghost`/`--destructive` variants. All 30+ testids preserved across the three files. svelte-migration-reviewer CLEAR. 62/62 cowork unit tests pass.
22
+ - **Design system re-skin — wizard modals (3.7 PR-B)** — second of three thin PRs for the combined 3.7+3.8 cluster. Aligns the three model/integration modals to the cluster-3.2 modal family recipe without touching reactivity, secrets contracts, or testids. **`ModelEditModal`** (the big one — 100% inline-style block prior) hoists every `style="..."` attribute into a scoped `<style>` block with `.mem-*` classes: backdrop swaps to `color-mix(in srgb, var(--tandem-bg) 70%, transparent)`, dialog `--tandem-r-4` → `--tandem-r-5` + 1px border, width gains `min(480px, calc(100vw - 40px))` resilience, close-× adopts the family `.settings-modal-close` shape (28×28, `--tandem-r-2`, fg-subtle → fg + surface-sunk on hover/focus-visible — `:hover`/`:focus-visible` cannot be expressed inline), all fields/inputs/select share a single `.mem-field`/`.mem-input(--mono)` recipe, and the Save/Cancel buttons become `.mem-btn--primary`/`.mem-btn--ghost`. **The reveal-gated API-key contract (#659) is preserved verbatim** — `{#if isEditing && initialEntry?.apiKeyRef && !replacingKey}` still gates the masked-preview branch (`••••••••{existingKeyTail}` + Replace button), and the plaintext `type="password"` `autocomplete="off"` input only renders in `{:else}`. **`FirstRunModelPickerModal`** and **`IntegrationWizardModal`** already had `<style>` blocks; this PR aligns just the modal-family tokens — scrim → `color-mix`, dialog → `--tandem-r-5`, IntegrationWizard gains the 1px border, both close buttons reshape to the family 28×28 pattern, and IntegrationWizard's `.iw-actions button` gains `:hover` + `:focus-visible` states. `type="password"` and `autocomplete="off"` preserved on all three secret inputs; `integration-wizard-keychain-fallback` banner + testid preserved. Every existing testid preserved across all three modals (`model-edit-{modal,cancel,provider,displayname,modelid,apikey,apikey-replace-btn,endpoint,save,advanced}`, `first-run-{model-modal,providers,provider-{anthropic,openai,gemini,local-ollama,local-llamacpp},displayname,modelid,apikey,endpoint,error,save,skip,skip-secondary}`, `integration-wizard{,-close,-step-{detect,pick,secrets,review,saving,done,error},-continue-{detect,pick,secrets},-pick-{kind},-secret-{input,submit}-{id},-keychain-fallback,-save,-apply-result-{id},-done-close}`). All `$state`/`$effect`/`$derived`/`bind:this` reactivity surfaces untouched. Wizard step-indicator markup (per derived-spec §3.7) and the F7 success-flash are deferred to follow-ups — both require new reactive surface and are outside this PR's CSS/markup-only scope. security-reviewer + svelte-migration-reviewer CLEAR.
23
+ - **Design system re-skin — primitives (3.8)** — first of three thin PRs for the combined 3.7+3.8 cluster (split for reviewer focus on the security-sensitive wizard modals). **`CollapsibleSection`** gains a rotating chevron (`›` glyph, 140ms `transform` transition rotating to 90° on `[open]`) consistent with the rest of the floating-chrome family, and lifts its const-string inline styles into a scoped `<style>` block; the reduced-motion guard is dual-mechanism (`prefers-reduced-motion` + `body.tandem-reduce-motion`) parallel to cluster 3.10's authorship-decoration treatment. **`ApplyChangesButton`** lifts its inline conditional-ternary `style="..."` block into a `<style>` block with `.acb-btn` + `is-disabled` / `is-applying` class directives; the info-family token choice (`--tandem-info-bg` / `--tandem-info-fg`) is preserved deliberately — "Apply as Tracked Changes" reads as an informational hand-off to Word, not as a primary commit, so accent tokens are intentionally avoided here. testids preserved (`apply-changes-btn`; CollapsibleSection's `testid` + `${testid}-toggle` passthrough). Public API of both primitives unchanged so consumers (ModelEditModal etc.) rerender on the new internals automatically.
24
+ - **Design system re-skin — foundation (W0)** — hardened the bundle-token CI gate (`scripts/check-semantic-tokens.ts`) ahead of the Phase 3 cluster re-skins: added `#1e1e2e` (the D7 onboarding prototype's dark-swatch stand-in, sourced by cluster 3.11) to `BUNDLE_BLOCKLIST_HEX` with a pinning test, and corrected the now-stale bundle source path in the blocklist docstring. No runtime or token changes.
25
+ - **Design system re-skin — side-rail collapse (R)** — rebuilt the side-rail collapse from a swap (`{#if visible}<rail>{:else}<PeekStrip>`) into an always-mounted dual-layer shell: the full panel and a contextual peek sliver are both mounted, display-toggled by a `collapsed` state, so the rail's instance and scroll position persist across collapse instead of unmounting. The collapsed peek now previews its panel's contents — outline tick-marks (left) and one color-coded dot per annotation (right, keyed to the five-row author/type taxonomy). Collapse is a snap; the width-slide + opacity crossfade between layers is deferred to motion (#798). Testids, focus restoration, and the Alt+Shift+Arrow keyboard model are preserved.
26
+ - **Design system re-skin — banner family (3.1)** — three D2/D3/D4 banners now share the bundle's banner recipe consistently. The shared `tandem-banner.css` gains a `--tandem-shadow-1` lift on the base (per recipe — banners read as elevated above the editor surface) and a `--error` family modifier (parallel to the existing `--info`) so D2 ConnectionBanner can express its error semantics through tokens rather than an opaque override. **D2 ConnectionBanner** switches from `--info` to `--error` (lost connection is an error state, per recipe). **D3 UpdaterBanner** is unchanged (info family, already on shared class). **D4 ReviewOnlyBanner** is refactored from ~50 lines of inline styles into the shared `tandem-banner` class with `--info`; the dismiss control becomes the family's icon-× pattern and the banner gains `role="status"` + `aria-live="polite"` for family parity. CTAs (Retry / Restart-to-install / Convert-to-Markdown) preserved across all three — the recipe's "dismiss-only" anti-pattern is overruled by the bundle-vs-production rule: each CTA carries a recovery action specific to the banner's context that stripping would erase. testids, dismiss-key persistence, and the `Convert to Markdown` flow preserved.
27
+ - **Design system re-skin — batch + bulk (3.4)** — `BatchPromoteBar` adopts the bundle's quieter sticky-bar recipe: the bar surface moves from `--tandem-accent-bg` to `--tandem-surface` + `--tandem-border` + `--tandem-shadow-1` (the accent now reads as the "Send to Claude" CTA only — primary signal moves from the whole bar to the action button it carries), and the "N selected" count drops to `--tandem-text-2xs` / `--tandem-fg-subtle`. testids and the don't-render-when-empty guard preserved; BulkActions is intentionally untouched (it's a confirmation widget, not a sticky promote bar — the recipe's sticky/shadow shape doesn't apply, and its success-bg/error-bg semantic colors carry the accept-vs-reject information that a re-skin must preserve).
28
+ - **Design system re-skin — standalone surfaces (3.11, A9 + D7)** — applied the newly-shipped bundle designs for two of the three 3.11 surfaces. **A9 Highlight Color Picker**: the popover gains a `--tandem-r-4` radius, a top-pointing 1px-bordered caret (twin pseudo-element technique) toward its toggle, and each swatch is restructured from a single bg-colored button into a 24×24 transparent outer with a 20×20 colored inner + an absolute check SVG; hover gives a soft border + 1.12 scale + shadow-1, selected gives a 2px `accent-bg` + 3.5px `accent` glow ring plus the check. testids preserved (`toolbar-highlight-color-toggle`, `toolbar-highlight-color-{yellow|green|blue|pink}`, `color-picker-close`); no API change so the `FormattingBar`/`Toolbar` callsites stay untouched. **D7 Onboarding Tutorial**: the Next/Done button radius shifts from `--tandem-r-2` to `--tandem-r-pill` to match the rest of the floating-chrome family (one-line change; testids, activation gate, and localStorage try-catch preserved). **D5 Empty States deferred** — faithful bundle adoption requires functional CTAs (`Open file…`, `Retry`, `Open settings`) + state restructuring + a serif-vs-sans product call (production's `font-family: serif` headline is a deliberate Tandem-editor echo); filed for a follow-up iteration once consumer needs and design direction settle.
29
+ - **Design system re-skin — Settings tabs (3.6)** — the Settings shell (`SettingsModal.svelte`) already shipped the bundle's sidebar-nav + scrolling-content layout in Phase 1; this cluster cleans up the per-tab content styling against the bundle's `ui_kits/app/Settings.svelte` content recipe without stripping production-specific information. Added two shared `:global` classes to the shell — `.settings-hint` (11px / `--tandem-fg-subtle` / 1.4 line-height) and `.settings-mode-btn(s)` (semantic-radiogroup segmented control with `[aria-checked="true"]`-driven active state) — and replaced inline 10px hint blocks across Collaboration + ClaudeCode tabs and the inline Tandem/Solo mode-button recipe in Collaboration with those classes. Models' provider-group header consumes the existing shared `.settings-section-label` instead of duplicating its inline style. About's data row is monospaced (matching the bundle) and uses the bundle's 14px column gap. No structural changes, no testid changes, no production information removed; Shortcuts is a no-op (already minimal). The bundle's pill-input shape, the About "View Documentation" button promotion to mode-btn styling, and the in-tab top border above About are intentionally NOT adopted — those are decoration calls that touch production's longstanding shape and warrant explicit sign-off.
30
+ - **Design system re-skin — find/replace + mode toggle (3.9)** — two CSS-only re-skins layering the bundle's B4 Find/Replace and A8 ModeToggle recipes onto the existing components. **FindReplaceBar** lifts every inline `style="..."` attribute into a scoped `<style>` block with `.fr-*` classes (recipe-faithful to B4): inputs default to `--tandem-surface-sunk` and lift to `--tandem-surface` on `:focus` (so the bar's content reads as nested into its floating surface), the match-count chip moves to `--tandem-font-mono` (state-not-content), prev/next nav buttons + close-× gain proper `:hover`/`:focus-visible` states that inline styles can't express, and replace buttons gain a quiet hover wash. Scope pills consolidate from inline ternary recipes onto an `.fr-scope-pill.on` class pattern. Production-specific anchoring (bottom-right of the editor pane, rather than the bundle's top-right) is preserved — position is information, not decoration. The 13 `$state` / 4 `$effect` / 2 `$derived` / `bind:this={queryInput}` reactivity surface is untouched, and every testid (`find-replace-bar`, `find-input`, `find-scope-pills`, `find-scope-{doc,tabs}`, `find-match-count`, `find-{prev,next,close}-btn`, `find-{case,word,regex}-toggle`, `replace-input`, `replace-{btn,all-btn}`, `find-cross-doc-results`) is preserved. **ModeToggle** aligns to the bundle's `.a8 .seg` recipe: 2px track padding + 1px border (was 3px no border), font-size 12→11px / weight 600, button padding 4×12→5×14px, active-pill shadow swapped to the `--tandem-shadow-1` token. The A8 sliding-thumb motion is intentionally deferred to motion (#798). DocumentTabs is intentionally untouched — the bundle's `.c7-tab` recipe already lives on `TabItem.svelte` from Phase 1.2/1.13.
31
+ - **Design system re-skin — modals (3.2)** — the three full-screen modals (`HelpModal`, `FileOpenDialog`, `ErrorBoundary`) align to the bundle's modal recipe. Backdrops swap from `rgba(0, 0, 0, 0.x)` to `color-mix(in srgb, var(--tandem-bg) 70%, transparent)` so the wash adapts to theme (warm/dark/light) rather than always reading as black. Dialog shells lift from `--tandem-r-4` to `--tandem-r-5`; `FileOpenDialog` additionally gains the `1px solid var(--tandem-border)` family-defining hairline (already present on `HelpModal`). The close `×` button on both `HelpModal` and `FileOpenDialog` adopts the `.settings-modal-close` shape (28×28 grid, `--tandem-r-2`, `--tandem-fg-subtle` on transparent → `--tandem-fg` on `--tandem-surface-sunk` hover/focus-visible) as a per-component scoped class — `:hover`/`:focus-visible` cannot be expressed inline. `ErrorBoundary` gains a `min-height: 36px` on its `.btn` rule (anti-pattern guard); its inline-error layout intentionally stays as-is — promoting it to a full modal surface is a markup restructure outside this cluster's scope. testids preserved on all three surfaces (`help-modal`, `help-modal-close`, `file-open-dialog`, `file-open-browse`, `error-boundary-recover-btn`, `error-boundary-reload-btn`); reactivity (HelpModal's focus-trap `$effect`, `ErrorBoundary`'s recovery counter + `<svelte:boundary>`) unchanged.
32
+ - **Design system re-skin — annotation decorations (3.10)** — the inline annotation/authorship decorations already follow ADR-026 (character-level `data-tandem-author` attributes with a paragraph-level dominant-author gutter) and use the protected color tokens, so production stays the source of truth for this surface. This cluster closes a latent accessibility gap: the Claude-presence animations (the active-paragraph gutter pulse and the character-cursor blink) were not honored under reduced motion. They are now wrapped for both the OS `prefers-reduced-motion` pref and the in-app `reduceMotion` setting (`body.tandem-reduce-motion`), staying fully visible but static. Locked the gutter-reduction invariant with a unit test: a mixed-author paragraph keeps both per-character tints under a single dominant (majority-chars, user-wins-tie) gutter bar. No token or coordinate-path changes.
33
+ - **Audience & monetization direction recorded (ADR-040)** — documentation now reflects the decided product direction: Tandem targets **individuals** (not institutions), the moat is the **same-canvas / no-copy-paste review experience** backed by **persistent, queryable annotations + the .docx review-record loop**, and monetization is **free during public beta → a one-time paid license at v1.0** with **offline signed-license activation**. Existing beta users will be grandfathered with a free license. Updated `docs/decisions.md` (new ADR-040; ADR-039 reserved for the Agent SDK adapter), `README.md`, `docs/positioning.md`, `docs/roadmap.md` (#394), `docs/security.md`, `docs/workflows.md`, and `docs/user-guide.md`. No code changes — the in-app license-verification, trial gate, and license-checked updater are v1.0 engineering work tracked separately.
34
+
35
+ ### Fixed
36
+
37
+ - **Authorship gutter no longer renders as a full-height bar down the editor** — the per-paragraph authorship thread (#518B) anchored its `position: absolute` `::before` to the wrong element: the intended positioning-context rule used a `.tandem-editor .ProseMirror p` descendant selector, but Tiptap stacks both classes on a single node, so the selector matched nothing and non-empty paragraphs stayed `position: static`. The gutter then anchored to `.tandem-editor` (the ~500px editor body) and stretched the full height instead of spanning its one block. Empty paragraphs masked the bug because `p.is-empty` carries its own `position: relative`, so the bar only appeared once real text was typed. Fixed by correcting the selector to `.tandem-editor p, …h1–h6`; the same latent defect in `.docx` paged layout is corrected too.
38
+ - **Markdown save no longer leaves `\@` escape noise in non-email prose (#850)** — the serializer's #605 un-escape chain now conditionally reverses `\@`→`@` in positions that cannot re-form a GFM email autolink-literal (e.g. `@`-handles, `@` with no host, numeric-only TLDs), while keeping the escape where a `local@domain`-shaped host follows, for canonical output consistent with how `\[`/`\_` are handled. The host guard is deliberately conservative — verified zero false-negatives against the GFM autolink boundary, including the leading-dot host `user@.com`. This is an escape-noise cleanup, not a structural-safety fix: CommonMark un-escapes `\@`→`@` at parse time, so an email-shaped `@` autolinks on the next load regardless of the escape. Follow-up to PR #849.
39
+ - **Orphaned atomic-write temp files are reaped on startup** — a `.tandem-tmp-*` sibling left behind when the process is force-killed (dev restarts, crashes) between `writeFile` and `rename` is now swept from the annotations and sessions dirs at boot if it's over an hour old. Files younger than an hour, and all real store/session files, are never touched.
40
+
8
41
  ## [0.13.0] - 2026-05-25
9
42
 
10
43
  ### Added
@@ -30,6 +63,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
30
63
 
31
64
  ### Changed
32
65
 
66
+ - **Design system re-skin (umbrella `feat/design-system-impl`):**
67
+ - TitleBar + BrandMenu + ModeToggle re-skinned to the bundle's visual language (sub-PR 1.1)
68
+ - Added `--tandem-swatch-light|dark|warm` semantic tokens for the BrandMenu theme-picker chips (fixed-identity colors, `:root` only)
69
+ - FormatBar pill chrome re-skinned to the bundle's recipe — 26px buttons, `--tandem-r-pill` corners, hover/active states moved from inline style to scoped CSS so `:hover` / `:focus-visible` win the cascade (sub-PR 1.2)
70
+ - Authorship toggle moved from TitleBar to FormatBar (sub-PR 1.2). Testid renamed `toolbar-authorship-toggle` → `formatbar-authorship-toggle`. Keyboard shortcut `Ctrl+Alt+A` and command-palette action unchanged. (The standalone toggle was later subsumed into the Decorations control — sub-PR 1.13 below.)
71
+ - Editor body + left outline rail re-skinned (sub-PR 1.3) — h1/h2 type-role tokens with serif display face and tightened tracking, outline-panel header label, active-heading tick, and a 12px edge-collapse rail with a centered grip bar. Active tick is driven by reactive `style=` on the span (a scoped-CSS descendant override failed to settle on first paint). Bundled live-smoke fixes: scroll-spy now measures its threshold from the scroll container rather than the (content-scrolling) ProseMirror element, so the active heading tracks scroll instead of freezing on the first heading; an "End of document" pill adds trailing scroll room so the last heading can be pinned to the top from the outline; the right edge-collapse rail runs full-height to match the left; and the Solo/Tandem ModeToggle track uses `--tandem-surface-sunk` so the active pill stays visible in dark mode.
72
+ - Collapsed-rail peek strip enriched (sub-PR 1.4) — hovering or focusing a peek strip now widens it to 28px and reveals a rotated panel label ("Outline" / "Annotations"). The rail container, body scroll-fade, and edge-collapse grip already matched the bundle (shipped via Wave E + sub-PR 1.3), so no change there. The right rail's Annotations/Chat tabs intentionally keep the production segmented-pill toggle rather than the bundle's underline tabs (consistent with the ModeToggle). The bundle's peek content preview (outline ticks / annotation dots) is deferred pending data plumbing + motion work.
73
+ - Annotation cards re-skinned (sub-PR 1.5) — the 3px left-edge type border is replaced by a full-card background tint per type (note → warning, suggestion → violet, comment → author-tinted, highlight → its own color, imported → neutral), a 6px authorship dot is added to the card header (user/Claude; imported keeps its byline), and the card chrome is lifted: rounder corners (`--tandem-r-5`), a soft resting shadow that raises on hover, and pill-shaped action buttons. Added `--tandem-author-user-bg` / `--tandem-author-claude-bg` tint tokens (light: `color-mix`; dark: hand-coded saturated hex). The five-file card split, dispatcher, audience-first separation, `annotation-private-pill`, suggestion diff block, and all testids are preserved (Conflict #8 "lift color" interpretation — no render-path merge, no `src/server/` changes).
74
+ - Command palette re-skinned (sub-PR 1.6) — added a leading search glyph and a trailing "Esc" keycap chip to the input row, a shared keycap treatment for the footer prefix hints (`#`/`@`/`?`/`>`) over a `--tandem-surface-muted` bar, rounded inset result rows, a wider (640px) modal with `--tandem-r-5` corners, and a `backdrop-filter: blur` overlay. The action registry contract is frozen (visual-only per Conflict #3) and the prefix-routing keyboard-hint footer is preserved; the bundle's per-item icons and section headers were intentionally skipped because they would require registry-contract changes and don't fit production's prefix-routed result model. All testids preserved; no `src/server/` changes.
75
+ - Command palette correctness follow-ups (sub-PR 1.6) — Escape now dismisses the palette regardless of which element holds focus (a capture-phase window listener replaces the unreliable modal-div `onkeydown`), and the dimming overlay's `z-index` was raised above the title bar's decorum drag-lift (`99999`) so the `+`new-tab button and Solo/Tandem toggle are covered by the backdrop instead of poking through.
76
+ - Settings shells re-skinned (sub-PR 1.7) — the SettingsModal and the legacy SettingsPopover were aligned to the bundle's `ui_kits/app/Settings` recipe: 12px dialog corners (`--tandem-r-5`), a 28px grid-centered close button with a `--tandem-surface-sunk` hover, sidebar `gap` tightened to `--tandem-space-3`, and nav-button hover/transition parity across both shells. The popover's close button moved from an inline `style=` to a `.settings-close-btn` class so `:hover`/`:focus-visible` apply (it previously had neither), and the popover nav buttons gained the missing `:hover` + focus ring. The responsive shell (narrow-viewport hamburger / 640px reflow) and tab registry are frozen (Conflict #4); tab-body content is deferred to Phase 2/3. The version chip stays `--tandem-fg-subtle` rather than the bundle's `--tandem-fg-faint` (which lowers contrast on the 10px chip below the AA-safe margin; Conflict #6 production-tokens-win). All testids preserved; no `src/server/` changes.
77
+ - StatusBar pill aligned to the bundle (sub-PR 1.8) — the floating status pill's vertical padding was bumped (`4px`→`6px`) and its forced height (`--tandem-h-statusbar`, a phantom token with no other consumer) dropped so the pill is content-driven like the bundle's `.status-pill`; the connection and Claude status dots shrank `8px`→`7px` to match the bundle's `.claude-pulse`. Base text color kept at `--tandem-fg-muted` (not the bundle's `fg-subtle`): the pill sits at `opacity: 0.4` at rest, so the lower-contrast subtle tone would compound below the AA-safe margin (same rationale as the 1.7 version chip). The bundle's `·` group separators were deferred (a structural change better reviewed visually). All functionality preserved (faint-until-hover, word-count cycle, connection states, display-name input, Review-Only, Claude working pill); all testids preserved; no `src/server/` changes. StatusBar is not in the baseline capture set, so no baseline regen.
78
+ - Recents store now carries open timestamps (sub-PR 1.9a) — the `tandem:recentFiles` localStorage shape migrated from `string[]` to `{ path, openedAt }[]` (legacy entries coerce to `openedAt: 0`; malformed entries dropped). Re-adding an already-present path preserves its original timestamp so the open-tab recents-sync doesn't churn. Groundwork for the a7 new-tab launcher (1.9b); no user-visible change yet. **1.9 was elevated from "clean port" to a full feature rebuild by an explicit scope override — see `docs/design-system-impl/conflicts-resolved.md` → Applied Overrides.**
79
+ - New-tab menu rebuilt as the a7 two-column launcher (sub-PR 1.9b) — the single-column recents dropdown is replaced by a searchable launcher: a full-width search bar filtering recents by name + path (with a live "{n} of {total}" count, clear button, and match highlighting), a left column of recents (file-type pip, name, directory, and a relative "when" label derived from 1.9a's timestamps), and a right column of actions — primary "New scratchpad", "Browse files…", and "Reopen last closed" (shown only when the in-session closed-tab stack is non-empty), each with its production shortcut hint. A keyboard-hint footer, search auto-focus on open, and ↑↓ list navigation round it out. The closed-tab stack (`useClosedTabStack`) was promoted to a reactive `.svelte.ts` singleton so the reopen affordance enables/disables live; clipboard import from the bundle was intentionally dropped (marginal over `Ctrl+N`+paste). Added `--tandem-filetype-txt` / `--tandem-filetype-html` pip tokens. Preserves `palette-item-new-scratchpad`; adds `new-tab-{search,recent-*,browse,reopen-closed,empty,no-match}`. No `src/server/` changes (clipboard's server path was never wired).
80
+ - Activity center added (sub-PR 1.10a) — the d1-toasts bundle is an activity center, not a toast restyle, so notifications now feed two surfaces from one store: warning/error notifications briefly POP a transient toast (re-skinned to the bundle's icon-square + message + `×count` vocabulary) AND land in a new persistent activity tray (bottom-right pill that expands into a scrollable history with severity glyph, relative time, coalesce count, hover-reveal dismiss, and "Clear all"). The tray is localStorage-backed (`tandem:activityHistory`, capped at 50, info entries TTL-pruned) and survives reload. Info notifications are gated by entry point: client-originated echoes (user actions) pop briefly, ambient server (SSE) info stays quiet-to-tray. New testids `activity-pill`, `activity-tray`, `activity-empty`, `activity-clear-all`, `activity-row-{id}`, `activity-dismiss-{id}`; transient `toast-*` testids preserved. The bundle's trayIn/rowIn/ledpulse animations are deferred to #798 (Conflict #9 — shipped static). No new color tokens; no `src/server/` changes. **Elevated from "clean port" to a feature by an explicit scope override — see `docs/design-system-impl/conflicts-resolved.md` → Applied Overrides.**
81
+ - Activity-tray Retry action (sub-PR 1.10b) — a `save-error` row now shows a "Retry" button that re-runs the save for the failed document (`activity-action-{id}`). If that document was closed since the error fired, Retry instead surfaces a "Reopen the document to retry" notice (a closed doc can't be saved). A pure `resolveActivityAction` resolver maps notification `type` → action; only `save-error` has a safe v1 semantic, Undo is deferred. Client-only (no `src/server/` changes); the server re-emits `save-error` on a failed retry, so no client error path is needed.
82
+ - Slash menu re-skinned to the bundle's B3 design (sub-PR 1.12) — each block row now leads with a 26px icon badge (¶-style heading glyphs / stroke-SVG list, quote, and code icons) and trails a mono shortcut-alias chip (`h1`, `ul`, `code`, …), inside a wider card-shaped surface (`--tandem-r-5`, 256px). The selected row drops its border in favor of an accent fill + accent-tinted badge; its shortcut chip text uses `--tandem-accent-fg-strong` rather than the bundle's `--tandem-accent` (accent-on-accent-bg is AA-marginal in light mode — same Conflict #6 call as the 1.7 version chip / 1.8 status pill). Icon badge and alias chip are `aria-hidden` with an explicit `aria-label` so each option's accessible name stays exactly its label. Visual-only: the command registry (8 blocks, existing labels), the inline `/`-typing trigger, live filtering, and keyboard/pointer behavior are unchanged; the bundle's separate filter-input row and empty-state are intentionally dropped (production filters inline and closes on zero matches), and the `popIn` animation is deferred to #798 per Conflict #9. All testids/roles preserved; no `src/server/` changes; not a baseline-capture scene, so no baseline regen.
83
+ - Decorations control (sub-PR 1.13) — annotation/authorship display toggles consolidated into a **Decorations split button in the formatting bar**: the eye half mutes/restores all decorations (clean reading view ⇄ restore), the caret half opens per-type options (authorship colors, comments, highlights, notes). This **subsumes the standalone authorship toggle** added in 1.2 — the `formatbar-authorship-toggle` testid is removed and authorship becomes the dropdown's first row (`Ctrl+Alt+A` and the command-palette action are unchanged). The single `showAnnotationDecorations` flag (#596) is split into per-type `showComments` / `showHighlights` / `showNotes` settings plus a transient `decorationsMuted` master overlay; a mirrored four-row group lives in Settings → Appearance. Master mute is an overlay (it never clobbers the per-type prefs, so restore returns exactly the prior set); editing any row auto-unmutes. The control is mounted outside the formatting bar's overflow-clip track so its dropdown is never clipped and never truncates on a narrow window. Display-only — ADR-027 is unchanged (notes are still never read by Claude; this only hides the user's own marks in their own view). Settings schema migrated v8→v9 (old "all marks off" maps onto all three per-type flags). No `src/server/` changes.
84
+ - Selection surface + optional formatting bar (sub-PR 1.11) — the on-text selection popup is now **always the full stacked surface**: a format pill (reusing `FormattingToolbar` in a new `variant="popup"` mode — the full mark/block set minus Undo/Redo, which stay on the bar) plus the **mirrored Decorations control**, stacked over the annotate pill (highlight swatches + Annotate). The persistent floating formatting bar becomes **optional/hideable** via a trailing collapse control (`formatbar-hide-btn`), governed by a new `formattingBarVisible` setting (default `true`, schema migrated v9→v10); restore it via the **Show formatting bar** button that appears in the selection popup while the bar is hidden (`popup-show-formatbar-btn`, the symmetric affordance to the bar's collapse control), the **Toggle formatting bar** command-palette action, or the new **Show formatting bar** Appearance toggle (`appearance-formatting-bar`) — and formatting stays fully reachable in the popup while the bar is hidden. The annotate popover's keybindings changed (Conflict #5, overridden 2026-05-26): **plain `Enter` = newline**, **`Alt+Enter` = Note to self** (private), **`Ctrl/Cmd+Enter` = Send to Claude** (outbound); both submits are modifier-gated and empty-guarded (the "Comment" button is relabeled "Send to Claude", testid `popup-comment-submit` preserved). Display/UI only — ADR-027's audience-first model is unchanged (notes never read by Claude); all `popup-*` / `toolbar-link-*` / `toolbar-highlight-*` / `decorations-*` testids preserved; no `src/server/` changes.
85
+ - Modal layering tokenized — title-bar poke-through fixed app-wide (#839). Introduced `--tandem-z-titlebar` (the `tauri-plugin-decorum` drag-overlay lift) and `--tandem-z-above-titlebar` tokens, replacing the raw `99999`/`100000`/`100001` magic numbers in TitleBar and SettingsModal/CommandPalette. Every full-screen modal overlay now sits above the title bar so its controls are dimmed, not clickable through: HelpModal, FileOpenDialog, ModelEditModal, IntegrationWizardModal, FirstRunModelPickerModal, CoworkAdminDeclinedModal, and the legacy SettingsPopover. `ReplyThreadOverlay` additionally portals to `<body>` because it renders inside the right rail's stacking context (`z-index: 1`), where a raised z-index alone couldn't escape. Contextual z-indexes that live inside the title bar's own stacking context (brand menu, selection toolbar) are deliberately unchanged.
33
86
  - **Network settings split into Connection / Advanced (Wave 2 PR 6)** — connection status, transport, and the restart-sidecar button stay always-visible; loopback port, degraded-banner delay, reconnect strategy, hold-while-offline, and token rotation collapsed under a new "Advanced" disclosure. Disclosure state is ephemeral (resets each time the modal opens). New `CollapsibleSection.svelte` primitive uses native `<details>/<summary>` for free keyboard a11y.
34
87
  - **v7 floating-chrome redesign sweep (Waves 1–M)** — a multi-wave reskin that lifts the editor chrome off the canvas:
35
88
  - Edge-anchored side rails with inner-rounded corners (Wave 2, #739) and a shared `.tandem-floating-pill` recipe (Wave 3, #740) applied across the formatting bar, status bar (Wave 5, #741), selection popup (Wave C, #762), and slash menu (Wave 10, #757).
package/README.md CHANGED
@@ -55,10 +55,12 @@ Tandem is built to work with Anthropic's Claude out of the box. Other AI tools c
55
55
  ## Who Tandem is for
56
56
 
57
57
  - If you draft long-form writing and want a second reader for tone and structure.
58
- - If you review contracts, policies, RFP responses, or compliance filings and want a faster pass.
59
- - When a colleague hands you a report to mark up.
58
+ - If you review documents — an essay, a thesis chapter, a report, or a contract and want a faster pass.
59
+ - When a colleague hands you a document to mark up.
60
60
  - When the AI wrote a draft and you need to decide what to keep.
61
61
 
62
+ Tandem is built for individuals working on their own documents. The example document types above are just that — examples; the workflow is the same whatever you are writing or reviewing.
63
+
62
64
  ## Getting started
63
65
 
64
66
  ### System requirements
@@ -104,13 +106,13 @@ See [docs/workflows.md](docs/workflows.md) for examples of how this looks in dai
104
106
  - Tandem itself runs on your computer and stores your documents on your disk. We do not operate any servers that hold your files.
105
107
  - When you ask the AI to do something, the text you share with it goes to whichever AI service you are using. For example, if you connect Claude, the text goes to Anthropic under their terms. Tandem does not relay or copy your document anywhere else.
106
108
  - Tandem includes a private notes feature. Notes you mark as private are stripped from every response the AI sees ([ADR-027](docs/decisions.md)).
107
- - Tandem does not collect telemetry or analytics.
109
+ - Tandem does not collect telemetry or analytics — no usage data, no crash reporting. When paid licensing arrives at v1.0, running the app will validate a signed license file on your own machine (no network call required); update checks will remain network-only, carry no analytics, and the update service will log only what it needs to authorize the download.
108
110
 
109
111
  See [docs/security.md](docs/security.md) for the full security model.
110
112
 
111
113
  ## Where Tandem is headed
112
114
 
113
- Tandem is on the way to a v1.0 release. Recent releases added support for multiple AI providers and in-app configuration for connections and models. Work still in progress covers improvements to how Word documents round-trip through the editor, turnkey setup on macOS and Linux, and final polish. The full plan lives in [docs/roadmap.md](docs/roadmap.md).
115
+ Tandem is on the way to a v1.0 release. Recent releases added support for multiple AI providers and in-app configuration for connections and models. Work still in progress covers improvements to how Word documents round-trip through the editor, turnkey setup on macOS and Linux, and final polish. Tandem is free during the public beta; at v1.0 it moves to a one-time paid license, and beta users are grandfathered with a free license. The full plan lives in [docs/roadmap.md](docs/roadmap.md).
114
116
 
115
117
  ## Documentation
116
118
 
@@ -129,7 +131,7 @@ Tandem is on the way to a v1.0 release. Recent releases added support for multip
129
131
 
130
132
  ## License
131
133
 
132
- Tandem is free to use. It is licensed under the Business Source License 1.1 (BUSL-1.1); see [LICENSE](LICENSE) for the terms.
134
+ Tandem is free during the public beta. At v1.0 it moves to a one-time paid license; existing beta users are grandfathered with a free license. It is licensed under the Business Source License 1.1 (BUSL-1.1); see [LICENSE](LICENSE) for the terms.
133
135
 
134
136
  ---
135
137