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/aura/README.md

19 KiB

Aura

The agent presence surface — the eidos face of the canonical materializer of the delegate family (book ch. 29: ¿quién actúa ahora?). Soma owns the custody-of-control state, the axis's one polite live region and the global exit; Eidos adds the living orb (a $scene aurora modulated by state) and the size axis. Doctrine: docs/architecture/agent.md §7.

Baseline

  • Classification: interactive (a Cancel trigger). Composes the soma headless provider ($soma/components/aura) over a structural agent port; with no agent the surface is a valid idle (§0 degradation).
  • Anatomy: Aura (root, data-state) → .Ring (the custody ring — it WRAPS the Indicator) → .Indicator (the orb, aria-hidden; the $scene effect paints inside) + .Status (state + detail, two lines) + .Region (the axis's ONE role="status" polite live region) + .Cancel (the global exit, composes <Button> via asChild). The recipe's radial gradient is the static truth ONLY where no canvas mounted (:has(canvas) drops it): with the live render up, a filled disc behind a contour just muddies its silhouette.
  • Two axes: size (sm | md | lg — orb + label scale, orb derived from the Avatar primitive --avatar-size-*) and the semantic data-state tint (soma-owned; the recipe maps it to role slots — reviewing = risk, escalated = threat, acting/offered/planned = primary, idle = neutral). Aura has no color prop by design: the tint is evaluative, not decorative; a theme re-tints via the --aura-orb-color-* role tokens.
  • The orb is the D4 consumer: .Indicator prefers the shared uix.scene engine (global concurrent-scene budget) with a per-dom fallback outside a uix tree; under reduced motion the engine's mandatory static-frame policy freezes a painted frame — presence never disappears.
  • The orb is a CONTOUR (thickness: 0.9): the core is hollow at every value and what shows through it is the page. The effect used to blend with a backgroundColor and derive alpha from the max channel, which painted an opaque disc of surface colour in the middle (a light theme got a filled circle, a dark one the seed's deep blue). Now draw() returns the rim's own alpha, so the hole is real on ANY ground and the light/dark branch is gone.
  • The orb is PAINTED in the role's colours (colorA = solid, colorB = solid-hover, colorDeep = element), never hue-rotated: the shader rotates in YIQ, so a delta measured in OKLCH lands somewhere else entirely — risk used to read GREEN next to its own orange ring. Tokens resolve through eidos.resolveToken and travel as concrete hex, which is exactly the seam $scene/effects/color.ts documents.

Talla y tema

37 clave(s) pública(s) en lib/recipes/base.ts (bloque aura), con sus pasos por talla y el nombre RESUELTO que consume la receta. Es el contrato vivo: cada clave es un --{componente}-{ranura} que un tema puede escribir, y el guard R-5.4 comprueba que ninguna miente.

Su demo todavía no tiene pestaña Tokens (el paso 11 del protocolo §7 llegó después de que este componente cerrara): la tabla de aquí es la fuente hasta que la tenga.

Grupo Claves
escalas status-font-size-md · status-line-height-md · status-letter-spacing-md · status-font-size-lg · status-line-height-lg · status-letter-spacing-lg · status-detail-font-size-md · status-detail-line-height-md · status-detail-letter-spacing-md · status-detail-font-size-lg · status-detail-line-height-lg · status-detail-letter-spacing-lg · orb-size-sm · orb-size-md · orb-size-lg
raíz idle-opacity · orb-radius · gap · orb-color-idle · orb-color-offered · orb-color-planned · orb-color-reviewing · orb-color-acting · orb-color-escalated · orb-halo-alpha · ring-scale · ring-track-fg · ring-track-width · ring-arc-width · ring-halo-width · ring-halo-opacity · status-fg · status-weight · status-gap · status-detail-fg · transition-duration · transition-ease

Lo que el guard R-5.4 da por silencioso, con su razón medida (scripts/theming-sentinel-exceptions.ts):

  • orb-color-idle — the canvas covers the disc by design; removed in place -> reaches (oklch(0.6434 0 0) -> rgb(1,2,3) inside the radial-gradient)
  • orb-color-offered — same disc, same pass
  • orb-color-planned — same disc, same pass
  • orb-color-reviewing — same disc, same pass
  • orb-color-acting — same disc, same pass
  • orb-color-escalated — same disc, same pass
  • orb-halo-alpha — the outer stop of the same gradient; removed the canvas -> the gradient changes
  • ring-arc-width — the arc slot is not mounted in the idle demo; mounted in place -> reaches (2px -> 1234px)
  • ring-halo-width — the halo slot is not mounted; mounted in place -> reaches (1px -> 1234px)
  • ring-halo-opacity — same slot; mounted in place -> reaches (0.5 -> 0.123)
  • transition-duration — the guard freezes transitions on purpose; the orb tint and the arc stroke transition on it

Comparativa

Agent-presence-with-a11y is a near-empty landscape — no shipping framework has a perceptual surface for the delegation cycle. The refs are adjacent, not equivalent; the comparativa maps what each does and what Aura adds.

Ref Equivalente Qué adoptamos Qué no
ChatGPT / Claude "thinking" indicator pulsing dot while the model works la continuidad pensando → actuando como estado perceptible indicador SOLO visual, sin región viva ni máquina de estados — un usuario de lector de pantalla no sabe que el agente actúa
CopilotKit activity / generative-UI render de actividad de tool en el transcript la idea de superficie de presencia del agente acoplado al chat (no hay presencia sin conversación); sin semántica de «quién actúa» ni a11y dedicada
Vercel AI SDK useChat.status submitted / streaming / ready mapear el estado del run a una superficie un string de estado, no un canal perceptivo (sin sonido/motion/hold ni anuncio atribuido)
Presencia colaborativa (Figma / Google Docs) «X está editando» (cursores/avatares) el precedente a11y de anunciar a otro actor (live region, «quién») modela HUMANOS concurrentes, no el ciclo de delegación (offer→review→act→return) ni la autoridad/autonomía
assistant-ui estado running por mensaje — estado por-mensaje, no una presencia del agente con salida global y ciclo tipado

Lo que Aura añade y nadie tiene (informe §8.3): una semántica perceptiva de la delegación (delegate + sustain con sonido/motion/hold), anuncios atribuidos y coalescidos por plan (WCAG 4.1.3), reduced-motion que conserva la presencia (static-frame, nunca hide), y una salida global por teclado.

Decisiones

  • Sin prop color: el tinte es la valoración del estado (evaluativo), no decoración — cargar color invitaría al antipatrón «marco rojo». El tema re-tinta los slots de rol --aura-orb-color-*.

  • expression: 'family-default': el perfil sonoro bajo de delegate es decisión firmada de capa 1 (los canales fuertes aparecen en composición con signal/commit); la modulación por intent reservada en scene §F6 (velocidad/amplitud/hue ≈ pitch/gain/contour) es VISUAL → recipe + $scene, no un pack de sonido.

  • Orb = Avatar: el tamaño deriva de la primitiva canónica del disco de presencia/identidad (--avatar-size-{sm,md,lg} = 32/40/48); un tema que retoca avatares retoca el orb. (Los 14/20/28 iniciales eran magic numbers inventados — corregido.)

  • Anillo de custodia (D1, 2026-07-22): el estado se lee como FORMA, no solo como tinte — raíl punteado en reposo, un arco que respira al ofrecerse, segmentos = pasos del plan (late), dos arcos enfrentados y QUIETOS esperando tu revisión (la quietud lo separa de acting), un arco que gira actuando, y anillo cerrado con pulso saliente cuando te necesita. Ningún estado depende del color. El movimiento continuo son loops registrados (data-animation-style="spin|pulse|ping"), nunca @keyframes de receta (R-4.5): vienen con su puerta de reduced-motion, donde el dash sigue distinguiendo el estado. El atributo va en los hijos del SVG, jamás en la parte — motionAttrs() estampa además data-state, que en ese subárbol es del morfo.

  • Variante portal (2026-07-28, encargo del usuario): el mismo ciclo leído como una ESFERA a contraluz — estelas de luz que la envuelven y giran, cada capa sobre su eje inclinado y con su ritmo, con motion blur a lo largo del barrido. La elige el desarrollador: no sustituye al orb, convive con él. orb está afinado para 32–48px (silueta simple, movimiento perceptible a ese tamaño); portal tiene mucho más detalle y luce en superficies grandes — a tamaño de componente pierde la irregularidad que lo define. La demo trae un switch studio (220px) precisamente para poder juzgar eso.

    Implementación propia ($scene/effects/portal), no un port: la referencia del look es un componente de pago cuya fuente no está disponible, y la regla de la casa son implementaciones propias de todos modos.

    Los tres colores salen de los MISMOS slots de rol que el orb — solid en el filo, solid-hover en la cara cercana, element en la que se aleja — así que un tema los retinta a la vez. El interior es TRANSPARENTE: no se pinta nada dentro, así que lo que se ve a través es la página. Eso es lo que hace la variante agnóstica al tema sin una sola rama claro/oscuro — un interior relleno tendría que elegir bando, y elegirlo fue lo que en el primer intento la convirtió en una bola de cristal.

    Modulación §F6 propia: speed = ritmo del barrido (pitch), layers = cuántas capas vuelan, density = cuán apretadas, brightness = gain; escalated gana capas Y corre caliente — forma y energía juntas, nunca color solo (AG-44).

    Tres lecciones que costaron render, anotadas porque son del FORMATO y no de este efecto: (1) las estelas no pueden salir de senos — un sin() da anillos de latitud perfectamente regulares que se leen como jaula de alambre por mucho que se afinen; la irregularidad exige ruido anisótropo (aquí 1 : 9 en longitud/latitud). (2) sin(q.y * freq) sobre la esfera unidad da freq/π ciclos: ~9 son tres trazos finos por capa, 22 es malla y 2 es un borrón — el número sale de la cuenta, no del tanteo. (3) el motion blur promedia: 5 muestras × 0.16 rad son 46° de barrido, que borra las bandas en vez de estirarlas.

  • Variante orb por defecto (2026-07-22, decisión del usuario): el render vivo del indicator es el efecto orb de $scene — presencia esférica con anillo respirando (ruido) + luz orbitando; movimiento de silueta perceptible a 32–48px, donde el campo aurora (banda horizontal recortada al círculo) leía estático. aurora queda como variante (<Aura.Indicator variant="aurora">) para superficies grandes. Modulación §F6 del orb: el estado fija la ENERGÍA (forceHoverState sostiene la distorsión, hoverIntensity es el gain, rotateOnHover acumula rotación en acting/escalated) y el rol da el matiz como DELTA de hue OKLCH sobre la identidad del shader (primary; reviewing=risk, escalated=threat). El puntero deforma el orb (el hover machine del efecto) — la presencia es táctil además de visible.

  • Cancel compone <Button> vía asChild (nunca el <button> nativo del soma); variant/size/color diferidos a los defaults del Button.

  • La Region es sr-only en el recipe (present para AT, silenciosa a la vista); el orb es la presencia visible.

Status: dos líneas

El estado lleva el peso (--aura-status-fg = content-primary, --aura-status-weight = medium) y el detalle recede (--aura-status-detail-fg = content-subtle, un escalón menos de tamaño). Es la mitad de D1 que faltaba: el anillo dice el estado como FORMA, pero la respuesta a «¿quién actúa?» es texto, y una palabra gris de 13px junto a un orbe vivo no la daba. Los dos renglones escalan con data-size.

R-1.5 exception: the only focusable part is Cancel, which composes <Button> — the focus-ring is owned by the Button recipe (THM §32). The orb, status and region are non-interactive, so the aura recipe declares no focus treatment of its own.

Patrón: Aura al centro de un OnionMenu (medio anillo, luminoso)

Composición APP-LAND canónica (doctrina agent.md §7 — el framework da estados + afordancias, el app da forma a la superficie de review): el orb es el trigger central de un <OnionMenu placement="center"> y las afordancias de custodia aparecen como sectores CONTEXTUALES por estado; el menú se abre solo cuando la custodia espera a un humano. Vivo en /active/docs/agnt (§Real model).

El anillo abre 180° (placement="bottom-center") en vez de 360°: dos sectores de 90° hacia arriba, donde el ojo ya está y sin tapar lo que se revisa. El trigger va sin chrome (data-chrome="bare": sin disco, sin sombra FAB, sin salto en hover) porque el orbe ES el trigger, no su contenido. Y los sectores usan look="luminous" — filo de luz en vez de relleno — para que el menú hable el idioma del orbe y no le pegue dos cuñas planas al lado.

<Aura {agent}>
	<OnionMenu bind:open placement="bottom-center" look="luminous"
		innerRadius={44} ringWidth={40}>
		{#snippet trigger()}
			<Aura.Ring><Aura.Indicator /></Aura.Ring>
		{/snippet}
		<OnionLayer>
			{#if run?.state === 'reviewing'}
				<OnionOption label="Authorize" color="affirm" onSelect={authorize} />
				<OnionOption label="Reject" color="loss" onSelect={reject} />
			{:else if run?.state === 'escalated'}
				<OnionOption label="Approve" color="affirm" onSelect={approve} />
				<OnionOption label="Deny" color="loss" onSelect={deny} />
			{:else if active}
				<OnionOption label="Stop" color="neutral" onSelect={stop} />
			{/if}
		</OnionLayer>
	</OnionMenu>
	<Aura.Status />
	<Aura.Region />
</Aura>

<!-- custody waits => the ring opens itself; it collapses when control
     RETURNS (a transition), not whenever there is no run — at rest the ring
     stays available, because opening it is how you delegate. -->
$effect(() => {
	const s = run?.state;
	if ((s === 'reviewing' || s === 'escalated') && !open) open = true;
	if (wasActive && !active) open = false;
	wasActive = active;
});

Never toggle open from onTriggerClick: the root already owns open and has toggled it before that callback fires — doing it again cancels it out and the ring never shows.

Colour is the axis's vocabulary, one hue = one meaning (a hue that means two things on the same surface destroys self-perception): gray = no agency · orange = your review · purple = the agent acts · red = the standing escalated claim, exclusive · teal = your yes. So the authority ring reuses the custody colours with the SAME meaning (Review = risk because you will live in reviewing; Auto = primary because it acts), and declines go neutral — canon-aligned too, since a cancel's intent is absent, not loss.

Sema events

Six — the delegate cycle (first eidos component to react to the family): sustain-processing (stateBound; orb tint + scene energy while acting), delegate-offer, delegate-escalate (untilAction), and three terminals picked by the run outcome: delegate-return (fulfill) · delegate-return-aborted (no intent) · delegate-return-failed (loss).

The terminals target the Ring and are the first delegate firmas in the motion registry (BUILTIN_SIGNATURES): fulfilled pulses like a fulfilled commit, failed plays return-recoil — a dip + dim + recovery. The loss firma is motion, not tint (GUIA §11: loss exits by withdrawal): no new hue appears, because red belongs exclusively to the standing escalated claim and loss's plum sits 16° from primary in the default theme. aborted stays silent on purpose. The Ring carries border-radius: full so the box-shadow pulses land circular. Full table in the soma README ($soma/components/aura).

Gaps

Gap Disposition
Presets de escena por estado (carácter de aurora distinto más allá de speed/amplitude/hue) diferir — la modulación actual cubre el ciclo; se especializa cuando el diseño lo pida
Superficie de elicitación en escalated (pregunta tipada vía Form.AutoFields) construible ya (desbloqueada 2026-07-23: no depende de superficie conversacional — PLAN-agent §5b). Falta la costura en el motor: la escalada no lleva la pregunta y resolveEscalation no lleva la respuesta
Variante compacta/inline (solo orb, sin status/cancel) para toolbars diferir — hasta que un host real lo pida (la barra de admisión)
Prop color decorativo descartar — el tinte es evaluativo (estado del run); un color invitaría al antipatrón «marco rojo». El tema re-tinta los slots de rol

Powered by TurnKey Linux.