|
|
|
|
import type { Morfo } from '../types';
|
|
|
|
|
import { v } from '../types';
|
|
|
|
|
|
|
|
|
|
export const floatPanelMorfo = {
|
|
|
|
|
name: 'FloatPanel',
|
|
|
|
|
kebab: 'float-panel',
|
fix(uix): audit Phase 3 — SYS-1 scope-drift, declare 'eidos' where a recipe exists
Decision (the one definitional call SYS-1 required): morfo `scope` enumerates
the layers that IMPLEMENT the component (per the glossary), so a component with
an `eidos/components/{c}/` recipe + wrapper MUST list `'eidos'`. This matches the
62 components that already declared it correctly (dropdown-menu, accordion, tabs,
checkbox, radio-group, splitter, …) and makes "which components have eidos"
answerable from the morfo. Chosen over the implicit-eidos alternative for
explicitness.
Added `'eidos'` to the morfo-level `scope` of every drifted component — the 62
with a real eidos recipe (.svelte/.css, not a README-only dir) whose scope
omitted it. After this, all 124 audited morfos declare scope consistently;
scope-drift = 0.
`scope` is purely documentary (the only readers are the schema validator —
`layerSchema` already accepts `'eidos'` — and the 2-of-3 doctrine; nothing keys
behavior off it), so this is a behavior-safe documentation alignment: every diff
is exactly one `scope:` line, `npm run check` adds zero errors.
words/palabras/chronos excluded. calendar included (its morfo is clean; the
view-switch refactor lives in soma/eidos, untouched here).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
|
|
|
scope: ['soma', 'sema', 'eidos'],
|
|
|
|
|
expression: 'pack',
|
|
|
|
|
apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/',
|
|
|
|
|
texts: {
|
|
|
|
|
label: '#?components.float-panel.label|Floating panel',
|
|
|
|
|
'content.roledescription': '#?components.float-panel.content.roledescription|window'
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
events: [
|
|
|
|
|
{
|
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the
checkpoint verdicts in docs/audit/components/) and the executed fix packages.
- P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 +
API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d,
component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts
(role=application removed ×4, aria-selected off the Day, drp translationRef,
field data-state prune, pin-input commit-set, media-player renames), 13 new
sema packs + 12 morfos family-default → pack.
- P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar);
onValueCommit terminal-callback norm (pin-input/search/password/textarea +
date/time/color-field add); typed validation reason + onInvalid (tags-input,
css-field); index/onIndexChange (carousel); deselectable; openDelay/
groupSkipDelay; allowCustomValue; defaultValue prune.
- P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across
cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in
field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel);
touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual,
44 AAA). S6 (Field composition) + S8 (calendar-surface) pending.
Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline);
morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched
components; per-component vitest suites green.
Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md
Excluded (broken by the N1 rename, left broken per user decision, not staged):
words/**, palabras/**, chronos, web/routes/alpha/**.
Reconciliation pending: the touch-rows ::before for checkbox/switch reverses
changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed
markers to a labeled-row/Field task — flagged for the user in the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
// Verb per book cap. 26 §5: a floating panel opens "con marco
|
|
|
|
|
// propio" — emerge.open (was `present`; C2 verbs pass, 2026-07-07).
|
feat(morfo,sema,eidos): todo evento dice de que familia es, y el cruce por fin se ve
El framework llamaba a la misma cosa de dos maneras: `open` pelado en ocho
componentes y `emerge-open` en tres. No era estetica — un preset de movimiento
engancha el nombre con `^=`, asi que el dialecto pelado no casaba con ninguna
firma y simplemente no animaba, sin romper una sola prueba.
Los 40 nombres sin prefijo pasan a `{familia}-{verbo}[-{matiz}]`: 256 eventos,
256 con prefijo, 0 ambiguos. El plan decia 36 y decia `handle-drag-start`; eran
40, y el canon (c25) dice que esos verbos son `pick` y `drop` — `handle-pick` y
`handle-drop` ya existian en 10 y 4 componentes.
`validateMorfo` cierra la puerta: un `events[].name` que no empiece por su
familia ahora lanza. Visto fallar antes con un nombre pelado inyectado.
Lo que el renombrado destapo, y va aqui tambien:
- La receta del splitter enganchaba `commit-resize`, muerto desde
`bd2e40366`. No casaba desde mayo y nadie chillo. Reescrita por FAMILIA, como
slider y knob, y `eidos-lint` valida ahora el VALOR de `data-event*` contra el
catalogo de morfos — el guard que lo habria cazado en su dia.
- La familia `shift` era muda en el canal visual, contra su propia doctrina
(c27: el cruce debe percibirse; c34 tipifica el «shift invisible»). Su mapa ya
describia la firma que le faltaba y su sonido por defecto es `slide`. Ahora
tiene firma direccional: sexto atributo del sello (`data-event-direction`,
`forward`|`backward`, por emision) y deslizamiento de 320ms RTL-safe por
`:dir()`. Medido: LTR -30px/+30px, RTL los invierte.
- El sello de `shift-navigate` pasa del BOTON al `grid` en los cuatro
calendarios. Medido: el boton recibia `contact-activate` y 8,5 ms despues
—media trama— el `shift-navigate` pisaba la misma ranura y el `press-squeeze`
moria sin pintar un fotograma. Una superficie, una ranura (A-36).
- 101 contradicciones docs<->morfo adjudicadas con evidencia (git log, docs de
decision, el componente vivo). Las docs desfasadas, corregidas; los nueve
DEFECTOS de codigo obsoleto quedan abiertos y sin tocar.
- `SoundDirection` -> `SoundContour`: era un contorno de tono, no un sentido, y
habia tres cosas distintas deletreadas «direction».
check en su linea base con 0 errores nuevos por diferencia de conjuntos ·
docs:check 0/0 · eidos-lint invalid 0 · el censo y las escenas de navegador
medidas con raton real y rAF vivo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
name: 'emerge-open',
|
|
|
|
|
semantic: {
|
|
|
|
|
family: 'emerge',
|
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the
checkpoint verdicts in docs/audit/components/) and the executed fix packages.
- P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 +
API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d,
component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts
(role=application removed ×4, aria-selected off the Day, drp translationRef,
field data-state prune, pin-input commit-set, media-player renames), 13 new
sema packs + 12 morfos family-default → pack.
- P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar);
onValueCommit terminal-callback norm (pin-input/search/password/textarea +
date/time/color-field add); typed validation reason + onInvalid (tags-input,
css-field); index/onIndexChange (carousel); deselectable; openDelay/
groupSkipDelay; allowCustomValue; defaultValue prune.
- P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across
cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in
field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel);
touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual,
44 AAA). S6 (Field composition) + S8 (calendar-surface) pending.
Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline);
morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched
components; per-component vitest suites green.
Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md
Excluded (broken by the N1 rename, left broken per user decision, not staged):
words/**, palabras/**, chronos, web/routes/alpha/**.
Reconciliation pending: the touch-rows ::before for checkbox/switch reverses
changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed
markers to a labeled-row/Field task — flagged for the user in the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
verb: 'open',
|
|
|
|
|
target: v.partRef('content'),
|
fix(sema,morfo,soma): una superficie no admite dos ocurrencias, y el nombre ya no miente
`data-event-*` es UNA RANURA por elemento. Tres auditorias independientes
encontraron el mismo defecto y ninguna lo cerro: fable S1 (2026-07-01, con el
README diciendo «findings executed»), sema S-17 (2026-08-05, midio el estampado
del knob muriendo a los 24ms de un hold de 240) y blocks A-36/A-65 (reproducido
en navegador, dos estampados a 1,1ms). Lo que faltaba no era diagnostico: nadie
podia distinguir una redireccion legitima de una deriva, porque la opcion que
redirigia se llamaba `fallbackTarget` y hacia lo contrario de lo que decia.
EL NOMBRE PRIMERO. `fallbackTarget` -> `targetOverride` (202 apariciones, 64
ficheros, dirigido con `git ls-files`). Siempre GANO sobre el ref registrado; el
nombre honesto es la precondicion para auditarlo. Las cronicas conservan el viejo
por diseño.
LA PROPIEDAD. `unstampEventAttrs` recibia el signal y lo DESCARTABA, asi que la
ocurrencia que terminaba antes borraba a la que tenia la ranura. Ahora comprueba
`data-event-id`. Con eso, `replace` deja de ser el comportamiento roto y pasa a
ser el suelo correcto.
A-36, SIN `regime`. Los cuatro overlays cedian el estampado del `open` al
trigger por un workaround de la era `sequence: 'pre'`; con `post` el content ya
esta montado. Retirado el override (float-panel migrado `pre`->`post`, ultimo
fuera de la doctrina que sema.md escribe). Medido en /blocks/site-header/preview
a 375px: contact-activate en el TRIGGER +18,7ms y open en el CONTENT +20,6ms,
con press-squeeze y slide-from-right-full corriendo. Antes: los dos en el
trigger, press-squeeze jamas.
`regime` DEFINIDO — y son dos valores, no cuatro. `replace` (default) y `queue`,
este solo para las 4 parejas irreducibles: los tres toggles (el provider ES el
boton) y el knob. `collapse` retirado (su caso murio con la propiedad: medido, 0
huecos en 5 emisiones a 72ms) y `lock` retirado (significaria «un cierre que no
se anuncia»), con sus 4 declaraciones muertas.
El guard de `queue` PASO EN VERDE SIENDO INCORRECTO: con timers falsos no hay
animaciones, asi que awaitExpression volvia al instante. El navegador midio el
commit encolado llegando 1,6s tarde. `queue` espera el HOLD, no la expresion.
Medir la envolvente no es medir la salida.
`renderAttrs` — los attrs que no son partes. Vaciar ACTIVE_DEV_TRACK (palabras y
chronos ya no estan excluidos) destapo 23 `data-palabras-*` que ningun morfo
declaraba y que palabras.css SI estiliza: aterrizan en el arbol que el usuario
escribio, asi que no pueden ser partes. Campo nuevo con la linea afilada —si el
consumidor puede componerlo, es una PARTE— y 3 consumidores (palabras, waveform,
aura). Declarado en types.ts Y en el esquema sium: la leccion de MorfoElement.
Ademas: 5 selectores muertos en el predicado del focus-scope de palabras
(barrido de todo el repo: aparecian solo ahi) · RTL-1 real en palabras-chrome
(inset logico + translate fisico = doble volteo; rtl:check 1->0) · el union
inline al `Direction` canonico, que destapo que el censo de direccion grepea el
NOMBRE del tipo · deuda de chronos (scope, barrel, README).
Y de la cola: S-36 (barrel 68->71 + guard), S-31 (vibrate(NaN)), S5 (la
preferencia de reduced-motion del usuario ya gana a los channels del morfo), SO2
(el warn que el JSDoc prometia), SO4 (validar el target antes del prewrite).
Todos los guards nuevos vistos fallar antes de arreglar nada.
Base: check 74 (= base) · sema+morfo+contracts 454 verdes / 6 ajenos
preexistentes · soma navegador 1258/1259 (el rojo es un timeout ajeno,
reproducido con los cambios en stash) · docs:check 0/618 · rtl:check 0/177.
Handoff: docs/process/CONTINUE-perceptual-surface.md
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
// `post` (2026-08-10 — the last overlay to migrate; Dialog /
|
|
|
|
|
// Drawer / Popover made the move earlier, same doctrine as the
|
|
|
|
|
// checkbox-lag fix): the provider flips `open` in this event's
|
|
|
|
|
// HANDLER, so with `pre` the emit — and the family's ~240ms
|
|
|
|
|
// hold — would gate the content mount. With `post` the handler
|
|
|
|
|
// runs, `tick()` mounts the content, and the emit stamps the
|
|
|
|
|
// DECLARED target: the appearance is perceived on the thing
|
|
|
|
|
// appearing, and the trigger keeps its own surface free for its
|
|
|
|
|
// own events (A-36). Exit stays `pre` on `close`.
|
|
|
|
|
sequence: 'post'
|
|
|
|
|
},
|
|
|
|
|
commits: { part: v.partRef('content'), attr: 'data-state', value: 'open' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// Polymorphic close (book §5.3) — mirror of Popover / Dialog / Drawer.
|
|
|
|
|
// Provider concretes the cause via `dismissWith(action, opts?)`:
|
|
|
|
|
// sets `data-last-action` imperatively + passes `opts.semantic`.
|
feat(morfo,sema,eidos): todo evento dice de que familia es, y el cruce por fin se ve
El framework llamaba a la misma cosa de dos maneras: `open` pelado en ocho
componentes y `emerge-open` en tres. No era estetica — un preset de movimiento
engancha el nombre con `^=`, asi que el dialecto pelado no casaba con ninguna
firma y simplemente no animaba, sin romper una sola prueba.
Los 40 nombres sin prefijo pasan a `{familia}-{verbo}[-{matiz}]`: 256 eventos,
256 con prefijo, 0 ambiguos. El plan decia 36 y decia `handle-drag-start`; eran
40, y el canon (c25) dice que esos verbos son `pick` y `drop` — `handle-pick` y
`handle-drop` ya existian en 10 y 4 componentes.
`validateMorfo` cierra la puerta: un `events[].name` que no empiece por su
familia ahora lanza. Visto fallar antes con un nombre pelado inyectado.
Lo que el renombrado destapo, y va aqui tambien:
- La receta del splitter enganchaba `commit-resize`, muerto desde
`bd2e40366`. No casaba desde mayo y nadie chillo. Reescrita por FAMILIA, como
slider y knob, y `eidos-lint` valida ahora el VALOR de `data-event*` contra el
catalogo de morfos — el guard que lo habria cazado en su dia.
- La familia `shift` era muda en el canal visual, contra su propia doctrina
(c27: el cruce debe percibirse; c34 tipifica el «shift invisible»). Su mapa ya
describia la firma que le faltaba y su sonido por defecto es `slide`. Ahora
tiene firma direccional: sexto atributo del sello (`data-event-direction`,
`forward`|`backward`, por emision) y deslizamiento de 320ms RTL-safe por
`:dir()`. Medido: LTR -30px/+30px, RTL los invierte.
- El sello de `shift-navigate` pasa del BOTON al `grid` en los cuatro
calendarios. Medido: el boton recibia `contact-activate` y 8,5 ms despues
—media trama— el `shift-navigate` pisaba la misma ranura y el `press-squeeze`
moria sin pintar un fotograma. Una superficie, una ranura (A-36).
- 101 contradicciones docs<->morfo adjudicadas con evidencia (git log, docs de
decision, el componente vivo). Las docs desfasadas, corregidas; los nueve
DEFECTOS de codigo obsoleto quedan abiertos y sin tocar.
- `SoundDirection` -> `SoundContour`: era un contorno de tono, no un sentido, y
habia tres cosas distintas deletreadas «direction».
check en su linea base con 0 errores nuevos por diferencia de conjuntos ·
docs:check 0/0 · eidos-lint invalid 0 · el censo y las escenas de navegador
medidas con raton real y rAF vivo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
name: 'emerge-close',
|
|
|
|
|
semantic: {
|
|
|
|
|
family: 'emerge',
|
|
|
|
|
verb: 'close',
|
|
|
|
|
target: v.partRef('content'),
|
feat(morfo,soma): la emisión deja de aceptar un elemento anónimo
El defecto que tres auditorías encontraron y ninguna cerró no era que N eventos
redirigieran sin declararlo: era que la API de emisión aceptaba un HTMLElement
suelto, compensando que el registro de partes perdía la identidad de instancia.
Censar y arreglar a mano reproduce el modo de fallo. Esto lo hace inexpresable.
F1 — SomaRuntime<M> genérico, SIN default. Nombres de evento, kebabs de parte y
claves de events/actions tipados contra la declaración (EventNameOf /
PartKebabOf / ActionNameOf, los extractores que semaSelector ya usaba del otro
lado). 104 anotaciones migradas + 3 wrappers que devolvían tipado pero tomaban
sources ANCHO. SomaRuntime<Morfo> queda sólo en la tabla de contratos.
F2 — registro por instancia: Map<part, PartRegistration[]>, pertenencia
mantenida por el stream del attachment (poda al desmontar, re-entrada al
remontar), resolución liveReg = la instancia VIVA más nueva. Mata de paso la
clase "la más nueva muerta ensombrece a una viva".
F3 — tres formas legales de destino: el target declarado; el anclaje por
instancia (SomaRuntimePart.trigger, tipado por EventNameTargeting, +
runtime.partInstance por identidad de referencia); y targetFallback, la cadena
por estado de montaje declarada en el morfo y resuelta por resolveEmitTarget —
LA resolución única para validación, emit y foco a11y, que ya no pueden
discrepar. ~30 componentes migrados; censo de targetOverride 148 -> 89.
F4 — A-36 tenía un superviviente. Medido en dropdown-menu: bajo `pre` el `open`
no colisionaba con el contact-activate del Button compuesto, se PERDÍA entero
porque su content no había montado. Aplicada la receta de float-panel a
dropdown-menu Y context-menu. Ahora: contact-activate en trigger, open en
content — dos superficies, ambas expresando.
Submenús mudos, tres componentes. No era cableado: el morfo no declaraba el
evento. onion-menu recibe emerge-expand/emerge-collapse (la forma de TreeView:
un nivel revela sus hijos en el sitio); dropdown-menu y context-menu reciben
sub-open/sub-close sobre sub-content, distintos del open raíz a propósito
porque el sub es su propia superficie flotante. Nacen con la receta A-36 para
no repetirla en código nuevo.
chronos — declaraba 26 partes y registraba UNA; 17 role= a mano y el ARIA
declarado no llegaba nunca al DOM. Se anula la mitad sobre-extendida de la
decisión de 29ffe902a: la reutilización sigue viajando por el motor puro, pero
la ESTRUCTURA se compone como en Table y Calendar. C1 (las 6 partes que emiten,
chronos con cero targetOverride) + C2 (8 singletons) + C3 8/9.
Registrar una parte DESTAPA declaraciones falsas, tres veces en un pase: el chip
declaraba defaultElement button y ponía type= sobre un div (anida el asa de
resize); grid-row declaraba archetype item y pintó la semana entera como fila de
lista; y un aria por propRef desaparece en SILENCIO si el provider no publica la
fuente. En los tres el arreglo fue la DECLARACIÓN, no el DOM.
Verificado: check 74 = línea base medida con stash, DELTA CERO sostenido en cada
lote · 458 pasan (chronos+morfo+sema) · navegador limpio en las tres vistas de
chronos, con el contrato ARIA vivo por primera vez (aria-labelledby resolviendo
a «junio de 2026», aria-readonly/disabled que no se habían emitido nunca).
Handoff en docs/process/CONTINUE-perceptual-surface.md. Aparte, y decidido por
el autor: docs/process/PLAN-event-name-normalization.md — prefijo de familia
siempre; 36 de 252 sin prefijo, cero ambiguos.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
// Mount-state fallback, resolved by the RUNTIME: the close lands
|
|
|
|
|
// on the TRIGGER when the content is already gone. Declared on
|
|
|
|
|
// the fallback axis (not `allowedTargets` — that is the caller's
|
|
|
|
|
// choice set) since 2026-08-10; the provider no longer hand-rolls
|
|
|
|
|
// `content ?? partRef(content) ?? trigger`.
|
|
|
|
|
targetFallback: [v.partRef('trigger')],
|
|
|
|
|
sequence: 'pre',
|
|
|
|
|
persistence: 'transient',
|
|
|
|
|
allowedFamilies: ['emerge', 'commit', 'signal']
|
|
|
|
|
},
|
|
|
|
|
commits: { part: v.partRef('content'), attr: 'data-state', value: 'closed' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// Pickup cue when a drag (move) gesture starts on the Header handle.
|
feat(morfo,sema,eidos): todo evento dice de que familia es, y el cruce por fin se ve
El framework llamaba a la misma cosa de dos maneras: `open` pelado en ocho
componentes y `emerge-open` en tres. No era estetica — un preset de movimiento
engancha el nombre con `^=`, asi que el dialecto pelado no casaba con ninguna
firma y simplemente no animaba, sin romper una sola prueba.
Los 40 nombres sin prefijo pasan a `{familia}-{verbo}[-{matiz}]`: 256 eventos,
256 con prefijo, 0 ambiguos. El plan decia 36 y decia `handle-drag-start`; eran
40, y el canon (c25) dice que esos verbos son `pick` y `drop` — `handle-pick` y
`handle-drop` ya existian en 10 y 4 componentes.
`validateMorfo` cierra la puerta: un `events[].name` que no empiece por su
familia ahora lanza. Visto fallar antes con un nombre pelado inyectado.
Lo que el renombrado destapo, y va aqui tambien:
- La receta del splitter enganchaba `commit-resize`, muerto desde
`bd2e40366`. No casaba desde mayo y nadie chillo. Reescrita por FAMILIA, como
slider y knob, y `eidos-lint` valida ahora el VALOR de `data-event*` contra el
catalogo de morfos — el guard que lo habria cazado en su dia.
- La familia `shift` era muda en el canal visual, contra su propia doctrina
(c27: el cruce debe percibirse; c34 tipifica el «shift invisible»). Su mapa ya
describia la firma que le faltaba y su sonido por defecto es `slide`. Ahora
tiene firma direccional: sexto atributo del sello (`data-event-direction`,
`forward`|`backward`, por emision) y deslizamiento de 320ms RTL-safe por
`:dir()`. Medido: LTR -30px/+30px, RTL los invierte.
- El sello de `shift-navigate` pasa del BOTON al `grid` en los cuatro
calendarios. Medido: el boton recibia `contact-activate` y 8,5 ms despues
—media trama— el `shift-navigate` pisaba la misma ranura y el `press-squeeze`
moria sin pintar un fotograma. Una superficie, una ranura (A-36).
- 101 contradicciones docs<->morfo adjudicadas con evidencia (git log, docs de
decision, el componente vivo). Las docs desfasadas, corregidas; los nueve
DEFECTOS de codigo obsoleto quedan abiertos y sin tocar.
- `SoundDirection` -> `SoundContour`: era un contorno de tono, no un sentido, y
habia tres cosas distintas deletreadas «direction».
check en su linea base con 0 errores nuevos por diferencia de conjuntos ·
docs:check 0/0 · eidos-lint invalid 0 · el censo y las escenas de navegador
medidas con raton real y rAF vivo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
name: 'handle-pick',
|
|
|
|
|
semantic: {
|
|
|
|
|
family: 'handle',
|
|
|
|
|
verb: 'pick',
|
|
|
|
|
target: v.partRef('content'),
|
|
|
|
|
sequence: 'pre',
|
|
|
|
|
channels: ['sound', 'haptic']
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// Drop cue when the move gesture ends. Continuous movement does NOT
|
|
|
|
|
// emit per-frame (would buzz haptics nonstop — virtual-list doctrine).
|
feat(morfo,sema,eidos): todo evento dice de que familia es, y el cruce por fin se ve
El framework llamaba a la misma cosa de dos maneras: `open` pelado en ocho
componentes y `emerge-open` en tres. No era estetica — un preset de movimiento
engancha el nombre con `^=`, asi que el dialecto pelado no casaba con ninguna
firma y simplemente no animaba, sin romper una sola prueba.
Los 40 nombres sin prefijo pasan a `{familia}-{verbo}[-{matiz}]`: 256 eventos,
256 con prefijo, 0 ambiguos. El plan decia 36 y decia `handle-drag-start`; eran
40, y el canon (c25) dice que esos verbos son `pick` y `drop` — `handle-pick` y
`handle-drop` ya existian en 10 y 4 componentes.
`validateMorfo` cierra la puerta: un `events[].name` que no empiece por su
familia ahora lanza. Visto fallar antes con un nombre pelado inyectado.
Lo que el renombrado destapo, y va aqui tambien:
- La receta del splitter enganchaba `commit-resize`, muerto desde
`bd2e40366`. No casaba desde mayo y nadie chillo. Reescrita por FAMILIA, como
slider y knob, y `eidos-lint` valida ahora el VALOR de `data-event*` contra el
catalogo de morfos — el guard que lo habria cazado en su dia.
- La familia `shift` era muda en el canal visual, contra su propia doctrina
(c27: el cruce debe percibirse; c34 tipifica el «shift invisible»). Su mapa ya
describia la firma que le faltaba y su sonido por defecto es `slide`. Ahora
tiene firma direccional: sexto atributo del sello (`data-event-direction`,
`forward`|`backward`, por emision) y deslizamiento de 320ms RTL-safe por
`:dir()`. Medido: LTR -30px/+30px, RTL los invierte.
- El sello de `shift-navigate` pasa del BOTON al `grid` en los cuatro
calendarios. Medido: el boton recibia `contact-activate` y 8,5 ms despues
—media trama— el `shift-navigate` pisaba la misma ranura y el `press-squeeze`
moria sin pintar un fotograma. Una superficie, una ranura (A-36).
- 101 contradicciones docs<->morfo adjudicadas con evidencia (git log, docs de
decision, el componente vivo). Las docs desfasadas, corregidas; los nueve
DEFECTOS de codigo obsoleto quedan abiertos y sin tocar.
- `SoundDirection` -> `SoundContour`: era un contorno de tono, no un sentido, y
habia tres cosas distintas deletreadas «direction».
check en su linea base con 0 errores nuevos por diferencia de conjuntos ·
docs:check 0/0 · eidos-lint invalid 0 · el censo y las escenas de navegador
medidas con raton real y rAF vivo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
name: 'handle-drop',
|
|
|
|
|
semantic: {
|
|
|
|
|
family: 'handle',
|
|
|
|
|
verb: 'drop',
|
|
|
|
|
target: v.partRef('content'),
|
|
|
|
|
sequence: 'post',
|
|
|
|
|
channels: ['haptic']
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
feat(morfo,sema,eidos): todo evento dice de que familia es, y el cruce por fin se ve
El framework llamaba a la misma cosa de dos maneras: `open` pelado en ocho
componentes y `emerge-open` en tres. No era estetica — un preset de movimiento
engancha el nombre con `^=`, asi que el dialecto pelado no casaba con ninguna
firma y simplemente no animaba, sin romper una sola prueba.
Los 40 nombres sin prefijo pasan a `{familia}-{verbo}[-{matiz}]`: 256 eventos,
256 con prefijo, 0 ambiguos. El plan decia 36 y decia `handle-drag-start`; eran
40, y el canon (c25) dice que esos verbos son `pick` y `drop` — `handle-pick` y
`handle-drop` ya existian en 10 y 4 componentes.
`validateMorfo` cierra la puerta: un `events[].name` que no empiece por su
familia ahora lanza. Visto fallar antes con un nombre pelado inyectado.
Lo que el renombrado destapo, y va aqui tambien:
- La receta del splitter enganchaba `commit-resize`, muerto desde
`bd2e40366`. No casaba desde mayo y nadie chillo. Reescrita por FAMILIA, como
slider y knob, y `eidos-lint` valida ahora el VALOR de `data-event*` contra el
catalogo de morfos — el guard que lo habria cazado en su dia.
- La familia `shift` era muda en el canal visual, contra su propia doctrina
(c27: el cruce debe percibirse; c34 tipifica el «shift invisible»). Su mapa ya
describia la firma que le faltaba y su sonido por defecto es `slide`. Ahora
tiene firma direccional: sexto atributo del sello (`data-event-direction`,
`forward`|`backward`, por emision) y deslizamiento de 320ms RTL-safe por
`:dir()`. Medido: LTR -30px/+30px, RTL los invierte.
- El sello de `shift-navigate` pasa del BOTON al `grid` en los cuatro
calendarios. Medido: el boton recibia `contact-activate` y 8,5 ms despues
—media trama— el `shift-navigate` pisaba la misma ranura y el `press-squeeze`
moria sin pintar un fotograma. Una superficie, una ranura (A-36).
- 101 contradicciones docs<->morfo adjudicadas con evidencia (git log, docs de
decision, el componente vivo). Las docs desfasadas, corregidas; los nueve
DEFECTOS de codigo obsoleto quedan abiertos y sin tocar.
- `SoundDirection` -> `SoundContour`: era un contorno de tono, no un sentido, y
habia tres cosas distintas deletreadas «direction».
check en su linea base con 0 errores nuevos por diferencia de conjuntos ·
docs:check 0/0 · eidos-lint invalid 0 · el censo y las escenas de navegador
medidas con raton real y rAF vivo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
name: 'handle-resize-start',
|
|
|
|
|
semantic: {
|
|
|
|
|
family: 'handle',
|
|
|
|
|
verb: 'resize',
|
|
|
|
|
target: v.partRef('content'),
|
|
|
|
|
sequence: 'pre',
|
|
|
|
|
channels: ['haptic']
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
feat(morfo,sema,eidos): todo evento dice de que familia es, y el cruce por fin se ve
El framework llamaba a la misma cosa de dos maneras: `open` pelado en ocho
componentes y `emerge-open` en tres. No era estetica — un preset de movimiento
engancha el nombre con `^=`, asi que el dialecto pelado no casaba con ninguna
firma y simplemente no animaba, sin romper una sola prueba.
Los 40 nombres sin prefijo pasan a `{familia}-{verbo}[-{matiz}]`: 256 eventos,
256 con prefijo, 0 ambiguos. El plan decia 36 y decia `handle-drag-start`; eran
40, y el canon (c25) dice que esos verbos son `pick` y `drop` — `handle-pick` y
`handle-drop` ya existian en 10 y 4 componentes.
`validateMorfo` cierra la puerta: un `events[].name` que no empiece por su
familia ahora lanza. Visto fallar antes con un nombre pelado inyectado.
Lo que el renombrado destapo, y va aqui tambien:
- La receta del splitter enganchaba `commit-resize`, muerto desde
`bd2e40366`. No casaba desde mayo y nadie chillo. Reescrita por FAMILIA, como
slider y knob, y `eidos-lint` valida ahora el VALOR de `data-event*` contra el
catalogo de morfos — el guard que lo habria cazado en su dia.
- La familia `shift` era muda en el canal visual, contra su propia doctrina
(c27: el cruce debe percibirse; c34 tipifica el «shift invisible»). Su mapa ya
describia la firma que le faltaba y su sonido por defecto es `slide`. Ahora
tiene firma direccional: sexto atributo del sello (`data-event-direction`,
`forward`|`backward`, por emision) y deslizamiento de 320ms RTL-safe por
`:dir()`. Medido: LTR -30px/+30px, RTL los invierte.
- El sello de `shift-navigate` pasa del BOTON al `grid` en los cuatro
calendarios. Medido: el boton recibia `contact-activate` y 8,5 ms despues
—media trama— el `shift-navigate` pisaba la misma ranura y el `press-squeeze`
moria sin pintar un fotograma. Una superficie, una ranura (A-36).
- 101 contradicciones docs<->morfo adjudicadas con evidencia (git log, docs de
decision, el componente vivo). Las docs desfasadas, corregidas; los nueve
DEFECTOS de codigo obsoleto quedan abiertos y sin tocar.
- `SoundDirection` -> `SoundContour`: era un contorno de tono, no un sentido, y
habia tres cosas distintas deletreadas «direction».
check en su linea base con 0 errores nuevos por diferencia de conjuntos ·
docs:check 0/0 · eidos-lint invalid 0 · el censo y las escenas de navegador
medidas con raton real y rAF vivo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
name: 'handle-resize-end',
|
|
|
|
|
semantic: {
|
|
|
|
|
family: 'handle',
|
|
|
|
|
verb: 'resize',
|
|
|
|
|
target: v.partRef('content'),
|
|
|
|
|
sequence: 'post',
|
|
|
|
|
channels: ['haptic']
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
focus: {
|
feat(morfo): 28 morfos declaran su política de foco — la especie, por escrito
9 trap (dialog, drawer, alert-dialog, context-menu, dropdown-menu,
menu-dial, onion-menu, float-panel, popover) y 19 roving (toolbar, menubar,
tabs, listbox, radio-group, stepper, tag-group, tags-input, toggle-group,
carousel, drag-drop, tree-view, tree-grid, grid-list, chronos, calendar,
range-calendar, month-grid, year-grid).
Actas dentro de cada declaración; las que corrigen mentiras:
- popover: trap FALSE — declaraba true desde hacía meses con cero lectores;
la verdad embarcada manda.
- radio-group: orientation 'both' por APG — su propio keyboard[] declaraba
las 4 flechas; lo embarcado (2 flechas) era la deriva. El provider pasa
both=true al ejecutor.
- calendarios ×4 (calendar, range-calendar, month-grid, year-grid):
grid + executor.custom firmado — roving-tabindex REAL medido en sus
providers (focused ? 0 : -1), 2D que el primitivo 1D no expresa.
- grids/árboles (tree-grid, grid-list, chronos, carousel, drag-drop,
tree-view): custom firmado con su razón.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
|
|
|
kind: 'trap',
|
|
|
|
|
initial: 'first-focusable',
|
|
|
|
|
// Non-modal by default — the provider resolves the effective trap from
|
|
|
|
|
// the `modal` prop (modal panels trap, free-floating panels don't).
|
|
|
|
|
trap: false,
|
|
|
|
|
return: 'trigger',
|
|
|
|
|
restore: true
|
|
|
|
|
},
|
|
|
|
|
|
feat(direction): el mecanismo pertenece al morfo — la declaracion exige el cable
El campo obligatorio `dir: SomaRuntimeDir | null` de 4.1 conservaba el gate a
costa de ~116 lineas de ceremonia: 67 `dir: null` en fuente + 47 en tests
obligaban a ~75 componentes SIN direccion a pronunciarse sobre un eje que no
les concierne, y `parts` era un `readonly string[]` sin validar — un typo
estampaba nada, en silencio (fail-open). El usuario lo dijo antes que la
auditoria: "los dir no deben ser obligados de pasar".
Ahora lo declara el MORFO, que es donde viven los contratos:
direction: {} // 46 morfos — estampa en 'provider'
direction: { parts: ['trigger'] } // dropdown-menu (su raiz no renderiza)
direction: { parts: ['content'] } // drawer · float-panel (pintan en portal)
direction: { parts: [...,'input'] } // css-field · number-field (dos caras)
y el TIPO se computa de la declaracion, con el generico que ya existia inerte
en soma.runtime<M>:
morfo declara direction => dir: Active<Direction|undefined> REQUERIDO
no declara => dir?: never PROHIBIDO
Olvidar el cable no compila; cablear un eje a un componente sin direccion
tampoco. Y compileMorfo valida direction.parts contra el arbol declarado —
fail-CLOSED: el typo que antes estampaba nada ahora revienta al registrar.
El colapso: 49 getters `{ get: () => this.opts.dir.current }` -> `dir:
this.opts.dir`; los 67+47 `dir: null` MUEREN. El estampado gana su sitio en el
tipo de la superficie (`SomaRuntimePart.props` incluye `dir?: Direction`), asi
que los casts `(props as Record...).dir` de los tests sobran.
El gate cazo en el acto, y las dos presas eran reales:
- `command`: 4.1 le dejo `dir: null` en el runtime y el estampado manual
inline en el assert (la linea no estaba sola y el barrido no la vio) — un
componente CON direccion que habia quedado fuera del mecanismo.
- 5 runtimes SECUNDARIOS del mismo morfo (accordion item, nav-tree item,
emoji-picker cell, context-menu root-runtime, feed sentinel): mismo morfo,
misma exigencia — reciben la afirmacion del dueno (el estampado solo aterriza
en direction.parts, que esos runtimes no registran).
Guards nuevos:
- compile.test: normalizacion a ['provider'], partes explicitas, y el
fail-closed del typo (4 casos).
- direction-census.test: prop publica `dir?: Direction` <=> morfo.direction,
en ambos sentidos, con las excepciones FIRMADAS y sus razones (field-langs:
LangSpec; waveform: delega en el Slider; popover/tooltip/link-preview:
overlays puros cuya pintura estampa la capa flotante en el wrapper
portalizado — declararles direction seria el atributo inutil contra el que
4.1 avisaba). El propio censo cazo a esos tres: la excepcion quedo escrita
el mismo dia que el guard.
Verificado en Chrome con la fuente nueva (el morfo): dropdown-menu estampa en
el TRIGGER, css-field en raiz E input, drawer con pagina rtl por prefs deja el
content SIN atributo y computa rtl por herencia — el modelo del flip intacto.
check 77 = linea base · morfo+soma+active-uix 1401/1402 (el 1 es
soma-attr-audit, flaky bajo carga, pasa aislado) · rtl:check 1 (palabras) ·
docs:check 0/566.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
|
|
|
direction: { parts: ['content'] },
|
|
|
|
|
parts: [
|
|
|
|
|
{
|
|
|
|
|
name: 'Provider',
|
|
|
|
|
kebab: 'provider',
|
|
|
|
|
archetype: 'provider',
|
|
|
|
|
kind: 'virtual',
|
|
|
|
|
defaultElement: 'none',
|
|
|
|
|
optional: false,
|
|
|
|
|
data: [],
|
|
|
|
|
aria: []
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'Trigger',
|
|
|
|
|
kebab: 'trigger',
|
|
|
|
|
archetype: 'trigger',
|
|
|
|
|
kind: 'public',
|
|
|
|
|
defaultElement: 'button',
|
|
|
|
|
role: 'button',
|
|
|
|
|
optional: true,
|
|
|
|
|
states: ['open', 'closed'],
|
|
|
|
|
data: [{ attr: 'data-state', values: ['open', 'closed'], value: v.stateRef('open') }],
|
|
|
|
|
aria: [
|
|
|
|
|
{ attr: 'type', value: v.literal('button') },
|
|
|
|
|
{ attr: 'aria-haspopup', value: v.literal('dialog') },
|
|
|
|
|
{ attr: 'aria-expanded', value: v.stateRef('open') },
|
|
|
|
|
{
|
|
|
|
|
attr: 'aria-controls',
|
|
|
|
|
value: v.partRef('content'),
|
|
|
|
|
condition: { when: 'part-present', part: 'content' },
|
|
|
|
|
severity: 'recommended'
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
keyboard: [
|
|
|
|
|
{ key: 'Enter', action: 'open' },
|
|
|
|
|
{ key: ' ', action: 'open' }
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'Content',
|
|
|
|
|
kebab: 'content',
|
|
|
|
|
archetype: 'content',
|
|
|
|
|
kind: 'public',
|
|
|
|
|
defaultElement: 'div',
|
|
|
|
|
role: 'dialog',
|
|
|
|
|
optional: false,
|
|
|
|
|
states: ['open', 'closed'],
|
|
|
|
|
data: [
|
|
|
|
|
{ attr: 'data-state', values: ['open', 'closed'], value: v.stateRef('open') },
|
|
|
|
|
{
|
|
|
|
|
attr: 'data-stage',
|
|
|
|
|
values: ['default', 'minimized', 'maximized'],
|
|
|
|
|
value: v.propRef('stage'),
|
|
|
|
|
severity: 'optional'
|
|
|
|
|
},
|
|
|
|
|
{ attr: 'data-dragging', value: v.propRef('dragging'), severity: 'optional' },
|
|
|
|
|
{ attr: 'data-resizing', value: v.propRef('resizing'), severity: 'optional' },
|
fix(contracts): catalogue-guards census — 12 -> 3 failures (knob/menu-dial/onion-menu + unmasked debt)
The "3 ajenos" census. What the 12 pre-existing failures really were: a few
genuine violations + several guards CRASHING on menu-dial's missing
exports.ts (ENOENT) and hiding their true violation lists. All addressed
except the two runtime-discipline files (see tail).
- knob: soma barrel export + the missing soma README (headless contract —
parts/ARIA/absolute angular keyboard/props/published CSS vars/sema table).
- menu-dial + onion-menu: joined SOMA_INTERNAL_PRIMITIVES — both are
documented provider-less bridges (keyboard dispatch + nav maths + emission;
the eidos root owns state and render). The scope guard now subtracts the
internal set from BOTH sides so a bridge may truthfully keep 'soma' in its
morfo scope without being a public namespace. Self-alias imports fixed;
the two morfo docblocks realigned to the implementation ("soma owns open"
was drift — the bridge files own dispatch/emission only; onion-menu's
stale "family-default, no pack" tail also corrected, S3b shipped its pack).
- Public barrels decoupled from provider implementation filenames (the
picker `internals.ts` house pattern) for the 6 offenders: calendar,
cropper, field-langs, gradient-builder, gradient-picker, image-adjustments.
- listSourceFiles now skips ACTIVE_DEV_TRACK paths (words/palabras/chronos)
— the track doctrine already said catalogue guards skip them; the
file-scanning guards just didn't.
- data-archetype added to the known morfo attrs (compiler-emitted from part
archetype declarations); the four field-overlay picker triggers
(color-picker, date-range-picker, time-picker, time-range-picker) declared
archetype 'trigger' while stamping 'field-trigger' at runtime — morfos
aligned to the date-picker/ntp precedent (contract now matches the DOM;
zero runtime change, the stamp already won).
- float-panel: the consumer-placed data-float-panel-no-drag opt-out marker
is now declared in the morfo (presence entry with the consumer-marker
note) — the NON_DRAG_SELECTOR contract stops being invisible.
Remaining tail (3 failures, deliberately deferred to a dedicated
runtime-discipline session with browser gesture verification):
float-panel-provider direct style writes + raw timer (drag/resize hot path
-> dom.apply/uix.timers migration) and media-player media-provider raw
listeners (-> dom.listen).
Gates: contracts 34/37 (was 25/37) · component-audit 102 PASS · vocab exit 0
· type baseline 57 · eidos-lint invalid 0 on the four touched pickers ·
compile + eidos contract tests 42/42.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
|
|
|
// CONSUMER-placed opt-out marker (never emitted by the runtime):
|
|
|
|
|
// elements carrying it inside a `dragHandle="content"` panel do not
|
|
|
|
|
// start a drag — the provider's NON_DRAG_SELECTOR consumes it
|
|
|
|
|
// alongside the native interactive tags.
|
|
|
|
|
{ attr: 'data-float-panel-no-drag', severity: 'optional' },
|
|
|
|
|
// Which element grabs the drag — lets the recipe put the grab cursor
|
|
|
|
|
// on the whole surface when `dragHandle="content"`. `values` makes the
|
|
|
|
|
// runtime emit the STRING (header/content), not a boolean presence.
|
|
|
|
|
{
|
|
|
|
|
attr: 'data-drag-handle',
|
|
|
|
|
values: ['header', 'content'],
|
|
|
|
|
value: v.propRef('dragHandle'),
|
|
|
|
|
severity: 'optional'
|
|
|
|
|
},
|
|
|
|
|
{ attr: 'data-topmost', value: v.propRef('topmost'), severity: 'optional' },
|
|
|
|
|
{ attr: 'data-behind', value: v.propRef('behind'), severity: 'optional' },
|
|
|
|
|
{
|
|
|
|
|
attr: 'data-last-action',
|
|
|
|
|
values: ['saved', 'cancelled', 'dismissed', 'dismissed-outside', 'failed'],
|
|
|
|
|
severity: 'optional'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
attr: 'data-starting-style',
|
|
|
|
|
severity: 'optional',
|
|
|
|
|
condition: { when: 'state-equals', state: 'open', value: 'starting' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
attr: 'data-ending-style',
|
|
|
|
|
severity: 'optional',
|
|
|
|
|
condition: { when: 'state-equals', state: 'closed', value: 'ending' }
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
aria: [
|
|
|
|
|
{
|
|
|
|
|
attr: 'aria-labelledby',
|
|
|
|
|
value: v.propRef('ariaLabelledby'),
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'ariaLabelledby' },
|
|
|
|
|
severity: 'recommended'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
attr: 'aria-describedby',
|
|
|
|
|
value: v.propRef('ariaDescribedby'),
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'ariaDescribedby' },
|
|
|
|
|
severity: 'optional'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
attr: 'aria-modal',
|
|
|
|
|
value: v.literal('true'),
|
|
|
|
|
severity: 'optional',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'modal' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// SR users hear "… window" rather than just "dialog".
|
|
|
|
|
attr: 'aria-roledescription',
|
|
|
|
|
value: v.translationRef('content.roledescription', 'window'),
|
|
|
|
|
severity: 'recommended'
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
keyboard: [
|
|
|
|
|
{ key: 'Escape', action: 'close' },
|
|
|
|
|
{ key: 'Tab', action: 'focus-next' },
|
|
|
|
|
{ key: 'Shift+Tab', action: 'focus-prev' }
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// Drag handle. Grabbing it moves the panel. `data-dragging` lets the
|
|
|
|
|
// recipe swap the cursor (grab → grabbing).
|
|
|
|
|
name: 'Header',
|
|
|
|
|
kebab: 'header',
|
|
|
|
|
archetype: 'header',
|
|
|
|
|
kind: 'public',
|
|
|
|
|
defaultElement: 'div',
|
|
|
|
|
optional: true,
|
|
|
|
|
data: [
|
|
|
|
|
{ attr: 'data-dragging', value: v.propRef('dragging'), severity: 'optional' },
|
|
|
|
|
// Keyboard "grab" state — eidos styles a grabbed-handle ring.
|
|
|
|
|
{ attr: 'data-grabbed', value: v.propRef('moveMode'), severity: 'optional' }
|
|
|
|
|
],
|
|
|
|
|
aria: [
|
|
|
|
|
// Focusable drag-handle: a keyboard user tabs here and presses
|
|
|
|
|
// Enter/Space to enter a move "grab mode", then the arrows move it.
|
|
|
|
|
{ attr: 'tabindex', value: v.literal('0') },
|
|
|
|
|
{ attr: 'aria-keyshortcuts', value: v.literal('Enter Space') }
|
|
|
|
|
],
|
|
|
|
|
// Move keys are gated on `moveMode` so out-of-mode arrows BUBBLE (the
|
|
|
|
|
// runtime skips an entry whose condition is falsy → no preventDefault).
|
|
|
|
|
// One `kb-move` handler reads the event (key + Shift) and applies.
|
|
|
|
|
keyboard: [
|
feat(morfo): 28 morfos declaran su política de foco — la especie, por escrito
9 trap (dialog, drawer, alert-dialog, context-menu, dropdown-menu,
menu-dial, onion-menu, float-panel, popover) y 19 roving (toolbar, menubar,
tabs, listbox, radio-group, stepper, tag-group, tags-input, toggle-group,
carousel, drag-drop, tree-view, tree-grid, grid-list, chronos, calendar,
range-calendar, month-grid, year-grid).
Actas dentro de cada declaración; las que corrigen mentiras:
- popover: trap FALSE — declaraba true desde hacía meses con cero lectores;
la verdad embarcada manda.
- radio-group: orientation 'both' por APG — su propio keyboard[] declaraba
las 4 flechas; lo embarcado (2 flechas) era la deriva. El provider pasa
both=true al ejecutor.
- calendarios ×4 (calendar, range-calendar, month-grid, year-grid):
grid + executor.custom firmado — roving-tabindex REAL medido en sus
providers (focused ? 0 : -1), 2D que el primitivo 1D no expresa.
- grids/árboles (tree-grid, grid-list, chronos, carousel, drag-drop,
tree-view): custom firmado con su razón.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
|
|
|
{
|
|
|
|
|
key: 'Enter',
|
|
|
|
|
action: 'kb-move-toggle',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'draggable' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: ' ',
|
|
|
|
|
action: 'kb-move-toggle',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'draggable' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'Escape',
|
|
|
|
|
action: 'kb-move-cancel',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'moveMode' }
|
|
|
|
|
},
|
|
|
|
|
{ key: 'ArrowUp', action: 'kb-move', condition: { when: 'prop-truthy', prop: 'moveMode' } },
|
feat(morfo): 28 morfos declaran su política de foco — la especie, por escrito
9 trap (dialog, drawer, alert-dialog, context-menu, dropdown-menu,
menu-dial, onion-menu, float-panel, popover) y 19 roving (toolbar, menubar,
tabs, listbox, radio-group, stepper, tag-group, tags-input, toggle-group,
carousel, drag-drop, tree-view, tree-grid, grid-list, chronos, calendar,
range-calendar, month-grid, year-grid).
Actas dentro de cada declaración; las que corrigen mentiras:
- popover: trap FALSE — declaraba true desde hacía meses con cero lectores;
la verdad embarcada manda.
- radio-group: orientation 'both' por APG — su propio keyboard[] declaraba
las 4 flechas; lo embarcado (2 flechas) era la deriva. El provider pasa
both=true al ejecutor.
- calendarios ×4 (calendar, range-calendar, month-grid, year-grid):
grid + executor.custom firmado — roving-tabindex REAL medido en sus
providers (focused ? 0 : -1), 2D que el primitivo 1D no expresa.
- grids/árboles (tree-grid, grid-list, chronos, carousel, drag-drop,
tree-view): custom firmado con su razón.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
|
|
|
{
|
|
|
|
|
key: 'ArrowDown',
|
|
|
|
|
action: 'kb-move',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'moveMode' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'ArrowLeft',
|
|
|
|
|
action: 'kb-move',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'moveMode' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'ArrowRight',
|
|
|
|
|
action: 'kb-move',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'moveMode' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'Shift+ArrowUp',
|
|
|
|
|
action: 'kb-move',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'moveMode' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'Shift+ArrowDown',
|
|
|
|
|
action: 'kb-move',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'moveMode' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'Shift+ArrowLeft',
|
|
|
|
|
action: 'kb-move',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'moveMode' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'Shift+ArrowRight',
|
|
|
|
|
action: 'kb-move',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'moveMode' }
|
|
|
|
|
},
|
|
|
|
|
{ key: 'PageUp', action: 'kb-move', condition: { when: 'prop-truthy', prop: 'moveMode' } },
|
feat(morfo): 28 morfos declaran su política de foco — la especie, por escrito
9 trap (dialog, drawer, alert-dialog, context-menu, dropdown-menu,
menu-dial, onion-menu, float-panel, popover) y 19 roving (toolbar, menubar,
tabs, listbox, radio-group, stepper, tag-group, tags-input, toggle-group,
carousel, drag-drop, tree-view, tree-grid, grid-list, chronos, calendar,
range-calendar, month-grid, year-grid).
Actas dentro de cada declaración; las que corrigen mentiras:
- popover: trap FALSE — declaraba true desde hacía meses con cero lectores;
la verdad embarcada manda.
- radio-group: orientation 'both' por APG — su propio keyboard[] declaraba
las 4 flechas; lo embarcado (2 flechas) era la deriva. El provider pasa
both=true al ejecutor.
- calendarios ×4 (calendar, range-calendar, month-grid, year-grid):
grid + executor.custom firmado — roving-tabindex REAL medido en sus
providers (focused ? 0 : -1), 2D que el primitivo 1D no expresa.
- grids/árboles (tree-grid, grid-list, chronos, carousel, drag-drop,
tree-view): custom firmado con su razón.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
|
|
|
{
|
|
|
|
|
key: 'PageDown',
|
|
|
|
|
action: 'kb-move',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'moveMode' }
|
|
|
|
|
},
|
|
|
|
|
{ key: 'Home', action: 'kb-move', condition: { when: 'prop-truthy', prop: 'moveMode' } },
|
|
|
|
|
{ key: 'End', action: 'kb-move', condition: { when: 'prop-truthy', prop: 'moveMode' } }
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'Title',
|
|
|
|
|
kebab: 'title',
|
|
|
|
|
archetype: 'title',
|
|
|
|
|
kind: 'public',
|
|
|
|
|
defaultElement: 'div',
|
|
|
|
|
role: 'heading',
|
|
|
|
|
optional: true,
|
|
|
|
|
data: [],
|
|
|
|
|
aria: [{ attr: 'aria-level', value: v.propRef('level'), severity: 'recommended' }]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'Close',
|
|
|
|
|
kebab: 'close',
|
|
|
|
|
archetype: 'close',
|
|
|
|
|
kind: 'public',
|
|
|
|
|
defaultElement: 'button',
|
|
|
|
|
role: 'button',
|
|
|
|
|
optional: true,
|
|
|
|
|
data: [],
|
|
|
|
|
aria: [
|
|
|
|
|
{ attr: 'type', value: v.literal('button') },
|
|
|
|
|
{
|
|
|
|
|
attr: 'aria-label',
|
|
|
|
|
value: v.commonRef('buttons.close', 'Close'),
|
|
|
|
|
severity: 'recommended'
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// Optional button cluster in the Header (right side) — holds Close /
|
|
|
|
|
// Action and any custom buttons, grouped + right-aligned. Mirror of
|
|
|
|
|
// Ark's `control` group. Purely structural (no state / events).
|
|
|
|
|
name: 'Controls',
|
|
|
|
|
kebab: 'controls',
|
|
|
|
|
archetype: 'group',
|
|
|
|
|
kind: 'public',
|
|
|
|
|
defaultElement: 'div',
|
|
|
|
|
optional: true,
|
|
|
|
|
data: [],
|
|
|
|
|
aria: []
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// Generic Header action button (e.g. a delete icon in a properties
|
|
|
|
|
// panel). The panel does NOT own its behaviour — the consumer wires
|
|
|
|
|
// `onclick` + `aria-label` via passthrough. Repeatable.
|
|
|
|
|
name: 'Action',
|
|
|
|
|
kebab: 'action',
|
|
|
|
|
archetype: 'action',
|
|
|
|
|
kind: 'public',
|
|
|
|
|
defaultElement: 'button',
|
|
|
|
|
role: 'button',
|
|
|
|
|
optional: true,
|
|
|
|
|
data: [],
|
|
|
|
|
aria: [{ attr: 'type', value: v.literal('button') }]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// Switches the panel's stage (minimize / maximize / restore). The
|
|
|
|
|
// target stage is a soma-level prop; clicking it while already in that
|
|
|
|
|
// stage returns to 'default' (toggle). Repeatable.
|
|
|
|
|
name: 'StageTrigger',
|
|
|
|
|
kebab: 'stage-trigger',
|
|
|
|
|
archetype: 'trigger',
|
|
|
|
|
kind: 'public',
|
|
|
|
|
defaultElement: 'button',
|
|
|
|
|
role: 'button',
|
|
|
|
|
optional: true,
|
|
|
|
|
data: [],
|
|
|
|
|
aria: [{ attr: 'type', value: v.literal('button') }]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// Scrollable content region (below the Header).
|
|
|
|
|
name: 'Body',
|
|
|
|
|
kebab: 'body',
|
|
|
|
|
archetype: 'viewport',
|
|
|
|
|
kind: 'public',
|
|
|
|
|
defaultElement: 'div',
|
|
|
|
|
optional: true,
|
|
|
|
|
data: [],
|
|
|
|
|
aria: []
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// Resize affordance. Repeatable; `data-resize-edge` names which
|
|
|
|
|
// edge/corner this handle drives (n/s/e/w/ne/nw/se/sw).
|
|
|
|
|
name: 'ResizeHandle',
|
|
|
|
|
kebab: 'resize-handle',
|
|
|
|
|
archetype: 'thumb',
|
|
|
|
|
kind: 'public',
|
|
|
|
|
defaultElement: 'div',
|
|
|
|
|
role: 'presentation',
|
|
|
|
|
optional: true,
|
|
|
|
|
data: [
|
|
|
|
|
{ attr: 'data-resize-edge', severity: 'optional' },
|
|
|
|
|
{ attr: 'data-resizing', value: v.propRef('resizing'), severity: 'optional' }
|
|
|
|
|
],
|
|
|
|
|
aria: [{ attr: 'aria-hidden', value: v.literal('true') }]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// Resize affordance for the se corner — a focusable button that owns
|
|
|
|
|
// BOTH pointer resize (pointerdown → se) and keyboard resize (Enter/Space
|
|
|
|
|
// enters a grab mode, arrows grow/shrink se-anchored). The keyboard path
|
|
|
|
|
// makes resize fully keyboard-operable (WCAG 2.1.1), which the aria-hidden
|
|
|
|
|
// pointer-only ResizeHandle divs do not.
|
|
|
|
|
name: 'ResizeGrip',
|
|
|
|
|
kebab: 'resize-grip',
|
|
|
|
|
archetype: 'thumb',
|
|
|
|
|
kind: 'public',
|
|
|
|
|
defaultElement: 'button',
|
|
|
|
|
role: 'button',
|
|
|
|
|
optional: true,
|
|
|
|
|
data: [{ attr: 'data-grabbed', value: v.propRef('resizeMode'), severity: 'optional' }],
|
|
|
|
|
aria: [
|
|
|
|
|
{ attr: 'type', value: v.literal('button') },
|
|
|
|
|
{ attr: 'tabindex', value: v.literal('0') },
|
|
|
|
|
{ attr: 'aria-keyshortcuts', value: v.literal('Enter Space') }
|
|
|
|
|
],
|
|
|
|
|
keyboard: [
|
feat(morfo): 28 morfos declaran su política de foco — la especie, por escrito
9 trap (dialog, drawer, alert-dialog, context-menu, dropdown-menu,
menu-dial, onion-menu, float-panel, popover) y 19 roving (toolbar, menubar,
tabs, listbox, radio-group, stepper, tag-group, tags-input, toggle-group,
carousel, drag-drop, tree-view, tree-grid, grid-list, chronos, calendar,
range-calendar, month-grid, year-grid).
Actas dentro de cada declaración; las que corrigen mentiras:
- popover: trap FALSE — declaraba true desde hacía meses con cero lectores;
la verdad embarcada manda.
- radio-group: orientation 'both' por APG — su propio keyboard[] declaraba
las 4 flechas; lo embarcado (2 flechas) era la deriva. El provider pasa
both=true al ejecutor.
- calendarios ×4 (calendar, range-calendar, month-grid, year-grid):
grid + executor.custom firmado — roving-tabindex REAL medido en sus
providers (focused ? 0 : -1), 2D que el primitivo 1D no expresa.
- grids/árboles (tree-grid, grid-list, chronos, carousel, drag-drop,
tree-view): custom firmado con su razón.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
|
|
|
{
|
|
|
|
|
key: 'Enter',
|
|
|
|
|
action: 'kb-resize-toggle',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'resizable' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: ' ',
|
|
|
|
|
action: 'kb-resize-toggle',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'resizable' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'Escape',
|
|
|
|
|
action: 'kb-resize-cancel',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'resizeMode' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'ArrowUp',
|
|
|
|
|
action: 'kb-resize',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'resizeMode' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'ArrowDown',
|
|
|
|
|
action: 'kb-resize',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'resizeMode' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'ArrowLeft',
|
|
|
|
|
action: 'kb-resize',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'resizeMode' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'ArrowRight',
|
|
|
|
|
action: 'kb-resize',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'resizeMode' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'Shift+ArrowUp',
|
|
|
|
|
action: 'kb-resize',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'resizeMode' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'Shift+ArrowDown',
|
|
|
|
|
action: 'kb-resize',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'resizeMode' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'Shift+ArrowLeft',
|
|
|
|
|
action: 'kb-resize',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'resizeMode' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'Shift+ArrowRight',
|
|
|
|
|
action: 'kb-resize',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'resizeMode' }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'Home',
|
|
|
|
|
action: 'kb-resize',
|
|
|
|
|
condition: { when: 'prop-truthy', prop: 'resizeMode' }
|
|
|
|
|
},
|
|
|
|
|
{ key: 'End', action: 'kb-resize', condition: { when: 'prop-truthy', prop: 'resizeMode' } }
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
} as const satisfies Morfo;
|