@hajimism/dek 0.5.1 → 0.6.1

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 (48) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/package.json +10 -3
  3. package/src/cli/build.ts +3 -1
  4. package/src/cli/check.ts +20 -11
  5. package/src/cli/commands.ts +24 -10
  6. package/src/cli/contract.ts +28 -2
  7. package/src/cli/flags.ts +5 -0
  8. package/src/cli/lint.ts +53 -1
  9. package/src/cli/sync.ts +21 -6
  10. package/src/cli/text.ts +20 -2
  11. package/src/core/build.ts +4 -0
  12. package/src/core/config-keys.ts +7 -2
  13. package/src/core/config.ts +63 -0
  14. package/src/core/diagnostic.ts +9 -1
  15. package/src/core/document.ts +28 -3
  16. package/src/core/lint/script.ts +2 -4
  17. package/src/core/page.ts +1 -0
  18. package/src/core/plugin-host.ts +709 -0
  19. package/src/core/plugin.ts +442 -0
  20. package/src/core/sarif.ts +47 -5
  21. package/src/core/script-lines.ts +11 -0
  22. package/src/core/sync.ts +4 -2
  23. package/src/plugin-api.ts +126 -0
  24. package/src/plugins/study/check.ts +128 -0
  25. package/src/plugins/study/main.ts +47 -0
  26. package/src/plugins/study/manifest.ts +32 -0
  27. package/src/plugins/study/page.ts +295 -0
  28. package/src/plugins/study/place.ts +88 -0
  29. package/src/plugins/study/sarif.ts +30 -0
  30. package/src/plugins/study/study.ts +137 -0
  31. package/src/plugins/study/text.ts +55 -0
  32. package/src/plugins/study/yaml.ts +226 -0
  33. package/src/plugins/words/check.ts +109 -0
  34. package/src/plugins/words/lines.ts +26 -0
  35. package/src/plugins/words/list.ts +288 -0
  36. package/src/plugins/words/main.ts +50 -0
  37. package/src/plugins/words/manifest.ts +36 -0
  38. package/src/plugins/words/match.ts +60 -0
  39. package/src/plugins/words/sarif.ts +37 -0
  40. package/src/plugins/words/text.ts +352 -0
  41. package/src/plugins/words/yaml.ts +225 -0
  42. package/src/runtime/player.ts +18 -2
  43. package/src/runtime/plugins.ts +236 -0
  44. package/src/runtime/start.ts +16 -1
  45. package/src/runtime/window.ts +3 -0
  46. package/src/server/deck-snapshot.ts +32 -1
  47. package/src/server/dev.ts +7 -1
  48. package/src/server/pages.ts +9 -1
@@ -4,6 +4,7 @@ import { escapeAttr, escapeHtml } from "./escape.ts";
4
4
  import { collectSlidesHtml, htmlShell } from "./html.ts";
5
5
  import { type OgpImage, ogpHead } from "./ogp.ts";
6
6
  import { PAGE_ID, type PageMode, pageConfigAttrs } from "./page.ts";
7
+ import type { PluginPage } from "./plugin.ts";
7
8
  import {
8
9
  nextPresenterTitle,
9
10
  presenterSlides,
@@ -49,14 +50,19 @@ export type PageTarget =
49
50
  /** The deck at `dir`, rendered for `target`. */
50
51
  export function renderDeckHtml(
51
52
  dir: string,
52
- options: { playerScript: string; target: PageTarget },
53
+ options: { playerScript: string; target: PageTarget; plugins?: PluginPage[] },
53
54
  ): string {
54
55
  return renderDeckDocument(resolveDeck(dir).deck, options);
55
56
  }
56
57
 
57
58
  export function renderDeckDocument(
58
59
  deck: ProjectDeck,
59
- options: { playerScript: string; target: PageTarget },
60
+ options: {
61
+ playerScript: string;
62
+ target: PageTarget;
63
+ /** Each plugin's page; only the player and the build carry them (ADR 33). */
64
+ plugins?: PluginPage[];
65
+ },
60
66
  ): string {
61
67
  const { target } = options;
62
68
  const mode: PageMode =
@@ -160,7 +166,8 @@ export function renderDeckDocument(
160
166
  ${slideScriptTags(slideScripts)}
161
167
  <script>${options.playerScript}</script>
162
168
  ${target.kind === "dev" ? `<script>${target.liveReloadScript}</script>` : ""}
163
- ${target.kind === "dev" && includeNotes ? `<script>${target.annotateScript}</script>` : ""}`,
169
+ ${target.kind === "dev" && includeNotes ? `<script>${target.annotateScript}</script>` : ""}
170
+ ${pluginPagesHtml(target, options.plugins ?? [])}`,
164
171
  });
165
172
  }
