Repository navigation
1.18: Vector versions as one group, a viewer with the drawing, Delete version, a depth stack, and pictures by paste - #40
Merged
Conversation
…N section 35) A stacked Vector run can now say which colour is the back sheet: stack_order is area_desc (the most ink at the back, today's rule and the default), area_asc (the least ink) or palette (the first ink of the form's list, then the rest of the form's inks, then the inks the picture chose). Colours are assigned before the reordering, so the order never changes a layer's colour. The choice only counts for a run stacked by colour; side by side and by shape ignore it and the check says stack_order null. The check gains height for each layer (its number when stacked, the back sheet being 1, and 1 when side by side) and stack_order at its top. The option goes through the request, the run's stored options, limits.vector (stack_orders and its default), the job's options file (a 1.17 file without it means the default) and the contract file. Tests: the disc/square/dot picture the spec measured, six nested colours making six levels, the reverse and palette orders, colours unchanged by the order, the choice ignored side by side and by shape, an unknown value refused by name, and a 1.17 options file still loading. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_011NDCKtu1QcLwawbdzwmFA1
…s (DESIGN sections 35.2 to 35.4)
Schema 8 adds vectors.deleted_at (the last column, so an upgraded database equals a new one; a copy
of the database is made first). A version is now binned, restored, deleted for good, counted and
purged by the same code as a picture or a track: _ITEM_TABLES gains "vector", and the three routes
POST /api/vectors/{id}/bin, POST .../restore and DELETE /api/vectors/{id} are registered with the
picture routes. Binning answers moved: "version" or "run" (the last version sends the run).
vectors_for_runs now leaves out versions in the bin and binned_vectors_for_runs lists them; the run
payload gains binned_vectors (empty for a run that is itself in the bin); the Deleted view's filter
(RunFilter) and the counts include a run with a version in the bin. A version's four files (SVG,
ZIP, PNG preview and the model's drawing) are removed together.
Two routes for the viewer: GET /api/vectors/{id}/drawing (the stored <idx>-source.png, nosniff,
immutable, a download name ending -drawing.png, a 404 that says the drawing is no longer on the
disk) and GET /api/vectors/{id}/layers/{n}.svg (one layer's SVG out of the ZIP, with the same
script-forbidding headers as the combined SVG). The version payload gains drawing_url,
drawing_download_url and layer_urls.
A version that could not be made is named by its seed in the run's note, not by its place (a place
changes when another version is deleted).
Tests: tests/test_vector_version_bin.py (32), the picture-bin database tests now also run for
versions, the shared filter table gains a Vector run with a version in the bin, a schema 7 to 8
migration test, and the schema-7-specific tests follow the current schema. The contract file gains
binned_vectors, the binned version example and the new payload keys; the fake clean-up job packs one
SVG per layer and mirrors a stacked check.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_011NDCKtu1QcLwawbdzwmFA1
… view, Back sheet The Vector card is now one card for the run, as an Images card is: a grid of the versions' previews at the left (each a button that opens the viewer), the run's buttons in one row at the right. A run with exactly one version keeps its facts and its three downloads on the card; a run with several has Download..., which opens the viewer at the first version. The viewer (VectorViewer) covers the page as the Images viewer does, shows the model's drawing beside the vector (with Download drawing, and a plain line when the file is gone), the colours line, the layer table, the smallest feature, the notes, the downloads, one PNG size box for the whole viewer, and Delete version. A version made Stacked (depth) also gets the Height column and the stack view: the layers' own SVGs as sheets, back to front, with a Sheets shown control. The form says Stacked (depth), has a Back sheet choice (greyed with the reason unless the run is stacked by colour) and a Bleed hint that says what it does. The Deleted view shows a run's deleted versions as one card with Restore and Delete forever, and opens them read-only in the viewer. Deleting a run says how many versions go with it. Copy SVG code moved out of the page component into useCopyVectorSvg so that it is tested on its own (one request at a time, the file only on the clipboard, and the answer goes to wherever asked last: a toast, or the viewer's note). Vitest: 747 -> 789 tests (vector.test.tsx rewritten for the new card; new vectorviewer.test.tsx for the viewer, stack view, Height column and the card of deleted versions). Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_011NDCKtu1QcLwawbdzwmFA1
…with Undo, Upscale by drop or paste
One page-level paste listener (usePicturePaste) replaces the prompt box's own, so
a paste acts once. Where a paste goes is a pure function (paste.ts routePaste):
a paste with no picture is never touched; nothing is taken while a dialog, the
Options drawer or a viewer is open; on the Music and Vector tabs a picture is not
taken and a note says "Pictures are used on the Images tab."; on the Images tab a
picture wins in the prompt box, and in any other text field it is taken only when
the clipboard has no plain text; with Upscale a picture... focused it goes there.
In Generate a pasted or dropped picture is no longer refused: the pictures are
added in order, the card becomes Edit with the prompt kept, and a toast counts
them ("Refer to them as image 1, 2, 3.") and offers Undo, which removes exactly
those pictures and goes back to Generate. When none is accepted the card stays in
Generate and each rejection is named; without editing, "Editing isn't available."
and nothing is added. The tray reports what it refused (a file that is not a
picture, a file over the size limit, how many did not fit) and can be asked to
stay quiet, which Generate does because its tray is not on screen.
Upscale a picture... takes a chosen, dropped (it lights up) or pasted picture
(only while it has the focus; a click focuses it), uses the first of several and
says so, and says "Still upscaling the last picture." while busy. Generate's help
line and Upscale's tooltip gain the sentences the spec gives.
No server change. Backend tests added for a limit of 8 (eight in order, a ninth
refused naming the limit, reuse with all eight, the shipped default still 4).
Vitest 789 -> 837; new e2e/paste.spec.ts (12 scenarios), edit.spec.ts and
fourk.spec.ts updated for the new behaviour.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_011NDCKtu1QcLwawbdzwmFA1
… checklist e2e/vector.spec.ts: the 40 scenarios now drive the compact card (a grid of preview buttons, Download... for several versions, the one-version card's own downloads) and the viewer (the drawing beside the vector, the facts, one PNG size box, the drawing and per-layer downloads with their headers, Escape and focus, paging, the phone layout). Delete on a card says how many versions go. Version 1.18 in backend/studio/__init__.py, frontend/package.json and its lock file, and the test fixtures; two schema tests that were still named for schema 7 now say 8. docs/SPARK_TEST.md section 29: the viewer and the drawing, Delete version, the three Back sheet orders in the laser software, a 1.17 run opened in 1.18, paste on the user's own browsers and phone, Upscale by drop and paste, and the measurement of 6 and 8 pictures that sets the shipped limit. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_011NDCKtu1QcLwawbdzwmFA1
…tests from the first mutation round e2e/vectorversions.spec.ts (11 scenarios): Delete version in the viewer (the question, the viewer moving on, the others numbered again, Undo restoring the version at its place), the last version sending the run, the Deleted view's card of versions with its read-only viewer, Restore and Delete forever (and the files gone), a run in the bin showing its own versions, another open page following live, a server refusal, the Back sheet (greyed, sent, remembered, Reuse), the Height column and the stack view's slider, and a 320 px phone. Fixed, found by those scenarios: the page's reducer counted only pictures and tracks in the bin when deciding that the counts had changed, so restoring a version of a Vector run left the number beside Deleted where it was. Tests added for survivors of the first mutation round: a drop on the Music or Vector tab while a dialog is open or before the page is ready; a stacked version of one layer; the alt text, help line and range ends of the viewer's PNG box; arrow keys with one and two versions; a file at exactly the size limit; the tray's announcements; a back sheet when one ink is listed; a version whose files are already gone; the words of the viewer routes' 404s. README: the built 1.18 row. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_011NDCKtu1QcLwawbdzwmFA1
…in the browser Survivors of the server, page and browser-level rounds that were real gaps now have tests: a run stored by 1.17 with no back sheet is traced as the default; a version whose preview file is already gone still has its drawing removed; the viewer routes' 404 words; the card's empty preview (shimmer, alert and stop icons, hidden from a screen reader), the corners of a preview, and "+N" only past four; a run with one thing in the bin; an unknown back sheet order listed by its own name; the deleted card's accessible name; placeLabel. Copy SVG code in the viewer is now a browser scenario (the answer is said inside the viewer, not as a toast behind it). Removed stackOrderLabel, which nothing used. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_011NDCKtu1QcLwawbdzwmFA1
…s-about-versions tests DESIGN.md: the status row and the tests line for 1.18, and a new section 36.9: what was built for sections 35 and 36, the tests (backend 1804, front end 858, 281 in a browser), the 530 mutants of the 1.18 code in three rounds and what they found (a real bug in the Deleted count, found by the browser scenarios; weak tests, found by mutation), what was not checked (a real drawing, the laser software with a stacked file, real clipboards and phones, 8 pictures on the GPU), the small choices made while building, and the known limits. Tests: the words about deleting versions (the toast and tooltip for a run with several, Empty bin's question and toast, the question about one version and its toasts) were only covered in the browser; they now have unit tests, and a run with eight source pictures shows all eight on its card. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_011NDCKtu1QcLwawbdzwmFA1
NoSQLKnowHow
pushed a commit
that referenced
this pull request
Nov 8, 2026
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_011NDCKtu1QcLwawbdzwmFA1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
Version 1.18, built to
docs/DESIGN.md§35 and §36 (specified in #39). Two changes that share onlyApp.tsx:Vector (§35)
stack_order:area_desc, the default and what 1.17 did;area_asc;palette). The order is applied after colours are assigned, so it can never change a layer's colour.Pictures by paste (§36)
STUDIO_MAX_INPUT_IMAGES; the default stays 4 until the Spark has measured memory).Back up first
The first start of 1.18 upgrades the database to schema 8 (
vectors.deleted_at, added as the last column so an upgraded database equals a new one). A copy is made first (data/studio.sqlite.before-schema-8); every run keeps its data.docs/SPARK_TEST.md§29 is the checklist and starts with this.How it was built
The 1.15 code that bins, restores, deletes and purges one picture or track is now one piece of code for
image,trackandvector. New routes:POST /api/vectors/{id}/bin,POST …/restore,DELETE /api/vectors/{id},GET …/drawing,GET …/layers/{n}.svg. No server change for §36. What was built, and where it differs from the spec, is inDESIGN.md§36.9.Evidence (all local; the repo has no CI)
Run on the final tree:
area_desc), each now killed by a test. Details in §36.9.Array.forEach's three arguments to a function that takes one.music.spec.tsAdd lyrics shows Voice and the lyrics box…. It fails the same way on the untouched 1.17 tree (3 runs out of 3) on the machine I used, whose Chromium is not the build the pinned Playwright expects. It is not about 1.18. One Vector scenario failed once under CPU load and passed on rerun.Not checked
SPARK_TEST.md§29 (d)).ClipboardEventby hand in Chromium; a phone's keyboard may not offer a picture to paste at all.Small choices made while building
See §36.9 for all five; the one most worth a look: in Generate, "a picture was accepted" means it passed the page's own checks. A file that passes those but that the server cannot decode (a corrupt PNG) switches the card to Edit and is then refused on upload, as a note in the tray. §36.2 said Generate should stay put for an unreadable picture; knowing that would mean decoding every file in the page first.
Known limits
The Back sheet is chosen before the run (changing it afterwards needs the clean-up run again, §35.7). The stack view is a picture of offset sheets with a shadow, not a 3-D model. A drop on the bare Images page still does nothing. A deleted version's drawing, ZIP and SVG stay on the disk until its days are over.
Notes
Version bumped to 1.18 (
__init__.py,package.json, lock file, test data).Generated by Claude Code