tandem-editor 0.11.2 → 0.13.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/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +3 -3
- package/CHANGELOG.md +201 -72
- package/README.md +141 -238
- package/dist/channel/index.js +211 -81
- package/dist/channel/index.js.map +1 -1
- package/dist/cli/index.js +749 -170
- package/dist/cli/index.js.map +1 -1
- package/dist/client/assets/CoworkSettings-BOYbyKul.js +3 -0
- package/dist/client/assets/event-CNdo2oXa.js +1 -0
- package/dist/client/assets/index-D8uS4cj7.css +1 -0
- package/dist/client/assets/index-Dm_QtxGQ.js +1 -0
- package/dist/client/assets/index-g-KwmRn9.js +271 -0
- package/dist/client/assets/webview-KiZyy_pC.js +1 -0
- package/dist/client/assets/window-DePn7tLG.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 +206 -17
- package/dist/client/logo.png +0 -0
- package/dist/monitor/index.js +241 -160
- package/dist/monitor/index.js.map +1 -1
- package/dist/server/index.js +22828 -19659
- package/dist/server/index.js.map +1 -1
- package/package.json +12 -4
- package/sample/welcome.md +6 -6
- package/skills/tandem/SKILL.md +15 -0
- package/dist/client/assets/CoworkSettings-DK3jjdwK.js +0 -3
- package/dist/client/assets/index-CfT503n4.js +0 -297
- package/dist/client/assets/index-DeJe09pn.css +0 -1
- package/dist/client/assets/webview-Ben21ZLJ.js +0 -1
- package/dist/client/assets/window-BxBvHL5k.js +0 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tandem-editor",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Edit and iterate on documents with Claude
|
|
3
|
+
"version": "0.13.0",
|
|
4
|
+
"description": "Edit and iterate on documents with any MCP-capable AI (Claude by default). Real-time push via plugin monitor or channel shim.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/bloknayrb/tandem"
|
|
@@ -55,6 +55,11 @@
|
|
|
55
55
|
"dev:tauri": "node scripts/download-node-sidecar.mjs && cargo tauri dev",
|
|
56
56
|
"build:tauri": "cargo tauri build",
|
|
57
57
|
"check:tokens": "tsx scripts/check-semantic-tokens.ts",
|
|
58
|
+
"audit:dead-code": "knip",
|
|
59
|
+
"audit:origins": "tsx scripts/audit-origins.ts",
|
|
60
|
+
"audit:ymap-keys": "tsx scripts/audit-ymap-keys.ts",
|
|
61
|
+
"kg": "node scripts/kg.mjs",
|
|
62
|
+
"kg:lint": "node scripts/kg-lint.mjs",
|
|
58
63
|
"prepare": "husky",
|
|
59
64
|
"prepublishOnly": "npm run build"
|
|
60
65
|
},
|
|
@@ -87,7 +92,9 @@
|
|
|
87
92
|
"@hocuspocus/provider": "3.4.4",
|
|
88
93
|
"@hocuspocus/server": "2.15.3",
|
|
89
94
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
95
|
+
"@napi-rs/keyring": "^1.3.0",
|
|
90
96
|
"@tauri-apps/api": "^2.11.0",
|
|
97
|
+
"@tauri-apps/plugin-dialog": "^2.7.1",
|
|
91
98
|
"@tiptap/core": "^2.11.0",
|
|
92
99
|
"@tiptap/extension-collaboration": "^2.11.0",
|
|
93
100
|
"@tiptap/extension-collaboration-cursor": "^2.11.0",
|
|
@@ -98,7 +105,6 @@
|
|
|
98
105
|
"@tiptap/extension-table-cell": "^2.11.0",
|
|
99
106
|
"@tiptap/extension-table-header": "^2.11.0",
|
|
100
107
|
"@tiptap/extension-table-row": "^2.11.0",
|
|
101
|
-
"@tiptap/extension-unique-id": "^2.11.0",
|
|
102
108
|
"@tiptap/pm": "^2.11.0",
|
|
103
109
|
"@tiptap/starter-kit": "^2.11.0",
|
|
104
110
|
"dom-serializer": "^3.0.0",
|
|
@@ -111,6 +117,7 @@
|
|
|
111
117
|
"remark-parse": "^11.0.0",
|
|
112
118
|
"remark-stringify": "^11.0.0",
|
|
113
119
|
"unified": "^11.0.0",
|
|
120
|
+
"unist-util-visit": "^5.1.0",
|
|
114
121
|
"update-notifier": "^7.3.1",
|
|
115
122
|
"y-prosemirror": "1.3.7",
|
|
116
123
|
"y-protocols": "1.0.7",
|
|
@@ -125,12 +132,13 @@
|
|
|
125
132
|
"@testing-library/svelte": "^5.0.0",
|
|
126
133
|
"@types/node": "^25.5.0",
|
|
127
134
|
"@types/update-notifier": "^6.0.8",
|
|
128
|
-
"concurrently": "^9.1.0",
|
|
129
135
|
"cross-env": "^10.1.0",
|
|
136
|
+
"docx": "^9.6.1",
|
|
130
137
|
"eslint": "^9.39.4",
|
|
131
138
|
"eslint-plugin-react-hooks": "^5.2.0",
|
|
132
139
|
"happy-dom": "^20.9.0",
|
|
133
140
|
"husky": "^9.1.7",
|
|
141
|
+
"knip": "^5.66.0",
|
|
134
142
|
"lint-staged": "^16.4.0",
|
|
135
143
|
"svelte": "^5.37.0",
|
|
136
144
|
"svelte-check": "^4.0.0",
|
package/sample/welcome.md
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
# Welcome to Tandem
|
|
2
2
|
|
|
3
|
-
Tandem lets you work on documents with
|
|
3
|
+
Tandem lets you work on documents with an AI — by default Claude, but any MCP-capable client can connect over MCP. You can highlight text and your AI sees it directly, no copy-paste needed. Because Tandem speaks MCP, your AI brings all its knowledge and tools to the document.
|
|
4
4
|
|
|
5
5
|
## Getting Started
|
|
6
6
|
|
|
7
7
|
> Follow the tutorial in the bottom-left corner to learn the basics. You can dismiss it at any time. For a full walkthrough of all features, see the [User Guide](../docs/user-guide.md).
|
|
8
8
|
|
|
9
|
-
Open Claude Code from any directory
|
|
9
|
+
Open Claude Code from any directory — your Tandem tools are already configured. Both you and your AI can see and edit this document at the same time. Your AI's cursor appears as a soft blue highlight on the paragraph being read.
|
|
10
10
|
|
|
11
11
|
## Try These
|
|
12
12
|
|
|
13
|
-
1. **Respond to a suggestion**
|
|
14
|
-
2. **Ask
|
|
15
|
-
3. **Make an edit**
|
|
13
|
+
1. **Respond to a suggestion** — Look at the highlighted text in this document. Open the side panel to accept or dismiss annotations, or press Ctrl+Shift+R for Review Mode.
|
|
14
|
+
2. **Ask a question** — Select some text and press Ctrl+Shift+A, or type in the Chat panel.
|
|
15
|
+
3. **Make an edit** — Click anywhere in the document and start typing. You can simplify sentences, fix typos, or add new content.
|
|
16
16
|
|
|
17
17
|
## Sample Content
|
|
18
18
|
|
|
19
19
|
The project launched in early 2025 with three core goals: simplify onboarding, reduce support tickets by 40%, and ship a self-service dashboard by Q3. The team completed the first two milestones ahead of schedule, but the dashboard timeline slipped due to an unexpected API redesign in May.
|
|
20
20
|
|
|
21
|
-
> Once
|
|
21
|
+
> Once your AI connects, try asking it to help you improve this text — you'll see highlights and suggestions appear in the side panel.
|
package/skills/tandem/SKILL.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tandem
|
|
3
|
+
version: 2
|
|
3
4
|
description: >
|
|
4
5
|
Use when tandem_* MCP tools are available, the user asks about Tandem
|
|
5
6
|
document editing, or iterating on text collaboratively. Provides workflow
|
|
@@ -9,6 +10,8 @@ description: >
|
|
|
9
10
|
|
|
10
11
|
# Tandem — Collaborative Document Editor
|
|
11
12
|
|
|
13
|
+
> **Scope:** This skill teaches Claude Code how to use Tandem effectively. Tandem's integration contract is MCP, and **Claude is the default integration** per [ADR-038](https://github.com/bloknayrb/tandem/blob/master/docs/decisions.md#adr-038-mcp-first-integration-policy-claude-as-default-integration). This skill is a Claude-Code-specific resource shipped via the npm `skills/` folder; other MCP clients receive the tool descriptions directly through MCP and don't need this file.
|
|
14
|
+
|
|
12
15
|
Tandem lets you annotate and edit documents alongside the user in real time. The user sees your changes in the editor; you interact via the tandem_* MCP tool suite.
|
|
13
16
|
|
|
14
17
|
## Hard Rules
|
|
@@ -91,3 +94,15 @@ When starting a new Claude session with Tandem already running:
|
|
|
91
94
|
## Multi-Document
|
|
92
95
|
|
|
93
96
|
When multiple documents are open, always pass `documentId` explicitly — omitting it targets the active document, which may have changed since your last call. Use `tandem_listDocuments` to see what's available. Cross-reference by reading both docs via `tandem_getTextContent({ documentId: "..." })` and annotating the relevant one.
|
|
97
|
+
|
|
98
|
+
## Project Context Discovery
|
|
99
|
+
|
|
100
|
+
Tandem auto-launches you in a single working directory (the user's home by default, or whatever they configured under Settings → Claude Code → Working directory). The document the user opens may live elsewhere — a different project, a different repo. When you're working on a file outside your launch cwd:
|
|
101
|
+
|
|
102
|
+
1. **Read `<docDir>/CLAUDE.md`** if it exists — it's the project's own playbook.
|
|
103
|
+
2. **Walk up** the directory tree from `<docDir>` looking for `CLAUDE.md`, `.claude/`, `README.md`, or `package.json`/`Cargo.toml`/`pyproject.toml` to identify the project root.
|
|
104
|
+
3. **Surface a relaunch nudge** when you detect project-scoped Claude tools you can't load mid-session:
|
|
105
|
+
- `.claude/skills/`, `.claude/agents/`, `.claude/hooks/`, or a `.mcp.json` you haven't loaded
|
|
106
|
+
- Tell the user: *"I see project-specific Claude tools at `<path>`. I can't load them in this session — open the command palette and run `Relaunch Claude in this folder` if you'd like me to pick them up."*
|
|
107
|
+
|
|
108
|
+
The user is in control: relaunch ends the current conversation, so only suggest it when the project-scoped tools materially change what you can help with.
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{p as Fa,c as La,o as Oa,s,a as i,i as k,g as a,b as l,d as Ra,e as N,m as Da,u as $,f as Na,h as d,t as f,j as C,T as Aa,k as Ba,l as Z,n as w,q as S,r as _,v as c,w as Ma,x as Wa,C as Ua,y as aa,z as ja,A as Pa,B as ea,D as Va,E as Ka,F as qa,G as Ga}from"./index-CfT503n4.js";var Ha=c('<div data-testid="cowork-settings-loading" style="font-size: 12px; color: var(--tandem-fg-subtle);">Loading Cowork status...</div>'),Ja=c(`<div data-testid="cowork-settings-unsupported">Cowork integration is available on Windows in v0.8.0. macOS/Linux support tracked in #316 /
|
|
2
|
-
#317.</div>`),Qa=c('<div data-testid="cowork-settings-undetected"> <a target="_blank" rel="noreferrer" style="color: var(--tandem-accent);">Learn more</a></div>'),Xa=c(`<div data-testid="cowork-enable-confirm" role="dialog" style="border: 1px solid var(--tandem-warning-border); background: var(--tandem-warning-bg); color: var(--tandem-warning-fg-strong); border-radius: var(--tandem-r-3); padding: 8px 10px; font-size: 12px;"><div style="font-weight: 600; margin-bottom: 4px;">Confirm: Enable Cowork</div> <div style="margin-bottom: 8px;">Windows will prompt for admin permission to modify firewall rules. This is expected.
|
|
3
|
-
Tandem will write plugin entries to every detected Cowork workspace.</div> <div style="display: flex; gap: 8px;"><button data-testid="cowork-enable-confirm-btn" type="button">Enable</button> <button data-testid="cowork-enable-cancel-btn" type="button">Cancel</button></div></div>`),Ya=c('<div data-testid="cowork-vethernet-cidr" style="font-size: 12px;">Detected VM subnet: <code> </code></div>'),Za=c('<div><label data-testid="cowork-lan-ip-override"><input data-testid="cowork-lan-ip-override-checkbox" type="checkbox" style="accent-color: var(--tandem-accent);"/> <span>Use LAN IP instead of host.docker.internal</span></label> <div> </div></div>'),ae=c('<div style="font-size: 12px; color: var(--tandem-fg-subtle);">No Cowork workspaces detected yet.</div>'),ee=c('<a href="mailto:maintainers@tandem.invalid?subject=Cowork%20schema%20drift" style="color: var(--tandem-error-fg-strong); text-decoration: underline;">Report</a>'),te=c('<div><span style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"> </span> <span style="font-weight: 600; white-space: nowrap;"> </span> <!></div>'),re=c('<div data-testid="cowork-workspace-table" style="display: flex; flex-direction: column; gap: 4px;"></div>'),oe=c('<label data-testid="cowork-toggle"><input data-testid="cowork-toggle-checkbox" type="checkbox" style="accent-color: var(--tandem-accent);"/> <span>Enable Cowork integration</span></label> <div> </div> <!> <!> <!> <div><div> </div> <!> <button data-testid="cowork-rescan-btn" type="button">Re-scan workspaces</button></div>',1),ne=c('<div data-testid="cowork-settings-error" role="alert"> </div>'),de=c('<div data-testid="cowork-inline-toast" role="alert"> </div>'),ie=c('<div data-testid="cowork-settings" style="display: flex; flex-direction: column; gap: 10px;"><div>Cowork Integration</div> <!> <!> <!></div>');function le(ta,ra){Fa(ra,!0);const oa={success:{bg:"var(--tandem-success-bg)",fg:"var(--tandem-success-fg-strong)",border:"var(--tandem-success-border)"},warning:{bg:"var(--tandem-warning-bg)",fg:"var(--tandem-warning-fg-strong)",border:"var(--tandem-warning-border)"},error:{bg:"var(--tandem-error-bg)",fg:"var(--tandem-error-fg-strong)",border:"var(--tandem-error-border)"}},A="font-size: 11px; font-weight: 600; color: var(--tandem-fg); margin-bottom: 6px; text-transform: uppercase; letter-spacing: 0.5px;",B="font-size: 10px; color: var(--tandem-fg-subtle); margin-top: 4px;",M="border: 1px solid var(--tandem-info-border); background: var(--tandem-info-bg); color: var(--tandem-info-fg-strong); border-radius: var(--tandem-r-3); padding: 8px 10px; font-size: 12px;",W="border: 1px solid var(--tandem-error-border); background: var(--tandem-error-bg); color: var(--tandem-error-fg-strong); border-radius: var(--tandem-r-3); padding: 8px 10px; font-size: 12px;",na="padding: 4px 10px; font-size: 12px; border: 1px solid var(--tandem-accent); border-radius: var(--tandem-r-2); background: var(--tandem-accent); color: var(--tandem-accent-fg); cursor: pointer; font-weight: 600;",da="padding: 4px 10px; font-size: 12px; border: 1px solid var(--tandem-border-strong); border-radius: var(--tandem-r-2); background: var(--tandem-surface); color: var(--tandem-fg-muted); cursor: pointer;",u=La(()=>!0),{refetch:I}=u;let z=N(null),T=N(null),b=N(!1);const U=Da(Ua);Oa(()=>U.cancel());const F=$(()=>Na(u.status));async function E(t,e){_(b,!0);try{const n=await Ma();await t(n),_(z,null)}catch(n){const x=n instanceof Error?n.message:String(n),y=Wa(x);_(z,`${e}: ${y}`)}finally{_(b,!1)}}async function ia(){await E(async t=>{await aa(t,!0),await I(),_(T,null)},"Failed to enable Cowork")}async function sa(){await E(async t=>{await aa(t,!1),await I()},"Failed to disable Cowork")}function la(){U.schedule(()=>{E(async t=>{await ja(t),await I()},"Re-scan failed")})}async function ca(t){await E(async e=>{await qa(e,t),await I()},"Failed to update LAN-IP override")}function va(t){const e=ea(t),n=oa[Ga(e)];return`display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 4px 6px; border: 1px solid ${n.border}; background: ${n.bg}; color: ${n.fg}; border-radius: var(--tandem-r-2); font-size: 11px;`}var j=ie(),P=d(j);s(P,A);var V=i(P,2);{var pa=t=>{var e=Ha();l(t,e)},ga=t=>{var e=Ja();s(e,M),l(t,e)},ba=t=>{var e=Qa();s(e,M);var n=d(e);n.nodeValue="Cowork not detected on this system. ";var x=i(n);f(()=>C(x,"href",`${Aa}#cowork`)),l(t,e)},fa=t=>{const e=$(()=>u.status);var n=oe(),x=Ba(n),y=d(x),L=i(x,2);s(L,B);var wa=d(L),q=i(L,2);{var xa=o=>{var v=Xa(),p=i(d(v),4),r=d(p);s(r,na);var g=i(r,2);s(g,da),f(()=>{r.disabled=a(b),g.disabled=a(b)}),S("click",r,()=>void ia()),S("click",g,()=>{_(T,null)}),l(o,v)};k(q,o=>{a(T)==="enable"&&o(xa)})}var G=i(q,2);{var _a=o=>{var v=Ya(),p=i(d(v)),r=d(p);f(()=>w(r,a(e).vethernetCidr)),l(o,v)};k(G,o=>{a(e).vethernetCidr!==null&&o(_a)})}var H=i(G,2);{var ya=o=>{var v=Za(),p=d(v),r=d(p),g=i(p,2);s(g,B);var D=d(g);f(()=>{s(p,`display: flex; align-items: center; gap: 8px; font-size: 12px; cursor: ${a(b)?"wait":"pointer"};`),Z(r,a(e).useLanIpOverride),r.disabled=a(b),w(D,`Fallback: ${a(e).lanIpFallback??""}`)}),S("change",r,m=>void ca(m.target.checked)),l(o,v)};k(H,o=>{a(e).lanIpFallback!==null&&o(ya)})}var ha=i(H,2),O=d(ha);s(O,A);var Ca=d(O),J=i(O,2);{var Sa=o=>{var v=ae();l(o,v)},Ia=o=>{var v=re();Pa(v,21,()=>a(e).workspaces,p=>`${p.workspaceId}/${p.vmId}`,(p,r)=>{const g=$(()=>ea(a(r))),D=$(()=>Va(a(g)));var m=te(),Q=d(m),za=d(Q),X=i(Q,2),Ta=d(X),Ea=i(X,2);{var $a=h=>{var Y=ee();f(()=>C(Y,"data-testid",`cowork-workspace-report-${a(r).workspaceId}-${a(r).vmId}`)),l(h,Y)};k(Ea,h=>{a(g)==="schemaDrift"&&h($a)})}f(h=>{C(m,"data-testid",`cowork-workspace-row-${a(r).workspaceId}-${a(r).vmId}`),C(m,"data-status",a(g)),C(m,"title",a(r).failureDetail??a(r).path),s(m,h),w(za,`${a(r).workspaceId??""} / ${a(r).vmId??""}`),w(Ta,a(D))},[()=>va(a(r))]),l(p,m)}),l(o,v)};k(J,o=>{a(e).workspaces.length===0?o(Sa):o(Ia,-1)})}var R=i(J,2);s(R,"padding: 4px 10px; font-size: 12px; border: 1px solid var(--tandem-border-strong); border-radius: var(--tandem-r-2); background: var(--tandem-surface); color: var(--tandem-fg-muted); cursor: pointer; margin-top: 8px;"),f(()=>{s(x,`display: flex; align-items: center; gap: 8px; cursor: ${a(b)?"wait":"pointer"}; font-size: 12px; color: var(--tandem-fg); min-height: 24px;`),Z(y,a(e).enabled),y.disabled=a(b),w(wa,`Token provisioned: ${a(e).enabled?"yes":"no"}`),w(Ca,`Workspaces (${a(e).workspaces.length??""})`),R.disabled=a(b)}),S("change",y,o=>{o.target.checked?_(T,"enable"):sa()}),S("click",R,la),l(t,n)};k(V,t=>{u.loading?t(pa):a(F)==="unsupported"?t(ga,1):a(F)==="undetected"?t(ba,2):a(F)==="normal"&&u.status!==null&&t(fa,3)})}var K=i(V,2);{var ua=t=>{var e=ne();s(e,W);var n=d(e);f(()=>w(n,`Failed to load Cowork status: ${u.error??""}`)),l(t,e)};k(K,t=>{u.error&&!u.status&&t(ua)})}var ma=i(K,2);{var ka=t=>{var e=de();s(e,W);var n=d(e);f(()=>w(n,a(z))),l(t,e)};k(ma,t=>{a(z)&&t(ka)})}l(ta,j),Ra()}Ka(["change","click"]);export{le as default};
|