Files
apex-fusion-site/fusion/fusion.js
jerry dfa72eec45 Fusion AI launch film: the HAL 8 card and the widget's lightbox
A 32 s trailer rendered in Blender from the Apex Universe scene. The
pane in it is the real widget replaying answers Fusion AI gave on the
live route, voiced by ElevenLabs Brian, with an original score.
fusion/launch/ holds the 16:9 cut, its poster and English captions.
The HAL 8 section gets a 'Meet Fusion AI' card (Talk to Fusion AI opens
the widget through a fusion:open event), and the widget's welcome
screen a 'Watch the launch film' chip that plays it in a lightbox.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 01:34:11 -05:00

316 lines
22 KiB
JavaScript

// Fusion AI, powered by HAL 8: the hub's consumer assistant.
// One control (Standard | Incognito); everything else is always on: chat and code, images and
// video, web search, a Python interpreter that runs in the visitor's browser, and voice.
// The browser never holds a key: it talks to HAL 8's public Fusion endpoint, which runs the
// request through HAL 8's headless pipeline under its own capped service account.
// Standard - hal8-auto; the conversation is kept in this browser (localStorage) until cleared.
// Incognito - a Venice private model; no conversation is kept, here or on HAL 8, and the
// pictures and clips it makes are deleted after a day.
const EYE = 'universe/logos/hal8-eye.png';
const LAUNCH = new URL('launch/', import.meta.url);
const FILM_V = '1';
const STORE = 'f8.chat.v1';
const VISITOR = 'f8.visitor';
const PYODIDE = 'https://cdn.jsdelivr.net/pyodide/v0.26.4/full/';
const SUGGEST = {
standard: [['Ask', 'How do I stake AP3X?'], ['Code', 'A Python snippet that converts DFM to AP3X'],
['Image', 'The AP3X star rising over PRIME'], ['Video', 'A five-second flight through an asteroid belt']],
incognito: [['Ask', 'Explain AP3X tokenomics simply'], ['Image', 'A night sky over the Nexus world']],
};
const I = {
incog: '<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3l18 18"/><path d="M10.6 5.1A10.4 10.4 0 0 1 12 5c5 0 9 4.5 10 7-.4 1-1.2 2.3-2.4 3.5M6.2 6.3C4.2 7.6 2.7 9.6 2 12c1 2.5 5 7 10 7 1.7 0 3.3-.5 4.6-1.3"/><path d="M9.9 9.9a3 3 0 0 0 4.2 4.2"/></svg>',
mic: '<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"><rect x="9" y="3" width="6" height="11" rx="3"/><path d="M5 11a7 7 0 0 0 14 0M12 18v3"/></svg>',
send: '<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg>',
stop: '<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><rect x="6" y="6" width="12" height="12" rx="2"/></svg>',
plus: '<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><path d="M12 5v14M5 12h14"/></svg>',
copy: '<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="1.7"><rect x="8" y="8" width="12" height="12" rx="2"/><path d="M16 8V5a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h3"/></svg>',
speak: '<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"><path d="M11 5 6 9H3v6h3l5 4zM15.5 8.5a5 5 0 0 1 0 7M18.5 5.5a9 9 0 0 1 0 13"/></svg>',
dl: '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><path d="M12 4v11M7 10l5 5 5-5M5 20h14"/></svg>',
};
const esc = s => String(s ?? '').replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
// media and links: https only; paths HAL 8 returns relative to itself resolve against the endpoint
let ORIGIN = 'https://hal8.odyssey-works.io';
const safeUrl = u => {
if (/^\/(?!\/)/.test(u)) return ORIGIN + u;
if (/^https:\/\//i.test(u)) return u;
if (ORIGIN.startsWith('http://127.0.0.1') && /^http:\/\/127\.0\.0\.1[:/]/.test(u)) return u; // local testing only
return null;
};
const isVideo = u => /\.(mp4|webm|mov)(\?|#|$)/i.test(u) || /[?&](type|kind)=video/i.test(u);
// HAL 8's signed file links carry no extension: a HAL 8 file linked as "video" / "clip" is one
const isHal8File = u => u.startsWith(ORIGIN + '/api/v1/files/');
const namedVideo = (label, u) => isHal8File(u) && /^\s*(video|clip|watch)\b/i.test(label);
// ------------------------------------------------------------------ a small, safe markdown renderer
// Everything is escaped first; only code fences, inline code, emphasis, headings, lists, links,
// images and videos (https only) are turned back into markup.
export function renderMarkdown(src) {
const blocks = [];
let text = String(src ?? '').replace(/```([\w+-]*)[^\n]*\n([\s\S]*?)(```|$)/g, (_, lang, code) => {
blocks.push({ lang: (lang || '').toLowerCase(), code: code.replace(/\n$/, '') });
return `\u0000${blocks.length - 1}\u0000`;
});
text = esc(text);
const inline = s => s
.replace(/!\[([^\]]*)\]\(([^)\s]+)\)/g, (m, alt, u) => { u = u.replace(/&amp;/g, '&'); const ok = safeUrl(u); if (!ok) return esc(alt);
return isVideo(ok) ? media(ok, 'video', alt) : media(ok, 'image', alt); })
.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (m, t, u) => { u = u.replace(/&amp;/g, '&'); const ok = safeUrl(u); if (!ok) return t;
return isVideo(ok) || namedVideo(t, ok) ? media(ok, 'video', t) : `<a href="${esc(ok)}" target="_blank" rel="noopener nofollow">${t}</a>`; })
.replace(/`([^`]+)`/g, '<code>$1</code>')
.replace(/\*\*([^*]+)\*\*/g, '<b>$1</b>').replace(/(^|[\s(])\*([^*\n]+)\*/g, '$1<i>$2</i>');
const out = [];
let list = null;
const flush = () => { if (list) { out.push(`<${list.t}>${list.items.map(x => `<li>${inline(x)}</li>`).join('')}</${list.t}>`); list = null; } };
for (const raw of text.split('\n')) {
const line = raw.trimEnd();
const code = line.match(/^\u0000(\d+)\u0000$/);
if (code) { flush(); out.push(codeBlock(blocks[+code[1]])); continue; }
const h = line.match(/^(#{1,4})\s+(.*)$/);
if (h) { flush(); out.push(`<h4>${inline(h[2])}</h4>`); continue; }
const ul = line.match(/^\s*[-*•]\s+(.*)$/), ol = line.match(/^\s*\d+[.)]\s+(.*)$/);
if (ul || ol) { const t = ul ? 'ul' : 'ol'; if (!list || list.t !== t) { flush(); list = { t, items: [] }; } list.items.push((ul || ol)[1]); continue; }
flush();
if (!line.trim()) continue;
out.push(`<p>${inline(line).replace(/\u0000(\d+)\u0000/g, (_, n) => codeBlock(blocks[+n]))}</p>`);
}
flush();
return out.join('');
}
function media(u, kind, label) {
const body = kind === 'video' ? `<video src="${esc(u)}" controls playsinline preload="metadata"></video>` : `<img src="${esc(u)}" alt="${esc(label || '')}" loading="lazy">`;
return `<span class="f8-media">${body}<span class="f8-media-bar"><span>${kind === 'video' ? 'Video' : 'Image'} &middot; HAL 8</span><a href="${esc(u)}" download target="_blank" rel="noopener" title="Download">${I.dl}</a></span></span>`;
}
function codeBlock(b) {
const runnable = ['python', 'py', ''].includes(b.lang) && /print|=|def |import /.test(b.code);
return `<div class="f8-code" data-lang="${esc(b.lang || 'code')}"><div class="f8-code-h"><span>${esc(b.lang || 'code')}</span><span class="f8-code-act">` +
(runnable ? '<button data-f8="run">&#9654; Run</button>' : '') + `<button data-f8="copy-code">${I.copy} Copy</button></span></div><pre>${esc(b.code)}</pre></div>`;
}
// ------------------------------------------------------------------ the widget
export function mount(root, opts = {}) {
const endpoint = opts.endpoint;
try { ORIGIN = new URL(endpoint).origin; } catch { /* keep HAL 8 */ }
const state = { open: false, mode: 'standard', msgs: { standard: load(), incognito: [] }, busy: false, ctrl: null, voiceTurn: false };
root.classList.add('f8-root');
root.innerHTML = `
<button class="f8-fab" aria-label="Ask Fusion AI" aria-expanded="false" aria-controls="f8-pane"><img src="${EYE}" alt="HAL 8"><span class="f8-fab-x" aria-hidden="true">&times;</span></button>
<div class="f8-pane" id="f8-pane" role="dialog" aria-label="Fusion AI" hidden>
<div class="f8-head">
<div><div class="f8-title serif">Fusion <span class="serif-i">AI</span></div><div class="f8-power"><img src="${EYE}" alt="">POWERED BY HAL 8</div></div>
<div class="f8-head-actions"><button data-f8="new" title="New chat" aria-label="New chat">${I.plus}</button><button data-f8="close" class="f8-x" title="Close" aria-label="Close">&times;</button></div>
</div>
<div class="f8-modebar"><div class="f8-pill" role="tablist" aria-label="Mode">
<button role="tab" data-mode="standard" aria-selected="true" class="on">Standard</button>
<button role="tab" data-mode="incognito" aria-selected="false">${I.incog} Incognito</button></div></div>
<div class="f8-incog-note">${I.incog} Incognito &middot; nothing is saved &middot; pictures vanish in a day</div>
<div class="f8-body" aria-live="polite"></div>
<div class="f8-compose">
<div class="f8-listen" hidden><span class="f8-wave"><i></i><i></i><i></i><i></i><i></i><i></i><i></i></span><span class="f8-heard">Listening&hellip;</span></div>
<textarea rows="1" placeholder="Ask anything, or tap the mic" aria-label="Message Fusion AI"></textarea>
<button class="f8-mic" data-f8="mic" title="Speak" aria-label="Speak">${I.mic}</button>
<button class="f8-send" data-f8="send" title="Send" aria-label="Send">${I.send}</button>
</div>
</div>`;
const $ = s => root.querySelector(s);
const fab = $('.f8-fab'), pane = $('.f8-pane'), body = $('.f8-body'), ta = $('textarea'), sendBtn = $('.f8-send'), micBtn = $('.f8-mic');
// a HAL 8 file embedded as an image that will not decode is a clip: play it instead
body.addEventListener('error', e => {
const img = e.target;
if (img.tagName !== 'IMG' || !img.closest('.f8-media') || !isHal8File(img.src)) return;
const v = document.createElement('video');
Object.assign(v, { src: img.src, controls: true, playsInline: true, preload: 'metadata' });
img.replaceWith(v);
const tag = v.parentElement?.querySelector('.f8-media-bar span');
if (tag) tag.innerHTML = 'Video &middot; HAL 8';
}, true);
function load() { try { return JSON.parse(localStorage.getItem(STORE) || '[]').slice(-60); } catch { return []; } }
function save() { if (state.mode !== 'standard') return; try { localStorage.setItem(STORE, JSON.stringify(state.msgs.standard.slice(-60))); } catch { /* storage off */ } }
function visitor() {
try { let v = localStorage.getItem(VISITOR); if (!v) { v = crypto.randomUUID(); localStorage.setItem(VISITOR, v); } return v; } catch { return 'anon'; }
}
const msgs = () => state.msgs[state.mode];
function setOpen(o) {
state.open = o; pane.hidden = !o; root.classList.toggle('f8-open', o); fab.setAttribute('aria-expanded', String(o));
if (o) { draw(); setTimeout(() => ta.focus(), 30); } else { stopVoice(); speechSynthesis?.cancel?.(); }
}
function setMode(m) {
if (m === state.mode) return;
state.ctrl?.abort();
if (state.mode === 'incognito') state.msgs.incognito = []; // leaving incognito forgets it
state.mode = m; root.classList.toggle('f8-incognito', m === 'incognito');
root.querySelectorAll('.f8-pill button').forEach(b => { const on = b.dataset.mode === m; b.classList.toggle('on', on); b.setAttribute('aria-selected', String(on)); });
draw();
}
function draw() {
const list = msgs();
if (!list.length) {
const s = SUGGEST[state.mode];
body.innerHTML = `<div class="f8-empty"><p class="serif">${state.mode === 'incognito' ? "You're incognito. <span class=\"serif-i\">Nothing you say here is kept.</span>" : "Hi, I'm Fusion AI. <span class=\"serif-i\">Ask, code, create or just talk.</span>"}</p>
<div class="f8-sugg">${s.map(([k, t]) => `<button data-f8="suggest"><b>${k}</b>${esc(t)}</button>`).join('')}</div>
${state.mode === 'standard' ? '<button class="f8-watch" data-f8="watch"><i></i>Watch the launch film<span>0:32</span></button>' : ''}</div>`;
return;
}
body.innerHTML = list.map((m, i) => bubble(m, i)).join('');
body.scrollTop = body.scrollHeight;
}
function bubble(m, i) {
if (m.role === 'user') return `<div class="f8-u">${esc(m.content)}</div>`;
if (m.kind === 'limit') return `<div class="f8-a f8-limit">${esc(m.content)}</div>`;
const tools = m.content && !m.pending ? `<div class="f8-meta"><button data-f8="copy" data-i="${i}" title="Copy">${I.copy}</button><button data-f8="speak" data-i="${i}" title="Read aloud">${I.speak}</button></div>` : '';
const status = m.pending && m.status ? `<div class="f8-status"><span class="f8-dots"><i></i><i></i><i></i></span>${esc(m.status)}</div>` : '';
const html = m.content ? renderMarkdown(m.content) : '';
return `<div class="f8-a${m.pending && !m.content ? ' f8-thinking' : ''}">${html}${status || (m.pending && !m.content ? '<span class="f8-dots"><i></i><i></i><i></i></span>' : '')}${tools}</div>`;
}
function redrawLast() {
const list = msgs(), el = body.lastElementChild;
if (!el || !list.length) return draw();
const tmp = document.createElement('div'); tmp.innerHTML = bubble(list[list.length - 1], list.length - 1);
el.replaceWith(tmp.firstElementChild); body.scrollTop = body.scrollHeight;
}
// ---------------------------------------------------------------- sending, streamed
async function send(text) {
text = (text || '').trim();
if (!text || state.busy) return;
const list = msgs();
list.push({ role: 'user', content: text });
const reply = { role: 'assistant', content: '', pending: true };
list.push(reply);
ta.value = ''; autoGrow(); state.busy = true; sendBtn.innerHTML = I.stop; sendBtn.title = 'Stop'; draw();
const ctrl = new AbortController(); state.ctrl = ctrl;
const history = list.filter(m => !m.pending && m.kind !== 'limit').slice(-16).map(m => ({ role: m.role, content: m.content }));
try {
const r = await fetch(endpoint, {
method: 'POST', signal: ctrl.signal, headers: { 'Content-Type': 'application/json', Accept: 'text/event-stream' },
body: JSON.stringify({ mode: state.mode, messages: history, visitor: state.mode === 'incognito' ? undefined : visitor(), stream: true }),
});
if (!r.ok) {
let e = {}; try { e = (await r.json()).error || {}; } catch { /* not json */ }
reply.pending = false;
if (r.status === 429) { reply.kind = 'limit'; reply.content = e.message || "That's today's allowance for Fusion AI. It resets at 00:00 UTC; see you then."; }
else reply.content = e.message || "I couldn't reach HAL 8 just now. Try again in a moment.";
return;
}
const ct = r.headers.get('content-type') || '';
if (ct.includes('text/event-stream') && r.body) {
const rd = r.body.getReader(), dec = new TextDecoder(); let buf = '';
for (;;) {
const { value, done } = await rd.read(); if (done) break;
buf += dec.decode(value, { stream: true });
let k;
while ((k = buf.indexOf('\n\n')) >= 0) {
const evt = buf.slice(0, k); buf = buf.slice(k + 2);
for (const ln of evt.split('\n')) {
if (!ln.startsWith('data:')) continue;
const d = ln.slice(5).trim(); if (!d || d === '[DONE]') continue;
let j; try { j = JSON.parse(d); } catch { continue; }
if (j.error) { reply.kind = j.error.code && /limit|budget/.test(j.error.code) ? 'limit' : ''; reply.content = j.error.message || 'Something went wrong.'; continue; }
if (j.status) reply.status = j.status; // e.g. "Searching the web", "Rendering video"
const delta = j.choices?.[0]?.delta?.content ?? j.choices?.[0]?.message?.content ?? '';
if (delta) reply.content += delta;
}
redrawLast();
}
}
} else {
const j = await r.json();
reply.content = j.choices?.[0]?.message?.content || j.text || '';
}
if (!reply.content) reply.content = 'Hmm, an empty answer. Try asking again.';
} catch (e) {
if (e.name !== 'AbortError') reply.content = reply.content || "I couldn't reach HAL 8 just now. Try again in a moment.";
} finally {
reply.pending = false; reply.status = ''; state.busy = false; state.ctrl = null; sendBtn.innerHTML = I.send; sendBtn.title = 'Send';
draw(); save();
if (state.voiceTurn && reply.content && reply.kind !== 'limit') speak(reply.content);
state.voiceTurn = false;
}
}
// ---------------------------------------------------------------- voice: speak in, read out
const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
let rec = null;
function startVoice() {
if (!SR) { toast('Voice needs Chrome, Edge or Safari on this device.'); return; }
speechSynthesis?.cancel?.();
rec = new SR(); rec.lang = navigator.language || 'en-US'; rec.interimResults = true; rec.continuous = false;
let finalText = '';
const heard = $('.f8-heard'); $('.f8-listen').hidden = false; root.classList.add('f8-voice');
rec.onresult = ev => { let interim = ''; for (let i = ev.resultIndex; i < ev.results.length; i++) { const t = ev.results[i][0].transcript; if (ev.results[i].isFinal) finalText += t; else interim += t; }
heard.innerHTML = `Listening&hellip; <em>${esc((finalText + ' ' + interim).trim())}</em>`; };
rec.onerror = ev => { if (ev.error === 'not-allowed') toast('Allow the microphone to talk to Fusion AI.'); };
rec.onend = () => { stopVoice(); if (finalText.trim()) { state.voiceTurn = true; send(finalText); } };
rec.start();
}
function stopVoice() { root.classList.remove('f8-voice'); $('.f8-listen').hidden = true; $('.f8-heard').innerHTML = 'Listening&hellip;'; if (rec) { try { rec.stop(); } catch { /* already stopped */ } rec = null; } }
function speak(md) {
if (!('speechSynthesis' in window)) return;
const plain = md.replace(/```[\s\S]*?```/g, ' (code shown on screen) ').replace(/!?\[([^\]]*)\]\([^)]+\)/g, '$1').replace(/[#*`>_]/g, '').slice(0, 1800);
speechSynthesis.cancel(); const u = new SpeechSynthesisUtterance(plain); u.lang = navigator.language || 'en-US'; speechSynthesis.speak(u);
}
// ---------------------------------------------------------------- the code interpreter (Pyodide, in this browser)
let py = null;
async function runPython(btn) {
const box = btn.closest('.f8-code'); const code = box.querySelector('pre').textContent;
let out = box.querySelector('.f8-out'); if (!out) { out = document.createElement('div'); out.className = 'f8-out'; box.appendChild(out); }
out.innerHTML = '<span>output</span>Starting Python&hellip;';
try {
if (!py) py = (async () => { await new Promise((res, rej) => { const s = document.createElement('script'); s.src = PYODIDE + 'pyodide.js'; s.onload = res; s.onerror = rej; document.head.appendChild(s); }); return window.loadPyodide({ indexURL: PYODIDE }); })();
const p = await py; let buf = '';
p.setStdout({ batched: s => { buf += s + '\n'; } }); p.setStderr({ batched: s => { buf += s + '\n'; } });
await p.loadPackagesFromImports(code);
const res = await p.runPythonAsync(code);
if (res !== undefined && res !== null && !buf) buf = String(res);
out.innerHTML = `<span>output</span>${esc(buf.trim() || '(no output)')}`;
} catch (e) { out.innerHTML = `<span>error</span>${esc(String(e.message || e).split('\n').slice(-3).join('\n'))}`; }
}
// ---------------------------------------------------------------- events
function toast(t) { const el = document.createElement('div'); el.className = 'f8-toast'; el.textContent = t; pane.appendChild(el); setTimeout(() => el.remove(), 3200); }
function autoGrow() { ta.style.height = 'auto'; ta.style.height = Math.min(ta.scrollHeight, 120) + 'px'; }
fab.addEventListener('click', () => setOpen(!state.open));
ta.addEventListener('input', autoGrow);
ta.addEventListener('keydown', e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); send(ta.value); } });
root.addEventListener('keydown', e => { if (e.key === 'Escape' && state.open) setOpen(false); });
root.addEventListener('click', e => {
const b = e.target.closest('[data-f8],[data-mode]'); if (!b) return;
if (b.dataset.mode) return setMode(b.dataset.mode);
const act = b.dataset.f8, list = msgs();
if (act === 'close') setOpen(false);
else if (act === 'new') { state.ctrl?.abort(); state.msgs[state.mode] = []; if (state.mode === 'standard') { try { localStorage.removeItem(STORE); } catch { /* */ } } draw(); }
else if (act === 'send') { if (state.busy) state.ctrl?.abort(); else send(ta.value); }
else if (act === 'mic') { if (root.classList.contains('f8-voice')) stopVoice(); else startVoice(); }
else if (act === 'suggest') send(b.childNodes[1]?.textContent || b.textContent);
else if (act === 'copy') navigator.clipboard?.writeText(list[+b.dataset.i]?.content || '');
else if (act === 'speak') speak(list[+b.dataset.i]?.content || '');
else if (act === 'copy-code') navigator.clipboard?.writeText(b.closest('.f8-code').querySelector('pre').textContent);
else if (act === 'run') runPython(b);
else if (act === 'watch') watchFilm();
});
function watchFilm() {
const box = document.createElement('div');
box.className = 'f8-film';
box.setAttribute('role', 'dialog');
box.setAttribute('aria-label', 'Fusion AI launch film');
box.innerHTML = `<div class="f8-film-frame"><video controls autoplay playsinline preload="auto"
src="${new URL('fusion-ai-launch.mp4?v=' + FILM_V, LAUNCH)}" poster="${new URL('poster.jpg?v=' + FILM_V, LAUNCH)}">
<track kind="captions" srclang="en" label="English" src="${new URL('fusion-ai-launch.vtt?v=' + FILM_V, LAUNCH)}"></video>
<button class="f8-film-x" aria-label="Close">&times;</button></div>`;
const close = () => { box.querySelector('video')?.pause(); box.remove(); document.removeEventListener('keydown', onKey); };
const onKey = e => { if (e.key === 'Escape') close(); };
box.addEventListener('click', e => { if (e.target === box || e.target.closest('.f8-film-x')) close(); });
document.addEventListener('keydown', onKey);
document.body.appendChild(box);
}
const onExternalOpen = () => { setOpen(true); };
window.addEventListener('fusion:open', onExternalOpen);
return {
open: () => setOpen(true),
destroy() { state.ctrl?.abort(); stopVoice(); window.removeEventListener('fusion:open', onExternalOpen); root.innerHTML = ''; },
};
}