Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.3.0] - 2026-09-12

### Added

- **Save and load projects.** A versioned project file holds the whole job — objects,
placements, pens per object, paper, mode and magnets — plus the pen library it refers to,
so a drawing opened on another machine still comes out in the right colours. Save to your
own device as a file, or to the plotter, which now keeps a library every connected client
can list, open and delete (`PLOTTER_PROJECTS`, default `gateway/projects/`). Project names
can list, open and delete (`PLOTTER_PROJECTS`, defaulting to a `projects/` directory beside the state file). Project names
are sanitised before they become paths on the Pi, and the resolved path is checked against
the projects directory as well.
- **Import from anything.** One import that identifies a file by its *contents* rather than
Expand All @@ -28,6 +30,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- App settings and saved projects defaulted to paths beside the *bundle* rather than
beside the state file. On a packaged Pi that is a read-only `/opt`, and the config file
naming the right paths is a dpkg conffile — so an upgraded machine whose operator had
edited it would have kept the old copy, never learned the new variables, and silently
failed to persist either. Both now default to the state file's directory, which has been
configured since the first packaged release (as the update-status file already did).
- The daemon served `.mjs` as `application/octet-stream`, which browsers refuse to execute as
a module — a lazily-loaded chunk would have worked in the dev server and failed only in the
packaged build. `.wasm` and `.map` were missing too.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,8 @@ On macOS the daemon automatically runs `caffeinate -dimsu` for its lifetime so i
| `PLOTTER_PATH` | _(auto)_ | Pin the serial device; otherwise auto-detect a `usbserial`/`wchusbserial`/`ttyUSB`/`ttyACM` port |
| `PLOTTER_STATE` | `gateway/.plotter-state.json` | Where the remembered position is persisted |
| `PLOTTER_SESSION` | `gateway/.session.json` | Where the shared editable session (artwork + page) is persisted |
| `PLOTTER_APP_SETTINGS` | `gateway/.app-settings.json` | Where app settings (machine setup, preferences) are persisted — shared by every client |
| `PLOTTER_PROJECTS` | `gateway/projects/` | Where saved projects are stored, one file per project |
| `PLOTTER_APP_SETTINGS` | _(beside `PLOTTER_STATE`)_ | Where app settings (machine setup, preferences) are persisted — shared by every client |
| `PLOTTER_PROJECTS` | _(`projects/` beside `PLOTTER_STATE`)_ | Where saved projects are stored, one file per project |
| `GATEWAY_ALLOWED_ORIGINS` | _(none)_ | Extra browser origins allowed to open the WebSocket, comma-separated. Same-origin always passes; add `http://localhost:5173` when driving a live daemon from the Vite dev server |

## Registration (cut to a printed sticker)
Expand Down
13 changes: 9 additions & 4 deletions gateway/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,18 @@ const SESSION_FILE =
// App settings (machine setup, preferences) live here — one plotter, one setup,
// so every client that attaches adopts these. Separate from the session file on
// purpose: the artwork is replaced constantly, the machine setup almost never.
//
// Defaults are derived from the *state* file's directory, not from the bundle's,
// because the packaged app lives in a read-only /opt and its config file is a
// dpkg conffile: an upgraded Pi whose operator had edited that file would keep
// the old copy, never learn the new variable, and silently fail to write here.
// PLOTTER_STATE has been set since the first packaged release, so its directory
// is the one place known to be writable. (`UPDATE_STATUS` already does this.)
const APP_SETTINGS_FILE =
process.env.PLOTTER_APP_SETTINGS ??
join(fileURLToPath(new URL('.', import.meta.url)), '.app-settings.json');
process.env.PLOTTER_APP_SETTINGS ?? join(dirname(STATE_FILE), '.app-settings.json');
// Saved projects live on the Pi so it holds a library of plots any client can
// open — one file per project, in a directory of their own.
const PROJECTS_DIR =
process.env.PLOTTER_PROJECTS ?? join(fileURLToPath(new URL('.', import.meta.url)), 'projects');
const PROJECTS_DIR = process.env.PLOTTER_PROJECTS ?? join(dirname(STATE_FILE), 'projects');

// ---- self-update config ----
// Where the update oneshot records its progress; the daemon reads it back after
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "penplotter271",
"private": true,
"version": "1.2.1",
"version": "1.3.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down