0011 Defaults: one install, TipTap by default, lazy elements
- Status: accepted (2026-10-29)
- Amended 2026-10-29: the first-load budget is 96 KiB raw / 32 KiB gzip (was 90 / 30); the group palette took
astrolabe-player.jsto 29.9 KiB gzip, and pictures, the URL policy and open answers belong in the first load too. - Amended 2026-10-30: each element chunk is at most 32 KiB raw / 10 KiB gzip, and the editor's first load (TipTap
aside) at most 370 KiB raw / 112 KiB gzip plus 10 KiB raw / 3.5 KiB gzip per built-in type: the editor loads every
type's definition, manifest, catalogs and descriptor (live issues, the preview), so its budget grows with each type.
packages/astrolabe/scripts/check-bundles.mjsholds the numbers.
Context
Section titled “Context”Hosts wired everything by hand: createEngine({ components: builtInDefinitions }), definePlayer({ elements }) and
the authoring kit around their own editor, with TipTap only as an example. The student bundle carried every element
(180.6 KiB raw / 51 KiB gzip for six types, about 420 / 125 projected for 21). The library has to be easy to use, with
defaults that are easy to override.
Decision
Section titled “Decision”- E3, the editor. A package
@astrolabe/editor-tiptapwith a ready<astl-editor>: a TipTap surface, the insert menu, the inspector, the issues list and the preview.@astrolabe/editorstays free of any WYSIWYG engine (the adapter contract, the kit and the textarea adapter); another editor is another adapter, with its element inslot="surface". Onlyeditor-tiptapimports TipTap: its main entry through a dynamicimport()of the surface only, and its/tiptapentry (astrolabeExtensions,createTiptapAdapter) for hosts with their own TipTap editor. - E4, one install.
packages/cdnbecomes the umbrellaastrolabe(a working name), with the entriesastrolabe/engine,/player,/editorand/serverand the script-tag bundlesastrolabe.js,astrolabe-player.jsandastrolabe-editor.js. The defaults are every built-in component, the playful and notebook looks next to the default look, English and Polish, and TipTap. Nothing is defined on import:definePlayer()anddefineEditor()with no arguments wire everything, and the bundles call them on load. The engine secret is optional and onlycompileneeds it; there is no default secret. The@astrolabe/*packages stay for lean installs. - E5, lazy elements. The player loads component elements per tag (
LazyElement { tag, load }): a page with only gaps downloads only the gap element. The built-in lists are lazy one-liners. - Options replace defaults. An option replaces its default and is never merged with it. The built-in lists are
exported (
builtInDefinitions,builtInElements,builtInEditors,builtInStyleTemplates,builtInManifests): extend with[...builtIns, mine], restrict with.filter(...). IndefineEditor,editorsandelementsdefault to the built-in entries of thecomponentstags, so{ components: [gap, choice] }needs no other option; a passededitorsorelementsstill replaces its default. Always on, not options: each package's English and Polish messages, the engine's built-in slot kinds andDEFAULT_PROFILE;catalogs,slotKindsandprofileadd to them.
- One install and no options cover the common case, and every default is one option away.
- One rule for every option, and one that can restrict: a merge could only ever add.
- Defining on import would run during server-side imports (Astro, Next) and break
sideEffects: false. - A known secret would undo the keyed shuffle; for
astl-orderthe source order is the answer. - TipTap is about 385 KiB raw / 117 KiB gzip. A page that brings its own editor must not download it, so the surface is a dynamic import and the TipTap building blocks are a separate entry.
- Lazy elements keep the student's first load flat as components are added: about 84 KiB raw / 28 KiB gzip with 21
types instead of about 420 / 125. Measured with the six built-ins:
astrolabe-player.jsloads 88.2 KiB raw / 29.5 KiB gzip first (budget 90 / 30), and each element adds 12.6 to 27.7 KiB raw (5.1 to 9.4 gzip) when a view first uses it.
Rejected
Section titled “Rejected”- Merging options with defaults (
{ components: [mine] }meaning the built-ins plus mine): nothing could be restricted, and every option would need two rules. - Defining elements on import: server-side imports would register custom elements.
- A default secret: it undoes the keyed option order.
- TipTap inside
@astrolabe/editor, or any WYSIWYG engine in every install: hosts with their own editor would load it for nothing (0006 stays for the kit). - Eager built-in elements in the student bundle: it grows with every component.
- Folding the
calmlook into the default look: that would change the default.calmis dropped instead, and a source that still names it plays in the default look with the warningSTYLE_TEMPLATE_UNKNOWN. - Shared chunks that import an entry: a page loading
astrolabe.js?v=2would run a second instance and throw "already defined".
Consequences
Section titled “Consequences”definePlayer()fromastrolabe/playerplays every built-in, anddefineEditor()fromastrolabe/editorgives TipTap with everything; the quick start lists every default and its override.- Element chunks wait for the view to be parsed (one round trip). A chunk that fails fails the view with
load_failed, and the next view retries it. - A missing secret fails at the first
compile, not at startup. - Server-only users of the umbrella still install TipTap;
@astrolabe/*stay for lean installs. - The published name of the umbrella waits for the npm scope: unscoped
astrolabeis a 2014 Protractor package. - After every build,
packages/astrolabe/scripts/check-bundles.mjschecks no answer logic inastrolabe-player.js, no editor code in either player bundle, TipTap only behind a dynamic import inastrolabe-editor.js, no chunk that imports an entry, and the budgets of the first loads and the element chunks.