166
173
 
@@ -225,6 +232,24 @@ function renderKeyHintHtml(lang: string): string {
225
232
  return `<div id="${PAGE_ID.hint}" role="status"><span><kbd>s</kbd>${labels.rail}</span><span><kbd>p</kbd>${labels.presenter}</span></div>`;
226
233
  }
227
234
 
235
+ /**
236
+ * The plugins' pages, after the player, which gives them `dek.plugin`: their keys and data as one
237
+ * JSON block, then each script in name order. Only the player and the build carry them; the
238
+ * presenter view and a video, like every still page, never do.
239
+ */
240
+ function pluginPagesHtml(target: PageTarget, plugins: PluginPage[]): string {
241
+ const carried = target.kind === "build" || (target.kind === "dev" && target.mode === "player");
242
+ if (!carried || plugins.length === 0) {
243
+ return "";
244
+ }
245
+ const ordered = plugins.toSorted((a, b) => a.name.localeCompare(b.name));
246
+ const config = Object.fromEntries(ordered.map(({ name, keys, data }) => [name, { keys, data }]));
247
+ return [
248
+ `<script type="application/json" id="${PAGE_ID.plugins}">${jsonForScript(config)}</script>`,
249
+ ...ordered.map(({ script }) => `<script>${script.replace(/<\/(script)/gi, "<\\/$1")}</script>`),
250
+ ].join("\n ");
251
+ }
252
+
228
253
  function jsonForScript(value: unknown): string {
229
254
  return JSON.stringify(value).replace(/</g, "\\u003c");
230
255
  }
@@ -1,7 +1,7 @@
1
1
  import { type Diagnostic, diag } from "../diagnostic.ts";
2
2
  import type { DekError } from "../error.ts";
3
3
  import { ScriptError } from "../parse.ts";
4
- import { scriptLines } from "../script-lines.ts";
4
+ import { scriptLines, slugAtLine } from "../script-lines.ts";
5
5
  import type { LintContext } from "./context.ts";
6
6
 
7
7
  /** DEK004: a section id or a beat id used twice. */
@@ -56,7 +56,6 @@ export function strayHeadingDiagnostics(ctx: LintContext): Diagnostic[] {
56
56
  if (!script) {
57
57
  return [];
58
58
  }
59
- const starts = deck.deck.sections.map((section) => ({ line: section.line, slug: section.slug }));
60
59
  const diagnostics: Diagnostic[] = [];
61
60
  // What the parser reads as text, a code block or a comment, is not a heading here either.
62
61
  for (const [offset, { text, literal }] of scriptLines(script.body).entries()) {
@@ -65,8 +64,7 @@ export function strayHeadingDiagnostics(ctx: LintContext): Diagnostic[] {
65
64
  continue;
66
65
  }
67
66
  const line = script.bodyStartLine + offset;
68
- // Under a slide heading it belongs to that slide; above the first, to the deck.
69
- const slug = starts.filter((start) => start.line < line).at(-1)?.slug;
67
+ const slug = slugAtLine(deck.deck.sections, line);
70
68
  diagnostics.push(
71
69
  diag("DEK044", {
72
70
  message: `${"#".repeat(level)} heading is not a slide or a beat; it is read as spoken text`,
package/src/core/page.ts CHANGED
@@ -7,6 +7,7 @@ import { escapeAttr } from "./escape.ts";
7
7
  export const PAGE_ID = {
8
8
  deck: "deck",
9
9
  data: "dek-data",
10
+ plugins: "dek-plugins",
10
11
  shell: "dek-shell",
11
12
  rail: "dek-rail",
12
13
  railResize: "dek-rail-resize",