// While² — macOS variants with glyph-scramble navigation // 1. Menu-bar popover (interactive) 2. Full terminal window (interactive) const _MAC_INK = '#e8e6df'; // Reuse glyph-scramble hook from while2-scramble.jsx // (Scram component is exposed there but we redefine a slim version to avoid load-order coupling.) const _MAC_POOL = '!<>-_\\/[]{}—=+*^?#·░▒▓█'.split(''); function _useMacScramble(target, key, dur = 520) { const [out, setOut] = React.useState(target); React.useEffect(() => { if (typeof target !== 'string') { setOut(target); return; } const start = performance.now(); const reveals = Array.from({ length: target.length }, (_, i) => { const wave = (i / Math.max(1, target.length)) * 0.55; const jitter = Math.random() * 0.45; return (wave + jitter) * dur; }); let raf; const tick = () => { const t = performance.now() - start; let s = ''; for (let i = 0; i < target.length; i++) { const ch = target[i]; if (ch === ' ' || ch === '\n') { s += ch; continue; } s += t >= reveals[i] ? ch : _MAC_POOL[Math.floor(Math.random() * _MAC_POOL.length)]; } setOut(s); if (t < dur) raf = requestAnimationFrame(tick); else setOut(target); }; raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf); }, [key, target, dur]); return out; } const _S = ({ text, k, dur, style }) => { const out = _useMacScramble(text, k, dur); return {out}; }; // Desktop wallpaper function _Desktop({ children, w = 1280, h = 800 }) { return (
{children}
); } function _MenuBar({ activeIcon = false }) { return (
While² File Edit View Help
↑↓ 📶 🔋 Mon 10:24
); } function _PopoverArrow({ left }) { return (
); } // ── Popover bodies (compact, scramble-aware) ── function _PopToday({ k }) { const today = [ { name: 'burnable trash', rule: 'weekly · tue' }, { name: 'stretch', rule: 'daily' }, { name: 'haircut', rule: 'every 4w · overdue', warn: true }, ]; const upcoming = [ { t: '04/30', name: 'non-burnable trash', rule: 'biweekly' }, { t: '05/03', name: 'pay rent', rule: 'monthly · 3' }, { t: '05/05', name: 'restock shampoo', rule: 'every 6w' }, ]; return (
<_S text="// today" k={k} />
{today.map((t, i) => (
<_S text={t.name} k={k} dur={520 + i * 60} /> <_S text={t.rule} k={k} dur={520 + i * 60} style={{ fontSize: 9.5, color: t.warn ? W2.cyan : 'rgba(232,230,223,0.45)' }} />
))}
<_S text="// upcoming" k={k} />
{upcoming.map((t, i) => (
<_S text={t.t} k={k} dur={580 + i * 60} style={{ color: 'rgba(232,230,223,0.55)' }} /> <_S text={t.name} k={k} dur={580 + i * 60} /> <_S text={t.rule} k={k} dur={580 + i * 60} style={{ fontSize: 9.5, color: 'rgba(232,230,223,0.45)' }} />
))}
); } function _PopNew({ k }) { const Field = ({ kk, v, hint }) => (
<_S text={kk} k={k} /> <_S text={v} k={k} dur={620} />
{hint && (
<_S text={hint} k={k} />
)}
); return (
?{' '} <_S text="done 04/28 → next 05/26 → 06/23 …" k={k} dur={680} />
); } function _PopConfig({ k }) { const rows = [ ['operator', 'user.local'], ['timezone', 'asia/tokyo'], ['notify_at', '09:00'], ['theme', 'dark.cyan'], ['icloud', 'on'], ['last_synced', '2 min ago'], ]; return (
{rows.map(([kk, v], i) => (
<_S text={kk} k={k} dur={500 + i * 30} /> = <_S text={v} k={k} dur={520 + i * 30} />
))}
); } function _PopoverInteractive() { const [screen, setScreen] = React.useState('today'); const [tick, setTick] = React.useState(0); const goto = (next) => { if (next === screen) return; setScreen(next); setTick(t => t + 1); }; const k = `${screen}-${tick}`; const promptText = screen === 'today' ? 'today' : screen === 'new' ? 'new_' : 'config'; return (
> while²{' '} <_S text={promptText} k={k} dur={420} /> _
{screen === 'today' && <_PopToday k={k} />} {screen === 'new' && <_PopNew k={k} />} {screen === 'config' && <_PopConfig k={k} />}
{[ ['today', 'today'], ['new', '+ new_'], ['config', 'config'], ].map(([id, label]) => ( goto(id)} style={{ cursor: 'pointer', userSelect: 'none', color: screen === id ? W2.cyan : 'rgba(232,230,223,0.55)', }}> {screen === id ? > : ' '} {label} ))}
); } // ── Variant 1 — Menu bar popover ── function MacMenuBar() { return ( <_Desktop w={1280} h={800}> <_MenuBar activeIcon />
<_PopoverArrow left={300} /> <_PopoverInteractive />
{[1,2,3,4,5,6,7].map(i => (
))}
); } // ── Terminal window bodies (scramble-aware) ── function _TermToday({ k }) { const today = [ { name: 'burnable trash', rule: 'weekly · tue' }, { name: 'stretch', rule: 'daily' }, { name: 'haircut', rule: 'every 4w · overdue', warn: true }, ]; const upcoming = [ { t: '04/30', name: 'non-burnable trash', rule: 'biweekly' }, { t: '05/03', name: 'pay rent', rule: 'monthly · 3' }, { t: '05/05', name: 'restock shampoo', rule: 'every 6w' }, { t: '05/06', name: 'book checkup', rule: 'every 12m' }, ]; return ( <>
<_S text="// today" k={k} />
{today.map((t, i) => (
[{i + 1}] <_S text={t.name} k={k} dur={520 + i * 60} /> <_S text={t.rule} k={k} dur={520 + i * 60} style={{ fontSize: 10, color: t.warn ? W2.cyan : 'rgba(232,230,223,0.45)' }} />
))}
<_S text="// upcoming" k={k} />
{upcoming.map((t, i) => (
<_S text={t.t} k={k} dur={580 + i * 50} style={{ color: 'rgba(232,230,223,0.55)' }} /> <_S text={t.name} k={k} dur={580 + i * 50} /> <_S text={t.rule} k={k} dur={580 + i * 50} style={{ fontSize: 10, color: 'rgba(232,230,223,0.45)' }} />
))} ); } function _TermNew({ k }) { const Field = ({ kk, v, hint }) => (
<_S text={kk} k={k} /> <_S text={v} k={k} dur={620} />
{hint && (
<_S text={hint} k={k} />
)}
); return (
?{' '} <_S text="done 04/28 → next 05/26 → 06/23 …" k={k} dur={680} />
); } function _TermConfig({ k }) { const rows = [ ['operator', 'user.local'], ['timezone', 'asia/tokyo'], ['week_starts', 'mon'], [null, null], ['notify_at', '09:00'], ['recap_at', '21:00'], ['overdue_ping', 'on'], [null, null], ['theme', 'dark.cyan'], ['reduce_motion', 'off'], [null, null], ['icloud', 'on'], ['last_synced', '2 min ago'], ]; return (
{rows.map(([kk, v], i) => kk === null ? (
) : (
<_S text={kk} k={k} dur={500 + i * 25} /> = <_S text={v} k={k} dur={520 + i * 25} />
))}
); } // ── Variant 2 — Full terminal window (interactive) ── function MacTerminal() { const [screen, setScreen] = React.useState('today'); const [tick, setTick] = React.useState(0); const goto = (next) => { if (next === screen) return; setScreen(next); setTick(t => t + 1); }; const k = `${screen}-${tick}`; const cmd = screen === 'today' ? 'ls today' : screen === 'new' ? 'new --interactive' : 'config'; return ( <_Desktop w={1280} h={800}> <_MenuBar />
{/* title bar */}
while²@local — {screen} — 80×24
{/* body */}
while²@local ~ $ <_S text={cmd} k={k} dur={420} />
{screen === 'today' && <_TermToday k={k} />} {screen === 'new' && <_TermNew k={k} />} {screen === 'config' && <_TermConfig k={k} />}
while²@local ~ $ _
{/* footer nav */}
{[ ['today', 'today'], ['new', 'new_'], ['config', 'config'], ].map(([id, label]) => ( goto(id)} style={{ cursor: 'pointer', userSelect: 'none', color: screen === id ? W2.cyan : 'rgba(232,230,223,0.55)', }}> {screen === id ? > : ' '} {label} ))}
iCloud · synced 2m ago
); } window.MacMenuBar = MacMenuBar; window.MacTerminal = MacTerminal; window._PopToday = _PopToday; window._PopNew = _PopNew; window._PopConfig = _PopConfig;