Rich-text editor primitive with canonical JSON, contenteditable bridge, command toolbar, form
serialization and semantic events. Morfo declares the editor parts and semantic events; Soma
owns state, selection, input, links and history; Eidos only paints the emitted data contract.
Typing stays local until it commits; discrete focus, format, link, history, clear and
invalid input actions emit semantic events.
Name
Family
Verb
Sequence
Intent
Target
Play
{#each events as action}
{action.name}
{action.semantic.family}
{action.semantic.verb ?? '-'}
{action.semantic.sequence ?? '-'}
{action.semantic.intent ?? '-'}
{action.target}
{/each}
{/if}
{#if tab === 'recipe'}
Eidos recipe
Selectors live in src/uix/eidos/components/words/words.css; public tokens are
declared under THEME_BASE_RECIPE_TOKENS.words.
Selector
Source
Role
{#each recipeSelectors as row}
{row.selector}
{row.source}
{row.note}
{/each}
{/if}
{#if tab === 'a11y'}
Accessibility
Concern
Contract
Content
role="textbox", aria-multiline="true" and contenteditable managed
by Soma.
Toolbar
role="toolbar" with orientation propagated to ARIA.
Buttons
Command buttons are native buttons; toggle commands expose aria-pressed.
BubbleMenu
Contextual selection toolbar uses role="toolbar", keeps focus from
collapsing the editor selection on pointer down, and hides when no text range is
available.
Link editor
Link editing is a native form inside a reusable tool popover; URL, apply,
unlink and close controls stay keyboard reachable and operate on Soma selection state.
Readonly
Readonly keeps content focusable and sets aria-readonly.
Disabled
Disabled removes editing and prevents command execution.
Status
Status uses role="status" and aria-live="polite".
Form
HiddenInput serializes the full JSON document when name is present.
Keyboard
Key
Action
{#each (wordsMorfo.parts.find((part) => part.kebab === 'content') as unknown as RawPart | undefined)?.keyboard ?? [] as key}