├── .changeset
├── README.md
└── config.json
├── .eslintignore
├── .eslintrc
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── documentation.md
│ ├── feature.md
│ └── testing.md
└── workflows
│ └── main.yml
├── .gitignore
├── .husky
└── pre-commit
├── .npmignore
├── .prettierrc
├── .turbo
└── config.json
├── .vscode
├── extensions.json
├── launch.json
├── settings.json
└── snippets.code-snippets
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── apps
├── electron
│ ├── LICENSE.md
│ ├── README.md
│ ├── electron-esbuild.config.yaml
│ ├── esbuild.main.config.ts
│ ├── esbuild.renderer.config.ts
│ ├── package.json
│ ├── resources
│ │ ├── entitlements.mac.plist
│ │ ├── icon.icns
│ │ ├── icon.ico
│ │ └── notarize.js
│ ├── src
│ │ ├── main
│ │ │ ├── createMenu.ts
│ │ │ ├── createWindow.ts
│ │ │ ├── main.ts
│ │ │ └── preload.ts
│ │ ├── renderer
│ │ │ ├── app.tsx
│ │ │ ├── index.html
│ │ │ ├── index.tsx
│ │ │ └── styles.css
│ │ └── types.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── vscode
│ ├── README.md
│ ├── editor
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE.md
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── scripts
│ │ │ ├── build.mjs
│ │ │ └── dev.mjs
│ │ ├── src
│ │ │ ├── app.tsx
│ │ │ ├── index.tsx
│ │ │ ├── styles.css
│ │ │ ├── types.ts
│ │ │ └── utils
│ │ │ │ ├── defaultDocument.ts
│ │ │ │ ├── export.ts
│ │ │ │ └── vscode.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.tsbuildinfo
│ │ └── yarn.lock
│ └── extension
│ │ ├── .gitignore
│ │ ├── .vscode
│ │ ├── extensions.json
│ │ ├── launch.json
│ │ └── settings.json
│ │ ├── .vscodeignore
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE.md
│ │ ├── README.md
│ │ ├── assets
│ │ ├── recording.gif
│ │ └── screenshot.png
│ │ ├── examples
│ │ ├── 1.tldr
│ │ ├── 2.tldr
│ │ └── 3.tldr
│ │ ├── icon.png
│ │ ├── package.json
│ │ ├── scripts
│ │ ├── build.js
│ │ ├── dev.js
│ │ └── package.js
│ │ ├── src
│ │ ├── TldrawEditorProvider.ts
│ │ ├── TldrawWebviewManager.ts
│ │ ├── extension.ts
│ │ ├── types.ts
│ │ └── utils.ts
│ │ ├── tsconfig.json
│ │ └── yarn.lock
└── www
│ ├── .babelrc
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── components
│ ├── Editor.tsx
│ └── MultiplayerEditor.tsx
│ ├── hooks
│ ├── useAccountHandlers.ts
│ ├── useMultiplayerAssets.ts
│ └── useMultiplayerState.ts
│ ├── next-env.d.ts
│ ├── next.config.js
│ ├── package.json
│ ├── pages
│ ├── _app.tsx
│ ├── _document.tsx
│ ├── api
│ │ ├── auth
│ │ │ └── [...nextauth].ts
│ │ ├── export.ts
│ │ ├── sponsors.ts
│ │ └── upload.ts
│ ├── index.tsx
│ ├── r
│ │ ├── [id].tsx
│ │ └── index.tsx
│ └── sponsorware.tsx
│ ├── public
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ ├── apple-touch-icon.png
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon.ico
│ ├── flat.png
│ ├── icons
│ │ ├── Redo.svg
│ │ ├── Trash.svg
│ │ ├── Undo.svg
│ │ ├── grab.svg
│ │ ├── pointer.svg
│ │ └── resize.svg
│ ├── images
│ │ └── hello.mp4
│ ├── manifest.json
│ └── social-image.png
│ ├── styles
│ ├── globals.css
│ ├── index.ts
│ └── stitches.config.ts
│ ├── tsconfig.json
│ ├── types.ts
│ ├── utils
│ ├── export.ts
│ ├── github.ts
│ ├── gtag.ts
│ ├── sentry.ts
│ └── useGtag.ts
│ └── worker
│ └── index.js
├── assets
├── card-repo.png
├── icon_flat_black.png
├── icon_flat_black_on_white.png
├── icon_flat_white.png
├── icon_flat_white_on_black.png
├── oppizi.png
├── recording.gif
├── screenshot.png
├── sentry.svg
├── tldraw.png
└── vercel.svg
├── examples
├── core-example-advanced
│ ├── LICENSE.md
│ ├── README.md
│ ├── package.json
│ ├── scripts
│ │ ├── build.mjs
│ │ └── dev.mjs
│ ├── src
│ │ ├── app.tsx
│ │ ├── components
│ │ │ ├── TitleLinks.tsx
│ │ │ └── Toolbar.tsx
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── shapes
│ │ │ ├── CustomShapeUtil.ts
│ │ │ ├── arrow
│ │ │ │ ├── ArrowComponent.tsx
│ │ │ │ ├── ArrowIndicator.tsx
│ │ │ │ ├── ArrowShape.ts
│ │ │ │ ├── ArrowUtil.ts
│ │ │ │ └── index.ts
│ │ │ ├── box
│ │ │ │ ├── BoxComponent.tsx
│ │ │ │ ├── BoxIndicator.tsx
│ │ │ │ ├── BoxShape.ts
│ │ │ │ ├── BoxUtil.ts
│ │ │ │ └── index.ts
│ │ │ ├── index.ts
│ │ │ └── pencil
│ │ │ │ ├── PencilComponent.tsx
│ │ │ │ ├── PencilShape.ts
│ │ │ │ ├── PencilUtil.ts
│ │ │ │ ├── PenclIndicator.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── pencil-helpers.ts
│ │ ├── state
│ │ │ ├── actions
│ │ │ │ ├── bindings
│ │ │ │ │ ├── createBindings.ts
│ │ │ │ │ ├── deleteBindings.ts
│ │ │ │ │ ├── getBoundHandlePoint.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── removePartialBindings.ts
│ │ │ │ │ ├── updateBindings.ts
│ │ │ │ │ └── updateBoundShapes.ts
│ │ │ │ ├── camera
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── panCamera.ts
│ │ │ │ │ ├── pinchCamera.ts
│ │ │ │ │ ├── zoomIn.ts
│ │ │ │ │ ├── zoomOut.ts
│ │ │ │ │ ├── zoomToFit.ts
│ │ │ │ │ └── zoomToSelection.ts
│ │ │ │ ├── data
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── loadDocument.ts
│ │ │ │ │ ├── loadNewDocument.ts
│ │ │ │ │ └── restoreSavedDocument.ts
│ │ │ │ ├── erase
│ │ │ │ │ ├── eraseGhostShapes.ts
│ │ │ │ │ ├── eraseShapes.ts
│ │ │ │ │ ├── eraseShapesAtPoint.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── handles
│ │ │ │ │ ├── clearPointedHandle.ts
│ │ │ │ │ ├── getBoundTarget.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── setPointedHandle.ts
│ │ │ │ │ └── translateHandle.ts
│ │ │ │ ├── history
│ │ │ │ │ ├── addToHistory.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── redo.ts
│ │ │ │ │ └── undo.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── mutables
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── restoreSnapshot.ts
│ │ │ │ │ ├── setInitialPoint.ts
│ │ │ │ │ ├── setSnapshot.ts
│ │ │ │ │ ├── setViewport.ts
│ │ │ │ │ └── updatePointer.ts
│ │ │ │ ├── performance
│ │ │ │ │ ├── clearPerformanceMode.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── setTransformPerformanceMode.ts
│ │ │ │ │ └── setTranslatePerformanceMode.ts
│ │ │ │ ├── selection
│ │ │ │ │ ├── clearBrush.ts
│ │ │ │ │ ├── clearHoveredShape.ts
│ │ │ │ │ ├── clearPointedShape.ts
│ │ │ │ │ ├── deselectAllShapes.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── selectAllShapes.ts
│ │ │ │ │ ├── selectShape.ts
│ │ │ │ │ ├── setHoveredShape.ts
│ │ │ │ │ └── updateBrush.ts
│ │ │ │ ├── shapes
│ │ │ │ │ ├── createArrowShape.ts
│ │ │ │ │ ├── createBoxShape.ts
│ │ │ │ │ ├── createPencilShape.ts
│ │ │ │ │ ├── createShapes.ts
│ │ │ │ │ ├── deleteSelectedShapes.ts
│ │ │ │ │ ├── deleteShapes.ts
│ │ │ │ │ ├── extendPencilShape.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── updateShapes.ts
│ │ │ │ ├── snapping
│ │ │ │ │ └── clearSnapLines.ts
│ │ │ │ ├── snaps
│ │ │ │ │ ├── clearSnapInfo.ts
│ │ │ │ │ ├── clearSnapLines.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── setSnapInfo.ts
│ │ │ │ ├── transform
│ │ │ │ │ ├── clearPointedBoundsHandle.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── resizeSelectedShapes.ts
│ │ │ │ │ ├── rotateSelectedShapes.ts
│ │ │ │ │ ├── setInitialCommonBounds.ts
│ │ │ │ │ ├── setPointedBoundsHandle.ts
│ │ │ │ │ └── transformSelectedShapes.ts
│ │ │ │ └── translate
│ │ │ │ │ ├── clearIsCloning.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── translateSelectedShapes.ts
│ │ │ ├── api.ts
│ │ │ ├── constants.ts
│ │ │ ├── helpers.ts
│ │ │ ├── history.ts
│ │ │ ├── machine.ts
│ │ │ └── mutables.ts
│ │ ├── stitches.config.ts
│ │ ├── styles.css
│ │ └── types.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── core-example
│ ├── LICENSE.md
│ ├── README.md
│ ├── package.json
│ ├── scripts
│ │ ├── build.mjs
│ │ └── dev.mjs
│ ├── src
│ │ ├── app.tsx
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── shapes
│ │ │ ├── index.ts
│ │ │ └── rect
│ │ │ │ ├── RectComponent.tsx
│ │ │ │ ├── RectIndicator.tsx
│ │ │ │ ├── RectShape.ts
│ │ │ │ ├── RectUtil.ts
│ │ │ │ └── index.ts
│ │ ├── stores.ts
│ │ └── styles.css
│ ├── tsconfig.json
│ └── yarn.lock
└── tldraw-example
│ ├── LICENSE
│ ├── README.md
│ ├── card-repo.png
│ ├── package.json
│ ├── scripts
│ ├── build.mjs
│ └── dev.mjs
│ ├── src
│ ├── api-control.tsx
│ ├── api.tsx
│ ├── app.tsx
│ ├── basic.tsx
│ ├── changing-id.tsx
│ ├── develop.tsx
│ ├── embedded.tsx
│ ├── export.tsx
│ ├── file-system.tsx
│ ├── index.tsx
│ ├── loading-files.tsx
│ ├── multiplayer-with-images
│ │ ├── index.ts
│ │ ├── multiplayer.tsx
│ │ └── useMultiplayerState.ts
│ ├── multiplayer
│ │ ├── index.ts
│ │ ├── multiplayer.tsx
│ │ └── useMultiplayerState.ts
│ ├── no-size-embedded.tsx
│ ├── persisted.tsx
│ ├── props-control.tsx
│ ├── public
│ │ ├── Example.tldr
│ │ ├── card-repo.png
│ │ └── index.html
│ ├── readonly.tsx
│ ├── styles.css
│ └── ui-options.tsx
│ └── tsconfig.json
├── guides
├── development.md
├── documentation.md
└── publishing.md
├── lerna.json
├── package.json
├── packages
├── core
│ ├── CHANGELOG.md
│ ├── LICENSE.md
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── TLShapeUtil
│ │ │ ├── TLShapeUtil.spec.tsx
│ │ │ ├── TLShapeUtil.tsx
│ │ │ └── index.ts
│ │ ├── components
│ │ │ ├── Binding
│ │ │ │ ├── Binding.test.tsx
│ │ │ │ ├── Binding.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Bounds
│ │ │ │ ├── Bounds.tsx
│ │ │ │ ├── BoundsBg.tsx
│ │ │ │ ├── CenterHandle.tsx
│ │ │ │ ├── CloneButton.tsx
│ │ │ │ ├── CloneButtons.tsx
│ │ │ │ ├── CornerHandle.tsx
│ │ │ │ ├── EdgeHandle.tsx
│ │ │ │ ├── LinkHandle.tsx
│ │ │ │ ├── RotateHandle.tsx
│ │ │ │ ├── __tests__
│ │ │ │ │ ├── Bounds.test.tsx
│ │ │ │ │ ├── BoundsBg.test.tsx
│ │ │ │ │ ├── CenterHandle.test.tsx
│ │ │ │ │ ├── CloneButton.test.tsx
│ │ │ │ │ ├── CornerHandle.test.tsx
│ │ │ │ │ ├── EdgeHandle.test.tsx
│ │ │ │ │ ├── LinkHandle.test.tsx
│ │ │ │ │ └── RotateHandle.test.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Brush
│ │ │ │ ├── Brush.test.tsx
│ │ │ │ ├── Brush.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Canvas
│ │ │ │ ├── Canvas.test.tsx
│ │ │ │ ├── Canvas.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Container
│ │ │ │ ├── Container.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Grid
│ │ │ │ ├── Grid.tsx
│ │ │ │ └── index.ts
│ │ │ ├── HTMLContainer
│ │ │ │ ├── HTMLContainer.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Handles
│ │ │ │ ├── Handle.test.tsx
│ │ │ │ ├── Handle.tsx
│ │ │ │ ├── Handles.test.tsx
│ │ │ │ ├── Handles.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Overlay
│ │ │ │ ├── Overlay.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Page
│ │ │ │ ├── Page.test.tsx
│ │ │ │ ├── Page.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Renderer
│ │ │ │ ├── Renderer.test.tsx
│ │ │ │ ├── Renderer.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── SVGContainer
│ │ │ │ ├── SVGContainer.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Shape
│ │ │ │ ├── RenderedShape.tsx
│ │ │ │ ├── Shape.test.tsx
│ │ │ │ ├── Shape.tsx
│ │ │ │ ├── ShapeNode.tsx
│ │ │ │ └── index.ts
│ │ │ ├── ShapeIndicator
│ │ │ │ ├── ShapeIndicator.test.tsx
│ │ │ │ ├── ShapeIndicator.tsx
│ │ │ │ └── index.ts
│ │ │ ├── SnapLines
│ │ │ │ ├── SnapLines.tsx
│ │ │ │ └── index.ts
│ │ │ ├── User
│ │ │ │ ├── User.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Users
│ │ │ │ ├── Users.tsx
│ │ │ │ └── index.ts
│ │ │ ├── UsersIndicators
│ │ │ │ ├── UsersIndicators.tsx
│ │ │ │ └── index.ts
│ │ │ └── index.tsx
│ │ ├── hooks
│ │ │ ├── index.ts
│ │ │ ├── useBoundsEvents.tsx
│ │ │ ├── useBoundsHandleEvents.tsx
│ │ │ ├── useCameraCss.tsx
│ │ │ ├── useCanvasEvents.tsx
│ │ │ ├── useCursorAnimation.ts
│ │ │ ├── useHandleEvents.tsx
│ │ │ ├── useHandles.ts
│ │ │ ├── useKeyEvents.ts
│ │ │ ├── usePerformanceCss.ts
│ │ │ ├── usePosition.ts
│ │ │ ├── usePreventNavigationCss.tsx
│ │ │ ├── useResizeObserver.ts
│ │ │ ├── useSafariFocusOutFix.tsx
│ │ │ ├── useSelection.tsx
│ │ │ ├── useShapeEvents.tsx
│ │ │ ├── useShapeTree.tsx
│ │ │ ├── useStyle.tsx
│ │ │ ├── useTLContext.tsx
│ │ │ └── useZoomEvents.ts
│ │ ├── index.ts
│ │ ├── inputs.ts
│ │ ├── test
│ │ │ ├── ContextWrapper.tsx
│ │ │ ├── index.ts
│ │ │ ├── mockDocument.ts
│ │ │ ├── mockUtils.tsx
│ │ │ ├── renderWithContext.tsx
│ │ │ └── renderWithSvg.tsx
│ │ ├── types.ts
│ │ └── utils
│ │ │ ├── index.d.ts
│ │ │ ├── index.ts
│ │ │ ├── polyfills.ts
│ │ │ └── utils.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.dev.json
│ └── tsconfig.json
├── curve
│ ├── CHANGELOG.md
│ ├── LICENSE.md
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ └── index.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.dev.json
│ └── tsconfig.json
├── intersect
│ ├── CHANGELOG.md
│ ├── LICENSE.md
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ └── index.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.dev.json
│ └── tsconfig.json
├── tldraw
│ ├── CHANGELOG.md
│ ├── LICENSE.md
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── Tldraw.spec.tsx
│ │ ├── Tldraw.tsx
│ │ ├── components
│ │ │ ├── BottomPanel
│ │ │ │ ├── BottomPanel.tsx
│ │ │ │ └── index.ts
│ │ │ ├── ContextMenu
│ │ │ │ ├── ContextMenu.test.tsx
│ │ │ │ ├── ContextMenu.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Deck
│ │ │ │ ├── Deck.tsx
│ │ │ │ └── index.ts
│ │ │ ├── DeckContextMenu
│ │ │ │ ├── DeckContextMenu.tsx
│ │ │ │ └── index.ts
│ │ │ ├── FocusButton
│ │ │ │ ├── FocusButton.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Loading
│ │ │ │ ├── Loading.tsx
│ │ │ │ └── index.ts
│ │ │ ├── Primitives
│ │ │ │ ├── Divider
│ │ │ │ │ ├── Divider.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── DropdownMenu
│ │ │ │ │ ├── DMArrow.tsx
│ │ │ │ │ ├── DMCheckboxItem.tsx
│ │ │ │ │ ├── DMContent.tsx
│ │ │ │ │ ├── DMDivider.tsx
│ │ │ │ │ ├── DMItem.tsx
│ │ │ │ │ ├── DMRadioItem.tsx
│ │ │ │ │ ├── DMSubMenu.tsx
│ │ │ │ │ ├── DMTriggerIcon.tsx
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── IconButton
│ │ │ │ │ ├── IconButton.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── Kbd
│ │ │ │ │ ├── Kbd.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── MenuContent
│ │ │ │ │ ├── MenuContent.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── Panel
│ │ │ │ │ ├── Panel.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── RowButton
│ │ │ │ │ ├── RowButton.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── SmallIcon
│ │ │ │ │ ├── SmallIcon.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── ToolButton
│ │ │ │ │ ├── ToolButton.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── Tooltip
│ │ │ │ │ ├── Tooltip.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ └── icons
│ │ │ │ │ ├── BoxIcon.tsx
│ │ │ │ │ ├── CircleIcon.tsx
│ │ │ │ │ ├── DashDashedIcon.tsx
│ │ │ │ │ ├── DashDottedIcon.tsx
│ │ │ │ │ ├── DashDrawIcon.tsx
│ │ │ │ │ ├── DashSolidIcon.tsx
│ │ │ │ │ ├── DiscordIcon.tsx
│ │ │ │ │ ├── EraserIcon.tsx
│ │ │ │ │ ├── HeartIcon.tsx
│ │ │ │ │ ├── IsFilledIcon.tsx
│ │ │ │ │ ├── LineIcon.tsx
│ │ │ │ │ ├── MultiplayerIcon.tsx
│ │ │ │ │ ├── RedoIcon.tsx
│ │ │ │ │ ├── SizeLargeIcon.tsx
│ │ │ │ │ ├── SizeMediumIcon.tsx
│ │ │ │ │ ├── SizeSmallIcon.tsx
│ │ │ │ │ ├── TrashIcon.tsx
│ │ │ │ │ ├── UndoIcon.tsx
│ │ │ │ │ └── index.ts
│ │ │ ├── ReadOnlyEditor
│ │ │ │ ├── ReadOnlyEditor.tsx
│ │ │ │ └── index.ts
│ │ │ ├── ToolsPanel
│ │ │ │ ├── ActionButton.tsx
│ │ │ │ ├── BackToContent.tsx
│ │ │ │ ├── DeleteButton.tsx
│ │ │ │ ├── LockButton.tsx
│ │ │ │ ├── PenMenu.tsx
│ │ │ │ ├── PrimaryTools.tsx
│ │ │ │ ├── ShapesMenu.tsx
│ │ │ │ ├── StatusBar.tsx
│ │ │ │ ├── ToolsPanel.test.tsx
│ │ │ │ ├── ToolsPanel.tsx
│ │ │ │ └── index.ts
│ │ │ ├── TopPanel
│ │ │ │ ├── Menu
│ │ │ │ │ ├── Menu.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── MultiplayerMenu
│ │ │ │ │ ├── MultiplayerMenu.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── PageMenu
│ │ │ │ │ ├── PageMenu.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── PageOptionsDialog
│ │ │ │ │ ├── PageOptionsDialog.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── PreferencesMenu
│ │ │ │ │ ├── PreferencesMenu.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── StyleMenu
│ │ │ │ │ ├── StyleMenu.test.tsx
│ │ │ │ │ ├── StyleMenu.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── TopPanel.tsx
│ │ │ │ ├── ZoomMenu
│ │ │ │ │ ├── ZoomMenu.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ └── index.ts
│ │ │ ├── breakpoints.tsx
│ │ │ ├── preventEvent.ts
│ │ │ └── stopPropagation.ts
│ │ ├── constants.ts
│ │ ├── hooks
│ │ │ ├── index.ts
│ │ │ ├── useFileSystem.ts
│ │ │ ├── useFileSystemHandlers.ts
│ │ │ ├── useKeyboardShortcuts.tsx
│ │ │ ├── useStylesheet.ts
│ │ │ ├── useTheme.ts
│ │ │ └── useTldrawApp.tsx
│ │ ├── index.ts
│ │ ├── state
│ │ │ ├── StateManager
│ │ │ │ ├── StateManager.ts
│ │ │ │ ├── copy.ts
│ │ │ │ └── index.ts
│ │ │ ├── TLDR.ts
│ │ │ ├── TldrawApp.spec.ts
│ │ │ ├── TldrawApp.ts
│ │ │ ├── __snapshots__
│ │ │ │ └── TldrawApp.spec.ts.snap
│ │ │ ├── commands
│ │ │ │ ├── alignShapes
│ │ │ │ │ ├── alignShapes.spec.ts
│ │ │ │ │ ├── alignShapes.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── changePage
│ │ │ │ │ ├── changePage.spec.ts
│ │ │ │ │ ├── changePage.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── createPage
│ │ │ │ │ ├── createPage.spec.ts
│ │ │ │ │ ├── createPage.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── createShapes
│ │ │ │ │ ├── createShapes.spec.ts
│ │ │ │ │ ├── createShapes.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── deletePage
│ │ │ │ │ ├── deletePage.spec.ts
│ │ │ │ │ ├── deletePage.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── deleteShapes
│ │ │ │ │ ├── deleteShapes.spec.ts
│ │ │ │ │ ├── deleteShapes.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── distributeShapes
│ │ │ │ │ ├── distributeShapes.spec.ts
│ │ │ │ │ ├── distributeShapes.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── duplicatePage
│ │ │ │ │ ├── duplicatePage.spec.ts
│ │ │ │ │ ├── duplicatePage.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── duplicateShapes
│ │ │ │ │ ├── duplicateShapes.spec.ts
│ │ │ │ │ ├── duplicateShapes.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── flipShapes
│ │ │ │ │ ├── flipShapes.spec.ts
│ │ │ │ │ ├── flipShapes.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── groupShapes
│ │ │ │ │ ├── groupShapes.spec.ts
│ │ │ │ │ ├── groupShapes.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── moveShapesToPage
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── moveShapesToPage.spec.ts
│ │ │ │ │ └── moveShapesToPage.ts
│ │ │ │ ├── renamePage
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── renamePage.spec.ts
│ │ │ │ │ └── renamePage.ts
│ │ │ │ ├── reorderShapes
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── reorderShapes.spec.ts
│ │ │ │ │ └── reorderShapes.ts
│ │ │ │ ├── resetBounds
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── resetBounds.spec.ts
│ │ │ │ │ └── resetBounds.ts
│ │ │ │ ├── rotateShapes
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── rotateShapes.spec.ts
│ │ │ │ │ └── rotateShapes.ts
│ │ │ │ ├── setShapesProps
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── setShapesProps.spec.ts
│ │ │ │ │ └── setShapesProps.ts
│ │ │ │ ├── shared
│ │ │ │ │ └── removeShapesFromPage.ts
│ │ │ │ ├── stretchShapes
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── stretchShapes.spec.ts
│ │ │ │ │ └── stretchShapes.ts
│ │ │ │ ├── styleShapes
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── styleShapes.spec.ts
│ │ │ │ │ └── styleShapes.ts
│ │ │ │ ├── toggleShapesDecoration
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── toggleShapesDecoration.spec.ts
│ │ │ │ │ └── toggleShapesDecoration.ts
│ │ │ │ ├── toggleShapesProp
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── toggleShapesProp.spec.ts
│ │ │ │ │ └── toggleShapesProp.ts
│ │ │ │ ├── translateShapes
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── translateShapes.spec.ts
│ │ │ │ │ └── translateShapes.ts
│ │ │ │ ├── ungroupShapes
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── ungroupShapes.spec.ts
│ │ │ │ │ └── ungroupShapes.ts
│ │ │ │ └── updateShapes
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── updateShapes.spec.ts
│ │ │ │ │ └── updateShapes.ts
│ │ │ ├── data
│ │ │ │ ├── browser-fs-access
│ │ │ │ │ ├── directory-open.js
│ │ │ │ │ ├── file-open.js
│ │ │ │ │ ├── file-save.js
│ │ │ │ │ ├── fs-access
│ │ │ │ │ │ ├── directory-open.js
│ │ │ │ │ │ ├── file-open.js
│ │ │ │ │ │ └── file-save.js
│ │ │ │ │ ├── index.d.ts
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── legacy
│ │ │ │ │ │ ├── directory-open.js
│ │ │ │ │ │ ├── file-open.js
│ │ │ │ │ │ └── file-save.js
│ │ │ │ │ └── supported.js
│ │ │ │ ├── filesystem.spec.ts
│ │ │ │ ├── filesystem.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── migrate.spec.ts
│ │ │ │ └── migrate.ts
│ │ │ ├── index.ts
│ │ │ ├── internal.ts
│ │ │ ├── sessions
│ │ │ │ ├── ArrowSession
│ │ │ │ │ ├── ArrowSession.spec.ts
│ │ │ │ │ ├── ArrowSession.ts
│ │ │ │ │ ├── __snapshots__
│ │ │ │ │ │ └── ArrowSession.spec.ts.snap
│ │ │ │ │ ├── arrows.tldr
│ │ │ │ │ └── index.ts
│ │ │ │ ├── BaseSession.ts
│ │ │ │ ├── BrushSession
│ │ │ │ │ ├── BrushSession.spec.ts
│ │ │ │ │ ├── BrushSession.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── DrawSession
│ │ │ │ │ ├── DrawSession.spec.ts
│ │ │ │ │ ├── DrawSession.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── EraseSession
│ │ │ │ │ ├── EraseSession.spec.ts
│ │ │ │ │ ├── EraseSession.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── GridSession
│ │ │ │ │ ├── GridSession.spec.ts
│ │ │ │ │ ├── GridSession.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── HandleSession
│ │ │ │ │ ├── HandleSession.spec.ts
│ │ │ │ │ ├── HandleSession.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── RotateSession
│ │ │ │ │ ├── RotateSession.spec.ts
│ │ │ │ │ ├── RotateSession.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── TransformSession
│ │ │ │ │ ├── TransformSession.spec.ts
│ │ │ │ │ ├── TransformSession.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── TransformSingleSession
│ │ │ │ │ ├── TransformSingleSession.spec.ts
│ │ │ │ │ ├── TransformSingleSession.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── TranslateLabelSession
│ │ │ │ │ ├── TranslateLabelSession.spec.ts
│ │ │ │ │ ├── TranslateLabelSession.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── TranslateSession
│ │ │ │ │ ├── TranslateSession.spec.ts
│ │ │ │ │ ├── TranslateSession.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── about-sessions.md
│ │ │ │ └── index.ts
│ │ │ ├── shapes
│ │ │ │ ├── ArrowUtil
│ │ │ │ │ ├── ArrowUtil.spec.tsx
│ │ │ │ │ ├── ArrowUtil.tsx
│ │ │ │ │ ├── __snapshots__
│ │ │ │ │ │ └── ArrowUtil.spec.tsx.snap
│ │ │ │ │ ├── arrowHelpers.ts
│ │ │ │ │ ├── components
│ │ │ │ │ │ ├── ArrowHead.tsx
│ │ │ │ │ │ ├── CurvedArrow.tsx.tsx
│ │ │ │ │ │ └── StraightArrow.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── DrawUtil
│ │ │ │ │ ├── DrawUtil.spec.tsx
│ │ │ │ │ ├── DrawUtil.tsx
│ │ │ │ │ ├── __snapshots__
│ │ │ │ │ │ └── DrawUtil.spec.tsx.snap
│ │ │ │ │ ├── drawHelpers.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── EllipseUtil
│ │ │ │ │ ├── EllipseUtil.spec.tsx
│ │ │ │ │ ├── EllipseUtil.tsx
│ │ │ │ │ ├── __snapshots__
│ │ │ │ │ │ └── EllipseUtil.spec.tsx.snap
│ │ │ │ │ ├── components
│ │ │ │ │ │ ├── DashedEllipse.tsx
│ │ │ │ │ │ └── DrawEllipse.tsx
│ │ │ │ │ ├── ellipseHelpers.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── GroupUtil
│ │ │ │ │ ├── GroupUtil.spec.tsx
│ │ │ │ │ ├── GroupUtil.tsx
│ │ │ │ │ ├── __snapshots__
│ │ │ │ │ │ └── GroupUtil.spec.tsx.snap
│ │ │ │ │ └── index.ts
│ │ │ │ ├── ImageUtil
│ │ │ │ │ ├── ImageUtil.spec.tsx
│ │ │ │ │ ├── ImageUtil.tsx
│ │ │ │ │ ├── __snapshots__
│ │ │ │ │ │ └── ImageUtil.spec.tsx.snap
│ │ │ │ │ └── index.ts
│ │ │ │ ├── RectangleUtil
│ │ │ │ │ ├── RectangleUtil.spec.tsx
│ │ │ │ │ ├── RectangleUtil.tsx
│ │ │ │ │ ├── __snapshots__
│ │ │ │ │ │ └── RectangleUtil.spec.tsx.snap
│ │ │ │ │ ├── components
│ │ │ │ │ │ ├── BindingIndicator.tsx
│ │ │ │ │ │ ├── DashedRectangle.tsx
│ │ │ │ │ │ └── DrawRectangle.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── rectangleHelpers.ts
│ │ │ │ ├── StickyUtil
│ │ │ │ │ ├── StickyUtil.spec.tsx
│ │ │ │ │ ├── StickyUtil.tsx
│ │ │ │ │ └── index.ts
│ │ │ │ ├── TDShapeUtil.tsx
│ │ │ │ ├── TextUtil
│ │ │ │ │ ├── TextUtil.spec.tsx
│ │ │ │ │ ├── TextUtil.tsx
│ │ │ │ │ ├── __snapshots__
│ │ │ │ │ │ └── TextUtil.spec.tsx.snap
│ │ │ │ │ └── index.ts
│ │ │ │ ├── TriangleUtil
│ │ │ │ │ ├── TriangleUtil.spec.tsx
│ │ │ │ │ ├── TriangleUtil.tsx
│ │ │ │ │ ├── __snapshots__
│ │ │ │ │ │ └── TriangleUtil.spec.tsx.snap
│ │ │ │ │ ├── components
│ │ │ │ │ │ ├── DashedTriangle.tsx
│ │ │ │ │ │ ├── DrawTriangle.tsx
│ │ │ │ │ │ └── TriangleBindingIndicator.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── triangleHelpers.ts
│ │ │ │ ├── VideoUtil
│ │ │ │ │ ├── VideoUtil.spec.tsx
│ │ │ │ │ ├── VideoUtil.tsx
│ │ │ │ │ ├── __snapshots__
│ │ │ │ │ │ └── VideoUtil.spec.tsx.snap
│ │ │ │ │ └── index.ts
│ │ │ │ ├── about-shape-utils.md
│ │ │ │ ├── index.ts
│ │ │ │ └── shared
│ │ │ │ │ ├── LabelMask.tsx
│ │ │ │ │ ├── PolygonUtils.ts
│ │ │ │ │ ├── TextAreaUtils.ts
│ │ │ │ │ ├── TextLabel.tsx
│ │ │ │ │ ├── getBoundsRectangle.ts
│ │ │ │ │ ├── getTextAlign.ts
│ │ │ │ │ ├── getTextSize.ts
│ │ │ │ │ ├── getTextSvgElement.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── shape-styles.ts
│ │ │ │ │ ├── transformRectangle.ts
│ │ │ │ │ ├── transformSingleRectangle.ts
│ │ │ │ │ └── useTextKeyboardEvents.ts
│ │ │ └── tools
│ │ │ │ ├── ArrowTool
│ │ │ │ ├── ArrowTool.spec.ts
│ │ │ │ ├── ArrowTool.ts
│ │ │ │ └── index.ts
│ │ │ │ ├── BaseTool.ts
│ │ │ │ ├── DrawTool
│ │ │ │ ├── DrawTool.spec.ts
│ │ │ │ ├── DrawTool.ts
│ │ │ │ └── index.ts
│ │ │ │ ├── EllipseTool
│ │ │ │ ├── EllipseTool.spec.ts
│ │ │ │ ├── EllipseTool.ts
│ │ │ │ └── index.ts
│ │ │ │ ├── EraseTool
│ │ │ │ ├── EraseTool.spec.ts
│ │ │ │ ├── EraseTool.ts
│ │ │ │ └── index.ts
│ │ │ │ ├── LineTool
│ │ │ │ ├── LineTool.spec.ts
│ │ │ │ ├── LineTool.ts
│ │ │ │ └── index.ts
│ │ │ │ ├── RectangleTool
│ │ │ │ ├── RectangleTool.spec.ts
│ │ │ │ ├── RectangleTool.ts
│ │ │ │ └── index.ts
│ │ │ │ ├── SelectTool
│ │ │ │ ├── SelectTool.spec.ts
│ │ │ │ ├── SelectTool.ts
│ │ │ │ └── index.ts
│ │ │ │ ├── StickyTool
│ │ │ │ ├── StickyTool.spec.ts
│ │ │ │ ├── StickyTool.ts
│ │ │ │ └── index.ts
│ │ │ │ ├── TextTool
│ │ │ │ ├── TextTool.spec.ts
│ │ │ │ ├── TextTool.ts
│ │ │ │ └── index.ts
│ │ │ │ ├── TriangleTool
│ │ │ │ ├── TriangleTool.spec.ts
│ │ │ │ ├── TriangleTool.ts
│ │ │ │ └── index.ts
│ │ │ │ ├── about-tools.md
│ │ │ │ └── index.ts
│ │ ├── styles
│ │ │ ├── index.ts
│ │ │ └── stitches.config.ts
│ │ ├── test
│ │ │ ├── TldrawTestApp.tsx
│ │ │ ├── badDocument.spec.ts
│ │ │ ├── documents
│ │ │ │ ├── badDocument.ts
│ │ │ │ ├── old-doc-2.ts
│ │ │ │ └── old-doc.ts
│ │ │ ├── index.ts
│ │ │ ├── mockDocument.tsx
│ │ │ └── renderWithContext.tsx
│ │ └── types.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.dev.json
│ ├── tsconfig.json
│ └── tsconfig.tsbuildinfo
└── vec
│ ├── CHANGELOG.md
│ ├── LICENSE.md
│ ├── README.md
│ ├── package.json
│ ├── src
│ └── index.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.dev.json
│ └── tsconfig.json
├── setupTests.ts
├── tsconfig.base.json
├── tsconfig.json
├── turbo.json
└── yarn.lock
/.changeset/README.md:
--------------------------------------------------------------------------------
1 | # Changesets
2 |
3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4 | with multi-package repos, or single-package repos to help you version and publish your code. You can
5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6 |
7 | We have a quick list of common questions to get you started engaging with this project in
8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
9 |
--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@1.6.4/schema.json",
3 | "changelog": "@changesets/cli/changelog",
4 | "commit": false,
5 | "linked": [],
6 | "access": "public",
7 | "baseBranch": "main",
8 | "updateInternalDependencies": "patch",
9 | "ignore": []
10 | }
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/node_modules/*
2 | **/out/*
3 | **/.next/*
4 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "parser": "@typescript-eslint/parser",
4 | "plugins": ["@typescript-eslint"],
5 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
6 | "overrides": [
7 | {
8 | // enable the rule specifically for TypeScript files
9 | "files": ["*.ts", "*.tsx"],
10 | "rules": {
11 | "@typescript-eslint/explicit-module-boundary-types": [0]
12 | }
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [steveruizok]
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Report
3 | about: Writing and other documentation.
4 | title: '[bug] Bug description'
5 | labels: bug
6 | assignees: ''
7 | ---
8 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/documentation.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Documentation
3 | about: Writing and other documentation.
4 | title: '[documentation] Content'
5 | labels: documentation
6 | assignees: ''
7 | ---
8 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature
3 | about: Begin discussion of a new feature.
4 | title: '[feature] Feature or improvement'
5 | labels: feature
6 | assignees: ''
7 | ---
8 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/testing.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Testing
3 | about: Tests that need to be written.
4 | title: '[tests] Test'
5 | labels: testing
6 | assignees: ''
7 | ---
8 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on: push
3 | jobs:
4 | build:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - uses: actions/checkout@v2
8 |
9 | # turbo cache
10 | - name: Turbo Cache
11 | id: turbo-cache
12 | uses: actions/cache@v2
13 | with:
14 | path: node_modules/.cache/turbo
15 | key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ github.sha }}
16 | restore-keys: |
17 | turbo-${{ github.job }}-${{ github.ref_name }}-
18 |
19 | # install modules
20 | - name: Install modules
21 | run: yarn
22 |
23 | # build
24 | - name: Build Packages
25 | run: yarn build:packages --cache-dir=".turbo"
26 |
27 | # run unit tests
28 | - name: Jest Annotations & Coverage
29 | run: yarn test:ci
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | build/
3 | lib/
4 | dist/
5 | docs/
6 | .idea/*
7 |
8 | .DS_Store
9 | coverage
10 | *.log
11 |
12 | .vercel
13 | .next
14 | apps/www/public/workbox-*
15 | apps/www/public/worker-*
16 | apps/www/public/sw.js
17 | apps/www/public/sw.js.map
18 | .env
19 | firebase.config.*.turbo
20 | .turbo
21 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | yarn test
5 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | /.github/
2 | /.vscode/
3 | /node_modules/
4 | /build/
5 | /tmp/
6 | .idea/*
7 | /docs/
8 |
9 | coverage
10 | *.log
11 | .gitlab-ci.yml
12 |
13 | package-lock.json
14 | /*.tgz
15 | /tmp*
16 | /mnt/
17 | /package/
18 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "singleQuote": true,
4 | "semi": false,
5 | "printWidth": 100
6 | }
--------------------------------------------------------------------------------
/.turbo/config.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["esbenp.prettier-vscode", "tldraw-org.tldraw-vscode"]
3 | }
4 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "pwa-chrome",
9 | "request": "launch",
10 | "name": "Launch Chrome against localhost",
11 | "url": "http://localhost:8080",
12 | "webRoot": "${workspaceFolder}"
13 | }
14 | ]
15 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.tsdk": "node_modules/typescript/lib"
3 | }
4 |
--------------------------------------------------------------------------------
/.vscode/snippets.code-snippets:
--------------------------------------------------------------------------------
1 | {
2 | "createComment": {
3 | "scope": "typescript,typescriptreact",
4 | "prefix": "ccc",
5 | "body": [
6 | "/**",
7 | " * ${1:description}",
8 | " *",
9 | " * ### Example",
10 | " *",
11 | " *```ts",
12 | " * ${2:example}",
13 | " *```"
14 | ],
15 | "description": "comment"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/apps/electron/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |
4 |
5 | # @tlslides/electron
6 |
7 | An experimental electron wrapper for [tldraw](https://tldraw.com).
8 |
9 | Not yet distributed.
10 |
--------------------------------------------------------------------------------
/apps/electron/electron-esbuild.config.yaml:
--------------------------------------------------------------------------------
1 | mainConfig:
2 | type: esbuild
3 | path: esbuild.main.config.ts
4 | src: src/main/main.ts
5 | output: dist/main
6 | rendererConfig:
7 | type: esbuild
8 | path: esbuild.renderer.config.ts
9 | html: src/renderer/index.html
10 | src: src/renderer/index.tsx
11 | output: dist/renderer
12 |
--------------------------------------------------------------------------------
/apps/electron/esbuild.main.config.ts:
--------------------------------------------------------------------------------
1 | import { BuildOptions } from 'esbuild'
2 | import path from 'path'
3 |
4 | const config: BuildOptions = {
5 | platform: 'node',
6 | entryPoints: [path.resolve('src/main/main.ts'), path.resolve('src/main/preload.ts')],
7 | bundle: true,
8 | target: 'node16.5.0', // electron version target
9 | sourcemap: true,
10 | }
11 |
12 | export default config
13 |
--------------------------------------------------------------------------------
/apps/electron/esbuild.renderer.config.ts:
--------------------------------------------------------------------------------
1 | import { BuildOptions } from 'esbuild'
2 | import path from 'path'
3 |
4 | const config: BuildOptions = {
5 | platform: 'browser',
6 | entryPoints: [path.resolve('src/renderer/index.tsx')],
7 | bundle: true,
8 | target: 'chrome94', // electron version target
9 | sourcemap: true,
10 | }
11 |
12 | export default config
13 |
--------------------------------------------------------------------------------
/apps/electron/resources/entitlements.mac.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.cs.allow-jit
6 |
7 | com.apple.security.cs.allow-unsigned-executable-memory
8 |
9 | com.apple.security.cs.disable-library-validation
10 |
11 |
12 |
--------------------------------------------------------------------------------
/apps/electron/resources/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/apps/electron/resources/icon.icns
--------------------------------------------------------------------------------
/apps/electron/resources/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/apps/electron/resources/icon.ico
--------------------------------------------------------------------------------
/apps/electron/resources/notarize.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | require('dotenv').config()
4 | const { notarize } = require('electron-notarize')
5 |
6 | exports.default = async function notarizing(context) {
7 | const { electronPlatformName, appOutDir } = context
8 | if (electronPlatformName !== 'darwin') {
9 | return
10 | }
11 |
12 | const appName = context.packager.appInfo.productFilename
13 |
14 | return await notarize({
15 | appBundleId: 'com.tldraw.app',
16 | appPath: `${appOutDir}/${appName}.app`,
17 | appleId: process.env.APPLEID,
18 | appleIdPassword: process.env.APPLEIDPASS,
19 | })
20 | }
21 |
--------------------------------------------------------------------------------
/apps/electron/src/main/main.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-non-null-assertion */
2 | import { app, BrowserWindow } from 'electron'
3 | import { is } from 'electron-util'
4 | import type { Message } from 'src/types'
5 | import { createMenu } from './createMenu'
6 | import { createWindow } from './createWindow'
7 | import './preload'
8 |
9 | let win: BrowserWindow | null = null
10 |
11 | async function main() {
12 | win = await createWindow()
13 |
14 | async function send(message: Message) {
15 | win!.webContents.send('projectMsg', message)
16 | }
17 |
18 | await createMenu(send)
19 | }
20 |
21 | app
22 | .on('ready', main)
23 | .on('window-all-closed', () => {
24 | if (!is.macos) {
25 | app.quit()
26 | }
27 | })
28 | .on('activate', () => {
29 | if (win === null && app.isReady()) {
30 | main()
31 | }
32 | })
33 |
--------------------------------------------------------------------------------
/apps/electron/src/main/preload.ts:
--------------------------------------------------------------------------------
1 | import { contextBridge, ipcRenderer } from 'electron'
2 | import type { Message, TldrawBridgeApi } from 'src/types'
3 |
4 | const api: TldrawBridgeApi = {
5 | send: (channel: string, data: Message) => {
6 | ipcRenderer.send(channel, data)
7 | },
8 | on: (channel, cb) => {
9 | ipcRenderer.on(channel, (event, message) => cb(message as Message))
10 | },
11 | }
12 |
13 | contextBridge?.exposeInMainWorld('TldrawBridgeApi', api)
14 |
15 | export {}
16 |
--------------------------------------------------------------------------------
/apps/electron/src/renderer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/apps/electron/src/renderer/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import App from './app'
4 | import './styles.css'
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById('root')
11 | )
12 |
--------------------------------------------------------------------------------
/apps/electron/src/renderer/styles.css:
--------------------------------------------------------------------------------
1 | html,
2 | * {
3 | box-sizing: border-box;
4 | }
5 |
6 | body {
7 | overscroll-behavior: none;
8 | margin: 0px;
9 | padding: 0px;
10 | }
11 |
12 | .tldraw {
13 | position: fixed;
14 | top: 0px;
15 | left: 0px;
16 | right: 0px;
17 | bottom: 0px;
18 | width: 100%;
19 | height: 100%;
20 | }
21 |
--------------------------------------------------------------------------------
/apps/electron/src/types.ts:
--------------------------------------------------------------------------------
1 | export type Message =
2 | | { type: 'zoomIn' }
3 | | { type: 'zoomOut' }
4 | | { type: 'resetZoom' }
5 | | { type: 'zoomToFit' }
6 | | { type: 'zoomToSelection' }
7 | | { type: 'undo' }
8 | | { type: 'redo' }
9 | | { type: 'cut' }
10 | | { type: 'copy' }
11 | | { type: 'paste' }
12 | | { type: 'delete' }
13 | | { type: 'selectAll' }
14 | | { type: 'selectNone' }
15 |
16 | export type TldrawBridgeApi = {
17 | send: (channel: string, data: Message) => void
18 | on: (channel: string, cb: (message: Message) => void) => void
19 | }
20 |
--------------------------------------------------------------------------------
/apps/electron/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "include": ["src"],
4 | "exclude": ["node_modules", "dist", "docs"],
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": ".",
8 | "baseUrl": ".",
9 | "allowJs": false,
10 | "emitDeclarationOnly": true,
11 | "paths": {
12 | "@tlslides/tldraw": ["../../packages/tldraw"]
13 | }
14 | },
15 | "references": [
16 | {
17 | "path": "../../packages/tldraw"
18 | }
19 | ],
20 | "typedocOptions": {
21 | "entryPoints": ["src/index.ts"],
22 | "out": "docs"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/apps/vscode/editor/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @tlslides/vscode-editor
2 |
3 | ## 1.7.1
4 |
5 | ### Patch Changes
6 |
7 | - Fix bug with missing parents / children.
8 |
--------------------------------------------------------------------------------
/apps/vscode/editor/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |
4 |
5 | # @tlslides/vscode-editor
6 |
7 | The app for the tldraw VS Code Extension.
8 |
9 | See the README at `vscode` for more about this project.
10 |
--------------------------------------------------------------------------------
/apps/vscode/editor/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@tlslides/vscode-editor",
3 | "version": "1.7.1",
4 | "private": true,
5 | "description": "An an editor for the tldraw vscode extension.",
6 | "author": "@steveruizok",
7 | "license": "MIT",
8 | "keywords": [
9 | "react",
10 | "typescript",
11 | "esbuild"
12 | ],
13 | "scripts": {
14 | "start:vscode": "node scripts/dev.mjs -w",
15 | "build": "node scripts/build.mjs",
16 | "build:apps": "yarn build"
17 | },
18 | "devDependencies": {
19 | "@tlslides/tldraw": "*",
20 | "@types/node": "^17.0.14",
21 | "@types/react": "^17.0.38",
22 | "@types/react-dom": "^17.0.11",
23 | "@types/react-router-dom": "^5.1.8",
24 | "concurrently": "7.0.0",
25 | "create-serve": "1.0.1",
26 | "esbuild": "^0.14.18",
27 | "esbuild-serve": "^1.0.1",
28 | "react": ">=16.8",
29 | "react-dom": "^16.8 || ^17.0",
30 | "rimraf": "3.0.2",
31 | "tslib": "^2.3.1",
32 | "typescript": "4.5.5"
33 | },
34 | "gitHead": "a7dac0f83ad998e205c2aab58182cb4ba4e099a6"
35 | }
36 |
--------------------------------------------------------------------------------
/apps/vscode/editor/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import App from './app'
4 | import './styles.css'
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById('root')
11 | )
12 |
--------------------------------------------------------------------------------
/apps/vscode/editor/src/styles.css:
--------------------------------------------------------------------------------
1 | html,
2 | * {
3 | box-sizing: border-box;
4 | }
5 |
6 | body {
7 | overscroll-behavior: none;
8 | margin: 0px;
9 | padding: 0px;
10 | }
11 |
12 | .tldraw {
13 | position: fixed;
14 | top: 0px;
15 | left: 0px;
16 | right: 0px;
17 | bottom: 0px;
18 | width: 100%;
19 | height: 100%;
20 | }
21 |
--------------------------------------------------------------------------------
/apps/vscode/editor/src/types.ts:
--------------------------------------------------------------------------------
1 | export type MessageFromWebview = {
2 | type: 'editorUpdated'
3 | text: string
4 | }
5 |
6 | export type MessageFromExtension =
7 | | {
8 | type: 'openedFile'
9 | text: string
10 | }
11 | | {
12 | type: 'fileSaved'
13 | text: string
14 | }
15 |
--------------------------------------------------------------------------------
/apps/vscode/editor/src/utils/defaultDocument.ts:
--------------------------------------------------------------------------------
1 | import { TDDocument, TldrawApp } from '@tlslides/tldraw'
2 |
3 | export const defaultDocument: TDDocument = {
4 | id: 'doc',
5 | name: 'New Document',
6 | version: TldrawApp.version,
7 | pages: {
8 | page: {
9 | id: 'page',
10 | name: 'Page 1',
11 | childIndex: 1,
12 | shapes: {},
13 | bindings: {},
14 | },
15 | },
16 | pageStates: {
17 | page: {
18 | id: 'page',
19 | selectedIds: [],
20 | camera: {
21 | point: [0, 0],
22 | zoom: 1,
23 | },
24 | },
25 | },
26 | }
27 |
--------------------------------------------------------------------------------
/apps/vscode/editor/src/utils/export.ts:
--------------------------------------------------------------------------------
1 | import { TDExport } from '@tlslides/tldraw'
2 |
3 | export const EXPORT_ENDPOINT =
4 | process.env.NODE_ENV === 'development'
5 | ? 'http://localhost:3000/api/export'
6 | : 'https://www.tldraw.com/api/export'
7 |
8 | export async function exportToImage(info: TDExport) {
9 | if (info.serialized) {
10 | const link = document.createElement('a')
11 | link.href = 'data:text/plain;charset=utf-8,' + encodeURIComponent(info.serialized)
12 | link.download = info.name + '.' + info.type
13 | link.click()
14 |
15 | return
16 | }
17 |
18 | const response = await fetch(EXPORT_ENDPOINT, {
19 | method: 'POST',
20 | headers: { 'Content-Type': 'application/json' },
21 | body: JSON.stringify(info),
22 | })
23 | const blob = await response.blob()
24 | const blobUrl = URL.createObjectURL(blob)
25 | const link = document.createElement('a')
26 | link.href = blobUrl
27 | link.download = info.name + '.' + info.type
28 | link.click()
29 | }
30 |
--------------------------------------------------------------------------------
/apps/vscode/editor/src/utils/vscode.ts:
--------------------------------------------------------------------------------
1 | import type { MessageFromWebview } from '../types'
2 |
3 | // Will be placed in global scope by extension
4 | declare function acquireVsCodeApi(): {
5 | postMessage(options: MessageFromWebview): void
6 | }
7 |
8 | export const vscode = acquireVsCodeApi()
9 |
--------------------------------------------------------------------------------
/apps/vscode/editor/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig.base.json",
3 | "include": ["src"],
4 | "exclude": ["node_modules", "dist", "docs"],
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "rootDir": "src",
8 | "baseUrl": "src",
9 | "emitDeclarationOnly": false,
10 | "paths": {
11 | "@tlslides/tldraw": ["../../../packages/tldraw"]
12 | }
13 | },
14 | "references": [
15 | {
16 | "path": "../../../packages/tldraw"
17 | }
18 | ],
19 | "typedocOptions": {
20 | "entryPoints": ["src/index.ts"],
21 | "out": "docs"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/apps/vscode/extension/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | editor
3 | node_modules
4 | .vscode-test-web/
5 | *.vsix
6 | .DS_Store
--------------------------------------------------------------------------------
/apps/vscode/extension/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // See http://go.microsoft.com/fwlink/?LinkId=827846
3 | // for the documentation about the extensions.json format
4 | "recommendations": ["dbaeumer.vscode-eslint", "amodio.tsl-problem-matcher"]
5 | }
6 |
--------------------------------------------------------------------------------
/apps/vscode/extension/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | // A launch configuration that compiles the extension and then opens it inside a new window
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | {
6 | "version": "0.2.0",
7 | "configurations": [
8 | {
9 | "name": "Run Web Extension ",
10 | "type": "pwa-extensionHost",
11 | "request": "launch",
12 | "program": "${workspaceFolder}/src/extension.ts",
13 | "cwd": "${workspaceFolder}",
14 | "args": [
15 | "--disable-extensions",
16 | "--extensionDevelopmentPath=${workspaceFolder}",
17 | "${workspaceFolder}/src/extension.ts"
18 | ],
19 | "outFiles": [
20 | "${workspaceFolder}/dist/web/**/*.js",
21 | "!**/node_modules/**"
22 | ],
23 | "skipFiles": [
24 | "/**",
25 | "**/node_modules/**"
26 | ],
27 | "sourceMaps": true,
28 | }
29 | ]
30 | }
--------------------------------------------------------------------------------
/apps/vscode/extension/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | "files.exclude": {
4 | "out": false // set this to true to hide the "out" folder with the compiled JS files
5 | },
6 | "search.exclude": {
7 | "out": true // set this to false to include "out" folder in search results
8 | },
9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts
10 | "typescript.tsc.autoDetect": "off"
11 | }
12 |
--------------------------------------------------------------------------------
/apps/vscode/extension/.vscodeignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/apps/vscode/extension/.vscodeignore
--------------------------------------------------------------------------------
/apps/vscode/extension/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 1.2.4
2 |
3 | ## 1.8.4
4 |
5 | ### Patch Changes
6 |
7 | - Fix bug with missing parents / children.
8 |
9 | - Fixed bug that prevented saving.
10 |
11 | ## 1.1.9
12 |
13 | - Updates READMEs.
14 |
15 | ## 1.1.6
16 |
17 | - Fixes bugs in VS Code extension.
18 |
19 | ## 0.1.23
20 |
21 | - Fixing bugs related to saving files.
22 |
23 | ## 0.1.0
24 |
25 | - Launched!
26 |
--------------------------------------------------------------------------------
/apps/vscode/extension/assets/recording.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/apps/vscode/extension/assets/recording.gif
--------------------------------------------------------------------------------
/apps/vscode/extension/assets/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/apps/vscode/extension/assets/screenshot.png
--------------------------------------------------------------------------------
/apps/vscode/extension/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/apps/vscode/extension/icon.png
--------------------------------------------------------------------------------
/apps/vscode/extension/scripts/build.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | const fs = require('fs')
3 | const esbuild = require('esbuild')
4 |
5 | const { log: jslog } = console
6 |
7 | async function main() {
8 | if (fs.existsSync('./dist')) {
9 | fs.rmSync('./dist', { recursive: true }, (e) => {
10 | if (e) {
11 | throw e
12 | }
13 | })
14 | }
15 |
16 | try {
17 | esbuild.buildSync({
18 | entryPoints: ['./src/extension.ts'],
19 | outdir: 'dist/web',
20 | minify: false,
21 | bundle: true,
22 | format: 'cjs',
23 | target: 'es6',
24 | define: {
25 | 'process.env.NODE_ENV': '"production"',
26 | },
27 | tsconfig: './tsconfig.json',
28 | external: ['vscode'],
29 | })
30 | jslog(`Built package.`)
31 | } catch (e) {
32 | jslog(`× Build failed due to an error.`)
33 | jslog(e)
34 | }
35 | }
36 |
37 | main()
38 |
--------------------------------------------------------------------------------
/apps/vscode/extension/src/extension.ts:
--------------------------------------------------------------------------------
1 | import * as vscode from 'vscode'
2 | import { TldrawEditorProvider } from './TldrawEditorProvider'
3 |
4 | // When a .tldr is first opened or created, activate the extension.
5 | export function activate(context: vscode.ExtensionContext) {
6 | try {
7 | context.subscriptions.push(TldrawEditorProvider.register(context))
8 | } catch (e) {
9 | console.error(e)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/apps/vscode/extension/src/types.ts:
--------------------------------------------------------------------------------
1 | export type MessageFromWebview = {
2 | type: 'editorUpdated'
3 | text: string
4 | }
5 |
6 | export type MessageFromExtension =
7 | | {
8 | type: 'openedFile'
9 | text: string
10 | }
11 | | {
12 | type: 'fileSaved'
13 | text: string
14 | }
15 |
--------------------------------------------------------------------------------
/apps/vscode/extension/src/utils.ts:
--------------------------------------------------------------------------------
1 | export function getNonce() {
2 | let text = ''
3 | const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
4 | for (let i = 0; i < 32; i++) {
5 | text += possible.charAt(Math.floor(Math.random() * possible.length))
6 | }
7 | return text
8 | }
9 |
--------------------------------------------------------------------------------
/apps/vscode/extension/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es6",
5 | "outDir": "dist",
6 | "lib": ["es6", "WebWorker"],
7 | "sourceMap": true,
8 | "rootDir": "src",
9 | "strict": true
10 | },
11 | "exclude": ["node_modules", ".vscode-test-web"]
12 | }
13 |
--------------------------------------------------------------------------------
/apps/www/.babelrc:
--------------------------------------------------------------------------------
1 | // This file isn't doing much; however it cannot be removed, or else
2 | // the build will fail due to the fact that we're targeting ES6.
3 |
4 | {
5 | "presets": [
6 | [
7 | "next/babel",
8 | {
9 | "preset-env": { "targets": { "node": true } }
10 | }
11 | ]
12 | ],
13 | "plugins": []
14 | }
15 |
--------------------------------------------------------------------------------
/apps/www/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/apps/www/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
33 | # vercel
34 | .vercel
35 |
--------------------------------------------------------------------------------
/apps/www/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @tlslides/www
2 |
3 | ## 1.6.2
4 |
5 | ### Patch Changes
6 |
7 | - Fix publish version.
8 | - Updated dependencies
9 | - @tlslides/core@1.9.1
10 | - @tlslides/tldraw@1.9.1
11 |
--------------------------------------------------------------------------------
/apps/www/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |
4 |
5 | # @tlslides/www
6 |
7 | The [tldraw](https://tldraw.com) website.
8 |
--------------------------------------------------------------------------------
/apps/www/hooks/useAccountHandlers.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { signIn, signOut } from 'next-auth/react'
3 |
4 | export function useAccountHandlers() {
5 | const onSignIn = React.useCallback(() => {
6 | signIn()
7 | }, [])
8 |
9 | const onSignOut = React.useCallback(() => {
10 | signOut()
11 | }, [])
12 |
13 | return { onSignIn, onSignOut }
14 | }
15 |
--------------------------------------------------------------------------------
/apps/www/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/apps/www/pages/api/upload.ts:
--------------------------------------------------------------------------------
1 | import aws from 'aws-sdk'
2 |
3 | export default async function handler(req, res) {
4 | aws.config.update({
5 | accessKeyId: process.env.TL_AWS_ACCESS_KEY,
6 | secretAccessKey: process.env.TL_AWS_SECRET_KEY,
7 | region: process.env.TL_AWS_REGION,
8 | signatureVersion: 'v4',
9 | })
10 |
11 | const s3 = new aws.S3()
12 |
13 | const post = s3.createPresignedPost({
14 | Bucket: process.env.TL_AWS_BUCKET_NAME,
15 | Fields: {
16 | key: req.query.file,
17 | 'Content-Type': req.query.fileType,
18 | },
19 | Expires: 60, // seconds
20 | Conditions: [
21 | ['content-length-range', 0, 5242880], // up to 5 MB
22 | ],
23 | })
24 |
25 | res.status(200).json(post)
26 | }
27 |
--------------------------------------------------------------------------------
/apps/www/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import dynamic from 'next/dynamic'
2 | import Head from 'next/head'
3 | import { useRouter } from 'next/router'
4 | import { useMemo } from 'react'
5 |
6 | const Editor = dynamic(() => import('components/Editor'), { ssr: false })
7 |
8 | export default function Home(): JSX.Element {
9 | const { query } = useRouter()
10 | const isExportMode = useMemo(() => 'exportMode' in query, [query])
11 |
12 | return (
13 | <>
14 |
15 | tlslides
16 |
17 |
18 | >
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/apps/www/pages/r/[id].tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import type { GetServerSideProps } from 'next'
3 | import { getSession } from 'next-auth/react'
4 | import dynamic from 'next/dynamic'
5 | const MultiplayerEditor = dynamic(() => import('components/MultiplayerEditor'), { ssr: false })
6 |
7 | interface RoomProps {
8 | id: string
9 | isSponsor: boolean
10 | isUser: boolean
11 | }
12 |
13 | export default function Room({ id, isUser, isSponsor }: RoomProps): JSX.Element {
14 | return
15 | }
16 |
17 | export const getServerSideProps: GetServerSideProps = async (context) => {
18 | const session = await getSession(context)
19 | const id = context.query.id?.toString()
20 | return {
21 | props: {
22 | id,
23 | isUser: session?.user ? true : false,
24 | isSponsor: session?.isSponsor ?? false,
25 | },
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/apps/www/pages/r/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import type { GetServerSideProps } from 'next'
3 | import Head from 'next/head'
4 |
5 | export default function RandomRoomPage(): JSX.Element {
6 | return (
7 | <>
8 |
9 | tldraw
10 |
11 | >
12 | )
13 | }
14 |
15 | export const getServerSideProps: GetServerSideProps = async (context) => {
16 | // Generate random id
17 | const id = Date.now().toString()
18 |
19 | // Route to a room with that id
20 | context.res.setHeader('Location', `/r/${id}`)
21 | context.res.statusCode = 307
22 |
23 | // Return id (though it shouldn't matter)
24 | return {
25 | props: {},
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/apps/www/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/apps/www/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/apps/www/public/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/apps/www/public/android-chrome-512x512.png
--------------------------------------------------------------------------------
/apps/www/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/apps/www/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/apps/www/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/apps/www/public/favicon-16x16.png
--------------------------------------------------------------------------------
/apps/www/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/apps/www/public/favicon-32x32.png
--------------------------------------------------------------------------------
/apps/www/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/apps/www/public/favicon.ico
--------------------------------------------------------------------------------
/apps/www/public/flat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/apps/www/public/flat.png
--------------------------------------------------------------------------------
/apps/www/public/images/hello.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/apps/www/public/images/hello.mp4
--------------------------------------------------------------------------------
/apps/www/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tlslides",
3 | "short_name": "tlslides",
4 | "icons": [
5 | {
6 | "src": "/android-chrome-512x512.png",
7 | "sizes": "512x512",
8 | "type": "image/png",
9 | "purpose": "any"
10 | },
11 | {
12 | "src": "/android-chrome-192x192.png",
13 | "sizes": "192x192",
14 | "type": "image/png",
15 | "purpose": "any"
16 | }
17 | ],
18 | "theme_color": "#ffffff",
19 | "background_color": "#ffffff",
20 | "start_url": "/",
21 | "display": "standalone",
22 | "orientation": "portrait"
23 | }
24 |
--------------------------------------------------------------------------------
/apps/www/public/social-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/apps/www/public/social-image.png
--------------------------------------------------------------------------------
/apps/www/styles/index.ts:
--------------------------------------------------------------------------------
1 | export * from './stitches.config'
2 |
--------------------------------------------------------------------------------
/apps/www/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": false,
4 | "incremental": false,
5 | "resolveJsonModule": true,
6 | "target": "es6",
7 | "lib": ["dom", "esnext"],
8 | "allowJs": true,
9 | "skipLibCheck": true,
10 | "strict": false,
11 | "forceConsistentCasingInFileNames": true,
12 | "noEmit": true,
13 | "emitDeclarationOnly": false,
14 | "esModuleInterop": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "isolatedModules": true,
18 | "jsx": "preserve",
19 | "rootDir": ".",
20 | "baseUrl": ".",
21 | "paths": {
22 | "*": ["./*"],
23 | "@tlslides/core": ["../../packages/core"],
24 | "@tlslides/tldraw": ["../../packages/tldraw"]
25 | }
26 | },
27 | "references": [
28 | { "path": "../../packages/vec" },
29 | { "path": "../../packages/intersect" },
30 | { "path": "../../packages/core" },
31 | { "path": "../../packages/tldraw" }
32 | ],
33 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
34 | "exclude": ["node_modules"]
35 | }
36 |
--------------------------------------------------------------------------------
/apps/www/types.ts:
--------------------------------------------------------------------------------
1 | export {}
2 |
--------------------------------------------------------------------------------
/apps/www/utils/export.ts:
--------------------------------------------------------------------------------
1 | import { TDExport } from '@tlslides/tldraw'
2 |
3 | export const EXPORT_ENDPOINT =
4 | process.env.NODE_ENV === 'development'
5 | ? 'http://localhost:3000/api/export'
6 | : 'https://www.tldraw.com/api/export'
7 |
8 | export async function exportToImage(info: TDExport) {
9 | if (info.serialized) {
10 | const link = document.createElement('a')
11 | link.href = 'data:text/plain;charset=utf-8,' + encodeURIComponent(info.serialized)
12 | link.download = info.name + '.' + info.type
13 | link.click()
14 |
15 | return
16 | }
17 |
18 | const response = await fetch(EXPORT_ENDPOINT, {
19 | method: 'POST',
20 | headers: { 'Content-Type': 'application/json' },
21 | body: JSON.stringify(info),
22 | })
23 | const blob = await response.blob()
24 | const blobUrl = URL.createObjectURL(blob)
25 | const link = document.createElement('a')
26 | link.href = blobUrl
27 | link.download = info.name + '.' + info.type
28 | link.click()
29 | }
30 |
--------------------------------------------------------------------------------
/apps/www/utils/gtag.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-explicit-any */
2 | export const GA_TRACKING_ID = process.env.GA_MEASUREMENT_ID
3 |
4 | type GTagEvent = {
5 | action: string
6 | category: string
7 | label: string
8 | value: number
9 | }
10 |
11 | export const pageview = (url: URL): void => {
12 | if ('gtag' in window) {
13 | const win = window as any
14 | win?.gtag('config', GA_TRACKING_ID, {
15 | page_path: url,
16 | })
17 | }
18 | }
19 |
20 | export const event = ({ action, category, label, value }: GTagEvent): void => {
21 | if ('gtag' in window) {
22 | const win = window as any
23 | win?.gtag('event', action, {
24 | event_category: category,
25 | event_label: label,
26 | value: value,
27 | })
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/apps/www/utils/useGtag.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2 | import router from 'next/router'
3 | import { useEffect } from 'react'
4 | import * as gtag from 'utils/gtag'
5 |
6 | function handleRouteChange(url: URL) {
7 | gtag.pageview(url)
8 | }
9 |
10 | export default function useGtag() {
11 | useEffect(() => {
12 | if (process.env.NODE_ENV !== 'production') return
13 |
14 | router.events.on('routeChangeComplete', handleRouteChange)
15 |
16 | return () => {
17 | router.events.off('routeChangeComplete', handleRouteChange)
18 | }
19 | }, [])
20 | }
21 |
--------------------------------------------------------------------------------
/apps/www/worker/index.js:
--------------------------------------------------------------------------------
1 | self.__WB_DISABLE_DEV_LOGS = true
2 |
--------------------------------------------------------------------------------
/assets/card-repo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/assets/card-repo.png
--------------------------------------------------------------------------------
/assets/icon_flat_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/assets/icon_flat_black.png
--------------------------------------------------------------------------------
/assets/icon_flat_black_on_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/assets/icon_flat_black_on_white.png
--------------------------------------------------------------------------------
/assets/icon_flat_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/assets/icon_flat_white.png
--------------------------------------------------------------------------------
/assets/icon_flat_white_on_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/assets/icon_flat_white_on_black.png
--------------------------------------------------------------------------------
/assets/oppizi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/assets/oppizi.png
--------------------------------------------------------------------------------
/assets/recording.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/assets/recording.gif
--------------------------------------------------------------------------------
/assets/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/assets/screenshot.png
--------------------------------------------------------------------------------
/assets/tldraw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/assets/tldraw.png
--------------------------------------------------------------------------------
/examples/core-example-advanced/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |
4 |
5 | # @tlslides/core-example-advanced
6 |
7 | An advanced example project for `@tlslides/core`.
8 |
9 | To start this project:
10 |
11 | - run `yarn start:core` from the repository's root directory
12 | - open `http://localhost:5420` in your browser
13 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/components/TitleLinks.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { GitHub } from 'react-feather'
3 | import styled from 'stitches.config'
4 |
5 | export function TitleLinks() {
6 | return (
7 |
8 | @tlslides/core
9 |
10 | )
11 | }
12 |
13 | const TitleLinksContainer = styled('div', {
14 | position: 'fixed',
15 | top: 0,
16 | left: 0,
17 | width: '100%',
18 | display: 'flex',
19 | alignItems: 'center',
20 | justifyContent: 'center',
21 | zIndex: 100,
22 | fontSize: '$3',
23 |
24 | '& > a': {
25 | color: '$text',
26 | textDecoration: 'none',
27 | padding: '$2 $4',
28 | fontWeight: '$2',
29 | pointerEvents: 'all',
30 | '&:hover': {
31 | textDecoration: 'underline',
32 | },
33 | },
34 | })
35 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | tldraw
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import App from './app'
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | )
11 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/shapes/CustomShapeUtil.ts:
--------------------------------------------------------------------------------
1 | import { TLBounds, TLShape, TLShapeUtil } from '@tlslides/core'
2 |
3 | export abstract class CustomShapeUtil<
4 | T extends TLShape,
5 | E extends Element = Element
6 | > extends TLShapeUtil {
7 | /* ----------------- Custom Methods ----------------- */
8 |
9 | canBind = false
10 |
11 | hideBounds = false
12 |
13 | abstract getCenter: (shape: T) => number[]
14 |
15 | abstract getShape: (shape: Partial) => T
16 |
17 | abstract transform: (shape: T, bounds: TLBounds, initialShape: T, scale: number[]) => void
18 |
19 | abstract hitTestPoint: (shape: T, point: number[]) => boolean
20 |
21 | abstract hitTestLineSegment: (shape: T, A: number[], B: number[]) => boolean
22 | }
23 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/shapes/arrow/ArrowIndicator.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { TLShapeUtil } from '@tlslides/core'
3 | import type { ArrowShape } from './ArrowShape'
4 | import Vec from '@tlslides/vec'
5 |
6 | export const ArrowIndicator = TLShapeUtil.Indicator(({ shape }) => {
7 | const { start, end } = shape.handles
8 |
9 | const u = Vec.uni(Vec.sub(end.point, start.point))
10 | const dist = Vec.dist(end.point, start.point)
11 | const length = Math.min(18, dist / 2)
12 | const ahLeft = Vec.rotWith(Vec.sub(end.point, Vec.mul(u, length)), end.point, -Math.PI / 6)
13 | const ahRight = Vec.rotWith(Vec.sub(end.point, Vec.mul(u, length)), end.point, Math.PI / 6)
14 |
15 | return (
16 |
24 | )
25 | })
26 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/shapes/arrow/ArrowShape.ts:
--------------------------------------------------------------------------------
1 | import type { TLShape } from '@tlslides/core'
2 |
3 | export interface ArrowShape extends TLShape {
4 | type: 'arrow'
5 | handles: {
6 | start: {
7 | id: 'start'
8 | index: number
9 | point: number[]
10 | }
11 | end: {
12 | id: 'end'
13 | index: number
14 | point: number[]
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/shapes/arrow/index.ts:
--------------------------------------------------------------------------------
1 | export * from './ArrowShape'
2 | export * from './ArrowUtil'
3 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/shapes/box/BoxComponent.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { TLShapeUtil, SVGContainer } from '@tlslides/core'
3 | import type { BoxShape } from './BoxShape'
4 |
5 | export const BoxComponent = TLShapeUtil.Component(
6 | ({ shape, events, isGhost, meta }, ref) => {
7 | const color = meta.isDarkMode ? 'white' : 'black'
8 |
9 | return (
10 |
11 |
22 |
23 | )
24 | }
25 | )
26 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/shapes/box/BoxIndicator.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { TLShapeUtil } from '@tlslides/core'
3 | import type { BoxShape } from './BoxShape'
4 |
5 | export const BoxIndicator = TLShapeUtil.Indicator(({ shape }) => {
6 | return (
7 |
16 | )
17 | })
18 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/shapes/box/BoxShape.ts:
--------------------------------------------------------------------------------
1 | import type { TLShape } from '@tlslides/core'
2 |
3 | export interface BoxShape extends TLShape {
4 | type: 'box'
5 | size: number[]
6 | }
7 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/shapes/box/index.ts:
--------------------------------------------------------------------------------
1 | export * from './BoxShape'
2 | export * from './BoxUtil'
3 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/shapes/index.ts:
--------------------------------------------------------------------------------
1 | import type { CustomShapeUtil } from './CustomShapeUtil'
2 | import { ArrowShape, ArrowUtil } from './arrow'
3 | import { BoxShape, BoxUtil } from './box'
4 | import { PencilShape, PencilUtil } from './pencil'
5 |
6 | export * from './arrow'
7 | export * from './pencil'
8 | export * from './box'
9 |
10 | export type Shape = BoxShape | ArrowShape | PencilShape
11 |
12 | export const shapeUtils = {
13 | box: new BoxUtil(),
14 | arrow: new ArrowUtil(),
15 | pencil: new PencilUtil(),
16 | }
17 |
18 | export const getShapeUtils = (shape: T | T['type']) => {
19 | if (typeof shape === 'string') return shapeUtils[shape] as unknown as CustomShapeUtil
20 | return shapeUtils[shape.type] as unknown as CustomShapeUtil
21 | }
22 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/shapes/pencil/PencilComponent.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { TLShapeUtil, SVGContainer } from '@tlslides/core'
3 | import type { PencilShape } from './PencilShape'
4 | import { getComponentSvgPath } from './pencil-helpers'
5 |
6 | export const PencilComponent = TLShapeUtil.Component(
7 | ({ shape, events, isGhost, meta }, ref) => {
8 | const color = meta.isDarkMode ? 'white' : 'black'
9 | const pathData = getComponentSvgPath(shape.points)
10 | return (
11 |
12 |
13 |
14 |
15 | )
16 | }
17 | )
18 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/shapes/pencil/PencilShape.ts:
--------------------------------------------------------------------------------
1 | import type { TLShape } from '@tlslides/core'
2 |
3 | export interface PencilShape extends TLShape {
4 | type: 'pencil'
5 | points: number[][]
6 | }
7 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/shapes/pencil/PenclIndicator.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { TLShapeUtil } from '@tlslides/core'
3 | import type { PencilShape } from './PencilShape'
4 | import { getIndicatorSvgPath } from './pencil-helpers'
5 |
6 | export const PencilIndicator = TLShapeUtil.Indicator(({ shape }) => {
7 | return (
8 |
16 | )
17 | })
18 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/shapes/pencil/index.ts:
--------------------------------------------------------------------------------
1 | export * from './PencilShape'
2 | export * from './PencilUtil'
3 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/bindings/createBindings.ts:
--------------------------------------------------------------------------------
1 | import type { TLBinding } from '@tlslides/core'
2 | import { nanoid } from 'nanoid'
3 | import type { Action, CustomBinding } from 'state/constants'
4 |
5 | export const createBindings: Action = (
6 | data,
7 | payload: {
8 | bindings: (Partial & Pick)[]
9 | }
10 | ) => {
11 | payload.bindings.forEach((partial) => {
12 | const binding = {
13 | id: nanoid(),
14 | ...partial,
15 | }
16 |
17 | data.page.bindings[binding.id] = binding
18 | })
19 | }
20 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/bindings/deleteBindings.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 |
3 | export const deleteBindings: Action = (data, payload: { ids: string[] }) => {
4 | try {
5 | payload.ids.forEach((id, i) => {
6 | delete data.page.bindings[id]
7 | })
8 | } catch (e: any) {
9 | e.message = 'Could not delete bindings: ' + e.message
10 | console.error(e)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/bindings/index.ts:
--------------------------------------------------------------------------------
1 | export * from './updateBoundShapes'
2 | export * from './createBindings'
3 | export * from './removePartialBindings'
4 | export * from './updateBindings'
5 | export * from './deleteBindings'
6 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/bindings/removePartialBindings.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 |
3 | // Remove bindings from selected shapes to shapes that aren't also selected
4 | export const removePartialBindings: Action = (data) => {
5 | const { selectedIds } = data.pageState
6 |
7 | const bindings = Object.values(data.page.bindings)
8 |
9 | bindings
10 | .filter((binding) => selectedIds.includes(binding.fromId))
11 | .forEach((binding) => {
12 | if (!selectedIds.includes(binding.toId)) {
13 | delete data.page.bindings[binding.id]
14 | }
15 | })
16 | }
17 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/bindings/updateBindings.ts:
--------------------------------------------------------------------------------
1 | import type { TLBinding } from '@tlslides/core'
2 | import type { Action } from 'state/constants'
3 |
4 | export const updateBindings: Action = (
5 | data,
6 | payload: { bindings: (Partial & Pick)[] }
7 | ) => {
8 | try {
9 | payload.bindings.forEach((partial, i) => {
10 | Object.assign(data.page.bindings[partial.id], partial)
11 | })
12 | } catch (e: any) {
13 | e.message = 'Could not update shapes: ' + e.message
14 | console.error(e)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/camera/index.ts:
--------------------------------------------------------------------------------
1 | export * from './panCamera'
2 | export * from './pinchCamera'
3 | export * from './zoomIn'
4 | export * from './zoomOut'
5 | export * from './zoomToSelection'
6 | export * from './zoomToFit'
7 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/camera/panCamera.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 | import type { TLPointerInfo } from '@tlslides/core'
3 | import Vec from '@tlslides/vec'
4 |
5 | export const panCamera: Action = (data, payload: TLPointerInfo) => {
6 | const { point, zoom } = data.pageState.camera
7 | data.pageState.camera.point = Vec.sub(point, Vec.div(payload.delta, zoom))
8 | }
9 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/camera/pinchCamera.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 | import type { TLPointerInfo } from '@tlslides/core'
3 | import Vec from '@tlslides/vec'
4 |
5 | export const pinchCamera: Action = (data, payload: TLPointerInfo) => {
6 | const { camera } = data.pageState
7 | const nextZoom = payload.delta[2]
8 | const nextPoint = Vec.sub(camera.point, Vec.div(payload.delta, camera.zoom))
9 | const p0 = Vec.sub(Vec.div(payload.point, camera.zoom), nextPoint)
10 | const p1 = Vec.sub(Vec.div(payload.point, nextZoom), nextPoint)
11 | data.pageState.camera.point = Vec.toFixed(Vec.add(nextPoint, Vec.sub(p1, p0)))
12 | data.pageState.camera.zoom = nextZoom
13 | }
14 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/camera/zoomIn.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 | import { Utils } from '@tlslides/core'
3 | import Vec from '@tlslides/vec'
4 | import { mutables } from 'state/mutables'
5 |
6 | export const zoomIn: Action = (data) => {
7 | const { camera } = data.pageState
8 | const i = Math.round((data.pageState.camera.zoom * 100) / 25)
9 | const zoom = Math.min(5, (i + 1) * 0.25)
10 | const center = [mutables.rendererBounds.width / 2, mutables.rendererBounds.height / 2]
11 | const p0 = Vec.sub(Vec.div(center, camera.zoom), center)
12 | const p1 = Vec.sub(Vec.div(center, zoom), center)
13 | const point = Vec.toFixed(Vec.add(camera.point, Vec.sub(p1, p0)))
14 |
15 | data.pageState.camera.zoom = zoom
16 | data.pageState.camera.point = point
17 | }
18 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/camera/zoomOut.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 | import { Utils } from '@tlslides/core'
3 | import Vec from '@tlslides/vec'
4 | import { mutables } from 'state/mutables'
5 |
6 | export const zoomOut: Action = (data) => {
7 | const { camera } = data.pageState
8 | const i = Math.round((data.pageState.camera.zoom * 100) / 25)
9 | const zoom = Math.max(0.25, (i - 1) * 0.25)
10 | const center = [mutables.rendererBounds.width / 2, mutables.rendererBounds.height / 2]
11 | const p0 = Vec.sub(Vec.div(center, camera.zoom), center)
12 | const p1 = Vec.sub(Vec.div(center, zoom), center)
13 | const point = Vec.toFixed(Vec.add(camera.point, Vec.sub(p1, p0)))
14 |
15 | data.pageState.camera.zoom = zoom
16 | data.pageState.camera.point = point
17 | }
18 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/data/index.ts:
--------------------------------------------------------------------------------
1 | export * from './loadDocument'
2 | export * from './restoreSavedDocument'
3 | export * from './loadNewDocument'
4 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/data/loadDocument.ts:
--------------------------------------------------------------------------------
1 | import { current } from 'immer'
2 | import type { Action, AppDocument } from 'state/constants'
3 | import { mutables } from 'state/mutables'
4 |
5 | export const loadDocument: Action = (data, payload: { doc: AppDocument }) => {
6 | Object.assign(data, payload.doc)
7 |
8 | const snapshot = current(data)
9 |
10 | mutables.history.reset(snapshot)
11 |
12 | Object.assign(mutables, {
13 | snapshot,
14 | initialPoint: [0, 0],
15 | isCloning: false,
16 | pointedShapeId: undefined,
17 | pointedHandleId: undefined,
18 | pointedBoundsHandleId: undefined,
19 | initialCommonBounds: undefined,
20 | snapInfo: undefined,
21 | })
22 | }
23 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/data/restoreSavedDocument.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 | import { mutables } from '../../mutables'
3 |
4 | export const restoreSavedDocument: Action = (data) => {
5 | const snapshot = mutables.history.restore()
6 | Object.assign(data, snapshot)
7 | }
8 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/erase/eraseGhostShapes.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 |
3 | export const eraseGhostShapes: Action = (data) => {
4 | const idsToDelete = Object.values(data.page.shapes)
5 | .filter((shape) => shape.isGhost)
6 | .map((shape) => shape.id)
7 |
8 | idsToDelete.forEach((id) => delete data.page.shapes[id])
9 |
10 | data.pageState.selectedIds = data.pageState.selectedIds.filter((id) => !idsToDelete.includes(id))
11 |
12 | if (data.pageState.hoveredId && idsToDelete.includes(data.pageState.hoveredId)) {
13 | data.pageState.hoveredId = undefined
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/erase/eraseShapes.ts:
--------------------------------------------------------------------------------
1 | import type { TLPointerInfo } from '@tlslides/core'
2 | import type { Action } from 'state/constants'
3 | import { getPagePoint } from 'state/helpers'
4 | import { getShapeUtils } from 'shapes'
5 | import { mutables } from 'state/mutables'
6 |
7 | export const eraseShapes: Action = (data, payload: TLPointerInfo) => {
8 | const { previousPoint } = mutables
9 |
10 | Object.values(data.page.shapes)
11 | .filter((shape) => !shape.isGhost)
12 | .forEach((shape) => {
13 | if (getShapeUtils(shape).hitTestLineSegment(shape, previousPoint, mutables.currentPoint)) {
14 | shape.isGhost = true
15 | }
16 | })
17 | }
18 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/erase/eraseShapesAtPoint.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 | import { getShapeUtils } from 'shapes'
3 | import { mutables } from 'state/mutables'
4 |
5 | export const eraseShapesAtPoint: Action = (data) => {
6 | const { currentPoint } = mutables
7 |
8 | Object.values(data.page.shapes).forEach((shape) => {
9 | if (getShapeUtils(shape).hitTestPoint(shape, currentPoint)) {
10 | delete data.page.shapes[shape.id]
11 | }
12 | })
13 |
14 | const { shapes } = data.page
15 | const { selectedIds, hoveredId } = data.pageState
16 |
17 | // Filter out any deleted shapes
18 | data.pageState.selectedIds = selectedIds.filter((id) => {
19 | return shapes[id] !== undefined
20 | })
21 |
22 | // Remove hovered id if it's been deleted
23 | if (hoveredId && !shapes[hoveredId]) {
24 | data.pageState.hoveredId = undefined
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/erase/index.ts:
--------------------------------------------------------------------------------
1 | export * from './eraseShapes'
2 | export * from './eraseShapesAtPoint'
3 | export * from './eraseGhostShapes'
4 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/handles/clearPointedHandle.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 | import { mutables } from 'state/mutables'
3 |
4 | export const clearPointedHandle: Action = () => {
5 | mutables.pointedHandleId = undefined
6 | }
7 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/handles/index.ts:
--------------------------------------------------------------------------------
1 | export * from './setPointedHandle'
2 | export * from './clearPointedHandle'
3 | export * from './translateHandle'
4 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/handles/setPointedHandle.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 | import { mutables } from 'state/mutables'
3 |
4 | export const setPointedHandle: Action = (data, payload) => {
5 | mutables.pointedHandleId = payload.target
6 | }
7 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/history/addToHistory.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 | import { mutables } from '../../mutables'
3 |
4 | export const addToHistory: Action = (data) => {
5 | mutables.history.push(data)
6 | }
7 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/history/index.ts:
--------------------------------------------------------------------------------
1 | export * from './addToHistory'
2 | export * from './redo'
3 | export * from './undo'
4 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/history/redo.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 | import { mutables } from '../../mutables'
3 |
4 | export const redo: Action = (data) => {
5 | const snapshot = mutables.history.redo()
6 | Object.assign(data, snapshot)
7 | }
8 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/history/undo.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 | import { mutables } from '../../mutables'
3 |
4 | export const undo: Action = (data) => {
5 | const snapshot = mutables.history.undo()
6 | Object.assign(data, snapshot)
7 | }
8 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/index.ts:
--------------------------------------------------------------------------------
1 | export * from './bindings'
2 | export * from './camera'
3 | export * from './data'
4 | export * from './erase'
5 | export * from './handles'
6 | export * from './history'
7 | export * from './mutables/restoreSnapshot'
8 | export * from './selection'
9 | export * from './shapes'
10 | export * from './snaps'
11 | export * from './transform'
12 | export * from './translate'
13 | export * from './mutables'
14 | export * from './performance'
15 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/mutables/index.ts:
--------------------------------------------------------------------------------
1 | export * from './setInitialPoint'
2 | export * from './setSnapshot'
3 | export * from './setViewport'
4 | export * from './restoreSnapshot'
5 | export * from './updatePointer'
6 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/mutables/restoreSnapshot.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 | import { mutables } from 'state/mutables'
3 |
4 | export const restoreSnapshot: Action = (data) => {
5 | Object.assign(data, mutables.snapshot)
6 | }
7 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/mutables/setInitialPoint.ts:
--------------------------------------------------------------------------------
1 | import type { TLPointerInfo } from '@tlslides/core'
2 | import type { Action } from 'state/constants'
3 | import { getPagePoint } from 'state/helpers'
4 | import { mutables } from 'state/mutables'
5 |
6 | export const setInitialPoint: Action = (data, payload: TLPointerInfo) => {
7 | mutables.initialPoint = getPagePoint(payload.origin, data.pageState)
8 | mutables.previousPoint = [...mutables.initialPoint]
9 | }
10 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/mutables/setSnapshot.ts:
--------------------------------------------------------------------------------
1 | import { current } from 'immer'
2 | import type { Action } from 'state/constants'
3 | import { mutables } from 'state/mutables'
4 |
5 | export const setSnapshot: Action = (data) => {
6 | mutables.snapshot = current(data)
7 | }
8 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/mutables/setViewport.ts:
--------------------------------------------------------------------------------
1 | import type { TLBounds } from '@tlslides/core'
2 | import Vec from '@tlslides/vec'
3 | import type { Action } from 'state/constants'
4 | import { mutables } from 'state/mutables'
5 |
6 | export const setViewport: Action = (data, payload: { bounds: TLBounds }) => {
7 | const { camera } = data.pageState
8 | const { width, height } = payload.bounds
9 |
10 | const [minX, minY] = Vec.sub(Vec.div([0, 0], camera.zoom), camera.point)
11 | const [maxX, maxY] = Vec.sub(Vec.div([width, height], camera.zoom), camera.point)
12 |
13 | mutables.rendererBounds = { ...payload.bounds }
14 |
15 | mutables.viewport = {
16 | minX,
17 | minY,
18 | maxX,
19 | maxY,
20 | height: maxX - minX,
21 | width: maxY - minY,
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/mutables/updatePointer.ts:
--------------------------------------------------------------------------------
1 | import type { TLPointerInfo } from '@tlslides/core'
2 | import type { Action } from 'state/constants'
3 | import { getPagePoint } from 'state/helpers'
4 | import { mutables } from 'state/mutables'
5 |
6 | export const updatePointer: Action = (data, payload: TLPointerInfo) => {
7 | mutables.previousPoint = [...mutables.currentPoint]
8 | mutables.currentPoint = getPagePoint(payload.point, data.pageState)
9 | }
10 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/performance/clearPerformanceMode.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 |
3 | export const clearPerformanceMode: Action = (data) => {
4 | data.performanceMode = undefined
5 | }
6 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/performance/index.ts:
--------------------------------------------------------------------------------
1 | export * from './clearPerformanceMode'
2 | export * from './setTranslatePerformanceMode'
3 | export * from './setTransformPerformanceMode'
4 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/performance/setTransformPerformanceMode.ts:
--------------------------------------------------------------------------------
1 | import { TLPerformanceMode } from '@tlslides/core'
2 | import type { Action } from 'state/constants'
3 |
4 | export const setTransformPerformanceMode: Action = (data) => {
5 | data.performanceMode = undefined
6 | }
7 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/performance/setTranslatePerformanceMode.ts:
--------------------------------------------------------------------------------
1 | import { TLPerformanceMode } from '@tlslides/core'
2 | import type { Action } from 'state/constants'
3 |
4 | export const setTranslatePerformanceMode: Action = (data) => {
5 | data.performanceMode = undefined
6 | }
7 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/selection/clearBrush.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 |
3 | export const clearBrush: Action = (data) => {
4 | data.pageState.brush = undefined
5 | }
6 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/selection/clearHoveredShape.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 |
3 | export const clearHoveredShape: Action = (data) => {
4 | data.pageState.hoveredId = undefined
5 | }
6 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/selection/clearPointedShape.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 | import { mutables } from 'state/mutables'
3 |
4 | export const clearPointedShape: Action = () => {
5 | mutables.pointedShapeId = undefined
6 | }
7 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/selection/deselectAllShapes.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 |
3 | export const deselectAllShapes: Action = (data) => {
4 | data.pageState.selectedIds = []
5 | }
6 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/selection/index.ts:
--------------------------------------------------------------------------------
1 | export * from './clearBrush'
2 | export * from './clearHoveredShape'
3 | export * from './clearPointedShape'
4 | export * from './deselectAllShapes'
5 | export * from './selectShape'
6 | export * from './setHoveredShape'
7 | export * from './updateBrush'
8 | export * from './selectAllShapes'
9 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/selection/selectAllShapes.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 |
3 | export const selectAllShapes: Action = (data) => {
4 | data.pageState.selectedIds = Object.keys(data.page.shapes)
5 | }
6 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/selection/selectShape.ts:
--------------------------------------------------------------------------------
1 | import type { TLPointerInfo } from '@tlslides/core'
2 | import type { Action } from 'state/constants'
3 | import { mutables } from 'state/mutables'
4 |
5 | export const selectShape: Action = (data, payload: TLPointerInfo) => {
6 | const { selectedIds } = data.pageState
7 |
8 | if (payload.shiftKey) {
9 | if (selectedIds.includes(payload.target) && mutables.pointedShapeId !== payload.target) {
10 | selectedIds.splice(selectedIds.indexOf(payload.target), 1)
11 | } else {
12 | mutables.pointedShapeId = payload.target
13 | selectedIds.push(payload.target)
14 | }
15 | } else {
16 | data.pageState.selectedIds = [payload.target]
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/selection/setHoveredShape.ts:
--------------------------------------------------------------------------------
1 | import type { TLPointerInfo } from '@tlslides/core'
2 | import type { Action } from 'state/constants'
3 |
4 | export const setHoveredShape: Action = (data, payload: TLPointerInfo) => {
5 | data.pageState.hoveredId = payload.target
6 | }
7 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/shapes/createBoxShape.ts:
--------------------------------------------------------------------------------
1 | import { TLBoundsCorner, TLPointerInfo } from '@tlslides/core'
2 | import { shapeUtils } from 'shapes'
3 | import type { Action } from 'state/constants'
4 | import { getPagePoint } from 'state/helpers'
5 | import { mutables } from 'state/mutables'
6 |
7 | export const createBoxShape: Action = (data, payload: TLPointerInfo) => {
8 | const shape = shapeUtils.box.getShape({
9 | parentId: 'page1',
10 | point: mutables.currentPoint,
11 | size: [1, 1],
12 | childIndex: Object.values(data.page.shapes).length,
13 | })
14 |
15 | data.page.shapes[shape.id] = shape
16 | data.pageState.selectedIds = [shape.id]
17 |
18 | mutables.pointedBoundsHandleId = TLBoundsCorner.BottomRight
19 | }
20 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/shapes/createPencilShape.ts:
--------------------------------------------------------------------------------
1 | import type { TLPointerInfo } from '@tlslides/core'
2 | import type { Action } from 'state/constants'
3 | import { shapeUtils } from 'shapes'
4 | import { getPagePoint } from 'state/helpers'
5 | import { mutables } from 'state/mutables'
6 |
7 | export const createPencilShape: Action = (data, payload: TLPointerInfo) => {
8 | const shape = shapeUtils.pencil.getShape({
9 | parentId: 'page1',
10 | point: mutables.currentPoint,
11 | points: [[0, 0]],
12 | childIndex: Object.values(data.page.shapes).length,
13 | })
14 |
15 | data.page.shapes[shape.id] = shape
16 | data.pageState.selectedIds = [shape.id]
17 |
18 | mutables.rawPoints = [[0, 0]]
19 | mutables.pointedShapeId = shape.id
20 | }
21 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/shapes/createShapes.ts:
--------------------------------------------------------------------------------
1 | import { nanoid } from 'nanoid'
2 | import { getShapeUtils, Shape, shapeUtils } from 'shapes'
3 | import type { Action } from 'state/constants'
4 |
5 | export const createShapes: Action = (
6 | data,
7 | payload: { shapes: (Partial & Pick)[] }
8 | ) => {
9 | try {
10 | payload.shapes.forEach((partial, i) => {
11 | const shape = getShapeUtils(partial.type).getShape({
12 | id: nanoid(),
13 | childIndex: Object.values(data.page.shapes).length,
14 | ...partial,
15 | parentId: 'page1',
16 | })
17 |
18 | data.page.shapes[shape.id] = shape
19 | })
20 | } catch (e: any) {
21 | e.message = 'Could not create shapes: ' + e.message
22 | console.error(e)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/shapes/deleteSelectedShapes.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 |
3 | export const deleteSelectedShapes: Action = (data) => {
4 | const { page, pageState } = data
5 | if (pageState.hoveredId && pageState.selectedIds.includes(pageState.hoveredId)) {
6 | pageState.hoveredId = undefined
7 | }
8 | pageState.selectedIds.forEach((id) => delete page.shapes[id])
9 | pageState.selectedIds = []
10 | }
11 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/shapes/deleteShapes.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 |
3 | export const deleteShapes: Action = (data, payload: { ids: string[] }) => {
4 | try {
5 | data.pageState.selectedIds = data.pageState.selectedIds.filter(
6 | (id) => !payload.ids.includes(id)
7 | )
8 |
9 | payload.ids.forEach((id) => {
10 | delete data.page.shapes[id]
11 | })
12 | } catch (e: any) {
13 | e.message = 'Could not delete shapes: ' + e.message
14 | console.error(e)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/shapes/index.ts:
--------------------------------------------------------------------------------
1 | export * from './createPencilShape'
2 | export * from './createArrowShape'
3 | export * from './createBoxShape'
4 | export * from './deleteSelectedShapes'
5 | export * from './createShapes'
6 | export * from './updateShapes'
7 | export * from './deleteShapes'
8 | export * from './extendPencilShape'
9 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/shapes/updateShapes.ts:
--------------------------------------------------------------------------------
1 | import type { Shape } from 'shapes'
2 | import type { Action } from 'state/constants'
3 |
4 | export const updateShapes: Action = (
5 | data,
6 | payload: { shapes: (Partial & Pick)[] }
7 | ) => {
8 | try {
9 | payload.shapes.forEach((partial, i) => {
10 | Object.assign(data.page.shapes[partial.id], partial)
11 | })
12 | } catch (e: any) {
13 | e.message = 'Could not update shapes: ' + e.message
14 | console.error(e)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/snapping/clearSnapLines.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 |
3 | export const clearSnaplines: Action = (data) => {
4 | data.overlays.snapLines = []
5 | }
6 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/snaps/clearSnapInfo.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 | import { mutables } from 'state/mutables'
3 |
4 | export const clearSnapInfo: Action = () => {
5 | mutables.snapInfo = undefined
6 | }
7 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/snaps/clearSnapLines.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 |
3 | export const clearSnapLines: Action = (data) => {
4 | data.overlays.snapLines = []
5 | }
6 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/snaps/index.ts:
--------------------------------------------------------------------------------
1 | export * from './clearSnapLines'
2 | export * from './setSnapInfo'
3 | export * from './clearSnapInfo'
4 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/transform/clearPointedBoundsHandle.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 | import { mutables } from 'state/mutables'
3 |
4 | export const clearPointedBoundsHandle: Action = (data, payload) => {
5 | mutables.pointedBoundsHandleId = undefined
6 | }
7 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/transform/index.ts:
--------------------------------------------------------------------------------
1 | export * from './transformSelectedShapes'
2 | export * from './setPointedBoundsHandle'
3 | export * from './setInitialCommonBounds'
4 | export * from './clearPointedBoundsHandle'
5 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/transform/setInitialCommonBounds.ts:
--------------------------------------------------------------------------------
1 | import { Utils } from '@tlslides/core'
2 | import { getShapeUtils } from 'shapes'
3 | import type { Action } from 'state/constants'
4 | import { mutables } from 'state/mutables'
5 |
6 | export const setInitialCommonBounds: Action = (data) => {
7 | const { snapshot } = mutables
8 | const { selectedIds } = data.pageState
9 |
10 | const initialCommonBounds = Utils.getCommonBounds(
11 | selectedIds
12 | .map((id) => snapshot.page.shapes[id])
13 | .map((shape) => getShapeUtils(shape).getBounds(shape))
14 | )
15 |
16 | mutables.initialCommonBounds = initialCommonBounds
17 | }
18 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/transform/setPointedBoundsHandle.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 | import { mutables } from 'state/mutables'
3 |
4 | export const setPointedBoundsHandle: Action = (data, payload) => {
5 | mutables.pointedBoundsHandleId = payload.target
6 | }
7 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/transform/transformSelectedShapes.ts:
--------------------------------------------------------------------------------
1 | import type { TLPointerInfo } from '@tlslides/core'
2 | import type { Action } from 'state/constants'
3 | import { mutables } from 'state/mutables'
4 | import { resizeSelectedShapes } from './resizeSelectedShapes'
5 | import { rotateSelectedShapes } from './rotateSelectedShapes'
6 |
7 | export const transformSelectedShapes: Action = (data, payload: TLPointerInfo) => {
8 | const { pointedBoundsHandleId } = mutables
9 |
10 | if (pointedBoundsHandleId === 'rotate') {
11 | rotateSelectedShapes(data, payload)
12 | } else {
13 | resizeSelectedShapes(data, payload)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/translate/clearIsCloning.ts:
--------------------------------------------------------------------------------
1 | import type { Action } from 'state/constants'
2 | import { mutables } from 'state/mutables'
3 |
4 | export const clearIsCloning: Action = () => {
5 | mutables.isCloning = false
6 | }
7 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/state/actions/translate/index.ts:
--------------------------------------------------------------------------------
1 | export * from './clearIsCloning'
2 | export * from './translateSelectedShapes'
3 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/styles.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700');
2 |
3 | html,
4 | * {
5 | box-sizing: border-box;
6 | }
7 |
8 | body {
9 | font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
10 | Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
11 | overscroll-behavior: none;
12 | margin: 0px;
13 | padding: 0px;
14 | }
15 |
--------------------------------------------------------------------------------
/examples/core-example-advanced/src/types.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/examples/core-example-advanced/src/types.ts
--------------------------------------------------------------------------------
/examples/core-example-advanced/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "include": ["src"],
4 | "exclude": ["node_modules", "dist", "docs"],
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "baseUrl": ".",
8 | "rootDir": "src",
9 | "emitDeclarationOnly": false,
10 | "paths": {
11 | "*": ["src/*"]
12 | }
13 | },
14 | "references": [{ "path": "../../packages/core" }],
15 | "typedocOptions": {
16 | "entryPoints": ["src/index.ts"],
17 | "out": "docs"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/core-example/README.md:
--------------------------------------------------------------------------------
1 | # @tlslides/core Simple Example
2 |
3 | A (relatively) simple example project for `@tlslides/core`.
4 |
--------------------------------------------------------------------------------
/examples/core-example/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | tldraw
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/examples/core-example/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import App from './app'
4 | import './styles.css'
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById('root')
11 | )
12 |
--------------------------------------------------------------------------------
/examples/core-example/src/shapes/index.ts:
--------------------------------------------------------------------------------
1 | import type { RectShape } from './rect'
2 |
3 | export * from './rect'
4 |
5 | export type Shape = RectShape
6 |
--------------------------------------------------------------------------------
/examples/core-example/src/shapes/rect/RectComponent.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { TLShapeUtil, SVGContainer } from '@tlslides/core'
3 | import type { RectShape } from './RectShape'
4 |
5 | export const RectComponent = TLShapeUtil.Component(
6 | ({ shape, events, meta }, ref) => {
7 | const color = meta.isDarkMode ? 'white' : 'black'
8 |
9 | return (
10 |
11 |
20 |
21 | )
22 | }
23 | )
24 |
--------------------------------------------------------------------------------
/examples/core-example/src/shapes/rect/RectIndicator.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { TLShapeUtil } from '@tlslides/core'
3 | import type { RectShape } from './RectShape'
4 |
5 | export const RectIndicator = TLShapeUtil.Indicator(({ shape }) => {
6 | return (
7 |
15 | )
16 | })
17 |
--------------------------------------------------------------------------------
/examples/core-example/src/shapes/rect/RectShape.ts:
--------------------------------------------------------------------------------
1 | import type { TLShape } from '@tlslides/core'
2 |
3 | export interface RectShape extends TLShape {
4 | type: 'rect'
5 | size: number[]
6 | }
7 |
--------------------------------------------------------------------------------
/examples/core-example/src/shapes/rect/RectUtil.ts:
--------------------------------------------------------------------------------
1 | import { TLBounds, TLShapeUtil } from '@tlslides/core'
2 | import { RectComponent } from './RectComponent'
3 | import { RectIndicator } from './RectIndicator'
4 | import type { RectShape } from './RectShape'
5 |
6 | type T = RectShape
7 | type E = SVGSVGElement
8 |
9 | export class RectUtil extends TLShapeUtil {
10 | Component = RectComponent
11 |
12 | Indicator = RectIndicator
13 |
14 | getBounds = (shape: T) => {
15 | const [x, y] = shape.point
16 | const [width, height] = shape.size
17 |
18 | const bounds: TLBounds = {
19 | minX: x,
20 | maxX: x + width,
21 | minY: y,
22 | maxY: y + height,
23 | width,
24 | height,
25 | } as TLBounds
26 |
27 | return bounds
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/examples/core-example/src/shapes/rect/index.ts:
--------------------------------------------------------------------------------
1 | export * from './RectShape'
2 | export * from './RectUtil'
3 |
--------------------------------------------------------------------------------
/examples/core-example/src/styles.css:
--------------------------------------------------------------------------------
1 | html,
2 | * {
3 | box-sizing: border-box;
4 | }
5 |
6 | body {
7 | overscroll-behavior: none;
8 | margin: 0px;
9 | padding: 0px;
10 | }
11 |
12 | .tldraw {
13 | position: fixed;
14 | top: 0px;
15 | left: 0px;
16 | right: 0px;
17 | bottom: 0px;
18 | width: 100%;
19 | height: 100%;
20 | }
21 |
--------------------------------------------------------------------------------
/examples/core-example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "include": ["src"],
4 | "exclude": ["node_modules", "dist", "docs"],
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "baseUrl": ".",
8 | "rootDir": "src",
9 | "emitDeclarationOnly": false,
10 | "experimentalDecorators": true,
11 | "useDefineForClassFields": true,
12 | "paths": {
13 | "*": ["src/*"]
14 | }
15 | },
16 | "references": [{ "path": "../../packages/core" }],
17 | "typedocOptions": {
18 | "entryPoints": ["src/index.ts"],
19 | "out": "docs"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/tldraw-example/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |
4 |
5 | # @tlslides/tldraw-example
6 |
7 | An example project for `@tlslides/tldraw`.
8 |
9 | To start this project:
10 |
11 | - run `yarn start` from the repository's root directory
12 | - open `http://localhost:5420` in your browser
13 |
--------------------------------------------------------------------------------
/examples/tldraw-example/card-repo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/examples/tldraw-example/card-repo.png
--------------------------------------------------------------------------------
/examples/tldraw-example/src/api.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { Tldraw, TldrawApp, TDShapeType, ColorStyle } from '@tlslides/tldraw'
3 |
4 | declare const window: Window & { app: TldrawApp }
5 |
6 | export default function Api(): JSX.Element {
7 | const rTldrawApp = React.useRef()
8 |
9 | const handleMount = React.useCallback((app: TldrawApp) => {
10 | rTldrawApp.current = app
11 |
12 | window.app = app
13 |
14 | app
15 | .createShapes({
16 | id: 'rect1',
17 | type: TDShapeType.Rectangle,
18 | point: [100, 100],
19 | size: [200, 200],
20 | })
21 | .selectAll()
22 | .nudge([1, 1], true)
23 | .duplicate()
24 | .select('rect1')
25 | .style({ color: ColorStyle.Blue })
26 | .selectNone()
27 | }, [])
28 |
29 | return (
30 |
31 |
32 |
33 | )
34 | }
35 |
--------------------------------------------------------------------------------
/examples/tldraw-example/src/basic.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { Tldraw } from '@tlslides/tldraw'
3 |
4 | export default function Basic(): JSX.Element {
5 | return (
6 |
7 |
8 |
9 | )
10 | }
11 |
--------------------------------------------------------------------------------
/examples/tldraw-example/src/changing-id.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { Tldraw } from '@tlslides/tldraw'
3 |
4 | export default function ChangingId() {
5 | const [id, setId] = React.useState('example')
6 |
7 | React.useEffect(() => {
8 | const timeout = setTimeout(() => setId('example2'), 2000)
9 |
10 | return () => clearTimeout(timeout)
11 | }, [])
12 |
13 | return
14 | }
15 |
--------------------------------------------------------------------------------
/examples/tldraw-example/src/embedded.tsx:
--------------------------------------------------------------------------------
1 | import { Tldraw } from '@tlslides/tldraw'
2 | import * as React from 'react'
3 |
4 | export default function Embedded(): JSX.Element {
5 | return (
6 |
7 |
16 |
17 |
18 |
19 |
27 |
28 |
29 |
30 | )
31 | }
32 |
--------------------------------------------------------------------------------
/examples/tldraw-example/src/file-system.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { Tldraw, useFileSystem } from '@tlslides/tldraw'
3 |
4 | export default function FileSystem(): JSX.Element {
5 | const fileSystemEvents = useFileSystem()
6 |
7 | // Use the Menu > File to create, open, and save .tldr files.
8 |
9 | return (
10 |
11 |
12 |
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/examples/tldraw-example/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import App from './app'
4 | import { HashRouter } from 'react-router-dom'
5 |
6 | ReactDOM.render(
7 |
8 |
9 |
10 |
11 | ,
12 | document.getElementById('root')
13 | )
14 |
--------------------------------------------------------------------------------
/examples/tldraw-example/src/loading-files.tsx:
--------------------------------------------------------------------------------
1 | import { Tldraw, TDFile } from '@tlslides/tldraw'
2 | import * as React from 'react'
3 |
4 | export default function LoadingFiles(): JSX.Element {
5 | const [file, setFile] = React.useState()
6 |
7 | React.useEffect(() => {
8 | async function loadFile(): Promise {
9 | const file = await fetch('Example.tldr').then((response) => response.json())
10 | setFile(file)
11 | }
12 |
13 | loadFile()
14 | }, [])
15 |
16 | return
17 | }
18 |
--------------------------------------------------------------------------------
/examples/tldraw-example/src/multiplayer-with-images/index.ts:
--------------------------------------------------------------------------------
1 | export * from './multiplayer'
2 |
--------------------------------------------------------------------------------
/examples/tldraw-example/src/multiplayer/index.ts:
--------------------------------------------------------------------------------
1 | export * from './multiplayer'
2 |
--------------------------------------------------------------------------------
/examples/tldraw-example/src/no-size-embedded.tsx:
--------------------------------------------------------------------------------
1 | import { Tldraw } from '@tlslides/tldraw'
2 | import * as React from 'react'
3 |
4 | export default function NoSizeEmbedded(): JSX.Element {
5 | return
6 | }
7 |
--------------------------------------------------------------------------------
/examples/tldraw-example/src/persisted.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { Tldraw } from '@tlslides/tldraw'
3 |
4 | export default function Persisted(): JSX.Element {
5 | return (
6 |
7 |
8 |
9 | )
10 | }
11 |
--------------------------------------------------------------------------------
/examples/tldraw-example/src/public/card-repo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/examples/tldraw-example/src/public/card-repo.png
--------------------------------------------------------------------------------
/examples/tldraw-example/src/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | tldraw
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/examples/tldraw-example/src/readonly.tsx:
--------------------------------------------------------------------------------
1 | import { Tldraw, TDFile } from '@tlslides/tldraw'
2 | import * as React from 'react'
3 |
4 | export default function ReadOnly(): JSX.Element {
5 | const [file, setFile] = React.useState()
6 |
7 | React.useEffect(() => {
8 | async function loadFile(): Promise {
9 | const file = await fetch('Example.tldr').then((response) => response.json())
10 | setFile(file)
11 | }
12 |
13 | loadFile()
14 | }, [])
15 |
16 | return (
17 |
18 |
19 |
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/examples/tldraw-example/src/styles.css:
--------------------------------------------------------------------------------
1 | html,
2 | * {
3 | box-sizing: border-box;
4 | }
5 |
6 | body {
7 | overscroll-behavior: none;
8 | margin: 0px;
9 | padding: 0px;
10 | font-size: 1em;
11 | font-family: Arial, Helvetica, sans-serif;
12 | }
13 |
14 | .tldraw {
15 | position: fixed;
16 | top: 0px;
17 | left: 0px;
18 | right: 0px;
19 | bottom: 0px;
20 | width: 100%;
21 | height: 100%;
22 | }
23 |
24 | .hero {
25 | width: 100%;
26 | max-width: 720px;
27 | }
28 |
29 | .links {
30 | display: grid;
31 | width: fit-content;
32 | list-style: none;
33 | padding: 1em;
34 | margin: 0px;
35 | }
36 |
37 | .links a {
38 | padding: 0.5em 0.7em;
39 | color: black;
40 | text-decoration: none;
41 | font-size: 1.2em;
42 | display: block;
43 | border-radius: 8px;
44 | }
45 |
46 | .links a:hover {
47 | background-color: rgba(144, 144, 144, 0.1);
48 | }
49 |
--------------------------------------------------------------------------------
/examples/tldraw-example/src/ui-options.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { Tldraw } from '@tlslides/tldraw'
3 |
4 | export default function UIOptions(): JSX.Element {
5 | return (
6 |
7 |
15 |
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/examples/tldraw-example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "include": ["src"],
4 | "exclude": ["node_modules", "dist", "docs"],
5 | "compilerOptions": {
6 | "outDir": "./dist",
7 | "baseUrl": ".",
8 | "rootDir": "src",
9 | "emitDeclarationOnly": false,
10 | "paths": {
11 | "~*": ["./src/*"],
12 | "@tlslides/core": ["../../packages/core"],
13 | "@tlslides/tldraw": ["../../packages/tldraw"],
14 | "@tlslides/vec": ["../../packages/vec"],
15 | "@tlslides/intersect": ["../../packages/intersect"]
16 | }
17 | },
18 | "references": [
19 | {
20 | "path": "../../packages/core"
21 | },
22 | {
23 | "path": "../../packages/tldraw"
24 | }
25 | ],
26 | "typedocOptions": {
27 | "entryPoints": ["src/index.ts"],
28 | "out": "docs"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/guides/development.md:
--------------------------------------------------------------------------------
1 | # Development
2 |
3 | From the root folder:
4 |
5 | - Run `yarn` to install dependencies.
6 |
7 | - Run `yarn start` to start the development server for the package and for the example.
8 |
9 | - Open `localhost:5420` to view the example project.
10 |
11 | **Note:** The multiplayer examples and endpoints currently require an API key from [Liveblocks](https://liveblocks.io/), however the storage services that are used in tldraw are currently in alpha and (as of November 2021) not accessible to the general public. You won't be able to authenticate and run these parts of the project.
12 |
13 | Other scripts:
14 |
15 | - Run `yarn test` to execute unit tests via [Jest](https://jestjs.io).
16 |
17 | - Run `yarn docs` to build the docs via [ts-doc](https://typedoc.org/).
18 |
--------------------------------------------------------------------------------
/guides/publishing.md:
--------------------------------------------------------------------------------
1 | # Publishing
2 |
3 | At the moment, publishing is done by hand by the project's maintainers.
4 |
5 | This guide will be updated when more information is available.
6 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.6.1",
3 | "registry": "https://registry.npmjs.org/",
4 | "publishConfig": {
5 | "access": "public",
6 | "directory": "dist"
7 | },
8 | "npmClient": "yarn",
9 | "useWorkspaces": true
10 | }
11 |
--------------------------------------------------------------------------------
/packages/core/src/TLShapeUtil/index.ts:
--------------------------------------------------------------------------------
1 | import type { TLShape } from '../types'
2 | import type { TLShapeUtil } from './TLShapeUtil'
3 |
4 | export type TLShapeUtilsMap = {
5 | [K in T['type']]: TLShapeUtil>
6 | }
7 |
8 | export * from './TLShapeUtil'
9 |
--------------------------------------------------------------------------------
/packages/core/src/components/Binding/Binding.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 |
3 | interface BindingProps {
4 | point: number[]
5 | type: string
6 | }
7 |
8 | export function Binding({ point: [x, y], type }: BindingProps): JSX.Element {
9 | return (
10 |
11 | {type === 'center' && (
12 |
13 | )}
14 | {type !== 'pin' && (
15 |
16 | )}
17 |
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/packages/core/src/components/Binding/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Binding'
2 |
--------------------------------------------------------------------------------
/packages/core/src/components/Bounds/CenterHandle.tsx:
--------------------------------------------------------------------------------
1 | import { observer } from 'mobx-react-lite'
2 | import * as React from 'react'
3 | import type { TLBounds } from '~types'
4 |
5 | export interface CenterHandleProps {
6 | bounds: TLBounds
7 | isLocked: boolean
8 | isHidden: boolean
9 | }
10 |
11 | export const CenterHandle = observer(function CenterHandle({
12 | bounds,
13 | isLocked,
14 | isHidden,
15 | }): JSX.Element {
16 | return (
17 |
27 | )
28 | })
29 |
--------------------------------------------------------------------------------
/packages/core/src/components/Bounds/__tests__/BoundsBg.test.tsx:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react'
2 | import * as React from 'react'
3 | import { BoundsBg } from '../BoundsBg'
4 |
5 | jest.spyOn(console, 'error').mockImplementation(() => void null)
6 |
7 | describe('BoundsBg', () => {
8 | test('mounts component without crashing', () => {
9 | render(
10 |
15 | )
16 | })
17 | test('validate attributes for a bounds bg', () => {
18 | render(
19 |
24 | )
25 | const boundsBg = screen.getByLabelText('bounds bg')
26 | expect(boundsBg).toHaveAttribute('height', '100')
27 | expect(boundsBg).toHaveAttribute('width', '100')
28 | expect(boundsBg).toHaveAttribute('opacity', '1')
29 | })
30 | })
31 |
--------------------------------------------------------------------------------
/packages/core/src/components/Bounds/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Bounds'
2 |
--------------------------------------------------------------------------------
/packages/core/src/components/Brush/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Brush'
2 |
--------------------------------------------------------------------------------
/packages/core/src/components/Canvas/Canvas.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { mockDocument, renderWithContext } from '~test'
3 | import { Canvas } from './Canvas'
4 |
5 | describe('page', () => {
6 | test('mounts component without crashing', () => {
7 | renderWithContext(
8 |