You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/uix/eidos/components/words/words-drawer.svelte

412 lines
14 KiB

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>

Powered by TurnKey Linux.