/** * Headless canvas interaction primitives shared by visual editors. * * This module deliberately knows nothing about React, DOM elements, or how an * application persists objects. An app supplies its object adapter and turns * the returned semantic commands into its own mutations. */ export declare const DEFAULT_CANVAS_DRAG_THRESHOLD = 3; export declare const DEFAULT_CANVAS_NUDGE = 1; export declare const DEFAULT_CANVAS_ACCELERATED_NUDGE = 10; export declare const DEFAULT_CANVAS_MIN_SIZE = 24; export type CanvasTextActivation = "single-click" | "double-click"; export type CanvasEscapeBehavior = "select-object" | "clear-selection" | "cancel-edit"; export type CanvasDuplicateModifier = "alt" | "meta" | "ctrl" | "none"; export type CanvasResizeHandle = "nw" | "n" | "ne" | "w" | "e" | "sw" | "s" | "se"; export interface CanvasPoint { x: number; y: number; } export interface CanvasRect extends CanvasPoint { width: number; height: number; } export interface CanvasViewport { left: number; top: number; width: number; height: number; } export interface CanvasSize { width: number; height: number; } export interface CanvasModifiers { altKey?: boolean; ctrlKey?: boolean; metaKey?: boolean; shiftKey?: boolean; } export interface CanvasTextEditPolicy { activation?: CanvasTextActivation; escapeBehavior?: CanvasEscapeBehavior; } export interface CanvasDragPolicy { threshold?: number; duplicateModifier?: CanvasDuplicateModifier; } export interface CanvasNudgePolicy { amount?: number; acceleratedAmount?: number; } /** The interaction surface an app can durably support. */ export interface CanvasInteractionCapabilities { selection: boolean; multiSelection: boolean; move: boolean; resize: boolean; textEditing: boolean; nudge: boolean; duplicate: boolean; clipboard: boolean; delete: boolean; arrange: boolean; snapping: boolean; alignment: boolean; distribution: boolean; grouping: boolean; rotation: boolean; marquee: boolean; } export declare const DEFAULT_CANVAS_INTERACTION_CAPABILITIES: CanvasInteractionCapabilities; export interface CanvasInteractionConfig { capabilities?: Partial; textEditing?: CanvasTextEditPolicy; drag?: CanvasDragPolicy; nudge?: CanvasNudgePolicy; minSize?: number; shortcuts?: readonly CanvasShortcut[]; } /** The only persistence seam required by the shared interaction core. */ export interface CanvasInteractionAdapter { readonly capabilities: CanvasInteractionCapabilities; dispatch(command: CanvasCommand): CanvasCommandDispatchResult; } export type CanvasCommandDispatchResult = { handled: true; } | { handled: false; reason: "no-adapter" | "unsupported" | "unhandled"; }; export type CanvasCommandId = "select-all" | "undo" | "redo" | "copy" | "cut" | "paste" | "duplicate" | "delete" | "nudge" | "arrange-front" | "arrange-back" | "bring-forward" | "bring-to-front" | "send-backward" | "send-to-back" | "align-left" | "align-center" | "align-right" | "align-top" | "align-middle" | "align-bottom" | "distribute-horizontal" | "distribute-vertical" | "group" | "ungroup" | "frame-selection" | "escape" | "enter" | "nudge-left" | "nudge-right" | "nudge-up" | "nudge-down"; export interface CanvasCommand { id: CanvasCommandId; objectIds?: readonly TObjectId[]; delta?: CanvasPoint; } export type CanvasShortcutModifier = "primary" | "shift" | "alt" | "ctrl" | "meta"; export interface CanvasShortcut { command: CanvasCommandId; key: string; /** * Optional physical key identity for shifted punctuation. `KeyboardEvent.key` * changes `]` to `}` under Shift, while `KeyboardEvent.code` stays stable. */ code?: string; modifiers?: readonly CanvasShortcutModifier[]; } export interface CanvasShortcutInput extends CanvasModifiers { key: string; code?: string; } export interface CanvasShortcutRegistry { readonly shortcuts: readonly CanvasShortcut[]; resolve(input: CanvasShortcutInput): CanvasCommandId | null; } export interface CanvasTextActivationInput { clickCount: number; textEditable: boolean; } export type CanvasTextActivationResult = "edit" | "select"; export interface CanvasEscapeInput { editingObjectId: TObjectId | null; selectedObjectIds?: readonly TObjectId[]; } export type CanvasEscapeResult = { action: "select-object"; editingObjectId: null; selectedObjectIds: readonly TObjectId[]; } | { action: "cancel-edit"; editingObjectId: null; selectedObjectIds: readonly TObjectId[]; } | { action: "clear-selection"; editingObjectId: null; selectedObjectIds: readonly TObjectId[]; } | { action: "none"; editingObjectId: null; selectedObjectIds: readonly TObjectId[]; }; export interface CanvasResizeInput extends CanvasModifiers { handle: CanvasResizeHandle; delta: CanvasPoint; preserveAspectRatio?: boolean; minWidth?: number; minHeight?: number; } export interface CanvasNudgeInput extends CanvasModifiers { key: string; } export interface CanvasNudgeResult { command: Extract; delta: CanvasPoint; } /** A pointer expressed in browser client coordinates. */ export interface CanvasGesturePointer extends CanvasPoint, CanvasModifiers { } export type CanvasGestureKind = "move" | "resize"; export type CanvasGesturePhase = "idle" | "pending" | "active"; export interface CanvasGestureBase { readonly kind: CanvasGestureKind; readonly objectIds: readonly TObjectId[]; /** Pointer location when the gesture began, in browser client coordinates. */ readonly startPointer: CanvasGesturePointer; readonly pointer: CanvasGesturePointer; readonly clientDelta: CanvasPoint; readonly canvasDelta: CanvasPoint; /** Whether this gesture should create copies as part of its one final commit. */ readonly duplicate: boolean; } export interface CanvasMoveGesture extends CanvasGestureBase { readonly kind: "move"; } export interface CanvasResizeGesture extends CanvasGestureBase { readonly kind: "resize"; readonly handle: CanvasResizeHandle; readonly startRect: CanvasRect; readonly rect: CanvasRect; } export type CanvasGesture = CanvasMoveGesture | CanvasResizeGesture; export interface CanvasMoveGestureStart { readonly kind: "move"; readonly objectIds: readonly TObjectId[]; readonly pointer: CanvasGesturePointer; readonly viewport: CanvasViewport; readonly canvas: CanvasSize; } export interface CanvasResizeGestureStart { readonly kind: "resize"; readonly objectIds: readonly TObjectId[]; readonly pointer: CanvasGesturePointer; readonly viewport: CanvasViewport; readonly canvas: CanvasSize; readonly handle: CanvasResizeHandle; readonly rect: CanvasRect; } export type CanvasGestureStart = CanvasMoveGestureStart | CanvasResizeGestureStart; /** Typed result returned by host preview, commit, and cancel callbacks. */ export type CanvasGestureAdapterResult = { handled: true; } | { handled: false; reason: "unsupported" | "unhandled"; }; /** * Persistence-neutral callbacks for a single object gesture. Previews are * transient; `commit` is invoked at most once after an active gesture ends. */ export interface CanvasGestureAdapter { preview?(gesture: CanvasGesture): CanvasGestureAdapterResult; commit(gesture: CanvasGesture): CanvasGestureAdapterResult; cancel?(gesture: CanvasGesture): CanvasGestureAdapterResult; } export interface CanvasGestureControllerConfig extends CanvasInteractionConfig { adapter: CanvasGestureAdapter; } export interface CanvasGestureState { readonly phase: CanvasGesturePhase; readonly gesture: CanvasGesture | null; } export type CanvasGestureBeginResult = { accepted: true; state: CanvasGestureState; } | { accepted: false; reason: "unsupported" | "already-active" | "invalid-resize"; state: CanvasGestureState; }; export type CanvasGestureUpdateResult = { phase: "idle"; state: CanvasGestureState; } | { phase: "pending"; state: CanvasGestureState; } | { phase: "active"; gesture: CanvasGesture; preview: CanvasGestureAdapterResult | null; state: CanvasGestureState; }; export type CanvasGestureEndResult = { committed: true; gesture: CanvasGesture; result: CanvasGestureAdapterResult; state: CanvasGestureState; } | { committed: false; reason: "idle" | "below-threshold" | "unsupported"; state: CanvasGestureState; }; export type CanvasGestureCancelResult = { cancelled: true; gesture: CanvasGesture; result: CanvasGestureAdapterResult | null; state: CanvasGestureState; } | { cancelled: false; reason: "idle"; state: CanvasGestureState; }; export declare const DEFAULT_CANVAS_SHORTCUTS: readonly CanvasShortcut[]; /** Resolves click-to-edit without attaching event listeners. */ export declare function resolveCanvasTextActivation(input: CanvasTextActivationInput, policy?: CanvasTextEditPolicy): CanvasTextActivationResult; /** * Escape has one explicit owner: editing wins over any box-selection state. * The host applies this result before handing Escape to generic UI dismissal. */ export declare function resolveCanvasEscape(input: CanvasEscapeInput, policy?: CanvasTextEditPolicy): CanvasEscapeResult; /** Converts a browser client point into unscaled canvas coordinates. */ export declare function clientPointToCanvasPoint(point: CanvasPoint, viewport: CanvasViewport, canvas: CanvasSize): CanvasPoint; /** Converts a client-space drag delta to the canvas's unscaled coordinate space. */ export declare function clientDeltaToCanvasDelta(delta: CanvasPoint, viewport: CanvasViewport, canvas: CanvasSize): CanvasPoint; /** Whether the client pointer crossed the intentional-drag threshold. */ export declare function hasCrossedCanvasDragThreshold(start: CanvasPoint, current: CanvasPoint, threshold?: number): boolean; /** Locks a drag to its dominant axis, used for Shift-drag. */ export declare function constrainCanvasDragDelta(delta: CanvasPoint, lockAxis?: boolean): CanvasPoint; /** * Resizes against the opposite edge, or from the center with Alt. Shift * preserves aspect ratio on every handle; derived dimensions are centered. */ export declare function resizeCanvasRect(start: CanvasRect, input: CanvasResizeInput): CanvasRect; /** Whether this drag gesture should duplicate selected objects before moving. */ export declare function shouldDuplicateCanvasDrag(modifiers: CanvasModifiers, duplicateModifier?: CanvasDuplicateModifier): boolean; /** Resolves standard arrow-key movement, including Shift's accelerated nudge. */ export declare function resolveCanvasNudge(input: CanvasNudgeInput, policy?: CanvasNudgePolicy): CanvasNudgeResult | null; /** Looks up a semantic command without referring to platform-specific events. */ export declare function resolveCanvasShortcut(input: CanvasShortcutInput, shortcuts?: readonly CanvasShortcut[]): CanvasCommandId | null; /** Creates a reusable, immutable shortcut lookup for one editor policy. */ export declare function createCanvasShortcutRegistry(shortcuts?: readonly CanvasShortcut[]): CanvasShortcutRegistry; /** * Creates an app-configured pure interaction core. `dispatch` is optional so * hosts can either use the helpers directly or receive semantic commands via * their adapter without the Toolkit owning any state. */ export declare function createCanvasInteractionCore(config?: CanvasInteractionConfig, adapter?: CanvasInteractionAdapter): { capabilities: CanvasInteractionCapabilities; textActivation: (input: CanvasTextActivationInput) => CanvasTextActivationResult; escape: (input: CanvasEscapeInput) => CanvasEscapeResult; clientPointToCanvas: (point: CanvasPoint, viewport: CanvasViewport, canvas: CanvasSize) => CanvasPoint; clientDeltaToCanvas: (delta: CanvasPoint, viewport: CanvasViewport, canvas: CanvasSize) => CanvasPoint; hasCrossedDragThreshold: (start: CanvasPoint, current: CanvasPoint) => boolean; constrainDrag: (delta: CanvasPoint, modifiers?: CanvasModifiers) => CanvasPoint; resize: (start: CanvasRect, input: CanvasResizeInput) => CanvasRect; shouldDuplicateDrag: (modifiers: CanvasModifiers) => boolean; nudge: (input: CanvasNudgeInput) => CanvasNudgeResult | null; shortcut: (input: CanvasShortcutInput) => CanvasCommandId | null; dispatch: (command: CanvasCommand) => { handled: true; } | { handled: false; reason: "no-adapter" | "unsupported" | "unhandled"; } | { readonly handled: false; readonly reason: "no-adapter"; } | { readonly handled: false; readonly reason: "unsupported"; }; }; /** * Creates a small state machine for one pointer gesture at a time. It keeps * browser coordinates at its boundary, while every emitted preview and commit * is in stable canvas coordinates. Hosts own rendering and persistence. */ export declare function createCanvasGestureController(config: CanvasGestureControllerConfig): { capabilities: CanvasInteractionCapabilities; getState: () => CanvasGestureState; pointerDown: (gestureStart: CanvasGestureStart) => CanvasGestureBeginResult; pointerMove: (pointer: CanvasGesturePointer) => CanvasGestureUpdateResult; pointerUp: (pointer: CanvasGesturePointer) => CanvasGestureEndResult; cancel: () => CanvasGestureCancelResult; }; //# sourceMappingURL=canvas-interactions.d.ts.map