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/web/routes/uix/lib/SemaPanel.svelte

133 lines
4.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<script lang="ts">
/**
* Sema panel — the perceptual layer as a first-class citizen, not a flat
* event table. Per event it shows the declarative shape (family / verb /
* sequence / intent / hold) AND the RESOLVED firma via `resolveSignature`:
* the sound primitives (pitch / gain / contour / envelope) + the haptic
* (kind / intensity / pattern) + the active channels. ▶ play emits the real
* signal on the live stage target, so sound is audible (Semantics → Sound
* must be on in the topbar).
*/
import { signatureFor } from './harness.svelte';
let {
actions,
boundIntent = 'neutral',
uix,
getTarget
}: {
actions: readonly { name: string; semantic: Record<string, unknown> }[];
boundIntent?: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
uix: any;
getTarget: () => HTMLElement | null;
} = $props();
function effectiveIntent(semantic: Record<string, unknown>): string | undefined {
const decl = 'intent' in semantic ? (semantic.intent as unknown) : undefined;
if (typeof decl === 'string') return decl;
if (decl) return boundIntent; // fromProp binding → demo's current value
return undefined;
}
const CONTOUR_GLYPH: Record<string, string> = {
flat: '─',
arc: '⌒',
ascending: '╱',
descending: '╲'
};
function play(action: { name: string; semantic: Record<string, unknown> }) {
const target = getTarget();
if (!target) return;
const intent = effectiveIntent(action.semantic);
void uix.events?.emit({
name: action.name,
family: action.semantic.family,
target,
...(intent ? { intent } : {})
});
}
</script>
{#if actions.length === 0}
<p data-uix-section-desc>
This component's morfo declares <strong>no semantic events</strong>. That is only valid once
the component README justifies why it is perceptually passive (see the Morfo/Sema audit).
</p>
{:else}
<div data-uix-firma-grid>
{#each actions as action}
{@const sem = action.semantic}
{@const intent = effectiveIntent(sem)}
{@const sig = signatureFor(sem.family as string, intent)}
<div data-uix-firma-card>
<div data-uix-firma-head>
<span data-uix-firma-name>{action.name}</span>
<span data-uix-firma-tags>
<span data-uix-firma-tag>{sem.family}</span>
{#if sem.verb}<span data-uix-firma-tag>{sem.verb}</span>{/if}
<span data-uix-firma-tag>{(sem.sequence as string) ?? 'pre'}</span>
{#if intent}<span data-uix-firma-tag data-kind="intent">{intent}</span>{/if}
</span>
<button data-uix-play onclick={() => play(action)}>▶ play</button>
</div>
<div data-uix-firma-channels>
<span data-uix-firma-meta>
channels: {sig.activeChannels.length ? sig.activeChannels.join(' · ') : 'none'}
</span>
{#if sig.hold !== undefined}<span data-uix-firma-meta>hold: {sig.hold}ms</span>{/if}
</div>
<div data-uix-firma-signals>
<!-- Sound -->
<div data-uix-firma-signal data-muted={sig.sound ? undefined : 'true'}>
<span data-uix-firma-signal-label>sound</span>
{#if sig.sound}
<div data-uix-firma-rows>
<span>pitch <b>{Math.round(sig.sound.pitch)}Hz</b></span>
<span>
gain
<span data-uix-firma-bar><span style="inline-size: {Math.min(1, sig.sound.gain) * 100}%"></span></span>
</span>
<span>contour <b data-uix-firma-contour>{CONTOUR_GLYPH[sig.sound.contour] ?? sig.sound.contour}</b> {sig.sound.contour}</span>
<span>centroid <b>{Math.round(sig.sound.centroid)}</b></span>
<span>roughness <b>{sig.sound.roughness}</b></span>
<span>env <b>{sig.sound.attack}/{sig.sound.decay}/{sig.sound.duration}ms</b></span>
</div>
{:else}
<span data-uix-firma-none>silent</span>
{/if}
</div>
<!-- Haptic -->
<div data-uix-firma-signal data-muted={sig.haptic ? undefined : 'true'}>
<span data-uix-firma-signal-label>haptic</span>
{#if sig.haptic}
<div data-uix-firma-rows>
<span>kind <b>{sig.haptic.kind}</b></span>
<span>
intensity
<span data-uix-firma-bar><span style="inline-size: {Math.min(1, sig.haptic.intensity ?? 0) * 100}%"></span></span>
</span>
{#if sig.haptic.pattern}
<span data-uix-firma-pattern>
{#each sig.haptic.pattern as ms, i}
<span data-on={i % 2 === 0 ? 'true' : undefined} style="inline-size: {Math.max(4, ms / 6)}px"></span>
{/each}
</span>
{:else if sig.haptic.duration}
<span>duration <b>{sig.haptic.duration}ms</b></span>
{/if}
</div>
{:else}
<span data-uix-firma-none>none</span>
{/if}
</div>
</div>
</div>
{/each}
</div>
{/if}

Powered by TurnKey Linux.