Skip to content

Controls UI

Timeline: Feb 6, 2026 | Status: done

actual buttons in the toolbar — starting with scale.

Remaining Work


Done

  • add a sep between hamburger and other_buttons
  • move edit into other_buttons
  • three control rows for smart phones (narrower than 420)
    • first ham, other
    • second face_buttons
    • third mode_buttons
  • add to Stepper a horizontal layout option
  • convert + and - into a horizontal Stepper
  • add a slider for scaling, optional logarithmic effect (default is linear)
  • make the stepper vertical
  • move the stepper to the right of the slider
  • port the Slider.svelte from ws
  • move the stepper from controls into the slider
  • in Stepper, add numerical text between the buttons, value is the scale. one decimal place
  • add a button that toggles between 2D and 3D, associated with a store
  • add a button called "straighten" that sets the root SO's orientation to identity
  • button for precision during drag (avoid 3' 8 47/64")
  • straighten -> apply to forward face of root
  • switch 3D -> 2D
    • wrong face
    • wrong twist
    • child projections wrong
    • missing some dimensionals
  • change see through -> x-ray
  • add vertical separator

Height computation

Non-wrapped (window > 670px): The height is driven by the vertical separators with length={32} — the tallest items in the horizontal flex. Buttons are only 16px. Changing .right-col padding has zero effect because .right-col isn't rendered.

Wrapped (window < 670px): Height = .right-col padding + row heights + gaps + separator. Padding changes would matter here.

Scaling

the object's visual size had no UI. you could drag edges and corners to change bounds, but the overall scale? stuck at 1.0 forever. needed a way to zoom in and out on the geometry itself — logarithmic so it feels even whether you're at 0.1 or 10.0.

What was built

Scroll wheel on the canvas scales the object. each tick multiplies by a constant factor — that's the logarithmic part. hold Shift for fine control.

ModeUp factorDown factor
Coarse×1.1×0.9
Fine×1.02×0.98

Toolbar buttons and + in the controls bar, same coarse factors as the wheel. simple, always visible.

Persistence — scale now serializes with the SO and restores on reload. old saved scenes without scale default to 1.0 (backwards compatible).

How it works

O_Scene.scale was already there (uniform, applied in the world matrix via mat4.fromRotationTranslationScale). the work was wiring it up:

  • Events_3D got a wheel listener and scale_object() method — takes a direction (+1/−1) and fine flag, computes the factor, multiplies obj.scale
  • Setup.ts wires both the wheel handler and exports scale_up/scale_down for the toolbar
  • Smart_Object.serialize() now includes scale (pulled from this.scene.scale); deserialize() returns { so, scale } so the caller can apply it to the O_Scene
  • Controls.svelte imports the toolbar actions and renders the buttons with a shared .toolbar-btn style

Files changed

FileWhat
src/lib/ts/signals/Events_3D.tswheel listener, set_wheel_handler, scale_object
src/lib/ts/render/Setup.tsrestore scale on load, wire wheel, export scale_up/scale_down
src/lib/ts/runtime/Smart_Object.tsserialize/deserialize now include scale
src/lib/ts/managers/Scenes.tsSaved_Scene interface updated
src/lib/svelte/main/Controls.svelte/+ buttons, unified button style

Key decisions

  • Logarithmic by multiplication, not addition — each step is ×factor, so scaling feels proportional at any size. no need for a separate log/exp wrapper.
  • Scale lives on O_Scene, not SO — SO owns geometry (bounds, orientation). scale is a rendering/scene concern. SO serialize grabs it from this.scene for convenience.
  • No min/max clamp — let the user scale as far as they want. if it gets weird, hit reset.

Horizontal Steppers

the existing Stepper component was vertical-only (up/down arrows). needed it to go sideways for the toolbar.

added a horizontal prop. when true, arrows become left/right and layout switches from column to row. the container goes from position: absolute to position: relative so it can sit inline in the toolbar's flex row. used $derived for the SVG paths so Svelte tracks the prop reactively.

the +/ text buttons got replaced with a <Steppers horizontal> in Controls.svelte. same hit_closure callback — pointsUp=true → scale_up, false → scale_down.

BuildNotes.svelte still uses vertical Steppers unchanged (horizontal defaults to false).

Slider

needed absolute positioning of the scale — not just "bigger/smaller" steps. a slider gives direct control: click anywhere on the track to jump to that value, drag the thumb for fine adjustment.

How it works

Slider.svelte — a self-contained drag component. takes min, max, value, logarithmic, width, and an onchange callback.

  • Linear mode (default): value = min + t * (max - min) where t is thumb position 0..1
  • Logarithmic mode: value = min * (max/min)^t — uniform thumb travel maps to exponential scale change. middle of the slider = geometric mean of min and max.
  • Track is a thin grey bar, thumb is a circle that goes black on hover/drag
  • Drag uses window-level mousemove/mouseup so the thumb tracks even outside the slider bounds

Setup.ts exports set_scale(value) and get_scale() for absolute scale control. Controls.svelte keeps a reactive current_scale state that syncs with the engine.

Files changed

FileWhat
src/lib/svelte/mouse/Steppers.sveltehorizontal prop, flex layout, $derived paths
src/lib/svelte/mouse/Slider.sveltenew — track + thumb, linear/log mapping, drag
src/lib/svelte/main/Controls.svelteSteppers + Slider replace raw buttons
src/lib/ts/render/Setup.tsset_scale, get_scale exports

Compound Slider (ws port)

the custom div-based slider got replaced with a port of ws's Slider.svelte. and the standalone Steppers got folded inside it — one compound control: slider track + vertical up/down arrows on the right.

What changed

Slider rewrite — native <input type="range"> instead of DIY div+drag. ported from ws, adapted to Svelte 5 ($props, $state, $derived, $effect). stripped ws-specific deps (Global_Imports, Identifiable, elements, g) and wired di's hit system (S_Hit_Target, hits).

key features carried over from ws:

  • divisions (default 100) for granularity — slider snaps to discrete positions
  • logarithmic mode via Math.log10 / Math.pow(10, ...) — even distribution across magnitudes
  • cross-browser CSS for track and thumb (webkit, moz, ms)
  • thumb color changes on hover/drag via hit system

Steppers inside Slider — the up/down arrows now live inside Slider.svelte as an optional show_steppers prop (default true). Controls.svelte passes onstep callback, Slider renders vertical arrows to the right of the track. each arrow is its own S_Hit_Target with hover highlighting.

Controls simplified — no longer imports Steppers.svelte separately. just <Slider ... onstep={handle_scale} />. BuildNotes still uses standalone Steppers (unchanged).

Files changed

FileWhat
src/lib/svelte/mouse/Slider.svelterewritten — ws port, native range input, integrated steppers
src/lib/svelte/main/Controls.svelteremoved Steppers import, single <Slider> with onstep

Key decisions

  • Native input over DIY<input type="range"> gives free keyboard support, accessibility, and browser-native drag. the custom div slider was reinventing the wheel.
  • Compound control — slider + steppers as one unit means they share layout context. no more positioning headaches in the toolbar.
  • Steppers stay standaloneSteppers.svelte still exists as its own component for BuildNotes. the Slider just inlines its own copy of the arrow markup to avoid coupling.

Scale value display

added a numerical readout between the stepper arrows — shows current scale to one decimal place. bold 8px text, centered. the arrows and text are tightly stacked in a flex column with negative margins pulling them close. sub-pixel positioning (top: -0.75px on text, top: 0.5px on buttons) to get visual centering right.

Slider style prop

the slider now supports two visual styles via a style prop:

  • 'line' (default) — thin 4px track, 14px round thumb with subtle border. minimal, doesn't compete with the steppers.
  • 'pill' — tall rounded track matching --height, thumb fills the track. the ws look.

cross-browser CSS for both (webkit, moz, ms). the line style overrides the pill defaults with scoped .line input[type='range'] selectors.

Scale SOT refactor

scale's source of truth moved from raw O_Scene.scale reads/writes to a Svelte writable store w_scale in Setup.ts.

before: every consumer (Controls, wheel handler, steppers) mutated O_Scene.scale directly, then called persistence.save() manually. Controls.svelte kept a stale current_scale $state mirror that needed manual syncing via get_scale().

after: w_scale is the single store. its subscriber syncs O_Scene.scale and auto-persists. all mutations go through w_scale.set() or call scale_up/scale_down (which push to the store after e3.scale_object mutates). Controls.svelte just reads $w_scale — no local state, no manual sync.

removed: set_scale(), get_scale(), current_scale $state, the $effect sync hack.

Files changed

FileWhat
src/lib/ts/render/Setup.tsw_scale store, subscriber syncs O_Scene + persists, removed set_scale/get_scale
src/lib/svelte/main/Controls.svelteuses $w_scale directly, no local mirror
src/lib/svelte/mouse/Slider.sveltevalue display, style prop (pill/line), layout polish

Enhancements

  • bugs in controls:

    • imperial when active has a white outline around the blue border and the blue border is slightly taller than the button without the blue. metric and other
    • units should render with only 1 decimal point of precision
    • 2D should render in (ahem) 2D
  • add a controls button "show/hide dimensionals" a boolean

  • move Design Intuition label in controls up 3px

  • add a six segment control to controls, one for each face

    • darken segment corresponding to forward face of root SO
    • click on a button -> rotate root SO to orient (and straighten) that face at front
  • move "straighten" button to controls, between scaling and face orientation segmented control

  • scale

    • continuous / not steps
    • powers of 10 of the scale
    • tick marks and labels
    • needs larger range
  • remove "design intuition" from controls

  • reverse order of all controls (leave order within segmented controls as is)]]

  • move save to controls (after hamburger)

  • move fit to controls

  • add "<-> [no] grid" toggle button to controls

  • make controls double height for narrow screen (defined as "they don't fit")

  • rearrange so they fit narrow screen