AiCanvas

Canvas

Pannable, zoomable workflow canvas with grid, snap-to-grid, and fit-view.

User Query
inputdone
Embed Query
processdone
Vector Search
processrunning
Rerank
processidle
Generate
outputidle

Scroll to zoom · Shift+drag to pan · Drag nodes to reposition

Usage

<script setup lang="ts">
const canvasRef = ref()
const zoom = ref(1)

const nodes = reactive([
  { id: 'n1', label: 'Query', x: 40, y: 80, status: 'completed' },
  { id: 'n2', label: 'Retrieve', x: 220, y: 80, status: 'running' },
])
</script>

<template>
  <div class="relative">
    <AiCanvas
      ref="canvasRef"
      height="400px"
      snap-to-grid
      :zoom="zoom"
      @zoom="zoom = $event"
    >
      <template #default="{ zoom: canvasZoom, panX, panY }">
        <svg class="pointer-events-none absolute inset-0">
          <AiEdge
            id="e1"
            :source-x="152"
            :source-y="104"
            :target-x="220"
            :target-y="104"
            animated
          />
        </svg>

        <AiNode
          v-for="node in nodes"
          :key="node.id"
          v-bind="node"
          :zoom="canvasZoom"
          :pan-x="panX"
          :pan-y="panY"
          @move="(id, x, y) => { const n = nodes.find(n => n.id === id); if (n) { n.x = x; n.y = y } }"
        />
      </template>
    </AiCanvas>

    <AiControls
      :zoom="zoom"
      class="absolute bottom-3 right-3"
      @zoom-in="canvasRef?.zoomIn()"
      @zoom-out="canvasRef?.zoomOut()"
      @reset="canvasRef?.resetView()"
      @fit-view="canvasRef?.fitView(nodes.map(n => ({ x: n.x, y: n.y })))"
    />
  </div>
</template>

<!-- Exposed: zoomIn, zoomOut, resetView, fitView(positions), clientToCanvas(x, y) -->

Props

Prop Type Default Description
widthstring'100%'Canvas container width
heightstring'500px'Canvas container height
zoomnumber1Controlled zoom level (sync with @zoom)
panXnumber0Horizontal pan offset in px
panYnumber0Vertical pan offset in px
minZoomnumber0.1Minimum zoom (wheel / pinch)
maxZoomnumber3Maximum zoom (wheel / pinch)
gridSizenumber20Dot grid spacing in px
showGridbooleantrueShow background dot grid
snapToGridbooleanfalseSnap click / drag coords to grid

Events

Event Payload
@zoomnumber
@panx: number, y: number
@clickx: number, y: number
@selectids: string[]

Slots

  • default
  • background
  • minimap