@jokerized/decksmith 0.6.0 → 0.7.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/README.md CHANGED
@@ -850,8 +850,8 @@ Three, each a position rather than a hue.
850
850
 
851
851
  `mono`'s four tones are a grey ladder plus one red rather than four hues, because value is
852
852
  what survives a bad projector and hue is not; four hues would have collapsed into one
853
- grey. All three keep the Inter stack — font families auto-resolve from a fixed allowlist,
854
- and a serif naming a family the bundle does not declare falls back silently.
853
+ grey. All three keep the Inter stack — `build` ships Inter beside every deck, and a serif
854
+ naming a family the deck does not declare falls back silently.
855
855
 
856
856
  A theme is a name and a palette, and that is the whole extension point: a new one is a
857
857
  file in `src/emit/themes/` plus a line in `THEMES`. No archetype learns it exists.
@@ -1204,8 +1204,10 @@ mechanically rather than free-hand.
1204
1204
  presented, never rendered, and its subtitle loop legitimately reads a clock.
1205
1205
  - `deck.html` never contains the string `data-composition-id`. A root-level HTML file that
1206
1206
  does trips lint's `multiple_root_compositions`, and the deck stops being navigable.
1207
- - Fonts auto-resolve only from a fixed allowlist. Inter is on it, Noto Sans KR is not, so
1208
- every CJK deck ships its own subsetted `@font-face` or the text silently falls back.
1207
+ - Every deck declares the face its stack names. A CJK deck ships a subsetted Noto; every
1208
+ other deck ships Inter from `@fontsource-variable/inter`. HyperFrames resolves Inter from
1209
+ its own allowlist, but only inside a render: the gates' page and `deck.html` never run its
1210
+ compiler, and without the deck's own face they drew SF on a Mac and DejaVu on Linux.
1209
1211
  - Audience text never goes below 40px at 1920x1080, display equations sit at 60–76px. A
1210
1212
  30px equation passes every automated gate and is unreadable from row six.
1211
1213
  - **No archetype declares its own content width.** The box comes from
package/dist/cli.js CHANGED
@@ -157,6 +157,11 @@ async function copyAudio(from, narration, out, step2) {
157
157
  return names.map((n3) => join2(dir, n3));
158
158
  }
159
159
  async function refreshFont(storyboard, source, out, step2) {
160
+ if (familyFor(storyboard.lang) === null) {
161
+ const css = await vendorInter(join2(out, "assets", "fonts"));
162
+ step2("build: vendored Inter beside the deck");
163
+ return css;
164
+ }
160
165
  try {
161
166
  const bundle = await bundleFont(
162
167
  storyboard.lang,
@@ -172,6 +177,21 @@ async function refreshFont(storyboard, source, out, step2) {
172
177
  return void 0;
173
178
  }
174
179
  }
180
+ async function vendorInter(dir) {
181
+ const pkg = dirname(createRequire(import.meta.url).resolve("@fontsource-variable/inter"));
182
+ const css = await readFile2(join2(pkg, "index.css"), "utf8");
183
+ await mkdir2(dir, { recursive: true });
184
+ for (const [, name] of css.matchAll(/url\(\.\/files\/([^)]+)\)/g)) {
185
+ await cp(join2(pkg, "files", name ?? ""), join2(dir, name ?? ""));
186
+ }
187
+ const ours = css.replaceAll("font-family: 'Inter Variable'", "font-family: 'Inter'").replaceAll("font-display: swap", "font-display: block").replace(/url\(\.\/files\/([^)]+)\) format\('woff2-variations'\)/g, "url($1) format('woff2')");
188
+ if (/Inter Variable|\.\/files\/|swap/.test(ours) || !ours.includes("font-family: 'Inter';")) {
189
+ throw new Error(
190
+ `@fontsource-variable/inter's index.css changed shape; update vendorInter (${pkg})`
191
+ );
192
+ }
193
+ return ours;
194
+ }
175
195
 
176
196
  // src/emit/kit.ts
177
197
  var PAD_X = 110;
package/dist/index.js CHANGED
@@ -152,6 +152,11 @@ async function copyAudio(from, narration, out, step) {
152
152
  return names.map((n3) => join2(dir, n3));
153
153
  }
154
154
  async function refreshFont(storyboard, source, out, step) {
155
+ if (familyFor(storyboard.lang) === null) {
156
+ const css = await vendorInter(join2(out, "assets", "fonts"));
157
+ step("build: vendored Inter beside the deck");
158
+ return css;
159
+ }
155
160
  try {
156
161
  const bundle = await bundleFont(
157
162
  storyboard.lang,
@@ -167,6 +172,21 @@ async function refreshFont(storyboard, source, out, step) {
167
172
  return void 0;
168
173
  }
169
174
  }
175
+ async function vendorInter(dir) {
176
+ const pkg = dirname(createRequire(import.meta.url).resolve("@fontsource-variable/inter"));
177
+ const css = await readFile2(join2(pkg, "index.css"), "utf8");
178
+ await mkdir2(dir, { recursive: true });
179
+ for (const [, name] of css.matchAll(/url\(\.\/files\/([^)]+)\)/g)) {
180
+ await cp(join2(pkg, "files", name ?? ""), join2(dir, name ?? ""));
181
+ }
182
+ const ours = css.replaceAll("font-family: 'Inter Variable'", "font-family: 'Inter'").replaceAll("font-display: swap", "font-display: block").replace(/url\(\.\/files\/([^)]+)\) format\('woff2-variations'\)/g, "url($1) format('woff2')");
183
+ if (/Inter Variable|\.\/files\/|swap/.test(ours) || !ours.includes("font-family: 'Inter';")) {
184
+ throw new Error(
185
+ `@fontsource-variable/inter's index.css changed shape; update vendorInter (${pkg})`
186
+ );
187
+ }
188
+ return ours;
189
+ }
170
190
 
