@cyber-dash-tech/revela 0.19.2 → 0.19.4
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.
- package/README.md +14 -14
- package/README.zh-CN.md +14 -14
- package/designs/lucent/DESIGN.md +2 -1
- package/designs/lucent/design.css +16 -6
- package/designs/lucent-dark/DESIGN.md +2 -1
- package/designs/lucent-dark/design.css +27 -12
- package/designs/monet/DESIGN.md +2 -1
- package/designs/monet/design.css +18 -8
- package/designs/starter/DESIGN.md +2 -1
- package/designs/starter/design.css +16 -6
- package/designs/summit/DESIGN.md +2 -1
- package/designs/summit/design.css +18 -8
- package/lib/browser/chrome.ts +92 -8
- package/lib/page-templates/built-in-preview.html +42 -30
- package/lib/page-templates/render.ts +51 -10
- package/lib/page-templates/templates/index.ts +1 -0
- package/lib/page-templates/templates/table.ts +3 -0
- package/lib/page-templates/vocabulary.ts +8 -2
- package/lib/pdf/export.ts +57 -31
- package/package.json +1 -1
- package/plugins/revela/.codex-plugin/plugin.json +1 -1
package/lib/browser/chrome.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import puppeteer, { type Browser } from "puppeteer-core"
|
|
2
|
-
import { existsSync } from "fs"
|
|
2
|
+
import { existsSync, mkdtempSync, rmSync } from "fs"
|
|
3
|
+
import { tmpdir } from "os"
|
|
4
|
+
import { join } from "path"
|
|
3
5
|
|
|
4
6
|
export const CHROME_PATHS = [
|
|
5
7
|
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
@@ -10,13 +12,26 @@ export const CHROME_PATHS = [
|
|
|
10
12
|
"/usr/bin/chromium",
|
|
11
13
|
]
|
|
12
14
|
|
|
15
|
+
export const CHROME_PATH_ENV_VARS = ["REVELA_CHROME_PATH", "PUPPETEER_EXECUTABLE_PATH"] as const
|
|
16
|
+
|
|
13
17
|
export function findChromePath(): string {
|
|
18
|
+
for (const envName of CHROME_PATH_ENV_VARS) {
|
|
19
|
+
const override = process.env[envName]?.trim()
|
|
20
|
+
if (!override) continue
|
|
21
|
+
if (existsSync(override)) return override
|
|
22
|
+
throw new Error(
|
|
23
|
+
`Chrome executable configured by ${envName} does not exist: ${override}\n` +
|
|
24
|
+
"Set REVELA_CHROME_PATH to a valid Chrome/Chromium binary, or unset the override to use auto-detection."
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
14
28
|
for (const p of CHROME_PATHS) {
|
|
15
29
|
if (existsSync(p)) return p
|
|
16
30
|
}
|
|
17
31
|
throw new Error(
|
|
18
32
|
"Could not find a Chrome/Chromium installation.\n" +
|
|
19
|
-
"Tried:\n" + CHROME_PATHS.map((p) => ` ${p}`).join("\n")
|
|
33
|
+
"Tried:\n" + CHROME_PATHS.map((p) => ` ${p}`).join("\n") + "\n" +
|
|
34
|
+
"You can set REVELA_CHROME_PATH=/path/to/chromium or PUPPETEER_EXECUTABLE_PATH=/path/to/chrome."
|
|
20
35
|
)
|
|
21
36
|
}
|
|
22
37
|
|
|
@@ -26,23 +41,92 @@ export interface LaunchChromeOptions {
|
|
|
26
41
|
allowFileAccess?: boolean
|
|
27
42
|
}
|
|
28
43
|
|
|
44
|
+
export function createChromeUserDataDir(): string {
|
|
45
|
+
return mkdtempSync(join(tmpdir(), "revela-chrome-"))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function cleanupChromeUserDataDir(userDataDir: string): void {
|
|
49
|
+
rmSync(userDataDir, { recursive: true, force: true })
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function withProfileCleanup(browser: Browser, userDataDir: string): Browser {
|
|
53
|
+
const originalClose = browser.close.bind(browser)
|
|
54
|
+
browser.close = async () => {
|
|
55
|
+
try {
|
|
56
|
+
await originalClose()
|
|
57
|
+
} finally {
|
|
58
|
+
try {
|
|
59
|
+
cleanupChromeUserDataDir(userDataDir)
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.warn(`Could not clean up Chrome profile ${userDataDir}: ${String(error)}`)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return browser
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function formatLaunchError(error: unknown, executablePath: string, userDataDir: string): Error {
|
|
69
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
70
|
+
return new Error(
|
|
71
|
+
"Failed to launch Chrome/Chromium for Revela browser-backed checks.\n" +
|
|
72
|
+
`Executable: ${executablePath}\n` +
|
|
73
|
+
`Platform: ${process.platform} ${process.arch}\n` +
|
|
74
|
+
`Temporary profile: ${userDataDir}\n` +
|
|
75
|
+
`REVELA_CHROME_PATH: ${process.env.REVELA_CHROME_PATH || "(unset)"}\n` +
|
|
76
|
+
`PUPPETEER_EXECUTABLE_PATH: ${process.env.PUPPETEER_EXECUTABLE_PATH || "(unset)"}\n` +
|
|
77
|
+
"If this happens in Codex/macOS, set REVELA_CHROME_PATH to a Chrome/Chromium binary that can run headless, " +
|
|
78
|
+
"or run browser-backed tests in an environment that allows Chrome to start.\n" +
|
|
79
|
+
`Original error: ${message}`
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
29
83
|
export async function launchChrome(options: LaunchChromeOptions = {}): Promise<Browser> {
|
|
30
84
|
const width = options.width ?? 1920
|
|
31
85
|
const height = options.height ?? 1080
|
|
86
|
+
const executablePath = findChromePath()
|
|
87
|
+
const userDataDir = createChromeUserDataDir()
|
|
32
88
|
const args = [
|
|
33
89
|
"--no-sandbox",
|
|
34
90
|
"--disable-setuid-sandbox",
|
|
35
91
|
"--disable-dev-shm-usage",
|
|
92
|
+
"--no-first-run",
|
|
93
|
+
"--no-default-browser-check",
|
|
94
|
+
"--disable-background-networking",
|
|
95
|
+
"--disable-background-timer-throttling",
|
|
96
|
+
"--disable-client-side-phishing-detection",
|
|
97
|
+
"--disable-component-update",
|
|
98
|
+
"--disable-default-apps",
|
|
99
|
+
"--disable-extensions",
|
|
100
|
+
"--disable-hang-monitor",
|
|
101
|
+
"--disable-popup-blocking",
|
|
102
|
+
"--disable-prompt-on-repost",
|
|
103
|
+
"--disable-sync",
|
|
104
|
+
"--disable-translate",
|
|
105
|
+
"--metrics-recording-only",
|
|
106
|
+
"--mute-audio",
|
|
107
|
+
"--safebrowsing-disable-auto-update",
|
|
108
|
+
"--disable-features=Translate,OptimizationHints,MediaRouter",
|
|
36
109
|
`--window-size=${width},${height}`,
|
|
37
110
|
]
|
|
38
111
|
|
|
39
112
|
if (options.allowFileAccess) {
|
|
40
|
-
args.
|
|
113
|
+
args.push("--allow-file-access-from-files")
|
|
41
114
|
}
|
|
42
115
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
116
|
+
try {
|
|
117
|
+
const browser = await puppeteer.launch({
|
|
118
|
+
executablePath,
|
|
119
|
+
headless: true,
|
|
120
|
+
userDataDir,
|
|
121
|
+
args,
|
|
122
|
+
})
|
|
123
|
+
return withProfileCleanup(browser, userDataDir)
|
|
124
|
+
} catch (error) {
|
|
125
|
+
try {
|
|
126
|
+
cleanupChromeUserDataDir(userDataDir)
|
|
127
|
+
} catch (cleanupError) {
|
|
128
|
+
console.warn(`Could not clean up Chrome profile ${userDataDir}: ${String(cleanupError)}`)
|
|
129
|
+
}
|
|
130
|
+
throw formatLaunchError(error, executablePath, userDataDir)
|
|
131
|
+
}
|
|
48
132
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<div class="slide-canvas">
|
|
13
13
|
<div class="template-frame template-hero template-hero--cover">
|
|
14
14
|
<div data-template-slot="hero"><header>
|
|
15
|
-
<p class="template-eyebrow">Template 01 /
|
|
15
|
+
<p class="template-eyebrow">Template 01 / 17</p>
|
|
16
16
|
<h1 class="template-title template-hero-title">cover</h1>
|
|
17
17
|
</header></div>
|
|
18
18
|
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
<div class="slide-canvas">
|
|
25
25
|
<div class="template-frame template-hero template-hero--section-divider">
|
|
26
26
|
<div data-template-slot="hero"><header>
|
|
27
|
-
<p class="template-eyebrow">Template 02 /
|
|
27
|
+
<p class="template-eyebrow">Template 02 / 17</p>
|
|
28
28
|
<h1 class="template-title template-hero-title">section-divider</h1>
|
|
29
29
|
</header></div>
|
|
30
30
|
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
<div class="slide-canvas">
|
|
37
37
|
<div class="template-frame template-hero template-hero--closing">
|
|
38
38
|
<div data-template-slot="hero"><header>
|
|
39
|
-
<p class="template-eyebrow">Template 03 /
|
|
39
|
+
<p class="template-eyebrow">Template 03 / 17</p>
|
|
40
40
|
<h1 class="template-title template-hero-title">closing</h1>
|
|
41
41
|
</header></div>
|
|
42
42
|
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
<div class="template-body template-agenda-panel" data-template-slot="agenda">
|
|
51
51
|
<div class="template-agenda-inner">
|
|
52
52
|
<div class="template-agenda-header">
|
|
53
|
-
<p class="template-eyebrow">Template 04 /
|
|
53
|
+
<p class="template-eyebrow">Template 04 / 17</p>
|
|
54
54
|
<h1 class="template-title">agenda</h1>
|
|
55
55
|
<p class="template-agenda-footer">Structure-First-Design</p>
|
|
56
56
|
</div>
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
<div class="slide-canvas">
|
|
67
67
|
<div class="template-frame">
|
|
68
68
|
<header>
|
|
69
|
-
<p class="template-eyebrow">Template 05 /
|
|
69
|
+
<p class="template-eyebrow">Template 05 / 17</p>
|
|
70
70
|
<h1 class="template-title">executive-summary</h1>
|
|
71
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
72
|
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
<div class="slide-canvas">
|
|
79
79
|
<div class="template-frame">
|
|
80
80
|
<header>
|
|
81
|
-
<p class="template-eyebrow">Template 06 /
|
|
81
|
+
<p class="template-eyebrow">Template 06 / 17</p>
|
|
82
82
|
<h1 class="template-title">problem-context</h1>
|
|
83
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
84
|
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
<div class="slide-canvas">
|
|
91
91
|
<div class="template-frame">
|
|
92
92
|
<header>
|
|
93
|
-
<p class="template-eyebrow">Template 07 /
|
|
93
|
+
<p class="template-eyebrow">Template 07 / 17</p>
|
|
94
94
|
<h1 class="template-title">key-message-evidence</h1>
|
|
95
95
|
</header><div class="template-body template-grid cols-2"><div class="template-key-message-panel" data-template-slot="key-message">
|
|
96
96
|
<h2 class="template-key-message-kicker">Key message</h2>
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
<div class="slide-canvas">
|
|
106
106
|
<div class="template-frame">
|
|
107
107
|
<header>
|
|
108
|
-
<p class="template-eyebrow">Template 08 /
|
|
108
|
+
<p class="template-eyebrow">Template 08 / 17</p>
|
|
109
109
|
<h1 class="template-title">claim-supporting-visual</h1>
|
|
110
110
|
</header><div class="template-body template-grid cols-2"><div class="template-claim-text-panel" data-template-slot="claim">
|
|
111
111
|
<h2 class="template-claim-text-title">A single visual should carry one argument.</h2>
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
<div class="slide-canvas">
|
|
122
122
|
<div class="template-frame">
|
|
123
123
|
<header>
|
|
124
|
-
<p class="template-eyebrow">Template 09 /
|
|
124
|
+
<p class="template-eyebrow">Template 09 / 17</p>
|
|
125
125
|
<h1 class="template-title">metric-highlight</h1>
|
|
126
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
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>
|
|
@@ -136,9 +136,9 @@
|
|
|
136
136
|
<div class="slide-canvas">
|
|
137
137
|
<div class="template-frame">
|
|
138
138
|
<header>
|
|
139
|
-
<p class="template-eyebrow">Template 10 /
|
|
139
|
+
<p class="template-eyebrow">Template 10 / 17</p>
|
|
140
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">
|
|
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-text-panel--color template-chart-takeaway-panel" data-template-slot="takeaways">
|
|
142
142
|
<h2 class="template-text-panel-title">What to read</h2>
|
|
143
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
144
|
</div></div>
|
|
@@ -147,11 +147,23 @@
|
|
|
147
147
|
<div class="template-page-number">10</div>
|
|
148
148
|
</div>
|
|
149
149
|
</section>
|
|
150
|
-
<section class="slide template-slide" slide-qa="true" data-slide-index="11" data-design="built-in-preview" data-template="table
|
|
150
|
+
<section class="slide template-slide" slide-qa="true" data-slide-index="11" data-design="built-in-preview" data-template="table">
|
|
151
151
|
<div class="slide-canvas">
|
|
152
152
|
<div class="template-frame">
|
|
153
153
|
<header>
|
|
154
|
-
<p class="template-eyebrow">Template 11 /
|
|
154
|
+
<p class="template-eyebrow">Template 11 / 17</p>
|
|
155
|
+
<h1 class="template-title">table</h1>
|
|
156
|
+
</header><div class="template-body"><div class="template-table-layout"><div class="template-side-panel template-text-panel template-text-panel--clear" data-template-slot="text-card"><h2 class="template-side-panel-title template-text-panel-title">Financial readout</h2><p class="template-side-panel-body template-text-panel-body">Read top-line growth first, then check margin, cash conversion, and retention to see whether the plan is financially durable.</p></div><div class="template-table-region" data-template-slot="table"><div class="template-table-wrap"><table class="template-table"><thead><tr><th>Line item</th><th>FY2025</th><th>FY2026 Plan</th><th>YoY / note</th></tr></thead><tbody><tr><td>Revenue</td><td>$84.2M</td><td>$104.8M</td><td>+24% planned growth</td></tr><tr><td>Gross margin</td><td>68.4%</td><td>71.2%</td><td>+280 bps mix shift</td></tr><tr><td>Operating expense</td><td>$42.7M</td><td>$49.1M</td><td>Scale hiring below revenue growth</td></tr><tr><td>EBITDA</td><td>$14.9M</td><td>$23.6M</td><td>+58% operating leverage</td></tr><tr><td>Free cash flow</td><td>$9.8M</td><td>$16.4M</td><td>Cash conversion improves to 69%</td></tr><tr><td>Net retention</td><td>116%</td><td>121%</td><td>Expansion supports plan quality</td></tr></tbody></table></div></div></div></div>
|
|
157
|
+
|
|
158
|
+
</div>
|
|
159
|
+
<div class="template-page-number">11</div>
|
|
160
|
+
</div>
|
|
161
|
+
</section>
|
|
162
|
+
<section class="slide template-slide" slide-qa="true" data-slide-index="12" data-design="built-in-preview" data-template="table-comparison">
|
|
163
|
+
<div class="slide-canvas">
|
|
164
|
+
<div class="template-frame">
|
|
165
|
+
<header>
|
|
166
|
+
<p class="template-eyebrow">Template 12 / 17</p>
|
|
155
167
|
<h1 class="template-title">table-comparison</h1>
|
|
156
168
|
</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
169
|
<h2 class="template-insight-title"><i class="template-insight-icon" data-lucide="lightbulb" aria-hidden="true"></i><span>Insight</span></h2>
|
|
@@ -159,14 +171,14 @@
|
|
|
159
171
|
</div></div></div>
|
|
160
172
|
|
|
161
173
|
</div>
|
|
162
|
-
<div class="template-page-number">
|
|
174
|
+
<div class="template-page-number">12</div>
|
|
163
175
|
</div>
|
|
164
176
|
</section>
|
|
165
|
-
<section class="slide template-slide" slide-qa="true" data-slide-index="
|
|
177
|
+
<section class="slide template-slide" slide-qa="true" data-slide-index="13" data-design="built-in-preview" data-template="milestone">
|
|
166
178
|
<div class="slide-canvas">
|
|
167
179
|
<div class="template-frame">
|
|
168
180
|
<header>
|
|
169
|
-
<p class="template-eyebrow">Template
|
|
181
|
+
<p class="template-eyebrow">Template 13 / 17</p>
|
|
170
182
|
<h1 class="template-title">milestone</h1>
|
|
171
183
|
</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
184
|
<div class="template-timeline-copy template-card">
|
|
@@ -211,16 +223,16 @@
|
|
|
211
223
|
</article></div></div>
|
|
212
224
|
|
|
213
225
|
</div>
|
|
214
|
-
<div class="template-page-number">
|
|
226
|
+
<div class="template-page-number">13</div>
|
|
215
227
|
</div>
|
|
216
228
|
</section>
|
|
217
|
-
<section class="slide template-slide" slide-qa="true" data-slide-index="
|
|
229
|
+
<section class="slide template-slide" slide-qa="true" data-slide-index="14" data-design="built-in-preview" data-template="timeline">
|
|
218
230
|
<div class="slide-canvas">
|
|
219
231
|
<div class="template-frame">
|
|
220
232
|
<header>
|
|
221
|
-
<p class="template-eyebrow">Template
|
|
233
|
+
<p class="template-eyebrow">Template 14 / 17</p>
|
|
222
234
|
<h1 class="template-title">timeline</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">
|
|
235
|
+
</header><div class="template-body"><div class="template-timeline-layout template-timeline-layout--left"><div class="template-side-panel template-text-panel template-text-panel--color" 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
236
|
<span class="template-timeline-dot" aria-hidden="true"></span>
|
|
225
237
|
<div class="template-timeline-copy">
|
|
226
238
|
<p class="template-timeline-date">Mar 2019</p>
|
|
@@ -251,43 +263,43 @@
|
|
|
251
263
|
</article></div></div></div>
|
|
252
264
|
|
|
253
265
|
</div>
|
|
254
|
-
<div class="template-page-number">
|
|
266
|
+
<div class="template-page-number">14</div>
|
|
255
267
|
</div>
|
|
256
268
|
</section>
|
|
257
|
-
<section class="slide template-slide" slide-qa="true" data-slide-index="
|
|
269
|
+
<section class="slide template-slide" slide-qa="true" data-slide-index="15" data-design="built-in-preview" data-template="process-steps">
|
|
258
270
|
<div class="slide-canvas">
|
|
259
271
|
<div class="template-frame">
|
|
260
272
|
<header>
|
|
261
|
-
<p class="template-eyebrow">Template
|
|
273
|
+
<p class="template-eyebrow">Template 15 / 17</p>
|
|
262
274
|
<h1 class="template-title">process-steps</h1>
|
|
263
275
|
</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
276
|
|
|
265
277
|
</div>
|
|
266
|
-
<div class="template-page-number">
|
|
278
|
+
<div class="template-page-number">15</div>
|
|
267
279
|
</div>
|
|
268
280
|
</section>
|
|
269
|
-
<section class="slide template-slide" slide-qa="true" data-slide-index="
|
|
281
|
+
<section class="slide template-slide" slide-qa="true" data-slide-index="16" data-design="built-in-preview" data-template="recommendation-decision">
|
|
270
282
|
<div class="slide-canvas">
|
|
271
283
|
<div class="template-frame">
|
|
272
284
|
<header>
|
|
273
|
-
<p class="template-eyebrow">Template
|
|
285
|
+
<p class="template-eyebrow">Template 16 / 17</p>
|
|
274
286
|
<h1 class="template-title">recommendation-decision</h1>
|
|
275
287
|
</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
288
|
|
|
277
289
|
</div>
|
|
278
|
-
<div class="template-page-number">
|
|
290
|
+
<div class="template-page-number">16</div>
|
|
279
291
|
</div>
|
|
280
292
|
</section>
|
|
281
|
-
<section class="slide template-slide" slide-qa="true" data-slide-index="
|
|
293
|
+
<section class="slide template-slide" slide-qa="true" data-slide-index="17" data-design="built-in-preview" data-template="risks-tradeoffs">
|
|
282
294
|
<div class="slide-canvas">
|
|
283
295
|
<div class="template-frame">
|
|
284
296
|
<header>
|
|
285
|
-
<p class="template-eyebrow">Template
|
|
297
|
+
<p class="template-eyebrow">Template 17 / 17</p>
|
|
286
298
|
<h1 class="template-title">risks-tradeoffs</h1>
|
|
287
299
|
</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
300
|
|
|
289
301
|
</div>
|
|
290
|
-
<div class="template-page-number">
|
|
302
|
+
<div class="template-page-number">17</div>
|
|
291
303
|
</div>
|
|
292
304
|
</section>
|
|
293
305
|
<!-- revela-slides:end -->
|
|
@@ -142,6 +142,13 @@ const templates: PageTemplateDefinition[] = [
|
|
|
142
142
|
field("takeawaysTitle", "string", "Title for the interpretation text panel."),
|
|
143
143
|
field("items", "items[]", "Takeaways.", true),
|
|
144
144
|
], ["Chart area must be explicit and bounded."], ["Chart panel and takeaways both exist."]),
|
|
145
|
+
define("table", "Table", "Explain a structured table with a left reading card and right table region.", [
|
|
146
|
+
field("title", "string", "Slide title.", true),
|
|
147
|
+
field("textTitle", "string", "Left text card title."),
|
|
148
|
+
field("textBody", "string", "Left text card body."),
|
|
149
|
+
field("columns", "string[]", "Column labels.", true),
|
|
150
|
+
field("rows", "rows[]", "Table rows.", true),
|
|
151
|
+
], ["Use the text card to tell the audience how to read the table.", "Keep table rows structured and scannable."], ["Left text card and right table region both exist.", "Table has headers and body rows."]),
|
|
145
152
|
define("table-comparison", "Table / Comparison", "Compare options, segments, or facts in a structured table.", [
|
|
146
153
|
field("title", "string", "Slide title.", true),
|
|
147
154
|
field("columns", "string[]", "Column labels.", true),
|
|
@@ -277,6 +284,20 @@ export function builtInPreviewFixtures(): BuiltInPreviewFixture[] {
|
|
|
277
284
|
{ label: "Decision use", description: "Explain how the chart changes the recommendation, what threshold matters, and what follow-up evidence would reduce risk." },
|
|
278
285
|
],
|
|
279
286
|
}),
|
|
287
|
+
fixture("table", {
|
|
288
|
+
title: "table",
|
|
289
|
+
textTitle: "Financial readout",
|
|
290
|
+
textBody: "Read top-line growth first, then check margin, cash conversion, and retention to see whether the plan is financially durable.",
|
|
291
|
+
columns: ["Line item", "FY2025", "FY2026 Plan", "YoY / note"],
|
|
292
|
+
rows: [
|
|
293
|
+
["Revenue", "$84.2M", "$104.8M", "+24% planned growth"],
|
|
294
|
+
["Gross margin", "68.4%", "71.2%", "+280 bps mix shift"],
|
|
295
|
+
["Operating expense", "$42.7M", "$49.1M", "Scale hiring below revenue growth"],
|
|
296
|
+
["EBITDA", "$14.9M", "$23.6M", "+58% operating leverage"],
|
|
297
|
+
["Free cash flow", "$9.8M", "$16.4M", "Cash conversion improves to 69%"],
|
|
298
|
+
["Net retention", "116%", "121%", "Expansion supports plan quality"],
|
|
299
|
+
],
|
|
300
|
+
}),
|
|
280
301
|
fixture("table-comparison", {
|
|
281
302
|
title: "table-comparison",
|
|
282
303
|
columns: ["Layer", "Owns", "Agent task"],
|
|
@@ -546,19 +567,27 @@ ${lucentClosingBackgroundCss}
|
|
|
546
567
|
.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; }
|
|
547
568
|
.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; }
|
|
548
569
|
.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; }
|
|
549
|
-
.template-text-panel.template-chart-takeaway-panel { gap: 28px;
|
|
550
|
-
.template-
|
|
570
|
+
.template-text-panel.template-chart-takeaway-panel { gap: 28px; }
|
|
571
|
+
.template-text-panel--plain { background: rgba(255,255,255,0.74); border: 1px solid transparent; box-shadow: none; }
|
|
572
|
+
.template-text-panel--clear { background: transparent; border: 0; border-radius: 0; box-shadow: none; padding-left: 0; padding-right: 0; }
|
|
573
|
+
.template-text-panel--color { 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); }
|
|
574
|
+
.template-text-panel--color .template-text-panel-title { color: white; }
|
|
575
|
+
.template-text-panel--color .template-text-panel-body { color: rgba(255,255,255,0.78); }
|
|
551
576
|
.template-chart-takeaway-list { display: grid; gap: 22px; width: 100%; }
|
|
552
577
|
.template-chart-takeaway-item { display: grid; gap: 7px; padding-top: 18px; border-top: 1px solid rgba(255,255,255,0.24); }
|
|
553
578
|
.template-chart-takeaway-item:first-child { padding-top: 0; border-top: 0; }
|
|
554
579
|
.template-chart-takeaway-item h3 { margin: 0; font-size: 25px; line-height: 1.24; color: white; }
|
|
555
580
|
.template-chart-takeaway-item p { margin: 0; font-size: 20px; line-height: 1.46; color: rgba(255,255,255,0.78); }
|
|
556
581
|
.template-bar { flex: 1; background: linear-gradient(180deg, var(--accent-primary), var(--accent-cyan)); min-height: 80px; }
|
|
582
|
+
.template-table-layout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: 34px; height: 100%; align-items: stretch; }
|
|
583
|
+
.template-table-layout .template-side-panel { grid-column: 1; grid-row: 1; }
|
|
584
|
+
.template-table-region { grid-column: 2; grid-row: 1; min-width: 0; min-height: 0; height: 100%; }
|
|
585
|
+
.template-table-region .template-table-wrap { height: 100%; }
|
|
557
586
|
.template-table-wrap { display: grid; grid-template-rows: minmax(0, auto) auto; gap: 22px; height: 100%; align-content: start; }
|
|
558
587
|
.template-table { width: 100%; border-collapse: collapse; background: rgba(255,255,255,0.86); box-shadow: 0 18px 44px var(--shadow-soft); }
|
|
559
588
|
.template-table th, .template-table td { padding: 22px 24px; border-bottom: 1px solid var(--line); text-align: left; font-size: 21px; }
|
|
560
589
|
.template-table th { color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.12em; font-size: 15px; }
|
|
561
|
-
.template-text-panel { min-height: 0; display: flex; flex-direction: column; justify-content: flex-start; align-items: flex-start; gap: 20px;
|
|
590
|
+
.template-text-panel { min-height: 0; display: flex; flex-direction: column; justify-content: flex-start; align-items: flex-start; gap: 20px; border-radius: var(--surface-radius); padding: 42px; }
|
|
562
591
|
.template-text-panel-title { margin: 0; font-size: 34px; line-height: 1.28; color: var(--text-primary); padding-bottom: 4px; overflow: visible; }
|
|
563
592
|
.template-text-panel-body { margin: 0; font-size: 23px; line-height: 1.52; color: var(--text-secondary); }
|
|
564
593
|
.template-side-panel { align-self: stretch; }
|
|
@@ -580,9 +609,7 @@ ${lucentClosingBackgroundCss}
|
|
|
580
609
|
.template-timeline-layout--right { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
|
|
581
610
|
.template-timeline-layout--right .template-timeline { grid-column: 1; grid-row: 1; }
|
|
582
611
|
.template-timeline-layout--right .template-side-panel { grid-column: 2; grid-row: 1; }
|
|
583
|
-
.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); }
|
|
584
|
-
.template-timeline-layout .template-text-panel-title { color: white; }
|
|
585
|
-
.template-timeline-layout .template-text-panel-body { color: rgba(255,255,255,0.78); }
|
|
612
|
+
.template-timeline-layout .template-text-panel--color { background: linear-gradient(135deg, #7a7fe8 0%, #5f82c8 58%, #315eea 115%); color: white; box-shadow: 0 22px 56px rgba(49,94,234,0.22); }
|
|
586
613
|
.template-timeline--horizontal { grid-template-columns: repeat(var(--timeline-count), 1fr); column-gap: 18px; align-items: stretch; --timeline-axis-y: 86%; }
|
|
587
614
|
.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); }
|
|
588
615
|
.template-timeline-item { position: relative; min-height: 400px; display: grid; justify-items: center; align-items: center; }
|
|
@@ -656,6 +683,10 @@ ${lucentClosingBackgroundCss}
|
|
|
656
683
|
.template-frame--catalog .template-chart-takeaway-item { gap: 4px; padding-top: 11px; }
|
|
657
684
|
.template-frame--catalog .template-chart-takeaway-item h3 { font-size: 19px; line-height: 1.2; }
|
|
658
685
|
.template-frame--catalog .template-chart-takeaway-item p { font-size: 15px; line-height: 1.3; }
|
|
686
|
+
.template-frame--catalog .template-table-layout { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: 22px; }
|
|
687
|
+
.template-frame--catalog .template-table-layout .template-text-panel { padding: 22px; gap: 10px; }
|
|
688
|
+
.template-frame--catalog .template-table-layout .template-text-panel-title { font-size: 25px; line-height: 1.3; }
|
|
689
|
+
.template-frame--catalog .template-table-layout .template-text-panel-body { font-size: 18px; line-height: 1.4; }
|
|
659
690
|
.template-frame--catalog .template-table-wrap { gap: 16px; }
|
|
660
691
|
.template-frame--catalog .template-table th,
|
|
661
692
|
.template-frame--catalog .template-table td { padding: 14px 18px; font-size: 17px; line-height: 1.32; }
|
|
@@ -962,6 +993,7 @@ function renderBody(templateId: string, content: Record<string, any>): string {
|
|
|
962
993
|
if (templateId === "claim-supporting-visual") return `${renderHeader(content, "Claim + Supporting Visual")}<div class="template-body template-grid cols-2">${claimTextPanel(content)}${visualSlotPanel()}</div>`
|
|
963
994
|
if (templateId === "metric-highlight") return `${renderHeader(content, "Metric Highlight")}<div class="template-body">${metricHighlight(content)}</div>`
|
|
964
995
|
if (templateId === "chart-takeaways") return `${renderHeader(content, "Chart + Takeaways")}<div class="template-body template-grid template-chart-layout">${visualSlotPanel()}${chartTakeawayPanel(content)}</div>`
|
|
996
|
+
if (templateId === "table") return `${renderHeader(content, "Table")}<div class="template-body">${tablePage(content)}</div>`
|
|
965
997
|
if (templateId === "table-comparison") return `${renderHeader(content, "Table / Comparison")}<div class="template-body" data-template-slot="table">${table(content)}</div>`
|
|
966
998
|
if (templateId === "milestone") return `${renderHeader(content, "Milestone")}<div class="template-body">${timeline({ ...content, orientation: "horizontal" })}</div>`
|
|
967
999
|
if (templateId === "timeline") return `${renderHeader(content, "Timeline")}<div class="template-body">${timeline({ ...content, orientation: "vertical" })}</div>`
|
|
@@ -1010,7 +1042,7 @@ function evidenceCards(items: Array<{ label: string; description: string; image?
|
|
|
1010
1042
|
function chartTakeawayPanel(content: Record<string, any>): string {
|
|
1011
1043
|
const takeawayItems = items(content)
|
|
1012
1044
|
const title = stringValue(content.takeawaysTitle) || "What to read"
|
|
1013
|
-
return `<div class="template-text-panel template-chart-takeaway-panel" data-template-slot="takeaways">
|
|
1045
|
+
return `<div class="template-text-panel template-text-panel--color template-chart-takeaway-panel" data-template-slot="takeaways">
|
|
1014
1046
|
<h2 class="template-text-panel-title">${escapeHtml(title)}</h2>
|
|
1015
1047
|
<div class="template-chart-takeaway-list">${takeawayItems.map((item) => `<section class="template-chart-takeaway-item"><h3>${escapeHtml(item.label)}</h3><p>${escapeHtml(item.description)}</p></section>`).join("")}</div>
|
|
1016
1048
|
</div>`
|
|
@@ -1044,6 +1076,14 @@ function table(content: Record<string, any>): string {
|
|
|
1044
1076
|
return `<div class="template-table-wrap"><table class="template-table"><thead><tr>${columns.map((column) => `<th>${escapeHtml(column)}</th>`).join("")}</tr></thead><tbody>${rows.map((row) => `<tr>${columns.map((column, index) => `<td>${escapeHtml(Array.isArray(row) ? stringValue(row[index]) : stringValue(row[column]) || stringValue(row[slug(column)]))}</td>`).join("")}</tr>`).join("")}</tbody></table>${insight}</div>`
|
|
1045
1077
|
}
|
|
1046
1078
|
|
|
1079
|
+
function tablePage(content: Record<string, any>): string {
|
|
1080
|
+
const panelContent = {
|
|
1081
|
+
insightTitle: stringValue(content.textTitle) || "What to read",
|
|
1082
|
+
insightBody: stringValue(content.textBody) || "Use this card to explain the comparison, caveat, or decision implication before the audience scans the table.",
|
|
1083
|
+
}
|
|
1084
|
+
return `<div class="template-table-layout">${renderTextPanel(panelContent, "text-card", "clear")}<div class="template-table-region" data-template-slot="table">${table({ ...content, insightTitle: "", insightBody: "" })}</div></div>`
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1047
1087
|
function renderInsightPanel(content: Record<string, any>): string {
|
|
1048
1088
|
const body = stringValue(content.insightBody)
|
|
1049
1089
|
if (!body) return ""
|
|
@@ -1090,14 +1130,14 @@ function timelineMilestone(item: any, orientation: "horizontal" | "vertical"): s
|
|
|
1090
1130
|
}
|
|
1091
1131
|
|
|
1092
1132
|
function renderSidePanel(content: Record<string, any>): string {
|
|
1093
|
-
return renderTextPanel(content)
|
|
1133
|
+
return renderTextPanel(content, "insight", "color")
|
|
1094
1134
|
}
|
|
1095
1135
|
|
|
1096
|
-
function renderTextPanel(content: Record<string, any
|
|
1136
|
+
function renderTextPanel(content: Record<string, any>, slot = "insight", variant: "plain" | "clear" | "color" = "plain"): string {
|
|
1097
1137
|
const body = stringValue(content.insightBody)
|
|
1098
1138
|
if (!body) return ""
|
|
1099
1139
|
const title = stringValue(content.insightTitle) || "Insight"
|
|
1100
|
-
return `<div class="template-side-panel template-text-panel" data-template-slot="
|
|
1140
|
+
return `<div class="template-side-panel template-text-panel template-text-panel--${variant}" data-template-slot="${escapeAttribute(slot)}"><h2 class="template-side-panel-title template-text-panel-title">${escapeHtml(title)}</h2><p class="template-side-panel-body template-text-panel-body">${escapeHtml(body)}</p></div>`
|
|
1101
1141
|
}
|
|
1102
1142
|
|
|
1103
1143
|
function imageCard(input: any): string {
|
|
@@ -1150,6 +1190,7 @@ function scaffoldSeed(templateId: string, seed: Record<string, any>): Record<str
|
|
|
1150
1190
|
if (templateId === "claim-supporting-visual") return { claim: "Replace with one visual claim.", body: "Use this copy to guide how the visual should be read.", items: defaultItems(["Anchor", "Callout"]), ...base }
|
|
1151
1191
|
if (templateId === "metric-highlight") return { metrics: [{ value: "67%", label: "Metric", description: "Replace with interpretation." }, { value: "3x", label: "Comparison", description: "Replace with reading note." }, { value: "14d", label: "Window", description: "Replace with time context." }], insightTitle: "Read the signal", insightBody: "Replace with the decision implication, caveat, or next reading step.", ...base }
|
|
1152
1192
|
if (templateId === "chart-takeaways") return { takeawaysTitle: "What to read", items: defaultItems(["Trend", "Driver", "Decision use"]), ...base }
|
|
1193
|
+
if (templateId === "table") return { textTitle: "Financial readout", textBody: "Replace with the table reading note, caveat, or decision implication.", columns: ["Line item", "FY2025", "FY2026 Plan", "YoY / note"], rows: [["Revenue", "$84.2M", "$104.8M", "+24% planned growth"], ["Gross margin", "68.4%", "71.2%", "+280 bps mix shift"], ["Operating expense", "$42.7M", "$49.1M", "Scale hiring below revenue growth"], ["EBITDA", "$14.9M", "$23.6M", "+58% operating leverage"], ["Free cash flow", "$9.8M", "$16.4M", "Cash conversion improves"], ["Net retention", "116%", "121%", "Expansion supports plan quality"]], ...base }
|
|
1153
1194
|
if (templateId === "table-comparison") return { columns: ["Dimension", "Current", "Target"], rows: [["Replace", "Current state", "Target state"], ["Caveat", "Known limit", "Next proof"]], insightTitle: "Insight", insightBody: "Replace with the table reading note or caveat.", ...base }
|
|
1154
1195
|
if (templateId === "milestone" || templateId === "timeline-roadmap") return { orientation: "horizontal", milestones: [{ date: "2022", label: "Signal", description: "Name the starting condition." }, { date: "2023", label: "Proof", description: "Show the evidence threshold." }, { date: "2024", label: "Inflection", description: "Use the pivotal moment to frame the shift." }, { date: "2025", label: "Scale", description: "Use a taller card for the highlighted milestone.", highlight: true }, { date: "2026", label: "Decision", description: "State what changes next." }], ...base }
|
|
1155
1196
|
if (templateId === "timeline") return { orientation: "vertical", insightTitle: "Reading the journey", insightBody: "Replace with the timeline interpretation or caveat.", milestones: [{ date: "Mar 2019", label: "Launch", description: "Name the starting event." }, { date: "Nov 2019", label: "Audit", description: "Show the evidence threshold." }, { date: "May 2020", label: "Scale", description: "Explain the operating cadence." }, { date: "Feb 2021", label: "Review", description: "State what changes next." }], ...base }
|
|
@@ -16,6 +16,7 @@ export * from "./process-steps"
|
|
|
16
16
|
export * from "./recommendation-decision"
|
|
17
17
|
export * from "./risks-tradeoffs"
|
|
18
18
|
export * from "./section-divider"
|
|
19
|
+
export * from "./table"
|
|
19
20
|
export * from "./table-comparison"
|
|
20
21
|
export * from "./timeline"
|
|
21
22
|
export * from "./timeline-roadmap"
|
|
@@ -55,10 +55,11 @@ export const PAGE_TEMPLATE_VOCABULARY: PageTemplateVocabulary[] = [
|
|
|
55
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
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
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."]),
|
|
58
|
+
vocab("chart-takeaways", ["template-chart-panel", "template-chart-takeaway-panel", "template-text-panel--color"], ["visual", "takeaways"], ["visual", "takeaways"], ["Chart/image slot and color takeaway text panel must both remain present."]),
|
|
59
|
+
vocab("table", ["template-table-layout", "template-table-wrap", "template-table", "template-side-panel", "template-text-panel", "template-text-panel--clear"], ["text-card", "table"], ["text-card", "table"], ["Left clear text card explains how to read the structured table.", "Table headers and body should remain structured, not prose-only."]),
|
|
59
60
|
vocab("table-comparison", ["template-table-wrap", "template-table"], ["table"], ["table", "insight"], ["Table headers and body should remain structured, not prose-only."]),
|
|
60
61
|
vocab("milestone", ["template-timeline", "template-timeline-item", "template-timeline-dot", "template-timeline-copy", "template-insight-icon"], ["timeline"], ["timeline"], ["Each milestone item must keep dot and copy as sibling anchors inside one item.", "Milestone cards reuse .template-card; highlight uses the item modifier."]),
|
|
61
|
-
vocab("timeline", ["template-timeline", "template-timeline-item", "template-timeline-dot", "template-timeline-copy"], ["timeline"], ["timeline", "insight"], ["Each timeline item must keep dot and copy as sibling anchors inside one item.", "The optional insight slot explains the sequence without replacing event copy."]),
|
|
62
|
+
vocab("timeline", ["template-timeline", "template-timeline-item", "template-timeline-dot", "template-timeline-copy"], ["timeline"], ["timeline", "insight"], ["Each timeline item must keep dot and copy as sibling anchors inside one item.", "The optional color insight slot explains the sequence without replacing event copy."]),
|
|
62
63
|
vocab("process-steps", ["template-steps", "template-step-number"], ["steps"], ["steps"], ["Steps should remain ordered in DOM order."]),
|
|
63
64
|
vocab("recommendation-decision", ["template-card"], ["recommendation", "rationale", "next-steps"], ["recommendation", "rationale", "next-steps"], ["Keep recommendation, rationale, and next steps separate."]),
|
|
64
65
|
vocab("risks-tradeoffs", ["template-card"], ["risks"], ["risks"], ["Risk/tradeoff cards should name uncertainty explicitly."]),
|
|
@@ -91,6 +92,8 @@ const additionalClasses = [
|
|
|
91
92
|
"template-chart-takeaway-list",
|
|
92
93
|
"template-chart-takeaway-item",
|
|
93
94
|
"template-bar",
|
|
95
|
+
"template-table-layout",
|
|
96
|
+
"template-table-region",
|
|
94
97
|
"template-table",
|
|
95
98
|
"template-table-wrap",
|
|
96
99
|
"template-side-panel",
|
|
@@ -99,6 +102,9 @@ const additionalClasses = [
|
|
|
99
102
|
"template-side-panel--left",
|
|
100
103
|
"template-side-panel--right",
|
|
101
104
|
"template-text-panel",
|
|
105
|
+
"template-text-panel--plain",
|
|
106
|
+
"template-text-panel--clear",
|
|
107
|
+
"template-text-panel--color",
|
|
102
108
|
"template-text-panel-title",
|
|
103
109
|
"template-text-panel-body",
|
|
104
110
|
"template-insight-panel",
|