├── .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 | { 21 | // noop 22 | }} 23 | assets={{}} 24 | /> 25 | ) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /packages/core/src/components/Canvas/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Canvas' 2 | -------------------------------------------------------------------------------- /packages/core/src/components/Container/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Container' 2 | -------------------------------------------------------------------------------- /packages/core/src/components/Grid/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Grid' 2 | -------------------------------------------------------------------------------- /packages/core/src/components/HTMLContainer/HTMLContainer.tsx: -------------------------------------------------------------------------------- 1 | import { Observer } from 'mobx-react-lite' 2 | import * as React from 'react' 3 | 4 | interface HTMLContainerProps extends React.HTMLProps { 5 | children: React.ReactNode 6 | } 7 | 8 | export const HTMLContainer = React.forwardRef( 9 | function HTMLContainer({ children, className = '', ...rest }, ref) { 10 | return ( 11 | 12 | {() => ( 13 |
14 |
{children}
15 |
16 | )} 17 |
18 | ) 19 | } 20 | ) 21 | -------------------------------------------------------------------------------- /packages/core/src/components/HTMLContainer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './HTMLContainer' 2 | -------------------------------------------------------------------------------- /packages/core/src/components/Handles/Handle.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { renderWithContext } from '~test' 3 | import { screen } from '@testing-library/react' 4 | import { Handle } from './Handle' 5 | 6 | describe('handle', () => { 7 | test('mounts component without crashing', () => { 8 | renderWithContext() 9 | }) 10 | test('validate attributes for handle component', () => { 11 | renderWithContext() 12 | const handle = screen.getByLabelText('handle') 13 | expect(handle.querySelectorAll('circle').length).toBe(2) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/core/src/components/Handles/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Handles' 2 | -------------------------------------------------------------------------------- /packages/core/src/components/Overlay/Overlay.tsx: -------------------------------------------------------------------------------- 1 | import { observer } from 'mobx-react-lite' 2 | import * as React from 'react' 3 | 4 | type MyProps = { 5 | camera: { point: number[]; zoom: number } 6 | children: React.ReactNode 7 | } 8 | 9 | export const Overlay = observer(function Overlay({ camera: { zoom, point }, children }) { 10 | const l = 2.5 / zoom 11 | return ( 12 | 13 | 14 | 15 | 19 | 20 | 21 | {children} 22 | 23 | ) 24 | }) 25 | -------------------------------------------------------------------------------- /packages/core/src/components/Overlay/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Overlay' 2 | -------------------------------------------------------------------------------- /packages/core/src/components/Page/Page.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { mockDocument, renderWithContext } from '~test' 3 | import { Page } from './Page' 4 | 5 | describe('page', () => { 6 | test('mounts component without crashing', () => { 7 | renderWithContext( 8 | 20 | ) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /packages/core/src/components/Page/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Page' 2 | -------------------------------------------------------------------------------- /packages/core/src/components/Renderer/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Renderer' 2 | -------------------------------------------------------------------------------- /packages/core/src/components/SVGContainer/SVGContainer.tsx: -------------------------------------------------------------------------------- 1 | import { Observer } from 'mobx-react-lite' 2 | import * as React from 'react' 3 | 4 | interface SvgContainerProps extends React.SVGProps { 5 | children: React.ReactNode 6 | className?: string 7 | } 8 | 9 | export const SVGContainer = React.forwardRef( 10 | function SVGContainer({ id, className = '', children, ...rest }, ref) { 11 | return ( 12 | 13 | {() => ( 14 | 15 | 16 | {children} 17 | 18 | 19 | )} 20 | 21 | ) 22 | } 23 | ) 24 | -------------------------------------------------------------------------------- /packages/core/src/components/SVGContainer/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SVGContainer' 2 | -------------------------------------------------------------------------------- /packages/core/src/components/Shape/Shape.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { renderWithContext } from '~test' 3 | import { Shape } from './Shape' 4 | import { BoxUtil, boxShape } from '~TLShapeUtil/TLShapeUtil.spec' 5 | import type { TLShapeUtil } from '~TLShapeUtil' 6 | import type { TLShape } from '~types' 7 | 8 | describe('shape', () => { 9 | test('mounts component without crashing', () => { 10 | renderWithContext( 11 | } 14 | isEditing={false} 15 | isBinding={false} 16 | isHovered={false} 17 | isSelected={false} 18 | isGhost={false} 19 | isChildOfSelected={false} 20 | /> 21 | ) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /packages/core/src/components/Shape/ShapeNode.tsx: -------------------------------------------------------------------------------- 1 | import { observer } from 'mobx-react-lite' 2 | import * as React from 'react' 3 | import type { IShapeTreeNode, TLShape } from '~types' 4 | import { Shape } from './Shape' 5 | import type { TLShapeUtilsMap } from '~TLShapeUtil' 6 | 7 | interface ShapeNodeProps extends IShapeTreeNode { 8 | utils: TLShapeUtilsMap 9 | } 10 | 11 | export const ShapeNode = observer(function ShapeNode({ 12 | shape, 13 | utils, 14 | meta, 15 | children, 16 | ...rest 17 | }: ShapeNodeProps) { 18 | return ( 19 | <> 20 | 21 | {children && 22 | children.map((childNode) => ( 23 | 24 | ))} 25 | 26 | ) 27 | }) 28 | -------------------------------------------------------------------------------- /packages/core/src/components/Shape/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ShapeNode' 2 | -------------------------------------------------------------------------------- /packages/core/src/components/ShapeIndicator/ShapeIndicator.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { renderWithSvg } from '~test' 3 | import { ShapeIndicator } from './ShapeIndicator' 4 | import { boxShape } from '~TLShapeUtil/TLShapeUtil.spec' 5 | 6 | describe('shape indicator', () => { 7 | test('mounts component without crashing', () => { 8 | renderWithSvg( 9 | 16 | ) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /packages/core/src/components/ShapeIndicator/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ShapeIndicator' 2 | -------------------------------------------------------------------------------- /packages/core/src/components/SnapLines/SnapLines.tsx: -------------------------------------------------------------------------------- 1 | import { observer } from 'mobx-react-lite' 2 | import * as React from 'react' 3 | import type { TLSnapLine } from '~types' 4 | import Utils from '~utils' 5 | 6 | export const SnapLines = observer<{ snapLines: TLSnapLine[] }>(function SnapLines({ snapLines }) { 7 | return ( 8 | <> 9 | {snapLines.map((snapLine, i) => ( 10 | 11 | ))} 12 | 13 | ) 14 | }) 15 | 16 | export const SnapLine = observer<{ snapLine: TLSnapLine }>(function SnapLine({ snapLine }) { 17 | const bounds = Utils.getBoundsFromPoints(snapLine) 18 | 19 | return ( 20 | <> 21 | 28 | {snapLine.map(([x, y], i) => ( 29 | 30 | ))} 31 | 32 | ) 33 | }) 34 | -------------------------------------------------------------------------------- /packages/core/src/components/SnapLines/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SnapLines' 2 | -------------------------------------------------------------------------------- /packages/core/src/components/User/index.ts: -------------------------------------------------------------------------------- 1 | export * from './User' 2 | -------------------------------------------------------------------------------- /packages/core/src/components/Users/Users.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { User } from '~components/User/User' 3 | import type { TLShape, TLUsers } from '~types' 4 | 5 | export interface UserProps { 6 | userId?: string 7 | users: TLUsers 8 | } 9 | 10 | export function Users({ userId, users }: UserProps) { 11 | return ( 12 | <> 13 | {Object.values(users) 14 | .filter((user) => user && user.id !== userId) 15 | .map((user) => ( 16 | 17 | ))} 18 | 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /packages/core/src/components/Users/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Users' 2 | -------------------------------------------------------------------------------- /packages/core/src/components/UsersIndicators/index.ts: -------------------------------------------------------------------------------- 1 | export * from './UsersIndicators' 2 | -------------------------------------------------------------------------------- /packages/core/src/components/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Renderer' 2 | export * from './SVGContainer' 3 | export * from './HTMLContainer' 4 | -------------------------------------------------------------------------------- /packages/core/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useTLContext' 2 | export * from './useZoomEvents' 3 | export * from './useSafariFocusOutFix' 4 | export * from './useCanvasEvents' 5 | export * from './useShapeEvents' 6 | export * from './useShapeTree' 7 | export * from './useStyle' 8 | export * from './useCanvasEvents' 9 | export * from './useBoundsHandleEvents' 10 | export * from './useCameraCss' 11 | export * from './useSelection' 12 | export * from './useHandleEvents' 13 | export * from './useHandles' 14 | export * from './usePreventNavigationCss' 15 | export * from './useBoundsEvents' 16 | export * from './usePosition' 17 | export * from './useKeyEvents' 18 | export * from './useCursorAnimation' 19 | export * from './usePerformanceCss' 20 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useHandles.ts: -------------------------------------------------------------------------------- 1 | import type { TLBinding, TLPage, TLPageState, TLShape } from '../types' 2 | 3 | export function useHandles(page: TLPage, pageState: TLPageState) { 4 | const { selectedIds } = pageState 5 | 6 | let shapeWithHandles: TLShape | undefined = undefined 7 | 8 | if (selectedIds.length === 1) { 9 | const id = selectedIds[0] 10 | 11 | const shape = page.shapes[id] 12 | 13 | if (shape.handles !== undefined) { 14 | shapeWithHandles = shape 15 | } 16 | } 17 | 18 | return { shapeWithHandles } 19 | } 20 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useKeyEvents.ts: -------------------------------------------------------------------------------- 1 | import { useTLContext } from '../hooks' 2 | import * as React from 'react' 3 | 4 | export function useKeyEvents() { 5 | const { inputs, callbacks } = useTLContext() 6 | 7 | React.useEffect(() => { 8 | const handleKeyDown = (e: KeyboardEvent) => { 9 | callbacks.onKeyDown?.(e.key, inputs.keydown(e), e) 10 | } 11 | const handleKeyUp = (e: KeyboardEvent) => { 12 | inputs.keyup(e) 13 | callbacks.onKeyUp?.(e.key, inputs.keyup(e), e) 14 | } 15 | window.addEventListener('keydown', handleKeyDown) 16 | window.addEventListener('keyup', handleKeyUp) 17 | return () => { 18 | window.removeEventListener('keydown', handleKeyDown) 19 | window.removeEventListener('keyup', handleKeyUp) 20 | } 21 | }, [inputs, callbacks]) 22 | } 23 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useSafariFocusOutFix.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import Utils from '../utils' 3 | import { useTLContext } from './useTLContext' 4 | 5 | // Send event on iOS when a user presses the "Done" key while editing a text element. 6 | 7 | export function useSafariFocusOutFix(): void { 8 | const { callbacks } = useTLContext() 9 | 10 | useEffect(() => { 11 | function handleFocusOut() { 12 | callbacks.onShapeBlur?.() 13 | } 14 | 15 | if (Utils.isMobileSafari()) { 16 | document.addEventListener('focusout', handleFocusOut) 17 | return () => document.removeEventListener('focusout', handleFocusOut) 18 | } 19 | 20 | return () => null 21 | }, [callbacks]) 22 | } 23 | -------------------------------------------------------------------------------- /packages/core/src/hooks/useTLContext.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { Inputs } from '~inputs' 3 | import type { TLCallbacks, TLShape, TLBounds, TLPageState } from '~types' 4 | import type { TLShapeUtilsMap } from '~TLShapeUtil' 5 | 6 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 7 | export interface TLContextType { 8 | id?: string 9 | callbacks: Partial> 10 | shapeUtils: TLShapeUtilsMap 11 | rPageState: React.MutableRefObject 12 | rSelectionBounds: React.MutableRefObject 13 | inputs: Inputs 14 | bounds: TLBounds 15 | } 16 | 17 | export const TLContext = React.createContext({} as TLContextType) 18 | 19 | export function useTLContext() { 20 | const context = React.useContext(TLContext) 21 | 22 | return context 23 | } 24 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './components' 2 | export * from './types' 3 | export * from './utils' 4 | export * from './inputs' 5 | export * from './TLShapeUtil' 6 | -------------------------------------------------------------------------------- /packages/core/src/test/index.ts: -------------------------------------------------------------------------------- 1 | export * from './mockDocument' 2 | export * from './mockUtils' 3 | export * from './renderWithContext' 4 | export * from './renderWithSvg' 5 | -------------------------------------------------------------------------------- /packages/core/src/test/mockDocument.ts: -------------------------------------------------------------------------------- 1 | import type { BoxShape } from '~TLShapeUtil/TLShapeUtil.spec' 2 | import type { TLBinding, TLPage, TLPageState } from '~types' 3 | 4 | export const mockDocument: { page: TLPage; pageState: TLPageState } = { 5 | page: { 6 | id: 'page1', 7 | shapes: {}, 8 | bindings: {}, 9 | }, 10 | pageState: { 11 | id: 'page1', 12 | selectedIds: [], 13 | camera: { 14 | point: [0, 0], 15 | zoom: 1, 16 | }, 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /packages/core/src/test/mockUtils.tsx: -------------------------------------------------------------------------------- 1 | import { BoxUtil } from '~TLShapeUtil/TLShapeUtil.spec' 2 | 3 | export const mockUtils = { 4 | box: new BoxUtil(), 5 | } 6 | -------------------------------------------------------------------------------- /packages/core/src/test/renderWithContext.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { render } from '@testing-library/react' 3 | import { ContextWrapper } from './ContextWrapper' 4 | 5 | export const renderWithContext = (children: JSX.Element) => { 6 | return render({children}) 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/src/test/renderWithSvg.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { render } from '@testing-library/react' 3 | import { ContextWrapper } from './ContextWrapper' 4 | 5 | export const renderWithSvg = (children: JSX.Element) => { 6 | return render( 7 | 8 | {children} 9 | 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/src/utils/index.d.ts: -------------------------------------------------------------------------------- 1 | import { Utils } from './utils' 2 | export { Utils } from './utils' 3 | export { Svg } from './svg' 4 | export default Utils 5 | //# sourceMappingURL=index.d.ts.map 6 | -------------------------------------------------------------------------------- /packages/core/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { Utils } from './utils' 2 | export { Utils } from './utils' 3 | 4 | export default Utils 5 | -------------------------------------------------------------------------------- /packages/core/src/utils/polyfills.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/ban-ts-comment */ 2 | /* eslint-disable no-extend-native */ 3 | 4 | /** 5 | * String.prototype.replaceAll() polyfill 6 | * https://gomakethings.com/how-to-replace-a-section-of-a-string-with-another-one-with-vanilla-js/ 7 | * @author Chris Ferdinandi 8 | * @license MIT 9 | */ 10 | if (!String.prototype.replaceAll) { 11 | // @ts-ignore 12 | String.prototype.replaceAll = function (str: string, newStr: string) { 13 | // If a regex pattern 14 | if (Object.prototype.toString.call(str).toLowerCase() === '[object regexp]') { 15 | return this.replace(str, newStr) 16 | } 17 | 18 | // If a string 19 | return this.replace(new RegExp(str, 'g'), newStr) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/core/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": [ 4 | "node_modules", 5 | "**/*.test.tsx", 6 | "**/*.test.ts", 7 | "**/*.spec.tsx", 8 | "**/*.spec.ts", 9 | "src/test", 10 | "dist", 11 | "docs" 12 | ], 13 | "compilerOptions": { 14 | "composite": false, 15 | "incremental": false, 16 | "declaration": true, 17 | "declarationMap": true, 18 | "sourceMap": true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/core/tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src"], 4 | "exclude": [ 5 | "node_modules", 6 | "**/*.test.tsx", 7 | "**/*.test.ts", 8 | "**/*.spec.tsx", 9 | "**/*.spec.ts", 10 | "src/test", 11 | "dist", 12 | "docs" 13 | ], 14 | "compilerOptions": { 15 | "composite": false, 16 | "incremental": false, 17 | "declaration": true, 18 | "declarationMap": true, 19 | "sourceMap": true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "exclude": ["node_modules", "dist", "docs"], 4 | "compilerOptions": { 5 | "experimentalDecorators": true, 6 | "useDefineForClassFields": true, 7 | "outDir": "./dist", 8 | "rootDir": "src", 9 | "baseUrl": ".", 10 | "paths": { 11 | "~*": ["./src/*"] 12 | } 13 | }, 14 | "references": [{ "path": "../vec" }, { "path": "../intersect" }], 15 | "typedocOptions": { 16 | "entryPoints": ["src/index.ts"], 17 | "out": "docs" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/curve/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.7.0 4 | 5 | ### Minor Changes 6 | 7 | - Update dependencies and monorepo. 8 | 9 | ## 1.4.3 10 | 11 | - Update README 12 | - Update LICENSE year 13 | 14 | ## 0.2.2 15 | 16 | - Moves unused curve functions out of @tlslides/core and into own repo. 17 | -------------------------------------------------------------------------------- /packages/curve/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": [ 4 | "node_modules", 5 | "**/*.test.tsx", 6 | "**/*.test.ts", 7 | "**/*.spec.tsx", 8 | "**/*.spec.ts", 9 | "src/test", 10 | "dist", 11 | "docs" 12 | ], 13 | "compilerOptions": { 14 | "composite": false, 15 | "incremental": false, 16 | "declaration": true, 17 | "declarationMap": true, 18 | "sourceMap": true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/curve/tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": [ 4 | "node_modules", 5 | "**/*.test.tsx", 6 | "**/*.test.ts", 7 | "**/*.spec.tsx", 8 | "**/*.spec.ts", 9 | "src/test", 10 | "dist", 11 | "docs" 12 | ], 13 | "compilerOptions": { 14 | "composite": false, 15 | "incremental": false, 16 | "declaration": true, 17 | "declarationMap": true, 18 | "sourceMap": true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/curve/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": "." 9 | }, 10 | "typedocOptions": { 11 | "entryPoints": ["src/index.ts"], 12 | "out": "docs" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/intersect/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.7.1 4 | 5 | ### Patch Changes 6 | 7 | - e8dd64ba: Fix text in multiplayer 8 | 9 | ## 1.7.0 10 | 11 | ### Minor Changes 12 | 13 | - Update dependencies and monorepo. 14 | 15 | ### Patch Changes 16 | 17 | - Updated dependencies 18 | - @tlslides/vec@1.7.0 19 | 20 | ## 1.4.3 21 | 22 | - Update README 23 | - Update LICENSE year 24 | 25 | ## 0.1.4 26 | 27 | - Fixes bug in `polyline`, adds `polygon` intersections. 28 | 29 | ## 0.1.0 30 | 31 | - Hello world. 32 | -------------------------------------------------------------------------------- /packages/intersect/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": [ 4 | "node_modules", 5 | "**/*.test.tsx", 6 | "**/*.test.ts", 7 | "**/*.spec.tsx", 8 | "**/*.spec.ts", 9 | "src/test", 10 | "dist", 11 | "docs" 12 | ], 13 | "compilerOptions": { 14 | "composite": false, 15 | "incremental": false, 16 | "declaration": true, 17 | "declarationMap": true, 18 | "sourceMap": true 19 | }, 20 | "references": [{ "path": "../vec" }] 21 | } 22 | -------------------------------------------------------------------------------- /packages/intersect/tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": [ 4 | "node_modules", 5 | "**/*.test.tsx", 6 | "**/*.test.ts", 7 | "**/*.spec.tsx", 8 | "**/*.spec.ts", 9 | "src/test", 10 | "dist", 11 | "docs" 12 | ], 13 | "compilerOptions": { 14 | "composite": false, 15 | "incremental": false, 16 | "declaration": true, 17 | "declarationMap": true, 18 | "sourceMap": true 19 | }, 20 | "references": [{ "path": "../vec" }] 21 | } 22 | -------------------------------------------------------------------------------- /packages/intersect/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "exclude": ["node_modules", "dist", "docs"], 4 | "compilerOptions": { 5 | "outDir": "./dist", 6 | "rootDir": "src", 7 | "baseUrl": "." 8 | }, 9 | "references": [{ "path": "../vec" }], 10 | "typedocOptions": { 11 | "entryPoints": ["src/index.ts"], 12 | "out": "docs" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/tldraw/src/Tldraw.spec.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { render, waitFor } from '@testing-library/react' 3 | import { Tldraw } from './Tldraw' 4 | 5 | describe('Tldraw', () => { 6 | test('mounts component and calls onMount', async () => { 7 | const onMount = jest.fn() 8 | render() 9 | await waitFor(onMount) 10 | }) 11 | 12 | test('mounts component and calls onMount when id is present', async () => { 13 | const onMount = jest.fn() 14 | render() 15 | await waitFor(onMount) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/BottomPanel/index.ts: -------------------------------------------------------------------------------- 1 | export * from './BottomPanel' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/ContextMenu/ContextMenu.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { ContextMenu } from './ContextMenu' 3 | import { renderWithContext } from '~test' 4 | 5 | describe('context menu', () => { 6 | test('mounts component without crashing', () => { 7 | renderWithContext( 8 | 9 |
Hello
10 |
11 | ) 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/ContextMenu/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ContextMenu' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Deck/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Deck' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/DeckContextMenu/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DeckContextMenu' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/FocusButton/FocusButton.tsx: -------------------------------------------------------------------------------- 1 | import { DotFilledIcon } from '@radix-ui/react-icons' 2 | import * as React from 'react' 3 | import { IconButton } from '~components/Primitives/IconButton/IconButton' 4 | import { styled } from '~styles' 5 | 6 | interface FocusButtonProps { 7 | onSelect: () => void 8 | } 9 | 10 | export function FocusButton({ onSelect }: FocusButtonProps) { 11 | return ( 12 | 13 | 14 | 15 | 16 | 17 | ) 18 | } 19 | 20 | const StyledButtonContainer = styled('div', { 21 | opacity: 1, 22 | zIndex: 100, 23 | backgroundColor: 'transparent', 24 | 25 | '& svg': { 26 | color: '$text', 27 | }, 28 | 29 | '&:hover svg': { 30 | color: '$text', 31 | }, 32 | }) 33 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/FocusButton/index.ts: -------------------------------------------------------------------------------- 1 | export * from './FocusButton' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Loading/index.ts: -------------------------------------------------------------------------------- 1 | export { Loading } from './Loading' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/Divider/Divider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { styled } from '~styles' 3 | 4 | export const Divider = styled('hr', { 5 | height: 1, 6 | marginTop: '$1', 7 | marginRight: '-$2', 8 | marginBottom: '$1', 9 | marginLeft: '-$2', 10 | border: 'none', 11 | borderBottom: '1px solid $hover', 12 | }) 13 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/Divider/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Divider' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/DropdownMenu/DMArrow.tsx: -------------------------------------------------------------------------------- 1 | import { Arrow } from '@radix-ui/react-dropdown-menu' 2 | import { breakpoints } from '~components/breakpoints' 3 | import { styled } from '~styles/stitches.config' 4 | 5 | export const DMArrow = styled(Arrow, { fill: '$panel', bp: breakpoints }) 6 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/DropdownMenu/DMDivider.tsx: -------------------------------------------------------------------------------- 1 | import { Separator } from '@radix-ui/react-dropdown-menu' 2 | import { styled } from '~styles/stitches.config' 3 | 4 | export const DMDivider = styled(Separator, { 5 | backgroundColor: '$hover', 6 | height: 1, 7 | marginTop: '$2', 8 | marginRight: '-$2', 9 | marginBottom: '$2', 10 | marginLeft: '-$2', 11 | }) 12 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/DropdownMenu/DMItem.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Item } from '@radix-ui/react-dropdown-menu' 3 | import { RowButton, RowButtonProps } from '~components/Primitives/RowButton' 4 | 5 | export function DMItem({ 6 | onSelect, 7 | id, 8 | ...rest 9 | }: RowButtonProps & { onSelect?: (event: Event) => void; id?: string }): JSX.Element { 10 | return ( 11 | 12 | 13 | 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/DropdownMenu/DMTriggerIcon.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Trigger } from '@radix-ui/react-dropdown-menu' 3 | import { ToolButton, ToolButtonProps } from '~components/Primitives/ToolButton' 4 | 5 | interface DMTriggerIconProps extends ToolButtonProps { 6 | children: React.ReactNode 7 | id?: string 8 | } 9 | 10 | export function DMTriggerIcon({ id, children, ...rest }: DMTriggerIconProps) { 11 | return ( 12 | 13 | {children} 14 | 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/DropdownMenu/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './DMArrow' 2 | export * from './DMItem' 3 | export * from './DMCheckboxItem' 4 | export * from './DMContent' 5 | export * from './DMDivider' 6 | export * from './DMRadioItem' 7 | export * from './DMSubMenu' 8 | export * from './DMTriggerIcon' 9 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/IconButton/index.ts: -------------------------------------------------------------------------------- 1 | export * from './IconButton' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/Kbd/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Kbd' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/MenuContent/MenuContent.ts: -------------------------------------------------------------------------------- 1 | import { styled } from '~styles' 2 | 3 | export const MenuContent = styled('div', { 4 | position: 'relative', 5 | overflow: 'hidden', 6 | userSelect: 'none', 7 | display: 'flex', 8 | flexDirection: 'column', 9 | zIndex: 180, 10 | minWidth: 180, 11 | pointerEvents: 'all', 12 | backgroundColor: '$panel', 13 | boxShadow: '$panel', 14 | padding: '$2 $2', 15 | borderRadius: '$3', 16 | font: '$ui', 17 | variants: { 18 | size: { 19 | small: { 20 | minWidth: 72, 21 | }, 22 | }, 23 | }, 24 | }) 25 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/MenuContent/index.ts: -------------------------------------------------------------------------------- 1 | export * from './MenuContent' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/Panel/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Panel' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/RowButton/index.ts: -------------------------------------------------------------------------------- 1 | export * from './RowButton' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/SmallIcon/SmallIcon.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from '~styles' 2 | 3 | export const SmallIcon = styled('div', { 4 | height: '100%', 5 | borderRadius: '4px', 6 | marginRight: '1px', 7 | width: 'fit-content', 8 | display: 'grid', 9 | alignItems: 'center', 10 | justifyContent: 'center', 11 | outline: 'none', 12 | border: 'none', 13 | pointerEvents: 'all', 14 | cursor: 'pointer', 15 | color: 'currentColor', 16 | 17 | '& svg': { 18 | height: 16, 19 | width: 16, 20 | strokeWidth: 1, 21 | }, 22 | 23 | '& > *': { 24 | gridRow: 1, 25 | gridColumn: 1, 26 | }, 27 | }) 28 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/SmallIcon/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SmallIcon' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/ToolButton/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ToolButton' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/Tooltip/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Tooltip' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/icons/BoxIcon.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | export function BoxIcon({ 4 | fill = 'none', 5 | stroke = 'currentColor', 6 | strokeWidth = 2, 7 | }: { 8 | fill?: string 9 | stroke?: string 10 | strokeWidth?: number 11 | }): JSX.Element { 12 | return ( 13 | 22 | 23 | 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/icons/CircleIcon.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | export function CircleIcon( 4 | props: Pick, 'strokeWidth' | 'stroke' | 'fill'> & { 5 | size: number 6 | } 7 | ) { 8 | const { size = 16, ...rest } = props 9 | return ( 10 | 11 | 12 | 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/icons/DashDashedIcon.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | export function DashDashedIcon(): JSX.Element { 4 | return ( 5 | 6 | 15 | 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/icons/DashDottedIcon.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | const dottedDasharray = `${50.26548 * 0.025} ${50.26548 * 0.1}` 4 | 5 | export function DashDottedIcon(): JSX.Element { 6 | return ( 7 | 8 | 17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/icons/DashSolidIcon.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | export function DashSolidIcon(): JSX.Element { 4 | return ( 5 | 6 | 7 | 8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/icons/EraserIcon.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | export function EraserIcon(): JSX.Element { 4 | return ( 5 | 6 | 10 | 18 | 19 | 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/icons/HeartIcon.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | export function HeartIcon() { 4 | return ( 5 | 6 | 12 | 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/icons/IsFilledIcon.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | export function IsFilledIcon(): JSX.Element { 4 | return ( 5 | 6 | 16 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/icons/LineIcon.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | export function LineIcon() { 4 | return ( 5 | 12 | 13 | 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/icons/SizeLargeIcon.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | export function SizeLargeIcon(props: React.SVGProps): JSX.Element { 4 | return ( 5 | 13 | 14 | 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/Primitives/icons/index.ts: -------------------------------------------------------------------------------- 1 | export * from './BoxIcon' 2 | export * from './CircleIcon' 3 | export * from './DashDashedIcon' 4 | export * from './DashDottedIcon' 5 | export * from './DashDrawIcon' 6 | export * from './DashSolidIcon' 7 | export * from './IsFilledIcon' 8 | export * from './RedoIcon' 9 | export * from './TrashIcon' 10 | export * from './UndoIcon' 11 | export * from './SizeSmallIcon' 12 | export * from './SizeMediumIcon' 13 | export * from './SizeLargeIcon' 14 | export * from './EraserIcon' 15 | export * from './MultiplayerIcon' 16 | export * from './DiscordIcon' 17 | export * from './LineIcon' 18 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/ReadOnlyEditor/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ReadOnlyEditor' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/ToolsPanel/DeleteButton.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Tooltip } from '~components/Primitives/Tooltip' 3 | import { useTldrawApp } from '~hooks' 4 | import { ToolButton } from '~components/Primitives/ToolButton' 5 | import { TrashIcon } from '~components/Primitives/icons' 6 | 7 | export function DeleteButton(): JSX.Element { 8 | const app = useTldrawApp() 9 | 10 | const handleDelete = React.useCallback(() => { 11 | app.delete() 12 | }, [app]) 13 | 14 | const hasSelection = app.useStore( 15 | (s) => 16 | s.appState.status === 'idle' && 17 | s.document.pageStates[s.appState.currentPageId].selectedIds.length > 0 18 | ) 19 | 20 | return ( 21 | 22 | 23 | 24 | 25 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/ToolsPanel/LockButton.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { LockClosedIcon, LockOpen1Icon } from '@radix-ui/react-icons' 3 | import { Tooltip } from '~components/Primitives/Tooltip' 4 | import { useTldrawApp } from '~hooks' 5 | import { ToolButton } from '~components/Primitives/ToolButton' 6 | import type { TDSnapshot } from '~types' 7 | 8 | const isToolLockedSelector = (s: TDSnapshot) => s.appState.isToolLocked 9 | 10 | export function LockButton(): JSX.Element { 11 | const app = useTldrawApp() 12 | 13 | const isToolLocked = app.useStore(isToolLockedSelector) 14 | 15 | return ( 16 | 17 | 18 | {isToolLocked ? : } 19 | 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/ToolsPanel/ToolsPanel.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { ToolsPanel } from './ToolsPanel' 3 | import { renderWithContext } from '~test' 4 | 5 | describe('tools panel', () => { 6 | test('mounts component without crashing', () => { 7 | renderWithContext( void null} />) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/ToolsPanel/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ToolsPanel' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/TopPanel/Menu/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimeshnayaju/tlslides/818ef271b94ac8acd10806bf1adab246604a147c/packages/tldraw/src/components/TopPanel/Menu/index.ts -------------------------------------------------------------------------------- /packages/tldraw/src/components/TopPanel/MultiplayerMenu/index.ts: -------------------------------------------------------------------------------- 1 | export * from './MultiplayerMenu' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/TopPanel/PageMenu/index.ts: -------------------------------------------------------------------------------- 1 | export * from './PageMenu' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/TopPanel/PageOptionsDialog/index.ts: -------------------------------------------------------------------------------- 1 | export * from './PageOptionsDialog' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/TopPanel/PreferencesMenu/index.ts: -------------------------------------------------------------------------------- 1 | export * from './PreferencesMenu' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/TopPanel/StyleMenu/StyleMenu.test.tsx: -------------------------------------------------------------------------------- 1 | describe('the style menu', () => { 2 | test.todo('Correctly sets the style properties when shapes are selected') 3 | test.todo('Correctly sets the style properties when nothing is selected') 4 | }) 5 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/TopPanel/StyleMenu/index.ts: -------------------------------------------------------------------------------- 1 | export * from './StyleMenu' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/TopPanel/ZoomMenu/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ZoomMenu' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/TopPanel/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TopPanel' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/breakpoints.tsx: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------- */ 2 | /* Breakpoints */ 3 | /* -------------------------------------------------- */ 4 | 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 6 | export const breakpoints: any = { 7 | '@initial': 'mobile', 8 | '@micro': 'micro', 9 | '@sm': 'small', 10 | '@md': 'medium', 11 | '@lg': 'large', 12 | } 13 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/preventEvent.ts: -------------------------------------------------------------------------------- 1 | export const preventEvent = (e: Event) => e.preventDefault() 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/components/stopPropagation.ts: -------------------------------------------------------------------------------- 1 | import type React from 'react' 2 | 3 | export const stopPropagation = (e: KeyboardEvent | React.SyntheticEvent) => 4 | e.stopPropagation() 5 | -------------------------------------------------------------------------------- /packages/tldraw/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useKeyboardShortcuts' 2 | export * from './useTldrawApp' 3 | export * from './useTheme' 4 | export * from './useStylesheet' 5 | export * from './useFileSystemHandlers' 6 | export * from './useFileSystem' 7 | -------------------------------------------------------------------------------- /packages/tldraw/src/hooks/useStylesheet.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | const styles = new Map() 4 | 5 | const UID = `Tldraw-fonts` 6 | const WEBFONT_URL = 7 | 'https://fonts.googleapis.com/css2?family=Caveat+Brush&family=Source+Code+Pro&family=Source+Sans+Pro&family=Crimson+Pro&display=block' 8 | const CSS = ` 9 | @import url(''); 10 | ` 11 | 12 | export function useStylesheet() { 13 | React.useLayoutEffect(() => { 14 | if (styles.get(UID)) return 15 | const style = document.createElement('style') 16 | style.innerHTML = `@import url('${WEBFONT_URL}');` 17 | style.setAttribute('id', UID) 18 | document.head.appendChild(style) 19 | styles.set(UID, style) 20 | 21 | return () => { 22 | if (style && document.head.contains(style)) { 23 | document.head.removeChild(style) 24 | styles.delete(UID) 25 | } 26 | } 27 | }, [UID, CSS]) 28 | } 29 | -------------------------------------------------------------------------------- /packages/tldraw/src/hooks/useTheme.ts: -------------------------------------------------------------------------------- 1 | import type { TDSnapshot, Theme } from '~types' 2 | import { useTldrawApp } from './useTldrawApp' 3 | 4 | const themeSelector = (data: TDSnapshot): Theme => (data.settings.isDarkMode ? 'dark' : 'light') 5 | 6 | export function useTheme() { 7 | const app = useTldrawApp() 8 | const theme: Theme = app.useStore(themeSelector) 9 | 10 | return { 11 | theme, 12 | toggle: app.toggleDarkMode, 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/tldraw/src/hooks/useTldrawApp.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { TldrawApp } from '~state' 3 | 4 | export const TldrawContext = React.createContext({} as TldrawApp) 5 | 6 | export function useTldrawApp() { 7 | const context = React.useContext(TldrawContext) 8 | return context 9 | } 10 | -------------------------------------------------------------------------------- /packages/tldraw/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Tldraw' 2 | export * from './types' 3 | export * from './state/shapes' 4 | export { TldrawApp } from './state' 5 | export { useFileSystem } from './hooks' 6 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/StateManager/index.ts: -------------------------------------------------------------------------------- 1 | export * from './StateManager' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/alignShapes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './alignShapes' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/changePage/changePage.spec.ts: -------------------------------------------------------------------------------- 1 | import { mockDocument, TldrawTestApp } from '~test' 2 | 3 | describe('Change page command', () => { 4 | const app = new TldrawTestApp() 5 | 6 | it('does, undoes and redoes command', () => { 7 | app.loadDocument(mockDocument) 8 | 9 | const initialId = app.page.id 10 | 11 | app.createPage() 12 | 13 | const nextId = app.page.id 14 | 15 | app.changePage(initialId) 16 | 17 | expect(app.page.id).toBe(initialId) 18 | 19 | app.changePage(nextId) 20 | 21 | expect(app.page.id).toBe(nextId) 22 | 23 | app.undo() 24 | 25 | expect(app.page.id).toBe(initialId) 26 | 27 | app.redo() 28 | 29 | expect(app.page.id).toBe(nextId) 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/changePage/changePage.ts: -------------------------------------------------------------------------------- 1 | import type { TldrawCommand } from '~types' 2 | import type { TldrawApp } from '../../internal' 3 | 4 | export function changePage(app: TldrawApp, pageId: string): TldrawCommand { 5 | return { 6 | id: 'change_page', 7 | before: { 8 | appState: { 9 | currentPageId: app.currentPageId, 10 | }, 11 | }, 12 | after: { 13 | appState: { 14 | currentPageId: pageId, 15 | }, 16 | }, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/changePage/index.ts: -------------------------------------------------------------------------------- 1 | export * from './changePage' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/createPage/createPage.spec.ts: -------------------------------------------------------------------------------- 1 | import { mockDocument, TldrawTestApp } from '~test' 2 | 3 | describe('Create page command', () => { 4 | const app = new TldrawTestApp() 5 | 6 | it('does, undoes and redoes command', () => { 7 | app.loadDocument(mockDocument) 8 | 9 | const initialId = app.page.id 10 | const initialPageState = app.pageState 11 | 12 | app.createPage() 13 | 14 | const nextId = app.page.id 15 | const nextPageState = app.pageState 16 | 17 | expect(Object.keys(app.document.pages).length).toBe(2) 18 | expect(app.page.id).toBe(nextId) 19 | expect(app.pageState).toEqual(nextPageState) 20 | 21 | app.undo() 22 | 23 | expect(Object.keys(app.document.pages).length).toBe(1) 24 | expect(app.page.id).toBe(initialId) 25 | expect(app.pageState).toEqual(initialPageState) 26 | 27 | app.redo() 28 | 29 | expect(Object.keys(app.document.pages).length).toBe(2) 30 | expect(app.page.id).toBe(nextId) 31 | expect(app.pageState).toEqual(nextPageState) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/createPage/index.ts: -------------------------------------------------------------------------------- 1 | export * from './createPage' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/createShapes/createShapes.spec.ts: -------------------------------------------------------------------------------- 1 | import { mockDocument, TldrawTestApp } from '~test' 2 | 3 | describe('Create command', () => { 4 | const app = new TldrawTestApp() 5 | 6 | beforeEach(() => { 7 | app.loadDocument(mockDocument) 8 | }) 9 | 10 | describe('when no shape is provided', () => { 11 | it('does nothing', () => { 12 | const initialState = app.state 13 | app.create() 14 | 15 | const currentState = app.state 16 | 17 | expect(currentState).toEqual(initialState) 18 | }) 19 | }) 20 | 21 | it('does, undoes and redoes command', () => { 22 | const shape = { ...app.getShape('rect1'), id: 'rect4' } 23 | app.create([shape]) 24 | 25 | expect(app.getShape('rect4')).toBeTruthy() 26 | 27 | app.undo() 28 | 29 | expect(app.getShape('rect4')).toBe(undefined) 30 | 31 | app.redo() 32 | 33 | expect(app.getShape('rect4')).toBeTruthy() 34 | }) 35 | 36 | it.todo('Creates bindings') 37 | }) 38 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/createShapes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './createShapes' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/deletePage/deletePage.spec.ts: -------------------------------------------------------------------------------- 1 | import { mockDocument, TldrawTestApp } from '~test' 2 | 3 | describe('Delete page', () => { 4 | const app = new TldrawTestApp() 5 | 6 | beforeEach(() => { 7 | app.loadDocument(mockDocument) 8 | }) 9 | 10 | describe('when there are no pages in the current document', () => { 11 | it('does nothing', () => { 12 | app.resetDocument() 13 | const initialState = app.state 14 | app.deletePage('page1') 15 | const currentState = app.state 16 | 17 | expect(currentState).toEqual(initialState) 18 | }) 19 | }) 20 | 21 | it('does, undoes and redoes command', () => { 22 | const initialId = app.currentPageId 23 | 24 | app.createPage() 25 | 26 | const nextId = app.currentPageId 27 | 28 | app.deletePage() 29 | 30 | expect(app.currentPageId).toBe(initialId) 31 | 32 | app.undo() 33 | 34 | expect(app.currentPageId).toBe(nextId) 35 | 36 | app.redo() 37 | 38 | expect(app.currentPageId).toBe(initialId) 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/deletePage/index.ts: -------------------------------------------------------------------------------- 1 | export * from './deletePage' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/deleteShapes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './deleteShapes' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/distributeShapes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './distributeShapes' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/duplicatePage/duplicatePage.spec.ts: -------------------------------------------------------------------------------- 1 | import { mockDocument, TldrawTestApp } from '~test' 2 | 3 | describe('Duplicate page command', () => { 4 | const app = new TldrawTestApp() 5 | 6 | it('does, undoes and redoes command', () => { 7 | app.loadDocument(mockDocument) 8 | 9 | const initialId = app.page.id 10 | 11 | app.duplicatePage(app.currentPageId) 12 | 13 | const nextId = app.page.id 14 | 15 | app.undo() 16 | 17 | expect(app.page.id).toBe(initialId) 18 | 19 | app.redo() 20 | 21 | expect(app.page.id).toBe(nextId) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/duplicatePage/index.ts: -------------------------------------------------------------------------------- 1 | export * from './duplicatePage' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/duplicateShapes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './duplicateShapes' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/flipShapes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './flipShapes' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/groupShapes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './groupShapes' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/index.ts: -------------------------------------------------------------------------------- 1 | export * from './alignShapes' 2 | export * from './changePage' 3 | export * from './createPage' 4 | export * from './createShapes' 5 | export * from './deletePage' 6 | export * from './deleteShapes' 7 | export * from './distributeShapes' 8 | export * from './duplicatePage' 9 | export * from './duplicateShapes' 10 | export * from './flipShapes' 11 | export * from './groupShapes' 12 | export * from './moveShapesToPage' 13 | export * from './reorderShapes' 14 | export * from './renamePage' 15 | export * from './resetBounds' 16 | export * from './rotateShapes' 17 | export * from './stretchShapes' 18 | export * from './styleShapes' 19 | export * from './toggleShapesDecoration' 20 | export * from './toggleShapesProp' 21 | export * from './translateShapes' 22 | export * from './ungroupShapes' 23 | export * from './updateShapes' 24 | export * from './setShapesProps' 25 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/moveShapesToPage/index.ts: -------------------------------------------------------------------------------- 1 | export * from './moveShapesToPage' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/renamePage/index.ts: -------------------------------------------------------------------------------- 1 | export * from './renamePage' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/renamePage/renamePage.spec.ts: -------------------------------------------------------------------------------- 1 | import { mockDocument, TldrawTestApp } from '~test' 2 | 3 | describe('Rename page command', () => { 4 | const app = new TldrawTestApp() 5 | 6 | it('does, undoes and redoes command', () => { 7 | app.loadDocument(mockDocument) 8 | 9 | const initialId = app.page.id 10 | const initialName = app.page.name 11 | 12 | app.renamePage(initialId, 'My Special Page') 13 | 14 | expect(app.page.name).toBe('My Special Page') 15 | 16 | app.undo() 17 | 18 | expect(app.page.name).toBe(initialName) 19 | 20 | app.redo() 21 | 22 | expect(app.page.name).toBe('My Special Page') 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/renamePage/renamePage.ts: -------------------------------------------------------------------------------- 1 | import type { TldrawCommand } from '~types' 2 | import type { TldrawApp } from '../../internal' 3 | 4 | export function renamePage(app: TldrawApp, pageId: string, name: string): TldrawCommand { 5 | const { page } = app 6 | 7 | return { 8 | id: 'rename_page', 9 | before: { 10 | document: { 11 | pages: { 12 | [pageId]: { name: page.name }, 13 | }, 14 | }, 15 | }, 16 | after: { 17 | document: { 18 | pages: { 19 | [pageId]: { name: name }, 20 | }, 21 | }, 22 | }, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/reorderShapes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './reorderShapes' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/resetBounds/index.ts: -------------------------------------------------------------------------------- 1 | export * from './resetBounds' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/rotateShapes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './rotateShapes' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/setShapesProps/index.ts: -------------------------------------------------------------------------------- 1 | export * from './setShapesProps' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/setShapesProps/setShapesProps.spec.ts: -------------------------------------------------------------------------------- 1 | describe('Set shapes props command', () => { 2 | it.todo('sets the props of the provided shapes') 3 | }) 4 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/stretchShapes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './stretchShapes' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/styleShapes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './styleShapes' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/toggleShapesDecoration/index.ts: -------------------------------------------------------------------------------- 1 | export * from './toggleShapesDecoration' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/toggleShapesProp/index.ts: -------------------------------------------------------------------------------- 1 | export * from './toggleShapesProp' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/translateShapes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './translateShapes' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/ungroupShapes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ungroupShapes' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/updateShapes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './updateShapes' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/updateShapes/updateShapes.spec.ts: -------------------------------------------------------------------------------- 1 | import { mockDocument, TldrawTestApp } from '~test' 2 | 3 | describe('Update command', () => { 4 | const app = new TldrawTestApp() 5 | 6 | beforeEach(() => { 7 | app.loadDocument(mockDocument) 8 | }) 9 | 10 | describe('when no shape is selected', () => { 11 | it('does nothing', () => { 12 | const initialState = app.state 13 | app.updateShapes() 14 | const currentState = app.state 15 | 16 | expect(currentState).toEqual(initialState) 17 | }) 18 | }) 19 | 20 | it('does, undoes and redoes command', () => { 21 | app.updateShapes({ id: 'rect1', point: [100, 100] }) 22 | 23 | expect(app.getShape('rect1').point).toStrictEqual([100, 100]) 24 | 25 | app.undo() 26 | 27 | expect(app.getShape('rect1').point).toStrictEqual([0, 0]) 28 | 29 | app.redo() 30 | 31 | expect(app.getShape('rect1').point).toStrictEqual([100, 100]) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/commands/updateShapes/updateShapes.ts: -------------------------------------------------------------------------------- 1 | import type { TldrawCommand, TDShape } from '~types' 2 | import { TLDR } from '~state/TLDR' 3 | import type { TldrawApp } from '../../internal' 4 | 5 | export function updateShapes( 6 | app: TldrawApp, 7 | updates: ({ id: string } & Partial)[], 8 | pageId: string 9 | ): TldrawCommand { 10 | const ids = updates.map((update) => update.id) 11 | 12 | const change = TLDR.mutateShapes( 13 | app.state, 14 | ids.filter((id) => !app.getShape(id, pageId).isLocked), 15 | (_shape, i) => updates[i], 16 | pageId 17 | ) 18 | 19 | return { 20 | id: 'update', 21 | before: { 22 | document: { 23 | pages: { 24 | [pageId]: { 25 | shapes: change.before, 26 | }, 27 | }, 28 | }, 29 | }, 30 | after: { 31 | document: { 32 | pages: { 33 | [pageId]: { 34 | shapes: change.after, 35 | }, 36 | }, 37 | }, 38 | }, 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/data/browser-fs-access/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | // @license © 2020 Google LLC. Licensed under the Apache License, Version 2.0. 17 | 18 | /** 19 | * @module browser-fs-access 20 | */ 21 | export { fileOpen } from './file-open.js' 22 | export { directoryOpen } from './directory-open.js' 23 | export { fileSave } from './file-save.js' 24 | export { default as supported } from './supported.js' 25 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/data/filesystem.spec.ts: -------------------------------------------------------------------------------- 1 | describe('when saving data to the file system', () => { 2 | it.todo('saves a new file in the filesystem') 3 | it.todo('saves a new file in the filesystem') 4 | }) 5 | 6 | describe('when opening files from file system', () => { 7 | it.todo('opens a file and loads it into the document') 8 | it.todo('opens an older file, migrates it, and loads it into the document') 9 | it.todo('opens a corrupt file, tries to fix it, and fails without crashing') 10 | }) 11 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/data/index.ts: -------------------------------------------------------------------------------- 1 | export * from './migrate' 2 | export * from './filesystem' 3 | export * from './browser-fs-access' 4 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/data/migrate.spec.ts: -------------------------------------------------------------------------------- 1 | import type { TDDocument } from '~types' 2 | import { TldrawApp } from '~state' 3 | import oldDoc from '~test/documents/old-doc' 4 | import oldDoc2 from '~test/documents/old-doc-2' 5 | 6 | describe('When migrating bindings', () => { 7 | it('migrates a document without a version', () => { 8 | new TldrawApp().loadDocument(oldDoc as unknown as TDDocument) 9 | }) 10 | 11 | it('migrates a document with an older version', () => { 12 | const app = new TldrawApp().loadDocument(oldDoc2 as unknown as TDDocument) 13 | expect(app.getShape('d7ab0a49-3cb3-43ae-3d83-f5cf2f4a510a').style.color).toBe('black') 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/index.ts: -------------------------------------------------------------------------------- 1 | import './internal' 2 | 3 | export * from './TldrawApp' 4 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/internal.ts: -------------------------------------------------------------------------------- 1 | export * from './TldrawApp' 2 | export * from './sessions' 3 | export * from './commands' 4 | export * from './tools' 5 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/sessions/ArrowSession/__snapshots__/ArrowSession.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Arrow session arrow binding binds on the inside of a shape while alt is held 1`] = ` 4 | Array [ 5 | 0.76, 6 | 0.09, 7 | ] 8 | `; 9 | 10 | exports[`Arrow session arrow binding snaps to the inside center when the point is close to the center 1`] = ` 11 | Array [ 12 | 0.81, 13 | 0.19, 14 | ] 15 | `; 16 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/sessions/ArrowSession/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ArrowSession' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/sessions/BaseSession.ts: -------------------------------------------------------------------------------- 1 | import type { TLPerformanceMode } from '@tlslides/core' 2 | import type { SessionType, TldrawCommand, TldrawPatch } from '~types' 3 | import type { TldrawApp } from '../internal' 4 | 5 | export abstract class BaseSession { 6 | abstract type: SessionType 7 | abstract performanceMode: TLPerformanceMode | undefined 8 | constructor(public app: TldrawApp) {} 9 | abstract start: () => TldrawPatch | undefined 10 | abstract update: () => TldrawPatch | undefined 11 | abstract complete: () => TldrawPatch | TldrawCommand | undefined 12 | abstract cancel: () => TldrawPatch | undefined 13 | } 14 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/sessions/BrushSession/index.ts: -------------------------------------------------------------------------------- 1 | export * from './BrushSession' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/sessions/DrawSession/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DrawSession' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/sessions/EraseSession/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EraseSession' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/sessions/GridSession/index.ts: -------------------------------------------------------------------------------- 1 | export * from './GridSession' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/sessions/HandleSession/index.ts: -------------------------------------------------------------------------------- 1 | export * from './HandleSession' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/sessions/RotateSession/index.ts: -------------------------------------------------------------------------------- 1 | export * from './RotateSession' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/sessions/TransformSession/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TransformSession' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/sessions/TransformSingleSession/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TransformSingleSession' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/sessions/TranslateLabelSession/TranslateLabelSession.spec.ts: -------------------------------------------------------------------------------- 1 | import { mockDocument, TldrawTestApp } from '~test' 2 | import { SessionType, TDShapeType, TDStatus } from '~types' 3 | 4 | describe('Translate label session', () => { 5 | it.todo('begins, updateSession') 6 | it.todo('cancels session') 7 | }) 8 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/sessions/TranslateLabelSession/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TranslateLabelSession' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/sessions/TranslateSession/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TranslateSession' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/ArrowUtil/components/ArrowHead.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | export interface ArrowheadProps { 4 | left: number[] 5 | middle: number[] 6 | right: number[] 7 | stroke: string 8 | strokeWidth: number 9 | } 10 | 11 | export function Arrowhead({ left, middle, right, stroke, strokeWidth }: ArrowheadProps) { 12 | return ( 13 | 14 | 15 | 24 | 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/ArrowUtil/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ArrowUtil' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/DrawUtil/DrawUtil.spec.tsx: -------------------------------------------------------------------------------- 1 | import { Draw } from '..' 2 | 3 | describe('Draw shape', () => { 4 | it('Creates a shape', () => { 5 | expect(Draw.create({ id: 'draw' })).toMatchSnapshot('draw') 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/DrawUtil/__snapshots__/DrawUtil.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Draw shape Creates a shape: draw 1`] = ` 4 | Object { 5 | "childIndex": 1, 6 | "id": "draw", 7 | "isComplete": false, 8 | "name": "Draw", 9 | "parentId": "page", 10 | "point": Array [ 11 | 0, 12 | 0, 13 | ], 14 | "points": Array [], 15 | "rotation": 0, 16 | "style": Object { 17 | "color": "black", 18 | "dash": "draw", 19 | "isFilled": false, 20 | "scale": 1, 21 | "size": "small", 22 | }, 23 | "type": "draw", 24 | } 25 | `; 26 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/DrawUtil/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DrawUtil' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/EllipseUtil/EllipseUtil.spec.tsx: -------------------------------------------------------------------------------- 1 | import { Ellipse } from '..' 2 | 3 | describe('Ellipse shape', () => { 4 | it('Creates a shape', () => { 5 | expect(Ellipse.create({ id: 'ellipse' })).toMatchSnapshot('ellipse') 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/EllipseUtil/__snapshots__/EllipseUtil.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Ellipse shape Creates a shape: ellipse 1`] = ` 4 | Object { 5 | "childIndex": 1, 6 | "id": "ellipse", 7 | "label": "", 8 | "labelPoint": Array [ 9 | 0.5, 10 | 0.5, 11 | ], 12 | "name": "Ellipse", 13 | "parentId": "page", 14 | "point": Array [ 15 | 0, 16 | 0, 17 | ], 18 | "radius": Array [ 19 | 1, 20 | 1, 21 | ], 22 | "rotation": 0, 23 | "style": Object { 24 | "color": "black", 25 | "dash": "draw", 26 | "isFilled": false, 27 | "scale": 1, 28 | "size": "small", 29 | }, 30 | "type": "ellipse", 31 | } 32 | `; 33 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/EllipseUtil/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EllipseUtil' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/GroupUtil/GroupUtil.spec.tsx: -------------------------------------------------------------------------------- 1 | import { Group } from '..' 2 | 3 | describe('Group shape', () => { 4 | it('Creates a shape', () => { 5 | expect(Group.create({ id: 'group' })).toMatchSnapshot('group') 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/GroupUtil/__snapshots__/GroupUtil.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Group shape Creates a shape: group 1`] = ` 4 | Object { 5 | "childIndex": 1, 6 | "children": Array [], 7 | "id": "group", 8 | "name": "Group", 9 | "parentId": "page", 10 | "point": Array [ 11 | 0, 12 | 0, 13 | ], 14 | "rotation": 0, 15 | "size": Array [ 16 | 100, 17 | 100, 18 | ], 19 | "style": Object { 20 | "color": "black", 21 | "dash": "draw", 22 | "isFilled": false, 23 | "scale": 1, 24 | "size": "small", 25 | }, 26 | "type": "group", 27 | } 28 | `; 29 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/GroupUtil/index.ts: -------------------------------------------------------------------------------- 1 | export * from './GroupUtil' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/ImageUtil/ImageUtil.spec.tsx: -------------------------------------------------------------------------------- 1 | import { Image } from '..' 2 | 3 | describe('Image shape', () => { 4 | it('Creates a shape', () => { 5 | expect(Image.create({ id: 'image' })).toMatchSnapshot('image') 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/ImageUtil/__snapshots__/ImageUtil.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Image shape Creates a shape: image 1`] = ` 4 | Object { 5 | "assetId": "assetId", 6 | "childIndex": 1, 7 | "id": "image", 8 | "name": "Image", 9 | "parentId": "page", 10 | "point": Array [ 11 | 0, 12 | 0, 13 | ], 14 | "rotation": 0, 15 | "size": Array [ 16 | 1, 17 | 1, 18 | ], 19 | "style": Object { 20 | "color": "black", 21 | "dash": "draw", 22 | "isFilled": true, 23 | "scale": 1, 24 | "size": "small", 25 | }, 26 | "type": "image", 27 | } 28 | `; 29 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/ImageUtil/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ImageUtil' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/RectangleUtil/RectangleUtil.spec.tsx: -------------------------------------------------------------------------------- 1 | import { Rectangle } from '..' 2 | 3 | describe('Rectangle shape', () => { 4 | it('Creates a shape', () => { 5 | expect(Rectangle.create({ id: 'rectangle' })).toMatchSnapshot('rectangle') 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/RectangleUtil/__snapshots__/RectangleUtil.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Rectangle shape Creates a shape: rectangle 1`] = ` 4 | Object { 5 | "childIndex": 1, 6 | "id": "rectangle", 7 | "label": "", 8 | "labelPoint": Array [ 9 | 0.5, 10 | 0.5, 11 | ], 12 | "name": "Rectangle", 13 | "parentId": "page", 14 | "point": Array [ 15 | 0, 16 | 0, 17 | ], 18 | "rotation": 0, 19 | "size": Array [ 20 | 1, 21 | 1, 22 | ], 23 | "style": Object { 24 | "color": "black", 25 | "dash": "draw", 26 | "isFilled": false, 27 | "scale": 1, 28 | "size": "small", 29 | }, 30 | "type": "rectangle", 31 | } 32 | `; 33 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/RectangleUtil/components/BindingIndicator.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { BINDING_DISTANCE } from '~constants' 3 | 4 | interface BindingIndicatorProps { 5 | strokeWidth: number 6 | size: number[] 7 | } 8 | export function BindingIndicator({ strokeWidth, size }: BindingIndicatorProps) { 9 | return ( 10 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/RectangleUtil/index.ts: -------------------------------------------------------------------------------- 1 | export * from './RectangleUtil' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/StickyUtil/StickyUtil.spec.tsx: -------------------------------------------------------------------------------- 1 | import { Sticky } from '..' 2 | 3 | describe('Post-It shape', () => { 4 | it('Creates a shape', () => { 5 | expect(Sticky.create).toBeDefined() 6 | // expect(Sticky.create({ id: 'sticky' })).toMatchSnapshot('sticky') 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/StickyUtil/index.ts: -------------------------------------------------------------------------------- 1 | export * from './StickyUtil' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/TextUtil/TextUtil.spec.tsx: -------------------------------------------------------------------------------- 1 | import { Text } from '..' 2 | 3 | describe('Text shape', () => { 4 | it('Creates a shape', () => { 5 | expect(Text.create({ id: 'text' })).toMatchSnapshot('text') 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/TextUtil/__snapshots__/TextUtil.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Text shape Creates a shape: text 1`] = ` 4 | Object { 5 | "childIndex": 1, 6 | "id": "text", 7 | "name": "Text", 8 | "parentId": "page", 9 | "point": Array [ 10 | 0, 11 | 0, 12 | ], 13 | "rotation": 0, 14 | "style": Object { 15 | "color": "black", 16 | "dash": "draw", 17 | "font": "script", 18 | "isFilled": false, 19 | "scale": 1, 20 | "size": "small", 21 | "textAlign": "middle", 22 | }, 23 | "text": " ", 24 | "type": "text", 25 | } 26 | `; 27 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/TextUtil/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TextUtil' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/TriangleUtil/TriangleUtil.spec.tsx: -------------------------------------------------------------------------------- 1 | import { Triangle } from '..' 2 | 3 | describe('Triangle shape', () => { 4 | it('Creates a shape', () => { 5 | expect(Triangle.create({ id: 'triangle' })).toMatchSnapshot('triangle') 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/TriangleUtil/__snapshots__/TriangleUtil.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Triangle shape Creates a shape: triangle 1`] = ` 4 | Object { 5 | "childIndex": 1, 6 | "id": "triangle", 7 | "label": "", 8 | "labelPoint": Array [ 9 | 0.5, 10 | 0.5, 11 | ], 12 | "name": "Triangle", 13 | "parentId": "page", 14 | "point": Array [ 15 | 0, 16 | 0, 17 | ], 18 | "rotation": 0, 19 | "size": Array [ 20 | 1, 21 | 1, 22 | ], 23 | "style": Object { 24 | "color": "black", 25 | "dash": "draw", 26 | "isFilled": false, 27 | "scale": 1, 28 | "size": "small", 29 | }, 30 | "type": "triangle", 31 | } 32 | `; 33 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/TriangleUtil/components/TriangleBindingIndicator.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { BINDING_DISTANCE } from '~constants' 3 | import { getTrianglePoints } from '../triangleHelpers' 4 | 5 | interface TriangleBindingIndicatorProps { 6 | size: number[] 7 | } 8 | 9 | export function TriangleBindingIndicator({ size }: TriangleBindingIndicatorProps) { 10 | const trianglePoints = getTrianglePoints(size).join() 11 | return ( 12 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/TriangleUtil/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TriangleUtil' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/VideoUtil/VideoUtil.spec.tsx: -------------------------------------------------------------------------------- 1 | import { Video } from '..' 2 | 3 | describe('Video shape', () => { 4 | it('Creates a shape', () => { 5 | expect(Video.create({ id: 'video' })).toMatchSnapshot('video') 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/VideoUtil/__snapshots__/VideoUtil.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Video shape Creates a shape: video 1`] = ` 4 | Object { 5 | "assetId": "assetId", 6 | "childIndex": 1, 7 | "currentTime": 0, 8 | "id": "video", 9 | "isPlaying": true, 10 | "name": "Video", 11 | "parentId": "page", 12 | "point": Array [ 13 | 0, 14 | 0, 15 | ], 16 | "rotation": 0, 17 | "size": Array [ 18 | 1, 19 | 1, 20 | ], 21 | "style": Object { 22 | "color": "black", 23 | "dash": "draw", 24 | "isFilled": false, 25 | "scale": 1, 26 | "size": "small", 27 | }, 28 | "type": "video", 29 | } 30 | `; 31 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/VideoUtil/index.ts: -------------------------------------------------------------------------------- 1 | export * from './VideoUtil' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/about-shape-utils.md: -------------------------------------------------------------------------------- 1 | # Shape Utils 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/shared/getBoundsRectangle.ts: -------------------------------------------------------------------------------- 1 | import { TLBounds, TLShape, Utils } from '@tlslides/core' 2 | 3 | /** 4 | * Find the bounds of a rectangular shape. 5 | * @param shape 6 | * @param boundsCache 7 | */ 8 | export function getBoundsRectangle( 9 | shape: T, 10 | boundsCache: WeakMap 11 | ) { 12 | const bounds = Utils.getFromCache(boundsCache, shape, () => { 13 | const [width, height] = shape.size 14 | return { 15 | minX: 0, 16 | maxX: width, 17 | minY: 0, 18 | maxY: height, 19 | width, 20 | height, 21 | } 22 | }) 23 | 24 | return Utils.translateBounds(bounds, shape.point) 25 | } 26 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/shared/getTextAlign.ts: -------------------------------------------------------------------------------- 1 | import { AlignStyle } from '~types' 2 | 3 | const ALIGN_VALUES = { 4 | [AlignStyle.Start]: 'left', 5 | [AlignStyle.Middle]: 'center', 6 | [AlignStyle.End]: 'right', 7 | [AlignStyle.Justify]: 'justify', 8 | } as const 9 | 10 | export function getTextAlign(alignStyle: AlignStyle = AlignStyle.Start) { 11 | return ALIGN_VALUES[alignStyle] 12 | } 13 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/shared/index.ts: -------------------------------------------------------------------------------- 1 | export * from './getBoundsRectangle' 2 | export * from './transformRectangle' 3 | export * from './transformSingleRectangle' 4 | export * from './TextAreaUtils' 5 | export * from './shape-styles' 6 | export * from './getTextAlign' 7 | export * from './TextLabel' 8 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/shapes/shared/transformSingleRectangle.ts: -------------------------------------------------------------------------------- 1 | import type { TLBounds, TLShape } from '@tlslides/core' 2 | import Vec from '@tlslides/vec' 3 | 4 | /** 5 | * Transform a single rectangular shape. 6 | * @param shape 7 | * @param bounds 8 | */ 9 | export function transformSingleRectangle( 10 | shape: T, 11 | bounds: TLBounds 12 | ) { 13 | return { 14 | size: Vec.toFixed([bounds.width, bounds.height]), 15 | point: Vec.toFixed([bounds.minX, bounds.minY]), 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/tools/ArrowTool/ArrowTool.spec.ts: -------------------------------------------------------------------------------- 1 | import { TldrawApp } from '~state' 2 | import { ArrowTool } from '.' 3 | 4 | describe('ArrowTool', () => { 5 | it('creates tool', () => { 6 | const app = new TldrawApp() 7 | new ArrowTool(app) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/tools/ArrowTool/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ArrowTool' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/tools/DrawTool/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DrawTool' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/tools/EllipseTool/EllipseTool.spec.ts: -------------------------------------------------------------------------------- 1 | import { TldrawApp } from '~state' 2 | import { EllipseTool } from '.' 3 | 4 | describe('EllipseTool', () => { 5 | it('creates tool', () => { 6 | const app = new TldrawApp() 7 | new EllipseTool(app) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/tools/EllipseTool/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EllipseTool' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/tools/EraseTool/EraseTool.spec.ts: -------------------------------------------------------------------------------- 1 | import { TldrawApp } from '~state' 2 | import { EraseTool } from './EraseTool' 3 | 4 | describe('EraseTool', () => { 5 | it('creates tool', () => { 6 | const app = new TldrawApp() 7 | new EraseTool(app) 8 | }) 9 | 10 | it.todo('restores previous tool after erasing') 11 | }) 12 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/tools/EraseTool/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EraseTool' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/tools/LineTool/LineTool.spec.ts: -------------------------------------------------------------------------------- 1 | import { TldrawApp } from '~state' 2 | import { LineTool } from '.' 3 | 4 | describe('LineTool', () => { 5 | it('creates tool', () => { 6 | const app = new TldrawApp() 7 | new LineTool(app) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/tools/LineTool/index.ts: -------------------------------------------------------------------------------- 1 | export * from './LineTool' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/tools/RectangleTool/RectangleTool.spec.ts: -------------------------------------------------------------------------------- 1 | import { TldrawApp } from '~state' 2 | import { RectangleTool } from '.' 3 | 4 | describe('RectangleTool', () => { 5 | it('creates tool', () => { 6 | const app = new TldrawApp() 7 | new RectangleTool(app) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/tools/RectangleTool/index.ts: -------------------------------------------------------------------------------- 1 | export * from './RectangleTool' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/tools/SelectTool/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SelectTool' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/tools/StickyTool/StickyTool.spec.ts: -------------------------------------------------------------------------------- 1 | import { TldrawApp } from '~state' 2 | import { StickyTool } from '.' 3 | 4 | describe('StickyTool', () => { 5 | it('creates tool', () => { 6 | const app = new TldrawApp() 7 | new StickyTool(app) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/tools/StickyTool/index.ts: -------------------------------------------------------------------------------- 1 | export * from './StickyTool' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/tools/TextTool/TextTool.spec.ts: -------------------------------------------------------------------------------- 1 | import { TldrawApp } from '~state' 2 | import { TextTool } from '.' 3 | 4 | describe('TextTool', () => { 5 | it('creates tool', () => { 6 | const app = new TldrawApp() 7 | new TextTool(app) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/tools/TextTool/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TextTool' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/tools/TriangleTool/TriangleTool.spec.ts: -------------------------------------------------------------------------------- 1 | import { TldrawApp } from '~state' 2 | import { TriangleTool } from '.' 3 | 4 | describe('TriangleTool', () => { 5 | it('creates tool', () => { 6 | const app = new TldrawApp() 7 | new TriangleTool(app) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/tldraw/src/state/tools/TriangleTool/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TriangleTool' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/styles/index.ts: -------------------------------------------------------------------------------- 1 | export * from './stitches.config' 2 | -------------------------------------------------------------------------------- /packages/tldraw/src/test/badDocument.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-non-null-assertion */ 2 | import { badDocument } from './documents/badDocument' 3 | import { TldrawTestApp } from './TldrawTestApp' 4 | 5 | describe('When loading a bad document', () => { 6 | it('Fixes the document', () => { 7 | const app = new TldrawTestApp() 8 | 9 | global.console.warn = jest.fn() 10 | 11 | // This doc has parents that are missing and children set to missing shapes 12 | app.loadDocument(badDocument as any) 13 | 14 | for (const pageId in app.document.pages) { 15 | const page = app.document.pages[pageId] 16 | for (const shape of Object.values(page.shapes)) { 17 | if (shape.parentId === pageId) continue 18 | expect(page.shapes[shape.parentId]).toBeDefined() 19 | } 20 | } 21 | 22 | expect(app.getShape('rect2').parentId).toBe('page1') 23 | expect(app.getShape('group1').children!.length).toBe(0) 24 | 25 | expect(global.console.warn).toHaveBeenCalled() 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /packages/tldraw/src/test/index.ts: -------------------------------------------------------------------------------- 1 | export * from './mockDocument' 2 | export * from './renderWithContext' 3 | export * from './TldrawTestApp' 4 | -------------------------------------------------------------------------------- /packages/tldraw/src/test/renderWithContext.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { TldrawApp } from '~state' 3 | import { useKeyboardShortcuts, TldrawContext } from '~hooks' 4 | import { mockDocument } from './mockDocument' 5 | import { render } from '@testing-library/react' 6 | 7 | export const Wrapper: React.FC = ({ children }) => { 8 | const [app] = React.useState(() => new TldrawApp()) 9 | const [context] = React.useState(() => { 10 | return app 11 | }) 12 | 13 | const rWrapper = React.useRef(null) 14 | 15 | useKeyboardShortcuts(rWrapper) 16 | 17 | React.useEffect(() => { 18 | if (!document) return 19 | app.loadDocument(mockDocument) 20 | }, [document, app]) 21 | 22 | return ( 23 | 24 |
{children}
25 |
26 | ) 27 | } 28 | 29 | export const renderWithContext = (children: JSX.Element) => { 30 | return render({children}) 31 | } 32 | -------------------------------------------------------------------------------- /packages/tldraw/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": [ 4 | "node_modules", 5 | "**/*.test.tsx", 6 | "**/*.test.ts", 7 | "**/*.spec.tsx", 8 | "**/*.spec.ts", 9 | "src/test", 10 | "dist", 11 | "docs" 12 | ], 13 | "compilerOptions": { 14 | "skipLibCheck": true, 15 | "composite": false, 16 | "incremental": false, 17 | "declaration": true, 18 | "declarationMap": true, 19 | "sourceMap": true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/tldraw/tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": [ 4 | "node_modules", 5 | "**/*.test.tsx", 6 | "**/*.test.ts", 7 | "**/*.spec.tsx", 8 | "**/*.spec.ts", 9 | "src/test", 10 | "dist", 11 | "docs" 12 | ], 13 | "compilerOptions": { 14 | "skipLibCheck": true, 15 | "composite": false, 16 | "incremental": false, 17 | "declaration": true, 18 | "declarationMap": true, 19 | "sourceMap": true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/tldraw/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "exclude": ["node_modules", "dist", "docs"], 4 | "include": ["src"], 5 | "compilerOptions": { 6 | "outDir": "./dist", 7 | "rootDir": "src", 8 | "baseUrl": ".", 9 | "paths": { 10 | "~*": ["./src/*"] 11 | } 12 | }, 13 | "references": [{ "path": "../vec" }, { "path": "../intersect" }, { "path": "../core" }], 14 | "typedocOptions": { 15 | "entryPoints": ["src/index.ts"], 16 | "out": "docs" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/vec/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": [ 4 | "node_modules", 5 | "**/*.test.tsx", 6 | "**/*.test.ts", 7 | "**/*.spec.tsx", 8 | "**/*.spec.ts", 9 | "src/test", 10 | "dist", 11 | "docs" 12 | ], 13 | "compilerOptions": { 14 | "composite": false, 15 | "incremental": false, 16 | "declaration": true, 17 | "declarationMap": true, 18 | "sourceMap": true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/vec/tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": [ 4 | "node_modules", 5 | "**/*.test.tsx", 6 | "**/*.test.ts", 7 | "**/*.spec.tsx", 8 | "**/*.spec.ts", 9 | "src/test", 10 | "dist", 11 | "docs" 12 | ], 13 | "compilerOptions": { 14 | "composite": false, 15 | "incremental": false, 16 | "declaration": true, 17 | "declarationMap": true, 18 | "sourceMap": true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/vec/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": "." 9 | }, 10 | "typedocOptions": { 11 | "entryPoints": ["src/index.ts"], 12 | "out": "docs" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /setupTests.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom/extend-expect' 2 | import 'fake-indexeddb/auto' 3 | global.ResizeObserver = require('resize-observer-polyfill') 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "exclude": ["node_modules", "**/*.test.ts", "**/*.spec.ts"] 4 | } 5 | --------------------------------------------------------------------------------