171
191
  // src/emit/kit.ts
172
192
  var PAD_X = 110;
package/dist/mcp.js CHANGED
@@ -218,6 +218,11 @@ async function copyAudio(from, narration, out, step) {
218
218
  return names.map((n3) => join3(dir, n3));
219
219
  }
220
220
  async function refreshFont(storyboard, source, out, step) {
221
+ if (familyFor(storyboard.lang) === null) {
222
+ const css = await vendorInter(join3(out, "assets", "fonts"));
223
+ step("build: vendored Inter beside the deck");
224
+ return css;
225
+ }
221
226
  try {
222
227
  const bundle = await bundleFont(
223
228
  storyboard.lang,
@@ -233,6 +238,21 @@ async function refreshFont(storyboard, source, out, step) {
233
238
  return void 0;
234
239
  }
235
240
  }
241
+ async function vendorInter(dir) {
242
+ const pkg = dirname2(createRequire3(import.meta.url).resolve("@fontsource-variable/inter"));
243
+ const css = await readFile2(join3(pkg, "index.css"), "utf8");
244
+ await mkdir2(dir, { recursive: true });
245
+ for (const [, name] of css.matchAll(/url\(\.\/files\/([^)]+)\)/g)) {
246
+ await cp(join3(pkg, "files", name ?? ""), join3(dir, name ?? ""));
247
+ }
248
+ const ours = css.replaceAll("font-family: 'Inter Variable'", "font-family: 'Inter'").replaceAll("font-display: swap", "font-display: block").replace(/url\(\.\/files\/([^)]+)\) format\('woff2-variations'\)/g, "url($1) format('woff2')");
249
+ if (/Inter Variable|\.\/files\/|swap/.test(ours) || !ours.includes("font-family: 'Inter';")) {
250
+ throw new Error(
251
+ `@fontsource-variable/inter's index.css changed shape; update vendorInter (${pkg})`
252
+ );
253
+ }
254
+ return ours;
255
+ }
236
256
 
237
257
  // src/emit/kit.ts
238
258
  var PAD_X = 110;
@@ -97,6 +97,8 @@ export declare function copyAudio(from: string, narration: DeckNarration, out: s
97
97
  * Every glyph a document or plan can put on screen: serializing the whole object
98
98
  * over-collects ids and TeX, but those are ASCII and the bundle only exists for
99
99
  * CJK — over-collecting costs a few bytes, under-collecting costs a tofu box.
100
+ *
101
+ * A Latin deck gets `vendorInter` instead: nothing to subset, nothing to fetch.
100
102
  */
101
103
  export declare function refreshFont(storyboard: Storyboard, source: Source, out: string, step: Step): Promise<string | undefined>;
102
104
  export {};
@@ -102,8 +102,9 @@ export interface DeckOptions {
102
102
  * naming a family the bundle does not declare falls back silently.
103
103
  *
104
104
  * Passed in rather than read here because this function does no I/O. Absent,
105
- * the link is emitted as before, so a caller that has no bundle is unchanged
106
- * and every non-CJK deck is byte-identical either way.
105
+ * the link is emitted as before, so a caller that has no bundle is unchanged.
106
+ * `build` passes one for every deck: a Latin deck's is the vendored Inter
107
+ * (`vendorInter` in src/build/files.ts), for the same reason.
107
108
  */
108
109
  fontCss?: string;
109
110
  }
@@ -6,7 +6,7 @@ export interface FontBundle {
6
6
  /** The woff2 basenames written into `dir`, beside `fonts.css`. */
7
7
  files: string[];
8
8
  }
9
- /** The family a language needs shipped, or null when the renderer supplies one. */
9
+ /** The CJK family a language needs subset, or null: the deck ships Inter (`vendorInter`). */
10
10
  export declare function familyFor(lang: string): string | null;
11
11
  /**
12
12
  * Write `dir/fonts.css` plus its woff2 for `lang`, covering `glyphs`. Returns
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jokerized/decksmith",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Turn a source document into an animated explanation deck.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -58,6 +58,7 @@
58
58
  "demo": "node demo/build.mjs && node dist/cli.js build demo/storyboard.json --source demo/source.json -o demo/deck && python3 -m http.server 8080 --directory demo/deck --bind 127.0.0.1"
59
59
  },
60
60
  "dependencies": {
61
+ "@fontsource-variable/inter": "5.3.0",
61
62
  "@modelcontextprotocol/sdk": "^1.30.0",
62
63
  "@puppeteer/browsers": "^3.0.6",
63
64
  "commander": "^14.0.1",