diff --git a/src/uix/eidos/components/skin-media-player/README.md b/src/uix/eidos/components/skin-media-player/README.md index be170ae24..9660c1a36 100644 --- a/src/uix/eidos/components/skin-media-player/README.md +++ b/src/uix/eidos/components/skin-media-player/README.md @@ -2,154 +2,182 @@ Fecha: 2026-08-15. -Una **piel** para el `MediaPlayer`: la cara del reproductor es un objeto físico -dibujado — un disco, un tocadiscos, un casete o una pletina. El comportamiento -es del player entero; aquí sólo vive la representación y las afordancias que ese -objeto tiene de verdad. - -```svelte - - - -``` - -Para componer a mano, la pieza suelta dentro de cualquier player: +La cara del `MediaPlayer` como **objeto físico dibujado**: un disco, un +tocadiscos, un casete o una pletina. Es una **pieza dentro** del player, nunca +un player: se monta junto a las partes del propio `MediaPlayer`, que conservan +el volumen, la búsqueda y todo lo demás. ```svelte - + ``` -## Baseline +**La forma del reproductor no es suya.** `inline · bar · row · card` es el eje +`variant` de ``, y la piel viaja dentro como hija: -**No hay baseline.** `air` no tenía nada así, y las referencias headless del -proyecto (Radix / Ark / Bits / React Aria) no definen ni un media player, menos -aún una piel figurativa. El punto de partida son cuatro bocetos SVG del autor -(`web/routes/otros/players/`, banco de pruebas con estado simulado y sin audio) -y las dos pieles que ya existen en la casa: `ProofOfHuman.Clock` sobre el motor -`RotateAlign`, y las cuatro variantes de `AudioPlayer` sobre este mismo morfo. +```svelte + + + +``` + +## Cómo está montado + +Un componente cablea; los aparatos sólo pintan. + +| Fichero | Papel | +| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------- | +| `skin-media-player.svelte` | **El** cableado: un `MediaPlayerProvider.require()`, una derivación de todo, y las teclas generadas desde el mapa | +| `controls.ts` | Datos: por aparato, su lienzo, su tamaño natural y `[{ id, x, y, w, h, action }]` | +| `actions.ts` | El vocabulario único: `play · pause · toggle · stop · seek-back · seek-fwd · rpm · none`, resuelto una vez | +| `geometry.ts` | Matemática pura: enganche, brazo, bobinas, contador | +| `type-fit.svelte.ts` | El ajuste de ``, una vez para las dos superficies de papel | +| `actions.ts` → `SkinFader` | La ranura del volumen por máquina: un hueco, no un dial | +| `skin-media-player-{objeto}.svelte` | **Sólo pintura**: props entran, SVG sale. Sin provider, sin efectos, sin contexto | +| `skin-media-player-key.svelte` | El ` - {/each} - -
- {#each VARIANTS as v (v)} - - {/each} -
- - - -
-

MediaPlayer compuesto a mano

- - - - - - - - - - - - - - -
- - -
-

AudioPlayer variant="{variant}"

- - - -
- diff --git a/web/routes/uix/+layout@.svelte b/web/routes/uix/+layout@.svelte index 961493ec8..12b84541c 100644 --- a/web/routes/uix/+layout@.svelte +++ b/web/routes/uix/+layout@.svelte @@ -342,7 +342,8 @@ { slug: '/uix/components/carousel', label: 'Carousel' }, { slug: '/uix/components/qr-code', label: 'QR code' }, { slug: '/uix/components/barcode', label: 'Barcode' }, - { slug: '/uix/components/media-player', label: 'Media player' } + { slug: '/uix/components/media-player', label: 'Media player' }, + { slug: '/uix/components/skin-media-player', label: 'Media player skin' } ] }, { diff --git a/web/routes/uix/components/skin-media-player/+page.svelte b/web/routes/uix/components/skin-media-player/+page.svelte new file mode 100644 index 000000000..b73c74de9 --- /dev/null +++ b/web/routes/uix/components/skin-media-player/+page.svelte @@ -0,0 +1,787 @@ + + +
+
+
Media · SkinMediaPlayer
+

Media player skin

+

+ The face of a MediaPlayer as a drawn physical object — a record, a turntable, a + cassette, a deck. It is a piece inside the player, never a player: it mounts + beside the player's own parts, which keep volume, seeking and everything else. What the object + really has — a MARCHA key, a cueing lever, a piano keyboard — is wired to the real + MediaPlayer parts; what it does not have, it does not invent. One component wires; + each device contributes only its SVG and a row of coordinates. +

+
+ skins{SKINS.length} + own morfonone (eidos-only) + contract{mediaPlayerMorfo.kebab} +
+
+ + +
+
+
+ {#key host} + {#if host === 'audio-player'} + + + + + {:else} + + + + {#if useDefault} + + {:else} + + + + + + + + + {/if} + + {/if} + {/key} +
+
+
+ trace + {#if trace.entries.length === 0} + press a key on the machine — the events are the player's, not the skin's + {:else} + {#each trace.entries.slice(0, 3) as entry} + {entry.event} · {entry.family}{entry.intent + ? ' · ' + entry.intent + : ''} + {/each} + {/if} +
+
+ + +
+ + + + + + + + + +
+ + {#if tab === 'live'} +
+

Controls

+

+ skin chooses the depicted object; host chooses who mounts + it. Both machines drive the real transport: MARCHA and REPROD only start (a latched key must + not pause), PAUSA and the cueing lever only pause, PARO/STOP rewind to the beginning. GRAB + is painted unavailable — recording is capture, not playback, and the + MediaProvider port has none. +

+ +
+ eidos skin +
+
+ + {#if skin === 'record' || skin === 'turntable'} + + {/if} + + +
+ +
+ eidos host player +
+
+ + {#if host === 'audio-player'} + + {:else} + + {/if} + +
+ +
+ eidos identity + what the object prints on itself +
+
+ + + + + {#if skin === 'cassette' || skin === 'deck'} + + + {/if} + {#if skin === 'turntable' || skin === 'deck'} + + {/if} +
+ +
+
+ eidos + a piece inside the player + svelte +
+
{eidosSnippet}
+
+
+ {/if} + + {#if tab === 'system'} +
+

eidos System axes

+

+ Foundation knobs applied live to the stage. mode is the interesting one + here: the skin is a fixed-tone component — mahogany, brushed aluminium, chrome and + ivory are the depiction, not theme roles, so the object reads identically in light and dark + while the player's control bar around it follows the theme. dir does not mirror + the machine either: a tonearm sits on the right of every turntable ever built. +

+ +
+ {/if} + + {#if tab === 'motion'} +
+

Motion catalog

+

+ Nothing in this skin is hand-rolled. Every continuous turn — platter, strobe, free-running + reels — is the system's spin loop and the tape's breathing is + pulse, stamped as data-animation-style with the period written + into the loop's own knob (--motion-loop-spin: 1.333s at 45 rpm, + 1.8s at 33). Transitions consume --duration-* / + --ease-* through a --_smp-* slot per axis. A paused machine holds + its turn in place (animation-play-state), and a stopped one drops the preset so + the disc returns to 0°. The one local keyframe left is the VU needle: no preset depicts an + analogue meter. +

+ +
+ {/if} + + {#if tab === 'sema'} +
+

sema firma + sound

+

+ The skin declares no event. These {actions.length} are the + media-player's, and that is the whole point: a depicted key composes the real + part, so pressing MARCHA fires the player's commit-toggle-play exactly as the + canonical button does. Turn on Semantics → Sound in the topbar, then ▶ play to + hear each firma. +

+ stageRef?.querySelector('[data-media-player]') ?? stageRef} + /> +
+ {/if} + + {#if tab === 'services'} +
+

Services

+

+ langs (i18n). Every key that composes a real part inherits the player's + localized name — switch language in the topbar and MARCHA announces itself as Play / + Reproducir. Two keys have no part behind them and no catalog entry either (stop, record): they resolve through an idlangref + |fallback, which is silent on a top-level lookup but always renders English. A + declared debt, not an oversight — see the component README. +

+
+ + + + {#each Object.entries(mediaPlayerMorfo.texts ?? {}) as [key, ref]} + + {/each} + + + +
Text keyDefault (en)Source
{key}{String(ref).split('|')[1] ?? '—'}morfo
stopStopfallback only
recordRecordfallback only
+
+
+ {/if} + + {#if tab === 'api'} +
+

API reference

+

+ The whole surface is the object and what it prints. There is no + src, no controls and no shape prop: those belong to the player that + hosts it. +

+
+ + + + + + + + + + + +
PropTypeDefaultDescription
skin'record' | 'turntable' | 'cassette' | 'deck''turntable'Which object to depict. Accepts a responsive map. turntable nests + record in the platter well and deck nests + cassette behind the glass, so the medium's paint is reused whole.
rpm33 | 4545Platter speed. Decorative: it sets the spin period and the strobe + and never touches playbackRate — the browser preserves pitch by default, + so a 45 played at 33 would sound slow but not lower, which is exactly what a turntable + does not do.
title · artist · year · publisherstring—The record label / cassette inlay copy. Auto-fitted to the printed area.
imagestring—Cover art printed on the label or the inlay, under a wine veil.
imageVeilnumber0.55Opacity of that veil, so the type stays legible over any cover.
side · tapeTypestring'A' · 'C-60'Cassette markings.
brand · modelstring'Levante' · per deviceThe machine's badge.
+
+
+ {/if} + + {#if tab === 'morfo'} +
+

morfo contract

+

+ The skin has no morfo. It is an eidos-only piece by the four rules: it + creates no behaviour, is no event target, owns no ARIA relationships, and emits only + structure plus visual data-*. The contract below is + src/uix/morfo/components/media-player.ts, which is what its keys compose. +

+ +
Parts
+
+ + + + {#each partsList as part} + + + + + + + + {/each} + +
PartMarkerElementRoleArchetype
{part.kebab}[{part.marker}]<{part.defaultElement}>{part.role ?? '—'}{part.archetype ?? '—'}
+
+ +
Events
+
+ + + + {#each actions as action} + {@const sem = action.semantic} + + + + + + + + + {/each} + +
namefamilyverbsequenceintenttarget
{action.name}{sem.family}{sem.verb ?? '—'}{sem.sequence ?? 'pre'}{'intent' in sem && typeof sem.intent === 'string' ? sem.intent : '—'}{action.target}
+
+ +
What the skin adds
+
+ + + + + + + +
AttributeOnMeaning
data-skinthe skin rootWhich object is depicted. Visual axis, eidos-only.
data-animation-styleturning partsThe system's loop preset (spin / pulse).
data-pressed · data-raised · data-cueing · data-timedpaint groupsPurely visual state the recipe reads. No behaviour, no ARIA.
+
+
+ {/if} + + {#if tab === 'recipe'} +
+

Eidos recipe

+

+ Selectors at src/uix/eidos/components/skin-media-player/skin-media-player.css. + A fixed-tone component: the 83 colours of the drawing live as literals in + the SVG paint, under the exception the guard already carves for components that depict a + physical thing (natural-time-picker, color-picker, + proof-of-human). The recipe itself holds geometry, state and movement only. +

+
+ + + + + + + + + + +
SelectorSource
[data-media-player][data-paused] … [data-animation-style='spin']morfo
[data-skin-media-player][data-skin='X']eidos
[data-skin-media-player-key] · the overlay buttoneidos
.smp-arm · .smp-reel · .smp-lever · .smp-piano-keyeidos
--_smp-arm / --_smp-reel-* / --_smp-maxeidos (CSS var)
--motion-loop-spinmotion (loop knob)
+
+
+ {/if} + + {#if tab === 'a11y'} +
+

Accessibility

+

+ The drawing is a picture and the controls are real buttons — that split is the whole + accessibility model. The SVG is aria-hidden unless an identity is supplied, in + which case it is a labelled role="img"; every key is a native + <button> laid over it, so Enter / Space, disabled and the focus + ring are the platform's. +

+
Why not a role on the <g>
+

+ The source sketches put role="button" + tabindex on SVG groups and + re-implemented Space / Enter by hand. Nothing in eidos does that: + onion-menu overlays a real HTML button at its SVG centre for the same reason, + and + natural-time-picker uses a composed Slider thumb for its knob. A + <g> has no native keyboard, no disabled, and no outline the + focus-ring system can draw. +

+
Keyboard
+
+ + + + + + + +
KeyAction
TabReaches each depicted control in visual order
Enter / SpacePresses it — native, because it is a native button
The player's own hotkeys (Space, + ←/→, + m, …) keep working: they belong to the region, not to the + skin.
+
+
ARIA contract
+
+ + + + + + + + + +
AttributeValue
role="img" + aria-labelon the drawing, when a title / artist is given (the label is the work, not prose)
aria-hiddenon the drawing with no identity, and on every nested medium
aria-labelon each key — inherited from the composed part where one exists
aria-pressedon the 33 / 45 speed keys
disabledon GRAB — real, not a look: the port has no recording
+
+
+ {/if} +