@heyramzi/ui 2.11.0 → 2.11.2
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/dist/agents/helpers.js +38 -38
- package/dist/agents/page-state.js +49 -50
- package/dist/agents/vendor/color.js +146 -127
- package/dist/agents/vendor/draw.js +617 -508
- package/dist/agents/vendor/engine.js +698 -599
- package/dist/agents/vendor/mesh.js +653 -683
- package/dist/agents/vendor/plastic.js +3913 -3112
- package/dist/agents/vendor/presets.js +163 -164
- package/dist/agents/vendor/shapes.js +21 -20
- package/dist/agents/vendor/ticker.js +41 -42
- package/dist/agents/vendor/types.js +1 -0
- package/dist/agents/vendor/wear.js +1421 -1448
- package/dist/ai/confirmation/confirmation-context.svelte.js +6 -6
- package/dist/ai/confirmation/index.js +3 -1
- package/dist/ai/index.js +33 -0
- package/dist/ai/prompt-input/index.js +3 -1
- package/dist/ai/reasoning/index.js +3 -1
- package/dist/ai/reasoning/reasoning-context.svelte.js +6 -6
- package/dist/ai/suggestion/index.js +3 -1
- package/dist/ai/typing-dots/index.js +3 -1
- package/dist/animations/text-swap.js +24 -24
- package/dist/brand/index.js +54 -48
- package/dist/canvas/card/resize.js +5 -3
- package/dist/canvas/edges/edge-path.js +255 -242
- package/dist/canvas/editor/context-menu.js +39 -36
- package/dist/canvas/editor/dispatch.svelte.js +154 -151
- package/dist/canvas/editor/editor-context.js +7 -2
- package/dist/canvas/editor/editor-mode.js +1 -0
- package/dist/canvas/editor/frame.js +73 -76
- package/dist/canvas/editor/hand-context.js +5 -2
- package/dist/canvas/editor/history-core.js +163 -191
- package/dist/canvas/editor/keymap.js +95 -169
- package/dist/canvas/editor/layout-actions.js +143 -144
- package/dist/canvas/editor/lock.js +52 -51
- package/dist/canvas/editor/rebase.js +217 -240
- package/dist/canvas/editor/style.js +132 -137
- package/dist/canvas/editor/text-field-context.js +7 -2
- package/dist/canvas/editor/z-order.js +65 -70
- package/dist/canvas/headless/alignment.js +22 -16
- package/dist/canvas/headless/break-apart-find.js +286 -279
- package/dist/canvas/headless/break-apart-pieces.js +62 -58
- package/dist/canvas/headless/break-apart-plan.js +299 -325
- package/dist/canvas/headless/break-apart-style.js +75 -70
- package/dist/canvas/headless/break-apart.js +188 -181
- package/dist/canvas/headless/canvas-cleanup.js +78 -58
- package/dist/canvas/headless/edge-anchors.js +115 -116
- package/dist/canvas/headless/edge-memo.js +24 -22
- package/dist/canvas/headless/edge-serialization.js +17 -27
- package/dist/canvas/headless/figure-geometry.js +232 -264
- package/dist/canvas/headless/fit-viewport.js +84 -67
- package/dist/canvas/headless/grid.js +88 -86
- package/dist/canvas/headless/icon-node.js +44 -36
- package/dist/canvas/headless/index.js +1 -0
- package/dist/canvas/headless/linkify.js +17 -19
- package/dist/canvas/headless/pen-marks.js +101 -95
- package/dist/canvas/headless/sketch.js +231 -209
- package/dist/canvas/headless/snap-guides.js +99 -83
- package/dist/canvas/headless/svg-figure.js +133 -117
- package/dist/canvas/headless/table-layout.js +146 -107
- package/dist/canvas/index.js +2 -0
- package/dist/canvas/lod/lod-mode.svelte.js +20 -18
- package/dist/canvas/lod/render-all.svelte.js +36 -39
- package/dist/canvas/ops/arrange.js +78 -71
- package/dist/canvas/ops/ops.js +260 -255
- package/dist/canvas/pen/colors.js +37 -35
- package/dist/canvas/pen/eraser.js +28 -27
- package/dist/canvas/pen/geometry.js +120 -130
- package/dist/canvas/pen/index.js +1 -0
- package/dist/canvas/pen/ops.js +11 -11
- package/dist/canvas/pen/pen-session.js +150 -159
- package/dist/canvas/pen/plugin.js +107 -104
- package/dist/canvas/pen/shapes.js +73 -68
- package/dist/canvas/pen/stroke-path.js +32 -30
- package/dist/canvas/pen/types.js +21 -21
- package/dist/canvas/pen/ui.js +10 -8
- package/dist/canvas/plugin.js +46 -46
- package/dist/canvas/screen/glyphs.js +12 -11
- package/dist/canvas/screen/labels.js +22 -22
- package/dist/canvas/screen/parts.js +98 -111
- package/dist/canvas/screen/plugin.js +47 -56
- package/dist/canvas/screen/types.js +30 -35
- package/dist/canvas/screen/ui.js +7 -7
- package/dist/canvas/test-utils.js +14 -14
- package/dist/canvas/types.js +1 -0
- package/dist/canvas/ui/forward-wheel.js +24 -25
- package/dist/canvas/ui/icons.js +64 -131
- package/dist/canvas/ui/place-panel.js +58 -49
- package/dist/canvas/ui/shortcut-tip.js +14 -12
- package/dist/canvas/ui/tools.js +300 -385
- package/dist/charts/dashboard/chart.js +4 -0
- package/dist/charts/dashboard/data-table.js +11 -0
- package/dist/charts/dashboard/windows.js +8 -3
- package/dist/charts/edge-fade.js +10 -5
- package/dist/charts/hover-band.js +16 -11
- package/dist/charts/index.js +0 -1
- package/dist/charts/motion.js +11 -7
- package/dist/charts/primitives/chart-utils.js +12 -12
- package/dist/components/copy/clipboard.js +32 -32
- package/dist/components/copy/copied.svelte.js +31 -23
- package/dist/components/data-table/data-table.js +39 -39
- package/dist/components/docs/index.js +4 -0
- package/dist/components/docs/scroll-spy.svelte.js +73 -68
- package/dist/components/list-page/filter-section.js +4 -1
- package/dist/components/list-page/index.js +2 -0
- package/dist/components/list-page/list-table.js +7 -2
- package/dist/components/list-page/queue-rail.js +14 -0
- package/dist/components/list-page/section-host.js +4 -2
- package/dist/components/list-page/step.js +4 -3
- package/dist/components/list-page/types.js +1 -0
- package/dist/components/picker/trigger.js +48 -30
- package/dist/components/picker/types.js +1 -0
- package/dist/components/segmented/collapse.js +38 -27
- package/dist/components/segmented/index.js +8 -0
- package/dist/components/segmented/segmented.js +67 -42
- package/dist/components/star-rating-input/index.js +3 -1
- package/dist/components/table-view/types.js +1 -0
- package/dist/components/tags-input/tags.js +81 -69
- package/dist/components/theme/index.js +4 -0
- package/dist/components/toolbar/index.js +4 -0
- package/dist/components/toolbar/toolbar-trigger.js +14 -1
- package/dist/editor/anchors.js +17 -13
- package/dist/editor/block-id.js +52 -51
- package/dist/editor/block-ops.js +108 -80
- package/dist/editor/doc-blocks.js +155 -166
- package/dist/editor/extensions.js +139 -180
- package/dist/editor/index.js +4 -0
- package/dist/editor/markdown-test-helpers.js +4 -2
- package/dist/editor/markdown.js +420 -408
- package/dist/editor/serializer.js +204 -200
- package/dist/editor/slash-items.js +89 -101
- package/dist/editor/youtube-url.js +25 -20
- package/dist/effects/animated-beam/path-calculator.svelte.js +43 -38
- package/dist/effects/animated-beam/types.js +1 -0
- package/dist/effects/border-beam/css.js +92 -78
- package/dist/effects/border-beam/presets.js +1705 -1720
- package/dist/effects/border-beam/register-properties.js +43 -33
- package/dist/effects/border-beam/types.js +1 -0
- package/dist/effects/image-comparison/compare.js +3 -2
- package/dist/effects/image-reveal/types.js +1 -0
- package/dist/effects/magnetic/magnetic.js +58 -52
- package/dist/effects/reveal/observe.js +50 -40
- package/dist/effects/reveal/types.js +1 -0
- package/dist/effects/shader-gradient/color.js +36 -29
- package/dist/effects/shader-gradient/renderer.js +384 -295
- package/dist/effects/shader-gradient/shaders.js +31 -0
- package/dist/effects/shader-gradient/types.js +1 -0
- package/dist/icons/apple-symbol-exports.js +99 -124
- package/dist/icons/sf-symbols.js +357 -461
- package/dist/icons/trades.js +43 -28
- package/dist/inbox/index.js +1 -0
- package/dist/inbox/logic.js +126 -122
- package/dist/inbox/types.js +43 -43
- package/dist/lib/arrive.js +19 -12
- package/dist/lib/icon-types.js +1 -0
- package/dist/lib/initials.js +16 -10
- package/dist/lib/layout.js +5 -0
- package/dist/lib/panel.js +13 -0
- package/dist/lib/result.js +22 -22
- package/dist/lib/sliding-pill.js +59 -44
- package/dist/lib/theme-transition.js +43 -43
- package/dist/lib/tone.js +31 -24
- package/dist/lib/utils.js +3 -1
- package/dist/onboarding/define-tours.js +24 -23
- package/dist/onboarding/onboarding.svelte.js +16 -12
- package/dist/onboarding/pick.js +10 -6
- package/dist/onboarding/stores.js +40 -41
- package/dist/onboarding/wait.js +24 -23
- package/dist/panel/side-panel-types.js +1 -0
- package/dist/record/demo-data.js +459 -486
- package/dist/record/fields.js +129 -131
- package/dist/record/format.js +70 -82
- package/dist/record/gutter.js +1 -0
- package/dist/record/kinds.js +95 -105
- package/dist/record/logic.js +238 -219
- package/dist/record/types.js +49 -48
- package/dist/shadcn/accordion/index.js +3 -1
- package/dist/shadcn/alert/index.js +3 -1
- package/dist/shadcn/alert-dialog/index.js +3 -1
- package/dist/shadcn/attachment/index.js +3 -1
- package/dist/shadcn/avatar/index.js +3 -1
- package/dist/shadcn/breadcrumb/index.js +3 -1
- package/dist/shadcn/bubble/index.js +3 -1
- package/dist/shadcn/button/index.js +3 -1
- package/dist/shadcn/calendar/index.js +3 -1
- package/dist/shadcn/card/index.js +3 -1
- package/dist/shadcn/checkbox/index.js +3 -1
- package/dist/shadcn/code-block/index.js +3 -1
- package/dist/shadcn/collapsible/index.js +3 -1
- package/dist/shadcn/command/index.js +4 -1
- package/dist/shadcn/context-menu/index.js +3 -1
- package/dist/shadcn/dialog/index.js +3 -1
- package/dist/shadcn/dropdown-menu/menu-item.js +40 -20
- package/dist/shadcn/empty/index.js +3 -1
- package/dist/shadcn/field/index.js +3 -1
- package/dist/shadcn/hooks/highlighter.js +77 -75
- package/dist/shadcn/hooks/is-mobile.svelte.js +3 -3
- package/dist/shadcn/hooks/use-highlighted-code.svelte.js +62 -62
- package/dist/shadcn/input/index.js +3 -1
- package/dist/shadcn/kbd/index.js +3 -1
- package/dist/shadcn/label/index.js +3 -1
- package/dist/shadcn/marker/index.js +3 -1
- package/dist/shadcn/message/index.js +3 -1
- package/dist/shadcn/message-scroller/index.js +3 -1
- package/dist/shadcn/popover/index.js +3 -1
- package/dist/shadcn/progress/index.js +3 -1
- package/dist/shadcn/progress/tone.js +8 -7
- package/dist/shadcn/radio-group/index.js +3 -1
- package/dist/shadcn/scroll-area/index.js +3 -1
- package/dist/shadcn/select/index.js +3 -1
- package/dist/shadcn/separator/index.js +3 -1
- package/dist/shadcn/sheet/index.js +3 -1
- package/dist/shadcn/sidebar/context.svelte.js +42 -28
- package/dist/shadcn/sidebar/index.js +3 -1
- package/dist/shadcn/skeleton/index.js +3 -1
- package/dist/shadcn/slider/index.js +3 -1
- package/dist/shadcn/switch/index.js +3 -1
- package/dist/shadcn/table/index.js +3 -1
- package/dist/shadcn/tabs/index.js +3 -1
- package/dist/shadcn/textarea/index.js +3 -1
- package/dist/shadcn/toggle/index.js +3 -1
- package/dist/shadcn/toggle-group/index.js +3 -1
- package/dist/shadcn/tooltip/context.js +7 -2
- package/dist/shadcn/tooltip/index.js +3 -1
- package/dist/shadcn/trace/index.js +3 -1
- package/dist/slides/bridges.js +65 -59
- package/dist/slides/deck-controller.svelte.js +210 -207
- package/dist/slides/export/capture.js +66 -71
- package/dist/slides/export/ffmpeg.js +76 -72
- package/dist/slides/export/index.js +3 -0
- package/dist/slides/export/launch.js +9 -6
- package/dist/slides/export/pdf.js +60 -54
- package/dist/slides/export/types.js +1 -0
- package/dist/slides/labels.js +24 -24
- package/dist/slides/layout.js +39 -41
- package/dist/slides/model.js +9 -7
- package/dist/slides/navigation.js +78 -73
- package/dist/slides/types.js +2 -2
- package/dist/status/define-statuses.js +18 -14
- package/dist/status/status-types.js +1 -0
- package/dist/test-setup.js +24 -20
- package/dist/thread/logic.js +73 -77
- package/dist/thread/types.js +21 -21
- package/dist/work/board-order.js +94 -90
- package/dist/work/dates.js +16 -9
- package/dist/work/detail/detail-types.js +18 -18
- package/dist/work/docs/edited.js +32 -29
- package/dist/work/docs/index.js +4 -0
- package/dist/work/docs/labels.js +34 -34
- package/dist/work/docs/menu.js +113 -106
- package/dist/work/docs/outline.js +39 -29
- package/dist/work/gantt/logic.js +211 -235
- package/dist/work/inbox/inbox-logic.js +71 -75
- package/dist/work/inbox/inbox-statuses.js +75 -72
- package/dist/work/inbox/inbox-types.js +1 -0
- package/dist/work/kanban-drag.js +27 -22
- package/dist/work/kanban-types.js +1 -0
- package/dist/work/presets/sales.js +36 -32
- package/dist/work/reorder.js +9 -8
- package/dist/work/task-types.js +1 -0
- package/dist/work/time/duration.js +51 -46
- package/dist/work/time/time-types.js +23 -23
- package/dist/work/use-kanban-dnd.svelte.js +126 -125
- package/package.json +3 -1
- package/src/components/list-page/master-detail.svelte +3 -1
- package/src/components/list-page/rail-row.svelte +4 -4
- package/src/components/tags-input/tags-input.svelte +2 -0
- package/src/editor/markdown.ts +30 -35
|
@@ -1,85 +1,89 @@
|
|
|
1
|
+
// Encode frames to an mp4 with the ffmpeg on the machine. The package ships no
|
|
2
|
+
// binary; pass `ffmpeg` to name one that is not on PATH.
|
|
1
3
|
import { spawn } from "node:child_process";
|
|
2
4
|
import { mkdir, rm } from "node:fs/promises";
|
|
3
5
|
import path from "node:path";
|
|
6
|
+
/** Runs ffmpeg and resolves when it exits 0; the error carries the tail of its stderr. */
|
|
4
7
|
export function runFfmpeg(args, ffmpeg = "ffmpeg") {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
child.on("close", (code)=>code === 0 ? resolve() : reject(new Error(`${ffmpeg} exited ${code}:\n${stderr.slice(-1500)}`)));
|
|
19
|
-
});
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
const child = spawn(ffmpeg, args, { stdio: [
|
|
10
|
+
"ignore",
|
|
11
|
+
"ignore",
|
|
12
|
+
"pipe"
|
|
13
|
+
] });
|
|
14
|
+
let stderr = "";
|
|
15
|
+
child.stderr.on("data", (chunk) => {
|
|
16
|
+
stderr += chunk;
|
|
17
|
+
});
|
|
18
|
+
child.on("error", (error) => reject(new Error(`${ffmpeg} could not be started (${error.message}). Video export needs ffmpeg on PATH, or pass its path.`)));
|
|
19
|
+
child.on("close", (code) => code === 0 ? resolve() : reject(new Error(`${ffmpeg} exited ${code}:\n${stderr.slice(-1500)}`)));
|
|
20
|
+
});
|
|
20
21
|
}
|
|
22
|
+
/** The ffmpeg arguments for a silent yuv420p H.264 mp4 from a numbered image sequence. */
|
|
21
23
|
export function encodeArgs(dir, outFile, options) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
24
|
+
return [
|
|
25
|
+
"-y",
|
|
26
|
+
"-hide_banner",
|
|
27
|
+
"-loglevel",
|
|
28
|
+
"error",
|
|
29
|
+
"-framerate",
|
|
30
|
+
String(options.fps),
|
|
31
|
+
"-i",
|
|
32
|
+
path.join(dir, options.pattern ?? "%04d.png"),
|
|
33
|
+
"-an",
|
|
34
|
+
"-vf",
|
|
35
|
+
"format=yuv420p",
|
|
36
|
+
"-c:v",
|
|
37
|
+
"libx264",
|
|
38
|
+
"-preset",
|
|
39
|
+
options.preset ?? "slow",
|
|
40
|
+
"-crf",
|
|
41
|
+
String(options.crf ?? 23),
|
|
42
|
+
"-movflags",
|
|
43
|
+
"+faststart",
|
|
44
|
+
outFile
|
|
45
|
+
];
|
|
44
46
|
}
|
|
47
|
+
/** Encodes a folder of numbered frames. There is no audio stream at all. */
|
|
45
48
|
export async function encodeFrames(dir, outFile, options) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
});
|
|
49
|
-
await runFfmpeg(encodeArgs(dir, outFile, options), options.ffmpeg);
|
|
49
|
+
await mkdir(path.dirname(outFile), { recursive: true });
|
|
50
|
+
await runFfmpeg(encodeArgs(dir, outFile, options), options.ffmpeg);
|
|
50
51
|
}
|
|
52
|
+
/** Frames for a loop of this length: whole frames only, never fewer than one. */
|
|
51
53
|
export function loopFrameCount(seconds, fps) {
|
|
52
|
-
|
|
54
|
+
return Math.max(1, Math.round(seconds * fps));
|
|
53
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Renders a seamless loop from `drawFrame` at t = 0 to (n-1)/n, never 1, so the cut back
|
|
58
|
+
* to frame zero is the same step as every other.
|
|
59
|
+
*/
|
|
54
60
|
export async function renderLoop(outFile, options) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
seconds
|
|
84
|
-
};
|
|
61
|
+
const fps = options.fps ?? 30;
|
|
62
|
+
const seconds = options.seconds ?? 2.5;
|
|
63
|
+
const frames = loopFrameCount(seconds, fps);
|
|
64
|
+
const work = `${outFile}.frames`;
|
|
65
|
+
await mkdir(work, { recursive: true });
|
|
66
|
+
try {
|
|
67
|
+
for (let index = 0; index < frames; index++) {
|
|
68
|
+
await options.drawFrame({
|
|
69
|
+
index,
|
|
70
|
+
t: index / frames,
|
|
71
|
+
file: path.join(work, `${String(index).padStart(4, "0")}.png`)
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
await encodeFrames(work, outFile, {
|
|
75
|
+
...options,
|
|
76
|
+
fps
|
|
77
|
+
});
|
|
78
|
+
} finally {
|
|
79
|
+
await rm(work, {
|
|
80
|
+
recursive: true,
|
|
81
|
+
force: true
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
file: outFile,
|
|
86
|
+
frames,
|
|
87
|
+
seconds
|
|
88
|
+
};
|
|
85
89
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// @heyramzi/ui/slides/export: Node-only. Capture, print and encode slides without
|
|
2
|
+
// depending on Playwright or bundling ffmpeg. Hand it the Playwright pages and
|
|
3
|
+
// browsers you already have.
|
|
1
4
|
export * from "./capture.js";
|
|
2
5
|
export * from "./ffmpeg.js";
|
|
3
6
|
export * from "./launch.js";
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Launches Chromium, falling back to the installed Chrome. The bundled
|
|
3
|
+
* Chromium goes missing after a Playwright bump, and Chrome prints the same.
|
|
4
|
+
* Pass `chromium` from Playwright.
|
|
5
|
+
*/
|
|
1
6
|
export async function launchChromium(chromium) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
channel: "chrome"
|
|
7
|
-
}));
|
|
7
|
+
return chromium.launch({ headless: true }).catch(() => chromium.launch({
|
|
8
|
+
headless: true,
|
|
9
|
+
channel: "chrome"
|
|
10
|
+
}));
|
|
8
11
|
}
|
|
@@ -1,78 +1,84 @@
|
|
|
1
|
+
// Print a URL or HTML to PDF at a page size, and assemble captured PNGs into one.
|
|
1
2
|
import { readFile } from "node:fs/promises";
|
|
3
|
+
/** Chromium drops backgrounds in its print pipeline without the color-adjust line. */
|
|
2
4
|
export const RESET_CSS = `*{margin:0;padding:0;box-sizing:border-box;}
|
|
3
5
|
html{-webkit-print-color-adjust:exact;print-color-adjust:exact;}`;
|
|
6
|
+
/** The `@page` rule for a page of this size in CSS pixels, with no margin. */
|
|
4
7
|
export function pageRule(size) {
|
|
5
|
-
|
|
8
|
+
return `@page{size:${size.width}px ${size.height}px;margin:0;}`;
|
|
6
9
|
}
|
|
10
|
+
/** The reset, the page size and one full-bleed `.page` box per image. */
|
|
7
11
|
export function imagePagesCss(size, background) {
|
|
8
|
-
|
|
12
|
+
return `${RESET_CSS}
|
|
9
13
|
${background ? `body{background:${background};}\n` : ""}${pageRule(size)}
|
|
10
14
|
.page{display:block;width:${size.width}px;height:${size.height}px;break-after:page;}
|
|
11
15
|
.page:last-child{break-after:auto;}`;
|
|
12
16
|
}
|
|
17
|
+
/** One `<img class="page">` per data URL, laid out so each prints on its own page. */
|
|
13
18
|
export function imagePagesHtml(dataUrls, size, background) {
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
const pages = dataUrls.map((dataUrl) => `<img class="page" src="${dataUrl}" alt="">`).join("");
|
|
20
|
+
return `<!doctype html><html><head><meta charset="utf-8"><style>
|
|
16
21
|
${imagePagesCss(size, background)}
|
|
17
22
|
</style></head><body>${pages}</body></html>`;
|
|
18
23
|
}
|
|
24
|
+
/** The page count of a PDF, read off its page objects. */
|
|
19
25
|
export function countPdfPages(pdf) {
|
|
20
|
-
|
|
26
|
+
return (Buffer.from(pdf).toString("latin1").match(/\/Type\s*\/Page[^s]/g) ?? []).length;
|
|
21
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Prints the page once fonts, images and the caller's signal settle, returning the images
|
|
30
|
+
* that never loaded so a caller can refuse a half-painted sheet.
|
|
31
|
+
*/
|
|
22
32
|
export async function printPage(page, options = {}) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
});
|
|
48
|
-
return {
|
|
49
|
-
pdf,
|
|
50
|
-
pages: countPdfPages(pdf),
|
|
51
|
-
brokenImages
|
|
52
|
-
};
|
|
33
|
+
const { ready } = options;
|
|
34
|
+
if (typeof ready === "string") {
|
|
35
|
+
await page.waitForSelector(ready, { timeout: options.readyTimeout ?? 6e4 });
|
|
36
|
+
} else if (ready) {
|
|
37
|
+
await ready(page);
|
|
38
|
+
}
|
|
39
|
+
await page.evaluate(() => document.fonts.ready.then(() => undefined));
|
|
40
|
+
if (options.settle) await page.waitForTimeout(options.settle);
|
|
41
|
+
const brokenImages = await page.evaluate(() => [...document.images].filter((img) => !img.complete || img.naturalWidth === 0).map((img) => img.src));
|
|
42
|
+
const { size } = options;
|
|
43
|
+
const pdf = await page.pdf({
|
|
44
|
+
path: options.path,
|
|
45
|
+
printBackground: true,
|
|
46
|
+
...size ? {
|
|
47
|
+
width: `${size.width}px`,
|
|
48
|
+
height: `${size.height}px`
|
|
49
|
+
} : { preferCSSPageSize: true },
|
|
50
|
+
pageRanges: options.pageRanges
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
pdf,
|
|
54
|
+
pages: countPdfPages(pdf),
|
|
55
|
+
brokenImages
|
|
56
|
+
};
|
|
53
57
|
}
|
|
58
|
+
/** Opens a URL in the page and prints it. The caller owns the page and any routing on it. */
|
|
54
59
|
export async function printUrl(page, url, options = {}) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
options.check?.(response, page);
|
|
59
|
-
return printPage(page, options);
|
|
60
|
+
const response = await page.goto(url, { waitUntil: options.waitUntil ?? "load" });
|
|
61
|
+
options.check?.(response, page);
|
|
62
|
+
return printPage(page, options);
|
|
60
63
|
}
|
|
64
|
+
/** Loads HTML into the page and prints it. */
|
|
61
65
|
export async function printHtml(page, html, options = {}) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
});
|
|
65
|
-
return printPage(page, options);
|
|
66
|
+
await page.setContent(html, { waitUntil: options.waitUntil ?? "load" });
|
|
67
|
+
return printPage(page, options);
|
|
66
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Prints PNGs one per page, never the original document, because Chromium's print pipeline
|
|
71
|
+
* re-rasterises effects: a blurred backdrop lost 47% of its chroma.
|
|
72
|
+
*/
|
|
67
73
|
export async function imagesToPdf(page, images, options) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
if (images.length === 0) throw new Error("imagesToPdf needs at least one image.");
|
|
75
|
+
const dataUrls = await Promise.all(images.map(async (image) => {
|
|
76
|
+
const bytes = typeof image === "string" ? await readFile(image) : image;
|
|
77
|
+
return `data:image/png;base64,${Buffer.from(bytes).toString("base64")}`;
|
|
78
|
+
}));
|
|
79
|
+
const { pdf } = await printHtml(page, imagePagesHtml(dataUrls, options.size, options.background), {
|
|
80
|
+
size: options.size,
|
|
81
|
+
pageRanges: `1-${images.length}`
|
|
82
|
+
});
|
|
83
|
+
return pdf;
|
|
78
84
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/slides/labels.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
export const defaultLabels = {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
2
|
+
deck: "Slide deck",
|
|
3
|
+
deckRole: "presentation",
|
|
4
|
+
slideRole: "slide",
|
|
5
|
+
previous: "Previous slide",
|
|
6
|
+
next: "Next slide",
|
|
7
|
+
fullscreen: "Toggle fullscreen",
|
|
8
|
+
overview: "Slide overview",
|
|
9
|
+
presenter: "Open presenter view",
|
|
10
|
+
progress: "Deck progress",
|
|
11
|
+
slide: (n, of) => `Slide ${n} of ${of}`,
|
|
12
|
+
upcoming: "Next",
|
|
13
|
+
current: "Now",
|
|
14
|
+
notes: "Notes",
|
|
15
|
+
noNotes: "No notes for this slide.",
|
|
16
|
+
endOfDeck: "End of deck.",
|
|
17
|
+
timer: "Elapsed time",
|
|
18
|
+
startTimer: "Start timer",
|
|
19
|
+
pauseTimer: "Pause timer",
|
|
20
|
+
resetTimer: "Reset timer",
|
|
21
|
+
unknownType: (type) => `Unknown slide type: ${type}`
|
|
22
22
|
};
|
|
23
|
-
export const resolveLabels = (labels)=>({
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
export const resolveLabels = (labels) => ({
|
|
24
|
+
...defaultLabels,
|
|
25
|
+
...labels
|
|
26
|
+
});
|
package/dist/slides/layout.js
CHANGED
|
@@ -1,47 +1,45 @@
|
|
|
1
1
|
export function fitBox(view, canvas) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
const scale = Math.max(0, Math.min(view.w / canvas.w, view.h / canvas.h));
|
|
3
|
+
return {
|
|
4
|
+
x: (view.w - canvas.w * scale) / 2,
|
|
5
|
+
y: (view.h - canvas.h * scale) / 2,
|
|
6
|
+
scale
|
|
7
|
+
};
|
|
8
8
|
}
|
|
9
|
+
/** The scale at which `cols` columns of `count` canvases fit the view. */
|
|
9
10
|
function scaleFor(cols, count, view, canvas, pad, gap) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const rows = Math.ceil(count / cols);
|
|
12
|
+
const byWidth = (view.w - 2 * pad - (cols - 1) * gap) / cols / canvas.w;
|
|
13
|
+
const byHeight = (view.h - 2 * pad - (rows - 1) * gap) / rows / canvas.h;
|
|
14
|
+
return Math.min(byWidth, byHeight);
|
|
14
15
|
}
|
|
16
|
+
/** Lays `count` canvases in the roomiest grid that shows them all, centred. */
|
|
15
17
|
export function gridBoxes(view, canvas, count) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return {
|
|
44
|
-
cols,
|
|
45
|
-
boxes
|
|
46
|
-
};
|
|
18
|
+
if (count === 0 || view.w <= 0 || view.h <= 0) return {
|
|
19
|
+
cols: 1,
|
|
20
|
+
boxes: []
|
|
21
|
+
};
|
|
22
|
+
const pad = Math.min(56, Math.max(16, view.w * .04));
|
|
23
|
+
/* The gap holds the slide number that hangs below each cell. */
|
|
24
|
+
const gap = Math.max(26, pad * .6);
|
|
25
|
+
let cols = 1;
|
|
26
|
+
let scale = scaleFor(1, count, view, canvas, pad, gap);
|
|
27
|
+
for (let c = 2; c <= count; c++) {
|
|
28
|
+
const candidate = scaleFor(c, count, view, canvas, pad, gap);
|
|
29
|
+
if (candidate > scale) [cols, scale] = [c, candidate];
|
|
30
|
+
}
|
|
31
|
+
const cellW = canvas.w * scale;
|
|
32
|
+
const cellH = canvas.h * scale;
|
|
33
|
+
const rows = Math.ceil(count / cols);
|
|
34
|
+
const left = (view.w - (cols * cellW + (cols - 1) * gap)) / 2;
|
|
35
|
+
const top = (view.h - (rows * cellH + (rows - 1) * gap)) / 2;
|
|
36
|
+
const boxes = Array.from({ length: count }, (_, i) => ({
|
|
37
|
+
x: left + i % cols * (cellW + gap),
|
|
38
|
+
y: top + Math.floor(i / cols) * (cellH + gap),
|
|
39
|
+
scale
|
|
40
|
+
}));
|
|
41
|
+
return {
|
|
42
|
+
cols,
|
|
43
|
+
boxes
|
|
44
|
+
};
|
|
47
45
|
}
|
package/dist/slides/model.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
/** Identity function that fixes `Content` from the layout. */
|
|
2
|
+
export const defineSlideType = (definition) => definition;
|
|
3
|
+
/** Keys the types by their `type`. Two with the same name is a mistake worth failing on. */
|
|
2
4
|
export function defineRegistry(types) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
const registry = {};
|
|
6
|
+
for (const slide of types) {
|
|
7
|
+
if (slide.type in registry) throw new Error(`Slide type "${slide.type}" is registered twice.`);
|
|
8
|
+
registry[slide.type] = slide;
|
|
9
|
+
}
|
|
10
|
+
return registry;
|
|
9
11
|
}
|