feat(words): companion drawer right-rail (concept A+D) — shell + default panel (DRAWER-1)
A radical disposition shift: instead of N fragmented popovers (bubble
menu + slash menu + link editor + heading picker + language picker +
table popover + image controls) we mount ONE contextual right-rail
that changes its panel based on what the user is editing.
Spike-1 surface (this commit) ships:
- New eidos component <Words.Drawer> (words-drawer.svelte) — a
collapsible right sidebar with mode detection derived from snippet
props (currentBlock + selection). 6 modes mapped: default | format
| image | table | code | list.
- Default panel (visible when no contextual mode active):
- OUTLINE — flat list of h1/h2/h3 headings, indented by level
- STATS — word count + char count + reading time (200wpm)
- SUGGESTIONS — writer-coaching warnings derived from the document
model: missing alt text, long paragraphs (>600 chars), heading
skip patterns (h3 without h2 above). Empty state shows "Nothing
flagged" in affirm color.
- Contextual panels (format/image/table/code/list) render a
placeholder "coming soon" message — implemented in DRAWER-2.
- Eidos CSS:
- Provider host gains a grid-template-areas layout when a drawer is
present: 'toolbar drawer / content drawer / status drawer'.
Existing children (toolbar/content/placeholder/status) get
explicit grid-area assignments.
- Drawer collapses to 2.25rem (just the chevron) when closed.
16rem default open.
- Outline items, stats grid, warning list each have their own
discrete styling.
Demo (+page.svelte):
- New `showDrawer` state (default true).
- Children snippet refactored to capture full snippetProps via @const
destructure (kept selectedLink working).
- <Words.Drawer snippet={snippetProps} /> mounted inside the children
block alongside the other parts.
Visual verification:
- Drawer renders as a dedicated right column with the editor content
on the left, no layout overlap.
- DOCUMENT header + OUTLINE shows "H1 ActiveUIX Words", STATS shows
73 words / 508 chars / 1 min reading, SUGGESTIONS shows "Nothing
flagged" (sample doc has no warnings).
- Collapse button toggles open/closed via animated inline-size
transition.
Next:
- DRAWER-2: format/image/table/code/list contextual panels wired to
runCommand.
- DRAWER-3: polish (mobile drawer-as-bottom-sheet, transitions, A/B
toggle in demo to compare classic toolbar vs companion drawer).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
<script lang="ts">
|
|
|
|
|
|
/**
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
* Words.Drawer — companion right-rail eidos wrapper.
|
feat(words): companion drawer right-rail (concept A+D) — shell + default panel (DRAWER-1)
A radical disposition shift: instead of N fragmented popovers (bubble
menu + slash menu + link editor + heading picker + language picker +
table popover + image controls) we mount ONE contextual right-rail
that changes its panel based on what the user is editing.
Spike-1 surface (this commit) ships:
- New eidos component <Words.Drawer> (words-drawer.svelte) — a
collapsible right sidebar with mode detection derived from snippet
props (currentBlock + selection). 6 modes mapped: default | format
| image | table | code | list.
- Default panel (visible when no contextual mode active):
- OUTLINE — flat list of h1/h2/h3 headings, indented by level
- STATS — word count + char count + reading time (200wpm)
- SUGGESTIONS — writer-coaching warnings derived from the document
model: missing alt text, long paragraphs (>600 chars), heading
skip patterns (h3 without h2 above). Empty state shows "Nothing
flagged" in affirm color.
- Contextual panels (format/image/table/code/list) render a
placeholder "coming soon" message — implemented in DRAWER-2.
- Eidos CSS:
- Provider host gains a grid-template-areas layout when a drawer is
present: 'toolbar drawer / content drawer / status drawer'.
Existing children (toolbar/content/placeholder/status) get
explicit grid-area assignments.
- Drawer collapses to 2.25rem (just the chevron) when closed.
16rem default open.
- Outline items, stats grid, warning list each have their own
discrete styling.
Demo (+page.svelte):
- New `showDrawer` state (default true).
- Children snippet refactored to capture full snippetProps via @const
destructure (kept selectedLink working).
- <Words.Drawer snippet={snippetProps} /> mounted inside the children
block alongside the other parts.
Visual verification:
- Drawer renders as a dedicated right column with the editor content
on the left, no layout overlap.
- DOCUMENT header + OUTLINE shows "H1 ActiveUIX Words", STATS shows
73 words / 508 chars / 1 min reading, SUGGESTIONS shows "Nothing
flagged" (sample doc has no warnings).
- Collapse button toggles open/closed via animated inline-size
transition.
Next:
- DRAWER-2: format/image/table/code/list contextual panels wired to
runCommand.
- DRAWER-3: polish (mobile drawer-as-bottom-sheet, transitions, A/B
toggle in demo to compare classic toolbar vs companion drawer).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
*
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
* After DRAWER-CANON-2 the drawer is canonical: it consumes the
|
|
|
|
|
|
* soma `Words.Drawer` provider (which owns the open/collapsed state
|
|
|
|
|
|
* and the derived `mode`) and renders the per-mode body here in
|
|
|
|
|
|
* eidos.
|
feat(words): companion drawer right-rail (concept A+D) — shell + default panel (DRAWER-1)
A radical disposition shift: instead of N fragmented popovers (bubble
menu + slash menu + link editor + heading picker + language picker +
table popover + image controls) we mount ONE contextual right-rail
that changes its panel based on what the user is editing.
Spike-1 surface (this commit) ships:
- New eidos component <Words.Drawer> (words-drawer.svelte) — a
collapsible right sidebar with mode detection derived from snippet
props (currentBlock + selection). 6 modes mapped: default | format
| image | table | code | list.
- Default panel (visible when no contextual mode active):
- OUTLINE — flat list of h1/h2/h3 headings, indented by level
- STATS — word count + char count + reading time (200wpm)
- SUGGESTIONS — writer-coaching warnings derived from the document
model: missing alt text, long paragraphs (>600 chars), heading
skip patterns (h3 without h2 above). Empty state shows "Nothing
flagged" in affirm color.
- Contextual panels (format/image/table/code/list) render a
placeholder "coming soon" message — implemented in DRAWER-2.
- Eidos CSS:
- Provider host gains a grid-template-areas layout when a drawer is
present: 'toolbar drawer / content drawer / status drawer'.
Existing children (toolbar/content/placeholder/status) get
explicit grid-area assignments.
- Drawer collapses to 2.25rem (just the chevron) when closed.
16rem default open.
- Outline items, stats grid, warning list each have their own
discrete styling.
Demo (+page.svelte):
- New `showDrawer` state (default true).
- Children snippet refactored to capture full snippetProps via @const
destructure (kept selectedLink working).
- <Words.Drawer snippet={snippetProps} /> mounted inside the children
block alongside the other parts.
Visual verification:
- Drawer renders as a dedicated right column with the editor content
on the left, no layout overlap.
- DOCUMENT header + OUTLINE shows "H1 ActiveUIX Words", STATS shows
73 words / 508 chars / 1 min reading, SUGGESTIONS shows "Nothing
flagged" (sample doc has no warnings).
- Collapse button toggles open/closed via animated inline-size
transition.
Next:
- DRAWER-2: format/image/table/code/list contextual panels wired to
runCommand.
- DRAWER-3: polish (mobile drawer-as-bottom-sheet, transitions, A/B
toggle in demo to compare classic toolbar vs companion drawer).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
*
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
* Concept (still A+D from the spike):
|
|
|
|
|
|
* - Single contextual surface replacing bubble menu + slash menu +
|
|
|
|
|
|
* link editor + heading picker + language picker + table popover
|
|
|
|
|
|
* + image controls.
|
|
|
|
|
|
* - Default mode shows outline + stats + writer-coaching suggestions.
|
|
|
|
|
|
* - Format / image / table / code / list modes show controls scoped
|
|
|
|
|
|
* to what the user is editing.
|
feat(words): companion drawer right-rail (concept A+D) — shell + default panel (DRAWER-1)
A radical disposition shift: instead of N fragmented popovers (bubble
menu + slash menu + link editor + heading picker + language picker +
table popover + image controls) we mount ONE contextual right-rail
that changes its panel based on what the user is editing.
Spike-1 surface (this commit) ships:
- New eidos component <Words.Drawer> (words-drawer.svelte) — a
collapsible right sidebar with mode detection derived from snippet
props (currentBlock + selection). 6 modes mapped: default | format
| image | table | code | list.
- Default panel (visible when no contextual mode active):
- OUTLINE — flat list of h1/h2/h3 headings, indented by level
- STATS — word count + char count + reading time (200wpm)
- SUGGESTIONS — writer-coaching warnings derived from the document
model: missing alt text, long paragraphs (>600 chars), heading
skip patterns (h3 without h2 above). Empty state shows "Nothing
flagged" in affirm color.
- Contextual panels (format/image/table/code/list) render a
placeholder "coming soon" message — implemented in DRAWER-2.
- Eidos CSS:
- Provider host gains a grid-template-areas layout when a drawer is
present: 'toolbar drawer / content drawer / status drawer'.
Existing children (toolbar/content/placeholder/status) get
explicit grid-area assignments.
- Drawer collapses to 2.25rem (just the chevron) when closed.
16rem default open.
- Outline items, stats grid, warning list each have their own
discrete styling.
Demo (+page.svelte):
- New `showDrawer` state (default true).
- Children snippet refactored to capture full snippetProps via @const
destructure (kept selectedLink working).
- <Words.Drawer snippet={snippetProps} /> mounted inside the children
block alongside the other parts.
Visual verification:
- Drawer renders as a dedicated right column with the editor content
on the left, no layout overlap.
- DOCUMENT header + OUTLINE shows "H1 ActiveUIX Words", STATS shows
73 words / 508 chars / 1 min reading, SUGGESTIONS shows "Nothing
flagged" (sample doc has no warnings).
- Collapse button toggles open/closed via animated inline-size
transition.
Next:
- DRAWER-2: format/image/table/code/list contextual panels wired to
runCommand.
- DRAWER-3: polish (mobile drawer-as-bottom-sheet, transitions, A/B
toggle in demo to compare classic toolbar vs companion drawer).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
*/
|
|
|
|
|
|
import { ChevronRight, ChevronLeft } from '$uix/eidos/components/icon';
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
import * as Words from '$soma/components/words';
|
feat(words): companion drawer right-rail (concept A+D) — shell + default panel (DRAWER-1)
A radical disposition shift: instead of N fragmented popovers (bubble
menu + slash menu + link editor + heading picker + language picker +
table popover + image controls) we mount ONE contextual right-rail
that changes its panel based on what the user is editing.
Spike-1 surface (this commit) ships:
- New eidos component <Words.Drawer> (words-drawer.svelte) — a
collapsible right sidebar with mode detection derived from snippet
props (currentBlock + selection). 6 modes mapped: default | format
| image | table | code | list.
- Default panel (visible when no contextual mode active):
- OUTLINE — flat list of h1/h2/h3 headings, indented by level
- STATS — word count + char count + reading time (200wpm)
- SUGGESTIONS — writer-coaching warnings derived from the document
model: missing alt text, long paragraphs (>600 chars), heading
skip patterns (h3 without h2 above). Empty state shows "Nothing
flagged" in affirm color.
- Contextual panels (format/image/table/code/list) render a
placeholder "coming soon" message — implemented in DRAWER-2.
- Eidos CSS:
- Provider host gains a grid-template-areas layout when a drawer is
present: 'toolbar drawer / content drawer / status drawer'.
Existing children (toolbar/content/placeholder/status) get
explicit grid-area assignments.
- Drawer collapses to 2.25rem (just the chevron) when closed.
16rem default open.
- Outline items, stats grid, warning list each have their own
discrete styling.
Demo (+page.svelte):
- New `showDrawer` state (default true).
- Children snippet refactored to capture full snippetProps via @const
destructure (kept selectedLink working).
- <Words.Drawer snippet={snippetProps} /> mounted inside the children
block alongside the other parts.
Visual verification:
- Drawer renders as a dedicated right column with the editor content
on the left, no layout overlap.
- DOCUMENT header + OUTLINE shows "H1 ActiveUIX Words", STATS shows
73 words / 508 chars / 1 min reading, SUGGESTIONS shows "Nothing
flagged" (sample doc has no warnings).
- Collapse button toggles open/closed via animated inline-size
transition.
Next:
- DRAWER-2: format/image/table/code/list contextual panels wired to
runCommand.
- DRAWER-3: polish (mobile drawer-as-bottom-sheet, transitions, A/B
toggle in demo to compare classic toolbar vs companion drawer).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
import type {
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
DrawerSnippetProps,
|
|
|
|
|
|
DrawerProps,
|
|
|
|
|
|
ProviderSnippetProps
|
feat(words): companion drawer right-rail (concept A+D) — shell + default panel (DRAWER-1)
A radical disposition shift: instead of N fragmented popovers (bubble
menu + slash menu + link editor + heading picker + language picker +
table popover + image controls) we mount ONE contextual right-rail
that changes its panel based on what the user is editing.
Spike-1 surface (this commit) ships:
- New eidos component <Words.Drawer> (words-drawer.svelte) — a
collapsible right sidebar with mode detection derived from snippet
props (currentBlock + selection). 6 modes mapped: default | format
| image | table | code | list.
- Default panel (visible when no contextual mode active):
- OUTLINE — flat list of h1/h2/h3 headings, indented by level
- STATS — word count + char count + reading time (200wpm)
- SUGGESTIONS — writer-coaching warnings derived from the document
model: missing alt text, long paragraphs (>600 chars), heading
skip patterns (h3 without h2 above). Empty state shows "Nothing
flagged" in affirm color.
- Contextual panels (format/image/table/code/list) render a
placeholder "coming soon" message — implemented in DRAWER-2.
- Eidos CSS:
- Provider host gains a grid-template-areas layout when a drawer is
present: 'toolbar drawer / content drawer / status drawer'.
Existing children (toolbar/content/placeholder/status) get
explicit grid-area assignments.
- Drawer collapses to 2.25rem (just the chevron) when closed.
16rem default open.
- Outline items, stats grid, warning list each have their own
discrete styling.
Demo (+page.svelte):
- New `showDrawer` state (default true).
- Children snippet refactored to capture full snippetProps via @const
destructure (kept selectedLink working).
- <Words.Drawer snippet={snippetProps} /> mounted inside the children
block alongside the other parts.
Visual verification:
- Drawer renders as a dedicated right column with the editor content
on the left, no layout overlap.
- DOCUMENT header + OUTLINE shows "H1 ActiveUIX Words", STATS shows
73 words / 508 chars / 1 min reading, SUGGESTIONS shows "Nothing
flagged" (sample doc has no warnings).
- Collapse button toggles open/closed via animated inline-size
transition.
Next:
- DRAWER-2: format/image/table/code/list contextual panels wired to
runCommand.
- DRAWER-3: polish (mobile drawer-as-bottom-sheet, transitions, A/B
toggle in demo to compare classic toolbar vs companion drawer).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
} from '$soma/components/words';
|
|
|
|
|
|
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
let {
|
|
|
|
|
|
open = $bindable(undefined),
|
|
|
|
|
|
defaultOpen = true,
|
|
|
|
|
|
disabled = false,
|
|
|
|
|
|
children: customChildren,
|
|
|
|
|
|
...rest
|
|
|
|
|
|
}: Omit<DrawerProps, 'child'> = $props();
|
feat(words): companion drawer right-rail (concept A+D) — shell + default panel (DRAWER-1)
A radical disposition shift: instead of N fragmented popovers (bubble
menu + slash menu + link editor + heading picker + language picker +
table popover + image controls) we mount ONE contextual right-rail
that changes its panel based on what the user is editing.
Spike-1 surface (this commit) ships:
- New eidos component <Words.Drawer> (words-drawer.svelte) — a
collapsible right sidebar with mode detection derived from snippet
props (currentBlock + selection). 6 modes mapped: default | format
| image | table | code | list.
- Default panel (visible when no contextual mode active):
- OUTLINE — flat list of h1/h2/h3 headings, indented by level
- STATS — word count + char count + reading time (200wpm)
- SUGGESTIONS — writer-coaching warnings derived from the document
model: missing alt text, long paragraphs (>600 chars), heading
skip patterns (h3 without h2 above). Empty state shows "Nothing
flagged" in affirm color.
- Contextual panels (format/image/table/code/list) render a
placeholder "coming soon" message — implemented in DRAWER-2.
- Eidos CSS:
- Provider host gains a grid-template-areas layout when a drawer is
present: 'toolbar drawer / content drawer / status drawer'.
Existing children (toolbar/content/placeholder/status) get
explicit grid-area assignments.
- Drawer collapses to 2.25rem (just the chevron) when closed.
16rem default open.
- Outline items, stats grid, warning list each have their own
discrete styling.
Demo (+page.svelte):
- New `showDrawer` state (default true).
- Children snippet refactored to capture full snippetProps via @const
destructure (kept selectedLink working).
- <Words.Drawer snippet={snippetProps} /> mounted inside the children
block alongside the other parts.
Visual verification:
- Drawer renders as a dedicated right column with the editor content
on the left, no layout overlap.
- DOCUMENT header + OUTLINE shows "H1 ActiveUIX Words", STATS shows
73 words / 508 chars / 1 min reading, SUGGESTIONS shows "Nothing
flagged" (sample doc has no warnings).
- Collapse button toggles open/closed via animated inline-size
transition.
Next:
- DRAWER-2: format/image/table/code/list contextual panels wired to
runCommand.
- DRAWER-3: polish (mobile drawer-as-bottom-sheet, transitions, A/B
toggle in demo to compare classic toolbar vs companion drawer).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
const MODE_LABELS: Record<DrawerSnippetProps['mode'], string> = {
|
feat(words): companion drawer right-rail (concept A+D) — shell + default panel (DRAWER-1)
A radical disposition shift: instead of N fragmented popovers (bubble
menu + slash menu + link editor + heading picker + language picker +
table popover + image controls) we mount ONE contextual right-rail
that changes its panel based on what the user is editing.
Spike-1 surface (this commit) ships:
- New eidos component <Words.Drawer> (words-drawer.svelte) — a
collapsible right sidebar with mode detection derived from snippet
props (currentBlock + selection). 6 modes mapped: default | format
| image | table | code | list.
- Default panel (visible when no contextual mode active):
- OUTLINE — flat list of h1/h2/h3 headings, indented by level
- STATS — word count + char count + reading time (200wpm)
- SUGGESTIONS — writer-coaching warnings derived from the document
model: missing alt text, long paragraphs (>600 chars), heading
skip patterns (h3 without h2 above). Empty state shows "Nothing
flagged" in affirm color.
- Contextual panels (format/image/table/code/list) render a
placeholder "coming soon" message — implemented in DRAWER-2.
- Eidos CSS:
- Provider host gains a grid-template-areas layout when a drawer is
present: 'toolbar drawer / content drawer / status drawer'.
Existing children (toolbar/content/placeholder/status) get
explicit grid-area assignments.
- Drawer collapses to 2.25rem (just the chevron) when closed.
16rem default open.
- Outline items, stats grid, warning list each have their own
discrete styling.
Demo (+page.svelte):
- New `showDrawer` state (default true).
- Children snippet refactored to capture full snippetProps via @const
destructure (kept selectedLink working).
- <Words.Drawer snippet={snippetProps} /> mounted inside the children
block alongside the other parts.
Visual verification:
- Drawer renders as a dedicated right column with the editor content
on the left, no layout overlap.
- DOCUMENT header + OUTLINE shows "H1 ActiveUIX Words", STATS shows
73 words / 508 chars / 1 min reading, SUGGESTIONS shows "Nothing
flagged" (sample doc has no warnings).
- Collapse button toggles open/closed via animated inline-size
transition.
Next:
- DRAWER-2: format/image/table/code/list contextual panels wired to
runCommand.
- DRAWER-3: polish (mobile drawer-as-bottom-sheet, transitions, A/B
toggle in demo to compare classic toolbar vs companion drawer).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
default: 'Document',
|
|
|
|
|
|
format: 'Format',
|
|
|
|
|
|
image: 'Image',
|
|
|
|
|
|
table: 'Table',
|
|
|
|
|
|
code: 'Code',
|
|
|
|
|
|
list: 'List'
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
function countWordsInText(text: string): number {
|
|
|
|
|
|
const trimmed = text.trim();
|
|
|
|
|
|
return trimmed ? trimmed.split(/\s+/).length : 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
function imageBlockAtCaret(s: ProviderSnippetProps):
|
|
|
|
|
|
| {
|
|
|
|
|
|
readonly src: string;
|
|
|
|
|
|
readonly alt?: string;
|
|
|
|
|
|
readonly caption?: string;
|
|
|
|
|
|
readonly status?: 'pending' | 'error';
|
|
|
|
|
|
}
|
|
|
|
|
|
| undefined {
|
feat(words): drawer contextual panels — format/image/table/code/list (DRAWER-2)
The companion drawer now switches its body content based on where the
caret is. Five context modes wired to runCommand on the snippet
provider, all reading current state to show active highlights:
- format: B/I/U/S/code mark toggles (data-active reflects activeMarks),
align L/C/R/J (data-active reflects currentTextAlign), Add link (URL
prompt) + Remove (canUnlink-gated), Clear formatting.
- image: read-only view of src, alt (italic + "(missing)" placeholder
when empty), caption, status (pending → accent tint, error → threat
tint). Edit-image flow deferred to a follow-up (needs a new
updateImage operation; for the spike, view-only is enough to
validate the panel works).
- table: Rows +/- · Columns +/- · Row/Col header toggles · Striped /
Compact toggles · Cell tone Plain/Muted/Accent. Every toggle reads
current* state so the user sees which buttons are currently applied.
- code: Plain + TS/JS/CSS/HTML/JSON/Bash/Py language chips,
data-active reflects currentCodeLanguage.
- list: Bullet/Numbered/Check kind chips · ⇤ Outdent / ⇥ Indent.
All buttons use onmousedown preventDefault so the editor selection
isn't dropped when clicking a drawer action (would otherwise lose the
target paragraph / table cell / list item).
CSS adds a generic .data-words-drawer-action chip (with hover, active,
ghost, full, risk tones) + readonly-text style with pending/error
tints. Reused across all panels, so adding new panels later means
markup-only, no CSS extension.
Verified in browser:
- Click table cell → drawer shows TABLE panel with Striped active
(matches sample's striped=true) and Plain tone active (matches
default cell tone).
- Click code block → drawer shows CODE panel with TS chip active
(matches sample's language='ts').
- Click list item → drawer shows LIST panel with kind + indent
actions.
Concept A+D validated visually: ONE surface replaces what would
otherwise be bubble menu + slash menu + heading picker + language
picker + table popover + image controls + link editor. The contextual
switch happens automatically; the user just moves the caret.
DRAWER-3 (polish): mobile bottom-sheet layout, A/B toggle in demo
controls, image-edit flow — deferred to next session if needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
const blockIndex = s.selection?.anchor.path[0];
|
|
|
|
|
|
if (blockIndex === undefined) return undefined;
|
|
|
|
|
|
const block = s.document.children[blockIndex];
|
|
|
|
|
|
if (!block || block.type !== 'image') return undefined;
|
|
|
|
|
|
return block;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const CODE_LANGUAGES = [
|
|
|
|
|
|
{ id: '', label: 'Plain' },
|
|
|
|
|
|
{ id: 'ts', label: 'TS' },
|
|
|
|
|
|
{ id: 'js', label: 'JS' },
|
|
|
|
|
|
{ id: 'css', label: 'CSS' },
|
|
|
|
|
|
{ id: 'html', label: 'HTML' },
|
|
|
|
|
|
{ id: 'json', label: 'JSON' },
|
|
|
|
|
|
{ id: 'bash', label: 'Bash' },
|
|
|
|
|
|
{ id: 'python', label: 'Py' }
|
|
|
|
|
|
] as const;
|
feat(words): companion drawer right-rail (concept A+D) — shell + default panel (DRAWER-1)
A radical disposition shift: instead of N fragmented popovers (bubble
menu + slash menu + link editor + heading picker + language picker +
table popover + image controls) we mount ONE contextual right-rail
that changes its panel based on what the user is editing.
Spike-1 surface (this commit) ships:
- New eidos component <Words.Drawer> (words-drawer.svelte) — a
collapsible right sidebar with mode detection derived from snippet
props (currentBlock + selection). 6 modes mapped: default | format
| image | table | code | list.
- Default panel (visible when no contextual mode active):
- OUTLINE — flat list of h1/h2/h3 headings, indented by level
- STATS — word count + char count + reading time (200wpm)
- SUGGESTIONS — writer-coaching warnings derived from the document
model: missing alt text, long paragraphs (>600 chars), heading
skip patterns (h3 without h2 above). Empty state shows "Nothing
flagged" in affirm color.
- Contextual panels (format/image/table/code/list) render a
placeholder "coming soon" message — implemented in DRAWER-2.
- Eidos CSS:
- Provider host gains a grid-template-areas layout when a drawer is
present: 'toolbar drawer / content drawer / status drawer'.
Existing children (toolbar/content/placeholder/status) get
explicit grid-area assignments.
- Drawer collapses to 2.25rem (just the chevron) when closed.
16rem default open.
- Outline items, stats grid, warning list each have their own
discrete styling.
Demo (+page.svelte):
- New `showDrawer` state (default true).
- Children snippet refactored to capture full snippetProps via @const
destructure (kept selectedLink working).
- <Words.Drawer snippet={snippetProps} /> mounted inside the children
block alongside the other parts.
Visual verification:
- Drawer renders as a dedicated right column with the editor content
on the left, no layout overlap.
- DOCUMENT header + OUTLINE shows "H1 ActiveUIX Words", STATS shows
73 words / 508 chars / 1 min reading, SUGGESTIONS shows "Nothing
flagged" (sample doc has no warnings).
- Collapse button toggles open/closed via animated inline-size
transition.
Next:
- DRAWER-2: format/image/table/code/list contextual panels wired to
runCommand.
- DRAWER-3: polish (mobile drawer-as-bottom-sheet, transitions, A/B
toggle in demo to compare classic toolbar vs companion drawer).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
</script>
|
|
|
|
|
|
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
<Words.Drawer bind:open {defaultOpen} {disabled} {...rest}>
|
|
|
|
|
|
{#snippet children(d: DrawerSnippetProps)}
|
|
|
|
|
|
{@const { mode, snippet, toggle } = d}
|
|
|
|
|
|
{@const wordCount = countWordsInText(snippet.plainText)}
|
|
|
|
|
|
{@const charCount = snippet.plainText.length}
|
|
|
|
|
|
{@const readingTimeMin = Math.max(1, Math.round(wordCount / 200))}
|
|
|
|
|
|
|
|
|
|
|
|
<header data-words-drawer-header>
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
data-words-drawer-collapse
|
|
|
|
|
|
aria-label={d.open ? 'Collapse drawer' : 'Expand drawer'}
|
|
|
|
|
|
aria-expanded={d.open}
|
|
|
|
|
|
onclick={(e) => toggle(e.currentTarget as HTMLElement)}
|
|
|
|
|
|
>
|
|
|
|
|
|
{#if d.open}
|
|
|
|
|
|
<ChevronRight size="sm" decorative />
|
|
|
|
|
|
{:else}
|
|
|
|
|
|
<ChevronLeft size="sm" decorative />
|
|
|
|
|
|
{/if}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
{#if d.open}
|
|
|
|
|
|
<span data-words-drawer-title>{MODE_LABELS[mode]}</span>
|
feat(words): companion drawer right-rail (concept A+D) — shell + default panel (DRAWER-1)
A radical disposition shift: instead of N fragmented popovers (bubble
menu + slash menu + link editor + heading picker + language picker +
table popover + image controls) we mount ONE contextual right-rail
that changes its panel based on what the user is editing.
Spike-1 surface (this commit) ships:
- New eidos component <Words.Drawer> (words-drawer.svelte) — a
collapsible right sidebar with mode detection derived from snippet
props (currentBlock + selection). 6 modes mapped: default | format
| image | table | code | list.
- Default panel (visible when no contextual mode active):
- OUTLINE — flat list of h1/h2/h3 headings, indented by level
- STATS — word count + char count + reading time (200wpm)
- SUGGESTIONS — writer-coaching warnings derived from the document
model: missing alt text, long paragraphs (>600 chars), heading
skip patterns (h3 without h2 above). Empty state shows "Nothing
flagged" in affirm color.
- Contextual panels (format/image/table/code/list) render a
placeholder "coming soon" message — implemented in DRAWER-2.
- Eidos CSS:
- Provider host gains a grid-template-areas layout when a drawer is
present: 'toolbar drawer / content drawer / status drawer'.
Existing children (toolbar/content/placeholder/status) get
explicit grid-area assignments.
- Drawer collapses to 2.25rem (just the chevron) when closed.
16rem default open.
- Outline items, stats grid, warning list each have their own
discrete styling.
Demo (+page.svelte):
- New `showDrawer` state (default true).
- Children snippet refactored to capture full snippetProps via @const
destructure (kept selectedLink working).
- <Words.Drawer snippet={snippetProps} /> mounted inside the children
block alongside the other parts.
Visual verification:
- Drawer renders as a dedicated right column with the editor content
on the left, no layout overlap.
- DOCUMENT header + OUTLINE shows "H1 ActiveUIX Words", STATS shows
73 words / 508 chars / 1 min reading, SUGGESTIONS shows "Nothing
flagged" (sample doc has no warnings).
- Collapse button toggles open/closed via animated inline-size
transition.
Next:
- DRAWER-2: format/image/table/code/list contextual panels wired to
runCommand.
- DRAWER-3: polish (mobile drawer-as-bottom-sheet, transitions, A/B
toggle in demo to compare classic toolbar vs companion drawer).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
{/if}
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
</header>
|
feat(words): companion drawer right-rail (concept A+D) — shell + default panel (DRAWER-1)
A radical disposition shift: instead of N fragmented popovers (bubble
menu + slash menu + link editor + heading picker + language picker +
table popover + image controls) we mount ONE contextual right-rail
that changes its panel based on what the user is editing.
Spike-1 surface (this commit) ships:
- New eidos component <Words.Drawer> (words-drawer.svelte) — a
collapsible right sidebar with mode detection derived from snippet
props (currentBlock + selection). 6 modes mapped: default | format
| image | table | code | list.
- Default panel (visible when no contextual mode active):
- OUTLINE — flat list of h1/h2/h3 headings, indented by level
- STATS — word count + char count + reading time (200wpm)
- SUGGESTIONS — writer-coaching warnings derived from the document
model: missing alt text, long paragraphs (>600 chars), heading
skip patterns (h3 without h2 above). Empty state shows "Nothing
flagged" in affirm color.
- Contextual panels (format/image/table/code/list) render a
placeholder "coming soon" message — implemented in DRAWER-2.
- Eidos CSS:
- Provider host gains a grid-template-areas layout when a drawer is
present: 'toolbar drawer / content drawer / status drawer'.
Existing children (toolbar/content/placeholder/status) get
explicit grid-area assignments.
- Drawer collapses to 2.25rem (just the chevron) when closed.
16rem default open.
- Outline items, stats grid, warning list each have their own
discrete styling.
Demo (+page.svelte):
- New `showDrawer` state (default true).
- Children snippet refactored to capture full snippetProps via @const
destructure (kept selectedLink working).
- <Words.Drawer snippet={snippetProps} /> mounted inside the children
block alongside the other parts.
Visual verification:
- Drawer renders as a dedicated right column with the editor content
on the left, no layout overlap.
- DOCUMENT header + OUTLINE shows "H1 ActiveUIX Words", STATS shows
73 words / 508 chars / 1 min reading, SUGGESTIONS shows "Nothing
flagged" (sample doc has no warnings).
- Collapse button toggles open/closed via animated inline-size
transition.
Next:
- DRAWER-2: format/image/table/code/list contextual panels wired to
runCommand.
- DRAWER-3: polish (mobile drawer-as-bottom-sheet, transitions, A/B
toggle in demo to compare classic toolbar vs companion drawer).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
{#if d.open}
|
|
|
|
|
|
<div data-words-drawer-body>
|
|
|
|
|
|
{#if customChildren}
|
|
|
|
|
|
{@render customChildren(d)}
|
|
|
|
|
|
{:else if mode === 'default'}
|
|
|
|
|
|
{@const outline = snippet.document.children
|
|
|
|
|
|
.map((block, blockIndex) => ({ block, blockIndex }))
|
|
|
|
|
|
.filter(({ block }) => block.type === 'heading')
|
|
|
|
|
|
.map(({ block, blockIndex }) => ({
|
|
|
|
|
|
blockIndex,
|
|
|
|
|
|
level: block.type === 'heading' ? block.level : 1,
|
|
|
|
|
|
text:
|
|
|
|
|
|
block.type === 'heading'
|
|
|
|
|
|
? block.children
|
|
|
|
|
|
.map((i) => (i.type === 'text' ? i.text : ''))
|
|
|
|
|
|
.join('')
|
|
|
|
|
|
: ''
|
|
|
|
|
|
}))
|
|
|
|
|
|
.filter((e) => e.text.trim())}
|
|
|
|
|
|
{@const warnings = collectWarnings(snippet)}
|
feat(words): companion drawer right-rail (concept A+D) — shell + default panel (DRAWER-1)
A radical disposition shift: instead of N fragmented popovers (bubble
menu + slash menu + link editor + heading picker + language picker +
table popover + image controls) we mount ONE contextual right-rail
that changes its panel based on what the user is editing.
Spike-1 surface (this commit) ships:
- New eidos component <Words.Drawer> (words-drawer.svelte) — a
collapsible right sidebar with mode detection derived from snippet
props (currentBlock + selection). 6 modes mapped: default | format
| image | table | code | list.
- Default panel (visible when no contextual mode active):
- OUTLINE — flat list of h1/h2/h3 headings, indented by level
- STATS — word count + char count + reading time (200wpm)
- SUGGESTIONS — writer-coaching warnings derived from the document
model: missing alt text, long paragraphs (>600 chars), heading
skip patterns (h3 without h2 above). Empty state shows "Nothing
flagged" in affirm color.
- Contextual panels (format/image/table/code/list) render a
placeholder "coming soon" message — implemented in DRAWER-2.
- Eidos CSS:
- Provider host gains a grid-template-areas layout when a drawer is
present: 'toolbar drawer / content drawer / status drawer'.
Existing children (toolbar/content/placeholder/status) get
explicit grid-area assignments.
- Drawer collapses to 2.25rem (just the chevron) when closed.
16rem default open.
- Outline items, stats grid, warning list each have their own
discrete styling.
Demo (+page.svelte):
- New `showDrawer` state (default true).
- Children snippet refactored to capture full snippetProps via @const
destructure (kept selectedLink working).
- <Words.Drawer snippet={snippetProps} /> mounted inside the children
block alongside the other parts.
Visual verification:
- Drawer renders as a dedicated right column with the editor content
on the left, no layout overlap.
- DOCUMENT header + OUTLINE shows "H1 ActiveUIX Words", STATS shows
73 words / 508 chars / 1 min reading, SUGGESTIONS shows "Nothing
flagged" (sample doc has no warnings).
- Collapse button toggles open/closed via animated inline-size
transition.
Next:
- DRAWER-2: format/image/table/code/list contextual panels wired to
runCommand.
- DRAWER-3: polish (mobile drawer-as-bottom-sheet, transitions, A/B
toggle in demo to compare classic toolbar vs companion drawer).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
<section data-words-drawer-section>
|
|
|
|
|
|
<h3 data-words-drawer-section-title>Outline</h3>
|
|
|
|
|
|
{#if outline.length === 0}
|
|
|
|
|
|
<p data-words-drawer-empty>No headings yet</p>
|
|
|
|
|
|
{:else}
|
|
|
|
|
|
<ol data-words-drawer-outline>
|
|
|
|
|
|
{#each outline as entry (entry.blockIndex)}
|
|
|
|
|
|
<li data-words-drawer-outline-item data-level={entry.level}>
|
|
|
|
|
|
<span data-words-drawer-outline-bullet>H{entry.level}</span>
|
|
|
|
|
|
<span data-words-drawer-outline-text>{entry.text}</span>
|
|
|
|
|
|
</li>
|
|
|
|
|
|
{/each}
|
|
|
|
|
|
</ol>
|
|
|
|
|
|
{/if}
|
|
|
|
|
|
</section>
|
feat(words): companion drawer right-rail (concept A+D) — shell + default panel (DRAWER-1)
A radical disposition shift: instead of N fragmented popovers (bubble
menu + slash menu + link editor + heading picker + language picker +
table popover + image controls) we mount ONE contextual right-rail
that changes its panel based on what the user is editing.
Spike-1 surface (this commit) ships:
- New eidos component <Words.Drawer> (words-drawer.svelte) — a
collapsible right sidebar with mode detection derived from snippet
props (currentBlock + selection). 6 modes mapped: default | format
| image | table | code | list.
- Default panel (visible when no contextual mode active):
- OUTLINE — flat list of h1/h2/h3 headings, indented by level
- STATS — word count + char count + reading time (200wpm)
- SUGGESTIONS — writer-coaching warnings derived from the document
model: missing alt text, long paragraphs (>600 chars), heading
skip patterns (h3 without h2 above). Empty state shows "Nothing
flagged" in affirm color.
- Contextual panels (format/image/table/code/list) render a
placeholder "coming soon" message — implemented in DRAWER-2.
- Eidos CSS:
- Provider host gains a grid-template-areas layout when a drawer is
present: 'toolbar drawer / content drawer / status drawer'.
Existing children (toolbar/content/placeholder/status) get
explicit grid-area assignments.
- Drawer collapses to 2.25rem (just the chevron) when closed.
16rem default open.
- Outline items, stats grid, warning list each have their own
discrete styling.
Demo (+page.svelte):
- New `showDrawer` state (default true).
- Children snippet refactored to capture full snippetProps via @const
destructure (kept selectedLink working).
- <Words.Drawer snippet={snippetProps} /> mounted inside the children
block alongside the other parts.
Visual verification:
- Drawer renders as a dedicated right column with the editor content
on the left, no layout overlap.
- DOCUMENT header + OUTLINE shows "H1 ActiveUIX Words", STATS shows
73 words / 508 chars / 1 min reading, SUGGESTIONS shows "Nothing
flagged" (sample doc has no warnings).
- Collapse button toggles open/closed via animated inline-size
transition.
Next:
- DRAWER-2: format/image/table/code/list contextual panels wired to
runCommand.
- DRAWER-3: polish (mobile drawer-as-bottom-sheet, transitions, A/B
toggle in demo to compare classic toolbar vs companion drawer).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
<section data-words-drawer-section>
|
|
|
|
|
|
<h3 data-words-drawer-section-title>Stats</h3>
|
|
|
|
|
|
<dl data-words-drawer-stats>
|
|
|
|
|
|
<div><dt>Words</dt><dd>{wordCount}</dd></div>
|
|
|
|
|
|
<div><dt>Chars</dt><dd>{charCount}</dd></div>
|
|
|
|
|
|
<div><dt>Reading</dt><dd>{readingTimeMin} min</dd></div>
|
|
|
|
|
|
</dl>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<section data-words-drawer-section>
|
|
|
|
|
|
<h3 data-words-drawer-section-title>
|
|
|
|
|
|
Suggestions
|
|
|
|
|
|
{#if warnings.length > 0}
|
|
|
|
|
|
<span data-words-drawer-section-count>{warnings.length}</span>
|
|
|
|
|
|
{/if}
|
|
|
|
|
|
</h3>
|
|
|
|
|
|
{#if warnings.length === 0}
|
|
|
|
|
|
<p data-words-drawer-empty data-tone="affirm">Nothing flagged</p>
|
|
|
|
|
|
{:else}
|
|
|
|
|
|
<ul data-words-drawer-warnings>
|
|
|
|
|
|
{#each warnings as w (w.id)}
|
|
|
|
|
|
<li data-severity={w.severity}>{w.message}</li>
|
|
|
|
|
|
{/each}
|
|
|
|
|
|
</ul>
|
feat(words): companion drawer right-rail (concept A+D) — shell + default panel (DRAWER-1)
A radical disposition shift: instead of N fragmented popovers (bubble
menu + slash menu + link editor + heading picker + language picker +
table popover + image controls) we mount ONE contextual right-rail
that changes its panel based on what the user is editing.
Spike-1 surface (this commit) ships:
- New eidos component <Words.Drawer> (words-drawer.svelte) — a
collapsible right sidebar with mode detection derived from snippet
props (currentBlock + selection). 6 modes mapped: default | format
| image | table | code | list.
- Default panel (visible when no contextual mode active):
- OUTLINE — flat list of h1/h2/h3 headings, indented by level
- STATS — word count + char count + reading time (200wpm)
- SUGGESTIONS — writer-coaching warnings derived from the document
model: missing alt text, long paragraphs (>600 chars), heading
skip patterns (h3 without h2 above). Empty state shows "Nothing
flagged" in affirm color.
- Contextual panels (format/image/table/code/list) render a
placeholder "coming soon" message — implemented in DRAWER-2.
- Eidos CSS:
- Provider host gains a grid-template-areas layout when a drawer is
present: 'toolbar drawer / content drawer / status drawer'.
Existing children (toolbar/content/placeholder/status) get
explicit grid-area assignments.
- Drawer collapses to 2.25rem (just the chevron) when closed.
16rem default open.
- Outline items, stats grid, warning list each have their own
discrete styling.
Demo (+page.svelte):
- New `showDrawer` state (default true).
- Children snippet refactored to capture full snippetProps via @const
destructure (kept selectedLink working).
- <Words.Drawer snippet={snippetProps} /> mounted inside the children
block alongside the other parts.
Visual verification:
- Drawer renders as a dedicated right column with the editor content
on the left, no layout overlap.
- DOCUMENT header + OUTLINE shows "H1 ActiveUIX Words", STATS shows
73 words / 508 chars / 1 min reading, SUGGESTIONS shows "Nothing
flagged" (sample doc has no warnings).
- Collapse button toggles open/closed via animated inline-size
transition.
Next:
- DRAWER-2: format/image/table/code/list contextual panels wired to
runCommand.
- DRAWER-3: polish (mobile drawer-as-bottom-sheet, transitions, A/B
toggle in demo to compare classic toolbar vs companion drawer).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
{/if}
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
</section>
|
|
|
|
|
|
{:else if mode === 'format'}
|
|
|
|
|
|
<section data-words-drawer-section>
|
|
|
|
|
|
<h3 data-words-drawer-section-title>Marks</h3>
|
|
|
|
|
|
<div data-words-drawer-actions>
|
|
|
|
|
|
{#each [
|
|
|
|
|
|
{ id: 'bold', label: 'B', title: 'Bold' },
|
|
|
|
|
|
{ id: 'italic', label: 'I', title: 'Italic' },
|
|
|
|
|
|
{ id: 'underline', label: 'U', title: 'Underline' },
|
|
|
|
|
|
{ id: 'strike', label: 'S', title: 'Strikethrough' },
|
|
|
|
|
|
{ id: 'code', label: '<>', title: 'Inline code' }
|
|
|
|
|
|
] as mark (mark.id)}
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
data-words-drawer-action
|
|
|
|
|
|
data-active={snippet.activeMarks.includes(mark.id) ? '' : undefined}
|
|
|
|
|
|
title={mark.title}
|
|
|
|
|
|
aria-pressed={snippet.activeMarks.includes(mark.id)}
|
|
|
|
|
|
onmousedown={(e) => e.preventDefault()}
|
|
|
|
|
|
onclick={() => snippet.runCommand(mark.id)}
|
|
|
|
|
|
>
|
|
|
|
|
|
{mark.label}
|
|
|
|
|
|
</button>
|
feat(words): companion drawer right-rail (concept A+D) — shell + default panel (DRAWER-1)
A radical disposition shift: instead of N fragmented popovers (bubble
menu + slash menu + link editor + heading picker + language picker +
table popover + image controls) we mount ONE contextual right-rail
that changes its panel based on what the user is editing.
Spike-1 surface (this commit) ships:
- New eidos component <Words.Drawer> (words-drawer.svelte) — a
collapsible right sidebar with mode detection derived from snippet
props (currentBlock + selection). 6 modes mapped: default | format
| image | table | code | list.
- Default panel (visible when no contextual mode active):
- OUTLINE — flat list of h1/h2/h3 headings, indented by level
- STATS — word count + char count + reading time (200wpm)
- SUGGESTIONS — writer-coaching warnings derived from the document
model: missing alt text, long paragraphs (>600 chars), heading
skip patterns (h3 without h2 above). Empty state shows "Nothing
flagged" in affirm color.
- Contextual panels (format/image/table/code/list) render a
placeholder "coming soon" message — implemented in DRAWER-2.
- Eidos CSS:
- Provider host gains a grid-template-areas layout when a drawer is
present: 'toolbar drawer / content drawer / status drawer'.
Existing children (toolbar/content/placeholder/status) get
explicit grid-area assignments.
- Drawer collapses to 2.25rem (just the chevron) when closed.
16rem default open.
- Outline items, stats grid, warning list each have their own
discrete styling.
Demo (+page.svelte):
- New `showDrawer` state (default true).
- Children snippet refactored to capture full snippetProps via @const
destructure (kept selectedLink working).
- <Words.Drawer snippet={snippetProps} /> mounted inside the children
block alongside the other parts.
Visual verification:
- Drawer renders as a dedicated right column with the editor content
on the left, no layout overlap.
- DOCUMENT header + OUTLINE shows "H1 ActiveUIX Words", STATS shows
73 words / 508 chars / 1 min reading, SUGGESTIONS shows "Nothing
flagged" (sample doc has no warnings).
- Collapse button toggles open/closed via animated inline-size
transition.
Next:
- DRAWER-2: format/image/table/code/list contextual panels wired to
runCommand.
- DRAWER-3: polish (mobile drawer-as-bottom-sheet, transitions, A/B
toggle in demo to compare classic toolbar vs companion drawer).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
{/each}
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
feat(words): drawer contextual panels — format/image/table/code/list (DRAWER-2)
The companion drawer now switches its body content based on where the
caret is. Five context modes wired to runCommand on the snippet
provider, all reading current state to show active highlights:
- format: B/I/U/S/code mark toggles (data-active reflects activeMarks),
align L/C/R/J (data-active reflects currentTextAlign), Add link (URL
prompt) + Remove (canUnlink-gated), Clear formatting.
- image: read-only view of src, alt (italic + "(missing)" placeholder
when empty), caption, status (pending → accent tint, error → threat
tint). Edit-image flow deferred to a follow-up (needs a new
updateImage operation; for the spike, view-only is enough to
validate the panel works).
- table: Rows +/- · Columns +/- · Row/Col header toggles · Striped /
Compact toggles · Cell tone Plain/Muted/Accent. Every toggle reads
current* state so the user sees which buttons are currently applied.
- code: Plain + TS/JS/CSS/HTML/JSON/Bash/Py language chips,
data-active reflects currentCodeLanguage.
- list: Bullet/Numbered/Check kind chips · ⇤ Outdent / ⇥ Indent.
All buttons use onmousedown preventDefault so the editor selection
isn't dropped when clicking a drawer action (would otherwise lose the
target paragraph / table cell / list item).
CSS adds a generic .data-words-drawer-action chip (with hover, active,
ghost, full, risk tones) + readonly-text style with pending/error
tints. Reused across all panels, so adding new panels later means
markup-only, no CSS extension.
Verified in browser:
- Click table cell → drawer shows TABLE panel with Striped active
(matches sample's striped=true) and Plain tone active (matches
default cell tone).
- Click code block → drawer shows CODE panel with TS chip active
(matches sample's language='ts').
- Click list item → drawer shows LIST panel with kind + indent
actions.
Concept A+D validated visually: ONE surface replaces what would
otherwise be bubble menu + slash menu + heading picker + language
picker + table popover + image controls + link editor. The contextual
switch happens automatically; the user just moves the caret.
DRAWER-3 (polish): mobile bottom-sheet layout, A/B toggle in demo
controls, image-edit flow — deferred to next session if needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
<section data-words-drawer-section>
|
|
|
|
|
|
<h3 data-words-drawer-section-title>Align</h3>
|
|
|
|
|
|
<div data-words-drawer-actions>
|
|
|
|
|
|
{#each [
|
|
|
|
|
|
{ id: 'align-left', label: '⫷', title: 'Left' },
|
|
|
|
|
|
{ id: 'align-center', label: '☰', title: 'Center' },
|
|
|
|
|
|
{ id: 'align-right', label: '⫸', title: 'Right' },
|
|
|
|
|
|
{ id: 'align-justify', label: '☷', title: 'Justify' }
|
|
|
|
|
|
] as a (a.id)}
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
data-words-drawer-action
|
|
|
|
|
|
data-active={snippet.currentTextAlign === a.id.replace('align-', '') ? '' : undefined}
|
|
|
|
|
|
title={a.title}
|
|
|
|
|
|
onmousedown={(e) => e.preventDefault()}
|
|
|
|
|
|
onclick={() => snippet.runCommand(a.id)}
|
|
|
|
|
|
>
|
|
|
|
|
|
{a.label}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
{/each}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<section data-words-drawer-section>
|
|
|
|
|
|
<h3 data-words-drawer-section-title>Link</h3>
|
|
|
|
|
|
<div data-words-drawer-actions>
|
feat(words): drawer contextual panels — format/image/table/code/list (DRAWER-2)
The companion drawer now switches its body content based on where the
caret is. Five context modes wired to runCommand on the snippet
provider, all reading current state to show active highlights:
- format: B/I/U/S/code mark toggles (data-active reflects activeMarks),
align L/C/R/J (data-active reflects currentTextAlign), Add link (URL
prompt) + Remove (canUnlink-gated), Clear formatting.
- image: read-only view of src, alt (italic + "(missing)" placeholder
when empty), caption, status (pending → accent tint, error → threat
tint). Edit-image flow deferred to a follow-up (needs a new
updateImage operation; for the spike, view-only is enough to
validate the panel works).
- table: Rows +/- · Columns +/- · Row/Col header toggles · Striped /
Compact toggles · Cell tone Plain/Muted/Accent. Every toggle reads
current* state so the user sees which buttons are currently applied.
- code: Plain + TS/JS/CSS/HTML/JSON/Bash/Py language chips,
data-active reflects currentCodeLanguage.
- list: Bullet/Numbered/Check kind chips · ⇤ Outdent / ⇥ Indent.
All buttons use onmousedown preventDefault so the editor selection
isn't dropped when clicking a drawer action (would otherwise lose the
target paragraph / table cell / list item).
CSS adds a generic .data-words-drawer-action chip (with hover, active,
ghost, full, risk tones) + readonly-text style with pending/error
tints. Reused across all panels, so adding new panels later means
markup-only, no CSS extension.
Verified in browser:
- Click table cell → drawer shows TABLE panel with Striped active
(matches sample's striped=true) and Plain tone active (matches
default cell tone).
- Click code block → drawer shows CODE panel with TS chip active
(matches sample's language='ts').
- Click list item → drawer shows LIST panel with kind + indent
actions.
Concept A+D validated visually: ONE surface replaces what would
otherwise be bubble menu + slash menu + heading picker + language
picker + table popover + image controls + link editor. The contextual
switch happens automatically; the user just moves the caret.
DRAWER-3 (polish): mobile bottom-sheet layout, A/B toggle in demo
controls, image-edit flow — deferred to next session if needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
data-words-drawer-action
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
data-tone="full"
|
feat(words): drawer contextual panels — format/image/table/code/list (DRAWER-2)
The companion drawer now switches its body content based on where the
caret is. Five context modes wired to runCommand on the snippet
provider, all reading current state to show active highlights:
- format: B/I/U/S/code mark toggles (data-active reflects activeMarks),
align L/C/R/J (data-active reflects currentTextAlign), Add link (URL
prompt) + Remove (canUnlink-gated), Clear formatting.
- image: read-only view of src, alt (italic + "(missing)" placeholder
when empty), caption, status (pending → accent tint, error → threat
tint). Edit-image flow deferred to a follow-up (needs a new
updateImage operation; for the spike, view-only is enough to
validate the panel works).
- table: Rows +/- · Columns +/- · Row/Col header toggles · Striped /
Compact toggles · Cell tone Plain/Muted/Accent. Every toggle reads
current* state so the user sees which buttons are currently applied.
- code: Plain + TS/JS/CSS/HTML/JSON/Bash/Py language chips,
data-active reflects currentCodeLanguage.
- list: Bullet/Numbered/Check kind chips · ⇤ Outdent / ⇥ Indent.
All buttons use onmousedown preventDefault so the editor selection
isn't dropped when clicking a drawer action (would otherwise lose the
target paragraph / table cell / list item).
CSS adds a generic .data-words-drawer-action chip (with hover, active,
ghost, full, risk tones) + readonly-text style with pending/error
tints. Reused across all panels, so adding new panels later means
markup-only, no CSS extension.
Verified in browser:
- Click table cell → drawer shows TABLE panel with Striped active
(matches sample's striped=true) and Plain tone active (matches
default cell tone).
- Click code block → drawer shows CODE panel with TS chip active
(matches sample's language='ts').
- Click list item → drawer shows LIST panel with kind + indent
actions.
Concept A+D validated visually: ONE surface replaces what would
otherwise be bubble menu + slash menu + heading picker + language
picker + table popover + image controls + link editor. The contextual
switch happens automatically; the user just moves the caret.
DRAWER-3 (polish): mobile bottom-sheet layout, A/B toggle in demo
controls, image-edit flow — deferred to next session if needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
onmousedown={(e) => e.preventDefault()}
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
onclick={() => {
|
|
|
|
|
|
const url = window.prompt('Link URL:');
|
|
|
|
|
|
if (url) snippet.insertLink(url);
|
|
|
|
|
|
}}
|
feat(words): drawer contextual panels — format/image/table/code/list (DRAWER-2)
The companion drawer now switches its body content based on where the
caret is. Five context modes wired to runCommand on the snippet
provider, all reading current state to show active highlights:
- format: B/I/U/S/code mark toggles (data-active reflects activeMarks),
align L/C/R/J (data-active reflects currentTextAlign), Add link (URL
prompt) + Remove (canUnlink-gated), Clear formatting.
- image: read-only view of src, alt (italic + "(missing)" placeholder
when empty), caption, status (pending → accent tint, error → threat
tint). Edit-image flow deferred to a follow-up (needs a new
updateImage operation; for the spike, view-only is enough to
validate the panel works).
- table: Rows +/- · Columns +/- · Row/Col header toggles · Striped /
Compact toggles · Cell tone Plain/Muted/Accent. Every toggle reads
current* state so the user sees which buttons are currently applied.
- code: Plain + TS/JS/CSS/HTML/JSON/Bash/Py language chips,
data-active reflects currentCodeLanguage.
- list: Bullet/Numbered/Check kind chips · ⇤ Outdent / ⇥ Indent.
All buttons use onmousedown preventDefault so the editor selection
isn't dropped when clicking a drawer action (would otherwise lose the
target paragraph / table cell / list item).
CSS adds a generic .data-words-drawer-action chip (with hover, active,
ghost, full, risk tones) + readonly-text style with pending/error
tints. Reused across all panels, so adding new panels later means
markup-only, no CSS extension.
Verified in browser:
- Click table cell → drawer shows TABLE panel with Striped active
(matches sample's striped=true) and Plain tone active (matches
default cell tone).
- Click code block → drawer shows CODE panel with TS chip active
(matches sample's language='ts').
- Click list item → drawer shows LIST panel with kind + indent
actions.
Concept A+D validated visually: ONE surface replaces what would
otherwise be bubble menu + slash menu + heading picker + language
picker + table popover + image controls + link editor. The contextual
switch happens automatically; the user just moves the caret.
DRAWER-3 (polish): mobile bottom-sheet layout, A/B toggle in demo
controls, image-edit flow — deferred to next session if needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
>
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
Add link
|
feat(words): drawer contextual panels — format/image/table/code/list (DRAWER-2)
The companion drawer now switches its body content based on where the
caret is. Five context modes wired to runCommand on the snippet
provider, all reading current state to show active highlights:
- format: B/I/U/S/code mark toggles (data-active reflects activeMarks),
align L/C/R/J (data-active reflects currentTextAlign), Add link (URL
prompt) + Remove (canUnlink-gated), Clear formatting.
- image: read-only view of src, alt (italic + "(missing)" placeholder
when empty), caption, status (pending → accent tint, error → threat
tint). Edit-image flow deferred to a follow-up (needs a new
updateImage operation; for the spike, view-only is enough to
validate the panel works).
- table: Rows +/- · Columns +/- · Row/Col header toggles · Striped /
Compact toggles · Cell tone Plain/Muted/Accent. Every toggle reads
current* state so the user sees which buttons are currently applied.
- code: Plain + TS/JS/CSS/HTML/JSON/Bash/Py language chips,
data-active reflects currentCodeLanguage.
- list: Bullet/Numbered/Check kind chips · ⇤ Outdent / ⇥ Indent.
All buttons use onmousedown preventDefault so the editor selection
isn't dropped when clicking a drawer action (would otherwise lose the
target paragraph / table cell / list item).
CSS adds a generic .data-words-drawer-action chip (with hover, active,
ghost, full, risk tones) + readonly-text style with pending/error
tints. Reused across all panels, so adding new panels later means
markup-only, no CSS extension.
Verified in browser:
- Click table cell → drawer shows TABLE panel with Striped active
(matches sample's striped=true) and Plain tone active (matches
default cell tone).
- Click code block → drawer shows CODE panel with TS chip active
(matches sample's language='ts').
- Click list item → drawer shows LIST panel with kind + indent
actions.
Concept A+D validated visually: ONE surface replaces what would
otherwise be bubble menu + slash menu + heading picker + language
picker + table popover + image controls + link editor. The contextual
switch happens automatically; the user just moves the caret.
DRAWER-3 (polish): mobile bottom-sheet layout, A/B toggle in demo
controls, image-edit flow — deferred to next session if needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
</button>
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
{#if snippet.canUnlink}
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
data-words-drawer-action
|
|
|
|
|
|
onmousedown={(e) => e.preventDefault()}
|
|
|
|
|
|
onclick={() => snippet.unlink()}
|
|
|
|
|
|
>
|
|
|
|
|
|
Remove
|
|
|
|
|
|
</button>
|
|
|
|
|
|
{/if}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
feat(words): drawer contextual panels — format/image/table/code/list (DRAWER-2)
The companion drawer now switches its body content based on where the
caret is. Five context modes wired to runCommand on the snippet
provider, all reading current state to show active highlights:
- format: B/I/U/S/code mark toggles (data-active reflects activeMarks),
align L/C/R/J (data-active reflects currentTextAlign), Add link (URL
prompt) + Remove (canUnlink-gated), Clear formatting.
- image: read-only view of src, alt (italic + "(missing)" placeholder
when empty), caption, status (pending → accent tint, error → threat
tint). Edit-image flow deferred to a follow-up (needs a new
updateImage operation; for the spike, view-only is enough to
validate the panel works).
- table: Rows +/- · Columns +/- · Row/Col header toggles · Striped /
Compact toggles · Cell tone Plain/Muted/Accent. Every toggle reads
current* state so the user sees which buttons are currently applied.
- code: Plain + TS/JS/CSS/HTML/JSON/Bash/Py language chips,
data-active reflects currentCodeLanguage.
- list: Bullet/Numbered/Check kind chips · ⇤ Outdent / ⇥ Indent.
All buttons use onmousedown preventDefault so the editor selection
isn't dropped when clicking a drawer action (would otherwise lose the
target paragraph / table cell / list item).
CSS adds a generic .data-words-drawer-action chip (with hover, active,
ghost, full, risk tones) + readonly-text style with pending/error
tints. Reused across all panels, so adding new panels later means
markup-only, no CSS extension.
Verified in browser:
- Click table cell → drawer shows TABLE panel with Striped active
(matches sample's striped=true) and Plain tone active (matches
default cell tone).
- Click code block → drawer shows CODE panel with TS chip active
(matches sample's language='ts').
- Click list item → drawer shows LIST panel with kind + indent
actions.
Concept A+D validated visually: ONE surface replaces what would
otherwise be bubble menu + slash menu + heading picker + language
picker + table popover + image controls + link editor. The contextual
switch happens automatically; the user just moves the caret.
DRAWER-3 (polish): mobile bottom-sheet layout, A/B toggle in demo
controls, image-edit flow — deferred to next session if needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
<section data-words-drawer-section>
|
feat(words): drawer contextual panels — format/image/table/code/list (DRAWER-2)
The companion drawer now switches its body content based on where the
caret is. Five context modes wired to runCommand on the snippet
provider, all reading current state to show active highlights:
- format: B/I/U/S/code mark toggles (data-active reflects activeMarks),
align L/C/R/J (data-active reflects currentTextAlign), Add link (URL
prompt) + Remove (canUnlink-gated), Clear formatting.
- image: read-only view of src, alt (italic + "(missing)" placeholder
when empty), caption, status (pending → accent tint, error → threat
tint). Edit-image flow deferred to a follow-up (needs a new
updateImage operation; for the spike, view-only is enough to
validate the panel works).
- table: Rows +/- · Columns +/- · Row/Col header toggles · Striped /
Compact toggles · Cell tone Plain/Muted/Accent. Every toggle reads
current* state so the user sees which buttons are currently applied.
- code: Plain + TS/JS/CSS/HTML/JSON/Bash/Py language chips,
data-active reflects currentCodeLanguage.
- list: Bullet/Numbered/Check kind chips · ⇤ Outdent / ⇥ Indent.
All buttons use onmousedown preventDefault so the editor selection
isn't dropped when clicking a drawer action (would otherwise lose the
target paragraph / table cell / list item).
CSS adds a generic .data-words-drawer-action chip (with hover, active,
ghost, full, risk tones) + readonly-text style with pending/error
tints. Reused across all panels, so adding new panels later means
markup-only, no CSS extension.
Verified in browser:
- Click table cell → drawer shows TABLE panel with Striped active
(matches sample's striped=true) and Plain tone active (matches
default cell tone).
- Click code block → drawer shows CODE panel with TS chip active
(matches sample's language='ts').
- Click list item → drawer shows LIST panel with kind + indent
actions.
Concept A+D validated visually: ONE surface replaces what would
otherwise be bubble menu + slash menu + heading picker + language
picker + table popover + image controls + link editor. The contextual
switch happens automatically; the user just moves the caret.
DRAWER-3 (polish): mobile bottom-sheet layout, A/B toggle in demo
controls, image-edit flow — deferred to next session if needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
data-words-drawer-action
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
data-tone="ghost"
|
feat(words): drawer contextual panels — format/image/table/code/list (DRAWER-2)
The companion drawer now switches its body content based on where the
caret is. Five context modes wired to runCommand on the snippet
provider, all reading current state to show active highlights:
- format: B/I/U/S/code mark toggles (data-active reflects activeMarks),
align L/C/R/J (data-active reflects currentTextAlign), Add link (URL
prompt) + Remove (canUnlink-gated), Clear formatting.
- image: read-only view of src, alt (italic + "(missing)" placeholder
when empty), caption, status (pending → accent tint, error → threat
tint). Edit-image flow deferred to a follow-up (needs a new
updateImage operation; for the spike, view-only is enough to
validate the panel works).
- table: Rows +/- · Columns +/- · Row/Col header toggles · Striped /
Compact toggles · Cell tone Plain/Muted/Accent. Every toggle reads
current* state so the user sees which buttons are currently applied.
- code: Plain + TS/JS/CSS/HTML/JSON/Bash/Py language chips,
data-active reflects currentCodeLanguage.
- list: Bullet/Numbered/Check kind chips · ⇤ Outdent / ⇥ Indent.
All buttons use onmousedown preventDefault so the editor selection
isn't dropped when clicking a drawer action (would otherwise lose the
target paragraph / table cell / list item).
CSS adds a generic .data-words-drawer-action chip (with hover, active,
ghost, full, risk tones) + readonly-text style with pending/error
tints. Reused across all panels, so adding new panels later means
markup-only, no CSS extension.
Verified in browser:
- Click table cell → drawer shows TABLE panel with Striped active
(matches sample's striped=true) and Plain tone active (matches
default cell tone).
- Click code block → drawer shows CODE panel with TS chip active
(matches sample's language='ts').
- Click list item → drawer shows LIST panel with kind + indent
actions.
Concept A+D validated visually: ONE surface replaces what would
otherwise be bubble menu + slash menu + heading picker + language
picker + table popover + image controls + link editor. The contextual
switch happens automatically; the user just moves the caret.
DRAWER-3 (polish): mobile bottom-sheet layout, A/B toggle in demo
controls, image-edit flow — deferred to next session if needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
onmousedown={(e) => e.preventDefault()}
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
onclick={() => snippet.runCommand('clear-formatting')}
|
feat(words): drawer contextual panels — format/image/table/code/list (DRAWER-2)
The companion drawer now switches its body content based on where the
caret is. Five context modes wired to runCommand on the snippet
provider, all reading current state to show active highlights:
- format: B/I/U/S/code mark toggles (data-active reflects activeMarks),
align L/C/R/J (data-active reflects currentTextAlign), Add link (URL
prompt) + Remove (canUnlink-gated), Clear formatting.
- image: read-only view of src, alt (italic + "(missing)" placeholder
when empty), caption, status (pending → accent tint, error → threat
tint). Edit-image flow deferred to a follow-up (needs a new
updateImage operation; for the spike, view-only is enough to
validate the panel works).
- table: Rows +/- · Columns +/- · Row/Col header toggles · Striped /
Compact toggles · Cell tone Plain/Muted/Accent. Every toggle reads
current* state so the user sees which buttons are currently applied.
- code: Plain + TS/JS/CSS/HTML/JSON/Bash/Py language chips,
data-active reflects currentCodeLanguage.
- list: Bullet/Numbered/Check kind chips · ⇤ Outdent / ⇥ Indent.
All buttons use onmousedown preventDefault so the editor selection
isn't dropped when clicking a drawer action (would otherwise lose the
target paragraph / table cell / list item).
CSS adds a generic .data-words-drawer-action chip (with hover, active,
ghost, full, risk tones) + readonly-text style with pending/error
tints. Reused across all panels, so adding new panels later means
markup-only, no CSS extension.
Verified in browser:
- Click table cell → drawer shows TABLE panel with Striped active
(matches sample's striped=true) and Plain tone active (matches
default cell tone).
- Click code block → drawer shows CODE panel with TS chip active
(matches sample's language='ts').
- Click list item → drawer shows LIST panel with kind + indent
actions.
Concept A+D validated visually: ONE surface replaces what would
otherwise be bubble menu + slash menu + heading picker + language
picker + table popover + image controls + link editor. The contextual
switch happens automatically; the user just moves the caret.
DRAWER-3 (polish): mobile bottom-sheet layout, A/B toggle in demo
controls, image-edit flow — deferred to next session if needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
>
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
Clear formatting
|
feat(words): drawer contextual panels — format/image/table/code/list (DRAWER-2)
The companion drawer now switches its body content based on where the
caret is. Five context modes wired to runCommand on the snippet
provider, all reading current state to show active highlights:
- format: B/I/U/S/code mark toggles (data-active reflects activeMarks),
align L/C/R/J (data-active reflects currentTextAlign), Add link (URL
prompt) + Remove (canUnlink-gated), Clear formatting.
- image: read-only view of src, alt (italic + "(missing)" placeholder
when empty), caption, status (pending → accent tint, error → threat
tint). Edit-image flow deferred to a follow-up (needs a new
updateImage operation; for the spike, view-only is enough to
validate the panel works).
- table: Rows +/- · Columns +/- · Row/Col header toggles · Striped /
Compact toggles · Cell tone Plain/Muted/Accent. Every toggle reads
current* state so the user sees which buttons are currently applied.
- code: Plain + TS/JS/CSS/HTML/JSON/Bash/Py language chips,
data-active reflects currentCodeLanguage.
- list: Bullet/Numbered/Check kind chips · ⇤ Outdent / ⇥ Indent.
All buttons use onmousedown preventDefault so the editor selection
isn't dropped when clicking a drawer action (would otherwise lose the
target paragraph / table cell / list item).
CSS adds a generic .data-words-drawer-action chip (with hover, active,
ghost, full, risk tones) + readonly-text style with pending/error
tints. Reused across all panels, so adding new panels later means
markup-only, no CSS extension.
Verified in browser:
- Click table cell → drawer shows TABLE panel with Striped active
(matches sample's striped=true) and Plain tone active (matches
default cell tone).
- Click code block → drawer shows CODE panel with TS chip active
(matches sample's language='ts').
- Click list item → drawer shows LIST panel with kind + indent
actions.
Concept A+D validated visually: ONE surface replaces what would
otherwise be bubble menu + slash menu + heading picker + language
picker + table popover + image controls + link editor. The contextual
switch happens automatically; the user just moves the caret.
DRAWER-3 (polish): mobile bottom-sheet layout, A/B toggle in demo
controls, image-edit flow — deferred to next session if needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
</button>
|
|
|
|
|
|
</section>
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
{:else if mode === 'image'}
|
|
|
|
|
|
{@const block = imageBlockAtCaret(snippet)}
|
|
|
|
|
|
{#if block}
|
feat(words): drawer contextual panels — format/image/table/code/list (DRAWER-2)
The companion drawer now switches its body content based on where the
caret is. Five context modes wired to runCommand on the snippet
provider, all reading current state to show active highlights:
- format: B/I/U/S/code mark toggles (data-active reflects activeMarks),
align L/C/R/J (data-active reflects currentTextAlign), Add link (URL
prompt) + Remove (canUnlink-gated), Clear formatting.
- image: read-only view of src, alt (italic + "(missing)" placeholder
when empty), caption, status (pending → accent tint, error → threat
tint). Edit-image flow deferred to a follow-up (needs a new
updateImage operation; for the spike, view-only is enough to
validate the panel works).
- table: Rows +/- · Columns +/- · Row/Col header toggles · Striped /
Compact toggles · Cell tone Plain/Muted/Accent. Every toggle reads
current* state so the user sees which buttons are currently applied.
- code: Plain + TS/JS/CSS/HTML/JSON/Bash/Py language chips,
data-active reflects currentCodeLanguage.
- list: Bullet/Numbered/Check kind chips · ⇤ Outdent / ⇥ Indent.
All buttons use onmousedown preventDefault so the editor selection
isn't dropped when clicking a drawer action (would otherwise lose the
target paragraph / table cell / list item).
CSS adds a generic .data-words-drawer-action chip (with hover, active,
ghost, full, risk tones) + readonly-text style with pending/error
tints. Reused across all panels, so adding new panels later means
markup-only, no CSS extension.
Verified in browser:
- Click table cell → drawer shows TABLE panel with Striped active
(matches sample's striped=true) and Plain tone active (matches
default cell tone).
- Click code block → drawer shows CODE panel with TS chip active
(matches sample's language='ts').
- Click list item → drawer shows LIST panel with kind + indent
actions.
Concept A+D validated visually: ONE surface replaces what would
otherwise be bubble menu + slash menu + heading picker + language
picker + table popover + image controls + link editor. The contextual
switch happens automatically; the user just moves the caret.
DRAWER-3 (polish): mobile bottom-sheet layout, A/B toggle in demo
controls, image-edit flow — deferred to next session if needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
<section data-words-drawer-section>
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
<h3 data-words-drawer-section-title>Source</h3>
|
|
|
|
|
|
<p data-words-drawer-readonly-text>{block.src}</p>
|
feat(words): drawer contextual panels — format/image/table/code/list (DRAWER-2)
The companion drawer now switches its body content based on where the
caret is. Five context modes wired to runCommand on the snippet
provider, all reading current state to show active highlights:
- format: B/I/U/S/code mark toggles (data-active reflects activeMarks),
align L/C/R/J (data-active reflects currentTextAlign), Add link (URL
prompt) + Remove (canUnlink-gated), Clear formatting.
- image: read-only view of src, alt (italic + "(missing)" placeholder
when empty), caption, status (pending → accent tint, error → threat
tint). Edit-image flow deferred to a follow-up (needs a new
updateImage operation; for the spike, view-only is enough to
validate the panel works).
- table: Rows +/- · Columns +/- · Row/Col header toggles · Striped /
Compact toggles · Cell tone Plain/Muted/Accent. Every toggle reads
current* state so the user sees which buttons are currently applied.
- code: Plain + TS/JS/CSS/HTML/JSON/Bash/Py language chips,
data-active reflects currentCodeLanguage.
- list: Bullet/Numbered/Check kind chips · ⇤ Outdent / ⇥ Indent.
All buttons use onmousedown preventDefault so the editor selection
isn't dropped when clicking a drawer action (would otherwise lose the
target paragraph / table cell / list item).
CSS adds a generic .data-words-drawer-action chip (with hover, active,
ghost, full, risk tones) + readonly-text style with pending/error
tints. Reused across all panels, so adding new panels later means
markup-only, no CSS extension.
Verified in browser:
- Click table cell → drawer shows TABLE panel with Striped active
(matches sample's striped=true) and Plain tone active (matches
default cell tone).
- Click code block → drawer shows CODE panel with TS chip active
(matches sample's language='ts').
- Click list item → drawer shows LIST panel with kind + indent
actions.
Concept A+D validated visually: ONE surface replaces what would
otherwise be bubble menu + slash menu + heading picker + language
picker + table popover + image controls + link editor. The contextual
switch happens automatically; the user just moves the caret.
DRAWER-3 (polish): mobile bottom-sheet layout, A/B toggle in demo
controls, image-edit flow — deferred to next session if needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
</section>
|
|
|
|
|
|
<section data-words-drawer-section>
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
<h3 data-words-drawer-section-title>Alt text</h3>
|
|
|
|
|
|
<p data-words-drawer-readonly-text data-empty={!block.alt}>
|
|
|
|
|
|
{block.alt || '(missing)'}
|
feat(words): drawer contextual panels — format/image/table/code/list (DRAWER-2)
The companion drawer now switches its body content based on where the
caret is. Five context modes wired to runCommand on the snippet
provider, all reading current state to show active highlights:
- format: B/I/U/S/code mark toggles (data-active reflects activeMarks),
align L/C/R/J (data-active reflects currentTextAlign), Add link (URL
prompt) + Remove (canUnlink-gated), Clear formatting.
- image: read-only view of src, alt (italic + "(missing)" placeholder
when empty), caption, status (pending → accent tint, error → threat
tint). Edit-image flow deferred to a follow-up (needs a new
updateImage operation; for the spike, view-only is enough to
validate the panel works).
- table: Rows +/- · Columns +/- · Row/Col header toggles · Striped /
Compact toggles · Cell tone Plain/Muted/Accent. Every toggle reads
current* state so the user sees which buttons are currently applied.
- code: Plain + TS/JS/CSS/HTML/JSON/Bash/Py language chips,
data-active reflects currentCodeLanguage.
- list: Bullet/Numbered/Check kind chips · ⇤ Outdent / ⇥ Indent.
All buttons use onmousedown preventDefault so the editor selection
isn't dropped when clicking a drawer action (would otherwise lose the
target paragraph / table cell / list item).
CSS adds a generic .data-words-drawer-action chip (with hover, active,
ghost, full, risk tones) + readonly-text style with pending/error
tints. Reused across all panels, so adding new panels later means
markup-only, no CSS extension.
Verified in browser:
- Click table cell → drawer shows TABLE panel with Striped active
(matches sample's striped=true) and Plain tone active (matches
default cell tone).
- Click code block → drawer shows CODE panel with TS chip active
(matches sample's language='ts').
- Click list item → drawer shows LIST panel with kind + indent
actions.
Concept A+D validated visually: ONE surface replaces what would
otherwise be bubble menu + slash menu + heading picker + language
picker + table popover + image controls + link editor. The contextual
switch happens automatically; the user just moves the caret.
DRAWER-3 (polish): mobile bottom-sheet layout, A/B toggle in demo
controls, image-edit flow — deferred to next session if needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
</p>
|
|
|
|
|
|
</section>
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
{#if block.caption}
|
|
|
|
|
|
<section data-words-drawer-section>
|
|
|
|
|
|
<h3 data-words-drawer-section-title>Caption</h3>
|
|
|
|
|
|
<p data-words-drawer-readonly-text>{block.caption}</p>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
{/if}
|
|
|
|
|
|
{#if block.status}
|
|
|
|
|
|
<section data-words-drawer-section>
|
|
|
|
|
|
<h3 data-words-drawer-section-title>Status</h3>
|
|
|
|
|
|
<p data-words-drawer-readonly-text data-tone={block.status}>
|
|
|
|
|
|
{block.status === 'pending' ? 'Uploading…' : 'Upload failed'}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
{/if}
|
|
|
|
|
|
{:else}
|
|
|
|
|
|
<section data-words-drawer-section>
|
|
|
|
|
|
<p data-words-drawer-empty>Place caret on an image to edit it.</p>
|
|
|
|
|
|
</section>
|
feat(words): drawer contextual panels — format/image/table/code/list (DRAWER-2)
The companion drawer now switches its body content based on where the
caret is. Five context modes wired to runCommand on the snippet
provider, all reading current state to show active highlights:
- format: B/I/U/S/code mark toggles (data-active reflects activeMarks),
align L/C/R/J (data-active reflects currentTextAlign), Add link (URL
prompt) + Remove (canUnlink-gated), Clear formatting.
- image: read-only view of src, alt (italic + "(missing)" placeholder
when empty), caption, status (pending → accent tint, error → threat
tint). Edit-image flow deferred to a follow-up (needs a new
updateImage operation; for the spike, view-only is enough to
validate the panel works).
- table: Rows +/- · Columns +/- · Row/Col header toggles · Striped /
Compact toggles · Cell tone Plain/Muted/Accent. Every toggle reads
current* state so the user sees which buttons are currently applied.
- code: Plain + TS/JS/CSS/HTML/JSON/Bash/Py language chips,
data-active reflects currentCodeLanguage.
- list: Bullet/Numbered/Check kind chips · ⇤ Outdent / ⇥ Indent.
All buttons use onmousedown preventDefault so the editor selection
isn't dropped when clicking a drawer action (would otherwise lose the
target paragraph / table cell / list item).
CSS adds a generic .data-words-drawer-action chip (with hover, active,
ghost, full, risk tones) + readonly-text style with pending/error
tints. Reused across all panels, so adding new panels later means
markup-only, no CSS extension.
Verified in browser:
- Click table cell → drawer shows TABLE panel with Striped active
(matches sample's striped=true) and Plain tone active (matches
default cell tone).
- Click code block → drawer shows CODE panel with TS chip active
(matches sample's language='ts').
- Click list item → drawer shows LIST panel with kind + indent
actions.
Concept A+D validated visually: ONE surface replaces what would
otherwise be bubble menu + slash menu + heading picker + language
picker + table popover + image controls + link editor. The contextual
switch happens automatically; the user just moves the caret.
DRAWER-3 (polish): mobile bottom-sheet layout, A/B toggle in demo
controls, image-edit flow — deferred to next session if needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
{/if}
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
{:else if mode === 'table'}
|
|
|
|
|
|
<section data-words-drawer-section>
|
|
|
|
|
|
<h3 data-words-drawer-section-title>Rows</h3>
|
|
|
|
|
|
<div data-words-drawer-actions>
|
|
|
|
|
|
<button type="button" data-words-drawer-action onclick={() => snippet.runCommand('insert-table-row')}>+ Row</button>
|
|
|
|
|
|
<button type="button" data-words-drawer-action data-tone="risk" onclick={() => snippet.runCommand('delete-table-row')}>− Row</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
feat(words): drawer contextual panels — format/image/table/code/list (DRAWER-2)
The companion drawer now switches its body content based on where the
caret is. Five context modes wired to runCommand on the snippet
provider, all reading current state to show active highlights:
- format: B/I/U/S/code mark toggles (data-active reflects activeMarks),
align L/C/R/J (data-active reflects currentTextAlign), Add link (URL
prompt) + Remove (canUnlink-gated), Clear formatting.
- image: read-only view of src, alt (italic + "(missing)" placeholder
when empty), caption, status (pending → accent tint, error → threat
tint). Edit-image flow deferred to a follow-up (needs a new
updateImage operation; for the spike, view-only is enough to
validate the panel works).
- table: Rows +/- · Columns +/- · Row/Col header toggles · Striped /
Compact toggles · Cell tone Plain/Muted/Accent. Every toggle reads
current* state so the user sees which buttons are currently applied.
- code: Plain + TS/JS/CSS/HTML/JSON/Bash/Py language chips,
data-active reflects currentCodeLanguage.
- list: Bullet/Numbered/Check kind chips · ⇤ Outdent / ⇥ Indent.
All buttons use onmousedown preventDefault so the editor selection
isn't dropped when clicking a drawer action (would otherwise lose the
target paragraph / table cell / list item).
CSS adds a generic .data-words-drawer-action chip (with hover, active,
ghost, full, risk tones) + readonly-text style with pending/error
tints. Reused across all panels, so adding new panels later means
markup-only, no CSS extension.
Verified in browser:
- Click table cell → drawer shows TABLE panel with Striped active
(matches sample's striped=true) and Plain tone active (matches
default cell tone).
- Click code block → drawer shows CODE panel with TS chip active
(matches sample's language='ts').
- Click list item → drawer shows LIST panel with kind + indent
actions.
Concept A+D validated visually: ONE surface replaces what would
otherwise be bubble menu + slash menu + heading picker + language
picker + table popover + image controls + link editor. The contextual
switch happens automatically; the user just moves the caret.
DRAWER-3 (polish): mobile bottom-sheet layout, A/B toggle in demo
controls, image-edit flow — deferred to next session if needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
<section data-words-drawer-section>
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
<h3 data-words-drawer-section-title>Columns</h3>
|
|
|
|
|
|
<div data-words-drawer-actions>
|
|
|
|
|
|
<button type="button" data-words-drawer-action onclick={() => snippet.runCommand('insert-table-column')}>+ Col</button>
|
|
|
|
|
|
<button type="button" data-words-drawer-action data-tone="risk" onclick={() => snippet.runCommand('delete-table-column')}>− Col</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
<section data-words-drawer-section>
|
|
|
|
|
|
<h3 data-words-drawer-section-title>Headers</h3>
|
|
|
|
|
|
<div data-words-drawer-actions>
|
|
|
|
|
|
<button type="button" data-words-drawer-action data-active={snippet.currentTableHeaderRowActive ? '' : undefined} onclick={() => snippet.runCommand('toggle-table-header-row')}>Row header</button>
|
|
|
|
|
|
<button type="button" data-words-drawer-action data-active={snippet.currentTableHeaderColumnActive ? '' : undefined} onclick={() => snippet.runCommand('toggle-table-header-column')}>Col header</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
<section data-words-drawer-section>
|
|
|
|
|
|
<h3 data-words-drawer-section-title>Appearance</h3>
|
|
|
|
|
|
<div data-words-drawer-actions>
|
|
|
|
|
|
<button type="button" data-words-drawer-action data-active={snippet.currentTableStriped ? '' : undefined} onclick={() => snippet.runCommand('toggle-table-striped')}>Striped</button>
|
|
|
|
|
|
<button type="button" data-words-drawer-action data-active={snippet.currentTableCompact ? '' : undefined} onclick={() => snippet.runCommand('toggle-table-compact')}>Compact</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
<section data-words-drawer-section>
|
|
|
|
|
|
<h3 data-words-drawer-section-title>Cell tone</h3>
|
|
|
|
|
|
<div data-words-drawer-actions>
|
|
|
|
|
|
{#each [
|
|
|
|
|
|
{ id: 'table-cell-tone-default', label: 'Plain' },
|
|
|
|
|
|
{ id: 'table-cell-tone-muted', label: 'Muted' },
|
|
|
|
|
|
{ id: 'table-cell-tone-accent', label: 'Accent' }
|
|
|
|
|
|
] as t (t.id)}
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
data-words-drawer-action
|
|
|
|
|
|
data-active={snippet.currentTableCellTone === t.id.replace('table-cell-tone-', '') ? '' : undefined}
|
|
|
|
|
|
onclick={() => snippet.runCommand(t.id)}
|
|
|
|
|
|
>{t.label}</button>
|
|
|
|
|
|
{/each}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
{:else if mode === 'code'}
|
|
|
|
|
|
<section data-words-drawer-section>
|
|
|
|
|
|
<h3 data-words-drawer-section-title>Language</h3>
|
|
|
|
|
|
<div data-words-drawer-actions>
|
|
|
|
|
|
{#each CODE_LANGUAGES as lang (lang.id)}
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
data-words-drawer-action
|
|
|
|
|
|
data-active={snippet.currentCodeLanguage === lang.id ? '' : undefined}
|
|
|
|
|
|
onclick={() => snippet.setCodeLanguage(lang.id || undefined)}
|
|
|
|
|
|
>{lang.label}</button>
|
|
|
|
|
|
{/each}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
{:else if mode === 'list'}
|
|
|
|
|
|
<section data-words-drawer-section>
|
|
|
|
|
|
<h3 data-words-drawer-section-title>List kind</h3>
|
|
|
|
|
|
<div data-words-drawer-actions>
|
|
|
|
|
|
<button type="button" data-words-drawer-action onclick={() => snippet.runCommand('unordered-list')}>Bullet</button>
|
|
|
|
|
|
<button type="button" data-words-drawer-action onclick={() => snippet.runCommand('ordered-list')}>Numbered</button>
|
|
|
|
|
|
<button type="button" data-words-drawer-action onclick={() => snippet.runCommand('check-list')}>Check</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
<section data-words-drawer-section>
|
|
|
|
|
|
<h3 data-words-drawer-section-title>Indent</h3>
|
|
|
|
|
|
<div data-words-drawer-actions>
|
|
|
|
|
|
<button type="button" data-words-drawer-action onclick={() => snippet.runCommand('decrease-indent')}>⇤ Outdent</button>
|
|
|
|
|
|
<button type="button" data-words-drawer-action onclick={() => snippet.runCommand('increase-indent')}>⇥ Indent</button>
|
|
|
|
|
|
</div>
|
feat(words): drawer contextual panels — format/image/table/code/list (DRAWER-2)
The companion drawer now switches its body content based on where the
caret is. Five context modes wired to runCommand on the snippet
provider, all reading current state to show active highlights:
- format: B/I/U/S/code mark toggles (data-active reflects activeMarks),
align L/C/R/J (data-active reflects currentTextAlign), Add link (URL
prompt) + Remove (canUnlink-gated), Clear formatting.
- image: read-only view of src, alt (italic + "(missing)" placeholder
when empty), caption, status (pending → accent tint, error → threat
tint). Edit-image flow deferred to a follow-up (needs a new
updateImage operation; for the spike, view-only is enough to
validate the panel works).
- table: Rows +/- · Columns +/- · Row/Col header toggles · Striped /
Compact toggles · Cell tone Plain/Muted/Accent. Every toggle reads
current* state so the user sees which buttons are currently applied.
- code: Plain + TS/JS/CSS/HTML/JSON/Bash/Py language chips,
data-active reflects currentCodeLanguage.
- list: Bullet/Numbered/Check kind chips · ⇤ Outdent / ⇥ Indent.
All buttons use onmousedown preventDefault so the editor selection
isn't dropped when clicking a drawer action (would otherwise lose the
target paragraph / table cell / list item).
CSS adds a generic .data-words-drawer-action chip (with hover, active,
ghost, full, risk tones) + readonly-text style with pending/error
tints. Reused across all panels, so adding new panels later means
markup-only, no CSS extension.
Verified in browser:
- Click table cell → drawer shows TABLE panel with Striped active
(matches sample's striped=true) and Plain tone active (matches
default cell tone).
- Click code block → drawer shows CODE panel with TS chip active
(matches sample's language='ts').
- Click list item → drawer shows LIST panel with kind + indent
actions.
Concept A+D validated visually: ONE surface replaces what would
otherwise be bubble menu + slash menu + heading picker + language
picker + table popover + image controls + link editor. The contextual
switch happens automatically; the user just moves the caret.
DRAWER-3 (polish): mobile bottom-sheet layout, A/B toggle in demo
controls, image-edit flow — deferred to next session if needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
</section>
|
|
|
|
|
|
{/if}
|
refactor(words): drawer canon — morfo part + soma provider + eidos consumes via provider (DRAWER-CANON)
Closes the architectural gap from the DRAWER spike: the companion
drawer now follows the same morfo/soma/eidos pattern as every other
Words part (Toolbar/BubbleMenu/SlashMenu/LinkEditor/Status/etc.).
DRAWER-CANON-1 — morfo + soma:
- wordsMorfo gains a 'Drawer' part (kebab 'drawer', archetype 'group',
defaultElement 'aside', role 'complementary'). Declares data-words-
drawer-mode (default/format/image/table/code/list), data-open,
data-disabled, aria-label. New event 'commit-toggle-drawer' fires
on collapse/expand for sema/telemetry hooks.
- New WordsDrawerProvider in words-provider.svelte: owns `open`
bindable state, derives `mode` from parent provider's currentBlock
+ selection (same logic as the spike, now in soma where it
belongs), exposes snippetProps {mode, open, disabled, snippet,
toggle, setOpen}. `toggle()` and `setOpen()` emit the event.
- WordsDrawerProps + WordsDrawerSnippetProps + WordsDrawerMode types
added to types.ts and re-exported via exports.ts.
- soma/components/words/components/words-drawer.svelte (the soma
component) mounts the provider and forwards merged props to either
the `child` snippet or the default <aside> render.
DRAWER-CANON-2 — eidos refactor:
- eidos words-drawer.svelte rewritten as a consumer of
<Words.Drawer>. The shell (aside + header + collapse button) and
the per-mode body now live inside the children snippet that
receives the soma's drawer snippet props. Mode derivation removed
from eidos (it's now in soma). Local open $state removed (now
bindable via the soma component).
- Demo +page.svelte: `<Words.Drawer snippet={snippetProps} />`
collapses to plain `<Words.Drawer />` — the soma provider hands the
parent snippet down internally.
DRAWER-CANON-3 — verify:
- 214/214 tests pass in soma+morfo scope (no regressions).
- npx tsc --noEmit clean for words area.
- Visual verification: drawer still renders with DOCUMENT/OUTLINE/
STATS/SUGGESTIONS in default mode; context-switching to table /
code / list / format still works; aside element now carries
role='complementary', aria-label from morfo translationRef,
data-words-drawer-mode reflecting current panel.
The eidos drawer now passes the same canon checks as every other
Words part: morfo declares its contract, soma owns the state, eidos
only paints. The schema validator can verify the drawer's attrs, the
eidos lint can check selector usage, sema can react to the toggle
event.
Demo header chips reflect the new contract: PARTS 19→20, EVENTS
11→12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 months ago
|
|
|
|
</div>
|
|
|
|
|
|
{/if}
|
|
|
|
|
|
{/snippet}
|
|
|
|
|
|
</Words.Drawer>
|
|
|
|
|
|
|
|
|
|
|
|
<script context="module" lang="ts">
|
|
|
|
|
|
import type { ProviderSnippetProps as PSP } from '$soma/components/words';
|
|
|
|
|
|
type Warning = {
|
|
|
|
|
|
readonly id: string;
|
|
|
|
|
|
readonly severity: 'info' | 'warn';
|
|
|
|
|
|
readonly message: string;
|
|
|
|
|
|
};
|
|
|
|
|
|
export function collectWarnings(s: PSP): readonly Warning[] {
|
|
|
|
|
|
const out: Warning[] = [];
|
|
|
|
|
|
s.document.children.forEach((block, index) => {
|
|
|
|
|
|
if (block.type === 'image' && !block.alt?.trim()) {
|
|
|
|
|
|
out.push({
|
|
|
|
|
|
id: `alt-${index}`,
|
|
|
|
|
|
severity: 'warn',
|
|
|
|
|
|
message: `Image at block ${index + 1} has no alt text`
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
if (block.type === 'paragraph') {
|
|
|
|
|
|
const text = block.children
|
|
|
|
|
|
.map((i) => (i.type === 'text' ? i.text : ''))
|
|
|
|
|
|
.join('');
|
|
|
|
|
|
if (text.length > 600) {
|
|
|
|
|
|
out.push({
|
|
|
|
|
|
id: `long-${index}`,
|
|
|
|
|
|
severity: 'info',
|
|
|
|
|
|
message: `Paragraph ${index + 1} is long (${text.length} chars) — consider splitting`
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
const headingLevels = s.document.children
|
|
|
|
|
|
.filter((b) => b.type === 'heading')
|
|
|
|
|
|
.map((b) => (b.type === 'heading' ? b.level : 0));
|
|
|
|
|
|
for (let i = 1; i < headingLevels.length; i++) {
|
|
|
|
|
|
if (headingLevels[i] - headingLevels[i - 1] > 1) {
|
|
|
|
|
|
out.push({
|
|
|
|
|
|
id: `skip-${i}`,
|
|
|
|
|
|
severity: 'warn',
|
|
|
|
|
|
message: `Heading h${headingLevels[i]} appears without an h${headingLevels[i - 1] + 1} above — structure skip`
|
|
|
|
|
|
});
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return out;
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|