All posts
Posts

Sheet 006 — 2026

Building Eskiz, chapter one: the number input

Before a model becomes a building, a chair, or a very expensive mistake, someone changes a number. We should probably make that part good.

01 / 10

The most important box in the room.

In the previous article, we introduced Eskiz and the problem of making 3D modeling easier to change. This is chapter one of how we are approaching it. We are starting with the number input.

I would argue it is the most important component in a CAD application. Width, height, radius, angle, spacing: sooner or later, a design decision passes through this small rectangle. A model can look magnificent while changing its width remains an administrative procedure.

Take a panel, 120 millimeters wide. Make it 128. Easy. Now make it eight millimeters wider without doing the addition yourself. Enter a measurement in centimeters. Keep its width related to another part. Select three different panels and add eight to each. Still the same little box.

That is the story of our input: each apparently small convenience introduces a responsibility. The examples below use the actual shared component, with small local demonstrations around it. The panel drawings are illustrations, not a geometry kernel.

Two designers give a small wooden panel the attention of an enormous precision caliper.

01 / Start with 120

Eskiz’s shared field
Wmm
Type 120 + 8, then Enter. Try an unfinished 120 +.
Preview 120 mm · Committed 120 mm
A native number input beside the actual Eskiz field. Browser treatment of non-numeric characters varies; the native field does not evaluate arithmetic.

02 / 10

A number is not the same thing as an edit.

The native HTML number input is a useful starting point. It accepts numbers, supports bounds and steps, and may provide spinner buttons. It does not know what 12cm or 120 + 8 means. That is outside its job description.

Replacing it with a text field gets us the characters. It also gets us the responsibility for everything those characters mean. Halfway through typing 120 + 8, the text says 120 +. That is an unfinished thought, not a new panel width.

So we separate the text being edited, a usable preview, and the committed value. Plain numeric edits can preview immediately; an expression can show its evaluated result without committing every intermediate keystroke. Enter or blur settles a valid edit. A failed expression keeps its text so you can repair it. Quietly replacing your mistake with the old value would remove the evidence.

Try 120 + 8, then 120 +. The first has an answer. The second deserves a cursor, not a funeral.

Native behavior reference: MDN’s number input documentation.

02 / Text, preview, committed value

Eskiz’s shared field
Wmm
Type 120 + 8, then Enter. Try an unfinished 120 +.
120 mmPreview 120 mm · Committed 120 mm
The panel follows usable numeric previews. An unfinished expression remains editable without becoming a committed dimension.

03 / 10

Even focus has a job.

Typing a replacement is the first job. It is usually faster to write the new number than to place a cursor between the old digits. So focusing a plain value leaves it visible in a lighter color, ready for you to type straight over it. No selecting or clearing first. If you do want to edit the old value, an arrow key brings it into the field. Leaving without typing changes nothing.

A formula needs different treatment. At rest, 10 + 5 normally displays 15. On focus, its source returns. Keyboard focus selects the whole source; pointer focus can place the caret, with select-all-on-first-click available when the host wants it. We have a regression test for the alternative: the source appeared, the caret landed at the end, and typing 100 produced 10 + 5100. An ambitious interpretation of a small edit.

The visual design follows the same distinction. At rest, one quiet box. On engagement, the scrub handle reveals its seam; focus adds an internal bar, without an extra outline. The seam opens inside the existing footprint, so the neighboring fields do not shuffle around. The unit stays its own target. A little movement tells you where the handles are; the inspector does not need to perform a dance.

03 / One box, three jobs

Plain value
Wmm
Focus: the current value becomes a ghost. Type to replace it.
Stored calculation
Wmm
First click selects the source; the next can place the caret.
W120mmdragtype · focus · repairunits
These are live fields. The enlarged diagram explains their anatomy; it does not simulate the focus state.

04 / 10

A drag is one decision.

Sometimes you know the answer is 128. Sometimes you know the panel looks slightly too narrow. Typing serves the first situation; dragging the label serves the second. We use the label as a larger scrub target instead of adding tiny spinner arrows.

