Skip to content

Commit 84ed7e3

Browse files
committed
v2.22.0 — the printer board: machines, filament units and widgets you arrange yourself
The Printers view becomes a hand-arranged board like Storage, with per-machine filament units stored on the device document, a temperature widget, permanent card clusters and rubber-band selection on both boards. Also fixes a long-standing data-loss bug reported by a user: an in-place chip update stamped a fresh timestamp, which the next read misread as a chip reprogrammed elsewhere and answered by deleting the spool's document. The creation date — also the twin-pairing key — is now unwritable by construction.
1 parent 17bfbd2 commit 84ed7e3

13 files changed

Lines changed: 552 additions & 71 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,58 @@ Versions follow [Semantic Versioning](https://semver.org/).
55

66
---
77

8+
## v2.22.0 — 2026-08-28
9+
10+
### Added
11+
12+
- **The printer view is a board.** Machines are placed by hand on a permanent grid with the storage plan's magnetism and guides — the geometry, panning and zoom were made board-agnostic rather than duplicated, so one implementation serves both views. Wheel to zoom, middle-drag to pan, buttons and a typed value from 30 % up, with the point under the cursor held fixed. The board carries its own zoom (`tigertag.printer.planZoom`); a machine's position lives on its document as `plan: {x, y, z}`. A machine never placed flows below the arrangement; the one moved last sits in front — `renderer/inventory.js`, `renderer/css/40-printers.css`.
13+
- **A connected machine's filament storage is shown as UNITS**, one named block per physical object (Ext., AMS 1, AMS 2…), rendered from the record at startup — dimmed and desaturated until the brand's link comes up — and split onto the board individually via **Split units** in the widget's ⋮. Grouped and split arrangements are stored apart (the group's position on the machine, each unit's on the unit) so switching never discards the other — `renderer/inventory.js`, `renderer/printers/*/index.js`.
14+
- **Filament storage is stored on the machine**, as a `units` map on its own device document — one entry per physical unit (AMS, CFS, ACE box, holder pair, external arm), each carrying its shape, a user-renamable label, a position on *each* board, and slots holding what the user assigned (`uids`) and what the machine last reported, stamped with `seenAt`. A map rather than an array so one unit is written without rewriting its neighbours; on the document rather than in a subcollection because units are few and always read with the machine. The seeder never overwrites label, shape, positions or assignments, and a unit that stops being reported is marked `present: false` rather than deleted. Every brand now describes its units, not just a flat slot list. Vocabulary and shape: `docs/printer-storage-terms.md` — `renderer/inventory.js`, `renderer/printers/*/index.js`, `docs/firestore-schema.md`.
15+
- **Feed slots as a movable board object**, showing the same coloured squares as the machine's own panel at exactly a rack slot's size and scale in picture view (64 × 64, 6 px apart), in balanced rows of at most five. Four states: unknown reads `?`; assigned but not loaded shows its colour as a ring with a hollow `?`; a colour with no declared material reads `—`; a loaded bay is filled with its material family. Hover names the bay, brand and variant. Each brand normalises AMS trays, CFS materials, ACE slots and tool-changer nozzles behind one `getSlots` on the registry — `renderer/inventory.js`, `renderer/printers/*/index.js`.
16+
- **A temperature widget on the printer board**, with its own position (`tempPlan`). It renders each brand's existing temperature card via `getTempHtml` rather than a seventh copy of it, read-only; it stays on the board when the machine is silent, dimmed with an em dash per reading. Only the numbers are swapped on a live tick, never the frame — `renderer/inventory.js`, `renderer/printers/*/index.js`, `renderer/css/40-printers.css`.
17+
- **Cards can be bound together on the board, permanently.** Select (rubber band or shift-click), right-click, group: touching any member then carries the whole set at its existing offsets. A dashed outline rings the set and follows it. The binding lives on the members beside the coordinates it binds — `planCluster` on the machine's document, `unitsPlanCluster` for the units widget, `units.{id}.planPrintersCluster` for a single unit — rather than in a collection of its own: no new rules block, and deleting a machine takes its membership with it. Called a *cluster* in code because "group" already means three other things here. The board also gained its first right-click menu, built from the kebab menu's own component — `renderer/inventory.js`, `docs/firestore-schema.md`.
18+
- **Widgets are switched per machine from its ⋮**, as toggles. `widgets.{kind}` on the device — **absent means shown**, so only a deliberate choice is written. A widget is bound to its machine from the start, takes the first free place going down its own machine's column when switched on (another printer's card is not an obstacle), and forgets its position when switched off so it returns beside its machine rather than wherever it last stood — `renderer/inventory.js`, `renderer/css/30-racks.css`.
19+
- **Rubber-band selection and group moves on both boards** — sweep empty board to select machines (Printers) or racks (Storage, Arrange only), shift to extend, then drag any selected card to move the set at its existing offsets. One shared implementation (`wirePlanMarquee`) — `renderer/inventory.js`.
20+
- **A move handle on every card**, left of the ⋮. Outside Arrange it is the only thing that moves a machine or a rack; in Arrange the whole card stays a handle. New `icon_move` glyph — `renderer/inventory.js`, `assets/svg/icons/icon_move.svg`.
21+
- Groundwork for assigning an inventory spool to a machine slot: `renderer/printers/filament.js` resolves a TigerTag spool into the values any of the six brands accept — Bambu's catalogue id and tray type, Creality's id and pressure, the family/fill split, the vendor token, temperatures, and each brand's colour format. Never refuses: an unknown brand or material becomes a Generic stand-in at the right colour and temperature, with a flag for whether the identity is exact. Internal, no user-facing change yet.
22+
23+
### Changed
24+
25+
- **Adding a board widget is one entry in `BOARD_WIDGETS`.** It was thirteen edits in eleven places, each naming the kind explicitly. The prefix resolver, position, stacking order, cluster binding and its pre-pass, board-id list, switch-off cleanup, render and patch all read the table; widgets share one frame (`_widgetFrame`) so a new one supplies only its body. Storage stays out on purpose — its units are a map with their own keys. Documented in `docs/printer-board-widgets.md` — `renderer/inventory.js`.
26+
- The printer board dropped its "Connected" / "Offline" partition: a machine losing its link no longer changes place. With it went the `sortIndex` drag-and-drop that rewrote order behind the arrangement, the section headers, the full grid rebuild on every state blink (a connect now touches one dot), the sort grip, and the trailing "+ add a printer" card. `sortIndex` survives as the table view's order — `renderer/inventory.js`.
27+
- Arrange and zoom float over the board's top-right corner instead of a strip above it, anchored to the view so panning does not carry them off — `renderer/inventory.js`, `renderer/css/40-printers.css`.
28+
- Everything on the printer board shares one derived width — exactly what a four-slot unit measures, padding and border included — so a machine card and the unit beside it line up, and changing a slot's size carries it along — `renderer/css/40-printers.css`.
29+
- The group/ungroup menu opens on a right-click anywhere, including inside a cluster's ring (drawn without pointer events, hit-tested by geometry) and over bare board, where it acts on the current selection — `renderer/inventory.js`.
30+
- A printer card is a switch: clicking the machine whose panel is open closes it — `renderer/inventory.js`.
31+
- Panning both boards is the wheel button only; the left button on empty board draws a selection — `renderer/inventory.js`.
32+
- Several racks can be selected at once in Arrange mode — `renderer/inventory.js`, `renderer/css/30-racks.css`.
33+
- In a group move each carried card answers for its own landing spot, so the red overlap edge names the card actually sitting on something. Still a warning; a drop is never refused — `renderer/inventory.js`.
34+
35+
### Fixed
36+
37+
- **An in-place chip update destroyed the spool's own record on the next read** — the weight fell back to nominal capacity and the masterspool, rack and TD were lost, while the inventory count stayed put. A chip's timestamp is stamped once, at creation: it is the creation date and, as `twin_tag_pairing_id`, the key pairing a twin with its other half. `rfid:encode-cloud` stamped `_nowChipTs()` on every write, and the timestamp is also what the renderer compares to decide a chip was reprogrammed elsewhere — so the document kept the old value, the next read saw a mismatch and ran `docRef.delete()` before recreating from the chip alone, which carries none of those fields. The creation date is now unwritable by construction: `_keepChipTimestamp` overlays the chip's own timestamp (payload offset 32, u32 BE, one page) onto the payload before the surgical diff, on both write paths, so that page is identical and can never be among the pages sent. Reported on v2.21.1; the defect dates to v1.8.5 — `main.js`.
38+
- **A spool's TD vanished whenever its record was rebuilt from its chip** — a delete-and-rescan, or any chip rewrite — although the chip carried it throughout. The chip stores `td_raw` (TD × 10) and the record kept that faithfully, but every screen reads `TD`, which only the TD1S ever wrote. `TD` is now derived from `td_raw` on any scan that finds none in the record; a record already holding one keeps it, so a TD measured but not yet burned is never overwritten by the chip's older value — `renderer/inventory.js`.
39+
- **Switching to the printer board could silently do nothing.** Cards and widgets are assembled into one string before `host.innerHTML` is assigned, so anything throwing in one widget meant the assignment never happened and the view kept whatever it had. Every builder is wrapped in `_safe`, and a deferred rebuild gets a two-second watchdog — a deferral that can last for ever is a frozen screen — `renderer/inventory.js`.
40+
- Switching view could stop working entirely: any stray menu parked on `body` held the board's rebuild deferral for ever. Only a menu belonging to a machine on this board holds it back now, and changing view closes menus first — `renderer/inventory.js`.
41+
- **Group moves on the printer board, audited end to end.** Cards flashed back to their old position before landing, from five causes, all about order: overlapping gestures unlocked the board early (holds are counted now); a held object answers for its own position and the record is not consulted; cards were released on button-up rather than when the writes landed; the guard protecting a just-written position was cleared by our own optimistic patch, so it evaporated before the snapshot arrived; and a rebuild deferred during the drag was flushed before the new positions were written. A release outside the window was heard by nothing and froze the board, since anything held suppresses the rebuild. A group was clamped card by card at the board edge, flattening the spacing; the set is shifted as one. The cluster outline was destroyed and recreated on every layout pass, blinking twice per move, and is patched in place — `renderer/inventory.js`.
42+
- A card dragged on the printer board could teleport mid-move: every brand rebuilds the view on any live state change, detaching the node under the pointer, so the hand carried a ghost and the release wrote its coordinates. Nothing rebuilds or re-places the board while something is held; the rebuild is deferred, not dropped — `renderer/inventory.js`.
43+
- Toggling a widget closed the machine's ⋮. Four causes, the last being the real one: writing the setting comes back as a Firestore snapshot that re-renders the view. An open menu now defers the rebuild the way a held card does, and the debt is paid when it closes. The others: the action runner closing for every item; the full re-render taking down the menu itself; and the dismiss-on-scroll guard, since re-laying the board fires `scroll` — judged by position now, not by timing, because the events a resize provokes are delivered on a later frame — `renderer/inventory.js`.
44+
- A group's outline stayed behind other machines' cards when moved over them. The board spaces its cards two apart in the stacking order, leaving one slot under each for a ring — `renderer/inventory.js`, `renderer/css/40-printers.css`.
45+
- Rebuilding the printer view threw the board back to its top-left corner. The scroll is restored after the layout that gives the board its size — `renderer/inventory.js`.
46+
- The temperature widget did not follow its cluster: the list of a machine's board objects knew about its card and its storage but not about it. That list is also where a widget switched off drops out — `renderer/inventory.js`.
47+
- Grouping cards nudged them out of place: grouping writes no position but triggers a layout, and a card never given coordinates is re-placed by every pass. Members are pinned to where they are drawn before the binding is written — `renderer/inventory.js`.
48+
- Arrange mode no longer opens a machine's side panel; the ⋮ still answers — `renderer/inventory.js`.
49+
- Right-clicking a printer card opened its side panel behind the menu, and armed a card grab that cleared the very selection the menu was about to act on. Only the left button moves a card — `renderer/inventory.js`.
50+
- Clicking the ⋮ on a printer card also opened the side panel behind it, armed through the rebuild-safety fallback — `renderer/inventory.js`.
51+
- A rubber-band sweep leaving the board froze its box and left it behind, so the next attempt drew a second one. Leaving abandons the sweep and restores the selection, with the window catching a release outside it — `renderer/inventory.js`.
52+
- A card dragged past the board edge is dropped where it last stood, on both boards: pointer capture meant nothing told it the cursor had left — `renderer/inventory.js`.
53+
- Grabbing a card on either board rings it in the accent colour; the three states are one ordered block per board (held < selected < colliding) so a collision is never outranked by a lift. The closed-hand cursor appears on press rather than after the first pixels of travel — `renderer/inventory.js`, `renderer/css/30-racks.css`, `renderer/css/40-printers.css`.
54+
- Overlap warning on the Storage board is a red outline again, and only that — it used to recolour an outline Arrange alone declared, and the red wash replaced the drag shadow — `renderer/css/30-racks.css`.
55+
- Placing the board no longer sweeps up the marks drawn on it (cluster outline, rubber band) and gives them a spot among the cards; saving a position without an explicit stacking order no longer throws — `renderer/inventory.js`.
56+
- Anycubic showed an unreported temperature setpoint as a dash in both the panel and the board widget, while the pill's own editor assumed zero when clicked — `renderer/printers/anycubic/cards.js`.
57+
58+
---
59+
860
## v2.21.1 — 2026-08-24
961

