@@ -166,7 +187,7 @@ const auth = useAuth()
const authHeaders = computed(() => ({ Authorization: `Bearer ${auth.token.value}` }))
const elements = ref([])
const selectedNode = ref(null)
-const canvasName = computed(() => props.canvasId)
+const canvasName = ref(props.canvasId)
const nodeTypes = [
{ type: 'text', name: 'Текст', icon: '📝', description: 'Markdown заметка' },
@@ -181,6 +202,7 @@ const nodeTypes = [
const loadData = async () => {
const data = await loadCanvas(props.canvasId)
elements.value = transformToFlowElements(data)
+ canvasName.value = data.name || props.canvasId
// Mark initial state as saved
markAsSaved(data)
}
@@ -405,6 +427,23 @@ const onNodeDragStop = (event) => {
const onNodeDoubleClick = (event) => {
const node = event.node
+
+ // 1. Cross-Canvas Navigation Logic
+ // Check if it's a file node or has a URL/file reference to another canvas
+ const targetId = node.data?.file || node.data?.url || ''
+
+ if (targetId && (node.type === 'file' || targetId.includes('.canvas') || targetId.startsWith('/canvas/'))) {
+ // Clean up ID (e.g. "project.canvas" -> "project", "/canvas/project" -> "project")
+ const cleanId = targetId.replace('.canvas', '').replace('/canvas/', '')
+ console.log('[Navigation] Opening cross-linked canvas:', cleanId)
+ navigateTo(`/canvas/${cleanId}`)
+ return // Stop further processing
+ } else if (node.type === 'file' && !targetId) {
+ alert('У этого узла еще нет ссылки на другую карту. Выберите карту в панели свойств (справа).')
+ return
+ }
+
+ // 2. Task Toggle Logic (fallback)
const currentState = node.data?.state || 'todo'
// Quick toggle workflow: todo -> in_progress -> review -> completed -> todo
@@ -413,7 +452,7 @@ const onNodeDoubleClick = (event) => {
'in_progress': 'review',
'review': 'completed',
'completed': 'todo',
- 'blocked': 'todo' // From blocked back to todo
+ 'blocked': 'todo'
}
const nextState = nextStateMap[currentState] || 'todo'
@@ -446,6 +485,27 @@ const exportObsidian = () => {
URL.revokeObjectURL(url)
}
+const deleteCanvas = async () => {
+ if (!confirm('Вы уверены, что хотите полностью удалить эту карту? Это действие необратимо.')) return
+
+ try {
+ const res = await fetch(`/api/canvas/${props.canvasId}`, {
+ method: 'DELETE',
+ headers: authHeaders.value
+ })
+
+ if (res.ok) {
+ navigateTo('/canvases')
+ } else {
+ const err = await res.json()
+ alert('Ошибка при удалении: ' + (err.message || 'неизвестная ошибка'))
+ }
+ } catch (err) {
+ console.error('Delete error:', err)
+ alert('Не удалось удалить карту')
+ }
+}
+
const fitView = () => {
const padding = 40
const cols = 3
@@ -551,6 +611,7 @@ const saveCanvas = async () => {
}
const usersList = ref([])
+const canvasesList = ref([])
// Load data on mount
onMounted(async () => {
@@ -569,6 +630,19 @@ onMounted(async () => {
} catch (e) {
console.error('Failed to load users', e)
}
+
+ // Load canvases for cross-link dropdown
+ try {
+ const res = await fetch('/api/canvas', {
+ headers: authHeaders.value
+ })
+ if (res.ok) {
+ const data = await res.json()
+ canvasesList.value = data.canvases || []
+ }
+ } catch (e) {
+ console.error('Failed to load canvases', e)
+ }
})
diff --git a/app/components/FlowNodeRenderer.vue b/app/components/FlowNodeRenderer.vue
index c162500..1b50e3f 100644
--- a/app/components/FlowNodeRenderer.vue
+++ b/app/components/FlowNodeRenderer.vue
@@ -15,10 +15,10 @@
{{ data.label || getNodeTitle() }}
{{ data.type || type }}
+
{{ stateLabel }}
-
diff --git a/app/composables/useCanvas.ts b/app/composables/useCanvas.ts
index 9f230a9..041501e 100644
--- a/app/composables/useCanvas.ts
+++ b/app/composables/useCanvas.ts
@@ -23,6 +23,8 @@ export interface CanvasEdge {
}
export interface CanvasData {
+ id?: string
+ name?: string
nodes: CanvasNode[]
edges: CanvasEdge[]
}
@@ -72,7 +74,12 @@ export const useCanvas = () => {
toSide: e.toSide || 'left'
}))
- return { nodes, edges }
+ return {
+ id: json.id,
+ name: json.name,
+ nodes,
+ edges
+ }
}
const getAuthHeaders = (): Record
=> {
diff --git a/app/pages/canvases.vue b/app/pages/canvases.vue
index 852d901..659743a 100644
--- a/app/pages/canvases.vue
+++ b/app/pages/canvases.vue
@@ -60,8 +60,11 @@