diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 748410a..231c34b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -146,7 +146,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -677,7 +676,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" }, @@ -726,11 +724,31 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" } }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", @@ -1317,7 +1335,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -2306,8 +2323,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@tailwindcss/node": { "version": "4.2.2", @@ -2785,7 +2801,6 @@ "resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.168.22.tgz", "integrity": "sha512-W2LyfkfJtDCf//jOjZeUBWwOVl8iDRVTECpGHa2M28MT3T5/VVnjgicYNHR/ax0Filk1iU67MRjcjHheTYvK1Q==", "license": "MIT", - "peer": true, "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-store": "^0.9.3", @@ -2996,7 +3011,6 @@ "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -3158,7 +3172,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz", "integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -3191,7 +3204,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -3202,7 +3214,6 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -3696,7 +3707,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -4402,7 +4412,6 @@ "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", "hasInstallScript": true, "license": "MIT", - "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -5031,7 +5040,6 @@ "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.14.tgz", "integrity": "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==", "license": "MIT", - "peer": true, "engines": { "node": ">=16.9.0" } @@ -7223,8 +7231,7 @@ "url": "https://github.com/sponsors/sxzz" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/query-selector-shadow-dom": { "version": "1.0.1", @@ -7287,7 +7294,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -7297,7 +7303,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -7751,7 +7756,6 @@ "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.5.2.tgz", "integrity": "sha512-xcRN39BdsnO9Tf+VzsE7b3JyTJASItIV1FVFewJKCFcW4s4haIKS3e6vj8PGB9qBwC7tnuOywQMdv5N4qkzi7Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" } @@ -8085,8 +8089,7 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/tapable": { "version": "2.3.2", @@ -8379,7 +8382,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8631,7 +8633,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz", "integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==", "license": "MIT", - "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", @@ -8908,7 +8909,6 @@ "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", diff --git a/frontend/src/components/editor-clear-canvas.tsx b/frontend/src/components/editor-clear-canvas.tsx new file mode 100644 index 0000000..6d4028c --- /dev/null +++ b/frontend/src/components/editor-clear-canvas.tsx @@ -0,0 +1,36 @@ +import { RefreshIcon } from "@hugeicons/core-free-icons" +import { HugeiconsIcon } from "@hugeicons/react" + +// similar class as the adjacent export button next to it +const triggerClass = [ + "inline-flex h-9 shrink-0 items-center justify-center gap-1.5 rounded-full border border-black/[0.08] px-4 text-sm font-medium sm:h-10 sm:px-5", + "bg-gradient-to-br from-[#fafaf9] via-[#f2f0f3] to-[#ebe7f3]", + "text-[var(--text)] shadow-[0_1px_2px_rgba(0,0,0,0.04)]", + "outline-none transition-[background,box-shadow,filter] duration-200", + "hover:from-[#f5f4f2] hover:via-[#eceaf1] hover:to-[#e5e0f2] hover:shadow-[0_2px_8px_rgba(0,0,0,0.06)]", + "focus-visible:ring-2 focus-visible:ring-[var(--accent)]/50 focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--surface)]", + "disabled:pointer-events-none disabled:opacity-40", +].join(" "); + +type Props = { + onClearCanvas?: () => void; +}; + +export default function EditorClearCanvas({ onClearCanvas }: Props) { + return ( + + ) +} \ No newline at end of file diff --git a/frontend/src/components/scene-editor.tsx b/frontend/src/components/scene-editor.tsx index bbed8b8..0e7fe4f 100644 --- a/frontend/src/components/scene-editor.tsx +++ b/frontend/src/components/scene-editor.tsx @@ -146,6 +146,7 @@ export type SceneEditorHandle = { exportImage: (opts?: ExportImageOptions) => void saveDocument: () => void loadDocument: (file: File) => Promise + clearCanvas: () => void } type SceneEditorProps = { @@ -1618,6 +1619,17 @@ const SceneEditor = forwardRef(function Sce return } + const clearCanvas = useCallback(() => { + setDoc((prev) => ({ ...prev, objects: [] })) + setSelectedIds([]) + setTextEditingId(null) + }, [setDoc, setSelectedIds]) + + useImperativeHandle( + ref, + () => ({ exportImage, saveDocument, loadDocument, clearCanvas }), + [exportImage, saveDocument, loadDocument, clearCanvas], + ) const pageDoc = exportPages[0] ? pageExportDocument(doc, exportPages[0]) : doc const url = await renderAvnacDocumentToDataUrl(pageDoc, vectorBoardDocs, { format, diff --git a/frontend/src/routes/create.tsx b/frontend/src/routes/create.tsx index b39a5ba..3e0fce9 100644 --- a/frontend/src/routes/create.tsx +++ b/frontend/src/routes/create.tsx @@ -11,6 +11,8 @@ import { idbGetEditorRecord, idbMigrateLegacyDocument, idbSetDocumentName, +} from "../lib/avnac-editor-idb"; +import EditorClearCanvas from "#/components/editor-clear-canvas"; } from '../lib/avnac-editor-idb' type CreateSearch = { @@ -175,6 +177,7 @@ function CreatePage() { />
+ editorRef.current?.clearCanvas()} /> editorRef.current?.exportImage(opts)}