@jokerized/decksmith 0.3.2 → 0.4.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 +96 -14
- package/dist/cli.js +450 -151
- package/dist/deck-runtime.js +2 -2
- package/dist/index.js +359 -68
- package/dist/mcp.js +477 -179
- package/dist/types/deck/runtime.d.ts +127 -0
- package/dist/types/emit/camera.d.ts +14 -6
- package/dist/types/emit/composition.d.ts +13 -0
- package/dist/types/emit/kit.d.ts +33 -8
- package/dist/types/emit/svg.d.ts +27 -0
- package/dist/types/index.d.ts +19 -0
- package/dist/types/tmpdir.d.ts +33 -0
- package/dist/types/types.d.ts +28 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { cp, mkdir as mkdir10, readdir as readdir4, readFile as readFile17, stat as stat2, writeFile as writeFile12 } from "node:fs/promises";
|
|
3
|
-
import { createRequire as
|
|
4
|
-
import { dirname as
|
|
3
|
+
import { createRequire as createRequire4 } from "node:module";
|
|
4
|
+
import { dirname as dirname5, join as join17, resolve as resolve6 } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
7
7
|
// src/pack/media.ts
|
|
@@ -414,15 +414,53 @@ var dataTableParamsSchema = z.object({
|
|
|
414
414
|
path: ["highlight"],
|
|
415
415
|
message: "every highlight must name a row that params.rows draws"
|
|
416
416
|
});
|
|
417
|
+
var chartPointSchema = z.object({ x: z.string(), y: z.number() });
|
|
417
418
|
var lineChartParamsSchema = z.object({
|
|
418
419
|
eyebrow: z.string().optional(),
|
|
419
420
|
headline: z.string(),
|
|
420
421
|
xLabel: z.string(),
|
|
421
422
|
yLabel: z.string(),
|
|
422
|
-
points: z.array(
|
|
423
|
+
points: z.array(chartPointSchema).min(2),
|
|
423
424
|
/** Inter-point annotations, e.g. per-step deltas. One fewer than `points`. */
|
|
424
425
|
deltas: z.array(z.string()).optional(),
|
|
425
|
-
readout: z.string().optional()
|
|
426
|
+
readout: z.string().optional(),
|
|
427
|
+
/**
|
|
428
|
+
* THE SAME MEASUREMENT UNDER A SECOND CONDITION — a baseline the main series
|
|
429
|
+
* is to be read against, where the point is the change in the SHAPE of the
|
|
430
|
+
* curve rather than two numbers.
|
|
431
|
+
*
|
|
432
|
+
* Drawn first and alone; then the curve lifts off it and reshapes into
|
|
433
|
+
* `points`, leaving this one behind as a ghost. `label` names the ghost and
|
|
434
|
+
* is drawn wherever the chart has room for it.
|
|
435
|
+
*
|
|
436
|
+
* KEEP IT SHORT — two or three words. A label wider than the plot is refused
|
|
437
|
+
* outright, and that refusal reaches `onBeatError` and costs the whole beat.
|
|
438
|
+
* One that fits but cannot be placed clear of the axis names, the tick and
|
|
439
|
+
* category labels, the values, the deltas and both curves is DROPPED
|
|
440
|
+
* instead, silently: an unnamed ghost is still legibly the fainter, earlier
|
|
441
|
+
* curve, where a name printed through a number is a defect in both of them.
|
|
442
|
+
* Nothing warns about that one — see the placement note in
|
|
443
|
+
* `src/emit/archetypes/line-chart.ts`.
|
|
444
|
+
*/
|
|
445
|
+
compare: z.object({ label: z.string(), points: z.array(chartPointSchema).min(2) }).optional()
|
|
446
|
+
}).superRefine((p, ctx) => {
|
|
447
|
+
if (!p.compare) return;
|
|
448
|
+
if (p.compare.points.length !== p.points.length) {
|
|
449
|
+
ctx.addIssue({
|
|
450
|
+
code: z.ZodIssueCode.custom,
|
|
451
|
+
path: ["compare", "points"],
|
|
452
|
+
message: `compare has ${p.compare.points.length} points against ${p.points.length}; a comparison is over the same x values`
|
|
453
|
+
});
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
const off = p.compare.points.findIndex((c, i) => c.x !== p.points[i]?.x);
|
|
457
|
+
if (off >= 0) {
|
|
458
|
+
ctx.addIssue({
|
|
459
|
+
code: z.ZodIssueCode.custom,
|
|
460
|
+
path: ["compare", "points", off, "x"],
|
|
461
|
+
message: `compare point ${off} is "${p.compare.points[off]?.x}" where points has "${p.points[off]?.x}"`
|
|
462
|
+
});
|
|
463
|
+
}
|
|
426
464
|
});
|
|
427
465
|
var calloutParamsSchema = z.object({
|
|
428
466
|
eyebrow: z.string().optional(),
|
|
@@ -1205,8 +1243,8 @@ async function bundleFont(lang, glyphs, dir) {
|
|
|
1205
1243
|
${text2}`).digest("hex").slice(0, 16)} */`;
|
|
1206
1244
|
await mkdir(dir, { recursive: true });
|
|
1207
1245
|
const cssPath = join(dir, "fonts.css");
|
|
1208
|
-
const
|
|
1209
|
-
if (
|
|
1246
|
+
const cached2 = await readFile2(cssPath, "utf8").catch(() => "");
|
|
1247
|
+
if (cached2.startsWith(stamp)) return { family, css: cached2, files: localNames(cached2) };
|
|
1210
1248
|
const res = await fetch(
|
|
1211
1249
|
`https://fonts.googleapis.com/css2?family=${family.replaceAll(" ", "+")}:wght@400;500;700&text=${encodeURIComponent(text2)}&display=block`,
|
|
1212
1250
|
{ headers: { "User-Agent": UA } }
|
|
@@ -1419,6 +1457,21 @@ function nv(v) {
|
|
|
1419
1457
|
}
|
|
1420
1458
|
var DRAW_FROM = { drawSVG: "0%" };
|
|
1421
1459
|
var DRAW_TO = { drawSVG: "100%" };
|
|
1460
|
+
var SHAPE_INDEX = 0;
|
|
1461
|
+
function reshape(target, to, at, seconds, first) {
|
|
1462
|
+
if (!(seconds > 0)) throw new Error(`reshape ${target}: ${seconds}s is no time to reshape in`);
|
|
1463
|
+
return fromTo(
|
|
1464
|
+
target,
|
|
1465
|
+
{ morphSVG: { shape: target, shapeIndex: SHAPE_INDEX } },
|
|
1466
|
+
{
|
|
1467
|
+
morphSVG: { shape: to, shapeIndex: SHAPE_INDEX },
|
|
1468
|
+
duration: sec(seconds),
|
|
1469
|
+
ease: "power2.inOut",
|
|
1470
|
+
...first ? {} : { immediateRender: false }
|
|
1471
|
+
},
|
|
1472
|
+
sec(at)
|
|
1473
|
+
);
|
|
1474
|
+
}
|
|
1422
1475
|
function travel(target, route, at, seconds) {
|
|
1423
1476
|
if (!(seconds > 0)) throw new Error(`travel ${target}: ${seconds}s is no time to travel in`);
|
|
1424
1477
|
const legs = [];
|
|
@@ -4100,6 +4153,12 @@ var CHART_GAP = 60;
|
|
|
4100
4153
|
var READOUT_W = 460;
|
|
4101
4154
|
var READOUT_LH = 1.35;
|
|
4102
4155
|
var TALL_ASPECT = 1.15;
|
|
4156
|
+
var RESHAPE_SECONDS = 1.2;
|
|
4157
|
+
var GHOST_OPACITY = 0.28;
|
|
4158
|
+
var DRAW_SECONDS = 1.8;
|
|
4159
|
+
var DRAW_FLOOR = 1;
|
|
4160
|
+
var SEPARATE = 0.5;
|
|
4161
|
+
var STEP_FLOOR = 0.15;
|
|
4103
4162
|
function chartScale(values) {
|
|
4104
4163
|
const lo = Math.min(...values);
|
|
4105
4164
|
const hi = Math.max(...values);
|
|
@@ -4130,7 +4189,8 @@ var lineChart = (beat, ctx) => {
|
|
|
4130
4189
|
const { sid, theme } = ctx;
|
|
4131
4190
|
const p = beat.params;
|
|
4132
4191
|
const face = faceOf(theme.fontStack);
|
|
4133
|
-
const
|
|
4192
|
+
const cmp = p.compare;
|
|
4193
|
+
const scale = chartScale([...p.points, ...cmp?.points ?? []].map((pt) => pt.y));
|
|
4134
4194
|
const box = contentW(ctx.format);
|
|
4135
4195
|
const tall = isPortrait(ctx.format);
|
|
4136
4196
|
const width = tall ? box : box - (p.readout ? CHART_GAP + READOUT_W : 0);
|
|
@@ -4173,11 +4233,15 @@ var lineChart = (beat, ctx) => {
|
|
|
4173
4233
|
const LABEL_SIZE3 = 40;
|
|
4174
4234
|
const runW = (s) => textWidth(s, LABEL_SIZE3, 400, 0, false, face);
|
|
4175
4235
|
const catW = (s) => textWidth(s, LABEL_SIZE3, 400, 0, false, face);
|
|
4236
|
+
const nameW = (s) => textWidth(s, LABEL_SIZE3, 500, 0, false, face);
|
|
4237
|
+
const ghostW = (s) => textWidth(s, LABEL_SIZE3, 600, 0, false, face);
|
|
4176
4238
|
const shownX = fitIndices((i) => catW(p.points[i]?.x ?? ""), false);
|
|
4177
4239
|
const xLabels = p.points.map(
|
|
4178
4240
|
(pt, i) => shownX.has(i) ? `<text x="${n2(x(i))}" y="${H - PAD2.b + 56}">${esc(pt.x)}</text>` : ""
|
|
4179
4241
|
).join("");
|
|
4180
|
-
const
|
|
4242
|
+
const pathOf = (pts) => pts.map((pt, i) => `${i === 0 ? "M" : "L"}${n2(x(i))},${n2(y(pt.y))}`).join(" ");
|
|
4243
|
+
const path2 = pathOf(p.points);
|
|
4244
|
+
const basePath = cmp ? pathOf(cmp.points) : "";
|
|
4181
4245
|
const dots = p.points.map(
|
|
4182
4246
|
(pt, i) => `<circle class="dot" cx="${n2(x(i))}" cy="${n2(y(pt.y))}" r="${i === p.points.length - 1 ? 11 : 9}" fill="${i === p.points.length - 1 ? theme.tones.b : theme.accent}" />`
|
|
4183
4247
|
).join("");
|
|
@@ -4213,6 +4277,75 @@ var lineChart = (beat, ctx) => {
|
|
|
4213
4277
|
}).join("");
|
|
4214
4278
|
const first = { x: x(0), y: y(p.points[0]?.y ?? 0) };
|
|
4215
4279
|
const ring = p.points.length > 1 ? `<circle id="${sid}-ring" cx="${n2(first.x)}" cy="${n2(first.y)}" r="20" fill="none" stroke="${theme.tones.b}" stroke-width="5" opacity="0" />` : "";
|
|
4280
|
+
const lineTag = (part, d, extra = "") => `<path class="chartline" id="${sid}-${part}" d="${d}" fill="none" stroke="${theme.accent}"${extra} />`;
|
|
4281
|
+
const chartlines = cmp ? [
|
|
4282
|
+
lineTag("base", basePath),
|
|
4283
|
+
lineTag("line", basePath, ' opacity="0"'),
|
|
4284
|
+
`<path id="${sid}-target" d="${path2}" fill="none" stroke="none" />`
|
|
4285
|
+
].join("\n ") : lineTag("line", path2);
|
|
4286
|
+
const curveBand = (pts, x0, x1) => {
|
|
4287
|
+
const ys = [];
|
|
4288
|
+
const py = (i) => y(pts[i]?.y ?? 0);
|
|
4289
|
+
for (let i = 0; i < pts.length; i++) {
|
|
4290
|
+
if (x(i) >= x0 && x(i) <= x1) ys.push(py(i));
|
|
4291
|
+
}
|
|
4292
|
+
for (let i = 0; i + 1 < pts.length; i++) {
|
|
4293
|
+
const [ax, bx] = [x(i), x(i + 1)];
|
|
4294
|
+
for (const edge of [x0, x1]) {
|
|
4295
|
+
if (edge > Math.min(ax, bx) && edge < Math.max(ax, bx)) {
|
|
4296
|
+
ys.push(py(i) + (py(i + 1) - py(i)) * (edge - ax) / (bx - ax));
|
|
4297
|
+
}
|
|
4298
|
+
}
|
|
4299
|
+
}
|
|
4300
|
+
return ys.length > 0 ? [Math.min(...ys), Math.max(...ys)] : [Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY];
|
|
4301
|
+
};
|
|
4302
|
+
const fixedBoxes = [
|
|
4303
|
+
// `.axname` is the one run here set at weight 500 rather than 400, and
|
|
4304
|
+
// under-charging a width is the unrecoverable direction (see `padR`).
|
|
4305
|
+
// No `text-anchor` on the y half, so it runs rightwards from x=0.
|
|
4306
|
+
{ x: 0, y: 42 - LABEL_SIZE3, w: nameW(p.yLabel), h: LABEL_SIZE3 },
|
|
4307
|
+
{
|
|
4308
|
+
x: PAD2.l + plotW / 2 - nameW(p.xLabel) / 2,
|
|
4309
|
+
y: H - 16 - LABEL_SIZE3,
|
|
4310
|
+
w: nameW(p.xLabel),
|
|
4311
|
+
h: LABEL_SIZE3
|
|
4312
|
+
},
|
|
4313
|
+
...ticks.map((v) => {
|
|
4314
|
+
const w = runW(v.toFixed(scale.decimals));
|
|
4315
|
+
return { x: PAD2.l - 22 - w, y: y(v) + 13 - LABEL_SIZE3, w, h: LABEL_SIZE3 };
|
|
4316
|
+
}),
|
|
4317
|
+
...p.points.map((pt, i) => ({ pt, i })).filter(({ i }) => shownX.has(i)).map(({ pt, i }) => labelBox(x(i), H - PAD2.b + 56, pt.x))
|
|
4318
|
+
];
|
|
4319
|
+
const lastI = p.points.length - 1;
|
|
4320
|
+
const ghost = (() => {
|
|
4321
|
+
if (!cmp) return void 0;
|
|
4322
|
+
const w = ghostW(cmp.label);
|
|
4323
|
+
if (w > plotW) {
|
|
4324
|
+
throw new Error(
|
|
4325
|
+
`line-chart ${beat.id}: the compare label "${cmp.label}" is ${Math.ceil(w)}px against the ${Math.floor(plotW)}px of plot it is set in. Shorten it.`
|
|
4326
|
+
);
|
|
4327
|
+
}
|
|
4328
|
+
const atEnd = (i, start) => {
|
|
4329
|
+
const cy = y(cmp.points[i]?.y ?? 0);
|
|
4330
|
+
const away = Math.max(44, cy - 26);
|
|
4331
|
+
const toward = Math.min(cy + 52, H - PAD2.b + 4);
|
|
4332
|
+
const sides = cy < y(p.points[i]?.y ?? 0) ? [away, toward] : [toward, away];
|
|
4333
|
+
return sides.map((by) => ({ i, start, by }));
|
|
4334
|
+
};
|
|
4335
|
+
const drawnDeltas = deltasFit ? deltaBoxes : [];
|
|
4336
|
+
return [...atEnd(lastI, false), ...atEnd(0, true)].find((c) => {
|
|
4337
|
+
const box2 = { x: c.start ? x(c.i) : x(c.i) - w, y: c.by - LABEL_SIZE3, w, h: LABEL_SIZE3 };
|
|
4338
|
+
if (fixedBoxes.some((f) => overlaps(box2, f))) return false;
|
|
4339
|
+
if (valueBoxes.some((v) => overlaps(box2, v))) return false;
|
|
4340
|
+
if (drawnDeltas.some((d) => d !== null && overlaps(box2, d))) return false;
|
|
4341
|
+
return ![p.points, cmp.points].some((series) => {
|
|
4342
|
+
const [top, bottom] = curveBand(series, box2.x, box2.x + box2.w);
|
|
4343
|
+
return Math.min(box2.y + box2.h, bottom) - Math.max(box2.y, top) > 8;
|
|
4344
|
+
});
|
|
4345
|
+
});
|
|
4346
|
+
})();
|
|
4347
|
+
const ghostLabel = cmp && ghost ? `
|
|
4348
|
+
<text class="ghostlab" id="${sid}-ghostlab" text-anchor="${ghost.start ? "start" : "end"}" x="${n2(x(ghost.i))}" y="${n2(ghost.by)}">${esc(cmp.label)}</text>` : "";
|
|
4216
4349
|
const readout = p.readout ? `
|
|
4217
4350
|
<div class="readout" id="${sid}-read">${esc(p.readout)}</div>` : "";
|
|
4218
4351
|
const html = `${chrome(sid, p.eyebrow, p.headline, box, face)}
|
|
@@ -4225,7 +4358,7 @@ var lineChart = (beat, ctx) => {
|
|
|
4225
4358
|
the svg and the layout gate reports container_overflow. -->
|
|
4226
4359
|
<text class="axname" x="0" y="42">${esc(p.yLabel)}</text>
|
|
4227
4360
|
<text class="axname" x="${n2(PAD2.l + plotW / 2)}" y="${H - 16}" text-anchor="middle">${esc(p.xLabel)}</text>
|
|
4228
|
-
|
|
4361
|
+
${chartlines}${ghostLabel}
|
|
4229
4362
|
<g>${dots}</g>
|
|
4230
4363
|
${ring}
|
|
4231
4364
|
<g class="ptlab" text-anchor="middle">${values}</g>
|
|
@@ -4233,10 +4366,52 @@ var lineChart = (beat, ctx) => {
|
|
|
4233
4366
|
</svg>${readout}
|
|
4234
4367
|
</div>`;
|
|
4235
4368
|
const draw2 = 0.8;
|
|
4236
|
-
const
|
|
4369
|
+
const count = p.points.length;
|
|
4370
|
+
const idealStep = Math.min(0.45, DRAW_SECONDS / count);
|
|
4371
|
+
const spine = draw2 + SEPARATE + RESHAPE_SECONDS + (p.readout ? 0.8 : 0) + 0.15;
|
|
4372
|
+
const shownDeltas = deltas ? Math.min((p.deltas ?? []).length, count - 1) : 0;
|
|
4373
|
+
const tailAfter = (s) => Math.max(
|
|
4374
|
+
s * count + 0.4,
|
|
4375
|
+
// the ring: its walk, then the 0.4s it takes to leave
|
|
4376
|
+
0.3 + s * (count - 1),
|
|
4377
|
+
// the dots
|
|
4378
|
+
0.5 + s * (count - 1),
|
|
4379
|
+
...shownDeltas > 0 ? [0.95 + s * (shownDeltas - 1)] : []
|
|
4380
|
+
);
|
|
4381
|
+
const room = beat.seconds - spine;
|
|
4382
|
+
let drawCents = Math.round(DRAW_SECONDS * 100);
|
|
4383
|
+
let stepCents = Math.floor(idealStep * 100);
|
|
4384
|
+
if (cmp) {
|
|
4385
|
+
while (drawCents / 100 + tailAfter(stepCents / 100) > room) {
|
|
4386
|
+
if (drawCents > Math.round(DRAW_FLOOR * 100)) drawCents--;
|
|
4387
|
+
else if (stepCents > Math.round(STEP_FLOOR * 100)) stepCents--;
|
|
4388
|
+
else break;
|
|
4389
|
+
}
|
|
4390
|
+
}
|
|
4391
|
+
const drawFor = cmp ? drawCents / 100 : DRAW_SECONDS;
|
|
4392
|
+
const step = cmp ? stepCents / 100 : idealStep;
|
|
4393
|
+
const lift2 = sec(draw2 + drawFor);
|
|
4394
|
+
const settled = cmp ? sec(lift2 + SEPARATE + RESHAPE_SECONDS) : draw2;
|
|
4237
4395
|
const tl = [
|
|
4238
4396
|
...chromeIn(sid, p.eyebrow !== void 0),
|
|
4239
|
-
tween(
|
|
4397
|
+
tween(
|
|
4398
|
+
`#${sid}-${cmp ? "base" : "line"}`,
|
|
4399
|
+
DRAW_FROM,
|
|
4400
|
+
{ ...DRAW_TO, duration: drawFor, ease: "none" },
|
|
4401
|
+
draw2
|
|
4402
|
+
)
|
|
4403
|
+
];
|
|
4404
|
+
if (cmp) {
|
|
4405
|
+
tl.push(
|
|
4406
|
+
...ghost ? [tween(`#${sid}-ghostlab`, { opacity: 0 }, { opacity: 1, duration: 0.5 }, draw2 + 0.4)] : [],
|
|
4407
|
+
// The copy lifts off — same geometry, so the half-second reads as one line
|
|
4408
|
+
// separating from itself rather than as a second line arriving.
|
|
4409
|
+
tween(`#${sid}-line`, { opacity: 0 }, { opacity: 1, duration: SEPARATE }, lift2),
|
|
4410
|
+
tween(`#${sid}-base`, { opacity: 1 }, { opacity: GHOST_OPACITY, duration: SEPARATE }, lift2),
|
|
4411
|
+
reshape(`#${sid}-line`, `#${sid}-target`, lift2 + SEPARATE, RESHAPE_SECONDS, true)
|
|
4412
|
+
);
|
|
4413
|
+
}
|
|
4414
|
+
tl.push(
|
|
4240
4415
|
tween(
|
|
4241
4416
|
`#${sid} .dot`,
|
|
4242
4417
|
// Origin in both halves, or GSAP's smoothOrigin compensates the change with
|
|
@@ -4244,37 +4419,43 @@ var lineChart = (beat, ctx) => {
|
|
|
4244
4419
|
// polyline they mark, inside the frame and so invisible to every gate.
|
|
4245
4420
|
{ opacity: 0, scale: 0, transformOrigin: "center" },
|
|
4246
4421
|
{ opacity: 1, scale: 1, transformOrigin: "center", duration: 0.3, stagger: step },
|
|
4247
|
-
|
|
4422
|
+
settled
|
|
4248
4423
|
),
|
|
4249
|
-
tween(
|
|
4250
|
-
|
|
4424
|
+
tween(
|
|
4425
|
+
`#${sid} .pv`,
|
|
4426
|
+
{ opacity: 0 },
|
|
4427
|
+
{ opacity: 1, duration: 0.3, stagger: step },
|
|
4428
|
+
settled + 0.2
|
|
4429
|
+
)
|
|
4430
|
+
);
|
|
4251
4431
|
if (deltas) {
|
|
4252
4432
|
tl.push(
|
|
4253
4433
|
tween(
|
|
4254
4434
|
`#${sid} .dv`,
|
|
4255
4435
|
{ opacity: 0, y: -10 },
|
|
4256
4436
|
{ opacity: 1, y: 0, duration: 0.35, stagger: step },
|
|
4257
|
-
|
|
4437
|
+
settled + 0.6
|
|
4258
4438
|
)
|
|
4259
4439
|
);
|
|
4260
4440
|
}
|
|
4261
|
-
|
|
4441
|
+
const walk = cmp ? sec(step * count) : DRAW_SECONDS;
|
|
4442
|
+
if (count > 1) {
|
|
4262
4443
|
const route = p.points.map((pt, i) => ({ x: nv(x(i) - first.x), y: nv(y(pt.y) - first.y) }));
|
|
4263
4444
|
tl.push(
|
|
4264
|
-
tween(`#${sid}-ring`, { opacity: 0 }, { opacity: 1, duration: 0.25 },
|
|
4265
|
-
...travel(`#${sid}-ring`, route,
|
|
4445
|
+
tween(`#${sid}-ring`, { opacity: 0 }, { opacity: 1, duration: 0.25 }, settled),
|
|
4446
|
+
...travel(`#${sid}-ring`, route, settled, walk),
|
|
4266
4447
|
// And it leaves once the curve is whole: a marker parked on the last
|
|
4267
4448
|
// point for the rest of the beat reads as a defect, not as emphasis.
|
|
4268
4449
|
tween(
|
|
4269
4450
|
`#${sid}-ring`,
|
|
4270
4451
|
{ opacity: 1 },
|
|
4271
4452
|
{ opacity: 0, duration: 0.4, immediateRender: false },
|
|
4272
|
-
|
|
4453
|
+
settled + walk
|
|
4273
4454
|
)
|
|
4274
4455
|
);
|
|
4275
4456
|
}
|
|
4276
|
-
const drawn =
|
|
4277
|
-
const holds = [drawn];
|
|
4457
|
+
const drawn = settled + (cmp ? tailAfter(step) : step * count + 0.4);
|
|
4458
|
+
const holds = cmp ? [lift2, drawn] : [drawn];
|
|
4278
4459
|
if (p.readout) {
|
|
4279
4460
|
tl.push(
|
|
4280
4461
|
// It enters from wherever it sits: from the right when it is beside the
|
|
@@ -4283,9 +4464,22 @@ var lineChart = (beat, ctx) => {
|
|
|
4283
4464
|
);
|
|
4284
4465
|
holds.push(drawn + 0.8);
|
|
4285
4466
|
}
|
|
4467
|
+
const end = holds[holds.length - 1] ?? 0;
|
|
4468
|
+
const need = sec(end + 0.15);
|
|
4469
|
+
if (cmp && need > beat.seconds + 1e-9) {
|
|
4470
|
+
return {
|
|
4471
|
+
...lineChart({ ...beat, params: { ...p, compare: void 0 } }, ctx),
|
|
4472
|
+
warnings: [
|
|
4473
|
+
`line-chart ${beat.id}: a comparison against "${cmp.label}" over ${count} points needs ${need}s and the beat is ${beat.seconds}s, so the chart was drawn without it. Lengthen the beat to keep the comparison.`
|
|
4474
|
+
]
|
|
4475
|
+
};
|
|
4476
|
+
}
|
|
4286
4477
|
return {
|
|
4287
4478
|
html,
|
|
4288
4479
|
tl,
|
|
4480
|
+
// Named only when a beat actually reshapes, which is what keeps MorphSVG's
|
|
4481
|
+
// 21,195 bytes off every deck that does not. See `PLUGINS` in composition.ts.
|
|
4482
|
+
...cmp ? { plugins: ["morphSVG"] } : {},
|
|
4289
4483
|
holds: holdsWithin(holds, beat.seconds),
|
|
4290
4484
|
css: [
|
|
4291
4485
|
chromeCss(theme),
|
|
@@ -4310,6 +4504,14 @@ var lineChart = (beat, ctx) => {
|
|
|
4310
4504
|
`.axname{font-size:40px;fill:${theme.muted};font-weight:500}`,
|
|
4311
4505
|
`.ptlab{font-size:40px;fill:${theme.fg};font-weight:600}`,
|
|
4312
4506
|
`.delta{font-size:40px;fill:${theme.tones.b};font-weight:600}`,
|
|
4507
|
+
// Only when a ghost was actually named — not merely when there is a ghost,
|
|
4508
|
+
// since the label is dropped where neither side of the baseline is clear.
|
|
4509
|
+
// An unconditional rule would move the stylesheet bytes of every line chart
|
|
4510
|
+
// ever built for a part they do not draw, which is the whole thing
|
|
4511
|
+
// `Scene.plugins` is careful about one level up. `muted`, not `dim`: it
|
|
4512
|
+
// names a series, so it is read, and the curve it names is the thing that
|
|
4513
|
+
// has been faded, not its label.
|
|
4514
|
+
...ghost ? [`.ghostlab{font-size:40px;fill:${theme.muted};font-weight:600}`] : [],
|
|
4313
4515
|
// 1.7 set the two lines of a wrapped readout 68px apart, which reads as two
|
|
4314
4516
|
// unrelated fragments rather than one sentence. 1.35 keeps it a paragraph.
|
|
4315
4517
|
`.readout{font-size:${BODY_SIZE}px;line-height:${READOUT_LH};color:${theme.muted};max-width:${READOUT_W}px}`,
|
|
@@ -5555,7 +5757,10 @@ function round4(n3) {
|
|
|
5555
5757
|
// src/emit/composition.ts
|
|
5556
5758
|
var GSAP_SRC = "./vendor/gsap.min.js";
|
|
5557
5759
|
var DRAWSVG_SRC = "./vendor/DrawSVGPlugin.min.js";
|
|
5558
|
-
var
|
|
5760
|
+
var PLUGINS = {
|
|
5761
|
+
dsMorph: { src: "./vendor/ds-morph.js", global: "DSMorphPlugin" },
|
|
5762
|
+
morphSVG: { src: "./vendor/MorphSVGPlugin.min.js", global: "MorphSVGPlugin" }
|
|
5763
|
+
};
|
|
5559
5764
|
var KATEX_JS = "./vendor/katex.min.js";
|
|
5560
5765
|
var KATEX_CSS = "./katex/katex.min.css";
|
|
5561
5766
|
function emitDeck(storyboard, source, format, runtimeJs, opts = {}) {
|
|
@@ -5663,7 +5868,15 @@ function layout(storyboard, source, format, opts = {}) {
|
|
|
5663
5868
|
}
|
|
5664
5869
|
if (scene.css) archetypeCss.add(scene.css.trim());
|
|
5665
5870
|
if (scene.measure?.length) builds = true;
|
|
5666
|
-
for (const
|
|
5871
|
+
for (const w of cut2.scene.warnings ?? []) opts.onBeatWarning?.(beat.id, w);
|
|
5872
|
+
for (const p of scene.plugins ?? []) {
|
|
5873
|
+
if (!Object.hasOwn(PLUGINS, p)) {
|
|
5874
|
+
throw new Error(
|
|
5875
|
+
`${beat.archetype} ${beat.id}: no vendored plugin named "${p}" \u2014 Scene.plugins takes ${Object.keys(PLUGINS).join(" or ")}`
|
|
5876
|
+
);
|
|
5877
|
+
}
|
|
5878
|
+
plugins.add(p);
|
|
5879
|
+
}
|
|
5667
5880
|
scenes.push(
|
|
5668
5881
|
sceneHtml(
|
|
5669
5882
|
sid,
|
|
@@ -5783,9 +5996,11 @@ function renderComposition(storyboard, format, laid) {
|
|
|
5783
5996
|
<link rel="stylesheet" href="${FONT_BUNDLE_HREF}" />` : "";
|
|
5784
5997
|
const island = format.navigable ? `
|
|
5785
5998
|
${emitIsland(slides)}` : "";
|
|
5786
|
-
const
|
|
5787
|
-
|
|
5788
|
-
<script
|
|
5999
|
+
const plugins = Object.entries(PLUGINS).filter(([name]) => laid.plugins.has(name)).map(
|
|
6000
|
+
([, p]) => `
|
|
6001
|
+
<script src="${p.src}"></script>
|
|
6002
|
+
<script>gsap.registerPlugin(${p.global});</script>`
|
|
6003
|
+
).join("");
|
|
5789
6004
|
return `<!doctype html>
|
|
5790
6005
|
<html lang="${esc(storyboard.lang)}" data-resolution="${orientation}">
|
|
5791
6006
|
<head>
|
|
@@ -5794,7 +6009,7 @@ ${emitIsland(slides)}` : "";
|
|
|
5794
6009
|
<meta name="viewport" content="width=${format.width}, height=${format.height}" />
|
|
5795
6010
|
<script src="${GSAP_SRC}"></script>
|
|
5796
6011
|
<script src="${DRAWSVG_SRC}"></script>
|
|
5797
|
-
<script>gsap.registerPlugin(DrawSVGPlugin);</script>${
|
|
6012
|
+
<script>gsap.registerPlugin(DrawSVGPlugin);</script>${plugins}
|
|
5798
6013
|
<link rel="stylesheet" href="${KATEX_CSS}" />
|
|
5799
6014
|
<script src="${KATEX_JS}"></script>${fontLink}${fontFace}
|
|
5800
6015
|
<style>
|
|
@@ -6669,8 +6884,8 @@ async function send(url, target, headers, signal, opts) {
|
|
|
6669
6884
|
...secure && isIP(host) === 0 ? { servername: host } : {}
|
|
6670
6885
|
};
|
|
6671
6886
|
try {
|
|
6672
|
-
return await new Promise((
|
|
6673
|
-
const req = (secure ? httpsRequest : httpRequest)(options,
|
|
6887
|
+
return await new Promise((resolve7, reject) => {
|
|
6888
|
+
const req = (secure ? httpsRequest : httpRequest)(options, resolve7);
|
|
6674
6889
|
req.on("error", reject);
|
|
6675
6890
|
req.end();
|
|
6676
6891
|
});
|
|
@@ -6839,7 +7054,7 @@ import { z as z2 } from "zod";
|
|
|
6839
7054
|
async function fetchFigures(source, dir, warnings) {
|
|
6840
7055
|
const drops = warnings ?? [];
|
|
6841
7056
|
await mkdir3(dir, { recursive: true });
|
|
6842
|
-
const
|
|
7057
|
+
const cached2 = await readdir(dir).catch(() => []);
|
|
6843
7058
|
const figures = [];
|
|
6844
7059
|
for (const figure of source.figures) {
|
|
6845
7060
|
if (figure.kind === "clip") {
|
|
@@ -6847,7 +7062,7 @@ async function fetchFigures(source, dir, warnings) {
|
|
|
6847
7062
|
continue;
|
|
6848
7063
|
}
|
|
6849
7064
|
try {
|
|
6850
|
-
figures.push(figureSchema.parse(await localize(figure, dir,
|
|
7065
|
+
figures.push(figureSchema.parse(await localize(figure, dir, cached2)));
|
|
6851
7066
|
} catch (err) {
|
|
6852
7067
|
drops.push(`figure ${figure.id} was left out: ${reason(err)}`);
|
|
6853
7068
|
}
|
|
@@ -6861,15 +7076,15 @@ async function fetchFigures(source, dir, warnings) {
|
|
|
6861
7076
|
}
|
|
6862
7077
|
return parsed.data;
|
|
6863
7078
|
}
|
|
6864
|
-
async function localize(figure, dir,
|
|
7079
|
+
async function localize(figure, dir, cached2) {
|
|
6865
7080
|
const stem = assetStem(figure.id, figure.src);
|
|
6866
|
-
const hit =
|
|
7081
|
+
const hit = cached2.find((name) => name.startsWith(`${stem}.`));
|
|
6867
7082
|
const bytes = hit ? await readFile4(join3(dir, hit)) : await load(figure.src);
|
|
6868
7083
|
const size2 = imageSize(bytes);
|
|
6869
7084
|
if (hit) return { ...figure, src: hit, ...size2 };
|
|
6870
7085
|
const src = `${stem}${assetExt(bytes, figure.src)}`;
|
|
6871
7086
|
await writeFile3(join3(dir, src), bytes);
|
|
6872
|
-
|
|
7087
|
+
cached2.push(src);
|
|
6873
7088
|
return { ...figure, src, ...size2 };
|
|
6874
7089
|
}
|
|
6875
7090
|
function assetStem(id2, src) {
|
|
@@ -7455,7 +7670,7 @@ ${tail3}`);
|
|
|
7455
7670
|
}
|
|
7456
7671
|
}
|
|
7457
7672
|
function runLive(file, args) {
|
|
7458
|
-
return new Promise((
|
|
7673
|
+
return new Promise((resolve7, reject) => {
|
|
7459
7674
|
const child = spawn(file, args, { stdio: ["ignore", "inherit", "inherit"] });
|
|
7460
7675
|
child.on("error", (err) => {
|
|
7461
7676
|
reject(
|
|
@@ -7463,7 +7678,7 @@ function runLive(file, args) {
|
|
|
7463
7678
|
);
|
|
7464
7679
|
});
|
|
7465
7680
|
child.on("close", (code, signal) => {
|
|
7466
|
-
if (code === 0)
|
|
7681
|
+
if (code === 0) resolve7();
|
|
7467
7682
|
else if (signal) {
|
|
7468
7683
|
reject(
|
|
7469
7684
|
new Error(
|
|
@@ -8842,7 +9057,10 @@ var REVEALS = {
|
|
|
8842
9057
|
"equation-walk": "one per term",
|
|
8843
9058
|
"equation-morph": "2",
|
|
8844
9059
|
"data-table": "one per highlighted row, plus 1",
|
|
8845
|
-
"
|
|
9060
|
+
// "long enough" is not a hedge: below the emitter's own floor the comparison
|
|
9061
|
+
// is dropped and the chart stops twice becoming a chart that stops once. See
|
|
9062
|
+
// the line-chart entry under THE THIRTEEN ARCHETYPES for what that costs.
|
|
9063
|
+
"line-chart": "1, or 2 when compare is given and the beat is long enough for it",
|
|
8846
9064
|
callout: "one per panel",
|
|
8847
9065
|
pipeline: "one per stage",
|
|
8848
9066
|
"annotated-figure": "one per note, plus 1",
|
|
@@ -8965,6 +9183,29 @@ DRAWING ARCHETYPES \u2014 reach here first
|
|
|
8965
9183
|
scale, over training. Points come from the source's numbers;
|
|
8966
9184
|
deltas, if given, are the steps between consecutive points, so
|
|
8967
9185
|
there is always one fewer.
|
|
9186
|
+
\`compare\` adds a SECOND CONDITION over the same axis \u2014 the tell
|
|
9187
|
+
there is THE SAME QUANTITY MEASURED TWO WAYS, a baseline and the
|
|
9188
|
+
result, where what the source is claiming is the change in the
|
|
9189
|
+
SHAPE of the curve and not two numbers. Its \`label\` names the
|
|
9190
|
+
baseline ("without pretraining", "FP32") and is drawn on the
|
|
9191
|
+
slide where the chart has room for it. Keep it SHORT \u2014 two or
|
|
9192
|
+
three words. A label wider than the plot is refused and the
|
|
9193
|
+
whole slide goes with it, and one that cannot be placed clear of
|
|
9194
|
+
the axis names, the values and both curves is dropped without a
|
|
9195
|
+
word, leaving the baseline unnamed. A \`readout\` takes the plot
|
|
9196
|
+
down to about two thirds of its width, so a beat that has one
|
|
9197
|
+
has room for a shorter label still.
|
|
9198
|
+
\`compare.points\` must carry the SAME x values, in the
|
|
9199
|
+
same order, as \`points\`; the baseline is drawn first, then the
|
|
9200
|
+
curve reshapes into \`points\` and leaves the baseline behind. Do
|
|
9201
|
+
not use it for two unrelated series that happen to share an
|
|
9202
|
+
axis \u2014 nothing reshapes into something it is not a version of.
|
|
9203
|
+
A compare beat also costs TIME \u2014 the baseline draws, is held,
|
|
9204
|
+
then reshapes \u2014 so give it \`seconds\` of 7, or 8 with a
|
|
9205
|
+
\`readout\`. Give it too few and the emitter draws the chart
|
|
9206
|
+
without the comparison rather than stopping on a half-drawn one:
|
|
9207
|
+
the slide is still there, but the point about the baseline is
|
|
9208
|
+
gone.
|
|
8968
9209
|
|
|
8969
9210
|
DESCRIBING ARCHETYPES \u2014 the fallbacks
|
|
8970
9211
|
|
|
@@ -9749,7 +9990,7 @@ function codexCommand(args) {
|
|
|
9749
9990
|
}
|
|
9750
9991
|
function runCodex(args) {
|
|
9751
9992
|
const { argv, env } = codexCommand(args);
|
|
9752
|
-
return new Promise((
|
|
9993
|
+
return new Promise((resolve7, reject) => {
|
|
9753
9994
|
const child = spawn2("codex", argv, {
|
|
9754
9995
|
stdio: ["pipe", "ignore", "pipe"],
|
|
9755
9996
|
...env === void 0 ? {} : { env }
|
|
@@ -9772,7 +10013,7 @@ function runCodex(args) {
|
|
|
9772
10013
|
});
|
|
9773
10014
|
child.on("close", (code) => {
|
|
9774
10015
|
clearTimeout(timer);
|
|
9775
|
-
if (code === 0) return
|
|
10016
|
+
if (code === 0) return resolve7();
|
|
9776
10017
|
reject(
|
|
9777
10018
|
new Error(`codex exec exited ${code}.
|
|
9778
10019
|
${stderr.trim().split("\n").slice(-8).join("\n")}`)
|
|
@@ -10243,7 +10484,7 @@ async function find2(can) {
|
|
|
10243
10484
|
throw new Error(MISSING);
|
|
10244
10485
|
}
|
|
10245
10486
|
function runArgv(cmd, args) {
|
|
10246
|
-
return new Promise((
|
|
10487
|
+
return new Promise((resolve7) => {
|
|
10247
10488
|
const child = spawn3(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
10248
10489
|
let stdout = "";
|
|
10249
10490
|
let stderr = "";
|
|
@@ -10253,8 +10494,8 @@ function runArgv(cmd, args) {
|
|
|
10253
10494
|
child.stderr.on("data", (b) => {
|
|
10254
10495
|
stderr += b.toString();
|
|
10255
10496
|
});
|
|
10256
|
-
child.on("error", (e) =>
|
|
10257
|
-
child.on("close", (code) =>
|
|
10497
|
+
child.on("error", (e) => resolve7({ code: -1, stderr: String(e), stdout }));
|
|
10498
|
+
child.on("close", (code) => resolve7({ code: code ?? -1, stderr, stdout }));
|
|
10258
10499
|
});
|
|
10259
10500
|
}
|
|
10260
10501
|
var edgeTts = {
|
|
@@ -10353,8 +10594,8 @@ async function synthesize(text2, opts) {
|
|
|
10353
10594
|
const file = `${key}.mp3`;
|
|
10354
10595
|
const audio = join8(opts.dir, file);
|
|
10355
10596
|
const sidecar = join8(opts.dir, `${key}.json`);
|
|
10356
|
-
const
|
|
10357
|
-
if (
|
|
10597
|
+
const cached2 = await readSidecar(sidecar);
|
|
10598
|
+
if (cached2) return { audio, file, seconds: cached2.seconds, cues: cached2.cues };
|
|
10358
10599
|
await mkdir6(opts.dir, { recursive: true });
|
|
10359
10600
|
const spoken = await provider.speak({ text: text2, voice: opts.voice, rate, pitch, audio });
|
|
10360
10601
|
const { cues, seconds } = spoken;
|
|
@@ -12489,13 +12730,61 @@ ${issues}`);
|
|
|
12489
12730
|
return parsed.data;
|
|
12490
12731
|
}
|
|
12491
12732
|
|
|
12733
|
+
// src/tmpdir.ts
|
|
12734
|
+
import { realpathSync } from "node:fs";
|
|
12735
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
12736
|
+
import { tmpdir as tmpdir4 } from "node:os";
|
|
12737
|
+
import { basename as basename4, dirname as dirname4, join as join16, resolve as resolve5, sep as sep2 } from "node:path";
|
|
12738
|
+
var cached;
|
|
12739
|
+
function real(dir) {
|
|
12740
|
+
let at = resolve5(dir);
|
|
12741
|
+
const tail3 = [];
|
|
12742
|
+
for (; ; ) {
|
|
12743
|
+
try {
|
|
12744
|
+
return join16(realpathSync(at), ...tail3);
|
|
12745
|
+
} catch {
|
|
12746
|
+
const up = dirname4(at);
|
|
12747
|
+
if (up === at) return resolve5(dir);
|
|
12748
|
+
tail3.unshift(basename4(at));
|
|
12749
|
+
at = up;
|
|
12750
|
+
}
|
|
12751
|
+
}
|
|
12752
|
+
}
|
|
12753
|
+
function packageRoot() {
|
|
12754
|
+
cached ??= real(dirname4(createRequire3(import.meta.url).resolve("../package.json")));
|
|
12755
|
+
return cached;
|
|
12756
|
+
}
|
|
12757
|
+
function insideRoot(dir) {
|
|
12758
|
+
const root = packageRoot();
|
|
12759
|
+
const at = real(dir);
|
|
12760
|
+
return at === root || at.startsWith(root + sep2);
|
|
12761
|
+
}
|
|
12762
|
+
function guardTmpdir() {
|
|
12763
|
+
const before = tmpdir4();
|
|
12764
|
+
if (!insideRoot(before)) return;
|
|
12765
|
+
delete process.env.TMPDIR;
|
|
12766
|
+
const after = tmpdir4();
|
|
12767
|
+
if (insideRoot(after)) {
|
|
12768
|
+
throw new Error(
|
|
12769
|
+
`tmpdir: ${after} is still inside ${packageRoot()}. Set TMPDIR to a real temp directory.`
|
|
12770
|
+
);
|
|
12771
|
+
}
|
|
12772
|
+
const worker = process.env.VITEST_WORKER_ID;
|
|
12773
|
+
if (worker === void 0 || worker === "1") {
|
|
12774
|
+
process.stderr.write(
|
|
12775
|
+
`tmpdir: TMPDIR pointed at ${before}, inside the source tree, so every mkdtemp would land in the repo. Using ${after}. Fix the environment \u2014 this only stops the mess.
|
|
12776
|
+
`
|
|
12777
|
+
);
|
|
12778
|
+
}
|
|
12779
|
+
}
|
|
12780
|
+
|
|
12492
12781
|
// src/index.ts
|
|
12493
12782
|
async function buildDeck(storyboard, source, outDir, opts = {}) {
|
|
12494
12783
|
const format = opts.format ?? FORMATS["deck-16x9"];
|
|
12495
12784
|
if (!format) throw new Error("no deck-16x9 format");
|
|
12496
12785
|
const step = opts.onStep ?? (() => {
|
|
12497
12786
|
});
|
|
12498
|
-
const out =
|
|
12787
|
+
const out = resolve6(outDir);
|
|
12499
12788
|
await mkdir10(out, { recursive: true });
|
|
12500
12789
|
const speed = opts.speed ?? 1;
|
|
12501
12790
|
const fontCss = await refreshFont(storyboard, source, out, step);
|
|
@@ -12504,12 +12793,13 @@ async function buildDeck(storyboard, source, outDir, opts = {}) {
|
|
|
12504
12793
|
...opts.theme ? { theme: opts.theme } : {},
|
|
12505
12794
|
...opts.narration ? { narration: opts.narration } : {},
|
|
12506
12795
|
...opts.onBeatError ? { onBeatError: opts.onBeatError } : {},
|
|
12796
|
+
...opts.onBeatWarning ? { onBeatWarning: opts.onBeatWarning } : {},
|
|
12507
12797
|
...fontCss ? { fontCss } : {}
|
|
12508
12798
|
});
|
|
12509
12799
|
const files = [];
|
|
12510
12800
|
const write = async (name, text2) => {
|
|
12511
|
-
await writeFile12(
|
|
12512
|
-
files.push(
|
|
12801
|
+
await writeFile12(join17(out, name), text2);
|
|
12802
|
+
files.push(join17(out, name));
|
|
12513
12803
|
};
|
|
12514
12804
|
await write("index.html", deck.composition);
|
|
12515
12805
|
await write("hyperframes.json", HYPERFRAMES_JSON);
|
|
@@ -12538,8 +12828,8 @@ async function buildDeck(storyboard, source, outDir, opts = {}) {
|
|
|
12538
12828
|
}
|
|
12539
12829
|
if (deck.page) {
|
|
12540
12830
|
await write(DECK_PAGE, deck.page);
|
|
12541
|
-
await cp(playerBundle(),
|
|
12542
|
-
files.push(
|
|
12831
|
+
await cp(playerBundle(), join17(out, PLAYER_FILE));
|
|
12832
|
+
files.push(join17(out, PLAYER_FILE));
|
|
12543
12833
|
}
|
|
12544
12834
|
files.push(...await vendorKatex(out));
|
|
12545
12835
|
if (opts.assetsFrom) files.push(...await copyAssets(opts.assetsFrom, out));
|
|
@@ -12548,7 +12838,7 @@ async function buildDeck(storyboard, source, outDir, opts = {}) {
|
|
|
12548
12838
|
}
|
|
12549
12839
|
const of = deck.cut.kept.length === storyboard.beats.length ? "" : ` of ${storyboard.beats.length}`;
|
|
12550
12840
|
step(
|
|
12551
|
-
`build: ${deck.cut.kept.length}${of} beats at ${format.width}\xD7${format.height} \u2192 ${
|
|
12841
|
+
`build: ${deck.cut.kept.length}${of} beats at ${format.width}\xD7${format.height} \u2192 ${join17(out, "index.html")}`
|
|
12552
12842
|
);
|
|
12553
12843
|
for (const d of deck.cut.dropped) step(`build: cut ${d.beat.id} \u2014 ${d.reason}`);
|
|
12554
12844
|
for (const d of deck.cut.dangling) step(`build: check the wording \u2014 ${d.reason}`);
|
|
@@ -12572,40 +12862,40 @@ async function deckRuntime() {
|
|
|
12572
12862
|
}
|
|
12573
12863
|
}
|
|
12574
12864
|
function playerBundle() {
|
|
12575
|
-
const require2 =
|
|
12865
|
+
const require2 = createRequire4(import.meta.url);
|
|
12576
12866
|
try {
|
|
12577
|
-
return
|
|
12867
|
+
return join17(dirname5(require2.resolve("hyperframes/package.json")), "dist", PLAYER_FILE);
|
|
12578
12868
|
} catch {
|
|
12579
12869
|
throw new Error('Cannot locate the hyperframes player. Run "npm install".');
|
|
12580
12870
|
}
|
|
12581
12871
|
}
|
|
12582
12872
|
async function vendorKatex(out) {
|
|
12583
|
-
const require2 =
|
|
12584
|
-
const dist =
|
|
12585
|
-
const css = await readFile17(
|
|
12873
|
+
const require2 = createRequire4(import.meta.url);
|
|
12874
|
+
const dist = join17(dirname5(require2.resolve("katex/package.json")), "dist");
|
|
12875
|
+
const css = await readFile17(join17(dist, "katex.min.css"), "utf8");
|
|
12586
12876
|
const written = [];
|
|
12587
|
-
await mkdir10(
|
|
12588
|
-
for (const file of await readdir4(
|
|
12877
|
+
await mkdir10(join17(out, "katex/fonts"), { recursive: true });
|
|
12878
|
+
for (const file of await readdir4(join17(dist, "fonts"))) {
|
|
12589
12879
|
if (!file.endsWith(".woff2")) continue;
|
|
12590
|
-
await cp(
|
|
12591
|
-
written.push(
|
|
12880
|
+
await cp(join17(dist, "fonts", file), join17(out, "katex/fonts", file));
|
|
12881
|
+
written.push(join17(out, "katex/fonts", file));
|
|
12592
12882
|
}
|
|
12593
12883
|
const woff2Only = css.replace(/src:([^;}]*)/g, (whole, list) => {
|
|
12594
12884
|
const kept = list.split(",").filter((part) => part.includes(".woff2")).join(",");
|
|
12595
12885
|
return kept ? `src:${kept}` : whole;
|
|
12596
12886
|
});
|
|
12597
|
-
await writeFile12(
|
|
12598
|
-
written.push(
|
|
12887
|
+
await writeFile12(join17(out, "katex/katex.min.css"), woff2Only);
|
|
12888
|
+
written.push(join17(out, "katex/katex.min.css"));
|
|
12599
12889
|
return written;
|
|
12600
12890
|
}
|
|
12601
12891
|
async function copyAssets(sourceDir, out) {
|
|
12602
|
-
const from =
|
|
12892
|
+
const from = join17(resolve6(sourceDir), "assets");
|
|
12603
12893
|
if (!await stat2(from).catch(() => null)) return [];
|
|
12604
|
-
await cp(from,
|
|
12605
|
-
return [
|
|
12894
|
+
await cp(from, join17(out, "assets"), { recursive: true });
|
|
12895
|
+
return [join17(out, "assets")];
|
|
12606
12896
|
}
|
|
12607
12897
|
async function copyAudio(from, narration, out) {
|
|
12608
|
-
const dir =
|
|
12898
|
+
const dir = join17(out, narration.dir);
|
|
12609
12899
|
await mkdir10(dir, { recursive: true });
|
|
12610
12900
|
const names = [
|
|
12611
12901
|
...new Set(
|
|
@@ -12613,18 +12903,18 @@ async function copyAudio(from, narration, out) {
|
|
|
12613
12903
|
)
|
|
12614
12904
|
].sort();
|
|
12615
12905
|
for (const name of names) {
|
|
12616
|
-
await cp(
|
|
12906
|
+
await cp(join17(resolve6(from), name), join17(dir, name)).catch(() => {
|
|
12617
12907
|
throw new Error(`Narration names ${name}, but it is not in ${from}. Re-run \`narrate\`.`);
|
|
12618
12908
|
});
|
|
12619
12909
|
}
|
|
12620
|
-
return names.map((n3) =>
|
|
12910
|
+
return names.map((n3) => join17(dir, n3));
|
|
12621
12911
|
}
|
|
12622
12912
|
async function refreshFont(storyboard, source, out, step) {
|
|
12623
12913
|
try {
|
|
12624
12914
|
const bundle = await bundleFont(
|
|
12625
12915
|
storyboard.lang,
|
|
12626
12916
|
JSON.stringify(source) + JSON.stringify(storyboard),
|
|
12627
|
-
|
|
12917
|
+
join17(out, "assets", "fonts")
|
|
12628
12918
|
);
|
|
12629
12919
|
if (bundle) step(`build: font bundle covers ${bundle.family}`);
|
|
12630
12920
|
return bundle?.css;
|
|
@@ -12694,6 +12984,7 @@ export {
|
|
|
12694
12984
|
framePlan,
|
|
12695
12985
|
gradeOverprint,
|
|
12696
12986
|
gridParamsSchema,
|
|
12987
|
+
guardTmpdir,
|
|
12697
12988
|
harvest,
|
|
12698
12989
|
hasIllustrations,
|
|
12699
12990
|
illustrate,
|