parent
b052b238f3
commit
2dbf471b3f
@ -0,0 +1,59 @@
|
||||
---
|
||||
title: UIX — auditoría Sema de manipulación directa
|
||||
type: notes
|
||||
audience: human + agent
|
||||
authority: process — evaluación, no contrato de componente
|
||||
status: reviewed
|
||||
---
|
||||
|
||||
# Sema de Slider, ColorPicker y GradientBuilder
|
||||
|
||||
Fecha: 2026-09-23. Se releyeron las cuatro guías de componente exigidas por
|
||||
`AGENTS.md`, los README de ColorPicker y GradientBuilder, sus Morfos y la doctrina
|
||||
de packs de `docs/architecture/sema.md`. El usuario autoriza deduplicación interna
|
||||
que preserve el comportamiento; `web/` está excluido.
|
||||
|
||||
## Pre-flight
|
||||
|
||||
| Aspecto | Slider | ColorPicker | GradientBuilder | Decisión |
|
||||
| ---------------------- | ---------------------------------------- | ------------------------------ | ---------------------------- | -------------------------------------------------- |
|
||||
| Superficie de arrastre | Provider | Area | Track | Mantener el selector tipado y el target propio |
|
||||
| `handle-pick` | `air`, tap `tick` 0.22/6, sound + haptic | Idéntico | Idéntico | Compartir sólo la regla Sema |
|
||||
| `handle-drag` | Sound + haptic; payload de emisión | Idéntico | Idéntico | No añadir `sound` que pise el payload dinámico |
|
||||
| `commit-set` | Sin regla adicional en este pack | `settle`, tick 0.18/8 | Idéntico | Compartir la firma, conservar el selector Provider |
|
||||
| `commit-reset` | Sin regla adicional | Declarado, sin regla adicional | Misma firma que `commit-set` | Conservar la presencia distinta de la regla |
|
||||
|
||||
| Referencia oficial | Contrato pertinente | Consecuencia para UIX |
|
||||
| ------------------------------------------------------------------------ | --------------------------------------- | ----------------------------------------------------- |
|
||||
| [WAI-ARIA APG Slider](https://www.w3.org/WAI/ARIA/apg/patterns/slider/) | Thumb con valor accesible y teclado | No tocar Morfo ni interacción |
|
||||
| [Ark UI ColorPicker](https://ark-ui.com/docs/components/color-picker) | Area, sliders y fin de cambio separados | Mantener el commit distinto del drag |
|
||||
| [Chakra ColorPicker](https://chakra-ui.com/docs/components/color-picker) | Control, area y canales componibles | No unir providers ni superficies |
|
||||
| [React Aria ColorArea](https://react-aria.adobe.com/ColorArea) | Área de dos ejes con estado accesible | La política Sema puede coincidir sin compartir estado |
|
||||
|
||||
Morfo conserva partes, roles, valores ARIA y eventos; Soma conserva estado,
|
||||
pointer, teclado y emisiones; Sema posee únicamente las firmas de las reglas;
|
||||
Eidos no cambia. No se añaden props, tamaños, variantes, colores, partes, canales,
|
||||
demos ni API pública. Los sliders internos de ColorPicker y GradientBuilder siguen
|
||||
aportando sus propias señales; no se duplican en los packs padres.
|
||||
|
||||
## Hipótesis de extracción
|
||||
|
||||
Las dos primeras reglas son iguales en los tres packs salvo el selector. Las
|
||||
reglas `commit-set` de ColorPicker y GradientBuilder son iguales entre sí, y
|
||||
GradientBuilder repite esa firma para `commit-reset`. Dos constructores privados
|
||||
de reglas Sema pueden eliminar las copias dejando las llamadas a `semaSelector`
|
||||
en cada pack. La prueba de caracterización fijará los selectores, la lista
|
||||
completa de reglas y su orden antes de modificar producción. Si la salida o el
|
||||
tipado cambian, se abandona la extracción.
|
||||
|
||||
## Resultado de esta revisión
|
||||
|
||||
La prueba de caracterización de los tres packs pasa 170/170 junto con el censo,
|
||||
resolver y engine. `component:audit` da PASS para Slider y GradientBuilder,
|
||||
pero ColorPicker queda en NEEDS-WORK por un error previo `A-3.7`: ocho acciones
|
||||
de teclado mutantes frente a cuatro eventos semánticos; registra además el aviso
|
||||
`D-1.2` sobre la demo congelada. Ninguno procede de Sema. Corregir el primero
|
||||
requiere una decisión de contrato Morfo/Soma y el segundo afectaría `web/`, fuera
|
||||
del alcance. **No se extraen estas reglas en esta fase.** La caracterización
|
||||
queda como referencia para una revisión posterior; no hubo cambios de producción
|
||||
en esta familia.
|
||||
@ -0,0 +1,58 @@
|
||||
---
|
||||
title: UIX — auditoría Sema de CssField y NumberField
|
||||
type: notes
|
||||
audience: human + agent
|
||||
authority: process — evaluación, no contrato de componente
|
||||
status: reviewed
|
||||
---
|
||||
|
||||
# Firma Sema de CssField y NumberField
|
||||
|
||||
Fecha: 2026-09-23. Se releyeron las cuatro guías de componente de `AGENTS.md`,
|
||||
los Morfos, packs y emisiones de ambos providers. La autorización del usuario
|
||||
cubre sólo deduplicación interna sin cambiar comportamiento; `web/` queda fuera.
|
||||
|
||||
## Pre-flight
|
||||
|
||||
| Aspecto | NumberField | CssField | Decisión |
|
||||
| --------------------- | ---------------------------------------------- | -------------------------------------- | ----------------------------------- |
|
||||
| `commit-set` | Haptic `tap` en Provider | Idéntico | Una regla compartida |
|
||||
| `handle-pick` | `air`, tick 0.18/6, sound + haptic en Scrubber | Idéntico | Una regla compartida |
|
||||
| `handle-drag-scrub` | Tick 0.08/4, sólo haptic en Scrubber | Idéntico | Conservar el canal restringido |
|
||||
| `signal-warn-invalid` | No existe | Warning haptic en Input | Conservar la regla local y al final |
|
||||
| Estado y valor | Número y límites | Número, unidad y validación de formato | Providers separados |
|
||||
|
||||
| Referencia oficial | Contrato relevante | Consecuencia para UIX |
|
||||
| ------------------------------------------------------------------------------- | ----------------------------------------- | ----------------------------------- |
|
||||
| [WAI-ARIA APG Spinbutton](https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/) | Edición y teclado en el campo, valor ARIA | No cambiar Morfo ni foco |
|
||||
| [Ark UI Number Input](https://ark-ui.com/docs/components/number-input) | Input y controles de incremento separados | No unir estructura ni providers |
|
||||
| [Chakra Number Input](https://chakra-ui.com/docs/components/number-input) | Scrubber y steps como superficies propias | Compartir sólo firma Sema |
|
||||
| [React Aria NumberField](https://react-aria.adobe.com/NumberField) | Valor, teclado y controles accesibles | Mantener la gestión de estado local |
|
||||
|
||||
Morfo conserva roles, eventos, target, teclado y ARIA; Soma conserva valores,
|
||||
unidades, validación, temporización y emisión; Sema sólo construye las tres
|
||||
reglas idénticas con los selectores tipados de cada Morfo; Eidos no cambia.
|
||||
No se añaden partes, props, tamaños, variantes, colores, canales, demos ni API.
|
||||
|
||||
## Hipótesis de extracción
|
||||
|
||||
Los packs repiten exactamente las tres primeras reglas en el mismo orden. Un
|
||||
helper privado puede recibir `onProvider` y `onScrubber` de cada pack y devolver
|
||||
esas tres reglas. CssField mantendrá su cuarto selector `onInput` para la señal
|
||||
de valor inválido. Antes de modificar producción se fijará la cascada completa
|
||||
de ambos packs en una prueba; después se comparará la misma salida y se
|
||||
ejecutarán los tests de providers, el censo Sema, la auditoría dirigida y el gate.
|
||||
|
||||
## Resultado
|
||||
|
||||
La línea base pasó 191/191 tests en seis archivos (cascada, censo, resolver,
|
||||
engine y ambos providers); las dos auditorías dirigidas dieron PASS. El helper
|
||||
`components/internal/spin-field-cascade.ts` recibe los constructores locales de
|
||||
selectores, por lo que la comprobación de eventos y partes sigue dependiendo del
|
||||
Morfo de cada pack. NumberField usa las tres reglas; CssField las coloca antes de
|
||||
su advertencia local. La misma suite dirigida volvió a pasar 191/191.
|
||||
|
||||
El diff de producción añade 29 líneas y retira 34: **cinco líneas físicas netas
|
||||
menos**. El beneficio principal es una sola definición de las tres firmas y
|
||||
su orden, sin alterar eventos, targets, temporización, DOM ni CSS. No se hizo
|
||||
revisión visual manual porque esta extracción no toca render ni receta.
|
||||
@ -0,0 +1,25 @@
|
||||
import type { Sema } from '../../sema-map';
|
||||
|
||||
type OnProvider = (matchers: { eventName: 'commit-set' }) => string;
|
||||
type OnScrubber = (matchers: { eventName: 'handle-pick' | 'handle-drag-scrub' }) => string;
|
||||
|
||||
/** Shared spin-field signature; each pack supplies selectors from its own morfo. */
|
||||
export function spinFieldCascade(onProvider: OnProvider, onScrubber: OnScrubber): Sema['cascade'] {
|
||||
return [
|
||||
{
|
||||
selector: onProvider({ eventName: 'commit-set' }),
|
||||
haptic: { kind: 'tap' }
|
||||
},
|
||||
{
|
||||
selector: onScrubber({ eventName: 'handle-pick' }),
|
||||
channels: ['sound', 'haptic'],
|
||||
sound: 'air',
|
||||
haptic: { kind: 'tick', intensity: 0.18, duration: 6 }
|
||||
},
|
||||
{
|
||||
selector: onScrubber({ eventName: 'handle-drag-scrub' }),
|
||||
channels: ['haptic'],
|
||||
haptic: { kind: 'tick', intensity: 0.08, duration: 4 }
|
||||
}
|
||||
];
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { semaSelector } from '$uix/morfo';
|
||||
import { colorPickerMorfo } from '$uix/morfo/components/color-picker';
|
||||
import { gradientBuilderMorfo } from '$uix/morfo/components/gradient-builder';
|
||||
import { sliderMorfo } from '$uix/morfo/components/slider';
|
||||
import { colorPickerSema } from './components/color-picker';
|
||||
import { gradientBuilderSema } from './components/gradient-builder';
|
||||
import { sliderSema } from './components/slider';
|
||||
|
||||
describe('direct manipulation perceptual packs', () => {
|
||||
it('keeps Slider pickup and drag on its provider', () => {
|
||||
expect(sliderSema.cascade).toEqual([
|
||||
{
|
||||
selector: semaSelector(sliderMorfo, 'provider', { eventName: 'handle-pick' }),
|
||||
channels: ['sound', 'haptic'],
|
||||
sound: 'air',
|
||||
haptic: { kind: 'tick', intensity: 0.22, duration: 6 }
|
||||
},
|
||||
{
|
||||
selector: semaSelector(sliderMorfo, 'provider', { eventName: 'handle-drag' }),
|
||||
channels: ['sound', 'haptic']
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it('keeps ColorPicker pickup/drag on area and release on provider', () => {
|
||||
expect(colorPickerSema.cascade).toEqual([
|
||||
{
|
||||
selector: semaSelector(colorPickerMorfo, 'area', { eventName: 'handle-pick' }),
|
||||
channels: ['sound', 'haptic'],
|
||||
sound: 'air',
|
||||
haptic: { kind: 'tick', intensity: 0.22, duration: 6 }
|
||||
},
|
||||
{
|
||||
selector: semaSelector(colorPickerMorfo, 'area', { eventName: 'handle-drag' }),
|
||||
channels: ['sound', 'haptic']
|
||||
},
|
||||
{
|
||||
selector: semaSelector(colorPickerMorfo, 'provider', { eventName: 'commit-set' }),
|
||||
channels: ['sound', 'haptic'],
|
||||
sound: 'settle',
|
||||
haptic: { kind: 'tick', intensity: 0.18, duration: 8 }
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it('keeps GradientBuilder pickup/drag on track and both releases on provider', () => {
|
||||
expect(gradientBuilderSema.cascade).toEqual([
|
||||
{
|
||||
selector: semaSelector(gradientBuilderMorfo, 'track', { eventName: 'handle-pick' }),
|
||||
channels: ['sound', 'haptic'],
|
||||
sound: 'air',
|
||||
haptic: { kind: 'tick', intensity: 0.22, duration: 6 }
|
||||
},
|
||||
{
|
||||
selector: semaSelector(gradientBuilderMorfo, 'track', { eventName: 'handle-drag' }),
|
||||
channels: ['sound', 'haptic']
|
||||
},
|
||||
{
|
||||
selector: semaSelector(gradientBuilderMorfo, 'provider', { eventName: 'commit-set' }),
|
||||
channels: ['sound', 'haptic'],
|
||||
sound: 'settle',
|
||||
haptic: { kind: 'tick', intensity: 0.18, duration: 8 }
|
||||
},
|
||||
{
|
||||
selector: semaSelector(gradientBuilderMorfo, 'provider', { eventName: 'commit-reset' }),
|
||||
channels: ['sound', 'haptic'],
|
||||
sound: 'settle',
|
||||
haptic: { kind: 'tick', intensity: 0.18, duration: 8 }
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,57 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { semaSelector } from '$uix/morfo';
|
||||
import { cssFieldMorfo } from '$uix/morfo/components/css-field';
|
||||
import { numberFieldMorfo } from '$uix/morfo/components/number-field';
|
||||
import { cssFieldSema } from './components/css-field';
|
||||
import { numberFieldSema } from './components/number-field';
|
||||
|
||||
describe('spin field perceptual packs', () => {
|
||||
it('keeps NumberField commit and scrub signatures on their own targets', () => {
|
||||
expect(numberFieldSema.cascade).toEqual([
|
||||
{
|
||||
selector: semaSelector(numberFieldMorfo, 'provider', { eventName: 'commit-set' }),
|
||||
haptic: { kind: 'tap' }
|
||||
},
|
||||
{
|
||||
selector: semaSelector(numberFieldMorfo, 'scrubber', { eventName: 'handle-pick' }),
|
||||
channels: ['sound', 'haptic'],
|
||||
sound: 'air',
|
||||
haptic: { kind: 'tick', intensity: 0.18, duration: 6 }
|
||||
},
|
||||
{
|
||||
selector: semaSelector(numberFieldMorfo, 'scrubber', {
|
||||
eventName: 'handle-drag-scrub'
|
||||
}),
|
||||
channels: ['haptic'],
|
||||
haptic: { kind: 'tick', intensity: 0.08, duration: 4 }
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it('keeps CssField warning after the identical commit and scrub signatures', () => {
|
||||
expect(cssFieldSema.cascade).toEqual([
|
||||
{
|
||||
selector: semaSelector(cssFieldMorfo, 'provider', { eventName: 'commit-set' }),
|
||||
haptic: { kind: 'tap' }
|
||||
},
|
||||
{
|
||||
selector: semaSelector(cssFieldMorfo, 'scrubber', { eventName: 'handle-pick' }),
|
||||
channels: ['sound', 'haptic'],
|
||||
sound: 'air',
|
||||
haptic: { kind: 'tick', intensity: 0.18, duration: 6 }
|
||||
},
|
||||
{
|
||||
selector: semaSelector(cssFieldMorfo, 'scrubber', {
|
||||
eventName: 'handle-drag-scrub'
|
||||
}),
|
||||
channels: ['haptic'],
|
||||
haptic: { kind: 'tick', intensity: 0.08, duration: 4 }
|
||||
},
|
||||
{
|
||||
selector: semaSelector(cssFieldMorfo, 'input', { eventName: 'signal-warn-invalid' }),
|
||||
haptic: { kind: 'warning' }
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in new issue