1062
### Fixed

‎FEATURES.md‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Tiger Studio Manager — Feature Catalogue
22

3-
Tiger Studio Manager is the Electron desktop companion to the TigerTag ecosystem: a filament-inventory manager built around RFID/NFC-tagged spools ("TigerTag" chips), a fully-digital "TigerData" tier (and its catalogue-identified "TigerData+" rung), live 3D-printer integration across six brands, physical storage/rack management, a Firebase-backed social layer (friends, shareable wishlists, public profiles), and companion hardware (TD1S color sensor, TigerScale, TigerPOD dual-reader stand). This document catalogues every **shipped** feature, grouped by domain, current as of **v2.21.1**. Per-version release detail lives in `CHANGELOG.md`; forward-looking / in-progress work lives in `ROADMAP.md`.
3+
Tiger Studio Manager is the Electron desktop companion to the TigerTag ecosystem: a filament-inventory manager built around RFID/NFC-tagged spools ("TigerTag" chips), a fully-digital "TigerData" tier (and its catalogue-identified "TigerData+" rung), live 3D-printer integration across six brands, physical storage/rack management, a Firebase-backed social layer (friends, shareable wishlists, public profiles), and companion hardware (TD1S color sensor, TigerScale, TigerPOD dual-reader stand). This document catalogues every **shipped** feature, grouped by domain, current as of **v2.22.0**. Per-version release detail lives in `CHANGELOG.md`; forward-looking / in-progress work lives in `ROADMAP.md`.
44

