@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.
Files changed (187) hide show
  1. package/LICENSE +21 -0
  2. package/README.ja.md +75 -0
  3. package/README.md +75 -0
  4. package/package.json +65 -0
  5. package/src/cli/address.ts +53 -0
  6. package/src/cli/build.ts +85 -0
  7. package/src/cli/check.ts +97 -0
  8. package/src/cli/commands.ts +851 -0
  9. package/src/cli/contract.ts +444 -0
  10. package/src/cli/cues.ts +18 -0
  11. package/src/cli/files.ts +309 -0
  12. package/src/cli/flags.ts +295 -0
  13. package/src/cli/format.ts +156 -0
  14. package/src/cli/goto.ts +96 -0
  15. package/src/cli/init.ts +136 -0
  16. package/src/cli/keep-alive.ts +72 -0
  17. package/src/cli/lint.ts +84 -0
  18. package/src/cli/ls.ts +119 -0
  19. package/src/cli/main.ts +231 -0
  20. package/src/cli/marks.ts +16 -0
  21. package/src/cli/mv.ts +37 -0
  22. package/src/cli/new.ts +64 -0
  23. package/src/cli/pdf.ts +16 -0
  24. package/src/cli/qr.ts +466 -0
  25. package/src/cli/ref.ts +165 -0
  26. package/src/cli/rehearse.ts +42 -0
  27. package/src/cli/result.ts +161 -0
  28. package/src/cli/scope.ts +308 -0
  29. package/src/cli/serve.ts +125 -0
  30. package/src/cli/shot.ts +134 -0
  31. package/src/cli/show.ts +103 -0
  32. package/src/cli/sync.ts +37 -0
  33. package/src/cli/text.ts +479 -0
  34. package/src/cli/theme.ts +56 -0
  35. package/src/cli/tty.ts +81 -0
  36. package/src/cli/usage.ts +198 -0
  37. package/src/cli/video.ts +17 -0
  38. package/src/cli/voice.ts +113 -0
  39. package/src/cli.ts +4 -0
  40. package/src/core/assets.ts +239 -0
  41. package/src/core/build.ts +77 -0
  42. package/src/core/bundled-theme.ts +15 -0
  43. package/src/core/capture-go.ts +72 -0
  44. package/src/core/chrome.ts +128 -0
  45. package/src/core/config-keys.ts +82 -0
  46. package/src/core/config.ts +95 -0
  47. package/src/core/css-scan.ts +158 -0
  48. package/src/core/css-transform.ts +140 -0
  49. package/src/core/css-value.ts +127 -0
  50. package/src/core/css.ts +545 -0
  51. package/src/core/cue.ts +187 -0
  52. package/src/core/deck-paths.ts +34 -0
  53. package/src/core/diagnostic.ts +114 -0
  54. package/src/core/document.ts +218 -0
  55. package/src/core/error.ts +31 -0
  56. package/src/core/escape.ts +16 -0
  57. package/src/core/fill.ts +100 -0
  58. package/src/core/finish-beat.ts +30 -0
  59. package/src/core/github.ts +234 -0
  60. package/src/core/html-scan.ts +253 -0
  61. package/src/core/html.ts +234 -0
  62. package/src/core/in-page-go.ts +128 -0
  63. package/src/core/lines.ts +43 -0
  64. package/src/core/lint/asset-refs.ts +109 -0
  65. package/src/core/lint/context.ts +116 -0
  66. package/src/core/lint/files.ts +117 -0
  67. package/src/core/lint/keys.ts +48 -0
  68. package/src/core/lint/morph.ts +91 -0
  69. package/src/core/lint/script.ts +102 -0
  70. package/src/core/lint/shared-styles.ts +165 -0
  71. package/src/core/lint/slide-html.ts +466 -0
  72. package/src/core/lint/slide-script.ts +179 -0
  73. package/src/core/lint/theme-css.ts +295 -0
  74. package/src/core/lint/timing.ts +66 -0
  75. package/src/core/lint/tokens.ts +520 -0
  76. package/src/core/lint/voice.ts +76 -0
  77. package/src/core/lint.ts +137 -0
  78. package/src/core/live-protocol.ts +130 -0
  79. package/src/core/marks.ts +198 -0
  80. package/src/core/mv.ts +293 -0
  81. package/src/core/ogp.ts +99 -0
  82. package/src/core/optional.ts +24 -0
  83. package/src/core/overflow.ts +220 -0
  84. package/src/core/page-pool.ts +53 -0
  85. package/src/core/page.ts +66 -0
  86. package/src/core/parse.ts +307 -0
  87. package/src/core/path.ts +58 -0
  88. package/src/core/pdf.ts +71 -0
  89. package/src/core/playwright-module.d.ts +43 -0
  90. package/src/core/playwright-motion.ts +42 -0
  91. package/src/core/playwright-sheet.ts +32 -0
  92. package/src/core/playwright-visual.ts +391 -0
  93. package/src/core/playwright-worker.ts +5 -0
  94. package/src/core/playwright.ts +431 -0
  95. package/src/core/position.ts +146 -0
  96. package/src/core/presenter-state.ts +43 -0
  97. package/src/core/presenter.ts +131 -0
  98. package/src/core/prose.ts +16 -0
  99. package/src/core/pseudo-text.ts +67 -0
  100. package/src/core/rail-width.ts +4 -0
  101. package/src/core/ref-name.ts +110 -0
  102. package/src/core/ref.ts +238 -0
  103. package/src/core/resolve.ts +228 -0
  104. package/src/core/rumdl.ts +99 -0
  105. package/src/core/safe-fs.ts +196 -0
  106. package/src/core/sarif.ts +135 -0
  107. package/src/core/schema.ts +89 -0
  108. package/src/core/script-lines.ts +53 -0
  109. package/src/core/sheet.ts +223 -0
  110. package/src/core/shot/morph.ts +80 -0
  111. package/src/core/shot/motion.ts +187 -0
  112. package/src/core/shot/still.ts +216 -0
  113. package/src/core/shot-cache.ts +140 -0
  114. package/src/core/size.ts +11 -0
  115. package/src/core/skeleton-record.ts +51 -0
  116. package/src/core/skeleton.ts +89 -0
  117. package/src/core/slide-draw.ts +165 -0
  118. package/src/core/slide-eval-worker.ts +58 -0
  119. package/src/core/slide-measure.ts +272 -0
  120. package/src/core/slide-script-eval.ts +258 -0
  121. package/src/core/slide-script.ts +252 -0
  122. package/src/core/spawn.ts +128 -0
  123. package/src/core/step.ts +94 -0
  124. package/src/core/still-page.ts +70 -0
  125. package/src/core/suggest.ts +33 -0
  126. package/src/core/sync.ts +316 -0
  127. package/src/core/text-contrast.ts +466 -0
  128. package/src/core/theme-excerpt.ts +187 -0
  129. package/src/core/theme-facts.ts +51 -0
  130. package/src/core/timeline.ts +216 -0
  131. package/src/core/timing.ts +88 -0
  132. package/src/core/toml-keys.ts +349 -0
  133. package/src/core/url-attributes.ts +103 -0
  134. package/src/core/video-document.ts +15 -0
  135. package/src/core/visual-cache.ts +100 -0
  136. package/src/core/visual.ts +558 -0
  137. package/src/core/voice.ts +321 -0
  138. package/src/core/zod.ts +79 -0
  139. package/src/runtime/browser.ts +203 -0
  140. package/src/runtime/clone.ts +52 -0
  141. package/src/runtime/deck-control.ts +43 -0
  142. package/src/runtime/fit.ts +32 -0
  143. package/src/runtime/go.ts +56 -0
  144. package/src/runtime/input.ts +128 -0
  145. package/src/runtime/laser-view.ts +110 -0
  146. package/src/runtime/laser.ts +145 -0
  147. package/src/runtime/live-host.ts +33 -0
  148. package/src/runtime/live-reload.ts +29 -0
  149. package/src/runtime/live.ts +88 -0
  150. package/src/runtime/marks-view.ts +74 -0
  151. package/src/runtime/motion.ts +113 -0
  152. package/src/runtime/navigator.ts +189 -0
  153. package/src/runtime/player.ts +28 -0
  154. package/src/runtime/presenter-view.ts +289 -0
  155. package/src/runtime/presenter.ts +52 -0
  156. package/src/runtime/rail-view.ts +229 -0
  157. package/src/runtime/rail.ts +52 -0
  158. package/src/runtime/rehearse.ts +264 -0
  159. package/src/runtime/routes.ts +142 -0
  160. package/src/runtime/settle.ts +45 -0
  161. package/src/runtime/slide.d.ts +28 -0
  162. package/src/runtime/socket.ts +143 -0
  163. package/src/runtime/stage.ts +259 -0
  164. package/src/runtime/step.ts +139 -0
  165. package/src/runtime/window.ts +38 -0
  166. package/src/server/auth.ts +210 -0
  167. package/src/server/deck-snapshot.ts +146 -0
  168. package/src/server/dev.ts +251 -0
  169. package/src/server/hub.ts +104 -0
  170. package/src/server/lan.ts +51 -0
  171. package/src/server/lock.ts +97 -0
  172. package/src/server/pages.ts +71 -0
  173. package/src/server/pairing.ts +40 -0
  174. package/src/server/project-watch.ts +142 -0
  175. package/src/server/rooms.ts +133 -0
  176. package/src/server/routes.ts +286 -0
  177. package/src/server/serial.ts +25 -0
  178. package/src/server/watch.ts +308 -0
  179. package/src/theme/default.css +175 -0
  180. package/src/video/bake.ts +152 -0
  181. package/src/video/mux.ts +103 -0
  182. package/src/video/recorder.ts +115 -0
  183. package/src/video/sidecar.ts +60 -0
  184. package/src/video/worker.ts +51 -0
  185. package/src/voice/engine.ts +177 -0
  186. package/src/voice/synth.ts +127 -0
  187. package/src/voice/wav.ts +115 -0
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Which attributes name a URL, and where in the value each URL is written. Lint checks exactly
3
+ * these, and the build inlines exactly these, so a reference that passes one never breaks the other.
4
+ */
5
+
6
+ /**
7
+ * What the page does with a URL: a `resource` is loaded to draw the slide, so it must exist and
8
+ * live in the deck's `assets/`; a `link` is only followed when someone activates it.
9
+ */
10
+ export type UrlUse = "resource" | "link";
11
+
12
+ /**
13
+ * Every attribute that names a URL, first match wins. `list` marks a `srcset`, which names one URL
14
+ * per candidate. `href` loads a resource only in SVG; elsewhere it is a link, and `<link>` in a
15
+ * document's head points at the theme, which the build replaces.
16
+ */
17
+ const URL_ATTRIBUTES: ReadonlyArray<{
18
+ attr: string;
19
+ use: UrlUse;
20
+ tags?: readonly string[];
21
+ list?: boolean;
22
+ }> = [
23
+ { attr: "src", use: "resource" },
24
+ { attr: "srcset", use: "resource", list: true },
25
+ { attr: "imagesrcset", use: "resource", list: true },
26
+ { attr: "poster", use: "resource" },
27
+ { attr: "background", use: "resource" },
28
+ { attr: "data", use: "resource", tags: ["object"] },
29
+ { attr: "href", use: "resource", tags: ["image", "use", "feimage"] },
30
+ { attr: "xlink:href", use: "resource" },
31
+ { attr: "href", use: "link" },
32
+ { attr: "action", use: "link" },
33
+ { attr: "formaction", use: "link" },
34
+ ];
35
+
36
+ /** Whether `attr` on a `tag` element names a URL. */
37
+ export function isUrlAttribute(tag: string, attr: string): boolean {
38
+ return urlAttribute(tag, attr) !== undefined;
39
+ }
40
+
41
+ function urlAttribute(tag: string, attr: string): (typeof URL_ATTRIBUTES)[number] | undefined {
42
+ return URL_ATTRIBUTES.find(
43
+ (entry) => entry.attr === attr && (entry.tags === undefined || entry.tags.includes(tag)),
44
+ );
45
+ }
46
+
47
+ /**
48
+ * The URLs of a `srcset`, each with its offset in the value, parsed the way the HTML standard
49
+ * does: a URL runs to the next whitespace, a trailing comma ends it, and the descriptors after it
50
+ * run to the next comma. So `data:` URIs keep their commas.
51
+ */
52
+ export function srcsetUrls(value: string): Array<{ url: string; offset: number }> {
53
+ const candidates: Array<{ url: string; offset: number }> = [];
54
+ let index = 0;
55
+ while (index < value.length) {
56
+ while (index < value.length && /[\s,]/.test(value[index] ?? "")) {
57
+ index++;
58
+ }
59
+ const start = index;
60
+ while (index < value.length && !/\s/.test(value[index] ?? "")) {
61
+ index++;
62
+ }
63
+ let url = value.slice(start, index);
64
+ if (url.endsWith(",")) {
65
+ url = url.replace(/,+$/, "");
66
+ } else {
67
+ let depth = 0;
68
+ for (; index < value.length; index++) {
69
+ const char = value[index];
70
+ if (char === "(") {
71
+ depth++;
72
+ } else if (char === ")") {
73
+ depth = Math.max(0, depth - 1);
74
+ } else if (char === "," && depth === 0) {
75
+ break;
76
+ }
77
+ }
78
+ }
79
+ if (url) {
80
+ candidates.push({ url, offset: start });
81
+ }
82
+ }
83
+ return candidates;
84
+ }
85
+
86
+ /** The URLs an attribute names, each with its offset into the value; none when it names no URL. */
87
+ export function attributeUrls(
88
+ tag: string,
89
+ attr: string,
90
+ value: string,
91
+ ): { use: UrlUse; urls: Array<{ url: string; offset: number }> } | undefined {
92
+ const entry = urlAttribute(tag, attr);
93
+ const url = value.trim();
94
+ if (!entry || !url) {
95
+ return undefined;
96
+ }
97
+ return {
98
+ use: entry.use,
99
+ urls: entry.list
100
+ ? srcsetUrls(value)
101
+ : [{ url, offset: value.length - value.trimStart().length }],
102
+ };
103
+ }
@@ -0,0 +1,15 @@
1
+ import { loadConfig } from "./config.ts";
2
+ import { renderDeckDocument } from "./document.ts";
3
+ import type { ResolvedDeck } from "./resolve.ts";
4
+
5
+ /**
6
+ * The whole deck as `dekc video` plays it: every slide, assets inlined, no notes, and the player
7
+ * runtime that walks it. Motion stills, morph frames and the video itself all run on this page.
8
+ */
9
+ export function videoDocument({ project, deck }: ResolvedDeck, playerScript: string): string {
10
+ return renderDeckDocument(deck, {
11
+ config: loadConfig(project.configPath),
12
+ playerScript,
13
+ target: { kind: "video" },
14
+ });
15
+ }
@@ -0,0 +1,100 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readdirSync, readFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { cacheDir, deckProjectRoot } from "./path.ts";
5
+ import type { PageAction, PagesResponse, VisualPage } from "./playwright.ts";
6
+ import {
7
+ isCachedFile,
8
+ outputDir,
9
+ readSourceIfExists,
10
+ removeInside,
11
+ writeInside,
12
+ } from "./safe-fs.ts";
13
+
14
+ /** What measuring one page found: its overflows, its texts' contrast, and what its draw threw. */
15
+ export type PageFindings = Required<PagesResponse>;
16
+
17
+ /**
18
+ * The code that decides what a page's findings are. A change to any of it can change them, so
19
+ * its source is part of every key, and a cache written by another dek is never read.
20
+ */
21
+ const MEASURERS = [
22
+ "playwright-visual.ts",
23
+ "slide-measure.ts",
24
+ "text-contrast.ts",
25
+ "fill.ts",
26
+ "overflow.ts",
27
+ "pseudo-text.ts",
28
+ "finish-beat.ts",
29
+ "visual-cache.ts",
30
+ ];
31
+
32
+ let measurerFingerprint: string | undefined;
33
+
34
+ function measurers(): string {
35
+ measurerFingerprint ??= createHash("sha256")
36
+ .update(MEASURERS.map((name) => readFileSync(join(import.meta.dir, name), "utf8")).join("\0"))
37
+ .digest("hex");
38
+ return measurerFingerprint;
39
+ }
40
+
41
+ /**
42
+ * The findings of each page `--visual` measured, under `.cache/visual`, named by a hash of all
43
+ * that decides them: the page's HTML, which holds its theme, CSS, script, and assets, the
44
+ * viewport, the checks asked for, and the code that measures. A page whose name is on disk is not
45
+ * measured again. Like every cache, it can be deleted: the next run measures everything.
46
+ */
47
+ export type VisualCache = {
48
+ key(page: VisualPage): string;
49
+ read(key: string): PageFindings | undefined;
50
+ write(key: string, findings: PageFindings): void;
51
+ /** Removes every entry but `keep`, after a run that measured the whole deck. */
52
+ prune(keep: Set<string>): void;
53
+ };
54
+
55
+ export function visualCache(
56
+ deckDir: string,
57
+ request: { viewport: { width: number; height: number }; actions: PageAction[] },
58
+ ): VisualCache {
59
+ const root = deckProjectRoot(deckDir);
60
+ const dir = cacheDir(deckDir, "visual");
61
+ const pathOf = (key: string): string => join(dir, `${key}.json`);
62
+ const context = JSON.stringify({ measurers: measurers(), ...request });
63
+ return {
64
+ key: (page) =>
65
+ createHash("sha256")
66
+ .update(context)
67
+ .update("\0")
68
+ .update(JSON.stringify([page.slug, page.step, page.html]))
69
+ .digest("hex")
70
+ .slice(0, 32),
71
+ read: (key) => {
72
+ const path = pathOf(key);
73
+ if (!isCachedFile(path)) {
74
+ return undefined;
75
+ }
76
+ try {
77
+ return JSON.parse(readSourceIfExists(path, root) ?? "") as PageFindings;
78
+ } catch {
79
+ return undefined;
80
+ }
81
+ },
82
+ write: (key, findings) => {
83
+ outputDir(dir, root);
84
+ writeInside(pathOf(key), JSON.stringify(findings), root);
85
+ },
86
+ prune: (keep) => {
87
+ let entries: string[];
88
+ try {
89
+ entries = readdirSync(dir);
90
+ } catch {
91
+ return;
92
+ }
93
+ for (const name of entries) {
94
+ if (name.endsWith(".json") && !keep.has(name.slice(0, -".json".length))) {
95
+ removeInside(join(dir, name), root);
96
+ }
97
+ }
98
+ },
99
+ };
100
+ }