← Notes results

Notes

Gemma 4 31b · composer

Initial view of Notes

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"} -->