tandem-editor 0.12.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +121 -9
- package/README.md +142 -237
- package/dist/channel/index.js +158 -69
- package/dist/channel/index.js.map +1 -1
- package/dist/cli/index.js +656 -148
- package/dist/cli/index.js.map +1 -1
- package/dist/client/assets/CoworkSettings-BEvEpByR.js +3 -0
- package/dist/client/assets/CoworkSettings-C-1BnhBH.css +1 -0
- package/dist/client/assets/index-CXkPU0mo.css +1 -0
- package/dist/client/assets/index-D6kdw28e.js +238 -0
- package/dist/client/assets/index-Dm_QtxGQ.js +1 -0
- package/dist/client/assets/webview-KiZyy_pC.js +1 -0
- package/dist/client/fonts/OFL-Hanuman.txt +93 -0
- package/dist/client/fonts/OFL-InterTight.txt +93 -0
- package/dist/client/fonts/OFL-JetBrainsMono.txt +93 -0
- package/dist/client/fonts/OFL-SNPro.txt +93 -0
- package/dist/client/fonts/OFL-Sono.txt +93 -0
- package/dist/client/fonts/OFL-SourceSerif4.txt +93 -0
- package/dist/client/fonts/hanuman-latin.woff2 +0 -0
- package/dist/client/fonts/jetbrains-mono-latin.woff2 +0 -0
- package/dist/client/fonts/sn-pro-latin.woff2 +0 -0
- package/dist/client/fonts/sono-latin.woff2 +0 -0
- package/dist/client/fonts/source-serif-4-latin.woff2 +0 -0
- package/dist/client/index.html +341 -17
- package/dist/client/logo.png +0 -0
- package/dist/monitor/index.js +235 -161
- package/dist/monitor/index.js.map +1 -1
- package/dist/server/index.js +25323 -21754
- package/dist/server/index.js.map +1 -1
- package/package.json +11 -3
- package/sample/welcome.md +6 -6
- package/skills/tandem/SKILL.md +15 -0
- package/dist/client/assets/CoworkSettings-C0cS9R7L.js +0 -3
- package/dist/client/assets/index-Dn5JwXA3.css +0 -1
- package/dist/client/assets/index-n-vFW5By.js +0 -299
- package/dist/client/assets/webview-Bhf-n_os.js +0 -1
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"name": "Tandem"
|
|
5
5
|
},
|
|
6
6
|
"metadata": {
|
|
7
|
-
"description": "Tandem — collaborative AI-human document editor"
|
|
7
|
+
"description": "Tandem — collaborative AI-human document editor (MCP-first; Claude is the default integration)"
|
|
8
8
|
},
|
|
9
9
|
"plugins": [
|
|
10
10
|
{
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"source": "github",
|
|
14
14
|
"repo": "bloknayrb/tandem"
|
|
15
15
|
},
|
|
16
|
-
"description": "Edit and iterate on documents with Claude
|
|
16
|
+
"description": "Edit and iterate on documents with any MCP-capable AI. Claude is the default and best-supported client today."
|
|
17
17
|
}
|
|
18
18
|
]
|
|
19
19
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tandem",
|
|
3
3
|
"version": "0.8.0",
|
|
4
|
-
"description": "Edit and iterate on documents with Claude
|
|
4
|
+
"description": "Edit and iterate on documents with any MCP-capable AI. Claude is the default and best-supported client today.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Tandem"
|
|
7
7
|
},
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,125 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
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
|
+
|
|
41
|
+
## [0.13.0] - 2026-05-25
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- **Settings → Models tab + edit modal (Wave 2 PR 8b, #659)** — new tab in `SettingsModal` for managing the Models registry. Provider-grouped list with per-row enable toggle, edit, two-step delete confirm. `ModelEditModal` covers add and edit flows: provider select (Anthropic / OpenAI / Gemini / Ollama / llama.cpp), conditional cloud-vs-local fields, reveal-gated API key (masked `••••${last4}` on edit; "Replace key" button to enter a new value — existing key never round-trips through the DOM). Mandatory in-product disclosure banner about plaintext localStorage storage.
|
|
46
|
+
- **Models registry data model + `useModels` hook (Wave 2 PR 8a, #659)** — `TandemSettings` now carries a `models: ModelRegistryEntry[]` array tracking AI providers Tandem can call out to (Anthropic, OpenAI, Gemini, local Ollama, local llama.cpp). The data layer ships in this PR; the Settings → Models UI ships in PR 8b. Orthogonal to `IntegrationConfig` (#477) — that schema tracks MCP clients connecting INTO Tandem, while this tracks providers Tandem talks OUT to.
|
|
47
|
+
- **Multi-provider Models registry (#659, #784)** — keychain-backed secret storage, per-provider default selection, and a first-run model picker spanning Anthropic / OpenAI / Gemini cloud providers plus local Ollama / llama.cpp endpoints. A legacy-migration banner upgrades pre-registry single-key setups.
|
|
48
|
+
- **Schema v2 → v3 migration with forward-compat read-only mode** — `loadSettings` now walks an explicit migration chain (v1→v2→v3). An on-disk `schemaVersion` greater than v3 loads defensively with `_readOnly: true`, and `createTandemSettings.updateSettings` short-circuits writes on read-only settings. This is the load-bearing defence against a downgraded client clobbering a newer client's Models registry / future fields on first save.
|
|
49
|
+
- **Integration setup wizard + first-run auto-open (#477 PRs 1/3a/3b/3c-i/3c-ii-b — #728, #729, #730, #731, #773)** — a full-screen wizard for connecting MCP clients (Claude Code and other agents) to Tandem. Ships the `IntegrationConfig` schema with atomic storage + v1→v2→v3 migration, a non-mutating reader for existing `~/.claude.json` entries, and an apply path (`POST /api/integrations/apply`) that backs up the target config before rewriting it. On first launch with no `integrations.json` the wizard opens automatically; later launches do not. Enabled by default.
|
|
50
|
+
- **Native OS keychain on the Tauri desktop app (#477 PR 3c-tauri-keychain, #732)** — integration secrets are stored in the platform keychain on desktop, with graceful env-var fallback when the keychain is unavailable.
|
|
51
|
+
- **Auto-launcher for Claude Code (#477 PR 4a/4b, #800)** — Tandem spawns and supervises the Claude Code CLI alongside the server, with a native cross-platform parent-death reaper so the child is reliably cleaned up on exit. Adds command-palette actions "Relaunch Claude in this folder" and "Start fresh Claude conversation", a working-directory picker in Settings → Integrations, and a project-context skill bundled on startup. Enabled by default for users with a configured `claude-code` integration; opt out with `TANDEM_DISABLE_LAUNCHER=1`.
|
|
52
|
+
- **Native file picker + drag-and-drop on the Tauri desktop app (#378)** — open files through the OS-native file dialog and by dragging them onto the window; the File Open dialog is consolidated to a single Browse + Recent panel.
|
|
53
|
+
- **Heading-section collapse (#650)** — collapse everything under a heading via its fold control; collapse state persists per document in localStorage and survives heading-text edits and reloads.
|
|
54
|
+
- **Scratchpad save-to-disk (#827)** — save a scratchpad to a real file via the native dialog (`.md` / `.txt`); Save-As accepts user-chosen locations anywhere on disk while keeping the symlink + UNC guards.
|
|
55
|
+
- **Claude typing-presence indicator (#651)** — a presence cue shows when Claude is actively writing, with monotonic token ownership so overlapping presence updates resolve deterministically.
|
|
56
|
+
- **Warm canvas theme (#738)** — a fourth theme option (System / Light / Dark / Warm) and the foundation for the v7 floating-chrome redesign.
|
|
57
|
+
- **Re-landed D11 bundled fonts (#680)** — re-applied the fontsource Latin variable-axis builds of Source Serif 4, Inter Tight, and JetBrains Mono originally shipped in PR #663 then reverted via #678. The SIL OFL 1.1 license texts (`public/fonts/OFL-*.txt`) ship alongside the binaries per §4. Editor body text renders in Source Serif 4 via the existing `--tandem-editor-font-family` token.
|
|
58
|
+
- **Margin annotation view (#649, #711, #715, #719, #720)** — Word-style margin comment bubbles with breathing-room gaps and leader lines connecting each card to its anchor, action parity with the sidebar, and the annotation filter chip relocated into the panel header.
|
|
59
|
+
- **Margin view auto-collapses on rail open or narrow viewport (#683)** — opening the left rail now hides only the left margin column; opening the right rail hides only the right column. When the viewport gets narrow enough that margin reserve + open rails + readable editor would crowd, both columns hide together. A 32px hysteresis band on the viewport threshold prevents flicker when a user drags through the boundary. New `useViewportWidth` rune store provides the rAF-debounced subscription.
|
|
60
|
+
- **Status-bar word-count cycle + page count (#741, #792)** — the floating status pill cycles through word, character, and page counts.
|
|
61
|
+
- **Inline annotation decoration toggle (#791)** — turn the inline highlight/underline decorations on or off without hiding the annotations themselves.
|
|
62
|
+
- **Knowledge-graph pilot (#769, #771)** — 25 hand-curated concept/rule/ADR nodes with cross-edges, queryable via `npm run kg`.
|
|
63
|
+
|
|
64
|
+
### Changed
|
|
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.
|
|
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.
|
|
87
|
+
- **v7 floating-chrome redesign sweep (Waves 1–M)** — a multi-wave reskin that lifts the editor chrome off the canvas:
|
|
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).
|
|
89
|
+
- TitleBar + DocumentTabs reflow: bar seam removed so chrome melts into the canvas (Wave 4a, #746), pill-shaped tabs (Wave 4b, #748), tabs lifted into the TitleBar center cluster with mask-fade overflow and a floating `+` button (Wave 4a/4b maximalist, #752, #755).
|
|
90
|
+
- Left rail locked to outline-only (Wave D, #759); outline rail clears the status pill lane (Wave 6, #742); peek-from-side strips + edge-click panel collapse (Wave E, #764).
|
|
91
|
+
- Narrow-viewport Settings sidebar drawer + hamburger (Wave 9, #745); `.docx` batch-promote with always-visible checkboxes (Wave 8, #756).
|
|
92
|
+
- Native scrollbars hidden with mask-faded overflow edges (Wave B, #761); titlebar/status/right-rail header polish (Wave A, #760); placeholder + margin-card hover affordance (Wave F, #763).
|
|
93
|
+
- Redesign-parity sweep across titlebar, rails, picker teardown, popup, composer (Waves G–L, #765) plus the Wave M titlebar dropdown + solo-mode fade + dark-mode v7 alignment (#776).
|
|
94
|
+
- **README + documentation rewrite (#777, #778)** — README rewritten for a lay reader with technical detail moved below the fold, plus new `docs/cli.md`, `docs/configuration.md`, `docs/troubleshooting.md`, and `docs/security.md`.
|
|
95
|
+
- **MCP-first integration policy; your AI as the default integration (ADR-038, #722)** — documents that Tandem's integration contract is MCP and Claude is the default concrete integration.
|
|
96
|
+
|
|
97
|
+
### Fixed
|
|
98
|
+
|
|
99
|
+
- **Inline code combined with bold/italic/strikethrough/link no longer loses its `code` formatting on save** — opening a `.md` file in Tandem (editable, not read-only) and letting autosave write it back used to drop the `code` mark on any span that also carried bold/italic/strike, and silently discard the surrounding link for code-in-link. The damage surfaced as doubled asterisks and ` ` entity-spaces — code-heavy docs like `docs/decisions.md` were corrupted just by being viewed. `deltaToPhrasingContent` now treats `code` as a leaf and wraps link/strike/italic/bold unconditionally, and a new coalescing pass merges adjacent same-wrapper phrasing nodes so a bold run holding a code span serializes as one emphasis instead of several. Round-trip verified lossless across the repo's code-heavy docs.
|
|
100
|
+
- **Keyboard tab switch no longer reverts under a stale sync** — a local `Ctrl+1..9` (or click) tab switch is client-only and was being clobbered when a late `documentMeta` re-broadcast re-applied the server's active doc, surfacing as a flaky "Ctrl+N switches to the Nth tab" E2E test. The server now broadcasts a monotonic activation epoch (`activeDocumentEpoch`) alongside the active id; the client applies the server's active only when the epoch advances, so a stale re-sync is ignored while a genuine re-activation (e.g. re-opening the already-active doc) still steals focus.
|
|
101
|
+
- **Selection toolbar places itself clear of fixed chrome (#680)** — the selection BubbleMenu flips below the selection when above-placement would overlap the TitleBar + FormattingBar; selections that straddle the viewport fold pin the toolbar to the viewport bottom rather than clamping onto the chrome; and a 4px hysteresis band at the flip boundary keeps the toolbar from shimmering as a selection drifts across the threshold.
|
|
102
|
+
- **Clicked highlight stays focused over an overlapping comment (#817)** — clicking a highlight that overlaps a comment now collapses the ProseMirror selection so the overlay clears and the click-to-focus ordering resolves to the highlight; unknown annotation types fall back gracefully.
|
|
103
|
+
- **Solo/Tandem mode never auto-flips on a transient error (#822)** — a failed `/api/mode` poll preserves the last-known mode instead of silently reverting to Solo.
|
|
104
|
+
- **Annotation palette remap aligned with the v7 design (Wave 7, #743)**.
|
|
105
|
+
- **Post-wave correctness fixes across 9 sites (#758)** — a cleanup pass over regressions introduced during the redesign sweep.
|
|
106
|
+
- **Kept-tab metadata refreshes on document-list reconcile** — tab titles and state no longer go stale when the open-document list re-syncs.
|
|
107
|
+
- **Unsupported-extension toast names the right extension (#808)** — `extensionAllowed` extracts the basename correctly (handling dot-in-directory and dotfile edge cases) and surfaces `.htm` properly.
|
|
108
|
+
- **`.docx` comment-extraction failures surface via notification (#696, #701)** — instead of failing silently.
|
|
109
|
+
|
|
110
|
+
### Security
|
|
111
|
+
|
|
112
|
+
- **`IntegrationConfig.url` constrained to loopback only (#753)** — accepts `http://127.0.0.1[:port][/path]` only; rejects `localhost`, IPv6 loopback `[::1]`, other `127.x.x.x` addresses, `https`, and embedded credentials (`http://user:pw@…`). Legacy `http://localhost` entries are normalized to `http://127.0.0.1` on read; any other host shape is surfaced as invalid and re-prompts the wizard.
|
|
113
|
+
- **Windows ACL hardening on `.claude.json` (#643, #795)** — a restrictive DACL is applied to the written config.
|
|
114
|
+
- **`.claude.json` backed up before overwrite (#644, #796)** — backups written under `${appDataDir}/.backups/` with mode `0o600` (POSIX) / restrictive DACL (Windows), capped at `MAX_BACKUPS=3`. Token rotation triggers a backup, so up to 3 superseded bearer tokens may persist on disk per integration; clear the backup folder manually for tighter retention.
|
|
115
|
+
- **Malformed-input matrix + shape gates + BOM strip on integration-config reads (#645, #797)** — broken-JSON backups on Windows also receive a restrictive ACL, with the read-modify-write TOCTOU window closed.
|
|
116
|
+
- **CI guard: harness components must not leak into production client bundle (Wave 2 PR 7)** — new `scripts/ci/verify-harness-stripped.mjs` runs after `npm run build` and greps `dist/client/` for known harness symbols (`UpdateAvailableHarness`, `harness-acknowledge`, etc.). Defends against a future commit accidentally importing a harness component from a production-shipping module, which would expose internal state (e.g. acknowledge buttons, version accessors) to end users. Pairs with the #660 audit confirming every settings-open path routes through the ack-clearing wrapper.
|
|
117
|
+
|
|
118
|
+
### Internal
|
|
119
|
+
|
|
120
|
+
- **Annotation-lifecycle architecture pass (ADRs 031–037, #695–#714)** — origin-tagged transaction wrappers (ADR-031), a `RefreshResult` tagged variant (ADR-032), `DocumentRegistry` extracted from `document-service` (ADR-033), named file-open entry points (ADR-034), a per-key change-observer factory + `AnnotationLifecycle` module (ADR-035), the `FormatAdapter` capability set + `LoadResult` (ADR-036), and the `LayoutModel` rune store (ADR-037). Includes observer-driven tombstoning that records on every delete.
|
|
121
|
+
- **`#477` Phase 0 spikes (#712, #726, #750)** — session-resume Spike A (GO), plugin-monitor Spike B (NO-GO), marketplace-install spike re-validated NO-GO.
|
|
122
|
+
- **Sidecar-launcher validation spike (#642, #794)** — canonicalize-before-allowlist, Windows reparse-point walk, POSIX hardlink + world-writable-parent rejection, prototyped and tested under `#[cfg(test)]` in `integrations_probe.rs`. Not yet wired into the shipped spawn path.
|
|
123
|
+
- **Test-suite audit (#786)** — ADR-027 privacy bug fix, weak-assertion sweep, ADR-031 gap fill; E2E `waitForTimeout` flake removal (#785); schema-version assertions pinned to `CURRENT_SCHEMA_VERSION` (#789).
|
|
124
|
+
- **Refactors** — `matchShortcut` helper extraction + dead `useSettingsShortcut` removal (#820), shared SSE event-consumer extraction (#822), `THEME_OPTIONS` extracted from TitleBar (#790), typed `RejectionReason` from `extract_file_arg` (#819), dead Solo/Tandem mode plumbing removed.
|
|
125
|
+
- **Tooling / CI** — `check:tokens` extended with a bundle-color blocklist (#826), `claude-review` workflow hardened + auto `/code-review` on new PRs (#830), `block-e2e-port-kill` PreToolUse hook removed (#787).
|
|
126
|
+
- **Dependencies** — `ws` 8.19.0 → 8.20.1 (#772) and `qs` bumped (npm_and_yarn group); no CVE cited.
|
|
127
|
+
- **Docs** — `RelativeRange` invariants documented (#793), roadmap and CLAUDE.md file-map refreshes (#744, #749, #751), design-notes + redesign-review archival (#774, #775), reference-doc consolidation (#852, #854, #856), and AGENTS.md tracked as the cross-agent contributor guide (#858).
|
|
128
|
+
|
|
10
129
|
## [0.12.0] - 2026-05-15
|
|
11
130
|
|
|
12
131
|
### Fixed
|
|
@@ -144,6 +263,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
144
263
|
- **Tauri shell**: reload shortcuts (F5, Ctrl+F5, Shift+F5, Ctrl+R, Ctrl+Shift+R) are now blocked in the desktop app to prevent accidental navigation away from the editor; DevTools, Find, Print, and right-click context menu are preserved (#541)
|
|
145
264
|
- **Semantic token foundation expanded for redesign wave 2 (#521)** — added radius, font-size, shadow, z-index, editor-font-size, and highlight-color token families in `index.html`, plus checker rules that now flag raw `border-radius: <n>px` and inline `box-shadow: ... rgba(...)` in `src/client/`.
|
|
146
265
|
- **Read-only/info surfaces now use the shared info token family (#521)** — `ReviewOnlyBanner`, `ConnectionBanner`, `ToastContainer`, `StatusBar`, and related chrome now consume the shared token scales instead of hardcoded radius/text/shadow values.
|
|
266
|
+
- **Monitor and channel honor `CLAUDE_PLUGIN_OPTION_SERVER_URL`** — `resolveTandemUrl()` now checks the `CLAUDE_PLUGIN_OPTION_SERVER_URL` environment variable (exported by Claude Code's plugin host from `plugin.json` `userConfig`) before falling back to `TANDEM_URL` and the localhost default. Both the monitor (`src/monitor/index.ts`) and channel shim (`src/channel/run.ts`) benefit automatically. No change for existing installs that don't use `userConfig`. (Drafted as a standalone v0.10.1 patch that was never tagged; shipped as part of v0.11.0.)
|
|
267
|
+
- **Monitor and channel honor `CLAUDE_PLUGIN_OPTION_AUTH_TOKEN`** — new `resolveAuthToken()` function in `src/shared/cli-runtime.ts` mirrors `resolveTandemUrl()`. Precedence: `CLAUDE_PLUGIN_OPTION_AUTH_TOKEN` → `TANDEM_AUTH_TOKEN`. `authFetch` uses it automatically, so all stdio subcommands gain the new lookup without caller changes.
|
|
147
268
|
|
|
148
269
|
### Tests
|
|
149
270
|
|
|
@@ -165,15 +286,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
165
286
|
- **Dark annotation highlight colors** — `--tandem-highlight-yellow/green/blue/pink` now have dark-adapted overrides in `[data-theme="dark"]`; the light `rgba(255, 235, 59, 0.3)`-style values were washed out against dark surfaces.
|
|
166
287
|
- **Forced-colors fallbacks for background-only state surfaces (closes #311)** — StatusBar status dots, toast badge, ModeToggle active button, BulkActions confirm button, AnnotationCard type-badge and Private pill now have `border`/`outline` fallbacks in `@media (forced-colors: active)`.
|
|
167
288
|
|
|
168
|
-
## [0.10.1] - Unreleased
|
|
169
|
-
|
|
170
|
-
Plugin URL and auth resolution for custom-port and network-remote setups.
|
|
171
|
-
|
|
172
|
-
### Changed
|
|
173
|
-
|
|
174
|
-
- \*\*Monitor and channel honor \*\***CLAUDE_PLUGIN_OPTION_SERVER_URL** — `resolveTandemUrl()` now checks the `CLAUDE_PLUGIN_OPTION_SERVER_URL` environment variable (exported by Claude Code's plugin host from `plugin.json` `userConfig`) before falling back to `TANDEM_URL` and the localhost default. Both the monitor (`src/monitor/index.ts`) and channel shim (`src/channel/run.ts`) benefit automatically. No change for existing installs that don't use `userConfig`.
|
|
175
|
-
- \*\*Monitor and channel honor \*\***CLAUDE_PLUGIN_OPTION_AUTH_TOKEN** — new `resolveAuthToken()` function in `src/shared/cli-runtime.ts` mirrors `resolveTandemUrl()`. Precedence: `CLAUDE_PLUGIN_OPTION_AUTH_TOKEN` → `TANDEM_AUTH_TOKEN`. `authFetch` uses it automatically, so all stdio subcommands gain the new lookup without caller changes.
|
|
176
|
-
|
|
177
289
|
## [0.10.0] - 2026-05-03
|
|
178
290
|
|
|
179
291
|
Complete React → Svelte 5 migration. All 39 client `.tsx` files have been replaced with Svelte 5 rune-based equivalents; `react`, `react-dom`, and `@tiptap/react` are no longer in the bundle. Includes a review-mode correctness fix, accessibility improvements, and follow-on Codex security hardening.
|