From 211a4ea99cbd6d67d1e8d61d42c96bab686c0c48 Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Tue, 18 Aug 2026 18:52:19 +0300 Subject: [PATCH] fix(i18n): localize app notifications (#557) - Add app-owned notification translations for every supported locale.\n- Localize common file, clipboard, collaboration, chat, vectorization, storage, recovery, and library toasts.\n- Preserve actionable runtime details inside localized messages. --- CHANGELOG.md | 1 + bun.lock | 227 +++++++++++++++++- package.json | 5 +- src/app/document/io/dom.ts | 11 +- src/app/document/io/read.ts | 8 +- src/app/editor/canvas/menu/actions.ts | 18 +- src/app/editor/clipboard/paste-to-replace.ts | 5 +- src/app/editor/vectorize/action.ts | 28 ++- src/app/i18n/notifications/index.ts | 57 +++++ src/app/i18n/notifications/locales/de.json | 25 ++ src/app/i18n/notifications/locales/es.json | 25 ++ src/app/i18n/notifications/locales/fr.json | 25 ++ src/app/i18n/notifications/locales/it.json | 25 ++ src/app/i18n/notifications/locales/ja.json | 25 ++ src/app/i18n/notifications/locales/pl.json | 25 ++ src/app/i18n/notifications/locales/ru.json | 25 ++ src/app/i18n/notifications/locales/zh-cn.json | 25 ++ src/app/shell/menu/files.ts | 3 +- src/components/ChatPanel.vue | 9 +- src/components/CollabPanel/context.ts | 6 +- src/components/MobileHud/context.ts | 4 +- .../review/LibraryUpdateReviewDialog.vue | 12 +- src/components/libraries/useLibraryManager.ts | 9 +- .../instance-update/use.ts | 7 +- src/components/recovery/RecoveryDialog.vue | 8 +- .../settings/storage/StorageSettingsPanel.vue | 13 +- .../engine/app/shell/menu/file-batch.test.ts | 6 +- 27 files changed, 592 insertions(+), 45 deletions(-) create mode 100644 src/app/i18n/notifications/index.ts create mode 100644 src/app/i18n/notifications/locales/de.json create mode 100644 src/app/i18n/notifications/locales/es.json create mode 100644 src/app/i18n/notifications/locales/fr.json create mode 100644 src/app/i18n/notifications/locales/it.json create mode 100644 src/app/i18n/notifications/locales/ja.json create mode 100644 src/app/i18n/notifications/locales/pl.json create mode 100644 src/app/i18n/notifications/locales/ru.json create mode 100644 src/app/i18n/notifications/locales/zh-cn.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 9025784a3..abfe9fee8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ ### Changed +- Localize file, clipboard, collaboration, chat, vectorization, storage, recovery, and component-library notifications in every supported language. - Move MCP connections into their own Settings destination instead of presenting them as part of model configuration. - Pan horizontally with Shift+wheel while preserving native horizontal trackpad movement. diff --git a/bun.lock b/bun.lock index 2fb7e0dfd..501e0f4de 100644 --- a/bun.lock +++ b/bun.lock @@ -23,6 +23,8 @@ "@codemirror/search": "^6.7.1", "@codemirror/state": "^6.7.1", "@codemirror/view": "^6.43.9", + "@nanostores/i18n": "^1.3.3", + "@nanostores/vue": "^1.1.0", "@open-pencil/cli": "workspace:*", "@open-pencil/core": "workspace:*", "@open-pencil/dom-css": "workspace:*", @@ -58,6 +60,7 @@ "jspdf": "^4.2.1", "lib0": "^0.2.117", "motion-v": "^2.4.0", + "nanostores": "^1.5.0", "opentype.js": "^2.0.0", "reka-ui": "^2.10.3", "svg2pdf.js": "^2.7.0", @@ -69,7 +72,7 @@ "valibot": "^1.4.2", "vue": "^3.5.41", "vue-router": "^5.2.0", - "vue-stream-markdown": "^0.6.5", + "vue-stream-markdown": "1.0.4", "y-indexeddb": "^9.0.12", "y-protocols": "^1.0.7", "yjs": "^13.6.32", @@ -381,6 +384,8 @@ "@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=="], + "@antfu/utils": ["@antfu/utils@9.3.0", "", {}, "sha512-9hFT4RauhcUzqOE4f1+frMKLZrgNog5b06I7VmZQV1BkvwvqrbC8EBZf3L1eEL2AKb6rNKjER0sEvJiSP1FXEA=="], + "@apideck/better-ajv-errors": ["@apideck/better-ajv-errors@0.3.7", "", { "dependencies": { "jsonpointer": "^5.0.1", "leven": "^3.1.0" }, "peerDependencies": { "ajv": ">=8" } }, "sha512-TajUJwGWbDwkCx/CZi7tRE8PVB7simCvKJfHUsSdvps+aTM/PDPP4gkLmKnc+x3CE//y9i/nj74GqdL/hwk7Iw=="], "@arethetypeswrong/cli": ["@arethetypeswrong/cli@0.18.4", "", { "dependencies": { "@arethetypeswrong/core": "0.18.4", "chalk": "^4.1.2", "cli-table3": "^0.6.3", "commander": "^10.0.1", "marked": "^9.1.2", "marked-terminal": "^7.1.0", "semver": "^7.5.4" }, "bin": { "attw": "./dist/index.js" } }, "sha512-kNWo6LTzGAuLYPpJ7Sgo63whSUeeSuKMlYx6IBgzs4ONEG807gW4hSSENvpeCHzO2H2wIzG5EFl0OKBbqGBAyA=="], @@ -575,6 +580,8 @@ "@braidai/lang": ["@braidai/lang@1.1.2", "", {}, "sha512-qBcknbBufNHlui137Hft8xauQMTZDKdophmLFv05r2eNmdIv/MlPuP4TdUknHG68UdWLgVZwgxVe735HzJNIwA=="], + "@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.2", "", {}, "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA=="], + "@chainsafe/as-chacha20poly1305": ["@chainsafe/as-chacha20poly1305@0.1.0", "", {}, "sha512-BpNcL8/lji/GM3+vZ/bgRWqJ1q5kwvTFmGPk7pxm/QQZDbaMI98waOHjEymTjq2JmdD/INdNBFOVSyJofXg7ew=="], "@chainsafe/as-sha256": ["@chainsafe/as-sha256@1.2.4", "", {}, "sha512-3GXDysZOKD6cTYbm48lEdXdUbS7cafjXQZfgHOspTByhoGR/JM3KBXyF3vE6bf63ImjNPyoEZwnQcpYPQ6k3bQ=="], @@ -587,6 +594,8 @@ "@chenglou/pretext": ["@chenglou/pretext@0.0.7", "", {}, "sha512-FV5hj3fGqpBlzbANUbR+s+6XuNRrghVVyuNs33zdH2SzU7MvK+7GlW6xREjDCreixKbexEn7OEkDgAFeWuu5Hg=="], + "@chevrotain/types": ["@chevrotain/types@11.1.2", "", {}, "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw=="], + "@clack/core": ["@clack/core@0.4.1", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-Pxhij4UXg8KSr7rPek6Zowm+5M22rbd2g1nfojHJkxp5YkFqiZ2+YLEM/XGVIzvGOcM0nqjIFxrpDwWRZYWYjA=="], "@clack/prompts": ["@clack/prompts@0.9.1", "", { "dependencies": { "@clack/core": "0.4.1", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-JIpyaboYZeWYlyP0H+OoPPxd6nqueG/CmN6ixBiNFsIDHREevjIf0n0Ohh5gr5C8pEDknzgvz+pIJ8dMhzWIeg=="], @@ -925,8 +934,14 @@ "@marijn/find-cluster-break": ["@marijn/find-cluster-break@1.0.3", "", {}, "sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA=="], + "@markmend/ast": ["@markmend/ast@1.0.4", "", { "dependencies": { "@markmend/core": "1.0.4", "@types/mdast": "^4.0.4", "mdast-util-from-markdown": "^2.0.3", "mdast-util-frontmatter": "^2.0.1", "mdast-util-gfm": "^3.1.0", "mdast-util-math": "^3.0.0", "mdast-util-to-markdown": "^2.1.2", "micromark-extension-cjk-friendly": "^2.0.1", "micromark-extension-cjk-friendly-gfm-strikethrough": "^2.0.1", "micromark-extension-frontmatter": "^2.0.0", "micromark-extension-gfm": "^3.0.0", "micromark-extension-math": "^3.1.0", "micromark-util-types": "^2.0.2" } }, "sha512-t62H5gsFKhK10O9kss3UyKB77ARkoCsksXPqIreDM8mwY7wdvaHZrYTqwkmWEyf9JqCRGX9sIs4hHlnUCxBdIA=="], + + "@markmend/core": ["@markmend/core@1.0.4", "", { "dependencies": { "marked": "^18.0.7" } }, "sha512-x6ik8HhJLUmNjuNhfWT+jNahRnqY5zn2a8bwkqBH4NbvCWzG0S7Z2uWyZAa76IAEkgwXJjPC1uz3ZKbQDQ+/cg=="], + "@mdx-js/react": ["@mdx-js/react@3.1.1", "", { "dependencies": { "@types/mdx": "^2.0.0" }, "peerDependencies": { "@types/react": ">=16", "react": ">=16" } }, "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw=="], + "@mermaid-js/parser": ["@mermaid-js/parser@1.2.0", "", { "dependencies": { "@chevrotain/types": "~11.1.2" } }, "sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA=="], + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.30.0", "", { "dependencies": { "@hono/node-server": "^1.19.9 || ^2.0.5", "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-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA=="], "@multiformats/dns": ["@multiformats/dns@1.0.15", "", { "dependencies": { "@dnsquery/dns-packet": "^6.1.1", "@libp2p/interface": "^3.1.0", "hashlru": "^2.3.0", "p-queue": "^9.0.0", "progress-events": "^1.0.0", "uint8arrays": "^6.1.1" } }, "sha512-W0zAMABtAn+3chgFcPGvllKND7M6GblMAAFcJQTy+iMmGiyFErZYPzAh2b50Y3SFf340iFV7+ckVgZkGfUyxzA=="], @@ -1335,6 +1350,14 @@ "@storybook/vue3-vite": ["@storybook/vue3-vite@10.5.8", "", { "dependencies": { "@storybook/builder-vite": "10.5.8", "@storybook/vue3": "10.5.8", "magic-string": "^0.30.0", "typescript": "^5.9.3", "vue-component-meta": "^3.2.7", "vue-docgen-api": "^4.75.1" }, "peerDependencies": { "storybook": "^10.5.8", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-TdChq6KWgrT/ohK9idiV9K5JTQKY7IKBeFvtprdCnsjwrdk26VQwT7LZRrj7V8DsBKKrrbACUOdvJkYaOZOWrA=="], + "@stream-markdown/code": ["@stream-markdown/code@1.0.4", "", { "dependencies": { "@stream-markdown/core": "1.0.4", "shiki": "^4.0.2" } }, "sha512-hxyNhHbPayz/FWjcH8HVqWmzOEUALEByGj7rpWY3mOqHcdKpETGBAs1FH7RJ/SFMLwO8wr/Ps8dxxGfzPjTtoA=="], + + "@stream-markdown/core": ["@stream-markdown/core@1.0.4", "", { "dependencies": { "@markmend/ast": "1.0.4" } }, "sha512-DIFcUZbDme1h4v5rjxcbFLf9tyxwKcByqlnUqC1kdp8gn0hCdQjvlJRDsuFhEIwiBur7E4HhweljJzsGz4b7HQ=="], + + "@stream-markdown/math": ["@stream-markdown/math@1.0.4", "", { "dependencies": { "@stream-markdown/core": "1.0.4", "katex": "^0.16.38" } }, "sha512-ueEWSBxBW95BZZ0Ef4EVEmUfBwjWnCN8GWypXBURci+4VqYWH3oBW9kDx8XcjCHNfyx2OjtQBi0OPpGfrW73Sg=="], + + "@stream-markdown/mermaid": ["@stream-markdown/mermaid@1.0.4", "", { "dependencies": { "@antfu/utils": "^9.3.0", "@stream-markdown/core": "1.0.4", "beautiful-mermaid": "^1.1.3", "mermaid": "^11.13.0" } }, "sha512-c0lPcZDiQk8/pE4oRib79ieSXm7uFMb1XbEgfD38+Jt//PzeIP6AxTYM4lrMb0iQrBnYgMS0IXuOAkJiSnSgkQ=="], + "@supabase/auth-js": ["@supabase/auth-js@2.112.3", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-NA0rsgAlWZPvbhw8aUdmgfpHVgUAcd8zK5ov43l++o1bLIPXZhRiAlRobhwF5AatQuovpqxsMH50F4oyyV4XZw=="], "@supabase/functions-js": ["@supabase/functions-js@2.112.3", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-gfv481mTOVWtZIJgXupxZpni2V2UWPf6jeF/jOK7HdMHdH+mt6sU0sHHwf0POsPip8ltlulu9OUHgwVzl5ddRw=="], @@ -1449,6 +1472,68 @@ "@types/culori": ["@types/culori@4.0.1", "", {}, "sha512-43M51r/22CjhbOXyGT361GZ9vncSVQ39u62x5eJdBQFviI8zWp2X5jzqg7k4M6PVgDQAClpy2bUe2dtwEgEDVQ=="], + "@types/d3": ["@types/d3@7.4.3", "", { "dependencies": { "@types/d3-array": "*", "@types/d3-axis": "*", "@types/d3-brush": "*", "@types/d3-chord": "*", "@types/d3-color": "*", "@types/d3-contour": "*", "@types/d3-delaunay": "*", "@types/d3-dispatch": "*", "@types/d3-drag": "*", "@types/d3-dsv": "*", "@types/d3-ease": "*", "@types/d3-fetch": "*", "@types/d3-force": "*", "@types/d3-format": "*", "@types/d3-geo": "*", "@types/d3-hierarchy": "*", "@types/d3-interpolate": "*", "@types/d3-path": "*", "@types/d3-polygon": "*", "@types/d3-quadtree": "*", "@types/d3-random": "*", "@types/d3-scale": "*", "@types/d3-scale-chromatic": "*", "@types/d3-selection": "*", "@types/d3-shape": "*", "@types/d3-time": "*", "@types/d3-time-format": "*", "@types/d3-timer": "*", "@types/d3-transition": "*", "@types/d3-zoom": "*" } }, "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww=="], + + "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="], + + "@types/d3-axis": ["@types/d3-axis@3.0.6", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw=="], + + "@types/d3-brush": ["@types/d3-brush@3.0.6", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A=="], + + "@types/d3-chord": ["@types/d3-chord@3.0.6", "", {}, "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg=="], + + "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="], + + "@types/d3-contour": ["@types/d3-contour@3.0.6", "", { "dependencies": { "@types/d3-array": "*", "@types/geojson": "*" } }, "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg=="], + + "@types/d3-delaunay": ["@types/d3-delaunay@6.0.4", "", {}, "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw=="], + + "@types/d3-dispatch": ["@types/d3-dispatch@3.0.7", "", {}, "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA=="], + + "@types/d3-drag": ["@types/d3-drag@3.0.7", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ=="], + + "@types/d3-dsv": ["@types/d3-dsv@3.0.7", "", {}, "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g=="], + + "@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="], + + "@types/d3-fetch": ["@types/d3-fetch@3.0.7", "", { "dependencies": { "@types/d3-dsv": "*" } }, "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA=="], + + "@types/d3-force": ["@types/d3-force@3.0.10", "", {}, "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw=="], + + "@types/d3-format": ["@types/d3-format@3.0.4", "", {}, "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g=="], + + "@types/d3-geo": ["@types/d3-geo@3.1.1", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-65Emv9fQiQQqphLlRkuQ5ypPsOmWPhtBGCMv61JDPEPMvsx+gzhGf74yw1a78xFKPj6zw4AgQICJoQv0vK9M2w=="], + + "@types/d3-hierarchy": ["@types/d3-hierarchy@3.1.7", "", {}, "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg=="], + + "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="], + + "@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="], + + "@types/d3-polygon": ["@types/d3-polygon@3.0.2", "", {}, "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA=="], + + "@types/d3-quadtree": ["@types/d3-quadtree@3.0.6", "", {}, "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg=="], + + "@types/d3-random": ["@types/d3-random@3.0.4", "", {}, "sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA=="], + + "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="], + + "@types/d3-scale-chromatic": ["@types/d3-scale-chromatic@3.1.0", "", {}, "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ=="], + + "@types/d3-selection": ["@types/d3-selection@3.0.11", "", {}, "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w=="], + + "@types/d3-shape": ["@types/d3-shape@3.1.8", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w=="], + + "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="], + + "@types/d3-time-format": ["@types/d3-time-format@4.0.3", "", {}, "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg=="], + + "@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="], + + "@types/d3-transition": ["@types/d3-transition@3.0.9", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg=="], + + "@types/d3-zoom": ["@types/d3-zoom@3.0.8", "", { "dependencies": { "@types/d3-interpolate": "*", "@types/d3-selection": "*" } }, "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw=="], + "@types/debug": ["@types/debug@4.1.13", "", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="], "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], @@ -1457,6 +1542,8 @@ "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], + "@types/geojson": ["@types/geojson@7946.0.16", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="], + "@types/hast": ["@types/hast@3.0.5", "", { "dependencies": { "@types/unist": "*" } }, "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g=="], "@types/istanbul-lib-coverage": ["@types/istanbul-lib-coverage@2.0.6", "", {}, "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w=="], @@ -1559,6 +1646,8 @@ "@unhead/vue": ["@unhead/vue@2.1.17", "", { "dependencies": { "hookable": "^6.0.1", "unhead": "2.1.17" }, "peerDependencies": { "vue": ">=3.5.18" } }, "sha512-pnC8x9HLV3qQXdvWfylUEU25uhfCAy3ly9nmpQz84j9py818DRfU8jOsQ5wjdWtxyU1vX/fW2udfm4jtxUK8Bg=="], + "@upsetjs/venn.js": ["@upsetjs/venn.js@2.0.0", "", { "optionalDependencies": { "d3-selection": "^3.0.0", "d3-transition": "^3.0.1" } }, "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw=="], + "@valibot/to-json-schema": ["@valibot/to-json-schema@1.7.1", "", { "peerDependencies": { "valibot": "^1.4.0" } }, "sha512-3qkmU6KXWh8GIThEAW3kuRHPQBMjWkKy+Ppz3WkUucx53DTpOa6siMn4xDGSOhlVyMrDaJTCTMLYPZVAIk1P0A=="], "@vercel/oidc": ["@vercel/oidc@3.2.0", "", {}, "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug=="], @@ -1845,6 +1934,8 @@ "basic-ftp": ["basic-ftp@5.3.1", "", {}, "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw=="], + "beautiful-mermaid": ["beautiful-mermaid@1.1.3", "", { "dependencies": { "elkjs": "^0.11.0", "entities": "^7.0.1" } }, "sha512-TItrtrAyHp1vwFfFVYauWGrquouk/6SS21Aq3RsxindSYZODcN4xYrPZD6BiZRU+o5mKJzDPz9MUSMvELdylyg=="], + "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], "bind-event-listener": ["bind-event-listener@3.0.0", "", {}, "sha512-PJvH288AWQhKs2v9zyfYdPzlPqf5bXbGMmhmUIY9x4dAUGIWgomO771oBQNwJnMQSnUIXhKu6sgzpBRXTlvb8Q=="], @@ -1991,6 +2082,8 @@ "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], + "cose-base": ["cose-base@1.0.3", "", { "dependencies": { "layout-base": "^1.0.0" } }, "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg=="], + "cosmiconfig": ["cosmiconfig@9.0.2", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg=="], "crc-32": ["crc-32@1.2.2", "", { "bin": { "crc32": "bin/crc32.njs" } }, "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="], @@ -2025,6 +2118,78 @@ "culori": ["culori@4.0.2", "", {}, "sha512-1+BhOB8ahCn4O0cep0Sh2l9KCOfOdY+BXJnKMHFFzDEouSr/el18QwXEMRlOj9UY5nCeA8UN3a/82rUWRBeyBw=="], + "cytoscape": ["cytoscape@3.34.1", "", {}, "sha512-Lr0RvH9H75y9ar8h9Toy6u4lxRSCcxUq+hHcQ26sVWo6BnaQp1gwEZOYqwuYTZhyW7npyKnNLP8oJ2p1/3OZ7g=="], + + "cytoscape-cose-bilkent": ["cytoscape-cose-bilkent@4.1.0", "", { "dependencies": { "cose-base": "^1.0.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ=="], + + "cytoscape-fcose": ["cytoscape-fcose@2.2.0", "", { "dependencies": { "cose-base": "^2.2.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ=="], + + "d3": ["d3@7.9.0", "", { "dependencies": { "d3-array": "3", "d3-axis": "3", "d3-brush": "3", "d3-chord": "3", "d3-color": "3", "d3-contour": "4", "d3-delaunay": "6", "d3-dispatch": "3", "d3-drag": "3", "d3-dsv": "3", "d3-ease": "3", "d3-fetch": "3", "d3-force": "3", "d3-format": "3", "d3-geo": "3", "d3-hierarchy": "3", "d3-interpolate": "3", "d3-path": "3", "d3-polygon": "3", "d3-quadtree": "3", "d3-random": "3", "d3-scale": "4", "d3-scale-chromatic": "3", "d3-selection": "3", "d3-shape": "3", "d3-time": "3", "d3-time-format": "4", "d3-timer": "3", "d3-transition": "3", "d3-zoom": "3" } }, "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA=="], + + "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="], + + "d3-axis": ["d3-axis@3.0.0", "", {}, "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw=="], + + "d3-brush": ["d3-brush@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "3", "d3-transition": "3" } }, "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ=="], + + "d3-chord": ["d3-chord@3.0.1", "", { "dependencies": { "d3-path": "1 - 3" } }, "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g=="], + + "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="], + + "d3-contour": ["d3-contour@4.0.2", "", { "dependencies": { "d3-array": "^3.2.0" } }, "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA=="], + + "d3-delaunay": ["d3-delaunay@6.0.4", "", { "dependencies": { "delaunator": "5" } }, "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A=="], + + "d3-dispatch": ["d3-dispatch@3.0.1", "", {}, "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="], + + "d3-drag": ["d3-drag@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-selection": "3" } }, "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg=="], + + "d3-dsv": ["d3-dsv@3.0.1", "", { "dependencies": { "commander": "7", "iconv-lite": "0.6", "rw": "1" }, "bin": { "csv2json": "bin/dsv2json.js", "csv2tsv": "bin/dsv2dsv.js", "dsv2dsv": "bin/dsv2dsv.js", "dsv2json": "bin/dsv2json.js", "json2csv": "bin/json2dsv.js", "json2dsv": "bin/json2dsv.js", "json2tsv": "bin/json2dsv.js", "tsv2csv": "bin/dsv2dsv.js", "tsv2json": "bin/dsv2json.js" } }, "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q=="], + + "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="], + + "d3-fetch": ["d3-fetch@3.0.1", "", { "dependencies": { "d3-dsv": "1 - 3" } }, "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw=="], + + "d3-force": ["d3-force@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-quadtree": "1 - 3", "d3-timer": "1 - 3" } }, "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg=="], + + "d3-format": ["d3-format@3.1.2", "", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="], + + "d3-geo": ["d3-geo@3.1.1", "", { "dependencies": { "d3-array": "2.5.0 - 3" } }, "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q=="], + + "d3-hierarchy": ["d3-hierarchy@3.1.2", "", {}, "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA=="], + + "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="], + + "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="], + + "d3-polygon": ["d3-polygon@3.0.1", "", {}, "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg=="], + + "d3-quadtree": ["d3-quadtree@3.0.1", "", {}, "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw=="], + + "d3-random": ["d3-random@3.0.1", "", {}, "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ=="], + + "d3-sankey": ["d3-sankey@0.12.3", "", { "dependencies": { "d3-array": "1 - 2", "d3-shape": "^1.2.0" } }, "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ=="], + + "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="], + + "d3-scale-chromatic": ["d3-scale-chromatic@3.1.0", "", { "dependencies": { "d3-color": "1 - 3", "d3-interpolate": "1 - 3" } }, "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ=="], + + "d3-selection": ["d3-selection@3.0.0", "", {}, "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="], + + "d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="], + + "d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="], + + "d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="], + + "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="], + + "d3-transition": ["d3-transition@3.0.1", "", { "dependencies": { "d3-color": "1 - 3", "d3-dispatch": "1 - 3", "d3-ease": "1 - 3", "d3-interpolate": "1 - 3", "d3-timer": "1 - 3" }, "peerDependencies": { "d3-selection": "2 - 3" } }, "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w=="], + + "d3-zoom": ["d3-zoom@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "2 - 3", "d3-transition": "2 - 3" } }, "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw=="], + + "dagre-d3-es": ["dagre-d3-es@7.0.14", "", { "dependencies": { "d3": "^7.9.0", "lodash-es": "^4.17.21" } }, "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg=="], + "data-uri-to-buffer": ["data-uri-to-buffer@6.0.2", "", {}, "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw=="], "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="], @@ -2035,6 +2200,8 @@ "datastore-core": ["datastore-core@10.0.4", "", { "dependencies": { "@libp2p/logger": "^5.1.18", "interface-datastore": "^8.0.0", "interface-store": "^6.0.0", "it-drain": "^3.0.9", "it-filter": "^3.1.3", "it-map": "^3.1.3", "it-merge": "^3.0.11", "it-pipe": "^3.0.1", "it-sort": "^3.0.8", "it-take": "^3.0.8" } }, "sha512-IctgCO0GA7GHG7aRm3JRruibCsfvN4EXNnNIlLCZMKIv0TPkdAL5UFV3/xTYFYrrZ1jRNrXZNZRvfcVf/R+rAw=="], + "dayjs": ["dayjs@1.11.23", "", {}, "sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], "decamelize": ["decamelize@6.0.1", "", {}, "sha512-G7Cqgaelq68XHJNGlZ7lrNQyhZGsFqpwtGFexqUv4IQdjKoSYF7ipZ9UuTJZUSQXFj/XaoBLuEVIVqr8EJngEQ=="], @@ -2065,6 +2232,8 @@ "degenerator": ["degenerator@5.0.1", "", { "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", "esprima": "^4.0.1" } }, "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ=="], + "delaunator": ["delaunator@5.1.0", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ=="], + "delay": ["delay@6.0.0", "", {}, "sha512-2NJozoOHQ4NuZuVIr5CWd0iiLVIRSDepakaovIN+9eIDHEhdCAEvSy2cuf1DCrPPQLvHmbqTHODlhHg8UCy4zw=="], "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], @@ -2135,6 +2304,8 @@ "electron-to-chromium": ["electron-to-chromium@1.5.408", "", {}, "sha512-SLoprcYpJ/OH2v2ps0+N5biv9H4/KBT3+YmmDew64TwK5y9j2wv7pMOFY7IorVkyMtEyLSCRlXKLsNlakeAlPw=="], + "elkjs": ["elkjs@0.11.1", "", {}, "sha512-zxxR9k+rx5ktMwT/FwyLdPCrq7xN6e4VGGHH8hA01vVYKjTFik7nHOxBnAYtrgYUB1RpAiLvA1/U2YraWxyKKg=="], + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "emojilib": ["emojilib@2.4.0", "", {}, "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw=="], @@ -2375,6 +2546,8 @@ "gray-matter": ["gray-matter@4.0.3", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="], + "hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="], + "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], @@ -2469,6 +2642,8 @@ "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], + "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], + "iobuffer": ["iobuffer@5.4.0", "", {}, "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA=="], "ip-address": ["ip-address@10.5.0", "", {}, "sha512-R5SnVLJmgYYvf2F2ZgwSBnelz5G4q5AxIC277GDfUaNbrZKNANcBC7RHqYYePlszf4kBolVkJauG0ZjHHFh55g=="], @@ -2679,10 +2854,14 @@ "katex": ["katex@0.16.47", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg=="], + "khroma": ["khroma@2.1.0", "", {}, "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="], + "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], "knip": ["knip@6.20.0", "", { "dependencies": { "fdir": "^6.5.0", "formatly": "^0.3.0", "get-tsconfig": "4.14.0", "jiti": "^2.7.0", "oxc-parser": "^0.137.0", "oxc-resolver": "11.21.3", "picomatch": "^4.0.4", "smol-toml": "^1.6.1", "strip-json-comments": "5.0.3", "tinyglobby": "^0.2.17", "unbash": "^4.0.1", "yaml": "^2.9.0", "zod": "^4.1.11" }, "bin": { "knip": "bin/knip.js", "knip-bun": "bin/knip-bun.js" } }, "sha512-q+HYrS7FOERk32GM7g43IdTG7TurN7+YMLZrNpCxubZPYZyQwliBgd18yH4oYQL7YgGtOxP2fCqess3K2XaK2Q=="], + "layout-base": ["layout-base@1.0.2", "", {}, "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg=="], + "lazystream": ["lazystream@1.0.1", "", { "dependencies": { "readable-stream": "^2.0.5" } }, "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw=="], "leven": ["leven@3.1.0", "", {}, "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="], @@ -2823,6 +3002,8 @@ "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + "mermaid": ["mermaid@11.16.1", "", { "dependencies": { "@braintree/sanitize-url": "^7.1.2", "@iconify/utils": "^3.0.2", "@mermaid-js/parser": "^1.2.0", "@types/d3": "^7.4.3", "@upsetjs/venn.js": "^2.0.0", "cytoscape": "^3.33.3", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.14", "dayjs": "^1.11.20", "dompurify": "^3.3.3", "es-toolkit": "^1.45.1", "katex": "^0.16.45", "khroma": "^2.1.0", "marked": "^16.3.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0" } }, "sha512-TQsq6u22fAn3rek5VOubrhKPo1g5hwC3FXUN9hiyupTckcYiGuuKGkNQrKYwGJkXUxZdojwRG46gsSCFZMDp4g=="], + "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], @@ -3059,6 +3240,8 @@ "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], + "path-data-parser": ["path-data-parser@0.1.0", "", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="], + "path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="], "path-expression-matcher": ["path-expression-matcher@1.6.2", "", {}, "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ=="], @@ -3101,6 +3284,10 @@ "pluralize": ["pluralize@8.0.0", "", {}, "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="], + "points-on-curve": ["points-on-curve@0.2.0", "", {}, "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A=="], + + "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=="], + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], "postcss": ["postcss@8.5.26", "", { "dependencies": { "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ=="], @@ -3291,12 +3478,16 @@ "rgbcolor": ["rgbcolor@1.0.1", "", {}, "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw=="], + "robust-predicates": ["robust-predicates@3.0.3", "", {}, "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA=="], + "rolldown": ["rolldown@1.2.4", "", { "dependencies": { "@oxc-project/types": "=0.144.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.2.4", "@rolldown/binding-darwin-arm64": "1.2.4", "@rolldown/binding-darwin-x64": "1.2.4", "@rolldown/binding-freebsd-x64": "1.2.4", "@rolldown/binding-linux-arm-gnueabihf": "1.2.4", "@rolldown/binding-linux-arm64-gnu": "1.2.4", "@rolldown/binding-linux-arm64-musl": "1.2.4", "@rolldown/binding-linux-ppc64-gnu": "1.2.4", "@rolldown/binding-linux-s390x-gnu": "1.2.4", "@rolldown/binding-linux-x64-gnu": "1.2.4", "@rolldown/binding-linux-x64-musl": "1.2.4", "@rolldown/binding-openharmony-arm64": "1.2.4", "@rolldown/binding-win32-arm64-msvc": "1.2.4", "@rolldown/binding-win32-x64-msvc": "1.2.4" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-rSr7irW0K7QRWzjdJXqZowkcRdDtjRduh43rBltnVKd0VFq839l1lJoDvGJb6gl7+4rTTCrPWu+YfujUL8Ug7w=="], "rolldown-plugin-dts": ["rolldown-plugin-dts@0.27.14", "", { "dependencies": { "dts-resolver": "^3.0.0", "get-tsconfig": "5.0.0-beta.5", "obug": "^2.1.4", "yuku-ast": "^0.8.0", "yuku-codegen": "^0.8.0", "yuku-parser": "^0.8.0" }, "peerDependencies": { "@typescript/native-preview": "*", "@volar/typescript": "~2.4.0", "rolldown": "^1.0.0", "typescript": "^5.0.0 || ^6.0.0 || ~7.0.0", "vue-tsc": "~3.2.0 || ~3.3.0" }, "optionalPeers": ["@typescript/native-preview", "@volar/typescript", "typescript", "vue-tsc"] }, "sha512-ZvuDDwoIpRK9RPxDXratCpklFO9QZZWndf/sd0VBFb4LEj0jj07UcHK9OCh7V4XiFz2Z89ziyBC2K6tJiDjrbw=="], "rollup": ["rollup@4.62.4", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@napi-rs/lzma-linux-x64-gnu": "1.5.1", "@rollup/rollup-android-arm-eabi": "4.62.4", "@rollup/rollup-android-arm64": "4.62.4", "@rollup/rollup-darwin-arm64": "4.62.4", "@rollup/rollup-darwin-x64": "4.62.4", "@rollup/rollup-freebsd-arm64": "4.62.4", "@rollup/rollup-freebsd-x64": "4.62.4", "@rollup/rollup-linux-arm-gnueabihf": "4.62.4", "@rollup/rollup-linux-arm-musleabihf": "4.62.4", "@rollup/rollup-linux-arm64-gnu": "4.62.4", "@rollup/rollup-linux-arm64-musl": "4.62.4", "@rollup/rollup-linux-loong64-gnu": "4.62.4", "@rollup/rollup-linux-loong64-musl": "4.62.4", "@rollup/rollup-linux-ppc64-gnu": "4.62.4", "@rollup/rollup-linux-ppc64-musl": "4.62.4", "@rollup/rollup-linux-riscv64-gnu": "4.62.4", "@rollup/rollup-linux-riscv64-musl": "4.62.4", "@rollup/rollup-linux-s390x-gnu": "4.62.4", "@rollup/rollup-linux-x64-gnu": "4.62.4", "@rollup/rollup-linux-x64-musl": "4.62.4", "@rollup/rollup-openbsd-x64": "4.62.4", "@rollup/rollup-openharmony-arm64": "4.62.4", "@rollup/rollup-win32-arm64-msvc": "4.62.4", "@rollup/rollup-win32-ia32-msvc": "4.62.4", "@rollup/rollup-win32-x64-gnu": "4.62.4", "@rollup/rollup-win32-x64-msvc": "4.62.4", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg=="], + "roughjs": ["roughjs@4.6.6", "", { "dependencies": { "hachure-fill": "^0.5.2", "path-data-parser": "^0.1.0", "points-on-curve": "^0.2.0", "points-on-path": "^0.2.1" } }, "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ=="], + "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], "run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="], @@ -3305,6 +3496,8 @@ "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + "rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="], + "rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="], "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], @@ -3477,6 +3670,8 @@ "style-mod": ["style-mod@4.1.3", "", {}, "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="], + "stylis": ["stylis@4.4.0", "", {}, "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA=="], + "sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="], "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -3689,7 +3884,7 @@ "utrie": ["utrie@1.0.2", "", { "dependencies": { "base64-arraybuffer": "^1.0.2" } }, "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw=="], - "uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], + "uuid": ["uuid@14.0.1", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew=="], "valibot": ["valibot@1.4.2", "", { "peerDependencies": { "typescript": ">=5" }, "optionalPeers": ["typescript"] }, "sha512-gjdCvJ6d3RyHAneqxMYMW9QMCwYMb3jpOO0IyHZV1bnRHFBHrX3VkIILt5XYR0WhwHiH7Mty8ovuPZ/O3gamrg=="], @@ -3733,7 +3928,7 @@ "vue-router": ["vue-router@5.2.0", "", { "dependencies": { "@babel/generator": "^8.0.0", "@vue-macros/common": "^3.1.3", "@vue/devtools-api": "^8.1.5", "ast-walker-scope": "^0.9.0", "chokidar": "^5.0.0", "json5": "^2.2.3", "local-pkg": "^1.2.1", "magic-string": "^0.30.21", "mlly": "^1.8.2", "muggle-string": "^0.4.1", "nostics": "^1.1.4", "pathe": "^2.0.3", "picomatch": "^4.0.5", "scule": "^1.3.0", "tinyglobby": "^0.2.17", "unplugin": "^3.3.0", "unplugin-utils": "^0.3.2", "yaml": "^2.9.0" }, "peerDependencies": { "@pinia/colada": ">=0.21.2", "@vue/compiler-sfc": "^3.5.34 || ^4.0.0", "pinia": "^3.0.4 || ^4.0.2", "vite": "^7.3.0 || ^8.0.0", "vue": "^3.5.34 || ^4.0.0" }, "optionalPeers": ["@pinia/colada", "@vue/compiler-sfc", "pinia", "vite"] }, "sha512-QAC5i0LEb1GLG0LXDQmHu8L7FX12j0KwU/JTKmLQUJMrn04gQdKP6Du+p0QwpHb3iy71vBlqnHQ8WAfOSAWhqw=="], - "vue-stream-markdown": ["vue-stream-markdown@0.6.5", "", { "dependencies": { "@floating-ui/dom": "^1.7.5", "@vueuse/core": "^14.2.1", "marked": "^17.0.3", "mdast-util-from-markdown": "^2.0.3", "mdast-util-frontmatter": "^2.0.1", "mdast-util-gfm": "^3.1.0", "mdast-util-math": "^3.0.0", "mdast-util-to-markdown": "^2.1.2", "micromark-extension-cjk-friendly": "^2.0.1", "micromark-extension-cjk-friendly-gfm-strikethrough": "^2.0.1", "micromark-extension-frontmatter": "^2.0.0", "micromark-extension-gfm": "^3.0.0", "micromark-extension-math": "^3.1.0", "micromark-util-types": "^2.0.2" }, "peerDependencies": { "beautiful-mermaid": ">=0.1.0", "katex": ">=0.16.20", "mermaid": ">=11", "shiki": ">=3.15.0", "vue": ">=3.0.0" }, "optionalPeers": ["beautiful-mermaid", "katex", "mermaid", "shiki"] }, "sha512-wXj1Kjul+7tP1GcuEZ1fqrfT+UokBuTJXM8rGMzo8qH7pTmcUp//sGPIbjIPSfhhIrSHxuBdMLSBEbccwFp07g=="], + "vue-stream-markdown": ["vue-stream-markdown@1.0.4", "", { "dependencies": { "@floating-ui/dom": "^1.8.0", "@markmend/ast": "1.0.4", "@markmend/core": "1.0.4", "@stream-markdown/code": "1.0.4", "@stream-markdown/core": "1.0.4", "@stream-markdown/math": "1.0.4", "@stream-markdown/mermaid": "1.0.4", "@vueuse/core": "^14.4.0" }, "peerDependencies": { "beautiful-mermaid": ">=0.1.0", "katex": ">=0.16.20", "mermaid": ">=11", "shiki": ">=3.15.0", "vue": ">=3.0.0" }, "optionalPeers": ["beautiful-mermaid", "katex", "mermaid", "shiki"] }, "sha512-Ipm6jpcxWHags9ME/USdv2CM+mpoXIiAcaIsvjrMa+PhAIFM1AXNyviOqT+c+H2fa0bDiWDiw1UA83b6yd2Wpg=="], "vue-tsc": ["vue-tsc@3.3.10", "", { "dependencies": { "@volar/typescript": "2.4.28", "@vue/language-core": "3.3.10" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "bin/vue-tsc.js" } }, "sha512-YaDVxcW+CGtaOt3pZahMG5jYPx0hsUTxyEoPOTSMebcGUXP9lIBabQ14vfKMORb2CqqK5CxsNo/d1d+4IQwiKg=="], @@ -3947,6 +4142,8 @@ "@libp2p/utils/@libp2p/interface": ["@libp2p/interface@2.11.0", "", { "dependencies": { "@multiformats/dns": "^1.0.6", "@multiformats/multiaddr": "^12.4.4", "it-pushable": "^3.2.3", "it-stream-types": "^2.0.2", "main-event": "^1.0.1", "multiformats": "^13.3.6", "progress-events": "^1.0.1", "uint8arraylist": "^2.4.8" } }, "sha512-0MUFKoXWHTQW3oWIgSHApmYMUKWO/Y02+7Hpyp+n3z+geD4Xo2Rku2gYWmxcq+Pyjkz6Q9YjDWz3Yb2SoV2E8Q=="], + "@markmend/core/marked": ["marked@18.0.10", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-FJeH4bRpYoXiggcgriCGItKCSv3xkngJc4QCZ/rkQCogU3VYaLxYJoZl8Nw/b4+x7iij/pd+09mZ6A1dXzpL0A=="], + "@multiformats/dns/@libp2p/interface": ["@libp2p/interface@3.2.5", "", { "dependencies": { "@multiformats/dns": "^1.0.6", "@multiformats/multiaddr": "^13.0.3", "main-event": "^1.0.1", "multiformats": "^14.0.0", "progress-events": "^1.1.0", "uint8arraylist": "^3.0.2" } }, "sha512-RdTchTdakBBc4ShKKsvoME/bJYsrCoVDpdYQVdd4TQ30TCb8QwWKGClqAtw7gfLNuaUKm41xz06kASW6AZpbDA=="], "@multiformats/dns/uint8arrays": ["uint8arrays@6.1.1", "", { "dependencies": { "multiformats": "^14.0.0" } }, "sha512-iz7JN0XCSZYA111lhFG2Ui9EhFvTNekqSRHw3lvMHq+dzwWy1OQftxFQREEh4rffU0oSoXdQHsk2TiHKVm4fsA=="], @@ -3997,6 +4194,8 @@ "@waku/core/it-length-prefixed": ["it-length-prefixed@9.1.1", "", { "dependencies": { "it-reader": "^6.0.1", "it-stream-types": "^2.0.1", "uint8-varint": "^2.0.1", "uint8arraylist": "^2.0.0", "uint8arrays": "^5.0.1" } }, "sha512-O88nBweT6M9ozsmok68/auKH7ik/slNM4pYbM9lrfy2z5QnpokW5SlrepHZDKtN71llhG2sZvd6uY4SAl+lAQg=="], + "@waku/core/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], + "@waku/enr/@libp2p/crypto": ["@libp2p/crypto@5.1.6", "", { "dependencies": { "@libp2p/interface": "^2.10.4", "@noble/curves": "^1.9.1", "@noble/hashes": "^1.8.0", "multiformats": "^13.3.6", "protons-runtime": "^5.5.0", "uint8arraylist": "^2.4.8", "uint8arrays": "^5.1.0" } }, "sha512-hCNDInAsjfFTOr1ZlVTVuRKpkGEbR1GC+cDbmn2Vslwd0dHZHqhKv5ye7l6NZaiNUxxqUCVmqvJIWqVLuTPDdg=="], "@waku/enr/@libp2p/peer-id": ["@libp2p/peer-id@5.1.7", "", { "dependencies": { "@libp2p/crypto": "^5.1.6", "@libp2p/interface": "^2.10.4", "multiformats": "^13.3.6", "uint8arrays": "^5.1.0" } }, "sha512-KBT8Edx/Sqxj0vKe5mPM2PQx06VDmGzx2BZ1M+LiDAM94q9Sag4tyaUugHyTrJKGG8V+7lx1Fz46kfbezuwR9g=="], @@ -4043,6 +4242,8 @@ "babel-plugin-polyfill-corejs2/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "beautiful-mermaid/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], + "blamer/execa": ["execa@4.1.0", "", { "dependencies": { "cross-spawn": "^7.0.0", "get-stream": "^5.0.0", "human-signals": "^1.1.1", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.0", "onetime": "^5.1.0", "signal-exit": "^3.0.2", "strip-final-newline": "^2.0.0" } }, "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA=="], "body-parser/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], @@ -4069,6 +4270,16 @@ "create-wdio/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], + "cytoscape-fcose/cose-base": ["cose-base@2.2.0", "", { "dependencies": { "layout-base": "^2.0.0" } }, "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g=="], + + "d3-dsv/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], + + "d3-dsv/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + + "d3-sankey/d3-array": ["d3-array@2.12.1", "", { "dependencies": { "internmap": "^1.0.0" } }, "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ=="], + + "d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="], + "degenerator/ast-types": ["ast-types@0.13.4", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w=="], "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], @@ -4135,6 +4346,8 @@ "mdast-util-gfm-table/markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], + "mermaid/marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="], + "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], "mlly/pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], @@ -4257,8 +4470,6 @@ "vue-router/unplugin": ["unplugin@3.3.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "picomatch": "^4.0.4", "webpack-virtual-modules": "^0.6.2" }, "peerDependencies": { "@farmfe/core": "*", "@rspack/core": "*", "bun-types-no-globals": "*", "esbuild": "*", "rolldown": "*", "rollup": "*", "unloader": "*", "vite": "*", "webpack": "*" }, "optionalPeers": ["@farmfe/core", "@rspack/core", "bun-types-no-globals", "esbuild", "rolldown", "rollup", "unloader", "vite", "webpack"] }, "sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg=="], - "vue-stream-markdown/marked": ["marked@17.0.6", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA=="], - "wait-port/commander": ["commander@9.5.0", "", {}, "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ=="], "weald/ms": ["ms@4.0.0-nightly.202508271359", "", {}, "sha512-WC/Eo7NzFrOV/RRrTaI0fxKVbNCzEy76j2VqNV8SxDf9D69gSE2Lh0QwYvDlhiYmheBYExAvEAxVf5NoN0cj2A=="], @@ -4465,6 +4676,12 @@ "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "cytoscape-fcose/cose-base/layout-base": ["layout-base@2.0.1", "", {}, "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="], + + "d3-sankey/d3-array/internmap": ["internmap@1.0.1", "", {}, "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="], + + "d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="], + "edgedriver/which/isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="], "expect-webdriverio/@vitest/snapshot/@vitest/pretty-format": ["@vitest/pretty-format@4.1.10", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q=="], diff --git a/package.json b/package.json index 62953e0bd..0270784f4 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,8 @@ "@codemirror/search": "^6.7.1", "@codemirror/state": "^6.7.1", "@codemirror/view": "^6.43.9", + "@nanostores/i18n": "^1.3.3", + "@nanostores/vue": "^1.1.0", "@open-pencil/cli": "workspace:*", "@open-pencil/core": "workspace:*", "@open-pencil/dom-css": "workspace:*", @@ -116,6 +118,7 @@ "jspdf": "^4.2.1", "lib0": "^0.2.117", "motion-v": "^2.4.0", + "nanostores": "^1.5.0", "opentype.js": "^2.0.0", "reka-ui": "^2.10.3", "svg2pdf.js": "^2.7.0", @@ -127,7 +130,7 @@ "valibot": "^1.4.2", "vue": "^3.5.41", "vue-router": "^5.2.0", - "vue-stream-markdown": "^0.6.5", + "vue-stream-markdown": "1.0.4", "y-indexeddb": "^9.0.12", "y-protocols": "^1.0.7", "yjs": "^13.6.32", diff --git a/src/app/document/io/dom.ts b/src/app/document/io/dom.ts index 7efbc06c3..73d0b8a56 100644 --- a/src/app/document/io/dom.ts +++ b/src/app/document/io/dom.ts @@ -3,6 +3,7 @@ import { browserHTMLToSceneGraph } from '@open-pencil/dom-css/browser' import { yieldToUI } from '@/app/document/io/browser' import { applyImportedDocument } from '@/app/document/io/imported-document' +import { notificationMessages } from '@/app/i18n/notifications' import { toast } from '@/app/shell/ui' type OpenDOMDocumentState = EditorState & { @@ -33,6 +34,10 @@ type DOMTextImportOptions = { documentName?: string } +function errorDetail(error: unknown): string { + return error instanceof Error ? error.message : String(error) +} + function documentNameFor(file: File): string { return file.name.replace(/\.(html?|xhtml)$/i, '') } @@ -60,10 +65,10 @@ export function createDOMOpenActions({ state.loading = true const pageName = await applyDOMText(html, options) setDocumentSource(`${pageName}.html`, 'html') - toast.info('Imported DOM/CSS document') + toast.info(notificationMessages.get().importedDOMCSS) } catch (e) { console.error('Failed to import DOM/CSS:', e) - toast.error(`Failed to import DOM/CSS: ${e instanceof Error ? e.message : String(e)}`) + toast.error(notificationMessages.get().importDOMCSSFailed({ error: errorDetail(e) })) throw e } finally { state.loading = false @@ -81,7 +86,7 @@ export function createDOMOpenActions({ setDocumentSource(file.name, 'html', options.handle, options.path) } catch (e) { console.error('Failed to open DOM/CSS file:', e) - toast.error(`Failed to open DOM/CSS file: ${e instanceof Error ? e.message : String(e)}`) + toast.error(notificationMessages.get().openDOMCSSFailed({ error: errorDetail(e) })) } finally { state.loading = false } diff --git a/src/app/document/io/read.ts b/src/app/document/io/read.ts index 1349afed8..099b196bc 100644 --- a/src/app/document/io/read.ts +++ b/src/app/document/io/read.ts @@ -6,6 +6,7 @@ import { yieldToUI } from '@/app/document/io/browser' import { applyImportedDocument } from '@/app/document/io/imported-document' import { readReloadSource } from '@/app/document/io/reload-source' import { captureReloadState, restoreReloadState } from '@/app/document/io/reload-state' +import { notificationMessages } from '@/app/i18n/notifications' import { toast } from '@/app/shell/ui' type OpenDocumentState = EditorState & { @@ -54,7 +55,12 @@ export function createOpenActions({ editor.requestRender() } catch (e) { console.error('Failed to open .fig file:', e) - toast.error(`Failed to open file: ${e instanceof Error ? e.message : String(e)}`) + toast.error( + notificationMessages.get().openFileFailed({ + name: file.name, + error: e instanceof Error ? e.message : String(e) + }) + ) } finally { state.loading = false } diff --git a/src/app/editor/canvas/menu/actions.ts b/src/app/editor/canvas/menu/actions.ts index a29290fa7..278d4ec8b 100644 --- a/src/app/editor/canvas/menu/actions.ts +++ b/src/app/editor/canvas/menu/actions.ts @@ -7,6 +7,7 @@ import type { EditorStore } from '@/app/editor/active-store' import { pasteClipboardToReplace } from '@/app/editor/clipboard/paste-to-replace' import { executeClipboardCommand } from '@/app/editor/clipboard/system' import { canVectorizeImageNode, vectorizeImageNode } from '@/app/editor/vectorize' +import { notificationMessages } from '@/app/i18n/notifications' import { toast } from '@/app/shell/ui' import { writeTauriClipboardText } from '@/app/tauri/clipboard' import { isTauri } from '@/app/tauri/env' @@ -26,7 +27,7 @@ export function createCanvasMenuActions(store: EditorStore, selectedIds: Ref { - if (!ok) toast.error('Clipboard access is blocked in this browser context') + if (!ok) toast.error(notificationMessages.get().clipboardAccessBlocked) return undefined }) } @@ -38,13 +39,17 @@ export function createCanvasMenuActions(store: EditorStore, selectedIds: Ref 1 ? 's' : ''}`) + const messages = notificationMessages.get() + await clipboardWrite( + nodeIds.join(', '), + nodeIds.length > 1 ? messages.nodeIDs : messages.nodeID + ) } async function copyXPath() { @@ -54,19 +59,20 @@ export function createCanvasMenuActions(store: EditorStore, selectedIds: Ref nodeToXPath(store.graph, id)) .filter((xpath): xpath is string => xpath !== null) if (xpaths.length === 0) return - await clipboardWrite(xpaths.join('\n'), `XPath${xpaths.length > 1 ? 's' : ''}`) + const messages = notificationMessages.get() + await clipboardWrite(xpaths.join('\n'), xpaths.length > 1 ? messages.xPaths : messages.xPath) } async function copyAsPNG() { if (typeof ClipboardItem === 'undefined') { - toast.error('PNG clipboard export is not available in this browser') + toast.error(notificationMessages.get().pngClipboardUnavailable) return } const data = await store.renderExportImage(ids(), 2, 'PNG') if (!data) return const blob = new Blob([toArrayBuffer(data)], { type: 'image/png' }) await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]) - toast.info('Copied as PNG') + toast.info(notificationMessages.get().copiedAs({ format: 'PNG' })) } return { diff --git a/src/app/editor/clipboard/paste-to-replace.ts b/src/app/editor/clipboard/paste-to-replace.ts index aa1c48aa0..123c1dac3 100644 --- a/src/app/editor/clipboard/paste-to-replace.ts +++ b/src/app/editor/clipboard/paste-to-replace.ts @@ -1,4 +1,5 @@ import type { EditorStore } from '@/app/editor/active-store' +import { notificationMessages } from '@/app/i18n/notifications' import { toast } from '@/app/shell/ui' import { readTauriClipboardText } from '@/app/tauri/clipboard' import { isTauri } from '@/app/tauri/env' @@ -26,12 +27,12 @@ export async function pasteClipboardToReplace(store: EditorStore) { try { const html = await readClipboardHTML() if (!html) { - toast.error('Clipboard does not contain design data') + toast.error(notificationMessages.get().clipboardMissingDesignData) return } await store.pasteFromHTML(html, undefined, { replaceSelection: true }) } catch (error) { console.warn('Paste to replace failed', error) - toast.error('Clipboard access is blocked in this browser context') + toast.error(notificationMessages.get().clipboardAccessBlocked) } } diff --git a/src/app/editor/vectorize/action.ts b/src/app/editor/vectorize/action.ts index 83ff24952..5e406b7a2 100644 --- a/src/app/editor/vectorize/action.ts +++ b/src/app/editor/vectorize/action.ts @@ -2,6 +2,7 @@ import { preprocessForVectorize, svgToVectorPaths } from '@open-pencil/core/vect import type { Fill, SceneNode } from '@open-pencil/scene-graph' import type { EditorStore } from '@/app/editor/active-store' +import { notificationMessages } from '@/app/i18n/notifications' import { openSettingsDialog } from '@/app/settings/dialog' import { toast } from '@/app/shell/ui' @@ -27,11 +28,10 @@ export function canVectorizeImageNode(store: EditorStore): boolean { return Boolean(node && imageFill(node)) } -function vectorizeErrorMessage(error: unknown, providerName: string): string { - if (error instanceof TypeError) { - return `${providerName} could not be reached. Check your connection and try again.` - } - const message = error instanceof Error ? error.message : 'Vectorization failed' +function vectorizeErrorMessage(error: unknown): string { + let message = 'Vectorization failed' + if (error instanceof TypeError) message = error.message || 'Network request failed' + else if (error instanceof Error) message = error.message return message.length <= ERROR_MAX_LENGTH ? message : `${message.slice(0, ERROR_MAX_LENGTH)}…` } @@ -46,18 +46,20 @@ export async function vectorizeImageNode(store: EditorStore, nodeId: string): Pr try { const apiKey = await resolveVectorizeCredential(provider.id) if (!apiKey) { - toast.error(`Add a ${provider.name} API key in Settings → Media.`) + toast.error( + notificationMessages.get().vectorizeCredentialRequired({ provider: provider.name }) + ) openSettingsDialog('media') return } const imageBytes = store.graph.images.get(fill.imageHash) if (!imageBytes) { - toast.error('Image data is missing for this layer') + toast.error(notificationMessages.get().vectorizeImageMissing) return } - toast.info('Vectorizing image…') + toast.info(notificationMessages.get().vectorizingImage) const preprocessed = preprocessForVectorize(imageBytes, () => store.renderer?.ck ?? null) if (!preprocessed) throw new Error('Could not prepare this image for vectorization') @@ -75,13 +77,17 @@ export async function vectorizeImageNode(store: EditorStore, nodeId: string): Pr const frameId = store.replaceNodeWithVectorFrame(currentNode.id, vectorized) if (!frameId) throw new Error('Could not replace this image with vector layers') - toast.info('Image converted to vectors') + toast.info(notificationMessages.get().imageConvertedToVectors) } catch (error) { if (error instanceof VectorizeAuthError) { - toast.error(`${error.message}. Update it in Settings → Media.`) + toast.error(notificationMessages.get().vectorizeCredentialFailed({ error: error.message })) openSettingsDialog('media') } else { - toast.error(vectorizeErrorMessage(error, provider.name)) + toast.error( + notificationMessages + .get() + .vectorizeFailed({ provider: provider.name, error: vectorizeErrorMessage(error) }) + ) } } finally { activeStores.delete(store) diff --git a/src/app/i18n/notifications/index.ts b/src/app/i18n/notifications/index.ts new file mode 100644 index 000000000..f924b6bfe --- /dev/null +++ b/src/app/i18n/notifications/index.ts @@ -0,0 +1,57 @@ +import { createI18n, params } from '@nanostores/i18n' +import type { ComponentsJSON } from '@nanostores/i18n' +import { useStore } from '@nanostores/vue' + +import { locale, type Locale, type TranslatedLocale } from '@open-pencil/vue' + +export const notificationMessageDefaults = { + chatInitializationFailed: params('Could not initialize chat: {error}'), + linkCopied: 'Link copied to clipboard.', + clipboardMissingDesignData: 'Clipboard does not contain design data.', + clipboardAccessBlocked: 'Clipboard access is blocked in this browser context.', + copiedAs: params('Copied as {format}.'), + nodeID: 'node ID', + nodeIDs: 'node IDs', + xPath: 'XPath', + xPaths: 'XPaths', + pngClipboardUnavailable: 'PNG clipboard export is not available in this browser.', + openFileFailed: params('Could not open “{name}”: {error}'), + importedDOMCSS: 'Imported DOM/CSS document.', + importDOMCSSFailed: params('Could not import DOM/CSS: {error}'), + openDOMCSSFailed: params('Could not open DOM/CSS file: {error}'), + vectorizeCredentialRequired: params('Add a {provider} API key in Settings → Media.'), + vectorizeImageMissing: 'Image data is missing for this layer.', + vectorizingImage: 'Vectorizing image…', + imageConvertedToVectors: 'Image converted to vectors.', + vectorizeCredentialFailed: params('{error}. Update it in Settings → Media.'), + vectorizeFailed: params('{provider} could not vectorize this image: {error}'), + operationFailed: params('Operation failed: {error}'), + storageConnected: 'Connected. Storage namespace is ready.', + storageConnectionFailed: params('Could not connect to storage: {error}') +} as const + +const localeLoaders = { + de: () => import('./locales/de.json'), + es: () => import('./locales/es.json'), + fr: () => import('./locales/fr.json'), + it: () => import('./locales/it.json'), + ja: () => import('./locales/ja.json'), + pl: () => import('./locales/pl.json'), + ru: () => import('./locales/ru.json'), + 'zh-CN': () => import('./locales/zh-cn.json') +} satisfies Record Promise<{ default: Record }>> + +const appI18n = createI18n(locale, { + baseLocale: 'en', + async get(code): Promise { + if (code === 'en') return {} + const loaded = await localeLoaders[code]() + return { notifications: loaded.default } + } +}) + +export const notificationMessages = appI18n('notifications', notificationMessageDefaults) + +export function useNotificationMessages() { + return useStore(notificationMessages) +} diff --git a/src/app/i18n/notifications/locales/de.json b/src/app/i18n/notifications/locales/de.json new file mode 100644 index 000000000..85aee03cf --- /dev/null +++ b/src/app/i18n/notifications/locales/de.json @@ -0,0 +1,25 @@ +{ + "chatInitializationFailed": "Chat konnte nicht initialisiert werden: {error}", + "linkCopied": "Link in die Zwischenablage kopiert.", + "clipboardMissingDesignData": "Die Zwischenablage enthält keine Designdaten.", + "clipboardAccessBlocked": "Der Zugriff auf die Zwischenablage ist in diesem Browserkontext blockiert.", + "copiedAs": "Als {format} kopiert.", + "pngClipboardUnavailable": "Der PNG-Export in die Zwischenablage ist in diesem Browser nicht verfügbar.", + "openFileFailed": "„{name}“ konnte nicht geöffnet werden: {error}", + "importedDOMCSS": "DOM/CSS-Dokument importiert.", + "importDOMCSSFailed": "DOM/CSS konnte nicht importiert werden: {error}", + "openDOMCSSFailed": "DOM/CSS-Datei konnte nicht geöffnet werden: {error}", + "vectorizeCredentialRequired": "Füge einen {provider}-API-Schlüssel unter Einstellungen → Medien hinzu.", + "vectorizeImageMissing": "Für diese Ebene fehlen Bilddaten.", + "vectorizingImage": "Bild wird vektorisiert…", + "imageConvertedToVectors": "Bild in Vektoren umgewandelt.", + "vectorizeCredentialFailed": "{error}. Aktualisiere den Schlüssel unter Einstellungen → Medien.", + "vectorizeFailed": "{provider} konnte dieses Bild nicht vektorisieren: {error}", + "operationFailed": "Vorgang fehlgeschlagen: {error}", + "storageConnected": "Verbunden. Der Speicher-Namensraum ist bereit.", + "storageConnectionFailed": "Verbindung zum Speicher fehlgeschlagen: {error}", + "nodeID": "Knoten-ID", + "nodeIDs": "Knoten-IDs", + "xPath": "XPath", + "xPaths": "XPaths" +} diff --git a/src/app/i18n/notifications/locales/es.json b/src/app/i18n/notifications/locales/es.json new file mode 100644 index 000000000..68481ac54 --- /dev/null +++ b/src/app/i18n/notifications/locales/es.json @@ -0,0 +1,25 @@ +{ + "chatInitializationFailed": "No se pudo iniciar el chat: {error}", + "linkCopied": "Enlace copiado al portapapeles.", + "clipboardMissingDesignData": "El portapapeles no contiene datos de diseño.", + "clipboardAccessBlocked": "El acceso al portapapeles está bloqueado en este contexto del navegador.", + "copiedAs": "Copiado como {format}.", + "pngClipboardUnavailable": "La exportación PNG al portapapeles no está disponible en este navegador.", + "openFileFailed": "No se pudo abrir «{name}»: {error}", + "importedDOMCSS": "Documento DOM/CSS importado.", + "importDOMCSSFailed": "No se pudo importar DOM/CSS: {error}", + "openDOMCSSFailed": "No se pudo abrir el archivo DOM/CSS: {error}", + "vectorizeCredentialRequired": "Añade una clave API de {provider} en Ajustes → Multimedia.", + "vectorizeImageMissing": "Faltan los datos de imagen de esta capa.", + "vectorizingImage": "Vectorizando imagen…", + "imageConvertedToVectors": "Imagen convertida en vectores.", + "vectorizeCredentialFailed": "{error}. Actualízala en Ajustes → Multimedia.", + "vectorizeFailed": "{provider} no pudo vectorizar esta imagen: {error}", + "operationFailed": "La operación falló: {error}", + "storageConnected": "Conectado. El espacio de almacenamiento está listo.", + "storageConnectionFailed": "No se pudo conectar al almacenamiento: {error}", + "nodeID": "ID de nodo", + "nodeIDs": "ID de nodos", + "xPath": "XPath", + "xPaths": "XPaths" +} diff --git a/src/app/i18n/notifications/locales/fr.json b/src/app/i18n/notifications/locales/fr.json new file mode 100644 index 000000000..da6cc5990 --- /dev/null +++ b/src/app/i18n/notifications/locales/fr.json @@ -0,0 +1,25 @@ +{ + "chatInitializationFailed": "Impossible d’initialiser le chat : {error}", + "linkCopied": "Lien copié dans le presse-papiers.", + "clipboardMissingDesignData": "Le presse-papiers ne contient aucune donnée de design.", + "clipboardAccessBlocked": "L’accès au presse-papiers est bloqué dans ce contexte de navigateur.", + "copiedAs": "Copié au format {format}.", + "pngClipboardUnavailable": "L’export PNG vers le presse-papiers n’est pas disponible dans ce navigateur.", + "openFileFailed": "Impossible d’ouvrir « {name} » : {error}", + "importedDOMCSS": "Document DOM/CSS importé.", + "importDOMCSSFailed": "Impossible d’importer le DOM/CSS : {error}", + "openDOMCSSFailed": "Impossible d’ouvrir le fichier DOM/CSS : {error}", + "vectorizeCredentialRequired": "Ajoutez une clé API {provider} dans Réglages → Médias.", + "vectorizeImageMissing": "Les données d’image de ce calque sont manquantes.", + "vectorizingImage": "Vectorisation de l’image…", + "imageConvertedToVectors": "Image convertie en vecteurs.", + "vectorizeCredentialFailed": "{error}. Mettez-la à jour dans Réglages → Médias.", + "vectorizeFailed": "{provider} n’a pas pu vectoriser cette image : {error}", + "operationFailed": "Échec de l’opération : {error}", + "storageConnected": "Connexion établie. L’espace de stockage est prêt.", + "storageConnectionFailed": "Impossible de se connecter au stockage : {error}", + "nodeID": "ID de nœud", + "nodeIDs": "ID de nœuds", + "xPath": "XPath", + "xPaths": "XPaths" +} diff --git a/src/app/i18n/notifications/locales/it.json b/src/app/i18n/notifications/locales/it.json new file mode 100644 index 000000000..3ab498cd1 --- /dev/null +++ b/src/app/i18n/notifications/locales/it.json @@ -0,0 +1,25 @@ +{ + "chatInitializationFailed": "Impossibile inizializzare la chat: {error}", + "linkCopied": "Link copiato negli appunti.", + "clipboardMissingDesignData": "Gli appunti non contengono dati di design.", + "clipboardAccessBlocked": "L’accesso agli appunti è bloccato in questo contesto del browser.", + "copiedAs": "Copiato come {format}.", + "pngClipboardUnavailable": "L’esportazione PNG negli appunti non è disponibile in questo browser.", + "openFileFailed": "Impossibile aprire «{name}»: {error}", + "importedDOMCSS": "Documento DOM/CSS importato.", + "importDOMCSSFailed": "Impossibile importare DOM/CSS: {error}", + "openDOMCSSFailed": "Impossibile aprire il file DOM/CSS: {error}", + "vectorizeCredentialRequired": "Aggiungi una chiave API {provider} in Impostazioni → Media.", + "vectorizeImageMissing": "Mancano i dati immagine per questo livello.", + "vectorizingImage": "Vettorializzazione dell’immagine…", + "imageConvertedToVectors": "Immagine convertita in vettori.", + "vectorizeCredentialFailed": "{error}. Aggiornala in Impostazioni → Media.", + "vectorizeFailed": "{provider} non ha potuto vettorializzare questa immagine: {error}", + "operationFailed": "Operazione non riuscita: {error}", + "storageConnected": "Connessione riuscita. Lo spazio di archiviazione è pronto.", + "storageConnectionFailed": "Impossibile connettersi all’archiviazione: {error}", + "nodeID": "ID nodo", + "nodeIDs": "ID nodi", + "xPath": "XPath", + "xPaths": "XPath" +} diff --git a/src/app/i18n/notifications/locales/ja.json b/src/app/i18n/notifications/locales/ja.json new file mode 100644 index 000000000..45f22396a --- /dev/null +++ b/src/app/i18n/notifications/locales/ja.json @@ -0,0 +1,25 @@ +{ + "chatInitializationFailed": "チャットを初期化できませんでした: {error}", + "linkCopied": "リンクをクリップボードにコピーしました。", + "clipboardMissingDesignData": "クリップボードにデザインデータがありません。", + "clipboardAccessBlocked": "このブラウザー環境ではクリップボードへのアクセスがブロックされています。", + "copiedAs": "{format} としてコピーしました。", + "pngClipboardUnavailable": "このブラウザーでは PNG をクリップボードに書き出せません。", + "openFileFailed": "「{name}」を開けませんでした: {error}", + "importedDOMCSS": "DOM/CSS ドキュメントをインポートしました。", + "importDOMCSSFailed": "DOM/CSS をインポートできませんでした: {error}", + "openDOMCSSFailed": "DOM/CSS ファイルを開けませんでした: {error}", + "vectorizeCredentialRequired": "設定 → メディアで {provider} の API キーを追加してください。", + "vectorizeImageMissing": "このレイヤーの画像データがありません。", + "vectorizingImage": "画像をベクター化しています…", + "imageConvertedToVectors": "画像をベクターに変換しました。", + "vectorizeCredentialFailed": "{error}。設定 → メディアで更新してください。", + "vectorizeFailed": "{provider} でこの画像をベクター化できませんでした: {error}", + "operationFailed": "操作に失敗しました: {error}", + "storageConnected": "接続しました。ストレージ名前空間を使用できます。", + "storageConnectionFailed": "ストレージに接続できませんでした: {error}", + "nodeID": "ノード ID", + "nodeIDs": "ノード ID", + "xPath": "XPath", + "xPaths": "XPath" +} diff --git a/src/app/i18n/notifications/locales/pl.json b/src/app/i18n/notifications/locales/pl.json new file mode 100644 index 000000000..ae6ea80f7 --- /dev/null +++ b/src/app/i18n/notifications/locales/pl.json @@ -0,0 +1,25 @@ +{ + "chatInitializationFailed": "Nie udało się uruchomić czatu: {error}", + "linkCopied": "Link skopiowano do schowka.", + "clipboardMissingDesignData": "Schowek nie zawiera danych projektu.", + "clipboardAccessBlocked": "Dostęp do schowka jest zablokowany w tym kontekście przeglądarki.", + "copiedAs": "Skopiowano jako {format}.", + "pngClipboardUnavailable": "Eksport PNG do schowka nie jest dostępny w tej przeglądarce.", + "openFileFailed": "Nie udało się otworzyć „{name}”: {error}", + "importedDOMCSS": "Zaimportowano dokument DOM/CSS.", + "importDOMCSSFailed": "Nie udało się zaimportować DOM/CSS: {error}", + "openDOMCSSFailed": "Nie udało się otworzyć pliku DOM/CSS: {error}", + "vectorizeCredentialRequired": "Dodaj klucz API {provider} w Ustawienia → Multimedia.", + "vectorizeImageMissing": "Brakuje danych obrazu dla tej warstwy.", + "vectorizingImage": "Wektoryzowanie obrazu…", + "imageConvertedToVectors": "Obraz przekonwertowano na wektory.", + "vectorizeCredentialFailed": "{error}. Zaktualizuj klucz w Ustawienia → Multimedia.", + "vectorizeFailed": "{provider} nie mógł zwektoryzować tego obrazu: {error}", + "operationFailed": "Operacja nie powiodła się: {error}", + "storageConnected": "Połączono. Przestrzeń przechowywania jest gotowa.", + "storageConnectionFailed": "Nie udało się połączyć z magazynem: {error}", + "nodeID": "ID węzła", + "nodeIDs": "ID węzłów", + "xPath": "XPath", + "xPaths": "XPath" +} diff --git a/src/app/i18n/notifications/locales/ru.json b/src/app/i18n/notifications/locales/ru.json new file mode 100644 index 000000000..9cbe51832 --- /dev/null +++ b/src/app/i18n/notifications/locales/ru.json @@ -0,0 +1,25 @@ +{ + "chatInitializationFailed": "Не удалось запустить чат: {error}", + "linkCopied": "Ссылка скопирована в буфер обмена.", + "clipboardMissingDesignData": "В буфере обмена нет данных дизайна.", + "clipboardAccessBlocked": "Доступ к буферу обмена заблокирован в этом контексте браузера.", + "copiedAs": "Скопировано как {format}.", + "pngClipboardUnavailable": "Экспорт PNG в буфер обмена недоступен в этом браузере.", + "openFileFailed": "Не удалось открыть «{name}»: {error}", + "importedDOMCSS": "Документ DOM/CSS импортирован.", + "importDOMCSSFailed": "Не удалось импортировать DOM/CSS: {error}", + "openDOMCSSFailed": "Не удалось открыть файл DOM/CSS: {error}", + "vectorizeCredentialRequired": "Добавьте API-ключ {provider} в Настройки → Медиа.", + "vectorizeImageMissing": "Для этого слоя отсутствуют данные изображения.", + "vectorizingImage": "Векторизация изображения…", + "imageConvertedToVectors": "Изображение преобразовано в векторы.", + "vectorizeCredentialFailed": "{error}. Обновите ключ в Настройки → Медиа.", + "vectorizeFailed": "{provider} не смог векторизовать это изображение: {error}", + "operationFailed": "Не удалось выполнить операцию: {error}", + "storageConnected": "Подключено. Пространство хранилища готово.", + "storageConnectionFailed": "Не удалось подключиться к хранилищу: {error}", + "nodeID": "ID узла", + "nodeIDs": "ID узлов", + "xPath": "XPath", + "xPaths": "XPath" +} diff --git a/src/app/i18n/notifications/locales/zh-cn.json b/src/app/i18n/notifications/locales/zh-cn.json new file mode 100644 index 000000000..1716b4b72 --- /dev/null +++ b/src/app/i18n/notifications/locales/zh-cn.json @@ -0,0 +1,25 @@ +{ + "chatInitializationFailed": "无法初始化聊天:{error}", + "linkCopied": "链接已复制到剪贴板。", + "clipboardMissingDesignData": "剪贴板中没有设计数据。", + "clipboardAccessBlocked": "当前浏览器环境阻止了剪贴板访问。", + "copiedAs": "已复制为 {format}。", + "pngClipboardUnavailable": "此浏览器不支持将 PNG 导出到剪贴板。", + "openFileFailed": "无法打开“{name}”:{error}", + "importedDOMCSS": "已导入 DOM/CSS 文档。", + "importDOMCSSFailed": "无法导入 DOM/CSS:{error}", + "openDOMCSSFailed": "无法打开 DOM/CSS 文件:{error}", + "vectorizeCredentialRequired": "请在设置 → 媒体中添加 {provider} API 密钥。", + "vectorizeImageMissing": "此图层缺少图像数据。", + "vectorizingImage": "正在矢量化图像…", + "imageConvertedToVectors": "图像已转换为矢量。", + "vectorizeCredentialFailed": "{error}。请在设置 → 媒体中更新。", + "vectorizeFailed": "{provider} 无法矢量化此图像:{error}", + "operationFailed": "操作失败:{error}", + "storageConnected": "已连接。存储命名空间已就绪。", + "storageConnectionFailed": "无法连接到存储:{error}", + "nodeID": "节点 ID", + "nodeIDs": "节点 ID", + "xPath": "XPath", + "xPaths": "XPath" +} diff --git a/src/app/shell/menu/files.ts b/src/app/shell/menu/files.ts index 5fa99bbce..bacea5547 100644 --- a/src/app/shell/menu/files.ts +++ b/src/app/shell/menu/files.ts @@ -2,6 +2,7 @@ import { useFileDialog } from '@vueuse/core' import { setOpenPencilOpenFileHandler } from '@/app/browser-bridge' import { resolveBrowserFileURL } from '@/app/document/io/browser' +import { notificationMessages } from '@/app/i18n/notifications' import { toast } from '@/app/shell/ui' import { openFileInNewTab } from '@/app/tabs' import { isTauri } from '@/app/tauri/env' @@ -45,7 +46,7 @@ export async function openDesignFileBatch( const name = displayName(item) const detail = error instanceof Error ? error.message : String(error) console.error(`Failed to open ${name}:`, error) - toast.error(`Failed to open ${name}: ${detail}`) + toast.error(notificationMessages.get().openFileFailed({ name, error: detail })) } } } diff --git a/src/components/ChatPanel.vue b/src/components/ChatPanel.vue index c30225287..f3417b2a0 100644 --- a/src/components/ChatPanel.vue +++ b/src/components/ChatPanel.vue @@ -33,6 +33,8 @@ import { useAIChat } from '@/app/ai/chat/use' import { toast } from '@/app/shell/ui' import { useI18n } from '@open-pencil/vue' +import { useNotificationMessages } from '@/app/i18n/notifications' + import type { Chat } from '@ai-sdk/vue' import type { UIMessage } from 'ai' import type { JSONObject } from '@open-pencil/scene-graph/primitives' @@ -42,6 +44,7 @@ const IS_DEV = import.meta.env.DEV const { isConfigured, ensureChat, resetChat, chatFailure, clearChatFailure } = useAIChat() const { copy } = useClipboard() const { dialogs } = useI18n() +const notifications = useNotificationMessages() const chat = ref | null>(null) const isPreparingImages = ref(false) @@ -53,7 +56,11 @@ void ensureChat() return undefined }) .catch((error: unknown) => { - toast.error(error instanceof Error ? error.message : 'Failed to initialize chat') + toast.error( + notifications.value.chatInitializationFailed({ + error: error instanceof Error ? error.message : String(error) + }) + ) }) const messagesEnd = ref() const debugCopied = refAutoReset(false, 1500) diff --git a/src/components/CollabPanel/context.ts b/src/components/CollabPanel/context.ts index c3194aac5..053cabb03 100644 --- a/src/components/CollabPanel/context.ts +++ b/src/components/CollabPanel/context.ts @@ -6,6 +6,7 @@ import { useRoute, useRouter } from 'vue-router' import { useI18n } from '@open-pencil/vue' import { DEFAULT_COLLAB_STATE, useCollabInjected } from '@/app/collab/use' +import { useNotificationMessages } from '@/app/i18n/notifications' import { toast } from '@/app/shell/ui' import { getShareURL } from '@/constants' @@ -15,6 +16,7 @@ function createCollabPanelContext() { const collab = useCollabInjected() const { copy, copied } = useClipboard({ copiedDuring: 2000 }) const { dialogs } = useI18n() + const notifications = useNotificationMessages() const joinInput = ref('') const nameDraft = ref(collab?.state.value.localName ?? '') @@ -42,7 +44,7 @@ function createCollabPanelContext() { function copyLink() { if (!shareURL.value) return void copy(shareURL.value) - toast.info('Link copied to clipboard') + toast.info(notifications.value.linkCopied) } function share() { @@ -51,7 +53,7 @@ function createCollabPanelContext() { const roomId = collab.shareCurrentDoc() void router.push(`/share/${roomId}`) void copy(getShareURL(roomId)) - toast.info('Link copied to clipboard') + toast.info(notifications.value.linkCopied) popoverOpen.value = false } diff --git a/src/components/MobileHud/context.ts b/src/components/MobileHud/context.ts index 48deecc39..e30dd28e4 100644 --- a/src/components/MobileHud/context.ts +++ b/src/components/MobileHud/context.ts @@ -13,6 +13,7 @@ import { useEditorCommands, useI18n } from '@open-pencil/vue' import { DEFAULT_COLLAB_STATE, useCollabInjected } from '@/app/collab/use' import { useEditorStore } from '@/app/editor/active-store' import { toolIcons } from '@/app/editor/icons' +import { useNotificationMessages } from '@/app/i18n/notifications' import { openFileDialog } from '@/app/shell/menu/use' import { toast } from '@/app/shell/ui' import type { ToolbarActionItem } from '@/components/Toolbar/types' @@ -26,6 +27,7 @@ function createMobileHudContext() { const store = useEditorStore() const { copy } = useClipboard() const { dialogs } = useI18n() + const notifications = useNotificationMessages() const { getCommand } = useEditorCommands() const collabState = computed(() => collab?.state.value ?? DEFAULT_COLLAB_STATE) @@ -60,7 +62,7 @@ function createMobileHudContext() { const roomId = collab.shareCurrentDoc() void router.push(`/share/${roomId}`) void copy(getShareURL(roomId)) - toast.info('Link copied to clipboard') + toast.info(notifications.value.linkCopied) } function disconnect() { diff --git a/src/components/libraries/review/LibraryUpdateReviewDialog.vue b/src/components/libraries/review/LibraryUpdateReviewDialog.vue index d5212aa54..b8c514667 100644 --- a/src/components/libraries/review/LibraryUpdateReviewDialog.vue +++ b/src/components/libraries/review/LibraryUpdateReviewDialog.vue @@ -5,6 +5,8 @@ import { computed, ref, shallowRef, watch } from 'vue' import { createLibraryUpdatePreview, type LibraryUpdatePreview } from '@open-pencil/core/library' import { useI18n } from '@open-pencil/vue' +import { notificationMessages } from '@/app/i18n/notifications' + import { useEditorStore } from '@/app/editor/active-store' import { closeLibraryReview, libraryReviewRequest, useLibraryService } from '@/app/libraries' import { toast } from '@/app/shell/ui' @@ -31,6 +33,10 @@ interface ReviewOrigin { let origin: ReviewOrigin | null = null let accepted = false let requestId = 0 +const operationFailed = (cause: unknown) => + notificationMessages.get().operationFailed({ + error: cause instanceof Error ? cause.message : String(cause) + }) const open = computed({ get: () => libraryReviewRequest.value !== null, set: (value) => { @@ -77,7 +83,7 @@ async function loadPreview() { await focusCurrentInstance() } } catch (cause) { - toast.error(cause instanceof Error ? cause.message : String(cause)) + toast.error(operationFailed(cause)) } finally { if (currentRequest === requestId) loading.value = false } @@ -104,7 +110,7 @@ async function updateInstance() { accepted = true closeLibraryReview() } catch (cause) { - toast.error(cause instanceof Error ? cause.message : String(cause)) + toast.error(operationFailed(cause)) } finally { applying.value = false } @@ -125,7 +131,7 @@ async function updateAll() { accepted = true closeLibraryReview() } catch (cause) { - toast.error(cause instanceof Error ? cause.message : String(cause)) + toast.error(operationFailed(cause)) applying.value = false } } diff --git a/src/components/libraries/useLibraryManager.ts b/src/components/libraries/useLibraryManager.ts index d9306bbc9..a69d0c762 100644 --- a/src/components/libraries/useLibraryManager.ts +++ b/src/components/libraries/useLibraryManager.ts @@ -1,6 +1,7 @@ import { computed, ref, watch, type Ref } from 'vue' import type { EditorStore } from '@/app/editor/session' +import { notificationMessages } from '@/app/i18n/notifications' import { createActiveStorageAdapter, storagePreferencesComplete } from '@/app/integrations/storage' import { activeStorageProviderID } from '@/app/integrations/storage/preferences' import { @@ -13,6 +14,10 @@ import type { LibraryAssetUpdateGroup } from '@/app/libraries/update-groups' import { scopeLibraryUpdateGroups } from '@/app/libraries/update-groups' import { toast } from '@/app/shell/ui' +function errorDetail(cause: unknown): string { + return cause instanceof Error ? cause.message : String(cause) +} + export function useLibraryManager( open: Ref, editor: EditorStore, @@ -37,7 +42,7 @@ export function useLibraryManager( if (request === requestId) updateGroups.value = groups } catch (cause) { if (request === requestId) { - toast.error(cause instanceof Error ? cause.message : String(cause)) + toast.error(notificationMessages.get().operationFailed({ error: errorDetail(cause) })) } } finally { if (request === requestId) loading.value = false @@ -48,7 +53,7 @@ export function useLibraryManager( try { await operation() } catch (cause) { - toast.error(cause instanceof Error ? cause.message : String(cause)) + toast.error(notificationMessages.get().operationFailed({ error: errorDetail(cause) })) } } diff --git a/src/components/properties/component-properties/instance-update/use.ts b/src/components/properties/component-properties/instance-update/use.ts index 40a622b47..c8911d8f4 100644 --- a/src/components/properties/component-properties/instance-update/use.ts +++ b/src/components/properties/component-properties/instance-update/use.ts @@ -4,6 +4,7 @@ import { libraryAssetKeyForComponent } from '@open-pencil/core/library' import type { SceneNode } from '@open-pencil/scene-graph' import type { EditorStore } from '@/app/editor/session' +import { notificationMessages } from '@/app/i18n/notifications' import type { LibraryService } from '@/app/libraries' import { toast } from '@/app/shell/ui' @@ -42,7 +43,11 @@ export function useInstanceUpdate( await service.applyInstanceUpdate(editor, instance.id) available.value = false } catch (cause) { - toast.error(cause instanceof Error ? cause.message : String(cause)) + toast.error( + notificationMessages.get().operationFailed({ + error: cause instanceof Error ? cause.message : String(cause) + }) + ) } finally { updating.value = false } diff --git a/src/components/recovery/RecoveryDialog.vue b/src/components/recovery/RecoveryDialog.vue index 413ecafaf..082e1060f 100644 --- a/src/components/recovery/RecoveryDialog.vue +++ b/src/components/recovery/RecoveryDialog.vue @@ -4,6 +4,7 @@ import { useRoute } from 'vue-router' import { AlertDialogCancel, AlertDialogDescription, AlertDialogTitle } from 'reka-ui' import { useI18n } from '@open-pencil/vue' +import { useNotificationMessages } from '@/app/i18n/notifications' import { discardRecoverySnapshot, listRecoverySnapshots, restoreRecoverySnapshot } from '@/app/tabs' import type { RecoverySnapshotMeta } from '@/app/document/recovery' import { formatStorageBytes } from '@/app/storage/format-bytes' @@ -11,6 +12,7 @@ import { toast } from '@/app/shell/ui' import { AppAlertDialogRoot, AppDialogBody, AppDialogFooter } from '@/components/ui/dialog' const { dialogs } = useI18n() +const notifications = useNotificationMessages() const route = useRoute() const snapshots = ref([]) const busyId = ref(null) @@ -30,7 +32,11 @@ async function restore(snapshot: RecoverySnapshotMeta): Promise { snapshots.value = snapshots.value.filter((candidate) => candidate.id !== snapshot.id) if (snapshots.value.length === 0) open.value = false } catch (error) { - toast.error(error instanceof Error ? error.message : dialogs.value.recoveryFailed) + toast.error( + notifications.value.operationFailed({ + error: error instanceof Error ? error.message : dialogs.value.recoveryFailed + }) + ) } finally { busyId.value = null } diff --git a/src/components/settings/storage/StorageSettingsPanel.vue b/src/components/settings/storage/StorageSettingsPanel.vue index f700f581e..4b4e7da53 100644 --- a/src/components/settings/storage/StorageSettingsPanel.vue +++ b/src/components/settings/storage/StorageSettingsPanel.vue @@ -3,6 +3,8 @@ import { computed, onMounted, ref, watch } from 'vue' import { useRouter } from 'vue-router' import { useI18n } from '@open-pencil/vue' +import { useNotificationMessages } from '@/app/i18n/notifications' + import { activeStorageProviderID, createActiveStorageAdapter, @@ -21,6 +23,7 @@ import { resumeStorageSync } from '@/app/storage/sync' import AppInput from '@/components/ui/AppInput.vue' const { dialogs } = useI18n() +const notifications = useNotificationMessages() const router = useRouter() const provider = computed(() => storageProviderRegistry.get(activeStorageProviderID.value)) const preferenceDrafts = ref>({ @@ -90,10 +93,14 @@ async function testConnection(): Promise { } await resumeStorageSync() const connection = await createActiveStorageAdapter(provider.value.id).testConnection() - if (connection.ok) toast.info(connection.message) - else toast.error(connection.message) + if (connection.ok) toast.info(notifications.value.storageConnected) + else toast.error(notifications.value.storageConnectionFailed({ error: connection.message })) } catch (error) { - toast.error(error instanceof Error ? error.message : String(error)) + toast.error( + notifications.value.storageConnectionFailed({ + error: error instanceof Error ? error.message : String(error) + }) + ) } finally { busy.value = false } diff --git a/tests/engine/app/shell/menu/file-batch.test.ts b/tests/engine/app/shell/menu/file-batch.test.ts index ac1d71a99..8d7035973 100644 --- a/tests/engine/app/shell/menu/file-batch.test.ts +++ b/tests/engine/app/shell/menu/file-batch.test.ts @@ -1,10 +1,13 @@ import { afterEach, describe, expect, mock, test } from 'bun:test' +import { localeSetting } from '@open-pencil/vue' + import { openDesignFileBatch } from '@/app/shell/menu/files' import { toast } from '@/app/shell/ui' afterEach(() => { toast.toasts.value = [] + localeSetting.set(undefined) }) describe('openDesignFileBatch', () => { @@ -21,6 +24,7 @@ describe('openDesignFileBatch', () => { }) test('reports one failed file and continues opening later selections', async () => { + localeSetting.set('en') const opened: string[] = [] const openItem = mock(async (name: string) => { opened.push(name) @@ -34,7 +38,7 @@ describe('openDesignFileBatch', () => { expect(opened).toEqual(['first.fig', 'broken.fig', 'last.pen']) expect(toast.toasts.value).toHaveLength(1) expect(toast.toasts.value[0]).toMatchObject({ - message: 'Failed to open broken.fig: Invalid FIG container', + message: 'Could not open “broken.fig”: Invalid FIG container', variant: 'error' }) })