### Install @tldraw/core Package Source: https://github.com/tldraw/tldraw-v1/blob/main/packages/core/README.md Installs the @tldraw/core package and its peer dependencies using either yarn or npm. It also includes a note about running the build script before the dev script. ```bash yarn add @tldraw/core && yarn build # or npm i @tldraw/core && npm run build ``` -------------------------------- ### Install @tldraw/curve Package Source: https://github.com/tldraw/tldraw-v1/blob/main/packages/curve/README.md Installs the @tldraw/curve package using either yarn or npm. This is the first step to using the curve functionalities in your project. ```bash yarn add @tldraw/curve # or npm i @tldraw/curve ``` -------------------------------- ### Install @tldraw/vec Package Source: https://github.com/tldraw/tldraw-v1/blob/main/packages/vec/README.md Installs the @tldraw/vec package using either yarn or npm. This package is necessary for using the vector math functionalities within tldraw projects. ```bash yarn add @tldraw/vec # or npm i @tldraw/vec ``` -------------------------------- ### Tldraw Event Handling Example Source: https://context7.com/tldraw/tldraw-v1/llms.txt This TypeScript example demonstrates how to implement various event handlers for the Tldraw component, including mount, document change, undo/redo, pointer, canvas, shape, bounds, camera, and keyboard events. It logs event details to the console and persists the document to local storage. ```typescript import { Tldraw, TldrawApp, TDShape } from '@tldraw/tldraw' import { TLPointerInfo, TLKeyboardInfo, TLBounds } from '@tldraw/core' import * as React from 'react' export default function EventHandlingExample() { // Mount handler - called when app initializes const handleMount = (app: TldrawApp) => { console.log('App mounted with document:', app.document.id) } // Document change handler const handleChange = (app: TldrawApp, reason?: string) => { console.log('Document changed:', reason) localStorage.setItem('document', JSON.stringify(app.document)) } // Undo/Redo handlers const handleUndo = (app: TldrawApp) => { console.log('Undo performed') } const handleRedo = (app: TldrawApp) => { console.log('Redo performed') } // Shape lifecycle events const handlePersist = (app: TldrawApp) => { console.log('Changes persisted') } // Pointer events const handlePointerDown = (info: TLPointerInfo, e: React.PointerEvent) => { console.log('Pointer down at:', info.point, 'Target:', info.target) } const handlePointerMove = (info: TLPointerInfo, e: React.PointerEvent) => { // Called on every pointer movement (use sparingly) } const handlePointerUp = (info: TLPointerInfo, e: React.PointerEvent) => { console.log('Pointer up at:', info.point) } // Canvas events const handlePointCanvas = (info: TLPointerInfo, e: React.PointerEvent) => { console.log('Canvas clicked at:', info.point) } const handleDoubleClickCanvas = (info: TLPointerInfo, e: React.PointerEvent) => { console.log('Canvas double-clicked') } const handleRightPointCanvas = (info: TLPointerInfo, e: React.PointerEvent) => { console.log('Canvas right-clicked') } // Shape events const handlePointShape = (info: TLPointerInfo, e: React.PointerEvent) => { console.log('Shape clicked:', info.target) } const handleDoubleClickShape = (info: TLPointerInfo, e: React.PointerEvent) => { console.log('Shape double-clicked:', info.target) } const handleDragShape = (info: TLPointerInfo, e: React.PointerEvent) => { console.log('Shape dragging:', info.target, 'Delta:', info.delta) } // Bounds events (selection box) const handleBoundsChange = (bounds: TLBounds) => { console.log('Bounds changed:', bounds) } // Camera events const handlePan = (delta: number[], e: WheelEvent | PointerEvent) => { console.log('Pan:', delta) } const handleZoom = (zoom: number, e: WheelEvent | PointerEvent) => { console.log('Zoom level:', zoom) } // Keyboard events const handleKeyDown = (info: TLKeyboardInfo, e: KeyboardEvent) => { console.log('Key pressed:', info.key) } const handleKeyUp = (info: TLKeyboardInfo, e: KeyboardEvent) => { console.log('Key released:', info.key) } // Command events const handleCommand = (command: string) => { console.log('Command executed:', command) } return (
) } ``` -------------------------------- ### Install @tldraw/intersect using Yarn or npm Source: https://github.com/tldraw/tldraw-v1/blob/main/packages/intersect/README.md This snippet shows how to add the @tldraw/intersect package to your project using either the Yarn or npm package managers. This is the first step to utilizing its intersection functionalities. ```bash yarn add @tldraw/intersect ``` ```bash npm i @tldraw/intersect ``` -------------------------------- ### Perform 2D Vector Operations with TypeScript Source: https://context7.com/tldraw/tldraw-v1/llms.txt Demonstrates various 2D vector operations using the @tldraw/vec library in TypeScript. This includes basic arithmetic, distance and length calculations, angle computations, rotations, normalization, interpolation, projections, clamping, snapping, and comparisons. It also includes a practical example of calculating a point along a line. ```typescript import Vec from '@tldraw/vec' function vectorOperationsExample() { const pointA = [100, 100] const pointB = [300, 200] // Basic operations const sum = Vec.add(pointA, pointB) // [400, 300] const diff = Vec.sub(pointB, pointA) // [200, 100] const scaled = Vec.mul(pointA, 2) // [200, 200] const divided = Vec.div(pointB, 2) // [150, 100] // Distance and length const distance = Vec.dist(pointA, pointB) // 223.6 const length = Vec.len(pointA) // 141.4 const lengthSquared = Vec.len2(pointA) // 20000 // Angles const angle = Vec.angle(pointA, pointB) // radians between vectors const angleFromOrigin = Vec.angle(pointA) // angle from origin // Rotation const rotated = Vec.rot(pointA, Math.PI / 4) // rotate 45 degrees const rotatedAround = Vec.rotWith( pointA, [200, 200], // center point Math.PI / 2 // 90 degrees ) // Normalization and direction const normalized = Vec.uni(pointA) // unit vector const perpendicular = Vec.per(pointA) // perpendicular vector const negated = Vec.neg(pointA) // [-100, -100] // Interpolation const midpoint = Vec.lrp(pointA, pointB, 0.5) // linear interpolation const smoothPoint = Vec.med(pointA, pointB) // median point // Projection and products const dotProduct = Vec.dpr(pointA, pointB) // dot product const crossProduct = Vec.cpr(pointA, pointB) // cross product (2D) const projected = Vec.prj(pointA, pointB) // project A onto B // Clamping and snapping const clamped = Vec.clampV( [350, 350], [0, 0], // min bounds [300, 300] // max bounds ) // [300, 300] const snapped = Vec.snap(pointA, 25) // snap to 25-unit grid // Comparison const areEqual = Vec.isEqual(pointA, [100, 100]) // true const closeEnough = Vec.med([100.1, 99.9], [100, 100]) // Practical example: Calculate point along a line function getPointAlongLine(start: number[], end: number[], distance: number): number[] { const direction = Vec.uni(Vec.sub(end, start)) return Vec.add(start, Vec.mul(direction, distance)) } const pointAlongLine = getPointAlongLine(pointA, pointB, 50) return { sum, diff, distance, angle, rotated, midpoint, pointAlongLine } } ``` -------------------------------- ### Initialize Tldraw with File System Integration (TypeScript) Source: https://github.com/tldraw/tldraw-v1/blob/main/guides/documentation.md Demonstrates how to initialize the Tldraw component with file system event handlers using the useFileSystem hook. This allows for saving and opening projects using the FileSystem API. ```typescript import { Tldraw, useFileSystem } from '@tldraw/tldraw' function App() { const fileSystemEvents = useFileSystem() return } ``` -------------------------------- ### `useFileSystem` Hook Source: https://github.com/tldraw/tldraw-v1/blob/main/guides/documentation.md The `useFileSystem` hook provides callbacks for managing project file operations (new, open, save, save as) using the browser's FileSystem API. ```APIDOC ## `useFileSystem` Hook ### Description Provides callbacks for file system operations, designed to be passed as props to the `Tldraw` component. ### Method N/A (Hook) ### Endpoint N/A (Hook) ### Parameters N/A #### Path Parameters N/A #### Query Parameters N/A #### Request Body N/A ### Request Example ```jsx import { Tldraw, useFileSystem } from '@tldraw/tldraw' function App() { const fileSystemEvents = useFileSystem() return } ``` ### Response N/A (Hook) #### Success Response (200) N/A #### Response Example N/A ``` -------------------------------- ### Multiplayer Room Creation API Source: https://context7.com/tldraw/tldraw-v1/llms.txt API endpoint for creating Liveblocks-powered multiplayer drawing rooms with initial document state. ```APIDOC ## POST /api/create ### Description Creates a new multiplayer room for collaborative drawing using Liveblocks. This endpoint allows you to initialize a room with a specific document state. ### Method POST ### Endpoint /api/create ### Parameters #### Query Parameters - **pageId** (string) - Required - The ID of the page to associate with the room. #### Request Body - **document** (TDDocument) - Required - The initial state of the document to be created in the room. ### Request Example ```json { "pageId": "page1", "document": { "id": "doc1", "name": "My Drawing", "version": 15.5, "pages": { "page1": { "id": "page1", "name": "Page 1", "childIndex": 1, "shapes": {}, "bindings": {} } }, "pageStates": { "page1": { "id": "page1", "selectedIds": [], "camera": { "point": [0, 0], "zoom": 1 } } }, "assets": {} } } ``` ### Response #### Success Response (200) - **success** (boolean) - Indicates if the room creation was successful. - **roomId** (string) - The unique identifier for the created room. - **url** (string) - The URL to access the created multiplayer room. #### Error Response (e.g., 500) - **success** (boolean) - Indicates if the room creation was successful (will be false). - **error** (string) - A message describing the error that occurred. ### Response Example (Success) ```json { "success": true, "roomId": "uniqueRoomId123", "url": "/r/uniqueRoomId123" } ``` ### Response Example (Error) ```json { "success": false, "error": "Failed to create room" } ``` ``` -------------------------------- ### Initialize TldrawApp and Select All Shapes Source: https://github.com/tldraw/tldraw-v1/blob/main/guides/documentation.md This snippet demonstrates how to access the TldrawApp API upon component mount using the onMount callback. It then calls the selectAll method to select all shapes within the document. This is useful for performing batch operations on all existing elements. ```tsx import { Tldraw, TldrawApp } from '@tldraw/tldraw' import React from 'react' function App() { const handleMount = React.useCallback((app: TldrawApp) => { app.selectAll() }, []) return } ``` -------------------------------- ### TypeScript: 2D Shape Intersection Detection with @tldraw/intersect Source: https://context7.com/tldraw/tldraw-v1/llms.txt Demonstrates various intersection detection methods provided by the @tldraw/intersect package. It covers intersections between line segments, circles, bounding boxes, and ellipses. This function requires the @tldraw/intersect library to be installed. ```typescript import { intersectLineSegmentLineSegment, intersectLineSegmentCircle, intersectLineSegmentEllipse, intersectLineSegmentBounds, intersectCircleCircle, intersectRayBounds, TLIntersection, TLBounds, } from '@tldraw/intersect' function intersectionExamples() { // Line segment intersection const line1: number[][] = [[0, 0], [100, 100]] const line2: number[][] = [[0, 100], [100, 0]] const lineIntersection = intersectLineSegmentLineSegment( line1[0], line1[1], line2[0], line2[1] ) console.log('Lines intersect:', lineIntersection.didIntersect) console.log('Intersection point:', lineIntersection.points[0]) // [50, 50] // Circle intersection const circle1 = { center: [100, 100], radius: 50 } const circle2 = { center: [150, 100], radius: 50 } const circleIntersection = intersectCircleCircle( circle1.center, circle1.radius, circle2.center, circle2.radius ) console.log('Circles intersect:', circleIntersection.didIntersect) console.log('Intersection points:', circleIntersection.points) // Line-circle intersection const lineCircleIntersection = intersectLineSegmentCircle( [50, 100], // line start [150, 100], // line end [100, 100], // circle center 50 // circle radius ) console.log('Line intersects circle:', lineCircleIntersection.didIntersect) console.log('Points:', lineCircleIntersection.points) // Bounds (rectangle) intersection const bounds1: TLBounds = { minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 } const boundsIntersection = intersectLineSegmentBounds( [50, -50], // line start [50, 150], // line end bounds1 ) console.log('Line intersects bounds:', boundsIntersection.didIntersect) console.log('Entry/exit points:', boundsIntersection.points) // Ray-bounds intersection (useful for raycasting) const rayIntersections = intersectRayBounds( [50, 50], // ray origin [1, 0], // ray direction (normalized) bounds1 ) console.log('Ray intersections:', rayIntersections) // Ellipse intersection const ellipseIntersection = intersectLineSegmentEllipse( [0, 50], // line start [200, 50], // line end [100, 50], // ellipse center 80, // x radius 40, // y radius 0 // rotation in radians ) console.log('Line intersects ellipse:', ellipseIntersection.didIntersect) return { lineIntersection, circleIntersection, lineCircleIntersection, boundsIntersection, } } ``` -------------------------------- ### Add New Translation Entry in TypeScript Source: https://github.com/tldraw/tldraw-v1/blob/main/guides/translation.md This snippet demonstrates how to add a new language translation to the tldraw application. It involves importing the new language's JSON file and creating a corresponding entry in the TRANSLATIONS array in `translations.ts`. Ensure the file path and locale code are correct. ```typescript import ar from './ar.json' import en from './en.json' // import here ↖️ import eo from './eo.json' export const TRANSLATIONS: TDTranslations = [ // Default language: { locale: 'en', label: 'English', messages: en }, // Translations: { locale: 'ar', label: 'عربي', messages: ar }, { locale: 'eo', label: 'Esperanto', messages: eo }, // <-- add an entry here ] ``` -------------------------------- ### Initialize TDDocument with TldrawApp Source: https://github.com/tldraw/tldraw-v1/blob/main/guides/documentation.md Initializes a TDDocument object with basic structure including document ID, version, pages, and page states. It demonstrates how to set up the initial state for the Tldraw component. Dependencies include '@tldraw/tldraw'. ```typescript import { TDDocument, TldrawApp } from '@tldraw/tldraw' const myDocument: TDDocument = { id: 'doc', version: TldrawApp.version, pages: { page1: { id: 'page1', shapes: {}, bindings: {}, }, }, pageStates: { page1: { id: 'page1', selectedIds: [], currentParentId: 'page1', camera: { point: [0, 0], zoom: 1, }, }, }, assets: {}, } function App() { return } ``` -------------------------------- ### Render TLDraw Application with Renderer Component Source: https://github.com/tldraw/tldraw-v1/blob/main/packages/core/README.md Demonstrates how to import and use the Renderer component from @tldraw/core in a React application. It sets up initial state for the page and pageState, and passes shape utilities. ```tsx import * as React from "react" import { Renderer, TLShape, TLShapeUtil, Vec } from '@tldraw/core' import { BoxShape, BoxUtil } from "./shapes/box" const shapeUtils = { box: new BoxUtil() } function App() { const [page, setPage] = React.useState({ id: "page" shapes: { "box1": { id: 'box1', type: 'box', parentId: 'page', childIndex: 0, point: [0, 0], size: [100, 100], rotation: 0, } }, bindings: {} }) const [pageState, setPageState] = React.useState({ id: "page", selectedIds: [], camera: { point: [0,0], zoom: 1 } }) return () } ``` -------------------------------- ### Create Multiplayer Room with Liveblocks (TypeScript) Source: https://context7.com/tldraw/tldraw-v1/llms.txt This function creates a new multiplayer drawing room powered by Liveblocks. It first obtains an authentication token from Liveblocks, prepares the initial document storage in the required format, and then creates a new room with a unique ID. The function returns the room ID and a URL to join the room upon successful creation, or an error object if creation fails. It depends on `@tldraw/tldraw` and `@tldraw/core` for document types and utility functions, and requires a Liveblocks secret key environment variable. ```typescript // POST /api/create // Request body: { pageId: string, document: TDDocument } import { TDDocument } from '@tldraw/tldraw' import { Utils } from '@tldraw/core' async function createMultiplayerRoom(pageId: string, document: TDDocument) { try { // 1. Get authentication token from Liveblocks const authResponse = await fetch('https://liveblocks.io/api/authorize', { headers: { Authorization: `Bearer ${process.env.LIVEBLOCKS_SECRET_KEY}`, 'Content-Type': 'application/json', }, }) const { token } = await authResponse.json() // 2. Prepare storage structure const storageJson = { liveblocksType: 'LiveObject', data: { version: 2.1, shapes: { liveblocksType: 'LiveMap', data: {}, }, bindings: { liveblocksType: 'LiveMap', data: {}, }, assets: { liveblocksType: 'LiveMap', data: {}, }, }, } // 3. Create room with unique ID const roomId = Utils.uniqueId() const response = await fetch( `https://liveblocks.net/api/v1/room/${roomId}/storage`, { method: 'POST', body: JSON.stringify(storageJson), headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', }, } ) if (response.status === 200) { return { success: true, roomId, url: `/r/${roomId}`, } } else { throw new Error('Failed to create room') } } catch (error) { console.error('Error creating multiplayer room:', error) return { success: false, error: error.message, } } } // Usage example const document: TDDocument = { id: 'doc1', name: 'My Drawing', version: 15.5, pages: { page1: { id: 'page1', name: 'Page 1', childIndex: 1, shapes: {}, bindings: {}, }, }, pageStates: { page1: { id: 'page1', selectedIds: [], camera: { point: [0, 0], zoom: 1 }, }, }, assets: {}, } createMultiplayerRoom('page1', document).then((result) => { if (result.success) { console.log('Room created:', result.url) window.location.href = result.url } else { console.error('Failed to create room:', result.error) } }) ``` -------------------------------- ### TldrawApp API Methods Source: https://github.com/tldraw/tldraw-v1/blob/main/guides/documentation.md The TldrawApp API provides methods to imperatively control the state of the tldraw component. These methods can be accessed via callbacks like onMount or onPersist. ```APIDOC ## TldrawApp API ### Description The `TldrawApp` API allows for programmatic manipulation of the tldraw drawing state. It can be accessed through callbacks such as `onMount`. ### Methods - `loadDocument(document)`: Loads a new document. - `select(shapeIds)`: Selects the shapes with the given IDs. - `selectAll()`: Selects all shapes on the current page. - `selectNone()`: Deselects all currently selected shapes. - `delete(shapeIds)`: Deletes the shapes with the given IDs. - `deleteAll()`: Deletes all shapes on the current page. - `deletePage(pageId)`: Deletes the page with the given ID. - `changePage(pageId)`: Changes the current page to the one with the given ID. - `cut()`: Cuts the selected shapes to the clipboard. - `copy()`: Copies the selected shapes to the clipboard. - `paste()`: Pastes shapes from the clipboard. - `copyJson()`: Copies the document as JSON to the clipboard. - `copySvg()`: Copies the document as SVG to the clipboard. - `undo()`: Undoes the last action. - `redo()`: Redoes the last undone action. - `zoomIn()`: Zooms the viewport in. - `zoomOut()`: Zooms the viewport out. - `zoomToContent()`: Zooms the viewport to fit all content. - `zoomToSelection()`: Zooms the viewport to fit the selected shapes. - `zoomToFit()`: Zooms the viewport to fit the entire page. - `zoomTo(zoomLevel)`: Zooms the viewport to the specified zoom level. - `resetZoom()`: Resets the viewport zoom to its default. - `setCamera(camera)`: Sets the viewport camera to the specified camera settings. - `resetCamera()`: Resets the viewport camera to its default. - `align(alignType, shapeIds)`: Aligns the specified shapes. - `distribute(distributeType, shapeIds)`: Distributes the specified shapes. - `stretch(stretchType, shapeIds)`: Stretches the specified shapes. - `nudge(direction, delta, shapeIds)`: Nudges the specified shapes in a given direction. - `duplicate(shapeIds)`: Duplicates the specified shapes. - `flipHorizontal(shapeIds)`: Flips the specified shapes horizontally. - `flipVertical(shapeIds)`: Flips the specified shapes vertically. - `rotate(rotation, shapeIds)`: Rotates the specified shapes. - `style(style)`: Applies the given style to the selected shapes. - `group(shapeIds)`: Groups the specified shapes. - `ungroup(groupId)`: Ungroups the shapes within the specified group. - `createShapes(shapes)`: Creates new shapes. - `updateShapes(shapes)`: Updates existing shapes. - `updateDocument(document)`: Updates the entire document. - `updateUsers(users)`: Updates the user list. - `removeUser(userId)`: Removes a user. - `setSetting(settingName, settingValue)`: Sets a specific setting. - `selectTool(toolId)`: Selects the specified tool. - `cancel()`: Cancels the current tool action. ### Example Usage ```tsx import { Tldraw, TldrawApp } from '@tldraw/tldraw' function App() { const handleMount = React.useCallback((app: TldrawApp) => { app.selectAll() app.zoomIn() }, []) return } ``` ``` -------------------------------- ### TLDraw Geometry and Bounds Utilities in TypeScript Source: https://context7.com/tldraw/tldraw-v1/llms.txt Demonstrates various geometry utilities from the @tldraw/core library. This includes calculating bounds from points, performing operations like expansion and rotation on bounds, combining multiple bounds, and conducting collision detection. It also covers point-in-bounds testing, angle conversions, distance and angle calculations between points, interpolation for numbers and colors, and generating unique IDs. Performance utilities like debounce and throttle are also showcased. ```typescript import { Utils, TLBounds } from '@tldraw/core' function geometryUtilitiesExample() { // Calculate bounds from points const points = [[10, 20], [100, 50], [50, 150], [20, 80]] const bounds = Utils.getBoundsFromPoints(points) console.log('Bounds:', bounds) // { minX: 10, minY: 20, maxX: 100, maxY: 150, width: 90, height: 130 } // Bounds operations const center = Utils.getBoundsCenter(bounds) const expanded = Utils.expandBounds(bounds, 10) const rotated = Utils.rotateBounds(bounds, [50, 50], Math.PI / 4) const translated = Utils.translateBounds(bounds, [20, 30]) // Combine multiple bounds const bounds1: TLBounds = { minX: 0, minY: 0, maxX: 100, maxY: 100, width: 100, height: 100 } const bounds2: TLBounds = { minX: 50, minY: 50, maxX: 150, maxY: 150, width: 100, height: 100 } const combined = Utils.getCommonBounds([bounds1, bounds2]) console.log('Combined bounds:', combined) // { minX: 0, minY: 0, maxX: 150, maxY: 150, width: 150, height: 150 } // Collision detection const doCollide = Utils.boundsCollide(bounds1, bounds2) // true const doesContain = Utils.boundsContain(bounds1, bounds2) // false const isContained = Utils.boundsContained(bounds1, bounds2) // false const areEqual = Utils.boundsAreEqual(bounds1, bounds2) // false // Point testing const testPoint = [75, 75] const inBounds = Utils.pointInBounds(testPoint, bounds1) // true const inCircle = Utils.pointInCircle(testPoint, [50, 50], 50) // true const inEllipse = Utils.pointInEllipse( testPoint, [50, 50], // center 60, // x radius 40, // y radius 0 // rotation ) const inPolygon = Utils.pointInPolygon(testPoint, points) // check if inside polygon // Angle utilities const radians = Utils.degreesToRadians(90) // 1.5708 const degrees = Utils.radiansToDegrees(Math.PI) // 180 const clamped = Utils.clampRadians(Math.PI * 3) // clamp to 0-2π const snapped = Utils.snapAngleToSegments(0.7, 8) // snap to 45° segments // Distance and angle between points const pointA = [100, 100] const pointB = [200, 150] const distance = Utils.dist(pointA, pointB) const angle = Utils.angle(pointA, pointB) // Interpolation const interpolated = Utils.lerp(0, 100, 0.5) // 50 const colorInterpolated = Utils.lerpColor('#ff0000', '#0000ff', 0.5) const angles = Utils.lerpAngles(0, Math.PI, 0.5) // Rotated corners of bounds const corners = Utils.getRotatedCorners(bounds, Math.PI / 4) console.log('Rotated corners:', corners) // Generate unique IDs const id1 = Utils.uniqueId() const id2 = Utils.uniqueId('shape') // Deep clone objects const original = { shapes: { box1: { point: [0, 0] } } } const cloned = Utils.deepClone(original) // Performance: debounce and throttle const debouncedFn = Utils.debounce((value: string) => { console.log('Debounced:', value) }, 300) const throttledFn = Utils.throttle((value: string) => { console.log('Throttled:', value) }, 100) return { bounds, center, combined, doCollide, inBounds, distance, angle, interpolated, corners, } } ``` -------------------------------- ### TldrawApp API for Document Manipulation Source: https://context7.com/tldraw/tldraw-v1/llms.txt Demonstrates programmatic manipulation of shapes, pages, selection, camera, and styles using the TldrawApp fluent API. Includes creating, selecting, duplicating, grouping, styling, aligning, copying, pasting, undoing, and redoing operations. Requires a TldrawApp instance. ```typescript import { TldrawApp, TDShapeType, ColorStyle, DashStyle } from '@tldraw/tldraw' function demonstrateTldrawAPI(app: TldrawApp) { // Create multiple shapes with chaining app .createShapes( { id: 'box1', type: TDShapeType.Rectangle, point: [100, 100], size: [150, 150], }, { id: 'circle1', type: TDShapeType.Ellipse, point: [300, 100], radius: [75, 75], }, { id: 'arrow1', type: TDShapeType.Arrow, point: [100, 300], } ) .selectAll() .style({ color: ColorStyle.Blue, dash: DashStyle.Dashed }) .selectNone() // Select and manipulate shapes app .select('box1', 'circle1') .duplicate() .nudge([10, 10], false) .rotate(Math.PI / 4) .group() // Camera operations app.zoomToFit() app.pan([50, 50]) app.zoomIn() // Page management app.createPage('page2', 'Sketches') app.changePage('page2') // Style operations on selected shapes app .select('box1') .style({ color: ColorStyle.Red, size: 'large', isFilled: true }) .moveToFront() // Alignment and distribution app.selectAll().align('center').distribute('horizontal') // Clipboard operations app.select('box1').copy().paste([400, 400]) // Undo/redo app.undo() app.redo() // Access document state const document = app.document const currentPage = app.page const selectedShapes = app.selectedIds const camera = app.pageState.camera console.log('Current page:', currentPage.id) console.log('Selected:', selectedShapes) console.log('Camera:', camera) } ``` -------------------------------- ### Curve and Spline Generation with @tldraw/curve Source: https://context7.com/tldraw/tldraw-v1/llms.txt Demonstrates various curve and spline generation methods using the @tldraw/curve package. It includes generating bezier segments, smooth splines, and interpolated points. Also shows how to simplify curves and create SVG path data. This function takes an array of points as input and returns generated curve data. ```typescript import { getTLBezierCurveSegments, getSpline, getCurvePoints, simplify, computePointOnSpline, } from '@tldraw/curve' function curveOperationsExample() { // Input points for curve generation const points = [ [0, 100], [50, 150], [100, 120], [150, 80], [200, 100], ] // Generate bezier curve segments through points const bezierSegments = getTLBezierCurveSegments(points, 0.4) console.log('Bezier segments:', bezierSegments) // Each segment has: { start, c1, c2, end } // Generate smooth spline through points const splinePoints = getSpline(points, 0.5) console.log('Spline points:', splinePoints) // Returns array of points forming smooth curve // Get interpolated curve points with custom segments const smoothCurve = getCurvePoints( points, 0.5, // tension (0-1, lower = smoother) false, // is closed curve 3 // number of segments between points ) console.log('Smooth curve points:', smoothCurve.length) // Compute specific point on spline at parameter t const pointOnCurve = computePointOnSpline(0.5, points) console.log('Point at t=0.5:', pointOnCurve) // midpoint of curve // Simplify curve by reducing points (Ramer-Douglas-Peucker) const denseCurve = [ [0, 0], [1, 0], [2, 0], [3, 0], [4, 1], [5, 2], [6, 3], [7, 3], [8, 3], [9, 3], [10, 3] ] const simplified = simplify(denseCurve, 1.0) console.log('Simplified from', denseCurve.length, 'to', simplified.length, 'points') // Practical example: Draw smooth path through user input points function generateSmoothPath(userPoints: number[][]) { if (userPoints.length < 2) return '' const smoothPoints = getCurvePoints(userPoints, 0.5, false, 3) const simplified = simplify(smoothPoints, 2) const pathData = simplified .map((point, i) => `${i === 0 ? 'M' : 'L'} ${point[0]} ${point[1]}`) .join(' ') return pathData } const svgPath = generateSmoothPath(points) console.log('SVG path:', svgPath) return { bezierSegments, splinePoints, smoothCurve, simplified, svgPath, } } ``` -------------------------------- ### Custom Box Shape Rendering and Interaction with @tldraw/core (TypeScript) Source: https://context7.com/tldraw/tldraw-v1/llms.txt This snippet shows how to define a custom 'box' shape, implement its rendering logic using a `BoxUtil` class, and integrate it into a custom drawing application using the `` component. It handles shape creation, selection, and dragging. Dependencies include @tldraw/core and @tldraw/vec. ```typescript import { Renderer, TLPage, TLPageState, TLShapeUtil, TLBounds, TLPointerEventHandler } from '@tldraw/core' import Vec from '@tldraw/vec' import * as React from 'react' // Define custom shape type interface BoxShape { id: string type: 'box' parentId: string childIndex: number name: string point: number[] size: number[] rotation: number color: string } // Implement shape utility class BoxUtil extends TLShapeUtil { Component = React.forwardRef(({ shape, events }, ref) => ( )) Indicator = ({ shape }: { shape: BoxShape }) => ( ) getBounds = (shape: BoxShape): TLBounds => { const [x, y] = shape.point const [width, height] = shape.size return { minX: x, maxX: x + width, minY: y, maxY: y + height, width, height } } } // Create custom drawing application export default function CustomDrawingApp() { const [page, setPage] = React.useState>({ id: 'page1', shapes: { box1: { id: 'box1', type: 'box', parentId: 'page1', name: 'Box 1', childIndex: 1, rotation: 0, point: [100, 100], size: [120, 120], color: '#ff6b6b', }, }, bindings: {}, }) const [pageState, setPageState] = React.useState({ id: 'page1', selectedIds: [], camera: { point: [0, 0], zoom: 1 }, }) const shapeUtils = React.useMemo(() => ({ box: new BoxUtil() }), []) const handlePointShape: TLPointerEventHandler = (info) => { setPageState(prev => ({ ...prev, selectedIds: [info.target] })) } const handleDragShape: TLPointerEventHandler = (info) => { setPage(page => { const shape = page.shapes[info.target] return { ...page, shapes: { ...page.shapes, [shape.id]: { ...shape, point: Vec.sub(info.point, Vec.div(shape.size, 2)), }, }, } }) } const handleDoubleClickCanvas: TLPointerEventHandler = (info) => { const id = 'box_' + Date.now() setPage(page => ({ ...page, shapes: { ...page.shapes, [id]: { id, type: 'box', parentId: 'page1', name: 'Box', childIndex: Object.keys(page.shapes).length + 1, rotation: 0, point: info.point, size: [100, 100], color: '#4ecdc4', }, }, })) } return (
) } ```