claudes variant of ui

This commit is contained in:
JohannesItten
2026-07-07 23:25:03 +03:00
parent 29f8bb149d
commit e27770d1c3
26 changed files with 3232 additions and 1 deletions
+155
View File
@@ -0,0 +1,155 @@
<script setup lang="ts">
import { ref, markRaw, watch } from 'vue'
import { VueFlow } from '@vue-flow/core'
import type { Node, Edge, Connection } from '@vue-flow/core'
import '@vue-flow/core/dist/style.css'
import '@vue-flow/core/dist/theme-default.css'
import Toolbar from './components/Toolbar.vue'
import NodePalette from './components/NodePalette.vue'
import StudioNode from './components/nodes/StudioNode.vue'
import { useStudioStore } from './stores/studio'
import { buildGraph } from './graphBuilder'
import { defaultParams } from './nodeTypes'
import type { StudioNodeData } from './types'
const studio = useStudioStore()
const nodeTypes = { studio: markRaw(StudioNode) }
const nodes = ref<Node[]>([])
const edges = ref<Edge[]>([])
let nodeCounter = 0
// Sync server-push state into Vue Flow node data
watch(
() => studio.serverStatus,
(status) => {
for (const node of nodes.value) {
const d = node.data as StudioNodeData
const serverState = (status[node.id] as StudioNodeData['serverState']) ?? undefined
if (d.serverState !== serverState) {
node.data = { ...d, serverState }
}
}
},
{ deep: true }
)
function addNode(nodeType: string) {
nodeCounter++
const id = `${nodeType}-${nodeCounter}`
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const newNode: Node = {
id,
type: 'studio',
position: { x: 160 + (nodeCounter % 6) * 220, y: 80 + Math.floor(nodeCounter / 6) * 200 },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data: { nodeType, params: defaultParams(nodeType), serverState: undefined } as any,
}
// @ts-ignore TS2589: Vue Flow Node<T> generic is too deep for vue-tsc's instantiation limit
nodes.value = [...nodes.value, newNode]
}
function onConnect(connection: Connection) {
const srcHandle = connection.sourceHandle ?? ''
const tgtHandle = connection.targetHandle ?? ''
if (srcHandle.startsWith('video') !== tgtHandle.startsWith('video')) return
edges.value = [
...edges.value,
{ ...connection, id: `e-${Date.now()}`, animated: true } as Edge,
]
}
function run() {
const graph = buildGraph(nodes.value, edges.value)
studio.send({ type: 'load_graph', graph })
}
function stop() {
studio.send({ type: 'load_graph', graph: { nodes: [], edges: [] } })
}
function clear() {
nodes.value = []
edges.value = []
nodeCounter = 0
}
</script>
<template>
<div class="app-shell">
<Toolbar @run="run" @stop="stop" @clear="clear" />
<div class="canvas-area">
<NodePalette @add-node="addNode" />
<VueFlow
v-model:nodes="nodes"
v-model:edges="edges"
:node-types="nodeTypes"
class="flow-canvas"
:min-zoom="0.3"
:max-zoom="2"
fit-view-on-init
@connect="onConnect"
/>
</div>
</div>
</template>
<style>
html, body, #app {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background: #11111b;
font-family: 'Inter', system-ui, sans-serif;
color: #cdd6f4;
}
.vue-flow {
background: #11111b !important;
}
.vue-flow__background {
background: #11111b !important;
}
.vue-flow__edge-path {
stroke: #89b4fa !important;
stroke-width: 2 !important;
}
.vue-flow__edge.animated .vue-flow__edge-path {
stroke-dasharray: 5;
animation: dashdraw 0.5s linear infinite;
}
.vue-flow__handle {
border-radius: 50% !important;
}
@keyframes dashdraw {
to { stroke-dashoffset: -10; }
}
</style>
<style scoped>
.app-shell {
display: flex;
flex-direction: column;
width: 100vw;
height: 100vh;
overflow: hidden;
}
.canvas-area {
display: flex;
flex: 1;
overflow: hidden;
}
.flow-canvas {
flex: 1;
}
</style>