From e27e5e4c41b0fcee462219806f8ff3ceaeafd62e Mon Sep 17 00:00:00 2001 From: ruinivist <179396038+ruinivist@users.noreply.github.com> Date: Tue, 2 Jun 2026 10:41:45 +0000 Subject: [PATCH] feat: add security headers to caddyfile --- .github/workflows/publish-ghcr.yml | 2 +- .prettierignore | 3 + AGENTS.md | 4 + Caddyfile | 14 +- DECISIONS.md | 8 + Dockerfile | 1 + README.md | 3 +- bun.lock | 30 ++- package.json | 11 + patches/@excalidraw%2Fexcalidraw@0.18.1.patch | 51 ++++ src/client/App.tsx | 47 +++- src/client/components/DrawingSidebar.tsx | 175 ++++++++++-- src/client/components/EditorCanvas.tsx | 10 +- src/client/components/PublicViewer.tsx | 53 ++++ src/client/hooks/useDrawingSession.ts | 248 +++++++++++++++--- src/client/hooks/usePublicDrawing.ts | 82 ++++++ src/client/hooks/useThemeTokenSync.ts | 14 +- src/client/styles.css | 49 ++++ src/core/publication.ts | 25 ++ src/core/scene.test.ts | 8 +- src/core/scene.ts | 4 +- src/core/shared.ts | 13 + src/mcp/server.test.ts | 23 +- src/mcp/server.ts | 66 +++-- src/server/api.test.ts | 231 +++++++++++++++- src/server/api.ts | 133 +++++++++- src/server/db.test.ts | 123 ++++++++- src/server/db.ts | 226 +++++++++++++++- src/server/dev-server.test.ts | 11 + src/server/dev-server.tsx | 1 + src/server/http-server.test.ts | 48 +++- src/server/http-server.tsx | 22 +- 32 files changed, 1593 insertions(+), 146 deletions(-) create mode 100644 .prettierignore create mode 100644 DECISIONS.md create mode 100644 patches/@excalidraw%2Fexcalidraw@0.18.1.patch create mode 100644 src/client/components/PublicViewer.tsx create mode 100644 src/client/hooks/usePublicDrawing.ts create mode 100644 src/core/publication.ts create mode 100644 src/server/dev-server.test.ts diff --git a/.github/workflows/publish-ghcr.yml b/.github/workflows/publish-ghcr.yml index ec4e505..85d6c67 100644 --- a/.github/workflows/publish-ghcr.yml +++ b/.github/workflows/publish-ghcr.yml @@ -3,7 +3,7 @@ name: Publish GHCR Image on: push: tags: - - 'release-*' + - "release-*" permissions: contents: read diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..0f47a58 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +dist +data +excali-box-data diff --git a/AGENTS.md b/AGENTS.md index 95ef395..9484871 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,3 +5,7 @@ - Keep commit messages terse. Example: `feat: dark mode by default` + +# Decisions on architecture and design + +Read [DECISIONS.md](DECISIONS.md) diff --git a/Caddyfile b/Caddyfile index 85d3d15..cdb0256 100644 --- a/Caddyfile +++ b/Caddyfile @@ -12,25 +12,19 @@ Referrer-Policy "strict-origin-when-cross-origin" } - @mcp path /mcp - handle @mcp { + handle /mcp { reverse_proxy 127.0.0.1:3001 } - @api path /api/* - handle @api { - reverse_proxy 127.0.0.1:3000 - } - - @root path / - handle @root { + @app path / /api/* + handle @app { reverse_proxy 127.0.0.1:3000 } handle { root * /srv/public - @html path /index.html /d/* + @html path /index.html /d/* /p/* header @html Cache-Control "no-cache" @assets path_regexp assets \.(?:css|js|mjs|svg|png|jpg|jpeg|gif|webp|ico|woff2?|ttf|otf)$ diff --git a/DECISIONS.md b/DECISIONS.md new file mode 100644 index 0000000..b1332c7 --- /dev/null +++ b/DECISIONS.md @@ -0,0 +1,8 @@ +# a list of architectural decisions + +( to remind me when I over-engineer and be fickle minded ) + +## Image is NOT meant to be exposed directly to the internet + +- the defaults MUST take care of this +- and hence AUTH won't be a part of the image, whatever the user is using for reverse proxy should handle auth. diff --git a/Dockerfile b/Dockerfile index 03b4817..075d3f1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,7 @@ FROM oven/bun:1.3.11-alpine AS build WORKDIR /app COPY package.json bun.lock tsconfig.json ./ +COPY patches ./patches RUN bun install --frozen-lockfile COPY src ./src diff --git a/README.md b/README.md index 9645061..f6ba7c4 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,6 @@ Private self-hosted Excalidraw with Bun, Caddy, and SQLite. This is bare wrapper around the excalidraw packages that adds autosave and disk-persistence. No auth to keep things simple - you either run in a private network or put it behind Caddy auth or similar solutions. -> GPT 5.5 generated - ## Tl;dr When building the image yourself @@ -52,6 +50,7 @@ Open `http://localhost:3000`. ## Test ```bash +bun run format bun run test bun run typecheck ``` diff --git a/bun.lock b/bun.lock index f1349bd..59ca08d 100644 --- a/bun.lock +++ b/bun.lock @@ -17,10 +17,16 @@ "@types/bun": "^1.3.14", "@types/react": "^19.2.15", "@types/react-dom": "^19.2.3", + "prettier": "^3.8.3", + "pretty-quick": "^4.2.2", + "simple-git-hooks": "^2.13.1", "typescript": "^5.9.3", }, }, }, + "patchedDependencies": { + "@excalidraw/excalidraw@0.18.1": "patches/@excalidraw%2Fexcalidraw@0.18.1.patch", + }, "packages": { "@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="], @@ -66,6 +72,8 @@ "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="], + "@pkgr/core": ["@pkgr/core@0.2.10", "", {}, "sha512-x6fFWCeak8aCGfqZfe6CXYt5xVjxe9Os1cIPmVRcToInKLjhJkRVXvJ/L3/1KxFkjDQdbZV/YsuLKqa8t/xKpA=="], + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="], "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg=="], @@ -426,6 +434,8 @@ "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + "ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + "image-blob-reduce": ["image-blob-reduce@3.0.1", "", { "dependencies": { "pica": "^7.1.0" } }, "sha512-/VmmWgIryG/wcn4TVrV7cC4mlfUC/oyiKIfSg5eVM3Ten/c1c34RJhMYKCWTnoSMHSqXLt3tsrBR4Q2HInvN+Q=="], "immutable": ["immutable@4.3.8", "", {}, "sha512-d/Ld9aLbKpNwyl0KiM2CT1WYvkitQ1TSvmRtkcV8FKStiDoA7Slzgjmb/1G2yhKM1p0XeNOieaTbFZmU1d3Xuw=="], @@ -492,6 +502,8 @@ "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "multimath": ["multimath@2.0.0", "", { "dependencies": { "glur": "^1.1.2", "object-assign": "^4.1.1" } }, "sha512-toRx66cAMJ+Ccz7pMIg38xSIrtnbozk0dchXezwQDMgQmbGpfxjtv68H+L00iFL8hxDaVjrmwAFSb3I6bg8Q2g=="], @@ -528,7 +540,9 @@ "pica": ["pica@7.1.1", "", { "dependencies": { "glur": "^1.1.2", "inherits": "^2.0.3", "multimath": "^2.0.0", "object-assign": "^4.1.1", "webworkify": "^1.5.0" } }, "sha512-WY73tMvNzXWEld2LicT9Y260L43isrZ85tPuqRyvtkljSDLmnNFQmZICt4xUJMVulmcc6L9O7jbBrtx3DOz/YQ=="], - "picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], @@ -542,6 +556,10 @@ "points-on-path": ["points-on-path@0.2.1", "", { "dependencies": { "path-data-parser": "0.1.0", "points-on-curve": "0.2.0" } }, "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g=="], + "prettier": ["prettier@3.8.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw=="], + + "pretty-quick": ["pretty-quick@4.2.2", "", { "dependencies": { "@pkgr/core": "^0.2.7", "ignore": "^7.0.5", "mri": "^1.2.0", "picocolors": "^1.1.1", "picomatch": "^4.0.2", "tinyexec": "^0.3.2", "tslib": "^2.8.1" }, "peerDependencies": { "prettier": "^3.0.0" }, "bin": { "pretty-quick": "lib/cli.mjs" } }, "sha512-uAh96tBW1SsD34VhhDmWuEmqbpfYc/B3j++5MC/6b3Cb8Ow7NJsvKFhg0eoGu2xXX+o9RkahkTK6sUdd8E7g5w=="], + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], "pwacompat": ["pwacompat@2.0.17", "", {}, "sha512-6Du7IZdIy7cHiv7AhtDy4X2QRM8IAD5DII69mt5qWibC2d15ZU8DmBG1WdZKekG11cChSu4zkSUGPF9sweOl6w=="], @@ -600,6 +618,8 @@ "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + "simple-git-hooks": ["simple-git-hooks@2.13.1", "", { "bin": { "simple-git-hooks": "cli.js" } }, "sha512-WszCLXwT4h2k1ufIXAgsbiTOazqqevFCIncOuUBZJ91DdvWcC5+OFkluWRQPrcuSYd8fjq+o2y1QfWqYMoAToQ=="], + "sliced": ["sliced@1.0.1", "", {}, "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], @@ -608,7 +628,7 @@ "stylis": ["stylis@4.4.0", "", {}, "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA=="], - "tinyexec": ["tinyexec@1.2.2", "", {}, "sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g=="], + "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], @@ -664,6 +684,8 @@ "zustand": ["zustand@4.5.7", "", { "dependencies": { "use-sync-external-store": "^1.2.2" }, "peerDependencies": { "@types/react": ">=16.8", "immer": ">=9.0.6", "react": ">=16.8" }, "optionalPeers": ["@types/react", "immer", "react"] }, "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw=="], + "@antfu/install-pkg/tinyexec": ["tinyexec@1.2.2", "", {}, "sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g=="], + "@chevrotain/cst-dts-gen/@chevrotain/types": ["@chevrotain/types@11.0.3", "", {}, "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ=="], "@chevrotain/cst-dts-gen/lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="], @@ -708,6 +730,8 @@ "@radix-ui/react-tabs/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-use-callback-ref": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg=="], + "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "chevrotain/@chevrotain/types": ["@chevrotain/types@11.0.3", "", {}, "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ=="], "chevrotain/lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="], @@ -732,6 +756,8 @@ "points-on-path/points-on-curve": ["points-on-curve@0.2.0", "", {}, "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A=="], + "readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "roughjs/points-on-curve": ["points-on-curve@0.2.0", "", {}, "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A=="], "type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="], diff --git a/package.json b/package.json index d6461fe..c720e5c 100644 --- a/package.json +++ b/package.json @@ -8,10 +8,15 @@ "build:client": "bun build --target=browser --production --outdir ./dist/public ./src/client/index.html", "build:server": "mkdir -p ./dist/server && bun build --target=bun --production --outfile ./dist/server/server.js ./src/server/http-server.tsx", "build:mcp": "mkdir -p ./dist/mcp && bun build --target=bun --production --outfile ./dist/mcp/mcp-server.js ./src/mcp/server.ts", + "format": "prettier --write .", + "postinstall": "simple-git-hooks", "start": "DATABASE_PATH=./data/excalidraw.sqlite bun ./dist/server/server.js", "test": "bun test", "typecheck": "tsc --noEmit" }, + "simple-git-hooks": { + "pre-commit": "bunx pretty-quick --staged" + }, "dependencies": { "@excalidraw/excalidraw": "^0.18.1", "@modelcontextprotocol/sdk": "^1.29.0", @@ -25,6 +30,12 @@ "@types/bun": "^1.3.14", "@types/react": "^19.2.15", "@types/react-dom": "^19.2.3", + "prettier": "^3.8.3", + "pretty-quick": "^4.2.2", + "simple-git-hooks": "^2.13.1", "typescript": "^5.9.3" + }, + "patchedDependencies": { + "@excalidraw/excalidraw@0.18.1": "patches/@excalidraw%2Fexcalidraw@0.18.1.patch" } } diff --git a/patches/@excalidraw%2Fexcalidraw@0.18.1.patch b/patches/@excalidraw%2Fexcalidraw@0.18.1.patch new file mode 100644 index 0000000..e7c07e4 --- /dev/null +++ b/patches/@excalidraw%2Fexcalidraw@0.18.1.patch @@ -0,0 +1,51 @@ +diff --git a/dist/dev/chunk-4FTI6OG3.js b/dist/dev/chunk-4FTI6OG3.js +index 65df46b6cd47f78f2c2c7f03574d233414a995a2..d3fa26db2a351936cde8216c244b1d487a1315b8 100644 +--- a/dist/dev/chunk-4FTI6OG3.js ++++ b/dist/dev/chunk-4FTI6OG3.js +@@ -364,15 +364,15 @@ var ROUGHNESS = { + cartoonist: 2 + }; + var STROKE_WIDTH = { +- thin: 1, +- bold: 2, +- extraBold: 4 ++ thin: 0.5, ++ bold: 0.75, ++ extraBold: 1 + }; + var DEFAULT_ELEMENT_PROPS = { + strokeColor: COLOR_PALETTE.black, + backgroundColor: COLOR_PALETTE.transparent, + fillStyle: "solid", +- strokeWidth: 2, ++ strokeWidth: 0.75, + strokeStyle: "solid", + roughness: ROUGHNESS.artist, + opacity: 100, +diff --git a/dist/prod/chunk-K2UTITRG.js b/dist/prod/chunk-K2UTITRG.js +index fd073bcc762a7ead793b3fb371f5235c95ccb053..ec557589613ce0d132895a2963b8c4fdca6467fd 100644 +--- a/dist/prod/chunk-K2UTITRG.js ++++ b/dist/prod/chunk-K2UTITRG.js +@@ -1,4 +1,4 @@ +-import{a as M}from"./chunk-ZUYEQ4TG.js";import{d as P}from"./chunk-SRAX5OIU.js";import l6 from"png-chunks-extract";import U6 from"png-chunk-text";import q9 from"png-chunks-encode";import{deflate as C9,inflate as V9}from"pako";import f6 from"open-color";var p6=(e,t)=>t.reduce((n,r)=>(r in e&&(n[r]=e[r]),n),{}),k9=5,Z9=5,Ps=4,fo=4,po=1,ft=[0,2,4,6,8];var pt=(e,t)=>t.map(n=>f6[e][n]),Z={transparent:"transparent",black:"#1e1e1e",white:"#ffffff",gray:pt("gray",ft),red:pt("red",ft),pink:pt("pink",ft),grape:pt("grape",ft),violet:pt("violet",ft),blue:pt("blue",ft),cyan:pt("cyan",ft),teal:pt("teal",ft),green:pt("green",ft),yellow:pt("yellow",ft),orange:pt("orange",ft),bronze:["#f8f1ee","#eaddd7","#d2bab0","#a18072","#846358"]},Ns=p6(Z,["cyan","blue","violet","grape","pink","green","teal","yellow","orange","red"]),W9=[Z.black,Z.red[fo],Z.green[fo],Z.blue[fo],Z.yellow[fo]],z9=[Z.transparent,Z.red[po],Z.green[po],Z.blue[po],Z.yellow[po]],Q9=[Z.white,"#f8f9fa","#f5faff","#fffce8","#fdf8f6"],j9={transparent:Z.transparent,white:Z.white,gray:Z.gray,black:Z.black,bronze:Z.bronze,...Ns},eE={transparent:Z.transparent,white:Z.white,gray:Z.gray,black:Z.black,bronze:Z.bronze,...Ns},Fs=e=>[Z.cyan[e],Z.blue[e],Z.violet[e],Z.grape[e],Z.pink[e],Z.green[e],Z.teal[e],Z.yellow[e],Z.orange[e],Z.red[e]],tE=(e,t,n)=>`#${((1<<24)+(e<<16)+(t<<8)+n).toString(16).slice(1)}`;var uo=/Mac|iPod|iPhone|iPad/.test(navigator.platform),iE=/^Win/.test(navigator.platform),Bs=/\b(android)\b/i.test(navigator.userAgent),aE="netscape"in window&&navigator.userAgent.indexOf("rv:")>1&&navigator.userAgent.indexOf("Gecko")>1,u6=navigator.userAgent.indexOf("Chrome")!==-1,sE=!u6&&navigator.userAgent.indexOf("Safari")!==-1,_s=/iPad|iPhone/.test(navigator.platform)||navigator.userAgent.includes("Mac")&&"ontouchend"in document,dE=()=>navigator.brave?.isBrave?.name==="isBrave",cE=typeof window<"u"&&"ResizeObserver"in window,lE="Excalidraw",Os=36,As=10,Ks=8,UE=5,fE=1,pE=30,Nt=Math.PI/12,uE="red",mo={TEXT:"text",CROSSHAIR:"crosshair",GRABBING:"grabbing",GRAB:"grab",POINTER:"pointer",MOVE:"move",AUTO:""},mE={MAIN:0,WHEEL:1,SECONDARY:2,TOUCH:-1,ERASER:5},bE={enabled:"all",disabled:"none",inheritFromUI:"var(--ui-pointerEvents)"};var EE={UNSTARTED:-1,ENDED:0,PLAYING:1,PAUSED:2,BUFFERING:3,CUED:5},je={TEST:"test",DEVELOPMENT:"development"},gE={SHAPE_ACTIONS_MENU:"App-menu__left",ZOOM_ACTIONS:"zoom-actions",SEARCH_MENU_INPUT_WRAPPER:"layer-ui__search-inputWrapper"},Mn="Xiaolai",Tn="Segoe UI Emoji",Ie={Virgil:1,Helvetica:2,Cascadia:3,Excalifont:5,Nunito:6,"Lilita One":7,"Comic Shanns":8,"Liberation Sans":9},Un={[Mn]:100,[Tn]:1e3},bo=e=>{switch(e){case Ie.Excalifont:return[Mn,Tn];default:return[Tn]}},ke={LIGHT:"light",DARK:"dark"},Pe={strokeColor:"#bbb",strokeWidth:2,strokeStyle:"solid",fillStyle:"solid",roughness:0,roundness:null,backgroundColor:"transparent",radius:8,nameOffsetY:3,nameColorLightTheme:"#999999",nameColorDarkTheme:"#7a7a7a",nameFontSize:14,nameLineHeight:1.25},Eo=1,ut=20,Ft=Ie.Excalifont,Vn="left",go="top";var br=2,Er=2*br,m6=1e-5,xE=2*Er-m6,hE="#ffffff",yE="#1e1e1e",IE="#a2f1a6";var xo=20,ho=5,yo={svg:"image/svg+xml",png:"image/png",jpg:"image/jpeg",gif:"image/gif",webp:"image/webp",bmp:"image/bmp",ico:"image/x-icon",avif:"image/avif",jfif:"image/jfif"},H={text:"text/plain",html:"text/html",json:"application/json",excalidraw:"application/vnd.excalidraw+json",excalidrawlib:"application/vnd.excalidrawlib+json","excalidraw.svg":"image/svg+xml","excalidraw.png":"image/png",binary:"application/octet-stream",...yo},Hs=[H.text,H.html,...Object.values(yo)],wE={png:"png",svg:"svg",clipboard:"clipboard"},Ze={excalidraw:"excalidraw",excalidrawClipboard:"excalidraw/clipboard",excalidrawLibrary:"excalidrawlib",excalidrawClipboardWithAPI:"excalidraw-api/clipboard"},Ci=window.EXCALIDRAW_EXPORT_SOURCE||window.location.origin,RE=500,TE=300,ME=500;var LE=100,DE=.1,Js=.1,Ys=30,SE=300;var Io="invert(93%) hue-rotate(180deg)",vE={addLibrary:"addLibrary"},$E={addLibrary:"addLibrary"},PE={canvasActions:{changeViewBackgroundColor:!0,clearCanvas:!0,export:{saveFileToDisk:!0},loadScene:!0,saveToActiveFile:!0,toggleTheme:null,saveAsImage:!0},tools:{image:!0}},NE=730,FE=1e3,BE=500,_E=1229,qn=2,Cs=[1,2,3],Vi=10,OE=1440,AE=4*1024*1024,re="http://www.w3.org/2000/svg";var qi={excalidraw:2,excalidrawLibrary:2},Se=5,Vs=.7,qs=11,kt={TOP:"top",MIDDLE:"middle",BOTTOM:"bottom"},Xi={LEFT:"left",CENTER:"center",RIGHT:"right"},Xs=20,wo=.25,Gs=32,ot={LEGACY:1,PROPORTIONAL_RADIUS:2,ADAPTIVE_RADIUS:3},Gi={architect:0,artist:1,cartoonist:2},KE={thin:1,bold:2,extraBold:4},we={strokeColor:Z.black,backgroundColor:Z.transparent,fillStyle:"solid",strokeWidth:2,strokeStyle:"solid",roughness:Gi.artist,opacity:100,locked:!1},b6="library",HE="search",ks={name:"default",defaultTab:b6},JE=new Set(["iframe","embeddable","image"]),fn={selection:"selection",rectangle:"rectangle",diamond:"diamond",ellipse:"ellipse",arrow:"arrow",line:"line",freedraw:"freedraw",text:"text",image:"image",eraser:"eraser",hand:"hand",frame:"frame",magicframe:"magicframe",embeddable:"embeddable",laser:"laser"},YE={OAI_API_KEY:"excalidraw-oai-api-key",MERMAID_TO_EXCALIDRAW:"mermaid-to-excalidraw",PUBLISH_LIBRARY:"publish-library-data"},Zs="Untitled",ki={generalStats:1,elementProperties:2},CE=1,Ws={sharp:"sharp",round:"round",elbow:"elbow"},zs=.3,gr="element",Qs=Symbol.for("__test__originalId__"),E6=(r=>(r.ACTIVE="active",r.AWAY="away",r.IDLE="idle",r))(E6||{});import{nanoid as A9}from"nanoid";var g6=Cs.includes(devicePixelRatio)?devicePixelRatio:1,mt=()=>({showWelcomeScreen:!1,theme:ke.LIGHT,collaborators:new Map,currentChartType:"bar",currentItemBackgroundColor:we.backgroundColor,currentItemEndArrowhead:"arrow",currentItemFillStyle:we.fillStyle,currentItemFontFamily:Ft,currentItemFontSize:ut,currentItemOpacity:we.opacity,currentItemRoughness:we.roughness,currentItemStartArrowhead:null,currentItemStrokeColor:we.strokeColor,currentItemRoundness:"round",currentItemArrowType:Ws.round,currentItemStrokeStyle:we.strokeStyle,currentItemStrokeWidth:we.strokeWidth,currentItemTextAlign:Vn,currentHoveredFontFamily:null,cursorButton:"up",activeEmbeddable:null,newElement:null,editingTextElement:null,editingGroupId:null,editingLinearElement:null,activeTool:{type:"selection",customType:null,locked:we.locked,lastActiveTool:null},penMode:!1,penDetected:!1,errorMessage:null,exportBackground:!0,exportScale:g6,exportEmbedScene:!1,exportWithDarkMode:!1,fileHandle:null,gridSize:xo,gridStep:ho,gridModeEnabled:!1,isBindingEnabled:!0,defaultSidebarDockedPreference:!1,isLoading:!1,isResizing:!1,isRotating:!1,lastPointerDownWith:"mouse",multiElement:null,name:null,contextMenu:null,openMenu:null,openPopup:null,openSidebar:null,openDialog:null,pasteDialog:{shown:!1,data:null},previousSelectedElementIds:{},resizingElement:null,scrolledOutside:!1,scrollX:0,scrollY:0,selectedElementIds:{},hoveredElementIds:{},selectedGroupIds:{},selectedElementsAreBeingDragged:!1,selectionElement:null,shouldCacheIgnoreZoom:!1,stats:{open:!1,panels:ki.generalStats|ki.elementProperties},startBoundElement:null,suggestedBindings:[],frameRendering:{enabled:!0,clip:!0,name:!0,outline:!0},frameToHighlight:null,editingFrame:null,elementsToHighlight:null,toast:null,viewBackgroundColor:Z.white,zenModeEnabled:!1,zoom:{value:1},viewModeEnabled:!1,pendingImageElementId:null,showHyperlinkPopup:!1,selectedLinearElement:null,snapLines:[],originSnapOffset:{x:0,y:0},objectsSnapModeEnabled:!1,userToFollow:null,followedBy:new Set,isCropping:!1,croppingElementId:null,searchMatches:[]}),x6=(e=>e)({showWelcomeScreen:{browser:!0,export:!1,server:!1},theme:{browser:!0,export:!1,server:!1},collaborators:{browser:!1,export:!1,server:!1},currentChartType:{browser:!0,export:!1,server:!1},currentItemBackgroundColor:{browser:!0,export:!1,server:!1},currentItemEndArrowhead:{browser:!0,export:!1,server:!1},currentItemFillStyle:{browser:!0,export:!1,server:!1},currentItemFontFamily:{browser:!0,export:!1,server:!1},currentItemFontSize:{browser:!0,export:!1,server:!1},currentItemRoundness:{browser:!0,export:!1,server:!1},currentItemArrowType:{browser:!0,export:!1,server:!1},currentItemOpacity:{browser:!0,export:!1,server:!1},currentItemRoughness:{browser:!0,export:!1,server:!1},currentItemStartArrowhead:{browser:!0,export:!1,server:!1},currentItemStrokeColor:{browser:!0,export:!1,server:!1},currentItemStrokeStyle:{browser:!0,export:!1,server:!1},currentItemStrokeWidth:{browser:!0,export:!1,server:!1},currentItemTextAlign:{browser:!0,export:!1,server:!1},currentHoveredFontFamily:{browser:!1,export:!1,server:!1},cursorButton:{browser:!0,export:!1,server:!1},activeEmbeddable:{browser:!1,export:!1,server:!1},newElement:{browser:!1,export:!1,server:!1},editingTextElement:{browser:!1,export:!1,server:!1},editingGroupId:{browser:!0,export:!1,server:!1},editingLinearElement:{browser:!1,export:!1,server:!1},activeTool:{browser:!0,export:!1,server:!1},penMode:{browser:!0,export:!1,server:!1},penDetected:{browser:!0,export:!1,server:!1},errorMessage:{browser:!1,export:!1,server:!1},exportBackground:{browser:!0,export:!1,server:!1},exportEmbedScene:{browser:!0,export:!1,server:!1},exportScale:{browser:!0,export:!1,server:!1},exportWithDarkMode:{browser:!0,export:!1,server:!1},fileHandle:{browser:!1,export:!1,server:!1},gridSize:{browser:!0,export:!0,server:!0},gridStep:{browser:!0,export:!0,server:!0},gridModeEnabled:{browser:!0,export:!0,server:!0},height:{browser:!1,export:!1,server:!1},isBindingEnabled:{browser:!1,export:!1,server:!1},defaultSidebarDockedPreference:{browser:!0,export:!1,server:!1},isLoading:{browser:!1,export:!1,server:!1},isResizing:{browser:!1,export:!1,server:!1},isRotating:{browser:!1,export:!1,server:!1},lastPointerDownWith:{browser:!0,export:!1,server:!1},multiElement:{browser:!1,export:!1,server:!1},name:{browser:!0,export:!1,server:!1},offsetLeft:{browser:!1,export:!1,server:!1},offsetTop:{browser:!1,export:!1,server:!1},contextMenu:{browser:!1,export:!1,server:!1},openMenu:{browser:!0,export:!1,server:!1},openPopup:{browser:!1,export:!1,server:!1},openSidebar:{browser:!0,export:!1,server:!1},openDialog:{browser:!1,export:!1,server:!1},pasteDialog:{browser:!1,export:!1,server:!1},previousSelectedElementIds:{browser:!0,export:!1,server:!1},resizingElement:{browser:!1,export:!1,server:!1},scrolledOutside:{browser:!0,export:!1,server:!1},scrollX:{browser:!0,export:!1,server:!1},scrollY:{browser:!0,export:!1,server:!1},selectedElementIds:{browser:!0,export:!1,server:!1},hoveredElementIds:{browser:!1,export:!1,server:!1},selectedGroupIds:{browser:!0,export:!1,server:!1},selectedElementsAreBeingDragged:{browser:!1,export:!1,server:!1},selectionElement:{browser:!1,export:!1,server:!1},shouldCacheIgnoreZoom:{browser:!0,export:!1,server:!1},stats:{browser:!0,export:!1,server:!1},startBoundElement:{browser:!1,export:!1,server:!1},suggestedBindings:{browser:!1,export:!1,server:!1},frameRendering:{browser:!1,export:!1,server:!1},frameToHighlight:{browser:!1,export:!1,server:!1},editingFrame:{browser:!1,export:!1,server:!1},elementsToHighlight:{browser:!1,export:!1,server:!1},toast:{browser:!1,export:!1,server:!1},viewBackgroundColor:{browser:!0,export:!0,server:!0},width:{browser:!1,export:!1,server:!1},zenModeEnabled:{browser:!0,export:!1,server:!1},zoom:{browser:!0,export:!1,server:!1},viewModeEnabled:{browser:!1,export:!1,server:!1},pendingImageElementId:{browser:!1,export:!1,server:!1},showHyperlinkPopup:{browser:!1,export:!1,server:!1},selectedLinearElement:{browser:!0,export:!1,server:!1},snapLines:{browser:!1,export:!1,server:!1},originSnapOffset:{browser:!1,export:!1,server:!1},objectsSnapModeEnabled:{browser:!0,export:!1,server:!1},userToFollow:{browser:!1,export:!1,server:!1},followedBy:{browser:!1,export:!1,server:!1},isCropping:{browser:!1,export:!1,server:!1},croppingElementId:{browser:!1,export:!1,server:!1},searchMatches:{browser:!1,export:!1,server:!1}}),js=(e,t)=>{let n={};for(let r of Object.keys(e))if(x6[r]?.[t]){let i=e[r];n[r]=i}return n};var Ro=e=>js(e,"export"),ed=e=>js(e,"server"),td=({activeTool:e})=>e.type==="eraser",nd=({activeTool:e})=>e.type==="hand";import p9 from"lodash.throttle";import L6 from"es6-promise-pool";var se=(e,t,n)=>Math.min(Math.max(e,t),n),To=(e,t,n="round")=>{let r=Math.pow(10,t);return Math[n]((e+Number.EPSILON)*r)/r},WE=(e,t,n="round")=>{let r=1/t;return Math[n](e*r)/r},xr=(e,t)=>(e+t)/2,Mo=e=>typeof e=="number"&&Number.isFinite(e),Zi=(e,t,n=1e-4)=>Math.abs(e-t)e<0?e+2*Math.PI:e>=2*Math.PI?e-2*Math.PI:e;function yr(e){return e*Math.PI/180}function rd(e){return e*180/Math.PI}function od(e){return Math.abs(Math.sin(2*e))<1e-4}function Be(e,t,n=0,r=0){return[e-n,t-r]}function O(e,t=[0,0]){return Be(e[0]-t[0],e[1]-t[1])}function Me(e,t){return e[0]*t[1]-t[0]*e[1]}function h6(e,t){return e[0]*t[0]+e[1]*t[1]}function st(e,t){return[e[0]+t[0],e[1]+t[1]]}function Lo(e,t){return[e[0]-t[0],e[1]-t[1]]}function ue(e,t){return Be(e[0]*t,e[1]*t)}function y6(e){return e[0]*e[0]+e[1]*e[1]}function I6(e){return Math.sqrt(y6(e))}var Je=e=>{let t=I6(e);return t===0?Be(0,0):Be(e[0]/t,e[1]/t)};function u(e,t){return[e,t]}function Ir(e){return e.length===2?u(e[0],e[1]):void 0}function q(e,t=u(0,0)){return u(t[0]+e[0],t[1]+e[1])}function Do(e){return Array.isArray(e)&&e.length===2&&typeof e[0]=="number"&&!isNaN(e[0])&&typeof e[1]=="number"&&!isNaN(e[1])}function _e(e,t){let n=Math.abs;return n(e[0]-t[0])<1e-4&&n(e[1]-t[1])<1e-4}function T([e,t],[n,r],o){return u((e-n)*Math.cos(o)-(t-r)*Math.sin(o)+n,(e-n)*Math.sin(o)+(t-r)*Math.cos(o)+r)}function wr(e,t=[0,0]){return u(e[0]+t[0],e[1]+t[1])}function Zt(e,t){return u((e[0]+t[0])/2,(e[1]+t[1])/2)}function ie(e,t){return Math.hypot(t[0]-e[0],t[1]-e[1])}function Xn(e,t){let n=t[0]-e[0],r=t[1]-e[1];return n*n+r*r}var bt=(e,t,n)=>wr(t,ue(O(e,t),n)),So=(e,t,n)=>t[0]<=Math.max(e[0],n[0])&&t[0]>=Math.min(e[0],n[0])&&t[1]<=Math.max(e[1],n[1])&&t[1]>=Math.min(e[1],n[1]);function Rr(e,t){return[e,t]}function id(e,t){let n=e[1][1]-e[0][1],r=e[0][0]-e[1][0],o=t[1][1]-t[0][1],i=t[0][0]-t[1][0],a=n*i-o*r;if(a!==0){let s=n*e[0][0]+r*e[0][1],d=o*t[0][0]+i*t[0][1];return u((s*i-d*r)/a,(n*d-o*s)/a)}return null}function A(e,t){return[e,t]}var Bt=(e,t,n=1e-4)=>{let r=vo(e,t);return r===0?!0:r{let[n,r]=e,[[o,i],[a,s]]=t,d=n-o,c=r-i,l=a-o,U=s-i,p=d*l+c*U,m=l*l+U*U,b=-1;m!==0&&(b=p/m);let E,g;b<0?(E=o,g=i):b>1?(E=a,g=s):(E=o+b*l,g=i+b*U);let h=n-E,x=r-g;return Math.sqrt(h*h+x*x)};function pn(e,t){let n=id(Rr(e[0],e[1]),Rr(t[0],t[1]));return!n||!Bt(n,t)||!Bt(n,e)?null:n}function Tr(e,t){return[e,t]}function ad(e,t){return[A(e[0],u(e[1][0],e[0][1])),A(u(e[1][0],e[0][1]),e[1]),A(e[1],u(e[0][0],e[1][1])),A(u(e[0][0],e[1][1]),e[0])].map(n=>pn(t,n)).filter(n=>!!n)}function Et(e,t,n,r){return[e,t,n,r]}function sd(e,t,n,r=1e-6){return[(e(t+r,n)-e(t-r,n))/(2*r),(e(t,n+r)-e(t,n-r))/(2*r)]}function w6(e,t,n,r=.001,o=10){let i=1/0,a=0;for(;i>=r;){if(a>=o)return null;let s=e(t,n),d=[sd((E,g)=>e(E,g)[0],t,n),sd((E,g)=>e(E,g)[1],t,n)],c=[[-s[0]],[-s[1]]],l=d[0][0]*d[1][1]-d[0][1]*d[1][0];if(l===0)return null;let U=[[d[1][1]/l,-d[0][1]/l],[-d[1][0]/l,d[0][0]/l]],p=[[U[0][0]*c[0][0]+U[0][1]*c[1][0]],[U[1][0]*c[0][0]+U[1][1]*c[1][0]]];t=t+p[0][0],n=n+p[1][0];let[m,b]=e(t,n);i=Math.max(Math.abs(m),Math.abs(b)),a+=1}return[t,n]}var Mr=(e,t)=>u((1-t)**3*e[0][0]+3*(1-t)**2*t*e[1][0]+3*(1-t)*t**2*e[2][0]+t**3*e[3][0],(1-t)**3*e[0][1]+3*(1-t)**2*t*e[1][1]+3*(1-t)*t**2*e[2][1]+t**3*e[3][1]);function Wi(e,t){let n=T6(e);if(ad(Tr(u(n[0],n[1]),u(n[2],n[3])),t).length===0)return[];let r=s=>u(t[0][0]+s*(t[1][0]-t[0][0]),t[0][1]+s*(t[1][1]-t[0][1])),o=[[.5,0],[.2,0],[.8,0]],i=([s,d])=>{let c=w6((p,m)=>{let b=Mr(e,p),E=r(m);return[b[0]-E[0],b[1]-E[1]]},s,d);if(!c)return null;let[l,U]=c;return l<0||l>1||U<0||U>1?null:Mr(e,l)},a=i(o[0]);return a?[a]:(a=i(o[1]),a?[a]:(a=i(o[2]),a?[a]:[]))}function R6(e,t,n=.001){let r=(c,l,U,p=n)=>{let m=c,b=l,E;for(;b-m>p;)E=(b+m)/2,U(E-p)ie(t,Mr(e,c)));return d?Mr(e,d):null}function zi(e,t){let n=R6(e,t);return n?ie(t,n):0}function T6(e){let[t,n,r,o]=e,i=[t[0],n[0],r[0],o[0]],a=[t[1],n[1],r[1],o[1]];return[Math.min(...i),Math.min(...a),Math.max(...i),Math.max(...a)]}function Qi(...e){return cd(e)}function Gn(e){return cd(e)}var ji=(e,t)=>{let n=e[0],r=e[1],o=!1;for(let i=0,a=t.length-1;ir&&l<=r||d<=r&&l>r)&&n<(c-s)*(r-d)/(l-d)+s&&(o=!o)}return o},dd=(e,t,n=1e-4)=>{let r=!1;for(let o=0,i=t.length-1;oe<=n?t>=n:e>=n?r>=e:!1,Dr=([e,t],[n,r])=>{let o=Math.max(e,n),i=Math.min(t,r);return o<=i?tt([o,i]):null},Sr=(e,[t,n])=>e>=t&&e<=n;function Ln([e,t,n],r){let o=(l,U,p)=>(l[0]-p[0])*(U[1]-p[1])-(U[0]-p[0])*(l[1]-p[1]),i=o(r,e,t),a=o(r,t,n),s=o(r,n,e),d=i<0||a<0||s<0,c=i>0||a>0||s>0;return!(d&&c)}var ld=null;var ng=()=>{if(ld)return ld;let e=new Date,t=e.getFullYear(),n=`${e.getMonth()+1}`.padStart(2,"0"),r=`${e.getDate()}`.padStart(2,"0"),o=`${e.getHours()}`.padStart(2,"0"),i=`${e.getMinutes()}`.padStart(2,"0");return`${t}-${n}-${r}-${o}${i}`},rg=e=>e.charAt(0).toUpperCase()+e.slice(1),og=e=>e instanceof HTMLElement&&e.className.includes("ToolIcon"),D6=e=>e instanceof HTMLElement&&e.dataset.type==="wysiwyg"||e instanceof HTMLBRElement||e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement||e instanceof HTMLSelectElement,ig=e=>D6(e)||e instanceof Element&&!!e.closest("label, button"),ag=e=>e instanceof HTMLElement&&e.dataset.type==="wysiwyg"||e instanceof HTMLBRElement||e instanceof HTMLTextAreaElement||e instanceof HTMLInputElement&&(e.type==="text"||e.type==="number"||e.type==="password"),ea=({fontFamily:e})=>{for(let[t,n]of Object.entries(Ie))if(n===e)return`${t}${bo(n).map(r=>`, ${r}`).join("")}`;return Tn},Ee=({fontSize:e,fontFamily:t})=>`${e}px ${ea({fontFamily:t})}`,$o=(e,t)=>{let n=0,r=null,o=(...i)=>{r=i,clearTimeout(n),n=window.setTimeout(()=>{r=null,e(...i)},t)};return o.flush=()=>{if(clearTimeout(n),r){let i=r;r=null,e(...i)}},o.cancel=()=>{r=null,clearTimeout(n)},o},pd=(e,t)=>{let n=null,r=null,o=null,i=s=>{n=window.requestAnimationFrame(()=>{n=null,e(...s),r=null,o&&(r=o,o=null,i(r))})},a=(...s)=>{if(M.MODE==="test"){e(...s);return}r=s,n===null?i(r):t?.trailing&&(o=s)};return a.flush=()=>{n!==null&&(cancelAnimationFrame(n),n=null),r&&(e(...o||r),r=o=null)},a.cancel=()=>{r=o=null,n!==null&&(cancelAnimationFrame(n),n=null)},a},ud=e=>1-Math.pow(1-e,4),Ud=(e,t,n)=>(t-e)*ud(n)+e,sg=({fromValues:e,toValues:t,onStep:n,duration:r=250,interpolateValue:o,onStart:i,onEnd:a,onCancel:s})=>{let d=!1,c=0,l;function U(p){if(d)return;l===void 0&&(l=p,i?.());let m=Math.min(p-l,r),b=ud(m/r),E={};if(Object.keys(e).forEach(g=>{let h=g,x=(t[h]-e[h])*b+e[h];E[h]=x}),n(E),m{let y=x,w=e[y],I=t[y],S;S=o?o(w,I,g,y):Ud(w,I,g),S==null&&(S=Ud(w,I,g)),h[y]=S}),n(h),c=window.requestAnimationFrame(U)}else n(t),a?.()}return c=window.requestAnimationFrame(U),()=>{s?.(),d=!0,window.cancelAnimationFrame(c)}},dg=(e,t)=>{if(!e.length||t<1)return[];let n=0,r=0,o=Array(Math.ceil(e.length/t));for(;nMath.abs(e-t),$r=(e,t)=>t.type==="custom"?{...e.activeTool,type:"custom",customType:t.customType,locked:t.locked??e.activeTool.locked}:{...e.activeTool,lastActiveTool:t.lastActiveToolBeforeEraser===void 0?e.activeTool.lastActiveTool:t.lastActiveToolBeforeEraser,type:t.type,customType:null,locked:t.locked??e.activeTool.locked};var cg=e=>(e=e.replace(/\bAlt\b/i,"Alt").replace(/\bShift\b/i,"Shift").replace(/\b(Enter|Return)\b/i,"Enter"),uo?e.replace(/\bCtrlOrCmd\b/gi,"Cmd").replace(/\bAlt\b/i,"Option"):e.replace(/\bCtrlOrCmd\b/gi,"Ctrl")),Dn=({clientX:e,clientY:t},{zoom:n,offsetLeft:r,offsetTop:o,scrollX:i,scrollY:a})=>{let s=(e-r)/n.value-i,d=(t-o)/n.value-a;return{x:s,y:d}},ta=({sceneX:e,sceneY:t},{zoom:n,offsetLeft:r,offsetTop:o,scrollX:i,scrollY:a})=>{let s=(e+i)*n.value+r,d=(t+a)*n.value+o;return{x:s,y:d}},lg=e=>getComputedStyle(document.documentElement).getPropertyValue(`--${e}`),S6="A-Za-z\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u0300-\u0590\u0800-\u1FFF\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF",v6="\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC",$6=new RegExp(`^[^${S6}]*[${v6}]`),Po=e=>$6.test(e),un=e=>{let[t,n]=e;return{x:t,y:n}},Ug=e=>{if(e?.name==="AbortError"){console.warn(e);return}throw e},fg=(e,t,n=0)=>{n<0&&(n=e.length+n),n=Math.min(e.length,Math.max(n,0));let r=n-1;for(;++r{n<0&&(n=e.length+n),n=Math.min(e.length-1,Math.max(n,0));let r=n+1;for(;--r>-1;)if(t(e[r],r,e))return r;return-1},Wt=e=>{let t=e.length===5&&e.substr(4,1)==="0",n=e.length===9&&e.substr(7,2)==="00";return t||n||e===Z.transparent},No=e=>e.fillStyle!=="solid"||Wt(e.backgroundColor),ug=()=>{let e,t,n=new Promise((r,o)=>{e=r,t=o});return n.resolve=e,n.reject=t,n};var mg=e=>{let t=e.parentElement;for(;t;){if(t===document.body)return document;let{overflowY:n}=window.getComputedStyle(t);if(t.scrollHeight>t.clientHeight&&(n==="auto"||n==="scroll"||n==="overlay"))return t;t=t.parentElement}return document},bg=e=>{let t=e.parentElement;for(;t;){if(t.tabIndex>-1){t.focus();return}t=t.parentElement}},Eg=e=>{e.preventDefault(),e.returnValue=""},md=e=>Array.from(e).map(t=>`0${t.toString(16)}`.slice(-2)).join(""),zt=()=>Ot()?1:Date.now(),te=e=>e instanceof Map?e:e.reduce((t,n)=>(t.set(typeof n=="string"?n:n.id,n),t),new Map),gg=e=>e.reduce((t,n,r)=>(t.set(n.id,[n,r]),t),new Map),_t=(e,t)=>e.reduce((n,r)=>(n[t?t(r):String(r)]=r,n),{}),xg=e=>e.reduce((t,n,r)=>{let o={...n,prev:null,next:null};if(r!==0){let i=t[r-1];if(o.prev=i,i.next=o,r===e.length-1){let a=t[0];o.next=a,a.prev=o}}return t.push(o),t},[]),Ot=()=>M.MODE==="test",hg=()=>M.MODE==="development",bd=()=>typeof process<"u"&&!0,yg=(e,t)=>new CustomEvent(e,{detail:{nativeEvent:t},cancelable:!0}),Ig=(e,t)=>{let n=!1;for(let r in t){let o=t[r];if(typeof o<"u"){if(e[r]===o&&(typeof o!="object"||o===null))continue;n=!0}}return n?{...e,...t}:e};var wg=()=>{try{return window.self===window.top?"top":"iframe"}catch{return"iframe"}};var Fo=e=>!!e&&typeof e=="object"&&"then"in e&&"catch"in e&&"finally"in e,Rg=e=>{let t=e?.querySelectorAll("button, a, input, select, textarea, div[tabindex], label[tabindex]");return t?Array.from(t).filter(n=>n.tabIndex>-1&&!n.disabled):[]},fd=(e,t)=>Array.isArray(e)&&Array.isArray(t)&&e.length===0&&t.length===0?!0:e===t,Qt=(e,t,n,r=!1)=>{let o=Object.keys(e),i=Object.keys(t);if(o.length!==i.length)return r&&console.warn("%cisShallowEqual: objects don't have same properties ->","color: #8B4000",e,t),!1;if(n&&Array.isArray(n)){for(let a of n)if(!(e[a]===t[a]||fd(e[a],t[a])))return r&&console.warn(`%cisShallowEqual: ${a} not equal ->`,"color: #8B4000",e[a],t[a]),!1;return!0}return o.every(a=>{let s=n?.[a],d=s?s(e[a],t[a]):e[a]===t[a]||fd(e[a],t[a]);return!d&&r&&console.warn(`%cisShallowEqual: ${a} not equal ->`,"color: #8B4000",e[a],t[a]),d})},Tg=(e,t,{checkForDefaultPrevented:n=!0}={})=>function(o){if(e?.(o),!n||!o?.defaultPrevented)return t?.(o)},Sn=(e,t,n)=>{if(!t)return e;if(n)return console.error(t),e;throw new Error(t)};function Ae(e,t){if(!e)throw new Error(t)}var Mg=e=>{let t,n,r=function(o){let i=Object.entries(o);if(t){let s=!0;for(let[d,c]of i)if(t.get(d)!==c){s=!1;break}if(s)return n}let a=e(o);return t=new Map(i),n=a,a};return r.clear=()=>{t=void 0,n=void 0},r},Ed=(e,t)=>e instanceof Set||e instanceof Map?e.has(t):"includes"in e?e.includes(t):e.hasOwnProperty(t),Lg=e=>JSON.parse(JSON.stringify(e)),Dg=(e,t)=>Qt(e,t)?e:t;function Sg(e,t,n,r){return e?(e?.addEventListener?.(t,n,r),()=>{e?.removeEventListener?.(t,n,r)}):()=>{}}function vg(e,t=!0){let n=e.length;if(n<4)return"";let r=e[0],o=e[1],i=e[2],a=`M${r[0].toFixed(2)},${r[1].toFixed(2)} Q${o[0].toFixed(2)},${o[1].toFixed(2)} ${xr(o[0],i[0]).toFixed(2)},${xr(o[1],i[1]).toFixed(2)} T`;for(let s=2,d=n-1;se.replace(/\r?\n|\r/g,` ++import{a as M}from"./chunk-ZUYEQ4TG.js";import{d as P}from"./chunk-SRAX5OIU.js";import l6 from"png-chunks-extract";import U6 from"png-chunk-text";import q9 from"png-chunks-encode";import{deflate as C9,inflate as V9}from"pako";import f6 from"open-color";var p6=(e,t)=>t.reduce((n,r)=>(r in e&&(n[r]=e[r]),n),{}),k9=5,Z9=5,Ps=4,fo=4,po=1,ft=[0,2,4,6,8];var pt=(e,t)=>t.map(n=>f6[e][n]),Z={transparent:"transparent",black:"#1e1e1e",white:"#ffffff",gray:pt("gray",ft),red:pt("red",ft),pink:pt("pink",ft),grape:pt("grape",ft),violet:pt("violet",ft),blue:pt("blue",ft),cyan:pt("cyan",ft),teal:pt("teal",ft),green:pt("green",ft),yellow:pt("yellow",ft),orange:pt("orange",ft),bronze:["#f8f1ee","#eaddd7","#d2bab0","#a18072","#846358"]},Ns=p6(Z,["cyan","blue","violet","grape","pink","green","teal","yellow","orange","red"]),W9=[Z.black,Z.red[fo],Z.green[fo],Z.blue[fo],Z.yellow[fo]],z9=[Z.transparent,Z.red[po],Z.green[po],Z.blue[po],Z.yellow[po]],Q9=[Z.white,"#f8f9fa","#f5faff","#fffce8","#fdf8f6"],j9={transparent:Z.transparent,white:Z.white,gray:Z.gray,black:Z.black,bronze:Z.bronze,...Ns},eE={transparent:Z.transparent,white:Z.white,gray:Z.gray,black:Z.black,bronze:Z.bronze,...Ns},Fs=e=>[Z.cyan[e],Z.blue[e],Z.violet[e],Z.grape[e],Z.pink[e],Z.green[e],Z.teal[e],Z.yellow[e],Z.orange[e],Z.red[e]],tE=(e,t,n)=>`#${((1<<24)+(e<<16)+(t<<8)+n).toString(16).slice(1)}`;var uo=/Mac|iPod|iPhone|iPad/.test(navigator.platform),iE=/^Win/.test(navigator.platform),Bs=/\b(android)\b/i.test(navigator.userAgent),aE="netscape"in window&&navigator.userAgent.indexOf("rv:")>1&&navigator.userAgent.indexOf("Gecko")>1,u6=navigator.userAgent.indexOf("Chrome")!==-1,sE=!u6&&navigator.userAgent.indexOf("Safari")!==-1,_s=/iPad|iPhone/.test(navigator.platform)||navigator.userAgent.includes("Mac")&&"ontouchend"in document,dE=()=>navigator.brave?.isBrave?.name==="isBrave",cE=typeof window<"u"&&"ResizeObserver"in window,lE="Excalidraw",Os=36,As=10,Ks=8,UE=5,fE=1,pE=30,Nt=Math.PI/12,uE="red",mo={TEXT:"text",CROSSHAIR:"crosshair",GRABBING:"grabbing",GRAB:"grab",POINTER:"pointer",MOVE:"move",AUTO:""},mE={MAIN:0,WHEEL:1,SECONDARY:2,TOUCH:-1,ERASER:5},bE={enabled:"all",disabled:"none",inheritFromUI:"var(--ui-pointerEvents)"};var EE={UNSTARTED:-1,ENDED:0,PLAYING:1,PAUSED:2,BUFFERING:3,CUED:5},je={TEST:"test",DEVELOPMENT:"development"},gE={SHAPE_ACTIONS_MENU:"App-menu__left",ZOOM_ACTIONS:"zoom-actions",SEARCH_MENU_INPUT_WRAPPER:"layer-ui__search-inputWrapper"},Mn="Xiaolai",Tn="Segoe UI Emoji",Ie={Virgil:1,Helvetica:2,Cascadia:3,Excalifont:5,Nunito:6,"Lilita One":7,"Comic Shanns":8,"Liberation Sans":9},Un={[Mn]:100,[Tn]:1e3},bo=e=>{switch(e){case Ie.Excalifont:return[Mn,Tn];default:return[Tn]}},ke={LIGHT:"light",DARK:"dark"},Pe={strokeColor:"#bbb",strokeWidth:2,strokeStyle:"solid",fillStyle:"solid",roughness:0,roundness:null,backgroundColor:"transparent",radius:8,nameOffsetY:3,nameColorLightTheme:"#999999",nameColorDarkTheme:"#7a7a7a",nameFontSize:14,nameLineHeight:1.25},Eo=1,ut=20,Ft=Ie.Excalifont,Vn="left",go="top";var br=2,Er=2*br,m6=1e-5,xE=2*Er-m6,hE="#ffffff",yE="#1e1e1e",IE="#a2f1a6";var xo=20,ho=5,yo={svg:"image/svg+xml",png:"image/png",jpg:"image/jpeg",gif:"image/gif",webp:"image/webp",bmp:"image/bmp",ico:"image/x-icon",avif:"image/avif",jfif:"image/jfif"},H={text:"text/plain",html:"text/html",json:"application/json",excalidraw:"application/vnd.excalidraw+json",excalidrawlib:"application/vnd.excalidrawlib+json","excalidraw.svg":"image/svg+xml","excalidraw.png":"image/png",binary:"application/octet-stream",...yo},Hs=[H.text,H.html,...Object.values(yo)],wE={png:"png",svg:"svg",clipboard:"clipboard"},Ze={excalidraw:"excalidraw",excalidrawClipboard:"excalidraw/clipboard",excalidrawLibrary:"excalidrawlib",excalidrawClipboardWithAPI:"excalidraw-api/clipboard"},Ci=window.EXCALIDRAW_EXPORT_SOURCE||window.location.origin,RE=500,TE=300,ME=500;var LE=100,DE=.1,Js=.1,Ys=30,SE=300;var Io="invert(93%) hue-rotate(180deg)",vE={addLibrary:"addLibrary"},$E={addLibrary:"addLibrary"},PE={canvasActions:{changeViewBackgroundColor:!0,clearCanvas:!0,export:{saveFileToDisk:!0},loadScene:!0,saveToActiveFile:!0,toggleTheme:null,saveAsImage:!0},tools:{image:!0}},NE=730,FE=1e3,BE=500,_E=1229,qn=2,Cs=[1,2,3],Vi=10,OE=1440,AE=4*1024*1024,re="http://www.w3.org/2000/svg";var qi={excalidraw:2,excalidrawLibrary:2},Se=5,Vs=.7,qs=11,kt={TOP:"top",MIDDLE:"middle",BOTTOM:"bottom"},Xi={LEFT:"left",CENTER:"center",RIGHT:"right"},Xs=20,wo=.25,Gs=32,ot={LEGACY:1,PROPORTIONAL_RADIUS:2,ADAPTIVE_RADIUS:3},Gi={architect:0,artist:1,cartoonist:2},KE={thin:.5,bold:.75,extraBold:1},we={strokeColor:Z.black,backgroundColor:Z.transparent,fillStyle:"solid",strokeWidth:.75,strokeStyle:"solid",roughness:Gi.artist,opacity:100,locked:!1},b6="library",HE="search",ks={name:"default",defaultTab:b6},JE=new Set(["iframe","embeddable","image"]),fn={selection:"selection",rectangle:"rectangle",diamond:"diamond",ellipse:"ellipse",arrow:"arrow",line:"line",freedraw:"freedraw",text:"text",image:"image",eraser:"eraser",hand:"hand",frame:"frame",magicframe:"magicframe",embeddable:"embeddable",laser:"laser"},YE={OAI_API_KEY:"excalidraw-oai-api-key",MERMAID_TO_EXCALIDRAW:"mermaid-to-excalidraw",PUBLISH_LIBRARY:"publish-library-data"},Zs="Untitled",ki={generalStats:1,elementProperties:2},CE=1,Ws={sharp:"sharp",round:"round",elbow:"elbow"},zs=.3,gr="element",Qs=Symbol.for("__test__originalId__"),E6=(r=>(r.ACTIVE="active",r.AWAY="away",r.IDLE="idle",r))(E6||{});import{nanoid as A9}from"nanoid";var g6=Cs.includes(devicePixelRatio)?devicePixelRatio:1,mt=()=>({showWelcomeScreen:!1,theme:ke.LIGHT,collaborators:new Map,currentChartType:"bar",currentItemBackgroundColor:we.backgroundColor,currentItemEndArrowhead:"arrow",currentItemFillStyle:we.fillStyle,currentItemFontFamily:Ft,currentItemFontSize:ut,currentItemOpacity:we.opacity,currentItemRoughness:we.roughness,currentItemStartArrowhead:null,currentItemStrokeColor:we.strokeColor,currentItemRoundness:"round",currentItemArrowType:Ws.round,currentItemStrokeStyle:we.strokeStyle,currentItemStrokeWidth:we.strokeWidth,currentItemTextAlign:Vn,currentHoveredFontFamily:null,cursorButton:"up",activeEmbeddable:null,newElement:null,editingTextElement:null,editingGroupId:null,editingLinearElement:null,activeTool:{type:"selection",customType:null,locked:we.locked,lastActiveTool:null},penMode:!1,penDetected:!1,errorMessage:null,exportBackground:!0,exportScale:g6,exportEmbedScene:!1,exportWithDarkMode:!1,fileHandle:null,gridSize:xo,gridStep:ho,gridModeEnabled:!1,isBindingEnabled:!0,defaultSidebarDockedPreference:!1,isLoading:!1,isResizing:!1,isRotating:!1,lastPointerDownWith:"mouse",multiElement:null,name:null,contextMenu:null,openMenu:null,openPopup:null,openSidebar:null,openDialog:null,pasteDialog:{shown:!1,data:null},previousSelectedElementIds:{},resizingElement:null,scrolledOutside:!1,scrollX:0,scrollY:0,selectedElementIds:{},hoveredElementIds:{},selectedGroupIds:{},selectedElementsAreBeingDragged:!1,selectionElement:null,shouldCacheIgnoreZoom:!1,stats:{open:!1,panels:ki.generalStats|ki.elementProperties},startBoundElement:null,suggestedBindings:[],frameRendering:{enabled:!0,clip:!0,name:!0,outline:!0},frameToHighlight:null,editingFrame:null,elementsToHighlight:null,toast:null,viewBackgroundColor:Z.white,zenModeEnabled:!1,zoom:{value:1},viewModeEnabled:!1,pendingImageElementId:null,showHyperlinkPopup:!1,selectedLinearElement:null,snapLines:[],originSnapOffset:{x:0,y:0},objectsSnapModeEnabled:!1,userToFollow:null,followedBy:new Set,isCropping:!1,croppingElementId:null,searchMatches:[]}),x6=(e=>e)({showWelcomeScreen:{browser:!0,export:!1,server:!1},theme:{browser:!0,export:!1,server:!1},collaborators:{browser:!1,export:!1,server:!1},currentChartType:{browser:!0,export:!1,server:!1},currentItemBackgroundColor:{browser:!0,export:!1,server:!1},currentItemEndArrowhead:{browser:!0,export:!1,server:!1},currentItemFillStyle:{browser:!0,export:!1,server:!1},currentItemFontFamily:{browser:!0,export:!1,server:!1},currentItemFontSize:{browser:!0,export:!1,server:!1},currentItemRoundness:{browser:!0,export:!1,server:!1},currentItemArrowType:{browser:!0,export:!1,server:!1},currentItemOpacity:{browser:!0,export:!1,server:!1},currentItemRoughness:{browser:!0,export:!1,server:!1},currentItemStartArrowhead:{browser:!0,export:!1,server:!1},currentItemStrokeColor:{browser:!0,export:!1,server:!1},currentItemStrokeStyle:{browser:!0,export:!1,server:!1},currentItemStrokeWidth:{browser:!0,export:!1,server:!1},currentItemTextAlign:{browser:!0,export:!1,server:!1},currentHoveredFontFamily:{browser:!1,export:!1,server:!1},cursorButton:{browser:!0,export:!1,server:!1},activeEmbeddable:{browser:!1,export:!1,server:!1},newElement:{browser:!1,export:!1,server:!1},editingTextElement:{browser:!1,export:!1,server:!1},editingGroupId:{browser:!0,export:!1,server:!1},editingLinearElement:{browser:!1,export:!1,server:!1},activeTool:{browser:!0,export:!1,server:!1},penMode:{browser:!0,export:!1,server:!1},penDetected:{browser:!0,export:!1,server:!1},errorMessage:{browser:!1,export:!1,server:!1},exportBackground:{browser:!0,export:!1,server:!1},exportEmbedScene:{browser:!0,export:!1,server:!1},exportScale:{browser:!0,export:!1,server:!1},exportWithDarkMode:{browser:!0,export:!1,server:!1},fileHandle:{browser:!1,export:!1,server:!1},gridSize:{browser:!0,export:!0,server:!0},gridStep:{browser:!0,export:!0,server:!0},gridModeEnabled:{browser:!0,export:!0,server:!0},height:{browser:!1,export:!1,server:!1},isBindingEnabled:{browser:!1,export:!1,server:!1},defaultSidebarDockedPreference:{browser:!0,export:!1,server:!1},isLoading:{browser:!1,export:!1,server:!1},isResizing:{browser:!1,export:!1,server:!1},isRotating:{browser:!1,export:!1,server:!1},lastPointerDownWith:{browser:!0,export:!1,server:!1},multiElement:{browser:!1,export:!1,server:!1},name:{browser:!0,export:!1,server:!1},offsetLeft:{browser:!1,export:!1,server:!1},offsetTop:{browser:!1,export:!1,server:!1},contextMenu:{browser:!1,export:!1,server:!1},openMenu:{browser:!0,export:!1,server:!1},openPopup:{browser:!1,export:!1,server:!1},openSidebar:{browser:!0,export:!1,server:!1},openDialog:{browser:!1,export:!1,server:!1},pasteDialog:{browser:!1,export:!1,server:!1},previousSelectedElementIds:{browser:!0,export:!1,server:!1},resizingElement:{browser:!1,export:!1,server:!1},scrolledOutside:{browser:!0,export:!1,server:!1},scrollX:{browser:!0,export:!1,server:!1},scrollY:{browser:!0,export:!1,server:!1},selectedElementIds:{browser:!0,export:!1,server:!1},hoveredElementIds:{browser:!1,export:!1,server:!1},selectedGroupIds:{browser:!0,export:!1,server:!1},selectedElementsAreBeingDragged:{browser:!1,export:!1,server:!1},selectionElement:{browser:!1,export:!1,server:!1},shouldCacheIgnoreZoom:{browser:!0,export:!1,server:!1},stats:{browser:!0,export:!1,server:!1},startBoundElement:{browser:!1,export:!1,server:!1},suggestedBindings:{browser:!1,export:!1,server:!1},frameRendering:{browser:!1,export:!1,server:!1},frameToHighlight:{browser:!1,export:!1,server:!1},editingFrame:{browser:!1,export:!1,server:!1},elementsToHighlight:{browser:!1,export:!1,server:!1},toast:{browser:!1,export:!1,server:!1},viewBackgroundColor:{browser:!0,export:!0,server:!0},width:{browser:!1,export:!1,server:!1},zenModeEnabled:{browser:!0,export:!1,server:!1},zoom:{browser:!0,export:!1,server:!1},viewModeEnabled:{browser:!1,export:!1,server:!1},pendingImageElementId:{browser:!1,export:!1,server:!1},showHyperlinkPopup:{browser:!1,export:!1,server:!1},selectedLinearElement:{browser:!0,export:!1,server:!1},snapLines:{browser:!1,export:!1,server:!1},originSnapOffset:{browser:!1,export:!1,server:!1},objectsSnapModeEnabled:{browser:!0,export:!1,server:!1},userToFollow:{browser:!1,export:!1,server:!1},followedBy:{browser:!1,export:!1,server:!1},isCropping:{browser:!1,export:!1,server:!1},croppingElementId:{browser:!1,export:!1,server:!1},searchMatches:{browser:!1,export:!1,server:!1}}),js=(e,t)=>{let n={};for(let r of Object.keys(e))if(x6[r]?.[t]){let i=e[r];n[r]=i}return n};var Ro=e=>js(e,"export"),ed=e=>js(e,"server"),td=({activeTool:e})=>e.type==="eraser",nd=({activeTool:e})=>e.type==="hand";import p9 from"lodash.throttle";import L6 from"es6-promise-pool";var se=(e,t,n)=>Math.min(Math.max(e,t),n),To=(e,t,n="round")=>{let r=Math.pow(10,t);return Math[n]((e+Number.EPSILON)*r)/r},WE=(e,t,n="round")=>{let r=1/t;return Math[n](e*r)/r},xr=(e,t)=>(e+t)/2,Mo=e=>typeof e=="number"&&Number.isFinite(e),Zi=(e,t,n=1e-4)=>Math.abs(e-t)e<0?e+2*Math.PI:e>=2*Math.PI?e-2*Math.PI:e;function yr(e){return e*Math.PI/180}function rd(e){return e*180/Math.PI}function od(e){return Math.abs(Math.sin(2*e))<1e-4}function Be(e,t,n=0,r=0){return[e-n,t-r]}function O(e,t=[0,0]){return Be(e[0]-t[0],e[1]-t[1])}function Me(e,t){return e[0]*t[1]-t[0]*e[1]}function h6(e,t){return e[0]*t[0]+e[1]*t[1]}function st(e,t){return[e[0]+t[0],e[1]+t[1]]}function Lo(e,t){return[e[0]-t[0],e[1]-t[1]]}function ue(e,t){return Be(e[0]*t,e[1]*t)}function y6(e){return e[0]*e[0]+e[1]*e[1]}function I6(e){return Math.sqrt(y6(e))}var Je=e=>{let t=I6(e);return t===0?Be(0,0):Be(e[0]/t,e[1]/t)};function u(e,t){return[e,t]}function Ir(e){return e.length===2?u(e[0],e[1]):void 0}function q(e,t=u(0,0)){return u(t[0]+e[0],t[1]+e[1])}function Do(e){return Array.isArray(e)&&e.length===2&&typeof e[0]=="number"&&!isNaN(e[0])&&typeof e[1]=="number"&&!isNaN(e[1])}function _e(e,t){let n=Math.abs;return n(e[0]-t[0])<1e-4&&n(e[1]-t[1])<1e-4}function T([e,t],[n,r],o){return u((e-n)*Math.cos(o)-(t-r)*Math.sin(o)+n,(e-n)*Math.sin(o)+(t-r)*Math.cos(o)+r)}function wr(e,t=[0,0]){return u(e[0]+t[0],e[1]+t[1])}function Zt(e,t){return u((e[0]+t[0])/2,(e[1]+t[1])/2)}function ie(e,t){return Math.hypot(t[0]-e[0],t[1]-e[1])}function Xn(e,t){let n=t[0]-e[0],r=t[1]-e[1];return n*n+r*r}var bt=(e,t,n)=>wr(t,ue(O(e,t),n)),So=(e,t,n)=>t[0]<=Math.max(e[0],n[0])&&t[0]>=Math.min(e[0],n[0])&&t[1]<=Math.max(e[1],n[1])&&t[1]>=Math.min(e[1],n[1]);function Rr(e,t){return[e,t]}function id(e,t){let n=e[1][1]-e[0][1],r=e[0][0]-e[1][0],o=t[1][1]-t[0][1],i=t[0][0]-t[1][0],a=n*i-o*r;if(a!==0){let s=n*e[0][0]+r*e[0][1],d=o*t[0][0]+i*t[0][1];return u((s*i-d*r)/a,(n*d-o*s)/a)}return null}function A(e,t){return[e,t]}var Bt=(e,t,n=1e-4)=>{let r=vo(e,t);return r===0?!0:r{let[n,r]=e,[[o,i],[a,s]]=t,d=n-o,c=r-i,l=a-o,U=s-i,p=d*l+c*U,m=l*l+U*U,b=-1;m!==0&&(b=p/m);let E,g;b<0?(E=o,g=i):b>1?(E=a,g=s):(E=o+b*l,g=i+b*U);let h=n-E,x=r-g;return Math.sqrt(h*h+x*x)};function pn(e,t){let n=id(Rr(e[0],e[1]),Rr(t[0],t[1]));return!n||!Bt(n,t)||!Bt(n,e)?null:n}function Tr(e,t){return[e,t]}function ad(e,t){return[A(e[0],u(e[1][0],e[0][1])),A(u(e[1][0],e[0][1]),e[1]),A(e[1],u(e[0][0],e[1][1])),A(u(e[0][0],e[1][1]),e[0])].map(n=>pn(t,n)).filter(n=>!!n)}function Et(e,t,n,r){return[e,t,n,r]}function sd(e,t,n,r=1e-6){return[(e(t+r,n)-e(t-r,n))/(2*r),(e(t,n+r)-e(t,n-r))/(2*r)]}function w6(e,t,n,r=.001,o=10){let i=1/0,a=0;for(;i>=r;){if(a>=o)return null;let s=e(t,n),d=[sd((E,g)=>e(E,g)[0],t,n),sd((E,g)=>e(E,g)[1],t,n)],c=[[-s[0]],[-s[1]]],l=d[0][0]*d[1][1]-d[0][1]*d[1][0];if(l===0)return null;let U=[[d[1][1]/l,-d[0][1]/l],[-d[1][0]/l,d[0][0]/l]],p=[[U[0][0]*c[0][0]+U[0][1]*c[1][0]],[U[1][0]*c[0][0]+U[1][1]*c[1][0]]];t=t+p[0][0],n=n+p[1][0];let[m,b]=e(t,n);i=Math.max(Math.abs(m),Math.abs(b)),a+=1}return[t,n]}var Mr=(e,t)=>u((1-t)**3*e[0][0]+3*(1-t)**2*t*e[1][0]+3*(1-t)*t**2*e[2][0]+t**3*e[3][0],(1-t)**3*e[0][1]+3*(1-t)**2*t*e[1][1]+3*(1-t)*t**2*e[2][1]+t**3*e[3][1]);function Wi(e,t){let n=T6(e);if(ad(Tr(u(n[0],n[1]),u(n[2],n[3])),t).length===0)return[];let r=s=>u(t[0][0]+s*(t[1][0]-t[0][0]),t[0][1]+s*(t[1][1]-t[0][1])),o=[[.5,0],[.2,0],[.8,0]],i=([s,d])=>{let c=w6((p,m)=>{let b=Mr(e,p),E=r(m);return[b[0]-E[0],b[1]-E[1]]},s,d);if(!c)return null;let[l,U]=c;return l<0||l>1||U<0||U>1?null:Mr(e,l)},a=i(o[0]);return a?[a]:(a=i(o[1]),a?[a]:(a=i(o[2]),a?[a]:[]))}function R6(e,t,n=.001){let r=(c,l,U,p=n)=>{let m=c,b=l,E;for(;b-m>p;)E=(b+m)/2,U(E-p)ie(t,Mr(e,c)));return d?Mr(e,d):null}function zi(e,t){let n=R6(e,t);return n?ie(t,n):0}function T6(e){let[t,n,r,o]=e,i=[t[0],n[0],r[0],o[0]],a=[t[1],n[1],r[1],o[1]];return[Math.min(...i),Math.min(...a),Math.max(...i),Math.max(...a)]}function Qi(...e){return cd(e)}function Gn(e){return cd(e)}var ji=(e,t)=>{let n=e[0],r=e[1],o=!1;for(let i=0,a=t.length-1;ir&&l<=r||d<=r&&l>r)&&n<(c-s)*(r-d)/(l-d)+s&&(o=!o)}return o},dd=(e,t,n=1e-4)=>{let r=!1;for(let o=0,i=t.length-1;oe<=n?t>=n:e>=n?r>=e:!1,Dr=([e,t],[n,r])=>{let o=Math.max(e,n),i=Math.min(t,r);return o<=i?tt([o,i]):null},Sr=(e,[t,n])=>e>=t&&e<=n;function Ln([e,t,n],r){let o=(l,U,p)=>(l[0]-p[0])*(U[1]-p[1])-(U[0]-p[0])*(l[1]-p[1]),i=o(r,e,t),a=o(r,t,n),s=o(r,n,e),d=i<0||a<0||s<0,c=i>0||a>0||s>0;return!(d&&c)}var ld=null;var ng=()=>{if(ld)return ld;let e=new Date,t=e.getFullYear(),n=`${e.getMonth()+1}`.padStart(2,"0"),r=`${e.getDate()}`.padStart(2,"0"),o=`${e.getHours()}`.padStart(2,"0"),i=`${e.getMinutes()}`.padStart(2,"0");return`${t}-${n}-${r}-${o}${i}`},rg=e=>e.charAt(0).toUpperCase()+e.slice(1),og=e=>e instanceof HTMLElement&&e.className.includes("ToolIcon"),D6=e=>e instanceof HTMLElement&&e.dataset.type==="wysiwyg"||e instanceof HTMLBRElement||e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement||e instanceof HTMLSelectElement,ig=e=>D6(e)||e instanceof Element&&!!e.closest("label, button"),ag=e=>e instanceof HTMLElement&&e.dataset.type==="wysiwyg"||e instanceof HTMLBRElement||e instanceof HTMLTextAreaElement||e instanceof HTMLInputElement&&(e.type==="text"||e.type==="number"||e.type==="password"),ea=({fontFamily:e})=>{for(let[t,n]of Object.entries(Ie))if(n===e)return`${t}${bo(n).map(r=>`, ${r}`).join("")}`;return Tn},Ee=({fontSize:e,fontFamily:t})=>`${e}px ${ea({fontFamily:t})}`,$o=(e,t)=>{let n=0,r=null,o=(...i)=>{r=i,clearTimeout(n),n=window.setTimeout(()=>{r=null,e(...i)},t)};return o.flush=()=>{if(clearTimeout(n),r){let i=r;r=null,e(...i)}},o.cancel=()=>{r=null,clearTimeout(n)},o},pd=(e,t)=>{let n=null,r=null,o=null,i=s=>{n=window.requestAnimationFrame(()=>{n=null,e(...s),r=null,o&&(r=o,o=null,i(r))})},a=(...s)=>{if(M.MODE==="test"){e(...s);return}r=s,n===null?i(r):t?.trailing&&(o=s)};return a.flush=()=>{n!==null&&(cancelAnimationFrame(n),n=null),r&&(e(...o||r),r=o=null)},a.cancel=()=>{r=o=null,n!==null&&(cancelAnimationFrame(n),n=null)},a},ud=e=>1-Math.pow(1-e,4),Ud=(e,t,n)=>(t-e)*ud(n)+e,sg=({fromValues:e,toValues:t,onStep:n,duration:r=250,interpolateValue:o,onStart:i,onEnd:a,onCancel:s})=>{let d=!1,c=0,l;function U(p){if(d)return;l===void 0&&(l=p,i?.());let m=Math.min(p-l,r),b=ud(m/r),E={};if(Object.keys(e).forEach(g=>{let h=g,x=(t[h]-e[h])*b+e[h];E[h]=x}),n(E),m{let y=x,w=e[y],I=t[y],S;S=o?o(w,I,g,y):Ud(w,I,g),S==null&&(S=Ud(w,I,g)),h[y]=S}),n(h),c=window.requestAnimationFrame(U)}else n(t),a?.()}return c=window.requestAnimationFrame(U),()=>{s?.(),d=!0,window.cancelAnimationFrame(c)}},dg=(e,t)=>{if(!e.length||t<1)return[];let n=0,r=0,o=Array(Math.ceil(e.length/t));for(;nMath.abs(e-t),$r=(e,t)=>t.type==="custom"?{...e.activeTool,type:"custom",customType:t.customType,locked:t.locked??e.activeTool.locked}:{...e.activeTool,lastActiveTool:t.lastActiveToolBeforeEraser===void 0?e.activeTool.lastActiveTool:t.lastActiveToolBeforeEraser,type:t.type,customType:null,locked:t.locked??e.activeTool.locked};var cg=e=>(e=e.replace(/\bAlt\b/i,"Alt").replace(/\bShift\b/i,"Shift").replace(/\b(Enter|Return)\b/i,"Enter"),uo?e.replace(/\bCtrlOrCmd\b/gi,"Cmd").replace(/\bAlt\b/i,"Option"):e.replace(/\bCtrlOrCmd\b/gi,"Ctrl")),Dn=({clientX:e,clientY:t},{zoom:n,offsetLeft:r,offsetTop:o,scrollX:i,scrollY:a})=>{let s=(e-r)/n.value-i,d=(t-o)/n.value-a;return{x:s,y:d}},ta=({sceneX:e,sceneY:t},{zoom:n,offsetLeft:r,offsetTop:o,scrollX:i,scrollY:a})=>{let s=(e+i)*n.value+r,d=(t+a)*n.value+o;return{x:s,y:d}},lg=e=>getComputedStyle(document.documentElement).getPropertyValue(`--${e}`),S6="A-Za-z\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u0300-\u0590\u0800-\u1FFF\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF",v6="\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC",$6=new RegExp(`^[^${S6}]*[${v6}]`),Po=e=>$6.test(e),un=e=>{let[t,n]=e;return{x:t,y:n}},Ug=e=>{if(e?.name==="AbortError"){console.warn(e);return}throw e},fg=(e,t,n=0)=>{n<0&&(n=e.length+n),n=Math.min(e.length,Math.max(n,0));let r=n-1;for(;++r{n<0&&(n=e.length+n),n=Math.min(e.length-1,Math.max(n,0));let r=n+1;for(;--r>-1;)if(t(e[r],r,e))return r;return-1},Wt=e=>{let t=e.length===5&&e.substr(4,1)==="0",n=e.length===9&&e.substr(7,2)==="00";return t||n||e===Z.transparent},No=e=>e.fillStyle!=="solid"||Wt(e.backgroundColor),ug=()=>{let e,t,n=new Promise((r,o)=>{e=r,t=o});return n.resolve=e,n.reject=t,n};var mg=e=>{let t=e.parentElement;for(;t;){if(t===document.body)return document;let{overflowY:n}=window.getComputedStyle(t);if(t.scrollHeight>t.clientHeight&&(n==="auto"||n==="scroll"||n==="overlay"))return t;t=t.parentElement}return document},bg=e=>{let t=e.parentElement;for(;t;){if(t.tabIndex>-1){t.focus();return}t=t.parentElement}},Eg=e=>{e.preventDefault(),e.returnValue=""},md=e=>Array.from(e).map(t=>`0${t.toString(16)}`.slice(-2)).join(""),zt=()=>Ot()?1:Date.now(),te=e=>e instanceof Map?e:e.reduce((t,n)=>(t.set(typeof n=="string"?n:n.id,n),t),new Map),gg=e=>e.reduce((t,n,r)=>(t.set(n.id,[n,r]),t),new Map),_t=(e,t)=>e.reduce((n,r)=>(n[t?t(r):String(r)]=r,n),{}),xg=e=>e.reduce((t,n,r)=>{let o={...n,prev:null,next:null};if(r!==0){let i=t[r-1];if(o.prev=i,i.next=o,r===e.length-1){let a=t[0];o.next=a,a.prev=o}}return t.push(o),t},[]),Ot=()=>M.MODE==="test",hg=()=>M.MODE==="development",bd=()=>typeof process<"u"&&!0,yg=(e,t)=>new CustomEvent(e,{detail:{nativeEvent:t},cancelable:!0}),Ig=(e,t)=>{let n=!1;for(let r in t){let o=t[r];if(typeof o<"u"){if(e[r]===o&&(typeof o!="object"||o===null))continue;n=!0}}return n?{...e,...t}:e};var wg=()=>{try{return window.self===window.top?"top":"iframe"}catch{return"iframe"}};var Fo=e=>!!e&&typeof e=="object"&&"then"in e&&"catch"in e&&"finally"in e,Rg=e=>{let t=e?.querySelectorAll("button, a, input, select, textarea, div[tabindex], label[tabindex]");return t?Array.from(t).filter(n=>n.tabIndex>-1&&!n.disabled):[]},fd=(e,t)=>Array.isArray(e)&&Array.isArray(t)&&e.length===0&&t.length===0?!0:e===t,Qt=(e,t,n,r=!1)=>{let o=Object.keys(e),i=Object.keys(t);if(o.length!==i.length)return r&&console.warn("%cisShallowEqual: objects don't have same properties ->","color: #8B4000",e,t),!1;if(n&&Array.isArray(n)){for(let a of n)if(!(e[a]===t[a]||fd(e[a],t[a])))return r&&console.warn(`%cisShallowEqual: ${a} not equal ->`,"color: #8B4000",e[a],t[a]),!1;return!0}return o.every(a=>{let s=n?.[a],d=s?s(e[a],t[a]):e[a]===t[a]||fd(e[a],t[a]);return!d&&r&&console.warn(`%cisShallowEqual: ${a} not equal ->`,"color: #8B4000",e[a],t[a]),d})},Tg=(e,t,{checkForDefaultPrevented:n=!0}={})=>function(o){if(e?.(o),!n||!o?.defaultPrevented)return t?.(o)},Sn=(e,t,n)=>{if(!t)return e;if(n)return console.error(t),e;throw new Error(t)};function Ae(e,t){if(!e)throw new Error(t)}var Mg=e=>{let t,n,r=function(o){let i=Object.entries(o);if(t){let s=!0;for(let[d,c]of i)if(t.get(d)!==c){s=!1;break}if(s)return n}let a=e(o);return t=new Map(i),n=a,a};return r.clear=()=>{t=void 0,n=void 0},r},Ed=(e,t)=>e instanceof Set||e instanceof Map?e.has(t):"includes"in e?e.includes(t):e.hasOwnProperty(t),Lg=e=>JSON.parse(JSON.stringify(e)),Dg=(e,t)=>Qt(e,t)?e:t;function Sg(e,t,n,r){return e?(e?.addEventListener?.(t,n,r),()=>{e?.removeEventListener?.(t,n,r)}):()=>{}}function vg(e,t=!0){let n=e.length;if(n<4)return"";let r=e[0],o=e[1],i=e[2],a=`M${r[0].toFixed(2)},${r[1].toFixed(2)} Q${o[0].toFixed(2)},${o[1].toFixed(2)} ${xr(o[0],i[0]).toFixed(2)},${xr(o[1],i[1]).toFixed(2)} T`;for(let s=2,d=n-1;se.replace(/\r?\n|\r/g,` + `),tt=e=>e,mn=async(e,...t)=>new Promise(n=>{n(e(...t))}),xd=(...e)=>Math.max(...e.map(t=>t?1:0))>0;var vr=class{constructor(t,n){P(this,"pool");P(this,"entries",{});this.pool=new L6(t,n)}all(){let t=n=>{if(n.data.result){let[r,o]=n.data.result;this.entries[r]=o}};return this.pool.addEventListener("fulfilled",t),this.pool.start().then(()=>(setTimeout(()=>{this.pool.removeEventListener("fulfilled",t)}),Object.values(this.entries)))}},kn=e=>e.replace(/"/g,"""),$g=e=>Array.isArray(e)?e:[e];var At=e=>!!e&&e.type==="image"&&!!e.fileId,Ye=e=>!!e&&e.type==="image",Bo=e=>!!e&&e.type==="embeddable",_o=e=>!!e&&e.type==="iframe",gt=e=>!!e&&(e.type==="iframe"||e.type==="embeddable"),k=e=>e!=null&&e.type==="text",hd=e=>e!=null&&e.type==="frame",yd=e=>e!=null&&e.type==="magicframe",de=e=>e!=null&&(e.type==="frame"||e.type==="magicframe"),Ce=e=>e!=null&&P6(e.type),P6=e=>e==="freedraw",ae=e=>e!=null&&na(e.type),ee=e=>e!=null&&e.type==="arrow",X=e=>ee(e)&&e.elbowed,na=e=>e==="arrow"||e==="line",Pr=(e,t=!0)=>e!=null&&(!e.locked||t===!0)&&N6(e.type),N6=e=>e==="arrow",xt=(e,t=!0)=>e!=null&&(!e.locked||t===!0)&&(e.type==="rectangle"||e.type==="diamond"||e.type==="ellipse"||e.type==="image"||e.type==="iframe"||e.type==="embeddable"||e.type==="frame"||e.type==="magicframe"||e.type==="text"&&!e.containerId),Id=e=>e!=null&&(e.type==="rectangle"||e.type==="diamond"||e.type==="image"||e.type==="iframe"||e.type==="embeddable"||e.type==="frame"||e.type==="magicframe"||e.type==="text"&&!e.containerId);var F6=(e,t=!0)=>e!=null&&(!e.locked||t===!0)&&(e.type==="rectangle"||e.type==="diamond"||e.type==="ellipse"||ee(e)),ra=e=>{let t=e?.type;if(!t)return!1;switch(t){case"text":case"diamond":case"rectangle":case"iframe":case"embeddable":case"ellipse":case"arrow":case"freedraw":case"line":case"frame":case"magicframe":case"image":case"selection":return!0;default:return Sn(t,null),!1}},Og=e=>e.type==="rectangle"||e.type==="ellipse"||e.type==="diamond",bn=e=>F6(e)&&!!e.boundElements?.some(({type:t})=>t==="text"),Ne=e=>e!==null&&"containerId"in e&&e.containerId!==null&&k(e),Oo=e=>e==="rectangle"||e==="embeddable"||e==="iframe"||e==="image",wd=e=>e==="line"||e==="arrow"||e==="diamond",Ag=(e,t)=>!!((e===ot.ADAPTIVE_RADIUS||e===ot.LEGACY)&&Oo(t.type)||e===ot.PROPORTIONAL_RADIUS&&wd(t.type)),Kg=e=>wd(e.type)?{type:ot.PROPORTIONAL_RADIUS}:Oo(e.type)?{type:ot.ADAPTIVE_RADIUS}:null,vn=e=>Object.hasOwn(e,"fixedPoint")&&e.fixedPoint!=null;var ht=(e,t,n)=>{let r=e.split(` + `).map(s=>s||" ").join(` + `),o=parseFloat(t),i=B6(r,o,n);return{width:Dd(r,t),height:i}},Rd="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toLocaleUpperCase(),Td=(e,t)=>{let n=_6(e);return n===0?ht(Rd.split("").join(` +diff --git a/dist/types/excalidraw/constants.d.ts b/dist/types/excalidraw/constants.d.ts +index 8ef7e5a74d950ff37becb3a305beebe3deb69115..8e2be1f00b12686f611150d82587fbf7c2f1a7fd 100644 +--- a/dist/types/excalidraw/constants.d.ts ++++ b/dist/types/excalidraw/constants.d.ts +@@ -256,9 +256,9 @@ export declare const ROUGHNESS: { + readonly cartoonist: 2; + }; + export declare const STROKE_WIDTH: { +- readonly thin: 1; +- readonly bold: 2; +- readonly extraBold: 4; ++ readonly thin: 0.5; ++ readonly bold: 0.75; ++ readonly extraBold: 1; + }; + export declare const DEFAULT_ELEMENT_PROPS: { + strokeColor: ExcalidrawElement["strokeColor"]; diff --git a/src/client/App.tsx b/src/client/App.tsx index fc4dac4..afd929f 100644 --- a/src/client/App.tsx +++ b/src/client/App.tsx @@ -1,10 +1,27 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { DrawingSidebar, DrawingsToggle } from "./components/DrawingSidebar"; import { EditorCanvas } from "./components/EditorCanvas"; +import { PublicViewer } from "./components/PublicViewer"; +import { usePublicDrawing } from "./hooks/usePublicDrawing"; import { useDrawingSession } from "./hooks/useDrawingSession"; import { useThemeTokenSync } from "./hooks/useThemeTokenSync"; -export function App() { +type AppRoute = { type: "private" } | { type: "public"; slug: string }; + +function routeFromPath(pathname = window.location.pathname): AppRoute { + if (!pathname.startsWith("/p/")) { + return { type: "private" }; + } + + const slug = pathname.slice(3); + if (slug.length === 0 || slug.includes("/")) { + return { type: "private" }; + } + + return { type: "public", slug: decodeURIComponent(slug) }; +} + +function PrivateApp() { const [isSidebarOpen, setIsSidebarOpen] = useState(false); const appShellRef = useRef(null); const scheduleThemeTokenSync = useThemeTokenSync(appShellRef); @@ -17,12 +34,18 @@ export function App() { error, toastMessage, editorReloadNonce, + publication, + publicationSlug, + publicationBusy, setActiveTitle, submitTitle, handleSceneChange, selectDrawing, createDrawing, deleteDrawing, + setPublicationSlug, + publishPublication, + disablePublication, } = useDrawingSession(); useEffect(() => { @@ -98,13 +121,35 @@ export function App() { drawings={drawings} activeId={activeId} activeTitle={activeTitle} + publication={publication} + publicationSlug={publicationSlug} + publicationBusy={publicationBusy} onClose={closeSidebar} onCreate={handleCreateDrawing} onSelect={handleSelectDrawing} onDelete={(drawingId) => void deleteDrawing(drawingId)} onTitleChange={setActiveTitle} onTitleSubmit={() => void submitTitle()} + onPublicationSlugChange={setPublicationSlug} + onPublish={() => void publishPublication()} + onDisablePublication={() => void disablePublication()} /> ); } + +function PublicApp({ slug }: { slug: string }) { + const { drawing, loading, error } = usePublicDrawing(slug); + + return ; +} + +export function App() { + const route = routeFromPath(); + + if (route.type === "public") { + return ; + } + + return ; +} diff --git a/src/client/components/DrawingSidebar.tsx b/src/client/components/DrawingSidebar.tsx index bfab44f..824b1eb 100644 --- a/src/client/components/DrawingSidebar.tsx +++ b/src/client/components/DrawingSidebar.tsx @@ -1,24 +1,51 @@ -import { type DrawingMeta } from "../../core/shared"; +import { type DrawingMeta, type DrawingPublication } from "../../core/shared"; type DrawingSidebarProps = { open: boolean; drawings: DrawingMeta[]; activeId: string | null; activeTitle: string; + publication: DrawingPublication; + publicationSlug: string; + publicationBusy: boolean; onClose: () => void; onCreate: () => void; onSelect: (drawingId: string) => void; onDelete: (drawingId: string) => void; onTitleChange: (title: string) => void; onTitleSubmit: () => void; + onPublicationSlugChange: (slug: string) => void; + onPublish: () => void; + onDisablePublication: () => void; }; function DrawerIcon() { return ( ); } @@ -42,28 +69,49 @@ export function DrawingSidebar({ drawings, activeId, activeTitle, + publication, + publicationSlug, + publicationBusy, onClose, onCreate, onSelect, onDelete, onTitleChange, onTitleSubmit, + onPublicationSlugChange, + onPublish, + onDisablePublication, }: DrawingSidebarProps) { + const publicPath = publication.enabled ? `/p/${publication.slug}` : null; + const publicationStatus = publication.enabled + ? `Published at ${publicPath}` + : "Not published"; + return ( <>
-