diff --git a/web/backend/dist/index.html b/web/backend/dist/index.html new file mode 100644 index 000000000..15af737cc --- /dev/null +++ b/web/backend/dist/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + PicoClaw + + + + + + + +
+ + + + + + + + + + diff --git a/web/backend/dist/sessions.js b/web/backend/dist/sessions.js new file mode 100644 index 000000000..1e1967469 --- /dev/null +++ b/web/backend/dist/sessions.js @@ -0,0 +1,32 @@ +// web/backend/dist/sessions.js +// Session History Controller (~4KB) +function loadSessionHistory() { + const history = document.getElementById('session-history'); + if (!history) return; + + fetch('/api/sessions/search?limit=20') + .then(r => r.json()) + .then(sessions => { + history.innerHTML = '

Recent Sessions

'; + sessions.forEach(session => { + const div = document.createElement('div'); + div.className = 'session-item'; + div.innerHTML = ` +
${session.title || 'Untitled'}
+
${(session.tags || []).map(t => `#${t}`).join(' ')}
+
${new Date(session.timestamp).toLocaleDateString()}
+ `; + div.onclick = () => loadSessionContext(session.id); + history.appendChild(div); + }); + }) + .catch(e => console.error('Failed to load sessions:', e)); +} + +function loadSessionContext(sessionId) { + // TODO: load session context into chat + console.log('Load session:', sessionId); +} + +// Expose +window.loadSessionHistory = loadSessionHistory; diff --git a/web/backend/dist/styles/vault.css b/web/backend/dist/styles/vault.css new file mode 100644 index 000000000..eb1c44868 --- /dev/null +++ b/web/backend/dist/styles/vault.css @@ -0,0 +1,82 @@ +/* web/backend/dist/styles/vault.css */ +.sidebar { + position: fixed; + left: 0; + top: 0; + width: 300px; + height: 100%; + background: var(--background-primary, #1e1e1e); + color: var(--text-normal, #dcddde); + padding: 10px; + overflow-y: auto; + z-index: 1000; +} + +.sidebar.hidden { + display: none; +} + +.sidebar-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; +} + +.sidebar-header h3 { + margin: 0; +} + +.tree-item { + padding: 5px 10px; + cursor: pointer; + border-radius: 4px; +} + +.tree-item:hover { + background: var(--background-secondary, #2d2d2d); +} + +.tag-badge { + display: inline-block; + background: var(--background-secondary, #2d2d2d); + border-radius: 12px; + padding: 2px 8px; + margin: 2px; + cursor: pointer; + font-size: 0.9em; +} + +.tag-badge:hover { + background: var(--interactive-accent, #5865f2); + color: white; +} + +.skill-item { + padding: 8px; + border-bottom: 1px solid var(--background-secondary, #2d2d2d); +} + +.skill-name { + font-weight: bold; +} + +.skill-usage { + font-size: 0.9em; + color: var(--text-muted, #72767d); +} + +.session-item { + padding: 8px; + border-bottom: 1px solid var(--background-secondary, #2d2d2d); + cursor: pointer; +} + +.session-title { + font-weight: bold; +} + +.session-tags { + font-size: 0.9em; + color: var(--text-muted, #72767d); +} diff --git a/web/backend/dist/tags.js b/web/backend/dist/tags.js new file mode 100644 index 000000000..91e53d3d3 --- /dev/null +++ b/web/backend/dist/tags.js @@ -0,0 +1,36 @@ +// web/backend/dist/tags.js +// Tag Cloud Controller (~3KB) +function loadTagCloud() { + const cloud = document.getElementById('tag-cloud'); + if (!cloud) return; + + fetch('/api/vault/tags') + .then(r => r.json()) + .then(tags => { + cloud.innerHTML = '

Tags

'; + tags.forEach(tag => { + const badge = document.createElement('span'); + badge.className = 'tag-badge'; + badge.textContent = `#${tag.name} (${tag.count})`; + badge.onclick = () => filterByTag(tag.name); + cloud.appendChild(badge); + }); + }) + .catch(e => console.error('Failed to load tags:', e)); +} + +function filterByTag(tagName) { + fetch(`/api/sessions/search?tag=${encodeURIComponent(tagName)}`) + .then(r => r.json()) + .then(data => displaySearchResults(data)) + .catch(e => console.error('Failed to filter by tag:', e)); +} + +function displaySearchResults(data) { + // TODO: render search results in UI + console.log('Search results:', data); +} + +// Expose +window.loadTagCloud = loadTagCloud; +window.filterByTag = filterByTag; diff --git a/web/backend/dist/tools-skills.js b/web/backend/dist/tools-skills.js new file mode 100644 index 000000000..c12105c46 --- /dev/null +++ b/web/backend/dist/tools-skills.js @@ -0,0 +1,42 @@ +// web/backend/dist/tools-skills.js +// Tool Skills Panel Controller (~3KB) +function loadToolSkills() { + const panel = document.getElementById('tool-skills-panel'); + if (!panel) return; + + fetch('/api/tools/skills') + .then(r => r.json()) + .then(skills => { + panel.innerHTML = '

