Fusion AI: play HAL 8 video links inline
HAL 8's signed file links carry no extension, so a clip linked as [video](...) rendered as a plain link. A HAL 8 file labelled video/clip is now a player, and a HAL 8 file embedded as an image that will not decode is swapped for one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -36,6 +36,9 @@ const safeUrl = u => {
|
||||
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,
|
||||
@@ -51,7 +54,7 @@ export function renderMarkdown(src) {
|
||||
.replace(/!\[([^\]]*)\]\(([^)\s]+)\)/g, (m, alt, u) => { u = u.replace(/&/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(/&/g, '&'); const ok = safeUrl(u); if (!ok) return t;
|
||||
return isVideo(ok) ? media(ok, 'video', t) : `<a href="${esc(ok)}" target="_blank" rel="noopener nofollow">${t}</a>`; })
|
||||
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 = [];
|
||||
@@ -109,6 +112,16 @@ export function mount(root, opts = {}) {
|
||||
</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 · 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 */ } }
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user