- Add Tauri v2 shell (Cargo, tauri.conf.json, capabilities, plugins) - Migrate all fetch() calls to apiFetch() for Tauri-aware HTTP - Migrate WebSocket endpoints to resolveEndpoints() for dynamic URLs - Add ServerConfigDialog for remote server URL configuration - Add tauri.ts lib with isTauri detection, apiFetch wrapper, plugin helpers - Add server-config Pinia store with persistence via plugin-store - Conditional PWA (disabled in Tauri builds) - Android: home screen transcript widget (last 5 messages, 30s refresh) - Android: voice command / share activity (SpeechRecognizer + WebSocket) - Android: signed release APK with auto-copy to installers/ - Remove stale frontend/src-tauri directory
346 lines
10 KiB
TypeScript
346 lines
10 KiB
TypeScript
import { defineStore } from 'pinia'
|
|
import { ref, computed } from 'vue'
|
|
import { apiFetch } from '@/lib/tauri'
|
|
import type { ProjectCanvas, CanvasComponent, ComponentUsage } from '../types/canvas'
|
|
|
|
// Uses relative URLs - works with Vite proxy in dev and Traefik in production
|
|
const API_URL = ''
|
|
|
|
export const useProjectCanvasStore = defineStore('projectCanvas', () => {
|
|
// State
|
|
const canvases = ref<ProjectCanvas[]>([])
|
|
const activeCanvas = ref<ProjectCanvas | null>(null)
|
|
const activeCanvasComponents = ref<CanvasComponent[]>([])
|
|
const loading = ref(false)
|
|
const saving = ref(false)
|
|
const error = ref<string | null>(null)
|
|
|
|
// State adicional
|
|
const toolbarCanvases = ref<ProjectCanvas[]>([])
|
|
const defaultCanvas = ref<ProjectCanvas | null>(null)
|
|
|
|
// Getters
|
|
const projectCanvases = computed(() =>
|
|
canvases.value.filter(c => c.type === 'project')
|
|
)
|
|
|
|
const systemCanvases = computed(() =>
|
|
canvases.value.filter(c => c.type === 'system')
|
|
)
|
|
|
|
const dynamicCanvas = computed(() =>
|
|
canvases.value.find(c => c.type === 'dynamic')
|
|
)
|
|
|
|
const activeCanvasesList = computed(() =>
|
|
canvases.value.filter(c => c.status !== 'archived')
|
|
)
|
|
|
|
const archivedCanvases = computed(() =>
|
|
canvases.value.filter(c => c.status === 'archived')
|
|
)
|
|
|
|
const canvasCount = computed(() => canvases.value.length)
|
|
|
|
const hasActiveCanvas = computed(() => activeCanvas.value !== null)
|
|
|
|
const hasDefaultCanvas = computed(() => defaultCanvas.value !== null)
|
|
|
|
// Actions
|
|
async function fetchCanvases(includeArchived = false) {
|
|
loading.value = true
|
|
error.value = null
|
|
try {
|
|
const url = includeArchived ? `${API_URL}/api/canvas?include_archived=true` : `${API_URL}/api/canvas`
|
|
const res = await apiFetch(url)
|
|
if (!res.ok) throw new Error('Failed to fetch canvases')
|
|
canvases.value = await res.json()
|
|
} catch (e) {
|
|
error.value = e instanceof Error ? e.message : 'Unknown error'
|
|
console.error('Error fetching canvases:', e)
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
async function fetchToolbarCanvases() {
|
|
try {
|
|
const res = await apiFetch(`${API_URL}/api/canvas/toolbar`)
|
|
if (!res.ok) throw new Error('Failed to fetch toolbar canvases')
|
|
toolbarCanvases.value = await res.json()
|
|
} catch (e) {
|
|
console.error('Error fetching toolbar canvases:', e)
|
|
toolbarCanvases.value = []
|
|
}
|
|
}
|
|
|
|
async function fetchDefaultCanvas(): Promise<ProjectCanvas | null> {
|
|
try {
|
|
const res = await apiFetch(`${API_URL}/api/canvas/default`)
|
|
if (!res.ok) throw new Error('Failed to fetch default canvas')
|
|
const data = await res.json()
|
|
if (data.hasDefault) {
|
|
defaultCanvas.value = data.canvas
|
|
return data.canvas
|
|
}
|
|
defaultCanvas.value = null
|
|
return null
|
|
} catch (e) {
|
|
console.error('Error fetching default canvas:', e)
|
|
defaultCanvas.value = null
|
|
return null
|
|
}
|
|
}
|
|
|
|
async function fetchCanvasById(id: string): Promise<ProjectCanvas | null> {
|
|
try {
|
|
const res = await apiFetch(`${API_URL}/api/canvas/${id}`)
|
|
if (!res.ok) {
|
|
if (res.status === 404) return null
|
|
throw new Error('Failed to fetch canvas')
|
|
}
|
|
return await res.json()
|
|
} catch (e) {
|
|
console.error('Error fetching canvas:', e)
|
|
return null
|
|
}
|
|
}
|
|
|
|
async function createCanvas(data: Partial<ProjectCanvas>): Promise<string | null> {
|
|
saving.value = true
|
|
error.value = null
|
|
try {
|
|
const res = await apiFetch(`${API_URL}/api/canvas`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(data)
|
|
})
|
|
if (!res.ok) throw new Error('Failed to create canvas')
|
|
const result = await res.json()
|
|
await fetchCanvases()
|
|
return result.id
|
|
} catch (e) {
|
|
error.value = e instanceof Error ? e.message : 'Unknown error'
|
|
console.error('Error creating canvas:', e)
|
|
return null
|
|
} finally {
|
|
saving.value = false
|
|
}
|
|
}
|
|
|
|
async function updateCanvas(id: string, data: Partial<ProjectCanvas>): Promise<boolean> {
|
|
saving.value = true
|
|
error.value = null
|
|
try {
|
|
const res = await apiFetch(`${API_URL}/api/canvas/${id}`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(data)
|
|
})
|
|
if (!res.ok) {
|
|
const errorData = await res.json()
|
|
throw new Error(errorData.error || 'Failed to update canvas')
|
|
}
|
|
await fetchCanvases()
|
|
// Update active canvas if it's the one being updated
|
|
if (activeCanvas.value?.id === id) {
|
|
activeCanvas.value = await fetchCanvasById(id)
|
|
}
|
|
return true
|
|
} catch (e) {
|
|
error.value = e instanceof Error ? e.message : 'Unknown error'
|
|
console.error('Error updating canvas:', e)
|
|
return false
|
|
} finally {
|
|
saving.value = false
|
|
}
|
|
}
|
|
|
|
async function deleteCanvas(id: string): Promise<boolean> {
|
|
error.value = null
|
|
try {
|
|
const res = await apiFetch(`${API_URL}/api/canvas/${id}`, {
|
|
method: 'DELETE'
|
|
})
|
|
if (!res.ok) {
|
|
const errorData = await res.json()
|
|
throw new Error(errorData.error || 'Failed to delete canvas')
|
|
}
|
|
await fetchCanvases()
|
|
if (activeCanvas.value?.id === id) {
|
|
clearActiveCanvas()
|
|
}
|
|
return true
|
|
} catch (e) {
|
|
error.value = e instanceof Error ? e.message : 'Unknown error'
|
|
console.error('Error deleting canvas:', e)
|
|
return false
|
|
}
|
|
}
|
|
|
|
async function restoreCanvas(id: string): Promise<boolean> {
|
|
return updateCanvas(id, { status: 'active' } as any)
|
|
}
|
|
|
|
async function cloneCanvas(id: string, newName?: string): Promise<string | null> {
|
|
saving.value = true
|
|
error.value = null
|
|
try {
|
|
const res = await apiFetch(`${API_URL}/api/canvas/${id}/clone`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ name: newName })
|
|
})
|
|
if (!res.ok) throw new Error('Failed to clone canvas')
|
|
const result = await res.json()
|
|
await fetchCanvases()
|
|
return result.id
|
|
} catch (e) {
|
|
error.value = e instanceof Error ? e.message : 'Unknown error'
|
|
console.error('Error cloning canvas:', e)
|
|
return null
|
|
} finally {
|
|
saving.value = false
|
|
}
|
|
}
|
|
|
|
// Canvas Components
|
|
async function fetchCanvasComponents(canvasId: string) {
|
|
loading.value = true
|
|
try {
|
|
const res = await apiFetch(`${API_URL}/api/canvas/${canvasId}/components`)
|
|
if (!res.ok) throw new Error('Failed to fetch canvas components')
|
|
activeCanvasComponents.value = await res.json()
|
|
} catch (e) {
|
|
console.error('Error fetching canvas components:', e)
|
|
activeCanvasComponents.value = []
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
async function addComponentToCanvas(
|
|
canvasId: string,
|
|
componentId: string,
|
|
props?: Record<string, any>,
|
|
position?: number
|
|
): Promise<boolean> {
|
|
try {
|
|
const res = await apiFetch(`${API_URL}/api/canvas/${canvasId}/components`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ component_id: componentId, props, position })
|
|
})
|
|
if (!res.ok) throw new Error('Failed to add component to canvas')
|
|
await fetchCanvasComponents(canvasId)
|
|
return true
|
|
} catch (e) {
|
|
console.error('Error adding component to canvas:', e)
|
|
return false
|
|
}
|
|
}
|
|
|
|
async function removeComponentFromCanvas(canvasId: string, componentId: string): Promise<boolean> {
|
|
try {
|
|
const res = await apiFetch(`${API_URL}/api/canvas/${canvasId}/components/${componentId}`, {
|
|
method: 'DELETE'
|
|
})
|
|
if (!res.ok) throw new Error('Failed to remove component from canvas')
|
|
await fetchCanvasComponents(canvasId)
|
|
return true
|
|
} catch (e) {
|
|
console.error('Error removing component from canvas:', e)
|
|
return false
|
|
}
|
|
}
|
|
|
|
async function updateCanvasComponent(
|
|
canvasId: string,
|
|
componentId: string,
|
|
data: { position?: number; props?: Record<string, any>; layout?: any; is_visible?: boolean }
|
|
): Promise<boolean> {
|
|
try {
|
|
const res = await apiFetch(`${API_URL}/api/canvas/${canvasId}/components/${componentId}`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(data)
|
|
})
|
|
if (!res.ok) throw new Error('Failed to update canvas component')
|
|
await fetchCanvasComponents(canvasId)
|
|
return true
|
|
} catch (e) {
|
|
console.error('Error updating canvas component:', e)
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Component Usage
|
|
async function getComponentUsage(componentId: string): Promise<ComponentUsage | null> {
|
|
try {
|
|
const res = await apiFetch(`${API_URL}/api/components/${componentId}/usage`)
|
|
if (!res.ok) throw new Error('Failed to get component usage')
|
|
return await res.json()
|
|
} catch (e) {
|
|
console.error('Error getting component usage:', e)
|
|
return null
|
|
}
|
|
}
|
|
|
|
// Canvas Activation
|
|
function setActiveCanvas(canvas: ProjectCanvas) {
|
|
activeCanvas.value = canvas
|
|
}
|
|
|
|
function clearActiveCanvas() {
|
|
activeCanvas.value = null
|
|
activeCanvasComponents.value = []
|
|
}
|
|
|
|
async function activateCanvas(id: string): Promise<boolean> {
|
|
const canvas = await fetchCanvasById(id)
|
|
if (!canvas) return false
|
|
setActiveCanvas(canvas)
|
|
await fetchCanvasComponents(id)
|
|
return true
|
|
}
|
|
|
|
return {
|
|
// State
|
|
canvases,
|
|
activeCanvas,
|
|
activeCanvasComponents,
|
|
toolbarCanvases,
|
|
defaultCanvas,
|
|
loading,
|
|
saving,
|
|
error,
|
|
// Getters
|
|
projectCanvases,
|
|
systemCanvases,
|
|
dynamicCanvas,
|
|
activeCanvasesList,
|
|
archivedCanvases,
|
|
canvasCount,
|
|
hasActiveCanvas,
|
|
hasDefaultCanvas,
|
|
// Actions
|
|
fetchCanvases,
|
|
fetchToolbarCanvases,
|
|
fetchDefaultCanvas,
|
|
fetchCanvasById,
|
|
createCanvas,
|
|
updateCanvas,
|
|
deleteCanvas,
|
|
restoreCanvas,
|
|
cloneCanvas,
|
|
fetchCanvasComponents,
|
|
addComponentToCanvas,
|
|
removeComponentFromCanvas,
|
|
updateCanvasComponent,
|
|
getComponentUsage,
|
|
setActiveCanvas,
|
|
clearActiveCanvas,
|
|
activateCanvas
|
|
}
|
|
})
|