pindeck 3.1.0-beta.1 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,10 +1,9 @@
1
1
  # PinDeck
2
2
 
3
- > **macOS, and Linux as a beta · Version 3 beta preview** (`3.1.0-beta.1`). 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.
3
+ > **macOS and Linux · Version 3** (`3.2.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)).
8
7
 
9
8
  **Pin it. Say it. Done.** PinDeck is a local editor for HTML slide decks (reveal.js): the slides on
10
9
  the left, the slide in the middle, and a chat with Claude Code or OpenAI Codex on the right. You click elements to
@@ -32,39 +31,39 @@ checked the layout; the next request, still being typed, refers to the new pins
32
31
 
33
32
  ## Quick start
34
33
 
35
- Runs on macOS, and on Linux as a beta. The npm package declares `"os": ["darwin", "linux"]`, so normal
34
+ Runs on macOS and Linux. The npm package declares `"os": ["darwin", "linux"]`, so normal
36
35
  npm installation rejects other operating systems. Overriding that check does not make those platforms
37
36
  supported. Running the server from a cloned repository on another OS shows a local support notice
38
37
  instead of opening the deck or starting a backend.
39
38
 
40
- Install the beta from npm:
39
+ Install from npm:
41
40
 
42
41
  ```bash
43
- npx pindeck@beta /path/to/deck
44
- # Or select this exact beta:
45
- npx pindeck@3.1.0-beta.1 /path/to/deck
42
+ npx pindeck /path/to/deck
46
43
  ```
47
44
 
48
- Or run it from the `v3-beta` branch, which includes the example deck:
45
+ Or run it from source, which includes the example deck:
49
46
 
50
47
  ```bash
51
- git clone --branch v3-beta --single-branch https://github.com/ehabets/PinDeck.git PinDeck-v3
52
- cd PinDeck-v3
48
+ git clone https://github.com/ehabets/PinDeck.git
49
+ cd PinDeck
53
50
  node front.mjs ocean-example
54
51
  ```
55
52
 
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).
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).
58
56
 
59
57
  Then open the login link PinDeck prints (`PinDeck: http://127.0.0.1:5180/login?code=...`). It works
60
- once and logs this browser in; for a new one, run `pindeck login` (or `node front.mjs login`).
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.
61
60
 
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;
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;
64
63
  they share the folder's `theme.css` and `brand.css`, and the assistant edits only the deck that is open.
65
64
  PinDeck adds element ids to the deck file when it opens it (see below), so try it on a copy of a deck
66
65
  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)),
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)),
68
67
  `--help`. Runtime files (chat history, screenshots, backups) are kept in `~/.pindeck-v3/` (change with
69
68
  `PINDECK_HOME`), not in the deck. `ocean-example/` is a complete example deck to start from.
70
69
 
@@ -75,9 +74,16 @@ the asset roots, and in their folders one level down (`lecture/index.html`). A d
75
74
  tab, or with ⌘-click in a new one, on the same port and login, so one SSH tunnel serves them all. Each keeps
76
75
  its own chat and undo history while it is open, and the assistant finishes a turn in a deck you leave. A
77
76
  file PinDeck cannot open is greyed out with the reason; listing a deck changes nothing, and only opening it
78
- adds its element ids. Started with a folder that has no `index.html`, PinDeck shows the list of its decks.
79
- A deck with no editor open for 30 minutes is closed (its undo history goes, as on a restart) and opens
80
- again when you choose 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.
81
87
 
82
88
  ## Compatibility and requirements
83
89
 
@@ -86,11 +92,11 @@ again when you choose it.
86
92
  The corresponding executable must be on `PATH`; `CLAUDE_BIN` and `CODEX_BIN` can override its path.
87
93
  Only the selected backend is required for editing. Model and effort availability comes from that
88
94
  backend and account, and may differ from the provider’s website or desktop app.
