plans/README.md: index, execution order 000->005, executor ground rules (test gate, DSL landmines) 000: shared craft crate (easing/duration/color tokens, Pressable, CraftToast, motion math + units) 001: HIGH press contract - feedback on down, commit on up-if-over, 0.96 scale (all 3 crates) 002: MEDIUM toast enter/exit, interruptible, reduced-motion aware (all 3 crates) 003: MEDIUM insurance sheet momentum projection + velocity-handoff settle, rubber-band 004: MEDIUM koboyo drawer slide, origin-aware popovers, animated camera fit 005: LOW polish - color tokens, concentric radii, real icons, tabular figures
81 lines
4.7 KiB
Markdown
81 lines
4.7 KiB
Markdown
# 000 — Create the shared `craft` crate
|
||
|
||
- **Status**: TODO
|
||
- **Commit**: d50006e
|
||
- **Severity**: infra (prerequisite for 001–005)
|
||
- **Category**: Cohesion & tokens
|
||
- **Estimated scope**: 1 new crate (~5 files), 1 patch edit, 3 Cargo.toml edits
|
||
|
||
## Problem
|
||
|
||
Three crates re-implement chrome independently with zero shared motion language: no easing
|
||
tokens, no shared pressable, three copy-pasted toast blocks, brand hex repeated per widget
|
||
(`insurance/src/app.rs:31,108,122,170` — `#7b5cf6`, `#8a74ff`, `#f7a982`…). Every subsequent
|
||
plan needs one home for the house values, or the fix itself becomes three drifting copies.
|
||
|
||
## Target
|
||
|
||
New workspace member `makepad-ports/craft/` (crate name `makepad-example-craft`, copied to
|
||
`examples/craft` by `setup_makepad.sh`, same layout as the other three), exporting:
|
||
|
||
1. **`script_mod` tokens** stored on the `mod` object (the only cross-file sharing mechanism):
|
||
- `mod.craft.ease_out_strong` = `Ease.Bezier {cp0: 0.23, cp1: 1.0, cp2: 0.32, cp3: 1.0}`
|
||
- `mod.craft.ease_in_out_strong` = `Ease.Bezier {cp0: 0.77, cp1: 0.0, cp2: 0.175, cp3: 1.0}`
|
||
- `mod.craft.ease_sheet` = `Ease.Bezier {cp0: 0.32, cp1: 0.72, cp2: 0.0, cp3: 1.0}`
|
||
- `mod.craft.ease_polish` = `Ease.Bezier {cp0: 0.2, cp1: 0.0, cp2: 0.0, cp3: 1.0}`
|
||
- durations: `dur_press_release: 0.15`, `dur_hover: 0.1`, `dur_popover: 0.2`,
|
||
`dur_drawer: 0.25`, `dur_toast_in: 0.3`, `dur_toast_out: 0.2`
|
||
2. **`Pressable`** widget (Rust: `#[derive(Script, ScriptHook, Widget, Animator)]`) — full spec
|
||
in plan 001; defined here, adopted there.
|
||
3. **`CraftToast`** widget — full spec in plan 002.
|
||
4. **`reduce_motion`**: `pub fn play_or_cut(reduce: bool, cx, w: &mut impl AnimatorImpl, st)`
|
||
helper (RECIPES.md §6) + a `#[rust] reduce_motion: bool` convention documented in the crate
|
||
docs. Default `false`.
|
||
5. `src/model.rs`-style pure module `motion.rs` with unit-testable math:
|
||
- `pub fn project(v_px_per_s: f64, rate: f64) -> f64` → `(v/1000.0)*rate/(1.0-rate)`
|
||
- `pub fn rubberband(over: f64, dim: f64) -> f64` → `over*dim*0.55/(dim+0.55*over.abs())`
|
||
- `pub fn settle(from: f64, target: f64, v0: f64, lambda: f64, t: f64) -> (f64 /*pos*/, bool /*done*/)`
|
||
— exponential settle: `x = (d + (v0 + λd)t)·e^(−λt)`, done when `|x| < 0.5 && |v0·e^(−λt)| < 10.0`
|
||
|
||
## Repo conventions to follow
|
||
|
||
- Crate skeleton: copy `makepad-ports/insurance/` structure (`src/{lib,main,app,model}.rs`,
|
||
`tests/`), Cargo.toml identical shape (`makepad-widgets = { path = "../../widgets", version = "2.0.0" }`).
|
||
- Workspace registration: extend `patches/makepad-dev-headless-linux.patch` where it already
|
||
inserts members (`+ "examples/rider",` etc.) with `+ "examples/craft",`; keep the patch
|
||
applying cleanly against dev @ `b41e740` (`git apply --check`).
|
||
- Consumers add `makepad-example-craft = { path = "../craft" }` and call
|
||
`makepad_example_craft::script_mod(vm)` before their own `script_mod` in `App::run`.
|
||
- Widget registration exemplar: the `let ChatList = #(ChatList::register_widget(vm)) {…}`
|
||
pattern noted in `makepad-ports/README.md` bug 3.
|
||
|
||
## Steps
|
||
|
||
1. Create `craft/Cargo.toml`, `src/lib.rs` (exports `script_mod`, `motion`, widgets),
|
||
`src/motion.rs` with the three functions + `#[cfg(test)]` unit tests (below).
|
||
2. Create `src/widgets.rs`: `Pressable` + `CraftToast` Rust structs and their
|
||
`script_mod!` block registering `mod.craft.*` tokens and both widgets.
|
||
3. Edit the patch: add `"examples/craft"` to `workspace.members`; update `setup_makepad.sh`
|
||
copy step to include `craft`.
|
||
4. Add the dep + `script_mod` call to `rider`, `koboyo`, `insurance` (no behavior change yet).
|
||
5. Unit tests in `motion.rs`:
|
||
- `project(1000.0, 0.998)` ≈ `499.0` (±0.5); `project(0.0, _) == 0.0`; sign follows `v`.
|
||
- `rubberband` is monotonic, `< over`, and `rubberband(0.0, d) == 0.0`.
|
||
- `settle` reaches `done` with `pos` within `0.5` of `target` for
|
||
`(from=200, target=0, v0=−800, λ=12)` before `t = 1.0`; position is continuous at `t=0`
|
||
(`pos(0) == from`).
|
||
|
||
## Boundaries
|
||
|
||
- Do NOT move any existing UI into the crate yet (that's 001/002).
|
||
- Do NOT touch `reference/`, `uploads/`, or the three apps' `model.rs`.
|
||
- Do NOT add third-party dependencies.
|
||
- If the patch no longer applies to dev @ `b41e740`, STOP and report — do not rebase upstream.
|
||
|
||
## Verification
|
||
|
||
- **Mechanical**: `cargo test -p makepad-example-craft --lib` green (new tests);
|
||
`cargo test -p makepad-example-{rider,koboyo,insurance} --lib --test integration` still
|
||
21/9, 29/14, 28/14; `git apply --check` passes on a fresh checkout.
|
||
- **Done when**: all four crates build, tokens are reachable from a consumer `script_mod!`
|
||
block as `mod.craft.ease_out_strong`, and the 140-test baseline is intact.
|