tandem-editor 0.15.0 → 0.16.0
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/plugin.json +3 -3
- package/CHANGELOG.md +21 -0
- package/dist/cli/index.js +3 -3
- package/dist/client/assets/{CoworkSettings-BWtK14Nl.js → CoworkSettings-B4LapjhG.js} +1 -1
- package/dist/client/assets/{dist-BIC2phzY.js → dist-CJtsfiU2.js} +1 -1
- package/dist/client/assets/index-BIEOrpU_.js +270 -0
- package/dist/client/assets/index-DdjGUhNd.css +1 -0
- package/dist/client/assets/{prod-Bv0q-mQu.js → prod-p35ShryZ.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/index.js +4 -4
- package/package.json +2 -1
- package/dist/client/assets/index-BVcBJagd.css +0 -1
- package/dist/client/assets/index-DMmXYPP4.js +0 -270
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tandem",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
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"
|
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
"mcpServers": {
|
|
12
12
|
"tandem": {
|
|
13
13
|
"command": "npx",
|
|
14
|
-
"args": ["-y", "tandem-editor@0.
|
|
14
|
+
"args": ["-y", "tandem-editor@0.16.0", "mcp-stdio"],
|
|
15
15
|
"env": {
|
|
16
16
|
"TANDEM_URL": "http://127.0.0.1:3479"
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"tandem-channel": {
|
|
20
20
|
"command": "npx",
|
|
21
|
-
"args": ["-y", "tandem-editor@0.
|
|
21
|
+
"args": ["-y", "tandem-editor@0.16.0", "channel"],
|
|
22
22
|
"env": {
|
|
23
23
|
"TANDEM_URL": "http://127.0.0.1:3479"
|
|
24
24
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.16.0] - 2026-07-13
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **The status pill now flashes a "Saved" confirmation, and the word count can show reading time (#1184).** After a save completes, the status pill briefly shows "Saved HH:MM" (falling-edge detection, mirroring the reconnected flash) so you get a visible confirmation instead of an invisible one — and the flash fires only on a *successful* save, with a failed save surfacing an error toast instead. The word-count chip's click-cycle gains a "min read" unit (200 wpm).
|
|
15
|
+
- **Pressing Enter in a task list continues the checklist (#1184).** Enter inside a checked or unchecked task item now creates a new *unchecked* task item instead of dropping to a plain bullet. Plain list items are unaffected, and Enter on an empty item still exits the list.
|
|
16
|
+
- **Pasting Markdown now preserves tables and images (#1184).** Pasted GFM tables build real table nodes and standalone images become block images; an image mixed into a paragraph degrades to its alt text rather than silently dropping the paragraph. Image sources pass a strict allowlist (http/https/ftp/protocol-relative/relative/base64 raster data URIs, capped at ~5MB decoded; `svg+xml` and `blob:` are rejected). Closes the #885 follow-up.
|
|
17
|
+
- **Pasting a URL over a text selection now creates a link (#1184).** A single safe URL pasted over selected text applies a link mark to the selection instead of replacing it. Unsafe schemes, whitespace-bearing text, empty selections, and code-block selections fall through to a normal paste.
|
|
18
|
+
- **Smart typography (opt-in) and a spellcheck toggle in Settings (#1184).** Smart typography (default off) turns straight quotes into curly quotes and `--`/`...` into em/en dashes and an ellipsis as you type — and the serializer round-trips those characters unchanged. Spellcheck (default on) toggles the editor's native spellcheck live, with no editor re-initialization.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- **Command palette search is now fuzzy, ranked, and highlighted (#1184).** All four palette modes replace substring-only matching with a two-tier fuzzy scorer (substring matches always outrank subsequence matches; bonuses for consecutive runs, word boundaries, camelCase, and start-of-string). Results sort by score with a stable tiebreak, and matched spans are highlighted in the results.
|
|
23
|
+
- **Suggestion cards now show a word-level diff (#1184).** Instead of striking the entire original text, a suggestion card renders a token-level diff — unchanged words plain, deletions struck, insertions tinted — in the universal old→new order. Very large inputs and snapshot-less suggestions fall back to the previous rendering.
|
|
24
|
+
- **Annotation cards and chat anchor quotes are now keyboard-accessible (#1184).** Annotation cards with a click action are focusable and activate on Enter/Space (without hijacking keys pressed inside their inner controls), and chat anchor quotes expand on keyboard focus as well as hover, with a reduced-motion guard.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **Accepting a suggestion that no longer fits now tells you, instead of failing silently (#1184).** When a suggestion's range can't be resolved (the text changed underneath it), accepting it now surfaces a warning toast — "Couldn't apply the suggestion — the text has changed." — and the card reverts to pending. The message carries no annotation content (ADR-027).
|
|
29
|
+
- **The first-run integration setup wizard now auto-opens in the desktop app (#1181).** In the Tauri desktop build (the primary Windows distribution), the wizard never appeared on first launch: the first-run check fetched a *relative* `/api/integrations/first-run-needed` URL, but the desktop WebView origin is `tauri.localhost`, so the request hit Tauri's asset protocol instead of the sidecar on `127.0.0.1:3479`, silently failed, and defaulted to "not needed" — suppressing the wizard forever. (The bug stayed hidden because the npm browser distribution serves the client *from* the sidecar, where the relative URL is same-origin.) The check now targets the sidecar's absolute base like every sibling hook, and re-checks once the sidecar connection is confirmed so a cold-start race can't leave it stuck at "not needed" — while skipping that recheck once the wizard is already showing, so a transient hiccup can't yank it out from under you mid-flow.
|
|
30
|
+
|
|
10
31
|
## [0.15.0] - 2026-07-07
|
|
11
32
|
|
|
12
33
|
### Added
|
package/dist/cli/index.js
CHANGED
|
@@ -290,7 +290,7 @@ function resolveChannelDist(env = process.env, exists = existsSync) {
|
|
|
290
290
|
return resolve2(PACKAGE_ROOT, "dist/channel/index.js");
|
|
291
291
|
}
|
|
292
292
|
function resolveCliVersion() {
|
|
293
|
-
if (true) return "0.
|
|
293
|
+
if (true) return "0.16.0";
|
|
294
294
|
if (typeof __APP_VERSION__ !== "undefined") return __APP_VERSION__;
|
|
295
295
|
for (const rel of ["../../package.json", "../../../package.json"]) {
|
|
296
296
|
try {
|
|
@@ -2865,7 +2865,7 @@ function evaluateStaleGlobal(bundled, globalVersion) {
|
|
|
2865
2865
|
};
|
|
2866
2866
|
}
|
|
2867
2867
|
async function checkStaleGlobal(r) {
|
|
2868
|
-
const bundled = true ? "0.
|
|
2868
|
+
const bundled = true ? "0.16.0" : null;
|
|
2869
2869
|
if (!bundled) return;
|
|
2870
2870
|
let globalVersion;
|
|
2871
2871
|
try {
|
|
@@ -3917,7 +3917,7 @@ process.once("unhandledRejection", (reason) => {
|
|
|
3917
3917
|
`);
|
|
3918
3918
|
process.exit(1);
|
|
3919
3919
|
});
|
|
3920
|
-
var version = true ? "0.
|
|
3920
|
+
var version = true ? "0.16.0" : "0.0.0-dev";
|
|
3921
3921
|
var args = process.argv.slice(2);
|
|
3922
3922
|
if (args.includes("--help") || args.includes("-h")) {
|
|
3923
3923
|
console.log(`tandem v${version}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{A as e,B as t,C as n,D as r,E as i,F as a,H as o,I as s,L as c,M as l,N as u,O as d,P as f,R as p,S as m,T as h,V as g,_,a as v,b as y,c as ee,d as b,f as x,g as S,h as C,i as w,j as T,k as E,l as D,m as te,n as O,o as k,p as A,r as ne,s as j,t as M,u as N,v as P,w as F,x as I,y as L,z as R}from"./index-
|
|
1
|
+
import{A as e,B as t,C as n,D as r,E as i,F as a,H as o,I as s,L as c,M as l,N as u,O as d,P as f,R as p,S as m,T as h,V as g,_,a as v,b as y,c as ee,d as b,f as x,g as S,h as C,i as w,j as T,k as E,l as D,m as te,n as O,o as k,p as A,r as ne,s as j,t as M,u as N,v as P,w as F,x as I,y as L,z as R}from"./index-BIEOrpU_.js";var z=r(`<div class="cs-help-text svelte-kvy574" data-testid="cowork-settings-loading">Loading Cowork status...</div>`),B=r(`<div class="cs-info-banner svelte-kvy574" data-testid="cowork-settings-unsupported">Cowork integration is available on Windows today. macOS/Linux support is tracked in #316 /
|
|
2
2
|
#317.</div>`),V=r(`<div class="cs-info-banner svelte-kvy574" data-testid="cowork-settings-undetected"><!> <a class="cs-link svelte-kvy574" target="_blank" rel="noreferrer">Learn more</a></div>`),re=r(`<div class="cs-reachability" data-testid="cowork-reachability"><div class="cs-reachability-title svelte-kvy574"> </div> <div class="cs-reachability-detail svelte-kvy574"> </div></div>`),ie=r(`<div class="cs-warning-banner svelte-kvy574" data-testid="cowork-enable-confirm" role="dialog"><div class="cs-confirm-heading svelte-kvy574">Confirm: Enable Cowork</div> <div class="cs-confirm-body svelte-kvy574">Tandem will register itself as a plugin in every detected Cowork workspace so Claude in
|
|
3
3
|
Cowork can reach your open documents. This adds a Windows firewall rule so the Cowork VM
|
|
4
4
|
can connect back — admin is required once.</div> <div class="cs-actions svelte-kvy574"><button class="cs-btn cs-btn--primary svelte-kvy574" data-testid="cowork-enable-confirm-btn" type="button">Enable</button> <button class="cs-btn cs-btn--ghost svelte-kvy574" data-testid="cowork-enable-cancel-btn" type="button">Cancel</button></div></div>`),ae=r(`<div class="cs-vethernet svelte-kvy574" data-testid="cowork-vethernet-cidr">Detected Cowork environment: <code> </code></div>`),oe=r(`<div><label data-testid="cowork-lan-ip-override"><input class="cs-accent-cbx svelte-kvy574" data-testid="cowork-lan-ip-override-checkbox" type="checkbox"/> <span>Use LAN IP instead of host.docker.internal</span></label> <div class="cs-help svelte-kvy574"> </div></div>`),se=r(`<div class="cs-help-text svelte-kvy574">No Cowork workspaces detected yet.</div>`),ce=r(`<a class="cs-report-link svelte-kvy574" href="mailto:maintainers@tandem.invalid?subject=Cowork%20schema%20drift">Report</a>`),le=r(`<div class="cs-workspace-row svelte-kvy574"><span class="cs-workspace-id svelte-kvy574"> </span> <span class="cs-workspace-label svelte-kvy574"> </span> <!></div>`),ue=r(`<div class="cs-workspace-table svelte-kvy574" data-testid="cowork-workspace-table"></div>`),de=r(`<label data-testid="cowork-toggle"><input class="cs-accent-cbx svelte-kvy574" data-testid="cowork-toggle-checkbox" type="checkbox"/> <span>Enable Cowork integration</span></label> <div class="cs-help svelte-kvy574"> </div> <!> <details class="cs-explainer svelte-kvy574" data-testid="cowork-explainer"><summary class="svelte-kvy574">What this does & how to verify</summary> <div class="cs-explainer-body svelte-kvy574"><p class="svelte-kvy574">Enabling registers Tandem as a plugin in every detected Cowork workspace, so Claude
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createTransport as e}from"./prod-
|
|
1
|
+
import{createTransport as e}from"./prod-p35ShryZ.js";async function t(e,t={},n){return window.__TAURI_INTERNALS__.invoke(e,t,n)}var n=!1;function r(r){return e(r,async e=>{if(n)return{statusCode:200};try{await t(`plugin:sentry|envelope`,{envelope:e.body})}catch(e){console.error(`Failed to send envelope to Rust:`,e),n=!0}return{statusCode:200}})}function i(e){return n||typeof e.data?.url==`string`&&(e.data.url.startsWith(`ipc://`)||e.data.url.match(/^https?:\/\/ipc\.localhost/))||t(`plugin:sentry|breadcrumb`,{breadcrumb:e}).catch(e=>{console.error(`Failed to send breadcrumb to Rust:`,e),n=!0}),null}var a={dsn:`https://123456@dummy.dsn/0`,integrations:e=>e.filter(e=>e.name!==`BrowserSession`),transport:r,beforeBreadcrumb:i};export{a as defaultOptions};
|