@joewinke/jatui 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,102 +1,19 @@
1
1
  /**
2
- * Settings plumbing for VideoPlayer (mos-5lzns.28) — ported discipline from
3
- * extension-library's `settings.js`, adapted to a prop-driven component.
2
+ * Settings for VideoPlayer — the barrel (mos-5lzns.28 skeleton, filled in by
3
+ * mos-5lzns.35). The pieces live in `settings/`:
4
4
  *
5
- * The extension's registry exists so the settings panel is generated from one
6
- * array and "a setting added there appears in the UI by existing". jatui's
7
- * player is prop-driven (the HOST owns the panel), so this module owns the
8
- * smaller half of the contract that still matters here:
5
+ * · `registry.ts` — `VIDEO_SETTINGS`: every standing choice the player's
6
+ * automatic behaviours read, with its label, hint, group, the prop it
7
+ * drives and the availability gate. `coerce` never throws.
8
+ * · `store.ts` — the host-supplied `VideoSettingsStore` (get/set) and the
9
+ * session that reads/writes through it. jatui never persists on its own.
10
+ * · `panel.ts` — which rows a given player can offer, and how values read.
11
+ * · `SettingsPanel.svelte` — the panel, generated wholly from the registry.
9
12
  *
10
- * · `coerce` never throws and never returns junk. A bool accepts
11
- * true/"1"/"true"; a range parses and clamps to [min, max]; an enum must
12
- * match a declared option or the value is discarded. Anything
13
- * unrecognised returns undefined and the caller falls back to the
14
- * declared default. A corrupted blob degrades to defaults, not to a
15
- * broken player.
16
- * · One registry (`VIDEO_SETTINGS`) lists every knob the player's automatic
17
- * behaviours read, with the sub-task that owns each. A knob that is here
18
- * but unread by any module is a switch that visibly does nothing — the
19
- * extension calls that "declared but not yet wired" and refuses to fix it
20
- * quietly. Keep the same discipline: land the knob WITH its reader.
21
- *
22
- * Nothing under src/lib/video/ reads a setting directly except through the
23
- * value its owning feature passes in as an `opts` argument — the advisor
24
- * rule (see README). That keeps every module node-testable and keeps the
25
- * arbitration in the component.
26
- */
27
- /**
28
- * The registry. Populated as the behaviours land — .30 oneAxis, .31 audio,
29
- * .32 rate/speaker/silence, .33 night, .34 presence. An empty registry is
30
- * the honest state while every behaviour is prop-driven.
13
+ * Nothing under src/lib/video/ reads a setting directly: the component maps
14
+ * each value onto the prop it names and the feature reads that — the
15
+ * advisor rule (see README).
31
16
  */
