Text and expressions
What an attribute’s text becomes: rich text and its footnotes, live values, symbol names, measured and wrapped labels, and a compiled expression.
@internote/canvas-sdk#Functions
format or upper, to a plain string.(
name: TextFunctionName,
text: string,
attributes?: Record<string, unknown>,
): string(text: string, px: number, cap: number, family?: string): {
width: number;
wrapAt: number;
lines: number;
}wrapWidths fills each line to the limit, which is what CSS does by default; a label given a hard width carries text-wrap: balance, and the browser then breaks it into lines of even length instead. The two disagree badly: "what adding them would predict" wrapped greedily at a 260px cap is a 250px line and a 40px one, so 250px of gutter gets reserved — and the browser then sets two 130px lines, leaving 120px of empty air between the words and whatever stands beyond them.
So the balance is computed here too: the NARROWEST width that still sets the words in the same number of lines, found by bisection, which is what balancing amounts to. width is then the ink as it will actually appear, and wrapAt the box to hand the browser — the ink plus a hair, so a measurement a fraction out cannot tip a line over.
(
js: string,
variables: string[],
params: Record<string, number>,
): ((...args: Array<number | Complex>) => Complex | null) | nullformals, or null where the formals or the body fail the checks above, or JavaScript refuses the result (a repeated formal, unbalanced brackets).<F extends (
...args: never[],
) => unknown>(js: unknown, formals: readonly string[], namespaces?: ExpressionNamespaces): F | null(js: string, values: Record<string, number | Complex>): Complex | null(expr: string, params: Record<string, number>): numberexprparamsReturns The numeric result, or NaN if evaluation fails
(item: unknown): ChalkExpression | null(items: unknown): string(value: number | undefined, options?: LiveValueFormatOptions): string(value: unknown): boolean(value: unknown): value is Complexjs is an expression the compilers could have emitted over these formals — see the module remarks for exactly what that admits.(
js: unknown,
formals: readonly string[],
namespaces?: ExpressionNamespaces,
): js is stringformalsnamespacesns.member, defaulting to Math.name may be a formal of a compiled expression: an ASCII identifier, and not a reserved word.(name: unknown): name is stringname is a symbol the language has a drawing for.(name: unknown): name is SymbolNameAsked of the NAMES rather than of the glyph table, which is the same question — the table is keyed by them — and does not need the drawings to answer it.
(identifier: string): identifier is TextFunctionNametext-size: (or size:) to pixels at the environment's scale factor u.(points: unknown, u: number): number(source: string): unknownThe axis numbers under x-format: radians are written as TeX rather than authored as chalk, and they still want everything a label has: the halo, the KaTeX rendering, the fallback to the source when the maths does not parse. Wrapping the string in the micronode the compiler would have emitted hands it all of that instead of a second, parallel way to draw maths.
(items: unknown): stringflatten gives the SOURCE, and for maths the source is not the picture: $\theta$ is six characters of chalk and one letter on the page. Anything asking how long a label is — whether an axis name is a single letter, how much room to leave for it — has to ask about the letter. So each maths run is reduced to its atoms: a symbol macro stands for the one glyph it draws, an accent or a face for none, and braces and spacing for nothing.
The glyph a macro stands in as is an m, which is no narrower than the Greek letters this is nearly always used for — the answer is a margin's width, so erring wide is erring safe.
(raw: unknown): string | undefinedtext is at px in family, measured by the browser where there is one and estimated from an average glyph where there is not.(text: string, px: number, family?: string): numberCached, except while the face is still downloading — a measurement taken against the fallback must not be remembered as the answer.
(text: string, px: number, maxWidth: number, family?: string): number[]#Classes
{{#id.member}} reads.typeof LiveValueStoreA construct that computes something (a fit's slope, an integral's area) publishes it with publish; prose and labels read it with read and subscribe, or through useLiveValue. One store serves a whole document, above both the narrative column and the canvas, so a number in prose follows the environment the reader is manipulating. A read of an id that names a @define invocation resolves through the calls map given to setCalls, following bindings and bound expressions on every read. Free names go under the $free scope, lowercased. Publishing undefined clears a slot; clear empties them all.
#Constants
stringtext-size: value that gives a label the standard size, LABEL_SIZE; it is the schema default.numberu is applied.numberMath alone.ExpressionNamespacesRecord<SymbolName, string>#Interfaces
complex literal.imnumberrenumberattributesArray<{ identifier: string; value: unknown }>bodyunknowncontentstringidentifierbold, latex, a hue.stringtype'text' for a plain run, 'expression' for a bound {{…}} value.stringformatLiveValue.digitsnumberRichTextOptions' renderFootnote receives for one footnote.contentReact.ReactNodecontextnarrativecanvaskeyReact.Keynotestringbold or link, or a block produced by parseMarkdownToRichText.renderRichText renders text runs and micronodes; it renders nothing for a list or a block break, and a block equation renders inline.
attributeshref.Array<{ identifier: string; value: unknown }>bodyRichTextNode[] | stringcontentstringidentifierbold, latex or footnote.stringisBlockBreakbooleanisBlockEquationlatex node came from $$…$$, a display equation.booleanisListlistItems.booleanlistItemsRichTextNode[][]listTypeuloltype'text' for a text run; 'expression' for a bound expression; absent for a micronode.stringrenderRichText renders: where the text is shown, and the host's own renderers for the nodes that need app data.Each renderer left out falls back to a self-contained rendering.
contextnarrativecanvasrenderCitationcite marker. Injected by the host because the number it shows comes from the document's citations key, which the SDK cannot see. Falls back to a raised dot.(args: RichTextCitationArgs) => React.ReactNoderenderFootnotefootnote node. Injected by the host to use a richer popover. Falls back to an underlined trigger with a native title tooltip.(args: RichTextFootnoteArgs) => React.ReactNoderenderInternoteRefinternote cross-reference node. Injected by the host because it needs app data/loading. Falls back to a plain link to /{id}.(id: string | undefined, key: React.Key) => React.ReactNode#Types
Math, or a runtime such as the complex cx.Readonly<Record<string, ReadonlySet<string>>>[nodeId, member, value]; a value of undefined removes it.readonly [string, string, number | undefined]narrativecanvasformatupperlowertitletruncatepad