Quadrante: the whole app becomes one instrument for time - #299
Closed
JanWelker wants to merge 2 commits into
Closed
Conversation
The schedule was a vertical list of rows: a spreadsheet's idea of time, for a subject that is a cycle. It is now a dial. A 24-hour face with noon at the top, the plan drawn on it as a coil — one revolution per day, so a 32 h bake is a turn and a bit and a 72 h bake is three coils, and the length of a plan is something you see before you read a number. The bake sits on the outer rim and the dough unwinds inward from it, which is what "everything schedules backwards from readyBy" looks like when you draw it. The 22:00–08:00 night is a fixed wedge every coil passes through, because a moment's angle is its time of day and nothing else. The dial is a control, not a picture: the coil is a listbox of steps with roving tabindex, and its tail is a slider that turns like a knob, one revolution to the day, snapping to exactly the stops the rail in the form offers. Selecting an arc moves the readout beside the dial, which reads that step out in full from the same stepCopy functions the plan list uses. Everything else orbits it. The form is no longer a column standing in front of the answer but a settings rail, sticky and scrollable in its own right; the step list is the plan below the dial; the weights sit beside it; the three collections stay collapsed at the foot. The geometry is a pure module (src/lib/dial.ts) over ScheduleStep[] — angles, coil radii, sampled spiral paths, the night wedge, hit areas — unit-tested to 100 % including midnight wrap-around, a bake under one revolution, a 72 h three-coil plan, a phase too brief to draw and two pre-ferments sharing the same minutes. Nothing in src/lib/dough/ changed and no field was added to ComputedSchedule. Also folds two palettes into one: --kt-* theme roles (ink/face/rule and a new warm/cold pair, where hue means temperature) replace the hand-written text-stone-* dark:text-stone-* pairs throughout, and the quiet ink tier that had been sitting at 3.6:1 on 46 nodes is now AA in both themes. Fixed along the way, each with a test: a 25 h leg printed "01:00 PM – 02:45 PM" with no day, and a sticky rail taller than the viewport took its own lower half out of reach.
|
…trics The window card's bake-limit caption was one unwrapped line positioned at a percentage, so its width was whatever the font made it: the same string renders about 6 % wider on the CI runner than on the machine it was designed against, which was enough to hang it off the end of the rail. It now wraps inside a max-width derived from how much of the rail is available on its side, so it is inside by construction whatever the metrics do — with the pivot moved out to a quarter of the rail, since a centred caption only gets twice the shorter side to spread into. Both markers share one .rail-caption class. The tick labels had the same fragility from the other end: the rail is linear in stop index, so 48 h and 72 h sat on adjacent stops about one label-width apart, which needed a phone-only label set to paper over and still came within two pixels of colliding. The set is a doubling ladder now — 8 / 18 / 36 / 72, never two adjacent stops — the same at every width, and the special case is gone. 24 h and 48 h are still snap points; they just carry a notch, not text. The two tick tests become one rule asked at both widths, and the marker test now reads every line of both captions rather than only the ones that carried whitespace-nowrap — the class that let them overflow in the first place.
Owner
Author
|
Closing: design variant not chosen. #300 (Servizio × Bottega) merged as v7.0.0. Quadrante made the sharpest argument of the seven — that a vertical list is a spreadsheet's idea of time, and dough is a cycle — and the coil genuinely reads: 72 h is three revolutions before you read a number. It lost on risk, not on merit. It also found a real content bug the other six shipped: a 25 h fridge leg printing Kept for reference rather than deleted. |
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.
The whole app becomes one instrument for time: a 24-hour dial with the bake plan drawn on it as a coil, one revolution per day.
Every version of this app has drawn time as a vertical list of rows. That is a spreadsheet's idea of time, and it is a large part of why the app reads as engineered. Dough does not happen in rows — it happens over a day and a half, mostly overnight, mostly while nothing is being done. That shape is a cycle.
So the centre of the page is now a dial. Noon at the top, hours clockwise, the schedule drawn as a coil that makes one revolution per day: a 32 h bake is a turn and a bit, a 72 h bake is three coils, and you see how long the dough takes before you read a single number. The bake sits on the outer rim and the dough unwinds inward from it, which is what "everything schedules backwards from
readyBy" actually looks like when you draw it. The 22:00–08:00 night is a fixed wedge that every coil passes through — possible because a moment's angle is its time of day and nothing else.What a user sees differently, region by region
The dial (new). Warm phases (counter) are amber, cold phases (fridge) are blue — hue means temperature on this face and nothing else. Baker-action steps are engraved radial ticks across the coil; waiting phases are the coil itself. Two pre-ferments maturing in parallel get a lane each, drawn dashed, and merge at prep. A clock hand shows now, with a bead where it crosses the dough. The centre plate carries the bake time in plain words, because a dial nobody can read a time off is a picture of a schedule.
It is a control, not a picture. The coil is a
role="listbox"with roving tabindex — one tab stop, arrows walk the plan — and clicking or focusing an arc moves the readout. The coil's tail is arole="slider"that turns like a knob: one revolution of the face is one day of fermentation window, snapping to exactly the stops the rail in the form offers, with arrow keys and Home/End. Dragging past the bake deadline is refused out loud, in the same words the rail uses. Everything draggable keeps a keyboard path and a plain numeric field behind it.The step detail moved beside the dial. A readout panel gives the selected step in full — date, start and end time (naming the day when a leg crosses midnight), duration, what goes on the scale, the method copy, the detail paragraph. It and the plan list render from the same
stepCopy.tsfunctions, and share one selection, so they cannot disagree.The form became a settings rail. Sticky on the left at
lg+, with its own scroll region; belowlgit sits under the answer rather than in front of it. Same fields, same order, same beginner/expert rule.The plan list stays, as the secondary representation and the accessible equivalent of the drawing — same day headers, same rail, same per-step ingredients and detail copy. Its step titles are now buttons that move the dial.
The window card got tougher, not different. Its bake-limit caption was one unwrapped line positioned at a percentage, so its width was whatever the font made it — about 6 % wider on the CI runner than on my machine, which is enough to hang it off the rail. It wraps inside a max-width derived from the space on its side now, so it is inside by construction. And the tick labels became a doubling ladder — 8 / 18 / 36 / 72 — with no two adjacent stops labelled, which retires the phone-only label set that existed only because 48 h and 72 h are neighbours. Both are still snap points; they just carry a notch rather than text.
Everything else is quieter.
.cardlost its shadow, blur and 2xl radius: the instrument is the only thing on this page allowed to look like an object, and eight drop-shadowed boxes around it is what made the old layout read as a stack of widgets.What moved where
card(page, 'Schedule'), unchanged markupxl), under it otherwise. Round-numbers button unchangedwarningSlots.ts: window → slider card, temperature → under the temperature fields, ingredients → under the weightsDoughInputs)lg+, below the instrument on a phone. Nothing removed, nothing re-orderedWhenfieldset. The dial's tail is a second control on the same value.ics, print, save, TRMNL)/print/[[locale]], TRMNL payloadTokens and component classes introduced
--kt-*theme roles, aliased into@themeas--color-*so every Tailwind utility resolves through them andhtml.darkswaps all thirteen at once:ink,ink-soft,ink-faint,face,face-deep,rule,rule-soft,warm,warm-soft,cold,cold-soft,night. This replaces thetext-stone-* dark:text-stone-*pairs across every component — two palettes with a hand-written dark half each.frostcolour scale (50–900). The app had no cold colour at all, so "in the fridge" was told in the same warm palette as "on the counter"..instrument,.readout,.step-row/.step-row-on,.field-label,.legend-dot..cardrestyled to a hairline surface.src/lib/dial.ts:DIAL_VIEWBOX,R_OUTER/R_INNER,STRAND_GAP,MIN_ARC_LENGTH,MIN_HIT_DEGREES,SAMPLE_DEGREES,STEP_TEMPERATURE, plusbuildDial,stepKey,nightWedgePath,pointerAngle,unwrapDelta.The math is untouched
Nothing in
src/lib/dough/*.tschanged behaviour, and no field was added toComputedSchedule— the dial readsScheduleStep[]the same way the table does. Every share-URL reproduces the same recipe, gram for gram and minute for minute; thev=1 … v=6decode tests and the whole yeast-solve/mass-balance matrix are unchanged and green.Version
npm version minor→ 6.11.0, deliberately not a major. The addendum's test is whether the contract moved: no URL key changed,CURRENT_VERSIONstays 6, and a returning bookmark-holder gets exactly their recipe back. The app is the same tool arranged differently, so the schema version has nothing to say about it. This collides with the four sibling redesign PRs, which all bump to the same number — expected, and trivial to resolve.Tests
The browser suite reaches its rules through the layout, so a real restructure moves it. Nothing was weakened or deleted:
e2e/dial.spec.ts(11 tests) — the drawing and the list agreeing on one selection; the coil being walkable from the keyboard; the tail and the rail landing on the same stop; a refused drag saying so; a leg that ends on another day naming the day; the readout opening on the step that is running; the face fitting 390 px with no sideways scroll; the phone order (plan before weights, settings past both); the desktop rail beside the instrument; the rail being reachable to its bottom.src/lib/dial.test.ts(21 tests) — the geometry, including every edge case the direction called out.e2e/layout.spec.ts: its two placement tests were about the form/schedule/ingredients grid the dial replaced. They are not gone — a comment where they used to be names the two tests indial.spec.tsthat ask the same questions of the new structure.e2e/headings.spec.ts: the h2 list gains the dial's stage; the outline rules are unchanged.e2e/focus-dismissal.spec.ts: the fit-score locator follows the panel to its new home.e2e/window-slider.spec.ts: the marker test now reads every line of both captions rather than only the ones carryingwhitespace-nowrap— the class that let them overflow. Its two tick tests became one rule asked at both widths (same count).Two browser-only bugs found and fixed with their tests, per the house rule: a 25 h fridge leg printed
01:00 PM – 02:45 PMwith no day, and a sticky rail taller than the viewport pinned its own lower half out of reach.Checks run
npm run lintnpm run checknpm run test:coveragenpm run test:baselineE2E_PORT=4186 npm run test:e2eplaywright.config.tstakes the port fromE2E_PORT(the same one-line change all five redesign branches make, so it merges trivially).Looked at with my own eyes at 390 px and 1440 px, in both themes, for a short room-mode bake, a cold two-day bake and a 72 h biga + poolish.
Left undone, deliberately