Cierra F4.7. Los derivados wordCount/charCount/tableOfContents ya vivían en el snippet del provider; faltaba su affordance. Decisión del usuario (2026-07-19): barra de estado al pie con el recuento (readout pasivo) + botón «Índice» que abre un popover de encabezados; clic hace scroll al heading. - palabras-status.svelte (NUEVO): «N palabras · M caracteres» + `<Popover>` con `tableOfContents` (sangrado por `level` vía data-level); jumpTo hace scrollIntoView del `[data-palabras-id]` del encabezado y cierra el popover. El trigger se deshabilita sin encabezados. - palabras.svelte: montada al pie del shell (los 3 modos — el recuento es un readout del documento, no una propiedad; el usuario lo quitó del panel). - palabras-chrome.css: sección [data-palabras-status] + outline (tokens). El índice va en affordance PROPIA (no el panel de propiedades — decisión del usuario 2026-07-18: es navegación, no una propiedad de página). Langs es/en (status.words/chars/outline/untitled) DIFERIDOS (fichero de langs entangled con el merge pendiente; los `ts()` renderizan por fallback). Verificado: check 0 errores palabras; Playwright headless: «45 palabras · 253 caracteres», popover con 2 encabezados (niveles 1/2), clic en «Estrategias» → popover cierra + h2 en vista; 0 errores consola. Screenshot MIRADO. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>alpha-0.1-sec-dom
parent
72a6569c3e
commit
a97d8db740
@ -0,0 +1,76 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* palabras status bar (F4.7) — a passive word / char readout plus the heading
|
||||
* outline.
|
||||
*
|
||||
* Count is a READOUT, never a document property: the user's decision
|
||||
* (2026-07-18) is that an index / count is document NAVIGATION, not a page
|
||||
* property, so it lives in its own affordance rather than the properties panel.
|
||||
* The «Índice» button opens a popover of the document's headings; clicking an
|
||||
* entry scrolls its heading into view.
|
||||
*/
|
||||
import { Popover } from '$uix/eidos/components/popover';
|
||||
import { TableOfContents } from '$uix/eidos/components/icon';
|
||||
import { ActiveEidos } from '$uix/eidos';
|
||||
import type { ProviderSnippetProps } from '$soma/components/palabras';
|
||||
import type { ActiveDom } from '$adom';
|
||||
|
||||
let {
|
||||
api,
|
||||
content,
|
||||
dom
|
||||
}: {
|
||||
api: ProviderSnippetProps;
|
||||
content: HTMLElement | null;
|
||||
dom: ActiveDom;
|
||||
} = $props();
|
||||
|
||||
const eidos = ActiveEidos.require();
|
||||
const ts = (ref: string) => eidos.langs.ts(ref);
|
||||
|
||||
const toc = $derived(api.tableOfContents);
|
||||
let open = $state(false);
|
||||
|
||||
function jumpTo(id: string | undefined) {
|
||||
if (!id) return;
|
||||
const el = content ? dom.query<HTMLElement>(`[data-palabras-id="${CSS.escape(id)}"]`, content) : null;
|
||||
el?.scrollIntoView({ block: 'start', behavior: 'smooth' });
|
||||
open = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div data-palabras-status>
|
||||
<span data-palabras-status-count>
|
||||
{api.wordCount}
|
||||
{ts('#?components.palabras.status.words|palabras')} · {api.charCount}
|
||||
{ts('#?components.palabras.status.chars|caracteres')}
|
||||
</span>
|
||||
<Popover bind:open>
|
||||
<Popover.Trigger
|
||||
data-palabras-outline-trigger
|
||||
disabled={toc.length === 0}
|
||||
aria-label={ts('#?components.palabras.status.outline|Índice')}
|
||||
>
|
||||
<TableOfContents />
|
||||
<span>{ts('#?components.palabras.status.outline|Índice')}</span>
|
||||
</Popover.Trigger>
|
||||
<Popover.Portal>
|
||||
<Popover.Content data-palabras-outline>
|
||||
<ul>
|
||||
{#each toc as entry, i (entry.id ?? i)}
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
data-palabras-outline-item
|
||||
data-level={entry.level}
|
||||
onclick={() => jumpTo(entry.id)}
|
||||
>
|
||||
{entry.text || ts('#?components.palabras.status.untitled|(sin título)')}
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</Popover.Content>
|
||||
</Popover.Portal>
|
||||
</Popover>
|
||||
</div>
|
||||
Loading…
Reference in new issue