Repository navigation
feat(ui)!: add a visual template editor - #128
Merged
Merged
Conversation
The Editor mode keeps the templates a person is building as drafts, each a template's source folder under the config folder, and renders a test card through one as it is on disk. A draft is never installed, so changing one cannot change an installed template. New template starts from the engine's starter at 72 to 1200 dpi, and Edit a copy, beside each installed version in the Templates library, starts from that template. A copy keeps the original's license and credits and adds a credit that names the original and its version. The test card comes from nine presets that differ in colors, types and text length, or from a Scryfall search, and is kept with the draft. A draft that does not load lists its problems. Pipeline.RenderThrough renders with a template the caller names, and OpenSource opens an installed version for copying. The editor features not built yet are gated on #115. The engine is pinned to v0.19.2, which has the starter, the copy and the direct open of a draft. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Clicking the test card in the editor selects what is drawn at that point, and clicking the same place again reaches what lies beneath it. The layers list selects too and shows what the card does not draw. Dragging moves the selection with snapping to the card and to the other items, the handles of a text box, art window or symbol resize it, the arrow keys nudge it, and Raise and Lower change a layer's place in the stack. Undo and redo take changes back, and a run of arrow keys is one change. Each change is an operation that names an element and gives its numbers in the finished image's pixels. The app turns them into the layout's own, including its rotation, changes the numbers in the layout file with its key order and layout kept, and returns the operation that undoes it. It refuses a change that would leave a template that loads unable to load, and a layout that extends another is shown and not changed. Studio.Describe lists what the layout draws for the test card and where, and Studio.Apply makes the changes and saves them. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The editor outlined the rectangle a text box has in its layout file, which for a box set on its baseline starts at the first baseline, so the outline sat below the text it belonged to. A described item now says when its place is where its text is drawn, and the preview outlines that place. Such a box can still be moved, but it has no resize handles, since its rectangle only sets where the baseline starts. The engine is pinned to v0.19.3, which reports that place. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Under the selection in the editor, the settings of a layer, text box, art window or symbol are fields that read and write the layout file: place and size, what a box draws and when, its font role, size, color, alignment, spacing and shadow, a layer's position, opacity, blend and slot, and an art window's layer and turn. Layout holds the canvas, and Template details the name, description, author, license and credits, and a copy keeps its original's license and credits out of reach of the fields. A value is checked as it is typed, a change is made when typing stops, and an empty optional field takes its setting out. Edit JSON opens the template's files under the preview. Each mistake in the JSON is shown with its line and column and each mistake in what it means with its path. Text that is JSON and leaves the template loading is saved as typed, text that does not is held back, and a template that does not load can be fixed there. Changes are set, unset and replace operations that go through the same check as the others: the draft is loaded as it would be with the change, and the engine's own rules refuse a bad value with their reason. Studio.Source, CheckFile and Vocabulary feed the editor, and Apply reports whether the draft loads and what is found in it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A box that snapped to the middle of a canvas with an odd width was moved by half a pixel, and the fractional number could not be read by the edit that saves the move, so the drop failed with a binding error. The snapped move is rounded to whole pixels. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The editor's frontend modules, their tests and the service's main file were named for the studio, which is not what the feature is called. The files are renamed and the one comment that used the word now says editor. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…up with A click on the card selects the topmost item under the pointer, the one the preview outlines on hover, and no longer keeps the selected item or cycles through the items beneath it. A press on the stage around the card lets go of the selection. While a box is dragged, the guide it snaps to says which of the box's edges is held and which edges lie on that line, and the items on the line are outlined. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…and delete its layers A layer shows a cell for each material its part reads, with the image that draws it, the colors a pair is blended from, the any image that covers it or nothing, and an image is replaced, added or removed from the cell. A PNG is checked as it comes in, and one that is larger than the canvas, a different size than the part's others or has no transparent pixels is taken with a warning. Fonts are added from a file with their hash, names and license, and each text role chooses a font of the template's or one of the engine's. A template that gets its first font or role names the engine release that reads them. A text box, an art window or a layer for a part, new or existing, is added from a dialog, and a layer for a new part and its first image are one step of undo. Any element can be copied or deleted, and undo restores the layout file as it was. The files an undo needs are held in memory, and one that was let go is said to be gone. An item can be locked, so the card does not pick, hover, move or resize it, and an item can be marked as one a drag snaps to. A drag snaps to the card's own lines and to marked items only, and the guide names the edge held and outlines what lies on the line. Both marks are kept with the draft and are not part of the template. Studio.Assets, Thumbs, ImportImage, ImportFont and SetMarks are new, and the edit operations gain put, drop, add, delete and duplicate. Apply also removes files, and the draft forgets an image that changed so the next render reads it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Each row of the layers list has a grip to drag a layer by, an icon for what the item is, a padlock that locks it on the card, a grid that makes it a line for a drag to snap to, and a menu with Duplicate and Delete. A layer is also moved with Alt and the arrow keys, and with Move up and Move down in its menu. The Selection panel no longer has Raise, Lower, the two checkboxes or the Duplicate and Delete buttons that these replace. The list is no longer a listbox, since its rows now hold buttons, and the selected row is marked with aria-current. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The canvas size, resolution and turn belong to the whole template and not to the selection, so they are in a Canvas section of the left column under the template picker and not among the right column's sections. It starts collapsed, opens with the same arrow the other sections have, and keeps whether it is open with the rest of the layout. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…in proportion The six presets are real printings, Mirran Spy, Consuming Aberration, Eerie Ultimatum, Norin the Wary, Sensei's Divining Top and Flooded Strand, named by set and collector number and looked up when one is chosen. The test card holds its properties as a disclosure and sits in a section that, with the search, collapses and takes the height its reader gives it. The canvas keeps its width and height in proportion until the padlock is opened. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The card's view has camera controls pinned to its corner, a Select tool and a Pan tool with zoom out and in, the zoom as a percent and a button that fits the card back in the middle. The wheel zooms about the pointer, plus and minus step the zoom, and 0, 1 and 2 are fit, 100% and 200%. Pan, or Space held over the view, or the middle button, moves the card about the view by any distance without selecting or moving what is on it, and the outlines, handles and guides go with the card. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Use in mimic builds the draft and keeps it among the installed templates as user.<name> at the version draft, so Single, List and Run can draw with it. Using it again replaces the bundle and reopens the template in use, and deleting a draft takes its template back out. Export shows the license and credits, then writes a .mimic through the save dialog, and Show folder opens the draft's folder. A card can be saved as a test card with the changes made to its fields, up to 24, and is offered after the built-in presets with no lookup. The coverage strip draws every test card small and without art through the draft and says which could not be drawn and why. Release-As: 2.0.0 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Use in mimic and Export are no longer gated, so the test that expected them to be locked now checks that none of the finishing buttons carry a gate. The layout test that clicks the card's corner to pick the background now clicks the top corner. The Coverage row under the card's view takes a little height, which left the bottom corner under the activity strip. The export dialog's problem list drops role="alert" from the list itself. It overrode the list's semantics and failed the accessibility scan of the editor-export scene. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…eps around a layer About moves from the right column to the left, under Template and above Canvas, and starts collapsed. Search Scryfall moves above Test card and stays collapsed. The right column keeps Layers, Selection and Problems. The right column's saved layout is read as the defaults once, and the left column's is kept under a new key so the old order is not read as the new one. Canvas has a single Size group with the width and height in px, the resolution in dpi and the rotation, whose choices are written in degrees. Each unit is inside its field, at the right. The ratio bracket takes the padlock's color while the ratio is locked. Selection shows the name and type on one line, puts a note about a locked or undrawn item beneath it, and lays X and Y out in one column with Width and Height in the other. An image's Remove button sits in the image's top right corner. Every text button is now as tall as the primary one, and a dropdown is as tall as a text field. Several tooltips and button labels use plainer wording, Legendary Creature is now Legendary, and Insert Symbol is the name of the symbol palette's button in every form that has it. A text box has a Flow group for the layer it steps around, the box it stays clear of and the gap it keeps. When a box that steps around a layer is selected, the part of its box that layer covers is hatched on the card, and the note beneath the name says whether the text steps around the layer, has the whole box because the card does not draw the layer, or is not reached by it. An emptied flow is taken out of the layout file along with its last setting. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…view, show the room text has, and copy or save the card's picture A text box set on its baseline has the line its text sits on drawn across its width, with an end to drag at each side. Dragging an end stretches the box along the line through a new stretch operation, which moves one side of a text box in the finished image whatever the layout's turn and undoes as a resize. Such a box moves and snaps by its baseline, and the selection reads as the line, with X, Width and Baseline. Each row of the layers list has an eye that leaves the item out of the preview, or for a layer the card does not draw, draws it whatever its condition says, which describes the layout again so the layer has a place on the card. It changes only the preview, and a coverage strip ignores it. The render and describe requests carry the names, which the engine reads. A selected text box shows the room it has in the editor's three colors: purple for the room its text takes, a blue ghost for the room it has left, ending where the card ends it, and red for what is in its way. The layer it steps around, the box it stays clear of and the layer it moves for are outlined in red, and where the room it has as the layout gives it meets one, the space it cannot use, is striped. The same is shown the other way round when such a layer or box is selected. The marks stay on the card while a box is dragged and follow it, and the names of them are set in a caption beneath the outline, where no text is. A box on its baseline that the card moved or narrowed also has the line it ends up on drawn over its own, and a box with an area has the box it was left tinted. Under the selection's name are notes for what the card did to a text box, which override of it is in force and how its text fitted. The view's controls gain an eye that shows and hides these overlays, kept with the layout settings and on the O key, and Copy image and Save image, which put the picture of the card that is shown on the clipboard as a PNG or write it to a file. The engine is pinned at 0.19.4 for the fields these read. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The Vertical choice of a text box offers cap-top and cap-center beside baseline. A box set on either has its line drawn and dragged like a baseline's, and the engine's new Anchor field on a described item names it, so the line's caption and the selection's third number read cap top or cap center and a box without an anchor still reads baseline. An edit that sets a cap anchor names engine 0.19.5, the first release that reads one, in the template's minEngine, raising an older one, and undoing the edit puts back what was there. A new template starts with its title and mana boxes on cap-center, so the first edit of its layout names the release too. The engine is pinned at 0.19.5. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…es changed outside the app A PNG dropped on the card or on the layers list adds it to the template. A dialog names the file, offers a name for the part made from it, and asks what the image follows, either nothing, so it draws on every card, or one of the frame's color slots. It then makes the part with the image as its first and a layer for it on top of the stack, as one step of undo. Several files dropped are asked about one after another, and closing the dialog drops the rest. A file that is not a PNG is said and starts nothing, and a drop on another mode or on a template that cannot be changed does nothing. The card and the layers list are outlined while a file is dragged over them. A render of the test card can be asked to draw at the resolution the layout is authored at and not the preview's. A button in the view's controls does so and asks where to put the picture, leaving the preview and the card shown as they are. The copy image button and the code behind it are removed, since Save image writes the same picture to a file. A draft has a fingerprint of its template files, made from each file's name, size and time, that does not count the app's own notes in .studio.json. Opening a draft and applying an edit return it, and Studio.Stamp gives it on its own. When the window comes back to the front, the editor compares the fingerprint with the one it last wrote or read, and reads the template again if they differ, which draws the card again and starts its undo history over. Text in the JSON editor that was not applied is asked about first, and a no leaves it and the files as they are. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… one menu and drop the file drop Import template, beside New template in the Templates library, installs a .mimic file chosen from anywhere on the computer. The app checks that it can read the file, that the template's id looks like owner.name and its version like 1.2.3, and that its manifest loads, then copies it into the cache the downloaded templates are read from, where it is listed with that version as installed and not made the active template. Importing a file that is installed already says so, and a different copy of an installed version asks before it takes that one's place, reopening the template in use when that is the one replaced. Save image is one button. When the preview is drawn smaller than the template is authored, it opens a menu of As shown, which saves the preview at once, and At full size, which draws the card again at the layout's own resolution and then asks where to put it. Otherwise it saves at once. The separate full size button is removed. The dialog for images dropped on the card and the layers list is removed, along with the bridge call that took a path it had been given. A change to a draft's files made outside the app is still read again when the window comes back. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Each version in the Templates library that the app keeps, whether it was downloaded, imported or made available from the editor, has a remove button. It asks first, and says whether the version can come back: one the catalog lists can be downloaded again, a draft is still in the editor, and an imported version can return only from its file. Removing deletes the bundle from the cache and the template's folder with its last one. A version in use is switched away from first, to the newest other version of the same template, or else to the template the app starts with, or to none when that is not installed either. A developer folder is not the app's to delete and has no button, and nothing can be removed while a run is in progress. A version in the cache that the catalog does not list, such as one imported for a template the catalog carries, is now listed beside the catalog's versions, so it can be selected and removed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…s documentation The real card fonts that local-fonts held are embedded in the engine, and the m15 source and loose build that assets held are kept in mimic-templates, which carries the current one. Neither folder is needed to build, test or run the project now that a template ships its own fonts and the editor makes and opens templates, so the documentation stops describing them. The directory tables in the README and CLAUDE.md lose the local-fonts row, the fonts section of the README names only the -fonts flag as the way to point the app at another folder, the ui README no longer says a checkout's local-fonts folder can be the active one, and the release notes for the throughput benchmark say to install mimic.m15 from the Templates tab and not as a loose folder under assets. The ignore rules for both folders go, along with the assets path in the ui job's change filter, and local-fonts/README.md is deleted, which was the only tracked file in either. The code that would still read a local-fonts or assets folder in a checkout is unchanged. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A template's id is owner.name and started as user.<name>, which could be changed only by writing the id in the JSON. The template's details now have an Owner field, the first half of the id, that rewrites the id with the name after it kept. It takes lowercase letters, digits and hyphens, and a button beside it fills it in from the author's name. Templates shared as .mimic files can then carry ids of their own and not all collide as user.<name> when one is imported. Changing the owner takes back the copy that Use in mimic made under the old id, unless a run is in progress, and the choices kept for a template, such as its features and the faces assigned to it, belong to its id and start over under the new one. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… app's controls The visual baselines are drawn by the runner's own browser and fonts, and were last made before the editor existed. The editor's fourteen scenes now have a baseline in each theme, which covers its empty and populated states, the selection and the levers, the JSON pane with a problem, the images grid, the fonts, the coverage strip, the baseline box, and the new template, add and export dialogs. The other scenes change where the page does: text buttons and fields share one height across the app, and the templates library has its import and per-version edit and remove controls in aligned columns. Made with the Update visual baselines workflow on the head of the template editor branch. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… is being typed, and say where the launch check's nudge got to The Linux launch check timed out in the editor flow, waiting for an arrow key to move the title. The editor ignores keys while focus is in a text control, and some webviews leave the field of a mode that was left, such as Single's search box, as the active element after it is hidden. A field that is not drawn is now not taken for one a person is typing in, so the editor's shortcuts work in that state. When that step of the launch check times out it now says what holds the focus, how many dialogs are open, which mode shows, whether undo is on, where the title's rectangle is in the file and what the toasts say, so a failure in a window with no display to look at can be told apart. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This was referenced Oct 15, 2026
Closed
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.
Summary
The ui gains an Editor mode for building a template visually. A person starts from a plain
card or from a copy of an installed template, drags and edits layers, text boxes, art windows
and symbols on a rendered test card, brings in their own PNG layers and fonts, and finishes
with a template the app can use or a .mimic file to share. Every edit is written to the
template's own files, so a template made here is the same kind that a template author keeps.
The Templates library also gains Import template and a remove button for each version the
app keeps, which round out managing templates from inside the app.
What is in it
The editor lists a template's layers, selects and moves them on the card with snapping and
undo, and edits each item's fields with levers and, for anything else, in a JSON pane that
reports problems by line. Images and fonts are brought in through the studio service, which
checks them. A text box set on a single line can be anchored on its baseline, the top of its
capitals or their middle, which keeps a shrinking title centered with the mana cost. The card's
view zooms and pans, layers can be hidden or forced on in the preview, and a text box shows the
room it has left and what is in its way. Test cards are real cards from presets or a search,
and can be saved, and a coverage strip draws the template for each. A template's id is
owner.name, and its owner can be set from its details.
Use in mimic makes a template available in Single, List and Run, Export writes a .mimic file
after showing its license and credits, and Save image writes the card's picture as shown or
drawn again at the template's own resolution. The editor reads a template again when its files
change outside the app.
In the library, Import template installs a .mimic file beside the downloaded ones, asks before it
replaces a different copy of an installed version, and reopens the template in use if that was the
one replaced. Remove deletes a version from the cache, says whether it can come back, and switches
away from a version in use. A cached version that the catalog does not list is now shown with the
catalog's versions.
Compatibility
A template that uses the cap-top or cap-center anchors needs engine 0.19.5, and an edit that sets
one names that release in the template's minEngine. The ui pins engine v0.19.5.
The folders local-fonts and assets are no longer described in the documentation. The code that would
read them in a checkout is unchanged.
Testing
Go tests run with the race detector, Node tests cover the editor's pure modules, and page tests
drive the real frontend against a controlled bridge in Chromium and WebKit. Tests against the
test server cover drags, saving, export, import and remove, and the launch check opens the editor,
edits a template and undoes it. The accessibility scenes include the editor's panels and dialogs.
Not checked here: the page in a real desktop window. That covers the native file and confirm
dialogs, the Save image menu and the icons in the library rows. The visual baselines are
regenerated with the Update visual baselines workflow.
Release-As: 2.0.0
🤖 Generated with Claude Code