Tool Skills

'; + skills.forEach(skill => { + const div = document.createElement('div'); + div.className = 'skill-item'; + div.innerHTML = ` +
${skill.name || 'Unknown'}
+
Used ${skill.usage_count || 0} times
+
${(skill.tags || []).map(t => `#${t}`).join(' ')}
+ `; + panel.appendChild(div); + }); + }) + .catch(e => console.error('Failed to load tool skills:', e)); +} + +async function searchCommunityRegistry(query) { + try { + const response = await fetch(`/api/tools/registry?q=${encodeURIComponent(query || '')}`); + const results = await response.json(); + displayRegistryResults(results); + } catch (e) { + console.error('Failed to search registry:', e); + } +} + +function displayRegistryResults(results) { + console.log('Registry results:', results); + // TODO: render results in UI +} + +// Expose +window.loadToolSkills = loadToolSkills; +window.searchCommunityRegistry = searchCommunityRegistry; diff --git a/web/backend/dist/vault.js b/web/backend/dist/vault.js new file mode 100644 index 000000000..e22903ac3 --- /dev/null +++ b/web/backend/dist/vault.js @@ -0,0 +1,64 @@ +// web/backend/dist/vault.js +// Vault Sidebar Controller (Vanilla JS, ~5KB) +document.addEventListener('DOMContentLoaded', () => { + const sidebar = document.getElementById('vault-sidebar'); + const toggleBtn = document.getElementById('toggle-sidebar'); + const closeBtn = document.getElementById('close-sidebar'); + + if (toggleBtn) { + toggleBtn.addEventListener('click', () => { + sidebar.classList.toggle('hidden'); + if (!sidebar.classList.contains('hidden')) { + loadVaultTree(); + } + }); + } + + if (closeBtn) { + closeBtn.addEventListener('click', () => { + sidebar.classList.add('hidden'); + }); + } + + async function loadVaultTree() { + try { + const response = await fetch('/api/vault/list'); + const data = await response.json(); + renderTree(data.notes || []); + } catch (e) { + console.error('Failed to load vault tree:', e); + } + } + + function renderTree(items) { + const tree = document.getElementById('vault-tree'); + if (!tree) return; + tree.innerHTML = ''; + items.forEach(item => { + const div = document.createElement('div'); + div.className = 'tree-item'; + div.textContent = item.name || item.path || 'Untitled'; + div.onclick = () => loadNote(item.path || item.name); + tree.appendChild(div); + }); + } + + async function loadNote(path) { + try { + const response = await fetch(`/api/vault/note?path=${encodeURIComponent(path)}`); + const data = await response.json(); + showNoteModal(data); + } catch (e) { + console.error('Failed to load note:', e); + } + } + + function showNoteModal(noteData) { + // Simple modal implementation + alert(`Note: ${noteData.title || 'Note'}\n\n${noteData.content || ''}`); + } + + // Expose functions for other scripts + window.loadVaultTree = loadVaultTree; + window.loadNote = loadNote; +});