The same value has several gears: Alt for one tenth of a step, Shift for ten steps, and Command or Control with Shift for a hundred. A zero value must still move. Fine movement must keep its decimals. These sound like details until your mouse has crossed the desk and the number has barely noticed.

During a drag, the model can preview each change. On release, the field reports one commit. That lets the owner create one undo entry for the gesture. Escape cancels the scrub and restores its starting value. The field supplies the boundary; the application owns the history. In this example, a tiny history stack makes that distinction visible.

04 / Explore now. Commit once.

Wmm
120 mmPreview 120 mm · 0 commits · Saved 120 mm

Drag W. Alt ×0.1 · Shift ×10 · ⌘/Ctrl + Shift ×100. Escape cancels a drag.

The real field reports changes and commits; this example supplies a small undo stack. The drawing is capped to fit, while the readout keeps the actual value.

05 / 10

The unit is part of the meaning.

Our panel is 120 millimeters wide. Showing 12 centimeters must not make it ten times larger. Typing 12cm into a millimeter field converts the quantity; it does not paste a different label beside 12. A length cannot become an angle just because both fit in the same box.

There is another distinction we had to make explicit: changing an authored unit and changing a display preference. The field supports both. In the demo, the unit cap changes only the display. The stored magnitude, expression source, and edit unit remain millimeters. Enter the field and the authored unit returns visibly.

The same goes for precision. You might prefer to read 12.35 cm. That is not permission to replace the model’s 123.456789 mm with a rounded value. Display rounding and edit rounding have separate controls. A tidier inspector should not quietly become a manufacturing operation.

05 / Change the view, keep the quantity

W
123.456789 mmAuthored: 123.456789 mm · Display preference: mm · Numeric commits: 0

Click the unit cap to cycle mm → cm → in. Focus to edit in millimeters. Type 12cm and press Enter.

This example uses display-unit preferences, not authored-unit conversion. Focus returns to the authored unit. Switching the view creates no numeric commit.

06 / 10

Keep the calculation, too.

A width of 128 tells us the result. A width of 120 + 8 also tells us how we arrived there. A reference such as @panel.width / 2 says something stronger: this dimension depends on another one.

We keep the source. The result is usually what you see at rest; focus reveals the expression, syntax highlighting, and a live result preview. Type @ to choose from names supplied by the host. Completion stays attached to the input without taking focus away from the text you were writing. Escape dismisses the suggestions before a second Escape abandons the edit.

Then comes the less obvious decision: dragging a formula must not silently destroy it. Nudge 120 + 8 by two and the field folds the adjustment into the source. With a reference, the reference survives. Turning the result into a plain number is an explicit decision, not a side effect of touching the handle.

The field can ask what @panel.width means. It cannot decide which objects depend on it, detect every cycle, or propagate changes through a document by itself. Those belong to the owning system. This demo provides one fixed reference; it is a field demonstration, not a miniature dependency engine.

06 / The answer, and the reason

Wmm
128 mmSaved source: 120 + 8 · Result: 128 mm

Drag W or use ↑/↓. The calculation stays a calculation. @panel.width is fixed at 240 mm in this example.

Source-preserving arrows and scrubbing. The reference is supplied by this demo; document-wide propagation belongs to the host.

07 / 10

Seven ways to say 120.

The digits alone do not tell you whether a value was typed, inherited, calculated here, or controlled elsewhere. We represent seven readings: default, literal, overridden, expression, driven, mixed, and error.

Default means nobody set it. Literal means somebody did. Overridden means the authored value differs from a known default, so there is something useful to reset. Expression means calculated here. Driven means another source owns it. Mixed means the selected objects disagree. Error means it could not be resolved.

These are not seven colors. A formula has a marker. A driven value is read-only and identifies its source. An override can offer a revert button. Mixed says Mixed. An error retains the troublesome text and exposes an invalid state. The distinction should survive without the color.

Nor are all seven stored independently in the document. Mixed comes from the selection. Overridden comes from comparing with a default. Error comes from evaluation. Storing those conclusions as if they were permanent facts would create another set of numbers to keep honest.

07 / Seven readings of a field

Default

Wmm
Nobody set it; still editable.

Literal

Wmm
A directly authored value.

Overridden

Wmm
Different from its 100 mm default.