55
---
66

@@ -188,6 +188,13 @@ Live integrations across six brands, each with real-time temperatures, per-slot
188188
- Printer tags, with the same autocomplete UX as spool tags, plus a search bar and Brand/State/Tags filters in the Printers view (v2.0.0).
189189
- Printer error alerts surfacing the machine's own message and code (Anycubic first) (v1.10.4).
190190
- Snapmaker Paxx-firmware helper — always-current download link, up-to-date indicator, per-printer update notification (v1.10.29).
191+
- **Printer board** — the Printers view is a hand-arranged plan like Storage: machines placed on a permanent grid with magnetism and guides, wheel zoom from 30 %, middle-drag panning, position saved per account. Replaces the Connected/Offline partition, so a machine never changes place when its link drops (v2.22.0).
192+
- **Filament storage as units on the board** — one named block per physical unit (Ext., AMS 1, AMS 2, CFS, ACE, holder pair), shown from the record at startup and dimmed until the link comes up, kept grouped beside the machine or split into individually placeable objects. Slots are drawn at a rack slot's exact size and scale, with four states (unknown, assigned-not-loaded, colour-only, loaded) and hover naming bay, brand and variant (v2.22.0).
193+
- **Units stored on the machine** — a `units` map on the device document, one entry per physical unit carrying its shape, user-renamable label, a position on each board, and its slots (user assignments plus the machine's last report, stamped with `seenAt`). A unit that stops being reported is marked absent rather than deleted (v2.22.0).
194+
- **Temperature widget on the board** — the machine's own temperature card, placed like any other object, staying visible and dimmed when the printer is silent (v2.22.0).
195+
- **Permanent card clusters** — select several cards, right-click and group them so moving one carries the whole set at its existing offsets; the binding lives on the members and is released where they stand (v2.22.0).
196+
- **Per-machine widget switches** in the card's ⋮, each widget appearing in the first free place down its own machine's column (v2.22.0).
197+
- **Rubber-band selection and group moves on both boards**, plus a move handle on every card that is the only grab point outside Arrange mode (v2.22.0).
191198

192199
## Printer cameras
193200

‎README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ The **printers table** shows, per printer: a live **print preview** (the model o
145145

146146
Some brands also expose a **live control panel** (Snapmaker, Elegoo, Anycubic): home / jog the axes, set nozzle & bed targets, toggle the light, control the part-cooling fan, pick the print-speed mode, and load / unload filament per slot.
147147

148+
The **printer board** lays your fleet out the way your workshop actually looks: place every machine by hand on a plan you zoom and pan, and it stays where you put it — a printer going offline no longer changes place. Beside each machine you can show its **filament units** (Ext., AMS 1, AMS 2, CFS, ACE…) as separate boxes with their slots, colours and materials at the same size as your storage racks, and a **temperature widget** that stays put when the printer sleeps. Cards can be **grouped for good**, so moving one carries the whole set.
149+
148150
### 📦 Storage / Racks
149151

150152
<p align="center"><img src="assets/img/Panda_Feed_Rack.png" width="480" alt="A filament rack in Tiger Studio Manager" /></p>

0 commit comments

Comments
 (0)