├── .gitignore
├── .prettierrc
├── .prettierignore
├── .npmignore
├── components
├── shims.d.ts
├── useIsEditable.ts
├── tldraw.css
├── useSaveSnapshot.test.ts
├── useStore.ts
├── CustomMainMenu.vue
├── useSaveSnapshot.ts
├── CustomToolbar.vue
└── Tldraw.vue
├── example-export
├── 1.png
├── 2.png
├── 3.png
└── 4.png
├── example-assets
├── tldraw-in-slidev.png
├── cover-entire-slide.png
└── slidev-menu-grid-dark.png
├── vitest.config.ts
├── public
└── tldraw
│ ├── assets
│ ├── 9M9vWAk4iJbH0SFEUYD3h-tldrawFile
│ ├── BAiXDxZqnQ3CseqVYi5Qw-tldrawFile
│ ├── SJr22o2fBc9y7rKqqs7VS-tldrawFile
│ └── ySUCTtXYhzFfCLGpA1Orz-SparseConfetti.svg
│ ├── doc-fObyRfr4o2EBtfWnDo2GS.json
│ ├── example1.json
│ └── doc-jmR9lNpzKflz-zVIYq9To.json
├── tsconfig.json
├── .github
├── dependabot.yml
└── workflows
│ └── npm-publish.yml
├── example.md
├── vite.config.ts
├── eslint.config.js
├── LICENSE.md
├── server
└── storeFile.ts
├── package.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .DS_Store
4 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5"
3 | }
4 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | dist
2 | coverage
3 | public
4 | pnpm-lock.yaml
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .DS_Store
4 | .github
5 | example-export
6 |
--------------------------------------------------------------------------------
/components/shims.d.ts:
--------------------------------------------------------------------------------
1 | declare global {
2 | const __DEV__: boolean;
3 | }
4 | export {};
5 |
--------------------------------------------------------------------------------
/example-export/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlbertBrand/slidev-addon-tldraw/HEAD/example-export/1.png
--------------------------------------------------------------------------------
/example-export/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlbertBrand/slidev-addon-tldraw/HEAD/example-export/2.png
--------------------------------------------------------------------------------
/example-export/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlbertBrand/slidev-addon-tldraw/HEAD/example-export/3.png
--------------------------------------------------------------------------------
/example-export/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlbertBrand/slidev-addon-tldraw/HEAD/example-export/4.png
--------------------------------------------------------------------------------
/example-assets/tldraw-in-slidev.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlbertBrand/slidev-addon-tldraw/HEAD/example-assets/tldraw-in-slidev.png
--------------------------------------------------------------------------------
/example-assets/cover-entire-slide.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlbertBrand/slidev-addon-tldraw/HEAD/example-assets/cover-entire-slide.png
--------------------------------------------------------------------------------
/example-assets/slidev-menu-grid-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlbertBrand/slidev-addon-tldraw/HEAD/example-assets/slidev-menu-grid-dark.png
--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vitest/config";
2 |
3 | export default defineConfig({
4 | test: {
5 | environment: "jsdom",
6 | },
7 | });
8 |
--------------------------------------------------------------------------------
/public/tldraw/assets/9M9vWAk4iJbH0SFEUYD3h-tldrawFile:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlbertBrand/slidev-addon-tldraw/HEAD/public/tldraw/assets/9M9vWAk4iJbH0SFEUYD3h-tldrawFile
--------------------------------------------------------------------------------
/public/tldraw/assets/BAiXDxZqnQ3CseqVYi5Qw-tldrawFile:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlbertBrand/slidev-addon-tldraw/HEAD/public/tldraw/assets/BAiXDxZqnQ3CseqVYi5Qw-tldrawFile
--------------------------------------------------------------------------------
/public/tldraw/assets/SJr22o2fBc9y7rKqqs7VS-tldrawFile:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlbertBrand/slidev-addon-tldraw/HEAD/public/tldraw/assets/SJr22o2fBc9y7rKqqs7VS-tldrawFile
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "ESNext",
4 | "moduleResolution": "node",
5 | "target": "ESNext",
6 | "types": ["vite/client"],
7 | "noEmit": true,
8 | "allowImportingTsExtensions": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/components/useIsEditable.ts:
--------------------------------------------------------------------------------
1 | import { computed } from "vue";
2 | import { useSlideContext } from "@slidev/client";
3 | import { useIsSlideActive } from "@slidev/client";
4 |
5 | export function useIsEditable() {
6 | const context = useSlideContext();
7 | const isSlideActive = useIsSlideActive();
8 |
9 | // editor is editable only in dev mode, when rendered as 'slide' and active
10 | return computed(
11 | () =>
12 | isSlideActive.value && context.$renderContext.value === "slide" && __DEV__
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "npm" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "weekly"
12 |
--------------------------------------------------------------------------------
/example.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: center
3 | ---
4 |
5 | # Integration of tldraw with Slidev
6 |
7 | ---
8 |
9 | # Add tldraw diagrams to your slides
10 |
11 |
12 |
13 | ---
14 |
15 | # Automatic saving of tldraw diagrams from Slidev
16 |
17 |
18 |
19 | ---
20 |
21 | # Cover entire slide with tldraw diagram
22 |
23 |
24 |
25 | - Mixes well with Slidev content
26 | - Easy position images and floating text
27 |
28 | ---
29 |
30 | # See the source code
31 |
32 | [slidev-addon-tldraw](https://github.com/AlbertBrand/slidev-addon-tldraw)
33 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import { storeFile, type StoreFileData } from "./server/storeFile";
3 |
4 | export default defineConfig({
5 | optimizeDeps: {
6 | include: [
7 | "slidev-addon-tldraw > veaury",
8 | "slidev-addon-tldraw > tldraw",
9 | "slidev-addon-tldraw > react-dom/client",
10 | ],
11 | },
12 | plugins: [
13 | {
14 | name: "tldraw-storage",
15 | configureServer(server) {
16 | server.ws.on("tldraw:store-file", async (data: StoreFileData) => {
17 | // store the file on disk
18 | await storeFile(data);
19 | // send a message back to the client
20 | server.ws.send("tldraw:file-stored", { path: data.path });
21 | });
22 | },
23 | },
24 | ],
25 | });
26 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import eslint from "@eslint/js";
2 | import eslintConfigPrettier from "eslint-config-prettier";
3 | import eslintPluginVue from "eslint-plugin-vue";
4 | import globals from "globals";
5 | import typescriptEslint from "typescript-eslint";
6 |
7 | export default typescriptEslint.config(
8 | { ignores: ["coverage", "dist", "public"] },
9 | {
10 | extends: [
11 | eslint.configs.recommended,
12 | ...typescriptEslint.configs.recommended,
13 | ...eslintPluginVue.configs["flat/recommended"],
14 | ],
15 | files: ["**/*.{ts,js,vue}"],
16 | languageOptions: {
17 | ecmaVersion: "latest",
18 | sourceType: "module",
19 | globals: globals.browser,
20 | parserOptions: {
21 | parser: typescriptEslint.parser,
22 | },
23 | },
24 | rules: {
25 | "vue/multi-word-component-names": "off",
26 | "vue/attribute-hyphenation": "off",
27 | },
28 | },
29 | eslintConfigPrettier
30 | );
31 |
--------------------------------------------------------------------------------
/components/tldraw.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Inter:wght@500;700&display=swap");
2 | @import url("tldraw/tldraw.css");
3 |
4 | /*
5 | Slides are CSS transformed at parent level, and Tldraw breaks on such transformations.
6 | Inverse the transformation to make Tldraw work correctly. (note that `all: unset` only partially works)
7 | */
8 | .inverse-transform {
9 | width: calc(var(--slide-scale) * 100%);
10 | height: calc(var(--slide-scale) * 100%);
11 | transform: scale(calc(1 / var(--slide-scale)))
12 | translate(
13 | calc(calc(var(--slide-scale) - 1) * -50%),
14 | calc(calc(var(--slide-scale) - 1) * -50%)
15 | );
16 | }
17 |
18 | /* transparent background */
19 | .tl-theme__light,
20 | .tl-theme__dark {
21 | --color-background: rgba(0, 0, 0, 0);
22 | }
23 |
24 | /* only show outline in edit mode */
25 | .isNotEditable .tl-container__focused {
26 | outline: none;
27 | }
28 |
29 | .isNotVisible {
30 | visibility: hidden;
31 | }
32 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/server/storeFile.ts:
--------------------------------------------------------------------------------
1 | import { writeFile, mkdir } from "node:fs/promises";
2 | import { dirname } from "node:path";
3 | import { cwd } from "node:process";
4 |
5 | export type StoreFileData = {
6 | path: string;
7 | content: string;
8 | type: "file" | "json";
9 | };
10 |
11 | export async function storeFile(data: StoreFileData) {
12 | const docPath = cwd() + "/public/" + decodeURIComponent(data.path);
13 | const folderPath = dirname(docPath);
14 | try {
15 | let content: Blob;
16 | if (data.type === "json") {
17 | // convert json data to a blob
18 | content = new Blob([data.content], {
19 | type: "application/json;charset=utf-8",
20 | });
21 | } else {
22 | // use fetch to convert the dataURL to a blob
23 | const res = await fetch(data.content);
24 | content = await res.blob();
25 | }
26 | // write the blob to the file system
27 | await mkdir(folderPath, { recursive: true });
28 | await writeFile(docPath, content.stream());
29 | // pause for a moment to let the file system catch up
30 | await new Promise((resolve) => setTimeout(resolve, 200));
31 | } catch (e) {
32 | console.error(e);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/.github/workflows/npm-publish.yml:
--------------------------------------------------------------------------------
1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2 | # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
3 |
4 | name: Publish NPM package
5 |
6 | on:
7 | release:
8 | types: [created]
9 |
10 | jobs:
11 | build:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v4
15 | - name: Install pnpm
16 | uses: pnpm/action-setup@v4
17 | with:
18 | version: 9
19 | - uses: actions/setup-node@v4
20 | with:
21 | node-version: 20
22 | cache: "pnpm"
23 | - run: pnpm install
24 | - run: pnpm test
25 |
26 | publish-npm:
27 | needs: build
28 | runs-on: ubuntu-latest
29 | steps:
30 | - uses: actions/checkout@v4
31 | - name: Install pnpm
32 | uses: pnpm/action-setup@v4
33 | with:
34 | version: 9
35 | - uses: actions/setup-node@v4
36 | with:
37 | node-version: 20
38 | cache: "pnpm"
39 | registry-url: https://registry.npmjs.org/
40 | - run: pnpm install
41 | - run: pnpm publish --access public
42 | env:
43 | NODE_AUTH_TOKEN: ${{secrets.npm_token}}
44 |
--------------------------------------------------------------------------------
/components/useSaveSnapshot.test.ts:
--------------------------------------------------------------------------------
1 | import { vi, expect, test } from "vitest";
2 | import { updateTldrawProps, updateAttrs } from "./useSaveSnapshot";
3 |
4 | vi.mock("@slidev/client/composables/useSlideInfo.ts", () => ({}));
5 | vi.mock("@slidev/client", () => ({}));
6 |
7 | test("update props on open tag", () => {
8 | const content =
9 | '# First slide, show TLDraw\n\n\n';
10 | expect(updateTldrawProps(content, { doc: "doc-123" })).toMatchInlineSnapshot(`
11 | "# First slide, show TLDraw
12 |
13 |
14 | "
15 | `);
16 | });
17 |
18 | test("update props on selfclosed tag", () => {
19 | const content = '# First slide, show TLDraw\n\n';
20 | expect(updateTldrawProps(content, { doc: "doc-123" })).toMatchInlineSnapshot(`
21 | "# First slide, show TLDraw
22 |
23 | "
24 | `);
25 | });
26 |
27 | test("updateAttrs selfclosed", () => {
28 | const result = updateAttrs("", { doc: "doc-123" });
29 | expect(result).toBe('');
30 | });
31 |
32 | test("updateAttrs sets", () => {
33 | const result = updateAttrs("", { doc: "doc-123" });
34 | expect(result).toBe('');
35 | });
36 |
37 | test("updateAttrs overwrites", () => {
38 | const result = updateAttrs('', {
39 | doc: "doc-123",
40 | });
41 | expect(result).toBe('');
42 | });
43 |
--------------------------------------------------------------------------------
/components/useStore.ts:
--------------------------------------------------------------------------------
1 | import { createTLStore, TLAssetStore, uniqueId } from "tldraw";
2 |
3 | // convert files to dataURLs to send them as a string
4 | const encodeFileToDataURL = (file: File) =>
5 | new Promise((resolve, reject) => {
6 | const reader = new FileReader();
7 | reader.readAsDataURL(file);
8 | reader.onload = () => resolve(reader.result as string);
9 | reader.onerror = (error) => reject(error);
10 | });
11 |
12 | // custom asset store that communicates with the vite plugin
13 | const assets: TLAssetStore = {
14 | async upload(_, file) {
15 | const objectName = `${uniqueId()}-${file.name}`;
16 | const imagePath = `tldraw/assets/${encodeURIComponent(objectName)}`;
17 | const encodedFile = await encodeFileToDataURL(file);
18 |
19 | // store the asset on disk via the vite plugin
20 | if (import.meta.hot) {
21 | import.meta.hot.send("tldraw:store-file", {
22 | path: imagePath,
23 | content: encodedFile,
24 | type: "file",
25 | });
26 |
27 | // wait for the file to be stored
28 | await new Promise((resolve) => {
29 | const callback = (data: { path: string }) => {
30 | // TODO check if the path matches?
31 | import.meta.hot.off("tldraw:file-stored", callback);
32 | resolve(data.path);
33 | };
34 | import.meta.hot.on("tldraw:file-stored", callback);
35 | });
36 | }
37 |
38 | return { src: "/" + imagePath };
39 | },
40 |
41 | resolve(asset) {
42 | return asset.props.src;
43 | },
44 | };
45 |
46 | export function useStore() {
47 | return createTLStore({ assets });
48 | }
49 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "slidev-addon-tldraw",
3 | "version": "0.5.1",
4 | "description": "Slidev addon to show and edit tldraw diagrams",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "slidev example.md",
8 | "build": "slidev build example.md",
9 | "export": "slidev export example.md",
10 | "screenshot": "slidev export example.md --format png",
11 | "lint": "eslint . --fix",
12 | "format": "prettier . --write",
13 | "test": "vitest"
14 | },
15 | "keywords": [
16 | "slidev-addon",
17 | "slidev",
18 | "tldraw"
19 | ],
20 | "files": [
21 | "components",
22 | "server",
23 | "vite.config.ts"
24 | ],
25 | "repository": {
26 | "type": "git",
27 | "url": "git+https://github.com/AlbertBrand/slidev-addon-tldraw.git"
28 | },
29 | "bugs": {
30 | "url": "https://github.com/AlbertBrand/slidev-addon-tldraw/issues"
31 | },
32 | "homepage": "https://github.com/AlbertBrand/slidev-addon-tldraw#readme",
33 | "author": "Albert Brand ",
34 | "license": "MIT",
35 | "devDependencies": {
36 | "@slidev/cli": "^52.10.1",
37 | "@slidev/theme-default": "^0.25.0",
38 | "@slidev/types": "^52.10.1",
39 | "@types/eslint-config-prettier": "^6.11.3",
40 | "@types/react": "^19.2.7",
41 | "@types/react-dom": "^19.2.3",
42 | "eslint": "^9.39.1",
43 | "eslint-config-prettier": "^10.1.8",
44 | "eslint-plugin-prettier": "^5.5.4",
45 | "eslint-plugin-vue": "^10.6.2",
46 | "globals": "^16.5.0",
47 | "jsdom": "^27.2.0",
48 | "playwright-chromium": "^1.57.0",
49 | "prettier": "^3.7.4",
50 | "typescript-eslint": "^8.48.1",
51 | "vite": "^7.2.6",
52 | "vitest": "^4.0.15"
53 | },
54 | "peerDependencies": {
55 | "react": "^18.2.0 || ^19.0.0",
56 | "react-dom": "^18.2.0 || ^19.0.0",
57 | "vue": "^3.4.33"
58 | },
59 | "dependencies": {
60 | "@slidev/client": "^52.10.1",
61 | "@vueuse/core": "^14.1.0",
62 | "tldraw": "^3.15.5",
63 | "veaury": "^2.6.3"
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/components/CustomMainMenu.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
53 |
--------------------------------------------------------------------------------
/components/useSaveSnapshot.ts:
--------------------------------------------------------------------------------
1 | import { getSnapshot, TLStore } from "tldraw";
2 | import { watchEffect } from "vue";
3 | import { useSlideContext } from "@slidev/client";
4 | import { useDynamicSlideInfo } from "@slidev/client/composables/useSlideInfo.ts";
5 | import { State } from "./Tldraw.vue";
6 |
7 | export function useSaveSnapshot(store: TLStore, state: State) {
8 | const context = useSlideContext();
9 | const { info, update } = useDynamicSlideInfo(context.$page);
10 |
11 | // get the current slide content
12 | let content: string | undefined;
13 | watchEffect(() => (content = info.value?.content));
14 |
15 | // return function that saves the snapshot in the slide content
16 | return async () => {
17 | if (content === undefined) return;
18 |
19 | // get a snapshot of the current document
20 | const { document } = getSnapshot(store);
21 | const json = JSON.stringify(document, null, 2);
22 |
23 | // store the snapshot on disk via the vite plugin
24 | if (import.meta.hot) {
25 | import.meta.hot.send("tldraw:store-file", {
26 | path: state.doc,
27 | content: json,
28 | type: "json",
29 | });
30 | }
31 |
32 | // update the slide content to include the doc prop
33 | const newContent = updateTldrawProps(content, state);
34 |
35 | // skip update if content is same
36 | if (content === newContent) return;
37 |
38 | await update({
39 | content: newContent,
40 | skipHmr: true,
41 | });
42 | };
43 | }
44 |
45 | // Replace slide contents with new props on Tldraw component.
46 | // Currently only supports a single Tldraw component per slide.
47 | export function updateTldrawProps(content: string, state: State) {
48 | const openCloseRegex = /]*>.*<\/tldraw>/is;
49 |
50 | if (openCloseRegex.test(content)) {
51 | return content.replace(openCloseRegex, (match) =>
52 | updateAttrs(match, state)
53 | );
54 | }
55 |
56 | const selfCloseRegex = //is;
57 | if (selfCloseRegex.test(content)) {
58 | return content.replace(selfCloseRegex, (match) =>
59 | updateAttrs(match, state)
60 | );
61 | }
62 |
63 | return content;
64 | }
65 |
66 | export function updateAttrs(component: string, state: State) {
67 | const doc = new DOMParser().parseFromString(component, "text/html");
68 | doc.body.firstElementChild?.setAttribute("doc", state.doc ?? "");
69 | return doc.body.innerHTML;
70 | }
71 |
--------------------------------------------------------------------------------
/components/CustomToolbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
60 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # tldraw for Slidev
2 |
3 | Edit your [tldraw](https://tldraw.dev) diagrams directly in
4 | [Slidev](https://sli.dev/)
5 |
6 | 
7 |
8 | ## Installation
9 |
10 | Use your favorite package manager to install the addon in your existing Slidev presentation, for instance using `pnpm`:
11 |
12 | ```bash
13 | pnpm add slidev-addon-tldraw
14 | ```
15 |
16 | Then define this addon in the frontmatter of the slidedeck:
17 |
18 | ```yaml
19 | ---
20 | addons:
21 | - slidev-addon-tldraw
22 | ---
23 | ```
24 |
25 | or in the `slidev` field of the `package.json`:
26 |
27 | ```json
28 | "slidev": {
29 | "addons": [
30 | "slidev-addon-tldraw"
31 | ]
32 | },
33 | ```
34 |
35 | NOTE: you need to specify the full addon name since Slidev 52.x, not just `tldraw`.
36 |
37 | ## Example with a new tldraw diagram
38 |
39 | Add a new slide with the following content:
40 |
41 | ```md
42 | ---
43 | # New tldraw diagram
44 |
45 |
46 | ---
47 | ```
48 |
49 | Make sure the diagram has a fixed width and height. Check the
50 | [Slidev UnoCSS docs](https://sli.dev/custom/config-unocss) on which classes are
51 | available.
52 |
53 | Now, start the slideshow in development mode. Navigate to the slide, add new
54 | shapes etc. The diagram content will be saved under the `public` folder and a
55 | reference is created automatically in the existing slide.
56 |
57 | ## Example for existing tldraw diagrams
58 |
59 | Create a `public` folder, store the tldraw diagram in it and reference it in the
60 | slide:
61 |
62 | ```md
63 | ---
64 | # Existing tldraw diagram
65 |
66 |
67 | ---
68 | ```
69 |
70 | Note that the visible area is fixed to 800x800 for now.
71 |
72 | ## Cover entire slide
73 |
74 | 
75 |
76 | If you want to cover the entire slide with a tldraw diagram, use the following
77 | style:
78 |
79 | ```md
80 | ---
81 | # Diagram covering entire slide
82 |
83 |
84 | ---
85 | ```
86 |
87 | ## Features
88 |
89 | 
90 |
91 | - tldraw diagrams can be edited directly in Slidev and are automatically saved.
92 | - Uploading assets is fully supported, either via drag and drop or using the
93 | 'upload media' menu item. Assets are stored in the `public/tldraw/assets`
94 | folder after uploading.
95 | - Dark mode is automatically adjusted to the Slidev theme
96 | - Exporting a Slidev presentation will include the tldraw diagrams
97 | - Snap, tool lock and grid mode can be enabled via the menu or by keyboard
98 | shortcuts
99 | - Inserting embeds is enabled
100 |
101 | Other `tldraw` options, such as multi-page diagrams are disabled (for now). The
102 | addon makes a tradeoff between features and simplicity.
103 |
104 | ## License
105 |
106 | This addon is licensed MIT. Note that tldraw has a custom license, see their
107 | [license page](https://github.com/tldraw/tldraw/blob/main/LICENSE.md) for more
108 | information.
109 |
--------------------------------------------------------------------------------
/public/tldraw/doc-fObyRfr4o2EBtfWnDo2GS.json:
--------------------------------------------------------------------------------
1 | {
2 | "store": {
3 | "shape:jcBjziDcqO5oS3kAqwmF9": {
4 | "x": 77.81879995146784,
5 | "y": 257.1318171876236,
6 | "rotation": 0,
7 | "isLocked": false,
8 | "opacity": 1,
9 | "meta": {},
10 | "id": "shape:jcBjziDcqO5oS3kAqwmF9",
11 | "type": "note",
12 | "props": {
13 | "color": "black",
14 | "size": "m",
15 | "font": "draw",
16 | "align": "middle",
17 | "verticalAlign": "middle",
18 | "growY": 0,
19 | "fontSizeAdjustment": 0,
20 | "url": "",
21 | "scale": 1,
22 | "labelColor": "black",
23 | "richText": {
24 | "type": "doc",
25 | "content": [
26 | {
27 | "type": "paragraph"
28 | }
29 | ]
30 | }
31 | },
32 | "parentId": "page:page",
33 | "index": "a1",
34 | "typeName": "shape"
35 | },
36 | "shape:Yes7f8j7vrcUFnu76O6F9": {
37 | "x": 480,
38 | "y": 200,
39 | "rotation": 0,
40 | "isLocked": false,
41 | "opacity": 1,
42 | "meta": {},
43 | "id": "shape:Yes7f8j7vrcUFnu76O6F9",
44 | "type": "note",
45 | "props": {
46 | "color": "light-red",
47 | "size": "m",
48 | "font": "draw",
49 | "align": "middle",
50 | "verticalAlign": "middle",
51 | "growY": 0,
52 | "fontSizeAdjustment": 22,
53 | "url": "",
54 | "scale": 1,
55 | "labelColor": "black",
56 | "richText": {
57 | "type": "doc",
58 | "content": [
59 | {
60 | "type": "paragraph",
61 | "content": [
62 | {
63 | "type": "text",
64 | "text": "Saved!"
65 | }
66 | ]
67 | }
68 | ]
69 | }
70 | },
71 | "parentId": "page:page",
72 | "index": "a2",
73 | "typeName": "shape"
74 | },
75 | "shape:uNSvyohjF9xovnacDzYR2": {
76 | "x": 140.79809045066648,
77 | "y": 322.98544657902755,
78 | "rotation": 0,
79 | "isLocked": false,
80 | "opacity": 1,
81 | "meta": {},
82 | "id": "shape:uNSvyohjF9xovnacDzYR2",
83 | "type": "geo",
84 | "props": {
85 | "w": 73.58386984565323,
86 | "h": 75.3424029397932,
87 | "geo": "check-box",
88 | "color": "black",
89 | "labelColor": "black",
90 | "fill": "none",
91 | "dash": "draw",
92 | "size": "m",
93 | "font": "draw",
94 | "align": "middle",
95 | "verticalAlign": "middle",
96 | "growY": 0,
97 | "url": "",
98 | "scale": 1,
99 | "richText": {
100 | "type": "doc",
101 | "content": [
102 | {
103 | "type": "paragraph"
104 | }
105 | ]
106 | }
107 | },
108 | "parentId": "page:page",
109 | "index": "a3",
110 | "typeName": "shape"
111 | },
112 | "page:page": {
113 | "meta": {},
114 | "id": "page:page",
115 | "name": "Page 1",
116 | "index": "a1",
117 | "typeName": "page"
118 | },
119 | "document:document": {
120 | "gridSize": 10,
121 | "name": "",
122 | "meta": {},
123 | "id": "document:document",
124 | "typeName": "document"
125 | }
126 | },
127 | "schema": {
128 | "schemaVersion": 2,
129 | "sequences": {
130 | "com.tldraw.store": 4,
131 | "com.tldraw.asset": 1,
132 | "com.tldraw.camera": 1,
133 | "com.tldraw.document": 2,
134 | "com.tldraw.instance": 25,
135 | "com.tldraw.instance_page_state": 5,
136 | "com.tldraw.page": 1,
137 | "com.tldraw.instance_presence": 6,
138 | "com.tldraw.pointer": 1,
139 | "com.tldraw.shape": 4,
140 | "com.tldraw.asset.bookmark": 2,
141 | "com.tldraw.asset.image": 5,
142 | "com.tldraw.asset.video": 5,
143 | "com.tldraw.shape.arrow": 6,
144 | "com.tldraw.shape.bookmark": 2,
145 | "com.tldraw.shape.draw": 2,
146 | "com.tldraw.shape.embed": 4,
147 | "com.tldraw.shape.frame": 1,
148 | "com.tldraw.shape.geo": 10,
149 | "com.tldraw.shape.group": 0,
150 | "com.tldraw.shape.highlight": 1,
151 | "com.tldraw.shape.image": 5,
152 | "com.tldraw.shape.line": 5,
153 | "com.tldraw.shape.note": 9,
154 | "com.tldraw.shape.text": 3,
155 | "com.tldraw.shape.video": 4,
156 | "com.tldraw.binding.arrow": 1
157 | }
158 | }
159 | }
--------------------------------------------------------------------------------
/components/Tldraw.vue:
--------------------------------------------------------------------------------
1 |
2 |
23 |
24 |
25 |
286 |
--------------------------------------------------------------------------------
/public/tldraw/example1.json:
--------------------------------------------------------------------------------
1 | {
2 | "store": {
3 | "shape:3_UbSz04jDLuQjRQOAad-": {
4 | "x": -351.8566997877242,
5 | "y": 554.1875950094636,
6 | "rotation": 0,
7 | "isLocked": false,
8 | "opacity": 1,
9 | "meta": {},
10 | "id": "shape:3_UbSz04jDLuQjRQOAad-",
11 | "type": "geo",
12 | "props": {
13 | "w": 265.11378578441213,
14 | "h": 185.0014277375699,
15 | "geo": "cloud",
16 | "color": "light-blue",
17 | "labelColor": "black",
18 | "fill": "solid",
19 | "dash": "dotted",
20 | "size": "m",
21 | "font": "draw",
22 | "align": "middle",
23 | "verticalAlign": "middle",
24 | "growY": 0,
25 | "url": "",
26 | "scale": 1,
27 | "richText": {
28 | "type": "doc",
29 | "content": [
30 | {
31 | "type": "paragraph",
32 | "content": [
33 | {
34 | "type": "text",
35 | "text": "So good!"
36 | }
37 | ]
38 | }
39 | ]
40 | }
41 | },
42 | "parentId": "page:page",
43 | "index": "aC",
44 | "typeName": "shape"
45 | },
46 | "shape:Zd81MkEhpZONg-DS82MYE": {
47 | "x": -205.05260103803727,
48 | "y": 186.43696910692756,
49 | "rotation": 0,
50 | "isLocked": false,
51 | "opacity": 1,
52 | "meta": {},
53 | "id": "shape:Zd81MkEhpZONg-DS82MYE",
54 | "type": "geo",
55 | "props": {
56 | "w": 124.27197002894175,
57 | "h": 72.81046104416156,
58 | "geo": "rectangle",
59 | "color": "black",
60 | "labelColor": "black",
61 | "fill": "solid",
62 | "dash": "draw",
63 | "size": "s",
64 | "font": "draw",
65 | "align": "middle",
66 | "verticalAlign": "middle",
67 | "growY": 0,
68 | "url": "",
69 | "scale": 1,
70 | "richText": {
71 | "type": "doc",
72 | "content": [
73 | {
74 | "type": "paragraph",
75 | "content": [
76 | {
77 | "type": "text",
78 | "text": "Shape"
79 | }
80 | ]
81 | }
82 | ]
83 | }
84 | },
85 | "parentId": "page:page",
86 | "index": "a8",
87 | "typeName": "shape"
88 | },
89 | "shape:-f2zxkAoQiJnluPe27dKF": {
90 | "x": 146.75542354540744,
91 | "y": 39.10464268091212,
92 | "rotation": 0,
93 | "isLocked": false,
94 | "opacity": 1,
95 | "meta": {},
96 | "id": "shape:-f2zxkAoQiJnluPe27dKF",
97 | "type": "image",
98 | "props": {
99 | "w": 400.8686147046836,
100 | "h": 400.8686147046836,
101 | "assetId": "asset:-1842179584",
102 | "playing": true,
103 | "url": "",
104 | "crop": null,
105 | "flipX": false,
106 | "flipY": false,
107 | "altText": ""
108 | },
109 | "parentId": "page:page",
110 | "index": "aD",
111 | "typeName": "shape"
112 | },
113 | "asset:-1842179584": {
114 | "meta": {},
115 | "id": "asset:-1842179584",
116 | "type": "image",
117 | "typeName": "asset",
118 | "props": {
119 | "name": "SparseConfetti.svg",
120 | "src": "/tldraw/assets/ySUCTtXYhzFfCLGpA1Orz-SparseConfetti.svg",
121 | "w": 2667,
122 | "h": 2667,
123 | "fileSize": 53483,
124 | "mimeType": "image/svg+xml",
125 | "isAnimated": false
126 | }
127 | },
128 | "shape:eaWky_ucgEt9vQgO2ro6Q": {
129 | "x": -278.0255266496335,
130 | "y": 298.2086587785797,
131 | "rotation": 0,
132 | "isLocked": false,
133 | "opacity": 1,
134 | "meta": {},
135 | "id": "shape:eaWky_ucgEt9vQgO2ro6Q",
136 | "type": "geo",
137 | "props": {
138 | "w": 124.27197002894175,
139 | "h": 72.81046104416156,
140 | "geo": "rectangle",
141 | "color": "black",
142 | "labelColor": "black",
143 | "fill": "solid",
144 | "dash": "draw",
145 | "size": "s",
146 | "font": "draw",
147 | "align": "middle",
148 | "verticalAlign": "middle",
149 | "growY": 0,
150 | "url": "",
151 | "scale": 1,
152 | "richText": {
153 | "type": "doc",
154 | "content": [
155 | {
156 | "type": "paragraph",
157 | "content": [
158 | {
159 | "type": "text",
160 | "text": "Box"
161 | }
162 | ]
163 | }
164 | ]
165 | }
166 | },
167 | "parentId": "page:page",
168 | "index": "a9",
169 | "typeName": "shape"
170 | },
171 | "binding:ob37km7D52v9PrehZ4SYg": {
172 | "meta": {},
173 | "id": "binding:ob37km7D52v9PrehZ4SYg",
174 | "type": "arrow",
175 | "fromId": "shape:r3TfPhlREpUsepi-q9TR1",
176 | "toId": "shape:u5-Hl-RlK7_fT6djKAYfG",
177 | "props": {
178 | "isPrecise": false,
179 | "isExact": false,
180 | "normalizedAnchor": {
181 | "x": 0.6182494451280984,
182 | "y": 0.4468696503182925
183 | },
184 | "terminal": "end",
185 | "snap": "none"
186 | },
187 | "typeName": "binding"
188 | },
189 | "shape:FUn6KCAosSQTaMsc_q4w2": {
190 | "x": 600.1405434300603,
191 | "y": 201.41190625514517,
192 | "rotation": 5.934119456780721,
193 | "isLocked": false,
194 | "opacity": 1,
195 | "meta": {},
196 | "id": "shape:FUn6KCAosSQTaMsc_q4w2",
197 | "type": "geo",
198 | "props": {
199 | "w": 233.39300794371664,
200 | "h": 209.9571870541787,
201 | "geo": "star",
202 | "color": "yellow",
203 | "labelColor": "black",
204 | "fill": "solid",
205 | "dash": "draw",
206 | "size": "l",
207 | "font": "draw",
208 | "align": "middle",
209 | "verticalAlign": "middle",
210 | "growY": 0,
211 | "url": "",
212 | "scale": 1,
213 | "richText": {
214 | "type": "doc",
215 | "content": [
216 | {
217 | "type": "paragraph",
218 | "content": [
219 | {
220 | "type": "text",
221 | "text": "Crazy!"
222 | }
223 | ]
224 | }
225 | ]
226 | }
227 | },
228 | "parentId": "page:page",
229 | "index": "a7",
230 | "typeName": "shape"
231 | },
232 | "binding:BT2JH48_thSosYSD_AG9v": {
233 | "meta": {},
234 | "id": "binding:BT2JH48_thSosYSD_AG9v",
235 | "type": "arrow",
236 | "fromId": "shape:eeWMgxUEMEJm8izPib5-X",
237 | "toId": "shape:Zd81MkEhpZONg-DS82MYE",
238 | "props": {
239 | "isPrecise": false,
240 | "isExact": false,
241 | "normalizedAnchor": {
242 | "x": 0.5180895814264611,
243 | "y": 0.501923082749824
244 | },
245 | "terminal": "start",
246 | "snap": "none"
247 | },
248 | "typeName": "binding"
249 | },
250 | "shape:r3TfPhlREpUsepi-q9TR1": {
251 | "x": -142.19857780246622,
252 | "y": 220.69269569411568,
253 | "rotation": 0,
254 | "isLocked": false,
255 | "opacity": 1,
256 | "meta": {},
257 | "id": "shape:r3TfPhlREpUsepi-q9TR1",
258 | "type": "arrow",
259 | "props": {
260 | "dash": "draw",
261 | "size": "s",
262 | "fill": "solid",
263 | "color": "black",
264 | "labelColor": "black",
265 | "bend": 0,
266 | "start": {
267 | "x": 0,
268 | "y": 0
269 | },
270 | "end": {
271 | "x": 79.21458631507863,
272 | "y": 77.22833305107636
273 | },
274 | "arrowheadStart": "none",
275 | "arrowheadEnd": "arrow",
276 | "text": "",
277 | "labelPosition": 0.5,
278 | "font": "draw",
279 | "scale": 1,
280 | "kind": "arc",
281 | "elbowMidPoint": 0.5
282 | },
283 | "parentId": "page:page",
284 | "index": "aB",
285 | "typeName": "shape"
286 | },
287 | "shape:To35dgsqEchAkqW-ctkFa": {
288 | "x": 629.250962829296,
289 | "y": 37.805598621813004,
290 | "rotation": 0,
291 | "isLocked": false,
292 | "opacity": 1,
293 | "meta": {},
294 | "id": "shape:To35dgsqEchAkqW-ctkFa",
295 | "type": "text",
296 | "props": {
297 | "color": "black",
298 | "size": "m",
299 | "w": 258.99490404064727,
300 | "font": "draw",
301 | "textAlign": "start",
302 | "autoSize": false,
303 | "scale": 1.1349677164193805,
304 | "richText": {
305 | "type": "doc",
306 | "content": [
307 | {
308 | "type": "paragraph",
309 | "content": [
310 | {
311 | "type": "text",
312 | "text": "Drop SVG and images"
313 | }
314 | ]
315 | }
316 | ]
317 | }
318 | },
319 | "parentId": "page:page",
320 | "index": "aE",
321 | "typeName": "shape"
322 | },
323 | "shape:8dSUaTrysPNW5Wdqe6sp0": {
324 | "x": -106.66793752475559,
325 | "y": -64.47599441431123,
326 | "rotation": 0,
327 | "isLocked": false,
328 | "opacity": 1,
329 | "meta": {},
330 | "id": "shape:8dSUaTrysPNW5Wdqe6sp0",
331 | "type": "text",
332 | "props": {
333 | "color": "black",
334 | "size": "m",
335 | "w": 78.859375,
336 | "font": "draw",
337 | "textAlign": "start",
338 | "autoSize": true,
339 | "scale": 1.4193247168348746,
340 | "richText": {
341 | "type": "doc",
342 | "content": [
343 | {
344 | "type": "paragraph",
345 | "content": [
346 | {
347 | "type": "text",
348 | "text": "A text"
349 | }
350 | ]
351 | }
352 | ]
353 | }
354 | },
355 | "parentId": "page:page",
356 | "index": "a4",
357 | "typeName": "shape"
358 | },
359 | "binding:CCKbvPG6Jq5uBO-RFxk3Y": {
360 | "meta": {},
361 | "id": "binding:CCKbvPG6Jq5uBO-RFxk3Y",
362 | "type": "arrow",
363 | "fromId": "shape:zkNBEJRBzA2l-NxBcMpxe",
364 | "toId": "shape:-f2zxkAoQiJnluPe27dKF",
365 | "props": {
366 | "isPrecise": true,
367 | "isExact": false,
368 | "normalizedAnchor": {
369 | "x": 0.49066757745215406,
370 | "y": 0.3264988797117897
371 | },
372 | "terminal": "end",
373 | "snap": "none"
374 | },
375 | "typeName": "binding"
376 | },
377 | "shape:zkNBEJRBzA2l-NxBcMpxe": {
378 | "x": 768.7464538233241,
379 | "y": 205.24437202008284,
380 | "rotation": 0,
381 | "isLocked": false,
382 | "opacity": 1,
383 | "meta": {},
384 | "id": "shape:zkNBEJRBzA2l-NxBcMpxe",
385 | "type": "arrow",
386 | "props": {
387 | "dash": "draw",
388 | "size": "m",
389 | "fill": "none",
390 | "color": "black",
391 | "labelColor": "black",
392 | "bend": -92.70648165367699,
393 | "start": {
394 | "x": -17.310725594779115,
395 | "y": -130.33446102991104
396 | },
397 | "end": {
398 | "x": -444.0186763064263,
399 | "y": 70.53424612865942
400 | },
401 | "arrowheadStart": "none",
402 | "arrowheadEnd": "arrow",
403 | "text": "",
404 | "labelPosition": 0.5,
405 | "font": "draw",
406 | "scale": 1,
407 | "kind": "arc",
408 | "elbowMidPoint": 0.5
409 | },
410 | "parentId": "page:page",
411 | "index": "aF",
412 | "typeName": "shape"
413 | },
414 | "binding:BrOq6RAvBDVAGUB0-M5p1": {
415 | "meta": {},
416 | "id": "binding:BrOq6RAvBDVAGUB0-M5p1",
417 | "type": "arrow",
418 | "fromId": "shape:eeWMgxUEMEJm8izPib5-X",
419 | "toId": "shape:eaWky_ucgEt9vQgO2ro6Q",
420 | "props": {
421 | "isPrecise": true,
422 | "isExact": false,
423 | "normalizedAnchor": {
424 | "x": 0.5,
425 | "y": 0.5
426 | },
427 | "terminal": "end",
428 | "snap": "none"
429 | },
430 | "typeName": "binding"
431 | },
432 | "shape:eeWMgxUEMEJm8izPib5-X": {
433 | "x": -140.6685881027011,
434 | "y": 222.9822201706491,
435 | "rotation": 0,
436 | "isLocked": false,
437 | "opacity": 1,
438 | "meta": {},
439 | "id": "shape:eeWMgxUEMEJm8izPib5-X",
440 | "type": "arrow",
441 | "props": {
442 | "dash": "draw",
443 | "size": "s",
444 | "fill": "solid",
445 | "color": "black",
446 | "labelColor": "black",
447 | "bend": 0,
448 | "start": {
449 | "x": 0,
450 | "y": 0
451 | },
452 | "end": {
453 | "x": -81.20968600057108,
454 | "y": 150.94068786920297
455 | },
456 | "arrowheadStart": "none",
457 | "arrowheadEnd": "arrow",
458 | "text": "",
459 | "labelPosition": 0.5,
460 | "font": "draw",
461 | "scale": 1,
462 | "kind": "arc",
463 | "elbowMidPoint": 0.5
464 | },
465 | "parentId": "page:page",
466 | "index": "a9V",
467 | "typeName": "shape"
468 | },
469 | "shape:j2DELfiEEnP4yxbgbhquJ": {
470 | "x": 838.0901230978774,
471 | "y": 478.05826238166816,
472 | "rotation": 0,
473 | "isLocked": false,
474 | "opacity": 1,
475 | "meta": {},
476 | "id": "shape:j2DELfiEEnP4yxbgbhquJ",
477 | "type": "note",
478 | "parentId": "page:page",
479 | "index": "a1",
480 | "props": {
481 | "color": "black",
482 | "size": "m",
483 | "font": "draw",
484 | "align": "middle",
485 | "verticalAlign": "middle",
486 | "growY": 0,
487 | "fontSizeAdjustment": 22,
488 | "url": "",
489 | "scale": 1,
490 | "labelColor": "black",
491 | "richText": {
492 | "type": "doc",
493 | "content": [
494 | {
495 | "type": "paragraph",
496 | "content": [
497 | {
498 | "type": "text",
499 | "text": "Edit tldraw diagrams 😀 inline in Slidev!"
500 | }
501 | ]
502 | }
503 | ]
504 | }
505 | },
506 | "typeName": "shape"
507 | },
508 | "binding:FLt0YZlrtKYF3WsekLFe0": {
509 | "meta": {},
510 | "id": "binding:FLt0YZlrtKYF3WsekLFe0",
511 | "type": "arrow",
512 | "fromId": "shape:r3TfPhlREpUsepi-q9TR1",
513 | "toId": "shape:Zd81MkEhpZONg-DS82MYE",
514 | "props": {
515 | "isPrecise": false,
516 | "isExact": false,
517 | "normalizedAnchor": {
518 | "x": 0.5057779579814575,
519 | "y": 0.470478089218678
520 | },
521 | "terminal": "start",
522 | "snap": "none"
523 | },
524 | "typeName": "binding"
525 | },
526 | "page:page": {
527 | "meta": {},
528 | "id": "page:page",
529 | "name": "Page 1",
530 | "index": "a1",
531 | "typeName": "page"
532 | },
533 | "document:document": {
534 | "gridSize": 10,
535 | "name": "",
536 | "meta": {},
537 | "id": "document:document",
538 | "typeName": "document"
539 | },
540 | "shape:u5-Hl-RlK7_fT6djKAYfG": {
541 | "x": -117.31524283540068,
542 | "y": 298.2086587785797,
543 | "rotation": 0,
544 | "isLocked": false,
545 | "opacity": 1,
546 | "meta": {},
547 | "id": "shape:u5-Hl-RlK7_fT6djKAYfG",
548 | "type": "geo",
549 | "props": {
550 | "w": 124.27197002894175,
551 | "h": 72.81046104416156,
552 | "geo": "rectangle",
553 | "color": "black",
554 | "labelColor": "black",
555 | "fill": "solid",
556 | "dash": "draw",
557 | "size": "s",
558 | "font": "draw",
559 | "align": "middle",
560 | "verticalAlign": "middle",
561 | "growY": 0,
562 | "url": "",
563 | "scale": 1,
564 | "richText": {
565 | "type": "doc",
566 | "content": [
567 | {
568 | "type": "paragraph",
569 | "content": [
570 | {
571 | "type": "text",
572 | "text": "Circle"
573 | }
574 | ]
575 | }
576 | ]
577 | }
578 | },
579 | "parentId": "page:page",
580 | "index": "aA",
581 | "typeName": "shape"
582 | },
583 | "binding:TvawXdI6LP03gIvq7rwm9": {
584 | "meta": {},
585 | "id": "binding:TvawXdI6LP03gIvq7rwm9",
586 | "type": "arrow",
587 | "fromId": "shape:zkNBEJRBzA2l-NxBcMpxe",
588 | "toId": "shape:To35dgsqEchAkqW-ctkFa",
589 | "props": {
590 | "isPrecise": true,
591 | "isExact": false,
592 | "normalizedAnchor": {
593 | "x": 0.5,
594 | "y": 0.5
595 | },
596 | "terminal": "start",
597 | "snap": "none"
598 | },
599 | "typeName": "binding"
600 | },
601 | "shape:Y_iq5GJenFdWBOLRCMdYp": {
602 | "x": 101.27733134263872,
603 | "y": 458.6865396578984,
604 | "rotation": 6.213372137099813,
605 | "isLocked": false,
606 | "opacity": 1,
607 | "meta": {},
608 | "id": "shape:Y_iq5GJenFdWBOLRCMdYp",
609 | "type": "geo",
610 | "props": {
611 | "w": 466.98799831379364,
612 | "h": 224.38133252107522,
613 | "geo": "arrow-right",
614 | "color": "violet",
615 | "labelColor": "black",
616 | "fill": "solid",
617 | "dash": "draw",
618 | "size": "xl",
619 | "font": "draw",
620 | "align": "middle",
621 | "verticalAlign": "middle",
622 | "growY": 0,
623 | "url": "",
624 | "scale": 1,
625 | "richText": {
626 | "type": "doc",
627 | "content": [
628 | {
629 | "type": "paragraph",
630 | "content": [
631 | {
632 | "type": "text",
633 | "text": "slidev-addon-tldraw"
634 | }
635 | ]
636 | }
637 | ]
638 | }
639 | },
640 | "parentId": "page:page",
641 | "index": "a5",
642 | "typeName": "shape"
643 | }
644 | },
645 | "schema": {
646 | "schemaVersion": 2,
647 | "sequences": {
648 | "com.tldraw.store": 4,
649 | "com.tldraw.asset": 1,
650 | "com.tldraw.camera": 1,
651 | "com.tldraw.document": 2,
652 | "com.tldraw.instance": 25,
653 | "com.tldraw.instance_page_state": 5,
654 | "com.tldraw.page": 1,
655 | "com.tldraw.instance_presence": 6,
656 | "com.tldraw.pointer": 1,
657 | "com.tldraw.shape": 4,
658 | "com.tldraw.asset.bookmark": 2,
659 | "com.tldraw.asset.image": 5,
660 | "com.tldraw.asset.video": 5,
661 | "com.tldraw.shape.arrow": 6,
662 | "com.tldraw.shape.bookmark": 2,
663 | "com.tldraw.shape.draw": 2,
664 | "com.tldraw.shape.embed": 4,
665 | "com.tldraw.shape.frame": 1,
666 | "com.tldraw.shape.geo": 10,
667 | "com.tldraw.shape.group": 0,
668 | "com.tldraw.shape.highlight": 1,
669 | "com.tldraw.shape.image": 5,
670 | "com.tldraw.shape.line": 5,
671 | "com.tldraw.shape.note": 9,
672 | "com.tldraw.shape.text": 3,
673 | "com.tldraw.shape.video": 4,
674 | "com.tldraw.binding.arrow": 1
675 | }
676 | }
677 | }
--------------------------------------------------------------------------------
/public/tldraw/doc-jmR9lNpzKflz-zVIYq9To.json:
--------------------------------------------------------------------------------
1 | {
2 | "store": {
3 | "shape:5DeYTkM6kL0IeejujuQkK": {
4 | "x": 419.3244177108652,
5 | "y": 243.6520092717556,
6 | "rotation": 0.19198621771937624,
7 | "isLocked": false,
8 | "opacity": 1,
9 | "meta": {},
10 | "id": "shape:5DeYTkM6kL0IeejujuQkK",
11 | "type": "image",
12 | "props": {
13 | "w": 325.8551862421421,
14 | "h": 346.2324059684078,
15 | "assetId": "asset:941767786",
16 | "playing": true,
17 | "url": "",
18 | "crop": null,
19 | "flipX": false,
20 | "flipY": false,
21 | "altText": ""
22 | },
23 | "parentId": "page:page",
24 | "index": "a2",
25 | "typeName": "shape"
26 | },
27 | "binding:Tq6e8vMwfhij2RC9PkhtN": {
28 | "meta": {},
29 | "id": "binding:Tq6e8vMwfhij2RC9PkhtN",
30 | "type": "arrow",
31 | "fromId": "shape:jGgO-OZNQNCNQtyj3vsZc",
32 | "toId": "shape:5DeYTkM6kL0IeejujuQkK",
33 | "props": {
34 | "isPrecise": false,
35 | "isExact": false,
36 | "normalizedAnchor": {
37 | "x": 0.5324883952359072,
38 | "y": 0.6394477591138544
39 | },
40 | "terminal": "end",
41 | "snap": "none"
42 | },
43 | "typeName": "binding"
44 | },
45 | "asset:941767786": {
46 | "meta": {},
47 | "id": "asset:941767786",
48 | "type": "image",
49 | "typeName": "asset",
50 | "props": {
51 | "name": "tldrawFile",
52 | "src": "/tldraw/assets/SJr22o2fBc9y7rKqqs7VS-tldrawFile",
53 | "w": 1807,
54 | "h": 1920,
55 | "fileSize": 191014,
56 | "mimeType": "image/png",
57 | "isAnimated": false
58 | }
59 | },
60 | "shape:jGgO-OZNQNCNQtyj3vsZc": {
61 | "x": -7.541715343064823,
62 | "y": 528.5775185808919,
63 | "rotation": 0,
64 | "isLocked": false,
65 | "opacity": 1,
66 | "meta": {},
67 | "id": "shape:jGgO-OZNQNCNQtyj3vsZc",
68 | "type": "arrow",
69 | "props": {
70 | "dash": "draw",
71 | "size": "m",
72 | "fill": "none",
73 | "color": "black",
74 | "labelColor": "black",
75 | "bend": 0,
76 | "start": {
77 | "x": 0,
78 | "y": 0
79 | },
80 | "end": {
81 | "x": 345.76030245282755,
82 | "y": -0.31895265726427624
83 | },
84 | "arrowheadStart": "none",
85 | "arrowheadEnd": "arrow",
86 | "text": "",
87 | "labelPosition": 0.5,
88 | "font": "draw",
89 | "scale": 1,
90 | "kind": "arc",
91 | "elbowMidPoint": 0.5
92 | },
93 | "parentId": "page:page",
94 | "index": "a4",
95 | "typeName": "shape"
96 | },
97 | "shape:35fydjYCGGDqoQlyzIjaL": {
98 | "x": -75.92394709371632,
99 | "y": 115.54039919112671,
100 | "rotation": 0,
101 | "isLocked": false,
102 | "opacity": 1,
103 | "meta": {},
104 | "id": "shape:35fydjYCGGDqoQlyzIjaL",
105 | "type": "draw",
106 | "props": {
107 | "segments": [
108 | {
109 | "type": "free",
110 | "points": [
111 | {
112 | "x": 0,
113 | "y": 0,
114 | "z": 0.5
115 | },
116 | {
117 | "x": 1.62,
118 | "y": 0,
119 | "z": 0.5
120 | },
121 | {
122 | "x": 4.18,
123 | "y": 0,
124 | "z": 0.5
125 | },
126 | {
127 | "x": 6.47,
128 | "y": 0,
129 | "z": 0.5
130 | },
131 | {
132 | "x": 9.03,
133 | "y": 0,
134 | "z": 0.5
135 | },
136 | {
137 | "x": 12.72,
138 | "y": 0,
139 | "z": 0.5
140 | },
141 | {
142 | "x": 16.32,
143 | "y": 0,
144 | "z": 0.5
145 | },
146 | {
147 | "x": 18.61,
148 | "y": 0,
149 | "z": 0.5
150 | },
151 | {
152 | "x": 21.7,
153 | "y": 0,
154 | "z": 0.5
155 | },
156 | {
157 | "x": 25.54,
158 | "y": 0,
159 | "z": 0.5
160 | },
161 | {
162 | "x": 27.67,
163 | "y": 0.89,
164 | "z": 0.5
165 | },
166 | {
167 | "x": 30.76,
168 | "y": 1.89,
169 | "z": 0.5
170 | },
171 | {
172 | "x": 35.12,
173 | "y": 2.22,
174 | "z": 0.5
175 | },
176 | {
177 | "x": 39.92,
178 | "y": 2.22,
179 | "z": 0.5
180 | },
181 | {
182 | "x": 44.73,
183 | "y": 0.62,
184 | "z": 0.5
185 | },
186 | {
187 | "x": 48.65,
188 | "y": -1.78,
189 | "z": 0.5
190 | },
191 | {
192 | "x": 50.45,
193 | "y": -2.86,
194 | "z": 0.5
195 | },
196 | {
197 | "x": 54.98,
198 | "y": -5.31,
199 | "z": 0.5
200 | },
201 | {
202 | "x": 57.54,
203 | "y": -5.95,
204 | "z": 0.5
205 | },
206 | {
207 | "x": 59.67,
208 | "y": -5.95,
209 | "z": 0.5
210 | },
211 | {
212 | "x": 64.36,
213 | "y": -3.3,
214 | "z": 0.5
215 | },
216 | {
217 | "x": 68.32,
218 | "y": 1.98,
219 | "z": 0.5
220 | },
221 | {
222 | "x": 71.04,
223 | "y": 5.59,
224 | "z": 0.5
225 | },
226 | {
227 | "x": 77.31,
228 | "y": 10.99,
229 | "z": 0.5
230 | },
231 | {
232 | "x": 82.43,
233 | "y": 12.75,
234 | "z": 0.5
235 | },
236 | {
237 | "x": 86.79,
238 | "y": 13.2,
239 | "z": 0.5
240 | },
241 | {
242 | "x": 90.4,
243 | "y": 13.2,
244 | "z": 0.5
245 | },
246 | {
247 | "x": 92.97,
248 | "y": 12.88,
249 | "z": 0.5
250 | },
251 | {
252 | "x": 94.29,
253 | "y": 12.36,
254 | "z": 0.5
255 | },
256 | {
257 | "x": 95.34,
258 | "y": 12.36,
259 | "z": 0.5
260 | },
261 | {
262 | "x": 97.03,
263 | "y": 12.36,
264 | "z": 0.5
265 | },
266 | {
267 | "x": 100.11,
268 | "y": 12.36,
269 | "z": 0.5
270 | },
271 | {
272 | "x": 109.43,
273 | "y": 12.8,
274 | "z": 0.5
275 | },
276 | {
277 | "x": 117.11,
278 | "y": 12.8,
279 | "z": 0.5
280 | },
281 | {
282 | "x": 123.58,
283 | "y": 11.76,
284 | "z": 0.5
285 | },
286 | {
287 | "x": 124.43,
288 | "y": 11.2,
289 | "z": 0.5
290 | },
291 | {
292 | "x": 128.11,
293 | "y": 9.36,
294 | "z": 0.5
295 | },
296 | {
297 | "x": 132.12,
298 | "y": 7,
299 | "z": 0.5
300 | },
301 | {
302 | "x": 134.25,
303 | "y": 6.11,
304 | "z": 0.5
305 | },
306 | {
307 | "x": 137.33,
308 | "y": 6.11,
309 | "z": 0.5
310 | },
311 | {
312 | "x": 140.94,
313 | "y": 5.76,
314 | "z": 0.5
315 | },
316 | {
317 | "x": 145.82,
318 | "y": 5.32,
319 | "z": 0.5
320 | },
321 | {
322 | "x": 149.51,
323 | "y": 4.28,
324 | "z": 0.5
325 | },
326 | {
327 | "x": 152.59,
328 | "y": 3.24,
329 | "z": 0.5
330 | },
331 | {
332 | "x": 155.68,
333 | "y": 2.2,
334 | "z": 0.5
335 | },
336 | {
337 | "x": 158.65,
338 | "y": 1.88,
339 | "z": 0.5
340 | },
341 | {
342 | "x": 159.49,
343 | "y": 1.88,
344 | "z": 0.5
345 | },
346 | {
347 | "x": 162.03,
348 | "y": 1.88,
349 | "z": 0.5
350 | },
351 | {
352 | "x": 162.87,
353 | "y": 2.44,
354 | "z": 0.5
355 | },
356 | {
357 | "x": 165.48,
358 | "y": 3.57,
359 | "z": 0.5
360 | },
361 | {
362 | "x": 167.17,
363 | "y": 4.14,
364 | "z": 0.5
365 | },
366 | {
367 | "x": 168.85,
368 | "y": 4.14,
369 | "z": 0.5
370 | },
371 | {
372 | "x": 170.54,
373 | "y": 4.14,
374 | "z": 0.5
375 | },
376 | {
377 | "x": 173.11,
378 | "y": 4.14,
379 | "z": 0.5
380 | },
381 | {
382 | "x": 175.67,
383 | "y": 4.14,
384 | "z": 0.5
385 | },
386 | {
387 | "x": 177.36,
388 | "y": 4.14,
389 | "z": 0.5
390 | },
391 | {
392 | "x": 178.69,
393 | "y": 4.14,
394 | "z": 0.5
395 | },
396 | {
397 | "x": 180.13,
398 | "y": 4.14,
399 | "z": 0.5
400 | },
401 | {
402 | "x": 180.98,
403 | "y": 4.14,
404 | "z": 0.5
405 | },
406 | {
407 | "x": 184.39,
408 | "y": 4.14,
409 | "z": 0.5
410 | },
411 | {
412 | "x": 185.67,
413 | "y": 4.14,
414 | "z": 0.5
415 | },
416 | {
417 | "x": 188.76,
418 | "y": 4.14,
419 | "z": 0.5
420 | },
421 | {
422 | "x": 192.61,
423 | "y": 4.14,
424 | "z": 0.5
425 | },
426 | {
427 | "x": 194.73,
428 | "y": 3.53,
429 | "z": 0.5
430 | },
431 | {
432 | "x": 197.3,
433 | "y": 3.53,
434 | "z": 0.5
435 | },
436 | {
437 | "x": 200.38,
438 | "y": 3.53,
439 | "z": 0.5
440 | },
441 | {
442 | "x": 203.47,
443 | "y": 3.53,
444 | "z": 0.5
445 | },
446 | {
447 | "x": 206.04,
448 | "y": 3.53,
449 | "z": 0.5
450 | },
451 | {
452 | "x": 209.12,
453 | "y": 3.53,
454 | "z": 0.5
455 | },
456 | {
457 | "x": 212.21,
458 | "y": 3.53,
459 | "z": 0.5
460 | },
461 | {
462 | "x": 217.1,
463 | "y": 3.53,
464 | "z": 0.5
465 | },
466 | {
467 | "x": 220.7,
468 | "y": 3.18,
469 | "z": 0.5
470 | },
471 | {
472 | "x": 221.55,
473 | "y": 3.18,
474 | "z": 0.5
475 | },
476 | {
477 | "x": 224.16,
478 | "y": 3.18,
479 | "z": 0.5
480 | },
481 | {
482 | "x": 225.12,
483 | "y": 3.18,
484 | "z": 0.5
485 | },
486 | {
487 | "x": 225.81,
488 | "y": 3.18,
489 | "z": 0.5
490 | },
491 | {
492 | "x": 226.85,
493 | "y": 3.18,
494 | "z": 0.5
495 | },
496 | {
497 | "x": 227.82,
498 | "y": 3.42,
499 | "z": 0.5
500 | },
501 | {
502 | "x": 228.78,
503 | "y": 3.66,
504 | "z": 0.5
505 | },
506 | {
507 | "x": 230.11,
508 | "y": 3.66,
509 | "z": 0.5
510 | },
511 | {
512 | "x": 231.44,
513 | "y": 3.66,
514 | "z": 0.5
515 | },
516 | {
517 | "x": 232.76,
518 | "y": 3.94,
519 | "z": 0.5
520 | },
521 | {
522 | "x": 236.17,
523 | "y": 4.27,
524 | "z": 0.5
525 | },
526 | {
527 | "x": 239.26,
528 | "y": 4.63,
529 | "z": 0.5
530 | },
531 | {
532 | "x": 240.54,
533 | "y": 4.63,
534 | "z": 0.5
535 | },
536 | {
537 | "x": 244.91,
538 | "y": 4.63,
539 | "z": 0.5
540 | },
541 | {
542 | "x": 247.04,
543 | "y": 4.63,
544 | "z": 0.5
545 | },
546 | {
547 | "x": 249.16,
548 | "y": 4.03,
549 | "z": 0.5
550 | },
551 | {
552 | "x": 251.29,
553 | "y": 2.83,
554 | "z": 0.5
555 | },
556 | {
557 | "x": 253.42,
558 | "y": 1.9,
559 | "z": 0.5
560 | },
561 | {
562 | "x": 255.18,
563 | "y": 1.02,
564 | "z": 0.5
565 | },
566 | {
567 | "x": 256.51,
568 | "y": 0.78,
569 | "z": 0.5
570 | },
571 | {
572 | "x": 256.91,
573 | "y": 0.78,
574 | "z": 0.5
575 | },
576 | {
577 | "x": 257.32,
578 | "y": 0.58,
579 | "z": 0.5
580 | },
581 | {
582 | "x": 257.72,
583 | "y": 0.58,
584 | "z": 0.5
585 | },
586 | {
587 | "x": 258.11,
588 | "y": 0.58,
589 | "z": 0.5
590 | },
591 | {
592 | "x": 258.51,
593 | "y": 0.58,
594 | "z": 0.5
595 | },
596 | {
597 | "x": 258.9,
598 | "y": 0.58,
599 | "z": 0.5
600 | },
601 | {
602 | "x": 259.07,
603 | "y": 0.58,
604 | "z": 0.5
605 | }
606 | ]
607 | }
608 | ],
609 | "color": "light-green",
610 | "fill": "none",
611 | "dash": "draw",
612 | "size": "m",
613 | "isComplete": true,
614 | "isClosed": false,
615 | "isPen": false,
616 | "scale": 1
617 | },
618 | "parentId": "page:page",
619 | "index": "a1",
620 | "typeName": "shape"
621 | },
622 | "shape:GZ3PHYLj8KC8hudfNN14f": {
623 | "x": -102.78130681653244,
624 | "y": 411.6623141837062,
625 | "rotation": 0,
626 | "isLocked": false,
627 | "opacity": 1,
628 | "meta": {},
629 | "id": "shape:GZ3PHYLj8KC8hudfNN14f",
630 | "type": "geo",
631 | "props": {
632 | "w": 200,
633 | "h": 200,
634 | "geo": "rectangle",
635 | "color": "violet",
636 | "labelColor": "black",
637 | "fill": "solid",
638 | "dash": "draw",
639 | "size": "m",
640 | "font": "draw",
641 | "align": "middle",
642 | "verticalAlign": "middle",
643 | "growY": 0,
644 | "url": "",
645 | "scale": 1,
646 | "richText": {
647 | "type": "doc",
648 | "content": [
649 | {
650 | "type": "paragraph",
651 | "content": [
652 | {
653 | "type": "text",
654 | "text": "Bear"
655 | }
656 | ]
657 | }
658 | ]
659 | }
660 | },
661 | "parentId": "page:page",
662 | "index": "a3",
663 | "typeName": "shape"
664 | },
665 | "binding:665v3CaSbHdX9VL1JfnKB": {
666 | "meta": {},
667 | "id": "binding:665v3CaSbHdX9VL1JfnKB",
668 | "type": "arrow",
669 | "fromId": "shape:jGgO-OZNQNCNQtyj3vsZc",
670 | "toId": "shape:GZ3PHYLj8KC8hudfNN14f",
671 | "props": {
672 | "isPrecise": false,
673 | "isExact": false,
674 | "normalizedAnchor": {
675 | "x": 0.47619795736733805,
676 | "y": 0.5845760219859286
677 | },
678 | "terminal": "start",
679 | "snap": "none"
680 | },
681 | "typeName": "binding"
682 | },
683 | "page:page": {
684 | "meta": {},
685 | "id": "page:page",
686 | "name": "Page 1",
687 | "index": "a1",
688 | "typeName": "page"
689 | },
690 | "document:document": {
691 | "gridSize": 10,
692 | "name": "",
693 | "meta": {},
694 | "id": "document:document",
695 | "typeName": "document"
696 | },
697 | "asset:832450727": {
698 | "meta": {},
699 | "id": "asset:832450727",
700 | "type": "image",
701 | "typeName": "asset",
702 | "props": {
703 | "name": "tldrawFile",
704 | "src": "/tldraw/assets/9M9vWAk4iJbH0SFEUYD3h-tldrawFile",
705 | "w": 800,
706 | "h": 800,
707 | "fileSize": 263945,
708 | "mimeType": "image/png",
709 | "isAnimated": false
710 | }
711 | }
712 | },
713 | "schema": {
714 | "schemaVersion": 2,
715 | "sequences": {
716 | "com.tldraw.store": 4,
717 | "com.tldraw.asset": 1,
718 | "com.tldraw.camera": 1,
719 | "com.tldraw.document": 2,
720 | "com.tldraw.instance": 25,
721 | "com.tldraw.instance_page_state": 5,
722 | "com.tldraw.page": 1,
723 | "com.tldraw.instance_presence": 6,
724 | "com.tldraw.pointer": 1,
725 | "com.tldraw.shape": 4,
726 | "com.tldraw.asset.bookmark": 2,
727 | "com.tldraw.asset.image": 5,
728 | "com.tldraw.asset.video": 5,
729 | "com.tldraw.shape.arrow": 6,
730 | "com.tldraw.shape.bookmark": 2,
731 | "com.tldraw.shape.draw": 2,
732 | "com.tldraw.shape.embed": 4,
733 | "com.tldraw.shape.frame": 1,
734 | "com.tldraw.shape.geo": 10,
735 | "com.tldraw.shape.group": 0,
736 | "com.tldraw.shape.highlight": 1,
737 | "com.tldraw.shape.image": 5,
738 | "com.tldraw.shape.line": 5,
739 | "com.tldraw.shape.note": 9,
740 | "com.tldraw.shape.text": 3,
741 | "com.tldraw.shape.video": 4,
742 | "com.tldraw.binding.arrow": 1
743 | }
744 | }
745 | }
--------------------------------------------------------------------------------
/public/tldraw/assets/ySUCTtXYhzFfCLGpA1Orz-SparseConfetti.svg:
--------------------------------------------------------------------------------
1 |
2 |
905 |
--------------------------------------------------------------------------------