demo-renderer.js

  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