@cyber-dash-tech/revela 0.18.15 → 0.19.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 (103) hide show
  1. package/README.md +48 -45
  2. package/README.zh-CN.md +48 -45
  3. package/assets/img/lucent-01.jpg +0 -0
  4. package/assets/img/lucent-02.jpg +0 -0
  5. package/assets/img/lucent-03.jpg +0 -0
  6. package/assets/img/lucent-dark-01.jpg +0 -0
  7. package/assets/img/lucent-dark-02.jpg +0 -0
  8. package/assets/img/lucent-dark-03.jpg +0 -0
  9. package/assets/img/monet-01.jpg +0 -0
  10. package/assets/img/monet-02.jpg +0 -0
  11. package/assets/img/monet-03.jpg +0 -0
  12. package/assets/img/starter-01.jpg +0 -0
  13. package/assets/img/starter-02.jpg +0 -0
  14. package/assets/img/starter-03.jpg +0 -0
  15. package/assets/img/summit-01.jpg +0 -0
  16. package/assets/img/summit-02.jpg +0 -0
  17. package/assets/img/summit-03.jpg +0 -0
  18. package/designs/lucent/DESIGN.md +108 -1
  19. package/designs/lucent/design.css +283 -0
  20. package/designs/lucent-dark/DESIGN.md +278 -0
  21. package/designs/lucent-dark/assets/card-lens.jpg +0 -0
  22. package/designs/lucent-dark/assets/closing-background.jpg +0 -0
  23. package/designs/lucent-dark/assets/cover-background.jpg +0 -0
  24. package/designs/lucent-dark/assets/report-visual.jpg +0 -0
  25. package/designs/lucent-dark/assets/soft-texture.jpg +0 -0
  26. package/designs/lucent-dark/assets/toc-orb.png +0 -0
  27. package/designs/lucent-dark/design.css +417 -0
  28. package/designs/monet/DESIGN.md +53 -9
  29. package/designs/monet/assets/card-lens.jpg +0 -0
  30. package/designs/monet/assets/closing-background.jpg +0 -0
  31. package/designs/monet/assets/cover-background.jpg +0 -0
  32. package/designs/monet/assets/report-visual.jpg +0 -0
  33. package/designs/monet/assets/soft-texture.jpg +0 -0
  34. package/designs/monet/assets/toc-orb.png +0 -0
  35. package/designs/monet/design.css +340 -0
  36. package/designs/starter/DESIGN.md +22 -5
  37. package/designs/starter/assets/card-lens.jpg +0 -0
  38. package/designs/starter/assets/closing-background.jpg +0 -0
  39. package/designs/starter/assets/cover-background.jpg +0 -0
  40. package/designs/starter/assets/report-visual.jpg +0 -0
  41. package/designs/starter/assets/soft-texture.jpg +0 -0
  42. package/designs/starter/assets/toc-orb.png +0 -0
  43. package/designs/starter/design.css +322 -0
  44. package/designs/summit/DESIGN.md +54 -9
  45. package/designs/summit/assets/card-lens.jpg +0 -0
  46. package/designs/summit/assets/closing-background.jpg +0 -0
  47. package/designs/summit/assets/cover-background.jpg +0 -0
  48. package/designs/summit/assets/report-visual.jpg +0 -0
  49. package/designs/summit/assets/soft-texture.jpg +0 -0
  50. package/designs/summit/assets/toc-orb.png +0 -0
  51. package/designs/summit/design.css +334 -0
  52. package/lib/commands/designs-new.ts +18 -21
  53. package/lib/commands/designs-preview.ts +3 -8
  54. package/lib/deck-html/foundation.ts +8 -8
  55. package/lib/design/designs.ts +385 -14
  56. package/lib/narrative-state/deck-plan-artifact.ts +40 -3
  57. package/lib/page-templates/built-in-preview.html +373 -0
  58. package/lib/page-templates/contracts.ts +2 -0
  59. package/lib/page-templates/css.ts +2 -0
  60. package/lib/page-templates/foundation.ts +41 -0
  61. package/lib/page-templates/index.ts +6 -0
  62. package/lib/page-templates/registry.ts +3 -0
  63. package/lib/page-templates/render.ts +1202 -0
  64. package/lib/page-templates/templates/agenda.ts +4 -0
  65. package/lib/page-templates/templates/chart-takeaways.ts +4 -0
  66. package/lib/page-templates/templates/claim-supporting-visual.ts +4 -0
  67. package/lib/page-templates/templates/closing.ts +4 -0
  68. package/lib/page-templates/templates/cover.ts +4 -0
  69. package/lib/page-templates/templates/executive-summary.ts +4 -0
  70. package/lib/page-templates/templates/index.ts +19 -0
  71. package/lib/page-templates/templates/key-message-evidence.ts +4 -0
  72. package/lib/page-templates/templates/metric-highlight.ts +4 -0
  73. package/lib/page-templates/templates/problem-context.ts +4 -0
  74. package/lib/page-templates/templates/process-steps.ts +4 -0
  75. package/lib/page-templates/templates/recommendation-decision.ts +4 -0
  76. package/lib/page-templates/templates/risks-tradeoffs.ts +4 -0
  77. package/lib/page-templates/templates/section-divider.ts +4 -0
  78. package/lib/page-templates/templates/shared.ts +11 -0
  79. package/lib/page-templates/templates/table-comparison.ts +4 -0
  80. package/lib/page-templates/templates/timeline-roadmap.ts +4 -0
  81. package/lib/page-templates/vocabulary.ts +158 -0
  82. package/lib/prompt-builder.ts +9 -5
  83. package/lib/qa/artifact.ts +117 -7
  84. package/lib/qa/checks.ts +1 -1
  85. package/lib/qa/compliance.ts +5 -1
  86. package/lib/qa/component-contracts.ts +90 -0
  87. package/lib/runtime/index.ts +99 -3
  88. package/package.json +7 -15
  89. package/plugins/revela/.codex-plugin/plugin.json +4 -4
  90. package/plugins/revela/hooks/revela_guard.ts +35 -0
  91. package/plugins/revela/hooks/revela_post_write_notice.ts +39 -9
  92. package/plugins/revela/mcp/revela-server.ts +103 -7
  93. package/plugins/revela/skills/revela/SKILL.md +3 -3
  94. package/plugins/revela/skills/revela-design/SKILL.md +25 -14
  95. package/plugins/revela/skills/revela-helper/SKILL.md +3 -3
  96. package/plugins/revela/skills/revela-make-deck/SKILL.md +27 -12
  97. package/plugins/revela/skills/revela-research/SKILL.md +1 -0
  98. package/skill/SKILL.md +11 -2
  99. package/designs/lucent/preview.html +0 -612
  100. package/designs/monet/preview.html +0 -2293
  101. package/designs/starter/preview.html +0 -314
  102. package/designs/summit/preview.html +0 -2284
  103. package/plugins/revela/skills/revela-review/SKILL.md +0 -46
