pindeck 2.9.0 → 3.0.0-beta.2

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/README.md CHANGED
@@ -1,12 +1,14 @@
1
1
  # PinDeck
2
2
 
3
- > **macOS, and Linux as a beta.** On Linux, PinDeck runs as a headless server that you use from your
4
- > Mac's browser through an SSH tunnel (see [Running on a Linux server](#running-on-a-linux-server-beta)).
5
- > Windows is not supported.
3
+ > **macOS, and Linux as a beta · Version 3 beta preview** (`3.0.0-beta.2`). On Linux, PinDeck runs as a
4
+ > headless server that you use from your Mac's browser through an SSH tunnel (see
5
+ > [Running on a Linux server](#running-on-a-linux-server-beta)). Windows is not supported. The beta is on
6
+ > npm’s `beta` channel and the [`v3-beta`](https://github.com/ehabets/PinDeck/tree/v3-beta) branch; stable
7
+ > releases (`npx pindeck`) stay on 2.x. See installation below.
6
8
 
7
9
  **Pin it. Say it. Done.** PinDeck is a local editor for HTML slide decks (reveal.js): the slides on
8
- the left, the slide in the middle, and a chat with Claude Code on the right. You click elements to
9
- pin them, write what to change, and Claude edits the HTML and CSS. The slide updates in place.
10
+ the left, the slide in the middle, and a chat with Claude Code or OpenAI Codex on the right. You click elements to
11
+ pin them, write what to change, and the selected assistant edits the HTML and CSS. The slide updates in place.
10
12
 
11
13
  ![PinDeck: the slide list, slide 10 of the example deck with two numbered pins, and the chat with Claude's answer to an earlier request and a new one being typed](docs/pindeck.png)
12
14
 
@@ -16,43 +18,87 @@ checked the layout; the next request, still being typed, refers to the new pins
16
18
  ## What it does
17
19
 
18
20
  * **Point instead of describe.** Pins name the exact element, word or table cell; "make 2 match 1"
19
- is enough. Claude also gets the measured layout of the slide, so it sees overlaps and overflow
21
+ is enough. The assistant also gets the measured layout of the slide, so it sees overlaps and overflow
20
22
  without screenshots.
21
23
  * **Edit by hand, too.** Move, resize and nudge elements with snapping, edit text in place (math stays
22
24
  TeX), write speaker notes, add, hide, move and delete slides. One undo history covers your edits and
23
- Claude's.
24
- * **Stay on brand.** `brand.css` is locked: Claude edits `index.html` and `theme.css` only, and checks
25
- its own work with PinDeck's layout command.
25
+ the assistant's.
26
+ * **Stay on brand.** `brand.css` is locked: the assistant edits the deck, its styles and other files of the
27
+ deck folder, never `brand.css` or `vendor/`, and checks its own work with PinDeck's layout command.
26
28
  * **Bring material in.** Attach figures, audio and PDFs to a message, or import slides from PowerPoint
27
29
  into the deck's own layouts.
28
30
  * **Get it out.** Present from the browser, or export a handout or presentation PDF.
29
- * **Local and small.** One Node process on 127.0.0.1, no npm dependencies, your normal Claude login.
31
+ * **Local and small.** One Node process on 127.0.0.1, no npm dependencies, your existing Claude Code or Codex login.
30
32
 
31
33
  ## Quick start
32
34
 
33
35
  Runs on macOS, and on Linux as a beta. The npm package declares `"os": ["darwin", "linux"]`, so normal
34
36
  npm installation rejects other operating systems. Overriding that check does not make those platforms
35
37
  supported. Running the server from a cloned repository on another OS shows a local support notice
36
- instead of opening the deck or starting Claude.
38
+ instead of opening the deck or starting a backend.
39
+
40
+ Install the beta from npm:
37
41
 
38
42
  ```bash
39
- npx pindeck path/to/deck # from npm
43
+ npx pindeck@beta /path/to/deck
44
+ # Or select this exact beta:
45
+ npx pindeck@3.0.0-beta.2 /path/to/deck
46
+ ```
47
+
48
+ Or run it from the `v3-beta` branch, which includes the example deck:
40
49
 
41
- git clone https://github.com/ehabets/PinDeck.git && cd PinDeck
42
- node server.mjs ocean-example # or from source, with the example deck in this repository
50
+ ```bash
51
+ git clone --branch v3-beta --single-branch https://github.com/ehabets/PinDeck.git PinDeck-v3
52
+ cd PinDeck-v3
53
+ node server.mjs ocean-example
43
54
  ```
44
55
 
56
+ Use `npx pindeck@latest /path/to/deck` for the stable release; the beta channel does not replace it.
57
+ Releases, with notes for each beta, are on [GitHub](https://github.com/ehabets/PinDeck/releases).
58
+
45
59
  Then open the login link PinDeck prints (`PinDeck: http://127.0.0.1:5180/login?code=...`). It works
46
- once and logs this browser in; for a new one, run `pindeck login` (or `node server.mjs login`). Requirements: Node 20 or later and the `claude` CLI, signed in
47
- with your normal Claude account. Chrome (or Chromium, Edge, Brave) is optional and is used for
48
- screenshots and PDF export; PowerPoint or Keynote (macOS) for pictures in the PowerPoint import.
49
-
50
- PinDeck adds element ids to the deck's `index.html` when it opens it (see below), so try it on a copy
51
- of a deck first. Options: `--port 5180`, `--model <name>` (for this run only; otherwise the model in
52
- `~/.pindeck/config.json`), `--help`. The deck folder defaults to the current folder and must contain
53
- `index.html`. Runtime files (chat history, screenshots, backups) are kept in `~/.pindeck/` (change with
60
+ once and logs this browser in; for a new one, run `pindeck login` (or `node server.mjs login`).
61
+
62
+ The deck is a file (`pindeck path/to/talk.html`) or a folder, which opens its `index.html`; without
63
+ one, the current folder. Several decks can share a folder, each with its own chat and undo history;
64
+ they share the folder's `theme.css` and `brand.css`, and the assistant edits only the deck that is open.
65
+ PinDeck adds element ids to the deck file when it opens it (see below), so try it on a copy of a deck
66
+ first. Options: `--port 5180`, `--provider claude|codex`, `--model <name>` (for this run only; otherwise the
67
+ model in `~/.pindeck-v3/config.json`), `--asset-root <folder>` (see [Files outside the deck folder](#files-outside-the-deck-folder)),
68
+ `--help`. Runtime files (chat history, screenshots, backups) are kept in `~/.pindeck-v3/` (change with
54
69
  `PINDECK_HOME`), not in the deck. `ocean-example/` is a complete example deck to start from.
55
70
 
71
+ ## Compatibility and requirements
72
+
73
+ * **Runtime:** Node.js 20 or later, plus a modern browser. The release workflow uses Node.js 24.
74
+ * **Backends:** install and sign in to Claude Code (`claude auth login`) or OpenAI Codex (`codex login`).
75
+ The corresponding executable must be on `PATH`; `CLAUDE_BIN` and `CODEX_BIN` can override its path.
76
+ Only the selected backend is required for editing. Model and effort availability comes from that
77
+ backend and account, and may differ from the provider’s website or desktop app.
78
+ * **Platforms:** macOS, and Linux as a beta (see [Running on a Linux server](#running-on-a-linux-server-beta)).
79
+ Windows is not supported. The npm package declares `"os": ["darwin", "linux"]`, so normal npm
80
+ installation rejects other operating systems. This check can be overridden; bypassing it does not make
81
+ those platforms supported. Source launches on unsupported platforms show a local support page without
82
+ opening the deck or starting a backend. Native Windows support is a possible v4 feature, not part of this beta.
83
+ * **Screenshots and PDF export:** require Chrome, Chromium, Edge or Brave where the server runs.
84
+ * **PDF input through Codex:** pages are rendered with macOS PDFKit, or Poppler on Linux. Page-text
85
+ extraction uses `osascript` and is macOS-only; on Linux Codex reads the rendered page image. Claude uses
86
+ its native PDF-reading capabilities.
87
+ * **PowerPoint import:** basic `.pptx` parsing uses Node.js; rendered slide pictures require
88
+ PowerPoint or Keynote on macOS, or LibreOffice and Poppler on Linux.
89
+ * **Linux tools:** Poppler, LibreOffice and ffmpeg, each optional; features that need a missing one are
90
+ switched off with the reason shown in the editor.
91
+
92
+ The initial live editing checks used Codex CLI **0.144.1** and Claude Code **2.1.289**.
93
+ Further live checks on macOS passed with Codex CLI **0.162.1** and Claude Code **2.1.301**:
94
+ editing, Claude effort changes, Codex model changes, conversation handover, undo/redo,
95
+ cancellation and recovery. These are tested versions, not guaranteed minimum requirements. Backends must support the discovery and session interfaces
96
+ used by PinDeck. Older CLIs can expose fewer models or fail initialization.
97
+
98
+ Codex integration uses OpenAI’s experimental `dynamicTools` extension to the app-server.
99
+ A future Codex update may require a PinDeck adapter update. Incompatible backends report startup
100
+ errors; PinDeck does not fall back to broader permissions. Recheck backend compatibility when upgrading.
101
+
56
102
  ## Preparing a deck
57
103
 
58
104
  PinDeck works with reveal.js decks whose slides are `<section>` elements inside `.slides`. Two
@@ -68,7 +114,7 @@ the example's `deck.js` does for the footer), count only slides without that att
68
114
  numbers will jump where a slide is hidden.
69
115
 
70
116
  Without them PinDeck runs no footer check and locks only `.slide-foot` and `.logo`. A file called
71
- `brand.css` in the deck folder is locked for Claude. See `ocean-example/` for a complete deck.
117
+ `brand.css` in the deck folder is locked for both backends. See `ocean-example/` for a complete deck.
72
118
 
73
119
  ## Making your own template
74
120
 
@@ -79,18 +125,32 @@ cp -R ocean-example my-template
79
125
  ```
80
126
 
81
127
  1. **Brand in `brand.css`:** palette, backgrounds, footer and logo positions. PinDeck locks this file
82
- for Claude, so edit it by hand (or with Claude Code outside PinDeck). Replace the logos in
128
+ for both backends, so edit it by hand outside PinDeck. Replace the logos in
83
129
  `assets/` too.
84
130
  2. **Everything else in `theme.css`:** type sizes, spacing, layouts and components. This can be done
85
- in PinDeck with Claude.
131
+ in PinDeck with the selected assistant.
86
132
  3. **`index.html`:** the footer text in the `data-` attributes of `<div class="reveal">`, and the
87
133
  `pindeck:footer-top` and `pindeck:locked` meta tags if the footer or the brand elements move.
88
134
  `deck.js` holds the math macros, the footer markup and the audio player.
89
- 4. **`README.md` and `CLAUDE.md`:** Claude follows both, so describe the new classes and rules there.
135
+ 4. **`README.md`, `CLAUDE.md` and `AGENTS.md`:** both backends follow the deck's `CLAUDE.md` and
136
+ `AGENTS.md`, so describe the new classes and rules there; the assistant can read `README.md` for details.
90
137
  Refer to PinDeck's commands as "the PinDeck CLI command given in the system prompt", as the
91
- example's `CLAUDE.md` does; PinDeck tells Claude the right path wherever the deck is. An older
138
+ example's `CLAUDE.md` does; PinDeck tells the assistant the right path wherever the deck is. An older
92
139
  `node ../cli.mjs` in a deck's `CLAUDE.md` is replaced with that command.
93
140
 
141
+ **Instructions for the assistant.** PinDeck gives both backends the deck folder's own `CLAUDE.md` and
142
+ `AGENTS.md`. It does not load such files from parent folders, as Claude Code and Codex do in a terminal:
143
+ those are usually written for coding (run the tests, commit), which PinDeck's assistant cannot do; they
144
+ would change with where the deck happens to sit; and on a shared server a parent folder may be writable
145
+ by others. To share rules between decks, say so in the deck's own file, in one of two ways:
146
+
147
+ - **Import:** `Follow @../shared/STYLE.md for wording.` includes that file's text. Paths are relative to
148
+ the file they are in (or absolute, or `~/...`); imported files may import others, up to 5 levels,
149
+ each file once, 200 KB in all. Only text files are imported; an e-mail address, an `@name` that is
150
+ not a file, and anything in code are left as written.
151
+ - **Pointer:** `The style rules are in ../shared/STYLE.md; read them first.` The assistant reads the file
152
+ when it needs it.
153
+
94
154
  **New slide** and the PowerPoint import model new slides on the deck's own: keep a slide with class
95
155
  `title-slide`, one with `section-slide`, and a content slide with `.slide-head` (`h2`, optionally a
96
156
  `.kicker` or `.sub`) and `.slide-body`. Imported tables take the class of the deck's first table,
@@ -98,29 +158,94 @@ and imported audio copies the first `.demo-row` (with a `data-target`). Without
98
158
  slide, those PowerPoint slides are imported as content slides; without a `.demo-row`, audio gets
99
159
  plain players.
100
160
 
101
- ## Configuration
102
-
103
- `~/.pindeck/config.json` (or `$PINDECK_HOME/config.json`) is created on first start:
161
+ ## Models, providers and configuration
162
+
163
+ Open the existing model menu in the chat header to choose a model under **Claude** or
164
+ **OpenAI / ChatGPT**. Choices come from the installed backends: Claude's initialization response
165
+ and Codex's paginated `model/list`. Hidden or explicitly unavailable entries are omitted. Effort
166
+ choices appear in a separate reasoning-effort selector beside the model and follow its reported
167
+ capabilities. Readable labels preserve the exact backend values (for example, Extra high sends `xhigh`).
168
+ The selector shows provider descriptions where available and distinguishes **Model default** from a
169
+ **PinDeck default** fallback. Models without effort controls show a disabled Effort N/A selector.
170
+ Effort choices are remembered per provider/model in `effortPreferences` in config.json. A model with
171
+ no saved choice uses its default; an unsupported saved choice falls back to the current default.
172
+ The picker defaults to GPT-6 models (including 6.x) and the newest discovered Fable, Opus, Sonnet,
173
+ and Haiku model in each family. Claude versions are compared numerically from discovered names;
174
+ a rolling family alias wins ties, avoiding duplicate entries. **Show all available models** reveals
175
+ the full discovered catalog; this display preference is remembered in the browser. A currently selected
176
+ older model remains visible and is never switched automatically by the filter.
177
+ **Refresh models** retries discovery. A missing CLI, missing sign-in, or discovery failure is shown in its provider group.
178
+ These are backend-reported choices, not a guarantee of current quota or entitlement; a later rejected
179
+ request reports its error. Older CLIs may expose fewer models than a provider's website.
180
+
181
+ Changing models within a provider resumes that provider's conversation. Changing providers opens
182
+ **Switch with summary**, **Start fresh**, and **Cancel**. A switch prepares the new backend before
183
+ replacing the old one. Chat remains visible with a divider, and deck files and undo/redo are retained.
184
+ Switching back to a provider creates another new session; it does not silently restore old context.
185
+
186
+ The current handover is a local, bounded digest of recent user/assistant messages and notices, not an
187
+ additional model-generated summary. It excludes tool output, needs no extra inference request, and is
188
+ sent with the first message to the new session. Earlier messages may be omitted. **Start fresh** sends
189
+ no conversation digest; the assistant still receives PinDeck instructions and the current deck context.
190
+
191
+ Settings are saved when changed in `~/.pindeck-v3/config.json` (or `$PINDECK_HOME/config.json`):
104
192
 
105
193
  ```json
106
194
  {
195
+ "provider": "claude",
107
196
  "model": "sonnet",
108
197
  "effort": "medium"
109
198
  }
110
199
  ```
111
200
 
112
- `model` is `opus` or `sonnet`; `effort` is `low`, `medium`, `high`, `xhigh` or `max`. A choice in the
113
- chat header's menu is written to this file, so it always shows what PinDeck uses. Edit it while
114
- PinDeck is stopped, or simply use the menu.
201
+ Legacy configuration without `provider` is interpreted as Claude. Unsupported effort choices are
202
+ adjusted to the discovered model's default when starting. Provider-tagged chat/session records prevent
203
+ one backend from resuming the other's IDs. `--provider` and `--model` override startup settings for
204
+ that run; a later menu selection is saved. `CLAUDE_BIN` and `CODEX_BIN` override executable paths.
205
+
206
+ The v3 preview defaults to **`~/.pindeck-v3`**, keeping v2 settings and conversations separate. It does
207
+ not copy the v2 directory automatically. To run both versions at once, use another port, for example:
208
+
209
+ ```bash
210
+ node server.mjs /path/to/a/deck-copy --port 5183 --provider codex
211
+ ```
212
+
213
+ ## Backend permissions and attachments
214
+
215
+ Claude uses its native Read/Edit tools with PinDeck's narrow allowlist, safe mode, and no inherited
216
+ MCP configuration. Codex runs with a read-only sandbox, command execution disabled, and no approval
217
+ grants. Inherited MCP servers and plugins are disabled in the thread configuration. Codex receives
218
+ three host-controlled tools: `pindeck_read`, `pindeck_edit`, and `pindeck_command`.
219
+ Only `index.html` and `theme.css` can be edited. Linked writable files and paths outside the allowed
220
+ roots are rejected. Commands are restricted to PinDeck's layout, shot, slides, pdf and pdf-figure
221
+ helpers, executed with argument arrays rather than a shell. `brand.css` also retains the server's
222
+ post-turn restoration safeguard.
223
+
224
+ Codex reads text, PNG/JPEG/WebP/GIF images and screenshots through the bridge. SVG is read as text.
225
+ Attached PDFs are handled page by page using PDFKit on macOS: extracted text plus a rendered page image.
226
+ PDF reading/rendering through this bridge is currently macOS-only. Audio is embedded by path and is
227
+ not transcribed. Read operations have a 12 MB limit; long text can be read in chunks.
228
+
229
+ ### Files outside the deck folder
230
+
231
+ A deck can use files from a folder around it, such as a style sheet or figures shared by several
232
+ talks: start PinDeck with `--asset-root <folder>` (repeatable), or list the folders in config.json as
233
+ `"assetRoots": ["~/talks"]`. When the folder contains the deck, PinDeck serves the deck from inside
234
+ it, so relative paths such as `../shared/style.css` work, in the editor, in screenshots and in PDF
235
+ exports. Claude may read those files but does not edit them.
236
+
237
+ Symlinks: PinDeck serves a file only if, with symlinks followed, it lies inside the deck folder or an
238
+ asset root. A symlink in the deck that leads anywhere else is refused (403). To use a symlinked
239
+ library folder, add the folder it points to as an asset root; it does not have to contain the deck.
115
240
 
116
241
  ## Running on a Linux server (beta)
117
242
 
118
243
  PinDeck can run on a Linux machine where the deck lives, with the editor in your Mac's browser. The
119
- deck, PinDeck, Claude Code and the deck's git clone all stay on the server; the browser only shows the
244
+ deck, PinDeck, the backend (Claude Code or Codex) and the deck's git clone all stay on the server; the browser only shows the
120
245
  editor. The server listens on 127.0.0.1 there, as on a Mac, and an SSH tunnel brings that port to your Mac.
121
246
 
122
247
  On the server you need Node 20 or later (22 or 24 recommended), git if the deck is in a repository, and
123
- the `claude` CLI installed and logged in under your account there, if you want the chat. Without it,
248
+ the `claude` or `codex` CLI installed and logged in under your account there, if you want the chat. Without one,
124
249
  editing by hand still works. Then, in one SSH session on the server:
125
250
 
126
251
  ```bash
@@ -144,17 +269,26 @@ Other users of a shared server cannot open the editor or read the deck through P
144
269
  protects it is only in your browser's cookie and in PinDeck's own files in `~/.pindeck` (readable only by
145
270
  you), never in a page or on a command line.
146
271
 
147
- What works on Linux in this beta: editing by hand, speaker notes, slides, pins, the chat with Claude,
272
+ What works on Linux in this beta: editing by hand, speaker notes, slides, pins, the chat with Claude or Codex,
148
273
  Undo/Redo, live reload when files change on the server, layout checks, attaching figures, audio and
149
- PDFs, `git-setup`, and, with Chrome or Chromium installed on the server, screenshots and PDF export
150
- (`PINDECK_LINUX_CHROME=0` turns these off). What does not, with the reason shown in the editor (hover
151
- over "Server: Linux (beta)"): pictures of PDF pages (`pdf-figure`, needs macOS PDFKit) and the
152
- PowerPoint import. Claude is told which commands are not available. Chromium from snap may not be
153
- allowed to use hidden folders in your home folder such as `~/.pindeck`; if screenshots fail with it, set
274
+ PDFs, `git-setup` and the PowerPoint import. Some features use programs on the server, and are on when
275
+ they are installed (on Debian or Ubuntu: `sudo apt install poppler-utils libreoffice-impress ffmpeg`):
276
+
277
+ | Feature | Needs |
278
+ |---|---|
279
+ | Screenshots, layout checks without an open editor, PDF export | Chrome or Chromium (`PINDECK_LINUX_CHROME=0` turns these off) |
280
+ | Pictures of PDF pages (`pdf-figure`) | Poppler (`pdftoppm`, `pdfinfo`) |
281
+ | Pictures of PowerPoint slides in the import (thumbnails, diagrams) | LibreOffice (`soffice`) and Poppler |
282
+ | Waveforms of audio other than WAV in the import | ffmpeg |
283
+
284
+ Without them the editor says what is missing (hover over "Server: Linux (beta)"), and the assistant is told
285
+ which commands are not available. LibreOffice may draw some slides a little differently from PowerPoint;
286
+ the pictures are references and figures, the slides themselves are rebuilt from the file. Chromium from snap may not be
287
+ allowed to use hidden folders in your home folder such as `~/.pindeck-v3`; if screenshots fail with it, set
154
288
  `PINDECK_HOME` to a folder that is not hidden, or install Chrome or Chromium from a package.
155
289
 
156
- Run one PinDeck per deck. While PinDeck or Claude is working on a deck, do not edit it with other tools
157
- or switch git branches at the same time. Stop first, or wait until Claude has finished. PinDeck notices
290
+ Run one PinDeck per deck. While PinDeck or the assistant is working on a deck, do not edit it with other tools
291
+ or switch git branches at the same time. Stop first, or wait until the assistant has finished. PinDeck notices
158
292
  changes made by other programs and reloads, and it refuses Undo, Redo or an edit that would overwrite
159
293
  them. Its checks cannot rule out every race with a program that writes at the same moment.
160
294
 
@@ -168,7 +302,7 @@ node cli.mjs git-setup path/to/deck
168
302
 
169
303
  This adds a line like `path/to/deck/*.html filter=pindeck-ids` to `.gitattributes` (commit it) and
170
304
  defines the filter in the clone's git config: git then strips the ids whenever it reads the deck's
171
- HTML. The ids stay in the file on disk, where PinDeck and Claude need them, but commits and diffs
305
+ HTML. The ids stay in the file on disk, where PinDeck and the assistant need them, but commits and diffs
172
306
  never contain them, and a change that only adds ids does not show in `git status`. Your own edits
173
307
  show and commit as usual. Clones without the filter configured simply see the file as it is.
174
308
 
@@ -178,24 +312,24 @@ show and commit as usual. Clones without the filter configured simply see the fi
178
312
  |---|---|
179
313
  | Select, move, resize | Select mode (`V`, the default): click an element to select it, drag to move, pull a handle to resize. Elements snap to the slide edges and centre, the footer rule and other elements (pink guides); hold `Alt` to turn snapping off, `Shift` to move along one axis. Images keep their aspect ratio (`Shift` toggles). Arrow keys nudge by 1 px, `Shift`+arrows by 10 px. `Delete` removes the selected element after a confirmation; a picture goes with its caption, and files nothing else in the deck uses can be deleted with it. `⌘Z` brings both back. |
180
314
  | Edit text | Double-click the text (or select it and press `Enter`). The element shows its source, so math appears as TeX, e.g. `\(\mu=0.5\)`. `Enter` or a click elsewhere saves, `Esc` cancels, `Shift+Enter` adds a line break, `⌘B` / `⌘I` make text bold or italic. In a bullet, `Enter` splits it at the cursor and continues in a new bullet below; `Enter` on an empty new bullet removes it and stops. Pasted text arrives as plain text. Table cells are edited one at a time. |
181
- | Undo, redo | `⌘Z` / `⇧⌘Z` or the toolbar arrows. One history for your hand edits and Claude's changes. |
315
+ | Undo, redo | `⌘Z` / `⇧⌘Z` or the toolbar arrows. One history for your hand edits and the assistant's changes. |
182
316
  | Pin elements | Press `P` (or the Pin button), click elements; each click gets a number. Click a marker to remove it, `Esc` to stop. |
183
- | Ask Claude | Write in the sidebar and refer to pins by number: "1: align with 2, make 3 smaller". The selected element is sent too, so "make this smaller" works. `Enter` sends, `Shift+Enter` adds a new line. |
317
+ | Ask the assistant | Write in the sidebar and refer to pins by number: "1: align with 2, make 3 smaller". The selected element is sent too, so "make this smaller" works. `Enter` sends, `Shift+Enter` adds a new line. |
184
318
  | Visual questions | **+** in the chat, then **Screenshot**: the current slide with its pin markers is attached when you send. |
185
319
  | Review | Each answer ends with the changed files, a **Diff** button and an **Undo** button (most recent change). |
186
- | Stop | **Stop** interrupts Claude; the conversation continues. **New chat** starts over. |
320
+ | Stop | **Stop** interrupts the assistant; the conversation continues. **New chat** starts over. |
187
321
  | New slide | **+ New slide** above the slide list: a **content slide** (title and bullets), a **section divider**, or a **duplicate** of the current slide. It is added after the current slide, modelled on the deck's own slides, and its title opens for typing. |
188
- | Import from PowerPoint | **+ New slide** → **Import from PowerPoint…**, then choose a `.pptx` file and tick the slides you want (hidden PowerPoint slides start unticked). They are added after the current slide in this deck's own layouts, and Claude can improve the complex ones; see [PowerPoint import](#powerpoint-import). `⌘Z` removes them again, with their figures. |
322
+ | Import from PowerPoint | **+ New slide** → **Import from PowerPoint…**, then choose a `.pptx` file and tick the slides you want (hidden PowerPoint slides start unticked). They are added after the current slide in this deck's own layouts, and the assistant can improve the complex ones; see [PowerPoint import](#powerpoint-import). `⌘Z` removes them again, with their figures. |
189
323
  | Speaker notes | The **Notes** field under the slide shows the current slide's notes (the speaker view, `S`, shows them when presenting). Type to change them; they are saved when you click elsewhere or press `⌘Enter`, and `Esc` puts back what was there. A slide without notes gets them; emptying the field removes them. Notes with formatting other than line breaks are edited as HTML. Drag the line above the notes to give them more or less room (double-click it for the default size); collapse them with their header. |
190
324
  | Move a slide | Drag it up or down in the slide list; an orange line shows where it will go. It moves with its notes and the comment above it, and `⌘Z` puts it back. Slides inside vertical stacks cannot be moved yet. |
191
325
  | Hide a slide | The eye on a slide in the list, or `H`. A hidden slide stays in the deck and in PinDeck (greyed out, with a banner) but is skipped when presenting and in PDFs; **Export ▾** can include hidden slides. Show it the same way. |
192
- | Model and effort | Click **Claude ▾** in the chat header: Opus or Sonnet, and the effort (how long Claude thinks: low to max). The conversation continues after a change. The choice is saved in `~/.pindeck/config.json` (default: Sonnet, medium). |
193
- | Attach figures and audio | **+** in the chat, then **File…**, drag and drop onto the chat or a slide, or paste an image. Files are saved in `<deck>/figures/` straight away; audio also gets a waveform SVG. Then ask Claude, e.g. "put this figure on a new slide after 4". Removing an attachment before sending deletes the file again. |
194
- | Attach a PDF | The same way: a figure or table to take over, or a paper or report with more details. PDFs stay outside the deck (in `~/.pindeck`); Claude reads them, rebuilds tables as HTML, and renders a figure (a page or a cropped part of one) into `<deck>/figures/` as PNG with `cli.mjs pdf-figure` (macOS). E.g. "put Fig. 3 of this paper on slide 7" or "add the results of Table 2 to slide 9". |
326
+ | Model and effort | Choose a discovered Claude or OpenAI model in the chat header, then use the separate reasoning-effort selector. Provider changes offer an optional handover; same-provider changes continue the conversation. The choice is saved in `~/.pindeck-v3/config.json`. |
327
+ | Attach figures and audio | **+** in the chat, then **File…**, drag and drop onto the chat or a slide, or paste an image. Files are saved in `<deck>/figures/` straight away; audio also gets a waveform SVG. Then ask the assistant, e.g. "put this figure on a new slide after 4". Removing an attachment before sending deletes the file again. |
328
+ | Attach a PDF | The same way: a figure or table to take over, or a paper or report with more details. PDFs stay outside the deck (in `~/.pindeck-v3`); the assistant reads them, rebuilds tables as HTML, and renders a figure (a page or a cropped part of one) into `<deck>/figures/` as PNG with `cli.mjs pdf-figure` (macOS, or Linux with Poppler). E.g. "put Fig. 3 of this paper on slide 7" or "add the results of Table 2 to slide 9". |
195
329
  | Export PDF | **Export ▾** in the toolbar. **Handout PDF**: one page per slide, build steps shown complete. **Presentation PDF**: one page per click, so a slide with two build steps gives three pages. Files go to `<deck>/export/`. Needs Chrome or Chromium. |
196
330
  | Navigate | Slide list, `←` `→`, or the toolbar. **Present** opens the deck in a new tab. |
197
331
 
198
- You can also edit `index.html` or `theme.css` in any editor; the slide reloads, and Claude is told
332
+ You can also edit `index.html` or `theme.css` in any editor; the slide reloads, and the assistant is told
199
333
  the files changed. CSS changes are swapped in without a reload.
200
334
 
201
335
  ## PowerPoint import
@@ -230,10 +364,10 @@ The import keeps what can be carried over exactly and makes pictures of the rest
230
364
  accents, braces, matrices and the bold, italic and double-struck math letters are converted.
231
365
  * **Hidden slides** stay hidden (`data-visibility="hidden"`).
232
366
 
233
- **Hand-off to Claude.** Slides the import can only approximate are tagged *complex* in the dialog:
367
+ **Hand-off to the assistant.** Slides the import can only approximate are tagged *complex* in the dialog:
234
368
  diagrams of many shapes or tables with shapes over them that became pictures, pictures placed inside
235
369
  text (often equations as images), build steps on parts of a diagram, slides without a title, and
236
- text that had to shrink to fit. With **Then ask Claude to improve the complex slides** ticked, Claude
370
+ text that had to shrink to fit. With **Then ask the assistant to improve the complex slides** ticked, the assistant
237
371
  gets these slides right after the import, each with a picture of the original slide and the texts of
238
372
  its diagrams, and rebuilds diagrams as SVG, equations as TeX and tables as HTML in the deck's style.
239
373
  The request shows in the chat like your own messages, and its changes can be undone as usual.
@@ -245,12 +379,12 @@ Without PowerPoint or Keynote (or on other systems than macOS) only the text and
245
379
  * **Element ids.** On start, the server gives every slide and every visible element a readable
246
380
  `data-eid` attribute (`summary`, `summary/callout`, `summary/li-2`). Only the attribute is
247
381
  inserted; the rest of the file stays as it was. A backup of the file before the first change is
248
- kept in `~/.pindeck/decks/<deck>/backups/`. Ids survive edits, so pins, layout reports and Claude all name
382
+ kept in `~/.pindeck-v3/decks/<deck>/backups/`. Ids survive edits, so pins, layout reports and the assistant all name
249
383
  the same element.
250
384
  * **Hand edits.** A move is stored as the CSS `translate` property and a resize as `width`/`height`,
251
385
  inline on the element in `index.html`. `translate` moves an element without changing the layout
252
386
  around it, for elements in the flow and absolutely positioned ones alike. Only that one style
253
- attribute is rewritten; the rest of the file stays as it was. Claude gets a note about each hand
387
+ attribute is rewritten; the rest of the file stays as it was. The assistant gets a note about each hand
254
388
  edit. Brand-locked elements (footer, logos) cannot be selected.
255
389
  * **Text edits.** While you type, the element holds its source HTML from `index.html`, not the
256
390
  rendered slide, so math stays TeX and nothing that KaTeX or the deck script generates ends up in
@@ -262,21 +396,28 @@ Without PowerPoint or Keynote (or on other systems than macOS) only the text and
262
396
  locked.
263
397
  * **Layout instead of screenshots.** With each message the editor measures the current slide and
264
398
  every pinned slide in your browser: position and size of every element, font sizes, and issues
265
- (content below the footer rule, outside the slide, overlaps, clipped text). Claude gets this as
399
+ (content below the footer rule, outside the slide, overlaps, clipped text). The assistant gets this as
266
400
  text. To check its own work it runs the PinDeck CLI's `layout <slide>...` (`--issues` for the
267
401
  issues only). Screenshots are
268
- only taken when you tick the box or Claude needs a visual judgement.
269
- * **One Claude Code process.** The server starts `claude -p` in stream-json mode once and keeps it
270
- running, so there is no start-up delay per message. It runs in the deck folder under your Claude
271
- login, with `--append-system-prompt` from `lib/system-prompt.md` and the deck's `CLAUDE.md`.
272
- * **Permissions.** Claude may read files, edit `index.html` and `theme.css`, and run the editor's
273
- CLI. Nothing else, and in particular no edits to `brand.css`. Claude Code loads no settings files
274
- and no MCP servers, so no allow rule in your own, the deck's or a parent folder's settings widens this. If `brand.css` changes during a
275
- turn anyway, the server restores it and says so in the chat.
276
- * **Undo.** The server keeps a copy of the editable files from before each change (a Claude turn
277
- or a hand edit). Undo and redo walk that history and tell Claude on the next message. They first check
278
- that the files are still as that change left them; if something else changed them since, they do
279
- nothing and say which file differs, rather than overwrite that work.
402
+ only taken when you tick the box or the assistant needs a visual judgement.
403
+ * **One selected backend.** Claude runs as a long-lived `claude -p` stream-json process; Codex runs as a
404
+ local app-server over stdio. Both run in the deck folder under your own login, with the instructions
405
+ from `lib/system-prompt.md` and the deck's `CLAUDE.md` and `AGENTS.md`. Codex model and effort changes
406
+ reuse the active thread writer and apply to the next turn. Claude changes and provider switches prepare
407
+ a replacement process before retiring the old one. Events from replaced sessions are ignored.
408
+ * **Permissions.** The assistant may read files, edit and create files in the deck folder, and run the
409
+ editor's CLI. Nothing else: no other commands, and no edits to `brand.css`, `vendor/`, `.git/` or, for a
410
+ named deck file, the other decks in its folder. Claude Code loads no settings files and no MCP servers,
411
+ so no allow rule in your own, the deck's or a parent folder's settings widens this. Codex has no shell
412
+ and no file access of its own: it reads and edits through PinDeck's own tools, which apply the same
413
+ rules and refuse symlinks out of the deck. If `brand.css` changes during a turn anyway, the server
414
+ restores it and says so in the chat.
415
+ * **Undo.** The server keeps a copy of every file a change touched, from before and after it (an AI
416
+ turn, which may change, create or delete several files of the deck, or a hand edit). Files over 2 MB
417
+ are not copied: the chat says when a turn changed one, and Undo leaves it as it is. Hidden files,
418
+ `vendor/` and `export/` are not part of undo. Undo and redo walk that history and tell the assistant on
419
+ the next message. They first check that the files are still as that change left them; if something
420
+ else changed them since, they do nothing and say which file differs, rather than overwrite that work.
280
421
  * **Saving.** Every file PinDeck writes into the deck is written to a temporary file next to it and then
281
422
  renamed into place, so other programs never see half a file. Each change from the editor carries the
282
423
  revision of the files it was made on; if the files have changed since, the change is refused, the
@@ -286,36 +427,76 @@ Without PowerPoint or Keynote (or on other systems than macOS) only the text and
286
427
 
287
428
  | File | Purpose |
288
429
  |---|---|
289
- | `server.mjs` | Local server (127.0.0.1 only, random token per run, one-time login links): UI, deck files, events, Claude session, undo, CLI endpoints, `login`. |
290
- | `cli.mjs` | `layout`, `shot`, `slides`, `pdf handout` / `pdf presentation` and `pdf-figure` commands for Claude (and for you), and `git-setup`. |
430
+ | `server.mjs` | Local server (127.0.0.1 only, random token per run, one-time login links): UI, deck files, events, backend session, undo, CLI endpoints, `login`. |
431
+ | `cli.mjs` | `layout`, `shot`, `slides`, `pdf handout` / `pdf presentation` and `pdf-figure` commands for the backends (and for you), and `git-setup`. |
291
432
  | `public/` | The editor page, and `bridge.js`, which is injected into the deck in edit mode (never saved to it). |
292
433
  | `lib/pptx.mjs`, `lib/zip.mjs`, `lib/xml.mjs` | Reading `.pptx` files (no dependencies): slides, text, shapes and their positions, notes. |
293
434
  | `lib/pptx-import.mjs` | Turns PowerPoint slides into slides of the deck: figures, tables, build steps, audio, layout, HTML in the deck's style. |
294
435
  | `lib/waveform.mjs` | Waveform SVGs for imported audio. |
295
436
  | `lib/omml.mjs` | PowerPoint equations (Office Math) to TeX. |
296
- | `lib/pptx-render.mjs`, `lib/pdf-render.jxa.js` | Slide pictures: PowerPoint or Keynote saves a PDF, macOS PDFKit renders pages and regions of it. |
437
+ | `lib/pptx-render.mjs`, `lib/pdf-render.jxa.js` | Slide and PDF pictures: PowerPoint or Keynote (macOS) or LibreOffice (Linux) saves a PDF; macOS PDFKit or Poppler renders pages and regions of it. |
297
438
  | `lib/pdf.mjs` | PDF export: reveal.js print layout in headless Chrome, then print to PDF. |
298
439
  | `lib/eids.mjs` | Adds the `data-eid` attributes by editing the HTML as text. |
299
- | `lib/claude.mjs` | The long-running Claude Code process. |
300
- | `lib/capabilities.mjs` | What this server can do on this system (macOS, Linux), for the editor, the API, the CLI and Claude. |
440
+ | `lib/claude.mjs`, `lib/codex.mjs`, `lib/agent-process.mjs` | Backend adapters and JSON-line transport. |
441
+ | `lib/models.mjs`, `lib/agent-tools.mjs` | Model/settings normalization, handover, and restricted Codex tools. |
442
+ | `lib/capabilities.mjs` | What this server can do on this system (macOS, Linux), for the editor, the API, the CLI and the assistant. |
301
443
  | `lib/deck-files.mjs` | Atomic writes and the revision of the deck's files. |
302
- | `lib/system-prompt.md` | What Claude is told about the editor. |
444
+ | `lib/system-prompt.md` | What the assistant is told about the editor. |
303
445
  | `CONTRIBUTING.md` | How to report bugs and contribute. |
304
446
  | `docs/` | Images for this README. |
305
447
  | `LICENSE` | MIT. |
306
448
 
307
449
  ## Current Limitations
308
450
 
309
- * Text inside inline SVG diagrams cannot be edited in place; pin it and ask Claude.
451
+ * reveal.js decks only. A deck made with another framework (Marp, Slidev, impress.js, remark, Spectacle,
452
+ Shower, deck.js, bespoke.js) is recognised and refused with its name before PinDeck changes anything.
453
+ * Text inside inline SVG diagrams cannot be edited in place; pin it and ask the assistant.
310
454
  * Parts of an inline SVG move with the whole SVG; inline text (bold, spans) moves with its block.
311
- * No indenting or outdenting of bullets with `Tab` yet, and `Backspace` at the start of a bullet does not merge it with the one above; ask Claude for those.
312
- * Moves are offsets (`translate`), so the space an element came from stays reserved. Ask Claude to turn hand edits into proper CSS when a layout should really change.
455
+ * No indenting or outdenting of bullets with `Tab` yet, and `Backspace` at the start of a bullet does not merge it with the one above; ask the assistant for those.
456
+ * Moves are offsets (`translate`), so the space an element came from stays reserved. Ask the assistant to turn hand edits into proper CSS when a layout should really change.
313
457
  * The undo history lives on the server and starts fresh when PinDeck restarts.
314
458
  * One writer at a time: PinDeck is not made for editing the same deck from two editors or tools at once.
315
- * Linux (beta): no PDF page pictures or PowerPoint import yet; tested hosts are listed in the release notes.
459
+ * Linux (beta): pictures of PDF pages and PowerPoint slides, and waveforms of non-WAV audio, need
460
+ Poppler, LibreOffice and ffmpeg on the server (see [Running on a Linux server](#running-on-a-linux-server-beta)).
316
461
 
317
462
  ## Versions
318
463
 
464
+ * **3.0.0-beta.2**: everything from 2.9 and 2.10, with Codex brought up to it.
465
+ - Security: the fix from 2.9. The editor page no longer contains the token, and the deck needs it
466
+ too, so other users of the same machine cannot read either. PinDeck prints a one-time login link at
467
+ start (`pindeck login` prints a new one). Update from beta.1.
468
+ - Linux as a beta, safe saving, Undo/Redo that refuses to overwrite outside changes, decks with their
469
+ own file names, `--asset-root`, `@imports` in the deck's `CLAUDE.md` and `AGENTS.md`, and the other
470
+ 2.9 and 2.10 changes (see below).
471
+ - Both assistants may edit and create other files of the deck, never `brand.css`, `vendor/`, `.git/`
472
+ or other decks in the folder; one Undo takes back a whole turn. Codex creates files with a new
473
+ `pindeck_write` tool and edits through PinDeck's safe saving.
474
+ - Claude may run only PinDeck's own commands (layout, shot, slides, pdf, pdf-figure), each named.
475
+ - Starting with `--provider codex` and no saved model now picks Codex's default model, also while
476
+ the model lists are still loading.
477
+ - Stopping PinDeck waits for every backend process, including those that list models.
478
+
479
+ * **3.0.0-beta.1**: Claude and Codex adapters, discovered model/effort menu, explicit provider
480
+ switching with optional handover, curated model lists, a separate effort selector with per-model
481
+ preferences, restricted Codex tools, and separate v3 runtime state. On npm as `pindeck@beta`.
482
+
483
+ * **2.10.0-beta.1** (2.10 beta): decks with their own file names and shared files, and Claude working
484
+ on the whole deck.
485
+ - A deck file by name: `pindeck path/to/talk.html` (a folder still opens its `index.html`). Several
486
+ decks can share a folder, each with its own chat and undo history.
487
+ - Claude may edit and create other files of the deck (scripts, style sheets, notes), never
488
+ `brand.css`, `vendor/` or other decks in the folder. One Undo takes back everything a turn changed,
489
+ created or deleted; files over 2 MB are not copied, and the chat says so.
490
+ - Files outside the deck folder: `--asset-root <folder>` (or `"assetRoots"` in config.json) serves
491
+ the deck from inside that folder, so paths such as `../shared/style.css` work in the editor,
492
+ screenshots and PDF exports. A symlink that leads out of the deck and its asset roots is refused.
493
+ - Shared instructions: the deck's `CLAUDE.md` and `AGENTS.md` may import other files with `@path`.
494
+ `CLAUDE.md` files in parent folders are still not loaded, on purpose (see the README).
495
+ - Decks made with other slide frameworks (Marp, Slidev, impress.js, remark and others) are
496
+ recognised and refused with their name, before PinDeck changes anything.
497
+ - Linux: pictures of PDF pages with Poppler, the PowerPoint import with slide pictures from
498
+ LibreOffice, and waveforms of non-WAV audio with ffmpeg, each when installed.
499
+
319
500
  * **2.9**: Linux as a beta, and safer editing.
320
501
  - Linux (beta): PinDeck runs as a headless server, used from a Mac's browser through an SSH tunnel,
321
502
  with Claude Code running on the server. The tunnel may use another local port than the server.
@@ -393,7 +574,7 @@ Without PowerPoint or Keynote (or on other systems than macOS) only the text and
393
574
  * **1.11.1**: the Hide and Show icons sit at the same height.
394
575
  * **1.11**: hide and show slides (eye in the slide list, toolbar button, `H`); hidden slides are skipped
395
576
  when presenting and in PDFs unless the export includes them.
396
- * **1.10**: model and effort without "Default" entries (default Sonnet, medium), set in `~/.pindeck/config.json`.
577
+ * **1.10**: model and effort without "Default" entries (default Sonnet, medium), set in `~/.pindeck-v3/config.json`.
397
578
  * **1.9**: `node cli.mjs git-setup` keeps PinDeck's ids out of git with a clean filter; id-only changes no longer show in `git status`.
398
579
  * **1.8**: + New slide (content slide, section divider, duplicate); model (Opus, Sonnet) and effort menu in the chat header; a + menu in the chat for files and screenshots.
399
580
  * **1.7**: `Delete` removes the selected element after a confirmation, a picture with its caption, and optionally the files nothing else uses; undo restores elements and files. Claude reads attached images directly instead of trying to convert them; refused commands are explained in the chat.
@@ -410,7 +591,7 @@ Without PowerPoint or Keynote (or on other systems than macOS) only the text and
410
591
  Bug reports, ideas, example decks and pull requests are welcome. [CONTRIBUTING.md](CONTRIBUTING.md)
411
592
  explains how to report a bug, set up PinDeck for development, test a change by hand, and what a pull
412
593
  request should contain. In short: no npm dependencies, deck files are edited only where they must
413
- change, Claude's permissions stay narrow, and the README describes what users see.
594
+ change, both backends’ permissions stay narrow, and the README describes what users see.
414
595
 
415
596
  ## License
416
597
 
package/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- /* PinDeck command line: Claude's self-checks (need the PinDeck server running) and git setup.
2
+ /* PinDeck command line: the assistant's self-checks (need the PinDeck server running) and git setup.
3
3
 
4
4
  node cli.mjs layout <slide>... measured layout and issues of one or more slides
5
5
  (add --issues for the issues only: layout 9 10 11 --issues)
@@ -13,7 +13,7 @@
13
13
  from the top left), as PNG: without a name in the screenshots
14
14
  folder to look at, with one saved into the deck
15
15
 
16
- node cli.mjs git-setup [deck] keep PinDeck's data-eid ids out of git (no server needed)
16
+ node cli.mjs git-setup [deck] keep PinDeck's data-eid ids out of git (no server needed; a folder or deck file)
17
17
 
18
18
  <slide> is a slide id (data-eid), a slide number (1-based) or "current". */
19
19
 
@@ -27,11 +27,12 @@ const arg = rest[0];
27
27
 
28
28
  /* git-setup: a git "clean" filter strips PinDeck's data-eid attributes whenever git reads the deck's
29
29
  HTML, so ids never reach a commit and id-only changes do not show in `git status`. The ids stay in
30
- the file on disk, where PinDeck and Claude need them. The filter is plain sed, so it does not depend
30
+ the file on disk, where PinDeck and the assistant need them. The filter is plain sed, so it does not depend
31
31
  on where PinDeck is installed; git config is per clone, so run this once in each clone. */
32
32
  if (cmd === "git-setup") {
33
33
  const FILTER = `sed 's/ data-eid="[^"]*"//g'`;
34
34
  let deck = path.resolve(arg || ".");
35
+ try { if (!fs.statSync(deck).isDirectory()) deck = path.dirname(deck); } catch {} // a deck file: its folder
35
36
  try { deck = fs.realpathSync(deck); } catch {} // git reports the real path; a symlinked path would not match
36
37
  const git = (...a) => execFileSync("git", ["-C", deck, ...a], { encoding: "utf8" }).trim();
37
38
  let root;
@@ -68,14 +69,14 @@ function figureQuery(args) {
68
69
  let url = process.env.DECK_EDITOR_URL, token = process.env.DECK_EDITOR_TOKEN;
69
70
  if (!url || !token) {
70
71
  try {
71
- const home = process.env.PINDECK_HOME || path.join(os.homedir(), ".pindeck");
72
+ const home = process.env.PINDECK_HOME || path.join(os.homedir(), ".pindeck-v3");
72
73
  const s = JSON.parse(fs.readFileSync(path.join(home, "server.json"), "utf8"));
73
74
  url = s.url; token = s.token;
74
75
  } catch {}
75
76
  }
76
77
  if (!url) { console.error("The PinDeck server is not running (start it with: pindeck <deck-folder>)."); process.exit(1); }
77
78
 
78
- /* layout and shot take several slides, so Claude needs no shell loop or pipe (which PinDeck refuses) */
79
+ /* layout and shot take several slides, so the assistant needs no shell loop or pipe (which PinDeck refuses) */
79
80
  const queries = cmd === "pdf-figure" ? [figureQuery([...rest])]
80
81
  : cmd === "pdf" ? [{ slide: rest[0] || "handout", ...(rest[1] === "all" ? { all: "1" } : {}) }]
81
82
  : cmd === "slides" ? [{}] : (slides.length ? slides : ["current"]).map((slide) => ({ slide }));