@cyber-dash-tech/revela 0.18.16 → 0.19.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 (98) hide show
  1. package/README.md +45 -31
  2. package/README.zh-CN.md +45 -31
  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 +76 -0
  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 +14 -0
  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 +14 -0
  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 +18 -0
  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 +13 -25
  53. package/lib/commands/designs-preview.ts +3 -8
  54. package/lib/deck-html/foundation.ts +8 -8
  55. package/lib/design/designs.ts +317 -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 +5 -5
  83. package/lib/qa/artifact.ts +66 -1
  84. package/lib/qa/compliance.ts +5 -1
  85. package/lib/runtime/index.ts +99 -3
  86. package/package.json +7 -15
  87. package/plugins/revela/.codex-plugin/plugin.json +1 -1
  88. package/plugins/revela/hooks/revela_guard.ts +35 -0
  89. package/plugins/revela/hooks/revela_post_write_notice.ts +4 -4
  90. package/plugins/revela/mcp/revela-server.ts +104 -6
  91. package/plugins/revela/skills/revela/SKILL.md +1 -1
  92. package/plugins/revela/skills/revela-design/SKILL.md +22 -16
  93. package/plugins/revela/skills/revela-helper/SKILL.md +1 -1
  94. package/plugins/revela/skills/revela-make-deck/SKILL.md +25 -16
  95. package/designs/lucent/preview.html +0 -529
  96. package/designs/monet/preview.html +0 -190
  97. package/designs/starter/preview.html +0 -335
  98. package/designs/summit/preview.html +0 -186
