Skip to content

Core Plugin

The Core plugin provides the foundational systems that power Woven Canvas. It handles input processing, block intersection detection, cursor management, selection, transform, and includes built-in block types like sticky notes, text, images, shapes, and frames.

The Core plugin is always included automatically and cannot be disabled. It provides:

  • Input handling — Keyboard, mouse, pointer, and screen events
  • Selection — Single, multi-select, marquee selection
  • Transform — Scale, stretch, rotate handles
  • Block systems — Intersection detection, z-ordering, visibility culling
  • Cursor management — Visual cursor state
  • Keybind processing — Converts key presses to commands
  • Edge scrolling — Auto-scroll when dragging near viewport edges
  • Frame containment — Group blocks within frame containers
  • Built-in block types — Sticky notes, text, images, shapes, and frames

Configure the core plugin via the corePlugin editor option:

const editor = new Editor(el, {
corePlugin: {
edgeScrolling: {
enabled: true,
edgeSizePx: 20,
edgeScrollSpeedPxPerFrame: 10,
edgeScrollDelayMs: 100,
}
}
})

Auto-scrolls the camera when dragging near viewport edges.

OptionTypeDefaultDescription
edgeScrolling.enabledbooleantrueEnable auto-scroll when dragging near viewport edges
edgeScrolling.edgeSizePxnumber10Size of the edge zone in pixels
edgeScrolling.edgeScrollSpeedPxPerFramenumber15Camera scroll speed in pixels per frame
edgeScrolling.edgeScrollDelayMsnumber250Delay before scrolling starts in milliseconds

A colored note block with editable text and vertical alignment.

{
tag: 'sticky-note',
components: [Color, Text, VerticalAlign],
editOptions: { canEdit: true }
}

A text-only block that auto-sizes and can be removed when empty.

{
tag: 'text',
components: [Text],
resizeMode: ResizeMode.Text,
editOptions: {
canEdit: true,
removeWhenTextEmpty: true
}
}

An image block with asset loading support. The resizeMode can be overridden per-block to allow unlocking aspect ratio.

{
tag: 'image',
components: [Image, Asset],
resizeMode: ResizeMode.Scale
}

A geometric shape with optional text content.

{
tag: 'shape',
components: [Shape, Text, VerticalAlign],
resizeMode: ResizeMode.Free,
editOptions: { canEdit: true }
}

A container that groups blocks. Blocks dragged into a frame become children.

