├── .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 | 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 | 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 | ![example](./example-assets/tldraw-in-slidev.png) 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 | ![example](./example-assets/cover-entire-slide.png) 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 | ![example](./example-assets/slidev-menu-grid-dark.png) 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 | 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 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | 26 | 27 | 28 | 29 | 31 | 51 | 53 | 58 | 63 | 68 | 73 | 78 | 83 | 88 | 93 | 98 | 103 | 108 | 113 | 118 | 123 | 128 | 133 | 138 | 143 | 148 | 153 | 158 | 163 | 168 | 173 | 178 | 183 | 188 | 193 | 198 | 203 | 208 | 213 | 218 | 223 | 228 | 233 | 238 | 243 | 248 | 253 | 258 | 263 | 268 | 273 | 278 | 283 | 288 | 293 | 298 | 303 | 308 | 313 | 318 | 323 | 328 | 333 | 338 | 343 | 348 | 353 | 358 | 363 | 368 | 373 | 378 | 383 | 388 | 393 | 398 | 403 | 408 | 413 | 418 | 423 | 428 | 433 | 438 | 443 | 448 | 453 | 458 | 463 | 468 | 473 | 478 | 483 | 488 | 493 | 498 | 503 | 508 | 513 | 518 | 523 | 528 | 533 | 538 | 543 | 548 | 553 | 558 | 563 | 568 | 573 | 578 | 583 | 588 | 593 | 598 | 603 | 608 | 613 | 618 | 623 | 628 | 633 | 638 | 643 | 648 | 653 | 658 | 663 | 668 | 673 | 678 | 683 | 688 | 693 | 698 | 703 | 708 | 713 | 718 | 723 | 728 | 733 | 738 | 743 | 748 | 753 | 758 | 763 | 768 | 773 | 778 | 783 | 788 | 793 | 798 | 803 | 808 | 813 | 818 | 823 | 828 | 833 | 838 | 843 | 848 | 853 | 858 | 863 | 868 | 873 | 878 | 883 | 888 | 893 | 898 | 903 | 904 | 905 | --------------------------------------------------------------------------------