@joewinke/jatui 0.10.0 → 0.12.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.
- package/CLAUDE.md +10 -0
- package/dist/components/VideoPlayer.svelte +504 -8
- package/dist/components/VideoPlayer.svelte.d.ts +42 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +10 -0
- package/dist/video/README.md +18 -4
- package/dist/video/advisors/README.md +20 -0
- package/dist/video/advisors/qr.d.ts +95 -0
- package/dist/video/advisors/qr.js +546 -0
- package/dist/video/advisors/quiet-listening.d.ts +73 -0
- package/dist/video/advisors/quiet-listening.js +292 -0
- package/dist/video/advisors/resume-point.d.ts +71 -0
- package/dist/video/advisors/resume-point.js +302 -0
- package/dist/video/advisors/smart-speed.d.ts +82 -0
- package/dist/video/advisors/smart-speed.js +314 -0
- package/dist/video/advisors/speaker-rate.d.ts +74 -0
- package/dist/video/advisors/speaker-rate.js +322 -0
- package/dist/video/keys.d.ts +4 -4
- package/dist/video/keys.js +4 -3
- package/dist/video/night/clock.d.ts +31 -0
- package/dist/video/night/clock.js +58 -0
- package/dist/video/night/levels.d.ts +78 -0
- package/dist/video/night/levels.js +117 -0
- package/dist/video/night/meter.d.ts +53 -0
- package/dist/video/night/meter.js +115 -0
- package/dist/video/night/track.d.ts +105 -0
- package/dist/video/night/track.js +138 -0
- package/dist/video/night.d.ts +25 -20
- package/dist/video/night.js +25 -20
- package/dist/video/settings.js +10 -1
- package/package.json +3 -2
|
@@ -2,10 +2,12 @@ export type { VideoChapter, VideoSpeakerTurn, VideoTranscriptSegment, VideoBookm
|
|
|
2
2
|
export type { BandAxis, BandState } from '../video/band/axis';
|
|
3
3
|
export type { VideoLumaTrack } from '../video/band/luma';
|
|
4
4
|
export type { VideoMarker } from '../video/band/markers';
|
|
5
|
+
export type { NightMode, NightWindow } from '../video/night/clock';
|
|
5
6
|
import type { VideoBookmark, VideoChapter, VideoSpeakerTalkTime, VideoSpeakerTurn, VideoTranscriptSegment, VideoWaveform } from '../video/types';
|
|
6
7
|
import { type BandState } from '../video/band/axis';
|
|
7
8
|
import { type VideoLumaTrack } from '../video/band/luma';
|
|
8
9
|
import { type VideoMarker } from '../video/band/markers';
|
|
10
|
+
import { type NightMode, type NightWindow } from '../video/night/clock';
|
|
9
11
|
type $$ComponentProps = {
|
|
10
12
|
src: string;
|
|
11
13
|
poster?: string | null;
|
|
@@ -97,10 +99,49 @@ type $$ComponentProps = {
|
|
|
97
99
|
onAddBookmark?: (playbackSec: number, label: string) => void;
|
|
98
100
|
/** Right-click on the band. The host owns the panel; this only hit-tests. */
|
|
99
101
|
onBookmarkContext?: (sec: number, existing: VideoBookmark | null, clientX: number, clientY: number) => void;
|
|
102
|
+
/**
|
|
103
|
+
* Night dimming (mos-5lzns.33) — a METER, not a preference: ~1×/s while
|
|
104
|
+
* playing it reads the frame's actual brightness (32×18 canvas) and pulls
|
|
105
|
+
* the picture down in proportion, warming it through multiply layers so
|
|
106
|
+
* blacks stay black. A dark scene passes through untouched.
|
|
107
|
+
*
|
|
108
|
+
* `'auto'` arms it from the local clock (20:00–07:00; 18:00–08:00 when the
|
|
109
|
+
* OS prefers dark), `'on'` always, `'off'` never. Omitted (default) ⇒ the
|
|
110
|
+
* feature does not exist on this player: no N key, no button, no layers.
|
|
111
|
+
* Bindable — `N` and the moon button cycle it; `onNightModeChange` reports.
|
|
112
|
+
*
|
|
113
|
+
* Frame reading needs a CORS-clean load: pass `crossorigin="anonymous"`
|
|
114
|
+
* for cross-origin media. A tainted canvas runs night on a fixed level
|
|
115
|
+
* (no meter, no bias light) and calls `onNightBlind`.
|
|
116
|
+
*/
|
|
117
|
+
night?: NightMode;
|
|
118
|
+
/** How far night goes, 0.2–1 (default 0.7). Bindable — scrolling the moon button sets it. */
|
|
119
|
+
nightDepth?: number;
|
|
120
|
+
/** Override the auto window, local hours `{ from, to }` (wraps midnight when from > to). */
|
|
121
|
+
nightWindow?: NightWindow | null;
|
|
122
|
+
/** While night acts, spill the frame's own colours, blurred, behind the player. */
|
|
123
|
+
biasLight?: boolean;
|
|
124
|
+
/** Read the `luma` track ~1s ahead so dimming starts BEFORE a bright cut. Only ever adds dimming. */
|
|
125
|
+
predictive?: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Photosensitivity guard: across the `luma` track's flash intervals,
|
|
128
|
+
* drop the picture to about 55% brightness (instant in, released per
|
|
129
|
+
* tick — never a jump back up). Always at full strength: it does NOT
|
|
130
|
+
* follow `nightDepth`, and it acts whether night is on, off or absent,
|
|
131
|
+
* whenever the track carries `flashes`. `false` to disable.
|
|
132
|
+
*
|
|
133
|
+
* It REDUCES flashes; it does not make content WCAG 2.3.1-safe. Flash
|
|
134
|
+
* DETECTION is the host's job — the player guards only the intervals the
|
|
135
|
+
* host supplies in `luma.flashes`.
|
|
136
|
+
*/
|
|
137
|
+
strobeGuard?: boolean;
|
|
138
|
+
onNightModeChange?: (mode: NightMode) => void;
|
|
139
|
+
/** Frame readback was refused (CORS-tainted canvas) — night fell back to a fixed level. */
|
|
140
|
+
onNightBlind?: () => void;
|
|
100
141
|
class?: string;
|
|
101
142
|
};
|
|
102
143
|
declare const VideoPlayer: import("svelte").Component<$$ComponentProps, {
|
|
103
144
|
bandState: () => BandState;
|
|
104
|
-
}, "">;
|
|
145
|
+
}, "night" | "nightDepth">;
|
|
105
146
|
type VideoPlayer = ReturnType<typeof VideoPlayer>;
|
|
106
147
|
export default VideoPlayer;
|
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,12 @@ export type { ChapterBlock } from './components/replay/ChapterTimeline.svelte';
|
|
|
12
12
|
export { default as TranscriptPlayer } from './components/TranscriptPlayer.svelte';
|
|
13
13
|
export type { TranscriptSegment, TranscriptBookmark, TranscriptChapter, TranscriptSpeakerTalkTime, } from './components/TranscriptPlayer.svelte';
|
|
14
14
|
export { default as VideoPlayer } from './components/VideoPlayer.svelte';
|
|
15
|
-
export type { VideoChapter, VideoSpeakerTurn, VideoTranscriptSegment, VideoBookmark, VideoWaveform, VideoSpeakerTalkTime, VideoLumaTrack, VideoMarker, BandState, BandAxis, } from './components/VideoPlayer.svelte';
|
|
15
|
+
export type { VideoChapter, VideoSpeakerTurn, VideoTranscriptSegment, VideoBookmark, VideoWaveform, VideoSpeakerTalkTime, VideoLumaTrack, VideoMarker, BandState, BandAxis, NightMode, NightWindow, } from './components/VideoPlayer.svelte';
|
|
16
|
+
export { buildSilenceMap, boostAt, estimateSaving, type SilenceMap, type SilenceWave, type SilenceOpts, type BoostOpts } from './video/advisors/smart-speed';
|
|
17
|
+
export { speakerPace, suggestRates, rateAt, describeRates, type SpeakerPace, type SpeakerPaceRow, type RateMap, type RateTurn, type RateSegment, type SuggestOpts } from './video/advisors/speaker-rate';
|
|
18
|
+
export { snapResume, resumeLabel, type ResumePoint, type ResumeReason, type ResumeCtx, type ResumeOpts } from './video/advisors/resume-point';
|
|
19
|
+
export { createQuietChain, amountFor, type QuietChain, type QuietAudioContext, type AmountOpts } from './video/advisors/quiet-listening';
|
|
20
|
+
export { qrMatrix, qrCanvas, type QrMatrix, type QrCanvasOpts } from './video/advisors/qr';
|
|
16
21
|
export { default as Flythrough } from './components/Flythrough.svelte';
|
|
17
22
|
export { default as LessonEmbed } from './components/LessonEmbed.svelte';
|
|
18
23
|
export type { Collection, CollectionCut, CollectionBeat, } from './components/LessonEmbed.svelte';
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,16 @@ export { default as TranscriptPlayer } from './components/TranscriptPlayer.svelt
|
|
|
13
13
|
// optional so a bare mp4 still gets the full playback UX. TranscriptPlayer
|
|
14
14
|
// stays exported unchanged — meetings already depends on it.
|
|
15
15
|
export { default as VideoPlayer } from './components/VideoPlayer.svelte';
|
|
16
|
+
// Pure advisors (mos-5lzns.27): the extension-library self-test modules,
|
|
17
|
+
// ported verbatim. They measure and advise; the player's feature tasks
|
|
18
|
+
// (.31 audio / .32 rate / .34 presence) consume them. qrCanvas is the one
|
|
19
|
+
// DOM-touching surface (its only guard is a missing document), everything
|
|
20
|
+
// else is node-testable pure.
|
|
21
|
+
export { buildSilenceMap, boostAt, estimateSaving } from './video/advisors/smart-speed';
|
|
22
|
+
export { speakerPace, suggestRates, rateAt, describeRates } from './video/advisors/speaker-rate';
|
|
23
|
+
export { snapResume, resumeLabel } from './video/advisors/resume-point';
|
|
24
|
+
export { createQuietChain, amountFor } from './video/advisors/quiet-listening';
|
|
25
|
+
export { qrMatrix, qrCanvas } from './video/advisors/qr';
|
|
16
26
|
// Presentation-primitive video trio (unified-video-pipeline PRD): the frozen
|
|
17
27
|
// surface academy + mardukos consume. VideoPlayer (above) doubles as the full
|
|
18
28
|
// player AND the presentation player — extended with aspect/autoplay/loop +
|
package/dist/video/README.md
CHANGED
|
@@ -21,13 +21,18 @@ props + callback surfaces.
|
|
|
21
21
|
| `deepLink.ts` | .28 (done) | `?t=` parse/format/read/write (replaceState, throttled, other params preserved). |
|
|
22
22
|
| `settings.ts` | shared — knobs added by .30–.34 | `coerce` (never throws, never junk) + `VIDEO_SETTINGS` registry. A knob lands WITH its reader. |
|
|
23
23
|
| `audio.ts` | **.31** | Loudness matching, quiet listening, limiter — audio-graph contract is in the file header. |
|
|
24
|
-
| `night.ts` | **.33** |
|
|
24
|
+
| `night.ts` + `night/` | **.33** (done) | `night.ts` is the barrel. `meter.ts` — 32×18 frame bytes → Rec.709 luma + blown share, asymmetric smoothing, bias-light cells from the same bytes. `levels.ts` — luma → brightness/saturation/warmth/vignette off `NIGHT_FLOOR` 0.34, blind fixed level, the light-theme surround. `clock.ts` — local-clock `auto` window + the `N` auto/on/off cycle (no hyprsunset). `track.ts` — prediction (only ever adds dimming), flash intervals, `composeNight` (darker at once, brighter released per tick — never a jump). |
|
|
25
25
|
| `presence.ts` | **.34** | Wind-down (Idle Detection), resume-at-sentence, FLIP open. |
|
|
26
|
+
| `advisors/` | **.27 (done)** | The five extension-library pure advisors, ported verbatim: `smart-speed` (silence map + boost), `speaker-rate` (measured wpm + suggested rates), `resume-point` (sentence-snapping + the label), `quiet-listening` (tone-only chain + `amountFor`), `qr` (ISO 18004 encoder v1–10). Each has its own README row in `advisors/README.md`; the `--self-test` suites live on as vitest in `tests/unit/advisors/` (`npm run test:advisors`). |
|
|
26
27
|
|
|
27
28
|
Sub-task map: **.30** scrubbing/one-axis/ribbon/markers → `band.ts` (+ its own
|
|
28
29
|
paint layers); **.31** audio graph → `audio.ts`; **.32** rate composer →
|
|
29
30
|
`rate.ts` advisors; **.33** night → `night.ts`; **.34** presence/wind-down →
|
|
30
|
-
`presence.ts`.
|
|
31
|
+
`presence.ts`. **.27** (landed) ports the advisors themselves — .31's chain
|
|
32
|
+
builder and `amountFor` live in `advisors/quiet-listening`, .32's two advisor
|
|
33
|
+
modules in `advisors/smart-speed` + `advisors/speaker-rate`, .34's snapping in
|
|
34
|
+
`advisors/resume-point`; the wiring into the component stays with each
|
|
35
|
+
feature task.
|
|
31
36
|
|
|
32
37
|
## The advisor doctrine (from extension-library FEATURES.md — keep it)
|
|
33
38
|
|
|
@@ -43,7 +48,8 @@ paint layers); **.31** audio graph → `audio.ts`; **.32** rate composer →
|
|
|
43
48
|
MediaElementAudioSourceNode, never the element). Knobs arrive as an
|
|
44
49
|
`opts` argument; a bad argument returns a safe neutral value rather than
|
|
45
50
|
throwing. This is what keeps every module node-testable under
|
|
46
|
-
`npm run test:unit
|
|
51
|
+
`npm run test:unit` (the advisor vitest suites run under
|
|
52
|
+
`npm run test:advisors`).
|
|
47
53
|
4. **The rate composer is the shape to follow** (`rate.ts`): `rate = base ×
|
|
48
54
|
speaker × silence`, advisors collapse to 1, the UI reports only `base`.
|
|
49
55
|
|
|
@@ -51,4 +57,12 @@ paint layers); **.31** audio graph → `audio.ts`; **.32** rate composer →
|
|
|
51
57
|
|
|
52
58
|
Every pure module gets a `tests/unit/` node:test file (Node ≥ 22.18 strips
|
|
53
59
|
types natively). Canvas/WAAPI work stays out of unit tests by construction —
|
|
54
|
-
that's the point of the split.
|
|
60
|
+
that's the point of the split.
|
|
61
|
+
|
|
62
|
+
The advisor suites (`.27`) are the one deliberate exception to "node:test":
|
|
63
|
+
they came from the extension's `--self-test` blocks (string-printing PASS/FAIL
|
|
64
|
+
suites with their own helpers), and were ported to **vitest** in
|
|
65
|
+
`tests/unit/advisors/*.test.ts` — run with `npm run test:advisors`. The dir is
|
|
66
|
+
deliberately kept out of `test:unit`'s glob (its script names the node:test
|
|
67
|
+
files explicitly): vitest's `expect()` throws without the vitest process
|
|
68
|
+
around it, so node:test importing these files would fail the wrong runner.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure advisors for VideoPlayer — ported from extension-library `lib/`
|
|
3
|
+
* (mos-5lzns.27). Behaviour-verbatim ports of the five self-contained
|
|
4
|
+
* `--self-test` modules; each `--self-test` suite became a vitest file in
|
|
5
|
+
* `tests/unit/advisors/`.
|
|
6
|
+
*
|
|
7
|
+
* The advisor doctrine (src/lib/video/README.md): these modules measure and
|
|
8
|
+
* advise, never act. They read no settings, touch no DOM, never write
|
|
9
|
+
* `playbackRate`, and every function returns a safe neutral value rather than
|
|
10
|
+
* throwing on malformed input. Knobs arrive as `opts`; the component decides
|
|
11
|
+
* and applies.
|
|
12
|
+
*
|
|
13
|
+
* | Module | Answers | Consumed by |
|
|
14
|
+
* |---|---|---|
|
|
15
|
+
* | `smart-speed` | where the silence is, and the boost multiplier inside it | rate composer's `silence` advisor (.32) |
|
|
16
|
+
* | `speaker-rate` | each speaker's measured wpm, the rates that even them out | rate composer's `speaker` advisor (.32) |
|
|
17
|
+
* | `resume-point` | the nearest real boundary at-or-before a stored time + the line to say | resume-at-sentence (.34) |
|
|
18
|
+
* | `quiet-listening` | the loudness-compensated tone graph + how much of it is wanted | audio graph (.31) |
|
|
19
|
+
* | `qr` | the deep link as a scannable symbol | "continue on phone" (host UI) |
|
|
20
|
+
*/
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QR Code (ISO/IEC 18004) encoder: versions 1–10, byte mode, no deps. Ported
|
|
3
|
+
* verbatim from extension-library `lib/qr.js` (mos-5lzns.27).
|
|
4
|
+
*
|
|
5
|
+
* Why hand-rolled. The extension ran under a strict CSP where every CDN-hosted
|
|
6
|
+
* QR library was unreachable by construction, and there was no build step to
|
|
7
|
+
* vendor one through. The alternative — pasting a minified blob into the repo —
|
|
8
|
+
* ships a black box into a codebase whose whole premise is that you can read
|
|
9
|
+
* it. This is the spec, written out, and PROVEN by the vitest suite that
|
|
10
|
+
* replaced the `--self-test` block (a reader that decodes our own output, plus
|
|
11
|
+
* structural assertions against published tables) rather than trusted.
|
|
12
|
+
*
|
|
13
|
+
* What it is for: a QR of the current deep link — the player's `?t=` substrate
|
|
14
|
+
* (video/deepLink.ts) plus the host's own file/URL part — so the video you are
|
|
15
|
+
* watching moves from the desk to a phone mid-sentence. That URL is 60–120
|
|
16
|
+
* bytes over a tailnet hostname — a version 3–7 symbol at ECC M. Versions above
|
|
17
|
+
* 10 would buy capacity this will never use and cost another row in four tables
|
|
18
|
+
* each, so the ceiling is deliberate and qrMatrix throws at it instead of
|
|
19
|
+
* quietly growing.
|
|
20
|
+
*
|
|
21
|
+
* Guard philosophy, since it cuts both ways here: garbage OPTIONS degrade to
|
|
22
|
+
* defaults (an unknown ecc string is just "M"), because a slightly
|
|
23
|
+
* wrong-looking QR still scans. Text that genuinely cannot be represented
|
|
24
|
+
* THROWS, because the failure mode of the alternative is a symbol that scans
|
|
25
|
+
* perfectly and sends the phone to a truncated URL — silent, plausible, and
|
|
26
|
+
* wrong.
|
|
27
|
+
*
|
|
28
|
+
* qrMatrix(text, { ecc = "M", minVersion = 1 }) -> { size, modules }
|
|
29
|
+
* qrCanvas(text, { scale = 4, margin = 4, dark, light }) -> HTMLCanvasElement
|
|
30
|
+
*
|
|
31
|
+
* The self-test's independent READER (mask/format re-derivation, de-interleave,
|
|
32
|
+
* round-trip decode) lives with the vitest suite in tests/unit/advisors/ —
|
|
33
|
+
* deliberately NOT built on the encoder's own reserved map or chosen mask, and
|
|
34
|
+
* deliberately not shipped in dist.
|
|
35
|
+
*/
|
|
36
|
+
declare const MAX_VERSION = 10;
|
|
37
|
+
declare const MODE_BYTE = 4;
|
|
38
|
+
declare const PAD_BYTES: number[];
|
|
39
|
+
declare const ECC_BITS: Record<string, number>;
|
|
40
|
+
export type EccLevel = "L" | "M" | "Q" | "H";
|
|
41
|
+
declare const BLOCKS: Record<EccLevel, number[][]>;
|
|
42
|
+
declare const TOTAL_CW: number[];
|
|
43
|
+
declare const ALIGN_POS: number[][];
|
|
44
|
+
declare const MASKS: ((r: number, c: number) => boolean)[];
|
|
45
|
+
declare const GF_EXP: Uint8Array<ArrayBuffer>;
|
|
46
|
+
declare const GF_LOG: Uint8Array<ArrayBuffer>;
|
|
47
|
+
declare const gfMul: (a: number, b: number) => number;
|
|
48
|
+
declare function rsGenerator(n: number): number[];
|
|
49
|
+
declare function rsEncode(data: Uint8Array, ecLen: number): Uint8Array;
|
|
50
|
+
declare function rsSyndromes(block: number[], ecLen: number): Uint8Array;
|
|
51
|
+
declare function formatBits(ecc: string, mask: number): number;
|
|
52
|
+
declare function versionBits(version: number): number;
|
|
53
|
+
declare const charCountBits: (version: number) => 8 | 16;
|
|
54
|
+
declare const dataCodewords: (version: number, ecc: EccLevel) => number;
|
|
55
|
+
declare const maxBytes: (version: number, ecc: EccLevel) => number;
|
|
56
|
+
declare function toBytes(text: unknown): Uint8Array;
|
|
57
|
+
declare function encodeData(bytes: Uint8Array, version: number, ecc: EccLevel): Uint8Array;
|
|
58
|
+
declare function interleave(data: Uint8Array, version: number, ecc: EccLevel): Uint8Array;
|
|
59
|
+
interface DrawnPattern {
|
|
60
|
+
size: number;
|
|
61
|
+
modules: boolean[][];
|
|
62
|
+
fn: boolean[][];
|
|
63
|
+
}
|
|
64
|
+
declare function drawFunctionPatterns(version: number): DrawnPattern;
|
|
65
|
+
declare function placeData(modules: boolean[][], fn: boolean[][], size: number, cw: Uint8Array): number;
|
|
66
|
+
declare function drawFormatBits(m: boolean[][], size: number, ecc: string, mask: number): void;
|
|
67
|
+
declare const P3A: number[];
|
|
68
|
+
declare const P3B: number[];
|
|
69
|
+
declare function penalty(m: boolean[][], size: number): number;
|
|
70
|
+
interface BuiltMatrix {
|
|
71
|
+
size: number;
|
|
72
|
+
modules: boolean[][];
|
|
73
|
+
mask: number;
|
|
74
|
+
version: number;
|
|
75
|
+
ecc: EccLevel;
|
|
76
|
+
codewords: Uint8Array;
|
|
77
|
+
}
|
|
78
|
+
declare function buildMatrix(bytes: Uint8Array, version: number, ecc: EccLevel, forcedMask: number | null): BuiltMatrix;
|
|
79
|
+
export interface QrMatrix {
|
|
80
|
+
/** Module count per side (21 for v1, +4 per version). */
|
|
81
|
+
size: number;
|
|
82
|
+
/** size×size grid; true = dark module. */
|
|
83
|
+
modules: boolean[][];
|
|
84
|
+
}
|
|
85
|
+
export declare function qrMatrix(text: unknown, opts?: unknown): QrMatrix;
|
|
86
|
+
export interface QrCanvasOpts {
|
|
87
|
+
ecc?: string;
|
|
88
|
+
minVersion?: number;
|
|
89
|
+
scale?: number;
|
|
90
|
+
margin?: number;
|
|
91
|
+
dark?: string;
|
|
92
|
+
light?: string;
|
|
93
|
+
}
|
|
94
|
+
export declare function qrCanvas(text: unknown, opts?: QrCanvasOpts | null): HTMLCanvasElement;
|
|
95
|
+
export { MAX_VERSION, MODE_BYTE, PAD_BYTES, ECC_BITS, BLOCKS, TOTAL_CW, ALIGN_POS, MASKS, P3A, P3B, GF_EXP, GF_LOG, gfMul, rsGenerator, rsEncode, rsSyndromes, formatBits, versionBits, charCountBits, dataCodewords, maxBytes, toBytes, encodeData, interleave, drawFunctionPatterns, placeData, drawFormatBits, penalty, buildMatrix, };
|