diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f52cc13d..8a547227f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ - Add explicit shared/view editor-state ownership and canvas render-state hooks as a foundation for independent same-document canvas panes. - Show Figma-style temporary distance measurements between selected and Option/Alt-hovered layers. (#491) +- Add a reproducible Dev Container for web, package, CLI, and non-browser test development. +- Add local crash recovery for unsaved and pathless documents, including MCP-created documents. (#487) +- Add isolated visual inspection that sends bounded selection renders to the configured Vision model and returns text findings without retaining image data in Design chat history. (#232, #471) +- Add image attachments to AI chat with bounded analysis, immediate transcript thumbnails, hover previews, and click-to-view images. (#232) +- Add a single CodeMirror editor for live Design JSX and HTML/CSS canvas previews, with Tailwind JSX viewing, completion, diagnostics, line numbers, bounded execution, and session-level undo. (#130) +- Allow supported AI model profiles to set a provider-specific reasoning effort. (#454) +- Show unavailable or substituted document fonts with affected-layer selection and retry actions, and expose font fidelity through the Figma API and MCP tooling. (#503) - Add reusable remote MCP connections for ACP agents, with Streamable HTTP endpoints and bearer tokens stored in the configured credential backend. - Author multidimensional component variants in the Design panel, including property/value renaming, sparse-combination diagnostics, variant duplication, and exact instance transitions. (#239) - Create deterministic, dependency-complete component-library revisions through the provider-neutral Core library catalog, with browser IndexedDB persistence for local catalogs. (#239) diff --git a/bun.lock b/bun.lock index bf108851f..cbcf8bc7f 100644 --- a/bun.lock +++ b/bun.lock @@ -14,6 +14,15 @@ "@atlaskit/pragmatic-drag-and-drop": "^1.7.9", "@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.1.0", "@chenglou/pretext": "^0.0.7", + "@codemirror/autocomplete": "^6.20.3", + "@codemirror/commands": "^6.10.4", + "@codemirror/lang-html": "^6.4.11", + "@codemirror/lang-javascript": "^6.2.5", + "@codemirror/language": "^6.12.4", + "@codemirror/lint": "^6.9.7", + "@codemirror/search": "^6.7.1", + "@codemirror/state": "^6.5.4", + "@codemirror/view": "^6.43.8", "@open-pencil/cli": "workspace:*", "@open-pencil/core": "workspace:*", "@open-pencil/dom-css": "workspace:*", @@ -49,7 +58,6 @@ "lib0": "^0.2.117", "motion-v": "^2.0.0", "opentype.js": "^2.0.0", - "refractor": "^5.0.0", "reka-ui": "^2.9.0", "svg2pdf.js": "^2.7.0", "tailwind-merge": "^3.5.0", @@ -113,7 +121,7 @@ "unplugin-raw": "^0.6.4", "unplugin-vue": "^7.1.1", "unplugin-vue-components": "^31.0.0", - "vite": "^8.0.0", + "vite": "8.1.4", "vite-plugin-pwa": "^1.2.0", "vue-tsc": "^3.2.5", "workbox-window": "^7.4.0", @@ -571,6 +579,26 @@ "@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=="], + "@codemirror/autocomplete": ["@codemirror/autocomplete@6.20.3", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0" } }, "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g=="], + + "@codemirror/commands": ["@codemirror/commands@6.10.4", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.7.0", "@codemirror/view": "^6.27.0", "@lezer/common": "^1.1.0" } }, "sha512-Ryk9y9T0FFVF0cUGhAknveAyUOl/A1qReTFi+qPKtOh2Z9F4AUBz3XOrYD4ZEgZirdugVzHvd/2/Wcwy5OliTg=="], + + "@codemirror/lang-css": ["@codemirror/lang-css@6.3.1", "", { "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@lezer/common": "^1.0.2", "@lezer/css": "^1.1.7" } }, "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg=="], + + "@codemirror/lang-html": ["@codemirror/lang-html@6.4.12", "", { "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/lang-css": "^6.0.0", "@codemirror/lang-javascript": "^6.0.0", "@codemirror/language": "^6.4.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0", "@lezer/css": "^1.1.0", "@lezer/html": "^1.3.12" } }, "sha512-pw2ReWKUqSkbvh76RAT4NYxiogRu+PWkR2ukAwO9uOgrm8uipkzjtKKtNpyeAQwHOqxEeSvAXZ6vr3AfyB9y/w=="], + + "@codemirror/lang-javascript": ["@codemirror/lang-javascript@6.2.5", "", { "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/language": "^6.6.0", "@codemirror/lint": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0", "@lezer/javascript": "^1.0.0" } }, "sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A=="], + + "@codemirror/language": ["@codemirror/language@6.12.4", "", { "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", "@lezer/common": "^1.5.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0", "style-mod": "^4.0.0" } }, "sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A=="], + + "@codemirror/lint": ["@codemirror/lint@6.9.7", "", { "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.42.0", "crelt": "^1.0.5" } }, "sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg=="], + + "@codemirror/search": ["@codemirror/search@6.7.1", "", { "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.37.0", "crelt": "^1.0.5" } }, "sha512-uMe5UO6PamJtSHrXhhHOzSX3ReWtiJrva6GnPMwSOrZtiExb5X5eExhr2OUZQVvdxPsKpY3Ro2mFbQadpPWmHA=="], + + "@codemirror/state": ["@codemirror/state@6.7.1", "", { "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } }, "sha512-9QzNDgE4EYDnAHfrTlR2lwiPciiOymLtwKK+8yHQzCc7GXhAP9xdEbEJFy2IWB1j9UGUl9BsgMmTo/ImA02T7A=="], + + "@codemirror/view": ["@codemirror/view@6.43.8", "", { "dependencies": { "@codemirror/state": "^6.7.0", "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, "sha512-qtItTDssZ/5GFfi94hrILu9j/VUeFPDPkhovEfmWFj2ipTxnzPB8DdHgfbb8HYTzLTYhrndKmyQxXUz/PDLenw=="], + "@colors/colors": ["@colors/colors@1.5.0", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="], "@dependents/detective-less": ["@dependents/detective-less@5.0.3", "", { "dependencies": { "gonzales-pe": "^4.3.0", "node-source-walk": "^7.0.1" } }, "sha512-v6oD9Ukp+N7V4n6p5I/+mM5fIohSfkrDSGlFm5w/pYmchvbk+sMIHsLxrFJ5Lnujewj1BzWL0K84d88lwZAMQA=="], @@ -795,6 +823,18 @@ "@leichtgewicht/ip-codec": ["@leichtgewicht/ip-codec@2.0.5", "", {}, "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw=="], + "@lezer/common": ["@lezer/common@1.5.2", "", {}, "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ=="], + + "@lezer/css": ["@lezer/css@1.3.6", "", { "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.3.0" } }, "sha512-YJE78Wcg+zX8f10hiHWQ4Az48Qr/c13eId0VtRQYLBpxHDmDeSrXIlkbl+fJGW42rWC/uoUco9mhBZeVWP/A1g=="], + + "@lezer/highlight": ["@lezer/highlight@1.2.3", "", { "dependencies": { "@lezer/common": "^1.3.0" } }, "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g=="], + + "@lezer/html": ["@lezer/html@1.3.13", "", { "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0" } }, "sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg=="], + + "@lezer/javascript": ["@lezer/javascript@1.5.4", "", { "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.1.3", "@lezer/lr": "^1.3.0" } }, "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA=="], + + "@lezer/lr": ["@lezer/lr@1.4.10", "", { "dependencies": { "@lezer/common": "^1.0.0" } }, "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A=="], + "@libp2p/bootstrap": ["@libp2p/bootstrap@11.0.42", "", { "dependencies": { "@libp2p/interface": "^2.10.4", "@libp2p/interface-internal": "^2.3.17", "@libp2p/peer-id": "^5.1.7", "@multiformats/mafmt": "^12.1.6", "@multiformats/multiaddr": "^12.4.4", "main-event": "^1.0.1" } }, "sha512-xe5LMZrXR2cnFcR69ax/cHqNCc8zwSvqDWOf82u/ifAsUae7M0eeoEVi15lfrPewWst4YPkTqYDkZSPWC65I3Q=="], "@libp2p/crypto": ["@libp2p/crypto@5.1.13", "", { "dependencies": { "@libp2p/interface": "^3.1.0", "@noble/curves": "^2.0.1", "@noble/hashes": "^2.0.1", "multiformats": "^13.4.0", "protons-runtime": "^5.6.0", "uint8arraylist": "^2.4.8", "uint8arrays": "^5.1.0" } }, "sha512-8NN9cQP3jDn+p9+QE9ByiEoZ2lemDFf/unTgiKmS3JF93ph240EUVdbCyyEgOMfykzb0okTM4gzvwfx9osJebQ=="], @@ -827,6 +867,8 @@ "@loaderkit/resolve": ["@loaderkit/resolve@1.0.6", "", { "dependencies": { "@braidai/lang": "^1.0.0" } }, "sha512-G8FdIoF5CypfwmD9rl8BXod5HDn8JqB0CCNBXDTaRZ+yRYhARrrSToX1zg1zy9jX3zLqigsELwhT4gNtkdQAUg=="], + "@marijn/find-cluster-break": ["@marijn/find-cluster-break@1.0.3", "", {}, "sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA=="], + "@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=="], "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.28.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-gmloF+i+flI8ouQK7MWW4mOwuMh4RePBuPFAEPC6+pdqyWOUMDOixb6qZ69owLJpz6XmyllCouc4t8YWO+E2Nw=="], @@ -1321,8 +1363,6 @@ "@types/pako": ["@types/pako@2.0.4", "", {}, "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw=="], - "@types/prismjs": ["@types/prismjs@1.26.6", "", {}, "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw=="], - "@types/raf": ["@types/raf@3.4.3", "", {}, "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw=="], "@types/react": ["@types/react@19.2.17", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw=="], @@ -1599,8 +1639,6 @@ "character-parser": ["character-parser@2.2.0", "", { "dependencies": { "is-regex": "^1.0.3" } }, "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw=="], - "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="], - "check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="], "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], @@ -1661,6 +1699,8 @@ "cosmiconfig": ["cosmiconfig@9.0.1", "", { "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-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ=="], + "crelt": ["crelt@1.0.7", "", {}, "sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA=="], + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], "crypto-random-string": ["crypto-random-string@2.0.0", "", {}, "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA=="], @@ -1979,14 +2019,10 @@ "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], - "hast-util-parse-selector": ["hast-util-parse-selector@4.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A=="], - "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], - "hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], - "help-me": ["help-me@5.0.0", "", {}, "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg=="], "hey-listen": ["hey-listen@1.0.8", "", {}, "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q=="], @@ -2047,10 +2083,6 @@ "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], - "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], - - "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="], - "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], @@ -2069,8 +2101,6 @@ "is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="], - "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="], - "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], "is-electron": ["is-electron@2.2.2", "", {}, "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg=="], @@ -2089,8 +2119,6 @@ "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], - "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="], - "is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="], "is-loopback-addr": ["is-loopback-addr@2.0.2", "", {}, "sha512-26POf2KRCno/KTNL5Q0b/9TYnL00xEsSaLfiFRmjM7m7Lw7ZMmFybzzuX4CcsLAluZGd+niLUiMRxEooVE3aqg=="], @@ -2481,7 +2509,7 @@ "nanoevents": ["nanoevents@9.1.0", "", {}, "sha512-Jd0fILWG44a9luj8v5kED4WI+zfkkgwKyRQKItTtlPfEsh7Lznfi1kr8/iZ+XAIss4Qq5GqRB0qtWbaz9ceO/A=="], - "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "nanoid": ["nanoid@3.3.15", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA=="], "nanostores": ["nanostores@1.2.0", "", {}, "sha512-F0wCzbsH80G7XXo0Jd9/AVQC7ouWY6idUCTnMwW5t/Rv9W8qmO6endavDwg7TNp5GbugwSukFMVZqzPSrSMndg=="], @@ -2561,8 +2589,6 @@ "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], - "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], - "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], "parse5": ["parse5@8.0.1", "", { "dependencies": { "entities": "^8.0.0" } }, "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw=="], @@ -2611,7 +2637,7 @@ "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], - "postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="], + "postcss": ["postcss@8.5.16", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg=="], "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], @@ -2719,8 +2745,6 @@ "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="], - "refractor": ["refractor@5.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/prismjs": "^1.0.0", "hastscript": "^9.0.0", "parse-entities": "^4.0.0" } }, "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw=="], - "regenerate": ["regenerate@1.4.2", "", {}, "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A=="], "regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], @@ -2925,6 +2949,8 @@ "strip-json-comments": ["strip-json-comments@5.0.3", "", {}, "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw=="], + "style-mod": ["style-mod@4.1.3", "", {}, "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="], + "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=="], @@ -3133,7 +3159,7 @@ "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], - "vite": ["vite@8.0.3", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.12", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ=="], + "vite": ["vite@8.1.4", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.5", "postcss": "^8.5.16", "rolldown": "~1.1.4", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.3.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ=="], "vite-plugin-pwa": ["vite-plugin-pwa@1.2.0", "", { "dependencies": { "debug": "^4.3.6", "pretty-bytes": "^6.1.1", "tinyglobby": "^0.2.10", "workbox-build": "^7.4.0", "workbox-window": "^7.4.0" }, "peerDependencies": { "@vite-pwa/assets-generator": "^1.0.0", "vite": "^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["@vite-pwa/assets-generator"] }, "sha512-a2xld+SJshT9Lgcv8Ji4+srFJL4k/1bVbd1x06JIkvecpQkwkvCncD1+gSzcdm3s+owWLpMJerG3aN5jupJEVw=="], @@ -3165,6 +3191,8 @@ "vue-tsc": ["vue-tsc@3.2.6", "", { "dependencies": { "@volar/typescript": "2.4.28", "@vue/language-core": "3.2.6" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "bin/vue-tsc.js" } }, "sha512-gYW/kWI0XrwGzd0PKc7tVB/qpdeAkIZLNZb10/InizkQjHjnT8weZ/vBarZoj4kHKbUTZT/bAVgoOr8x4NsQ/Q=="], + "w3c-keyname": ["w3c-keyname@2.2.8", "", {}, "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="], + "walk-up-path": ["walk-up-path@4.0.0", "", {}, "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A=="], "weald": ["weald@1.1.1", "", { "dependencies": { "ms": "^3.0.0-canary.1", "supports-color": "^10.0.0" } }, "sha512-PaEQShzMCz8J/AD2N3dJMc1hTZWkJeLKS2NMeiVkV5KDHwgZe7qXLEzyodsT/SODxWDdXJJqocuwf3kHzcXhSQ=="], @@ -3423,6 +3451,8 @@ "@vue/compiler-sfc/@vue/shared": ["@vue/shared@3.5.31", "", {}, "sha512-nBxuiuS9Lj5bPkPbWogPUnjxxWpkRniX7e5UBQDWl6Fsf4roq9wwV+cR7ezQ4zXswNvPIlsdj1slcLB7XCsRAw=="], + "@vue/compiler-sfc/postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="], + "@vue/compiler-ssr/@vue/compiler-dom": ["@vue/compiler-dom@3.5.31", "", { "dependencies": { "@vue/compiler-core": "3.5.31", "@vue/shared": "3.5.31" } }, "sha512-BMY/ozS/xxjYqRFL+tKdRpATJYDTTgWSo0+AJvJNg4ig+Hgb0dOsHPXvloHQ5hmlivUqw1Yt2pPIqp4e0v1GUw=="], "@vue/compiler-ssr/@vue/shared": ["@vue/shared@3.5.31", "", {}, "sha512-nBxuiuS9Lj5bPkPbWogPUnjxxWpkRniX7e5UBQDWl6Fsf4roq9wwV+cR7ezQ4zXswNvPIlsdj1slcLB7XCsRAw=="], @@ -3523,8 +3553,6 @@ "p-queue/p-timeout": ["p-timeout@7.0.1", "", {}, "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg=="], - "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], - "parse5-htmlparser2-tree-adapter/parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="], "playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], @@ -3591,15 +3619,17 @@ "unplugin-vue/unplugin": ["unplugin@3.0.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg=="], + "unplugin-vue/vite": ["vite@8.0.3", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.12", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ=="], + "unplugin-vue-components/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], "unplugin-vue-components/obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="], "unplugin-vue-components/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], - "vite/rolldown": ["rolldown@1.0.0-rc.12", "", { "dependencies": { "@oxc-project/types": "=0.122.0", "@rolldown/pluginutils": "1.0.0-rc.12" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.12", "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", "@rolldown/binding-darwin-x64": "1.0.0-rc.12", "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A=="], + "vite/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], - "vite/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + "vite/rolldown": ["rolldown@1.1.5", "", { "dependencies": { "@oxc-project/types": "=0.139.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.1.5", "@rolldown/binding-darwin-arm64": "1.1.5", "@rolldown/binding-darwin-x64": "1.1.5", "@rolldown/binding-freebsd-x64": "1.1.5", "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", "@rolldown/binding-linux-arm64-gnu": "1.1.5", "@rolldown/binding-linux-arm64-musl": "1.1.5", "@rolldown/binding-linux-ppc64-gnu": "1.1.5", "@rolldown/binding-linux-s390x-gnu": "1.1.5", "@rolldown/binding-linux-x64-gnu": "1.1.5", "@rolldown/binding-linux-x64-musl": "1.1.5", "@rolldown/binding-openharmony-arm64": "1.1.5", "@rolldown/binding-wasm32-wasi": "1.1.5", "@rolldown/binding-win32-arm64-msvc": "1.1.5", "@rolldown/binding-win32-x64-msvc": "1.1.5" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA=="], "vite-plugin-pwa/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], @@ -3611,8 +3641,6 @@ "vitepress/shiki": ["shiki@4.3.1", "", { "dependencies": { "@shikijs/core": "4.3.1", "@shikijs/engine-javascript": "4.3.1", "@shikijs/engine-oniguruma": "4.3.1", "@shikijs/langs": "4.3.1", "@shikijs/themes": "4.3.1", "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oR+qDVi2OjX1tmDpyv+3KviX01KzO6Af+0NNnKnsp9491UEGz2YpxTuJboS/6VhYpTdqzmuJBuiTlrAWWJAssw=="], - "vitepress/vite": ["vite@8.1.4", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.5", "postcss": "^8.5.16", "rolldown": "~1.1.4", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.3.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ=="], - "vitepress-plugin-llms/markdown-it": ["markdown-it@14.1.1", "", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA=="], "vitepress-plugin-llms/pretty-bytes": ["pretty-bytes@7.1.0", "", {}, "sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw=="], @@ -3687,6 +3715,8 @@ "@vue/compiler-dom/@vue/compiler-core/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], + "@vue/compiler-sfc/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "@vueuse/integrations/@vueuse/core/@vueuse/metadata": ["@vueuse/metadata@14.3.0", "", {}, "sha512-BwxmbAzwAVF50+MW57GXOUEV61nFBGnlBvrTqj49PqWJu3uw7hdu72ztXeZ33RdZtDY6kO+bfCAE1PCn88Tktw=="], "@waku/enr/@libp2p/crypto/@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=="], @@ -3729,6 +3759,8 @@ "pptxgenjs/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "precinct/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "rolldown-plugin-dts/@babel/generator/@babel/types": ["@babel/types@8.0.0", "", { "dependencies": { "@babel/helper-string-parser": "^8.0.0", "@babel/helper-validator-identifier": "^8.0.0" } }, "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw=="], "rolldown-plugin-dts/@babel/parser/@babel/types": ["@babel/types@8.0.0", "", { "dependencies": { "@babel/helper-string-parser": "^8.0.0", "@babel/helper-validator-identifier": "^8.0.0" } }, "sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw=="], @@ -3783,39 +3815,45 @@ "unplugin-vue-components/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], - "vite/rolldown/@oxc-project/types": ["@oxc-project/types@0.122.0", "", {}, "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA=="], + "unplugin-vue/vite/postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="], - "vite/rolldown/@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.12", "", { "os": "android", "cpu": "arm64" }, "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA=="], + "unplugin-vue/vite/rolldown": ["rolldown@1.0.0-rc.12", "", { "dependencies": { "@oxc-project/types": "=0.122.0", "@rolldown/pluginutils": "1.0.0-rc.12" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.12", "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", "@rolldown/binding-darwin-x64": "1.0.0-rc.12", "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A=="], - "vite/rolldown/@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg=="], + "unplugin-vue/vite/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], - "vite/rolldown/@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw=="], + "vite/rolldown/@oxc-project/types": ["@oxc-project/types@0.139.0", "", {}, "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw=="], - "vite/rolldown/@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q=="], + "vite/rolldown/@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.1.5", "", { "os": "android", "cpu": "arm64" }, "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ=="], - "vite/rolldown/@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.12", "", { "os": "linux", "cpu": "arm" }, "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q=="], + "vite/rolldown/@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.1.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw=="], - "vite/rolldown/@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg=="], + "vite/rolldown/@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.1.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g=="], - "vite/rolldown/@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw=="], + "vite/rolldown/@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.1.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA=="], - "vite/rolldown/@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g=="], + "vite/rolldown/@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.1.5", "", { "os": "linux", "cpu": "arm" }, "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw=="], - "vite/rolldown/@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og=="], + "vite/rolldown/@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.1.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q=="], - "vite/rolldown/@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "x64" }, "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg=="], + "vite/rolldown/@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.1.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA=="], - "vite/rolldown/@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.12", "", { "os": "linux", "cpu": "x64" }, "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig=="], + "vite/rolldown/@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.1.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg=="], - "vite/rolldown/@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.12", "", { "os": "none", "cpu": "arm64" }, "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA=="], + "vite/rolldown/@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.1.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA=="], - "vite/rolldown/@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.12", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.1.1" }, "cpu": "none" }, "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg=="], + "vite/rolldown/@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.1.5", "", { "os": "linux", "cpu": "x64" }, "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ=="], - "vite/rolldown/@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q=="], + "vite/rolldown/@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.1.5", "", { "os": "linux", "cpu": "x64" }, "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg=="], - "vite/rolldown/@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.12", "", { "os": "win32", "cpu": "x64" }, "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw=="], + "vite/rolldown/@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.1.5", "", { "os": "none", "cpu": "arm64" }, "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw=="], - "vite/rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.12", "", {}, "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw=="], + "vite/rolldown/@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.1.5", "", { "dependencies": { "@emnapi/core": "1.11.1", "@emnapi/runtime": "1.11.1", "@napi-rs/wasm-runtime": "^1.1.6" }, "cpu": "none" }, "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA=="], + + "vite/rolldown/@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.1.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw=="], + + "vite/rolldown/@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.1.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA=="], + + "vite/rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], "vitepress-plugin-llms/markdown-it/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], @@ -3837,12 +3875,6 @@ "vitepress/shiki/@shikijs/themes": ["@shikijs/themes@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1" } }, "sha512-dgpoJ4WqNi2yTmizQHBJ5zcX6j2lE6icN/0yt4l1kkf16jrY/pwPLoTb1ETsWMz0OBLf9ZNvwmxft+cH+N9qSA=="], - "vitepress/vite/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], - - "vitepress/vite/postcss": ["postcss@8.5.16", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg=="], - - "vitepress/vite/rolldown": ["rolldown@1.1.5", "", { "dependencies": { "@oxc-project/types": "=0.139.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.1.5", "@rolldown/binding-darwin-arm64": "1.1.5", "@rolldown/binding-darwin-x64": "1.1.5", "@rolldown/binding-freebsd-x64": "1.1.5", "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", "@rolldown/binding-linux-arm64-gnu": "1.1.5", "@rolldown/binding-linux-arm64-musl": "1.1.5", "@rolldown/binding-linux-ppc64-gnu": "1.1.5", "@rolldown/binding-linux-s390x-gnu": "1.1.5", "@rolldown/binding-linux-x64-gnu": "1.1.5", "@rolldown/binding-linux-x64-musl": "1.1.5", "@rolldown/binding-openharmony-arm64": "1.1.5", "@rolldown/binding-wasm32-wasi": "1.1.5", "@rolldown/binding-win32-arm64-msvc": "1.1.5", "@rolldown/binding-win32-x64-msvc": "1.1.5" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA=="], - "vue-component-meta/@vue/language-core/@vue/compiler-dom": ["@vue/compiler-dom@3.5.34", "", { "dependencies": { "@vue/compiler-core": "3.5.34", "@vue/shared": "3.5.34" } }, "sha512-EbF/T++k0e2MMZlJsBhzK8Sgwt0HcIPOhzn1CTB/lv6sQcyk+OWf8YeiLxZp3ro7MbbLcAfAJ6sEvjFWuNgUCw=="], "vue-component-meta/@vue/language-core/@vue/shared": ["@vue/shared@3.5.34", "", {}, "sha512-24uqU4OIiX29ryC3MeWid/Xf2fa2EFRUVLb77nRhk+UrTVrh/XiGtFAFmJBAtBRbjwNdsPRP+jj/OL27Eg1NDA=="], @@ -3869,8 +3901,6 @@ "vue/@vue/compiler-sfc/@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.39", "", { "dependencies": { "@vue/compiler-dom": "3.5.39", "@vue/shared": "3.5.39" } }, "sha512-Ce7/wvwMHai74bdszfXExdazFigYnlF9zgCmEQUcM1j0fOymlouZ7XilTYNo8oUjhlnjYOZbGrcYKuqjz89Ucw=="], - "vue/@vue/compiler-sfc/postcss": ["postcss@8.5.16", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg=="], - "@open-pencil/docs/@tailwindcss/vite/@tailwindcss/node/enhanced-resolve": ["enhanced-resolve@5.21.6", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ=="], "@open-pencil/docs/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.2", "", { "os": "android", "cpu": "arm64" }, "sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA=="], @@ -3911,6 +3941,8 @@ "detective-vue2/@vue/compiler-sfc/@vue/compiler-core/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], + "detective-vue2/@vue/compiler-sfc/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "rolldown-plugin-dts/@babel/generator/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@8.0.0", "", {}, "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg=="], "rolldown-plugin-dts/@babel/parser/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@8.0.0", "", {}, "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg=="], @@ -3921,7 +3953,41 @@ "twoslash-vue/@vue/language-core/@vue/compiler-dom/@vue/compiler-core": ["@vue/compiler-core@3.5.34", "", { "dependencies": { "@babel/parser": "^7.29.3", "@vue/shared": "3.5.34", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-s9cLyK5mLcvZ4Agva5QgRsQyLKvts9WbU9DB6NqiZkkGEdwmcEiylj5Jbwkp680drF/NNCV8OlAJSe+yMLxaJw=="], - "vite/rolldown/@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" } }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="], + "unplugin-vue/vite/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "unplugin-vue/vite/rolldown/@oxc-project/types": ["@oxc-project/types@0.122.0", "", {}, "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA=="], + + "unplugin-vue/vite/rolldown/@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.12", "", { "os": "android", "cpu": "arm64" }, "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA=="], + + "unplugin-vue/vite/rolldown/@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg=="], + + "unplugin-vue/vite/rolldown/@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw=="], + + "unplugin-vue/vite/rolldown/@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q=="], + + "unplugin-vue/vite/rolldown/@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.12", "", { "os": "linux", "cpu": "arm" }, "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q=="], + + "unplugin-vue/vite/rolldown/@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg=="], + + "unplugin-vue/vite/rolldown/@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw=="], + + "unplugin-vue/vite/rolldown/@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g=="], + + "unplugin-vue/vite/rolldown/@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og=="], + + "unplugin-vue/vite/rolldown/@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "x64" }, "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg=="], + + "unplugin-vue/vite/rolldown/@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.12", "", { "os": "linux", "cpu": "x64" }, "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig=="], + + "unplugin-vue/vite/rolldown/@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.12", "", { "os": "none", "cpu": "arm64" }, "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA=="], + + "unplugin-vue/vite/rolldown/@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.12", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.1.1" }, "cpu": "none" }, "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg=="], + + "unplugin-vue/vite/rolldown/@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q=="], + + "unplugin-vue/vite/rolldown/@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.12", "", { "os": "win32", "cpu": "x64" }, "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw=="], + + "unplugin-vue/vite/rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.12", "", {}, "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw=="], "vitepress/@vue/devtools-api/@vue/devtools-kit/@vue/devtools-shared": ["@vue/devtools-shared@8.1.5", "", {}, "sha512-mhT4zcPFhF+Xk1O4BfhhrbXzpmfqY03fS6xGpcllbQG7lDjhQf8pQHcTIhqQIYx1hfwtHmk/6jM96ele0UxPqQ=="], @@ -3931,54 +3997,18 @@ "vitepress/shiki/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@4.3.6", "", { "dependencies": { "oniguruma-parser": "^0.12.2", "regex": "^6.1.0", "regex-recursion": "^6.0.2" } }, "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA=="], - "vitepress/vite/postcss/nanoid": ["nanoid@3.3.15", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA=="], - - "vitepress/vite/rolldown/@oxc-project/types": ["@oxc-project/types@0.139.0", "", {}, "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw=="], - - "vitepress/vite/rolldown/@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.1.5", "", { "os": "android", "cpu": "arm64" }, "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ=="], - - "vitepress/vite/rolldown/@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.1.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw=="], - - "vitepress/vite/rolldown/@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.1.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g=="], - - "vitepress/vite/rolldown/@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.1.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA=="], - - "vitepress/vite/rolldown/@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.1.5", "", { "os": "linux", "cpu": "arm" }, "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw=="], - - "vitepress/vite/rolldown/@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.1.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q=="], - - "vitepress/vite/rolldown/@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.1.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA=="], - - "vitepress/vite/rolldown/@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.1.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg=="], - - "vitepress/vite/rolldown/@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.1.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA=="], - - "vitepress/vite/rolldown/@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.1.5", "", { "os": "linux", "cpu": "x64" }, "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ=="], - - "vitepress/vite/rolldown/@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.1.5", "", { "os": "linux", "cpu": "x64" }, "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg=="], - - "vitepress/vite/rolldown/@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.1.5", "", { "os": "none", "cpu": "arm64" }, "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw=="], - - "vitepress/vite/rolldown/@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.1.5", "", { "dependencies": { "@emnapi/core": "1.11.1", "@emnapi/runtime": "1.11.1", "@napi-rs/wasm-runtime": "^1.1.6" }, "cpu": "none" }, "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA=="], - - "vitepress/vite/rolldown/@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.1.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw=="], - - "vitepress/vite/rolldown/@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.1.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA=="], - - "vitepress/vite/rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], - "vue-component-meta/@vue/language-core/@vue/compiler-dom/@vue/compiler-core": ["@vue/compiler-core@3.5.34", "", { "dependencies": { "@babel/parser": "^7.29.3", "@vue/shared": "3.5.34", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-s9cLyK5mLcvZ4Agva5QgRsQyLKvts9WbU9DB6NqiZkkGEdwmcEiylj5Jbwkp680drF/NNCV8OlAJSe+yMLxaJw=="], "vue-docgen-api/@vue/compiler-dom/@vue/compiler-core/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], "vue-docgen-api/@vue/compiler-sfc/@vue/compiler-core/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], + "vue-docgen-api/@vue/compiler-sfc/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "vue/@vue/compiler-sfc/@babel/parser/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], "vue/@vue/compiler-sfc/@vue/compiler-core/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], - "vue/@vue/compiler-sfc/postcss/nanoid": ["nanoid@3.3.15", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA=="], - "@open-pencil/docs/@tailwindcss/vite/@tailwindcss/node/enhanced-resolve/tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], "@open-pencil/docs/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="], @@ -4003,11 +4033,7 @@ "twoslash-vue/@vue/language-core/@vue/compiler-dom/@vue/compiler-core/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], - "vite/rolldown/@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/core": ["@emnapi/core@1.9.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" } }, "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA=="], - - "vite/rolldown/@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/runtime": ["@emnapi/runtime@1.9.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="], - - "vite/rolldown/@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + "unplugin-vue/vite/rolldown/@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" } }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="], "vue-component-meta/@vue/language-core/@vue/compiler-dom/@vue/compiler-core/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], @@ -4015,6 +4041,12 @@ "vue/@vue/compiler-sfc/@babel/parser/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - "vite/rolldown/@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg=="], + "unplugin-vue/vite/rolldown/@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/core": ["@emnapi/core@1.9.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" } }, "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA=="], + + "unplugin-vue/vite/rolldown/@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/runtime": ["@emnapi/runtime@1.9.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="], + + "unplugin-vue/vite/rolldown/@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + + "unplugin-vue/vite/rolldown/@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg=="], } } diff --git a/package.json b/package.json index 8f2022002..307347ca2 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,15 @@ "@atlaskit/pragmatic-drag-and-drop": "^1.7.9", "@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.1.0", "@chenglou/pretext": "^0.0.7", + "@codemirror/autocomplete": "^6.20.3", + "@codemirror/commands": "^6.10.4", + "@codemirror/lang-html": "^6.4.11", + "@codemirror/lang-javascript": "^6.2.5", + "@codemirror/language": "^6.12.4", + "@codemirror/lint": "^6.9.7", + "@codemirror/search": "^6.7.1", + "@codemirror/state": "^6.5.4", + "@codemirror/view": "^6.43.8", "@open-pencil/cli": "workspace:*", "@open-pencil/core": "workspace:*", "@open-pencil/dom-css": "workspace:*", @@ -105,7 +114,6 @@ "lib0": "^0.2.117", "motion-v": "^2.0.0", "opentype.js": "^2.0.0", - "refractor": "^5.0.0", "reka-ui": "^2.9.0", "svg2pdf.js": "^2.7.0", "tailwind-merge": "^3.5.0", @@ -169,7 +177,7 @@ "unplugin-raw": "^0.6.4", "unplugin-vue": "^7.1.1", "unplugin-vue-components": "^31.0.0", - "vite": "^8.0.0", + "vite": "8.1.4", "vite-plugin-pwa": "^1.2.0", "vue-tsc": "^3.2.5", "workbox-window": "^7.4.0", diff --git a/packages/core/src/design-jsx/index.ts b/packages/core/src/design-jsx/index.ts index 6851244c1..6ef615f41 100644 --- a/packages/core/src/design-jsx/index.ts +++ b/packages/core/src/design-jsx/index.ts @@ -60,6 +60,17 @@ export { defineVars, designVar, isVariable, type DesignVariable, type VarDef } f export { createElement } from './mini-react' export { renderJSX, renderTreeNode, buildComponent } from './render' +export { + DESIGN_JSX_ELEMENTS, + DESIGN_JSX_HELPERS, + DESIGN_JSX_PROPERTIES, + DESIGN_JSX_SUPPORTED_PROPERTIES, + DESIGN_JSX_SUPPORTED_PROPERTY_NAMES, + type DesignJSXElementDefinition, + type DesignJSXHelperDefinition, + type DesignJSXPropertyDefinition +} from './schema' +export { transformDesignJSXExpression } from './transform' import jsxReference from '#core/tools/prompts/jsx-reference.md' diff --git a/packages/core/src/design-jsx/render.ts b/packages/core/src/design-jsx/render.ts index be80e44eb..4c3d6b2c4 100644 --- a/packages/core/src/design-jsx/render.ts +++ b/packages/core/src/design-jsx/render.ts @@ -2,6 +2,7 @@ import { transform } from 'sucrase' import type { SceneGraph } from '@open-pencil/scene-graph' +import { DESIGN_JSX_SUPPORTED_PROPERTIES } from '#core/design-jsx/schema' import type { RenderOptions as RenderJSXOptions } from '#core/design-jsx/types' import { backgroundBlur, dropShadow, foregroundBlur, innerShadow, layerBlur } from './effects' @@ -21,114 +22,7 @@ import { isTreeNode, resolveToTree, type TreeNode } from './tree' * Build a component function from a JSX string using sucrase. * Works in both Node/Bun and the browser (no native bindings). */ -const SUPPORTED_PROPS = new Set([ - 'name', - 'key', - 'flex', - 'flow', - 'dir', - 'gap', - 'wrap', - 'rowGap', - 'columnGap', - 'justify', - 'justifyContent', - 'items', - 'align', - 'alignItems', - 'grow', - 'w', - 'h', - 'width', - 'height', - 'minW', - 'maxW', - 'minH', - 'maxH', - 'x', - 'y', - 'top', - 'left', - 'position', - 'p', - 'padding', - 'px', - 'py', - 'pt', - 'pr', - 'pb', - 'pl', - 'bg', - 'fill', - 'fills', - 'background', - 'backgroundColor', - 'stroke', - 'border', - 'borderColor', - 'strokeWidth', - 'borderWidth', - 'strokeAlign', - 'strokeDash', - 'rounded', - 'borderRadius', - 'roundedTL', - 'roundedTR', - 'roundedBL', - 'roundedBR', - 'cornerRadius', - 'cornerSmoothing', - 'opacity', - 'blendMode', - 'rotate', - 'rotation', - 'overflow', - 'shadow', - 'blur', - 'effects', - 'size', - 'fontSize', - 'font', - 'fontFamily', - 'weight', - 'fontWeight', - 'color', - 'text', - 'characters', - 'content', - 'value', - 'title', - 'textAlign', - 'textAlignHorizontal', - 'textHorizontalAlignment', - 'textAlignVertical', - 'textVerticalAlignment', - 'textAutoResize', - 'lineHeight', - 'letterSpacing', - 'textDecoration', - 'textCase', - 'maxLines', - 'truncate', - 'grid', - 'columns', - 'rows', - 'colStart', - 'rowStart', - 'col', - 'row', - 'colSpan', - 'rowSpan', - 'points', - 'pointCount', - 'innerRadius', - 'label', - 'style', - 'bind', - 'component', - 'componentId', - 'of' -]) +const SUPPORTED_PROPS = DESIGN_JSX_SUPPORTED_PROPERTIES function stripHTMLComments(jsxString: string): string { return jsxString.replace(//g, '') diff --git a/packages/core/src/design-jsx/schema.ts b/packages/core/src/design-jsx/schema.ts new file mode 100644 index 000000000..f398fba03 --- /dev/null +++ b/packages/core/src/design-jsx/schema.ts @@ -0,0 +1,165 @@ +export type DesignJSXElementDefinition = { + name: string + runtimeType: string + description: string +} + +export type DesignJSXNamedDefinition = { + name: string + description: string +} + +export type DesignJSXPropertyDefinition = DesignJSXNamedDefinition +export type DesignJSXHelperDefinition = DesignJSXNamedDefinition + +export const DESIGN_JSX_ELEMENTS: DesignJSXElementDefinition[] = [ + { name: 'Frame', runtimeType: 'frame', description: 'A frame or auto-layout container.' }, + { name: 'Text', runtimeType: 'text', description: 'A text layer.' }, + { name: 'Rectangle', runtimeType: 'rectangle', description: 'A rectangle layer.' }, + { name: 'Ellipse', runtimeType: 'ellipse', description: 'An ellipse layer.' }, + { name: 'Line', runtimeType: 'line', description: 'A line layer.' }, + { name: 'Star', runtimeType: 'star', description: 'A star layer.' }, + { name: 'Polygon', runtimeType: 'polygon', description: 'A polygon layer.' }, + { name: 'Vector', runtimeType: 'vector', description: 'A vector layer.' }, + { name: 'Group', runtimeType: 'group', description: 'A group of layers.' }, + { name: 'Section', runtimeType: 'section', description: 'A canvas section.' }, + { name: 'Component', runtimeType: 'component', description: 'A reusable component.' }, + { name: 'ComponentSet', runtimeType: 'component-set', description: 'A component variant set.' }, + { name: 'Instance', runtimeType: 'instance', description: 'An instance of a component.' }, + { name: 'View', runtimeType: 'frame', description: 'An alias for Frame.' }, + { name: 'Rect', runtimeType: 'rectangle', description: 'An alias for Rectangle.' }, + { name: 'Icon', runtimeType: 'icon', description: 'An Iconify icon.' } +] + +export const DESIGN_JSX_SUPPORTED_PROPERTY_NAMES = [ + 'name', + 'key', + 'flex', + 'flow', + 'dir', + 'gap', + 'wrap', + 'rowGap', + 'columnGap', + 'justify', + 'justifyContent', + 'items', + 'align', + 'alignItems', + 'grow', + 'w', + 'h', + 'width', + 'height', + 'minW', + 'maxW', + 'minH', + 'maxH', + 'x', + 'y', + 'top', + 'left', + 'position', + 'p', + 'padding', + 'px', + 'py', + 'pt', + 'pr', + 'pb', + 'pl', + 'bg', + 'fill', + 'fills', + 'background', + 'backgroundColor', + 'stroke', + 'border', + 'borderColor', + 'strokeWidth', + 'borderWidth', + 'strokeAlign', + 'strokeDash', + 'rounded', + 'borderRadius', + 'roundedTL', + 'roundedTR', + 'roundedBL', + 'roundedBR', + 'cornerRadius', + 'cornerSmoothing', + 'opacity', + 'blendMode', + 'rotate', + 'rotation', + 'overflow', + 'shadow', + 'blur', + 'effects', + 'size', + 'fontSize', + 'font', + 'fontFamily', + 'weight', + 'fontWeight', + 'color', + 'text', + 'characters', + 'content', + 'value', + 'title', + 'textAlign', + 'textAlignHorizontal', + 'textHorizontalAlignment', + 'textAlignVertical', + 'textVerticalAlignment', + 'textAutoResize', + 'lineHeight', + 'letterSpacing', + 'textDecoration', + 'textCase', + 'maxLines', + 'truncate', + 'grid', + 'columns', + 'rows', + 'colStart', + 'rowStart', + 'col', + 'row', + 'colSpan', + 'rowSpan', + 'points', + 'pointCount', + 'innerRadius', + 'label', + 'style', + 'bind', + 'component', + 'componentId', + 'of' +] as const + +export const DESIGN_JSX_SUPPORTED_PROPERTIES = new Set(DESIGN_JSX_SUPPORTED_PROPERTY_NAMES) + +export const DESIGN_JSX_PROPERTIES: DesignJSXPropertyDefinition[] = + DESIGN_JSX_SUPPORTED_PROPERTY_NAMES.map((name) => ({ + name, + description: `OpenPencil ${name} property.` + })) + +export const DESIGN_JSX_HELPERS: DesignJSXHelperDefinition[] = [ + 'solid', + 'gradient', + 'linearGradient', + 'radialGradient', + 'angularGradient', + 'diamondGradient', + 'dropShadow', + 'innerShadow', + 'layerBlur', + 'backgroundBlur', + 'foregroundBlur', + 'designVar', + 'defineVars' +].map((name) => ({ name, description: `OpenPencil ${name} helper.` })) diff --git a/packages/core/src/design-jsx/transform.ts b/packages/core/src/design-jsx/transform.ts new file mode 100644 index 000000000..d29700caa --- /dev/null +++ b/packages/core/src/design-jsx/transform.ts @@ -0,0 +1,44 @@ +import { transform } from 'sucrase' + +const options = { + transforms: ['typescript', 'jsx'] as Array<'typescript' | 'jsx'>, + jsxPragma: '__h', + jsxFragmentPragma: '__fragment', + production: true +} + +function transformExpression(source: string): string { + return transform(`return (${source.trim()})`, options).code +} + +function statementBoundaries(source: string): number[] { + const boundaries = new Set() + for (let index = 0; index < source.length; index++) { + if (source[index] === '\n') boundaries.add(index + 1) + } + return [...boundaries].sort((left, right) => right - left) +} + +/** + * Transform Design JSX into a function body. A plain JSX expression is accepted directly. + * For authored programs, top-level declarations must precede a final expression on a new line. + */ +export function transformDesignJSXExpression(source: string): string { + const trimmed = source.trim() + try { + return transformExpression(trimmed) + } catch (expressionError) { + for (const boundary of statementBoundaries(trimmed)) { + const statements = trimmed.slice(0, boundary).trim() + const expression = trimmed.slice(boundary).trim() + if (!statements || !expression) continue + try { + const transformedStatements = transform(statements, options).code + return `${transformedStatements}\n${transformExpression(expression)}` + } catch { + continue + } + } + throw expressionError + } +} diff --git a/packages/vue/src/i18n/locales/de/dialogs.json b/packages/vue/src/i18n/locales/de/dialogs.json index bbdadc024..21d32a02b 100644 --- a/packages/vue/src/i18n/locales/de/dialogs.json +++ b/packages/vue/src/i18n/locales/de/dialogs.json @@ -24,7 +24,6 @@ "createCollection": "Sammlung erstellen", "localVariables": "Lokale Variablen", "noVariableCollections": "Keine Variablensammlungen", - "selectLayerForJSX": "Wähle eine Ebene, um ihren JSX-Code zu sehen", "connectAIProvider": "Verbinde einen KI-Anbieter, um den Chat zu starten.", "connect": "Verbinden", "testConnection": "Verbindung testen", @@ -192,6 +191,7 @@ "copiedExclamation": "Kopiert!", "copyMessage": "Nachricht kopieren", "copyJSXReference": "JSX-Prop-Referenz in die Zwischenablage kopieren", + "jsxUpToDate": "Up to date", "newTab": "Neuer Tab", "closeTab": "{name} schließen", "showUI": "UI anzeigen ({shortcut})", @@ -276,5 +276,16 @@ "dismiss": "Schließen", "toolRunning": "Wird ausgeführt…", "toolError": "Fehler", - "permissionRequestTitle": "Berechtigungsanfrage" + "permissionRequestTitle": "Berechtigungsanfrage", + "codeSource": "Code source", + "codeSourceDesignJSX": "Design JSX", + "codeSourceTailwindJSX": "Tailwind JSX", + "codeSourceHTMLCSS": "HTML/CSS", + "codeEditorDesignLabel": "Design JSX", + "codeEditorHTMLCSSLabel": "HTML and CSS", + "codeUpdating": "Updating…", + "codeUpdatedLive": "Updated live", + "codePreviewFailed": "Preview failed", + "codeGeneratedReadOnly": "Generated, read only", + "codeReset": "Reset" } diff --git a/packages/vue/src/i18n/locales/es/dialogs.json b/packages/vue/src/i18n/locales/es/dialogs.json index 9e239a646..c311578a6 100644 --- a/packages/vue/src/i18n/locales/es/dialogs.json +++ b/packages/vue/src/i18n/locales/es/dialogs.json @@ -24,7 +24,6 @@ "createCollection": "Crear colección", "localVariables": "Variables locales", "noVariableCollections": "No hay colecciones de variables", - "selectLayerForJSX": "Selecciona una capa para ver su código JSX", "connectAIProvider": "Conecta un proveedor de IA para empezar a chatear.", "connect": "Conectar", "testConnection": "Probar conexión", @@ -192,6 +191,7 @@ "copiedExclamation": "¡Copiado!", "copyMessage": "Copiar mensaje", "copyJSXReference": "Copiar referencia de props JSX al portapapeles", + "jsxUpToDate": "Up to date", "newTab": "Nueva pestaña", "closeTab": "Cerrar {name}", "showUI": "Mostrar interfaz ({shortcut})", @@ -276,5 +276,16 @@ "dismiss": "Descartar", "toolRunning": "Ejecutando…", "toolError": "Error", - "permissionRequestTitle": "Solicitud de permiso" + "permissionRequestTitle": "Solicitud de permiso", + "codeSource": "Code source", + "codeSourceDesignJSX": "Design JSX", + "codeSourceTailwindJSX": "Tailwind JSX", + "codeSourceHTMLCSS": "HTML/CSS", + "codeEditorDesignLabel": "Design JSX", + "codeEditorHTMLCSSLabel": "HTML and CSS", + "codeUpdating": "Updating…", + "codeUpdatedLive": "Updated live", + "codePreviewFailed": "Preview failed", + "codeGeneratedReadOnly": "Generated, read only", + "codeReset": "Reset" } diff --git a/packages/vue/src/i18n/locales/fr/dialogs.json b/packages/vue/src/i18n/locales/fr/dialogs.json index 26bbb9cb0..2cf51869f 100644 --- a/packages/vue/src/i18n/locales/fr/dialogs.json +++ b/packages/vue/src/i18n/locales/fr/dialogs.json @@ -24,7 +24,6 @@ "createCollection": "Créer une collection", "localVariables": "Variables locales", "noVariableCollections": "Aucune collection de variables", - "selectLayerForJSX": "Sélectionnez un calque pour voir son code JSX", "connectAIProvider": "Connectez un fournisseur d’IA pour commencer à discuter.", "connect": "Connecter", "testConnection": "Tester la connexion", @@ -192,6 +191,7 @@ "copiedExclamation": "Copié !", "copyMessage": "Copier le message", "copyJSXReference": "Copier la référence des props JSX dans le presse-papiers", + "jsxUpToDate": "Up to date", "newTab": "Nouvel onglet", "closeTab": "Fermer {name}", "showUI": "Afficher l’interface ({shortcut})", @@ -276,5 +276,16 @@ "dismiss": "Ignorer", "toolRunning": "Exécution…", "toolError": "Erreur", - "permissionRequestTitle": "Demande d’autorisation" + "permissionRequestTitle": "Demande d’autorisation", + "codeSource": "Code source", + "codeSourceDesignJSX": "Design JSX", + "codeSourceTailwindJSX": "Tailwind JSX", + "codeSourceHTMLCSS": "HTML/CSS", + "codeEditorDesignLabel": "Design JSX", + "codeEditorHTMLCSSLabel": "HTML and CSS", + "codeUpdating": "Updating…", + "codeUpdatedLive": "Updated live", + "codePreviewFailed": "Preview failed", + "codeGeneratedReadOnly": "Generated, read only", + "codeReset": "Reset" } diff --git a/packages/vue/src/i18n/locales/it/dialogs.json b/packages/vue/src/i18n/locales/it/dialogs.json index 5c3b52606..bcc990b36 100644 --- a/packages/vue/src/i18n/locales/it/dialogs.json +++ b/packages/vue/src/i18n/locales/it/dialogs.json @@ -24,7 +24,6 @@ "createCollection": "Crea raccolta", "localVariables": "Variabili locali", "noVariableCollections": "Nessuna raccolta di variabili", - "selectLayerForJSX": "Seleziona un livello per vedere il suo codice JSX", "connectAIProvider": "Collega un provider AI per iniziare a chattare.", "connect": "Connetti", "testConnection": "Verifica connessione", @@ -192,6 +191,7 @@ "copiedExclamation": "Copiato!", "copyMessage": "Copia messaggio", "copyJSXReference": "Copia riferimento props JSX negli appunti", + "jsxUpToDate": "Up to date", "newTab": "Nuova scheda", "closeTab": "Chiudi {name}", "showUI": "Mostra interfaccia ({shortcut})", @@ -276,5 +276,16 @@ "dismiss": "Chiudi", "toolRunning": "Esecuzione…", "toolError": "Errore", - "permissionRequestTitle": "Richiesta di autorizzazione" + "permissionRequestTitle": "Richiesta di autorizzazione", + "codeSource": "Code source", + "codeSourceDesignJSX": "Design JSX", + "codeSourceTailwindJSX": "Tailwind JSX", + "codeSourceHTMLCSS": "HTML/CSS", + "codeEditorDesignLabel": "Design JSX", + "codeEditorHTMLCSSLabel": "HTML and CSS", + "codeUpdating": "Updating…", + "codeUpdatedLive": "Updated live", + "codePreviewFailed": "Preview failed", + "codeGeneratedReadOnly": "Generated, read only", + "codeReset": "Reset" } diff --git a/packages/vue/src/i18n/locales/ja/dialogs.json b/packages/vue/src/i18n/locales/ja/dialogs.json index eef0cd14d..501f9667e 100644 --- a/packages/vue/src/i18n/locales/ja/dialogs.json +++ b/packages/vue/src/i18n/locales/ja/dialogs.json @@ -24,7 +24,6 @@ "createCollection": "コレクションを作成", "localVariables": "ローカル変数", "noVariableCollections": "変数コレクションがありません", - "selectLayerForJSX": "JSXコードを表示するレイヤーを選択してください", "connectAIProvider": "チャットを始めるには、AIプロバイダーを接続してください。", "connect": "接続", "testConnection": "接続をテスト", @@ -192,6 +191,7 @@ "copiedExclamation": "コピーしました!", "copyMessage": "メッセージをコピー", "copyJSXReference": "JSXのprops参照をクリップボードにコピー", + "jsxUpToDate": "Up to date", "newTab": "新しいタブ", "closeTab": "{name} を閉じる", "showUI": "UIを表示 ({shortcut})", @@ -276,5 +276,16 @@ "dismiss": "閉じる", "toolRunning": "実行中…", "toolError": "エラー", - "permissionRequestTitle": "権限リクエスト" + "permissionRequestTitle": "権限リクエスト", + "codeSource": "Code source", + "codeSourceDesignJSX": "Design JSX", + "codeSourceTailwindJSX": "Tailwind JSX", + "codeSourceHTMLCSS": "HTML/CSS", + "codeEditorDesignLabel": "Design JSX", + "codeEditorHTMLCSSLabel": "HTML and CSS", + "codeUpdating": "Updating…", + "codeUpdatedLive": "Updated live", + "codePreviewFailed": "Preview failed", + "codeGeneratedReadOnly": "Generated, read only", + "codeReset": "Reset" } diff --git a/packages/vue/src/i18n/locales/pl/dialogs.json b/packages/vue/src/i18n/locales/pl/dialogs.json index 2faf9107c..86a8ba051 100644 --- a/packages/vue/src/i18n/locales/pl/dialogs.json +++ b/packages/vue/src/i18n/locales/pl/dialogs.json @@ -24,7 +24,6 @@ "createCollection": "Utwórz kolekcję", "localVariables": "Zmienne lokalne", "noVariableCollections": "Brak kolekcji zmiennych", - "selectLayerForJSX": "Wybierz warstwę, aby zobaczyć jej kod JSX", "connectAIProvider": "Połącz dostawcę AI, aby rozpocząć czat.", "connect": "Połącz", "testConnection": "Testuj połączenie", @@ -192,6 +191,7 @@ "copiedExclamation": "Skopiowano!", "copyMessage": "Kopiuj wiadomość", "copyJSXReference": "Skopiuj referencję propsów JSX do schowka", + "jsxUpToDate": "Up to date", "newTab": "Nowa karta", "closeTab": "Zamknij {name}", "showUI": "Pokaż interfejs ({shortcut})", @@ -276,5 +276,16 @@ "dismiss": "Zamknij", "toolRunning": "Uruchamianie…", "toolError": "Błąd", - "permissionRequestTitle": "Prośba o uprawnienie" + "permissionRequestTitle": "Prośba o uprawnienie", + "codeSource": "Code source", + "codeSourceDesignJSX": "Design JSX", + "codeSourceTailwindJSX": "Tailwind JSX", + "codeSourceHTMLCSS": "HTML/CSS", + "codeEditorDesignLabel": "Design JSX", + "codeEditorHTMLCSSLabel": "HTML and CSS", + "codeUpdating": "Updating…", + "codeUpdatedLive": "Updated live", + "codePreviewFailed": "Preview failed", + "codeGeneratedReadOnly": "Generated, read only", + "codeReset": "Reset" } diff --git a/packages/vue/src/i18n/locales/ru/dialogs.json b/packages/vue/src/i18n/locales/ru/dialogs.json index 693d769bb..151abc177 100644 --- a/packages/vue/src/i18n/locales/ru/dialogs.json +++ b/packages/vue/src/i18n/locales/ru/dialogs.json @@ -24,7 +24,6 @@ "createCollection": "Создать коллекцию", "localVariables": "Локальные переменные", "noVariableCollections": "Нет коллекций переменных", - "selectLayerForJSX": "Выберите слой, чтобы увидеть его JSX-код", "connectAIProvider": "Подключите AI-провайдера, чтобы начать чат.", "connect": "Подключить", "testConnection": "Проверить подключение", @@ -192,6 +191,7 @@ "copiedExclamation": "Скопировано!", "copyMessage": "Скопировать сообщение", "copyJSXReference": "Скопировать справку по JSX-свойствам в буфер", + "jsxUpToDate": "Up to date", "newTab": "Новая вкладка", "closeTab": "Закрыть {name}", "showUI": "Показать интерфейс ({shortcut})", @@ -276,5 +276,16 @@ "dismiss": "Закрыть", "toolRunning": "Выполняется…", "toolError": "Ошибка", - "permissionRequestTitle": "Запрос разрешения" + "permissionRequestTitle": "Запрос разрешения", + "codeSource": "Code source", + "codeSourceDesignJSX": "Design JSX", + "codeSourceTailwindJSX": "Tailwind JSX", + "codeSourceHTMLCSS": "HTML/CSS", + "codeEditorDesignLabel": "Design JSX", + "codeEditorHTMLCSSLabel": "HTML and CSS", + "codeUpdating": "Updating…", + "codeUpdatedLive": "Updated live", + "codePreviewFailed": "Preview failed", + "codeGeneratedReadOnly": "Generated, read only", + "codeReset": "Reset" } diff --git a/packages/vue/src/i18n/locales/zh-cn/dialogs.json b/packages/vue/src/i18n/locales/zh-cn/dialogs.json index 92b089508..defa84e2c 100644 --- a/packages/vue/src/i18n/locales/zh-cn/dialogs.json +++ b/packages/vue/src/i18n/locales/zh-cn/dialogs.json @@ -24,7 +24,6 @@ "createCollection": "创建集合", "localVariables": "本地变量", "noVariableCollections": "没有变量集合", - "selectLayerForJSX": "选择一个图层以查看其 JSX 代码", "connectAIProvider": "连接 AI 提供商以开始聊天。", "connect": "连接", "testConnection": "测试连接", @@ -192,6 +191,7 @@ "copiedExclamation": "已复制!", "copyMessage": "复制消息", "copyJSXReference": "复制 JSX 属性参考到剪贴板", + "jsxUpToDate": "Up to date", "newTab": "新标签页", "closeTab": "关闭 {name}", "showUI": "显示界面({shortcut})", @@ -276,5 +276,16 @@ "dismiss": "关闭", "toolRunning": "正在运行…", "toolError": "错误", - "permissionRequestTitle": "权限请求" + "permissionRequestTitle": "权限请求", + "codeSource": "Code source", + "codeSourceDesignJSX": "Design JSX", + "codeSourceTailwindJSX": "Tailwind JSX", + "codeSourceHTMLCSS": "HTML/CSS", + "codeEditorDesignLabel": "Design JSX", + "codeEditorHTMLCSSLabel": "HTML and CSS", + "codeUpdating": "Updating…", + "codeUpdatedLive": "Updated live", + "codePreviewFailed": "Preview failed", + "codeGeneratedReadOnly": "Generated, read only", + "codeReset": "Reset" } diff --git a/packages/vue/src/i18n/messages/dialogs.ts b/packages/vue/src/i18n/messages/dialogs.ts index 07a0d82f8..29d463dce 100644 --- a/packages/vue/src/i18n/messages/dialogs.ts +++ b/packages/vue/src/i18n/messages/dialogs.ts @@ -33,8 +33,19 @@ export const dialogMessageDefaults = { duplicateMode: 'Duplicate mode', deleteMode: 'Delete mode', setDefaultMode: 'Set as default', - selectLayerForJSX: 'Select a layer to see its JSX code', + codeSource: 'Code source', + codeSourceDesignJSX: 'Design JSX', + codeSourceTailwindJSX: 'Tailwind JSX', + codeSourceHTMLCSS: 'HTML/CSS', + codeEditorDesignLabel: 'Design JSX', + codeEditorHTMLCSSLabel: 'HTML and CSS', + codeUpdating: 'Updating…', + codeUpdatedLive: 'Updated live', + codePreviewFailed: 'Preview failed', + codeGeneratedReadOnly: 'Generated, read only', + codeReset: 'Reset', copyJSXReference: 'Copy JSX prop reference to clipboard', + jsxUpToDate: 'Up to date', importLabel: 'Import', importHTMLCSS: 'Import HTML/CSS', importHTMLCSSDescription: 'Paste HTML plus optional CSS or compiled Tailwind CSS.', diff --git a/src/app.css b/src/app.css index e226c1283..e5da7b5de 100644 --- a/src/app.css +++ b/src/app.css @@ -50,6 +50,8 @@ --color-warning-action: #fcd34d; --color-success: #4ade80; --color-error: #f87171; + --color-error-bg: rgb(248 113 113 / 0.1); + --color-error-border: rgb(248 113 113 / 0.4); --color-success-bg: #16a34a; --color-success-bg-hover: #15803d; --color-code-tag: #7dd3fc; @@ -87,6 +89,8 @@ html[data-theme='light'] { --color-warning-action: #78350f; --color-success: #15803d; --color-error: #b91c1c; + --color-error-bg: rgb(185 28 28 / 0.08); + --color-error-border: rgb(185 28 28 / 0.28); --color-success-bg: #16a34a; --color-success-bg-hover: #15803d; --color-code-tag: #0369a1; @@ -123,32 +127,6 @@ body { -webkit-user-select: none; } -.code-highlight .token.tag { - color: var(--color-code-tag); -} - -.code-highlight .token.attr-name { - color: var(--color-code-attribute); -} - -.code-highlight .token.attr-value, -.code-highlight .token.string { - color: var(--color-code-string); -} - -.code-highlight .token.number, -.code-highlight .token.boolean { - color: var(--color-code-number); -} - -.code-highlight .token.punctuation { - color: var(--color-code-punctuation); -} - -.code-highlight .token.keyword { - color: var(--color-code-attribute); -} - input[type='number'] { appearance: textfield; -moz-appearance: textfield; diff --git a/src/app/code/dom-preview.ts b/src/app/code/dom-preview.ts new file mode 100644 index 000000000..629607b85 --- /dev/null +++ b/src/app/code/dom-preview.ts @@ -0,0 +1,61 @@ +import { browserHTMLToSceneGraph } from '@open-pencil/dom-css/browser' +import type { SceneGraph } from '@open-pencil/scene-graph' + +import type { EditorStore } from '@/app/editor/active-store' + +export type DOMCodeSession = { + originalGraph: SceneGraph + originalPageId: string + previewGraph: SceneGraph | null + previewPageId: string | null +} + +export function createDOMCodeSession(store: EditorStore): DOMCodeSession { + return { + originalGraph: store.graph, + originalPageId: store.state.currentPageId, + previewGraph: null, + previewPageId: null + } +} + +export async function previewDOMCode( + store: EditorStore, + session: DOMCodeSession, + source: string +): Promise<{ ok: true } | { ok: false; error: string }> { + try { + const graph = await browserHTMLToSceneGraph(source, { pageName: 'Code preview' }) + const pageId = graph.getPages()[0]?.id ?? graph.rootId + session.previewGraph = graph + session.previewPageId = pageId + store.replaceGraph(graph) + return { ok: true } + } catch (error) { + return { ok: false, error: error instanceof Error ? error.message : String(error) } + } +} + +export function resetDOMCodePreview(store: EditorStore, session: DOMCodeSession): void { + store.replaceGraph(session.originalGraph) + void store.switchPage(session.originalPageId) + session.previewGraph = null + session.previewPageId = null +} + +export function commitDOMCodeSession(store: EditorStore, session: DOMCodeSession): void { + const after = session.previewGraph + const afterPageId = session.previewPageId + if (!after || !afterPageId) return + store.pushUndoEntry({ + label: 'Edit HTML/CSS', + forward: () => { + store.replaceGraph(after) + void store.switchPage(afterPageId) + }, + inverse: () => { + store.replaceGraph(session.originalGraph) + void store.switchPage(session.originalPageId) + } + }) +} diff --git a/src/app/code/highlight.ts b/src/app/code/highlight.ts deleted file mode 100644 index f6b610056..000000000 --- a/src/app/code/highlight.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { toHtml as toHTML } from 'hast-util-to-html' -import { refractor } from 'refractor/core' -import jsx from 'refractor/jsx' - -refractor.register(jsx) - -export function highlightJSX(code: string): string { - return toHTML(refractor.highlight(code, 'jsx')) -} diff --git a/src/app/code/live-preview.ts b/src/app/code/live-preview.ts new file mode 100644 index 000000000..eed76a2aa --- /dev/null +++ b/src/app/code/live-preview.ts @@ -0,0 +1,147 @@ +import type { TreeNode } from '@open-pencil/core/design-jsx' +import { renderTree } from '@open-pencil/core/design-jsx' +import { computeAllLayouts } from '@open-pencil/core/layout' +import type { Vector } from '@open-pencil/scene-graph' + +import { convertDesignJSXRoots } from '@/app/code/sandbox/convert' +import { evaluateDesignJSX } from '@/app/code/sandbox/evaluate' +import type { EditorStore } from '@/app/editor/active-store' + +export type ApplyDesignJSXResult = { ok: true; nodeIds: string[] } | { ok: false; error: string } +export type DesignJSXEditSession = { + originalSnapshot: ReturnType + originalSelectionIds: string[] + targetParentId: string + targetIndex: number + origins: Vector[] + fallbackOrigin: Vector + previewSnapshot: ReturnType | null + previewNodeIds: string[] +} + +function lockedSelectionError(store: EditorStore, ids: string[]): string | null { + const locked = ids.some((id) => { + const node = store.graph.getNode(id) + if (node?.locked) return true + return [...store.graph.getAllNodes()].some( + (candidate) => candidate.locked && store.graph.isDescendant(candidate.id, id) + ) + }) + return locked ? 'Unlock the selected layers and their contents before editing code.' : null +} + +export function createDesignJSXEditSession( + store: EditorStore +): { ok: true; session: DesignJSXEditSession } | { ok: false; error: string } { + const selected = [...store.state.selectedIds] + .map((id) => store.graph.getNode(id)) + .filter((node) => node !== undefined) + .sort((left, right) => { + if (left.parentId !== right.parentId) return left.id.localeCompare(right.id) + const parent = left.parentId ? store.graph.getNode(left.parentId) : undefined + return (parent?.childIds.indexOf(left.id) ?? 0) - (parent?.childIds.indexOf(right.id) ?? 0) + }) + const lockedError = lockedSelectionError( + store, + selected.map(({ id }) => id) + ) + if (lockedError) return { ok: false, error: lockedError } + const parentIds = new Set(selected.map(({ parentId }) => parentId ?? store.state.currentPageId)) + if (parentIds.size > 1) { + return { ok: false, error: 'Select layers with the same parent before editing code.' } + } + const first = selected.at(0) + const targetParentId = first?.parentId ?? store.state.currentPageId + const targetIndex = first + ? (store.graph.getNode(targetParentId)?.childIds.indexOf(first.id) ?? -1) + : -1 + return { + ok: true, + session: { + originalSnapshot: store.snapshotPage(), + originalSelectionIds: selected.map(({ id }) => id), + targetParentId, + targetIndex, + origins: selected.map(({ x, y }) => ({ x, y })), + fallbackOrigin: first ? { x: first.x, y: first.y } : store.viewportCanvasCenter(), + previewSnapshot: null, + previewNodeIds: [] + } + } +} + +export async function previewDesignJSX( + store: EditorStore, + session: DesignJSXEditSession, + source: string +): Promise { + const evaluated = await evaluateDesignJSX(source) + if (!evaluated.ok) return evaluated + + let roots: TreeNode[] + try { + roots = convertDesignJSXRoots(evaluated.roots) + } catch (error) { + return { ok: false, error: error instanceof Error ? error.message : String(error) } + } + + store.restorePageFromSnapshot(session.originalSnapshot) + try { + for (const id of session.originalSelectionIds) store.graph.deleteNode(id) + const results = [] + for (const [index, root] of roots.entries()) { + const origin = session.origins.at(index) ?? { + x: session.fallbackOrigin.x + index * 24, + y: session.fallbackOrigin.y + index * 24 + } + const result = await renderTree(store.graph, root, { + parentId: session.targetParentId, + x: origin.x, + y: origin.y + }) + results.push(result) + if (session.targetIndex >= 0) { + store.graph.insertChildAt(result.id, session.targetParentId, session.targetIndex + index) + } + } + const nodeIds = results.map(({ id }) => id) + computeAllLayouts(store.graph, store.state.currentPageId) + store.select(nodeIds) + store.requestRender() + session.previewSnapshot = store.snapshotPage() + session.previewNodeIds = nodeIds + return { ok: true, nodeIds } + } catch (error) { + store.restorePageFromSnapshot(session.previewSnapshot ?? session.originalSnapshot) + store.select( + session.previewNodeIds.length > 0 ? session.previewNodeIds : session.originalSelectionIds + ) + return { ok: false, error: error instanceof Error ? error.message : String(error) } + } +} + +export function resetDesignJSXPreview(store: EditorStore, session: DesignJSXEditSession): void { + store.restorePageFromSnapshot(session.originalSnapshot) + store.select(session.originalSelectionIds) + session.previewSnapshot = null + session.previewNodeIds = [] +} + +export function commitDesignJSXSession(store: EditorStore, session: DesignJSXEditSession): void { + const after = session.previewSnapshot + if (!after) return + const previewIds = [...session.previewNodeIds] + const originalSnapshot = session.originalSnapshot + const originalSelectionIds = [...session.originalSelectionIds] + store.pushUndoEntry({ + label: session.originalSelectionIds.length > 0 ? 'Edit JSX' : 'Insert JSX', + forward: () => { + store.restorePageFromSnapshot(after) + store.select(previewIds) + }, + inverse: () => { + store.restorePageFromSnapshot(originalSnapshot) + store.select(originalSelectionIds) + } + }) +} diff --git a/src/app/code/sandbox/convert.ts b/src/app/code/sandbox/convert.ts new file mode 100644 index 000000000..67646aef5 --- /dev/null +++ b/src/app/code/sandbox/convert.ts @@ -0,0 +1,97 @@ +import { + angularGradient, + backgroundBlur, + designVar, + defineVars, + diamondGradient, + dropShadow, + foregroundBlur, + gradient, + innerShadow, + layerBlur, + linearGradient, + radialGradient, + solid, + type TreeNode +} from '@open-pencil/core/design-jsx' + +import type { DesignJSXHelperDescriptor } from '@/app/code/sandbox/types' + +const HELPERS = { + solid, + gradient, + linearGradient, + radialGradient, + angularGradient, + diamondGradient, + dropShadow, + innerShadow, + layerBlur, + backgroundBlur, + foregroundBlur, + designVar, + defineVars +} + +type HelperName = keyof typeof HELPERS + +type PlainRecord = { [key: string]: unknown } + +type SerializedDesignJSXElement = { + type: string + props: PlainRecord + children: unknown[] +} + +function isRecord(value: unknown): value is PlainRecord { + return value !== null && typeof value === 'object' && !Array.isArray(value) +} + +function isSerializedElement(value: unknown): value is SerializedDesignJSXElement { + return ( + isRecord(value) && + typeof value.type === 'string' && + isRecord(value.props) && + Array.isArray(value.children) + ) +} + +function isHelper(value: unknown): value is DesignJSXHelperDescriptor { + return ( + isRecord(value) && typeof value.__openPencilHelper === 'string' && Array.isArray(value.args) + ) +} + +function convertValue(value: unknown): unknown { + if (isHelper(value)) { + const helperName = value.__openPencilHelper + const helper = Object.hasOwn(HELPERS, helperName) + ? (HELPERS[helperName as HelperName] as (...args: unknown[]) => unknown) + : undefined + if (typeof helper !== 'function') { + throw new TypeError(`Unknown Design JSX helper "${helperName}".`) + } + return helper(...value.args.map(convertValue)) + } + if (Array.isArray(value)) return value.map(convertValue) + if (isRecord(value)) + return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, convertValue(v)])) + return value +} + +function convertTree(value: unknown): TreeNode { + if (!isSerializedElement(value)) { + throw new Error('Design JSX must return an OpenPencil element.') + } + const children = value.children.map((child): TreeNode | string => { + if (typeof child === 'string') return child + if (typeof child === 'number') return String(child) + return convertTree(child) + }) + return { type: value.type, props: convertValue(value.props) as PlainRecord, children } +} + +export function convertDesignJSXRoots(values: unknown[]): TreeNode[] { + // Call validateDesignJSXOutput first; it enforces the depth bound used by this recursion. + return values.map(convertTree) +} diff --git a/src/app/code/sandbox/document.ts b/src/app/code/sandbox/document.ts new file mode 100644 index 000000000..e4050ac34 --- /dev/null +++ b/src/app/code/sandbox/document.ts @@ -0,0 +1,121 @@ +import type { DesignJSXValidationLimits } from '@/app/code/sandbox/types' + +const workerSource = String.raw` +self.onmessage = ({ data }) => { + const { id, code, elements, helpers: helperNames, limits } = data + const blocked = () => { throw new Error('Network access is unavailable in Design JSX.') } + self.fetch = blocked + self.XMLHttpRequest = undefined + self.WebSocket = undefined + self.EventSource = undefined + self.importScripts = blocked + + const normalizeChildren = (children) => children.flat(Infinity).filter((child) => child != null && child !== false) + const __fragment = '' + const __h = (type, props, ...children) => { + const normalizedProps = props == null ? {} : props + const normalizedChildren = normalizeChildren(children) + if (typeof type === 'function') return type({ ...normalizedProps, children: normalizedChildren }) + if (type === __fragment) return normalizedChildren + return { type, props: normalizedProps, children: normalizedChildren } + } + const helper = (name) => (...args) => ({ __openPencilHelper: name, args }) + const helperRuntime = (name) => { + if (name === 'defineVars') return (vars) => Object.fromEntries(Object.entries(vars).map(([key, value]) => [key, { __openPencilHelper: 'designVar', args: [value] }])) + return helper(name) + } + const names = Object.keys(elements) + const tags = elements + const helpers = Object.fromEntries(helperNames.map((name) => [name, helperRuntime(name)])) + const validate = (value, depth = 0, state = { elements: 0, bytes: 0 }) => { + if (depth > limits.depth) throw new Error('Design JSX output is too deeply nested.') + if (value === null) { + state.bytes += 4 + if (state.bytes > limits.outputBytes) throw new Error('Design JSX output is too large.') + return state + } + if (typeof value === 'boolean') { + state.bytes += value ? 4 : 5 + if (state.bytes > limits.outputBytes) throw new Error('Design JSX output is too large.') + return state + } + if (typeof value === 'number') { + if (!Number.isFinite(value)) throw new Error('Design JSX output contains an invalid number.') + state.bytes += 8 + if (state.bytes > limits.outputBytes) throw new Error('Design JSX output is too large.') + return state + } + if (typeof value === 'string') { + if (value.length > limits.stringLength) throw new Error('Design JSX output contains a string that is too long.') + state.bytes += new TextEncoder().encode(value).byteLength + if (state.bytes > limits.outputBytes) throw new Error('Design JSX output is too large.') + return state + } + if (Array.isArray(value)) { + state.bytes += 2 + Math.max(0, value.length - 1) + if (state.bytes > limits.outputBytes) throw new Error('Design JSX output is too large.') + if (value.length > limits.arrayLength) throw new Error('Design JSX output contains an array that is too long.') + for (const item of value) validate(item, depth + 1, state) + return state + } + if (typeof value !== 'object') throw new Error('Design JSX output contains an unsupported value.') + const keys = Object.keys(value) + state.bytes += 2 + Math.max(0, keys.length - 1) + if (state.bytes > limits.outputBytes) throw new Error('Design JSX output is too large.') + if (keys.length > limits.objectKeys) throw new Error('Design JSX output contains too many object properties.') + if ('type' in value && 'props' in value && 'children' in value && ++state.elements > limits.elements) throw new Error('Design JSX output has too many elements.') + for (const key of keys) { + if (key === '__proto__' || key === 'prototype' || key === 'constructor') throw new Error('Design JSX output contains a blocked property.') + state.bytes += key.length + if (state.bytes > limits.outputBytes) throw new Error('Design JSX output is too large.') + validate(value[key], depth + 1, state) + } + return state + } + try { + const argumentNames = ['__h', '__fragment', ...names, ...helperNames] + const argumentValues = [__h, __fragment, ...names.map((name) => tags[name]), ...helperNames.map((name) => helpers[name])] + const run = new Function(...argumentNames, code) + const value = run(...argumentValues) + validate(value) + self.postMessage({ id, ok: true, value }) + } catch (error) { + self.postMessage({ id, ok: false, error: error instanceof Error ? error.message : String(error) }) + } +} +` + +export type SandboxDocumentOptions = { + elements: Record + helpers: string[] + limits: DesignJSXValidationLimits +} + +export function sandboxDocument({ elements, helpers, limits }: SandboxDocumentOptions): string { + const escapedWorkerSource = JSON.stringify(workerSource) + const escapedElements = JSON.stringify(elements) + const escapedHelpers = JSON.stringify(helpers) + const escapedLimits = JSON.stringify(limits) + return `` +} diff --git a/src/app/code/sandbox/evaluate.ts b/src/app/code/sandbox/evaluate.ts new file mode 100644 index 000000000..23fc86e5e --- /dev/null +++ b/src/app/code/sandbox/evaluate.ts @@ -0,0 +1,108 @@ +import { IS_BROWSER } from '@open-pencil/core/constants' +import { + DESIGN_JSX_ELEMENTS, + DESIGN_JSX_HELPERS, + transformDesignJSXExpression +} from '@open-pencil/core/design-jsx' + +import { sandboxDocument } from '@/app/code/sandbox/document' +import { + DESIGN_JSX_DEFAULT_READY_TIMEOUT_MS, + DESIGN_JSX_DEFAULT_TIMEOUT_MS, + DESIGN_JSX_MAX_SOURCE_BYTES, + resolveDesignJSXValidationLimits, + type DesignJSXSandboxLimits, + type DesignJSXSandboxResult +} from '@/app/code/sandbox/types' +import { validateDesignJSXOutput } from '@/app/code/sandbox/validate' + +function sourceByteLength(source: string): number { + return new TextEncoder().encode(source).byteLength +} + +export async function evaluateDesignJSX( + source: string, + limits: DesignJSXSandboxLimits = {} +): Promise { + if (!IS_BROWSER) { + return { ok: false, error: 'Design JSX execution requires a browser.' } + } + const sourceBytes = limits.sourceBytes ?? DESIGN_JSX_MAX_SOURCE_BYTES + if (sourceByteLength(source) > sourceBytes) { + return { ok: false, error: 'Design JSX source is too large.' } + } + + let code: string + try { + code = transformDesignJSXExpression(source) + } catch (error) { + return { ok: false, error: error instanceof Error ? error.message : String(error) } + } + + const validationLimits = resolveDesignJSXValidationLimits(limits) + const iframe = document.createElement('iframe') + iframe.hidden = true + iframe.setAttribute('sandbox', 'allow-scripts') + iframe.srcdoc = sandboxDocument({ + elements: Object.fromEntries( + DESIGN_JSX_ELEMENTS.map(({ name, runtimeType }) => [name, runtimeType]) + ), + helpers: DESIGN_JSX_HELPERS.map(({ name }) => name), + limits: validationLimits + }) + document.body.append(iframe) + + return new Promise((resolve) => { + const id = crypto.randomUUID() + const timeoutMs = limits.timeoutMs ?? DESIGN_JSX_DEFAULT_TIMEOUT_MS + const readyTimeoutMs = limits.readyTimeoutMs ?? DESIGN_JSX_DEFAULT_READY_TIMEOUT_MS + let settled = false + let timer: ReturnType + + const finish = (result: DesignJSXSandboxResult) => { + if (settled) return + settled = true + clearTimeout(timer) + window.removeEventListener('message', onMessage) + iframe.remove() + // eslint-disable-next-line promise/no-multiple-resolved -- settled guard makes completion single-shot + resolve(result) + } + const onMessage = (event: MessageEvent) => { + if (event.source !== iframe.contentWindow || event.origin !== 'null') return + const message = event.data as { + type?: string + id?: string + ok?: boolean + value?: unknown + error?: string + } + if (message.type === 'open-pencil-design-jsx-ready') { + clearTimeout(timer) + // eslint-disable-next-line promise/no-multiple-resolved -- finish has a settled guard + timer = setTimeout( + () => finish({ ok: false, error: 'Design JSX execution timed out.' }), + timeoutMs + ) + iframe.contentWindow?.postMessage({ type: 'open-pencil-design-jsx-run', id, code }, '*') + return + } + if (message.type !== 'open-pencil-design-jsx-result' || message.id !== id) return + if (!message.ok) { + finish({ ok: false, error: message.error ?? 'Design JSX execution failed.' }) + return + } + try { + const roots = validateDesignJSXOutput(message.value, validationLimits) + finish({ ok: true, roots }) + } catch (error) { + finish({ ok: false, error: error instanceof Error ? error.message : String(error) }) + } + } + timer = setTimeout( + () => finish({ ok: false, error: 'Design JSX sandbox did not become ready.' }), + readyTimeoutMs + ) + window.addEventListener('message', onMessage) + }) +} diff --git a/src/app/code/sandbox/types.ts b/src/app/code/sandbox/types.ts new file mode 100644 index 000000000..7b04415d6 --- /dev/null +++ b/src/app/code/sandbox/types.ts @@ -0,0 +1,50 @@ +export const DESIGN_JSX_MAX_SOURCE_BYTES = 256_000 +export const DESIGN_JSX_MAX_OUTPUT_BYTES = 2_000_000 +export const DESIGN_JSX_MAX_ELEMENTS = 5_000 +export const DESIGN_JSX_MAX_DEPTH = 100 +export const DESIGN_JSX_MAX_ARRAY_LENGTH = 5_000 +export const DESIGN_JSX_MAX_OBJECT_KEYS = 1_000 +export const DESIGN_JSX_MAX_STRING_LENGTH = 100_000 +export const DESIGN_JSX_DEFAULT_TIMEOUT_MS = 1_000 +export const DESIGN_JSX_DEFAULT_READY_TIMEOUT_MS = 5_000 + +export type DesignJSXValidationLimits = { + outputBytes: number + elements: number + depth: number + arrayLength: number + objectKeys: number + stringLength: number +} + +export type DesignJSXSandboxLimits = { + sourceBytes?: number + outputBytes?: number + elements?: number + depth?: number + arrayLength?: number + objectKeys?: number + stringLength?: number + timeoutMs?: number + readyTimeoutMs?: number +} + +export function resolveDesignJSXValidationLimits( + limits: DesignJSXSandboxLimits +): DesignJSXValidationLimits { + return { + outputBytes: limits.outputBytes ?? DESIGN_JSX_MAX_OUTPUT_BYTES, + elements: limits.elements ?? DESIGN_JSX_MAX_ELEMENTS, + depth: limits.depth ?? DESIGN_JSX_MAX_DEPTH, + arrayLength: limits.arrayLength ?? DESIGN_JSX_MAX_ARRAY_LENGTH, + objectKeys: limits.objectKeys ?? DESIGN_JSX_MAX_OBJECT_KEYS, + stringLength: limits.stringLength ?? DESIGN_JSX_MAX_STRING_LENGTH + } +} + +export type DesignJSXHelperDescriptor = { + __openPencilHelper: string + args: unknown[] +} + +export type DesignJSXSandboxResult = { ok: true; roots: unknown[] } | { ok: false; error: string } diff --git a/src/app/code/sandbox/validate.ts b/src/app/code/sandbox/validate.ts new file mode 100644 index 000000000..1d69e3bd6 --- /dev/null +++ b/src/app/code/sandbox/validate.ts @@ -0,0 +1,108 @@ +import type { DesignJSXValidationLimits } from '@/app/code/sandbox/types' + +const BLOCKED_KEYS = new Set(['__proto__', 'prototype', 'constructor']) + +type ValidationState = { + elements: number + bytes: number +} + +type PlainRecord = { [key: string]: unknown } + +function isPlainRecord(value: unknown): value is PlainRecord { + if (value === null || typeof value !== 'object' || Array.isArray(value)) return false + const prototype = Object.getPrototypeOf(value) + return prototype === Object.prototype || prototype === null +} + +function addBytes(state: ValidationState, limits: DesignJSXValidationLimits, bytes: number): void { + state.bytes += bytes + if (state.bytes > limits.outputBytes) throw new Error('Design JSX output is too large.') +} + +function validatePrimitive( + value: unknown, + limits: DesignJSXValidationLimits, + state: ValidationState +): boolean { + if (value === null) { + addBytes(state, limits, 4) + return true + } + if (typeof value === 'boolean') { + addBytes(state, limits, value ? 4 : 5) + return true + } + if (typeof value === 'number') { + if (!Number.isFinite(value)) throw new Error('Design JSX output contains an invalid number.') + addBytes(state, limits, 8) + return true + } + if (typeof value === 'string') { + if (value.length > limits.stringLength) { + throw new Error('Design JSX output contains a string that is too long.') + } + addBytes(state, limits, new TextEncoder().encode(value).byteLength) + return true + } + return false +} + +function validateRecord( + record: PlainRecord, + limits: DesignJSXValidationLimits, + state: ValidationState, + depth: number +): void { + const keys = Object.keys(record) + addBytes(state, limits, 2 + Math.max(0, keys.length - 1)) + if (keys.length > limits.objectKeys) { + throw new Error('Design JSX output contains too many object properties.') + } + for (const key of keys) { + if (BLOCKED_KEYS.has(key)) throw new Error(`Design JSX output contains blocked key "${key}".`) + } + if ('type' in record && 'props' in record && 'children' in record) { + state.elements += 1 + if (state.elements > limits.elements) + throw new Error('Design JSX output has too many elements.') + } + for (const [key, item] of Object.entries(record)) { + addBytes(state, limits, key.length) + validateValue(item, limits, state, depth + 1) + } +} + +function validateValue( + value: unknown, + limits: DesignJSXValidationLimits, + state: ValidationState, + depth: number +): void { + if (depth > limits.depth) throw new Error('Design JSX output is too deeply nested.') + if (validatePrimitive(value, limits, state)) return + if (Array.isArray(value)) { + addBytes(state, limits, 2 + Math.max(0, value.length - 1)) + if (value.length > limits.arrayLength) { + throw new Error('Design JSX output contains an array that is too long.') + } + for (const item of value) validateValue(item, limits, state, depth + 1) + return + } + if (!isPlainRecord(value)) { + throw new Error('Design JSX output must contain plain structured data only.') + } + validateRecord(value, limits, state, depth) +} + +export function validateDesignJSXOutput( + value: unknown, + limits: DesignJSXValidationLimits +): unknown[] { + if (value === undefined) throw new Error('Design JSX must return an OpenPencil element.') + const roots = Array.isArray(value) ? value : [value] + if (roots.length === 0) throw new Error('Design JSX must return an OpenPencil element.') + const state: ValidationState = { elements: 0, bytes: 0 } + validateValue(roots, limits, state, 0) + return roots +} diff --git a/src/app/code/templates.ts b/src/app/code/templates.ts new file mode 100644 index 000000000..520ad334a --- /dev/null +++ b/src/app/code/templates.ts @@ -0,0 +1,17 @@ +export type CodeSource = 'design-jsx' | 'tailwind-jsx' | 'html-css' + +export const DESIGN_JSX_STARTER_SOURCE = '' + +export const HTML_CSS_STARTER_SOURCE = ` + +
Hello
` + +export function starterSourceFor(language: CodeSource): string { + return language === 'html-css' ? HTML_CSS_STARTER_SOURCE : DESIGN_JSX_STARTER_SOURCE +} diff --git a/src/components/CodePanel.vue b/src/components/CodePanel.vue index d9557b237..7f33f0cf9 100644 --- a/src/components/CodePanel.vue +++ b/src/components/CodePanel.vue @@ -1,235 +1,299 @@