32
- export const VIDEO_SETTINGS = [
33
- // Read by VideoPlayer's `oneAxis` prop → video/band/axis.ts resolveAxis().
34
- { id: 'player.oneAxis', type: 'bool', default: true, owner: 'mos-5lzns.30', wired: true },
35
- // Read by VideoPlayer's `loudness` / `loudnessTarget` / `quietListening` /
36
- // `limiter` props → video/audio/* (graph.createAudioSession, rider.*). As with
37
- // night, the DEFAULT is the host's opt-in: an absent prop means nothing is
38
- // ever routed through WebAudio on that player.
39
- { id: 'audio.level', type: 'bool', default: true, owner: 'mos-5lzns.31', wired: true },
40
- { id: 'audio.target', type: 'range', default: -20, min: -30, max: -12, step: 2, owner: 'mos-5lzns.31', wired: true },
41
- { id: 'audio.quiet', type: 'bool', default: true, owner: 'mos-5lzns.31', wired: true },
42
- { id: 'audio.limiter', type: 'bool', default: true, owner: 'mos-5lzns.31', wired: true },
43
- // Read by VideoPlayer's `smartSpeed` / `speakerPace` (+ `speakerRates`) /
44
- // `skipAds` (+ `adSpans`) props → video/rate/* (silenceMapFor + silenceFactor,
45
- // speakerRateMap + speakerFactor, normalizeAdSpans + adSkipTarget), composed
46
- // by rate/compose.ts composeRate. All default off: an automatic rate change
47
- // is the host's opt-in.
48
- { id: 'player.smartSpeed', type: 'bool', default: false, owner: 'mos-5lzns.32', wired: true },
49
- { id: 'player.speakerRate', type: 'bool', default: false, owner: 'mos-5lzns.32', wired: true },
50
- { id: 'player.skipAds', type: 'bool', default: false, owner: 'mos-5lzns.32', wired: true },
51
- // Read by VideoPlayer's `night` / `nightDepth` / `biasLight` / `predictive` /
52
- // `strobeGuard` props → video/night/* (clock.nightArmed, levels.nightLevels,
53
- // meter.biasCells, track.lumaAhead / inFlash / composeNight). Unlike the
54
- // extension, night.mode's DEFAULT here is the host's opt-in: an absent `night`
55
- // prop means the feature does not exist on that player.
56
- { id: 'night.mode', type: 'enum', default: 'auto', options: ['auto', 'on', 'off'], owner: 'mos-5lzns.33', wired: true },
57
- { id: 'night.depth', type: 'range', default: 0.7, min: 0.2, max: 1, step: 0.1, owner: 'mos-5lzns.33', wired: true },
58
- { id: 'night.bias', type: 'bool', default: true, owner: 'mos-5lzns.33', wired: true },
59
- { id: 'player.predictive', type: 'bool', default: true, owner: 'mos-5lzns.33', wired: true },
60
- { id: 'player.strobeGuard', type: 'bool', default: true, owner: 'mos-5lzns.33', wired: true },
61
- // Read by VideoPlayer's `windDown` / `windDownAfter` / `windDownNightOnly` /
62
- // `idleDetection` props → video/presence/winddown.ts (shouldBeginWindDown,
63
- // windDownAfterMs) + presence/idle.ts (startDesktopWatch). As with night,
64
- // the DEFAULT is the host's opt-in: an absent `windDown` prop means the
65
- // feature does not exist on that player. `sleep.desktopIdle` defaults OFF
66
- // because turning it on is what asks the browser for a permission — the
67
- // host calls `requestIdleDetection()` from the click that turns it on.
68
- { id: 'sleep.enabled', type: 'bool', default: true, owner: 'mos-5lzns.34', wired: true },
69
- { id: 'sleep.after', type: 'range', default: 30, min: 10, max: 90, step: 5, owner: 'mos-5lzns.34', wired: true },
70
- { id: 'sleep.nightOnly', type: 'bool', default: true, owner: 'mos-5lzns.34', wired: true },
71
- { id: 'sleep.desktopIdle', type: 'bool', default: false, owner: 'mos-5lzns.34', wired: true },
72
- // Read by VideoPlayer's `resumeSnap` prop (with `resumeKey` present) →
73
- // video/presence/resume.ts planResumeSnap → advisors/resume-point snapResume.
74
- { id: 'player.resumeSnap', type: 'bool', default: true, owner: 'mos-5lzns.34', wired: true },
75
- // Read by VideoPlayer's `openMotion` prop (with `openFrom` present) →
76
- // video/presence/flip.ts flipOpen. Off ⇒ the poster still paints.
77
- { id: 'player.openMotion', type: 'bool', default: true, owner: 'mos-5lzns.34', wired: true },
78
- ];
79
- /** Coerce a raw stored value against a spec — never throws, never returns junk. */
80
- export function coerce(spec, raw) {
81
- if (spec.type === 'bool') {
82
- if (raw === true || raw === '1' || raw === 'true')
83
- return true;
84
- if (raw === false || raw === '0' || raw === 'false')
85
- return false;
86
- return undefined;
87
- }
88
- if (spec.type === 'range') {
89
- const n = typeof raw === 'number' ? raw : Number(raw);
90
- if (!Number.isFinite(n))
91
- return undefined;
92
- const min = spec.min ?? -Infinity;
93
- const max = spec.max ?? Infinity;
94
- return Math.min(max, Math.max(min, n));
95
- }
96
- // enum: must match a declared option or the value is discarded
97
- if (typeof raw !== 'string')
98
- return undefined;
99
- if (!spec.options?.includes(raw))
100
- return undefined;
101
- return raw;
102
- }
17
+ export * from './settings/registry';
18
+ export * from './settings/store';
19
+ export * from './settings/panel';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joewinke/jatui",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "private": false,
5
5
  "description": "Shared Svelte 5 component library for JAT projects",
6
6
  "type": "module",