1// ============================================
2// DEMO RENDERER - Generic rendering for command and skill demos
3// ============================================
4
5import { getCommandDemo } from './demos/commands/index.js';
6import { getSkillDemo } from './demos/skills/index.js';
7
8/**
9 * Initialize a command demo's JS after its HTML has been inserted into the DOM.
10 * Call this after innerHTML is set and split compare is initialized.
11 */
12export function initCommandDemo(commandId, container) {
13 const demo = getCommandDemo(commandId);
14 if (demo && typeof demo.init === 'function') {
15 const demoArea = container.querySelector('.split-after .split-content') || container;
16 console.log('[initCommandDemo]', commandId, 'demoArea:', demoArea);
17 demo.init(demoArea);
18 }
19}
20
21/**
22 * Render a command demo with split-screen comparison
23 */
24export function renderCommandDemo(commandId) {
25 const demo = getCommandDemo(commandId);
26
27 if (!demo) {
28 // impeccable has multiple modes — show a usage guide
29 if (commandId === 'impeccable') {
30 return `
31 <div class="demo-container">
32 <div class="demo-viewport" style="padding: var(--spacing-lg); font-size: 13px; line-height: 1.6;">
33 <div style="display: flex; flex-direction: column; gap: 16px; color: var(--color-ash);">
34 <div style="font-size: 14px; color: var(--color-text); font-weight: 600;">Three ways to use /impeccable</div>
35 <div style="display: flex; flex-direction: column; gap: 14px;">
36 <div style="display: flex; flex-direction: column; gap: 4px;">
37 <div style="display: flex; gap: 8px; align-items: baseline;">
38 <code style="font-size: 12px; color: var(--spread-accent, var(--color-accent)); font-weight: 600; white-space: nowrap;">/impeccable</code>
39 <span style="opacity: 0.4; font-size: 11px;">freeform</span>
40 </div>
41 <span style="padding-left: 0; opacity: 0.8;">Use on any task. Loads full design intelligence, anti-patterns, and reference knowledge into the current context.</span>
42 </div>
43 <div style="display: flex; flex-direction: column; gap: 4px;">
44 <div style="display: flex; gap: 8px; align-items: baseline;">
45 <code style="font-size: 12px; color: var(--spread-accent, var(--color-accent)); font-weight: 600; white-space: nowrap;">/impeccable teach</code>
46 <span style="opacity: 0.4; font-size: 11px;">one-time setup</span>
47 </div>
48 <span style="padding-left: 0; opacity: 0.8;">Scans your codebase, interviews you about brand and audience, then saves a Design Context that all other commands use automatically.</span>
49 </div>
50 <div style="display: flex; flex-direction: column; gap: 4px;">
51 <div style="display: flex; gap: 8px; align-items: baseline;">
52 <code style="font-size: 12px; color: var(--spread-accent, var(--color-accent)); font-weight: 600; white-space: nowrap;">/impeccable craft</code>
53 <span style="opacity: 0.4; font-size: 11px;">build a feature</span>
54 </div>
55 <span style="padding-left: 0; opacity: 0.8;">Runs /shape to plan UX first, loads the right references, then builds and iterates visually until the result delights.</span>
56 </div>
57 </div>
58 <div style="font-size: 12px; opacity: 0.5; margin-top: 2px; font-style: italic;">Start with <code style="font-size: 11px;">/impeccable teach</code> once per project. Then use the other modes as needed.</div>
59 </div>
60 </div>
61 </div>
62 `;
63 }
64 // craft is the full end-to-end flow, show the four stages
65 if (commandId === 'craft') {
66 return `
67 <div class="demo-container">
68 <div class="demo-viewport" style="padding: var(--spacing-lg); font-size: 13px; line-height: 1.6;">
69 <div style="display: flex; flex-direction: column; gap: 16px; color: var(--color-ash);">
70 <div style="font-size: 14px; color: var(--color-text); font-weight: 600;">Shape, reference, build, iterate</div>
71 <div style="display: flex; flex-direction: column; gap: 14px;">
72 <div style="display: flex; flex-direction: column; gap: 4px;">
73 <div style="display: flex; gap: 8px; align-items: baseline;">
74 <span style="color: var(--spread-accent, var(--color-accent)); font-weight: 600; font-size: 12px;">1. Shape</span>
75 </div>
76 <span style="opacity: 0.8;">Runs <code style="font-size: 11px;">/impeccable shape</code> internally to build a design brief from discovery questions. No code yet.</span>
77 </div>
78 <div style="display: flex; flex-direction: column; gap: 4px;">
79 <div style="display: flex; gap: 8px; align-items: baseline;">
80 <span style="color: var(--spread-accent, var(--color-accent)); font-weight: 600; font-size: 12px;">2. Reference</span>
81 </div>
82 <span style="opacity: 0.8;">Loads the right reference files for the feature (spatial design, typography, motion, color) based on what the brief calls for.</span>
83 </div>
84 <div style="display: flex; flex-direction: column; gap: 4px;">
85 <div style="display: flex; gap: 8px; align-items: baseline;">
86 <span style="color: var(--spread-accent, var(--color-accent)); font-weight: 600; font-size: 12px;">3. Build</span>
87 </div>
88 <span style="opacity: 0.8;">Implements structure, spacing, type, color, states, motion, responsive. Every decision traces back to the brief.</span>
89 </div>
90 <div style="display: flex; flex-direction: column; gap: 4px;">
91 <div style="display: flex; gap: 8px; align-items: baseline;">
92 <span style="color: var(--spread-accent, var(--color-accent)); font-weight: 600; font-size: 12px;">4. Visual iteration</span>
93 </div>
94 <span style="opacity: 0.8;">Opens the result in a browser, checks against the brief and anti-pattern list, refines until the polish bar is high.</span>
95 </div>
96 </div>
97 <div style="font-size: 12px; opacity: 0.5; margin-top: 2px; font-style: italic;">The full shape-then-build flow in one command. Best for brand-new features.</div>
98 </div>
99 </div>
100 </div>
101 `;
102 }
103 // teach sets up the project's design context, show the flow
104 if (commandId === 'teach') {
105 return `
106 <div class="demo-container">
107 <div class="demo-viewport" style="padding: var(--spacing-lg); font-size: 13px; line-height: 1.6;">
108 <div style="display: flex; flex-direction: column; gap: 16px; color: var(--color-ash);">
109 <div style="font-size: 14px; color: var(--color-text); font-weight: 600;">One-time project setup</div>
110 <div style="display: flex; flex-direction: column; gap: 14px;">
111 <div style="display: flex; flex-direction: column; gap: 4px;">
112 <div style="display: flex; gap: 8px; align-items: baseline;">
113 <span style="color: var(--spread-accent, var(--color-accent)); font-weight: 600; font-size: 12px;">1. Explore</span>
114 </div>
115 <span style="opacity: 0.8;">Scans the codebase for brand assets, existing design tokens, typography, components, and documentation.</span>
116 </div>
117 <div style="display: flex; flex-direction: column; gap: 4px;">
118 <div style="display: flex; gap: 8px; align-items: baseline;">
119 <span style="color: var(--spread-accent, var(--color-accent)); font-weight: 600; font-size: 12px;">2. Interview</span>
120 </div>
121 <span style="opacity: 0.8;">Asks about audience, brand personality, aesthetic direction, and accessibility needs. Skips anything it can infer from the code.</span>
122 </div>
123 <div style="display: flex; flex-direction: column; gap: 4px;">
124 <div style="display: flex; gap: 8px; align-items: baseline;">
125 <span style="color: var(--spread-accent, var(--color-accent)); font-weight: 600; font-size: 12px;">3. Save</span>
126 </div>
127 <span style="opacity: 0.8;">Writes a <code style="font-size: 11px;">PRODUCT.md</code> file with users, brand, aesthetic direction, and design principles. Every future command reads it automatically.</span>
128 </div>
129 </div>
130 <div style="font-size: 12px; opacity: 0.5; margin-top: 2px; font-style: italic;">Run once per project. Then forget it exists.</div>
131 </div>
132 </div>
133 </div>
134 `;
135 }
136 // shape is a planning skill — show the process
137 if (commandId === 'shape') {
138 return `
139 <div class="demo-container">
140 <div class="demo-viewport" style="padding: var(--spacing-lg); font-size: 13px; line-height: 1.6;">
141 <div style="display: flex; flex-direction: column; gap: 16px; color: var(--color-ash);">
142 <div style="font-size: 14px; color: var(--color-text); font-weight: 600;">Design before you build</div>
143 <div style="display: flex; flex-direction: column; gap: 14px;">
144 <div style="display: flex; flex-direction: column; gap: 4px;">
145 <div style="display: flex; gap: 8px; align-items: baseline;">
146 <span style="color: var(--spread-accent, var(--color-accent)); font-weight: 600; font-size: 12px;">1. Discovery</span>
147 </div>
148 <span style="opacity: 0.8;">Interviews you about purpose, audience, content, constraints, and anti-goals. Adapts questions based on your answers.</span>
149 </div>
150 <div style="display: flex; flex-direction: column; gap: 4px;">
151 <div style="display: flex; gap: 8px; align-items: baseline;">
152 <span style="color: var(--spread-accent, var(--color-accent)); font-weight: 600; font-size: 12px;">2. Design Brief</span>
153 </div>
154 <span style="opacity: 0.8;">Synthesizes a 9-section brief: feature summary, primary action, design direction, layout strategy, key states, interaction model, content needs, recommended references, and open questions.</span>
155 </div>
156 <div style="display: flex; flex-direction: column; gap: 4px;">
157 <div style="display: flex; gap: 8px; align-items: baseline;">
158 <span style="color: var(--spread-accent, var(--color-accent)); font-weight: 600; font-size: 12px;">3. Handoff</span>
159 </div>
160 <span style="opacity: 0.8;">The confirmed brief guides <code style="font-size: 11px;">/impeccable craft</code> or any other implementation approach. No code written, just the thinking that makes code good.</span>
161 </div>
162 </div>
163 <div style="font-size: 12px; opacity: 0.5; margin-top: 2px; font-style: italic;">Use standalone or as the first step of <code style="font-size: 11px;">/impeccable craft</code>.</div>
164 </div>
165 </div>
166 </div>
167 `;
168 }
169 return `
170 <div class="demo-container">
171 <div class="demo-viewport">
172 <div style="text-align: center; color: var(--color-ash); font-style: italic; padding: var(--spacing-lg);">
173 Visual demo for /${commandId} coming soon
174 </div>
175 </div>
176 </div>
177 `;
178 }
179
180 // Use split-screen comparison
181 return `
182 <div class="demo-split-comparison" data-demo="command-${demo.id}">
183 <div class="split-container">
184 <div class="split-before">
185 <div class="split-content">${demo.before}</div>
186 </div>
187 <div class="split-after">
188 <div class="split-content">${demo.after || demo.before}</div>
189 </div>
190 <div class="split-divider"></div>
191 </div>
192 <div class="demo-caption">${demo.caption}</div>
193 </div>
194 `;
195}
196
197/**
198 * Render a skill demo (with tabs if multiple demos)
199 */
200export function renderSkillDemo(skillId) {
201 const skill = getSkillDemo(skillId);
202
203 if (!skill || !skill.tabs || skill.tabs.length === 0) {
204 return `
205 <div class="demo-container">
206 <div class="demo-viewport">
207 <div style="text-align: center; color: var(--color-ash); padding: var(--spacing-xl);">
208 <p>Demo for ${skillId.replace(/-/g, ' ')} coming soon</p>
209 </div>
210 </div>
211 </div>
212 `;
213 }
214
215 const showTabs = skill.tabs.length > 1;
216
217 const tabs = showTabs ? skill.tabs.map((tab, i) => `
218 <button class="demo-tab ${i === 0 ? 'active' : ''}" data-demo-tab="${tab.id}" data-skill="${skillId}">
219 ${tab.label}
220 </button>
221 `).join('') : '';
222
223 const panels = skill.tabs.map((tab, i) => `
224 <div class="demo-panel ${i === 0 ? 'active' : ''}" data-demo-panel="${tab.id}">
225 ${renderSkillTabDemo(skillId, tab)}
226 </div>
227 `).join('');
228
229 return `
230 <div class="demo-tabbed-container">
231 ${showTabs ? `<div class="demo-tabs">${tabs}</div>` : ''}
232 <div class="demo-panels">
233 ${panels}
234 </div>
235 </div>
236 `;
237}
238
239/**
240 * Render a single skill tab demo
241 */
242function renderSkillTabDemo(skillId, tab) {
243 const hasToggle = tab.hasToggle !== false;
244 const demoId = `${skillId}-${tab.id}`;
245
246 return `
247 <div class="demo-container">
248 <div class="demo-header">
249 ${hasToggle ? `
250 <div class="demo-toggle">
251 <span class="demo-toggle-label active" id="${demoId}-before-label">Before</span>
252 <button class="demo-toggle-switch" data-demo="${demoId}" role="switch" aria-checked="false" aria-labelledby="${demoId}-before-label ${demoId}-after-label"></button>
253 <span class="demo-toggle-label" id="${demoId}-after-label">After</span>
254 </div>
255 ` : ''}
256 </div>
257 <div class="demo-viewport" data-state="before" id="${demoId}-viewport">
258 ${tab.before}
259 </div>
260 <div class="demo-caption">${tab.caption}</div>
261 </div>
262 `;
263}
264
265/**
266 * Setup demo tab switching
267 */
268export function setupDemoTabs() {
269 document.querySelectorAll('.demo-tab').forEach(tab => {
270 tab.addEventListener('click', () => {
271 const tabId = tab.dataset.demoTab;
272 const container = tab.closest('.demo-tabbed-container');
273
274 container.querySelectorAll('.demo-tab').forEach(t => t.classList.remove('active'));
275 tab.classList.add('active');
276
277 container.querySelectorAll('.demo-panel').forEach(p => p.classList.remove('active'));
278 container.querySelector(`[data-demo-panel="${tabId}"]`)?.classList.add('active');
279 });
280 });
281}
282
283
284