@@ -2,6 +2,7 @@ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, wri
2
2
  import { dirname, join, relative } from "path"
3
3
  import { createHash } from "crypto"
4
4
  import type { DeckSpec, SlideSpec } from "../decks-state"
5
+ import { listPageTemplates } from "../page-templates"
5
6
  import { parseVaultFrontmatter } from "../narrative-vault/frontmatter"
6
7
  import { splitMarkdownSections } from "../narrative-vault/markdown"
7
8
  import { stableVaultRelationId } from "../narrative-vault/relations"
@@ -92,6 +93,8 @@ export interface DeckPlanSlideProjection {
92
93
  slideIndex?: number
93
94
  title: string
94
95
  chapter: string
96
+ template?: string
97
+ templateContent?: Record<string, any>
95
98
  layout: string
96
99
  components: string[]
97
100
  componentPlan: DeckPlanSlideComponentPlan[]
@@ -575,6 +578,8 @@ function readDeckPlanSlideFiles(workspaceRoot: string, knownNodeIds?: Set<string
575
578
  slideIndex: numberField(parsed.frontmatter, "slideIndex"),
576
579
  title: stringField(parsed.frontmatter, "title") || id,
577
580
  chapter: stringField(parsed.frontmatter, "chapter"),
581
+ template: stringField(parsed.frontmatter, "template"),
582
+ templateContent: parseTemplateContent(split.sections["template-content"] ?? ""),
578
583
  layout: stringField(parsed.frontmatter, "layout"),
579
584
  components: arrayField(parsed.frontmatter, "components"),
580
585
  componentPlan,
@@ -623,6 +628,8 @@ function readDeckPlanSlidesFromSingleFile(workspaceRoot: string, absolutePath: s
623
628
  slideIndex,
624
629
  title,
625
630
  chapter: fields.chapter || "",
631
+ template: fields.template || "",
632
+ templateContent: parseTemplateContent(singleFileSubsection(block, "Template Content")),
626
633
  layout: fields.layout || "",
627
634
  components: parseCsv(fields.components || componentPlan.map((component) => component.name).join(", ")),
628
635
  componentPlan,
@@ -671,6 +678,8 @@ function readDeckPlanSeparatorSlidesFromSingleFile(workspaceRoot: string, absolu
671
678
  slideIndex,
672
679
  title,
673
680
  chapter: stringField(parsed.frontmatter, "chapter") || fields.chapter || "",
681
+ template: stringField(parsed.frontmatter, "template") || fields.template || "",
682
+ templateContent: parseTemplateContent(singleFileSubsection(block, "Template Content")),
674
683
  layout: stringField(parsed.frontmatter, "layout") || fields.layout || "",
675
684
  components: arrayField(parsed.frontmatter, "components").length > 0 ? arrayField(parsed.frontmatter, "components") : parseCsv(fields.components || componentPlan.map((component) => component.name).join(", ")),
676
685
  componentPlan,
@@ -702,6 +711,27 @@ function parseSlideBlockFields(block: string): Record<string, string> {
702
711
  return fields
703
712
  }
704
713
 
714
+ function parseTemplateContent(section: string): Record<string, any> | undefined {
715
+ const trimmed = section.trim()
716
+ if (!trimmed) return undefined
717
+ const fenced = /^```(?:json)?\s*\n([\s\S]*?)\n```$/i.exec(trimmed)
718
+ const body = fenced ? fenced[1].trim() : trimmed
719
+ if (body.startsWith("{")) {
720
+ try {
721
+ return JSON.parse(body)
722
+ } catch {
723
+ return { raw: body }
724
+ }
725
+ }
726
+ const result: Record<string, any> = {}
727
+ for (const rawLine of body.split(/\r?\n/)) {
728
+ const match = /^-\s+([A-Za-z][A-Za-z0-9 _-]*):\s*(.*)$/.exec(rawLine.trim())
729
+ if (!match) continue
730
+ result[match[1].trim().replace(/\s+/g, "_").toLowerCase()] = cleanPlanValue(match[2])
731
+ }
732
+ return Object.keys(result).length > 0 ? result : { raw: body }
733
+ }
734
+
705
735
  function singleFileSubsection(block: string, heading: string): string {
706
736
  const re = new RegExp(`^[ \\t]*####\\s+${heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*$`, "mi")
707
737
  const match = re.exec(block)
@@ -906,9 +936,11 @@ function slideDiagnostics(slide: DeckPlanSlideProjection, knownNodeIds?: Set<str
906
936
  const diagnostics: DeckPlanProjectionDiagnostic[] = []
907
937
  if (!slide.structural && linksCount(slide.sourceLinks) === 0) diagnostics.push({ severity: "warning", code: "slide_source_link_missing", message: `Non-structural deck-plan slide ${slide.id} has no material, finding, asset, or URL source link.`, file: slide.path, nodeId: slide.id })
908
938
  if (!slide.structural && linksCount(slide.sourceLinks) > 0) diagnostics.push(...slideSynthesisDiagnostics(slide))
909
- if (!slide.layout) diagnostics.push({ severity: "warning", code: "slide_layout_missing", message: `Deck-plan slide ${slide.id} is missing a layout.`, file: slide.path, nodeId: slide.id })
910
- if (slide.components.length === 0) diagnostics.push({ severity: "warning", code: "slide_components_missing", message: `Deck-plan slide ${slide.id} has no component names in frontmatter.`, file: slide.path, nodeId: slide.id })
911
- if (slide.componentPlan.length === 0) diagnostics.push({ severity: "warning", code: "slide_component_plan_missing", message: `Deck-plan slide ${slide.id} is missing structured ## Component Plan entries.`, file: slide.path, nodeId: slide.id })
939
+ if (slide.template && !knownTemplateIds().has(slide.template)) diagnostics.push({ severity: "warning", code: "slide_template_unknown", message: `Deck-plan slide ${slide.id} uses unknown page template '${slide.template}'.`, file: slide.path, nodeId: slide.id })
940
+ if (slide.template && !slide.templateContent) diagnostics.push({ severity: "warning", code: "slide_template_content_missing", message: `Deck-plan slide ${slide.id} uses template '${slide.template}' but has no #### Template Content section.`, file: slide.path, nodeId: slide.id })
941
+ if (!slide.template && !slide.layout) diagnostics.push({ severity: "warning", code: "slide_layout_missing", message: `Deck-plan slide ${slide.id} is missing a layout.`, file: slide.path, nodeId: slide.id })
942
+ if (!slide.template && slide.components.length === 0) diagnostics.push({ severity: "warning", code: "slide_components_missing", message: `Deck-plan slide ${slide.id} has no component names in frontmatter.`, file: slide.path, nodeId: slide.id })
943
+ if (!slide.template && slide.componentPlan.length === 0) diagnostics.push({ severity: "warning", code: "slide_component_plan_missing", message: `Deck-plan slide ${slide.id} is missing structured ## Component Plan entries.`, file: slide.path, nodeId: slide.id })
912
944
  for (const component of slide.componentPlan) {
913
945
  for (const key of ["name", "slot", "position", "purpose", "content"] as const) {
914
946
  if (!component[key] || (Array.isArray(component[key]) && component[key].length === 0)) diagnostics.push({ severity: "warning", code: "slide_component_plan_incomplete", message: `Deck-plan slide ${slide.id} has incomplete component plan entry for ${component.name || "unnamed component"}: missing ${key}.`, file: slide.path, nodeId: slide.id })
@@ -922,6 +954,10 @@ function slideDiagnostics(slide: DeckPlanSlideProjection, knownNodeIds?: Set<str
922
954
  return diagnostics
923
955
  }
924
956
 
957
+ function knownTemplateIds(): Set<string> {
958
+ return new Set(listPageTemplates().templates.map((template) => template.id))
959
+ }
960
+
925
961
  function slideSynthesisDiagnostics(slide: DeckPlanSlideProjection): DeckPlanProjectionDiagnostic[] {
926
962
  const diagnostics: DeckPlanProjectionDiagnostic[] = []
927
963
  const contentPlan = singleFileSubsection(slide.markdown, "Content Plan")
@@ -958,6 +994,7 @@ export function deckPlanDesignDiagnostics(projection: DeckPlanProjection | undef
958
994
  const components = new Set(inventory.components)
959
995
  const diagnostics: DeckPlanProjectionDiagnostic[] = []
960
996
  for (const slide of projection.slides) {
997
+ if (slide.template) continue
961
998
  if (slide.layout && !layouts.has(slide.layout)) diagnostics.push({ severity: "warning", code: "slide_layout_unknown", message: `Deck-plan slide ${slide.id} uses layout '${slide.layout}' outside the active design inventory.`, file: slide.path, nodeId: slide.id })
962
999
  for (const component of slide.componentPlan) diagnostics.push(...componentDesignDiagnostics(slide, component, inventory, components))
963
1000
  }
@@ -0,0 +1,373 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Revela Page Templates</title>
7
+ <link rel="stylesheet" href="./design.css">
8
+ </head>
9
+ <body>
10
+ <!-- revela-slides:start -->
11
+ <section class="slide template-slide" slide-qa="false" data-slide-index="1" data-slide-role="cover" data-design="built-in-preview" data-template="cover">
12
+ <div class="slide-canvas">
13
+ <div class="template-frame template-hero template-hero--cover">
14
+ <div data-template-slot="hero"><header>
15
+ <p class="template-eyebrow">Template 01 / 16</p>
16
+ <h1 class="template-title template-hero-title">cover</h1>
17
+ </header></div>
18
+
19
+ </div>
20
+ <div class="template-page-number">01</div>
21
+ </div>
22
+ </section>
23
+ <section class="slide template-slide" slide-qa="false" data-slide-index="2" data-design="built-in-preview" data-template="section-divider">
24
+ <div class="slide-canvas">
25
+ <div class="template-frame template-hero template-hero--section-divider">
26
+ <div data-template-slot="hero"><header>
27
+ <p class="template-eyebrow">Template 02 / 16</p>
28
+ <h1 class="template-title template-hero-title">section-divider</h1>
29
+ </header></div>
30
+
31
+ </div>
32
+ <div class="template-page-number">02</div>
33
+ </div>
34
+ </section>
35
+ <section class="slide template-slide" slide-qa="false" data-slide-index="3" data-slide-role="closing" data-design="built-in-preview" data-template="closing">
36
+ <div class="slide-canvas">
37
+ <div class="template-frame template-hero template-hero--closing">
38
+ <div data-template-slot="hero"><header>
39
+ <p class="template-eyebrow">Template 03 / 16</p>
40
+ <h1 class="template-title template-hero-title">closing</h1>
41
+ </header></div>
42
+
43
+ </div>
44
+ <div class="template-page-number">03</div>
45
+ </div>
46
+ </section>
47
+ <section class="slide template-slide" slide-qa="true" data-slide-index="4" data-design="built-in-preview" data-template="agenda">
48
+ <div class="slide-canvas">
49
+ <div class="template-frame">
50
+ <div class="template-body template-agenda-panel" data-template-slot="agenda">
51
+ <div class="template-agenda-inner">
52
+ <div class="template-agenda-header">
53
+ <p class="template-eyebrow">Template 04 / 16</p>
54
+ <h1 class="template-title">agenda</h1>
55
+ <p class="template-agenda-footer">Structure-First-Design</p>
56
+ </div>
57
+ <ol class="template-agenda-list" data-template-slot="agenda-list"><li class="template-agenda-item"><span>01</span><strong>Frame the decision</strong></li><li class="template-agenda-item"><span>02</span><strong>Show the evidence</strong></li><li class="template-agenda-item"><span>03</span><strong>Compare options</strong></li><li class="template-agenda-item"><span>04</span><strong>Close with action</strong></li></ol>
58
+ </div>
59
+ </div>
60
+
61
+ </div>
62
+ <div class="template-page-number">04</div>
63
+ </div>
64
+ </section>
65
+ <section class="slide template-slide" slide-qa="true" data-slide-index="5" data-design="built-in-preview" data-template="executive-summary">
66
+ <div class="slide-canvas">
67
+ <div class="template-frame">
68
+ <header>
69
+ <p class="template-eyebrow">Template 05 / 16</p>
70
+ <h1 class="template-title">executive-summary</h1>
71
+ </header><div class="template-body template-grid cols-3" data-template-slot="summary-cards"><article class="template-card"><h2>Decision is ready</h2><p>The facts support moving from discussion to selection without adding another analysis cycle.</p><figure class="template-visual-placeholder"><div class="template-visual-placeholder-frame"><span class="template-visual-placeholder-label">image / chart slot (optional)</span></div></figure></article><article class="template-card"><h2>Risk is bounded</h2><p>Known caveats are visible, named, and can be managed through rollout gates.</p><figure class="template-visual-placeholder"><div class="template-visual-placeholder-frame"><span class="template-visual-placeholder-label">image / chart slot (optional)</span></div></figure></article><article class="template-card"><h2>Next step is narrow</h2><p>A pilot decision creates more learning without overcommitting capital or team capacity.</p><figure class="template-visual-placeholder"><div class="template-visual-placeholder-frame"><span class="template-visual-placeholder-label">image / chart slot (optional)</span></div></figure></article></div>
72
+
73
+ </div>
74
+ <div class="template-page-number">05</div>
75
+ </div>
76
+ </section>
77
+ <section class="slide template-slide" slide-qa="true" data-slide-index="6" data-design="built-in-preview" data-template="problem-context">
78
+ <div class="slide-canvas">
79
+ <div class="template-frame">
80
+ <header>
81
+ <p class="template-eyebrow">Template 06 / 16</p>
82
+ <h1 class="template-title">problem-context</h1>
83
+ </header><div class="template-body template-grid cols-2"><div class="template-card" data-template-slot="context"><p>Use this template when the audience needs the situation, tension, and implication before seeing recommendations.</p></div><div class="template-card" data-template-slot="supporting-points"><ul class="template-list"><li><strong>Situation</strong> A shift has changed the operating baseline.</li><li><strong>Tension</strong> Current process cannot absorb the new variance cleanly.</li><li><strong>Implication</strong> Delay increases rework and weakens decision confidence.</li></ul></div></div>
84
+
85
+ </div>
86
+ <div class="template-page-number">06</div>
87
+ </div>
88
+ </section>
89
+ <section class="slide template-slide" slide-qa="true" data-slide-index="7" data-design="built-in-preview" data-template="key-message-evidence">
90
+ <div class="slide-canvas">
91
+ <div class="template-frame">
92
+ <header>
93
+ <p class="template-eyebrow">Template 07 / 16</p>
94
+ <h1 class="template-title">key-message-evidence</h1>
95
+ </header><div class="template-body template-grid cols-2"><div class="template-key-message-panel" data-template-slot="key-message">
96
+ <h2 class="template-key-message-kicker">Key message</h2>
97
+ <p>This key message stays large and readable, while the supporting evidence is separated into traceable slots for source-backed proof.</p>
98
+ </div><div class="template-evidence-grid" data-template-slot="evidence"><article class="template-card template-evidence-card"><h3>Evidence 1</h3><p>The generated HTML separates the key-message panel from the evidence grid, so the claim cannot collapse into generic card content.</p></article><article class="template-card template-evidence-card"><h3>Evidence 2</h3><p>Each evidence slot has a stable title and explanation area, giving the agent a predictable place for proof, caveat, or source-backed detail.</p></article><article class="template-card template-evidence-card"><h3>Evidence 3</h3><p>QA can inspect the DOM contract before visual styling, which keeps template structure from depending on a design skin.</p></article></div></div>
99
+
100
+ </div>
101
+ <div class="template-page-number">07</div>
102
+ </div>
103
+ </section>
104
+ <section class="slide template-slide" slide-qa="true" data-slide-index="8" data-design="built-in-preview" data-template="claim-supporting-visual">
105
+ <div class="slide-canvas">
106
+ <div class="template-frame">
107
+ <header>
108
+ <p class="template-eyebrow">Template 08 / 16</p>
109
+ <h1 class="template-title">claim-supporting-visual</h1>
110
+ </header><div class="template-body template-grid cols-2"><div class="template-claim-text-panel" data-template-slot="claim">
111
+ <h2 class="template-claim-text-title">A single visual should carry one argument.</h2>
112
+ <p class="template-claim-text-body">The template reserves a stable visual region while keeping explanatory copy close enough to guide interpretation.</p>
113
+ <ul class="template-list"><li><strong>Anchor</strong> State what the reader should inspect first.</li><li><strong>Callout</strong> Use short labels instead of a second paragraph.</li></ul>
114
+ </div><div class="template-chart-panel template-visual-slot-panel" data-template-slot="visual"><span class="template-visual-slot-label">image / chart slot (optional)</span></div></div>
115
+
116
+ </div>
117
+ <div class="template-page-number">08</div>
118
+ </div>
119
+ </section>
120
+ <section class="slide template-slide" slide-qa="true" data-slide-index="9" data-design="built-in-preview" data-template="metric-highlight">
121
+ <div class="slide-canvas">
122
+ <div class="template-frame">
123
+ <header>
124
+ <p class="template-eyebrow">Template 09 / 16</p>
125
+ <h1 class="template-title">metric-highlight</h1>
126
+ </header><div class="template-body"><div class="template-metric-layout template-metric-layout--insight-bottom"><div class="template-stat-grid" data-template-slot="metrics"><article class="template-card"><div class="template-stat-value">67%</div><h2>Adoption signal</h2><p>Primary number plus interpretation.</p></article><article class="template-card"><div class="template-stat-value">3x</div><h2>Review speed</h2><p>Comparison is stated beside the metric.</p></article><article class="template-card"><div class="template-stat-value">14d</div><h2>Pilot window</h2><p>Time bound keeps the ask concrete.</p></article></div><div class="template-insight-panel">
127
+ <h2 class="template-insight-title"><i class="template-insight-icon" data-lucide="scan-search" aria-hidden="true"></i><span>Read the signal</span></h2>
128
+ <p class="template-insight-body">Treat the metric row as the evidence surface, then use this panel to state the decision implication, caveat, or next reading step.</p>
129
+ </div></div></div>
130
+
131
+ </div>
132
+ <div class="template-page-number">09</div>
133
+ </div>
134
+ </section>
135
+ <section class="slide template-slide" slide-qa="true" data-slide-index="10" data-design="built-in-preview" data-template="chart-takeaways">
136
+ <div class="slide-canvas">
137
+ <div class="template-frame">
138
+ <header>
139
+ <p class="template-eyebrow">Template 10 / 16</p>
140
+ <h1 class="template-title">chart-takeaways</h1>
141
+ </header><div class="template-body template-grid template-chart-layout"><div class="template-chart-panel template-visual-slot-panel" data-template-slot="visual"><span class="template-visual-slot-label">image / chart slot (optional)</span></div><div class="template-text-panel template-chart-takeaway-panel" data-template-slot="takeaways">
142
+ <h2 class="template-text-panel-title">What to read</h2>
143
+ <div class="template-chart-takeaway-list"><section class="template-chart-takeaway-item"><h3>Trend</h3><p>Call out the movement or comparison the chart is meant to prove, including the direction and the comparison baseline.</p></section><section class="template-chart-takeaway-item"><h3>Driver</h3><p>Name the likely reason without overclaiming; separate observed movement from the interpretation or hypothesis.</p></section><section class="template-chart-takeaway-item"><h3>Decision use</h3><p>Explain how the chart changes the recommendation, what threshold matters, and what follow-up evidence would reduce risk.</p></section></div>
144
+ </div></div>
145
+
146
+ </div>
147
+ <div class="template-page-number">10</div>
148
+ </div>
149
+ </section>
150
+ <section class="slide template-slide" slide-qa="true" data-slide-index="11" data-design="built-in-preview" data-template="table-comparison">
151
+ <div class="slide-canvas">
152
+ <div class="template-frame">
153
+ <header>
154
+ <p class="template-eyebrow">Template 11 / 16</p>
155
+ <h1 class="template-title">table-comparison</h1>
156
+ </header><div class="template-body" data-template-slot="table"><div class="template-table-wrap"><table class="template-table"><thead><tr><th>Layer</th><th>Owns</th><th>Agent task</th></tr></thead><tbody><tr><td>Template</td><td>Structure and DOM contract</td><td>Select the page pattern</td></tr><tr><td>Content</td><td>Claim, evidence, caveat</td><td>Fill the meaning</td></tr><tr><td>Design</td><td>Color, type, surfaces</td><td>Skin stable classes</td></tr></tbody></table><div class="template-insight-panel">
157
+ <h2 class="template-insight-title"><i class="template-insight-icon" data-lucide="lightbulb" aria-hidden="true"></i><span>Insight</span></h2>
158
+ <p class="template-insight-body">The template layer owns structure, while the design layer owns visual treatment. This keeps agent edits bounded without freezing the final look.</p>
159
+ </div></div></div>
160
+
161
+ </div>
162
+ <div class="template-page-number">11</div>
163
+ </div>
164
+ </section>
165
+ <section class="slide template-slide" slide-qa="true" data-slide-index="12" data-design="built-in-preview" data-template="timeline-roadmap">
166
+ <div class="slide-canvas">
167
+ <div class="template-frame">
168
+ <header>
169
+ <p class="template-eyebrow">Template 12 / 16</p>
170
+ <h1 class="template-title">timeline-roadmap</h1>
171
+ </header><div class="template-body"><div class="template-timeline template-timeline--horizontal" data-template-slot="timeline" style="--timeline-count:5"><article class="template-timeline-item">
172
+ <div class="template-timeline-copy template-card">
173
+ <i class="template-insight-icon" data-lucide="scan-search" aria-hidden="true"></i>
174
+ <h3>Signal</h3>
175
+ <p>Map the baseline.</p>
176
+ </div>
177
+ <span class="template-timeline-dot" aria-hidden="true"></span>
178
+ <p class="template-timeline-date">2022</p>
179
+ </article><article class="template-timeline-item">
180
+ <div class="template-timeline-copy template-card">
181
+ <i class="template-insight-icon" data-lucide="scan-search" aria-hidden="true"></i>
182
+ <h3>Proof</h3>
183
+ <p>Validate the evidence threshold.</p>
184
+ </div>
185
+ <span class="template-timeline-dot" aria-hidden="true"></span>
186
+ <p class="template-timeline-date">2023</p>
187
+ </article><article class="template-timeline-item">
188
+ <div class="template-timeline-copy template-card">
189
+ <i class="template-insight-icon" data-lucide="scan-search" aria-hidden="true"></i>
190
+ <h3>Inflection</h3>
191
+ <p>Use the pivotal moment to frame the shift.</p>
192
+ </div>
193
+ <span class="template-timeline-dot" aria-hidden="true"></span>
194
+ <p class="template-timeline-date">2024</p>
195
+ </article><article class="template-timeline-item template-timeline-item--highlight">
196
+ <div class="template-timeline-copy template-card">
197
+ <i class="template-insight-icon" data-lucide="scan-search" aria-hidden="true"></i>
198
+ <h3>Scale</h3>
199
+ <p>Use a taller card for the highlighted milestone.</p>
200
+ </div>
201
+ <span class="template-timeline-dot" aria-hidden="true"></span>
202
+ <p class="template-timeline-date">2025</p>
203
+ </article><article class="template-timeline-item">
204
+ <div class="template-timeline-copy template-card">
205
+ <i class="template-insight-icon" data-lucide="scan-search" aria-hidden="true"></i>
206
+ <h3>Decision</h3>
207
+ <p>Commit to the next path.</p>
208
+ </div>
209
+ <span class="template-timeline-dot" aria-hidden="true"></span>
210
+ <p class="template-timeline-date">2026</p>
211
+ </article></div></div>
212
+
213
+ </div>
214
+ <div class="template-page-number">12</div>
215
+ </div>
216
+ </section>
217
+ <section class="slide template-slide" slide-qa="true" data-slide-index="13" data-design="built-in-preview" data-template="timeline-roadmap">
218
+ <div class="slide-canvas">
219
+ <div class="template-frame">
220
+ <header>
221
+ <p class="template-eyebrow">Template 13 / 16</p>
222
+ <h1 class="template-title">timeline-roadmap-vertical</h1>
223
+ </header><div class="template-body"><div class="template-timeline-layout template-timeline-layout--left"><div class="template-side-panel template-text-panel" data-template-slot="insight"><h2 class="template-side-panel-title template-text-panel-title">Reading the journey</h2><p class="template-side-panel-body template-text-panel-body">The timeline should show sequence and decision rhythm, while the side panel explains why the milestones matter.</p></div><div class="template-timeline template-timeline--vertical" data-template-slot="timeline" style="--timeline-count:4"><article class="template-timeline-item">
224
+ <span class="template-timeline-dot" aria-hidden="true"></span>
225
+ <div class="template-timeline-copy">
226
+ <p class="template-timeline-date">Mar 2019</p>
227
+ <h3>Launch</h3>
228
+ <p>Baseline mapping.</p>
229
+ </div>
230
+ </article><article class="template-timeline-item">
231
+ <span class="template-timeline-dot" aria-hidden="true"></span>
232
+ <div class="template-timeline-copy">
233
+ <p class="template-timeline-date">Nov 2019</p>
234
+ <h3>Audit</h3>
235
+ <p>Evidence sprint.</p>
236
+ </div>
237
+ </article><article class="template-timeline-item">
238
+ <span class="template-timeline-dot" aria-hidden="true"></span>
239
+ <div class="template-timeline-copy">
240
+ <p class="template-timeline-date">May 2020</p>
241
+ <h3>Scale</h3>
242
+ <p>Operating cadence.</p>
243
+ </div>
244
+ </article><article class="template-timeline-item">
245
+ <span class="template-timeline-dot" aria-hidden="true"></span>
246
+ <div class="template-timeline-copy">
247
+ <p class="template-timeline-date">Feb 2021</p>
248
+ <h3>Review</h3>
249
+ <p>QA before export.</p>
250
+ </div>
251
+ </article></div></div></div>
252
+
253
+ </div>
254
+ <div class="template-page-number">13</div>
255
+ </div>
256
+ </section>
257
+ <section class="slide template-slide" slide-qa="true" data-slide-index="14" data-design="built-in-preview" data-template="process-steps">
258
+ <div class="slide-canvas">
259
+ <div class="template-frame">
260
+ <header>
261
+ <p class="template-eyebrow">Template 14 / 16</p>
262
+ <h1 class="template-title">process-steps</h1>
263
+ </header><div class="template-body"><div class="template-steps" data-template-slot="steps"><article class="template-card"><div class="template-step-number">1</div><h2>Choose</h2><p>Select the page template that matches the communication job.</p><figure class="template-visual-placeholder"><div class="template-visual-placeholder-frame"><span class="template-visual-placeholder-label">image / chart slot (optional)</span></div></figure></article><article class="template-card"><div class="template-step-number">2</div><h2>Fill</h2><p>Provide only the content fields the template needs.</p><figure class="template-visual-placeholder"><div class="template-visual-placeholder-frame"><span class="template-visual-placeholder-label">image / chart slot (optional)</span></div></figure></article><article class="template-card"><div class="template-step-number">3</div><h2>Style</h2><p>Let the active design control type, color, and surfaces.</p><figure class="template-visual-placeholder"><div class="template-visual-placeholder-frame"><span class="template-visual-placeholder-label">image / chart slot (optional)</span></div></figure></article><article class="template-card"><div class="template-step-number">4</div><h2>QA</h2><p>Run contract and visual checks before export.</p><figure class="template-visual-placeholder"><div class="template-visual-placeholder-frame"><span class="template-visual-placeholder-label">image / chart slot (optional)</span></div></figure></article></div></div>
264
+
265
+ </div>
266
+ <div class="template-page-number">14</div>
267
+ </div>
268
+ </section>
269
+ <section class="slide template-slide" slide-qa="true" data-slide-index="15" data-design="built-in-preview" data-template="recommendation-decision">
270
+ <div class="slide-canvas">
271
+ <div class="template-frame">
272
+ <header>
273
+ <p class="template-eyebrow">Template 15 / 16</p>
274
+ <h1 class="template-title">recommendation-decision</h1>
275
+ </header><div class="template-body template-grid cols-3"><div class="template-card" data-template-slot="recommendation"><h2>Recommendation</h2><p>Adopt page templates as the structural layer, with designs remaining user-customizable.</p><figure class="template-image-card"><div class="template-image-frame"><img src="./assets/card-lens.jpg" alt="Lucent design asset"></div><figcaption class="template-image-caption">Design asset example</figcaption></figure></div><div data-template-slot="rationale"><article class="template-card"><h3>Rationale</h3><p>This keeps generation reliable while leaving style expressive and replaceable.</p></article></div><div class="template-card" data-template-slot="next-steps"><h2>Next steps</h2><ol class="template-list"><li><strong>Pilot</strong> Use the built-in preview to tune every template.</li><li><strong>Validate</strong> Promote only contracts that pass QA and browser review.</li><li><strong>Ship</strong> Document the add-slide workflow for agents.</li></ol></div></div>
276
+
277
+ </div>
278
+ <div class="template-page-number">15</div>
279
+ </div>
280
+ </section>
281
+ <section class="slide template-slide" slide-qa="true" data-slide-index="16" data-design="built-in-preview" data-template="risks-tradeoffs">
282
+ <div class="slide-canvas">
283
+ <div class="template-frame">
284
+ <header>
285
+ <p class="template-eyebrow">Template 16 / 16</p>
286
+ <h1 class="template-title">risks-tradeoffs</h1>
287
+ </header><div class="template-body template-grid cols-3" data-template-slot="risks"><article class="template-card"><h3>Constraint</h3><p>Templates can over-constrain if they become decorative presets instead of communication jobs.</p><figure class="template-image-card"><div class="template-image-frame"><img src="./assets/report-visual.jpg" alt="Report visual"></div><figcaption class="template-image-caption">Design asset example</figcaption></figure></article><article class="template-card"><h3>Mitigation</h3><p>Keep bounded HTML edit flow after scaffold insertion so agents can improve the page directly.</p></article><article class="template-card"><h3>Tradeoff</h3><p>More structure improves QA, but only if template contracts stay small and semantic.</p></article></div>
288
+
289
+ </div>
290
+ <div class="template-page-number">16</div>
291
+ </div>
292
+ </section>
293
+ <!-- revela-slides:end -->
294
+ <script>
295
+ class SlidePresentation {
296
+ constructor() {
297
+ this.slides = document.querySelectorAll('.slide');
298
+ this.currentSlide = 0;
299
+ this.setupScaling();
300
+ this.setupIntersectionObserver();
301
+ this.setupKeyboardNav();
302
+ this.setupTouchNav();
303
+ this.setupMouseWheel();
304
+ }
305
+ setupScaling() {
306
+ const canvases = document.querySelectorAll('.slide-canvas');
307
+ const BASE_W = 1920;
308
+ const BASE_H = 1080;
309
+ const update = () => {
310
+ const scale = Math.min(window.innerWidth / BASE_W, window.innerHeight / BASE_H);
311
+ canvases.forEach((canvas) => { canvas.style.transform = `scale(${scale})`; });
312
+ };
313
+ window.addEventListener('resize', update);
314
+ update();
315
+ }
316
+ setupIntersectionObserver() {
317
+ const observer = new IntersectionObserver((entries) => {
318
+ entries.forEach((entry) => {
319
+ if (entry.isIntersecting) entry.target.querySelectorAll('.reveal').forEach((el) => el.classList.add('visible'));
320
+ });
321
+ }, { threshold: 0.2 });
322
+ this.slides.forEach((slide) => observer.observe(slide));
323
+ }
324
+ setupKeyboardNav() {
325
+ document.addEventListener('keydown', (event) => {
326
+ if (['ArrowDown', 'ArrowRight', ' ', 'PageDown'].includes(event.key)) { event.preventDefault(); this.goTo(this.currentSlide + 1); }
327
+ else if (['ArrowUp', 'ArrowLeft', 'PageUp'].includes(event.key)) { event.preventDefault(); this.goTo(this.currentSlide - 1); }
328
+ });
329
+ }
330
+ setupTouchNav() {
331
+ let startY = 0;
332
+ document.addEventListener('touchstart', (event) => { startY = event.touches[0].clientY; }, { passive: true });
333
+ document.addEventListener('touchend', (event) => {
334
+ const deltaY = startY - event.changedTouches[0].clientY;
335
+ if (Math.abs(deltaY) > 40) this.goTo(this.currentSlide + (deltaY > 0 ? 1 : -1));
336
+ }, { passive: true });
337
+ }
338
+ setupMouseWheel() {
339
+ let last = 0;
340
+ document.addEventListener('wheel', (event) => {
341
+ const now = Date.now();
342
+ if (now - last < 800) return;
343
+ last = now;
344
+ this.goTo(this.currentSlide + (event.deltaY > 0 ? 1 : -1));
345
+ }, { passive: true });
346
+ }
347
+ goTo(index) {
348
+ const clamped = Math.max(0, Math.min(this.slides.length - 1, index));
349
+ this.slides[clamped].scrollIntoView({ behavior: 'smooth' });
350
+ this.currentSlide = clamped;
351
+ }
352
+ }
353
+ if (document.querySelector(".slide")) { new SlidePresentation(); }
354
+ </script>
355
+ <script>
356
+ function revelaRenderLucideIcons() {
357
+ var icons = {
358
+ lightbulb: '<svg xmlns="http://www.w3.org/2000/svg" class="template-insight-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 14c.2-1 .7-1.7 1.5-2.5A5.5 5.5 0 0 0 18 7.5C18 4.5 15.5 2 12 2S6 4.5 6 7.5c0 1.5.6 2.9 1.5 4 .8.8 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/></svg>',
359
+ 'scan-search': '<svg xmlns="http://www.w3.org/2000/svg" class="template-insight-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 3H5a2 2 0 0 0-2 2v2"/><path d="M17 3h2a2 2 0 0 1 2 2v2"/><path d="M7 21H5a2 2 0 0 1-2-2v-2"/><path d="M14 14l4 4"/><circle cx="11" cy="11" r="3"/></svg>'
360
+ };
361
+ document.querySelectorAll("[data-lucide]").forEach(function (node) {
362
+ var name = node.getAttribute("data-lucide") || "lightbulb";
363
+ var svg = icons[name] || icons.lightbulb;
364
+ var wrapper = document.createElement("span");
365
+ wrapper.innerHTML = svg;
366
+ var next = wrapper.firstElementChild;
367
+ if (next) node.replaceWith(next);
368
+ });
369
+ }
370
+ revelaRenderLucideIcons();
371
+ </script>
372
+ </body>
373
+ </html>
@@ -0,0 +1,2 @@
1
+ export { formatPageTemplateContractReport, validatePageTemplateContracts } from "./render"
2
+
@@ -0,0 +1,2 @@
1
+ export { templateDeckCss } from "./render"
2
+
@@ -0,0 +1,41 @@
1
+ import { getPageTemplateVocabulary, listPageTemplateVocabulary, type PageTemplateVocabulary } from "./vocabulary"
2
+
3
+ export interface PageTemplateFoundation {
4
+ templateId: string
5
+ html: string
6
+ cssHooks: string[]
7
+ slots: PageTemplateVocabulary["slots"]
8
+ designNotes: string[]
9
+ contractNotes: string[]
10
+ }
11
+
12
+ export function listPageTemplateFoundations(): PageTemplateFoundation[] {
13
+ return listPageTemplateVocabulary().map((item) => getPageTemplateFoundation(item.templateId))
14
+ }
15
+
16
+ export function getPageTemplateFoundation(templateId: string): PageTemplateFoundation {
17
+ const vocabulary = getPageTemplateVocabulary(templateId)
18
+ return {
19
+ templateId: vocabulary.templateId,
20
+ html: foundationHtml(vocabulary),
21
+ cssHooks: [...new Set([...vocabulary.rootClasses, ...vocabulary.requiredClasses, ...vocabulary.optionalClasses])],
22
+ slots: vocabulary.slots,
23
+ designNotes: [
24
+ "Use this foundation as the custom design starting point; style classes, do not remove structural classes.",
25
+ "Prefer overriding visual treatment in design CSS while keeping data-template-slot attributes intact.",
26
+ "Visual slots may become image, chart, table, or diagram containers when the semantic container remains clear.",
27
+ ],
28
+ contractNotes: vocabulary.contractNotes,
29
+ }
30
+ }
31
+
32
+ function foundationHtml(vocabulary: PageTemplateVocabulary): string {
33
+ const slotHtml = vocabulary.slots.map((slot) => ` <div data-template-slot="${slot.name}">${slot.name}</div>`).join("\n")
34
+ return `<section class="slide template-slide" data-template="${vocabulary.templateId}">
35
+ <div class="slide-canvas">
36
+ <div class="template-frame">
37
+ ${slotHtml}
38
+ </div>
39
+ </div>
40
+ </section>`
41
+ }
@@ -0,0 +1,6 @@
1
+ export * from "./contracts"
2
+ export * from "./css"
3
+ export * from "./foundation"
4
+ export * from "./registry"
5
+ export * from "./render"
6
+ export * from "./vocabulary"
@@ -0,0 +1,3 @@
1
+ export { listPageTemplates } from "./render"
2
+ export type { PageTemplateDefinition, PageTemplateField, PageTemplateStatus } from "./render"
3
+