Files
Claude 3adebb731f Rename FunPack to FunUI (5.0.3 Classy Chopper): /fun endpoint, wink logo, load-time migration of old names
Old project/workflow names and browser settings migrate on load; /funpack redirects to /fun.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016acRss69eH1235eRdFTZHY
2026-10-25 13:11:03 +00:00

50 lines
3.2 KiB
JavaScript

// Files: what FunUI keeps on disk for prompts (the shortcut library, cut words) and the taste keys, to see and delete. As v4's tab.
import { composer as c } from "../../composer/composer.js";
const size = (n) => (n < 1024 ? `${n} B` : n < 1048576 ? `${(n / 1024).toFixed(1)} KB` : `${(n / 1048576).toFixed(1)} MB`);
export function files(app) {
const page = c.region.stack({ gap: "sm" });
const sure = (title, message) => c.modal.dialogue({ title, message: `${message} This cannot be undone.`, tone: "danger", confirmLabel: "Delete" }).result;
async function draw() {
let lib, keys = null;
try { lib = await app.api.libraryFiles(); } catch (err) { return page.set([c.banner.warn({ text: `Could not list the files: ${err.message}` })]); }
try { keys = (await app.api.tasteKeys()).keys || []; } catch { /* no taste module: no keys to show */ }
const row = (label, hint, onDelete) => c.settingsRow.default({ label, hint, control: c.button.sm({ label: "✕ Delete", tone: "danger", onClick: onDelete }) });
// A key's kinds: one module's learning each. Deleting one kind leaves the key's other kinds as they are.
const kindRows = async (k) => {
let kinds = [];
try { kinds = (await app.api.tasteKindsOf(k)).kinds || []; } catch (err) { return [c.hint.default({ text: `Could not list ${k}: ${err.message}` })]; }
if (!kinds.length) return [c.hint.default({ text: "Nothing learned under this key yet." })];
return kinds.map((kd) => c.settingsRow.default({ label: kd.title, hint: `${kd.hint} · ${size(kd.bytes)}`,
control: c.button.sm({ label: "✕ Delete", tone: "danger", onClick: async () => {
if (!(await sure("Delete what was learned", `Delete “${kd.title}” under “${k}”? The key's other learning stays.`))) return;
try { await app.api.clearTasteKind(k, kd.kind); } catch (err) { c.toast.warn({ text: err.message }); }
draw();
} }) }));
};
const keyBlocks = keys ? await Promise.all(keys.map(async (k) => [
c.settingsRow.default({ label: k, hint: "what your ratings taught under this name", control: c.button.sm({ label: "✕ Delete key", tone: "danger", onClick: async () => {
if (!(await sure("Delete taste key", `Delete “${k}” and everything it learned?`))) return;
try { await app.api.deleteTasteKey(k); } catch (err) { c.toast.warn({ text: err.message }); }
draw();
} }) }),
...(await kindRows(k)),
])) : null;
page.set([
c.toolbar.default({ items: [c.button.sm({ label: "↻ Refresh", tone: "ghost", onClick: draw })] }),
c.label.section({ text: "Prompt library" }),
c.hint.default({ text: lib.dir }),
...(lib.files.length ? lib.files.map((f) => row(f.name, size(f.size), async () => {
if (!(await sure("Delete file", `Delete “${f.name}”?`))) return;
try { await app.api.deleteLibraryFile(f.name); } catch (err) { c.toast.warn({ text: err.message }); }
draw();
})) : [c.hint.default({ text: "No files." })]),
...(keyBlocks ? [c.label.section({ text: "Taste keys" }),
...(keys.length ? keyBlocks.flat() : [c.hint.default({ text: "No keys." })])] : []),
]);
}
draw();
return page;
}