110 lines
3.0 KiB
Vue
110 lines
3.0 KiB
Vue
<script setup lang="ts">
|
|
import { ref, onMounted, onUnmounted } from 'vue'
|
|
import { renderInlineComponent, type VueComponentDefinition } from '../services/dynamicComponents'
|
|
import { useCanvasStore } from '../stores/canvas'
|
|
import CanvasGallery from './CanvasGallery.vue'
|
|
|
|
const canvasStore = useCanvasStore()
|
|
const showGallery = ref(true)
|
|
|
|
function handleLoadComponent(e: Event) {
|
|
const detail = (e as CustomEvent).detail
|
|
if (!detail) return
|
|
|
|
const container = document.getElementById('canvas-content')
|
|
if (!container) return
|
|
|
|
// Hide gallery when MCP renders content
|
|
showGallery.value = false
|
|
|
|
const placeholder = container.querySelector('.canvas-placeholder')
|
|
if (placeholder) placeholder.remove()
|
|
|
|
const definition: VueComponentDefinition = {
|
|
id: detail.id,
|
|
name: detail.name,
|
|
template: detail.template,
|
|
setup: detail.setup,
|
|
style: detail.style,
|
|
props: detail.props,
|
|
imports: detail.imports || ['ref', 'reactive', 'computed']
|
|
}
|
|
|
|
const result = renderInlineComponent(definition, container, {}, false)
|
|
;(window as any).__vueComponentUnmount = result.unmount
|
|
|
|
window.dispatchEvent(new CustomEvent('vue-component-rendered', { detail }))
|
|
canvasStore.addToHistory({ tool: 'load_vue_component', args: detail, timestamp: Date.now() })
|
|
}
|
|
|
|
function handleContentRendered() {
|
|
showGallery.value = false
|
|
canvasStore.isAnonymousCanvas = false
|
|
}
|
|
|
|
function handleStartAnonymous() {
|
|
showGallery.value = false
|
|
canvasStore.isAnonymousCanvas = true
|
|
}
|
|
|
|
function handleClearCanvas() {
|
|
showGallery.value = true
|
|
canvasStore.isAnonymousCanvas = false
|
|
const container = document.getElementById('canvas-content')
|
|
if (container) {
|
|
// Remove all non-gallery content
|
|
const children = Array.from(container.children)
|
|
for (const child of children) {
|
|
if (!(child as HTMLElement).classList?.contains('canvas-placeholder')) {
|
|
child.remove()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
window.addEventListener('load-vue-component', handleLoadComponent)
|
|
window.addEventListener('clear-canvas', handleClearCanvas)
|
|
window.addEventListener('canvas-content-rendered', handleContentRendered)
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
window.removeEventListener('load-vue-component', handleLoadComponent)
|
|
window.removeEventListener('clear-canvas', handleClearCanvas)
|
|
window.removeEventListener('canvas-content-rendered', handleContentRendered)
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="canvas-container">
|
|
<div id="canvas-content" class="canvas-content">
|
|
<div v-if="showGallery" class="canvas-placeholder">
|
|
<CanvasGallery @snapshot-restored="showGallery = false" @component-loaded="showGallery = false" @start-anonymous="handleStartAnonymous" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.canvas-container {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
background: var(--bg-primary);
|
|
overflow: hidden;
|
|
position: relative;
|
|
}
|
|
|
|
.canvas-content {
|
|
flex: 1;
|
|
position: relative;
|
|
min-height: 100%;
|
|
overflow: auto;
|
|
}
|
|
|
|
.canvas-placeholder {
|
|
height: 100%;
|
|
min-height: 400px;
|
|
}
|
|
</style>
|