From 005239c73208263f0d43cc1e52651697e8ca4438 Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 29 May 2026 14:07:00 +0200 Subject: [PATCH] feat(words): inline heading-level control in the Block drawer panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an H1/H2/H3 toggle to the drawer's Block panel, shown only when the caret is in a heading (currentBlock === 'heading'). The toggle reflects currentHeadingLevel and dispatches the structured `setBlock` command with the numeric level via applyCommand: applyCommand({ type: 'setBlock', block: 'heading', level }) runCommand / WordsCommandName has no per-level id (heading-1/2/3 are slash/insert-menu ids), so the structured command carries the level; `as const` keeps each level a 1|2|3 literal. Verified in browser: caret in a heading shows "Heading level"; the toggle reflects the active level and converts bidirectionally (H1→H3→H2) with the toggle updating. npm run check: 0 errors. Also closes the stale "drag handle outside the rail over " P1: it does not reproduce — the rail was removed in the gutter redesign (5450d5f7) and the handle now anchors to contentRect.left+8 for every block type (verified: identical left over code and paragraph). Both P1 items marked resolved in continue.md. Co-Authored-By: Claude Opus 4.7 --- continue.md | 38 ++++++++++++------- .../components/words/words-drawer.svelte | 29 ++++++++++++++ 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/continue.md b/continue.md index ac40660f9..74d8a06af 100644 --- a/continue.md +++ b/continue.md @@ -148,21 +148,31 @@ panel renderiza los 4 controles nuevos; click en swatch de Background actualiza el figure (`background-color:#22c55e`) preservando radius/border/shadow (shallow-merge correcto). `npm run check` 0 errors. -## Pendiente para mañana - -### P1 — Drag handle FUERA del rail sobre bloques `` (heredado) - -El grip se va fuera de la rail al hacer hover sobre un code block. -Sin diagnosticar. Lugar: `eidos/components/words/words-block-handle.svelte -> measureFromEl()`. Pasos: comparar `getBoundingClientRect().left` de -`
` vs `

`; si difieren, buscar padding/margin/transform en -`[data-words-content] :where(pre)` en `words.css`. +### ✅ P1 — Drag handle sobre `` (NO reproduce) + +Investigado: NO reproduce en el código actual. El "rail" al que se +refería el bug fue ELIMINADO en el rediseño de gutter (commit +`5450d5f7`, "rail out"), posterior al sprint EV donde se reportó. +`measureFromEl` (`words-block-handle.svelte`) ancla el handle a +`contentRect.left + 8` — block-type-agnostic. Verificado en browser: +sobre code y sobre paragraph el gutter cae en `left: 350.333` idéntico +(solo difiere `top`). No hay nada que arreglar; bug obsoleto. + +### ✅ P1 — Heading inline level change (h1 ↔ h2 ↔ h3) + +Añadido al Block panel del drawer +(`eidos/components/words/words-drawer.svelte > blockPanel`): cuando +`currentBlock === 'heading'`, un ToggleGroup H1/H2/H3 (value = +`currentHeadingLevel`) despacha `applyCommand({ type: 'setBlock', +block: 'heading', level })`. `runCommand` / `WordsCommandName` no tiene +id por-nivel (`heading-1/2/3` son ids de slash / insert-menu), por eso +se usa el comando estructurado `setBlock` con el nivel numérico (`as +const` mantiene cada `level` como literal 1|2|3). Verificado en +browser: caret en heading → el panel muestra "Heading level"; el toggle +refleja el nivel actual; el click convierte bidireccional (H1→H3→H2) y +el toggle se actualiza. `npm run check` 0 errors. -### P1 — Heading inline level change (h1 ↔ h2 ↔ h3) (heredado) - -Cambiar nivel de heading inline sin abrir drawer. Recomendación: -empezar por el Block panel del drawer (verificar que el HeadingPicker -se muestra cuando `currentBlock === 'heading'`). +## Pendiente para mañana ### P2 — Heredados diff --git a/src/uix/eidos/components/words/words-drawer.svelte b/src/uix/eidos/components/words/words-drawer.svelte index 4e688a6aa..132407acc 100644 --- a/src/uix/eidos/components/words/words-drawer.svelte +++ b/src/uix/eidos/components/words/words-drawer.svelte @@ -196,6 +196,15 @@ { id: 'align-right', label: '⫸' }, { id: 'align-justify', label: '☷' } ] as const satisfies readonly { id: WordsCommandName; label: string }[]; + + // Heading level uses the structured `setBlock` command (carries the + // numeric level) — `runCommand` / WordsCommandName has no per-level + // id. `as const` keeps each `level` a 1|2|3 literal for the command. + const HEADING_LEVEL_ACTIONS = [ + { level: 1, value: '1', label: 'H1', title: 'Heading 1' }, + { level: 2, value: '2', label: 'H2', title: 'Heading 2' }, + { level: 3, value: '3', label: 'H3', title: 'Heading 3' } + ] as const; @@ -530,6 +539,26 @@ {/snippet} {#snippet blockPanel(s: ProviderSnippetProps)} + {#if s.currentBlock === 'heading'} +

Heading level
+ { + const action = HEADING_LEVEL_ACTIONS.find((a) => a.value === next[0]); + if (action) s.applyCommand({ type: 'setBlock', block: 'heading', level: action.level }); + }} + aria-label="Heading level" + > + {#each HEADING_LEVEL_ACTIONS as h (h.value)} + e.preventDefault()}> + {h.label} + + {/each} + + {/if}
Text align