Expression

Wmm
Calculated here; focus shows the source.

Driven

WmmDriven by panel.width
Read-only; owned by panel.width.

Mixed

Wmm3 values
Three selected values disagree.

Error

WmmNo value named missing
Unresolved source; text survives.
Interactive specimens, initialized in seven different states. Editing an editable specimen may change its state. Driven remains read-only.

08 / 10

Mixed is not zero.

Now select three panels: 40, 60, and 100 millimeters wide. Showing zero would be false. Showing the first panel’s width would merely be false with more confidence.

The field says Mixed. Type 80 and all three become 80. Type + 8 and they become 48, 68, and 108. A signed subtraction works the same way: -40 subtracts forty from each. Dragging the label also applies a shared delta, keeping the differences between the panels. To assign an absolute negative value, use parentheses: (-40). Relative input is evaluated against each original value; the batch succeeds together or is refused together. The reader should never have to discover halfway down the selection that only some of the edit happened.

That still leaves ownership clear. The inspector chooses the targets. The field evaluates and reports the proposed values. A linked width-and-height pair is another coordinator’s job: each individual field should not secretly know about its neighbor. Small controls become difficult when we give them the whole application to manage.

Three wooden panels retain different lengths after each receives a small extension.

08 / One edit, three originals

Wmm3 values
Panel 140 mm
Panel 260 mm
Panel 3100 mm
Selected widths: 40 · 60 · 100 mm

Drag W to adjust all three together. Try +20, then -40. Type 80 to set all widths to 80; (-40) explicitly sets all to negative forty.

Relative edits use each original width. An absolute number replaces them all. Reset starts the comparison again.

09 / 10

The awkward cases are the component.

An inspector gets narrow. A formula gets long. We let the content borrow space from the caps before sacrificing its digits. On fine pointers, hover reveals the fuller anatomy again; keyboard focus can recover a collapsed unit control. Touch keeps the anatomy whole because there is no hover to come back through. Below a useful width, the honest answer is a wider inspector.

I am not very happy with the narrow-field behavior yet, especially with long formulas. Keeping the whole expression readable and easy to edit in a small inspector is still something I am thinking through. The example below shows where we are, rather than a decision I consider finished.

Limits also need meaning. A soft limit preserves an out-of-range entry and warns. A hard limit constrains it. An angle may wrap through 360 degrees; a hinge may have actual stops. We cannot infer those product decisions from the fact that the value happens to be numeric.

Even a mathematically valid answer can be wrong for a document. The host gets a chance to refuse a commit before it is applied. A refused edit keeps its draft and explanation. We already had a bug where a value preview changed before the owner rejected it, leaving the field and document disagreeing. It is considerably easier to trust a refusal when the object has not moved anyway.

09 / The edges of the happy path

W
Soft limit: 0–200
Wmm
Type 240: keep it, show a warning.
Hard limit: 0–200
Wmm
Type 240: constrain it to 200.
A cyclic angle
A°
Type 359 + 2: wrap to 1°.
The owner can refuse
Wmm
Type 10, then Enter. Saved: 120 mm.
Try the narrow field, limits, angle wrapping, and a host refusal. These use the shared component’s own behavior; the fixing rule is illustrative.

10 / 10

Back to the model.

The complexity is in the combinations: an expression while scrubbing, a unit preference while focused, an invalid draft when Apply is pressed, a read-only value reached by keyboard instead of mouse. We test those transitions because a screenshot of the resting field cannot tell us whether any of them works.

This is why we are starting the Eskiz series here. Easier modeling is partly the accumulation of small, dependable interactions: type what you mean, explore without losing your starting point, keep the relationships you created, and understand why a change was refused.

AI collaboration will still need those properties. Whether a person types a dimension or an agent proposes one, we need to inspect its source, adjust it, and trust what gets committed. The number input does not solve the whole CAD application. It does give us a very small place where the promises have to become real.

The production component is much richer than these examples. We have kept the demos focused so each decision is easy to try; there are many more advanced features to discover in the Eskiz app. Even the small rectangle has things left to show you.

We wanted to change a panel’s width. We now have opinions about the first arrow key. This is how it starts.