/* * Dev-only Impeccable Live state gallery. * * This is a static state harness, not a second implementation of Live. It * mirrors the exact chrome vocabulary and state wording from: * skill/scripts/live-browser.js * skill/scripts/live/ui-core.mjs * Command labels/icons are injected by LiveUiGallery.astro from the canonical * skill/scripts/live/vocabulary.mjs export. */ const ICONS = Object.freeze({ pick: '', insert: '', detect: '', chat: '', voice: '', submit: '', tune: '', edit: '', trash: '', exit: '', }); function parseJson(root, selector, fallback = []) { try { return JSON.parse(root.querySelector(selector)?.textContent || '[]'); } catch { return fallback; } } function escapeHtml(value) { return String(value ?? '') .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>') .replaceAll('"', '"') .replaceAll("'", '''); } function brandMark() { return ''; } function designMark() { return ''; } function hostPage({ edited = false, insert = false } = {}) { return `

Northstar field journal · Edition 08

${edited ? 'Useful observations, refined.' : 'Useful observations from the long way around.'}

Four routes, annotated maps, and practical details for unhurried weekends.

`; } function selectionOutline() { return ''; } function dots({ arrived = 0, visible = 0, expected = 3, clickable = false } = {}) { let html = ''; for (let index = 1; index <= expected; index += 1) { const active = index === visible; const pending = index > arrived; const className = `lvg-dot${active ? ' is-active' : ''}${pending ? ' is-pending' : ''}`; if (clickable && !pending) { const target = index === 1 ? 'cycling-progressive' : 'cycling-second'; html += ``; } else { html += ``; } } return html + ''; } function configureBar({ actionLabel, count, listening = false, locked = false, insert = false, picker = '' } = {}) { const inputValue = insert ? 'Add a compact proof strip' : listening ? 'Tighten the hierarchy' : locked ? '' : 'Make this feel more confident'; const actionControl = insert ? '' : ` `; return `
${actionControl}
${picker}
`; } function actionPicker(commands, selectedAction) { return `
${commands.map((command) => ` `).join('')}
`; } function generatingBar(recovery = false, actionLabel = 'Freeform') { return `
${escapeHtml(actionLabel)} ${dots({ expected: 3 })} ${recovery ? 'Variants ready. Reveal the selected element to resume.' : 'Source ready. Generating...'}
`; } function tuneButton(open) { return ` `; } function cyclingBar({ arrived = 1, visible = 1, tune = false } = {}) { const remaining = 3 - arrived; return `
${dots({ arrived, visible, expected: 3, clickable: true })} ${visible}/3 ${tuneButton(tune)} ${remaining > 0 ? `${remaining} more arriving...` : ''}
${tune ? tunePanel() : ''}
`; } function tunePanel() { return `
Color amount0.60
DensitySnug
MotionOff
`; } function statusBar(kind) { if (kind === 'confirmed') { return '
Variant applied
'; } return '
Applying variant...
'; } function editBadge(editing = false) { if (!editing) { return `
`; } return '
'; } function annotationLayer() { return `
Keep this line on one row
`; } function pendingDock(kind) { if (kind === 'attention') { return `
`; } const applying = kind === 'applying'; return `
`; } function designPanel(tab = 'visual') { const raw = tab === 'raw'; return ` `; } function globalBar({ connected = true, active = 'pick', steer = 'collapsed', detectCount = 0, designActive = false } = {}) { const modeButton = (key, icon, label, target, extra = '') => { const isActive = active === key || (key === 'design' && designActive); return ``; }; const steerHtml = steer === 'processing' ? `
${ICONS.chat}
` : steer === 'expanded' ? `
${ICONS.chat}
` : ``; return `
${brandMark()}${connected ? '' : ''}
${modeButton('pick', ICONS.pick, 'Pick', 'configure-replace')} ${modeButton('insert', ICONS.insert, 'Insert', 'insert-placeholder')} ${modeButton('detect', ICONS.detect, 'Detect', 'global-tools', detectCount ? `${detectCount}` : '')} ${modeButton('design', designMark(), 'DESIGN.md', 'design-panel')} ${steerHtml}
`; } function sceneFor(state, context) { const actionLabel = context.commands.find((command) => command.value === context.selectedAction)?.label || 'Freeform'; const commonGlobal = (opts = {}) => globalBar({ active: 'pick', ...opts }); switch (state) { case 'global-disconnected': return hostPage() + '' + commonGlobal({ connected: false }); case 'global-tools': return hostPage() + commonGlobal({ active: 'detect', detectCount: 7, designActive: true }); case 'steer-expanded': return hostPage() + commonGlobal({ steer: 'expanded' }); case 'steer-processing': return hostPage() + commonGlobal({ steer: 'processing' }); case 'configure-replace': return hostPage() + selectionOutline() + editBadge(false) + configureBar({ actionLabel, count: context.count }) + commonGlobal(); case 'action-picker': return hostPage() + selectionOutline() + editBadge(false) + configureBar({ actionLabel, count: context.count, picker: actionPicker(context.commands, context.selectedAction) }) + commonGlobal(); case 'configure-listening': return hostPage() + selectionOutline() + editBadge(false) + configureBar({ actionLabel, count: context.count, listening: true }) + commonGlobal(); case 'configure-locked': return hostPage({ edited: true }) + selectionOutline() + editBadge(false) + configureBar({ actionLabel, count: context.count, locked: true }) + pendingDock('applying') + commonGlobal(); case 'annotation': return hostPage() + selectionOutline() + annotationLayer() + configureBar({ actionLabel, count: context.count }) + commonGlobal(); case 'insert-placeholder': return hostPage({ insert: true }) + '
' + configureBar({ count: context.count, insert: true }) + globalBar({ active: 'insert' }); case 'generating': return hostPage() + selectionOutline() + generatingBar(false, actionLabel) + commonGlobal(); case 'generation-recovery': return hostPage() + generatingBar(true, actionLabel) + commonGlobal(); case 'cycling-progressive': return hostPage() + selectionOutline() + cyclingBar({ arrived: 1, visible: 1 }) + commonGlobal(); case 'cycling-second': return hostPage() + selectionOutline() + cyclingBar({ arrived: 2, visible: 2 }) + commonGlobal(); case 'tune-open': return hostPage() + selectionOutline() + cyclingBar({ arrived: 3, visible: 2, tune: true }) + commonGlobal(); case 'applying': return hostPage() + selectionOutline() + statusBar('applying') + commonGlobal(); case 'confirmed': return hostPage({ edited: true }) + statusBar('confirmed') + commonGlobal(); case 'edit-copy': return hostPage({ edited: true }) + selectionOutline() + editBadge(true) + configureBar({ actionLabel, count: context.count }) + commonGlobal(); case 'copy-pending': return hostPage({ edited: true }) + pendingDock('pending') + commonGlobal(); case 'copy-applying': return hostPage({ edited: true }) + pendingDock('applying') + commonGlobal(); case 'copy-attention': return hostPage({ edited: true }) + pendingDock('attention') + commonGlobal(); case 'design-panel': return hostPage() + designPanel(context.designTab) + globalBar({ active: 'design', designActive: true }); case 'toast-error': return hostPage() + '' + commonGlobal(); case 'global-ready': default: return hostPage() + commonGlobal(); } } function initLiveUiGallery(root) { if (!root || root.dataset.galleryReady === 'true') return; root.dataset.galleryReady = 'true'; const states = parseJson(root, '[data-live-gallery-states]'); const commands = parseJson(root, '[data-live-gallery-vocabulary]'); if (states.length === 0 || commands.length === 0) return; const select = root.querySelector('[data-gallery-state]'); const groupReadout = root.querySelector('[data-gallery-state-group]'); const labelReadout = root.querySelector('[data-gallery-state-label]'); const quickNav = root.querySelector('.live-ui-gallery__quick-nav'); const previews = [...root.querySelectorAll('[data-live-gallery-preview]')]; let selectedAction = 'impeccable'; let count = 3; let designTab = 'visual'; const stateIndex = (key) => Math.max(0, states.findIndex((state) => state.key === key)); const currentState = () => select?.value || states[0].key; function render(nextState = currentState(), { focusSelect = false } = {}) { const state = states[stateIndex(nextState)] || states[0]; if (select) select.value = state.key; if (groupReadout) groupReadout.textContent = state.group; if (labelReadout) labelReadout.textContent = state.label; let activeStateButton = null; root.querySelectorAll('[data-gallery-state-button]').forEach((button) => { const active = button.dataset.galleryStateButton === state.key; button.setAttribute('aria-current', active ? 'true' : 'false'); if (active) activeStateButton = button; }); for (const preview of previews) { preview.dataset.galleryState = state.key; preview.setAttribute('aria-label', `${preview.dataset.liveGalleryPreview} host — ${state.label}`); preview.innerHTML = sceneFor(state.key, { commands, selectedAction, count, designTab }); } if (quickNav && activeStateButton) { requestAnimationFrame(() => { const navRect = quickNav.getBoundingClientRect(); const buttonRect = activeStateButton.getBoundingClientRect(); if (buttonRect.left < navRect.left) quickNav.scrollLeft -= navRect.left - buttonRect.left + 8; if (buttonRect.right > navRect.right) quickNav.scrollLeft += buttonRect.right - navRect.right + 8; }); } if (focusSelect) select?.focus(); } function step(delta) { const next = (stateIndex(currentState()) + delta + states.length) % states.length; render(states[next].key, { focusSelect: true }); } select?.addEventListener('change', () => render(select.value)); root.addEventListener('click', (event) => { const stepButton = event.target.closest('[data-gallery-step]'); if (stepButton) { step(Number(stepButton.dataset.galleryStep) || 1); return; } const stateButton = event.target.closest('[data-gallery-state-button]'); if (stateButton) { render(stateButton.dataset.galleryStateButton); return; } const actionButton = event.target.closest('[data-gallery-action]'); if (actionButton) { selectedAction = actionButton.dataset.galleryAction || 'impeccable'; render('configure-replace'); return; } const countButton = event.target.closest('[data-gallery-count]'); if (countButton) { count = count >= 4 ? 1 : count + 1; render(currentState()); return; } const densityButton = event.target.closest('[data-gallery-density]'); if (densityButton) { const value = densityButton.dataset.galleryDensity; root.querySelectorAll('[data-gallery-density]').forEach((button) => { button.setAttribute('aria-pressed', button.dataset.galleryDensity === value ? 'true' : 'false'); }); root.querySelectorAll('[data-gallery-density-output]').forEach((output) => { output.textContent = value; }); return; } const toggleButton = event.target.closest('[data-gallery-param-toggle]'); if (toggleButton) { const next = toggleButton.getAttribute('aria-pressed') !== 'true'; root.querySelectorAll('[data-gallery-param-toggle]').forEach((button) => button.setAttribute('aria-pressed', String(next))); root.querySelectorAll('[data-gallery-toggle-output]').forEach((output) => { output.textContent = next ? 'On' : 'Off'; }); return; } const designTabButton = event.target.closest('[data-gallery-design-tab]'); if (designTabButton) { designTab = designTabButton.dataset.galleryDesignTab === 'raw' ? 'raw' : 'visual'; render('design-panel'); return; } const trigger = event.target.closest('[data-gallery-go]'); if (trigger && !trigger.disabled) render(trigger.dataset.galleryGo); }); root.addEventListener('input', (event) => { const range = event.target.closest('[data-gallery-range]'); if (!range) return; const value = Number(range.value).toFixed(2); root.querySelectorAll('[data-gallery-range]').forEach((input) => { if (input !== range) input.value = range.value; }); root.querySelectorAll('[data-gallery-range-output]').forEach((output) => { output.textContent = value; }); }); root.addEventListener('keydown', (event) => { if (event.target.matches('[data-gallery-steer-input]') && event.key === 'Enter') { event.preventDefault(); render('steer-processing'); return; } const stateButton = event.target.closest('[data-gallery-state-button]'); if (stateButton && ['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) { event.preventDefault(); const current = stateIndex(stateButton.dataset.galleryStateButton); const next = event.key === 'Home' ? 0 : event.key === 'End' ? states.length - 1 : (current + (event.key === 'ArrowLeft' ? -1 : 1) + states.length) % states.length; render(states[next].key); root.querySelector(`[data-gallery-state-button="${states[next].key}"]`)?.focus(); return; } const panel = event.target.closest('.live-ui-gallery__control-panel'); if (!panel || event.target.matches('input')) return; if (event.key === 'ArrowLeft') { event.preventDefault(); step(-1); } else if (event.key === 'ArrowRight') { event.preventDefault(); step(1); } else if (event.key === 'Home') { event.preventDefault(); render(states[0].key, { focusSelect: true }); } else if (event.key === 'End') { event.preventDefault(); render(states[states.length - 1].key, { focusSelect: true }); } }); render(states[0].key); } function initLiveUiGalleries() { document.querySelectorAll('[data-live-ui-gallery]').forEach(initLiveUiGallery); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initLiveUiGalleries, { once: true }); } else { initLiveUiGalleries(); } document.addEventListener('astro:page-load', initLiveUiGalleries);