@hajimism/dek 0.1.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/LICENSE +21 -0
- package/README.ja.md +75 -0
- package/README.md +75 -0
- package/package.json +65 -0
- package/src/cli/address.ts +53 -0
- package/src/cli/build.ts +85 -0
- package/src/cli/check.ts +97 -0
- package/src/cli/commands.ts +851 -0
- package/src/cli/contract.ts +444 -0
- package/src/cli/cues.ts +18 -0
- package/src/cli/files.ts +309 -0
- package/src/cli/flags.ts +295 -0
- package/src/cli/format.ts +156 -0
- package/src/cli/goto.ts +96 -0
- package/src/cli/init.ts +136 -0
- package/src/cli/keep-alive.ts +72 -0
- package/src/cli/lint.ts +84 -0
- package/src/cli/ls.ts +119 -0
- package/src/cli/main.ts +231 -0
- package/src/cli/marks.ts +16 -0
- package/src/cli/mv.ts +37 -0
- package/src/cli/new.ts +64 -0
- package/src/cli/pdf.ts +16 -0
- package/src/cli/qr.ts +466 -0
- package/src/cli/ref.ts +165 -0
- package/src/cli/rehearse.ts +42 -0
- package/src/cli/result.ts +161 -0
- package/src/cli/scope.ts +308 -0
- package/src/cli/serve.ts +125 -0
- package/src/cli/shot.ts +134 -0
- package/src/cli/show.ts +103 -0
- package/src/cli/sync.ts +37 -0
- package/src/cli/text.ts +479 -0
- package/src/cli/theme.ts +56 -0
- package/src/cli/tty.ts +81 -0
- package/src/cli/usage.ts +198 -0
- package/src/cli/video.ts +17 -0
- package/src/cli/voice.ts +113 -0
- package/src/cli.ts +4 -0
- package/src/core/assets.ts +239 -0
- package/src/core/build.ts +77 -0
- package/src/core/bundled-theme.ts +15 -0
- package/src/core/capture-go.ts +72 -0
- package/src/core/chrome.ts +128 -0
- package/src/core/config-keys.ts +82 -0
- package/src/core/config.ts +95 -0
- package/src/core/css-scan.ts +158 -0
- package/src/core/css-transform.ts +140 -0
- package/src/core/css-value.ts +127 -0
- package/src/core/css.ts +545 -0
- package/src/core/cue.ts +187 -0
- package/src/core/deck-paths.ts +34 -0
- package/src/core/diagnostic.ts +114 -0
- package/src/core/document.ts +218 -0
- package/src/core/error.ts +31 -0
- package/src/core/escape.ts +16 -0
- package/src/core/fill.ts +100 -0
- package/src/core/finish-beat.ts +30 -0
- package/src/core/github.ts +234 -0
- package/src/core/html-scan.ts +253 -0
- package/src/core/html.ts +234 -0
- package/src/core/in-page-go.ts +128 -0
- package/src/core/lines.ts +43 -0
- package/src/core/lint/asset-refs.ts +109 -0
- package/src/core/lint/context.ts +116 -0
- package/src/core/lint/files.ts +117 -0
- package/src/core/lint/keys.ts +48 -0
- package/src/core/lint/morph.ts +91 -0
- package/src/core/lint/script.ts +102 -0
- package/src/core/lint/shared-styles.ts +165 -0
- package/src/core/lint/slide-html.ts +466 -0
- package/src/core/lint/slide-script.ts +179 -0
- package/src/core/lint/theme-css.ts +295 -0
- package/src/core/lint/timing.ts +66 -0
- package/src/core/lint/tokens.ts +520 -0
- package/src/core/lint/voice.ts +76 -0
- package/src/core/lint.ts +137 -0
- package/src/core/live-protocol.ts +130 -0
- package/src/core/marks.ts +198 -0
- package/src/core/mv.ts +293 -0
- package/src/core/ogp.ts +99 -0
- package/src/core/optional.ts +24 -0
- package/src/core/overflow.ts +220 -0
- package/src/core/page-pool.ts +53 -0
- package/src/core/page.ts +66 -0
- package/src/core/parse.ts +307 -0
- package/src/core/path.ts +58 -0
- package/src/core/pdf.ts +71 -0
- package/src/core/playwright-module.d.ts +43 -0
- package/src/core/playwright-motion.ts +42 -0
- package/src/core/playwright-sheet.ts +32 -0
- package/src/core/playwright-visual.ts +391 -0
- package/src/core/playwright-worker.ts +5 -0
- package/src/core/playwright.ts +431 -0
- package/src/core/position.ts +146 -0
- package/src/core/presenter-state.ts +43 -0
- package/src/core/presenter.ts +131 -0
- package/src/core/prose.ts +16 -0
- package/src/core/pseudo-text.ts +67 -0
- package/src/core/rail-width.ts +4 -0
- package/src/core/ref-name.ts +110 -0
- package/src/core/ref.ts +238 -0
- package/src/core/resolve.ts +228 -0
- package/src/core/rumdl.ts +99 -0
- package/src/core/safe-fs.ts +196 -0
- package/src/core/sarif.ts +135 -0
- package/src/core/schema.ts +89 -0
- package/src/core/script-lines.ts +53 -0
- package/src/core/sheet.ts +223 -0
- package/src/core/shot/morph.ts +80 -0
- package/src/core/shot/motion.ts +187 -0
- package/src/core/shot/still.ts +216 -0
- package/src/core/shot-cache.ts +140 -0
- package/src/core/size.ts +11 -0
- package/src/core/skeleton-record.ts +51 -0
- package/src/core/skeleton.ts +89 -0
- package/src/core/slide-draw.ts +165 -0
- package/src/core/slide-eval-worker.ts +58 -0
- package/src/core/slide-measure.ts +272 -0
- package/src/core/slide-script-eval.ts +258 -0
- package/src/core/slide-script.ts +252 -0
- package/src/core/spawn.ts +128 -0
- package/src/core/step.ts +94 -0
- package/src/core/still-page.ts +70 -0
- package/src/core/suggest.ts +33 -0
- package/src/core/sync.ts +316 -0
- package/src/core/text-contrast.ts +466 -0
- package/src/core/theme-excerpt.ts +187 -0
- package/src/core/theme-facts.ts +51 -0
- package/src/core/timeline.ts +216 -0
- package/src/core/timing.ts +88 -0
- package/src/core/toml-keys.ts +349 -0
- package/src/core/url-attributes.ts +103 -0
- package/src/core/video-document.ts +15 -0
- package/src/core/visual-cache.ts +100 -0
- package/src/core/visual.ts +558 -0
- package/src/core/voice.ts +321 -0
- package/src/core/zod.ts +79 -0
- package/src/runtime/browser.ts +203 -0
- package/src/runtime/clone.ts +52 -0
- package/src/runtime/deck-control.ts +43 -0
- package/src/runtime/fit.ts +32 -0
- package/src/runtime/go.ts +56 -0
- package/src/runtime/input.ts +128 -0
- package/src/runtime/laser-view.ts +110 -0
- package/src/runtime/laser.ts +145 -0
- package/src/runtime/live-host.ts +33 -0
- package/src/runtime/live-reload.ts +29 -0
- package/src/runtime/live.ts +88 -0
- package/src/runtime/marks-view.ts +74 -0
- package/src/runtime/motion.ts +113 -0
- package/src/runtime/navigator.ts +189 -0
- package/src/runtime/player.ts +28 -0
- package/src/runtime/presenter-view.ts +289 -0
- package/src/runtime/presenter.ts +52 -0
- package/src/runtime/rail-view.ts +229 -0
- package/src/runtime/rail.ts +52 -0
- package/src/runtime/rehearse.ts +264 -0
- package/src/runtime/routes.ts +142 -0
- package/src/runtime/settle.ts +45 -0
- package/src/runtime/slide.d.ts +28 -0
- package/src/runtime/socket.ts +143 -0
- package/src/runtime/stage.ts +259 -0
- package/src/runtime/step.ts +139 -0
- package/src/runtime/window.ts +38 -0
- package/src/server/auth.ts +210 -0
- package/src/server/deck-snapshot.ts +146 -0
- package/src/server/dev.ts +251 -0
- package/src/server/hub.ts +104 -0
- package/src/server/lan.ts +51 -0
- package/src/server/lock.ts +97 -0
- package/src/server/pages.ts +71 -0
- package/src/server/pairing.ts +40 -0
- package/src/server/project-watch.ts +142 -0
- package/src/server/rooms.ts +133 -0
- package/src/server/routes.ts +286 -0
- package/src/server/serial.ts +25 -0
- package/src/server/watch.ts +308 -0
- package/src/theme/default.css +175 -0
- package/src/video/bake.ts +152 -0
- package/src/video/mux.ts +103 -0
- package/src/video/recorder.ts +115 -0
- package/src/video/sidecar.ts +60 -0
- package/src/video/worker.ts +51 -0
- package/src/voice/engine.ts +177 -0
- package/src/voice/synth.ts +127 -0
- package/src/voice/wav.ts +115 -0
package/src/cli/shot.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { DekError } from "../core/error.ts";
|
|
2
|
+
import { parseMorphAt, shotMorph } from "../core/shot/morph.ts";
|
|
3
|
+
import { type ShotMotion, shotMotion } from "../core/shot/motion.ts";
|
|
4
|
+
import { type ShotFile, shotDeck, shotSheet } from "../core/shot/still.ts";
|
|
5
|
+
import { playerScript } from "../runtime/player.ts";
|
|
6
|
+
import type { ReadableDeck } from "./scope.ts";
|
|
7
|
+
import { usageError } from "./usage.ts";
|
|
8
|
+
|
|
9
|
+
export type ShotCliResult = {
|
|
10
|
+
shots: ShotFile[];
|
|
11
|
+
/** Contact sheets, from --sheet or --motion; what text output prints when present. */
|
|
12
|
+
sheets?: string[];
|
|
13
|
+
/** With --motion, each beat's frames and the ms into its go each shows. */
|
|
14
|
+
motion?: ShotMotion["beats"];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type ShotCommandOptions = {
|
|
18
|
+
slug?: string;
|
|
19
|
+
step?: string;
|
|
20
|
+
to?: string;
|
|
21
|
+
at?: string;
|
|
22
|
+
sheet?: boolean;
|
|
23
|
+
motion?: boolean;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/** What `dekc shot` was asked to take, once its flags are known to fit together. */
|
|
27
|
+
export type ShotMode =
|
|
28
|
+
| { kind: "still"; slug?: string; step?: string }
|
|
29
|
+
| { kind: "sheet" }
|
|
30
|
+
| { kind: "motion"; slug: string; step?: string }
|
|
31
|
+
| { kind: "morph"; from: string; to: string; at: number };
|
|
32
|
+
|
|
33
|
+
export async function shotCommand(
|
|
34
|
+
{ project, deck }: ReadableDeck,
|
|
35
|
+
options: ShotCommandOptions,
|
|
36
|
+
): Promise<ShotCliResult> {
|
|
37
|
+
const mode = parseShotMode(options);
|
|
38
|
+
switch (mode.kind) {
|
|
39
|
+
case "sheet": {
|
|
40
|
+
const { sheets, shots } = await shotSheet({ project, deck });
|
|
41
|
+
return { shots, sheets };
|
|
42
|
+
}
|
|
43
|
+
case "motion": {
|
|
44
|
+
const { sheets, beats } = await shotMotion(
|
|
45
|
+
{ project, deck },
|
|
46
|
+
{
|
|
47
|
+
slug: mode.slug,
|
|
48
|
+
...(mode.step === undefined ? {} : { step: mode.step }),
|
|
49
|
+
playerScript: await playerScript(),
|
|
50
|
+
},
|
|
51
|
+
);
|
|
52
|
+
return { shots: [], sheets, motion: beats };
|
|
53
|
+
}
|
|
54
|
+
case "morph": {
|
|
55
|
+
const shots = await shotMorph(
|
|
56
|
+
{ project, deck },
|
|
57
|
+
{ from: mode.from, to: mode.to, at: mode.at, playerScript: await playerScript() },
|
|
58
|
+
);
|
|
59
|
+
return { shots };
|
|
60
|
+
}
|
|
61
|
+
case "still": {
|
|
62
|
+
const shots = await shotDeck({ project, deck }, { slug: mode.slug, step: mode.step });
|
|
63
|
+
return { shots };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function refuse(message: string, hint: string): never {
|
|
69
|
+
throw new DekError(message, { hint });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The one mode the flags name: --sheet is the whole deck at its last beats, --motion one slide's
|
|
74
|
+
* beats, --to a frame of the move to another slide, and none of them a still.
|
|
75
|
+
*/
|
|
76
|
+
export function parseShotMode(options: ShotCommandOptions): ShotMode {
|
|
77
|
+
const { slug, step, to, at } = options;
|
|
78
|
+
if (options.sheet && options.motion) {
|
|
79
|
+
refuse(
|
|
80
|
+
"--sheet and --motion together",
|
|
81
|
+
"use one of --sheet and --motion: --sheet tiles every slide, --motion one slide's beats",
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
if (options.sheet) {
|
|
85
|
+
if (to !== undefined || at !== undefined) {
|
|
86
|
+
refuse("--sheet with --to or --at", "--sheet takes no --to or --at; drop them");
|
|
87
|
+
}
|
|
88
|
+
if (step !== undefined) {
|
|
89
|
+
refuse("--sheet with --step", "--sheet shows every slide at its last beat; drop --step");
|
|
90
|
+
}
|
|
91
|
+
if (slug) {
|
|
92
|
+
refuse(
|
|
93
|
+
"--sheet with a slug",
|
|
94
|
+
`--sheet tiles every slide; for one slide's beats, run \`dekc shot ${slug} --motion\``,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
return { kind: "sheet" };
|
|
98
|
+
}
|
|
99
|
+
if (options.motion) {
|
|
100
|
+
if (to !== undefined) {
|
|
101
|
+
refuse(
|
|
102
|
+
"--motion with --to",
|
|
103
|
+
"the move from the slide before is row 1 of --motion; for a jump to any slide, use `dekc shot <a> --to <b>`",
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
if (at !== undefined) {
|
|
107
|
+
refuse("--motion with --at", "--motion holds each beat at several moments; drop --at");
|
|
108
|
+
}
|
|
109
|
+
if (!slug) {
|
|
110
|
+
throw usageError("shot", "--motion needs a slide", { match: "--motion" });
|
|
111
|
+
}
|
|
112
|
+
return { kind: "motion", slug, ...(step === undefined ? {} : { step }) };
|
|
113
|
+
}
|
|
114
|
+
if (to !== undefined) {
|
|
115
|
+
if (!slug) {
|
|
116
|
+
throw usageError("shot", "--to needs the slide it starts from", { match: "--to" });
|
|
117
|
+
}
|
|
118
|
+
if (step !== undefined) {
|
|
119
|
+
refuse(
|
|
120
|
+
"--to cannot be combined with --step",
|
|
121
|
+
"a morph frame always starts from the last beat; drop --step",
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
return { kind: "morph", from: slug, to, at: parseMorphAt(at) };
|
|
125
|
+
}
|
|
126
|
+
if (at !== undefined) {
|
|
127
|
+
throw usageError("shot", "--at needs --to", { match: "--to" });
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
kind: "still",
|
|
131
|
+
...(slug ? { slug } : {}),
|
|
132
|
+
...(step === undefined ? {} : { step }),
|
|
133
|
+
};
|
|
134
|
+
}
|
package/src/cli/show.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { classifyAssetRef } from "../core/assets.ts";
|
|
2
|
+
import { cssUrls, parseCss } from "../core/css.ts";
|
|
3
|
+
import { deckPaths } from "../core/deck-paths.ts";
|
|
4
|
+
import { scanSlideHtml } from "../core/html-scan.ts";
|
|
5
|
+
import { sectionChunks, splitLines } from "../core/lines.ts";
|
|
6
|
+
import { readDeckFile } from "../core/resolve.ts";
|
|
7
|
+
import type { Section } from "../core/schema.ts";
|
|
8
|
+
import { themeExcerpt } from "../core/theme-excerpt.ts";
|
|
9
|
+
import { type ReadableDeck, type RefInfo, requireSection } from "./scope.ts";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Everything one slide is made of, read in one call: enough to learn how it is
|
|
13
|
+
* built without opening theme.css whole.
|
|
14
|
+
*/
|
|
15
|
+
export type ShowResult = {
|
|
16
|
+
slug: string;
|
|
17
|
+
title: string;
|
|
18
|
+
/** The section as script.md has it, from its `##` heading, beat headings and ids included. */
|
|
19
|
+
script: string;
|
|
20
|
+
/** Its beats in order, the ids and positions a `data-step` binds to. */
|
|
21
|
+
beats: { id?: string; title: string; line: number }[];
|
|
22
|
+
html: string | null;
|
|
23
|
+
/** `slides/<slug>.css`. */
|
|
24
|
+
css: string | null;
|
|
25
|
+
/** `slides/<slug>.ts`. */
|
|
26
|
+
ts: string | null;
|
|
27
|
+
/** The rules of the deck's theme.css this slide uses; null when the deck has no theme.css. */
|
|
28
|
+
theme: string | null;
|
|
29
|
+
/** Deck-relative paths of the files the slide references that exist. */
|
|
30
|
+
assets: string[];
|
|
31
|
+
/** Set when the deck is a ref. */
|
|
32
|
+
ref?: RefInfo;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export function showCommand({ deck, ref }: ReadableDeck, slug: string): ShowResult {
|
|
36
|
+
const section = requireSection(deck, slug);
|
|
37
|
+
|
|
38
|
+
const paths = deckPaths(deck.dir);
|
|
39
|
+
// Through the same link rules as the build: show prints nothing a build would refuse.
|
|
40
|
+
const read = (path: string): string | null => readDeckFile(deck.dir, path) ?? null;
|
|
41
|
+
const html = read(paths.slide(slug, ".html"));
|
|
42
|
+
const css = read(paths.slide(slug, ".css"));
|
|
43
|
+
const ts = read(paths.slide(slug, ".ts"));
|
|
44
|
+
const themeCss = read(paths.theme);
|
|
45
|
+
const scan = html === null ? undefined : scanSlideHtml(html);
|
|
46
|
+
const sheet = css === null ? undefined : parseCss(css);
|
|
47
|
+
|
|
48
|
+
const theme =
|
|
49
|
+
themeCss === null
|
|
50
|
+
? null
|
|
51
|
+
: themeExcerpt(parseCss(themeCss), {
|
|
52
|
+
classes: scan?.classes ?? [],
|
|
53
|
+
...(scan?.layout !== undefined ? { layout: scan.layout } : {}),
|
|
54
|
+
...(sheet ? { css: sheet } : {}),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const refs = [
|
|
58
|
+
...(scan?.refs.map((ref) => ref.value) ?? []),
|
|
59
|
+
...(sheet ? cssUrls(sheet).map((url) => url.value) : []),
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
slug: section.slug,
|
|
64
|
+
title: section.title,
|
|
65
|
+
script: sectionSource(deck, section),
|
|
66
|
+
beats: section.beats.map(({ id, title, line }) => ({ ...(id ? { id } : {}), title, line })),
|
|
67
|
+
html,
|
|
68
|
+
css,
|
|
69
|
+
ts,
|
|
70
|
+
theme,
|
|
71
|
+
assets: existingRefs(refs, paths.slides, deck.dir),
|
|
72
|
+
...(ref ? { ref } : {}),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Local references that name a file inside the deck, as sorted deck-relative
|
|
78
|
+
* paths. theme.css and the slide's own files are left out: show returns them.
|
|
79
|
+
*/
|
|
80
|
+
function existingRefs(values: string[], slidesDir: string, deckDir: string): string[] {
|
|
81
|
+
const found = new Set<string>();
|
|
82
|
+
for (const value of values) {
|
|
83
|
+
const ref = classifyAssetRef(value, { deckDir, from: slidesDir });
|
|
84
|
+
if (
|
|
85
|
+
ref.kind === "file" &&
|
|
86
|
+
ref.deckPath !== "theme.css" &&
|
|
87
|
+
!ref.deckPath.startsWith("slides/")
|
|
88
|
+
) {
|
|
89
|
+
found.add(ref.deckPath);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return [...found].sort();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** The section's lines of script.md, up to the next section, without the blank lines between. */
|
|
96
|
+
function sectionSource(deck: ReadableDeck["deck"], section: Section): string {
|
|
97
|
+
const sections = deck.deck.sections;
|
|
98
|
+
const chunk = sectionChunks(
|
|
99
|
+
splitLines(readDeckFile(deck.dir, deck.scriptPath) ?? ""),
|
|
100
|
+
sections.map((entry) => entry.line),
|
|
101
|
+
)[sections.indexOf(section)];
|
|
102
|
+
return `${(chunk ?? []).join("\n").trimEnd()}\n`;
|
|
103
|
+
}
|
package/src/cli/sync.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { syncDeck } from "../core/sync.ts";
|
|
3
|
+
import { ignoreLocalState } from "./files.ts";
|
|
4
|
+
import type { DecksTarget } from "./scope.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Every file the sync wrote or removed: the decks' slides, then dek's own files, then .gitignore
|
|
8
|
+
* when it lacked what dek keeps for itself; and the slides it kept though their section is gone,
|
|
9
|
+
* which lint reports as DEK002.
|
|
10
|
+
*/
|
|
11
|
+
export type SyncCliResult = {
|
|
12
|
+
created: string[];
|
|
13
|
+
updated: string[];
|
|
14
|
+
removed: string[];
|
|
15
|
+
kept: string[];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function syncCommand({ project, decks }: DecksTarget): SyncCliResult {
|
|
19
|
+
const result: SyncCliResult = { created: [], updated: [], removed: [], kept: [] };
|
|
20
|
+
const dek = { created: [] as string[], updated: [] as string[] };
|
|
21
|
+
for (const deck of decks) {
|
|
22
|
+
const synced = syncDeck({ project, deck });
|
|
23
|
+
result.created.push(...synced.created);
|
|
24
|
+
result.updated.push(...synced.updated);
|
|
25
|
+
result.removed.push(...synced.removed);
|
|
26
|
+
result.kept.push(...synced.kept);
|
|
27
|
+
// The first deck brings them up to date; the rest find them current.
|
|
28
|
+
dek.created.push(...synced.dekFiles.created);
|
|
29
|
+
dek.updated.push(...synced.dekFiles.updated);
|
|
30
|
+
}
|
|
31
|
+
result.created.push(...dek.created);
|
|
32
|
+
result.updated.push(...dek.updated);
|
|
33
|
+
if (ignoreLocalState(project.root)) {
|
|
34
|
+
result.updated.push(join(project.root, ".gitignore"));
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
}
|