@@ -0,0 +1,4 @@
1
+ import { templateModule } from "./shared"
2
+
3
+ export const agendaTemplate = templateModule("agenda")
4
+
@@ -0,0 +1,4 @@
1
+ import { templateModule } from "./shared"
2
+
3
+ export const chartTakeawaysTemplate = templateModule("chart-takeaways")
4
+
@@ -0,0 +1,4 @@
1
+ import { templateModule } from "./shared"
2
+
3
+ export const claimSupportingVisualTemplate = templateModule("claim-supporting-visual")
4
+
@@ -0,0 +1,4 @@
1
+ import { templateModule } from "./shared"
2
+
3
+ export const closingTemplate = templateModule("closing")
4
+
@@ -0,0 +1,4 @@
1
+ import { templateModule } from "./shared"
2
+
3
+ export const coverTemplate = templateModule("cover")
4
+
@@ -0,0 +1,4 @@
1
+ import { templateModule } from "./shared"
2
+
3
+ export const executiveSummaryTemplate = templateModule("executive-summary")
4
+
@@ -0,0 +1,19 @@
1
+ import { listPageTemplateVocabulary } from "../vocabulary"
2
+
3
+ export const BUILTIN_PAGE_TEMPLATE_IDS = listPageTemplateVocabulary().map((template) => template.templateId)
4
+
5
+ export * from "./agenda"
6
+ export * from "./chart-takeaways"
7
+ export * from "./claim-supporting-visual"
8
+ export * from "./closing"
9
+ export * from "./cover"
10
+ export * from "./executive-summary"
11
+ export * from "./key-message-evidence"
12
+ export * from "./metric-highlight"
13
+ export * from "./problem-context"
14
+ export * from "./process-steps"
15
+ export * from "./recommendation-decision"
16
+ export * from "./risks-tradeoffs"
17
+ export * from "./section-divider"
18
+ export * from "./table-comparison"
19
+ export * from "./timeline-roadmap"
@@ -0,0 +1,4 @@
1
+ import { templateModule } from "./shared"
2
+
3
+ export const keyMessageEvidenceTemplate = templateModule("key-message-evidence")
4
+
@@ -0,0 +1,4 @@
1
+ import { templateModule } from "./shared"
2
+
3
+ export const metricHighlightTemplate = templateModule("metric-highlight")
4
+
@@ -0,0 +1,4 @@
1
+ import { templateModule } from "./shared"
2
+
3
+ export const problemContextTemplate = templateModule("problem-context")
4
+
@@ -0,0 +1,4 @@
1
+ import { templateModule } from "./shared"
2
+
3
+ export const processStepsTemplate = templateModule("process-steps")
4
+
@@ -0,0 +1,4 @@
1
+ import { templateModule } from "./shared"
2
+
3
+ export const recommendationDecisionTemplate = templateModule("recommendation-decision")
4
+
@@ -0,0 +1,4 @@
1
+ import { templateModule } from "./shared"
2
+
3
+ export const risksTradeoffsTemplate = templateModule("risks-tradeoffs")
4
+
@@ -0,0 +1,4 @@
1
+ import { templateModule } from "./shared"
2
+
3
+ export const sectionDividerTemplate = templateModule("section-divider")
4
+
@@ -0,0 +1,11 @@
1
+ import { getPageTemplateFoundation } from "../foundation"
2
+ import { getPageTemplateVocabulary } from "../vocabulary"
3
+
4
+ export function templateModule(templateId: string) {
5
+ return {
6
+ templateId,
7
+ foundation: () => getPageTemplateFoundation(templateId),
8
+ vocabulary: () => getPageTemplateVocabulary(templateId),
9
+ }
10
+ }
11
+
@@ -0,0 +1,4 @@
1
+ import { templateModule } from "./shared"
2
+
3
+ export const tableComparisonTemplate = templateModule("table-comparison")
4
+
@@ -0,0 +1,4 @@
1
+ import { templateModule } from "./shared"
2
+
3
+ export const timelineRoadmapTemplate = templateModule("timeline-roadmap")
4
+
@@ -0,0 +1,158 @@
1
+ export interface PageTemplateSlotVocabulary {
2
+ name: string
3
+ required: boolean
4
+ editable: boolean
5
+ replaceable: boolean
6
+ description: string
7
+ }
8
+
9
+ export interface PageTemplateVocabulary {
10
+ templateId: string
11
+ rootClasses: string[]
12
+ requiredClasses: string[]
13
+ optionalClasses: string[]
14
+ slots: PageTemplateSlotVocabulary[]
15
+ editableSlots: string[]
16
+ replaceableSlots: string[]
17
+ contractNotes: string[]
18
+ }
19
+
20
+ const sharedClasses = [
21
+ "template-slide",
22
+ "template-frame",
23
+ "template-eyebrow",
24
+ "template-title",
25
+ "template-body",
26
+ "template-grid",
27
+ "template-chart-layout",
28
+ "cols-2",
29
+ "cols-3",
30
+ "cols-4",
31
+ "template-card",
32
+ "template-list",
33
+ "template-hero",
34
+ "template-hero-title",
35
+ "template-hero--cover",
36
+ "template-hero--section-divider",
37
+ "template-hero--closing",
38
+ "template-frame--catalog",
39
+ "template-page-number",
40
+ "template-image-card",
41
+ "template-image-frame",
42
+ "template-image-caption",
43
+ "template-visual-placeholder",
44
+ "template-visual-placeholder-frame",
45
+ "template-visual-placeholder-label",
46
+ ]
47
+
48
+ export const PAGE_TEMPLATE_VOCABULARY: PageTemplateVocabulary[] = [
49
+ vocab("cover", ["template-hero"], ["hero"], ["hero"], ["Cover, divider, and closing templates use the hero frame; keep title hierarchy visible."]),
50
+ vocab("section-divider", ["template-hero"], ["hero"], ["hero"], ["Section divider uses the same hero-safe structure as cover."]),
51
+ vocab("closing", ["template-hero"], ["hero"], ["hero"], ["Closing uses the same hero-safe structure as cover."]),
52
+ vocab("agenda", ["template-agenda-panel"], ["agenda", "agenda-list"], ["agenda", "agenda-list"], ["Agenda numbers must remain in DOM order."]),
53
+ vocab("executive-summary", ["template-card"], ["summary-cards"], ["summary-cards"], ["Cards are editable; visual placeholders are optional and may become image/chart slots."]),
54
+ vocab("problem-context", ["template-card"], ["context", "supporting-points"], ["context", "supporting-points"], ["Context should stay separate from supporting bullets."]),
55
+ vocab("key-message-evidence", ["template-key-message-panel", "template-evidence-grid"], ["key-message", "evidence"], ["key-message", "evidence"], ["Key message and evidence regions must remain distinct."]),
56
+ vocab("claim-supporting-visual", ["template-claim-text-panel", "template-visual-slot-panel"], ["claim", "visual"], ["claim", "visual"], ["Visual slot may be replaced by image, chart, table, or diagram container."]),
57
+ vocab("metric-highlight", ["template-stat-grid"], ["metrics"], ["metrics", "insight"], ["Metric values should remain visible outside prose."]),
58
+ vocab("chart-takeaways", ["template-chart-panel", "template-chart-takeaway-panel"], ["visual", "takeaways"], ["visual", "takeaways"], ["Chart/image slot and takeaway text panel must both remain present."]),
59
+ vocab("table-comparison", ["template-table-wrap", "template-table"], ["table"], ["table", "insight"], ["Table headers and body should remain structured, not prose-only."]),
60
+ vocab("timeline-roadmap", ["template-timeline", "template-timeline-item", "template-timeline-dot", "template-timeline-copy", "template-insight-icon"], ["timeline"], ["timeline", "insight"], ["Each timeline item must keep dot and copy as sibling anchors inside one item.", "Horizontal timeline cards reuse .template-card; highlight uses the item modifier."]),
61
+ vocab("process-steps", ["template-steps", "template-step-number"], ["steps"], ["steps"], ["Steps should remain ordered in DOM order."]),
62
+ vocab("recommendation-decision", ["template-card"], ["recommendation", "rationale", "next-steps"], ["recommendation", "rationale", "next-steps"], ["Keep recommendation, rationale, and next steps separate."]),
63
+ vocab("risks-tradeoffs", ["template-card"], ["risks"], ["risks"], ["Risk/tradeoff cards should name uncertainty explicitly."]),
64
+ ]
65
+
66
+ const additionalClasses = [
67
+ "template-key-message-panel",
68
+ "template-key-message-kicker",
69
+ "template-evidence-grid",
70
+ "template-evidence-card",
71
+ "template-claim-text-panel",
72
+ "template-claim-text-title",
73
+ "template-claim-text-body",
74
+ "template-agenda-panel",
75
+ "template-agenda-inner",
76
+ "template-agenda-header",
77
+ "template-agenda-footer",
78
+ "template-agenda-list",
79
+ "template-agenda-item",
80
+ "template-stat-grid",
81
+ "template-stat-value",
82
+ "template-metric-layout",
83
+ "template-metric-layout--insight-top",
84
+ "template-metric-layout--insight-bottom",
85
+ "template-chart-panel",
86
+ "template-chart-placeholder",
87
+ "template-visual-slot-panel",
88
+ "template-visual-slot-label",
89
+ "template-chart-takeaway-panel",
90
+ "template-chart-takeaway-list",
91
+ "template-chart-takeaway-item",
92
+ "template-bar",
93
+ "template-table",
94
+ "template-table-wrap",
95
+ "template-side-panel",
96
+ "template-side-panel-title",
97
+ "template-side-panel-body",
98
+ "template-side-panel--left",
99
+ "template-side-panel--right",
100
+ "template-text-panel",
101
+ "template-text-panel-title",
102
+ "template-text-panel-body",
103
+ "template-insight-panel",
104
+ "template-insight-title",
105
+ "template-insight-icon",
106
+ "template-insight-body",
107
+ "template-timeline",
108
+ "template-timeline-layout",
109
+ "template-timeline-layout--left",
110
+ "template-timeline-layout--right",
111
+ "template-timeline--horizontal",
112
+ "template-timeline--vertical",
113
+ "template-timeline-item",
114
+ "template-timeline-item--highlight",
115
+ "template-timeline-dot",
116
+ "template-timeline-copy",
117
+ "template-timeline-date",
118
+ "template-steps",
119
+ "template-step-number",
120
+ "template-catalog-panel",
121
+ "template-catalog-kicker",
122
+ "template-catalog-title",
123
+ "template-catalog-grid",
124
+ "template-catalog-section",
125
+ "template-catalog-list",
126
+ ]
127
+
128
+ export const PAGE_TEMPLATE_CLASSES = [...new Set([...sharedClasses, ...additionalClasses])]
129
+
130
+ export function listPageTemplateVocabulary(): PageTemplateVocabulary[] {
131
+ return PAGE_TEMPLATE_VOCABULARY
132
+ }
133
+
134
+ export function getPageTemplateVocabulary(templateId: string): PageTemplateVocabulary {
135
+ const vocabulary = PAGE_TEMPLATE_VOCABULARY.find((item) => item.templateId === templateId)
136
+ if (!vocabulary) throw new Error(`Unknown page template vocabulary: ${templateId}`)
137
+ return vocabulary
138
+ }
139
+
140
+ function vocab(templateId: string, requiredClasses: string[], slotNames: string[], replaceableSlots: string[], contractNotes: string[]): PageTemplateVocabulary {
141
+ const slots = slotNames.map((name) => ({
142
+ name,
143
+ required: true,
144
+ editable: true,
145
+ replaceable: replaceableSlots.includes(name),
146
+ description: `${templateId} ${name} slot.`,
147
+ }))
148
+ return {
149
+ templateId,
150
+ rootClasses: ["template-slide", "template-frame", ...requiredClasses.slice(0, 1)],
151
+ requiredClasses,
152
+ optionalClasses: [],
153
+ slots,
154
+ editableSlots: slotNames,
155
+ replaceableSlots,
156
+ contractNotes,
157
+ }
158
+ }
@@ -82,12 +82,12 @@ export function buildPrompt(optionsOrDesignName?: BuildPromptOptions | string, l
82
82
  // Layer 1 — core skill for the selected prompt mode.
83
83
  const coreSkill = readFileSync(mode === "deck-render" ? SKILL_MD_PATH : NARRATIVE_SKILL_MD_PATH, "utf-8")
84
84
 
85
- // Check for preview.html
85
+ // Check for CSS-native design styling.
86
86
  const designDir = join(DESIGNS_DIR, design)
87
- const hasPreview = existsSync(join(designDir, "preview.html"))
88
- const previewLine = hasPreview
89
- ? "<!-- - preview.htmlcanonical visual reference (read this before generating slides) -->"
90
- : "<!-- - (no preview.html for this design) -->"
87
+ const hasDesignCss = existsSync(join(designDir, "design.css"))
88
+ const previewLine = hasDesignCss
89
+ ? "<!-- - design.cssexecutable design styling; generated previews use the built-in page template fixture -->"
90
+ : "<!-- - (no design.css for this design; compatibility CSS may be generated from DESIGN.md) -->"
91
91
 
92
92
  // Layer 2 — DOMAIN.md skill text (narrative mode only). Deck-render mode
93
93
  // renders the approved canonical narrative and must not re-interpret domain
@@ -4,8 +4,10 @@ import type { DesignClassVocabulary, DesignComponentContract } from "../design/d
4
4
  import { formatReport, runQA } from "./index"
5
5
  import { runComplianceQA } from "./compliance"
6
6
  import { runComponentContractQA } from "./component-contracts"
7
+ import { formatPageTemplateContractReport, validatePageTemplateContracts } from "../page-templates"
7
8
  import type { QAReport } from "./checks"
8
- import { basename, dirname } from "path"
9
+ import { existsSync, readFileSync } from "fs"
10
+ import { basename, dirname, resolve } from "path"
9
11
 
10
12
  export interface ArtifactQAReport {
11
13
  file: string
@@ -43,6 +45,16 @@ export async function runArtifactQA(input: {
43
45
  sections.push("**[deck HTML contract]**\n\n" + formatDeckHtmlContractReport(contract))
44
46
  }
45
47
 
48
+ const designCss = validateLinkedDesignCss(input.filePath)
49
+ if (designCss.errors.length > 0 || designCss.warnings.length > 0) {
50
+ hardErrorCount += designCss.errors.length
51
+ warningCount += designCss.warnings.length
52
+ sections.push("**[design CSS snapshot]**\n\n" + [
53
+ ...designCss.errors.map((message) => `- ERROR: ${message}`),
54
+ ...designCss.warnings.map((message) => `- WARNING: ${message}`),
55
+ ].join("\n"))
56
+ }
57
+
46
58
  if (shouldRunArtifactCompliance(input.filePath)) {
47
59
  const compliance = runComplianceQA(input.filePath, input.vocabulary)
48
60
  const complianceErrors = hardErrors(compliance)
@@ -64,6 +76,13 @@ export async function runArtifactQA(input: {
64
76
  }
65
77
  }
66
78
 
79
+ const templateContracts = validatePageTemplateContracts(input.filePath)
80
+ if (templateContracts.issues.length > 0) {
81
+ hardErrorCount += templateContracts.issues.filter((issue) => issue.severity === "error").length
82
+ warningCount += templateContracts.issues.filter((issue) => issue.severity === "warning").length
83
+ sections.push("**[page template contracts]**\n\n" + formatPageTemplateContractReport(templateContracts))
84
+ }
85
+
67
86
  try {
68
87
  const browser = await runQA(input.filePath)
69
88
  const browserErrors = hardErrors(browser)
@@ -86,6 +105,52 @@ export async function runArtifactQA(input: {
86
105
  }
87
106
  }
88
107
 
108
+ function validateLinkedDesignCss(filePath: string): { errors: string[]; warnings: string[] } {
109
+ if (isDesignPreviewFile(filePath)) return { errors: [], warnings: [] }
110
+ const errors: string[] = []
111
+ const warnings: string[] = []
112
+ let html = ""
113
+ try {
114
+ html = readFileSync(filePath, "utf-8")
115
+ } catch {
116
+ return { errors, warnings }
117
+ }
118
+ const hrefs = [...html.matchAll(/<link\b[^>]*rel=["']stylesheet["'][^>]*href=["']([^"']*design\.css)["'][^>]*>/gi)].map((match) => match[1])
119
+ if (hrefs.length === 0) {
120
+ warnings.push("Deck does not reference a design.css snapshot.")
121
+ return { errors, warnings }
122
+ }
123
+ for (const href of hrefs) {
124
+ if (/^[a-z][a-z0-9+.-]*:/i.test(href)) continue
125
+ const cssPath = resolve(dirname(filePath), href)
126
+ if (!existsSync(cssPath)) {
127
+ errors.push(`Linked design CSS is missing: ${href}`)
128
+ continue
129
+ }
130
+ const css = readFileSync(cssPath, "utf-8")
131
+ for (const asset of cssAssetUrls(css)) {
132
+ const assetPath = resolve(dirname(cssPath), asset)
133
+ if (!existsSync(assetPath)) errors.push(`Linked design CSS references missing asset: ${href} -> ${asset}`)
134
+ }
135
+ }
136
+ return { errors, warnings }
137
+ }
138
+
139
+ function cssAssetUrls(css: string): string[] {
140
+ const urls: string[] = []
141
+ const seen = new Set<string>()
142
+ const urlRe = /url\(\s*["']?([^"')]+)["']?\s*\)/gi
143
+ let match: RegExpExecArray | null
144
+ while ((match = urlRe.exec(css)) !== null) {
145
+ const raw = match[1].trim()
146
+ if (!raw || raw.startsWith("data:") || /^[a-z][a-z0-9+.-]*:/i.test(raw) || raw.startsWith("#")) continue
147
+ if (seen.has(raw)) continue
148
+ seen.add(raw)
149
+ urls.push(raw)
150
+ }
151
+ return urls
152
+ }
153
+
89
154
  function componentContractsForArtifact(filePath: string): DesignComponentContract[] {
90
155
  const designName = designNameFromPreviewPath(filePath)
91
156
  try {
@@ -103,9 +103,13 @@ function extractCssDefinedClasses(html: string): ClassUse[] {
103
103
  while ((styleMatch = styleRe.exec(html)) !== null) {
104
104
  const styleBody = styleMatch[1]
105
105
  const bodyOffset = styleMatch.index + styleMatch[0].indexOf(styleBody)
106
+ const scanBody = styleBody
107
+ .replace(/url\([^)]*\)/gi, "url()")
108
+ .replace(/"[^"]*"/g, '""')
109
+ .replace(/'[^']*'/g, "''")
106
110
  classRe.lastIndex = 0
107
111
  let classMatch: RegExpExecArray | null
108
- while ((classMatch = classRe.exec(styleBody)) !== null) {
112
+ while ((classMatch = classRe.exec(scanBody)) !== null) {
109
113
  const cls = classMatch[1]
110
114
  if (classes.has(cls)) continue
111
115
 
@@ -15,6 +15,7 @@ import {
15
15
  installDesignDraftPackage,
16
16
  listDesignAssets,
17
17
  listDesigns,
18
+ materializeDesignPreview,
18
19
  packDesignPackage,
19
20
  seedBuiltinDesigns,
20
21
  validateDesignDraftPackage,
@@ -30,6 +31,7 @@ import { runNarrativeMarkdownQa, type MarkdownQaOptions } from "../narrative-vau
30
31
  import { formatArtifactQaUserNotice, formatMarkdownQaUserNotice } from "../hook-notifications"
31
32
  import { deckPlanDesignDiagnostics, readDeckPlanArtifact, upsertDeckPlanSlideArtifact, type DeckPlanSlideUpsertInput } from "../narrative-state/deck-plan-artifact"
32
33
  import { extractDesignClasses } from "../design/designs"
34
+ import { addTemplateScaffold as addPageTemplateScaffold, addTemplateSlide as addPageTemplateSlide, getPageTemplateFoundation, getPageTemplateVocabulary, listPageTemplates as listBuiltinPageTemplates, renderTemplateScaffold as renderPageTemplateScaffold, renderTemplateSlide as renderPageTemplateSlide } from "../page-templates"
33
35
  import { recordRenderedArtifact, workspaceRelative } from "../workspace-state/rendered-artifacts"
34
36
  import { existingWorkspaceMetaPath, workspaceMetaPath } from "../workspace-meta"
35
37
  import { checkMaterialIntake, extractMaterial, materialIntakeNoticeForCommand, prepareLocalMaterials, recordMaterialReview } from "../material-intake"
@@ -84,17 +86,49 @@ export interface RuntimeDeckPlanSlideUpsertInput extends RuntimeWorkspaceInput,
84
86
  designName?: string
85
87
  }
86
88
 
89
+ export interface RuntimeTemplateSlideInput extends RuntimeWorkspaceInput {
90
+ templateId: string
91
+ slideIndex: number
92
+ content: Record<string, any>
93
+ designName?: string
94
+ }
95
+
96
+ export interface RuntimeTemplateSlideAddInput extends RuntimeTemplateSlideInput {
97
+ outputPath: string
98
+ }
99
+
100
+ export interface RuntimeTemplateScaffoldInput extends RuntimeWorkspaceInput {
101
+ templateId: string
102
+ slideIndex: number
103
+ seed?: Record<string, any>
104
+ designName?: string
105
+ }
106
+
107
+ export interface RuntimeTemplateScaffoldAddInput extends RuntimeTemplateScaffoldInput {
108
+ outputPath: string
109
+ }
110
+
111
+ export interface RuntimePageTemplateReadInput {
112
+ templateId: string
113
+ }
114
+
87
115
  export interface RuntimeDesignCreateInput {
88
116
  name: string
89
117
  base?: string
90
118
  designMd: string
91
- previewHtml: string
119
+ designCss?: string
120
+ previewHtml?: string
92
121
  assets?: DesignPackageAssetInput[]
93
122
  overwrite?: boolean
94
123
  }
95
124
 
96
125
  export interface RuntimeDesignDraftCreateInput extends RuntimeDesignCreateInput, RuntimeWorkspaceInput {}
97
126
 
127
+ export interface RuntimeDesignPreviewInput extends RuntimeWorkspaceInput {
128
+ name: string
129
+ source?: "draft" | "installed" | "builtin"
130
+ }
131
+
98
132
  export interface RuntimeDomainCreateInput {
99
133
  name: string
100
134
  domainMd: string
@@ -218,6 +252,58 @@ export function createDeckFoundation(input: RuntimeDeckFoundationInput) {
218
252
  })
219
253
  }
220
254
 
255
+ export function listPageTemplates() {
256
+ return listBuiltinPageTemplates()
257
+ }
258
+
259
+ export function renderTemplateSlide(input: RuntimeTemplateSlideInput) {
260
+ return renderPageTemplateSlide({
261
+ templateId: requiredString(input?.templateId, "templateId"),
262
+ slideIndex: input.slideIndex,
263
+ content: input.content ?? {},
264
+ designName: input.designName || activeDesign(),
265
+ })
266
+ }
267
+
268
+ export function renderTemplateScaffold(input: RuntimeTemplateScaffoldInput) {
269
+ return renderPageTemplateScaffold({
270
+ templateId: requiredString(input?.templateId, "templateId"),
271
+ slideIndex: input.slideIndex,
272
+ seed: input.seed ?? {},
273
+ designName: input.designName || activeDesign(),
274
+ })
275
+ }
276
+
277
+ export function addTemplateSlide(input: RuntimeTemplateSlideAddInput) {
278
+ return addPageTemplateSlide({
279
+ workspaceRoot: root(input.workspaceRoot),
280
+ outputPath: requiredString(input?.outputPath, "outputPath"),
281
+ templateId: requiredString(input?.templateId, "templateId"),
282
+ slideIndex: input.slideIndex,
283
+ content: input.content ?? {},
284
+ designName: input.designName || activeDesign(),
285
+ })
286
+ }
287
+
288
+ export function addTemplateScaffold(input: RuntimeTemplateScaffoldAddInput) {
289
+ return addPageTemplateScaffold({
290
+ workspaceRoot: root(input.workspaceRoot),
291
+ outputPath: requiredString(input?.outputPath, "outputPath"),
292
+ templateId: requiredString(input?.templateId, "templateId"),
293
+ slideIndex: input.slideIndex,
294
+ seed: input.seed ?? {},
295
+ designName: input.designName || activeDesign(),
296
+ })
297
+ }
298
+
299
+ export function pageTemplateFoundation(input: RuntimePageTemplateReadInput) {
300
+ return { ok: true, foundation: getPageTemplateFoundation(requiredString(input?.templateId, "templateId")) }
301
+ }
302
+
303
+ export function pageTemplateVocabulary(input: RuntimePageTemplateReadInput) {
304
+ return { ok: true, vocabulary: getPageTemplateVocabulary(requiredString(input?.templateId, "templateId")) }
305
+ }
306
+
221
307
  export async function runDeckQa(input: RuntimeFileInput) {
222
308
  const { formatArtifactQAReport, runArtifactQA } = await import("../qa/artifact")
223
309
  const workspaceRoot = root(input.workspaceRoot)
@@ -362,7 +448,8 @@ export function designCreate(input: RuntimeDesignCreateInput) {
362
448
  name: requiredString(input?.name, "design name"),
363
449
  base: input.base,
364
450
  designMd: requiredString(input?.designMd, "designMd"),
365
- previewHtml: requiredString(input?.previewHtml, "previewHtml"),
451
+ designCss: input.designCss,
452
+ previewHtml: input.previewHtml,
366
453
  assets: input.assets,
367
454
  overwrite: input.overwrite ?? false,
368
455
  })
@@ -378,7 +465,8 @@ export function designDraftCreate(input: RuntimeDesignDraftCreateInput) {
378
465
  name: requiredString(input?.name, "design name"),
379
466
  base: input.base,
380
467
  designMd: requiredString(input?.designMd, "designMd"),
381
- previewHtml: requiredString(input?.previewHtml, "previewHtml"),
468
+ designCss: input.designCss,
469
+ previewHtml: input.previewHtml,
382
470
  assets: input.assets,
383
471
  overwrite: input.overwrite ?? false,
384
472
  })
@@ -388,6 +476,14 @@ export function designDraftValidate(input: RuntimeWorkspaceInput & RuntimeNameIn
388
476
  return validateDesignDraftPackage(root(input.workspaceRoot), requiredName(input, "design draft"))
389
477
  }
390
478
 
479
+ export function designPreview(input: RuntimeDesignPreviewInput) {
480
+ return materializeDesignPreview({
481
+ workspaceRoot: root(input.workspaceRoot),
482
+ name: requiredName(input, "design"),
483
+ source: input.source,
484
+ })
485
+ }
486
+
391
487
  export function designDraftInstall(input: RuntimeDraftInstallInput) {
392
488
  seedBuiltinDesigns()
393
489
  return installDesignDraftPackage({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyber-dash-tech/revela",
3
- "version": "0.18.16",
3
+ "version": "0.19.1",
4
4
  "description": "Codex-first CLI/MCP workspace for trusted narrative artifacts from local sources, research, and evidence",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
@@ -11,11 +11,7 @@
11
11
  ".": "./index.ts"
12
12
  },
13
13
  "files": [
14
- "assets/img/logo.png",
15
- "assets/img/logo-64.png",
16
- "assets/img/logo-256.png",
17
- "assets/img/logo-wordmark.png",
18
- "assets/img/review-ui.png",
14
+ "assets/img/",
19
15
  "bin/",
20
16
  "plugin.ts",
21
17
  "lib/",
@@ -23,15 +19,11 @@
23
19
  "tools/",
24
20
  "skill/",
25
21
  "designs/aurora/DESIGN.md",
26
- "designs/starter/DESIGN.md",
27
- "designs/starter/preview.html",
28
- "designs/summit/DESIGN.md",
29
- "designs/summit/preview.html",
30
- "designs/monet/DESIGN.md",
31
- "designs/monet/preview.html",
32
- "designs/lucent/DESIGN.md",
33
- "designs/lucent/preview.html",
34
- "designs/lucent/assets/",
22
+ "designs/starter/",
23
+ "designs/summit/",
24
+ "designs/monet/",
25
+ "designs/lucent/",
26
+ "designs/lucent-dark/",
35
27
  "domains/general/INDUSTRY.md",
36
28
  "domains/deeptech-investment/INDUSTRY.md",
37
29
  "domains/consulting/INDUSTRY.md",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "revela",
3
- "version": "0.18.16",
3
+ "version": "0.19.0",
4
4
  "description": "Use Revela in Codex to specify, research, plan, make, and export trusted narrative decision artifacts.",
5
5
  "author": {
6
6
  "name": "cyber-dash-tech",