@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
@@ -0,0 +1,334 @@
1
+ /* Lucent design CSS. Template HTML owns structure; this file owns visual treatment. */
2
+
3
+ :root {
4
+ --bg-frame: #07111f;
5
+ --bg-page: #f7f9fc;
6
+ --bg-page-alt: #eef3f9;
7
+ --surface: #ffffff;
8
+ --surface-tint: #f1f6fc;
9
+ --surface-blue: #e7f0fb;
10
+ --text-primary: #101a2b;
11
+ --text-secondary: #42526a;
12
+ --text-muted: #7b8aa0;
13
+ --line: rgba(44, 70, 108, 0.14);
14
+ --line-strong: rgba(44, 70, 108, 0.28);
15
+ --accent-primary: #315eea;
16
+ --accent-secondary: #6e5df6;
17
+ --accent-cyan: #18a8d8;
18
+ --accent-coral: #f06370;
19
+ --accent-soft: #dbe8ff;
20
+ --shadow-soft: rgba(30, 65, 130, 0.13);
21
+ --font-display: DengXian, "Microsoft YaHei", "PingFang SC", Arial, ui-sans-serif, sans-serif;
22
+ --font-body: DengXian, "Microsoft YaHei", "PingFang SC", Arial, ui-sans-serif, sans-serif;
23
+ --grid-margin-x: 72px;
24
+ --grid-margin-y: 56px;
25
+ --grid-columns: 12;
26
+ --grid-gutter: 24px;
27
+ --grid-safe-top: 56px;
28
+ --grid-safe-bottom: 64px;
29
+ --space-1: 8px;
30
+ --space-2: 16px;
31
+ --space-3: 24px;
32
+ --space-4: 32px;
33
+ --space-5: 48px;
34
+ --space-6: 72px;
35
+ --font-size-caption: 16px;
36
+ --font-size-body: 22px;
37
+ --font-size-body-small: 18px;
38
+ --font-size-h3: 31px;
39
+ --font-size-h2: 52px;
40
+ --font-size-hero: 104px;
41
+ --surface-radius: 8px;
42
+ --surface-radius-large: 12px;
43
+ }
44
+
45
+ * { box-sizing: border-box; }
46
+ html { scroll-snap-type: y mandatory; overflow-y: scroll; height: 100%; }
47
+ body { margin: 0; background: var(--bg-frame, #07111f); color: var(--text-primary, #101a2b); font-family: var(--font-body, Arial, sans-serif); -webkit-font-smoothing: antialiased; }
48
+ .slide { min-height: 100dvh; scroll-snap-align: start; display: flex; align-items: center; justify-content: center; overflow: hidden; background: var(--bg-frame, #07111f); }
49
+ .slide-canvas { width: 1920px; height: 1080px; flex-shrink: 0; transform-origin: center center; position: relative; overflow: hidden; }
50
+ .template-slide .slide-canvas {
51
+ background:
52
+ radial-gradient(circle at 82% 16%, rgba(49, 94, 234, 0.11), transparent 29%),
53
+ linear-gradient(135deg, var(--bg-page), var(--bg-page-alt));
54
+ color: var(--text-primary);
55
+ padding: 72px;
56
+ box-sizing: border-box;
57
+ }
58
+ .template-frame { width: 100%; height: 100%; display: flex; flex-direction: column; gap: 34px; }
59
+ .template-frame--catalog { gap: 26px; }
60
+ .template-eyebrow { margin: 0 0 14px; font-size: 16px; text-transform: uppercase; letter-spacing: 0.18em; color: var(--text-muted); font-weight: 700; }
61
+ .template-frame header { flex: 0 0 auto; padding-bottom: 8px; overflow: visible; }
62
+ .template-title { margin: 0; max-width: 1320px; font-family: var(--font-display); font-size: 62px; line-height: 1.22; color: var(--text-primary); padding-bottom: 6px; overflow: visible; }
63
+ .template-body { flex: 1; min-height: 0; }
64
+ .template-grid { display: grid; gap: 24px; height: 100%; }
65
+ .template-grid.cols-2 { grid-template-columns: 0.95fr 1.05fr; }
66
+ .template-grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
67
+ .template-grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
68
+ .template-chart-layout { grid-template-columns: 2fr 1fr; }
69
+ .template-card { background: rgba(255,255,255,0.82); border: 1px solid var(--line); border-radius: var(--surface-radius); padding: 28px; box-shadow: 0 18px 44px var(--shadow-soft); }
70
+ .template-card h2, .template-card h3 { margin: 0 0 12px; font-size: 28px; line-height: 1.32; padding-bottom: 4px; overflow: visible; }
71
+ .template-card p { margin: 10px 0; font-size: 21px; line-height: 1.42; color: var(--text-secondary); }
72
+ .template-key-message-panel { display: flex; flex-direction: column; justify-content: flex-start; gap: 24px; padding: 0; background: transparent; border-radius: 0; box-shadow: none; }
73
+ .template-key-message-kicker { margin: 0; max-width: 720px; font-size: 32px; line-height: 1.24; letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent-primary); font-weight: 800; padding-bottom: 6px; overflow: visible; }
74
+ .template-key-message-panel p { margin: 0; max-width: 760px; font-size: 25px; line-height: 1.5; color: var(--text-secondary); }
75
+ .template-evidence-grid { display: grid; gap: 24px; min-height: 0; }
76
+ .template-evidence-card { min-height: 0; }
77
+ .template-claim-text-panel { min-height: 0; display: flex; flex-direction: column; justify-content: flex-start; align-items: flex-start; gap: 18px; padding: 0; background: transparent; border: 0; border-radius: 0; box-shadow: none; }
78
+ .template-claim-text-title { margin: 0; max-width: 760px; font-size: 31px; line-height: 1.26; color: var(--text-primary); padding-bottom: 4px; overflow: visible; }
79
+ .template-claim-text-body { margin: 0; max-width: 760px; font-size: 22px; line-height: 1.48; color: var(--text-secondary); }
80
+ .template-claim-text-panel .template-list { margin-top: 4px; }
81
+ .template-list { display: grid; gap: 18px; margin: 0; padding: 0; list-style: none; }
82
+ .template-list li { position: relative; padding-left: 24px; font-size: 24px; line-height: 1.38; color: var(--text-secondary); }
83
+ .template-list li::before { content: ""; position: absolute; left: 0; top: 14px; width: 7px; height: 7px; background: var(--accent-primary); }
84
+ .template-hero { margin: 0; max-width: none; justify-content: center; align-items: flex-start; }
85
+ .template-hero > [data-template-slot="hero"] { width: 100%; }
86
+ .template-hero header { padding-bottom: 0; }
87
+ .template-hero-title { font-size: 120px; line-height: 1.18; color: white; font-weight: 800; opacity: 0.8; padding: 12px 0 20px; max-width: 1320px; }
88
+ .template-hero .template-eyebrow { color: rgba(255,255,255,0.78); }
89
+ .template-hero--cover, .template-hero--section-divider { justify-content: center; align-items: flex-start; }
90
+ .template-hero--closing { justify-content: flex-end; align-items: flex-end; }
91
+ .template-hero--closing > [data-template-slot="hero"] { display: flex; justify-content: flex-end; text-align: right; }
92
+ .template-hero--closing .template-hero-title { max-width: 1120px; }
93
+ .template-slide[data-template="agenda"] .template-frame { display: grid; grid-template-rows: 1fr auto; gap: 28px; }
94
+ .template-slide[data-template="cover"] .slide-canvas,
95
+ .template-slide[data-template="section-divider"] .slide-canvas,
96
+ .template-slide[data-template="closing"] .slide-canvas {
97
+ background:
98
+ radial-gradient(circle at 80% 14%, rgba(24,168,216,0.32), transparent 28%),
99
+ linear-gradient(135deg, #07111f, #101a2b 62%, #243a73);
100
+ }
101
+
102
+ .template-slide[data-design="lucent"][data-template="cover"] .slide-canvas {
103
+ background:
104
+ linear-gradient(90deg, rgba(7,17,31,0.82), rgba(7,17,31,0.42) 52%, rgba(7,17,31,0.24)),
105
+ url("./assets/cover-background.jpg") center center / cover no-repeat;
106
+ }
107
+ .template-slide[data-design="lucent"][data-template="agenda"] .slide-canvas {
108
+ background:
109
+ linear-gradient(90deg, rgba(7,17,31,0.86), rgba(7,17,31,0.58) 52%, rgba(7,17,31,0.32)),
110
+ url("./assets/cover-background.jpg") center center / cover no-repeat;
111
+ }
112
+ .template-slide[data-design="lucent"][data-template="section-divider"] .slide-canvas {
113
+ background:
114
+ linear-gradient(90deg, rgba(7,17,31,0.86), rgba(16,26,43,0.62) 58%, rgba(36,58,115,0.36)),
115
+ url("./assets/cover-background.jpg") center center / cover no-repeat;
116
+ }
117
+ .template-slide[data-template="closing"] .slide-canvas { background: linear-gradient(135deg, #07111f, #315eea 58%, #18a8d8); }
118
+
119
+ .template-slide[data-design="lucent"][data-template="closing"] .slide-canvas {
120
+ background:
121
+ linear-gradient(90deg, rgba(7,17,31,0.82), rgba(49,94,234,0.42) 58%, rgba(24,168,216,0.24)),
122
+ url("./assets/closing-background.jpg") center center / cover no-repeat;
123
+ }
124
+ .template-agenda-panel { height: 100%; min-height: 0; display: flex; overflow: hidden; color: white; }
125
+ .template-agenda-inner { width: 100%; display: grid; grid-template-columns: 37% minmax(0, 1fr); align-items: stretch; gap: 76px; }
126
+ .template-agenda-header { display: flex; flex-direction: column; min-height: 0; padding: 10px 0 0; }
127
+ .template-agenda-header .template-eyebrow { color: rgba(255,255,255,0.64); }
128
+ .template-agenda-header .template-title { max-width: 440px; font-size: 54px; line-height: 1.16; letter-spacing: 0; text-transform: uppercase; color: white; padding-bottom: 8px; overflow: visible; }
129
+ .template-agenda-footer { margin: auto 0 0; font-size: 13px; line-height: 1.4; letter-spacing: 0.12em; text-transform: uppercase; font-weight: 800; color: rgba(255,255,255,0.84); }
130
+ .template-agenda-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; justify-content: center; gap: 40px; height: 100%; }
131
+ .template-agenda-item { display: grid; grid-template-columns: 86px minmax(0, 1fr); gap: 44px; align-items: center; min-height: 58px; overflow: visible; }
132
+ .template-agenda-item span { font-family: var(--font-display); font-size: 44px; line-height: 1; letter-spacing: 0.03em; color: var(--accent-cyan, #18a8d8); font-weight: 800; font-variant-numeric: tabular-nums; }
133
+ .template-agenda-item strong { font-size: 18px; line-height: 1.45; letter-spacing: 0.1em; text-transform: uppercase; font-weight: 700; color: rgba(255,255,255,0.92); }
134
+ .template-metric-layout { height: 100%; min-height: 0; display: grid; gap: 26px; }
135
+ .template-metric-layout--insight-top { grid-template-rows: auto minmax(0, 1fr); }
136
+ .template-metric-layout--insight-bottom { grid-template-rows: minmax(0, 1fr) auto; }
137
+ .template-stat-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; align-items: stretch; }
138
+ .template-stat-value { display: block; min-height: 96px; font-size: 58px; line-height: 1.42; color: var(--accent-primary); font-weight: 800; margin-bottom: 18px; padding-bottom: 14px; overflow: visible; }
139
+ .template-chart-panel { min-height: 520px; display: grid; place-items: center; border: 1px solid var(--line); background: rgba(255,255,255,0.72); }
140
+ .template-chart-placeholder { width: 76%; height: 56%; border-left: 2px solid var(--line-strong); border-bottom: 2px solid var(--line-strong); display: flex; align-items: end; gap: 28px; padding: 0 28px 24px; }
141
+ .template-visual-slot-panel { width: 100%; min-height: 520px; border: 1px dashed var(--line-strong); border-radius: var(--surface-radius); background: linear-gradient(135deg, rgba(49,94,234,0.08), rgba(24,168,216,0.08)); display: grid; place-items: center; padding: 0; }
142
+ .template-visual-slot-label { font-size: 13px; line-height: 1.35; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-muted); font-weight: 800; }
143
+ .template-text-panel.template-chart-takeaway-panel { gap: 28px; background: linear-gradient(135deg, #5f82c8 0%, var(--accent-primary) 58%, #18a8d8 115%); color: white; box-shadow: 0 22px 56px rgba(49,94,234,0.24); }
144
+ .template-chart-takeaway-panel .template-text-panel-title { color: white; }
145
+ .template-chart-takeaway-list { display: grid; gap: 22px; width: 100%; }
146
+ .template-chart-takeaway-item { display: grid; gap: 7px; padding-top: 18px; border-top: 1px solid rgba(255,255,255,0.24); }
147
+ .template-chart-takeaway-item:first-child { padding-top: 0; border-top: 0; }
148
+ .template-chart-takeaway-item h3 { margin: 0; font-size: 25px; line-height: 1.24; color: white; }
149
+ .template-chart-takeaway-item p { margin: 0; font-size: 20px; line-height: 1.46; color: rgba(255,255,255,0.78); }
150
+ .template-bar { flex: 1; background: linear-gradient(180deg, var(--accent-primary), var(--accent-cyan)); min-height: 80px; }
151
+ .template-table-wrap { display: grid; grid-template-rows: minmax(0, auto) auto; gap: 22px; height: 100%; align-content: start; }
152
+ .template-table { width: 100%; border-collapse: collapse; background: rgba(255,255,255,0.86); box-shadow: 0 18px 44px var(--shadow-soft); }
153
+ .template-table th, .template-table td { padding: 22px 24px; border-bottom: 1px solid var(--line); text-align: left; font-size: 21px; }
154
+ .template-table th { color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.12em; font-size: 15px; }
155
+ .template-text-panel { min-height: 0; display: flex; flex-direction: column; justify-content: flex-start; align-items: flex-start; gap: 20px; background: rgba(255,255,255,0.74); border-radius: var(--surface-radius); padding: 42px; }
156
+ .template-text-panel-title { margin: 0; font-size: 34px; line-height: 1.28; color: var(--text-primary); padding-bottom: 4px; overflow: visible; }
157
+ .template-text-panel-body { margin: 0; font-size: 23px; line-height: 1.52; color: var(--text-secondary); }
158
+ .template-side-panel { align-self: stretch; }
159
+ .template-side-panel-title { margin: 0; }
160
+ .template-side-panel-body { margin: 0; }
161
+ .template-insight-panel { display: grid; gap: 10px; background: rgba(255,255,255,0.88); border: 1px solid var(--line); border-radius: var(--surface-radius); padding: 22px 24px; box-shadow: 0 14px 34px var(--shadow-soft); }
162
+ .template-insight-title { margin: 0; display: flex; align-items: center; gap: 12px; font-size: 24px; line-height: 1.24; color: var(--text-primary); }
163
+ .template-insight-icon { width: 24px; height: 24px; color: var(--accent-primary); stroke-width: 2.2; flex: 0 0 auto; }
164
+ .template-insight-body { margin: 0; font-size: 20px; line-height: 1.42; color: var(--text-secondary); }
165
+ .template-metric-layout .template-insight-panel { border: 0; box-shadow: none; background: rgba(255,255,255,0.74); padding: 24px 28px; }
166
+ .template-metric-layout .template-insight-title { font-size: 18px; line-height: 1.22; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-muted); }
167
+ .template-metric-layout .template-insight-icon { width: 20px; height: 20px; }
168
+ .template-metric-layout .template-insight-body { font-size: 24px; line-height: 1.42; color: var(--text-primary); }
169
+ .template-timeline { position: relative; height: 100%; display: grid; align-items: center; }
170
+ .template-timeline-layout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: 34px; height: 100%; align-items: stretch; }
171
+ .template-timeline-layout--left { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }
172
+ .template-timeline-layout--left .template-side-panel { grid-column: 1; grid-row: 1; }
173
+ .template-timeline-layout--left .template-timeline { grid-column: 2; grid-row: 1; }
174
+ .template-timeline-layout--right { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
175
+ .template-timeline-layout--right .template-timeline { grid-column: 1; grid-row: 1; }
176
+ .template-timeline-layout--right .template-side-panel { grid-column: 2; grid-row: 1; }
177
+ .template-timeline-layout .template-text-panel { background: linear-gradient(135deg, #7a7fe8 0%, #5f82c8 58%, #315eea 115%); color: white; box-shadow: 0 22px 56px rgba(49,94,234,0.22); }
178
+ .template-timeline-layout .template-text-panel-title { color: white; }
179
+ .template-timeline-layout .template-text-panel-body { color: rgba(255,255,255,0.78); }
180
+ .template-timeline--horizontal { grid-template-columns: repeat(var(--timeline-count), 1fr); column-gap: 18px; align-items: stretch; --timeline-axis-y: 86%; }
181
+ .template-timeline--horizontal::before { content: ""; position: absolute; left: 4%; right: 4%; top: var(--timeline-axis-y); border-top: 2px solid var(--line-strong); transform: translateY(-1px); }
182
+ .template-timeline-item { position: relative; min-height: 400px; display: grid; justify-items: center; align-items: center; }
183
+ .template-timeline--horizontal .template-timeline-item { grid-template-rows: minmax(0, 1fr) 42px 86px; align-items: stretch; }
184
+ .template-timeline-dot { z-index: 2; width: 22px; height: 22px; border-radius: 999px; background: var(--accent-primary); box-shadow: 0 0 0 8px rgba(49,94,234,0.12); }
185
+ .template-timeline-copy { z-index: 2; width: 86%; padding: 18px 4px; background: transparent; border: 0; box-shadow: none; }
186
+ .template-timeline--horizontal .template-timeline-copy.template-card { width: 94%; height: calc(100% - 55px); min-height: 254px; align-self: end; justify-self: center; display: flex; flex-direction: column; justify-content: flex-start; padding: 28px 24px 24px; margin-bottom: 22px; }
187
+ .template-timeline--horizontal .template-timeline-item--highlight .template-timeline-copy.template-card { height: calc(100% - 22px); min-height: 254px; }
188
+ .template-timeline--horizontal .template-insight-icon { width: 28px; height: 28px; margin: 0 auto 28px; color: var(--accent-primary); stroke-width: 2; flex: 0 0 auto; }
189
+ .template-timeline--horizontal .template-timeline-item--highlight .template-insight-icon { color: #f37021; }
190
+ .template-timeline--horizontal .template-timeline-item--highlight .template-timeline-copy h3 { color: #f37021; }
191
+ .template-timeline--horizontal .template-timeline-dot { grid-row: 2; align-self: center; justify-self: center; }
192
+ .template-timeline--horizontal .template-timeline-date { grid-row: 3; align-self: start; justify-self: center; margin: 8px 0 0; font-size: 38px; line-height: 1; font-weight: 300; letter-spacing: 0.03em; color: var(--text-muted); }
193
+ .template-timeline-date { margin: 0 0 8px; font-size: 15px; text-transform: uppercase; letter-spacing: 0.14em; color: var(--accent-primary); font-weight: 800; }
194
+ .template-timeline-copy h3 { margin: 0 0 8px; font-size: 27px; line-height: 1.28; padding-bottom: 4px; overflow: visible; }
195
+ .template-timeline-copy p:last-child { margin: 0; font-size: 19px; color: var(--text-secondary); }
196
+ .template-timeline--vertical { grid-template-columns: 1fr; align-items: stretch; padding: 18px 0; }
197
+ .template-timeline--vertical::before { content: ""; position: absolute; top: 0; bottom: 0; left: 50%; border-left: 2px solid var(--line-strong); }
198
+ .template-timeline--vertical .template-timeline-item { min-height: 128px; grid-template-columns: 1fr 56px 1fr; justify-items: stretch; }
199
+ .template-timeline--vertical .template-timeline-dot { grid-column: 2; grid-row: 1; justify-self: center; align-self: center; }
200
+ .template-timeline--vertical .template-timeline-copy { grid-row: 1; width: auto; align-self: center; background: transparent; border: 0; box-shadow: none; }
201
+ .template-timeline--vertical .template-timeline-item:nth-child(odd) .template-timeline-copy { grid-column: 1; text-align: right; align-self: center; }
202
+ .template-timeline--vertical .template-timeline-item:nth-child(even) .template-timeline-copy { grid-column: 3; text-align: left; align-self: center; }
203
+ .template-steps { display: grid; grid-template-columns: repeat(4, 1fr); gap: 22px; }
204
+ .template-step-number { font-size: 48px; color: var(--accent-primary); font-weight: 800; margin-bottom: 30px; }
205
+ .template-image-card { width: 100%; margin: 18px 0 0; display: grid; gap: 8px; }
206
+ .template-image-frame { width: 100%; height: 128px; border-radius: var(--surface-radius); overflow: hidden; background: var(--surface-tint, #f1f6fc); border: 1px solid var(--line); }
207
+ .template-image-frame img { display: block; width: 100%; height: 100%; object-fit: cover; }
208
+ .template-image-caption { margin: 0; font-size: 13px; line-height: 1.35; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.08em; }
209
+ .template-visual-placeholder { width: 100%; margin: 18px 0 0; display: grid; gap: 8px; }
210
+ .template-visual-placeholder-frame { width: 100%; height: 148px; border-radius: var(--surface-radius); border: 1px dashed var(--line-strong); background: linear-gradient(135deg, rgba(49,94,234,0.08), rgba(24,168,216,0.08)); display: grid; place-items: center; }
211
+ .template-visual-placeholder-label { font-size: 13px; line-height: 1.35; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-muted); font-weight: 800; }
212
+ .template-page-number { position: absolute; right: 72px; bottom: 52px; font-size: 15px; color: var(--text-muted); letter-spacing: 0.18em; }
213
+ .template-catalog-panel { flex: 0 0 auto; margin-top: auto; background: rgba(255,255,255,0.9); border: 1px solid var(--line); border-radius: var(--surface-radius); box-shadow: 0 18px 44px var(--shadow-soft); padding: 16px 22px; color: var(--text-primary); }
214
+ .template-hero .template-catalog-panel { background: rgba(247,249,252,0.92); }
215
+ .template-catalog-kicker { margin: 0 0 4px; font-size: 12px; text-transform: uppercase; letter-spacing: 0.16em; color: var(--accent-primary); font-weight: 800; }
216
+ .template-catalog-title { margin: 0 0 10px; font-size: 20px; line-height: 1.28; font-weight: 800; }
217
+ .template-catalog-grid { display: grid; grid-template-columns: 1.15fr 1fr 1fr; gap: 16px; }
218
+ .template-catalog-section { min-width: 0; }
219
+ .template-catalog-section h3 { margin: 0 0 6px; font-size: 13px; text-transform: uppercase; letter-spacing: 0.12em; color: var(--text-muted); }
220
+ .template-catalog-section p { margin: 0; font-size: 15px; line-height: 1.36; color: var(--text-secondary); }
221
+ .template-catalog-list { margin: 0; padding-left: 16px; display: grid; gap: 2px; }
222
+ .template-catalog-list li { font-size: 14px; line-height: 1.3; color: var(--text-secondary); }
223
+ .template-frame--catalog .template-title { font-size: 52px; line-height: 1.18; }
224
+ .template-slide[data-template="agenda"] .template-frame--catalog .template-title { font-size: 54px; line-height: 1.04; }
225
+ .template-frame--catalog .template-card { padding: 22px; }
226
+ .template-frame--catalog .template-card h2,
227
+ .template-frame--catalog .template-card h3 { font-size: 24px; line-height: 1.22; margin-bottom: 8px; }
228
+ .template-frame--catalog .template-card p { font-size: 18px; line-height: 1.32; }
229
+ .template-frame--catalog .template-key-message-panel { gap: 16px; }
230
+ .template-frame--catalog .template-key-message-kicker { font-size: 23px; line-height: 1.2; }
231
+ .template-frame--catalog .template-key-message-panel p { font-size: 19px; line-height: 1.42; }
232
+ .template-frame--catalog .template-claim-text-panel { gap: 12px; }
233
+ .template-frame--catalog .template-claim-text-title { font-size: 24px; line-height: 1.24; }
234
+ .template-frame--catalog .template-claim-text-body { font-size: 18px; line-height: 1.36; }
235
+ .template-frame--catalog .template-evidence-grid { gap: 18px; }
236
+ .template-frame--catalog .template-list { gap: 12px; }
237
+ .template-frame--catalog .template-list li { font-size: 20px; line-height: 1.28; }
238
+ .template-frame--catalog .template-metric-layout { gap: 18px; }
239
+ .template-frame--catalog .template-metric-layout .template-card { padding: 20px; }
240
+ .template-frame--catalog .template-metric-layout .template-stat-value { min-height: 70px; font-size: 48px; line-height: 1.24; margin-bottom: 10px; padding-bottom: 8px; }
241
+ .template-frame--catalog .template-metric-layout .template-insight-panel { padding: 18px 22px; gap: 7px; }
242
+ .template-frame--catalog .template-metric-layout .template-insight-title { font-size: 13px; line-height: 1.22; }
243
+ .template-frame--catalog .template-metric-layout .template-insight-icon { width: 16px; height: 16px; }
244
+ .template-frame--catalog .template-metric-layout .template-insight-body { font-size: 19px; line-height: 1.34; }
245
+ .template-frame--catalog .template-chart-panel { min-height: 360px; }
246
+ .template-frame--catalog .template-visual-slot-panel { min-height: 360px; }
247
+ .template-frame--catalog .template-visual-slot-label { font-size: 11px; }
248
+ .template-frame--catalog .template-chart-takeaway-panel { padding: 24px; gap: 16px; }
249
+ .template-frame--catalog .template-chart-takeaway-list { gap: 13px; }
250
+ .template-frame--catalog .template-chart-takeaway-item { gap: 4px; padding-top: 11px; }
251
+ .template-frame--catalog .template-chart-takeaway-item h3 { font-size: 19px; line-height: 1.2; }
252
+ .template-frame--catalog .template-chart-takeaway-item p { font-size: 15px; line-height: 1.3; }
253
+ .template-frame--catalog .template-table-wrap { gap: 16px; }
254
+ .template-frame--catalog .template-table th,
255
+ .template-frame--catalog .template-table td { padding: 14px 18px; font-size: 17px; line-height: 1.32; }
256
+ .template-frame--catalog .template-table th { font-size: 12px; }
257
+ .template-frame--catalog .template-insight-panel { padding: 16px 18px; gap: 6px; }
258
+ .template-frame--catalog .template-insight-title { font-size: 20px; line-height: 1.2; }
259
+ .template-frame--catalog .template-insight-icon { width: 20px; height: 20px; }
260
+ .template-frame--catalog .template-insight-body { font-size: 16px; line-height: 1.32; }
261
+ .template-frame--catalog .template-timeline--vertical { padding: 6px 0; }
262
+ .template-frame--catalog .template-timeline--vertical .template-timeline-item { min-height: 96px; }
263
+ .template-frame--catalog .template-timeline-layout { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: 22px; }
264
+ .template-frame--catalog .template-timeline-layout--left { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }
265
+ .template-frame--catalog .template-timeline-layout--right { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
266
+ .template-frame--catalog .template-timeline-layout .template-text-panel { padding: 22px; gap: 10px; }
267
+ .template-frame--catalog .template-timeline-layout .template-text-panel-title { font-size: 25px; line-height: 1.3; }
268
+ .template-frame--catalog .template-timeline-layout .template-text-panel-body { font-size: 18px; line-height: 1.4; }
269
+ .template-frame--catalog .template-timeline--horizontal .template-timeline-item { grid-template-rows: minmax(0, 1fr) 32px 58px; min-height: 330px; }
270
+ .template-frame--catalog .template-timeline--horizontal .template-timeline-copy.template-card { height: calc(100% - 51px); min-height: 178px; padding: 20px 18px 18px; margin-bottom: 18px; }
271
+ .template-frame--catalog .template-timeline--horizontal .template-timeline-item--highlight .template-timeline-copy.template-card { height: calc(100% - 18px); min-height: 178px; }
272
+ .template-frame--catalog .template-timeline--horizontal .template-insight-icon { width: 22px; height: 22px; margin-bottom: 18px; }
273
+ .template-frame--catalog .template-timeline--horizontal .template-timeline-date { font-size: 28px; }
274
+ .template-frame--catalog .template-timeline-copy { padding: 8px 4px; }
275
+ .template-frame--catalog .template-timeline-copy h3 { font-size: 21px; line-height: 1.18; margin-bottom: 4px; }
276
+ .template-frame--catalog .template-timeline-date { font-size: 12px; margin-bottom: 4px; }
277
+ .template-frame--catalog .template-timeline-copy p:last-child { font-size: 15px; line-height: 1.24; }
278
+ .template-frame--catalog .template-steps { gap: 16px; }
279
+ .template-frame--catalog .template-step-number { font-size: 40px; margin-bottom: 20px; }
280
+ .template-frame--catalog .template-image-frame { height: 86px; }
281
+ .template-frame--catalog .template-image-caption { font-size: 11px; }
282
+ .template-frame--catalog .template-visual-placeholder-frame { height: 110px; }
283
+ .template-frame--catalog .template-visual-placeholder-label { font-size: 11px; }
284
+
285
+ /* Summit overrides. Editorial, confident, border-light report system. */
286
+ :root {
287
+ --bg-frame: #0a1010;
288
+ --bg-page: #f7f7f2;
289
+ --bg-page-alt: #eceee5;
290
+ --surface: #ffffff;
291
+ --surface-tint: #f1f2eb;
292
+ --surface-blue: #e7eee8;
293
+ --text-primary: #111a18;
294
+ --text-secondary: #4b5a56;
295
+ --text-muted: #7a8b86;
296
+ --line: rgba(35, 53, 49, 0.1);
297
+ --line-strong: rgba(35, 53, 49, 0.22);
298
+ --accent-primary: #176c63;
299
+ --accent-secondary: #b0822e;
300
+ --accent-cyan: #3a9ca0;
301
+ --accent-coral: #b85d45;
302
+ --accent-soft: #dcebe6;
303
+ --shadow-soft: rgba(18, 37, 34, 0.1);
304
+ --font-display: Georgia, "Times New Roman", "Microsoft YaHei", "PingFang SC", serif;
305
+ --font-body: "Aptos", DengXian, "Microsoft YaHei", "PingFang SC", Arial, ui-sans-serif, sans-serif;
306
+ }
307
+ .template-card,
308
+ .template-table,
309
+ .template-insight-panel,
310
+ .template-catalog-panel {
311
+ border-color: transparent;
312
+ box-shadow: 0 22px 54px rgba(18, 37, 34, 0.1);
313
+ }
314
+ .template-title,
315
+ .template-card h2,
316
+ .template-card h3,
317
+ .template-text-panel-title,
318
+ .template-claim-text-title { letter-spacing: 0; }
319
+ .template-text-panel.template-chart-takeaway-panel,
320
+ .template-timeline-layout .template-text-panel {
321
+ background: linear-gradient(135deg, #176c63 0%, #2a8f84 54%, #b0822e 125%);
322
+ }
323
+ .template-slide[data-design="summit"][data-template="cover"] .slide-canvas,
324
+ .template-slide[data-design="summit"][data-template="agenda"] .slide-canvas,
325
+ .template-slide[data-design="summit"][data-template="section-divider"] .slide-canvas {
326
+ background:
327
+ linear-gradient(90deg, rgba(10,16,16,0.84), rgba(10,16,16,0.46) 52%, rgba(10,16,16,0.18)),
328
+ url("./assets/cover-background.jpg") center center / cover no-repeat;
329
+ }
330
+ .template-slide[data-design="summit"][data-template="closing"] .slide-canvas {
331
+ background:
332
+ linear-gradient(90deg, rgba(10,16,16,0.78), rgba(23,108,99,0.4) 58%, rgba(176,130,46,0.22)),
333
+ url("./assets/closing-background.jpg") center center / cover no-repeat;
334
+ }
@@ -74,17 +74,20 @@ const VISUAL_QUALITY_RULES = `Visual extraction and CSS quality rules:
74
74
  - Preserve composition, not just colors and shapes. If the reference is a bottom strip, compact badge, side rail, or sparse header mark, keep that scale and anchoring; do not enlarge it into a full-slide mascot or background unless requested.
75
75
  - Do not rewrite the entire base layout system from scratch. Preserve base layout/container CSS where possible; mainly change tokens, typography, component skins, and small decorative components.
76
76
  - Keep CSS scoped and boring. Prefer CSS variables and reusable classes over many one-off absolute-positioned selectors.
77
+ - Treat the design as an executable visual system, not a mood board. Document grid, safe-area, spacing, type scale, surface, and chart tokens in \`@design:foundation\`.
78
+ - Layouts must use declared slots and stable grid/flex containers. Do not fake alignment with scattered one-off absolute positioning when a layout grid should own placement.
79
+ - Components must declare normal, dense, and long-copy behavior when relevant. Chart, table, media, and source-note components need stable container dimensions.
77
80
  - If a reference is flat vector, doodle, mascot, blob, line-art, or geometric illustration, prefer a self-contained SVG component with a fixed viewBox. CSS should place the SVG; the SVG should draw the motif.
78
81
  - If a reference is photography, UI screenshot, webpage, or product surface, do not convert it to SVG. Extract palette, type scale, spacing, layout rhythm, borders, and image treatment instead.
79
82
  - For SVG motifs: set a viewBox, keep all eyes/mouths/decorations inside that coordinate system, and document intended placement/scale in the component notes.
80
- - Before saving, review the preview for text overlap, scale drift, lost anchoring, overflow, and whether the preview preserves the reference composition.`
83
+ - Before saving, generate the built-in template preview with the new design CSS and review it for text overlap, scale drift, lost anchoring, overflow, and whether the preview preserves the reference composition.`
81
84
 
82
85
  const PREVIEW_REQUIREMENTS = `Preview requirements:
83
- - \`preview.html\` must include a cover slide and a closing slide. Mark their \`<section class="slide">\` elements with \`data-slide-role="cover"\` and \`data-slide-role="closing"\`.
84
- - \`preview.html\` must define an explicit CSS rule for \`.slide-canvas\` with \`width: 1920px\` and \`height: 1080px\`; every direct \`.slide-canvas\` is the fixed 1920px x 1080px export surface.
85
- - \`preview.html\` must showcase every \`@component:*\` defined in \`DESIGN.md\`. Mark each showcased component with \`data-preview-component="<component-name>"\`.
86
- - Do not save with \`revela-designs-author\` until every component has a corresponding preview marker. If a component is decorative or abstract, include a visible labeled sample state.
87
- - When the design supports chart styling, \`preview.html\` should include a 3x3 ECharts gallery with at least 9 chart examples. This is a preview quality requirement, not a validation blocker.`
86
+ - Do not hand-write package \`preview.html\` for ordinary CSS-native designs.
87
+ - The preview is generated from Revela's built-in page-template fixture plus the package \`design.css\`.
88
+ - \`design.css\` must style the stable template DOM classes while preserving structural classes and \`data-template-slot\` semantics.
89
+ - Preview review should check cover, closing, agenda, cards, metric, chart takeaways, table, timeline, process steps, recommendation, risk, and image/chart slots.
90
+ - If the design uses package assets, keep them under \`assets/**\` and reference them from \`design.css\` with package-relative paths.`
88
91
 
89
92
  export function buildDesignsNewPrompt({ name, base }: DesignsNewArgs): string {
90
93
  return `You are creating a new Revela visual design package.
@@ -110,7 +113,7 @@ You must replace unless the user explicitly requests otherwise:
110
113
  - decorative language
111
114
  - tone
112
115
  - composition rules
113
- - preview content
116
+ - design.css content
114
117
 
115
118
  ${VISUAL_QUALITY_RULES}
116
119
 
@@ -123,8 +126,8 @@ Workflow:
123
126
  4. Collect a concise design brief covering intent, tone, density, content mode, industry fit, references, visual schema, must-have, and must-avoid.
124
127
  5. Summarize the brief and visual schema, then ask the user to confirm them.
125
128
  6. After confirmation, inspect the base design using the \`revela-designs\` tool.
126
- 7. Generate a complete \`DESIGN.md\` and matching \`preview.html\`.
127
- 8. Self-review the preview against the visual schema before saving.
129
+ 7. Generate a complete \`DESIGN.md\` and matching \`design.css\`.
130
+ 8. Save a draft package, generate the built-in preview from \`design.css\`, and self-review the preview against the visual schema.
128
131
  9. Save the package with \`revela-designs-author\` using action \`create\`.
129
132
  10. Validate it with \`revela-designs-author\` using action \`validate\`.
130
133
  11. Report the saved path and activation command: \`/revela design --use ${name}\`.
@@ -133,11 +136,8 @@ Hard requirements:
133
136
  - \`DESIGN.md\` must include frontmatter with name, description, author, and version.
134
137
  - \`DESIGN.md\` must include valid \`@design\`, \`@layout\`, and \`@component\` markers.
135
138
  - \`DESIGN.md\` must include at least \`@design:foundation\`, \`@design:rules\`, one layout, and one component.
136
- - \`preview.html\` must be self-contained and directly openable in a browser.
137
- - \`preview.html\` must include an explicit CSS rule: \`.slide-canvas { width: 1920px; height: 1080px; }\`.
138
- - Every preview slide must include \`slide-qa="true"\` or \`slide-qa="false"\`.
139
- - \`preview.html\` must include \`data-slide-role="cover"\` and \`data-slide-role="closing"\` on slide sections.
140
- - \`preview.html\` must showcase every \`@component:*\` with \`data-preview-component="<component-name>"\` before saving.
139
+ - \`DESIGN.md\` should document the design contract: grid/safe-area, spacing scale, typography scale, surfaces, and chart tokens when charts are supported.
140
+ - \`design.css\` must include fixed \`.slide-canvas { width: 1920px; height: 1080px; }\` behavior and style the core template classes.
141
141
  - Do not save anything until the user confirms the brief.
142
142
 
143
143
  Start now by interviewing the user. Keep the first question concise.`
@@ -164,8 +164,8 @@ Workflow:
164
164
  3. Inspect the existing design using \`revela-designs\` with action \`read\` and name \`${name}\`. Fetch relevant layouts/components as needed.
165
165
  4. Summarize an edit brief covering current issue, desired direction, visual schema, must-preserve, and must-avoid.
166
166
  5. Ask the user to confirm the edit brief.
167
- 6. After confirmation, generate the updated complete \`DESIGN.md\` and updated complete \`preview.html\`.
168
- 7. Self-review the preview for text overlap, scale drift, lost anchoring, overflow, and whether the requested change is visible.
167
+ 6. After confirmation, generate the updated complete \`DESIGN.md\` and updated complete \`design.css\`.
168
+ 7. Save a draft package, generate the built-in preview from \`design.css\`, and self-review it for text overlap, scale drift, lost anchoring, overflow, and whether the requested change is visible.
169
169
  8. Save with \`revela-designs-author\` using action \`create\`, name \`${name}\`, and overwrite=true.
170
170
  9. Validate with \`revela-designs-author\` using action \`validate\`.
171
171
  10. Report the saved path and activation command: \`/revela design --use ${name}\`.
@@ -173,11 +173,8 @@ Workflow:
173
173
  Hard requirements:
174
174
  - Preserve valid frontmatter and marker structure.
175
175
  - Preserve at least \`@design:foundation\`, \`@design:rules\`, one layout, and one component.
176
- - \`preview.html\` must be self-contained and directly openable in a browser.
177
- - \`preview.html\` must include an explicit CSS rule: \`.slide-canvas { width: 1920px; height: 1080px; }\`.
178
- - Every preview slide must include \`slide-qa="true"\` or \`slide-qa="false"\`.
179
- - \`preview.html\` must include \`data-slide-role="cover"\` and \`data-slide-role="closing"\` on slide sections.
180
- - \`preview.html\` must showcase every \`@component:*\` with \`data-preview-component="<component-name>"\` before saving.
176
+ - Preserve or add the design contract: grid/safe-area, spacing scale, typography scale, surfaces, and chart tokens when charts are supported.
177
+ - \`design.css\` must include fixed \`.slide-canvas { width: 1920px; height: 1080px; }\` behavior and style the core template classes.
181
178
  - Do not save anything until the user confirms the edit brief.
182
179
 
183
180
  Start now by asking what the user wants to change in \`${name}\`.`
@@ -1,4 +1,4 @@
1
- import { resolveDesignPreview } from "../design/designs"
1
+ import { activeDesign, materializeDesignPreview } from "../design/designs"
2
2
 
3
3
  function openFile(filePath: string): void {
4
4
  if (process.platform === "darwin") {
@@ -22,14 +22,9 @@ export async function handleDesignsPreview(
22
22
  send: (text: string) => Promise<void>,
23
23
  ): Promise<void> {
24
24
  try {
25
- const preview = resolveDesignPreview(name || undefined)
26
- if (!preview.hasPreview) {
27
- await send(`Design \`${preview.name}\` has no \`preview.html\`.`)
28
- return
29
- }
30
-
25
+ const preview = materializeDesignPreview({ workspaceRoot: process.cwd(), name: name || activeDesign() })
31
26
  openFile(preview.previewPath)
32
- await send(`Opened preview for design \`${preview.name}\`: \`${preview.previewPath}\``)
27
+ await send(`Generated and opened preview for design \`${preview.name}\`: \`${preview.previewPath}\``)
33
28
  } catch (e: any) {
34
29
  await send(`**Preview failed:** ${e.message || String(e)}`)
35
30
  }
@@ -1,6 +1,6 @@
1
1
  import { existsSync, mkdirSync, writeFileSync } from "fs"
2
2
  import { dirname, isAbsolute, normalize, resolve } from "path"
3
- import { activeDesign, getDesignSection } from "../design/designs"
3
+ import { activeDesign, getDesignSection, materializeDesignCssSnapshot } from "../design/designs"
4
4
 
5
5
  export type DeckFoundationMode = "create" | "repair"
6
6
  export type DeckFoundationStatus = "created" | "updated"
@@ -47,14 +47,14 @@ export function createDeckFoundation(input: CreateDeckFoundationInput): CreateDe
47
47
  const design = input.designName || activeDesign()
48
48
  const foundation = getDesignSection("foundation", design)
49
49
  const parts = parseFoundationParts(foundation)
50
- if (parts.cssBlocks.length === 0) throw new Error(`Design '${design}' foundation does not include a CSS code block.`)
51
50
  if (parts.scriptBlocks.length === 0) throw new Error(`Design '${design}' foundation does not include a SlidePresentation JavaScript code block.`)
51
+ const snapshot = materializeDesignCssSnapshot({ workspaceRoot: input.workspaceRoot, outputPath, designName: design })
52
52
 
53
53
  const html = renderFoundationHtml({
54
54
  language: input.language || "en",
55
55
  title: input.title || "Revela Deck",
56
56
  fontLinks: parts.fontLinks,
57
- css: parts.cssBlocks.join("\n\n"),
57
+ stylesheetHrefs: [snapshot.href],
58
58
  script: parts.scriptBlocks.map(guardEmptyDeckNavigation).join("\n\n"),
59
59
  })
60
60
 
@@ -68,7 +68,8 @@ export function createDeckFoundation(input: CreateDeckFoundationInput): CreateDe
68
68
  includedSections: [
69
69
  "design:foundation",
70
70
  parts.fontLinks.length > 0 ? "foundation:font-links" : "foundation:font-links:none",
71
- "foundation:css",
71
+ snapshot.generatedFallback ? "design-css:fallback" : "design-css:snapshot",
72
+ snapshot.assetCount > 0 ? "design-assets:snapshot" : "design-assets:none",
72
73
  "foundation:SlidePresentation",
73
74
  ],
74
75
  status: existed ? "updated" : "created",
@@ -144,10 +145,11 @@ function renderFoundationHtml(input: {
144
145
  language: string
145
146
  title: string
146
147
  fontLinks: string[]
147
- css: string
148
+ stylesheetHrefs: string[]
148
149
  script: string
149
150
  }): string {
150
151
  const fontLinks = input.fontLinks.map((link) => ` ${link}`).join("\n")
152
+ const stylesheetLinks = input.stylesheetHrefs.map((href) => ` <link rel="stylesheet" href="${escapeAttribute(href)}">`).join("\n")
151
153
  return [
152
154
  "<!DOCTYPE html>",
153
155
  `<html lang="${escapeAttribute(input.language)}">`,
@@ -156,9 +158,7 @@ function renderFoundationHtml(input: {
156
158
  " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">",
157
159
  ` <title>${escapeHtml(input.title)}</title>`,
158
160
  fontLinks,
159
- " <style>",
160
- input.css,
161
- " </style>",
161
+ stylesheetLinks,
162
162
  "</head>",
163
163
  "<body>",
164
164
  ` ${SLIDES_START}`,