pindeck 3.3.0 → 4.0.0-beta.1

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,765 +1,74 @@
1
1
  # PinDeck
2
2
 
3
- > **macOS and Linux · Version 3** (`3.3.0`). On Linux, PinDeck runs as a headless server that you use
4
- > from your Mac's browser through an SSH tunnel (see [Running on a Linux server](#running-on-a-linux-server)).
5
- > Windows is not supported. Coming from PinDeck 2: version 3 keeps its settings and chats in
6
- > `~/.pindeck-v3`, apart from 2.x (see [Models, providers and configuration](#models-providers-and-configuration)).
3
+ > **macOS and Linux · Version 4 beta preview** (`4.0.0-beta.1`). The beta is on npm’s `beta` channel and the [`v4-beta`](https://github.com/ehabets/PinDeck/tree/v4-beta) branch; the stable release is 3.4.0 (`npx pindeck`, on [`main`](https://github.com/ehabets/PinDeck)). On Linux, PinDeck runs as a headless server that you use from your Mac's browser through an SSH tunnel (see [Running on a Linux server](docs/linux-server.md#running-on-a-linux-server)). Windows is not supported. The beta keeps its settings and chats in `~/.pindeck-v4`, apart from 3.x (`~/.pindeck-v3`) and 2.x (`~/.pindeck`) (see [Models, providers and configuration](docs/configuration.md#models-providers-and-configuration)).
7
4
 
8
- **Pin it. Say it. Done.** PinDeck is a local editor for HTML slide decks (reveal.js): the slides on
9
- the left, the slide in the middle, and a chat with Claude Code or OpenAI Codex on the right. You click elements to
10
- pin them, write what to change, and the selected assistant edits the HTML and CSS. The slide updates in place.
5
+ **Pin it. Say it. Done.** PinDeck is a local editor for HTML slide decks (reveal.js): the slides on the left, the slide in the middle, and a chat with Claude Code or OpenAI Codex on the right. You click elements to pin them, write what to change, and the selected assistant edits the HTML and CSS. The slide updates in place.
11
6
 
12
7
  ![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)
13
8
 
14
- *The example deck in PinDeck. Claude has lined up the three numbers from the first request and
15
- checked the layout; the next request, still being typed, refers to the new pins 1 and 2.*
9
+ *The example deck in PinDeck. Claude has lined up the three numbers from the first request and checked the layout; the next request, still being typed, refers to the new pins 1 and 2.*
16
10
 
17
- ## What it does
11
+ ## Features
18
12
 
19
- * **Point instead of describe.** Pins name the exact element, word or table cell; "make 2 match 1"
20
- is enough. The assistant also gets the measured layout of the slide, so it sees overlaps and overflow
21
- without screenshots.
22
- * **Edit by hand, too.** Move, resize and nudge elements with snapping, edit text in place (math stays
23
- TeX), write speaker notes, add, hide, move and delete slides. One undo history covers your edits and
24
- the assistant's.
25
- * **Stay on brand.** `brand.css` is locked: the assistant edits the deck, its styles and other files of the
26
- deck folder, never `brand.css` or `vendor/`, and checks its own work with PinDeck's layout command.
27
- * **Bring material in.** Attach figures, audio and PDFs to a message, or import slides from PowerPoint
28
- into the deck's own layouts.
13
+ * **Point instead of describe.** Pins name the exact element, word or table cell; "make 2 match 1" is enough. The assistant also gets the measured layout of the slide, so it sees overlaps and overflow without screenshots.
14
+ * **Edit by hand, too.** Move, resize and nudge elements with snapping, edit text in place (math stays TeX), write speaker notes, add, hide, move and delete slides. One undo history covers your edits and the assistant's.
15
+ * **Stay on brand.** `brand.css` is locked: the assistant edits the deck, its styles and other files of the deck folder, never `brand.css` or `vendor/`, and checks its own work with PinDeck's layout command.
16
+ * **Bring material in.** Attach figures, audio and PDFs to a message, or import slides from PowerPoint into the deck's own layouts.
29
17
  * **Get it out.** Present from the browser, or export a handout or presentation PDF.
30
18
  * **Local and small.** One Node process on 127.0.0.1, no npm dependencies, your existing Claude Code or Codex login.
31
19
 
32
20
  ## Quick start
33
21
 
34
- Runs on macOS and Linux. The npm package declares `"os": ["darwin", "linux"]`, so normal
35
- npm installation rejects other operating systems. Overriding that check does not make those platforms
36
- supported. Running the server from a cloned repository on another OS shows a local support notice
37
- instead of opening the deck or starting a backend.
22
+ Runs on macOS and Linux. The npm package declares `"os": ["darwin", "linux"]`, so normal npm installation rejects other operating systems. Overriding that check does not make those platforms supported. Running the server from a cloned repository on another OS shows a local support notice instead of opening the deck or starting a backend.
38
23
 
39
- Install from npm:
24
+ Install the beta from npm:
40
25
 
41
26
  ```bash
42
- npx pindeck /path/to/deck
27
+ npx pindeck@beta /path/to/deck
28
+ # Or select this exact beta:
29
+ npx pindeck@4.0.0-beta.1 /path/to/deck
43
30
  ```
44
31
 
45
- Or run it from source, which includes the example deck:
32
+ Or run it from the `v4-beta` branch, which includes the example decks:
46
33
 
47
34
  ```bash
48
- git clone https://github.com/ehabets/PinDeck.git
35
+ git clone -b v4-beta https://github.com/ehabets/PinDeck.git
49
36
  cd PinDeck
50
37
  node front.mjs ocean-example
51
38
  ```
52
39
 
53
- PinDeck 2 stays available as `npx pindeck@2 /path/to/deck` (source: the
54
- [`2.x`](https://github.com/ehabets/PinDeck/tree/2.x) branch). Releases, with notes for each version, are on
55
- [GitHub](https://github.com/ehabets/PinDeck/releases).
40
+ PinDeck 2 stays available as `npx pindeck@2 /path/to/deck` (source: the [`2.x`](https://github.com/ehabets/PinDeck/tree/2.x) branch). Releases, with notes for each version, are on [GitHub](https://github.com/ehabets/PinDeck/releases).
56
41
 
57
- Then open the login link PinDeck prints (`PinDeck: http://127.0.0.1:5180/login?code=...`). It works
58
- once and logs this browser in; for a new one, run `pindeck login` (or `node front.mjs login`). On a Mac,
59
- pressing Enter in the terminal where PinDeck runs opens a new link in your default browser.
42
+ Then open the login link PinDeck prints (`PinDeck: http://127.0.0.1:5180/login?code=...`). It works once and logs this browser in; for a new one, run `pindeck login` (or `node front.mjs login`). On a Mac, pressing Enter in the terminal where PinDeck runs opens a new link in your default browser.
60
43
 
61
- The deck is an `.html` file (`pindeck path/to/talk.html`) or a folder, which opens its `index.html`;
62
- without one, the current folder. Several decks can share a folder, each with its own chat and undo history;
63
- they share the folder's `theme.css` and `brand.css`, and the assistant edits only the deck that is open.
64
- PinDeck adds element ids to the deck file when it opens it (see below), so try it on a copy of a deck
65
- first. Options: `--port 5180`, `--provider claude|codex`, `--model <name>` (for this run only; otherwise the
66
- deck's own choice, see [Models, providers and configuration](#models-providers-and-configuration)), `--asset-root <folder>` (see [Files outside the deck folder](#files-outside-the-deck-folder)),
67
- `--help`. Runtime files (chat history, screenshots, backups) are kept in `~/.pindeck-v3/` (change with
68
- `PINDECK_HOME`), not in the deck. `ocean-example/` is a complete example deck to start from.
69
-
70
- ### Several decks
71
-
72
- Click the deck's name above the slide for the other decks: the `.html` files in the deck's folder and in
73
- the asset roots, and in their folders one level down (`lecture/index.html`). A deck opens in the same
74
- tab, or with ⌘-click in a new one, on the same port and login, so one SSH tunnel serves them all. Each keeps
75
- its own chat and undo history while it is open, and the assistant finishes a turn in a deck you leave. A
76
- file PinDeck cannot open is greyed out with the reason; listing a deck changes nothing, and only opening it
77
- adds its element ids. Symlinked decks and folders are listed too; a deck file that links to a deck in
78
- another folder is greyed out (open that deck itself). Started with a folder that has no `index.html`,
79
- PinDeck shows the list of its decks. A deck with no editor open for 30 minutes is closed (its undo history
80
- goes, as on a restart) and opens again when you choose it. Open as many decks in as many tabs as you like:
81
- the tabs of a browser share one connection for their live updates.
82
-
83
- A deck is open in one PinDeck at a time: started again for a deck that another PinDeck (another port, or
84
- `server.mjs` on its own) has open, PinDeck says where it is open and stops, so their chats and undo
85
- histories never overwrite each other. `pindeck login <deck>` and `cli.mjs` find the PinDeck that serves
86
- the deck, whichever port it runs on.
44
+ The deck is an `.html` file (`pindeck path/to/talk.html`) or a folder, which opens its `index.html`; without one, the current folder. Several decks can share a folder, each with its own chat and undo history; they share the folder's `theme.css` and `brand.css`, and the assistant edits only the deck that is open. PinDeck adds element ids to the deck file when it opens it (see [How it works](docs/how-it-works.md)), so try it on a copy of a deck first. Options: `--port 5180`, `--provider claude|codex`, `--model <name>` (for this run only; otherwise the deck's own choice, see [Models, providers and configuration](docs/configuration.md#models-providers-and-configuration)), `--asset-root <folder>` (see [Files outside the deck folder](docs/configuration.md#files-outside-the-deck-folder)), `--help`. Runtime files (chat history, screenshots, backups) are kept in `~/.pindeck-v4/` (change with `PINDECK_HOME`), not in the deck. `ocean-example/` is a complete example deck to start from.
87
45
 
88
46
  ## Compatibility and requirements
89
47
 
90
48
  * **Runtime:** Node.js 20 or later, plus a modern browser. The release workflow uses Node.js 24.
91
- * **Backends:** install and sign in to Claude Code (`claude auth login`) or OpenAI Codex (`codex login`).
92
- The corresponding executable must be on `PATH`; `CLAUDE_BIN` and `CODEX_BIN` can override its path.
93
- Only the selected backend is required for editing. Model and effort availability comes from that
94
- backend and account, and may differ from the provider’s website or desktop app.
95
- * **Platforms:** macOS and Linux (see [Running on a Linux server](#running-on-a-linux-server)).
96
- Windows is not supported. The npm package declares `"os": ["darwin", "linux"]`, so normal npm
97
- installation rejects other operating systems. This check can be overridden; bypassing it does not make
98
- those platforms supported. Source launches on unsupported platforms show a local support page without
99
- opening the deck or starting a backend. Native Windows support is a possible v4 feature, not part of version 3.
49
+ * **Backends:** install and sign in to Claude Code (`claude auth login`) or OpenAI Codex (`codex login`). The corresponding executable must be on `PATH`; `CLAUDE_BIN` and `CODEX_BIN` can override its path. Only the selected backend is required for editing. Model and effort availability comes from that backend and account, and may differ from the provider’s website or desktop app.
50
+ * **Platforms:** macOS and Linux (see [Running on a Linux server](docs/linux-server.md#running-on-a-linux-server)). Windows is not supported. The npm package declares `"os": ["darwin", "linux"]`, so normal npm installation rejects other operating systems. This check can be overridden; bypassing it does not make those platforms supported. Source launches on unsupported platforms show a local support page without opening the deck or starting a backend. Native Windows support is a possible v4 feature, not part of version 3.
100
51
  * **Screenshots and PDF export:** require Chrome, Chromium, Edge or Brave where the server runs.
101
- * **PDF input through Codex:** pages are rendered with macOS PDFKit, or Poppler on Linux. Page-text
102
- extraction uses `osascript` and is macOS-only; on Linux Codex reads the rendered page image. Claude uses
103
- its native PDF-reading capabilities.
104
- * **PowerPoint import:** basic `.pptx` parsing uses Node.js; rendered slide pictures require
105
- PowerPoint or Keynote on macOS, or LibreOffice and Poppler on Linux.
106
- * **Linux tools:** Poppler, LibreOffice and ffmpeg, each optional; features that need a missing one are
107
- switched off with the reason shown in the editor.
108
-
109
- The initial live editing checks used Codex CLI **0.144.1** and Claude Code **2.1.289**.
110
- Further live checks on macOS passed with Codex CLI **0.162.1** and Claude Code **2.1.301**:
111
- editing, Claude effort changes, Codex model changes, conversation handover, undo/redo,
112
- cancellation and recovery. These are tested versions, not guaranteed minimum requirements. Backends must support the discovery and session interfaces
113
- used by PinDeck. Older CLIs can expose fewer models or fail initialization.
114
-
115
- Codex integration uses OpenAI’s experimental `dynamicTools` extension to the app-server.
116
- A future Codex update may require a PinDeck adapter update. Incompatible backends report startup
117
- errors; PinDeck does not fall back to broader permissions. Recheck backend compatibility when upgrading.
118
-
119
- ## Preparing a deck
120
-
121
- PinDeck works with reveal.js decks whose slides are `<section>` elements inside `.slides`, and with other
122
- decks that name their slides (see [Decks made without reveal.js](#decks-made-without-revealjs)). Two
123
- optional `<meta>` tags in the deck's `<head>` tell it about the design:
124
-
125
- ```html
126
- <meta name="pindeck:footer-top" content="652"> <!-- y of the footer rule: content must stay above it -->
127
- <meta name="pindeck:locked" content=".slide-foot, .logo"> <!-- brand elements that cannot be selected or moved -->
128
- ```
129
-
130
- Hidden slides use reveal.js's `data-visibility="hidden"`. If the deck numbers its slides itself (as
131
- the example's `deck.js` does for the footer), count only slides without that attribute, or the
132
- numbers will jump where a slide is hidden.
133
-
134
- Without them PinDeck runs no footer check and locks only `.slide-foot` and `.logo`. A file called
135
- `brand.css` in the deck folder is locked for both backends. See `ocean-example/` for a complete deck.
136
-
137
- ### Decks made without reveal.js
138
-
139
- A deck with a script of its own, or made with a framework whose slides are elements of their own
140
- (Shower, deck.js, a hand-written deck), can be edited too once it names its slides in its `<head>`:
141
-
142
- ```html
143
- <meta name="pindeck:slides" content="main.deck > section.slide"> <!-- a CSS selector for the slides -->
144
- <meta name="pindeck:size" content="1440x1080"> <!-- their size in px -->
145
- ```
146
-
147
- The selector may use tag names, `#id`, `.class`, `[attr]` and `[attr="value"]`, joined by spaces or `>`,
148
- and a list of them separated by commas. In the editor, PinDeck shows the slides itself, one at a time and
149
- scaled to the window, and switches off the deck's own scaling, slide keys and inert or hidden marks while
150
- you edit; that changes nothing in the deck file. Everything in [Working with it](#working-with-it) works as
151
- with a reveal.js deck, with these differences:
152
-
153
- * Build steps (`.fragment`) belong to reveal.js: other decks show every element at once.
154
- * Hidden slides (`data-visibility="hidden"`) are left out of PinDeck's PDF exports; whether presenting
155
- skips them is up to the deck's own script.
156
- * Speaker notes are an `<aside class="notes">` in the slide, as in reveal.js. PinDeck hides them in the
157
- editor; the deck's CSS should hide them too (`aside.notes { display: none }`).
158
- * **Present** opens the deck at `#<slide id>`, or `#<number>` (counting from 1) for a slide without an id.
159
- Present may open on the first slide if the deck does not go to slides by their address.
160
- * **+ New slide** models a new slide on the deck's own (see [Making your own template](#making-your-own-template)):
161
- a content slide gets placeholder bullets in a `.slide-body`; without one it has only its title. A new or
162
- duplicated slide does not take the original's `id`.
163
-
164
- Both tags are needed: PinDeck opens the slides at the given size for screenshots and PDF pages. A missing
165
- size, or a selector that finds no slides or that PinDeck cannot read, is reported before anything changes.
166
- While PinDeck runs, it reads the tags again for every screenshot, PDF and assistant session; the editor
167
- reads them when the slide reloads. If a tag stops working meanwhile, the editor shows the same message and
168
- the assistant is told. A session already running keeps the slide selector it started with; **New chat**
169
- starts one with the new selector.
170
-
171
- ## Making your own template
172
-
173
- PinDeck has no template generator; a template is simply a deck folder. Start from a copy of the example:
174
-
175
- ```bash
176
- cp -R ocean-example my-template
177
- ```
178
-
179
- 1. **Brand in `brand.css`:** palette, backgrounds, footer and logo positions. PinDeck locks this file
180
- for both backends, so edit it by hand outside PinDeck. Replace the logos in
181
- `assets/` too.
182
- 2. **Everything else in `theme.css`:** type sizes, spacing, layouts and components. This can be done
183
- in PinDeck with the selected assistant.
184
- 3. **`index.html`:** the footer text in the `data-` attributes of `<div class="reveal">`, and the
185
- `pindeck:footer-top` and `pindeck:locked` meta tags if the footer or the brand elements move.
186
- `deck.js` holds the math macros, the footer markup and the audio player.
187
- 4. **`README.md`, `CLAUDE.md` and `AGENTS.md`:** both backends follow the deck's `CLAUDE.md` and
188
- `AGENTS.md`, so describe the new classes and rules there; the assistant can read `README.md` for details.
189
- Refer to PinDeck's commands as "the PinDeck CLI command given in the system prompt", as the
190
- example's `CLAUDE.md` does; PinDeck tells the assistant the right path wherever the deck is. An older
191
- `node ../cli.mjs` in a deck's `CLAUDE.md` is replaced with that command.
192
-
193
- **Instructions for the assistant.** PinDeck gives both backends the deck folder's own `CLAUDE.md` and
194
- `AGENTS.md`. It does not load such files from parent folders, as Claude Code and Codex do in a terminal:
195
- those are usually written for coding (run the tests, commit), which PinDeck's assistant cannot do; they
196
- would change with where the deck happens to sit; and on a shared server a parent folder may be writable
197
- by others. To share rules between decks, say so in the deck's own file, in one of two ways:
198
-
199
- - **Import:** `Follow @../shared/STYLE.md for wording.` includes that file's text. Paths are relative to
200
- the file they are in (or absolute, or `~/...`); imported files may import others, up to 5 levels,
201
- each file once, 200 KB in all. Only text files are imported; an e-mail address, an `@name` that is
202
- not a file, and anything in code are left as written.
203
- - **Pointer:** `The style rules are in ../shared/STYLE.md; read them first.` The assistant reads the file
204
- when it needs it.
205
-
206
- **New slide** and the PowerPoint import model new slides on the deck's own: keep a slide with class
207
- `title-slide`, one with `section-slide`, and a content slide with `.slide-head` (`h2`, optionally a
208
- `.kicker` or `.sub`) and `.slide-body`. Imported tables take the class of the deck's first table,
209
- and imported audio copies the first `.demo-row` (with a `data-target`). Without a title or section
210
- slide, those PowerPoint slides are imported as content slides; without a `.demo-row`, audio gets
211
- plain players.
212
-
213
- ## Models, providers and configuration
214
-
215
- Open the existing model menu in the chat header to choose a model under **Claude** or
216
- **OpenAI / ChatGPT**. Choices come from the installed backends: Claude's initialization response
217
- and Codex's paginated `model/list`. Hidden or explicitly unavailable entries are omitted. Effort
218
- choices appear in a separate reasoning-effort selector beside the model and follow its reported
219
- capabilities. Readable labels preserve the exact backend values (for example, Extra high sends `xhigh`).
220
- The selector shows provider descriptions where available and distinguishes **Model default** from a
221
- **PinDeck default** fallback. Models without effort controls show a disabled Effort N/A selector.
222
- Effort choices are remembered per provider/model in `effortPreferences` in config.json. A model with
223
- no saved choice uses its default; an unsupported saved choice falls back to the current default.
224
- The picker defaults to GPT-6 models (including 6.x) and the newest discovered Fable, Opus, Sonnet,
225
- and Haiku model in each family. Claude versions are compared numerically from discovered names;
226
- a rolling family alias wins ties, avoiding duplicate entries. **Show all available models** reveals
227
- the full discovered catalog; this display preference is remembered in the browser. A currently selected
228
- older model remains visible and is never switched automatically by the filter.
229
- **Refresh models** asks the backends again; otherwise the lists are shared by the decks you open for 10 minutes
230
- (`models.json` in the state folder), so opening a deck starts no backend just to list models. A missing CLI,
231
- missing sign-in, or discovery failure is shown in its provider group.
232
- These are backend-reported choices, not a guarantee of current quota or entitlement; a later rejected
233
- request reports its error. Older CLIs may expose fewer models than a provider's website.
234
-
235
- Changing models within a provider resumes that provider's conversation. Changing providers opens
236
- **Switch with summary**, **Start fresh**, and **Cancel**. A switch prepares the new backend before
237
- replacing the old one. Chat remains visible with a divider, and deck files and undo/redo are retained.
238
- Switching back to a provider creates another new session; it does not silently restore old context.
239
-
240
- The current handover is a local, bounded digest of recent user/assistant messages and notices, not an
241
- additional model-generated summary. It excludes tool output, needs no extra inference request, and is
242
- sent with the first message to the new session. Earlier messages may be omitted. **Start fresh** sends
243
- no conversation digest; the assistant still receives PinDeck instructions and the current deck context.
244
-
245
- Each deck keeps its own provider, model and effort with its conversation (in its `chat.json`), so a choice in
246
- one deck never moves another deck's conversation to another backend. The last choice is also saved in
247
- `~/.pindeck-v3/config.json` (or `$PINDECK_HOME/config.json`), as the start for decks that have none yet:
248
-
249
- ```json
250
- {
251
- "provider": "claude",
252
- "model": "sonnet",
253
- "effort": "medium"
254
- }
255
- ```
256
-
257
- Legacy configuration without `provider` is interpreted as Claude; a provider without a model gets that
258
- provider's default model. Once the backend has listed its models, a saved model it no longer offers gives way
259
- to its default model (the chat says so), and an effort the model does not offer to its saved or default one,
260
- before the first message. config.json is read again and merged whenever a choice is saved, so decks open at
261
- the same time keep each other's effort preferences. Provider-tagged chat/session records prevent one backend
262
- from resuming the other's IDs. `--provider` and `--model` override the deck's settings for that run; a
263
- `--provider` other than the deck's starts a new conversation (the chat says so), and the next run without it
264
- is back on the deck's own choice. A later menu selection is saved. `CLAUDE_BIN` and `CODEX_BIN` override
265
- executable paths. `CLAUDE_MODEL`, which PinDeck 2 read, is not used: PinDeck warns and names `--model`.
266
-
267
- PinDeck 3 defaults to **`~/.pindeck-v3`**, keeping v2 settings and conversations separate. It does
268
- not copy the v2 directory automatically; `config.json` and `decks/*/chat.json` can be copied over by hand
269
- (asset roots too: they are in config.json). Never give both versions the same `PINDECK_HOME`: they keep files
270
- of the same names in different formats and overwrite each other's settings and chats. PinDeck 3 warns at
271
- start when its folder holds a chat that PinDeck 2 wrote. To run both versions at once, use another port, for
272
- example:
273
-
274
- ```bash
275
- node front.mjs /path/to/a/deck-copy --port 5183 --provider codex
276
- ```
277
-
278
- ## Backend permissions and attachments
279
-
280
- Claude uses its native Read/Edit tools with PinDeck's narrow allowlist, safe mode, and no inherited
281
- MCP configuration. Codex runs with a read-only sandbox, command execution disabled, and no approval
282
- grants. Inherited MCP servers and plugins are disabled in the thread configuration. Codex receives
283
- four host-controlled tools: `pindeck_read`, `pindeck_edit`, `pindeck_write` and `pindeck_command`.
284
- Both assistants may edit and create the files of the deck folder: the deck file, `theme.css` and its other
285
- files. Never `brand.css`, `vendor/`, `.git/`, or the other decks in the folder (its other `.html` files, and
286
- subfolders that hold a deck of their own). Linked writable files and paths outside the allowed roots are
287
- rejected. Commands are restricted to PinDeck's layout, shot, slides, pdf and pdf-figure helpers, executed
288
- with argument arrays rather than a shell. `brand.css` also retains the server's post-turn restoration safeguard.
289
-
290
- Codex reads text, PNG/JPEG/WebP/GIF images and screenshots through the bridge. SVG is read as text.
291
- Attached PDFs are handled page by page: a rendered page image (PDFKit on macOS, Poppler on Linux), plus the
292
- page's text on macOS. Audio is embedded by path and is not transcribed. Read operations have a 12 MB limit;
293
- long text can be read in chunks.
294
-
295
- ### Files outside the deck folder
296
-
297
- A deck can use files from a folder around it, such as a style sheet or figures shared by several
298
- talks: start PinDeck with `--asset-root <folder>` (repeatable), or list the folders in config.json as
299
- `"assetRoots": ["~/talks"]`. When the folder contains the deck, PinDeck serves the deck from inside
300
- it, so relative paths such as `../shared/style.css` work, in the editor, in screenshots and in PDF
301
- exports. Claude may read those files but does not edit them.
302
-
303
- Symlinks: PinDeck serves a file only if, with symlinks followed, it lies inside the deck folder or an
304
- asset root. A symlink in the deck that leads anywhere else is refused (403). To use a symlinked
305
- library folder, add the folder it points to as an asset root; it does not have to contain the deck.
306
- PinDeck never writes or deletes through a symlink out of the deck folder: with a `figures/` or `export/`
307
- that links elsewhere, uploads, exports and deleting a picture's file are refused with the reason.
308
-
309
- ## Running on a Linux server
310
-
311
- PinDeck can run on a Linux machine where the deck lives, with the editor in your Mac's browser. The
312
- deck, PinDeck, the backend (Claude Code or Codex) and the deck's git clone all stay on the server; the browser only shows the
313
- editor. The server listens on 127.0.0.1 there, as on a Mac, and an SSH tunnel brings that port to your Mac.
314
-
315
- On the server you need Node 20 or later (22 or 24 recommended), git if the deck is in a repository, and
316
- the `claude` or `codex` CLI installed and logged in under your account there, if you want the chat. Without one,
317
- editing by hand still works. Then, in one SSH session on the server:
318
-
319
- ```bash
320
- node front.mjs path/to/deck --port 5180
321
- ```
322
-
323
- and on your Mac, in another terminal (`myserver` is your usual SSH host):
324
-
325
- ```bash
326
- ssh -N -T -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -L 127.0.0.1:5180:127.0.0.1:5180 myserver
327
- ```
328
-
329
- Open the login link PinDeck printed on the server in the Mac's browser. The local port may differ from
330
- the server's: with `-L 127.0.0.1:8080:127.0.0.1:5180`, change 5180 in the link to 8080. PinDeck accepts any
331
- port, as long as the address is `127.0.0.1` or `localhost`. If the tunnel drops, a message you have already sent keeps running on the
332
- server. Restart the tunnel and the editor reconnects and shows the files as they are then; it never sends a
333
- message twice. If PinDeck restarts, the editor asks you to log in again: run `node front.mjs login
334
- path/to/deck` on the server for a new link.
335
-
336
- Other users of a shared server cannot open the editor or read the deck through PinDeck: the token that
337
- protects it is only in your browser's cookie and in PinDeck's own files in `~/.pindeck-v3` (readable only by
338
- you), never in a page or on a command line.
339
-
340
- What works on Linux: editing by hand, speaker notes, slides, pins, the chat with Claude or Codex,
341
- Undo/Redo, live reload when files change on the server, layout checks, attaching figures, audio and
342
- PDFs, `git-setup` and the PowerPoint import. Some features use programs on the server, and are on when
343
- they are installed (on Debian or Ubuntu: `sudo apt install poppler-utils libreoffice-impress ffmpeg`):
344
-
345
- | Feature | Needs |
346
- |---|---|
347
- | Screenshots, layout checks without an open editor, PDF export | Chrome or Chromium (`PINDECK_LINUX_CHROME=0` turns these off) |
348
- | Pictures of PDF pages (`pdf-figure`) | Poppler (`pdftoppm`, `pdfinfo`) |
349
- | Pictures of PowerPoint slides in the import (thumbnails, diagrams) | LibreOffice (`soffice`) and Poppler |
350
- | Waveforms of audio other than WAV in the import | ffmpeg |
351
-
352
- Without them the editor says what is missing (hover over "Server: Linux"), and the assistant is told
353
- which commands are not available. LibreOffice may draw some slides a little differently from PowerPoint;
354
- the pictures are references and figures, the slides themselves are rebuilt from the file. Chromium from snap may not be
355
- allowed to use hidden folders in your home folder such as `~/.pindeck-v3`; if screenshots fail with it, set
356
- `PINDECK_HOME` to a folder that is not hidden (not the one PinDeck 2 uses), or install Chrome or Chromium from
357
- a package. Run as root (in a container, say), PinDeck starts Chrome with `--no-sandbox`, which Chrome needs
358
- there; a screenshot or PDF that fails gives Chrome's own reason.
359
-
360
- One PinDeck serves all your decks on one port, and a deck can be open in only one PinDeck at a time (a
361
- second one says where it is open). While PinDeck or the assistant is working on a deck, do not edit it with other tools
362
- or switch git branches at the same time. Stop first, or wait until the assistant has finished. PinDeck notices
363
- changes made by other programs and reloads, and it refuses Undo, Redo or an edit that would overwrite
364
- them. Its checks cannot rule out every race with a program that writes at the same moment.
365
-
366
- ## Keeping the ids out of git
367
-
368
- PinDeck adds `data-eid` attributes to the deck it opens. If the deck is in git, run once per clone:
369
-
370
- ```bash
371
- node cli.mjs git-setup path/to/deck
372
- ```
373
-
374
- This adds a line like `path/to/deck/*.html filter=pindeck-ids` to `.gitattributes` (commit it) and
375
- defines the filter in the clone's git config: git then strips the ids whenever it reads the deck's
376
- HTML. The ids stay in the file on disk, where PinDeck and the assistant need them, but commits and diffs
377
- never contain them, and a change that only adds ids does not show in `git status`. Your own edits
378
- show and commit as usual. Clones without the filter configured simply see the file as it is.
379
-
380
- ## Working with it
381
-
382
- | Action | How |
383
- |---|---|
384
- | 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. |
385
- | 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. |
386
- | Undo, redo | `⌘Z` / `⇧⌘Z` or the toolbar arrows. One history for your hand edits and the assistant's changes. |
387
- | Pin elements | Press `P` (or the Pin button), click elements; each click gets a number. Click a marker to remove it, `Esc` to stop. |
388
- | 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. |
389
- | Visual questions | **+** in the chat, then **Screenshot**: the current slide with the pins of that message is attached when you send. If Chrome cannot take it, the assistant is told. **Stop** during the screenshot stops at once. |
390
- | Review | Each answer ends with the changed files and a **Diff** button; the most recent change still in effect also has an **Undo** button. After a restart, earlier changes keep their *undone* mark but have no Diff or Undo (the undo history ends with the run). |
391
- | Stop | **Stop** interrupts the assistant; the conversation continues. **New chat** starts a new conversation; your slides, Undo and Redo stay as they are. |
392
- | 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; a new content slide or divider opens its title for typing. |
393
- | 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. |
394
- | 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. |
395
- | 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. |
396
- | 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. |
397
- | 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`. |
398
- | 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. |
399
- | 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". |
400
- | 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. |
401
- | Navigate | Slide list, `←` `→`, or the toolbar. **Present** opens the deck in a new tab, at the current slide. |
402
- | Check from a terminal | `node cli.mjs layout 3 --issues`, `slides`, `shot 3` or `pdf handout`, in the deck's folder (or with `--deck <folder or file.html>`): asks the PinDeck that serves the deck. |
403
-
404
- You can also edit `index.html` or `theme.css` in any editor; the slide reloads, and the assistant is told
405
- the files changed. CSS changes are swapped in without a reload. Typing in the slide is not lost meanwhile:
406
- when you finish, PinDeck refuses to save it over the other change and offers the text to copy.
407
-
408
- ## PowerPoint import
409
-
410
- The import keeps what can be carried over exactly and makes pictures of the rest:
411
-
412
- * **Text becomes HTML** in the deck's layouts: the title (and the subtitle, as a `.sub` line or a
413
- kicker, as the deck does it), bullets with their levels, numbered lists, bold, italics, coloured words (as `.hl`),
414
- superscripts, web and mail links and line breaks, other text as paragraphs, references at the bottom of a slide
415
- as `.refs`, and the speaker notes. Side notes and labels clearly smaller than the body text stay smaller.
416
- * **Tables become HTML tables** with the class of the deck's own tables, merged header cells, header rows
417
- and a caption from the text above them. Cells merged down in the body become a label on the group's
418
- first row with empty cells below it, and each later group's first row gets `class="group"`. A table with shapes drawn over it (e.g. boxes marking rows)
419
- is kept as a picture instead.
420
- * **Build steps:** shapes and paragraphs that appear (or disappear) on a click become reveal.js
421
- fragments in the same order, also when several things appear on one click. A picture made of several
422
- shapes appears on a click only if all of its shapes do.
423
- * **Audio** goes into the deck's own audio player (a copy of its first `.demo-row`, with a waveform made
424
- from the sound), labelled with the nearest text on the slide; without such a player, plain players.
425
- **Video** keeps its place, with its poster frame.
426
- The first slide is filled into the deck's title slide (title, subtitle, authors, event, institute)
427
- and PowerPoint section headers into its section divider.
428
- * **Pictures are copied** to `figures/` (`<file>-s04-1.png`, ...).
429
- * **Everything drawn** (diagrams built from shapes and arrows, charts, pictures with labels on
430
- them, EMF graphics) becomes a picture cut from the slide as PowerPoint renders it. For this PinDeck
431
- asks PowerPoint (or else Keynote) to save the presentation as a PDF; macOS asks once whether PinDeck
432
- may control the app. The same PDF gives the thumbnails in the import dialog.
433
- * **Layout:** blocks that sit side by side become `.cols` with the slide's proportions, others are
434
- stacked top to bottom; figures and, if needed, the text are scaled so that the slide fits.
435
- * **Equations** (PowerPoint's equation editor) become TeX for the deck's KaTeX: `\( \)` in the text,
436
- `\[ \]` on their own line; fractions, scripts, roots, sums and integrals, functions and limits,
437
- accents, braces, matrices and the bold, italic and double-struck math letters are converted.
438
- * **Hidden slides** stay hidden (`data-visibility="hidden"`).
439
-
440
- **Hand-off to the assistant.** Slides the import can only approximate are tagged *complex* in the dialog:
441
- diagrams of many shapes or tables with shapes over them that became pictures, pictures placed inside
442
- text (often equations as images), build steps on parts of a diagram, slides without a title, and
443
- text that had to shrink to fit. With **Then ask the assistant to improve the complex slides** ticked, the assistant
444
- gets these slides right after the import, each with a picture of the original slide and the texts of
445
- its diagrams, and rebuilds diagrams as SVG, equations as TeX and tables as HTML in the deck's style.
446
- The request shows in the chat like your own messages, and its changes can be undone as usual.
447
-
448
- Without PowerPoint or Keynote (or on other systems than macOS) only the text and the pictures are imported.
449
-
450
- ## How it works
451
-
452
- * **One port, a server per deck.** `pindeck` starts the front (`front.mjs`): it holds the port, the login
453
- and the deck list, and serves each open deck at `/d/<deck>/`. Every deck runs its own deck server
454
- (`server.mjs`) on a private port of 127.0.0.1 with a token of its own; the front passes each request on
455
- with that token. A deck's server, its assistant and its undo history are those of that deck alone. The
456
- editor tabs of a browser share one event stream from the front (a shared worker), since browsers open
457
- only six connections to one port; if a deck server stops, the front opens the deck again and the tab
458
- catches up. Each PinDeck process notes its address in `~/.pindeck-v3/servers/` for `pindeck login` and
459
- `cli.mjs`, and a deck server holds its deck's `open.json`, so a deck opens in one PinDeck only.
460
- * **Deck pages are sandboxed.** The deck page (in the editor's frames, and in Present) runs with a sandbox
461
- policy in an origin of its own, at a per-run address that serves the deck's files and nothing else. A
462
- script in a deck, a template, or written by the assistant cannot reach the login cookie, the API, the
463
- chat or other decks. The editor and its bridge in the slide frame talk by `postMessage`. Headless Chrome
464
- (screenshots, layout, PDFs) does not log in at all; the page it asks for is served without the sandbox,
465
- which its screenshot and print modes cannot handle, only with a key made for that one run.
466
- * **Element ids.** On start, the server gives every slide and every visible element a readable
467
- `data-eid` attribute (`summary`, `summary/callout`, `summary/li-2`). Only the attribute is
468
- inserted; the rest of the file stays as it was. A backup of the file before the first change is
469
- kept in `~/.pindeck-v3/decks/<deck>/backups/`. Ids survive edits, so pins, layout reports and the assistant all name
470
- the same element.
471
- * **Hand edits.** A move is stored as the CSS `translate` property and a resize as `width`/`height`,
472
- inline on the element in `index.html`. `translate` moves an element without changing the layout
473
- around it, for elements in the flow and absolutely positioned ones alike. Only that one style
474
- attribute is rewritten; the rest of the file stays as it was. The assistant gets a note about each hand
475
- edit. Brand-locked elements (footer, logos) cannot be selected.
476
- * **Text edits.** While you type, the element holds its source HTML from `index.html`, not the
477
- rendered slide, so math stays TeX and nothing that KaTeX or the deck script generates ends up in
478
- the file. On save only that element's content is replaced. Browser editing artefacts are cleaned
479
- up: `<b>`/`<i>` become `<strong>`/`<em>`, styled spans from formatting are removed, trailing
480
- `<br>`s dropped.
481
- * **Pins.** A pin records the element id, its box in slide coordinates (1280 x 720), the click
482
- point, the clicked word and, in tables, the cell. Pins on the footer or logos are marked as
483
- locked.
484
- * **Layout instead of screenshots.** With each message the editor measures the current slide and
485
- every pinned slide in your browser: position and size of every element, font sizes, and issues
486
- (content below the footer rule, outside the slide, overlaps, clipped text). The assistant gets this as
487
- text. To check its own work it runs the PinDeck CLI's `layout <slide>...` (`--issues` for the
488
- issues only). Screenshots are
489
- only taken when you tick the box or the assistant needs a visual judgement.
490
- * **One selected backend.** Claude runs as a long-lived `claude -p` stream-json process; Codex runs as a
491
- local app-server over stdio. Both run in the deck folder under your own login, with the instructions
492
- from `lib/system-prompt.md` and the deck's `CLAUDE.md` and `AGENTS.md`. Codex model and effort changes
493
- reuse the active thread writer and apply to the next turn. Claude changes and provider switches prepare
494
- a replacement process before retiring the old one. Events from replaced sessions are ignored.
495
- * **Permissions.** The assistant may read the deck folder, its screenshots, attached PDFs and asset roots,
496
- edit and create files in the deck folder, and run the editor's CLI. Nothing else: no other commands, and no edits to `brand.css`, `vendor/`, `.git/` or, for a
497
- named deck file, the other decks in its folder. Claude Code loads no settings files and no MCP servers,
498
- so no allow rule in your own, the deck's or a parent folder's settings widens this. Codex has no shell
499
- and no file access of its own: it reads and edits through PinDeck's own tools, which apply the same
500
- rules and refuse symlinks out of the deck. If `brand.css` changes during a turn anyway, the server
501
- restores it and says so in the chat. Subfolders that hold a deck of their own count as other decks.
502
- * **Undo.** The server keeps a copy of every file a change touched, from before and after it (an AI
503
- turn, which may change, create or delete several files of the deck, or a hand edit). Files over 2 MB
504
- are not copied: the chat says when a turn changed one, and Undo leaves it as it is. Hidden files,
505
- `vendor/` and `export/` are not part of undo. Undo and redo walk that history and tell the assistant on
506
- the next message. They first check that the files are still as that change left them; if something
507
- else changed them since, they do nothing and say which file differs, rather than overwrite that work.
508
- Decks that share a folder share its files: while another deck's assistant works there too, a turn keeps
509
- only the files its own assistant said it wrote, and says which it left out.
510
- * **Saving.** Every file PinDeck writes into the deck is written to a temporary file next to it and then
511
- renamed into place, so other programs never see half a file. Each change from the editor carries the
512
- revision of the files it was made on; if the files have changed since, the change is refused, the
513
- slide reloads, and edited text can be copied from the message to type in again.
514
-
515
- ## Files
516
-
517
- | File | Purpose |
518
- |---|---|
519
- | `front.mjs` | What `pindeck` starts (127.0.0.1 only, random token per run, one-time login links): the login, the deck list and one deck server per open deck at `/d/<deck>/`; `login`. |
520
- | `server.mjs` | The server for one deck: UI, deck files, events, backend session, undo, CLI endpoints. Behind the front, or on its own (`node server.mjs <deck>`). |
521
- | `lib/login.mjs`, `lib/deck-target.mjs`, `lib/deck-list.mjs` | Login links and cookies, which deck a path names (and its state folder), and the decks the menu lists. |
522
- | `lib/servers.mjs` | The PinDecks running for this user, and which one serves a deck (`pindeck login`, `cli.mjs`). |
523
- | `cli.mjs` | `layout`, `shot`, `slides`, `pdf handout` / `pdf presentation` and `pdf-figure` commands for the backends (and for you), and `git-setup`. |
524
- | `public/` | The editor page; `bridge.js`, which is injected into the deck in edit mode (never saved to it); `events-worker.js`, the event stream the tabs of a browser share. |
525
- | `lib/pptx.mjs`, `lib/zip.mjs`, `lib/xml.mjs` | Reading `.pptx` files (no dependencies): slides, text, shapes and their positions, notes. |
526
- | `lib/pptx-import.mjs` | Turns PowerPoint slides into slides of the deck: figures, tables, build steps, audio, layout, HTML in the deck's style. |
527
- | `lib/waveform.mjs` | Waveform SVGs for imported audio. |
528
- | `lib/omml.mjs` | PowerPoint equations (Office Math) to TeX. |
529
- | `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. |
530
- | `lib/pdf.mjs` | PDF export: reveal.js print layout in headless Chrome, then print to PDF. |
531
- | `lib/eids.mjs` | Adds the `data-eid` attributes by editing the HTML as text. |
532
- | `lib/claude.mjs`, `lib/codex.mjs`, `lib/agent-process.mjs` | Backend adapters and JSON-line transport. |
533
- | `lib/models.mjs`, `lib/agent-tools.mjs` | Model/settings normalization, handover, and restricted Codex tools. |
534
- | `lib/capabilities.mjs` | What this server can do on this system (macOS, Linux), for the editor, the API, the CLI and the assistant. |
535
- | `lib/deck-files.mjs` | Atomic writes and the revision of the deck's files. |
536
- | `lib/system-prompt.md` | What the assistant is told about the editor. |
537
- | `CONTRIBUTING.md` | How to report bugs and contribute. |
538
- | `docs/` | Images for this README. |
539
- | `LICENSE` | MIT. |
540
-
541
- ## Current Limitations
542
-
543
- * reveal.js decks, and decks that name their slides with `<meta name="pindeck:slides">` (see [Decks made
544
- without reveal.js](#decks-made-without-revealjs)). Any other deck made with another framework (Marp, Slidev,
545
- impress.js, remark, Spectacle, Shower, deck.js, bespoke.js) is recognised and refused with its name before
546
- PinDeck changes anything.
547
- * Text inside inline SVG diagrams cannot be edited in place; pin it and ask the assistant.
548
- * Parts of an inline SVG move with the whole SVG; inline text (bold, spans) moves with its block.
549
- * 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.
550
- * 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.
551
- * The undo history lives on the server and starts fresh when PinDeck restarts, or when a deck closes after
552
- 30 minutes without an open editor.
553
- * One writer at a time: PinDeck is not made for editing the same deck from two editors or tools at once.
554
- * A deck's own scripts run sandboxed in the editor and in Present: they cannot use `localStorage`, cookies
555
- or the editor page. A deck that needs them for its slides to work should catch the error.
556
- * Linux: pictures of PDF pages and PowerPoint slides, and waveforms of non-WAV audio, need
557
- Poppler, LibreOffice and ffmpeg on the server (see [Running on a Linux server](#running-on-a-linux-server)).
558
-
559
- ## Versions
560
-
561
- * **3.3.0**: fixes from a code audit.
562
- - After a PowerPoint import, refining the slides no longer deletes pictures that the deck still uses
563
- (when the turn was stopped, failed or only changed `theme.css`); Undo brings removed pictures back.
564
- - Long texts with non-ASCII characters (large notes, tables, SVG) no longer get broken characters.
565
- - Claude reads only the deck, its screenshots, attached PDFs and asset roots, as Codex already did, and
566
- cannot create a `.git` file in the deck folder.
567
-
568
- * **3.2.0**: PinDeck 3, the first stable release of version 3: everything from the 3.0, 3.1 and 3.2 betas
569
- below. Compared with 2.11:
570
- - OpenAI Codex as a second assistant next to Claude Code, with the models and efforts each backend
571
- offers; each deck keeps its own provider, model and effort.
572
- - Several decks on one port: a deck menu above the slide, each deck with its own chat, assistant
573
- session and Undo.
574
- - Deck pages run sandboxed, so a deck's script cannot use the API or read other decks.
575
- - Linux is no longer a beta.
576
- - Settings and chats are kept in `~/.pindeck-v3`, apart from PinDeck 2's `~/.pindeck`; nothing is copied
577
- over (see [Models, providers and configuration](#models-providers-and-configuration)). PinDeck 2 stays
578
- on npm as `pindeck@2`.
579
-
580
- * **3.2.0-beta.1**: fixes from a code review and a round of user tests.
581
- - Security: deck pages run sandboxed, so a deck's script can no longer use the API, read other decks or
582
- send messages; the login link's `next` stays on PinDeck. A deck's own script cannot use `localStorage`
583
- there either (it throws).
584
- - Typing in a bullet that cannot be saved because the deck changed meanwhile can be copied, as other text
585
- can; a deck that never chose a model keeps the one its conversation started with.
586
- - Several decks in one folder: a turn no longer records or undoes another deck's files, and deleting a
587
- picture keeps a file another deck uses. Subfolder decks are locked for the assistant.
588
- - Each deck keeps its provider, model and effort; config.json is merged, not overwritten; unusable saved
589
- settings are made usable before the first message.
590
- - Many tabs on one port; a deck server that stops reconnects; `pindeck login` and `cli.mjs` find the right
591
- PinDeck; a deck opens in one PinDeck only.
592
- - Screenshots show the pins of their own message; Stop during a screenshot; failures reach the assistant.
593
- - New chat keeps Undo; undone marks survive a restart; only the latest change offers Undo.
594
- - A deck page that cannot start says why, and Send no longer hangs; typing survives an outside save; the
595
- deck menu at every width; keyboard use of the model menus; Enter in a bullet leaves no copied ids.
596
- - Large decks get their ids in linear time; symlinked decks; a deck that is gone; Chrome's own errors,
597
- and `--no-sandbox` as root; a warning when PinDeck 2 writes to the same state folder.
598
- - On a Mac, Enter in the terminal opens PinDeck in the browser.
599
- - From the code review: locked paths regardless of case (macOS) and `.git/config` restored after each
600
- turn; Codex reads PDFs of a deck reached through a symlink; "not signed in" only when Claude says so;
601
- betas tested before they are staged; model lists shared between decks; a faster deck list; smaller
602
- status updates; asset roots read alike by the front and the deck servers.
603
-
604
- * **3.1.0-beta.1**: several decks on one port, and screenshots you can open from the chat.
605
- - A deck menu: click the deck's name above the slide to open another deck from its folder or the
606
- asset roots (and one folder down). Each open deck keeps its own chat, assistant session and Undo;
607
- listing decks changes no file. `pindeck` now starts a small front that holds the port and the
608
- login and runs one deck server per open deck. A deck with no editor open for 30 minutes closes
609
- (its Undo history goes, as on a restart) and reopens when chosen again.
610
- - A folder without `index.html` opens a page listing its decks instead of failing.
611
- - `pindeck login talk.html` prints a link that opens that deck.
612
- - Screenshots the assistant took show as a small picture on the chat line; click it to see it large.
613
- A screenshot PinDeck no longer keeps says so.
614
-
615
- * **3.0.0-beta.3**: everything from 2.10.0, for both assistants.
616
- - Decks made without reveal.js: a deck that names its slides with `<meta name="pindeck:slides">`
617
- and their size with `<meta name="pindeck:size">` is edited like a reveal.js deck (contributed by
618
- Zeyu Xu). Claude and Codex are both told what the deck's slides are.
619
- - Screenshots fill the picture instead of being letterboxed into a shorter page.
620
-
621
- * **3.0.0-beta.2**: everything from 2.9 and 2.10, with Codex brought up to it.
622
- - Security: the fix from 2.9. The editor page no longer contains the token, and the deck needs it
623
- too, so other users of the same machine cannot read either. PinDeck prints a one-time login link at
624
- start (`pindeck login` prints a new one). Update from beta.1.
625
- - Linux as a beta, safe saving, Undo/Redo that refuses to overwrite outside changes, decks with their
626
- own file names, `--asset-root`, `@imports` in the deck's `CLAUDE.md` and `AGENTS.md`, and the other
627
- 2.9 and 2.10 changes (see below).
628
- - Both assistants may edit and create other files of the deck, never `brand.css`, `vendor/`, `.git/`
629
- or other decks in the folder; one Undo takes back a whole turn. Codex creates files with a new
630
- `pindeck_write` tool and edits through PinDeck's safe saving.
631
- - Claude may run only PinDeck's own commands (layout, shot, slides, pdf, pdf-figure), each named.
632
- - Starting with `--provider codex` and no saved model now picks Codex's default model, also while
633
- the model lists are still loading.
634
- - Stopping PinDeck waits for every backend process, including those that list models.
635
-
636
- * **3.0.0-beta.1**: Claude and Codex adapters, discovered model/effort menu, explicit provider
637
- switching with optional handover, curated model lists, a separate effort selector with per-model
638
- preferences, restricted Codex tools, and separate v3 runtime state. On npm as `pindeck@beta`.
639
-
640
- * **2.11**: screenshots in the chat. A screenshot sent with a message, and one Claude reads, shows as
641
- a small picture that opens larger with a click (Esc or a click closes it). PinDeck keeps the 40
642
- newest screenshots of a deck; an older one says "no longer kept". Chats from before 2.11 keep their
643
- plain label. (In 3.x since 3.1.0-beta.1.)
52
+ * **PDF input through Codex:** pages are rendered with macOS PDFKit, or Poppler on Linux. Page-text extraction uses `osascript` and is macOS-only; on Linux Codex reads the rendered page image. Claude uses its native PDF-reading capabilities.
53
+ * **PowerPoint import:** basic `.pptx` parsing uses Node.js; rendered slide pictures require PowerPoint or Keynote on macOS, or LibreOffice and Poppler on Linux.
54
+ * **Linux tools:** Poppler, LibreOffice and ffmpeg, each optional; features that need a missing one are switched off with the reason shown in the editor.
644
55
 
645
- * **2.10**: decks with their own file names and shared files, decks made without reveal.js, and Claude
646
- working on the whole deck.
647
- - A deck file by name: `pindeck path/to/talk.html` (a folder still opens its `index.html`). Several
648
- decks can share a folder, each with its own chat and undo history.
649
- - Claude may edit and create other files of the deck (scripts, style sheets, notes), never
650
- `brand.css`, `vendor/` or other decks in the folder. One Undo takes back everything a turn changed,
651
- created or deleted; files over 2 MB are not copied, and the chat says so.
652
- - Files outside the deck folder: `--asset-root <folder>` (or `"assetRoots"` in config.json) serves
653
- the deck from inside that folder, so paths such as `../shared/style.css` work in the editor,
654
- screenshots and PDF exports. A symlink that leads out of the deck and its asset roots is refused.
655
- - Shared instructions: the deck's `CLAUDE.md` and `AGENTS.md` may import other files with `@path`.
656
- `CLAUDE.md` files in parent folders are still not loaded, on purpose (see the README).
657
- - Decks made without reveal.js (a script of their own, Shower, deck.js, a hand-written deck): a deck
658
- that names its slides with `<meta name="pindeck:slides">` (a CSS selector) and their size with
659
- `<meta name="pindeck:size">` is edited like a reveal.js deck, with screenshots at the slide size and
660
- one PDF page per slide (contributed by Zeyu Xu). Decks made with other slide frameworks (Marp,
661
- Slidev, impress.js, remark and others) without these tags are recognised and refused with their
662
- name, before PinDeck changes anything; the message says how to add the tags.
663
- - Screenshots fill the picture: headless Chrome lays the page out shorter than its window, which
664
- left the slide letterboxed in some Chrome versions (reported by Zeyu Xu).
665
- - Linux: pictures of PDF pages with Poppler, the PowerPoint import with slide pictures from
666
- LibreOffice, and waveforms of non-WAV audio with ffmpeg, each when installed.
56
+ The initial live editing checks used Codex CLI **0.144.1** and Claude Code **2.1.289**. Further live checks on macOS passed with Codex CLI **0.162.1** and Claude Code **2.1.301**: editing, Claude effort changes, Codex model changes, conversation handover, undo/redo, cancellation and recovery. These are tested versions, not guaranteed minimum requirements. Backends must support the discovery and session interfaces used by PinDeck. Older CLIs can expose fewer models or fail initialization.
667
57
 
668
- * **2.9**: Linux as a beta, and safer editing.
669
- - Linux (beta): PinDeck runs as a headless server, used from a Mac's browser through an SSH tunnel,
670
- with Claude Code running on the server. The tunnel may use another local port than the server.
671
- Screenshots, layout checks without an open editor and PDF export work when Chrome or Chromium is
672
- found (`PINDECK_LINUX_CHROME=0` turns them off). Features a system cannot provide are switched off,
673
- with the reason shown in the editor. Changes made to the deck by other programs are seen every
674
- time, also after a file was replaced by a rename.
675
- - Security: the editor page no longer contains the token, and the deck needs it too, so other users
676
- of the same machine cannot read either. PinDeck prints a one-time login link at start
677
- (`pindeck login` prints a new one), which keeps the token in an HttpOnly cookie named after the
678
- server's port; headless Chrome logs in the same way, so the token is never on a command line. After
679
- a restart, log in again with a new link.
680
- - Saving is atomic; edits made on files that changed meanwhile are refused, and Undo/Redo no longer
681
- overwrite changes made outside PinDeck.
682
- - Audio and video play in Safari: deck files are sent with their length and byte ranges, and ogg,
683
- opus, m4a, aac, flac and mov get their media types.
684
- - Claude gets the right CLI command for decks outside the PinDeck folder; the example's `CLAUDE.md`
685
- no longer names a path. The deck's `AGENTS.md` reaches Claude along with its `CLAUDE.md`.
686
- - macOS: pictures of PDF pages (`pdf-figure`) and of PowerPoint slides have a white page instead of a
687
- transparent one, so dark text no longer disappears on a dark slide.
688
- - Screenshots and PDF exports are no longer now and then reported as failed although Chrome wrote them.
689
- - `git-setup` works for decks behind symlinks or with spaces in their path. The tests run on Linux
690
- and macOS in CI, including a real Chrome.
58
+ Codex integration uses OpenAI’s experimental `dynamicTools` extension to the app-server. A future Codex update may require a PinDeck adapter update. Incompatible backends report startup errors; PinDeck does not fall back to broader permissions. Recheck backend compatibility when upgrading.
691
59
 
692
- * **2.8.2**: declares macOS-only support in npm metadata and the README to prevent accidental
693
- installation on unsupported operating systems. Source launches on other platforms show a support
694
- notice without opening the deck.
60
+ ## Documentation
695
61
 
696
- * **2.8.1**: fixes from a review. Speaker notes that fail to load leave the field empty and locked, instead
697
- of showing the previous slide's notes and saving your edits over them. A save that fails because the
698
- server cannot be reached is reported, and the edits after it (drags, text, bullets, notes) are still
699
- saved instead of being dropped without a word. Stop no longer ends a turn started after it. Slides in
700
- vertical stacks keep their order and ids, and a slide moved to the end goes after a final stack.
701
- A first automated test suite (`npm test`).
702
- * **2.8**: a code review: safer, sturdier, and a PowerPoint import that keeps more. The server answers
703
- only to `127.0.0.1` and `localhost`, so another website cannot read the editor's token (DNS
704
- rebinding). A missing `claude` command ends the turn with a message instead of leaving the chat
705
- working forever, and a replaced Claude Code process can no longer end the next turn. Esc in the import
706
- dialog no longer repeats the last import, and an import never overwrites edits made while it runs. The
707
- PowerPoint import keeps animations that play by themselves off the clicks, wraps shapes that come and
708
- go in fragments of their own, skips hidden shapes and `javascript:` links, reads packages with
709
- absolute part names, takes only the first line of a subtitle placeholder as the subtitle, keeps
710
- equations in titles and notes, writes numbered lists as `<ol>`, keeps the text of coloured boxes
711
- without PowerPoint or Keynote, and makes valid TeX of escaped text, equation arrays, odd delimiters
712
- and primes after superscripts. Damaged WAV or XML parts no longer stop an import. The toolbar wraps
713
- instead of overlapping when the slide area is narrow.
714
- * **2.7**: attach PDFs to chat messages (a figure, a table, a paper or report). They stay outside the deck; Claude reads
715
- them, rebuilds tables as HTML and renders figures into `figures/` with `cli.mjs pdf-figure` (a page or a cropped part, macOS).
716
- * **2.6.1**: tinted table rows are one rectangle per row instead of one per cell, so PDF viewers show no hairline seams between the cells.
717
- * **2.6**: booktabs-style tables in both example decks (rules at top, under the header and at the bottom,
718
- short rules under grouped headings, numbers right-aligned); the import writes row groups without `rowspan`.
719
- * **2.5**: a notes field under the slide for the speaker notes, saved as hand edits with undo.
720
- * **2.4.1**: the layout check judges a plain container by what it draws (its children), not by its box, so
721
- a body stretched down to the references no longer counts as an overlap; a real overlap is reported once,
722
- for the outermost pair.
723
- * **2.4**: move slides by dragging them in the slide list; undo puts them back.
724
- * **2.3**: lessons from a PowerPoint hand-off. Claude Code runs without settings files or MCP servers
725
- (the deck's `CLAUDE.md` goes into the system prompt instead), so stray allow rules no longer let it run
726
- scripts; `layout` takes several slides and `--issues`; the chat no longer labels a refused script as a
727
- layout check; shared SVG arrowheads live outside the slides, where hidden slides cannot hide them.
728
- * **2.2**: PowerPoint import with equations as TeX for KaTeX, and the hand-off to Claude: complex slides are
729
- tagged in the import dialog and, if wanted, improved by Claude right after the import, with a picture of
730
- each original slide.
731
- * **2.1**: PowerPoint import with build steps (fragments in click order), HTML tables in the deck's table
732
- style, audio in the deck's player with a waveform, video with its poster, coloured words as `.hl`.
733
- * **2.0**: import slides from PowerPoint (`.pptx`): text as HTML in the deck's layouts, pictures copied,
734
- diagrams, tables and charts as pictures cut from PowerPoint's rendering; thumbnails in the import
735
- dialog; undo removes the imported slides and their figures.
736
- * **1.12.1**: the PinDeck pin as the browser tab icon (SVG, with PNG versions for Safari).
737
- * **1.12**: double-clicking an equation opens its text with the equation's TeX selected, also for
738
- equations on their own line and for parts KaTeX draws as SVG (roots, wide accents, arrows).
739
- * **1.11.3**: a calmer interface: no slide count, no Hide/Show button in the header (eye and `H`
740
- remain), no messages or chat entries for hiding and showing, shorter export descriptions.
741
- * **1.11.2**: the header shows the deck's name instead of the slide title; the browser tab is named after the deck.
742
- * **1.11.1**: the Hide and Show icons sit at the same height.
743
- * **1.11**: hide and show slides (eye in the slide list, toolbar button, `H`); hidden slides are skipped
744
- when presenting and in PDFs unless the export includes them.
745
- * **1.10**: model and effort without "Default" entries (default Sonnet, medium), set in `~/.pindeck-v3/config.json`.
746
- * **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`.
747
- * **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.
748
- * **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.
749
- * **1.6**: attach figures and audio to chat messages (file picker, drag and drop, paste); audio gets a waveform SVG; files dropped on a slide carry their position.
750
- * **1.5**: PDF export (handout and presentation with build steps on separate pages).
751
- * **1.4**: decks describe their footer and locked brand elements with `<meta name="pindeck:...">` tags; the layout check ignores invisible elements. New public example deck: `ocean-example/`.
752
- * **1.3**: `Enter` in a bullet starts a new bullet (split at the cursor); empty new bullets are removed again.
753
- * **1.2**: text editing on the slide (double-click or Enter), with math as TeX, table cells, bold and italic, plain-text paste; text edits are part of the undo history and reported to Claude.
754
- * **1.1**: select, drag, resize and nudge with snapping guides; one undo/redo history for hand edits and Claude's changes; the selected element is sent to Claude.
755
- * **1.0**: pins, chat with Claude Code, layout reports, element ids, diff and undo.
62
+ * [Using PinDeck](docs/using.md): several decks, every action in the editor, review comments.
63
+ * [Preparing decks](docs/preparing-decks.md): getting a deck ready, your own template, Quarto decks, PowerPoint import.
64
+ * [Configuration](docs/configuration.md): models and providers, backend permissions and attachments, keeping the ids out of git.
65
+ * [Running on a Linux server](docs/linux-server.md): the headless server and the SSH tunnel.
66
+ * [How it works](docs/how-it-works.md): the architecture, the files, and the current limitations.
67
+ * [Changelog](CHANGELOG.md): what each version brought.
756
68
 
757
69
  ## Contributing
758
70
 
759
- Bug reports, ideas, example decks and pull requests are welcome. [CONTRIBUTING.md](CONTRIBUTING.md)
760
- explains how to report a bug, set up PinDeck for development, test a change by hand, and what a pull
761
- request should contain. In short: no npm dependencies, deck files are edited only where they must
762
- change, both backends’ permissions stay narrow, and the README describes what users see.
71
+ Bug reports, ideas, example decks and pull requests are welcome. [CONTRIBUTING.md](CONTRIBUTING.md) explains how to report a bug, set up PinDeck for development, test a change by hand, and what a pull request should contain. In short: no npm dependencies, deck files are edited only where they must change, both backends’ permissions stay narrow, and the README describes what users see.
763
72
 
764
73
  ## License
765
74