feat: Auto-save components, soft delete, tags, compact WCO header
- Auto-save rendered Vue components to DB on render_vue_component - Soft delete (archive) instead of hard delete for components - Tags support for component categorization - Gallery limited to 10 most recent items per section - Upsert with ON CONFLICT for component saves - PUT endpoint for partial component updates - Collapsible toolbar with animated toggle button - Window Controls Overlay support for PWA titlebar - Compact header mode (32px) with hidden dot toggle - Dynamic theme-color meta sync for Windows titlebar
This commit is contained in:
@@ -38,13 +38,15 @@ const editIcon = ref('')
|
||||
const editOrder = ref(99)
|
||||
|
||||
const filteredCanvases = computed(() => {
|
||||
const list = showArchived.value ? store.canvases : store.activeCanvasesList
|
||||
if (!searchQuery.value) return list
|
||||
const q = searchQuery.value.toLowerCase()
|
||||
return list.filter(c =>
|
||||
c.name.toLowerCase().includes(q) ||
|
||||
(c.description && c.description.toLowerCase().includes(q))
|
||||
)
|
||||
let list = showArchived.value ? store.canvases : store.activeCanvasesList
|
||||
if (searchQuery.value) {
|
||||
const q = searchQuery.value.toLowerCase()
|
||||
list = list.filter(c =>
|
||||
c.name.toLowerCase().includes(q) ||
|
||||
(c.description && c.description.toLowerCase().includes(q))
|
||||
)
|
||||
}
|
||||
return list.slice(0, 10)
|
||||
})
|
||||
|
||||
const filteredSnapshots = computed(() => {
|
||||
@@ -168,7 +170,7 @@ async function loadComponent(comp: VueComponentDefinition) {
|
||||
|
||||
async function fetchComponents() {
|
||||
try {
|
||||
savedComponents.value = await componentsApi.getAll()
|
||||
savedComponents.value = await componentsApi.getAll({ limit: 10 })
|
||||
} catch {
|
||||
savedComponents.value = []
|
||||
}
|
||||
@@ -432,10 +434,14 @@ onMounted(() => {
|
||||
<div class="card-content">
|
||||
<div class="card-name">{{ comp.name }}</div>
|
||||
<div class="card-desc card-id">{{ comp.id }}</div>
|
||||
<div v-if="comp.tags?.length" class="card-tags">
|
||||
<span v-for="tag in comp.tags" :key="tag" class="tag-pill">{{ tag }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-meta">
|
||||
<span class="card-badge component">componente</span>
|
||||
<span v-if="comp.status === 'archived'" class="card-badge archived-badge">Archivado</span>
|
||||
</div>
|
||||
|
||||
<!-- Loading overlay -->
|
||||
@@ -955,4 +961,20 @@ onMounted(() => {
|
||||
.new-btn.cancel:hover {
|
||||
background: var(--border-color);
|
||||
}
|
||||
|
||||
.card-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.25rem;
|
||||
margin-top: 0.375rem;
|
||||
}
|
||||
|
||||
.tag-pill {
|
||||
padding: 0.0625rem 0.375rem;
|
||||
background: rgba(99, 102, 241, 0.1);
|
||||
color: #818cf8;
|
||||
border-radius: 999px;
|
||||
font-size: 0.625rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,6 +4,10 @@ import { RouterLink, useRoute } from 'vue-router'
|
||||
import { useCanvasStore } from '../stores/canvas'
|
||||
import { useProjectCanvasStore } from '../stores/projectCanvas'
|
||||
|
||||
defineProps<{
|
||||
collapsed?: boolean
|
||||
}>()
|
||||
|
||||
const route = useRoute()
|
||||
const canvasStore = useCanvasStore()
|
||||
const projectCanvasStore = useProjectCanvasStore()
|
||||
@@ -26,7 +30,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<aside class="toolbar">
|
||||
<aside class="toolbar" :class="{ collapsed }">
|
||||
<!-- Navegacion principal -->
|
||||
<div class="toolbar-section nav-section">
|
||||
<RouterLink to="/" class="toolbar-btn" :class="{ active: route.path === '/' }" title="Home">
|
||||
@@ -121,6 +125,12 @@ onMounted(() => {
|
||||
<path d="M18 9a9 9 0 0 1-9 9"/>
|
||||
</svg>
|
||||
</RouterLink>
|
||||
|
||||
<RouterLink to="/agents" class="toolbar-btn" :class="{ active: route.path === '/agents' }" title="Agents">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M12 2L9.5 7.5 4 8.5l4 4-1 5.5L12 15l5 3-1-5.5 4-4-5.5-1z"/>
|
||||
</svg>
|
||||
</RouterLink>
|
||||
</div>
|
||||
|
||||
<div class="toolbar-divider"></div>
|
||||
@@ -154,6 +164,19 @@ onMounted(() => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
overflow: hidden;
|
||||
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
padding 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
opacity 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.toolbar.collapsed {
|
||||
width: 0;
|
||||
padding: 0;
|
||||
border-right-color: transparent;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.toolbar-section {
|
||||
|
||||
Reference in New Issue
Block a user