diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ce8cff520..e60744f21 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,9 @@ on: options: - core - viewer - - both + - editor + - mcp + - all bump: description: "Version bump" required: true @@ -51,56 +53,104 @@ jobs: git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - - name: Bump & publish core - if: inputs.package == 'core' || inputs.package == 'both' + - name: Bump versions and sync inter-package references + run: | + BUMP=${{ inputs.bump }} + TARGET=${{ inputs.package }} + + bump_version() { + local v=$1 + IFS='.' read -r MAJ MIN PAT <<< "$v" + if [ "$BUMP" = "major" ]; then MAJ=$((MAJ+1)); MIN=0; PAT=0; fi + if [ "$BUMP" = "minor" ]; then MIN=$((MIN+1)); PAT=0; fi + if [ "$BUMP" = "patch" ]; then PAT=$((PAT+1)); fi + echo "$MAJ.$MIN.$PAT" + } + + for pkg in core viewer editor mcp; do + if [ "$TARGET" = "$pkg" ] || [ "$TARGET" = "all" ]; then + CUR=$(jq -r '.version' packages/$pkg/package.json) + NEW=$(bump_version "$CUR") + jq --arg v "$NEW" '.version = $v' packages/$pkg/package.json > tmp.json && mv tmp.json packages/$pkg/package.json + UPPER=$(echo "$pkg" | tr '[:lower:]' '[:upper:]') + echo "${UPPER}_VERSION=$NEW" >> $GITHUB_ENV + echo "Bumped @pascal-app/$pkg: $CUR → $NEW" + fi + done + + # Sync inter-package references in peerDependencies and devDependencies. + # Anything that references a bumped @pascal-app/* package is updated to ^NEW. + for pkg in core viewer editor mcp; do + FILE=packages/$pkg/package.json + for dep in core viewer editor mcp; do + UPPER=$(echo "$dep" | tr '[:lower:]' '[:upper:]') + VAR="${UPPER}_VERSION" + VAL="${!VAR}" + [ -z "$VAL" ] && continue + jq --arg name "@pascal-app/$dep" --arg v "^$VAL" ' + if .peerDependencies[$name] then .peerDependencies[$name] = $v else . end + | if .devDependencies[$name] then .devDependencies[$name] = $v else . end + ' "$FILE" > tmp.json && mv tmp.json "$FILE" + done + done + + - name: Build & publish core + if: inputs.package == 'core' || inputs.package == 'all' working-directory: packages/core env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} run: | - BUMP=${{ inputs.bump }} - VERSION=$(jq -r '.version' package.json) - IFS='.' read -r MAJ MIN PAT <<< "$VERSION" - if [ "$BUMP" = "major" ]; then MAJ=$((MAJ+1)); MIN=0; PAT=0; fi - if [ "$BUMP" = "minor" ]; then MIN=$((MIN+1)); PAT=0; fi - if [ "$BUMP" = "patch" ]; then PAT=$((PAT+1)); fi - VERSION="$MAJ.$MIN.$PAT" - jq --arg v "$VERSION" '.version = $v' package.json > tmp.json && mv tmp.json package.json - echo "CORE_VERSION=$VERSION" >> $GITHUB_ENV - bun run build - if [ "${{ inputs.dry-run }}" = "true" ]; then - echo "🏜️ Dry run — would publish @pascal-app/core@$VERSION" + echo "🏜️ Dry run — would publish @pascal-app/core@$CORE_VERSION" npm publish --dry-run --access public else npm publish --access public - echo "📦 Published @pascal-app/core@$VERSION" + echo "📦 Published @pascal-app/core@$CORE_VERSION" fi - - name: Bump & publish viewer - if: inputs.package == 'viewer' || inputs.package == 'both' + - name: Build & publish viewer + if: inputs.package == 'viewer' || inputs.package == 'all' working-directory: packages/viewer env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} run: | - BUMP=${{ inputs.bump }} - VERSION=$(jq -r '.version' package.json) - IFS='.' read -r MAJ MIN PAT <<< "$VERSION" - if [ "$BUMP" = "major" ]; then MAJ=$((MAJ+1)); MIN=0; PAT=0; fi - if [ "$BUMP" = "minor" ]; then MIN=$((MIN+1)); PAT=0; fi - if [ "$BUMP" = "patch" ]; then PAT=$((PAT+1)); fi - VERSION="$MAJ.$MIN.$PAT" - jq --arg v "$VERSION" '.version = $v' package.json > tmp.json && mv tmp.json package.json - echo "VIEWER_VERSION=$VERSION" >> $GITHUB_ENV - bun run build + if [ "${{ inputs.dry-run }}" = "true" ]; then + echo "🏜️ Dry run — would publish @pascal-app/viewer@$VIEWER_VERSION" + npm publish --dry-run --access public + else + npm publish --access public + echo "📦 Published @pascal-app/viewer@$VIEWER_VERSION" + fi + - name: Publish editor + if: inputs.package == 'editor' || inputs.package == 'all' + working-directory: packages/editor + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | if [ "${{ inputs.dry-run }}" = "true" ]; then - echo "🏜️ Dry run — would publish @pascal-app/viewer@$VERSION" + echo "🏜️ Dry run — would publish @pascal-app/editor@$EDITOR_VERSION" npm publish --dry-run --access public else npm publish --access public - echo "📦 Published @pascal-app/viewer@$VERSION" + echo "📦 Published @pascal-app/editor@$EDITOR_VERSION" + fi + + - name: Build & publish mcp + if: inputs.package == 'mcp' || inputs.package == 'all' + working-directory: packages/mcp + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | + bun run build + if [ "${{ inputs.dry-run }}" = "true" ]; then + echo "🏜️ Dry run — would publish @pascal-app/mcp@$MCP_VERSION" + npm publish --dry-run --access public + else + npm publish --access public + echo "📦 Published @pascal-app/mcp@$MCP_VERSION" fi - name: Commit version bumps & tag @@ -118,6 +168,14 @@ jobs: PKGS="$PKGS @pascal-app/viewer@$VIEWER_VERSION" TAGS="$TAGS @pascal-app/viewer@$VIEWER_VERSION" fi + if [ -n "$EDITOR_VERSION" ]; then + PKGS="$PKGS @pascal-app/editor@$EDITOR_VERSION" + TAGS="$TAGS @pascal-app/editor@$EDITOR_VERSION" + fi + if [ -n "$MCP_VERSION" ]; then + PKGS="$PKGS @pascal-app/mcp@$MCP_VERSION" + TAGS="$TAGS @pascal-app/mcp@$MCP_VERSION" + fi git commit -m "release:${PKGS}" diff --git a/apps/editor/app/api/scenes/[id]/route.ts b/apps/editor/app/api/scenes/[id]/route.ts index 3c93f0206..1712ad4ad 100644 --- a/apps/editor/app/api/scenes/[id]/route.ts +++ b/apps/editor/app/api/scenes/[id]/route.ts @@ -173,7 +173,7 @@ function parseIfMatch(raw: string | null): number | undefined { const inner = match ? match[1] : trimmed if (!inner) return undefined const n = Number(inner) - if (!Number.isFinite(n) || !Number.isInteger(n) || n < 0) return undefined + if (!(Number.isFinite(n) && Number.isInteger(n)) || n < 0) return undefined return n } diff --git a/apps/editor/app/globals.css b/apps/editor/app/globals.css index 78ca5812e..390aa6e4a 100644 --- a/apps/editor/app/globals.css +++ b/apps/editor/app/globals.css @@ -1,5 +1,6 @@ @import "tailwindcss"; @import "tw-animate-css"; +@import "../../../styles/elevation.css"; @source "../../../packages/editor/src"; @custom-variant dark (&:is(.dark *)); diff --git a/apps/editor/app/page.tsx b/apps/editor/app/page.tsx index 72a45c41f..67924841d 100644 --- a/apps/editor/app/page.tsx +++ b/apps/editor/app/page.tsx @@ -1,13 +1,36 @@ 'use client' -import { Editor, type SidebarTab, ViewerToolbarLeft, ViewerToolbarRight } from '@pascal-app/editor' +import { + Editor, + ItemsPanel, + type SidebarTab, + ViewerToolbarLeft, + ViewerToolbarRight, +} from '@pascal-app/editor' +import { Layers, Package, Settings } from 'lucide-react' import Link from 'next/link' -const SIDEBAR_TABS: (SidebarTab & { component: React.ComponentType })[] = [ +const SIDEBAR_TABS = [ { id: 'site', label: 'Scene', - component: () => null, // Built-in SitePanel handles this + component: () => null, + mobileDefaultSnap: 0.5, + mobileIcon: , + }, + { + id: 'items', + label: 'Items', + component: ItemsPanel, + mobileDefaultSnap: 0.5, + mobileIcon: , + }, + { + id: 'settings', + label: 'Settings', + component: () => null, + mobileDefaultSnap: 0.5, + mobileIcon: , }, ] diff --git a/apps/editor/env.mjs b/apps/editor/env.mjs index 141ce000e..64f1d9d39 100644 --- a/apps/editor/env.mjs +++ b/apps/editor/env.mjs @@ -1,8 +1,8 @@ /** * Environment variable validation for the editor app. * - * This file validates that required environment variables are set at runtime. - * Variables are defined in the root .env file. + * This file validates environment variables used by the standalone editor app. + * Values are loaded from the repo root .env.local by package scripts. * * @see https://env.t3.gg/docs/nextjs */ @@ -13,42 +13,21 @@ export const env = createEnv({ /** * Server-side environment variables (not exposed to client) */ - server: { - // Database - POSTGRES_URL: z.string().min(1), - SUPABASE_SERVICE_ROLE_KEY: z.string().min(1), - - // Auth - BETTER_AUTH_SECRET: z.string().min(1), - GOOGLE_CLIENT_ID: z.string().optional(), - GOOGLE_CLIENT_SECRET: z.string().optional(), - - // Email - RESEND_API_KEY: z.string().optional(), - }, + server: {}, /** * Client-side environment variables (exposed to browser via NEXT_PUBLIC_) */ client: { - NEXT_PUBLIC_SUPABASE_URL: z.string().min(1), - NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().optional(), + NEXT_PUBLIC_ASSETS_CDN_URL: z.string().optional(), }, /** * Runtime values - pulls from process.env */ runtimeEnv: { - // Server - POSTGRES_URL: process.env.POSTGRES_URL, - SUPABASE_SERVICE_ROLE_KEY: process.env.SUPABASE_SERVICE_ROLE_KEY, - BETTER_AUTH_SECRET: process.env.BETTER_AUTH_SECRET, - GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID, - GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET, - RESEND_API_KEY: process.env.RESEND_API_KEY, - // Client - NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL, - NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, + NEXT_PUBLIC_ASSETS_CDN_URL: + process.env.NEXT_PUBLIC_ASSETS_CDN_URL ?? process.env.NEXT_PUBLIC_EDITOR_ASSETS_CDN_URL, }, /** diff --git a/apps/editor/next.config.ts b/apps/editor/next.config.ts index 6c684f1bf..0120eaf4c 100644 --- a/apps/editor/next.config.ts +++ b/apps/editor/next.config.ts @@ -1,6 +1,9 @@ import type { NextConfig } from 'next' const nextConfig: NextConfig = { + logging: { + browserToTerminal: true, + }, typescript: { ignoreBuildErrors: true, }, diff --git a/apps/editor/package.json b/apps/editor/package.json index e8b713347..21e8dad85 100644 --- a/apps/editor/package.json +++ b/apps/editor/package.json @@ -4,8 +4,8 @@ "type": "module", "private": true, "scripts": { - "dev": "dotenv -e ./.env.local --override -- next dev --port 3002", - "build": "dotenv -e ./.env.local --override -- next build", + "dev": "dotenv -e ../../.env.local -- next dev --port 3002", + "build": "dotenv -e ../../.env.local -- next build", "start": "next start", "lint": "biome lint", "check-types": "next typegen && tsc --noEmit" @@ -21,6 +21,7 @@ "@tailwindcss/postcss": "^4.2.1", "clsx": "^2.1.1", "geist": "^1.7.0", + "lucide-react": "^1.7.0", "next": "16.2.1", "postcss": "^8.5.6", "react": "^19.2.4", @@ -37,9 +38,9 @@ "@types/react": "19.2.2", "@types/react-dom": "19.2.2", "agentation": "^2.3.2", - "react-grab": "^0.1.25", + "react-grab": "^0.1.29", "react-scan": "^0.5.3", "tw-animate-css": "^1.4.0", - "typescript": "5.9.3" + "typescript": "6.0.2" } } diff --git a/apps/editor/public/audios/sfx/snapshot_capture.mp3 b/apps/editor/public/audios/sfx/snapshot_capture.mp3 new file mode 100644 index 000000000..f2cc2d9d5 Binary files /dev/null and b/apps/editor/public/audios/sfx/snapshot_capture.mp3 differ diff --git a/apps/editor/public/icons/spawn-point.svg b/apps/editor/public/icons/spawn-point.svg new file mode 100644 index 000000000..1e48cde50 --- /dev/null +++ b/apps/editor/public/icons/spawn-point.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/editor/vercel.json b/apps/editor/vercel.json new file mode 100644 index 000000000..45691b1dd --- /dev/null +++ b/apps/editor/vercel.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "buildCommand": "cd ../.. && npx -y bun@1.3.13 run build --filter=editor", + "installCommand": "cd ../.. && npx -y bun@1.3.13 install --frozen-lockfile", + "outputDirectory": ".next", + "cleanUrls": true, + "trailingSlash": false, + "bunVersion": "1.x" +} diff --git a/bun.lock b/bun.lock index 6479db97d..00c23b30c 100644 --- a/bun.lock +++ b/bun.lock @@ -1,6 +1,6 @@ { "lockfileVersion": 1, - "configVersion": 0, + "configVersion": 1, "workspaces": { "": { "name": "editor", @@ -8,9 +8,19 @@ "@biomejs/biome": "^2.4.6", "dotenv-cli": "^11.0.0", "turbo": "^2.8.15", - "typescript": "5.9.3", + "typescript": "6.0.2", "ultracite": "^7.2.5", }, + "optionalDependencies": { + "@tailwindcss/oxide-darwin-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-x64": "4.3.0", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + }, }, "apps/editor": { "name": "editor", @@ -26,6 +36,7 @@ "@tailwindcss/postcss": "^4.2.1", "clsx": "^2.1.1", "geist": "^1.7.0", + "lucide-react": "^1.7.0", "next": "16.2.1", "postcss": "^8.5.6", "react": "^19.2.4", @@ -42,15 +53,15 @@ "@types/react": "19.2.2", "@types/react-dom": "19.2.2", "agentation": "^2.3.2", - "react-grab": "^0.1.25", + "react-grab": "^0.1.29", "react-scan": "^0.5.3", "tw-animate-css": "^1.4.0", - "typescript": "5.9.3", + "typescript": "6.0.2", }, }, "packages/core": { "name": "@pascal-app/core", - "version": "0.6.1", + "version": "0.7.0", "dependencies": { "dedent": "^1.7.1", "idb-keyval": "^6.2.2", @@ -62,9 +73,10 @@ }, "devDependencies": { "@pascal/typescript-config": "*", + "@types/bun": "^1.3.0", "@types/react": "^19.2.2", "@types/three": "^0.184.0", - "typescript": "5.9.3", + "typescript": "6.0.2", }, "peerDependencies": { "@react-three/drei": "^10", @@ -75,7 +87,7 @@ }, "packages/editor": { "name": "@pascal-app/editor", - "version": "0.6.1", + "version": "0.7.0", "dependencies": { "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", @@ -100,29 +112,29 @@ "clsx": "^2.1.1", "cmdk": "^1.1.1", "howler": "^2.2.4", - "lucide-react": "^0.562.0", + "lucide-react": "^1.7.0", "mitt": "^3.0.1", "motion": "^12.34.3", "nanoid": "^5.1.6", "tailwind-merge": "^3.5.0", - "three-mesh-bvh": "^0.9.8", + "three-mesh-bvh": "~0.9.8", "zod": "^4.3.6", "zustand": "^5.0.11", }, "devDependencies": { - "@pascal-app/core": "^0.6.0", - "@pascal-app/viewer": "^0.6.0", + "@pascal-app/core": "^0.7.0", + "@pascal-app/viewer": "^0.7.0", "@pascal/typescript-config": "*", + "@types/bun": "^1.3.0", "@types/howler": "^2.2.12", - "@types/node": "^22.19.12", "@types/react": "19.2.2", "@types/react-dom": "19.2.2", "@types/three": "^0.184.0", - "typescript": "5.9.3", + "typescript": "6.0.2", }, "peerDependencies": { - "@pascal-app/core": "^0.6.0", - "@pascal-app/viewer": "^0.6.0", + "@pascal-app/core": "^0.7.0", + "@pascal-app/viewer": "^0.7.0", "@react-three/drei": "^10", "@react-three/fiber": "^9", "next": ">=15", @@ -159,13 +171,13 @@ "zod": "^4.3.5", }, "devDependencies": { - "@pascal-app/core": "workspace:*", + "@pascal-app/core": "^0.7.0", "@pascal/typescript-config": "*", "@types/node": "^25.5.0", "typescript": "5.9.3", }, "peerDependencies": { - "@pascal-app/core": "workspace:*", + "@pascal-app/core": "^0.7.0", }, }, "packages/typescript-config": { @@ -191,7 +203,7 @@ }, "packages/viewer": { "name": "@pascal-app/viewer", - "version": "0.6.1", + "version": "0.7.0", "dependencies": { "polygon-clipping": "^0.15.7", "three-bvh-csg": "^0.0.18", @@ -200,13 +212,13 @@ }, "devDependencies": { "@pascal/typescript-config": "*", - "@types/node": "^25.5.0", + "@types/node": "^22", "@types/react": "^19.2.2", "@types/three": "^0.184.0", - "typescript": "5.9.3", + "typescript": "6.0.2", }, "peerDependencies": { - "@pascal-app/core": "^0.6.0", + "@pascal-app/core": "^0.7.0", "@react-three/drei": "^10", "@react-three/fiber": "^9", "react": "^18 || ^19", @@ -218,14 +230,19 @@ "version": "0.0.0", }, }, + "overrides": { + "@types/react": "19.2.14", + "@types/react-dom": "19.2.3", + "@types/three": "0.184.0", + }, "packages": { "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], - "@antfu/ni": ["@antfu/ni@0.23.2", "", { "bin": { "na": "bin/na.mjs", "ni": "bin/ni.mjs", "nr": "bin/nr.mjs", "nu": "bin/nu.mjs", "nci": "bin/nci.mjs", "nlx": "bin/nlx.mjs", "nun": "bin/nun.mjs" } }, "sha512-FSEVWXvwroExDXUu8qV6Wqp2X3D1nJ0Li4LFymCyvCVrm7I3lNfG0zZWSWvGU1RE7891eTnFTyh31L3igOwNKQ=="], + "@antfu/ni": ["@antfu/ni@30.1.0", "", { "dependencies": { "fzf": "^0.5.2", "package-manager-detector": "^1.6.0", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15" }, "bin": { "ni": "bin/ni.mjs", "nci": "bin/nci.mjs", "nr": "bin/nr.mjs", "nup": "bin/nup.mjs", "nd": "bin/nd.mjs", "nlx": "bin/nlx.mjs", "na": "bin/na.mjs", "nun": "bin/nun.mjs" } }, "sha512-3VuAbPjgY52rQNn4wABaXMhBU2Oq91uy6L8nX49eJ35OLI68CyckGU+HZxcaHix4ymuGM2nFL1D6sLpgODK5xw=="], "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], - "@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="], + "@babel/compat-data": ["@babel/compat-data@7.29.3", "", {}, "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg=="], "@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="], @@ -247,7 +264,7 @@ "@babel/helpers": ["@babel/helpers@7.29.2", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.29.0" } }, "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw=="], - "@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="], + "@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="], "@babel/runtime": ["@babel/runtime@7.29.2", "", {}, "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="], @@ -257,27 +274,27 @@ "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], - "@biomejs/biome": ["@biomejs/biome@2.4.9", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.9", "@biomejs/cli-darwin-x64": "2.4.9", "@biomejs/cli-linux-arm64": "2.4.9", "@biomejs/cli-linux-arm64-musl": "2.4.9", "@biomejs/cli-linux-x64": "2.4.9", "@biomejs/cli-linux-x64-musl": "2.4.9", "@biomejs/cli-win32-arm64": "2.4.9", "@biomejs/cli-win32-x64": "2.4.9" }, "bin": { "biome": "bin/biome" } }, "sha512-wvZW92FrwitTcacvCBT8xdAbfbxWfDLwjYMmU3djjqQTh7Ni4ZdiWIT/x5VcZ+RQuxiKzIOzi5D+dcyJDFZMsA=="], + "@biomejs/biome": ["@biomejs/biome@2.4.15", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.15", "@biomejs/cli-darwin-x64": "2.4.15", "@biomejs/cli-linux-arm64": "2.4.15", "@biomejs/cli-linux-arm64-musl": "2.4.15", "@biomejs/cli-linux-x64": "2.4.15", "@biomejs/cli-linux-x64-musl": "2.4.15", "@biomejs/cli-win32-arm64": "2.4.15", "@biomejs/cli-win32-x64": "2.4.15" }, "bin": { "biome": "bin/biome" } }, "sha512-j5VH3a/h/HXTKBM50MDMxRCzkeLv9S2XJcW2WgnZT1+xyisi+0bISrXR82gCX+8S9lvK0skEvHJRN+3Ktr2hlw=="], - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-d5G8Gf2RpH5pYwiHLPA+UpG3G9TLQu4WM+VK6sfL7K68AmhcEQ9r+nkj/DvR/GYhYox6twsHUtmWWWIKfcfQQA=="], + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-rF3PPqLq1yoST79zaQbDjVJwsuIeci/O+9bgNmC5QpgOqz6aqYuzA4abyAGx+mgyiDXn4A049xAN8gijbuR1Qg=="], - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-LNCLNgqDMG7BLdc3a8aY/dwKPK7+R8/JXJoXjCvZh2gx8KseqBdFDKbhrr7HCWF8SzNhbTaALhTBoh/I6rf9lA=="], + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-/5KHXYMfSJs1fNXiX30xFtI8JcCFV6zaVVLxOa0M2sfqBKHkpQhRTv94yxQWxeTY2lzo2OuTlNvPC+hDQt2wcQ=="], - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-4adnkAUi6K4C/emPRgYznMOcLlUqZdXWM6aIui4VP4LraE764g6Q4YguygnAUoxKjKIXIWPteKMgRbN0wsgwcg=="], + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-owaAMZD/T4LrD0ELNCk0Km3qrRHuM0X6EAyVE1FSqGY0rbLoiDLrO4Us2tllm6cAeB2Ioa9C2C08NZPdr8+0Ug=="], - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-8RCww5xnPn2wpK4L/QDGDOW0dq80uVWfppPxHIUg6mOs9B6gRmqPp32h1Ls3T8GnW8Wo5A8u7vpTwz4fExN+sw=="], + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZPcxznxm0pogHBLZhYntyR3sR+MrZjqJIKEr7ZqVen0Rl+P/4upVmfYXjftizi9RoqZntg33fv/1fbdhbYXpEQ=="], - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.9", "", { "os": "linux", "cpu": "x64" }, "sha512-L10na7POF0Ks/cgLFNF1ZvIe+X4onLkTi5oP9hY+Rh60Q+7fWzKDDCeGyiHUFf1nGIa9dQOOUPGe2MyYg8nMSQ=="], + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.15", "", { "os": "linux", "cpu": "x64" }, "sha512-0jj7THz12GbUOLmMibktK6DZjqz2zV64KFxyBtcFTKPiiOIY0a7vns1elpO1dERvxpsZ5ik0oFfz0oGwFde1+g=="], - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.9", "", { "os": "linux", "cpu": "x64" }, "sha512-5TD+WS9v5vzXKzjetF0hgoaNFHMcpQeBUwKKVi3JbG1e9UCrFuUK3Gt185fyTzvRdwYkJJEMqglRPjmesmVv4A=="], + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.15", "", { "os": "linux", "cpu": "x64" }, "sha512-CNq/9W38SYSH023lfcQ4KKU8K0YX8T//FZUhcgtMMRABDojx5XsMV7jlweAvGSl389wJQB29Qo6Zb/a+jdvt+w=="], - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-aDZr0RBC3sMGJOU10BvG7eZIlWLK/i51HRIfScE2lVhfts2dQTreowLiJJd+UYg/tHKxS470IbzpuKmd0MiD6g=="], + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-ouhkYdlhp/1GghEJPdWwD/Vi3gQ1nFxuSpMolWsbq3Lsq3QUR4jl6UdhhscdCugKU5vOEuMiJhvKj66O0OCq+w=="], - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.9", "", { "os": "win32", "cpu": "x64" }, "sha512-NS4g/2G9SoQ4ktKtz31pvyc/rmgzlcIDCGU/zWbmHJAqx6gcRj2gj5Q/guXhoWTzCUaQZDIqiCQXHS7BcGYc0w=="], + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.15", "", { "os": "win32", "cpu": "x64" }, "sha512-zBrGq5mx5wwpnow4+2BxUvleDM+GNd4sLbPaMapsSLQLD0NGRCquqPBTgN+7XkUteHvj7M+BstuI8tmnV7+HgQ=="], - "@clack/core": ["@clack/core@1.1.0", "", { "dependencies": { "sisteransi": "^1.0.5" } }, "sha512-SVcm4Dqm2ukn64/8Gub2wnlA5nS2iWJyCkdNHcvNHPIeBTGojpdJ+9cZKwLfmqy7irD4N5qLteSilJlE0WLAtA=="], + "@clack/core": ["@clack/core@1.3.0", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-xJPHpAmEQUBrXSLx0gF+q5K/IyihXpsHZcha+jB+tyahsKRK3Dxo4D0coZDewHo12NhiuzC3dTtMPbm53GEAAA=="], - "@clack/prompts": ["@clack/prompts@1.1.0", "", { "dependencies": { "@clack/core": "1.1.0", "sisteransi": "^1.0.5" } }, "sha512-pkqbPGtohJAvm4Dphs2M8xE29ggupihHdy1x84HNojZuMtFsHiUlRvqD24tM2+XmI+61LlfNceM3Wr7U5QES5g=="], + "@clack/prompts": ["@clack/prompts@1.3.0", "", { "dependencies": { "@clack/core": "1.3.0", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-GgcWwRCs/xPtaqlMy8qRhPnZf9vlWcWZNHAitnVQ3yk7JmSralSiq5q07yaffYE8SogtDm7zFeKccx1QNVARpw=="], "@dimforge/rapier3d-compat": ["@dimforge/rapier3d-compat@0.12.0", "", {}, "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow=="], @@ -289,59 +306,7 @@ "@dnd-kit/utilities": ["@dnd-kit/utilities@3.2.2", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg=="], - "@emnapi/runtime": ["@emnapi/runtime@1.9.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="], - - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], - - "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], - - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], - - "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], - - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], - - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], - - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], - - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], - - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], - - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], - - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], - - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], - - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], - - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], - - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], - - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], - - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], - - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], - - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], - - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], - - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], - - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], - - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], - - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], - - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], - - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + "@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="], @@ -371,9 +336,11 @@ "@hono/node-server": ["@hono/node-server@1.19.14", "", { "peerDependencies": { "hono": "^4" } }, "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw=="], - "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], + "@humanfs/core": ["@humanfs/core@0.19.2", "", { "dependencies": { "@humanfs/types": "^0.15.0" } }, "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA=="], - "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], + "@humanfs/node": ["@humanfs/node@0.16.8", "", { "dependencies": { "@humanfs/core": "^0.19.2", "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ=="], + + "@humanfs/types": ["@humanfs/types@0.15.0", "", {}, "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q=="], "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], @@ -445,15 +412,13 @@ "@mediapipe/tasks-vision": ["@mediapipe/tasks-vision@0.10.17", "", {}, "sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg=="], - "@medv/finder": ["@medv/finder@4.0.2", "", {}, "sha512-RraNY9SCcx4KZV0Dh6BEW6XEW2swkqYca74pkFFRw6hHItSHiy+O/xMnpbofjYbzXj0tSpBGthUF1hHTsr3vIQ=="], - "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="], "@monogrid/gainmap-js": ["@monogrid/gainmap-js@3.4.0", "", { "dependencies": { "promise-worker-transferable": "^1.0.4" }, "peerDependencies": { "three": ">= 0.159.0" } }, "sha512-2Z0FATFHaoYJ8b+Y4y4Hgfn3FRFwuU5zRrk+9dFWp4uGAdHGqVEdP7HP+gLA3X469KXHmfupJaUbKo1b/aDKIg=="], "@next/env": ["@next/env@16.2.1", "", {}, "sha512-n8P/HCkIWW+gVal2Z8XqXJ6aB3J0tuM29OcHpCsobWlChH/SITBs1DFBk/HajgrwDkqqBXPbuUuzgDvUekREPg=="], - "@next/eslint-plugin-next": ["@next/eslint-plugin-next@15.5.14", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-ogBjgsFrPPz19abP3VwcYSahbkUOMMvJjxCOYWYndw+PydeMuLuB4XrvNkNutFrTjC9St2KFULRdKID8Sd/CMQ=="], + "@next/eslint-plugin-next": ["@next/eslint-plugin-next@15.5.18", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-w4MYq8M26a8PNrfto0JosLf5/3ssln1rsyP96g2DkC8uFVymStM5DLSz5ElxxrPRg2XnTMnFo3kREFlhYvxhWw=="], "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BwZ8w8YTaSEr2HIuXLMLxIdElNMPvY9fLqb20LX9A9OMGtJilhHLbCL3ggyd0TwjmMcTxi0XXt+ur1vWUoxj2Q=="], @@ -489,19 +454,19 @@ "@pascal/typescript-config": ["@pascal/typescript-config@workspace:tooling/typescript"], - "@pmndrs/msdfonts": ["@pmndrs/msdfonts@1.0.64", "", {}, "sha512-fyDNvCIGUTUYxDOzQnLOAGEuDQbyjx1JUlEYrjHB8KLDWzVl0j2SoFiLN4AwahEh4pXiA7XdJBNneQKnfMa/Bw=="], + "@pmndrs/msdfonts": ["@pmndrs/msdfonts@1.0.67", "", {}, "sha512-tBEK+suALkhZ5i9rJBR0PYx4uvDD0nVQGoEENBMLTmeL1kNzhbJ7+mfAv1ATW1w42TOuREI9FfAa5ca1N8pcxQ=="], "@pmndrs/pointer-events": ["@pmndrs/pointer-events@6.6.29", "", {}, "sha512-o4YD6VfJgDYjFgde/YyAw2X5KY454tdmOXrHGOvKTWJBHzkL90B5vH4rqmexwRVvaDfT3YLvVh/Dm5cBbgZXMg=="], - "@pmndrs/uikit": ["@pmndrs/uikit@1.0.64", "", { "dependencies": { "@pmndrs/msdfonts": "^1.0.64", "@pmndrs/uikit-pub-sub": "^1.0.64", "@preact/signals-core": "^1.5.1", "@zappar/msdf-generator": "^1.2.4", "yoga-layout": "^3.2.1" }, "peerDependencies": { "three": ">=0.162" } }, "sha512-FQgF8sf46U6fmHnqOn32S04lnWiXVxQO6MatL7FgoLKJ74YIBgEHgwKHRwltgLrN/rcTu7LgJm7LgvXvMccEGA=="], + "@pmndrs/uikit": ["@pmndrs/uikit@1.0.67", "", { "dependencies": { "@pmndrs/msdfonts": "^1.0.67", "@pmndrs/uikit-pub-sub": "^1.0.67", "@preact/signals-core": "^1.5.1", "@zappar/msdf-generator": "^1.2.4", "yoga-layout": "^3.2.1" }, "peerDependencies": { "three": ">=0.162" } }, "sha512-36zaHVHxRPSWgr78r1GUEP0wcfC2hvx1a5t459Yx9IOYqolKCCloCticoSDCQHNnEbeC53xSDMbb70dz8IIljA=="], - "@pmndrs/uikit-lucide": ["@pmndrs/uikit-lucide@1.0.64", "", { "dependencies": { "@pmndrs/uikit": "^1.0.64" } }, "sha512-WU9CqboaBJHLvAk3Qt6V+7oMSk2lafY/ZwdcjuwSt3U+ZExHMnM24APeBdwi6w0e0sER2I/qiSVDs26JPOg7ew=="], + "@pmndrs/uikit-lucide": ["@pmndrs/uikit-lucide@1.0.67", "", { "dependencies": { "@pmndrs/uikit": "^1.0.67" } }, "sha512-/qJlNX/xd+KsbS2SdU/7VBGGk67VS4tx2X/2A6FeMa2L2/NsvLjs7mReoRLR2DvKVHsz6GxPJd0m3uSoRjuELw=="], - "@pmndrs/uikit-pub-sub": ["@pmndrs/uikit-pub-sub@1.0.64", "", { "dependencies": { "@preact/signals-core": "^1.8.0" } }, "sha512-+hlc5aWC3wQlA/zihwh+OGSm3aY9O+L/TEuNLwXw6R8syHMv7obqeY6s/hcjzD92k6DRQZLhVzVy+yqqVKAbRA=="], + "@pmndrs/uikit-pub-sub": ["@pmndrs/uikit-pub-sub@1.0.67", "", { "dependencies": { "@preact/signals-core": "^1.8.0" } }, "sha512-WC14YjujQ+gIliaEN06/7zygIwxKzweasNicwqVjTQ1PRJLe/HK9pyyS7DBAtfUy9y/9i5dfVFJLhGI8R61dWA=="], "@preact/signals": ["@preact/signals@1.3.4", "", { "dependencies": { "@preact/signals-core": "^1.7.0" }, "peerDependencies": { "preact": "10.x" } }, "sha512-TPMkStdT0QpSc8FpB63aOwXoSiZyIrPsP9Uj347KopdS6olZdAYeeird/5FZv/M1Yc1ge5qstub2o8VDbvkT4g=="], - "@preact/signals-core": ["@preact/signals-core@1.14.0", "", {}, "sha512-AowtCcCU/33lFlh1zRFf/u+12rfrhtNakj7UpaGEsmMwUKpKWMVvcktOGcwBBNiB4lWrZWc01LhiyyzVklJyaQ=="], + "@preact/signals-core": ["@preact/signals-core@1.14.1", "", {}, "sha512-vxPpfXqrwUe9lpjqfYNjAF/0RF/eFGeLgdJzdmIIZjpOnTmGmAB4BjWone562mJGMRP4frU6iZ6ei3PDsu52Ng=="], "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], @@ -581,15 +546,15 @@ "@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="], - "@react-grab/cli": ["@react-grab/cli@0.1.29", "", { "dependencies": { "@antfu/ni": "^0.23.0", "commander": "^14.0.0", "ignore": "^7.0.5", "jsonc-parser": "^3.3.1", "ora": "^8.2.0", "picocolors": "^1.1.1", "prompts": "^2.4.2", "smol-toml": "^1.6.0" }, "bin": { "react-grab": "dist/cli.js" } }, "sha512-idgTec/ganPsag5BvbR3srF42UNNoUwHY+p94YORk5sCBl9ZaGJOXcDO93F8M4wruzTqIZgf1MUeY0+sTo0fVA=="], + "@react-grab/cli": ["@react-grab/cli@0.1.33", "", { "dependencies": { "@antfu/ni": "^30.1.0", "commander": "^14.0.3", "ignore": "^7.0.5", "jsonc-parser": "^3.3.1", "ora": "^9.4.0", "picocolors": "^1.1.1", "prompts": "^2.4.2", "smol-toml": "^1.6.1" }, "bin": { "react-grab": "bin/cli.js" } }, "sha512-UOc3PwN11Osw0NzaxRLK8trP4X+5iW1Dst3gvHRCafe3wXHyadzHYH8H1hdkcXdlIx3gsoD9ASJ+G/JH+A/jqA=="], "@react-three/drei": ["@react-three/drei@10.7.7", "", { "dependencies": { "@babel/runtime": "^7.26.0", "@mediapipe/tasks-vision": "0.10.17", "@monogrid/gainmap-js": "^3.0.6", "@use-gesture/react": "^10.3.1", "camera-controls": "^3.1.0", "cross-env": "^7.0.3", "detect-gpu": "^5.0.56", "glsl-noise": "^0.0.0", "hls.js": "^1.5.17", "maath": "^0.10.8", "meshline": "^3.3.1", "stats-gl": "^2.2.8", "stats.js": "^0.17.0", "suspend-react": "^0.1.3", "three-mesh-bvh": "^0.8.3", "three-stdlib": "^2.35.6", "troika-three-text": "^0.52.4", "tunnel-rat": "^0.1.2", "use-sync-external-store": "^1.4.0", "utility-types": "^3.11.0", "zustand": "^5.0.1" }, "peerDependencies": { "@react-three/fiber": "^9.0.0", "react": "^19", "react-dom": "^19", "three": ">=0.159" }, "optionalPeers": ["react-dom"] }, "sha512-ff+J5iloR0k4tC++QtD/j9u3w5fzfgFAWDtAGQah9pF2B1YgOq/5JxqY0/aVoQG5r3xSZz0cv5tk2YuBob4xEQ=="], - "@react-three/fiber": ["@react-three/fiber@9.5.0", "", { "dependencies": { "@babel/runtime": "^7.17.8", "@types/webxr": "*", "base64-js": "^1.5.1", "buffer": "^6.0.3", "its-fine": "^2.0.0", "react-use-measure": "^2.1.7", "scheduler": "^0.27.0", "suspend-react": "^0.1.3", "use-sync-external-store": "^1.4.0", "zustand": "^5.0.3" }, "peerDependencies": { "expo": ">=43.0", "expo-asset": ">=8.4", "expo-file-system": ">=11.0", "expo-gl": ">=11.0", "react": ">=19 <19.3", "react-dom": ">=19 <19.3", "react-native": ">=0.78", "three": ">=0.156" }, "optionalPeers": ["expo", "expo-asset", "expo-file-system", "expo-gl", "react-dom", "react-native"] }, "sha512-FiUzfYW4wB1+PpmsE47UM+mCads7j2+giRBltfwH7SNhah95rqJs3ltEs9V3pP8rYdS0QlNne+9Aj8dS/SiaIA=="], + "@react-three/fiber": ["@react-three/fiber@9.6.1", "", { "dependencies": { "@babel/runtime": "^7.17.8", "@types/webxr": "*", "base64-js": "^1.5.1", "buffer": "^6.0.3", "its-fine": "^2.0.0", "react-use-measure": "^2.1.7", "scheduler": "^0.27.0", "suspend-react": "^0.1.3", "use-sync-external-store": "^1.4.0", "zustand": "^5.0.3" }, "peerDependencies": { "expo": ">=43.0", "expo-asset": ">=8.4", "expo-file-system": ">=11.0", "expo-gl": ">=11.0", "react": ">=19 <19.3", "react-dom": ">=19 <19.3", "react-native": ">=0.78", "three": ">=0.156" }, "optionalPeers": ["expo", "expo-asset", "expo-file-system", "expo-gl", "react-dom", "react-native"] }, "sha512-zF0rsKcVYpcJwbFEnv2HkHX9cvOEgsfQo/X8lwmR2dn13S4qEQJXir9fxf5js2LQFoXqxOY7MDkOkYx2uZ4gSg=="], - "@react-three/uikit": ["@react-three/uikit@1.0.64", "", { "dependencies": { "@pmndrs/pointer-events": "^6.6.29", "@pmndrs/uikit": "^1.0.64", "@preact/signals-core": "^1.5.1", "suspend-react": "^0.1.3", "zustand": "^5.0.6" }, "peerDependencies": { "@react-three/fiber": ">=8", "react": ">=18" } }, "sha512-//wUWePJHHHWgDokWs2WUs8yUqOJ1Dy/g1OufGHw/BeTujtoNG0hmuISjdeDXZOQPfNjpMgrbemfmvryTeCfLg=="], + "@react-three/uikit": ["@react-three/uikit@1.0.67", "", { "dependencies": { "@pmndrs/pointer-events": "^6.6.29", "@pmndrs/uikit": "^1.0.67", "@preact/signals-core": "^1.5.1", "suspend-react": "^0.1.3", "zustand": "^5.0.6" }, "peerDependencies": { "@react-three/fiber": ">=8", "react": ">=18" } }, "sha512-yq0dL8S0NDY2ffudRtnEGvrntRAJPwMGKWnJFZdbulP1AXgTwpiSV4ES6zlwjiXL4or3FiOeV0Vz3T94SGUSMw=="], - "@react-three/uikit-lucide": ["@react-three/uikit-lucide@1.0.64", "", { "dependencies": { "@pmndrs/uikit-lucide": "^1.0.64", "@react-three/uikit": "^1.0.64" } }, "sha512-7csHVPDa6OoKog3HrRo4E9OuC4n98Fj9iDsZnNbNUt0YjocoUCBygCStSqaFcZnppgy78eMk1qS6nfu78R0AZw=="], + "@react-three/uikit-lucide": ["@react-three/uikit-lucide@1.0.67", "", { "dependencies": { "@pmndrs/uikit-lucide": "^1.0.67", "@react-three/uikit": "^1.0.67" } }, "sha512-MsJYwLFibaX3aa8FFiJPz1Vm9tIAXyrWxIdkiaigKPlaM3yIo7DkTY1E5Xkq10X+EP7f48UjNDa1cbX0eqXo6g=="], "@repo/eslint-config": ["@repo/eslint-config@workspace:packages/eslint-config"], @@ -601,65 +566,67 @@ "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], - "@tailwindcss/node": ["@tailwindcss/node@4.2.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.2" } }, "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA=="], + "@tailwindcss/node": ["@tailwindcss/node@4.3.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.21.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.0" } }, "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g=="], - "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.2", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.2", "@tailwindcss/oxide-darwin-arm64": "4.2.2", "@tailwindcss/oxide-darwin-x64": "4.2.2", "@tailwindcss/oxide-freebsd-x64": "4.2.2", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", "@tailwindcss/oxide-linux-x64-musl": "4.2.2", "@tailwindcss/oxide-wasm32-wasi": "4.2.2", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" } }, "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg=="], + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.0", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.0", "@tailwindcss/oxide-darwin-arm64": "4.3.0", "@tailwindcss/oxide-darwin-x64": "4.3.0", "@tailwindcss/oxide-freebsd-x64": "4.3.0", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", "@tailwindcss/oxide-linux-x64-musl": "4.3.0", "@tailwindcss/oxide-wasm32-wasi": "4.3.0", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" } }, "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg=="], - "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.2", "", { "os": "android", "cpu": "arm64" }, "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg=="], + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.0", "", { "os": "android", "cpu": "arm64" }, "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng=="], - "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg=="], + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ=="], - "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw=="], + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.3.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA=="], - "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ=="], + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.3.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ=="], - "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2", "", { "os": "linux", "cpu": "arm" }, "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ=="], + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0", "", { "os": "linux", "cpu": "arm" }, "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA=="], - "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw=="], + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg=="], - "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag=="], + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ=="], - "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg=="], + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ=="], - "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ=="], + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg=="], - "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.2", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q=="], + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.3.0", "", { "dependencies": { "@emnapi/core": "^1.10.0", "@emnapi/runtime": "^1.10.0", "@emnapi/wasi-threads": "^1.2.1", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA=="], - "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ=="], + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.3.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ=="], - "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA=="], + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA=="], - "@tailwindcss/postcss": ["@tailwindcss/postcss@4.2.2", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.2.2", "@tailwindcss/oxide": "4.2.2", "postcss": "^8.5.6", "tailwindcss": "4.2.2" } }, "sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ=="], + "@tailwindcss/postcss": ["@tailwindcss/postcss@4.3.0", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.3.0", "@tailwindcss/oxide": "4.3.0", "postcss": "^8.5.10", "tailwindcss": "4.3.0" } }, "sha512-Jm05Tjx+9yCLGv5qw1c+84Psds8MnyrEQYCB+FFk2lgGiUjlRqdxke4mVTuYrj2xnVZqKim2Apr5ySuQRYAw/w=="], - "@turbo/darwin-64": ["@turbo/darwin-64@2.8.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-FQ9EX1xMU5nbwjxXxM3yU88AQQ6Sqc6S44exPRroMcx9XZHqqppl5ymJF0Ig/z3nvQNwDmz1Gsnvxubo+nXWjQ=="], + "@turbo/darwin-64": ["@turbo/darwin-64@2.9.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-eu3eFRmE9NjgZ0wPdRJ44l+LGSeIky+tz5ZQd8zQkw/Yqi+BM7wq+8nbabeoiVUcICi/IZweMOKl/MCmkrd1+g=="], - "@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.8.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Gpyh9ATFGThD6/s9L95YWY54cizg/VRWl2B67h0yofG8BpHf67DFAh9nuJVKG7bY0+SBJDAo5cMur+wOl9YOYw=="], + "@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.9.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RUkAE404z/J8NsyrUosMcBaXT6M4bRFxTQrmkDQBLQVXaC8Jl0e9bMvYDSX0GW7Ffm2m3j9y7RXgR1foeUAM9w=="], - "@turbo/linux-64": ["@turbo/linux-64@2.8.20", "", { "os": "linux", "cpu": "x64" }, "sha512-p2QxWUYyYUgUFG0b0kR+pPi8t7c9uaVlRtjTTI1AbCvVqkpjUfCcReBn6DgG/Hu8xrWdKLuyQFaLYFzQskZbcA=="], + "@turbo/linux-64": ["@turbo/linux-64@2.9.12", "", { "os": "linux", "cpu": "x64" }, "sha512-InIUtH7cw/vqXNX1Gr7QgWfmw3ct08pV5CpfdEOR48z2u2rzdmpIuk00B/Q2xCb0PMWtKgiMQynfuphmEuUyTQ=="], - "@turbo/linux-arm64": ["@turbo/linux-arm64@2.8.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-Gn5yjlZGLRZWarLWqdQzv0wMqyBNIdq1QLi48F1oY5Lo9kiohuf7BPQWtWxeNVS2NgJ1+nb/DzK1JduYC4AWOA=="], + "@turbo/linux-arm64": ["@turbo/linux-arm64@2.9.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-lC6nD//Xh67fmJM0LKaLsg74Wry0aYrgMklpiNgCbUaMdPIOqj0A00iri3NU7Lb7pZHx8ViisgpeDKlpSgFUCA=="], - "@turbo/windows-64": ["@turbo/windows-64@2.8.20", "", { "os": "win32", "cpu": "x64" }, "sha512-vyaDpYk/8T6Qz5V/X+ihKvKFEZFUoC0oxYpC1sZanK6gaESJlmV3cMRT3Qhcg4D2VxvtC2Jjs9IRkrZGL+exLw=="], + "@turbo/windows-64": ["@turbo/windows-64@2.9.12", "", { "os": "win32", "cpu": "x64" }, "sha512-conYri8VUl72JOdYnLDPYwzqbPcY5ECoHmo9FWoKznemhaAIilj4maHqs9Uar0aKfNoZIULniy+6iWaLtLO34A=="], - "@turbo/windows-arm64": ["@turbo/windows-arm64@2.8.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-voicVULvUV5yaGXo0Iue13BcHGYW3u0VgqSbfQwBaHbpj1zLjYV4KIe+7fYIo6DO8FVUJzxFps3ODCQG/Wy2Qw=="], + "@turbo/windows-arm64": ["@turbo/windows-arm64@2.9.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-XoR4bsg62/L/esRVcmoMESEiNZ36+YmyjYGLpoqk8nwMgXzzVjNOgX0lRSz5w/U/ajLGv3nhMsS0Q2QOdvp2AQ=="], "@tweenjs/tween.js": ["@tweenjs/tween.js@23.1.3", "", {}, "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA=="], + "@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="], + "@types/draco3d": ["@types/draco3d@1.4.10", "", {}, "sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw=="], - "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], "@types/howler": ["@types/howler@2.2.12", "", {}, "sha512-hy769UICzOSdK0Kn1FBk4gN+lswcj1EKRkmiDtMkUGvFfYJzgaDXmVXkSShS2m89ERAatGIPnTUlp2HhfkVo5g=="], "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], - "@types/node": ["@types/node@22.19.15", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg=="], + "@types/node": ["@types/node@25.6.2", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-sokuT28dxf9JT5Kady1fsXOvI4HVpjZa95NKT5y9PNTIrs2AsobR4GFAA90ZG8M+nxVRLysCXsVj6eGC7Vbrlw=="], "@types/offscreencanvas": ["@types/offscreencanvas@2019.7.3", "", {}, "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A=="], "@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], - "@types/react-dom": ["@types/react-dom@19.2.2", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw=="], + "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], "@types/react-reconciler": ["@types/react-reconciler@0.28.9", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg=="], @@ -669,25 +636,25 @@ "@types/webxr": ["@types/webxr@0.5.24", "", {}, "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg=="], - "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.57.2", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.57.2", "@typescript-eslint/type-utils": "8.57.2", "@typescript-eslint/utils": "8.57.2", "@typescript-eslint/visitor-keys": "8.57.2", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.57.2", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w=="], + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.59.2", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.59.2", "@typescript-eslint/type-utils": "8.59.2", "@typescript-eslint/utils": "8.59.2", "@typescript-eslint/visitor-keys": "8.59.2", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.59.2", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ=="], - "@typescript-eslint/parser": ["@typescript-eslint/parser@8.57.2", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.57.2", "@typescript-eslint/types": "8.57.2", "@typescript-eslint/typescript-estree": "8.57.2", "@typescript-eslint/visitor-keys": "8.57.2", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA=="], + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.59.2", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.59.2", "@typescript-eslint/types": "8.59.2", "@typescript-eslint/typescript-estree": "8.59.2", "@typescript-eslint/visitor-keys": "8.59.2", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ=="], - "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.57.2", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.57.2", "@typescript-eslint/types": "^8.57.2", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw=="], + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.59.2", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.59.2", "@typescript-eslint/types": "^8.59.2", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw=="], - "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.57.2", "", { "dependencies": { "@typescript-eslint/types": "8.57.2", "@typescript-eslint/visitor-keys": "8.57.2" } }, "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw=="], + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.59.2", "", { "dependencies": { "@typescript-eslint/types": "8.59.2", "@typescript-eslint/visitor-keys": "8.59.2" } }, "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg=="], - "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.57.2", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw=="], + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.59.2", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw=="], - "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.57.2", "", { "dependencies": { "@typescript-eslint/types": "8.57.2", "@typescript-eslint/typescript-estree": "8.57.2", "@typescript-eslint/utils": "8.57.2", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg=="], + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.59.2", "", { "dependencies": { "@typescript-eslint/types": "8.59.2", "@typescript-eslint/typescript-estree": "8.59.2", "@typescript-eslint/utils": "8.59.2", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ=="], - "@typescript-eslint/types": ["@typescript-eslint/types@8.57.2", "", {}, "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA=="], + "@typescript-eslint/types": ["@typescript-eslint/types@8.59.2", "", {}, "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q=="], - "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.57.2", "", { "dependencies": { "@typescript-eslint/project-service": "8.57.2", "@typescript-eslint/tsconfig-utils": "8.57.2", "@typescript-eslint/types": "8.57.2", "@typescript-eslint/visitor-keys": "8.57.2", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA=="], + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.59.2", "", { "dependencies": { "@typescript-eslint/project-service": "8.59.2", "@typescript-eslint/tsconfig-utils": "8.59.2", "@typescript-eslint/types": "8.59.2", "@typescript-eslint/visitor-keys": "8.59.2", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg=="], - "@typescript-eslint/utils": ["@typescript-eslint/utils@8.57.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.57.2", "@typescript-eslint/types": "8.57.2", "@typescript-eslint/typescript-estree": "8.57.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg=="], + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.59.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.59.2", "@typescript-eslint/types": "8.59.2", "@typescript-eslint/typescript-estree": "8.59.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q=="], - "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.57.2", "", { "dependencies": { "@typescript-eslint/types": "8.57.2", "eslint-visitor-keys": "^5.0.0" } }, "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw=="], + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.59.2", "", { "dependencies": { "@typescript-eslint/types": "8.59.2", "eslint-visitor-keys": "^5.0.0" } }, "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA=="], "@use-gesture/core": ["@use-gesture/core@10.3.1", "", {}, "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw=="], @@ -697,8 +664,6 @@ "@visual-json/react": ["@visual-json/react@0.4.0", "", { "dependencies": { "@visual-json/core": "0.4.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0" } }, "sha512-qiDrXI7wu9VoTE0TaLoJp7kwRj8SQTpkS/wkts5sBPA8078zdrIKc+0u0Y8wAXGYd3udw9dNfD4kmpO48OEwXQ=="], - "@webgpu/types": ["@webgpu/types@0.1.69", "", {}, "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ=="], - "@zappar/msdf-generator": ["@zappar/msdf-generator@1.2.4", "", { "dependencies": { "comlink": "^4.4.2" } }, "sha512-6S/MCk0Ky0ipewZJw4xFEzH/2aYfWmPXEkTdBtNyDDfkbicrNwgJgtxZ4SnTDyNe9XHMqDA4sL9srRsgDLRMqA=="], "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], @@ -709,7 +674,7 @@ "agentation": ["agentation@2.3.3", "", { "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-AUZgFCdBQ/nAohlFsHByM9S2Dp7ECMNqVjlOke4hv/90v+wTiwrGladEkgWS60RDQp+CJ5p97meeCthYgTFlKQ=="], - "ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + "ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], @@ -743,25 +708,27 @@ "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.10.10", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.10.29", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ=="], "bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="], - "bippy": ["bippy@0.5.32", "", { "dependencies": { "@types/react-reconciler": "^0.28.9" }, "peerDependencies": { "react": ">=17.0.1" } }, "sha512-yt1mC8eReTxjfg41YBZdN4PvsDwHFWxltoiQX0Q+Htlbf41aSniopb7ECZits01HwNAvXEh69RGk/ImlswDTEw=="], + "bippy": ["bippy@0.5.39", "", { "peerDependencies": { "react": ">=17.0.1" } }, "sha512-8hE8rKSl8JWyeaY+JjpnmceWAZPpLEyzOZQpWXM5Rc7861c5WotMJHy2aRZKZrGA8nMpvLNF01t4yQQ+HcZG3w=="], "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], - "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "brace-expansion": ["brace-expansion@1.1.14", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], + "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], + "bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], - "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], + "call-bind": ["call-bind@1.0.9", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" } }, "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ=="], "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], @@ -771,17 +738,17 @@ "camera-controls": ["camera-controls@3.1.2", "", { "peerDependencies": { "three": ">=0.126.1" } }, "sha512-xkxfpG2ECZ6Ww5/9+kf4mfg1VEYAoe9aDSY+IwF0UEs7qEzwy0aVRfs2grImIECs/PoBtWFrh7RXsQkwG922JA=="], - "caniuse-lite": ["caniuse-lite@1.0.30001781", "", {}, "sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw=="], + "caniuse-lite": ["caniuse-lite@1.0.30001792", "", {}, "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw=="], "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "citty": ["citty@0.2.1", "", {}, "sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg=="], + "citty": ["citty@0.2.2", "", {}, "sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w=="], "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], "cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], - "cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], + "cli-spinners": ["cli-spinners@3.4.0", "", {}, "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw=="], "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], @@ -845,7 +812,7 @@ "doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="], - "dotenv": ["dotenv@17.3.1", "", {}, "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA=="], + "dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="], "dotenv-cli": ["dotenv-cli@11.0.0", "", { "dependencies": { "cross-spawn": "^7.0.6", "dotenv": "^17.1.0", "dotenv-expand": "^12.0.0", "minimist": "^1.2.6" }, "bin": { "dotenv": "cli.js" } }, "sha512-r5pA8idbk7GFWuHEU7trSTflWcdBpQEK+Aw17UrSHjS6CReuhrrPcyC3zcQBPQvhArRHnBo/h6eLH1fkCvNlww=="], @@ -859,23 +826,19 @@ "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], - "electron-to-chromium": ["electron-to-chromium@1.5.325", "", {}, "sha512-PwfIw7WQSt3xX7yOf5OE/unLzsK9CaN2f/FvV3WjPR1Knoc1T9vePRVV4W1EM301JzzysK51K7FNKcusCr0zYA=="], - - "element-source": ["element-source@0.0.3", "", { "dependencies": { "bippy": "^0.5.32" } }, "sha512-o3VMv2BIfY/axhIBKlE9HrR5rNqnhjHN2PEAKxG65O0VCSfONoMi9QMQjY12XVVvMuTzr1cAg/4xLMkvh+/Wlg=="], - - "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + "electron-to-chromium": ["electron-to-chromium@1.5.353", "", {}, "sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w=="], "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], - "enhanced-resolve": ["enhanced-resolve@5.20.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA=="], + "enhanced-resolve": ["enhanced-resolve@5.21.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-xe9vQb5kReirPUxgQrXA3ihgbCqssmTiM7cOZ+Gzu+VeGWgpV98lLZvp0dl4yriyAePcewxGUs9UpKD8PET9KQ=="], - "es-abstract": ["es-abstract@1.24.1", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw=="], + "es-abstract": ["es-abstract@1.24.2", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg=="], "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], - "es-iterator-helpers": ["es-iterator-helpers@1.3.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.1", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.3.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.5", "math-intrinsics": "^1.1.0", "safe-array-concat": "^1.1.3" } }, "sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ=="], + "es-iterator-helpers": ["es-iterator-helpers@1.3.2", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.2", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.3.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.5", "math-intrinsics": "^1.1.0" } }, "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw=="], "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], @@ -885,8 +848,6 @@ "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="], - "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], - "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], @@ -903,7 +864,7 @@ "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="], - "eslint-plugin-turbo": ["eslint-plugin-turbo@2.8.20", "", { "dependencies": { "dotenv": "16.0.3" }, "peerDependencies": { "eslint": ">6.6.0", "turbo": ">2.0.0" } }, "sha512-sMDremg73XbX+Imh7iDRS3TAro5jIf7CdzSqrT50wCGhClbiUvmWcLomnX+ldUbfTy+OvtqOeePvTivJMR87eQ=="], + "eslint-plugin-turbo": ["eslint-plugin-turbo@2.9.12", "", { "dependencies": { "dotenv": "16.0.3" }, "peerDependencies": { "eslint": ">6.6.0", "turbo": ">2.0.0" } }, "sha512-Ilv1DTYyghdIdTUsW/VbjVTYKt1Hfs7X2C+rq/i4u7ZVofzcHZwk/3QwrV5UyOGADmxmWNyD+xcRS7+ZE5arQg=="], "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], @@ -919,7 +880,7 @@ "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], - "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], @@ -927,11 +888,11 @@ "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], - "eventsource-parser": ["eventsource-parser@3.0.7", "", {}, "sha512-zwxwiQqexizSXFZV13zMiEtW1E3lv7RlUv+1f5FBiR4x7wFhEjm3aFTyYkZQWzyN08WnPdox015GoRH5D/E5YA=="], + "eventsource-parser": ["eventsource-parser@3.0.8", "", {}, "sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ=="], "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], - "express-rate-limit": ["express-rate-limit@8.3.2", "", { "dependencies": { "ip-address": "10.1.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg=="], + "express-rate-limit": ["express-rate-limit@8.5.1", "", { "dependencies": { "ip-address": "^10.2.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-5O6KYmyJEpuPJV5hNTXKbAHWRqrzyu+OI3vUnSd2kXFubIVpG7ezpgxQy76Zo5GQZtrQBg86hF+CM/NX+cioiQ=="], "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], @@ -941,7 +902,13 @@ "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], - "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], + "fast-string-truncated-width": ["fast-string-truncated-width@3.0.3", "", {}, "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g=="], + + "fast-string-width": ["fast-string-width@3.0.2", "", { "dependencies": { "fast-string-truncated-width": "^3.0.2" } }, "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg=="], + + "fast-uri": ["fast-uri@3.1.2", "", {}, "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ=="], + + "fast-wrap-ansi": ["fast-wrap-ansi@0.2.0", "", { "dependencies": { "fast-string-width": "^3.0.2" } }, "sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w=="], "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], @@ -975,13 +942,15 @@ "functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="], + "fzf": ["fzf@0.5.2", "", {}, "sha512-Tt4kuxLXFKHy8KT40zwsUPUkg1CrsgY25FxA2U/j/0WgEDCk3ddc/zLTCCcbSHX9FcKtLuVaDGtGE/STWC+j3Q=="], + "geist": ["geist@1.7.0", "", { "peerDependencies": { "next": ">=13.2.0" } }, "sha512-ZaoiZwkSf0DwwB1ncdLKp+ggAldqxl5L1+SXaNIBGkPAqcu+xjVJLxlf3/S8vLt9UHx1xu5fz3lbzKCj5iOVdQ=="], "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], - "get-east-asian-width": ["get-east-asian-width@1.5.0", "", {}, "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA=="], + "get-east-asian-width": ["get-east-asian-width@1.6.0", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="], "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], @@ -1017,11 +986,11 @@ "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + "hasown": ["hasown@2.0.3", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg=="], - "hls.js": ["hls.js@1.6.15", "", {}, "sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA=="], + "hls.js": ["hls.js@1.6.16", "", {}, "sha512-VSIRpLfRwlAAdGL4wiTucx2ScRipo0ed1FBatWkyt832jC4CReKstga6yIhYVwGu9LOBjuX9wzmRMeQdBJtzEA=="], - "hono": ["hono@4.12.14", "", {}, "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w=="], + "hono": ["hono@4.12.18", "", {}, "sha512-RWzP96k/yv0PQfyXnWjs6zot20TqfpfsNXhOnev8d1InAxubW93L11/oNUc3tQqn2G0bSdAOBpX+2uDFHV7kdQ=="], "howler": ["howler@2.2.4", "", {}, "sha512-iARIBPgcQrwtEr+tALF+rapJ8qSc+Set2GJQl7xT1MQzWaVkFebdJhR3alVlSiUf5U7nAANKuj3aWpwerocD5w=="], @@ -1045,7 +1014,7 @@ "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], - "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], + "ip-address": ["ip-address@10.2.0", "", {}, "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA=="], "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], @@ -1059,7 +1028,7 @@ "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], - "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], + "is-core-module": ["is-core-module@2.16.2", "", { "dependencies": { "hasown": "^2.0.3" } }, "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA=="], "is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="], @@ -1113,9 +1082,9 @@ "its-fine": ["its-fine@2.0.0", "", { "dependencies": { "@types/react-reconciler": "^0.28.9" }, "peerDependencies": { "react": "^19.0.0" } }, "sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng=="], - "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], - "jose": ["jose@6.2.2", "", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="], + "jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="], "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], @@ -1173,13 +1142,13 @@ "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], - "log-symbols": ["log-symbols@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" } }, "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw=="], + "log-symbols": ["log-symbols@7.0.1", "", { "dependencies": { "is-unicode-supported": "^2.0.0", "yoctocolors": "^2.1.1" } }, "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg=="], "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], - "lru-cache": ["lru-cache@11.2.7", "", {}, "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA=="], + "lru-cache": ["lru-cache@11.3.6", "", {}, "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A=="], - "lucide-react": ["lucide-react@0.562.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw=="], + "lucide-react": ["lucide-react@1.14.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-+1mdWcfSJVUsaTIjN9zoezmUhfXo5l0vP7ekBMPo3jcS/aIkxHnXqAPsByszMZx/Y8oQBRJxJx5xg+RH3urzxA=="], "maath": ["maath@0.10.8", "", { "peerDependencies": { "@types/three": ">=0.134.0", "three": ">=0.134.0" } }, "sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g=="], @@ -1221,7 +1190,7 @@ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "nanoid": ["nanoid@5.1.7", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ=="], + "nanoid": ["nanoid@5.1.11", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg=="], "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], @@ -1231,11 +1200,11 @@ "node-exports-info": ["node-exports-info@1.6.0", "", { "dependencies": { "array.prototype.flatmap": "^1.3.3", "es-errors": "^1.3.0", "object.entries": "^1.1.9", "semver": "^6.3.1" } }, "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw=="], - "node-releases": ["node-releases@2.0.36", "", {}, "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA=="], + "node-releases": ["node-releases@2.0.38", "", {}, "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw=="], "number-flow": ["number-flow@0.5.12", "", { "dependencies": { "esm-env": "^1.1.4" } }, "sha512-CIs21h2JkfYG4rfgERaUNAk0Cz+Ef14fNJfSCbGGhgRgconQc9b7rcCQfi9SZ36kNjVXmsl2BrzDbjGtEgumAA=="], - "nypm": ["nypm@0.6.5", "", { "dependencies": { "citty": "^0.2.0", "pathe": "^2.0.3", "tinyexec": "^1.0.2" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ=="], + "nypm": ["nypm@0.6.6", "", { "dependencies": { "citty": "^0.2.2", "pathe": "^2.0.3", "tinyexec": "^1.1.1" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-vRyr0r4cbBapw07Xw8xrj9Teq3o7MUD35rSaTcanDbW+aK2XHDgJFiU6ZTj2GBw7Q12ysdsyFss+Vdz4hQ0Y6Q=="], "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], @@ -1259,7 +1228,7 @@ "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], - "ora": ["ora@8.2.0", "", { "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.0.0", "log-symbols": "^6.0.0", "stdin-discarder": "^0.2.2", "string-width": "^7.2.0", "strip-ansi": "^7.1.0" } }, "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw=="], + "ora": ["ora@9.4.0", "", { "dependencies": { "chalk": "^5.6.2", "cli-cursor": "^5.0.0", "cli-spinners": "^3.2.0", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.1.0", "log-symbols": "^7.0.1", "stdin-discarder": "^0.3.2", "string-width": "^8.1.0" } }, "sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ=="], "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], @@ -1267,6 +1236,8 @@ "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + "package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], @@ -1293,11 +1264,11 @@ "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.14", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg=="], "potpack": ["potpack@1.0.2", "", {}, "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ=="], - "preact": ["preact@10.29.0", "", {}, "sha512-wSAGyk2bYR1c7t3SZ3jHcM6xy0lcBcDel6lODcs9ME6Th++Dx2KU+6D3HD8wMMKGA8Wpw7OMd3/4RGzYRpzwRg=="], + "preact": ["preact@10.29.1", "", {}, "sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg=="], "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], @@ -1319,11 +1290,11 @@ "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], - "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], + "react": ["react@19.2.6", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="], - "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], + "react-dom": ["react-dom@19.2.6", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.6" } }, "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g=="], - "react-grab": ["react-grab@0.1.29", "", { "dependencies": { "@medv/finder": "^4.0.2", "@react-grab/cli": "0.1.29", "bippy": "^0.5.32", "element-source": "^0.0.3", "solid-js": "^1.9.10" }, "peerDependencies": { "react": ">=17.0.0" }, "optionalPeers": ["react"], "bin": { "react-grab": "bin/cli.js" } }, "sha512-s6CDIwjA0pzhCJqgFabM3GZ6m9vsmStOHopl7c8Xyx702QZ5ZXxOMRTpd2/7Gg+GC/1XwFmyLHwQiOCbUYIIYQ=="], + "react-grab": ["react-grab@0.1.33", "", { "dependencies": { "@react-grab/cli": "0.1.33", "bippy": "^0.5.39" }, "peerDependencies": { "react": ">=17.0.0" }, "optionalPeers": ["react"], "bin": { "react-grab": "bin/cli.js" } }, "sha512-ER919JMsE4TTrb2CpEivqsIjNMSycD4HtS8v7mS3pq67U7WL1K3+C8m9AYOwW4dpuYh+EanC2eJBmfuczHJZ0A=="], "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], @@ -1331,7 +1302,7 @@ "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], - "react-scan": ["react-scan@0.5.3", "", { "dependencies": { "@babel/core": "^7.26.0", "@babel/generator": "^7.26.2", "@babel/types": "^7.26.0", "@preact/signals": "^1.3.1", "@rollup/pluginutils": "^5.1.3", "@types/node": "^20.17.9", "bippy": "^0.5.30", "commander": "^14.0.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "picocolors": "^1.1.1", "preact": "^10.25.1", "prompts": "^2.4.2" }, "optionalDependencies": { "unplugin": "2.1.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "bin": { "react-scan": "bin/cli.js" } }, "sha512-qde9PupmUf0L3MU1H6bjmoukZNbCXdMyTEwP4Gh8RQ4rZPd2GGNBgEKWszwLm96E8k+sGtMpc0B9P0KyFDP6Bw=="], + "react-scan": ["react-scan@0.5.6", "", { "dependencies": { "@babel/core": "^7.26.0", "@babel/types": "^7.26.0", "@preact/signals": "^1.3.1", "@rollup/pluginutils": "^5.1.3", "bippy": "^0.5.39", "commander": "^14.0.0", "picocolors": "^1.1.1", "preact": "^10.25.1", "prompts": "^2.4.2", "react-grab": "latest" }, "optionalDependencies": { "unplugin": "2.1.0" }, "peerDependencies": { "esbuild": ">=0.18.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["esbuild"], "bin": { "react-scan": "bin/cli.js" } }, "sha512-FrZ75yWjabNQmBbN9wxP+FgfgcPYoOFFWXYBhi6OnUbF/DyJDwOEfV2RRs9IWVfPVjhsku2CPK3oohYC5GEHpg=="], "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], @@ -1357,7 +1328,7 @@ "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], - "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="], + "safe-array-concat": ["safe-array-concat@1.1.4", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "get-intrinsic": "^1.3.0", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg=="], "safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="], @@ -1371,10 +1342,6 @@ "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], - "seroval": ["seroval@1.5.1", "", {}, "sha512-OwrZRZAfhHww0WEnKHDY8OM0U/Qs8OTfIDWhUD4BLpNJUfXK4cGmjiagGze086m+mhI+V2nD0gfbHEnJjb9STA=="], - - "seroval-plugins": ["seroval-plugins@1.5.1", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-4FbuZ/TMl02sqv0RTFexu0SP6V+ywaIe5bAWCCEik0fk17BhALgwvUDVF7e3Uvf9pxmwCEJsRPmlkUE6HdzLAw=="], - "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], @@ -1393,7 +1360,7 @@ "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], - "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], @@ -1405,8 +1372,6 @@ "smol-toml": ["smol-toml@1.6.1", "", {}, "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg=="], - "solid-js": ["solid-js@1.9.12", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.5.0", "seroval-plugins": "~1.5.0" } }, "sha512-QzKaSJq2/iDrWR1As6MHZQ8fQkdOBf8GReYb7L5iKwMGceg7HxDcaOHk0at66tNgn9U2U7dXo8ZZpLIAmGMzgw=="], - "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], "splaytree": ["splaytree@3.2.3", "", {}, "sha512-7OXrNWzy6CK+r7Ch9OLPBDTKfB6XlWHjX4P0RU5B3IgFuWPeYN0XtRtlexGRjgbQxpfaUve6jTAwBGWuGntz/w=="], @@ -1417,11 +1382,11 @@ "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], - "stdin-discarder": ["stdin-discarder@0.2.2", "", {}, "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ=="], + "stdin-discarder": ["stdin-discarder@0.3.2", "", {}, "sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A=="], "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], - "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + "string-width": ["string-width@8.2.1", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA=="], "string.prototype.matchall": ["string.prototype.matchall@4.0.12", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA=="], @@ -1447,9 +1412,9 @@ "tailwind-merge": ["tailwind-merge@3.5.0", "", {}, "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A=="], - "tailwindcss": ["tailwindcss@4.2.2", "", {}, "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q=="], + "tailwindcss": ["tailwindcss@4.3.0", "", {}, "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q=="], - "tapable": ["tapable@2.3.2", "", {}, "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA=="], + "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], "three": ["three@0.184.0", "", {}, "sha512-wtTRjG92pM5eUg/KuUnHsqSAlPM296brTOcLgMRqEeylYTh/CdtvKUvCyyCQTzFuStieWxvZb8mVTMvdPyUpxg=="], @@ -1459,9 +1424,9 @@ "three-stdlib": ["three-stdlib@2.36.1", "", { "dependencies": { "@types/draco3d": "^1.4.0", "@types/offscreencanvas": "^2019.6.4", "@types/webxr": "^0.5.2", "draco3d": "^1.4.1", "fflate": "^0.6.9", "potpack": "^1.0.1" }, "peerDependencies": { "three": ">=0.128.0" } }, "sha512-XyGQrFmNQ5O/IoKm556ftwKsBg11TIb301MB5dWNicziQBEs2g3gtOYIf7pFiLa0zI2gUwhtCjv9fmjnxKZ1Cg=="], - "tinyexec": ["tinyexec@1.0.4", "", {}, "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw=="], + "tinyexec": ["tinyexec@1.1.2", "", {}, "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA=="], - "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + "tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="], "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], @@ -1479,7 +1444,7 @@ "tunnel-rat": ["tunnel-rat@0.1.2", "", { "dependencies": { "zustand": "^4.3.2" } }, "sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ=="], - "turbo": ["turbo@2.8.20", "", { "optionalDependencies": { "@turbo/darwin-64": "2.8.20", "@turbo/darwin-arm64": "2.8.20", "@turbo/linux-64": "2.8.20", "@turbo/linux-arm64": "2.8.20", "@turbo/windows-64": "2.8.20", "@turbo/windows-arm64": "2.8.20" }, "bin": { "turbo": "bin/turbo" } }, "sha512-Rb4qk5YT8RUwwdXtkLpkVhNEe/lor6+WV7S5tTlLpxSz6MjV5Qi8jGNn4gS6NAvrYGA/rNrE6YUQM85sCZUDbQ=="], + "turbo": ["turbo@2.9.12", "", { "optionalDependencies": { "@turbo/darwin-64": "2.9.12", "@turbo/darwin-arm64": "2.9.12", "@turbo/linux-64": "2.9.12", "@turbo/linux-arm64": "2.9.12", "@turbo/windows-64": "2.9.12", "@turbo/windows-arm64": "2.9.12" }, "bin": { "turbo": "bin/turbo" } }, "sha512-lCPgus1NuTiBdaITWqzSH/Ff6HVL8HHGBtOXHg1dHRfcshN79XkygSdh0M6g8b0td91ILLG5MTkLOkp5UvyPJw=="], "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], @@ -1495,15 +1460,15 @@ "typed-array-length": ["typed-array-length@1.0.7", "", { "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", "is-typed-array": "^1.1.13", "possible-typed-array-names": "^1.0.0", "reflect.getprototypeof": "^1.0.6" } }, "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg=="], - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "typescript": ["typescript@6.0.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ=="], - "typescript-eslint": ["typescript-eslint@8.57.2", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.57.2", "@typescript-eslint/parser": "8.57.2", "@typescript-eslint/typescript-estree": "8.57.2", "@typescript-eslint/utils": "8.57.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A=="], + "typescript-eslint": ["typescript-eslint@8.59.2", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.59.2", "@typescript-eslint/parser": "8.59.2", "@typescript-eslint/typescript-estree": "8.59.2", "@typescript-eslint/utils": "8.59.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-pJw051uomb3ZeCzGTpRb8RbEqB5Y4WWet8gl/GcTlU35BSx0PVdZ86/bqkQCyKKuraVQEK7r6kBHQXF+fBhkoQ=="], - "ultracite": ["ultracite@7.3.2", "", { "dependencies": { "@clack/prompts": "^1.1.0", "commander": "^14.0.3", "deepmerge": "^4.3.1", "glob": "^13.0.6", "jsonc-parser": "^3.3.1", "nypm": "^0.6.5" }, "peerDependencies": { "oxlint": "^1.0.0" }, "optionalPeers": ["oxlint"], "bin": { "ultracite": "dist/index.js" } }, "sha512-JV2TdeBDc1aaj3wFzXLjRcBlUZkXBdShMLAjHh9YLK0rXPSK42CzHqfWXUfibFUAUiUGNwoOFnIpS45EaJAcdg=="], + "ultracite": ["ultracite@7.6.5", "", { "dependencies": { "@clack/prompts": "^1.3.0", "commander": "^14.0.3", "cross-spawn": "^7.0.6", "deepmerge": "^4.3.1", "glob": "^13.0.6", "jsonc-parser": "^3.3.1", "nypm": "^0.6.6", "yaml": "^2.8.4", "zod": "^4.4.3" }, "peerDependencies": { "oxfmt": ">=0.1.0", "oxlint": "^1.0.0" }, "optionalPeers": ["oxfmt", "oxlint"], "bin": { "ultracite": "dist/index.js" } }, "sha512-q68Ht5cTvjzmyDvhwZUPfxWLpcwioG5e5ODrVxBAbc0qWEn5hD3+CT1Tgq8FEFEypjKVote01inkG1nDOE9hmw=="], "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], - "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], @@ -1545,31 +1510,33 @@ "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + "yaml": ["yaml@2.8.4", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog=="], + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + "yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="], + "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], - "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], "zundo": ["zundo@2.3.0", "", { "peerDependencies": { "zustand": "^4.3.0 || ^5.0.0" } }, "sha512-4GXYxXA17SIKYhVbWHdSEU04P697IMyVGXrC2TnzoyohEAWytFNOKqOp5gTGvaW93F/PM5Y0evbGtOPF0PWQwQ=="], - "zustand": ["zustand@5.0.12", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g=="], + "zustand": ["zustand@5.0.13", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-efI2tVaVQPqtOh114loML/Z80Y4NP3yc+Ff0fYiZJPauNeWZeIp/bRFD7I9bfmCOYBh/PHxlglQ9+wvlwnPikQ=="], "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], - "@eslint/eslintrc/ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], + "@eslint/eslintrc/ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], - "@pascal-app/editor/@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="], + "@pascal-app/mcp/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], - "@pascal-app/mcp/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="], - - "@pascal-app/viewer/@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="], + "@pascal-app/viewer/@types/node": ["@types/node@22.19.18", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-9v00a+dn2yWVsYDEunWC4g/TcRKVq3r8N5FuZp7u0SGrPvdN9c2yXI9bBuf5Fl0hNCb+QTIePTn5pJs2pwBOQQ=="], "@radix-ui/react-alert-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], @@ -1593,47 +1560,43 @@ "@react-three/drei/three-mesh-bvh": ["three-mesh-bvh@0.8.3", "", { "peerDependencies": { "three": ">= 0.159.0" } }, "sha512-4G5lBaF+g2auKX3P0yqx+MJC6oVt6sB5k+CchS6Ob0qvH0YIhuUk1eYr7ktsIpY+albCqE80/FVQGV190PmiAg=="], - "@repo/ui/@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="], + "@repo/eslint-config/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "@repo/ui/@types/node": ["@types/node@22.19.18", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-9v00a+dn2yWVsYDEunWC4g/TcRKVq3r8N5FuZp7u0SGrPvdN9c2yXI9bBuf5Fl0hNCb+QTIePTn5pJs2pwBOQQ=="], - "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + "@repo/ui/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], - "@tailwindcss/oxide-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" }, "bundled": true }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="], + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], - "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], - "@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], - "@typescript-eslint/typescript-estree/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "@typescript-eslint/typescript-estree/semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="], "@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="], - "cmdk/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="], - "dotenv-expand/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], - "editor/@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="], + "editor/@types/node": ["@types/node@22.19.18", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-9v00a+dn2yWVsYDEunWC4g/TcRKVq3r8N5FuZp7u0SGrPvdN9c2yXI9bBuf5Fl0hNCb+QTIePTn5pJs2pwBOQQ=="], - "eslint/ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], + "eslint/ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], "eslint-plugin-turbo/dotenv": ["dotenv@16.0.3", "", {}, "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ=="], "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - "glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], - - "log-symbols/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], - - "log-symbols/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="], + "glob/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], @@ -1641,15 +1604,11 @@ "ora/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], - "postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - - "react-scan/@types/node": ["@types/node@20.19.37", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw=="], + "postcss/nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], "router/is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], - "sharp/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], - - "stats-gl/@types/three": ["@types/three@0.183.1", "", { "dependencies": { "@dimforge/rapier3d-compat": "~0.12.0", "@tweenjs/tween.js": "~23.1.3", "@types/stats.js": "*", "@types/webxr": ">=0.5.17", "@webgpu/types": "*", "fflate": "~0.8.2", "meshoptimizer": "~1.0.1" } }, "sha512-f2Pu5Hrepfgavttdye3PsH5RWyY/AvdZQwIVhrc4uNtvF7nOWJacQKcoVJn0S4f0yYbmAE6AR+ve7xDcuYtMGw=="], + "sharp/semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="], "stats-gl/three": ["three@0.170.0", "", {}, "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ=="], @@ -1659,19 +1618,19 @@ "@eslint/eslintrc/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], - "@pascal-app/mcp/@types/node/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], + "@pascal-app/viewer/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "@pascal-app/viewer/@types/node/undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], + "@repo/ui/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.5", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="], + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="], - "eslint/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + "editor/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "glob/minimatch/brace-expansion": ["brace-expansion@5.0.5", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="], + "eslint/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], - "next/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "glob/minimatch/brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="], - "stats-gl/@types/three/meshoptimizer": ["meshoptimizer@1.0.1", "", {}, "sha512-Vix+QlA1YYT3FwmBBZ+49cE5y/b+pRrcXKqGpS5ouh33d3lSp2PoTpCw19E0cKDFWalembrHnIaZetf27a+W2g=="], + "next/postcss/nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], "@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], diff --git a/package.json b/package.json index 10b3d7f58..bb9acd33c 100644 --- a/package.json +++ b/package.json @@ -12,23 +12,40 @@ "check:fix": "biome check --write", "check-types": "turbo run check-types", "kill": "lsof -ti:3002 | xargs kill -9 2>/dev/null || echo 'No processes found on port 3002'", - "release": "gh workflow run release.yml -f package=both -f bump=patch", + "release": "gh workflow run release.yml -f package=all -f bump=patch", "release:viewer": "gh workflow run release.yml -f package=viewer -f bump=patch", "release:core": "gh workflow run release.yml -f package=core -f bump=patch", - "release:minor": "gh workflow run release.yml -f package=both -f bump=minor", - "release:major": "gh workflow run release.yml -f package=both -f bump=major" + "release:editor": "gh workflow run release.yml -f package=editor -f bump=patch", + "release:mcp": "gh workflow run release.yml -f package=mcp -f bump=patch", + "release:minor": "gh workflow run release.yml -f package=all -f bump=minor", + "release:major": "gh workflow run release.yml -f package=all -f bump=major" }, "devDependencies": { "@biomejs/biome": "^2.4.6", "dotenv-cli": "^11.0.0", "turbo": "^2.8.15", - "typescript": "5.9.3", + "typescript": "6.0.2", "ultracite": "^7.2.5" }, "engines": { "node": ">=18" }, "packageManager": "bun@1.3.0", + "overrides": { + "@types/react": "19.2.14", + "@types/react-dom": "19.2.3", + "@types/three": "0.184.0" + }, + "optionalDependencies": { + "@tailwindcss/oxide-darwin-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-x64": "4.3.0", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0" + }, "workspaces": [ "apps/*", "packages/*", diff --git a/packages/core/package.json b/packages/core/package.json index ff3e7dd31..5b156b931 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -73,9 +73,10 @@ }, "devDependencies": { "@pascal/typescript-config": "*", + "@types/bun": "^1.3.0", "@types/react": "^19.2.2", - "typescript": "5.9.3", - "@types/three": "^0.184.0" + "@types/three": "^0.184.0", + "typescript": "6.0.2" }, "keywords": [ "3d", diff --git a/packages/core/src/events/bus.ts b/packages/core/src/events/bus.ts index 0364a8804..5e2898091 100644 --- a/packages/core/src/events/bus.ts +++ b/packages/core/src/events/bus.ts @@ -28,12 +28,13 @@ export interface GridEvent { /** World-space intersection point on the grid plane. */ position: [number, number, number] /** - * Building-local intersection point, relative to the currently selected building. + * Building-local intersection point — relative to the currently selected building. * Equals `position` when no building is selected. - * Use this for placing or committing anything that lives inside a building - * (walls, slabs, items, etc.). + * Use this for placing/committing anything that lives inside a building (walls, slabs, items, etc.). */ localPosition: [number, number, number] + faceIndex?: number + object: Object3D nativeEvent: ThreeEvent } @@ -66,7 +67,7 @@ export type StairSegmentEvent = NodeEvent export type WindowEvent = NodeEvent export type DoorEvent = NodeEvent -// Event suffixes, exported for use in hooks +// Event suffixes - exported for use in hooks export const eventSuffixes = [ 'click', 'move', @@ -99,7 +100,7 @@ export interface ThumbnailGenerateEvent { /** * When true, snap levels to their true positions before capturing (for a * consistent auto-thumbnail angle) and defer the capture if the tab is - * hidden, the background auto-save path. Omit for user-driven captures + * hidden — the background auto-save path. Omit for user-driven captures * that should fire immediately from the current camera pose. */ snapLevels?: boolean @@ -107,8 +108,10 @@ export interface ThumbnailGenerateEvent { export interface CameraControlFitSceneEvent { /** - * XZ-plane axis-aligned bounds for camera framing. Omitted values let the - * listener choose its default framing pose. + * XZ-plane axis-aligned bounds of the scene's geometry, computed from the + * scene graph (see `@pascal-app/editor`'s `computeSceneBoundsXZ`). The + * viewer's camera-controls listener frames the camera onto this box. + * Omitted values fall back to the camera's default pose. */ bounds?: { min: [number, number] diff --git a/packages/core/src/lib/polygon-geometry.ts b/packages/core/src/lib/polygon-geometry.ts index f8f639b56..b2689b842 100644 --- a/packages/core/src/lib/polygon-geometry.ts +++ b/packages/core/src/lib/polygon-geometry.ts @@ -6,10 +6,7 @@ export function insetPolygonFromCentroid( return polygon.map(([x, z]) => [x, z] as [number, number]) } - const centroid = polygon.reduce( - (acc, [x, z]) => ({ x: acc.x + x, z: acc.z + z }), - { x: 0, z: 0 }, - ) + const centroid = polygon.reduce((acc, [x, z]) => ({ x: acc.x + x, z: acc.z + z }), { x: 0, z: 0 }) centroid.x /= Math.max(polygon.length, 1) centroid.z /= Math.max(polygon.length, 1) @@ -70,7 +67,10 @@ function dedupePolygonPoints( return deduped } -function simplifyPolyline(points: Array<[number, number]>, tolerance: number): Array<[number, number]> { +function simplifyPolyline( + points: Array<[number, number]>, + tolerance: number, +): Array<[number, number]> { if (points.length <= 2) { return points.map(([x, z]) => [x, z] as [number, number]) } diff --git a/packages/core/src/lib/space-detection.ts b/packages/core/src/lib/space-detection.ts index eb7ef6cc8..f9fda76e0 100644 --- a/packages/core/src/lib/space-detection.ts +++ b/packages/core/src/lib/space-detection.ts @@ -1,14 +1,20 @@ import { - getClampedWallCurveOffset, - getWallCurveFrameAt, - isCurvedWall, -} from '../systems/wall/wall-curve' -import { CeilingNode, SlabNode, type CeilingNode as CeilingNodeType, type SlabNode as SlabNodeType, type WallNode } from '../schema' + CeilingNode, + type CeilingNode as CeilingNodeType, + SlabNode, + type SlabNode as SlabNodeType, + type WallNode, +} from '../schema' import { getSceneHistoryPauseDepth, pauseSceneHistory, resumeSceneHistory, } from '../store/history-control' +import { + getClampedWallCurveOffset, + getWallCurveFrameAt, + isCurvedWall, +} from '../systems/wall/wall-curve' import { simplifyClosedPolygon } from './polygon-geometry' type Point2D = { x: number; y: number } @@ -147,10 +153,10 @@ function polygonCentroid(points: Point2D[]) { } function bboxOf(points: Point2D[]) { - let minX = Infinity - let minY = Infinity - let maxX = -Infinity - let maxY = -Infinity + let minX = Number.POSITIVE_INFINITY + let minY = Number.POSITIVE_INFINITY + let maxX = Number.NEGATIVE_INFINITY + let maxY = Number.NEGATIVE_INFINITY for (const point of points) { minX = Math.min(minX, point.x) @@ -216,7 +222,13 @@ function sampleWallPointsForRoomDetection( return [start, end] } - const subdivide = (t0: number, p0: Point2D, t1: number, p1: Point2D, depth: number): Point2D[] => { + const subdivide = ( + t0: number, + p0: Point2D, + t1: number, + p1: Point2D, + depth: number, + ): Point2D[] => { const midT = (t0 + t1) / 2 const midPoint = getWallCurveFrameAt(wall, midT).point const deviation = pointLineDistance(midPoint, p0, p1) @@ -361,7 +373,7 @@ function extractRoomPolygons(walls: WallNode[]): Point2D[][] { const signedArea = polygonArea(polygon) if (signedArea <= 0) continue - if (signedArea < 0.5 || signedArea > 10000) continue + if (signedArea < 0.5 || signedArea > 10_000) continue const signature = polygonSignature(polygon) if (faces.some((face) => polygonSignature(face) === signature)) continue @@ -442,10 +454,7 @@ function nextAutoRoomName( return `Room ${maxIndex + 1} ${suffix}` } -function sameTuplePolygon( - current: Array<[number, number]>, - next: Array<[number, number]>, -) { +function sameTuplePolygon(current: Array<[number, number]>, next: Array<[number, number]>) { return ( current.length === next.length && current.every((point, index) => point[0] === next[index]?.[0] && point[1] === next[index]?.[1]) @@ -727,7 +736,9 @@ function syncAutoCeilingsForLevel( const polygon = updatesById.get(ceiling.id) if (!polygon) return [] - return sameTuplePolygon(ceiling.polygon, polygon) ? [] : [{ id: ceiling.id, data: { polygon } }] + return sameTuplePolygon(ceiling.polygon, polygon) + ? [] + : [{ id: ceiling.id, data: { polygon } }] }) const plannedCeilingsForNaming: Array<{ name?: string }> = [...existingCeilings] diff --git a/packages/core/src/material-library.ts b/packages/core/src/material-library.ts index 5b08bec1e..a3622cbe7 100644 --- a/packages/core/src/material-library.ts +++ b/packages/core/src/material-library.ts @@ -1,5 +1,7 @@ import { type MaterialPresetPayload, + type MaterialTarget, + MaterialTarget as MaterialTargetSchema, } from './schema/material' export type MaterialCatalogItem = { @@ -12,6 +14,32 @@ export type MaterialCatalogItem = { preset: MaterialPresetPayload } +const WALL_TARGETS: MaterialTarget[] = [MaterialTargetSchema.enum.wall] + +const SLAB_TARGETS: MaterialTarget[] = [MaterialTargetSchema.enum.slab] + +const WALL_AND_SLAB_TARGETS: MaterialTarget[] = [ + MaterialTargetSchema.enum.wall, + MaterialTargetSchema.enum.slab, +] + +const STAIR_TARGETS: MaterialTarget[] = [ + MaterialTargetSchema.enum.stair, + MaterialTargetSchema.enum['stair-segment'], +] + +const STAIR_AND_FENCE_TARGETS: MaterialTarget[] = [ + ...STAIR_TARGETS, + MaterialTargetSchema.enum.fence, +] + +const ROOF_TARGETS: MaterialTarget[] = [ + MaterialTargetSchema.enum.roof, + MaterialTargetSchema.enum['roof-segment'], +] + +const CEILING_TARGETS: MaterialTarget[] = [MaterialTargetSchema.enum.ceiling] + export const MATERIAL_CATEGORIES = [ 'wood', 'wallpaper', diff --git a/packages/core/src/schema/asset-url.test.ts b/packages/core/src/schema/asset-url.test.ts index 85facfc7f..61ead092b 100644 --- a/packages/core/src/schema/asset-url.test.ts +++ b/packages/core/src/schema/asset-url.test.ts @@ -1,7 +1,3 @@ -// @ts-expect-error — bun:test is provided by the Bun runtime; core does not -// depend on @types/bun so the import type is unresolved at compile time. -// The tsconfig in packages/core still emits this file; the @ts-expect-error -// keeps the build green while letting `bun test` pick it up normally. import { afterEach, beforeEach, describe, expect, test } from 'bun:test' import { ALLOWED_ORIGINS_ENV, AssetUrl } from './asset-url' diff --git a/packages/core/src/schema/nodes/fence.ts b/packages/core/src/schema/nodes/fence.ts index 234da4073..d7726084f 100644 --- a/packages/core/src/schema/nodes/fence.ts +++ b/packages/core/src/schema/nodes/fence.ts @@ -13,9 +13,9 @@ export const FenceNode = BaseNode.extend({ materialPreset: z.string().optional(), start: z.tuple([z.number(), z.number()]), end: z.tuple([z.number(), z.number()]), - curveOffset: z.number().optional(), height: z.number().default(1.8), thickness: z.number().default(0.08), + curveOffset: z.number().optional(), baseHeight: z.number().default(0.22), postSpacing: z.number().default(2), postSize: z.number().default(0.1), @@ -29,8 +29,8 @@ export const FenceNode = BaseNode.extend({ dedent` Fence node - used to represent a fence segment in the building/site level coordinate system - start/end: fence endpoints in level coordinate system - - curveOffset: midpoint sagitta offset used to bend the fence into an arc - height/thickness: overall fence dimensions in meters + - curveOffset: midpoint sagitta offset used to bend the fence into an arc - baseHeight/postSpacing/postSize/topRailHeight: exact geometric controls from the plan3D fence model - groundClearance/edgeInset/baseStyle: fence support and inset configuration - color/style: visual appearance options diff --git a/packages/core/src/schema/nodes/item.ts b/packages/core/src/schema/nodes/item.ts index 5fb0f15ed..f8434d35e 100644 --- a/packages/core/src/schema/nodes/item.ts +++ b/packages/core/src/schema/nodes/item.ts @@ -83,6 +83,18 @@ const assetSchema = z.object({ // Optional top-down 2D image shown inside the item's footprint on the // floor plan. When present, replaces the default diagonal-cross marker. floorPlanUrl: z.string().optional(), + // Where the item came from in the catalog. Used by the editor's items + // panel to filter Library / Community / Mine. The server populates it + // from `items.userId`: null → 'library', current user → 'mine', + // other user → 'community'. Defaults to 'library' when absent (e.g. + // the seeded built-in catalog). + source: z.enum(['library', 'community', 'mine']).default('library'), + // True when the item belongs to the caller and is still in draft status. + // The catalog only loads my drafts (other users' drafts are never + // published to the catalog). Used so the Community filter can include + // *my* published items alongside other users', while leaving drafts + // visible only under Mine. + isDraft: z.boolean().optional(), src: AssetUrl, dimensions: z.tuple([z.number(), z.number(), z.number()]).default([1, 1, 1]), // [w, h, d] attachTo: z.enum(['wall', 'wall-side', 'ceiling']).optional(), diff --git a/packages/core/src/schema/nodes/roof.ts b/packages/core/src/schema/nodes/roof.ts index 944cca04a..0dd2f39a5 100644 --- a/packages/core/src/schema/nodes/roof.ts +++ b/packages/core/src/schema/nodes/roof.ts @@ -1,25 +1,26 @@ import dedent from 'dedent' import { z } from 'zod' import { BaseNode, nodeType, objectId } from '../base' -import { type MaterialSchema, MaterialSchema as MaterialSchemaSchema } from '../material' +import type { MaterialSchema as MaterialSchemaType } from '../material' +import { MaterialSchema } from '../material' import { RoofSegmentNode } from './roof-segment' export type RoofSurfaceMaterialRole = 'top' | 'edge' | 'wall' export type RoofSurfaceMaterialSpec = { - material?: MaterialSchema + material?: MaterialSchemaType materialPreset?: string } export const RoofNode = BaseNode.extend({ id: objectId('roof'), type: nodeType('roof'), - material: MaterialSchemaSchema.optional(), + material: MaterialSchema.optional(), materialPreset: z.string().optional(), - topMaterial: MaterialSchemaSchema.optional(), + topMaterial: MaterialSchema.optional(), topMaterialPreset: z.string().optional(), - edgeMaterial: MaterialSchemaSchema.optional(), + edgeMaterial: MaterialSchema.optional(), edgeMaterialPreset: z.string().optional(), - wallMaterial: MaterialSchemaSchema.optional(), + wallMaterial: MaterialSchema.optional(), wallMaterialPreset: z.string().optional(), position: z.tuple([z.number(), z.number(), z.number()]).default([0, 0, 0]), // Rotation around Y axis in radians @@ -54,7 +55,8 @@ export function getEffectiveRoofSurfaceMaterial( if (node.topMaterial !== undefined || typeof node.topMaterialPreset === 'string') { return { material: node.topMaterial, - materialPreset: typeof node.topMaterialPreset === 'string' ? node.topMaterialPreset : undefined, + materialPreset: + typeof node.topMaterialPreset === 'string' ? node.topMaterialPreset : undefined, } } } diff --git a/packages/core/src/schema/nodes/stair.ts b/packages/core/src/schema/nodes/stair.ts index 819698281..a0c68fe18 100644 --- a/packages/core/src/schema/nodes/stair.ts +++ b/packages/core/src/schema/nodes/stair.ts @@ -1,7 +1,8 @@ import dedent from 'dedent' import { z } from 'zod' import { BaseNode, nodeType, objectId } from '../base' -import { type MaterialSchema, MaterialSchema as MaterialSchemaSchema } from '../material' +import type { MaterialSchema as MaterialSchemaType } from '../material' +import { MaterialSchema } from '../material' import { StairSegmentNode } from './stair-segment' export const StairRailingMode = z.enum(['none', 'left', 'right', 'both']) @@ -15,20 +16,20 @@ export type StairTopLandingMode = z.infer export type StairSlabOpeningMode = z.infer export type StairSurfaceMaterialRole = 'railing' | 'tread' | 'side' export type StairSurfaceMaterialSpec = { - material?: MaterialSchema + material?: MaterialSchemaType materialPreset?: string } export const StairNode = BaseNode.extend({ id: objectId('stair'), type: nodeType('stair'), - material: MaterialSchemaSchema.optional(), + material: MaterialSchema.optional(), materialPreset: z.string().optional(), - railingMaterial: MaterialSchemaSchema.optional(), + railingMaterial: MaterialSchema.optional(), railingMaterialPreset: z.string().optional(), - treadMaterial: MaterialSchemaSchema.optional(), + treadMaterial: MaterialSchema.optional(), treadMaterialPreset: z.string().optional(), - sideMaterial: MaterialSchemaSchema.optional(), + sideMaterial: MaterialSchema.optional(), sideMaterialPreset: z.string().optional(), position: z.tuple([z.number(), z.number(), z.number()]).default([0, 0, 0]), // Rotation around Y axis in radians @@ -126,18 +127,26 @@ export function getEffectiveStairSurfaceMaterial( const treadFallback = { material: node.treadMaterial, - materialPreset: typeof node.treadMaterialPreset === 'string' ? node.treadMaterialPreset : undefined, + materialPreset: + typeof node.treadMaterialPreset === 'string' ? node.treadMaterialPreset : undefined, } const sideFallback = { material: node.sideMaterial, - materialPreset: typeof node.sideMaterialPreset === 'string' ? node.sideMaterialPreset : undefined, + materialPreset: + typeof node.sideMaterialPreset === 'string' ? node.sideMaterialPreset : undefined, } - if (role === 'tread' && (sideFallback.material !== undefined || sideFallback.materialPreset !== undefined)) { + if ( + role === 'tread' && + (sideFallback.material !== undefined || sideFallback.materialPreset !== undefined) + ) { return sideFallback } - if (role === 'side' && (treadFallback.material !== undefined || treadFallback.materialPreset !== undefined)) { + if ( + role === 'side' && + (treadFallback.material !== undefined || treadFallback.materialPreset !== undefined) + ) { return treadFallback } diff --git a/packages/core/src/store/actions/node-actions.ts b/packages/core/src/store/actions/node-actions.ts index d9e50b79b..81f587b7c 100644 --- a/packages/core/src/store/actions/node-actions.ts +++ b/packages/core/src/store/actions/node-actions.ts @@ -122,7 +122,7 @@ function buildMergedWallAttachmentUpdates( const wallChildren = [...(primary.children ?? []), ...(secondary.children ?? [])] as AnyNodeId[] for (const childId of wallChildren) { const child = nodes[childId] - if (!child || !('position' in child) || !Array.isArray(child.position)) { + if (!(child && 'position' in child && Array.isArray(child.position))) { continue } @@ -190,10 +190,12 @@ function buildWallMergePlans( }) const [primary, secondary] = sortedCandidates if ( - !primary || - !secondary || - !areWallStylesCompatible(primary, secondary) || - !areWallsCollinearAcrossPoint(primary, secondary, junction) + !( + primary && + secondary && + areWallStylesCompatible(primary, secondary) && + areWallsCollinearAcrossPoint(primary, secondary, junction) + ) ) { continue } @@ -275,6 +277,7 @@ export const createNodesAction = ( ops.forEach(({ node, parentId }) => { get().markDirty(node.id) if (parentId) get().markDirty(parentId) + else if (node.parentId) get().markDirty(node.parentId as AnyNodeId) }) } diff --git a/packages/core/src/store/use-scene.ts b/packages/core/src/store/use-scene.ts index 51e1caa9f..4a0cf5c53 100644 --- a/packages/core/src/store/use-scene.ts +++ b/packages/core/src/store/use-scene.ts @@ -111,7 +111,7 @@ function migrateWallSurfaceMaterials(node: Record) { materialPreset: typeof node.materialPreset === 'string' ? node.materialPreset : undefined, } - if (!hasInterior && !hasExterior) { + if (!(hasInterior || hasExterior)) { if (legacyFinish.material === undefined && legacyFinish.materialPreset === undefined) { return node } @@ -174,7 +174,7 @@ function migrateStairSurfaceMaterials(node: Record) { return legacyFinish } - if (!hasRailing && !hasTread && !hasSide) { + if (!(hasRailing || hasTread || hasSide)) { if (legacyFinish.material === undefined && legacyFinish.materialPreset === undefined) { return node } @@ -236,7 +236,7 @@ function migrateRoofSurfaceMaterials(node: Record) { materialPreset: typeof node.materialPreset === 'string' ? node.materialPreset : undefined, } - if (!hasTop && !hasEdge && !hasWall) { + if (!(hasTop || hasEdge || hasWall)) { if (legacyFinish.material === undefined && legacyFinish.materialPreset === undefined) { return node } @@ -350,7 +350,7 @@ function migrateNodes(nodes: Record): Record { } function getNodeChildIds(node: AnyNode): AnyNodeId[] { - if (!('children' in node) || !Array.isArray(node.children)) { + if (!('children' in node && Array.isArray(node.children))) { return [] } @@ -511,6 +511,13 @@ const useScene: UseSceneStore = create()( } } + set({ + nodes: cleanedNodes, + rootNodeIds, + dirtyNodes: new Set(), + collections: {}, + }) + const normalizedRootNodeIds = normalizeRootNodeIds(cleanedNodes, rootNodeIds) const reachableNodeIds = collectReachableNodeIds(cleanedNodes, normalizedRootNodeIds) if (normalizedRootNodeIds.length > 0) { @@ -701,8 +708,8 @@ let prevFutureLength = 0 let prevNodesSnapshot: Record | null = null export function clearSceneHistory() { - useScene.temporal.getState().clear() resetSceneHistoryPauseDepth() + useScene.temporal.getState().clear() prevPastLength = 0 prevFutureLength = 0 prevNodesSnapshot = null diff --git a/packages/core/src/systems/stair/stair-opening-sync.test.ts b/packages/core/src/systems/stair/stair-opening-sync.test.ts index 80221b12c..6d96b272f 100644 --- a/packages/core/src/systems/stair/stair-opening-sync.test.ts +++ b/packages/core/src/systems/stair/stair-opening-sync.test.ts @@ -1,5 +1,3 @@ -// @ts-expect-error — bun:test is provided by the Bun runtime; core does not -// depend on @types/bun so the import type is unresolved at compile time. import { describe, expect, test } from 'bun:test' import type { AnyNode } from '../../schema' import { BuildingNode, LevelNode, SlabNode, StairNode, StairSegmentNode } from '../../schema' diff --git a/packages/core/src/systems/stair/stair-opening-sync.ts b/packages/core/src/systems/stair/stair-opening-sync.ts index 2e478cd26..684083f51 100644 --- a/packages/core/src/systems/stair/stair-opening-sync.ts +++ b/packages/core/src/systems/stair/stair-opening-sync.ts @@ -1,14 +1,12 @@ +import { resolveLevelId } from '../../hooks/spatial-grid/spatial-grid-sync' import type { AnyNode, AnyNodeId, CeilingNode, - LevelNode, SlabNode, StairNode, StairSegmentNode, } from '../../schema' - -import { resolveLevelId } from '../../hooks/spatial-grid/spatial-grid-sync' import { DEFAULT_WALL_HEIGHT } from '../wall/wall-footprint' type Point2D = [number, number] @@ -36,10 +34,9 @@ type AxisAlignedRect = { maxZ: number } -const CURVED_STAIR_SLAB_OPENING_RATIO = 0.9 +const CURVED_STAIR_SLAB_OPENING_RATIO = 0.8 const STRAIGHT_STAIR_TARGET_THRESHOLD_MIN = 0.35 const STAIR_SLAB_OPENING_TIGHTENING = 0 -const CURVED_STAIR_OPENING_STEP_PADDING = 3 function clamp(value: number, min: number, max: number) { return Math.min(max, Math.max(min, value)) @@ -280,7 +277,7 @@ function polygonArea(points: Point2D[]) { for (let index = 0; index < points.length; index += 1) { const current = points[index] const next = points[(index + 1) % points.length] - if (!current || !next) continue + if (!(current && next)) continue area += current[0] * next[1] - next[0] * current[1] } return area / 2 @@ -426,39 +423,24 @@ function buildUnionPolygonsFromRects(rects: AxisAlignedRect[]): Point2D[][] { return polygons } -function getCurvedOpeningStepCount( - stair: StairNode, - innerRadius: number, - outerRadius: number, - totalSweep: number, -) { - const stepCount = Math.max(2, Math.round(stair.stepCount ?? 10)) - const stepSweep = Math.abs(totalSweep) / stepCount - const midRadius = Math.max((innerRadius + outerRadius) * 0.5, 0.01) - const treadDepth = Math.max(stepSweep * midRadius, 0.2) - return Math.min( - stepCount, +function getCurvedOpeningPolygon(stair: StairNode): Point2D[] { + const width = Math.max(stair.width ?? 1, 0.4) + const innerRadius = Math.max(0.2, stair.innerRadius ?? 0.9) + const outerRadius = innerRadius + width + const totalSweep = stair.sweepAngle ?? Math.PI / 2 + const openingSweep = + Math.sign(totalSweep || 1) * Math.max( - 1, - Math.ceil(1.8 / treadDepth), - Math.ceil(stepCount * CURVED_STAIR_SLAB_OPENING_RATIO), - ), - ) -} - -function buildArcOpeningPolygon( - stair: StairNode, - innerRadius: number, - outerRadius: number, - startAngle: number, - endAngle: number, -): Point2D[] { - const sweep = endAngle - startAngle + Math.abs(totalSweep) * CURVED_STAIR_SLAB_OPENING_RATIO, + Math.abs(totalSweep) / Math.max(stair.stepCount ?? 1, 1), + ) + const startAngle = totalSweep / 2 - openingSweep + const endAngle = totalSweep / 2 const segmentCount = Math.max( 10, Math.min( 32, - Math.ceil(Math.abs(sweep) / (Math.PI / 24) + Math.max(stair.stepCount ?? 1, 1) * 0.5), + Math.ceil(Math.abs(openingSweep) / (Math.PI / 24) + Math.max(stair.stepCount ?? 1, 1) * 0.5), ), ) const outerPoints: Point2D[] = [] @@ -466,7 +448,7 @@ function buildArcOpeningPolygon( for (let index = 0; index <= segmentCount; index++) { const t = index / segmentCount - const angle = startAngle + sweep * t + const angle = startAngle + (endAngle - startAngle) * t outerPoints.push( toWorldPlanPoint(stair, Math.cos(angle) * outerRadius, Math.sin(angle) * outerRadius), ) @@ -474,8 +456,7 @@ function buildArcOpeningPolygon( for (let index = segmentCount; index >= 0; index--) { const t = index / segmentCount - const angle = startAngle + sweep * t - + const angle = startAngle + (endAngle - startAngle) * t innerPoints.push( toWorldPlanPoint(stair, Math.cos(angle) * innerRadius, Math.sin(angle) * innerRadius), ) @@ -484,39 +465,6 @@ function buildArcOpeningPolygon( return [...outerPoints, ...innerPoints] } -function getCurvedOpeningPolygon(stair: StairNode, targetElevation?: number): Point2D[] { - const width = Math.max(stair.width ?? 1, 0.4) - const innerRadius = Math.max(0.2, stair.innerRadius ?? 0.9) - const outerRadius = innerRadius + width - const totalSweep = stair.sweepAngle ?? Math.PI / 2 - const stepCount = Math.max(2, Math.round(stair.stepCount ?? 10)) - const stepHeight = Math.max(stair.totalRise ?? 2.5, 0.1) / stepCount - const stepSweep = totalSweep / stepCount - const targetThreshold = Math.max(stepHeight * 2, STRAIGHT_STAIR_TARGET_THRESHOLD_MIN) - const endAngle = totalSweep / 2 - - const fallbackStartStepIndex = Math.max( - 0, - stepCount - getCurvedOpeningStepCount(stair, innerRadius, outerRadius, totalSweep), - ) - let startStepIndex = fallbackStartStepIndex - if (typeof targetElevation === 'number') { - for (let index = 0; index < stepCount; index += 1) { - const stepTopElevation = stepHeight * (index + 1) - if (stepTopElevation >= targetElevation - targetThreshold) { - startStepIndex = Math.max( - 0, - Math.min(fallbackStartStepIndex, index - CURVED_STAIR_OPENING_STEP_PADDING), - ) - break - } - } - } - - const startAngle = -totalSweep / 2 + stepSweep * startStepIndex - return buildArcOpeningPolygon(stair, innerRadius, outerRadius, startAngle, endAngle) -} - function getSpiralOpeningPolygon(stair: StairNode): Point2D[] { const radius = Math.max(0.05, stair.innerRadius ?? 0.9) + Math.max(stair.width ?? 1, 0.4) const segmentCount = 48 @@ -621,7 +569,7 @@ function getStairOpeningPolygons( } if (stair.stairType === 'curved') { - return [getCurvedOpeningPolygon(stair, targetElevation)] + return [getCurvedOpeningPolygon(stair)] } if (stair.stairType === 'spiral') { @@ -775,8 +723,7 @@ export function syncAutoStairOpenings(nodes: Record) { const nextMetadata = [...manualMetadata, ...stairHoles.map((hole) => hole.metadata)] if ( - !polygonsEqual(existingHoles, nextHoles) || - !metadataEqual(existingMetadata, nextMetadata) + !(polygonsEqual(existingHoles, nextHoles) && metadataEqual(existingMetadata, nextMetadata)) ) { updates.push({ id: slab.id, @@ -826,8 +773,7 @@ export function syncAutoStairOpenings(nodes: Record) { const nextMetadata = [...manualMetadata, ...stairHoles.map((hole) => hole.metadata)] if ( - !polygonsEqual(existingHoles, nextHoles) || - !metadataEqual(existingMetadata, nextMetadata) + !(polygonsEqual(existingHoles, nextHoles) && metadataEqual(existingMetadata, nextMetadata)) ) { updates.push({ id: ceiling.id, diff --git a/packages/core/src/systems/wall/wall-curve.ts b/packages/core/src/systems/wall/wall-curve.ts index 902050ba8..743f14cac 100644 --- a/packages/core/src/systems/wall/wall-curve.ts +++ b/packages/core/src/systems/wall/wall-curve.ts @@ -1,5 +1,5 @@ -import type { Point2D } from './wall-mitering' import type { FenceNode, WallNode } from '../../schema' +import type { Point2D } from './wall-mitering' const CURVE_EPSILON = 1e-6 const DEFAULT_SAMPLE_SEGMENTS = 24 @@ -115,7 +115,7 @@ function getWallArcData(wall: WallCurveLike) { } const absSagitta = Math.abs(sagitta) - const radius = chord.length * chord.length / (8 * absSagitta) + absSagitta / 2 + const radius = (chord.length * chord.length) / (8 * absSagitta) + absSagitta / 2 const centerOffset = radius - absSagitta const direction = Math.sign(sagitta) || 1 const center = { @@ -183,7 +183,10 @@ export function getWallMidpointHandlePoint(wall: WallCurveLike) { export function sampleWallCenterline(wall: WallCurveLike, segments = DEFAULT_SAMPLE_SEGMENTS) { const count = Math.max(1, segments) - return Array.from({ length: count + 1 }, (_, index) => getWallCurveFrameAt(wall, index / count).point) + return Array.from( + { length: count + 1 }, + (_, index) => getWallCurveFrameAt(wall, index / count).point, + ) } export function getWallCurveLength(wall: WallCurveLike, segments = DEFAULT_SAMPLE_SEGMENTS) { diff --git a/packages/core/src/systems/wall/wall-mitering.ts b/packages/core/src/systems/wall/wall-mitering.ts index f1ab2729f..e6a2f2953 100644 --- a/packages/core/src/systems/wall/wall-mitering.ts +++ b/packages/core/src/systems/wall/wall-mitering.ts @@ -135,10 +135,7 @@ function findJunctions(walls: WallNode[]): Map { return actualJunctions } -function getWallDirectionFromJunction( - wall: WallNode, - endType: 'start' | 'end' | 'passthrough', -) { +function getWallDirectionFromJunction(wall: WallNode, endType: 'start' | 'end' | 'passthrough') { if (endType === 'passthrough') { return { x: wall.end[0] - wall.start[0], @@ -148,9 +145,7 @@ function getWallDirectionFromJunction( if (isCurvedWall(wall)) { const frame = getWallCurveFrameAt(wall, endType === 'start' ? 0 : 1) - return endType === 'start' - ? frame.tangent - : { x: -frame.tangent.x, y: -frame.tangent.y } + return endType === 'start' ? frame.tangent : { x: -frame.tangent.x, y: -frame.tangent.y } } return endType === 'start' @@ -158,18 +153,12 @@ function getWallDirectionFromJunction( : { x: wall.start[0] - wall.end[0], y: wall.start[1] - wall.end[1] } } -function getWallBoundaryFrame( - wall: WallNode, - endType: 'start' | 'end', -) { +function getWallBoundaryFrame(wall: WallNode, endType: 'start' | 'end') { if (isCurvedWall(wall)) { const frame = getWallCurveFrameAt(wall, endType === 'start' ? 0 : 1) return { point: frame.point, - tangent: - endType === 'start' - ? frame.tangent - : { x: -frame.tangent.x, y: -frame.tangent.y }, + tangent: endType === 'start' ? frame.tangent : { x: -frame.tangent.x, y: -frame.tangent.y }, normal: frame.normal, } } diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 68f558341..98b1f97c9 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -5,7 +5,8 @@ "rootDir": "src", "noEmit": false, "composite": true, - "incremental": true + "incremental": true, + "types": ["bun"] }, "include": ["src"], "exclude": ["node_modules", "dist"] diff --git a/packages/editor/package.json b/packages/editor/package.json index 6c4697dc0..ed1a7e3e2 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -44,24 +44,24 @@ "clsx": "^2.1.1", "cmdk": "^1.1.1", "howler": "^2.2.4", - "lucide-react": "^0.562.0", + "lucide-react": "^1.7.0", "mitt": "^3.0.1", "motion": "^12.34.3", "nanoid": "^5.1.6", "tailwind-merge": "^3.5.0", - "three-mesh-bvh": "^0.9.8", "zod": "^4.3.6", - "zustand": "^5.0.11" + "zustand": "^5.0.11", + "three-mesh-bvh": "~0.9.8" }, "devDependencies": { "@pascal-app/core": "^0.7.0", "@pascal-app/viewer": "^0.7.0", "@pascal/typescript-config": "*", + "@types/bun": "^1.3.0", "@types/howler": "^2.2.12", - "@types/node": "^22.19.12", "@types/react": "19.2.2", "@types/react-dom": "19.2.2", "@types/three": "^0.184.0", - "typescript": "5.9.3" + "typescript": "6.0.2" } } diff --git a/packages/editor/src/components/editor-2d/renderers/floorplan-draft-layer.tsx b/packages/editor/src/components/editor-2d/renderers/floorplan-draft-layer.tsx index eef666588..587d24754 100644 --- a/packages/editor/src/components/editor-2d/renderers/floorplan-draft-layer.tsx +++ b/packages/editor/src/components/editor-2d/renderers/floorplan-draft-layer.tsx @@ -72,7 +72,12 @@ export const FloorplanDraftLayer = memo(function FloorplanDraftLayer({ )} {polygonDraftPolygonPoints && ( - + )} {polygonDraftPolylinePoints && ( diff --git a/packages/editor/src/components/editor-2d/renderers/floorplan-measurements-layer.tsx b/packages/editor/src/components/editor-2d/renderers/floorplan-measurements-layer.tsx index 8c5663baf..af732f91a 100644 --- a/packages/editor/src/components/editor-2d/renderers/floorplan-measurements-layer.tsx +++ b/packages/editor/src/components/editor-2d/renderers/floorplan-measurements-layer.tsx @@ -125,7 +125,12 @@ export const FloorplanMeasurementsLayer = memo(function FloorplanMeasurementsLay return ( <> {measurements.map((measurement) => ( - + > ) : null} @@ -438,8 +438,6 @@ export const FloorplanStairLayer = memo(function FloorplanStairLayer({ } : undefined } - onPointerEnter={canSelectStairs ? () => onStairHoverEnter(stair.id) : undefined} - onPointerLeave={canSelectStairs ? () => onStairHoverChange(null) : undefined} onPointerDown={ canFocusStairs && stairSelected ? (event) => { @@ -449,6 +447,8 @@ export const FloorplanStairLayer = memo(function FloorplanStairLayer({ } : undefined } + onPointerEnter={canSelectStairs ? () => onStairHoverEnter(stair.id) : undefined} + onPointerLeave={canSelectStairs ? () => onStairHoverChange(null) : undefined} pointerEvents={canSelectStairs ? undefined : 'none'} style={canSelectStairs ? { cursor } : undefined} > @@ -456,8 +456,8 @@ export const FloorplanStairLayer = memo(function FloorplanStairLayer({ s.isCaptureMode) const isMobile = useIsMobile() if (isMobile) { @@ -229,7 +230,7 @@ export function EditorLayoutV2({ {/* Main content: left column + right column */} - {sidebarTabs.length > 0 && ( + {!isCaptureMode && sidebarTabs.length > 0 && ( {viewerContent} diff --git a/packages/editor/src/components/editor/first-person-controls.tsx b/packages/editor/src/components/editor/first-person-controls.tsx index cbc7ad140..c2f2fdf1c 100644 --- a/packages/editor/src/components/editor/first-person-controls.tsx +++ b/packages/editor/src/components/editor/first-person-controls.tsx @@ -455,14 +455,15 @@ export const FirstPersonControls = () => { {controllerStart && ( { floatSpringK={1200} gravity={9.81} jumpVel={6} + key="first-person-controller" maxRunSpeed={5.5} maxSlope={1.2} maxWalkSpeed={4} position={controllerStart.position} - acceleration={26} - airDragFactor={0.3} - deceleration={30} + ref={controllerRef} /> )} @@ -551,12 +551,12 @@ export const FirstPersonOverlay = ({ onExit }: { onExit: () => void }) => { {isLocked && ( - + - - - - + + + + Click to look around @@ -591,7 +591,7 @@ function ControlHint({ label, keys }: { label: string; keys: string[] }) { function InlineControlHint({ label, keyLabel }: { label: string; keyLabel: string }) { return ( - + {label} diff --git a/packages/editor/src/components/editor/first-person/build-collider-world.ts b/packages/editor/src/components/editor/first-person/build-collider-world.ts index a908d3e05..fa5b563f8 100644 --- a/packages/editor/src/components/editor/first-person/build-collider-world.ts +++ b/packages/editor/src/components/editor/first-person/build-collider-world.ts @@ -1,7 +1,7 @@ import { - getGarageVisibleOpeningRatio, type AnyNodeId, type DoorNode, + getGarageVisibleOpeningRatio, isOperationDoorType, sceneRegistry, useInteractive, @@ -97,11 +97,11 @@ function shouldSkipColliderNode(nodeId: string, type: (typeof COLLIDER_NODE_TYPE const node = useScene.getState().nodes[nodeId as AnyNodeId] if (!node || node.type !== 'door') return false - if (node.openingKind === 'opening') return true - if (!node.segments.length) return true - return node.segments.every((segment) => segment.type === 'empty') + if (node.openingKind === 'opening') return true + + return node.segments.every((segment: { type: string }) => segment.type === 'empty') } function createDoorLeafColliderGeometry(root: THREE.Object3D, node: DoorNode) { @@ -273,9 +273,7 @@ export function buildFirstPersonColliderWorldFromRegistry(): FirstPersonCollider } const mergedGeometry = mergeGeometries(geometries, false) - geometries.forEach((geometry) => { - geometry.dispose() - }) + geometries.forEach((geometry) => geometry.dispose()) if (!mergedGeometry || mergedGeometry.getAttribute('position') == null) { mergedGeometry?.dispose() diff --git a/packages/editor/src/components/editor/first-person/bvh-ecctrl.tsx b/packages/editor/src/components/editor/first-person/bvh-ecctrl.tsx index 2a3e1e795..1f579a543 100644 --- a/packages/editor/src/components/editor/first-person/bvh-ecctrl.tsx +++ b/packages/editor/src/components/editor/first-person/bvh-ecctrl.tsx @@ -1,15 +1,8 @@ import '../../../three-types' import { TransformControls, useKeyboardControls } from '@react-three/drei' -import { useFrame, useThree, type ThreeElements } from '@react-three/fiber' -import { - Suspense, - forwardRef, - useCallback, - useImperativeHandle, - useMemo, - useRef, -} from 'react' +import { type ThreeElements, useFrame, useThree } from '@react-three/fiber' import type { ReactNode } from 'react' +import { forwardRef, Suspense, useCallback, useImperativeHandle, useMemo, useRef } from 'react' import * as THREE from 'three' import { clamp } from 'three/src/math/MathUtils.js' @@ -156,16 +149,7 @@ const BVHEcctrl = forwardRef( const moveDirRef = useRef(null) const elapsedRef = useRef(0) - function useIsInsideKeyboardControls() { - try { - return !!useKeyboardControls() - } catch { - return false - } - } - - const isInsideKeyboardControls = useIsInsideKeyboardControls() - const [_, getKeys] = isInsideKeyboardControls ? useKeyboardControls() : [null, null] + const [, getKeys] = useKeyboardControls() const presetKeys = { forward: false, backward: false, @@ -222,11 +206,11 @@ const BVHEcctrl = forwardRef( const scaledContactRadiusVec = useRef(new THREE.Vector3()) const deltaDist = useRef(new THREE.Vector3()) const currSlopeAngle = useRef(0) - const localMinDistance = useRef(Infinity) + const localMinDistance = useRef(Number.POSITIVE_INFINITY) const localClosestPoint = useRef(new THREE.Vector3()) const localHitNormal = useRef(new THREE.Vector3()) const triNormal = useRef(new THREE.Vector3()) - const globalMinDistance = useRef(Infinity) + const globalMinDistance = useRef(Number.POSITIVE_INFINITY) const globalClosestPoint = useRef(new THREE.Vector3()) const triHitPoint = useRef(new THREE.Vector3()) const segHitPoint = useRef(new THREE.Vector3()) @@ -286,7 +270,13 @@ const BVHEcctrl = forwardRef( const moving = currentLinVel.current.lengthSq() > 1e-6 const platformIsMoving = totalPlatformDeltaPos.current.lengthSq() > 1e-6 - if (!moving && isOnGround.current && !jump && !isOnMovingPlatform.current && !platformIsMoving) { + if ( + !moving && + isOnGround.current && + !jump && + !isOnMovingPlatform.current && + !platformIsMoving + ) { idleTime.current += delta if (idleTime.current > sleepTimeout) isSleeping.current = true } else { @@ -340,7 +330,10 @@ const BVHEcctrl = forwardRef( upAxis.current, ) characterModelTargetQuat.current.setFromRotationMatrix(characterModelLookMatrix.current) - characterModelRef.current.quaternion.slerp(characterModelTargetQuat.current, delta * turnSpeed) + characterModelRef.current.quaternion.slerp( + characterModelTargetQuat.current, + delta * turnSpeed, + ) } const maxSpeed = run ? maxRunSpeed : maxWalkSpeed @@ -358,18 +351,33 @@ const BVHEcctrl = forwardRef( ) currentLinVel.current.add(deltaLinVel.current) } else if (isOnGround.current) { - deltaLinVel.current.copy(currentLinVelOnPlane.current).clampLength(0, deceleration * friction * delta) + deltaLinVel.current + .copy(currentLinVelOnPlane.current) + .clampLength(0, deceleration * friction * delta) currentLinVel.current.sub(deltaLinVel.current) } }, - [acceleration, airDragFactor, counterAccFactor, deceleration, maxRunSpeed, maxWalkSpeed, turnSpeed, characterOrigin], + [ + acceleration, + airDragFactor, + counterAccFactor, + deceleration, + maxRunSpeed, + maxWalkSpeed, + turnSpeed, + characterOrigin, + ], ) const updateSegmentBBox = useCallback(() => { if (!characterGroupRef.current) return - characterSegment.current.start.set(0, capsuleLength / 2, 0).add(characterGroupRef.current.position) - characterSegment.current.end.set(0, -capsuleLength / 2, 0).add(characterGroupRef.current.position) + characterSegment.current.start + .set(0, capsuleLength / 2, 0) + .add(characterGroupRef.current.position) + characterSegment.current.end + .set(0, -capsuleLength / 2, 0) + .add(characterGroupRef.current.position) characterBbox.current .makeEmpty() @@ -394,11 +402,18 @@ const BVHEcctrl = forwardRef( const collisionCheck = useCallback( (mesh: THREE.Mesh, originMatrix: THREE.Matrix4, delta: number) => { - if (!mesh.visible || !mesh.geometry.boundsTree || mesh.userData.excludeCollisionCheck) return + if (!(mesh.visible && mesh.geometry.boundsTree) || mesh.userData.excludeCollisionCheck) + return - originMatrix.decompose(contactTempPos.current, contactTempQuat.current, contactTempScale.current) + originMatrix.decompose( + contactTempPos.current, + contactTempQuat.current, + contactTempScale.current, + ) collideInvertMatrix.current.copy(originMatrix).invert() - localCharacterSegment.current.copy(characterSegment.current).applyMatrix4(collideInvertMatrix.current) + localCharacterSegment.current + .copy(characterSegment.current) + .applyMatrix4(collideInvertMatrix.current) scaledContactRadiusVec.current.set( capsuleRadius / contactTempScale.current.x, @@ -445,7 +460,10 @@ const BVHEcctrl = forwardRef( contactDepth.current = capsuleRadius - capsuleContactPoint.current.distanceTo(triContactPoint.current) - accumulatedContactNormal.current.addScaledVector(contactNormal.current, contactDepth.current) + accumulatedContactNormal.current.addScaledVector( + contactNormal.current, + contactDepth.current, + ) accumulatedContactPoint.current.add(triContactPoint.current) totalDepth.current += contactDepth.current triangleCount.current += 1 @@ -492,14 +510,16 @@ const BVHEcctrl = forwardRef( const floatingCheck = useCallback( (mesh: THREE.Mesh, originMatrix: THREE.Matrix4) => { - if (!mesh.visible || !mesh.geometry.boundsTree || mesh.userData.excludeFloatHit) return + if (!(mesh.visible && mesh.geometry.boundsTree) || mesh.userData.excludeFloatHit) return originMatrix.decompose(floatTempPos.current, floatTempQuat.current, floatTempScale.current) floatInvertMatrix.current.copy(originMatrix).invert() floatNormalInverseMatrix.current.getNormalMatrix(floatInvertMatrix.current) floatNormalMatrix.current.getNormalMatrix(originMatrix) - localFloatSensorSegment.current.copy(floatSensorSegment.current).applyMatrix4(floatInvertMatrix.current) + localFloatSensorSegment.current + .copy(floatSensorSegment.current) + .applyMatrix4(floatInvertMatrix.current) localFloatSensorBboxExpendPoint.current .copy(floatSensorBboxExpendPoint.current) .applyMatrix4(floatInvertMatrix.current) @@ -517,20 +537,33 @@ const BVHEcctrl = forwardRef( localFloatSensorBbox.current.min.addScaledVector(scaledFloatRadiusVec.current, -1) localFloatSensorBbox.current.max.add(scaledFloatRadiusVec.current) - localMinDistance.current = Infinity - localClosestPoint.current.set(Infinity, Infinity, Infinity) + localMinDistance.current = Number.POSITIVE_INFINITY + localClosestPoint.current.set( + Number.POSITIVE_INFINITY, + Number.POSITIVE_INFINITY, + Number.POSITIVE_INFINITY, + ) mesh.geometry.boundsTree.shapecast({ intersectsBounds: (box) => box.intersectsBox(localFloatSensorBbox.current), intersectsTriangle: (tri) => { - tri.closestPointToSegment(localFloatSensorSegment.current, triHitPoint.current, segHitPoint.current) - localUpAxis.current.copy(upAxis.current).applyMatrix3(floatNormalInverseMatrix.current).normalize() + tri.closestPointToSegment( + localFloatSensorSegment.current, + triHitPoint.current, + segHitPoint.current, + ) + localUpAxis.current + .copy(upAxis.current) + .applyMatrix3(floatNormalInverseMatrix.current) + .normalize() deltaHit.current.subVectors(triHitPoint.current, localFloatSensorSegment.current.start) deltaHit.current.divide(scaledFloatRadiusVec.current) const totalLengthSq = deltaHit.current.lengthSq() const dot = deltaHit.current.dot(localUpAxis.current) - const verticalLength = Math.abs(dot) / ((capsuleRadius + floatHeight + floatPullBackHeight) / floatSensorRadius) + const verticalLength = + Math.abs(dot) / + ((capsuleRadius + floatHeight + floatPullBackHeight) / floatSensorRadius) const horizontalLength = Math.sqrt(Math.max(0, totalLengthSq - dot * dot)) if (horizontalLength < 1 && verticalLength < 1) { @@ -560,9 +593,14 @@ const BVHEcctrl = forwardRef( const handleFloatingResponse = useCallback( (meshes: THREE.Mesh[], jump: boolean, delta: number) => { if (meshes.length === 0) return + let shouldJump = jump - globalMinDistance.current = Infinity - globalClosestPoint.current.set(Infinity, Infinity, Infinity) + globalMinDistance.current = Number.POSITIVE_INFINITY + globalClosestPoint.current.set( + Number.POSITIVE_INFINITY, + Number.POSITIVE_INFINITY, + Number.POSITIVE_INFINITY, + ) floatHitNormal.current.set(0, 1, 0) isOnGround.current = false totalPlatformDeltaPos.current.set(0, 0, 0) @@ -574,7 +612,11 @@ const BVHEcctrl = forwardRef( } } - if (floatCheckType !== 'SHAPECAST' && floatRaycastCandidates.length > 0 && globalMinDistance.current === Infinity) { + if ( + floatCheckType !== 'SHAPECAST' && + floatRaycastCandidates.length > 0 && + globalMinDistance.current === Number.POSITIVE_INFINITY + ) { floatRaycaster.current.ray.origin.copy(floatSensorSegment.current.start) floatRaycaster.current.ray.direction.copy(gravityDir.current) const hits = floatRaycaster.current.intersectObjects(floatRaycastCandidates, false) @@ -582,23 +624,28 @@ const BVHEcctrl = forwardRef( if (hit?.point) { globalClosestPoint.current.copy(hit.point) if (hit.face) { - floatHitNormal.current.copy(hit.face.normal).transformDirection(hit.object.matrixWorld).normalize() + floatHitNormal.current + .copy(hit.face.normal) + .transformDirection(hit.object.matrixWorld) + .normalize() } } } - if (globalClosestPoint.current.x === Infinity) return + if (globalClosestPoint.current.x === Number.POSITIVE_INFINITY) return - relativeHitPoint.current.copy(globalClosestPoint.current).sub(floatSensorSegment.current.start) + relativeHitPoint.current + .copy(globalClosestPoint.current) + .sub(floatSensorSegment.current.start) const currentDistance = relativeHitPoint.current.length() currSlopeAngle.current = floatHitNormal.current.angleTo(upAxis.current) if (currentDistance < floatHeight + capsuleRadius) { isOnGround.current = true - jump = false + shouldJump = false } - if (!jump) { + if (!shouldJump) { const displacement = floatHeight + capsuleRadius - currentDistance const velocityOnHitNormal = currentLinVel.current.dot(floatHitNormal.current) const springForce = displacement * floatSpringK @@ -608,7 +655,17 @@ const BVHEcctrl = forwardRef( currentLinVel.current.addScaledVector(floatHitNormal.current, (totalForce / mass) * delta) } }, - [capsuleRadius, floatCheckType, floatDampingC, floatHeight, floatRaycastCandidates, floatSpringK, floatingCheck, gravity, mass], + [ + capsuleRadius, + floatCheckType, + floatDampingC, + floatHeight, + floatRaycastCandidates, + floatSpringK, + floatingCheck, + gravity, + mass, + ], ) const updateCharacterWithPlatform = useCallback(() => { @@ -646,8 +703,14 @@ const BVHEcctrl = forwardRef( ) const resetLinVel = useCallback(() => currentLinVel.current.set(0, 0, 0), []) - const addLinVel = useCallback((velocity: THREE.Vector3) => currentLinVel.current.add(velocity), []) - const setLinVel = useCallback((velocity: THREE.Vector3) => currentLinVel.current.copy(velocity), []) + const addLinVel = useCallback( + (velocity: THREE.Vector3) => currentLinVel.current.add(velocity), + [], + ) + const setLinVel = useCallback( + (velocity: THREE.Vector3) => currentLinVel.current.copy(velocity), + [], + ) const setMovement = useCallback((movement: MovementInput) => { if (movement.forward !== undefined) forwardState.current = movement.forward if (movement.backward !== undefined) backwardState.current = movement.backward @@ -682,7 +745,9 @@ const BVHEcctrl = forwardRef( debugRaySensorEnd.current?.position.copy(floatSensorSegment.current.end) standPointRef.current?.position.copy(globalClosestPoint.current) if (characterGroupRef.current) { - lookDirRef.current?.position.copy(characterGroupRef.current.position).addScaledVector(upAxis.current, 0.7) + lookDirRef.current?.position + .copy(characterGroupRef.current.position) + .addScaledVector(upAxis.current, 0.7) } lookDirRef.current?.lookAt(lookDirRef.current.position.clone().add(camProjDir.current)) inputDirRef.current?.position.copy(characterSegment.current.end) @@ -698,7 +763,7 @@ const BVHEcctrl = forwardRef( if (paused || elapsedRef.current < delay) return const deltaTime = Math.min(1 / 45, delta) * slowMotionFactor - const keys = isInsideKeyboardControls && getKeys ? getKeys() : presetKeys + const keys = getKeys() ?? presetKeys const forward = forwardState.current || keys.forward const backward = backwardState.current || keys.backward const leftward = leftwardState.current || keys.leftward @@ -740,7 +805,7 @@ const BVHEcctrl = forwardRef( return ( - + {debug && ( @@ -777,8 +842,8 @@ const BVHEcctrl = forwardRef( - - + + diff --git a/packages/editor/src/components/editor/floating-action-menu.tsx b/packages/editor/src/components/editor/floating-action-menu.tsx old mode 100755 new mode 100644 diff --git a/packages/editor/src/components/editor/floorplan-panel.tsx b/packages/editor/src/components/editor/floorplan-panel.tsx index 7c91df600..a64b8800a 100644 --- a/packages/editor/src/components/editor/floorplan-panel.tsx +++ b/packages/editor/src/components/editor/floorplan-panel.tsx @@ -4092,7 +4092,6 @@ const FloorplanGeometryLayer = memo(function FloorplanGeometryLayer({ d={path} fill={isDeleteHovered ? palette.deleteFill : palette.slabFill} fillRule="evenodd" - opacity={slabFillOpacity} onClick={ canSelectSlabs ? (event) => { @@ -4111,9 +4110,10 @@ const FloorplanGeometryLayer = memo(function FloorplanGeometryLayer({ } onPointerEnter={canSelectSlabs ? () => onSlabHoverChange(slab.id) : undefined} onPointerLeave={canSelectSlabs ? () => onSlabHoverChange(null) : undefined} + opacity={slabFillOpacity} pointerEvents={canSelectSlabs ? undefined : 'none'} - style={canSelectSlabs ? { cursor: EDITOR_CURSOR } : undefined} stroke="none" + style={canSelectSlabs ? { cursor: EDITOR_CURSOR } : undefined} /> {isSelected && !isDeleteHovered ? ( { @@ -4189,9 +4188,10 @@ const FloorplanGeometryLayer = memo(function FloorplanGeometryLayer({ canSelectCeilings ? () => onCeilingHoverChange(ceiling.id) : undefined } onPointerLeave={canSelectCeilings ? () => onCeilingHoverChange(null) : undefined} + opacity={ceilingFillOpacity} pointerEvents={canSelectCeilings ? undefined : 'none'} - style={canSelectCeilings ? { cursor: EDITOR_CURSOR } : undefined} stroke="none" + style={canSelectCeilings ? { cursor: EDITOR_CURSOR } : undefined} /> {isSelected && !isDeleteHovered ? ( ) : null} @@ -4427,8 +4427,8 @@ const FloorplanGeometryLayer = memo(function FloorplanGeometryLayer({ {canSelectGeometry && ( @@ -5963,6 +5963,12 @@ function FloorplanItemImage({ }) { const resolvedUrl = useResolvedAssetUrl(url) if (!resolvedUrl) return null + // The PNG is captured with the modal's top-down camera (default up = +Y), + // so its pixel-right is world +X and pixel-up is world -Z. The plan SVG + // negates both axes (`toSvgX(v) = -v`, `toSvgY(v) = -v`), which together + // are a 180° rotation — so the captured image lands upside-down / + // mirrored when overlaid as-is. Bake that 180° into the image transform + // here; the panel / modal previews use the PNG directly and stay correct. const rotationDeg = (-rotation * 180) / Math.PI + 180 return ( ) : null} {itemDimensionMeasurements.length > 0 ? ( @@ -6308,8 +6315,8 @@ const FloorplanNodeLayer = memo(function FloorplanNodeLayer({ onEdgePointerDown(nodeId, edgeIndex, event) + : undefined + } pointerEvents="stroke" stroke="transparent" strokeLinecap="round" @@ -7234,11 +7246,6 @@ const FloorplanPolygonHandleLayer = memo(function FloorplanPolygonHandleLayer({ x2={endSvg.x} y1={startSvg.y} y2={endSvg.y} - onPointerDown={ - onEdgePointerDown - ? (event) => onEdgePointerDown(nodeId, edgeIndex, event) - : undefined - } /> ) @@ -9838,7 +9845,7 @@ export function FloorplanPanel() { zoneVertexDragState != null || isPolygonDraftBuildActive - if (!hasUserAdjustedViewportRef.current && !transientFloorplanFit) { + if (!(hasUserAdjustedViewportRef.current || transientFloorplanFit)) { setViewport((current) => floorplanViewportEquals(current, fittedViewport) ? current : fittedViewport, ) @@ -10970,6 +10977,14 @@ export function FloorplanPanel() { } }, [isItemPlacementPreviewActive, scheduleMovingFloorplanNodeRefresh]) + useEffect(() => { + if (!hasPendingItemMeshFootprints) { + return + } + + scheduleMovingFloorplanNodeRefresh() + }, [hasPendingItemMeshFootprints, scheduleMovingFloorplanNodeRefresh]) + // Subscribe to the live-transforms store so rotation/position changes that // *don't* go through pointer events still refresh the floorplan — e.g. R/T // keyboard rotation during placement updates `useLiveTransforms` but emits @@ -10985,14 +11000,6 @@ export function FloorplanPanel() { return unsubscribe }, [isItemPlacementPreviewActive, scheduleMovingFloorplanNodeRefresh]) - useEffect(() => { - if (!hasPendingItemMeshFootprints) { - return - } - - scheduleMovingFloorplanNodeRefresh() - }, [hasPendingItemMeshFootprints, scheduleMovingFloorplanNodeRefresh]) - useEffect(() => { if (!(movingNode?.type === 'door' || movingNode?.type === 'window')) { return @@ -16132,17 +16139,13 @@ export function FloorplanPanel() { onDuplicate: handleSelectedItemDuplicate, onMove: handleSelectedItemMove, }} + offsetY={FLOORPLAN_ACTION_MENU_OFFSET_Y} opening={{ position: selectedOpeningActionMenuPosition, onDelete: handleSelectedOpeningDelete, onDuplicate: handleSelectedOpeningDuplicate, onMove: handleSelectedOpeningMove, }} - spawn={{ - position: selectedSpawnActionMenuPosition, - onDelete: handleSelectedSpawnDelete, - onMove: handleSelectedSpawnMove, - }} roof={{ position: selectedRoofActionMenuPosition, onDelete: handleSelectedRoofDelete, @@ -16157,6 +16160,11 @@ export function FloorplanPanel() { : handleSelectedSlabDelete, onMove: selectedSlabEditingHole ? handleSelectedSlabHoleMove : handleSelectedSlabMove, }} + spawn={{ + position: selectedSpawnActionMenuPosition, + onDelete: handleSelectedSpawnDelete, + onMove: handleSelectedSpawnMove, + }} stair={{ position: selectedStairActionMenuPosition, onDelete: handleSelectedStairDelete, @@ -16168,7 +16176,6 @@ export function FloorplanPanel() { onDelete: handleSelectedWallDelete, onMove: handleSelectedWallMove, }} - offsetY={FLOORPLAN_ACTION_MENU_OFFSET_Y} /> {referenceScaleDraft && ( @@ -16201,7 +16208,7 @@ export function FloorplanPanel() { - + Drawn line @@ -16363,8 +16370,8 @@ export function FloorplanPanel() { @@ -16483,8 +16490,8 @@ export function FloorplanPanel() { {hints.map((hint) => ( @@ -753,7 +755,7 @@ function PaintCursorLayer({ (activePaintMaterial.material !== undefined || activePaintMaterial.materialPreset !== undefined), ) - const label = !hasMaterial ? 'Choose material' : `Paint ${activePaintTarget}` + const label = hasMaterial ? `Paint ${activePaintTarget}` : 'Choose material' const icon = 'mdi:format-color-fill' useLayoutEffect(() => { @@ -786,16 +788,16 @@ function PaintCursorLayer({ const ViewerCanvas = memo(function ViewerCanvas({ isVersionPreviewMode, isLoading, + isFirstPersonMode, hasLoadedInitialScene, showLoader, - isFirstPersonMode, onThumbnailCapture, }: { isVersionPreviewMode: boolean isLoading: boolean + isFirstPersonMode: boolean hasLoadedInitialScene: boolean showLoader: boolean - isFirstPersonMode: boolean onThumbnailCapture?: (blob: Blob, cameraData: SnapshotCameraData) => void }) { const viewMode = useEditor((s) => s.viewMode) @@ -837,7 +839,7 @@ const ViewerCanvas = memo(function ViewerCanvas({ window.removeEventListener('pointermove', handlePointerMove) window.removeEventListener('pointerup', handlePointerUp) } - }, [setFloorplanPaneRatio]) + }, []) useEffect(() => { setIsCameraControlsHintVisible(!readCameraControlsHintDismissed()) @@ -940,7 +942,9 @@ export default function Editor({ commandPaletteEmptyAction, }: EditorProps) { const isFirstPersonMode = useEditor((s) => s.isFirstPersonMode) + useKeyboard({ isVersionPreviewMode, disabled: isFirstPersonMode }) + const { isLoadingSceneRef } = useAutoSave({ onSave, onDirty, @@ -951,8 +955,7 @@ export default function Editor({ const [isSceneLoading, setIsSceneLoading] = useState(false) const [hasLoadedInitialScene, setHasLoadedInitialScene] = useState(false) const isPreviewMode = useEditor((s) => s.isPreviewMode) - const firstPersonPreviousLevelRef = useRef(useViewer.getState().selection.levelId) - const wasFirstPersonModeRef = useRef(isFirstPersonMode) + const isCaptureMode = useEditor((s) => s.isCaptureMode) const sidebarWidth = useSidebarStore((s) => s.width) const isSidebarCollapsed = useSidebarStore((s) => s.isCollapsed) @@ -970,39 +973,6 @@ export default function Editor({ } }, [projectId]) - useEffect(() => { - const wasFirstPersonMode = wasFirstPersonModeRef.current - wasFirstPersonModeRef.current = isFirstPersonMode - - if (isFirstPersonMode && !wasFirstPersonMode) { - const viewer = useViewer.getState() - firstPersonPreviousLevelRef.current = viewer.selection.levelId - viewer.setCameraMode('perspective') - viewer.setWallMode('up') - viewer.setWalkthroughMode(true) - viewer.setSelection({ selectedIds: [], zoneId: null }) - return - } - - if (!(wasFirstPersonMode && !isFirstPersonMode)) return - - const viewer = useViewer.getState() - const previousLevelId = firstPersonPreviousLevelRef.current - firstPersonPreviousLevelRef.current = null - viewer.setWalkthroughMode(false) - - if (!previousLevelId) return - - const previousLevelNode = useScene.getState().nodes[previousLevelId] - if (previousLevelNode?.type === 'level') { - viewer.setSelection({ - levelId: previousLevelId, - zoneId: null, - selectedIds: [], - }) - } - }, [isFirstPersonMode]) - // Load scene on mount (or when onLoad identity changes, e.g. project switch) useEffect(() => { let cancelled = false @@ -1064,6 +1034,42 @@ export default function Editor({ const showLoader = isLoading || isSceneLoading + const firstPersonPreviousLevelRef = useRef(useViewer.getState().selection.levelId) + const wasFirstPersonModeRef = useRef(isFirstPersonMode) + + useEffect(() => { + const wasFirstPersonMode = wasFirstPersonModeRef.current + wasFirstPersonModeRef.current = isFirstPersonMode + + if (isFirstPersonMode && !wasFirstPersonMode) { + const viewer = useViewer.getState() + firstPersonPreviousLevelRef.current = viewer.selection.levelId + viewer.setCameraMode('perspective') + viewer.setWallMode('up') + viewer.setWalkthroughMode(true) + viewer.setSelection({ selectedIds: [], zoneId: null }) + return + } + + if (!(wasFirstPersonMode && !isFirstPersonMode)) return + + const viewer = useViewer.getState() + const previousLevelId = firstPersonPreviousLevelRef.current + firstPersonPreviousLevelRef.current = null + viewer.setWalkthroughMode(false) + + if (!previousLevelId) return + + const previousLevelNode = useScene.getState().nodes[previousLevelId] + if (previousLevelNode?.type === 'level') { + viewer.setSelection({ + levelId: previousLevelId, + zoneId: null, + selectedIds: [], + }) + } + }, [isFirstPersonMode]) + const previewViewerContent = ( @@ -1135,21 +1141,24 @@ export default function Editor({ navbarSlot={navbarSlot} overlays={ <> - - {!isVersionPreviewMode && ( + {!isCaptureMode && } + {!(isVersionPreviewMode || isCaptureMode) && ( )} - {!isVersionPreviewMode && ( + {!(isVersionPreviewMode || isCaptureMode) && ( )} - - - + {!isCaptureMode && ( + + + + )} {viewerBanner} + {projectId ? : null} > } renderTabContent={renderTabContent} @@ -1159,14 +1168,14 @@ export default function Editor({ viewerToolbarLeft={viewerToolbarLeft} viewerToolbarRight={viewerToolbarRight} /> + + {/* First-person overlay — rendered on top of normal layout */} {isFirstPersonMode && ( useEditor.getState().setFirstPersonMode(false)} /> )} - - > )} @@ -1222,6 +1231,12 @@ export default function Editor({ + {/* First-person overlay — rendered on top of normal layout */} + {isFirstPersonMode && ( + + useEditor.getState().setFirstPersonMode(false)} /> + + )} > )} diff --git a/packages/editor/src/components/editor/selection-manager.tsx b/packages/editor/src/components/editor/selection-manager.tsx old mode 100755 new mode 100644 index 71f278cf1..780f4d44c --- a/packages/editor/src/components/editor/selection-manager.tsx +++ b/packages/editor/src/components/editor/selection-manager.tsx @@ -341,7 +341,7 @@ function applySingleSurfacePaintPreview( if (node.type === 'ceiling') { const root = getRegisteredMesh(node.id) const overlay = root?.getObjectByName('ceiling-grid') as Mesh | undefined - if (!root || !overlay) return null + if (!(root && overlay)) return null const previewColor = getMaterialPresetByRef(material.materialPreset)?.mapProperties.color ?? @@ -999,7 +999,6 @@ export const SelectionManager = () => { 'roof-segment', 'stair', 'stair-segment', - 'spawn', 'window', 'door', 'zone', @@ -1392,6 +1391,7 @@ export const SelectionManager = () => { 'roof-segment', 'stair', 'stair-segment', + 'spawn', 'window', 'door', 'zone', diff --git a/packages/editor/src/components/editor/snapshot-capture-overlay.tsx b/packages/editor/src/components/editor/snapshot-capture-overlay.tsx new file mode 100644 index 000000000..d86325959 --- /dev/null +++ b/packages/editor/src/components/editor/snapshot-capture-overlay.tsx @@ -0,0 +1,465 @@ +'use client' + +import { emitter } from '@pascal-app/core' +import { Camera, Check, Crop, Loader2, Maximize2, Monitor, X } from 'lucide-react' +import { useCallback, useEffect, useRef, useState } from 'react' +import { useIsMobile } from '../../hooks/use-mobile' +import { triggerSFX } from '../../lib/sfx-bus' +import useEditor from '../../store/use-editor' + +type CaptureMode = 'standard' | 'viewport' | 'area' +type CaptureState = 'idle' | 'capturing' | 'saved' + +interface DragPoint { + x: number + y: number +} + +interface Drag { + start: DragPoint + end: DragPoint +} + +function getResolution( + mode: CaptureMode, + overlayEl: HTMLDivElement | null, + drag: Drag | null, +): { w: number; h: number } | null { + if (mode === 'standard') return { w: 1920, h: 1080 } + + if (!overlayEl) return null + const rect = overlayEl.getBoundingClientRect() + const dpr = Math.min(window.devicePixelRatio, 1.5) + + if (mode === 'viewport') { + return { w: Math.round(rect.width * dpr), h: Math.round(rect.height * dpr) } + } + + if (mode === 'area' && drag) { + const w = Math.abs(drag.end.x - drag.start.x) + const h = Math.abs(drag.end.y - drag.start.y) + if (w < 4 || h < 4) return null + return { w: Math.round(w * dpr), h: Math.round(h * dpr) } + } + + return null +} + +export function SnapshotCaptureOverlay({ projectId }: { projectId: string }) { + const isCaptureMode = useEditor((s) => s.isCaptureMode) + const setCaptureMode = useEditor((s) => s.setCaptureMode) + const isMobile = useIsMobile() + + const [mode, setMode] = useState('standard') + const [drag, setDrag] = useState(null) + const [isDragging, setIsDragging] = useState(false) + const [captureState, setCaptureState] = useState('idle') + const overlayRef = useRef(null) + + // Dismiss on Esc + useEffect(() => { + if (!isCaptureMode) return + const onKey = (e: KeyboardEvent) => { + if (e.key === 'Escape') setCaptureMode(false) + } + window.addEventListener('keydown', onKey) + return () => window.removeEventListener('keydown', onKey) + }, [isCaptureMode, setCaptureMode]) + + // Reset local state when entering capture mode + useEffect(() => { + if (isCaptureMode) { + setMode('standard') + setDrag(null) + setIsDragging(false) + setCaptureState('idle') + } + }, [isCaptureMode]) + + // Listen for snapshot saved to show feedback then exit + useEffect(() => { + const handler = () => { + setCaptureState('saved') + setTimeout(() => { + setCaptureMode(false) + setCaptureState('idle') + }, 1500) + } + emitter.on('snapshot:saved', handler) + return () => emitter.off('snapshot:saved', handler) + }, [setCaptureMode]) + + const dismiss = useCallback(() => setCaptureMode(false), [setCaptureMode]) + + // Tracks whether the active drag is a "move entire rect" gesture + const moveStartRef = useRef<{ pt: DragPoint; drag: Drag } | null>(null) + + // Area drag handlers — relative to the overlay container + const onPointerDown = useCallback( + (e: React.PointerEvent) => { + if (mode !== 'area' || captureState !== 'idle') return + e.preventDefault() + const rect = overlayRef.current!.getBoundingClientRect() + const pt = { x: e.clientX - rect.left, y: e.clientY - rect.top } + + // If clicking inside an existing selection → move mode + if (drag) { + const x0 = Math.min(drag.start.x, drag.end.x) + const y0 = Math.min(drag.start.y, drag.end.y) + const x1 = Math.max(drag.start.x, drag.end.x) + const y1 = Math.max(drag.start.y, drag.end.y) + if (pt.x >= x0 && pt.x <= x1 && pt.y >= y0 && pt.y <= y1) { + moveStartRef.current = { pt, drag } + setIsDragging(true) + ;(e.target as HTMLElement).setPointerCapture(e.pointerId) + return + } + } + + // Outside / no selection → start new drag + moveStartRef.current = null + setDrag({ start: pt, end: pt }) + setIsDragging(true) + ;(e.target as HTMLElement).setPointerCapture(e.pointerId) + }, + [mode, captureState, drag], + ) + + const onPointerMove = useCallback( + (e: React.PointerEvent) => { + if (!isDragging) return + const rect = overlayRef.current!.getBoundingClientRect() + const pt = { + x: Math.max(0, Math.min(e.clientX - rect.left, rect.width)), + y: Math.max(0, Math.min(e.clientY - rect.top, rect.height)), + } + if (moveStartRef.current) { + // Move mode: translate the whole rect by the delta + const { pt: origin, drag: snapshot } = moveStartRef.current + const dx = pt.x - origin.x + const dy = pt.y - origin.y + setDrag({ + start: { x: snapshot.start.x + dx, y: snapshot.start.y + dy }, + end: { x: snapshot.end.x + dx, y: snapshot.end.y + dy }, + }) + } else { + setDrag((d) => (d ? { start: d.start, end: pt } : null)) + } + }, + [isDragging], + ) + + const onPointerUp = useCallback(() => { + const wasMoving = moveStartRef.current !== null + setIsDragging(false) + moveStartRef.current = null + // Clear the rect if the user just clicked without drawing (not a move gesture) + if (!wasMoving) { + setDrag((d) => { + if (!d) return null + const w = Math.abs(d.end.x - d.start.x) + const h = Math.abs(d.end.y - d.start.y) + return w < 4 && h < 4 ? null : d + }) + } + }, []) + + // Corner-handle resize: re-anchor to the opposite corner then reuse the same drag machinery + const onCornerPointerDown = useCallback( + (e: React.PointerEvent, cornerIndex: number) => { + if (captureState !== 'idle' || !drag) return + e.stopPropagation() + e.preventDefault() + moveStartRef.current = null + const x0 = Math.min(drag.start.x, drag.end.x) + const y0 = Math.min(drag.start.y, drag.end.y) + const x1 = Math.max(drag.start.x, drag.end.x) + const y1 = Math.max(drag.start.y, drag.end.y) + // anchor = opposite corner; dragged = current corner + const corners: [DragPoint, DragPoint][] = [ + [ + { x: x1, y: y1 }, + { x: x0, y: y0 }, + ], // TL → anchor BR + [ + { x: x0, y: y1 }, + { x: x1, y: y0 }, + ], // TR → anchor BL + [ + { x: x1, y: y0 }, + { x: x0, y: y1 }, + ], // BL → anchor TR + [ + { x: x0, y: y0 }, + { x: x1, y: y1 }, + ], // BR → anchor TL + ] + const [anchor, current] = corners[cornerIndex]! + setDrag({ start: anchor, end: current }) + setIsDragging(true) + }, + [captureState, drag], + ) + + const handleCapture = useCallback(() => { + if (captureState !== 'idle') return + + let cropRegion: { x: number; y: number; width: number; height: number } | undefined + if (mode === 'area' && drag && overlayRef.current) { + const rect = overlayRef.current.getBoundingClientRect() + const x0 = Math.min(drag.start.x, drag.end.x) + const y0 = Math.min(drag.start.y, drag.end.y) + const w = Math.abs(drag.end.x - drag.start.x) + const h = Math.abs(drag.end.y - drag.start.y) + cropRegion = { + x: x0 / rect.width, + y: y0 / rect.height, + width: w / rect.width, + height: h / rect.height, + } + } + + setCaptureState('capturing') + triggerSFX('sfx:snapshot-capture') + emitter.emit('camera-controls:generate-thumbnail', { + projectId, + captureMode: mode, + cropRegion, + }) + }, [captureState, mode, drag, projectId]) + + if (!isCaptureMode) return null + + const resolution = getResolution(mode, overlayRef.current, drag) + + // Area selection rect (CSS px, relative to overlay) + const selectionStyle = + mode === 'area' && drag + ? { + left: Math.min(drag.start.x, drag.end.x), + top: Math.min(drag.start.y, drag.end.y), + width: Math.abs(drag.end.x - drag.start.x), + height: Math.abs(drag.end.y - drag.start.y), + } + : null + + const hasSelection = + selectionStyle != null && selectionStyle.width > 3 && selectionStyle.height > 3 + + const captureDisabled = captureState !== 'idle' || (mode === 'area' && !hasSelection) + + return ( + + {/* Area mode: dim layer + crosshair cursor + drag-to-select */} + {mode === 'area' && ( + { + onPointerMove(e) + // Update cursor: 'move' when hovering inside an existing selection + if (!isDragging && drag && overlayRef.current) { + const rect = overlayRef.current.getBoundingClientRect() + const px = e.clientX - rect.left + const py = e.clientY - rect.top + const x0 = Math.min(drag.start.x, drag.end.x) + const y0 = Math.min(drag.start.y, drag.end.y) + const x1 = Math.max(drag.start.x, drag.end.x) + const y1 = Math.max(drag.start.y, drag.end.y) + e.currentTarget.style.cursor = + px >= x0 && px <= x1 && py >= y0 && py <= y1 ? 'move' : 'crosshair' + } + }} + onPointerUp={onPointerUp} + style={{ cursor: 'crosshair' }} + > + {/* "No selection" hint */} + {!selectionStyle && ( + + + Drag the area you want to capture + + + )} + + {/* Selection rect */} + {selectionStyle && ( + + {/* Corner handles */} + {( + [ + { pos: { top: -5, left: -5 }, cursor: 'nwse-resize' }, + { pos: { top: -5, right: -5 }, cursor: 'nesw-resize' }, + { pos: { bottom: -5, left: -5 }, cursor: 'nesw-resize' }, + { pos: { bottom: -5, right: -5 }, cursor: 'nwse-resize' }, + ] as const + ).map(({ pos, cursor }, i) => ( + onCornerPointerDown(e, i)} + style={{ + position: 'absolute', + width: 10, + height: 10, + borderRadius: '50%', + background: 'white', + boxShadow: '0 1px 4px rgba(0,0,0,0.4)', + pointerEvents: 'auto', + cursor, + ...pos, + }} + /> + ))} + + )} + + )} + + {/* Top-right dismiss button (icon-only on mobile) */} + + + + {!isMobile && 'Esc to cancel'} + + + + {/* Bottom-center mode toolbar */} + + {(() => { + const modeButtons = ( + <> + } + label="Standard" + onClick={() => { + setMode('standard') + setDrag(null) + }} + /> + } + label="Viewport" + onClick={() => { + setMode('viewport') + setDrag(null) + }} + /> + } + label="Area" + onClick={() => setMode('area')} + /> + > + ) + + const resolutionDisplay = ( + + {resolution ? `${resolution.w} × ${resolution.h}` : '—'} + + ) + + const captureButton = ( + + {captureState === 'capturing' ? ( + <> + + Capturing + > + ) : captureState === 'saved' ? ( + <> + + Saved + > + ) : ( + <> + + Capture + > + )} + + ) + + if (isMobile) { + return ( + + {modeButtons} + + {resolutionDisplay} + {captureButton} + + + ) + } + + return ( + + {modeButtons} + + {resolutionDisplay} + + {captureButton} + + ) + })()} + + + ) +} + +function ModeButton({ + active, + icon, + label, + badge, + onClick, +}: { + active: boolean + icon: React.ReactNode + label: string + badge?: string + onClick: () => void +}) { + return ( + + {icon} + {label} + {badge && ( + + {badge} + + )} + + ) +} diff --git a/packages/editor/src/components/editor/thumbnail-generator.tsx b/packages/editor/src/components/editor/thumbnail-generator.tsx index 826eb09ae..b11359431 100644 --- a/packages/editor/src/components/editor/thumbnail-generator.tsx +++ b/packages/editor/src/components/editor/thumbnail-generator.tsx @@ -1,6 +1,6 @@ 'use client' -import { emitter, sceneRegistry, useScene } from '@pascal-app/core' +import { emitter, sceneRegistry } from '@pascal-app/core' import { SSGI_PARAMS, snapLevelsToTruePositions, useViewer } from '@pascal-app/viewer' import type { CameraControls } from '@react-three/drei' import { useThree } from '@react-three/fiber' @@ -28,7 +28,6 @@ import { EDITOR_LAYER } from '../../lib/constants' const THUMBNAIL_WIDTH = 1920 const THUMBNAIL_HEIGHT = 1080 -const AUTO_SAVE_DELAY = 10_000 export interface SnapshotCameraData { position: [number, number, number] @@ -49,8 +48,6 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro const mainCamera = useThree((state) => state.camera) const controls = useThree((state) => state.controls) as CameraControls | null const isGenerating = useRef(false) - const debounceTimerRef = useRef | null>(null) - const pendingAutoRef = useRef(false) const onThumbnailCaptureRef = useRef(onThumbnailCapture) const thumbnailCameraRef = useRef(null) @@ -61,7 +58,7 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro onThumbnailCaptureRef.current = onThumbnailCapture }, [onThumbnailCapture]) - // Build the thumbnail camera, SSGI pipeline, and render target once, reused on every capture. + // Build the thumbnail camera, SSGI pipeline, and render target once — reused on every capture. useEffect(() => { const cam = new THREE.PerspectiveCamera(60, THUMBNAIL_WIDTH / THUMBNAIL_HEIGHT, 0.1, 1000) cam.layers.disable(EDITOR_LAYER) @@ -75,7 +72,7 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro if (!mounted) return // pass() handles MRT internally for all material types, including custom - // shaders, unlike renderer.setMRT() which crashes on non-NodeMaterials. + // shaders — unlike renderer.setMRT() which crashes on non-NodeMaterials. // pass() also respects camera.layers, so EDITOR_LAYER objects are filtered. const scenePass = pass(scene, cam) scenePass.setMRT( @@ -117,15 +114,15 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro const ao = (denoisePass as any).r const finalOutput = vec4(scenePassColor.rgb.mul(ao), scenePassColor.a) - // FXAA requires a texture node as input, convertToTexture renders finalOutput - // into an intermediate RT so FXAA can sample it with neighbor UV offsets. + // FXAA requires a texture node as input; convertToTexture renders finalOutput + // into an intermediate RT so FXAA can sample it with neighbour UV offsets. const aaOutput = fxaa(convertToTexture(finalOutput)) const pipeline = new RenderPipeline(gl as unknown as WebGPURenderer) pipeline.outputNode = aaOutput pipelineRef.current = pipeline - // Dedicated render target, pipeline outputs here instead of the canvas, + // Dedicated render target — pipeline outputs here instead of the canvas, // so R3F's main render loop can never overwrite our capture. const { width, height } = gl.domElement renderTargetRef.current = new RenderTarget(width, height, { depthBuffer: true }) @@ -176,7 +173,7 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro thumbnailCamera.aspect = width / height thumbnailCamera.updateProjectionMatrix() - // Capture camera data for snapshot storage. + // Capture camera data for snapshot storage const pos = mainCamera.position let tgt: [number, number, number] | null = null if (controls && 'getTarget' in controls) { @@ -192,7 +189,7 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro ...(isOrtho && { zoom: (mainCamera as THREE.OrthographicCamera).zoom }), } - // For auto-save, snap levels to stacked positions and reset levelMode. + // For auto-save: snap levels to stacked positions and reset levelMode let restoreLevelMode: (() => void) | null = null let restoreLevels: () => void = () => {} if (snapLevels) { @@ -205,7 +202,7 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro } // Hide scan and guide nodes directly so they are excluded from the - // thumbnail regardless of whether ScanSystem or GuideSystem listeners are + // thumbnail regardless of whether ScanSystem/GuideSystem listeners are // registered. Returns a function that restores the original visibility. const restoreNodeVisibility = (() => { const saved = new Map() @@ -231,7 +228,7 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro if (pipelineRef.current && renderTargetRef.current) { const rt = renderTargetRef.current - // Resize RT if the canvas dimensions changed. + // Resize RT if the canvas dimensions changed if (rt.width !== width || rt.height !== height) { rt.setSize(width, height) } @@ -248,7 +245,7 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro emitter.emit('thumbnail:after-capture', undefined) // Restore level positions, levelMode, and node visibility immediately after the - // render, before the async GPU readback. + // render — before the async GPU readback. restoreLevels() restoreLevelMode?.() restoreNodeVisibility() @@ -339,6 +336,7 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro offscreen.getContext('2d')!.drawImage(srcCanvas, sx, sy, outW, outH, 0, 0, outW, outH) blob = await offscreen.convertToBlob({ type: 'image/png' }) } else { + // Standard: center-crop to 1920×1080 aspect ratio const srcAspect = width / height const dstAspect = THUMBNAIL_WIDTH / THUMBNAIL_HEIGHT let sx = 0, @@ -364,7 +362,7 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro if (captureMode !== undefined) cameraData.captureMode = captureMode cameraData.resolution = { w: outW, h: outH } } else { - // Fallback: plain render directly to the canvas. + // Fallback: plain render directly to the canvas emitter.emit('thumbnail:before-capture', undefined) gl.render(scene, thumbnailCamera) emitter.emit('thumbnail:after-capture', undefined) @@ -450,10 +448,11 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro ) // Thumbnail request via emitter. Two call shapes: - // - user-driven capture: `{ projectId, captureMode, cropRegion }`, captures + // - user-driven capture: `{ projectId, captureMode, cropRegion }` — captures // the current pose with the supplied crop. - // - auto-save capture: `{ projectId, snapLevels: true }`, snaps levels to - // their true positions first for a consistent auto-thumbnail angle. + // - host-driven auto-save: `{ projectId, snapLevels: true }` — snaps levels + // to their true positions first for a consistent auto-thumbnail angle. + // The caller owns policy (when to fire, whether the tab is visible). useEffect(() => { if (!onThumbnailCapture) return @@ -469,49 +468,7 @@ export const ThumbnailGenerator = ({ onThumbnailCapture }: ThumbnailGeneratorPro return () => emitter.off('camera-controls:generate-thumbnail', handleGenerateThumbnail) }, [generate, onThumbnailCapture]) - // OSS adaptation: keep local debounced auto-capture behavior because the - // community host-side autosave hook is not part of this repo. - useEffect(() => { - if (!onThumbnailCapture) return - - const triggerNow = () => { - void generate(true) - } - - const scheduleOrDefer = () => { - if (document.visibilityState === 'visible') { - triggerNow() - } else { - pendingAutoRef.current = true - } - } - - const onSceneChange = () => { - if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current) - debounceTimerRef.current = setTimeout(scheduleOrDefer, AUTO_SAVE_DELAY) - } - - const onVisibilityChange = () => { - if (document.visibilityState === 'visible' && pendingAutoRef.current) { - pendingAutoRef.current = false - triggerNow() - } - } - - const unsubscribe = useScene.subscribe((state, prevState) => { - if (state.nodes !== prevState.nodes) onSceneChange() - }) - - document.addEventListener('visibilitychange', onVisibilityChange) - - return () => { - if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current) - unsubscribe() - document.removeEventListener('visibilitychange', onVisibilityChange) - } - }, [generate, onThumbnailCapture]) - - // Go-to-camera: animate camera to a saved snapshot position or target. + // Go-to-camera: animate camera to a saved snapshot position/target useEffect(() => { const handler = ({ position, diff --git a/packages/editor/src/components/editor/use-floorplan-background-placement.ts b/packages/editor/src/components/editor/use-floorplan-background-placement.ts index a96db8220..d6228200a 100644 --- a/packages/editor/src/components/editor/use-floorplan-background-placement.ts +++ b/packages/editor/src/components/editor/use-floorplan-background-placement.ts @@ -144,11 +144,11 @@ export function useFloorplanBackgroundPlacement({ emitFloorplanGridEvent('click', snappedPoint, event) setCursorPoint(snappedPoint) - if (!roofDraftStart) { + if (roofDraftStart) { + clearRoofPlacementDraft() + } else { setRoofDraftStart(snappedPoint) setRoofDraftEnd(snappedPoint) - } else { - clearRoofPlacementDraft() } return true } diff --git a/packages/editor/src/components/editor/wall-measurement-label.tsx b/packages/editor/src/components/editor/wall-measurement-label.tsx old mode 100755 new mode 100644 diff --git a/packages/editor/src/components/feedback-dialog.tsx b/packages/editor/src/components/feedback-dialog.tsx deleted file mode 100644 index c38471c80..000000000 --- a/packages/editor/src/components/feedback-dialog.tsx +++ /dev/null @@ -1,265 +0,0 @@ -'use client' - -import { useScene } from '@pascal-app/core' -import { ImageIcon, MessageSquare, X } from 'lucide-react' -import { useCallback, useRef, useState } from 'react' -import { Button } from './ui/primitives/button' -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, -} from './ui/primitives/dialog' - -const MAX_IMAGES = 5 -const MAX_IMAGE_SIZE = 5 * 1024 * 1024 - -type ImagePreview = { file: File; url: string } - -export function FeedbackDialog({ - projectId: projectIdProp, - onSubmit, -}: { - projectId?: string - onSubmit?: (data: { - message: string - projectId?: string - sceneGraph: unknown - images: File[] - }) => Promise<{ success: boolean; error?: string }> -}) { - const projectId = projectIdProp - - const [open, setOpen] = useState(false) - const [message, setMessage] = useState('') - const [images, setImages] = useState([]) - const [isDragging, setIsDragging] = useState(false) - const [isSubmitting, setIsSubmitting] = useState(false) - const [error, setError] = useState(null) - const [sent, setSent] = useState(false) - const fileInputRef = useRef(null) - const dragCounter = useRef(0) - - const handleOpen = () => { - setOpen(true) - setSent(false) - setError(null) - setMessage('') - setImages([]) - setIsDragging(false) - dragCounter.current = 0 - } - - const handleClose = () => { - if (isSubmitting) return - setOpen(false) - images.forEach((img) => { - URL.revokeObjectURL(img.url) - }) - } - - const addFiles = useCallback((files: FileList | File[]) => { - const incoming = Array.from(files).filter( - (f) => f.type.startsWith('image/') && f.size <= MAX_IMAGE_SIZE, - ) - setImages((prev) => { - const remaining = MAX_IMAGES - prev.length - const added = incoming.slice(0, remaining).map((file) => ({ - file, - url: URL.createObjectURL(file), - })) - return [...prev, ...added] - }) - }, []) - - const removeImage = (index: number) => { - setImages((prev) => { - const img = prev[index] - if (img) URL.revokeObjectURL(img.url) - return prev.filter((_, i) => i !== index) - }) - } - - // ── Drag handlers (on the entire dialog content) ── - const onDragEnter = (e: React.DragEvent) => { - e.preventDefault() - e.stopPropagation() - dragCounter.current++ - if (e.dataTransfer.types.includes('Files')) { - setIsDragging(true) - } - } - - const onDragLeave = (e: React.DragEvent) => { - e.preventDefault() - e.stopPropagation() - dragCounter.current-- - if (dragCounter.current === 0) { - setIsDragging(false) - } - } - - const onDragOver = (e: React.DragEvent) => { - e.preventDefault() - e.stopPropagation() - } - - const onDrop = (e: React.DragEvent) => { - e.preventDefault() - e.stopPropagation() - dragCounter.current = 0 - setIsDragging(false) - if (e.dataTransfer.files.length > 0) { - addFiles(e.dataTransfer.files) - } - } - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault() - setError(null) - setIsSubmitting(true) - - try { - if (!onSubmit) return - const { nodes, rootNodeIds } = useScene.getState() - const sceneGraph = { nodes, rootNodeIds } - const result = await onSubmit({ - message, - projectId, - sceneGraph, - images: images.map((img) => img.file), - }) - if (result.success) { - setSent(true) - setTimeout(() => setOpen(false), 1500) - } else { - setError(result.error ?? 'Something went wrong') - } - } finally { - setIsSubmitting(false) - } - } - - return ( - <> - - - Feedback - - - - - {/* Drag overlay — only visible when dragging files over the dialog */} - {isDragging && ( - - - - Drop images here - - - )} - - - Send Feedback - We'd love to hear your thoughts - - - {sent ? ( - - Thanks for your feedback! - - ) : ( - - - - Your feedback - - setMessage(e.target.value)} - placeholder="Share your thoughts, suggestions, feature requests, or report issues..." - rows={5} - value={message} - /> - - - {/* Image thumbnails */} - {images.length > 0 && ( - - {images.map((img, i) => ( - - - removeImage(i)} - type="button" - > - - - - ))} - - )} - - {error && {error}} - - - {/* Subtle attach button */} - = MAX_IMAGES} - onClick={() => fileInputRef.current?.click()} - type="button" - > - - {images.length > 0 ? `${images.length}/${MAX_IMAGES}` : 'Attach'} - - { - if (e.target.files) addFiles(e.target.files) - e.target.value = '' - }} - ref={fileInputRef} - type="file" - /> - - - - Cancel - - - {isSubmitting ? 'Sending...' : 'Send Feedback'} - - - - - )} - - - > - ) -} diff --git a/packages/editor/src/components/pascal-radio.tsx b/packages/editor/src/components/pascal-radio.tsx deleted file mode 100644 index 14359774a..000000000 --- a/packages/editor/src/components/pascal-radio.tsx +++ /dev/null @@ -1,280 +0,0 @@ -'use client' - -import { Howl } from 'howler' -import { Disc3, Settings2, SkipBack, SkipForward, Volume2, VolumeX } from 'lucide-react' -import { AnimatePresence, motion } from 'motion/react' -import { useCallback, useEffect, useRef, useState } from 'react' -import { Slider } from '../components/ui/slider' -import { cn } from '../lib/utils' -import useAudio from '../store/use-audio' - -const PLAYLIST = [ - { - title: 'Ballroom in Miniature', - file: '/audios/radios/classic/Ballroom in Miniature.mp3', - }, - { - title: 'Blueprints in Springtime', - file: '/audios/radios/classic/Blueprints in Springtime.mp3', - }, - { - title: 'Clockwork Tea Party', - file: '/audios/radios/classic/Clockwork Tea Party.mp3', - }, - { - title: 'Clockwork Tea Party (Alternate)', - file: '/audios/radios/classic/Clockwork Tea Party (Alternate).mp3', - }, - { - title: 'Clockwork Teacups', - file: '/audios/radios/classic/Clockwork Teacups.mp3', - }, - { - title: 'Evening in the Parlor', - file: '/audios/radios/classic/Evening in the Parlor.mp3', - }, - { - title: 'Glass Atrium', - file: '/audios/radios/classic/Glass Atrium.mp3', - }, - { - title: 'Moonlight On The Drafting Table', - file: '/audios/radios/classic/Moonlight On The Drafting Table.mp3', - }, - { - title: 'Sunlit Garden Reverie', - file: '/audios/radios/classic/Sunlit Garden Reverie.mp3', - }, - { - title: 'Sunlit Waltz in Pastel Hues', - file: '/audios/radios/classic/Sunlit Waltz in Pastel Hues.mp3', - }, -] - -// Shuffle array helper -function shuffleArray(array: T[]): T[] { - const shuffled = [...array] - for (let i = shuffled.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)) - ;[shuffled[i], shuffled[j]] = [shuffled[j]!, shuffled[i]!] - } - return shuffled -} - -export function PascalRadio() { - const [shuffledPlaylist] = useState(() => shuffleArray(PLAYLIST)) - const [currentTrackIndex, setCurrentTrackIndex] = useState(0) - const { masterVolume, radioVolume, muted, isRadioPlaying, setRadioPlaying } = useAudio() - const soundRef = useRef(null) - const [isOpen, setIsOpen] = useState(false) - const containerRef = useRef(null) - - const currentTrack = shuffledPlaylist[currentTrackIndex]! - - // Calculate effective volume (masterVolume * radioVolume, both are 0-100) - const effectiveVolume = (masterVolume / 100) * (radioVolume / 100) - - // Keep a ref so the track-init effect can read current volume/muted/isRadioPlaying - // without those values being part of its dependency array (which would restart the song). - const effectiveVolumeRef = useRef(effectiveVolume) - const mutedRef = useRef(muted) - const isPlayingRef = useRef(isRadioPlaying) - effectiveVolumeRef.current = effectiveVolume - mutedRef.current = muted - isPlayingRef.current = isRadioPlaying - - const handleNext = useCallback(() => { - setCurrentTrackIndex((prev) => (prev + 1) % shuffledPlaylist.length) - }, [shuffledPlaylist.length]) - - const handlePrevious = useCallback(() => { - setCurrentTrackIndex((prev) => (prev - 1 + shuffledPlaylist.length) % shuffledPlaylist.length) - }, [shuffledPlaylist.length]) - - // Initialize Howler only when the track changes — not on volume/mute/play-state changes. - // Volume and mute are handled by the separate effect below. - useEffect(() => { - if (soundRef.current) { - soundRef.current.unload() - } - - const wasPlaying = isPlayingRef.current - - soundRef.current = new Howl({ - src: [currentTrack.file], - volume: mutedRef.current ? 0 : effectiveVolumeRef.current, - onend: handleNext, - }) - - if (wasPlaying && !mutedRef.current) { - soundRef.current?.play() - } - - return () => { - soundRef.current?.unload() - } - }, [handleNext, currentTrack.file]) - - // Update volume when settings change - useEffect(() => { - if (soundRef.current) { - soundRef.current.volume(muted ? 0 : effectiveVolume) - - // Pause if muted, resume if unmuted and was playing - if (muted && isRadioPlaying) { - soundRef.current.pause() - } else if (!muted && isRadioPlaying && !soundRef.current.playing()) { - soundRef.current.play() - } else if (!isRadioPlaying && soundRef.current.playing()) { - soundRef.current.pause() - } - } - }, [effectiveVolume, muted, isRadioPlaying]) - - const handlePlayPause = () => { - if (!soundRef.current || muted) return - - if (isRadioPlaying) { - soundRef.current.pause() - } else { - soundRef.current.play() - } - setRadioPlaying(!isRadioPlaying) - } - - const handleVolumeChange = (value: number[]) => { - useAudio.setState({ radioVolume: value[0] }) - } - - // Handle click outside to close - useEffect(() => { - function handleClickOutside(event: MouseEvent) { - if (containerRef.current && !containerRef.current.contains(event.target as Node)) { - setIsOpen(false) - } - } - if (isOpen) { - document.addEventListener('mousedown', handleClickOutside) - } - return () => { - document.removeEventListener('mousedown', handleClickOutside) - } - }, [isOpen]) - - return ( - { - if (!isOpen) setIsOpen(true) - }} - ref={containerRef} - transition={{ type: 'spring', bounce: 0.2, duration: 0.6 }} - > - - - - Radio Pascal - - - { - e.stopPropagation() - handlePlayPause() - }} - onKeyDown={(e) => { - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault() - e.stopPropagation() - handlePlayPause() - } - }} - role="button" - tabIndex={0} - > - {isRadioPlaying ? ( - - ) : ( - - )} - - { - e.stopPropagation() - setIsOpen(!isOpen) - }} - > - - - - - - - {isOpen && ( - - - - {/* Current song info with prev/next */} - - Now Playing - - - - - - {currentTrack.title} - - - - - - - - {/* Volume control */} - - - - - {radioVolume}% - - - - - )} - - - ) -} diff --git a/packages/editor/src/components/preview-button.tsx b/packages/editor/src/components/preview-button.tsx deleted file mode 100644 index 0be919aac..000000000 --- a/packages/editor/src/components/preview-button.tsx +++ /dev/null @@ -1,16 +0,0 @@ -'use client' - -import { Eye } from 'lucide-react' -import useEditor from '../store/use-editor' - -export function PreviewButton() { - return ( - useEditor.getState().setPreviewMode(true)} - > - - Preview - - ) -} diff --git a/packages/editor/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx b/packages/editor/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx index 40c01c955..ee0df9859 100644 --- a/packages/editor/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +++ b/packages/editor/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx @@ -75,7 +75,9 @@ const CeilingSelectionAffordance = ({ ceiling: CeilingNode levelId: string }) => { - const [levelObject, setLevelObject] = useState(() => sceneRegistry.nodes.get(levelId) ?? null) + const [levelObject, setLevelObject] = useState( + () => sceneRegistry.nodes.get(levelId) ?? null, + ) const corners = useMemo(() => buildCornerBrackets(ceiling.polygon), [ceiling.polygon]) @@ -110,11 +112,7 @@ const CeilingSelectionAffordance = ({ return createPortal( {corners.map((corner, index) => ( - + ))} , levelObject, @@ -210,11 +208,7 @@ const BracketLeg = ({ ] return ( - + @@ -234,7 +228,11 @@ function buildCornerBrackets(polygon: Array<[number, number]>): CornerBracketDat const incomingLength = Math.hypot(incomingVector[0], incomingVector[1]) const outgoingLength = Math.hypot(outgoingVector[0], outgoingVector[1]) - const cornerStrength = 1 - Math.abs(incomingDirection[0] * outgoingDirection[0] + incomingDirection[1] * outgoingDirection[1]) + const cornerStrength = + 1 - + Math.abs( + incomingDirection[0] * outgoingDirection[0] + incomingDirection[1] * outgoingDirection[1], + ) return { corner, diff --git a/packages/editor/src/components/systems/roof/roof-edit-system.tsx b/packages/editor/src/components/systems/roof/roof-edit-system.tsx index 933416a5b..e0055dd09 100644 --- a/packages/editor/src/components/systems/roof/roof-edit-system.tsx +++ b/packages/editor/src/components/systems/roof/roof-edit-system.tsx @@ -6,7 +6,7 @@ import { useEffect, useRef } from 'react' * Imperatively toggles the Three.js visibility of roof objects based on the * editor selection — without causing React re-renders in RoofRenderer. * - * When a roof-segment is selected: + * When a roof (or one of its segments) is selected: * - merged-roof mesh is hidden * - segments-wrapper group is shown (individual segments visible for editing) * - all children are marked dirty so RoofSystem rebuilds their geometry diff --git a/packages/editor/src/components/systems/stair/stair-edit-system.tsx b/packages/editor/src/components/systems/stair/stair-edit-system.tsx index 265d57b50..ef1f91244 100644 --- a/packages/editor/src/components/systems/stair/stair-edit-system.tsx +++ b/packages/editor/src/components/systems/stair/stair-edit-system.tsx @@ -68,7 +68,7 @@ export const StairEditSystem = () => { const segmentsWrapper = group.getObjectByName('segments-wrapper') const isActive = activeStairIds.has(stairId) - if (mergedMesh) mergedMesh.visible = !isActive && !isCurved + if (mergedMesh) mergedMesh.visible = !(isActive || isCurved) if (segmentsWrapper) segmentsWrapper.visible = isActive && !isCurved if (stairNode?.children?.length) { diff --git a/packages/editor/src/components/systems/zone/zone-label-editor-system.tsx b/packages/editor/src/components/systems/zone/zone-label-editor-system.tsx old mode 100755 new mode 100644 diff --git a/packages/editor/src/components/systems/zone/zone-system.tsx b/packages/editor/src/components/systems/zone/zone-system.tsx old mode 100755 new mode 100644 diff --git a/packages/editor/src/components/tools/ceiling/move-ceiling-tool.tsx b/packages/editor/src/components/tools/ceiling/move-ceiling-tool.tsx index 98745e08a..ce14f8a75 100644 --- a/packages/editor/src/components/tools/ceiling/move-ceiling-tool.tsx +++ b/packages/editor/src/components/tools/ceiling/move-ceiling-tool.tsx @@ -1,13 +1,19 @@ 'use client' -import { type AnyNodeId, emitter, type GridEvent, useScene, type CeilingNode } from '@pascal-app/core' +import { + type AnyNodeId, + type CeilingNode, + emitter, + type GridEvent, + useScene, +} from '@pascal-app/core' import { useViewer } from '@pascal-app/viewer' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { BufferGeometry, DoubleSide, Path, Shape, ShapeGeometry, Vector3 } from 'three' import { markToolCancelConsumed } from '../../../hooks/use-keyboard' import { sfxEmitter } from '../../../lib/sfx-bus' import useEditor from '../../../store/use-editor' import { CursorSphere } from '../shared/cursor-sphere' -import { BufferGeometry, DoubleSide, Path, Shape, ShapeGeometry, Vector3 } from 'three' function snap(value: number) { return Math.round(value * 2) / 2 @@ -202,6 +208,7 @@ export const MoveCeilingTool: React.FC<{ node: CeilingNode }> = ({ node }) => { transparent /> + {/* @ts-ignore */} diff --git a/packages/editor/src/components/tools/fence/curve-fence-tool.tsx b/packages/editor/src/components/tools/fence/curve-fence-tool.tsx index 4599cec38..65fe650ab 100644 --- a/packages/editor/src/components/tools/fence/curve-fence-tool.tsx +++ b/packages/editor/src/components/tools/fence/curve-fence-tool.tsx @@ -83,11 +83,10 @@ export const CurveFenceTool: React.FC<{ node: FenceNode }> = ({ node }) => { ? event.localPosition[2] : snapScalarToGrid(event.localPosition[2], snapStep) - const offsetFromMidpoint = - -( - (localX - chord.midpoint.x) * chord.normal.x + - (localZ - chord.midpoint.y) * chord.normal.y - ) + const offsetFromMidpoint = -( + (localX - chord.midpoint.x) * chord.normal.x + + (localZ - chord.midpoint.y) * chord.normal.y + ) const snappedOffset = shiftPressedRef.current ? offsetFromMidpoint : snapScalarToGrid(offsetFromMidpoint, snapStep) diff --git a/packages/editor/src/components/tools/fence/fence-tool.tsx b/packages/editor/src/components/tools/fence/fence-tool.tsx index c52f9b413..5afa67893 100644 --- a/packages/editor/src/components/tools/fence/fence-tool.tsx +++ b/packages/editor/src/components/tools/fence/fence-tool.tsx @@ -297,7 +297,7 @@ export const FenceTool: React.FC = () => { return ( - + - + {label}
Drop images here
- Thanks for your feedback! -
{error}
Now Playing
- {currentTrack.title} -