Skip to content

<notatio-in>

The subset of MathLive's <math-field> this element drives. */ interface MathField extends HTMLElement { value: string; readOnly: boolean; /** MathLive's current selection, and a reader for the LaTeX inside it. */ selection?: unknown; getValue?: (range?: unknown, format?: string) => string; /** MathLive's fill-in-the-blank API: the content of a \placeholder[id]{}. */ getPromptValue?: (id: string, format?: string) => string; }

/** How a compute-engine type is written on the page. Everything else falls back to an upright name, which is still readable if less conventional. / const TYPE_LATEX: Record<string, string> = { integer: "\\mathbb{Z}", finite_integer: "\\mathbb{Z}", rational: "\\mathbb{Q}", finite_rational: "\\mathbb{Q}", real: "\\mathbb{R}", finite_real: "\\mathbb{R}", complex: "\\mathbb{C}", finite_complex: "\\mathbb{C}", number: "\\mathbb{C}", boolean: "\\mathbb{B}", };

const typeLatex = (type: string): string => TYPE_LATEX[type] ?? \\mathrm{${type.replace(/_/g, "\\_")}};

/** The assignment operators a bound cell may be written with. */ const ASSIGN = /\\coloneqq?|:=/;

/** Everything after the assignment operator -- the part a pinned input lets you edit. */ function valuePart(latex: string): string { const m = ASSIGN.exec(latex); return (m ? latex.slice(m.index + m[0].length) : latex).trim(); }

/** The id of the one editable hole in a pinned field. */ const PROMPT = "value";

/** <notatio-in> -- a LaTeX math field wrapping MathLive's <math-field>. Emits notatio-change with { latex } on each edit. When readonly, it renders static markup via mathlive/ssr and never loads the (heavy) editor. MathLive is lazy-loaded the first time an editable input mounts.

A control on the field writes a *wrapper head* around what the reader typed -- N(x) for a number, FullForm(x) for the AST, TraditionalForm(x) for the rendering (see WRAPPER_HEADS). The head lands in the emitted value, not in the editor: the field keeps showing the expression as written, so the wrapper is a reversible request rather than an edit. A value handed back wrapped is unwrapped again into expression plus head, which is also how a saved one is restored.

With bind (and optionally type) the field is *pinned*: it shows a declaration like p \in \mathbb{Z} \coloneq 3 where only the value is editable. The declaration is not text the reader could delete -- it is a read-only field with one \placeholder[value]{} hole, so the caret cannot leave the value at all.

The asserted type is shown, not parsed: p \in \mathbb{Z} \coloneq 3 reads as a declaration but parses as Element(p, Assign(Integers, 3)), binding the assignment to the domain instead of to p. So value stays the plain p \coloneq 3 that everything downstream already understands, and the type travels beside it as structure. Enforcing it is a separate job, done by whoever declares the symbol.

A pinned field holding a number can play: play adds a button that sweeps the value through min..max by step (inferred from the value when not given, the way a knob's are), emitting each step as if it had been typed. loop says what the ends do -- cycle, reflect or none (the default: a range has ends) -- interval and rate set the pace, and holding the button opens the speed-and-loop panel. Typing into the field stops it.

Stories in the playground·packages/notatio-lit/src/notatio-in.ts· in Vue: <In>

Attributes

AttributeTypeDefaultNotes
valuestring""The LaTeX in the editor; changes emit notatio-change.
readonlybooleanfalseTypeset the value without allowing edits.reflects
input-form
property inputForm
string""Read-only: value as InputForm, kept in sync and reflected so a copy can read it off a cloned selection fragment. Set value, not this.reflects
headstring""The wrapper head asked for -- N, FullForm, TraditionalForm, ... (see WRAPPER_HEADS), or "" for none. The emitted value is wrapped in it while the editor keeps showing the expression as written.reflects
bindstring""Pin this field to a binding: the symbol name and its \coloneq become fixed chrome, and only the value can be edited.
domainstring""The domain asserted for bind, spelled as a compute-engine type (integer, real, complex, ...). Shown as a membership beside the name. Not type: that attribute already means "which symbol this component is" elsewhere.
playbooleanfalseShow a play button that sweeps a pinned numeric value through its range.reflects
loopLoop | """"What a sweep does at the ends: cycle, reflect or none (default).reflects
ratenumber1Playback speed as a multiplier on interval.reflects
intervalnumberNumber.NaNMilliseconds per step; defaults to a whole sweep in a few seconds.
minnumberNumber.NaNThe sweep's range and step; inferred from the value when not given.
maxnumberNumber.NaN
stepnumberNumber.NaN