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/sema/channels.ts

164 lines
5.7 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.

/**
* Sema channel contract.
*
* This is the single source for runtime channel ids, channel signatures and
* per-channel overrides. `sema-map.ts` owns data; this file owns the open
* channel type surface that both the map and event declarations consume.
*/
export interface SoundSignature {
pitch: number;
centroid: number;
roughness: number;
attack: number;
decay: number;
duration: number;
contour: 'flat' | 'ascending' | 'descending' | 'arc' | 'bell';
gain: number;
sampleUrl?: string;
}
/**
* Haptic / vibration feedback. Categorical `kind` lets future channels map
* to platform haptic primitives (iOS Haptic Engine, Android
* HapticFeedbackConstants) without breaking consumers; V1 of `HapticChannel`
* implements the Vibration API path with intensity-modulated durations.
*/
export interface HapticSignature {
/**
* Categorical pattern. Maps to system primitives where available; falls
* back to a Vibration API pattern derived from `duration` + `intensity`.
*/
kind: 'tick' | 'tap' | 'pulse' | 'thud' | 'success' | 'warning' | 'error';
/** 0..1. Modulates Vibration API duration; ignored on categorical-only platforms. */
intensity: number;
/** Base pulse duration in ms (single Vibration API pulse). */
duration: number;
/** Explicit on/off/on/off… pattern in ms. */
pattern?: readonly number[];
/** Delay relative to signal start (ms). */
delay?: number;
}
/**
* Canonical channel registry. Apps extend it via declaration merging:
*
* declare module '$uix/sema' {
* interface SemaChannelSignatures {
* a11y: A11ySignature;
* voice: VoiceSignature;
* }
* }
*/
export interface SemaChannelSignatures {
sound: SoundSignature;
haptic: HapticSignature;
}
/**
* Open string id of a channel. Known channels autocomplete in editors;
* arbitrary strings are accepted for channels registered with the engine but
* without a typed signature slice.
*/
export type SemaChannelId = keyof SemaChannelSignatures | (string & {});
export type DeltaOp =
| { op: 'multiply'; factor: number }
| { op: 'replace'; value: number | string | boolean | null | readonly DeltaValue[] }
| { op: 'add'; value: number };
export type DeltaValue =
| number
| string
| boolean
| null
| DeltaOp
| readonly DeltaValue[]
| { [key: string]: DeltaValue };
/**
* Reduction level for a perceptual channel (book ch. 32 §12, ch. 33 §7:
* `BK-REDUCTIONS`). The book asks for a symmetric per-channel reduction story —
* "controles de sonido", reduced motion, etc. — not a single global switch.
*
* - `full` — normal expression (default).
* - `reduce` — attenuated but present (lower gain / intensity).
* - `off` — the channel is silenced; meaning migrates per `SEMA_MIGRATION`.
*/
export type SemaReduceLevel = 'full' | 'reduce' | 'off';
/**
* App-controlled per-channel reduction preferences, read by the channels at
* dispatch time (so an app may back this with reactive state). Sound has no OS
* media query, so its reduction is an explicit app control; haptic ALSO honors
* `prefers-reduced-motion` from the active-dom on top of this. Visual-motion
* reduction lives in eidos CSS (`@media prefers-reduced-motion`), not here.
*/
export interface SemaPreferences {
sound?: SemaReduceLevel;
haptic?: SemaReduceLevel;
}
/**
* THE canonical silence — one value for the whole system.
*
* Silence admits no family, no verb and no intent: there is nothing to
* modulate, so there is nothing to parameterise. A channel slice set to
* `SILENT` means «this event does not speak here», and the channel that owns
* the slice MUST ignore it and dispatch nothing downstream.
*
* It is a VALUE, not an arithmetic. The framework used to express silence by
* subtracting a family's base gain (`form.toggle.silent = gain -0.3`), which
* needed one key per family and produced a number, not an absence — and a
* number is something later layers can move. It was measured: an intent delta
* that raises `roughness` past the AM threshold turned a signature declared at
* gain 0 into a raw tremolo at −13.9 dBFS (`risk`) and −6.7 dBFS (`threat`),
* louder than a real button press. The engine had already written the rule it
* was breaking — «muting DROPS the channel rather than scaling to zero,
* because a `0` still buzzes» (`engine.ts`). This value is that rule, applied
* to the signature instead of to the channel list.
*
* Absorbing by construction: intent deltas do not apply on top of `SILENT`
* (layer 2 cannot un-silence). A later override CAN lift it by REPLACING the
* slice with a full signature — declaring sound is a decision, not a delta.
*/
export const SILENT = 'silent' as const;
/** The type of {@link SILENT}. */
export type Silent = typeof SILENT;
/**
* Per-channel partial override applied at resolution time.
*/
export type SemaSignatureOverride = {
/**
* Replace the active-channel list for matching signals. Last-write-wins
* across the cascade. Empty array silences the signal entirely.
*/
channels?: readonly SemaChannelId[];
} & {
/**
* `sound` is a NAME from the active pack, or `SILENT` — never a signature.
* Nobody authors a sound inline: not a morfo, not a pack, not an app, not a
* per-emit override. A product that needs a sound the pack lacks REGISTERS
* it (name + definition, once) and then names it.
*/
sound?: SoundNameRef | Silent;
} & {
[K in Exclude<keyof SemaChannelSignatures, 'sound'>]?:
| Partial<SemaChannelSignatures[K]>
| Record<string, DeltaValue>
| Silent;
};
/**
* Loose reference to a pack entry. The precise union (`SoundName`) is derived
* from the map, which imports this module — so the tight type is applied where
* it can be: `Sema['cascade'].sound` and `SemaCascadeRule.sound`.
*/
export type SoundNameRef = string;

Powered by TurnKey Linux.