Notes
Gemma 4 31b · composer

Browser check: Artifact runtime error. This is a technical check, not a quality review.
Reviews & generation details
Reviews
No human review yet. Unreviewed does not mean unsuccessful.
Generation context
Imported historical artifact. Its exact original instructions and lineage are not known.
Generation prompt · exact text unavailable
See the current prompt for reference. It may differ from the instructions used to generate this artifact.
Inspect original source 20,455 bytes · SHA-256 092da8af8b25
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Lumina Notes | Knowledge Base</title>
<script src="https://cdn.jsdelivr.net/npm/lucide@0.344.0/dist/umd/lucide.min.js"></script>
<style>
/* ── css: main-styles ── */
:root {
--bg-primary: #ffffff;
--bg-secondary: #f8f9fa;
--bg-tertiary: #f0f2f5;
--text-main: #1a1a1a;
--text-muted: #6c757d;
--accent: #4a90e2;
--accent-hover: #357abd;
--border-color: #e0e0e0;
--sidebar-width: 260px;
--browser-width: 320px;
--shadow-sm: 0 2px 4px rgba(0,0,0,0.05);
--transition: all 0.2s ease;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
color: var(--text-main);
background-color: var(--bg-primary);
height: 100vh;
overflow: hidden;
}
#app {
display: flex;
height: 100vh;
width: 100vw;
}
/* Sidebar Styles */
#sidebar {
width: var(--sidebar-width);
background-color: var(--bg-secondary);
border-right: 1px solid var(--border-color);
display: flex;
flex-direction: column;
transition: var(--transition);
}
.sidebar-header {
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
display: flex;
align-items: center;
gap: 10px;
font-weight: 700;
font-size: 1.2rem;
color: var(--accent);
}
.logo-icon {
width: 24px;
height: 24px;
background: var(--accent);
border-radius: 6px;
position: relative;
}
.logo-icon::after {
content: '';
position: absolute;
width: 8px;
height: 8px;
background: white;
top: 6px;
left: 6px;
border-radius: 2px;
}
.sidebar-nav {
flex: 1;
overflow-y: auto;
padding: 0 12px;
}
.nav-section {
margin-bottom: 24px;
}
.section-title {
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
color: var(--text-muted);
margin: 0 0 8px 8px;
letter-spacing: 0.05em;
}
.list-group {
display: flex;
flex-direction: column;
gap: 4px;
}
.list-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
font-size: 0.9rem;
transition: var(--transition);
color: var(--text-main);
user-select: none;
}
.list-item:hover {
background-color: var(--bg-tertiary);
}
.list-item.active {
background-color: #e3f2fd;
color: var(--accent);
font-weight: 500;
}
.sidebar-footer {
padding: 16px;
border-top: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
}
.user-profile {
display: flex;
align-items: center;
gap: 10px;
font-size: 0.85rem;
font-weight: 500;
}
.avatar {
width: 28px;
height: 28px;
background: #ddd;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.7rem;
font-weight: bold;
}
/* Main Content Area */
#main-content {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
#top-bar {
height: 60px;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
}
.search-container {
display: flex;
align-items: center;
gap: 10px;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 6px 12px;
width: 400px;
transition: var(--transition);
}
.search-container:focus-within {
border-color: var(--accent);
background: white;
box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
}
.search-container i {
width: 16px;
color: var(--text-muted);
}
.search-container input {
border: none;
background: transparent;
outline: none;
width: 100%;
font-size: 0.9rem;
}
.top-bar-actions {
display: flex;
gap: 10px;
}
/* Editor Layout */
#editor-container {
flex: 1;
display: flex;
overflow: hidden;
}
#notes-browser {
width: var(--browser-width);
border-right: 1px solid var(--border-color);
display: flex;
flex-direction: column;
}
.browser-header {
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--border-color);
}
.browser-header h2 {
font-size: 1.1rem;
font-weight: 600;
}
#notes-list {
flex: 1;
overflow-y: auto;
}
.note-item {
padding: 16px 20px;
border-bottom: 1px solid var(--border-color);
cursor: pointer;
transition: var(--transition);
}
.note-item:hover {
background-color: var(--bg-secondary);
}
.note-item.active {
background-color: #f0f7ff;
border-left: 4px solid var(--accent);
}
.note-item-title {
font-weight: 600;
font-size: 0.95rem;
margin-bottom: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.note-item-snippet {
font-size: 0.8rem;
color: var(--text-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
#editor-pane {
flex: 1;
display: flex;
flex-direction: column;
position: relative;
}
#editor-toolbar {
height: 48px;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
padding: 0 15px;
gap: 10px;
}
.toolbar-group {
display: flex;
gap: 4px;
}
.toolbar-divider {
width: 1px;
height: 24px;
background: var(--border-color);
margin: 0 4px;
}
.tool-btn {
width: 32px;
height: 32px;
border: none;
background: transparent;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-main);
transition: var(--transition);
}
.tool-btn:hover {
background: var(--bg-tertiary);
color: var(--accent);
}
.editor-content-wrapper {
flex: 1;
overflow-y: auto;
padding: 40px 60px;
display: flex;
flex-direction: column;
align-items: center;
}
#note-title {
width: 100%;
max-width: 800px;
border: none;
outline: none;
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 20px;
color: var(--text-main);
}
#note-body {
width: 100%;
max-width: 800px;
min-height: 500px;
border: none;
outline: none;
font-size: 1.1rem;
line-height: 1.6;
color: var(--text-main);
white-space: pre-wrap;
}
#note-body:empty:before {
content: attr(placeholder);
color: var(--text-muted);
pointer-events: none;
}
#editor-footer {
height: 30px;
border-top: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
font-size: 0.75rem;
color: var(--text-muted);
}
/* Generic Components */
.btn-icon {
width: 32px;
height: 32px;
border: none;
background: var(--bg-tertiary);
border-radius: 6px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: var(--transition);
}
.btn-icon:hover {
background: #ddd;
}
.btn-secondary {
padding: 6px 12px;
border: 1px solid var(--border-color);
background: white;
border-radius: 6px;
font-size: 0.85rem;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
transition: var(--transition);
}
.btn-secondary:hover {
background: var(--bg-secondary);
}
.btn-ghost {
border: none;
background: transparent;
cursor: pointer;
color: var(--text-muted);
}
</style>
</head>
<body>
<div id="app">
<aside id="sidebar">
<div class="sidebar-header">
<div class="logo">
<div class="logo-icon"></div>
<span>Lumina</span>
</div>
<button id="btn-new-note" class="btn-icon" title="New Note">
<i data-lucide="plus"></i>
</button>
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<div class="section-title">Library</div>
<div id="notebooks-list" class="list-group"></div>
</div>
<div class="nav-section">
<div class="section-title">Tags</div>
<div id="tags-list" class="list-group"></div>
</div>
</nav>
<div class="sidebar-footer">
<div class="user-profile">
<div class="avatar">JS</div>
<span>Jane Smith</span>
</div>
<button class="btn-icon" title="Settings">
<i data-lucide="settings"></i>
</button>
</div>
</aside>
<main id="main-content">
<header id="top-bar">
<div class="search-container">
<i data-lucide="search"></i>
<input type="text" id="global-search" placeholder="Search your knowledge base...">
</div>
<div class="top-bar-actions">
<button id="btn-share" class="btn-secondary">
<i data-lucide="share-2"></i> Share
</button>
<button id="btn-sync" class="btn-secondary">
<i data-lucide="refresh-cw"></i>
</button>
</div>
</header>
<div id="editor-container">
<div id="notes-browser">
<div class="browser-header">
<h2 id="current-notebook-name">All Notes</h2>
<button id="btn-filter" class="btn-ghost">
<i data-lucide="filter"></i>
</button>
</div>
<div id="notes-list" class="list-group"></div>
</div>
<div id="editor-pane">
<div id="editor-toolbar">
<div class="toolbar-group">
<button class="tool-btn" data-cmd="bold"><i data-lucide="bold"></i></button>
<button class="tool-btn" data-cmd="italic"><i data-lucide="italic"></i></button>
<button class="tool-btn" data-cmd="underline"><i data-lucide="underline"></i></button>
</div>
<div class="toolbar-divider"></div>
<div class="toolbar-group">
<button class="tool-btn" data-cmd="formatBlock" data-val="h1"><i data-lucide="heading-1"></i></button>
<button class="tool-btn" data-cmd="formatBlock" data-val="h2"><i data-lucide="heading-2"></i></button>
<button class="tool-btn" data-cmd="insertUnorderedList"><i data-lucide="list"></i></button>
</div>
<div class="toolbar-divider"></div>
<div class="toolbar-group">
<button class="tool-btn" id="btn-insert-image"><i data-lucide="image"></i></button>
<button class="tool-btn" id="btn-insert-table"><i data-lucide="table"></i></button>
</div>
</div>
<div class="editor-content-wrapper">
<input type="text" id="note-title" placeholder="Note Title...">
<div id="note-body" contenteditable="true" placeholder="Start writing your thoughts..."></div>
</div>
<div id="editor-footer">
<span id="word-count">0 words</span>
<span id="last-saved">Saved just now</span>
</div>
</div>
</div>
</main>
</div>
<script>
'use strict';
// ── class: NotesStore ──
class NotesStore {
constructor() {
this.notes = JSON.parse(localStorage.getItem('lumina_notes')) || this.getSeedData();
this.notebooks = JSON.parse(localStorage.getItem('lumina_notebooks')) || [
{ id: 'nb1', name: 'Personal', icon: 'user' },
{ id: 'nb2', name: 'Work', icon: 'briefcase' },
{ id: 'nb3', name: 'Ideas', icon: 'lightbulb' },
{ id: 'nb4', name: 'Archive', icon: 'archive' },
];
this.tags = JSON.parse(localStorage.getItem('lumina_tags')) || ['Project X', 'Urgent', 'Reference', 'Journal'];
this.activeNotebookId = 'nb1';
this.activeNoteId = this.notes[0]?.id || null;
}
getSeedData() {
return [
{
id: 'n1',
notebookId: 'nb1',
title: 'Welcome to Lumina',
content: 'Welcome to your new knowledge base! <p>This is a <b>fully functional prototype</b>.</p><ul><li>Organize by Notebooks</li><li>Tag your thoughts</li><li>Write in rich text</li></ul>',
tags: ['Reference'],
updatedAt: Date.now()
},
{
id: 'n2',
notebookId: 'nb2',
title: 'Q4 Project Roadmap',
content: 'Focus areas for the next quarter: <br>1. UI Refresh<br>2. API Integration<br>3. Performance Optimization',
tags: ['Project X', 'Urgent'],
updatedAt: Date.now() - 100000
},
{
id: 'n3',
notebookId: 'nb3',
title: 'Sci-Fi Story Prompt',
content: 'What if memory could be traded as a currency?',
tags: ['Ideas'],
updatedAt: Date.now() - 500000
}
];
}
save() {
localStorage.setItem('lumina_notes', JSON.stringify(this.notes));
localStorage.setItem('lumina_notebooks', JSON.stringify(this.notebooks));
localStorage.setItem('lumina_tags', JSON.stringify(this.tags));
}
addNote(notebookId) {
const newNote = {
id: 'n' + Date.now(),
notebookId,
title: 'Untitled Note',
content: '',
tags: [],
updatedAt: Date.now()
};
this.notes.unshift(newNote);
this.activeNoteId = newNote.id;
this.save();
return newNote;
}
updateNote(id, updates) {
const index = this.notes.findIndex(n => n.id === id);
if (index !== -1) {
this.notes[index] = { ...this.notes[index], ...updates, updatedAt: Date.now() };
this.save();
}
}
getNotesByNotebook(notebookId) {
if (!notebookId) return this.notes;
return this.notes.filter(n => n.notebookId === notebookId);
}
}
// ── class: NotesUI ──
class NotesUI {
constructor(store) {
this.store = store;
this.initElements();
this.setupEventListeners();
this.renderAll();
lucide.createIcons();
}
initElements() {
this.els = {
notebooksList: document.getElementById('notebooks-list'),
tagsList: document.getElementById('tags-list'),
notesList: document.getElementById('notes-list'),
noteTitle: document.getElementById('note-title'),
noteBody: document.getElementById('note-body'),
notebookName: document.getElementById('current-notebook-name'),
wordCount: document.getElementById('word-count'),
lastSaved: document.getElementById('last-saved'),
btnNewNote: document.getElementById('btn-new-note'),
globalSearch: document.getElementById('global-search'),
};
}
setupEventListeners() {
this.els.btnNewNote.onclick = () => {
this.store.addNote(this.store.activeNotebookId);
this.renderAll();
this.selectNote(this.store.activeNoteId);
};
this.els.noteTitle.oninput = (e) => {
this.store.updateNote(this.store.activeNoteId, { title: e.target.value });
this.updateNoteList();
this.updateSavedStatus();
};
this.els.noteBody.oninput = (e) => {
this.store.updateNote(this.store.activeNoteId, { content: e.target.innerHTML });
this.updateNoteList();
this.updateWordCount();
this.updateSavedStatus();
};
this.els.globalSearch.oninput = (e) => {
this.renderNoteList(e.target.value);
};
// Rich Text Toolbar
document.querySelectorAll('.tool-btn[data-cmd]').forEach(btn => {
btn.onclick = () => {
const cmd = btn.dataset.cmd;
const val = btn.dataset.val || null;
document.execCommand(cmd, false, val);
this.els.noteBody.focus();
};
});
}
renderAll() {
this.renderNotebooks();
this.renderTags();
this.renderNoteList();
this.selectNote(this.store.activeNoteId);
}
renderNotebooks() {
this.els.notebooksList.innerHTML = this.store.notebooks.map(nb => `
<div class="list-item ${this.store.activeNotebookId === nb.id ? 'active' : ''}" data-id="${nb.id}">
<i data-lucide="${nb.icon}" style="width:16px"></i>
${nb.name}
</div>
`).join('');
this.els.notebooksList.querySelectorAll('.list-item').forEach(item => {
item.onclick = () => {
this.store.activeNotebookId = item.dataset.id;
this.els.notebookName.textContent = this.store.notebooks.find(nb => nb.id === item.dataset.id).name;
this.renderNotebooks();
this.renderNoteList();
};
});
lucide.createIcons();
}
renderTags() {
this.els.tagsList.innerHTML = this.store.tags.map(tag => `
<div class="list-item">
<i data-lucide="tag" style="width:16px"></i>
${tag}
</div>
`).join('');
lucide.createIcons();
}
renderNoteList(filter = '') {
const notes = this.store.getNotesByNotebook(this.store.activeNotebookId);
const filtered = notes.filter(n =>
n.title.toLowerCase().includes(filter.toLowerCase()) ||
n.content.toLowerCase().includes(filter.toLowerCase())
);
this.els.notesList.innerHTML = filtered.map(n => `
<div class="note-item ${this.store.activeNoteId === n.id ? 'active' : ''}" data-id="${n.id}">
<div class="note-item-title">${n.title || 'Untitled Note'}</div>
<div class="note-item-snippet">${this.stripHtml(n.content)}</div>
</div>
`).join('');
this.els.notesList.querySelectorAll('.note-item').forEach(item => {
item.onclick = () => this.selectNote(item.dataset.id);
});
}
updateNoteList() {
// Refresh current list without losing search filter if active
this.renderNoteList(this.els.globalSearch.value);
}
selectNote(id) {
if (!id) return;
this.store.activeNoteId = id;
const note = this.store.notes.find(n => n.id === id);
if (!note) return;
this.els.noteTitle.value = note.title;
this.els.noteBody.innerHTML = note.content;
this.renderNoteList(this.els.globalSearch.value);
this.updateWordCount();
}
stripHtml(html) {
const tmp = document.createElement('DIV');
tmp.innerHTML = html;
return tmp.textContent || tmp.innerText || '';
}
updateWordCount() {
const text = this.els.noteBody.innerText.trim();
const words = text ? text.split(/\s+/).length : 0;
this.els.wordCount.textContent = `${words} words`;
}
updateSavedStatus() {
this.els.lastSaved.textContent = 'Saving...';
clearTimeout(this.saveTimeout);
this.saveTimeout = setTimeout(() => {
this.els.lastSaved.textContent = 'Saved just now';
}, 1000);
}
}
// ── main ──
const store = new NotesStore();
const ui = new NotesUI(store);
</script>
</body>
</html>
<!-- agent-meta {"model":"gemma-4-31b","provider":"cerebras","persona":"composer","sessionId":"d8d45264-279f-4cff-b073-1a159cede721","tokensIn":14678,"tokensOut":6519,"tokensTotal":21197,"turns":2,"toolCalls":8,"failedToolCalls":0,"timestamp":"2026-07-14T16:17:29.561Z"} -->