feat(demo): per-folder media merge + AIUI seed-chats bootstrap

- Curated files loader now MERGES per top-level folder: dropping real files into
  demo/files/Music/ swaps only Music and keeps the sample Documents/Photos/Videos
  (verified). Media plays with the Range support already in place.
- AIUI index.html: a ?seed bootstrap pre-loads the example "Content Showcase"
  conversation into AIUI's IndexedDB by calling the bundle's own
  seedPromptsToConversation() (identical to its /seed command), so the chat
  history isn't empty when the demo points users to "previous chats". Guarded by
  try/catch + an existence check; no-op without ?seed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago 2026-06-22 16:45:26 -04:00
parent 445f08a5c1
commit 7efebb4a8c
2 changed files with 39 additions and 4 deletions

View File

@ -14,6 +14,31 @@
<link rel="icon" href="/aiui/favicon.svg" type="image/svg+xml" />
<link rel="apple-touch-icon" href="/aiui/apple-touch-icon-180x180.png" />
<title>AIUI</title>
<!-- Demo (?seed): pre-load the example "Content Showcase" conversation into
AIUI's IndexedDB so the chat history isn't empty (live chat is disabled
in the demo and points users to these previous chats). Mirrors the app's
own /seed exactly by calling its seedPromptsToConversation(). -->
<script type="module">
(async () => {
try {
if (!new URLSearchParams(location.search).has('seed')) return;
const db = await new Promise((res, rej) => {
const r = indexedDB.open('aiui-store', 1);
r.onupgradeneeded = (e) => { const d = e.target.result; if (!d.objectStoreNames.contains('conversations')) d.createObjectStore('conversations', { keyPath: 'id' }); };
r.onsuccess = () => res(r.result); r.onerror = () => rej(r.error);
});
const exists = await new Promise((res) => {
try { const q = db.transaction('conversations', 'readonly').objectStore('conversations').getKey('seed-all'); q.onsuccess = () => res(!!q.result); q.onerror = () => res(false); }
catch { res(false); }
});
if (exists) return;
const { seedPromptsToConversation } = await import('/aiui/assets/seedPrompts-CLWaUv28.js');
const conv = seedPromptsToConversation();
await new Promise((res, rej) => { const t = db.transaction('conversations', 'readwrite'); t.objectStore('conversations').put(conv); t.oncomplete = () => res(); t.onerror = () => rej(t.error); });
try { localStorage.setItem('aiui-active-conversation', conv.id); } catch {}
} catch (e) { console.warn('[demo] AIUI seed bootstrap failed', e); }
})();
</script>
<script type="module" crossorigin src="/aiui/assets/index-Lh5NfTCq.js"></script>
<link rel="stylesheet" crossorigin href="/aiui/assets/index-CHQ7uqBj.css">
<link rel="manifest" href="/aiui/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/aiui/registerSW.js"></script></head>

View File

@ -3626,11 +3626,21 @@ function loadDemoDiskFiles() {
}
walk(root, '/')
if (!tree['/'].length) return // empty folder → keep the hardcoded seeds
for (const k of Object.keys(SEED_FILES)) delete SEED_FILES[k]
Object.assign(SEED_FILES, tree)
for (const k of Object.keys(SEED_FILE_CONTENTS)) delete SEED_FILE_CONTENTS[k]
// Per-folder MERGE: a top-level folder present in demo/files replaces that
// seed folder; seed folders the curator didn't provide (e.g. sample Documents)
// are kept. So dropping real Music/ doesn't wipe the sample Documents.
const provided = new Set(tree['/'].map(e => e.name))
for (const k of Object.keys(SEED_FILES)) {
if (k !== '/' && provided.has(k.split('/')[1])) delete SEED_FILES[k]
}
for (const k of Object.keys(SEED_FILE_CONTENTS)) {
if (provided.has(k.split('/')[1])) delete SEED_FILE_CONTENTS[k]
}
const keptRoot = (SEED_FILES['/'] || []).filter(e => !provided.has(e.name))
for (const [k, v] of Object.entries(tree)) { if (k !== '/') SEED_FILES[k] = v }
SEED_FILES['/'] = [...keptRoot, ...tree['/']]
Object.assign(SEED_FILE_CONTENTS, contents)
console.log(`[Demo] Loaded curated files from demo/files (${Object.keys(diskFilePaths).length} binary, ${Object.keys(contents).length} text)`)
console.log(`[Demo] Merged curated files from demo/files (${Object.keys(diskFilePaths).length} binary, ${Object.keys(contents).length} text; folders: ${[...provided].join(', ')})`)
}
loadDemoDiskFiles()