@real-music-packages/web-core 0.4.0 → 0.5.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.
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Framework-agnostic promo video module.
3
+ *
4
+ * Provides:
5
+ * - PromoTheme — colour/font/brand tokens
6
+ * - Scene — {durationMs, draw, onEnter?}
7
+ * - recordScenes — rAF-based canvas capture → Blob (browser only)
8
+ * - hookScene / revealScene / ctaScene — scene builders (pure, unit-testable)
9
+ * - runPromoCapture — orchestrator with dependency-injected recorder
10
+ *
11
+ * Visual style ported from whozart/src/lib/promo.ts + video.ts.
12
+ */
13
+ interface PromoTheme {
14
+ paper: string;
15
+ ink: string;
16
+ accent: string;
17
+ sepia: string;
18
+ gold: string;
19
+ fontDisplay: string;
20
+ fontBody: string;
21
+ brand: string;
22
+ }
23
+ interface Scene {
24
+ durationMs: number;
25
+ draw: (ctx: CanvasRenderingContext2D, t01: number) => void;
26
+ onEnter?: () => void;
27
+ }
28
+ interface RecordOpts {
29
+ audioStream: MediaStream;
30
+ width: number;
31
+ height: number;
32
+ fps?: number;
33
+ background?: string;
34
+ onProgress?: (p: {
35
+ phase: string;
36
+ progress01: number;
37
+ }) => void;
38
+ }
39
+ interface PromoMeta {
40
+ id?: string;
41
+ title?: string;
42
+ composer?: string;
43
+ [k: string]: unknown;
44
+ }
45
+ interface PromoCaptureOpts {
46
+ buildScenes: () => Scene[];
47
+ audioStream: MediaStream;
48
+ meta: PromoMeta;
49
+ width?: number;
50
+ height?: number;
51
+ fps?: number;
52
+ background?: string;
53
+ download?: boolean;
54
+ }
55
+ declare function pickMimeType(): string;
56
+ declare function recordScenes(scenes: Scene[], opts: RecordOpts): Promise<Blob>;
57
+ declare function truncate(s: string, max: number): string;
58
+ declare function initials(name: string): string;
59
+ interface HookSceneOpts {
60
+ lines: string[];
61
+ brand?: string;
62
+ }
63
+ declare function hookScene(theme: PromoTheme, opts: HookSceneOpts): Scene;
64
+ interface RevealSceneOpts {
65
+ title: string;
66
+ subtitle: string;
67
+ initials?: string;
68
+ }
69
+ declare function revealScene(theme: PromoTheme, opts: RevealSceneOpts): Scene;
70
+ interface CtaSceneOpts {
71
+ lines: string[];
72
+ }
73
+ declare function ctaScene(theme: PromoTheme, opts: CtaSceneOpts): Scene;
74
+ declare function runPromoCapture(opts: PromoCaptureOpts, record?: (scenes: Scene[], o: RecordOpts) => Promise<Blob>): Promise<void>;
75
+
76
+ export { type CtaSceneOpts, type HookSceneOpts, type PromoCaptureOpts, type PromoMeta, type PromoTheme, type RecordOpts, type RevealSceneOpts, type Scene, ctaScene, hookScene, initials, pickMimeType, recordScenes, revealScene, runPromoCapture, truncate };
package/dist/video.js ADDED
@@ -0,0 +1,202 @@
1
+ // src/video.ts
2
+ function pickMimeType() {
3
+ const candidates = [
4
+ "video/webm;codecs=vp9,opus",
5
+ "video/webm;codecs=vp8,opus",
6
+ "video/webm"
7
+ ];
8
+ for (const t of candidates) {
9
+ if (typeof MediaRecorder !== "undefined" && MediaRecorder.isTypeSupported(t)) return t;
10
+ }
11
+ return "video/webm";
12
+ }
13
+ async function recordScenes(scenes, opts) {
14
+ const { audioStream, width, height, fps = 30, background = "#000000", onProgress } = opts;
15
+ const canvas = document.createElement("canvas");
16
+ canvas.width = width;
17
+ canvas.height = height;
18
+ const maybeCtx = canvas.getContext("2d");
19
+ if (!maybeCtx) throw new Error("Failed to get 2D context");
20
+ const ctx = maybeCtx;
21
+ const videoStream = canvas.captureStream(fps);
22
+ const combined = new MediaStream([
23
+ ...videoStream.getVideoTracks(),
24
+ ...audioStream.getAudioTracks()
25
+ ]);
26
+ const mimeType = pickMimeType();
27
+ const recorder = new MediaRecorder(combined, {
28
+ mimeType,
29
+ videoBitsPerSecond: 4e6,
30
+ audioBitsPerSecond: 128e3
31
+ });
32
+ const chunks = [];
33
+ recorder.ondataavailable = (e) => {
34
+ if (e.data.size > 0) chunks.push(e.data);
35
+ };
36
+ const stopped = new Promise((res) => {
37
+ recorder.onstop = () => res();
38
+ });
39
+ recorder.start();
40
+ const totalMs = scenes.reduce((s, sc) => s + sc.durationMs, 0);
41
+ const t0 = performance.now();
42
+ onProgress?.({ phase: "capturing", progress01: 0 });
43
+ let sceneStart = 0;
44
+ let sceneIdx = 0;
45
+ let enteredScene = -1;
46
+ ctx.fillStyle = background;
47
+ ctx.fillRect(0, 0, width, height);
48
+ scenes[0].draw(ctx, 0);
49
+ await new Promise((resolve) => {
50
+ function tick() {
51
+ const now = performance.now() - t0;
52
+ if (now >= totalMs) {
53
+ setTimeout(resolve, 100);
54
+ return;
55
+ }
56
+ while (sceneIdx < scenes.length - 1 && now - sceneStart >= scenes[sceneIdx].durationMs) {
57
+ sceneStart += scenes[sceneIdx].durationMs;
58
+ sceneIdx += 1;
59
+ }
60
+ const scene = scenes[sceneIdx];
61
+ if (enteredScene !== sceneIdx) {
62
+ enteredScene = sceneIdx;
63
+ scene.onEnter?.();
64
+ }
65
+ const tInScene = (now - sceneStart) / scene.durationMs;
66
+ ctx.fillStyle = background;
67
+ ctx.fillRect(0, 0, width, height);
68
+ scene.draw(ctx, Math.min(1, Math.max(0, tInScene)));
69
+ onProgress?.({
70
+ phase: "capturing",
71
+ progress01: Math.min(0.95, now / totalMs)
72
+ });
73
+ requestAnimationFrame(tick);
74
+ }
75
+ requestAnimationFrame(tick);
76
+ });
77
+ onProgress?.({ phase: "finalizing", progress01: 0.96 });
78
+ recorder.stop();
79
+ await stopped;
80
+ return new Blob(chunks, { type: mimeType });
81
+ }
82
+ function truncate(s, max) {
83
+ return s.length > max ? s.slice(0, max - 1).trimEnd() + "\u2026" : s;
84
+ }
85
+ function initials(name) {
86
+ return name.split(/\s+/).filter(Boolean).slice(0, 3).map((w) => w[0]).join("").toUpperCase();
87
+ }
88
+ function hookScene(theme, opts) {
89
+ return {
90
+ durationMs: 2e3,
91
+ draw(ctx, _t01) {
92
+ const W = ctx.canvas.width;
93
+ const H = ctx.canvas.height;
94
+ ctx.textAlign = "center";
95
+ ctx.fillStyle = theme.ink;
96
+ ctx.font = `bold 64px ${theme.fontDisplay}`;
97
+ opts.lines.forEach((line, i) => {
98
+ ctx.fillText(line, W / 2, H * 0.42 + i * 78);
99
+ });
100
+ const brand = opts.brand ?? theme.brand;
101
+ ctx.font = `italic 28px ${theme.fontBody}`;
102
+ ctx.fillStyle = theme.sepia;
103
+ ctx.fillText(brand, W / 2, H * 0.42 + opts.lines.length * 78 + 4);
104
+ }
105
+ };
106
+ }
107
+ function revealScene(theme, opts) {
108
+ return {
109
+ durationMs: 2200,
110
+ draw(ctx, t01) {
111
+ const W = ctx.canvas.width;
112
+ const H = ctx.canvas.height;
113
+ const cx = W / 2;
114
+ const cy = H * 0.4;
115
+ const r = 92;
116
+ const k = Math.min(1, t01 * 1.4);
117
+ ctx.globalAlpha = k;
118
+ ctx.textAlign = "center";
119
+ ctx.beginPath();
120
+ ctx.arc(cx, cy, r, 0, Math.PI * 2);
121
+ ctx.fillStyle = theme.paper;
122
+ ctx.fill();
123
+ ctx.lineWidth = 5;
124
+ ctx.strokeStyle = theme.gold;
125
+ ctx.stroke();
126
+ ctx.fillStyle = theme.ink;
127
+ ctx.font = `bold 60px ${theme.fontDisplay}`;
128
+ const badge = opts.initials ?? initials(opts.title);
129
+ ctx.fillText(badge, cx, cy + 21);
130
+ const truncated = truncate(opts.title, 24);
131
+ let fontSize = 54;
132
+ ctx.font = `bold ${fontSize}px ${theme.fontDisplay}`;
133
+ if (ctx.measureText(truncated).width > W * 0.86) {
134
+ fontSize = 46;
135
+ ctx.font = `bold ${fontSize}px ${theme.fontDisplay}`;
136
+ }
137
+ ctx.fillStyle = theme.ink;
138
+ ctx.fillText(truncated, cx, cy + r + 96);
139
+ ctx.font = `italic 34px ${theme.fontBody}`;
140
+ ctx.fillStyle = theme.sepia;
141
+ ctx.fillText(opts.subtitle, cx, cy + r + 150);
142
+ ctx.globalAlpha = 1;
143
+ }
144
+ };
145
+ }
146
+ function ctaScene(theme, opts) {
147
+ return {
148
+ durationMs: 2500,
149
+ draw(ctx, _t01) {
150
+ const W = ctx.canvas.width;
151
+ const H = ctx.canvas.height;
152
+ ctx.textAlign = "center";
153
+ if (opts.lines.length > 0) {
154
+ ctx.fillStyle = theme.ink;
155
+ ctx.font = `bold 52px ${theme.fontDisplay}`;
156
+ ctx.fillText(opts.lines[0], W / 2, H * 0.42);
157
+ }
158
+ opts.lines.slice(1).forEach((line, i) => {
159
+ ctx.fillStyle = theme.accent;
160
+ ctx.font = `bold 60px ${theme.fontDisplay}`;
161
+ ctx.fillText(line, W / 2, H * 0.42 + 86 * (i + 1));
162
+ });
163
+ }
164
+ };
165
+ }
166
+ async function runPromoCapture(opts, record = recordScenes) {
167
+ const w = window;
168
+ w.__promoMeta = opts.meta;
169
+ try {
170
+ const blob = await record(opts.buildScenes(), {
171
+ audioStream: opts.audioStream,
172
+ width: opts.width ?? 1080,
173
+ height: opts.height ?? 1920,
174
+ fps: opts.fps,
175
+ background: opts.background
176
+ });
177
+ const url = URL.createObjectURL(blob);
178
+ w.__promoBlobUrl = url;
179
+ if (opts.download !== false) {
180
+ const a = document.createElement("a");
181
+ a.href = url;
182
+ a.download = `promo-${opts.meta.id ?? "clip"}.webm`;
183
+ document.body.appendChild(a);
184
+ a.click();
185
+ }
186
+ w.__promoReady = true;
187
+ } catch (e) {
188
+ w.__promoError = String(e);
189
+ throw e;
190
+ }
191
+ }
192
+ export {
193
+ ctaScene,
194
+ hookScene,
195
+ initials,
196
+ pickMimeType,
197
+ recordScenes,
198
+ revealScene,
199
+ runPromoCapture,
200
+ truncate
201
+ };
202
+ //# sourceMappingURL=video.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/video.ts"],"sourcesContent":["/**\n * Framework-agnostic promo video module.\n *\n * Provides:\n * - PromoTheme — colour/font/brand tokens\n * - Scene — {durationMs, draw, onEnter?}\n * - recordScenes — rAF-based canvas capture → Blob (browser only)\n * - hookScene / revealScene / ctaScene — scene builders (pure, unit-testable)\n * - runPromoCapture — orchestrator with dependency-injected recorder\n *\n * Visual style ported from whozart/src/lib/promo.ts + video.ts.\n */\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface PromoTheme {\n paper: string;\n ink: string;\n accent: string;\n sepia: string;\n gold: string;\n fontDisplay: string;\n fontBody: string;\n brand: string;\n}\n\nexport interface Scene {\n durationMs: number;\n draw: (ctx: CanvasRenderingContext2D, t01: number) => void;\n onEnter?: () => void;\n}\n\nexport interface RecordOpts {\n audioStream: MediaStream;\n width: number;\n height: number;\n fps?: number;\n background?: string;\n onProgress?: (p: { phase: string; progress01: number }) => void;\n}\n\nexport interface PromoMeta {\n id?: string;\n title?: string;\n composer?: string;\n [k: string]: unknown;\n}\n\nexport interface PromoCaptureOpts {\n buildScenes: () => Scene[];\n audioStream: MediaStream;\n meta: PromoMeta;\n width?: number;\n height?: number;\n fps?: number;\n background?: string;\n download?: boolean;\n}\n\n// ─── MIME type picker ─────────────────────────────────────────────────────────\n\nexport function pickMimeType(): string {\n const candidates = [\n 'video/webm;codecs=vp9,opus',\n 'video/webm;codecs=vp8,opus',\n 'video/webm',\n ];\n for (const t of candidates) {\n if (typeof MediaRecorder !== 'undefined' && MediaRecorder.isTypeSupported(t)) return t;\n }\n return 'video/webm';\n}\n\n// ─── recordScenes ─────────────────────────────────────────────────────────────\n\nexport async function recordScenes(scenes: Scene[], opts: RecordOpts): Promise<Blob> {\n const { audioStream, width, height, fps = 30, background = '#000000', onProgress } = opts;\n\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n\n const maybeCtx = canvas.getContext('2d');\n if (!maybeCtx) throw new Error('Failed to get 2D context');\n const ctx: CanvasRenderingContext2D = maybeCtx;\n\n const videoStream = canvas.captureStream(fps);\n const combined = new MediaStream([\n ...videoStream.getVideoTracks(),\n ...audioStream.getAudioTracks(),\n ]);\n\n const mimeType = pickMimeType();\n const recorder = new MediaRecorder(combined, {\n mimeType,\n videoBitsPerSecond: 4_000_000,\n audioBitsPerSecond: 128_000,\n });\n\n const chunks: Blob[] = [];\n recorder.ondataavailable = (e) => {\n if (e.data.size > 0) chunks.push(e.data);\n };\n const stopped = new Promise<void>((res) => {\n recorder.onstop = () => res();\n });\n\n recorder.start();\n\n const totalMs = scenes.reduce((s, sc) => s + sc.durationMs, 0);\n const t0 = performance.now();\n\n onProgress?.({ phase: 'capturing', progress01: 0 });\n\n let sceneStart = 0;\n let sceneIdx = 0;\n let enteredScene = -1;\n\n // Draw first frame immediately\n ctx.fillStyle = background;\n ctx.fillRect(0, 0, width, height);\n scenes[0].draw(ctx, 0);\n\n await new Promise<void>((resolve) => {\n function tick() {\n const now = performance.now() - t0;\n if (now >= totalMs) {\n setTimeout(resolve, 100);\n return;\n }\n\n // Advance scene index\n while (\n sceneIdx < scenes.length - 1 &&\n now - sceneStart >= scenes[sceneIdx].durationMs\n ) {\n sceneStart += scenes[sceneIdx].durationMs;\n sceneIdx += 1;\n }\n\n const scene = scenes[sceneIdx];\n\n // Fire onEnter once per scene\n if (enteredScene !== sceneIdx) {\n enteredScene = sceneIdx;\n scene.onEnter?.();\n }\n\n const tInScene = (now - sceneStart) / scene.durationMs;\n\n // Clear to background\n ctx.fillStyle = background;\n ctx.fillRect(0, 0, width, height);\n\n scene.draw(ctx, Math.min(1, Math.max(0, tInScene)));\n\n onProgress?.({\n phase: 'capturing',\n progress01: Math.min(0.95, now / totalMs),\n });\n\n requestAnimationFrame(tick);\n }\n\n requestAnimationFrame(tick);\n });\n\n onProgress?.({ phase: 'finalizing', progress01: 0.96 });\n recorder.stop();\n await stopped;\n\n return new Blob(chunks, { type: mimeType });\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nexport function truncate(s: string, max: number): string {\n return s.length > max ? s.slice(0, max - 1).trimEnd() + '…' : s;\n}\n\nexport function initials(name: string): string {\n return name\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 3)\n .map((w) => w[0])\n .join('')\n .toUpperCase();\n}\n\n// ─── Scene builders ───────────────────────────────────────────────────────────\n\nexport interface HookSceneOpts {\n lines: string[];\n brand?: string;\n}\n\nexport function hookScene(theme: PromoTheme, opts: HookSceneOpts): Scene {\n return {\n durationMs: 2000,\n draw(ctx, _t01) {\n const W = ctx.canvas.width;\n const H = ctx.canvas.height;\n ctx.textAlign = 'center';\n ctx.fillStyle = theme.ink;\n ctx.font = `bold 64px ${theme.fontDisplay}`;\n opts.lines.forEach((line, i) => {\n ctx.fillText(line, W / 2, H * 0.42 + i * 78);\n });\n const brand = opts.brand ?? theme.brand;\n ctx.font = `italic 28px ${theme.fontBody}`;\n ctx.fillStyle = theme.sepia;\n ctx.fillText(brand, W / 2, H * 0.42 + opts.lines.length * 78 + 4);\n },\n };\n}\n\nexport interface RevealSceneOpts {\n title: string;\n subtitle: string;\n initials?: string;\n}\n\nexport function revealScene(theme: PromoTheme, opts: RevealSceneOpts): Scene {\n return {\n durationMs: 2200,\n draw(ctx, t01) {\n const W = ctx.canvas.width;\n const H = ctx.canvas.height;\n const cx = W / 2;\n const cy = H * 0.40;\n const r = 92;\n const k = Math.min(1, t01 * 1.4);\n ctx.globalAlpha = k;\n ctx.textAlign = 'center';\n\n // Medallion circle (faint surface)\n ctx.beginPath();\n ctx.arc(cx, cy, r, 0, Math.PI * 2);\n ctx.fillStyle = theme.paper;\n ctx.fill();\n ctx.lineWidth = 5;\n ctx.strokeStyle = theme.gold;\n ctx.stroke();\n\n // Initials inside medallion\n ctx.fillStyle = theme.ink;\n ctx.font = `bold 60px ${theme.fontDisplay}`;\n const badge = opts.initials ?? initials(opts.title);\n ctx.fillText(badge, cx, cy + 21);\n\n // Title (with font-size fallback if too wide)\n const truncated = truncate(opts.title, 24);\n let fontSize = 54;\n ctx.font = `bold ${fontSize}px ${theme.fontDisplay}`;\n if (ctx.measureText(truncated).width > W * 0.86) {\n fontSize = 46;\n ctx.font = `bold ${fontSize}px ${theme.fontDisplay}`;\n }\n ctx.fillStyle = theme.ink;\n ctx.fillText(truncated, cx, cy + r + 96);\n\n // Subtitle\n ctx.font = `italic 34px ${theme.fontBody}`;\n ctx.fillStyle = theme.sepia;\n ctx.fillText(opts.subtitle, cx, cy + r + 150);\n\n ctx.globalAlpha = 1;\n },\n };\n}\n\nexport interface CtaSceneOpts {\n lines: string[];\n}\n\nexport function ctaScene(theme: PromoTheme, opts: CtaSceneOpts): Scene {\n return {\n durationMs: 2500,\n draw(ctx, _t01) {\n const W = ctx.canvas.width;\n const H = ctx.canvas.height;\n ctx.textAlign = 'center';\n if (opts.lines.length > 0) {\n ctx.fillStyle = theme.ink;\n ctx.font = `bold 52px ${theme.fontDisplay}`;\n ctx.fillText(opts.lines[0], W / 2, H * 0.42);\n }\n opts.lines.slice(1).forEach((line, i) => {\n ctx.fillStyle = theme.accent;\n ctx.font = `bold 60px ${theme.fontDisplay}`;\n ctx.fillText(line, W / 2, H * 0.42 + 86 * (i + 1));\n });\n },\n };\n}\n\n// ─── runPromoCapture ──────────────────────────────────────────────────────────\n\nexport async function runPromoCapture(\n opts: PromoCaptureOpts,\n record: (scenes: Scene[], o: RecordOpts) => Promise<Blob> = recordScenes,\n): Promise<void> {\n const w = window as unknown as Record<string, unknown>;\n w.__promoMeta = opts.meta;\n try {\n const blob = await record(opts.buildScenes(), {\n audioStream: opts.audioStream,\n width: opts.width ?? 1080,\n height: opts.height ?? 1920,\n fps: opts.fps,\n background: opts.background,\n });\n const url = URL.createObjectURL(blob);\n w.__promoBlobUrl = url;\n if (opts.download !== false) {\n const a = document.createElement('a');\n a.href = url;\n a.download = `promo-${opts.meta.id ?? 'clip'}.webm`;\n document.body.appendChild(a);\n a.click();\n }\n w.__promoReady = true;\n } catch (e) {\n w.__promoError = String(e);\n throw e;\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,16 +1,28 @@
1
1
  {
2
2
  "name": "@real-music-packages/web-core",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Shared music-theory + audio primitives for the music-suite web apps",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
9
9
  "exports": {
10
- ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" },
11
- "./audio": { "types": "./dist/audio.d.ts", "import": "./dist/audio.js" }
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./audio": {
15
+ "types": "./dist/audio.d.ts",
16
+ "import": "./dist/audio.js"
17
+ },
18
+ "./video": {
19
+ "types": "./dist/video.d.ts",
20
+ "import": "./dist/video.js"
21
+ }
12
22
  },
13
- "files": ["dist"],
23
+ "files": [
24
+ "dist"
25
+ ],
14
26
  "scripts": {
15
27
  "build": "tsup",
16
28
  "test": "vitest run",
@@ -21,12 +33,17 @@
21
33
  "publishConfig": {
22
34
  "access": "public"
23
35
  },
24
- "repository": { "type": "git", "url": "git+https://github.com/e7mac/web-core.git" },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/e7mac/web-core.git"
39
+ },
25
40
  "peerDependencies": {
26
41
  "tone": ">=14"
27
42
  },
28
43
  "peerDependenciesMeta": {
29
- "tone": { "optional": true }
44
+ "tone": {
45
+ "optional": true
46
+ }
30
47
  },
31
48
  "devDependencies": {
32
49
  "tone": "^15.1.22",