89
- * **Platforms:** macOS, and Linux as a beta (see [Running on a Linux server](#running-on-a-linux-server-beta)).
95
+ * **Platforms:** macOS and Linux (see [Running on a Linux server](#running-on-a-linux-server)).
90
96
  Windows is not supported. The npm package declares `"os": ["darwin", "linux"]`, so normal npm
91
97
  installation rejects other operating systems. This check can be overridden; bypassing it does not make
92
98
  those platforms supported. Source launches on unsupported platforms show a local support page without
93
- opening the deck or starting a backend. Native Windows support is a possible v4 feature, not part of this beta.
99
+ opening the deck or starting a backend. Native Windows support is a possible v4 feature, not part of version 3.
94
100
  * **Screenshots and PDF export:** require Chrome, Chromium, Edge or Brave where the server runs.
95
101
  * **PDF input through Codex:** pages are rendered with macOS PDFKit, or Poppler on Linux. Page-text
96
102
  extraction uses `osascript` and is macOS-only; on Linux Codex reads the rendered page image. Claude uses
@@ -220,7 +226,9 @@ and Haiku model in each family. Claude versions are compared numerically from di
220
226
  a rolling family alias wins ties, avoiding duplicate entries. **Show all available models** reveals
221
227
  the full discovered catalog; this display preference is remembered in the browser. A currently selected
222
228
  older model remains visible and is never switched automatically by the filter.
223
- **Refresh models** retries discovery. A missing CLI, missing sign-in, or discovery failure is shown in its provider group.
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.
224
232
  These are backend-reported choices, not a guarantee of current quota or entitlement; a later rejected
225
233
  request reports its error. Older CLIs may expose fewer models than a provider's website.
226
234
 
@@ -234,7 +242,9 @@ additional model-generated summary. It excludes tool output, needs no extra infe
234
242
  sent with the first message to the new session. Earlier messages may be omitted. **Start fresh** sends
235
243
  no conversation digest; the assistant still receives PinDeck instructions and the current deck context.
236
244
 
237
- Settings are saved when changed in `~/.pindeck-v3/config.json` (or `$PINDECK_HOME/config.json`):
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:
238
248
 
239
249
  ```json
240
250
  {
@@ -244,13 +254,22 @@ Settings are saved when changed in `~/.pindeck-v3/config.json` (or `$PINDECK_HOM
244
254
  }
245
255
  ```
246
256
 
247
- Legacy configuration without `provider` is interpreted as Claude. Unsupported effort choices are
248
- adjusted to the discovered model's default when starting. Provider-tagged chat/session records prevent
249
- one backend from resuming the other's IDs. `--provider` and `--model` override startup settings for
250
- that run; a later menu selection is saved. `CLAUDE_BIN` and `CODEX_BIN` override executable paths.
251
-
252
- The v3 preview defaults to **`~/.pindeck-v3`**, keeping v2 settings and conversations separate. It does
253
- not copy the v2 directory automatically. To run both versions at once, use another port, for example:
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:
254
273
 
255
274
  ```bash
256
275
  node front.mjs /path/to/a/deck-copy --port 5183 --provider codex
@@ -261,16 +280,17 @@ node front.mjs /path/to/a/deck-copy --port 5183 --provider codex
261
280
  Claude uses its native Read/Edit tools with PinDeck's narrow allowlist, safe mode, and no inherited
262
281
  MCP configuration. Codex runs with a read-only sandbox, command execution disabled, and no approval
263
282
  grants. Inherited MCP servers and plugins are disabled in the thread configuration. Codex receives
264
- three host-controlled tools: `pindeck_read`, `pindeck_edit`, and `pindeck_command`.
265
- Only `index.html` and `theme.css` can be edited. Linked writable files and paths outside the allowed
266
- roots are rejected. Commands are restricted to PinDeck's layout, shot, slides, pdf and pdf-figure
267
- helpers, executed with argument arrays rather than a shell. `brand.css` also retains the server's
268
- post-turn restoration safeguard.
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.
269
289
 
270
290
  Codex reads text, PNG/JPEG/WebP/GIF images and screenshots through the bridge. SVG is read as text.
271
- Attached PDFs are handled page by page using PDFKit on macOS: extracted text plus a rendered page image.
272
- PDF reading/rendering through this bridge is currently macOS-only. Audio is embedded by path and is
273
- not transcribed. Read operations have a 12 MB limit; long text can be read in chunks.
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.
274
294
 
275
295
  ### Files outside the deck folder
276
296
 
@@ -283,8 +303,10 @@ exports. Claude may read those files but does not edit them.
283
303
  Symlinks: PinDeck serves a file only if, with symlinks followed, it lies inside the deck folder or an
284
304
  asset root. A symlink in the deck that leads anywhere else is refused (403). To use a symlinked
285
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.
286
308
 
287
- ## Running on a Linux server (beta)
309
+ ## Running on a Linux server
288
310
 
289
311
  PinDeck can run on a Linux machine where the deck lives, with the editor in your Mac's browser. The
290
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
@@ -312,10 +334,10 @@ message twice. If PinDeck restarts, the editor asks you to log in again: run `no
312
334
  path/to/deck` on the server for a new link.
313
335
 
314
336
  Other users of a shared server cannot open the editor or read the deck through PinDeck: the token that
315
- protects it is only in your browser's cookie and in PinDeck's own files in `~/.pindeck` (readable only by
337
+ protects it is only in your browser's cookie and in PinDeck's own files in `~/.pindeck-v3` (readable only by
316
338
  you), never in a page or on a command line.
317
339
 
318
- What works on Linux in this beta: editing by hand, speaker notes, slides, pins, the chat with Claude or Codex,
340
+ What works on Linux: editing by hand, speaker notes, slides, pins, the chat with Claude or Codex,
319
341
  Undo/Redo, live reload when files change on the server, layout checks, attaching figures, audio and
320
342
  PDFs, `git-setup` and the PowerPoint import. Some features use programs on the server, and are on when
321
343
  they are installed (on Debian or Ubuntu: `sudo apt install poppler-utils libreoffice-impress ffmpeg`):
@@ -327,13 +349,16 @@ they are installed (on Debian or Ubuntu: `sudo apt install poppler-utils libreof
327
349
  | Pictures of PowerPoint slides in the import (thumbnails, diagrams) | LibreOffice (`soffice`) and Poppler |
328
350
  | Waveforms of audio other than WAV in the import | ffmpeg |
329
351
 
330
- Without them the editor says what is missing (hover over "Server: Linux (beta)"), and the assistant is told
352
+ Without them the editor says what is missing (hover over "Server: Linux"), and the assistant is told
331
353
  which commands are not available. LibreOffice may draw some slides a little differently from PowerPoint;
332
354
  the pictures are references and figures, the slides themselves are rebuilt from the file. Chromium from snap may not be
333
355
  allowed to use hidden folders in your home folder such as `~/.pindeck-v3`; if screenshots fail with it, set
334
- `PINDECK_HOME` to a folder that is not hidden, or install Chrome or Chromium from a package.
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.
335
359
 
336
- Run one PinDeck per deck. While PinDeck or the assistant is working on a deck, do not edit it with other tools
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
337
362
  or switch git branches at the same time. Stop first, or wait until the assistant has finished. PinDeck notices
338
363
  changes made by other programs and reloads, and it refuses Undo, Redo or an edit that would overwrite
339
364
  them. Its checks cannot rule out every race with a program that writes at the same moment.
@@ -361,10 +386,10 @@ show and commit as usual. Clones without the filter configured simply see the fi
361
386
  | Undo, redo | `⌘Z` / `⇧⌘Z` or the toolbar arrows. One history for your hand edits and the assistant's changes. |
362
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. |
363
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. |
364
- | Visual questions | **+** in the chat, then **Screenshot**: the current slide with its pin markers is attached when you send. |
365
- | Review | Each answer ends with the changed files, a **Diff** button and an **Undo** button (most recent change). |
366
- | Stop | **Stop** interrupts the assistant; the conversation continues. **New chat** starts over. |
367
- | 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. |
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. |
368
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. |
369
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. |
370
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. |
@@ -373,10 +398,12 @@ show and commit as usual. Clones without the filter configured simply see the fi
373
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. |
374
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". |
375
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. |
376
- | Navigate | Slide list, `←` `→`, or the toolbar. **Present** opens the deck in a new tab. |
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. |
377
403
 
378
404
  You can also edit `index.html` or `theme.css` in any editor; the slide reloads, and the assistant is told
379
- the files changed. CSS changes are swapped in without a reload.
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.
380
407
 
381
408
  ## PowerPoint import
382
409
 
@@ -425,7 +452,17 @@ Without PowerPoint or Keynote (or on other systems than macOS) only the text and
425
452
  * **One port, a server per deck.** `pindeck` starts the front (`front.mjs`): it holds the port, the login
426
453
  and the deck list, and serves each open deck at `/d/<deck>/`. Every deck runs its own deck server
427
454
  (`server.mjs`) on a private port of 127.0.0.1 with a token of its own; the front passes each request on
428
- with that token. A deck's server, its assistant and its undo history are those of that deck alone.
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.
429
466
  * **Element ids.** On start, the server gives every slide and every visible element a readable
430
467
  `data-eid` attribute (`summary`, `summary/callout`, `summary/li-2`). Only the attribute is
431
468
  inserted; the rest of the file stays as it was. A backup of the file before the first change is
@@ -461,13 +498,15 @@ Without PowerPoint or Keynote (or on other systems than macOS) only the text and
461
498
  so no allow rule in your own, the deck's or a parent folder's settings widens this. Codex has no shell
462
499
  and no file access of its own: it reads and edits through PinDeck's own tools, which apply the same
463
500
  rules and refuse symlinks out of the deck. If `brand.css` changes during a turn anyway, the server
464
- restores it and says so in the chat.
501
+ restores it and says so in the chat. Subfolders that hold a deck of their own count as other decks.
465
502
  * **Undo.** The server keeps a copy of every file a change touched, from before and after it (an AI
466
503
  turn, which may change, create or delete several files of the deck, or a hand edit). Files over 2 MB
467
504
  are not copied: the chat says when a turn changed one, and Undo leaves it as it is. Hidden files,
468
505
  `vendor/` and `export/` are not part of undo. Undo and redo walk that history and tell the assistant on
469
506
  the next message. They first check that the files are still as that change left them; if something
470
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.
471
510
  * **Saving.** Every file PinDeck writes into the deck is written to a temporary file next to it and then
472
511
  renamed into place, so other programs never see half a file. Each change from the editor carries the
473
512
  revision of the files it was made on; if the files have changed since, the change is refused, the
@@ -480,8 +519,9 @@ Without PowerPoint or Keynote (or on other systems than macOS) only the text and
480
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`. |
481
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>`). |
482
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`). |
483
523
  | `cli.mjs` | `layout`, `shot`, `slides`, `pdf handout` / `pdf presentation` and `pdf-figure` commands for the backends (and for you), and `git-setup`. |
484
- | `public/` | The editor page, and `bridge.js`, which is injected into the deck in edit mode (never saved to it). |
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. |
485
525
  | `lib/pptx.mjs`, `lib/zip.mjs`, `lib/xml.mjs` | Reading `.pptx` files (no dependencies): slides, text, shapes and their positions, notes. |
486
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. |
487
527
  | `lib/waveform.mjs` | Waveform SVGs for imported audio. |
@@ -511,11 +551,50 @@ Without PowerPoint or Keynote (or on other systems than macOS) only the text and
511
551
  * The undo history lives on the server and starts fresh when PinDeck restarts, or when a deck closes after
512
552
  30 minutes without an open editor.
513
553
  * One writer at a time: PinDeck is not made for editing the same deck from two editors or tools at once.
514
- * Linux (beta): pictures of PDF pages and PowerPoint slides, and waveforms of non-WAV audio, need
515
- Poppler, LibreOffice and ffmpeg on the server (see [Running on a Linux server](#running-on-a-linux-server-beta)).
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
+ * Decks are `.html` files; `.htm` files are not listed or opened as decks.
557
+ * Linux: pictures of PDF pages and PowerPoint slides, and waveforms of non-WAV audio, need
558
+ Poppler, LibreOffice and ffmpeg on the server (see [Running on a Linux server](#running-on-a-linux-server)).
516
559
 
517
560
  ## Versions
518
561
 
562
+ * **3.2.0**: PinDeck 3, the first stable release of version 3: everything from the 3.0, 3.1 and 3.2 betas
563
+ below. Compared with 2.11:
564
+ - OpenAI Codex as a second assistant next to Claude Code, with the models and efforts each backend
565
+ offers; each deck keeps its own provider, model and effort.
566
+ - Several decks on one port: a deck menu above the slide, each deck with its own chat, assistant
567
+ session and Undo.
568
+ - Deck pages run sandboxed, so a deck's script cannot use the API or read other decks.
569
+ - Linux is no longer a beta.
570
+ - Settings and chats are kept in `~/.pindeck-v3`, apart from PinDeck 2's `~/.pindeck`; nothing is copied
571
+ over (see [Models, providers and configuration](#models-providers-and-configuration)). PinDeck 2 stays
572
+ on npm as `pindeck@2`.
573
+
574
+ * **3.2.0-beta.1**: fixes from a code review and a round of user tests.
575
+ - Security: deck pages run sandboxed, so a deck's script can no longer use the API, read other decks or
576
+ send messages; the login link's `next` stays on PinDeck. A deck's own script cannot use `localStorage`
577
+ there either (it throws).
578
+ - Typing in a bullet that cannot be saved because the deck changed meanwhile can be copied, as other text
579
+ can; a deck that never chose a model keeps the one its conversation started with.
580
+ - Several decks in one folder: a turn no longer records or undoes another deck's files, and deleting a
581
+ picture keeps a file another deck uses. Subfolder decks are locked for the assistant.
582
+ - Each deck keeps its provider, model and effort; config.json is merged, not overwritten; unusable saved
583
+ settings are made usable before the first message.
584
+ - Many tabs on one port; a deck server that stops reconnects; `pindeck login` and `cli.mjs` find the right
585
+ PinDeck; a deck opens in one PinDeck only.
586
+ - Screenshots show the pins of their own message; Stop during a screenshot; failures reach the assistant.
587
+ - New chat keeps Undo; undone marks survive a restart; only the latest change offers Undo.
588
+ - A deck page that cannot start says why, and Send no longer hangs; typing survives an outside save; the
589
+ deck menu at every width; keyboard use of the model menus; Enter in a bullet leaves no copied ids.
590
+ - Large decks get their ids in linear time; symlinked decks; a deck that is gone; Chrome's own errors,
591
+ and `--no-sandbox` as root; a warning when PinDeck 2 writes to the same state folder.
592
+ - On a Mac, Enter in the terminal opens PinDeck in the browser.
593
+ - From the code review: locked paths regardless of case (macOS) and `.git/config` restored after each
594
+ turn; Codex reads PDFs of a deck reached through a symlink; "not signed in" only when Claude says so;
595
+ betas tested before they are staged; model lists shared between decks; a faster deck list; smaller
596
+ status updates; asset roots read alike by the front and the deck servers.
597
+
519
598
  * **3.1.0-beta.1**: several decks on one port, and screenshots you can open from the chat.
520
599
  - A deck menu: click the deck's name above the slide to open another deck from its folder or the
521
600
  asset roots (and one folder down). Each open deck keeps its own chat, assistant session and Undo;
@@ -552,6 +631,11 @@ Without PowerPoint or Keynote (or on other systems than macOS) only the text and
552
631
  switching with optional handover, curated model lists, a separate effort selector with per-model
553
632
  preferences, restricted Codex tools, and separate v3 runtime state. On npm as `pindeck@beta`.
554
633
 
634
+ * **2.11**: screenshots in the chat. A screenshot sent with a message, and one Claude reads, shows as
635
+ a small picture that opens larger with a click (Esc or a click closes it). PinDeck keeps the 40
636
+ newest screenshots of a deck; an older one says "no longer kept". Chats from before 2.11 keep their
637
+ plain label. (In 3.x since 3.1.0-beta.1.)
638
+
555
639
  * **2.10**: decks with their own file names and shared files, decks made without reveal.js, and Claude
556
640
  working on the whole deck.
557
641
  - A deck file by name: `pindeck path/to/talk.html` (a folder still opens its `index.html`). Several
package/cli.mjs CHANGED
@@ -15,14 +15,20 @@
15
15
 
16
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
- <slide> is a slide id (data-eid), a slide number (1-based) or "current". */
18
+ <slide> is a slide id (data-eid), a slide number (1-based) or "current". The assistant's commands reach its
19
+ own deck server (DECK_EDITOR_URL); run from a terminal, they reach the PinDeck that serves the deck in the
20
+ current folder (its index.html), or the deck named with --deck <folder | file.html>. */
19
21
 
20
22
  import fs from "node:fs";
21
- import os from "node:os";
22
23
  import path from "node:path";
23
24
  import { execFileSync } from "node:child_process";
25
+ import { deckTarget, homeDir } from "./lib/deck-target.mjs";
26
+ import { serverFor } from "./lib/servers.mjs";
24
27
 
25
- const [cmd, ...rest] = process.argv.slice(2);
28
+ const args = process.argv.slice(2);
29
+ const deckAt = args.indexOf("--deck");
30
+ const deckArg = deckAt >= 0 ? args.splice(deckAt, 2)[1] : null;
31
+ const [cmd, ...rest] = args;
26
32
  const arg = rest[0];
27
33
 
28
34
  /* git-setup: a git "clean" filter strips PinDeck's data-eid attributes whenever git reads the deck's
@@ -68,13 +74,17 @@ function figureQuery(args) {
68
74
 
69
75
  let url = process.env.DECK_EDITOR_URL, token = process.env.DECK_EDITOR_TOKEN;
70
76
  if (!url || !token) {
71
- try {
72
- const home = process.env.PINDECK_HOME || path.join(os.homedir(), ".pindeck-v3");
73
- const s = JSON.parse(fs.readFileSync(path.join(home, "server.json"), "utf8"));
74
- url = s.url; token = s.token;
75
- } catch {}
77
+ // from a terminal: the deck's own server if it is open, else a front that lists it (which opens it)
78
+ const deck = deckTarget(deckArg || process.cwd());
79
+ if (!fs.existsSync(path.join(deck.dir, deck.file))) {
80
+ console.error(`No ${deck.file} in ${deck.dir}: run this in a deck's folder, or name the deck with --deck <folder | file.html>.`);
81
+ process.exit(2);
82
+ }
83
+ const found = await serverFor(homeDir(), deck);
84
+ if (!found) { console.error(`No running PinDeck serves ${path.join(deck.dir, deck.file)} (start it with: pindeck ${deckArg || "."}).`); process.exit(1); }
85
+ ({ url, token } = found.own || found.front);
86
+ if (!found.own) url = `${url}/d/${found.key}`;
76
87
  }
77
- if (!url) { console.error("The PinDeck server is not running (start it with: pindeck <deck-folder>)."); process.exit(1); }
78
88
 
79
89
  /* layout and shot take several slides, so the assistant needs no shell loop or pipe (which PinDeck refuses) */
80
90
  const queries = cmd === "pdf-figure" ? [figureQuery([...rest])]