{
tag: 'frame',
components: [Frame],
resizeMode: ResizeMode.Free,
canRotate: false,
connectors: { enabled: false }
}
ShortcutAction
Ctrl/Cmd + ASelect all blocks
DeleteRemove selected blocks
]Bring selected forward (z-order)
[Send selected backward (z-order)

Commands can be dispatched programmatically via editor.command():

import {
SelectBlock,
DeselectBlock,
DeselectAll,
SelectAll,
ToggleSelect,
} from '@woven-canvas/core'
// Select a specific block
editor.command(SelectBlock, { entityId, deselectOthers: true })
// Deselect all blocks
editor.command(DeselectAll)
// Select all blocks
editor.command(SelectAll)
import {
RemoveSelected,
DuplicateSelected,
BringForwardSelected,
SendBackwardSelected,
} from '@woven-canvas/core'
// Delete all selected blocks
editor.command(RemoveSelected)
// Duplicate selected blocks
editor.command(DuplicateSelected)
// Change z-order
editor.command(BringForwardSelected)
editor.command(SendBackwardSelected)
ComponentDescription
BlockCore block data (position, size, rotation, stratum, rank)
AabbAxis-aligned bounding box for collision detection
HitGeometryHit detection geometry (arcs, capsules)
ScaleWithZoomMarks entities that scale with camera zoom
ComponentDescription
TextText content and styling
ColorColor values (hue, saturation, lightness)
ShapeShape type and stroke configuration
ImageImage dimensions and source
AssetAsset upload state and URL
VerticalAlignVertical text alignment
OpacityEntity opacity
ComponentDescription
HoveredMarks an entity as hovered
HeldMarks an entity as held/pressed
EditedMarks an entity as being edited
ConnectorArrow connection point data
PointerPointer input state with sample history
UserUser presence information
ComponentDescription
SelectedMarks an entity as selected
DragStartTracks drag initiation position
EditAfterPlacingTriggers edit mode after block creation
SelectionBoxMarquee selection rectangle
TransformBoxTransform box container
TransformHandleCorner, edge, and rotation handles
ComponentDescription
FrameFrame container data (label)
FrameDropTargetMarks a frame as a drop target during drag
import { defineQuery, Block, Selected } from '@woven-canvas/core'
// Query for selected blocks
const selectedBlocks = defineQuery((q) => q.with(Block, Selected))
// In a system
for (const entityId of selectedBlocks.current(ctx)) {
const block = Block.read(ctx, entityId)
console.log(block.position)
}

Singleton components store global editor state (one instance per world):

SingletonDescription
CameraViewport position and zoom level
ScreenScreen dimensions and device pixel ratio
CursorCurrent cursor style
TickTick timing and delta time
KeyboardKeyboard state bitmask
MouseMouse position and button state
ControlsActive tool and control mode
GridGrid snapping configuration
IntersectBlock intersection results
RankBoundsMin/max block z-order values
ScaleWithZoomStateZoom-based scaling state
SelectionStateSingletonSelection state machine state
TransformBoxStateSingletonTransform box state machine state
ScrollEdgesStateSingletonEdge scrolling state machine state
FrameDrawStateFrame drawing state machine state
FrameContainmentStateFrame containment state machine state
import { defineEditorSystem } from '@woven-canvas/core'
import { Camera, Mouse } from '@woven-canvas/core'
const mySystem = defineEditorSystem({ phase: 'update' }, (ctx) => {
const camera = Camera.read(ctx)
const mouse = Mouse.read(ctx)
console.log(`Camera at (${camera.x}, ${camera.y}), zoom: ${camera.zoom}`)
console.log(`Mouse at (${mouse.x}, ${mouse.y})`)
})

The Core plugin runs these systems automatically:

SystemDescription
frameSystemUpdates frame timing and delta time
rankBoundsSystemComputes min/max block z-order
keyboardSystemProcesses keyboard events
mouseSystemProcesses mouse events
screenSystemMonitors screen/viewport changes
pointerSystemProcesses pointer events with pressure
SystemDescription
intersectSystemDetects which block is under the pointer
selectSystemRuns the selection state machine (click, drag, marquee)
keybindSystemMatches key presses to registered keybinds
hoverCursorSystemSets cursor when hovering transform handles
scrollEdgesSystemAuto-scrolls when dragging near viewport edges
captureTransformBoxSystemManages transform box lifecycle
captureFrameContainmentSystemTracks frame containment during drag
captureFrameDrawSystemHandles frame drawing interactions
SystemDescription
blockSystemHandles block manipulation commands
dragHandlerSystemUpdates blocks when transform box moves
updateSelectSystemHandles selection box commands
updateFrameDrawSystemProcesses frame draw commands
updateFrameContainmentSystemProcesses frame containment commands
updateFrameSetupSystemSets up hit geometry for frames
SystemDescription
scaleWithZoomSystemUpdates scale for zoom-dependent entities
canSeeBlocksSystemCulls blocks outside the viewport
cursorSystemApplies cursor style to the DOM
presenceSystemRenders user presence indicators

The Keyboard singleton tracks which keys are currently pressed:

import { defineEditorSystem } from '@woven-canvas/core'
import { Keyboard, Key } from '@woven-canvas/core'
const mySystem = defineEditorSystem({ phase: 'update' }, (ctx) => {
const keyboard = Keyboard.read(ctx)
// Check if a key is pressed
if (keyboard.isDown(Key.Space)) {
console.log('Space is held')
}
// Check modifier keys
if (keyboard.mod) {
console.log('Ctrl/Cmd is held')
}
})
import { Key } from '@woven-canvas/core'
Key.Space
Key.Enter
Key.Escape
Key.Backspace
Key.Delete
Key.ArrowUp
Key.ArrowDown
Key.ArrowLeft
Key.ArrowRight
// ... and all alphanumeric keys

The Camera singleton controls the viewport:

import { Camera } from '@woven-canvas/core'
// Read camera state
const camera = Camera.read(ctx)
console.log(camera.x, camera.y, camera.zoom)
// Modify camera (in a system)
const cam = Camera.write(ctx)
cam.x = 100
cam.y = 200
cam.zoom = 1.5

Convert between screen and world coordinates:

import { screenToWorld, worldToScreen } from '@woven-canvas/core'
// Screen pixel to world coordinate
const worldPos = screenToWorld(ctx, screenX, screenY)
// World coordinate to screen pixel
const screenPos = worldToScreen(ctx, worldX, worldY)

The Vue package provides toolbar and floating menu components for core block types:

ComponentDescription
SelectToolToolbar button for the select tool
StickyNoteToolToolbar button to create sticky notes
TextToolToolbar button to create text blocks
ImageToolToolbar button to upload images
ShapeToolToolbar button to create shapes
ColorButtonColor picker for block color
ShapeKindButtonFloating menu dropdown for shape type
ShapeFillColorButtonFloating menu dropdown for shape fill
ShapeStrokeColorButtonFloating menu dropdown for shape stroke
<script setup lang="ts">
import {
WovenCanvas,
SelectTool,
StickyNoteTool,
TextTool,
ImageTool,
ShapeTool,
Toolbar,
} from '@woven-canvas/vue'
</script>
<template>
<WovenCanvas>
<template #toolbar>
<Toolbar>
<SelectTool />
<StickyNoteTool />
<TextTool />
<ImageTool />
<ShapeTool />
</Toolbar>
</template>
</WovenCanvas>
</template>

When blocks are selected, the floating menu automatically shows contextual controls based on block type. These are included in the default FloatingMenuBar. To customize:

<script setup lang="ts">
import {
WovenCanvas,
FloatingMenu,
FloatingMenuBar,
ColorButton,
ShapeKindButton,
ShapeFillColorButton,
ShapeStrokeColorButton,
} from '@woven-canvas/vue'
</script>
<template>
<WovenCanvas>
<template #floating-menu>
<FloatingMenu>
<FloatingMenuBar>
<template #button:color="{ entityIds }">
<ColorButton :entity-ids="entityIds" />
</template>
<template #button:shape="{ entityIds }">
<ShapeKindButton :entity-ids="entityIds" />
<ShapeFillColorButton :entity-ids="entityIds" />
<ShapeStrokeColorButton :entity-ids="entityIds" />
</template>
</FloatingMenuBar>
</FloatingMenu>
</template>
</WovenCanvas>
</template>