UNPKG

@mr_hugo/boredom

Version:

The LLM-First JavaScript Framework

433 lines (404 loc) 12 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>boreDOM Scaffold Example</title> <style> :root { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: #f8fafc; color: #0f172a; --bg: #f8fafc; --surface: #ffffff; --muted: #64748b; --border: #e2e8f0; --accent: #0f172a; --accent-2: #6366f1; --accent-soft: #eef2ff; --shadow: 0 10px 30px rgba(15, 23, 42, 0.08); } * { box-sizing: border-box; } body { margin: 0; padding: 32px 24px 48px; background: linear-gradient(135deg, #f8fafc 0%, #eef2ff 100%); } h1, h2, h3, h4 { margin: 0 0 8px 0; } p { margin: 0; color: var(--muted); } button { font: inherit; cursor: pointer; } </style> </head> <body> <script id="initial-state" type="application/json"> { "projects": [ { "id": "proj-1", "name": "Demo Project", "events": [ { "id": "evt-1", "note": "C4", "start": 0, "duration": 0.25 } ] } ], "activeProjectId": "proj-1", "lastOp": null } </script> <app-shell></app-shell> <style data-component="app-shell"> @layer components.app-shell { app-shell .app { display: grid; grid-template-columns: 240px 1fr; gap: 16px; max-width: 1100px; margin: 0 auto; } app-shell header { grid-column: 1 / -1; display: flex; justify-content: space-between; gap: 16px; align-items: center; background: var(--surface); padding: 16px; border-radius: 16px; box-shadow: var(--shadow); } app-shell .title { display: grid; gap: 4px; } app-shell .header-actions { display: flex; align-items: center; gap: 12px; } app-shell header input[type="text"] { flex: 1; padding: 8px 10px; border-radius: 10px; border: 1px solid var(--border); } app-shell .button { border: none; border-radius: 10px; padding: 8px 12px; background: var(--accent); color: white; } app-shell .button.secondary { background: var(--accent-soft); color: var(--accent); border: 1px solid var(--border); } app-shell .sidebar, app-shell .main { background: var(--surface); border-radius: 16px; padding: 16px; box-shadow: var(--shadow); } app-shell .project-list { padding: 0; margin: 0 0 12px 0; display: grid; gap: 8px; } app-shell .project button { width: 100%; text-align: left; border: 1px solid var(--border); background: #f1f5f9; padding: 6px 8px; border-radius: 10px; } app-shell .project button.active { background: var(--accent); color: white; border-color: var(--accent); } app-shell .section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; } app-shell .pill { padding: 4px 8px; border-radius: 999px; background: #f1f5f9; font-size: 12px; color: var(--muted); } app-shell .pad-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-bottom: 12px; } app-shell .pad { padding: 14px 0; border-radius: 12px; border: 1px solid var(--border); background: #e2e8f0; } app-shell .timeline { display: grid; gap: 6px; } app-shell .note { padding: 6px 8px; border-radius: 10px; background: #e0f2fe; } app-shell .status { margin-top: 12px; font-size: 12px; color: var(--muted); } app-shell .modal { position: fixed; inset: 0; display: grid; place-items: center; background: rgba(15, 23, 42, 0.5); } app-shell .modal-card { background: var(--surface); padding: 20px; border-radius: 16px; min-width: 280px; } app-shell .key-capture { position: absolute; opacity: 0; pointer-events: none; height: 0; width: 0; } } </style> <template data-component="app-shell"> <div class="app"> <header> <div class="title"> <h1>Mini MPC</h1> <p>Single-file scaffold with persistence and key capture.</p> </div> <div class="header-actions"> <input data-ref="nameInput" type="text" placeholder="Project name" data-value="local.projectName" data-dispatch-input="editName" /> <button class="button" data-dispatch="saveProject">Save</button> <label> <input type="checkbox" data-checked="local.metronomeOn" data-dispatch-change="toggleMetronome" /> Metronome </label> </div> </header> <aside class="sidebar"> <div class="section-header"> <h3>Projects</h3> <span class="pill" data-text="'Active: ' + state.activeProjectId"></span> </div> <div class="project-list" data-list="state.projects" data-list-key="item.id"> <div> <template data-item> <div class="project"> <button data-dispatch="selectProject" data-arg-id="item.id" data-class="active:item.id === state.activeProjectId" data-text="item.name" ></button> </div> </template> </div> </div> <button class="button secondary" data-dispatch="newProject">New Project</button> </aside> <main class="main"> <div class="section-header"> <h3>Pads</h3> <button class="button secondary" data-dispatch="focusKeys">Focus Keys</button> </div> <div class="pad-grid" data-list="local.padMap" data-list-key="item.id"> <template data-item> <button class="pad" data-dispatch="triggerPad" data-arg-pad="item.id" data-arg-key="item.key" data-text="'Pad ' + item.id + ' (' + item.key + ')'" ></button> </template> </div> <div class="section-header"> <h3>Timeline</h3> <span class="pill" data-text="'Events: ' + local.activeEvents.length"></span> </div> <div class="timeline" data-list="local.activeEvents" data-list-key="item.id"> <template data-item> <div class="note" data-text="item.note"></div> </template> </div> <div class="status" data-text="'Last action: ' + (state.lastOp || 'idle')"></div> </main> <input class="key-capture" data-ref="keyCapture" data-dispatch-keydown="keyDown" data-dispatch-keyup="keyUp" aria-hidden="true" /> <div class="modal" data-show="local.showModal"> <div class="modal-card"> <h4>Modal</h4> <p data-text="local.modalText"></p> <button data-dispatch="closeModal">Close</button> </div> </div> </div> </template> <script type="text/boredom" data-component="app-shell"> const STORAGE_KEY = "boredom-projects"; export default ({ state, local, refs, on, onMount, onCleanup }) => { local.projectName = ""; local.metronomeOn = false; local.showModal = false; local.modalText = ""; local.padMap = [ { id: "1", key: "1" }, { id: "2", key: "2" }, { id: "3", key: "3" }, { id: "4", key: "4" }, { id: "5", key: "Q" }, { id: "6", key: "W" }, { id: "7", key: "E" }, { id: "8", key: "R" }, { id: "9", key: "A" }, { id: "10", key: "S" }, { id: "11", key: "D" }, { id: "12", key: "F" }, { id: "13", key: "Z" }, { id: "14", key: "X" }, { id: "15", key: "C" }, { id: "16", key: "V" } ]; local.activeEvents = []; const load = () => { const raw = localStorage.getItem(STORAGE_KEY); if (!raw) return; try { const data = JSON.parse(raw); if (Array.isArray(data.projects)) state.projects = data.projects; if (data.activeProjectId) state.activeProjectId = data.activeProjectId; } catch (err) { console.error("Failed to load project", err); } }; const save = () => { const payload = { projects: state.projects, activeProjectId: state.activeProjectId, }; localStorage.setItem(STORAGE_KEY, JSON.stringify(payload)); }; const syncActiveProject = () => { const project = state.projects.find((p) => p.id === state.activeProjectId) || state.projects[0]; if (!project) return; state.activeProjectId = project.id; local.projectName = project.name; local.activeEvents = project.events; }; onMount(() => { load(); syncActiveProject(); if (refs.keyCapture) refs.keyCapture.focus(); }); on("editName", ({ e }) => { local.projectName = e.event.target.value; }); on("focusKeys", () => { if (refs.keyCapture) refs.keyCapture.focus(); }); on("saveProject", () => { const project = state.projects.find((p) => p.id === state.activeProjectId); if (!project) return; project.name = local.projectName || project.name; save(); state.lastOp = `saved:${project.id}`; }); on("toggleMetronome", ({ e }) => { local.metronomeOn = !!e.event.target.checked; state.lastOp = `metronome:${local.metronomeOn}`; }); on("newProject", () => { const id = `proj-${Date.now()}`; const next = { id, name: "Untitled", events: [] }; state.projects.push(next); state.activeProjectId = id; syncActiveProject(); save(); state.lastOp = `new:${id}`; }); on("selectProject", ({ e }) => { const id = e.args.id; if (!id) return; state.activeProjectId = id; syncActiveProject(); state.lastOp = `select:${id}`; }); const addPadEvent = (pad) => { const project = state.projects.find((p) => p.id === state.activeProjectId); if (!project) return; const event = { id: `evt-${Date.now()}`, note: `Pad ${pad}`, start: 0, duration: 0.25 }; project.events.push(event); local.activeEvents = project.events; save(); state.lastOp = `pad:${pad}`; }; on("triggerPad", ({ e }) => { const pad = e.args.pad || "?"; addPadEvent(pad); }); on("keyDown", ({ e }) => { const key = String(e.event.key || "").toUpperCase(); const match = local.padMap.find((pad) => pad.key === key); if (match) { addPadEvent(match.id); return; } if (key === "?") { local.modalText = "Keyboard shortcut help"; local.showModal = true; } }); on("keyUp", () => {}); on("closeModal", () => { local.showModal = false; }); onCleanup(() => { if (refs.keyCapture) refs.keyCapture.blur(); }); }; </script> <script src="./boreDOM.js" data-state="#initial-state"></script> </body> </html>