Preset editor as a flow: forks, triggers, appearance - #34
Merged
Merged
Conversation
Settings gains an Appearance picker (Follow the Mac / Light / Dark), applied through preferredColorScheme on the window; the default keeps following the system. Every sheet (Convert options, Preset editor, Confirmation, Redaction review, Add folder) uses the thin material so the window shows through, on macOS 13.3 and later. AppSettings decodes with a tolerant initialiser: a settings file saved before the appearance key existed no longer fails to decode and reset everything. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
Name, description and category sit in a static card: they lived in a grouped Form inside a fixed frame, which is a list, and a list scrolls under the pointer. "Asks for" and "Comes out as" are no longer sections above the steps; the format, each question and the naming template are blocks in the same list as the operations, added from the same menu and removed with the same button. The Add-a-step menu is centred under the last block. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
The editor is no longer a sheet. It covers the window: name and description across the top, the chain of blocks stacked down the middle, and a searchable library of blocks on the right. The first block, the files that come in, is fixed and cannot be removed; everything else is added from the library with a click, dragged into order, and removed with its own button. Formats are kept apart from the steps so the steps reorder by index. The Presets toolbar steps aside while the editor is up. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
A List spent the first click selecting the row, so the block was never added. The library is a ScrollView of plain buttons with a plus on each row. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
…rop library The input block offers every kind Forge reads - images, videos, audio, documents, data files, 3D models, subtitles, fonts - and a custom preset picks the exact formats it answers to from a popover of everything the processors open. The choice is saved as inputFormats and honoured by the Convert sheet and the folder watcher. A line with an arrow runs from each block to the next, so the canvas reads as the flow it is. The library has a real search field, one colour per group, tiles with a + each, and every tile drags onto the canvas; a step dropped between two blocks lands there. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
A question can be asked once per batch or read from the file's own name (holiday_10MB.jpg, holiday_1920px.jpg, holiday_q80.jpg), with a default for files that say nothing. Name tokens now carry width and quality as well as size and privacy, and the Convert sheet only asks the questions that are asked. Group colours are muted, icons are smaller, and the formats popover stays put: its anchor no longer changes width with the count. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
A List only took drops on rows it already had, wanted a click to select a row before its fields took typing, and drew its own drag previews. Every block is now a drop spot: a library block lands there, a step dragged from elsewhere moves there, and the end zone takes the rest. A library tile is a button, so clicking it adds the block; onDrag on the same view had been swallowing the click. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
…fonts Those kinds sat on the Documents shelf until they had shelves of their own; a preset or a watched folder saved back then must keep taking the files it always took. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
…cked Save is on whenever the preset does something and, when it is off, the reason sits beside it. Changing the kind removes the steps and formats that no longer apply, on the canvas where it can be seen. A question starts with the key the docs use (maxsize, width, quality); an empty or duplicated key gets a red edge and holds Save. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
…, one editor per request Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
A chain can now fork. An If decides per file on its longest side, width, height, size or extension and runs one of two chains. A Split ends the main line and opens an arm per copy, each its own chain with its own steps and, if it wants, its own format; the outputs are named after the arms. A Join brings the arms back to the main line, where what follows runs on every copy. A Merge turns the copies into one PDF, the copies never touching the destination. The coordinator resolves every fork for the file in hand before anything runs, so the processors keep seeing flat chains. The editor draws the fork as independent arms side by side, each with its own end, and the If as a block with two paths that rejoin. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
…le and gate on a test Every fork - if or split - is now a node on the main line with an independent arm per path, side by side, each its own chain. An if's two arms are its two answers. The input block says when the preset runs: dropped on Forge, landed in a watched folder, or a file renamed with the preset's word (foto_web.jpg), which beats the folder's own preset and is dropped from the output's name. An 'only if' test gates the whole preset; a file that fails it is left alone and history says so. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
…er the fork A condition can now test the file's name (is, is not, contains, starts with, ends with), its folder, its kind, or pass everything, besides size, sides and extension; each subject offers only the comparisons it can make. The arms of a fork sit centred under the node. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
Every remove button has a 24pt hit area and a spoken label. Fields that looked like labels - the preset name and description, the names of a split's copies - have a surface that says they take typing, plainer under the pointer and ringed when focused. The next-node slot under a fork is a menu of what can go there, not only a drop target. Blocks arrive, leave and move with motion. A fork whose arms hold steps asks before it goes. Counts keep their width, prose wraps instead of truncating, library tiles lift under the pointer, ⌘F reaches the search, ⌘S is named on Save. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
Nothing under the arms unless the user put a join or a merge after the fork: no bar back to one line, no shared slot. A main line that ends in a fork has no end zone of its own; the shared output blocks sit before the fork. A step dropped after any fork gets a join in front of it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
The arms rendered their steps as plain blocks, so a nested If lost its two paths. One renderer now serves every depth: fork with arms, join node, or block; an arm that ends in a fork has no end zone of its own; a step dropped after a nested fork gets its join. Arms flex to the width they are given, so a fork inside one has room. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
…sideways An arm is 420pt wide with plain steps and as wide as its own fork with a nested one; the arms hug their content under the node instead of sharing whatever width the parent had, so a fork inside an arm no longer spills over its neighbour. The canvas scrolls sideways when the tree is wider than the window and stays centred when it is not. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
The editor is seven files instead of one: the view, its actions and their tree, the block library, the controls every block shares (card, chip, remove button, icon, condition editor, format chips, editable surface, step list), the action row, the question block, the formats popover; OutputFormat is a model. Four hand-rolled lock boxes are OSAllocatedUnfairLock. Dead code goes: toOperations(), isFormat, isLogic, isText, mainLineOpen, isThrottled, updateAvailable, loadPreset. A format step no longer poses as a resize: it has no library kind, so changing the preset's kind leaves it alone - it used to be pruned from an audio preset's arms. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK
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 changes
thinMaterial, macOS 13.3+).AppSettingsdecodes tolerantly, so a new key no longer wipes saved settings.RulePreset.inputFormats, honoured by the Convert sheet and the folder watcher. Old Documents presets keep taking data files, models, subtitles and fonts._10MB,_1920px,_q80).If(name, folder, kind, extension, size, sides; two arms),Split into copies(N independent arms, outputs named after the arm, per-arm formats),Join(arms carry on together),Merge(copies become one PDF). Forks are resolved per file in the coordinator before any processor runs; nested forks work.foto_web.jpg); an Only if condition gates the whole preset.OSAllocatedUnfairLock.Test plan
swift test— 376 tests, 0 failures (new: settings, input formats, name tokens, logic resolution, split/merge/join end-to-end, triggers, conditions, library)Scripts/build_app.shand a real launch after every step; screens capturedx_web.jpgin a watched folder with a preset whose word isweb🤖 Generated with Claude Code
https://claude.ai/code/session_01CjRgz3rt1BHJxPDYMawAAK