diff --git a/apps/web/bun.lock b/apps/web/bun.lock index 65ebaee96a7..487d4adc6e2 100644 --- a/apps/web/bun.lock +++ b/apps/web/bun.lock @@ -5,11 +5,18 @@ "": { "name": "@vellumai/web", "dependencies": { + "@hey-api/client-fetch": "0.13.1", + "@radix-ui/react-slot": "1.2.4", "@tanstack/react-query": "5.90.21", "@vellum/design-library": "file:../../packages/design-library", + "class-variance-authority": "0.7.1", + "clsx": "2.1.1", + "lucide-react": "1.16.0", + "motion": "12.39.0", "react": "19.2.6", "react-dom": "19.2.6", "react-router": "7.15.0", + "tailwind-merge": "3.6.0", }, "devDependencies": { "@hey-api/openapi-ts": "0.97.1", @@ -26,14 +33,102 @@ }, }, "packages": { + "@adobe/css-tools": ["@adobe/css-tools@4.4.4", "", {}, "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg=="], + "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], + "@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.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=="], + + "@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], + + "@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.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=="], + + "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], + + "@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="], + + "@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=="], + "@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], "@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.7", "", { "os": "android", "cpu": "arm64" }, "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.27.7", "", { "os": "android", "cpu": "x64" }, "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.7", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.7", "", { "os": "linux", "cpu": "arm" }, "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.7", "", { "os": "linux", "cpu": "ia32" }, "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.7", "", { "os": "linux", "cpu": "ppc64" }, "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.7", "", { "os": "linux", "cpu": "s390x" }, "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.7", "", { "os": "linux", "cpu": "x64" }, "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.7", "", { "os": "none", "cpu": "x64" }, "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.7", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.7", "", { "os": "openbsd", "cpu": "x64" }, "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.7", "", { "os": "sunos", "cpu": "x64" }, "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.7", "", { "os": "win32", "cpu": "ia32" }, "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="], + "@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=="], "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], @@ -48,6 +143,8 @@ "@eslint/plugin-kit": ["@eslint/plugin-kit@0.7.1", "", { "dependencies": { "@eslint/core": "^1.2.1", "levn": "^0.4.1" } }, "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ=="], + "@hey-api/client-fetch": ["@hey-api/client-fetch@0.13.1", "", { "peerDependencies": { "@hey-api/openapi-ts": "< 2" } }, "sha512-29jBRYNdxVGlx5oewFgOrkulZckpIpBIRHth3uHFn1PrL2ucMy52FvWOY3U3dVx2go1Z3kUmMi6lr07iOpUqqA=="], + "@hey-api/codegen-core": ["@hey-api/codegen-core@0.8.1", "", { "dependencies": { "@hey-api/types": "0.1.4", "ansi-colors": "4.1.3", "c12": "3.3.4", "color-support": "1.1.3" } }, "sha512-Iciv2vUCJTW9lWM/ROvyZLblmcbYJHPuXfzb1SzeDVVn4xEXu2ilLU1pq3fn+09FZ/Y0P7VyvRE47UDU6om8xA=="], "@hey-api/json-schema-ref-parser": ["@hey-api/json-schema-ref-parser@1.4.2", "", { "dependencies": { "@jsdevtools/ono": "7.1.3", "@types/json-schema": "7.0.15", "js-yaml": "4.1.1" } }, "sha512-ZhCFSKI2ipZHEbgmtUHdyddvRU3wJ4elgCfYUC7T7hZa4EivSrVflTQf2w+v3TuaYxR1Y2V2kq3otqTttrrK8Q=="], @@ -72,8 +169,12 @@ "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], + "@joshwooding/vite-plugin-react-docgen-typescript": ["@joshwooding/vite-plugin-react-docgen-typescript@0.7.0", "", { "dependencies": { "glob": "^13.0.1", "react-docgen-typescript": "^2.2.2" }, "peerDependencies": { "typescript": ">= 4.3.x", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["typescript"] }, "sha512-qvsTEwEFefhdirGOPnu9Wp6ChfIwy2dBCRuETU3uE+4cC+PFoxMSiiEhxk4lOluA34eARHA0OxqsEUYDqRMgeQ=="], + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], @@ -84,10 +185,16 @@ "@lukeed/ms": ["@lukeed/ms@2.0.2", "", {}, "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA=="], + "@mdx-js/react": ["@mdx-js/react@3.1.1", "", { "dependencies": { "@types/mdx": "^2.0.0" }, "peerDependencies": { "@types/react": ">=16", "react": ">=16" } }, "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw=="], + "@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" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], "@oxc-project/types": ["@oxc-project/types@0.128.0", "", {}, "sha512-huv1Y/LzBJkBVHt3OlC7u0zHBW9qXf1FdD7sGmc1rXc2P1mTwHssYv7jyGx5KAACSCH+9B3Bhn6Z9luHRvf7pQ=="], + "@radix-ui/react-compose-refs": ["@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-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "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-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.18", "", { "os": "android", "cpu": "arm64" }, "sha512-lIDyUAfD7U3+BWKzdxMbJcsYHuqXqmGz40aeRqvuAm3y5TkJSYTBW2RDrn65DJFPQqVjUAUqq5uz8urzQ8aBdQ=="], "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.18", "", { "os": "darwin", "cpu": "arm64" }, "sha512-apJq2ktnGp27nSInMR5Vcj8kY6xJzDAvfdIFlpDcAK/w4cDO58qVoi1YQsES/SKiFNge/6e4CUzgjfHduYqWpQ=="], @@ -120,6 +227,26 @@ "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.7", "", {}, "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA=="], + "@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="], + + "@storybook/addon-a11y": ["@storybook/addon-a11y@10.3.6", "", { "dependencies": { "@storybook/global": "^5.0.0", "axe-core": "^4.2.0" }, "peerDependencies": { "storybook": "^10.3.6" } }, "sha512-cbwXIT5CeHZ9AFbTKQ6YB7Ct6TAl/kKOgALbvzzVtFfRvm51JYygGaiJaB7PbPWn9wgJP2olJcFt+erlEc6cRw=="], + + "@storybook/addon-docs": ["@storybook/addon-docs@10.3.6", "", { "dependencies": { "@mdx-js/react": "^3.0.0", "@storybook/csf-plugin": "10.3.6", "@storybook/icons": "^2.0.1", "@storybook/react-dom-shim": "10.3.6", "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", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^10.3.6" } }, "sha512-TvIdADVPtauxW0LzXIpIv7X6GxwetorhyNh+6+7MHC27XSBCWVxxRUwL63YeLlHTuXsIk0quG3b1xgwVRzWOJA=="], + + "@storybook/builder-vite": ["@storybook/builder-vite@10.3.6", "", { "dependencies": { "@storybook/csf-plugin": "10.3.6", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^10.3.6", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-gpvR/sE4BcrFtmQZ+Ker7zD23oQzoVeqD9nF6cK6yzY+Q0svJXyX2EPmFG4y+EwygD5/vNzDpP84gGMut8VRwg=="], + + "@storybook/csf-plugin": ["@storybook/csf-plugin@10.3.6", "", { "dependencies": { "unplugin": "^2.3.5" }, "peerDependencies": { "esbuild": "*", "rollup": "*", "storybook": "^10.3.6", "vite": "*", "webpack": "*" }, "optionalPeers": ["esbuild", "rollup", "vite", "webpack"] }, "sha512-9kBf7VRdRqTSIYo+rPtVn5yjYYyK8kP2QhEYx3oiXvfwy4RexmbJnhk/tXa/lNiTqukA1TqaWQ2+5MqF4fu6YQ=="], + + "@storybook/global": ["@storybook/global@5.0.0", "", {}, "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ=="], + + "@storybook/icons": ["@storybook/icons@2.0.2", "", { "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" } }, "sha512-KZBCpXsshAIjczYNXR/rlxEtCUX/eAbpFNwKi8bcOomrLA4t/SyPz5RF+lVPO2oZBUE4sAkt43mfJUevQDSEEw=="], + + "@storybook/react": ["@storybook/react@10.3.6", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/react-dom-shim": "10.3.6", "react-docgen": "^8.0.2", "react-docgen-typescript": "^2.2.2" }, "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", "storybook": "^10.3.6", "typescript": ">= 4.9.x" }, "optionalPeers": ["typescript"] }, "sha512-oZQZ6xayWe5IdHmFUTL0TL8rX/gpNNh9gWhT2vzW5eeUvlkVG/RBKdsja6Ndrk2s1D9vcnwiI6r6CNXy3IEEmg=="], + + "@storybook/react-dom-shim": ["@storybook/react-dom-shim@10.3.6", "", { "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", "storybook": "^10.3.6" } }, "sha512-/Tu1gPu+Fw+zOnAGmxRmOD30FX3a04LxcTAKflEtdpmtIMVR5bA3qpjy+f5YhoyDCecbXyKmL1OeIU2FIIZHqQ=="], + + "@storybook/react-vite": ["@storybook/react-vite@10.3.6", "", { "dependencies": { "@joshwooding/vite-plugin-react-docgen-typescript": "^0.7.0", "@rollup/pluginutils": "^5.0.2", "@storybook/builder-vite": "10.3.6", "@storybook/react": "10.3.6", "empathic": "^2.0.0", "magic-string": "^0.30.0", "react-docgen": "^8.0.0", "resolve": "^1.22.8", "tsconfig-paths": "^4.2.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", "storybook": "^10.3.6", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-tySQRc+8q7V2NkylQMNJjDV8zXy6tkxb8oDqw/DIhHhI9Xn77MTKVZ8Cihbo5NMm7HYTB6xDKr6wqdSMgdufYQ=="], + "@tailwindcss/node": ["@tailwindcss/node@4.1.8", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.8" } }, "sha512-OWwBsbC9BFAJelmnNcrKuf+bka2ZxCE2A4Ft53Tkg4uoiE67r/PMEYwCsourC26E+kmxfwE0hVzMdxqeW+xu7Q=="], "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.8", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.8", "@tailwindcss/oxide-darwin-arm64": "4.1.8", "@tailwindcss/oxide-darwin-x64": "4.1.8", "@tailwindcss/oxide-freebsd-x64": "4.1.8", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.8", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.8", "@tailwindcss/oxide-linux-arm64-musl": "4.1.8", "@tailwindcss/oxide-linux-x64-gnu": "4.1.8", "@tailwindcss/oxide-linux-x64-musl": "4.1.8", "@tailwindcss/oxide-wasm32-wasi": "4.1.8", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.8", "@tailwindcss/oxide-win32-x64-msvc": "4.1.8" } }, "sha512-d7qvv9PsM5N3VNKhwVUhpK6r4h9wtLkJ6lz9ZY9aeZgrUWk1Z8VPyqyDT9MZlem7GTGseRQHkeB1j3tC7W1P+A=="], @@ -154,18 +281,44 @@ "@tanstack/react-query": ["@tanstack/react-query@5.90.21", "", { "dependencies": { "@tanstack/query-core": "5.90.20" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg=="], + "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], + + "@testing-library/jest-dom": ["@testing-library/jest-dom@6.9.1", "", { "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", "picocolors": "^1.1.1", "redent": "^3.0.0" } }, "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA=="], + + "@testing-library/user-event": ["@testing-library/user-event@14.6.1", "", { "peerDependencies": { "@testing-library/dom": ">=7.21.4" } }, "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw=="], + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], + "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], + + "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], + + "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], + + "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], + + "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], + + "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], + + "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + + "@types/doctrine": ["@types/doctrine@0.0.9", "", {}, "sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA=="], + "@types/esrecurse": ["@types/esrecurse@4.3.1", "", {}, "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw=="], "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], + "@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.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + "@types/resolve": ["@types/resolve@1.20.6", "", {}, "sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ=="], + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.58.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.58.1", "@typescript-eslint/type-utils": "8.58.1", "@typescript-eslint/utils": "8.58.1", "@typescript-eslint/visitor-keys": "8.58.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.58.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-eSkwoemjo76bdXl2MYqtxg51HNwUSkWfODUOQ3PaTLZGh9uIWWFZIjyjaJnex7wXDu+TRx+ATsnSxdN9YWfRTQ=="], "@typescript-eslint/parser": ["@typescript-eslint/parser@8.58.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.58.1", "@typescript-eslint/types": "8.58.1", "@typescript-eslint/typescript-estree": "8.58.1", "@typescript-eslint/visitor-keys": "8.58.1", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-gGkiNMPqerb2cJSVcruigx9eHBlLG14fSdPdqMoOcBfh+vvn4iCq2C8MzUB89PrxOXk0y3GZ1yIWb9aOzL93bw=="], @@ -186,10 +339,20 @@ "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.58.1", "", { "dependencies": { "@typescript-eslint/types": "8.58.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-y+vH7QE8ycjoa0bWciFg7OpFcipUuem1ujhrdLtq1gByKwfbC7bPeKsiny9e0urg93DqwGcHey+bGRKCnF1nZQ=="], - "@vellum/design-library": ["@vellum/design-library@file:../../packages/design-library", { "devDependencies": { "@types/react": "19.2.14", "@types/react-dom": "19.2.3", "react": "19.2.6", "react-dom": "19.2.6", "typescript": "5.9.3" }, "peerDependencies": { "react": ">=19.0.0", "react-dom": ">=19.0.0", "tailwindcss": ">=4.0.0" } }], + "@vellum/design-library": ["@vellum/design-library@file:../../packages/design-library", { "dependencies": { "@radix-ui/react-slot": "1.2.4", "class-variance-authority": "0.7.1", "clsx": "2.1.1", "lucide-react": "1.16.0", "tailwind-merge": "3.6.0" }, "devDependencies": { "@storybook/addon-a11y": "10.3.6", "@storybook/addon-docs": "10.3.6", "@storybook/react-vite": "10.3.6", "@tailwindcss/vite": "4.3.0", "@types/react": "19.2.14", "@types/react-dom": "19.2.3", "react": "19.2.6", "react-dom": "19.2.6", "storybook": "10.3.6", "typescript": "5.9.3", "vite": "8.0.11" }, "peerDependencies": { "react": ">=19.0.0", "react-dom": ">=19.0.0", "tailwindcss": ">=4.0.0" } }], "@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.1", "", { "dependencies": { "@rolldown/pluginutils": "1.0.0-rc.7" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ=="], + "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], + + "@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], + + "@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], + + "@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], + + "@webcontainer/env": ["@webcontainer/env@1.1.1", "", {}, "sha512-6aN99yL695Hi9SuIk1oC88l9o0gmxL1nGWWQ/kNy81HigJ0FoaoTXpytCj6ItzgyCEwA9kF1wixsTuv5cjsgng=="], + "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], @@ -198,34 +361,66 @@ "ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="], + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], + + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + + "ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="], + + "axe-core": ["axe-core@4.11.4", "", {}, "sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA=="], + "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.10.31", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-MujYO3eP72uvmSE0i4wltsodRfIpZATP3jvzRNRGGxgzId7aVocVJJV3nf01qnzzKFGxQVC9bpWxl5cjxTr/7Q=="], + "brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="], + "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=="], + "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], "c12": ["c12@3.3.4", "", { "dependencies": { "chokidar": "^5.0.0", "confbox": "^0.2.4", "defu": "^6.1.6", "dotenv": "^17.3.1", "exsolve": "^1.0.8", "giget": "^3.2.0", "jiti": "^2.6.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.1.0", "pkg-types": "^2.3.0", "rc9": "^3.0.1" }, "peerDependencies": { "magicast": "*" }, "optionalPeers": ["magicast"] }, "sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA=="], + "caniuse-lite": ["caniuse-lite@1.0.30001793", "", {}, "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA=="], + + "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], + + "check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="], + "chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], + "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + "color-support": ["color-support@1.1.3", "", { "bin": { "color-support": "bin.js" } }, "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="], "commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], "confbox": ["confbox@0.2.4", "", {}, "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ=="], + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + "css.escape": ["css.escape@1.5.1", "", {}, "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg=="], + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], "default-browser": ["default-browser@5.5.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="], @@ -236,13 +431,29 @@ "defu": ["defu@6.1.7", "", {}, "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="], + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + "destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="], "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "doctrine": ["doctrine@3.0.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w=="], + + "dom-accessibility-api": ["dom-accessibility-api@0.6.3", "", {}, "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="], + "dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="], - "enhanced-resolve": ["enhanced-resolve@5.21.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-QyL119InA+XXEkNLNTPCXPugSvOfhwv0JOlGNzvxs0hZaiHLNvXSpudUWsOlsXGWJh8G6ckCScEkVHfX3kw/2Q=="], + "electron-to-chromium": ["electron-to-chromium@1.5.357", "", {}, "sha512-NHlTIQDK8fmVwHwuIzmXYEJ1Ewq3D9wDNc0cWXxDGysP6Pb21giwGNkxiTifyKy/4SoPuN5l6GLP1W9Sv7zB2g=="], + + "empathic": ["empathic@2.0.1", "", {}, "sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q=="], + + "enhanced-resolve": ["enhanced-resolve@5.21.4", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-wE4fDO8OjJhrPFH69HUQStq5oKvGRTNXEyW+k5C/pUQLASSsTu7obd2V3GvCDgPcY9AWjhJ4jz9Kh7iRvrxhJg=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], @@ -254,12 +465,16 @@ "espree": ["espree@11.2.0", "", { "dependencies": { "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^5.0.1" } }, "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw=="], + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + "esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="], "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], "exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="], @@ -280,20 +495,34 @@ "flatted": ["flatted@3.4.2", "", {}, "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA=="], + "framer-motion": ["framer-motion@12.39.0", "", { "dependencies": { "motion-dom": "^12.39.0", "motion-utils": "^12.39.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-+vnLfzrv0MzjLzNl+nvNvR7jdg3q4cxxjz/YvzfifHl0TREtL00cs1RoMTxs+1PzLiEqZGV6gYsBY0oEAYZ24w=="], + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + "get-tsconfig": ["get-tsconfig@4.14.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA=="], "giget": ["giget@3.2.0", "", { "bin": { "giget": "dist/cli.mjs" } }, "sha512-GvHTWcykIR/fP8cj8dMpuMMkvaeJfPvYnhq0oW+chSeIr+ldX21ifU2Ms6KBoyKZQZmVaUAAhQ2EZ68KJF8a7A=="], + "glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="], + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + "hasown": ["hasown@2.0.3", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg=="], + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], + + "is-core-module": ["is-core-module@2.16.2", "", { "dependencies": { "hasown": "^2.0.3" } }, "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA=="], + "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], @@ -310,14 +539,20 @@ "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], @@ -348,20 +583,40 @@ "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], + + "lru-cache": ["lru-cache@11.4.0", "", {}, "sha512-W+R+kFL4HgVxONq2bhXPi3bGpzGe/yEhVOp233qw9wCRtgncJ15P3bC+e4zZMu4Cq7d+WAJjXGW0uUkifhcatA=="], + + "lucide-react": ["lucide-react@1.16.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-dYwyPzb4MEKpGUmNYk3WKWPnMrHs3FKM+q94kAnJrcDIqqn1hq2xY8scaS2ovsOCM5D51ey2gaRG3PBb1vgoYQ=="], + + "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + "min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="], + "minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + "minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], "minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], + "motion": ["motion@12.39.0", "", { "dependencies": { "framer-motion": "^12.39.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-H4a+Ze+a9j+/NTla5ezfb/g9vmIOxC+viDj++NGDZyTZkdRKjiOz3kSv6TalRWM8ZmD2y/CfC6TkQc97ybyqSA=="], + + "motion-dom": ["motion-dom@12.39.0", "", { "dependencies": { "motion-utils": "^12.39.0" } }, "sha512-Xn7aAcGDhco/JZTXOub64UmaYn73C6J1Po7Fk+8EvkJsNGTqfhon6UJY53vJKXW5v5Zl8HrYsVxv6oPXeGoGLQ=="], + + "motion-utils": ["motion-utils@12.39.0", "", {}, "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + "node-releases": ["node-releases@2.0.44", "", {}, "sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ=="], + "ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], "open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="], @@ -376,8 +631,14 @@ "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="], + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], + "perfect-debounce": ["perfect-debounce@2.1.0", "", {}, "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], @@ -392,18 +653,32 @@ "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], "rc9": ["rc9@3.0.1", "", { "dependencies": { "defu": "^6.1.6", "destr": "^2.0.5" } }, "sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ=="], "react": ["react@19.2.6", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="], + "react-docgen": ["react-docgen@8.0.3", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.2", "@types/babel__core": "^7.20.5", "@types/babel__traverse": "^7.20.7", "@types/doctrine": "^0.0.9", "@types/resolve": "^1.20.2", "doctrine": "^3.0.0", "resolve": "^1.22.1", "strip-indent": "^4.0.0" } }, "sha512-aEZ9qP+/M+58x2qgfSFEWH1BxLyHe5+qkLNJOZQb5iGS017jpbRnoKhNRrXPeA6RfBrZO5wZrT9DMC1UqE1f1w=="], + + "react-docgen-typescript": ["react-docgen-typescript@2.4.0", "", { "peerDependencies": { "typescript": ">= 4.3.x" } }, "sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg=="], + "react-dom": ["react-dom@19.2.6", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.6" } }, "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g=="], + "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], + "react-router": ["react-router@7.15.0", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-HW9vYwuM8f4yx66Izy8xfrzCM+SBJluoZcCbww9A1TySax11S5Vgw6fi3ZjMONw9J4gQwngL7PzkyIpJJpJ7RQ=="], "readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], + "recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="], + + "redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="], + + "resolve": ["resolve@1.22.12", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="], + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], "rolldown": ["rolldown@1.0.0-rc.18", "", { "dependencies": { "@oxc-project/types": "=0.128.0", "@rolldown/pluginutils": "1.0.0-rc.18" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.18", "@rolldown/binding-darwin-arm64": "1.0.0-rc.18", "@rolldown/binding-darwin-x64": "1.0.0-rc.18", "@rolldown/binding-freebsd-x64": "1.0.0-rc.18", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.18", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.18", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.18", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.18", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.18", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.18", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.18", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.18", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.18", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.18", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.18" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-phmyKBpuBdRYDf4hgyynGAYn/rDDe+iZXKVJ7WX5b1zQzpLkP5oJRPGsfJuHdzPMlyyEO/4sPW6yfSx2gf7lVg=="], @@ -420,18 +695,40 @@ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + "storybook": ["storybook@10.3.6", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/user-event": "^14.6.1", "@vitest/expect": "3.2.4", "@vitest/spy": "3.2.4", "@webcontainer/env": "^1.1.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0", "open": "^10.2.0", "recast": "^0.23.5", "semver": "^7.7.3", "use-sync-external-store": "^1.5.0", "ws": "^8.18.0" }, "peerDependencies": { "prettier": "^2 || ^3", "vite-plus": "^0.1.15" }, "optionalPeers": ["prettier", "vite-plus"], "bin": "./dist/bin/dispatcher.js" }, "sha512-vbSz7g/1rGMC1uAULqMZjALkIuLu2QABqfhRYhyr/11kzyesi+vAmwyJLukZP1FfecxGOgMwOh6GS0YsGpHAvQ=="], + + "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], + + "strip-indent": ["strip-indent@4.1.1", "", {}, "sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "tailwind-merge": ["tailwind-merge@3.6.0", "", {}, "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w=="], + "tailwindcss": ["tailwindcss@4.1.8", "", {}, "sha512-kjeW8gjdxasbmFKpVGrGd5T4i40mV5J2Rasw48QARfYeQ8YS9x02ON9SFWax3Qf616rt4Cp3nVNIj6Hd1mP3og=="], "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], "tar": ["tar@7.5.15", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ=="], + "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], + "tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="], + "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], + + "tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], + "ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="], + "ts-dedent": ["ts-dedent@2.2.0", "", {}, "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ=="], + + "tsconfig-paths": ["tsconfig-paths@4.2.0", "", { "dependencies": { "json5": "^2.2.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg=="], + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], @@ -440,20 +737,36 @@ "typescript-eslint": ["typescript-eslint@8.58.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.58.1", "@typescript-eslint/parser": "8.58.1", "@typescript-eslint/typescript-estree": "8.58.1", "@typescript-eslint/utils": "8.58.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-gf6/oHChByg9HJvhMO1iBexJh12AqqTfnuxscMDOVqfJW3htsdRJI/GfPpHTTcyeB8cSTUY2JcZmVgoyPqcrDg=="], + "unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="], + + "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], + "vite": ["vite@8.0.11", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.14", "rolldown": "1.0.0-rc.18", "tinyglobby": "^0.2.16" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-Jz1mxtUBR5xTT65VOdJZUUeoyLtqljmFkiUXhPTLZka3RDc9vpi/xXkyrnsdRcm2lIi3l3GPMnAidTsEGIj3Ow=="], + "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + "ws": ["ws@8.20.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w=="], + "wsl-utils": ["wsl-utils@0.3.1", "", { "dependencies": { "is-wsl": "^3.1.0", "powershell-utils": "^0.1.0" } }, "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg=="], "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], "@tailwindcss/node/lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="], @@ -470,12 +783,22 @@ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "@testing-library/dom/aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], + + "@testing-library/dom/dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], - "@typescript-eslint/typescript-estree/semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="], + "@vellum/design-library/@tailwindcss/vite": ["@tailwindcss/vite@4.3.0", "", { "dependencies": { "@tailwindcss/node": "4.3.0", "@tailwindcss/oxide": "4.3.0", "tailwindcss": "4.3.0" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw=="], + + "redent/strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], "rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.18", "", {}, "sha512-CUY5Mnhe64xQBGZEEXQ5WyZwsc1JU3vAZLIxtrsBt3LO6UOb+C8GunVKqe9sT8NeWb4lqSaoJtp2xo6GxT1MNw=="], + "storybook/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="], + + "@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + "@tailwindcss/node/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="], "@tailwindcss/node/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="], @@ -497,5 +820,49 @@ "@tailwindcss/node/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="], "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], + + "@vellum/design-library/@tailwindcss/vite/@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=="], + + "@vellum/design-library/@tailwindcss/vite/@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=="], + + "@vellum/design-library/@tailwindcss/vite/tailwindcss": ["tailwindcss@4.3.0", "", {}, "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q=="], + + "storybook/open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="], + + "@vellum/design-library/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.0", "", { "os": "android", "cpu": "arm64" }, "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng=="], + + "@vellum/design-library/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ=="], + + "@vellum/design-library/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.3.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA=="], + + "@vellum/design-library/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.3.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ=="], + + "@vellum/design-library/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0", "", { "os": "linux", "cpu": "arm" }, "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA=="], + + "@vellum/design-library/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg=="], + + "@vellum/design-library/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ=="], + + "@vellum/design-library/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ=="], + + "@vellum/design-library/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg=="], + + "@vellum/design-library/@tailwindcss/vite/@tailwindcss/oxide/@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=="], + + "@vellum/design-library/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.3.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ=="], + + "@vellum/design-library/@tailwindcss/vite/@tailwindcss/oxide/@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=="], + + "@vellum/design-library/@tailwindcss/vite/@tailwindcss/oxide/@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=="], + + "@vellum/design-library/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], + + "@vellum/design-library/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + + "@vellum/design-library/@tailwindcss/vite/@tailwindcss/oxide/@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=="], + + "@vellum/design-library/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], + + "@vellum/design-library/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], } } diff --git a/apps/web/package.json b/apps/web/package.json index cfc2f09e345..d160e0a0db6 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -9,14 +9,22 @@ "preview": "vite preview", "lint": "eslint", "typecheck": "bunx tsc --noEmit", + "postinstall": "rm -rf ../../packages/design-library/node_modules/react ../../packages/design-library/node_modules/@types/react ../../packages/design-library/node_modules/react-dom ../../packages/design-library/node_modules/@types/react-dom 2>/dev/null; true", "openapi-ts": "openapi-ts" }, "dependencies": { + "@hey-api/client-fetch": "0.13.1", + "@radix-ui/react-slot": "1.2.4", "@tanstack/react-query": "5.90.21", "@vellum/design-library": "file:../../packages/design-library", + "class-variance-authority": "0.7.1", + "clsx": "2.1.1", + "lucide-react": "1.16.0", + "motion": "12.39.0", "react": "19.2.6", "react-dom": "19.2.6", - "react-router": "7.15.0" + "react-router": "7.15.0", + "tailwind-merge": "3.6.0" }, "devDependencies": { "@hey-api/openapi-ts": "0.97.1", diff --git a/apps/web/src/components/avatar/animated-avatar.tsx b/apps/web/src/components/avatar/animated-avatar.tsx new file mode 100644 index 00000000000..17f3ea0f0c0 --- /dev/null +++ b/apps/web/src/components/avatar/animated-avatar.tsx @@ -0,0 +1,306 @@ +import { useEffect, useMemo, useRef, useState } from "react"; +import { useReducedMotion } from "motion/react"; + +import { computeTransforms, resolveDefinitions } from "@/domains/avatar/svg-compositor.js"; +import type { CharacterComponents, CharacterTraits } from "@/domains/avatar/types.js"; + +interface AnimatedAvatarProps { + components: CharacterComponents; + traits: CharacterTraits; + size: number; + isStreaming?: boolean; +} + +function randomBetween(min: number, max: number): number { + return min + Math.random() * (max - min); +} + +// SVG path wobble — port of macOS EditablePath.wobbled() + +interface PathPoint { + x: number; + y: number; +} + +function parsePathNumbers(d: string): number[] { + const nums: number[] = []; + const re = /-?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?/gi; + let m: RegExpExecArray | null; + while ((m = re.exec(d)) !== null) { + nums.push(parseFloat(m[0])); + } + return nums; +} + +function computeCentroid(d: string): PathPoint { + const nums = parsePathNumbers(d); + let sx = 0; + let sy = 0; + let count = 0; + for (let i = 0; i < nums.length - 1; i += 2) { + sx += nums[i]!; + sy += nums[i + 1]!; + count++; + } + return count > 0 ? { x: sx / count, y: sy / count } : { x: 0, y: 0 }; +} + +function wobblePath(d: string, seed: number, amount: number): string { + const center = computeCentroid(d); + const phase = seed * 1.1; + + return d.replace(/-?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?/gi, (match, offset: number) => { + const val = parseFloat(match); + const prevText = d.slice(0, offset); + const numsBefore = prevText.match(/-?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?/gi); + const idx = numsBefore ? numsBefore.length : 0; + const isX = idx % 2 === 0; + + const refVal = isX ? center.x : center.y; + const otherNums = parsePathNumbers(d); + const pairedIdx = isX ? idx + 1 : idx - 1; + const pairedVal = + pairedIdx >= 0 && pairedIdx < otherNums.length + ? otherNums[pairedIdx]! + : refVal; + + const px = isX ? val : pairedVal; + const py = isX ? pairedVal : val; + + const angle = Math.atan2(py - center.y, px - center.x); + const wobble = + Math.sin(angle * 2.0 + phase) * 0.7 + + Math.sin(angle * 3.0 - phase * 0.5) * 0.3; + const scale = 1.0 + wobble * amount; + + const result = refVal + (val - refVal) * scale; + return result.toFixed(3); + }); +} + +function precomputeWobbledPaths( + basePath: string, + count: number, + amount: number, +): string[] { + const paths: string[] = [basePath]; + for (let i = 1; i < count; i++) { + paths.push(wobblePath(basePath, i, amount)); + } + return paths; +} + +/** + * Character avatar rendered as React SVG elements with idle animations: + * - Breathing: continuous 4s scale pulse (CSS keyframe) + * - Blink: random 3-7s eye scaleY squish, 20% double-blink + * - Twitch: random 8-15s body rotation wobble + * + * During streaming (`isStreaming`): + * - Morph: body path cycles through 16 wobbled variants + * - Scale + rotation CSS animations + * - Blink + twitch paused + * + * All animations respect `prefers-reduced-motion`. + */ +export function AnimatedAvatar({ + components, + traits, + size, + isStreaming = false, +}: AnimatedAvatarProps) { + const reduce = useReducedMotion(); + + const { bodyShape, eyeStyle, color } = resolveDefinitions( + components, + traits.bodyShape, + traits.eyeStyle, + traits.color, + ); + const { bodyTransform, eyeTransform } = computeTransforms( + bodyShape, + eyeStyle, + components, + size, + ); + + const eyeVB = eyeStyle.sourceViewBox; + const bodyVB = bodyShape.viewBox; + const bodyScaleFactor = Math.min(size / bodyVB.width, size / bodyVB.height); + const bodyTx = (size - bodyVB.width * bodyScaleFactor) / 2; + const bodyTy = (size - bodyVB.height * bodyScaleFactor) / 2; + const remapScale = Math.min( + bodyVB.width / eyeVB.width, + bodyVB.height / eyeVB.height, + ); + + const override = components.faceCenterOverrides.find( + (o) => o.bodyShape === bodyShape.id && o.eyeStyle === eyeStyle.id, + ); + const faceCenter = override ? override.faceCenter : bodyShape.faceCenter; + const remapTx = faceCenter.x - eyeStyle.eyeCenter.x * remapScale; + const remapTy = faceCenter.y - eyeStyle.eyeCenter.y * remapScale; + + const eyeCenterOutputX = + bodyScaleFactor * (remapTx + eyeStyle.eyeCenter.x * remapScale) + bodyTx; + const eyeCenterOutputY = + bodyScaleFactor * (remapTy + eyeStyle.eyeCenter.y * remapScale) + bodyTy; + + const morphPaths = useMemo( + () => precomputeWobbledPaths(bodyShape.svgPath, 16, 0.06), + [bodyShape.svgPath], + ); + + const [isBlinking, setIsBlinking] = useState(false); + const [twitchAngle, setTwitchAngle] = useState(0); + const [morphIndex, setMorphIndex] = useState(0); + + const morphTimerRef = useRef | null>(null); + + useEffect(() => { + if (reduce || isStreaming) return; + let cancelled = false; + + function scheduleBlink() { + const timer = setTimeout(() => { + if (cancelled) return; + setIsBlinking(true); + setTimeout(() => { + if (cancelled) return; + setIsBlinking(false); + if (Math.random() < 0.2) { + setTimeout(() => { + if (cancelled) return; + setIsBlinking(true); + setTimeout(() => { + if (cancelled) return; + setIsBlinking(false); + scheduleBlink(); + }, 150); + }, 200); + } else { + scheduleBlink(); + } + }, 150); + }, randomBetween(3000, 7000)); + + return timer; + } + + const timer = scheduleBlink(); + + return () => { + cancelled = true; + clearTimeout(timer); + }; + }, [reduce, isStreaming]); + + useEffect(() => { + if (reduce || isStreaming) return; + let cancelled = false; + + function scheduleTwitch() { + const timer = setTimeout(() => { + if (cancelled) return; + const angle = + (Math.random() < 0.5 ? -1 : 1) * randomBetween(1, 2); + setTwitchAngle(angle); + setTimeout(() => { + if (cancelled) return; + setTwitchAngle(0); + scheduleTwitch(); + }, 200); + }, randomBetween(8000, 15000)); + + return timer; + } + + const timer = scheduleTwitch(); + + return () => { + cancelled = true; + clearTimeout(timer); + }; + }, [reduce, isStreaming]); + + // Morph path cycling (only during streaming) + useEffect(() => { + if (!isStreaming || reduce) { + setMorphIndex(0); + return; + } + + let idx = 0; + morphTimerRef.current = setInterval(() => { + idx = (idx + 1) % morphPaths.length; + setMorphIndex(idx); + }, 150); + + return () => { + if (morphTimerRef.current) clearInterval(morphTimerRef.current); + morphTimerRef.current = null; + }; + }, [isStreaming, reduce, morphPaths.length]); + + const bodyCenterX = size / 2; + const bodyCenterY = size / 2; + + const breatheAnimation = reduce + ? "none" + : isStreaming + ? "avatar-morph-scale 2.4s ease-in-out infinite, avatar-morph-rotate 3s ease-in-out infinite" + : "avatar-breathe-kf 4s ease-in-out infinite"; + + const effectiveTwitchAngle = isStreaming ? 0 : twitchAngle; + const currentBodyPath = morphPaths[morphIndex] ?? bodyShape.svgPath; + + return ( + + + + + + + {eyeStyle.paths.map((p, i) => ( + + ))} + + + ); +} diff --git a/apps/web/src/components/avatar/chat-avatar.tsx b/apps/web/src/components/avatar/chat-avatar.tsx new file mode 100644 index 00000000000..2337725c9e5 --- /dev/null +++ b/apps/web/src/components/avatar/chat-avatar.tsx @@ -0,0 +1,137 @@ +import { motion, useReducedMotion } from "motion/react"; +import { useCallback, useMemo, useState } from "react"; + +import type { CharacterComponents, CharacterTraits } from "@/domains/avatar/types.js"; +import { AnimatedAvatar } from "./animated-avatar.js"; + +export interface ChatAvatarProps { + components: CharacterComponents | null; + traits: CharacterTraits | null; + customImageUrl: string | null; + size?: number; + className?: string; + interactive?: boolean; + isStreaming?: boolean; +} + +/** + * Displays the assistant's avatar in chat messages. + * + * Priority: + * 1. Animated character avatar from saved traits + * 2. Custom uploaded image + * 3. Default animated character avatar from first component of each type + * 4. Vellum "V" fallback + * + * Animation: + * - Mount plays an entrance spring (scale 0.6 → 1, opacity 0 → 1). + * - When `interactive`, click triggers a spring bounce. + * - `prefers-reduced-motion` short-circuits both. + */ +export function ChatAvatar({ + components, + traits, + customImageUrl, + size = 28, + className, + interactive = false, + isStreaming = false, +}: ChatAvatarProps) { + const reduce = useReducedMotion(); + const [isPoking, setIsPoking] = useState(false); + + const triggerBounce = useCallback(() => { + if (reduce) return; + setIsPoking(true); + window.setTimeout(() => setIsPoking(false), 360); + }, [reduce]); + + const handleClick = interactive ? triggerBounce : undefined; + + const effectiveTraits = useMemo(() => { + if (traits) return traits; + if (!components) return null; + const body = components.bodyShapes[0]; + const eyes = components.eyeStyles[0]; + const color = components.colors[0]; + if (!body || !eyes || !color) return null; + return { bodyShape: body.id, eyeStyle: eyes.id, color: color.id }; + }, [traits, components]); + + const hasCharacter = !!components && !!effectiveTraits; + const preferCharacter = hasCharacter && (!!traits || !customImageUrl); + + const wrapperStyle: React.CSSProperties = { + width: size, + height: size, + flexShrink: 0, + cursor: interactive ? "pointer" : undefined, + transformOrigin: "center", + }; + + const transition = reduce + ? { duration: 0 } + : { type: "spring" as const, visualDuration: 0.3, bounce: 0.5 }; + + const initial = reduce + ? { scale: 1, opacity: 1 } + : { scale: 0.6, opacity: 0 }; + const animate = { scale: isPoking ? 1.15 : 1, opacity: 1 }; + + if (preferCharacter) { + return ( + + + + ); + } + + if (customImageUrl) { + return ( + + Assistant avatar + + ); + } + + return ( + + V + + ); +} diff --git a/apps/web/src/domains/avatar/api.ts b/apps/web/src/domains/avatar/api.ts new file mode 100644 index 00000000000..f66eef5f1c2 --- /dev/null +++ b/apps/web/src/domains/avatar/api.ts @@ -0,0 +1,60 @@ +/** + * Avatar API functions for fetching character components and traits. + * + * These call daemon endpoints via the configured HeyAPI client singleton. + */ +import { client } from "@/lib/api-client.js"; +import { assertHasResponse } from "@/lib/api-errors.js"; +import type { CharacterComponents, CharacterTraits } from "./types.js"; +import { isCharacterTraits } from "./types.js"; + +export async function fetchCharacterComponents( + assistantId: string, +): Promise { + try { + const { data, error, response } = await client.get({ + url: "/v1/assistants/{assistant_id}/avatar/character-components", + path: { assistant_id: assistantId }, + }); + assertHasResponse(response, error, "Failed to fetch character components"); + if (!response.ok || !data || typeof data !== "object") return null; + return data as CharacterComponents; + } catch { + return null; + } +} + +export async function fetchCharacterTraits( + assistantId: string, +): Promise { + try { + const { data, error, response } = await client.get({ + url: "/v1/assistants/{assistant_id}/avatar/character-traits", + path: { assistant_id: assistantId }, + }); + assertHasResponse(response, error, "Failed to fetch character traits"); + if (!response.ok || !data) return null; + if (!isCharacterTraits(data)) return null; + return data; + } catch { + return null; + } +} + +export async function fetchAvatarImageUrl( + assistantId: string, +): Promise { + try { + const { data, error, response } = await client.get({ + url: "/v1/assistants/{assistant_id}/workspace/file/content/", + path: { assistant_id: assistantId }, + query: { path: "data/avatar/avatar-image.png" }, + parseAs: "blob", + }); + assertHasResponse(response, error, "Failed to fetch avatar image"); + if (!response.ok || !data) return null; + return URL.createObjectURL(data as Blob); + } catch { + return null; + } +} diff --git a/apps/web/src/domains/avatar/svg-compositor.ts b/apps/web/src/domains/avatar/svg-compositor.ts new file mode 100644 index 00000000000..4c35df80c68 --- /dev/null +++ b/apps/web/src/domains/avatar/svg-compositor.ts @@ -0,0 +1,70 @@ +import type { + BodyShapeDefinition, + CharacterComponents, + ColorDefinition, + EyeStyleDefinition, +} from "./types.js"; + +export interface AvatarTransforms { + bodyTransform: string; + eyeTransform: string; +} + +/** + * Compute the SVG transform strings for body and eye groups. + */ +export function computeTransforms( + bodyShape: BodyShapeDefinition, + eyeStyle: EyeStyleDefinition, + components: CharacterComponents, + size: number, +): AvatarTransforms { + const override = components.faceCenterOverrides.find( + (o) => o.bodyShape === bodyShape.id && o.eyeStyle === eyeStyle.id, + ); + const faceCenter = override ? override.faceCenter : bodyShape.faceCenter; + + const bodyVB = bodyShape.viewBox; + const bodyScale = Math.min(size / bodyVB.width, size / bodyVB.height); + const bodyTx = (size - bodyVB.width * bodyScale) / 2; + const bodyTy = (size - bodyVB.height * bodyScale) / 2; + + const eyeVB = eyeStyle.sourceViewBox; + const remapScale = Math.min( + bodyVB.width / eyeVB.width, + bodyVB.height / eyeVB.height, + ); + const remapTx = faceCenter.x - eyeStyle.eyeCenter.x * remapScale; + const remapTy = faceCenter.y - eyeStyle.eyeCenter.y * remapScale; + + const composedScale = bodyScale * remapScale; + const composedTx = bodyScale * remapTx + bodyTx; + const composedTy = bodyScale * remapTy + bodyTy; + + return { + bodyTransform: `matrix(${bodyScale},0,0,${bodyScale},${bodyTx},${bodyTy})`, + eyeTransform: `matrix(${composedScale},0,0,${composedScale},${composedTx},${composedTy})`, + }; +} + +/** + * Resolve the active definitions from components + trait IDs. + */ +export function resolveDefinitions( + components: CharacterComponents, + bodyShapeId: string, + eyeStyleId: string, + colorId: string, +): { + bodyShape: BodyShapeDefinition; + eyeStyle: EyeStyleDefinition; + color: ColorDefinition; +} { + const bodyShape = components.bodyShapes.find((b) => b.id === bodyShapeId); + if (!bodyShape) throw new Error(`Unknown body shape: "${bodyShapeId}"`); + const eyeStyle = components.eyeStyles.find((e) => e.id === eyeStyleId); + if (!eyeStyle) throw new Error(`Unknown eye style: "${eyeStyleId}"`); + const color = components.colors.find((c) => c.id === colorId); + if (!color) throw new Error(`Unknown color: "${colorId}"`); + return { bodyShape, eyeStyle, color }; +} diff --git a/apps/web/src/domains/avatar/types.ts b/apps/web/src/domains/avatar/types.ts new file mode 100644 index 00000000000..2776dcbc756 --- /dev/null +++ b/apps/web/src/domains/avatar/types.ts @@ -0,0 +1,52 @@ +export interface BodyShapeDefinition { + id: string; + viewBox: { width: number; height: number }; + faceCenter: { x: number; y: number }; + svgPath: string; +} + +export interface EyePathDefinition { + svgPath: string; + color: string; +} + +export interface EyeStyleDefinition { + id: string; + sourceViewBox: { width: number; height: number }; + eyeCenter: { x: number; y: number }; + paths: EyePathDefinition[]; +} + +export interface ColorDefinition { + id: string; + hex: string; +} + +export interface FaceCenterOverride { + bodyShape: string; + eyeStyle: string; + faceCenter: { x: number; y: number }; +} + +export interface CharacterComponents { + bodyShapes: BodyShapeDefinition[]; + eyeStyles: EyeStyleDefinition[]; + colors: ColorDefinition[]; + faceCenterOverrides: FaceCenterOverride[]; +} + +export interface CharacterTraits { + bodyShape: string; + eyeStyle: string; + color: string; +} + +export function isCharacterTraits(value: unknown): value is CharacterTraits { + if (typeof value !== "object" || value === null) return false; + const obj = value as Record; + return ( + typeof obj.bodyShape === "string" && + typeof obj.eyeStyle === "string" && + typeof obj.color === "string" + ); +} diff --git a/apps/web/src/domains/avatar/use-assistant-avatar.ts b/apps/web/src/domains/avatar/use-assistant-avatar.ts new file mode 100644 index 00000000000..2647fbbc462 --- /dev/null +++ b/apps/web/src/domains/avatar/use-assistant-avatar.ts @@ -0,0 +1,75 @@ +import { useCallback } from "react"; +import { useQuery, useQueryClient } from "@tanstack/react-query"; + +import { + fetchCharacterComponents, + fetchCharacterTraits, + fetchAvatarImageUrl, +} from "./api.js"; +import type { CharacterComponents, CharacterTraits } from "./types.js"; + +interface AvatarData { + components: CharacterComponents | null; + traits: CharacterTraits | null; + customImageUrl: string | null; +} + +const AVATAR_QUERY_KEY_PREFIX = "assistantAvatar"; + +function avatarQueryKey(assistantId: string) { + return [AVATAR_QUERY_KEY_PREFIX, assistantId] as const; +} + +const activeBlobUrls = new Map(); + +/** + * Shared hook for assistant avatar data backed by React Query. + * + * All consumers of the same `assistantId` share a single cached result. + * Call `invalidate()` to trigger a refetch that every consumer sees. + */ +export function useAssistantAvatar(assistantId: string | null) { + const queryClient = useQueryClient(); + + const { data, isLoading } = useQuery({ + queryKey: avatarQueryKey(assistantId ?? ""), + queryFn: async () => { + const id = assistantId!; + const [components, traits, imageUrl] = await Promise.all([ + fetchCharacterComponents(id), + fetchCharacterTraits(id), + fetchAvatarImageUrl(id), + ]); + + const prev = activeBlobUrls.get(id); + if (prev && prev !== imageUrl) { + URL.revokeObjectURL(prev); + } + if (imageUrl) { + activeBlobUrls.set(id, imageUrl); + } else { + activeBlobUrls.delete(id); + } + + return { components, traits, customImageUrl: imageUrl }; + }, + enabled: Boolean(assistantId), + staleTime: Infinity, + structuralSharing: false, + }); + + const invalidate = useCallback(() => { + if (!assistantId) return; + void queryClient.invalidateQueries({ + queryKey: avatarQueryKey(assistantId), + }); + }, [assistantId, queryClient]); + + return { + components: data?.components ?? null, + traits: data?.traits ?? null, + customImageUrl: data?.customImageUrl ?? null, + isLoading, + invalidate, + }; +} diff --git a/apps/web/src/domains/home/api.ts b/apps/web/src/domains/home/api.ts new file mode 100644 index 00000000000..48a20fd34b8 --- /dev/null +++ b/apps/web/src/domains/home/api.ts @@ -0,0 +1,77 @@ +/** + * Hand-written fetch wrappers for daemon home endpoints. + * + * These endpoints are not in the Django OpenAPI schema, so we use the + * HeyAPI client singleton directly rather than generated hooks. + */ +import { client } from "@/lib/api-client.js"; +import type { + FeedItem, + FeedItemStatus, + HomeFeedResponse, + RelationshipState, +} from "./types.js"; + +export async function fetchHomeFeed( + assistantId: string, + timeAwaySeconds: number = 0, +): Promise { + const { data, response } = await client.get({ + url: "/v1/assistants/{assistant_id}/home/feed", + path: { assistant_id: assistantId }, + query: { timeAwaySeconds }, + }); + if (!response.ok) { + throw new Error(`Failed to fetch home feed: ${response.status}`); + } + return data as HomeFeedResponse; +} + +export async function fetchRelationshipState( + assistantId: string, +): Promise { + const { data, response } = await client.get({ + url: "/v1/assistants/{assistant_id}/home/state", + path: { assistant_id: assistantId }, + }); + if (!response.ok) { + throw new Error(`Failed to fetch relationship state: ${response.status}`); + } + return data as RelationshipState; +} + +export async function updateFeedItemStatus( + assistantId: string, + itemId: string, + status: FeedItemStatus, +): Promise { + const { data, response } = await client.patch({ + url: "/v1/assistants/{assistant_id}/home/feed/{item_id}", + path: { assistant_id: assistantId, item_id: itemId }, + body: { status }, + }); + if (!response.ok) { + throw new Error(`Failed to update feed item: ${response.status}`); + } + return data as FeedItem; +} + +export async function triggerFeedAction( + assistantId: string, + itemId: string, + actionId: string, +): Promise<{ conversationId: string }> { + const { data, response } = await client.post({ + url: "/v1/assistants/{assistant_id}/home/feed/{item_id}/actions/{action_id}", + path: { + assistant_id: assistantId, + item_id: itemId, + action_id: actionId, + }, + body: {}, + }); + if (!response.ok) { + throw new Error(`Failed to trigger feed action: ${response.status}`); + } + return data as { conversationId: string }; +} diff --git a/apps/web/src/domains/home/detail-panel/home-detail-panel.tsx b/apps/web/src/domains/home/detail-panel/home-detail-panel.tsx new file mode 100644 index 00000000000..30605a98a8e --- /dev/null +++ b/apps/web/src/domains/home/detail-panel/home-detail-panel.tsx @@ -0,0 +1,104 @@ +import { Circle, CircleCheck, X } from "lucide-react"; + +import { Button } from "@vellum/design-library/components/button"; +import { Typography } from "@vellum/design-library/components/typography"; +import { CATEGORY_STYLES } from "../home-feed-filter-bar.js"; +import { HomeGenericDetail } from "./home-generic-detail.js"; +import { HomeToolPermissionCard } from "./home-tool-permission-card.js"; +import type { FeedItem, FeedItemCategory, FeedItemStatus } from "../types.js"; + +function resolveCategoryStyle(category?: FeedItemCategory) { + if (category && CATEGORY_STYLES[category]) { + return CATEGORY_STYLES[category]; + } + return CATEGORY_STYLES.system; +} + +export interface HomeDetailPanelProps { + item: FeedItem | null; + onClose: () => void; + onGoToThread: (conversationId: string) => void; + onUpdateStatus: (itemId: string, status: FeedItemStatus) => void; +} + +export function HomeDetailPanel({ + item, + onClose, + onGoToThread, + onUpdateStatus, +}: HomeDetailPanelProps) { + if (!item) return null; + + const panelKind = item.detailPanel?.kind; + const categoryStyle = resolveCategoryStyle(item.category); + const CategoryIcon = categoryStyle.icon; + const isUnread = item.status === "new"; + + return ( +
+ {/* Header */} +
+ + + + {item.title} + + + + ) : null} + +
+ + {/* Scrollable content */} +
+ {panelKind === "toolPermission" ? ( + + ) : ( + + )} +
+
+ ); +} diff --git a/apps/web/src/domains/home/detail-panel/home-generic-detail.tsx b/apps/web/src/domains/home/detail-panel/home-generic-detail.tsx new file mode 100644 index 00000000000..828f57b7ffb --- /dev/null +++ b/apps/web/src/domains/home/detail-panel/home-generic-detail.tsx @@ -0,0 +1,43 @@ +import { Typography } from "@vellum/design-library/components/typography"; +import { CATEGORY_STYLES, CATEGORY_ORDER } from "../home-feed-filter-bar.js"; +import type { FeedItem, FeedItemCategory } from "../types.js"; + +function resolveStyle(category?: FeedItemCategory) { + if (category && CATEGORY_STYLES[category]) { + return CATEGORY_STYLES[category]; + } + const fallback = CATEGORY_ORDER[0] ?? "security"; + return CATEGORY_STYLES[fallback]; +} + +export interface HomeGenericDetailProps { + item: FeedItem; +} + +export function HomeGenericDetail({ item }: HomeGenericDetailProps) { + const style = resolveStyle(item.category); + const Icon = style.icon; + + return ( +
+ + + + {item.summary} + +
+ ); +} diff --git a/apps/web/src/domains/home/detail-panel/home-tool-permission-card.tsx b/apps/web/src/domains/home/detail-panel/home-tool-permission-card.tsx new file mode 100644 index 00000000000..306aadb5775 --- /dev/null +++ b/apps/web/src/domains/home/detail-panel/home-tool-permission-card.tsx @@ -0,0 +1,128 @@ +import { Typography } from "@vellum/design-library/components/typography"; +import type { FeedItem } from "../types.js"; + +type CredentialStatus = + | "revoked" + | "expired" + | "missing_scopes" + | "missing_token" + | "ping_failed" + | "unreachable"; + +function statusDotColor(status: string): string { + switch (status as CredentialStatus) { + case "revoked": + case "expired": + return "var(--system-negative-strong)"; + case "missing_scopes": + case "missing_token": + case "ping_failed": + return "var(--system-mid-strong)"; + case "unreachable": + default: + return "var(--content-disabled)"; + } +} + +function capitalizeStatus(status: string): string { + return status + .split("_") + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(" "); +} + +export interface HomeToolPermissionCardProps { + item: FeedItem; +} + +export function HomeToolPermissionCard({ + item, +}: HomeToolPermissionCardProps) { + const metadata = item.metadata; + const provider = metadata?.provider as string | undefined; + + if (!provider) { + return ( + + {item.title} + + ); + } + + const accountInfo = (metadata?.accountInfo as string) ?? null; + const status = (metadata?.status as string) ?? "unreachable"; + const details = (metadata?.details as string) ?? ""; + const missingScopes = Array.isArray(metadata?.missingScopes) + ? (metadata.missingScopes as string[]) + : []; + + return ( +
+ + {provider} + + + {accountInfo ? ( + + {accountInfo} + + ) : null} + +
+
+ + {details ? ( + + {details} + + ) : null} + + {missingScopes.length > 0 ? ( +
+ + Missing scopes + +
    + {missingScopes.map((scope) => ( +
  • + + {scope} + +
  • + ))} +
+
+ ) : null} +
+ ); +} diff --git a/apps/web/src/domains/home/home-feed-filter-bar.tsx b/apps/web/src/domains/home/home-feed-filter-bar.tsx new file mode 100644 index 00000000000..5f6dfbbeffa --- /dev/null +++ b/apps/web/src/domains/home/home-feed-filter-bar.tsx @@ -0,0 +1,151 @@ +import { + Bell, + Clock, + List, + Mail, + Settings, + ShieldCheck, +} from "lucide-react"; +import { type ComponentType, type SVGProps } from "react"; + +import { Typography } from "@vellum/design-library/components/typography"; +import { cn } from "@vellum/design-library/utils/cn"; +import type { FeedItemCategory } from "./types.js"; + +type LucideIcon = ComponentType>; + +interface CategoryStyle { + icon: LucideIcon; + strong: string; + weak: string; +} + +export const CATEGORY_STYLES: Record = { + security: { + icon: ShieldCheck, + strong: "var(--feed-nudge-strong)", + weak: "var(--feed-nudge-weak)", + }, + email: { + icon: Mail, + strong: "var(--feed-digest-strong)", + weak: "var(--feed-digest-weak)", + }, + scheduling: { + icon: Clock, + strong: "var(--feed-thread-strong)", + weak: "var(--feed-thread-weak)", + }, + background: { + icon: Settings, + strong: "var(--system-info-strong)", + weak: "var(--system-info-weak)", + }, + system: { + icon: Bell, + strong: "var(--feed-digest-strong)", + weak: "var(--feed-digest-weak)", + }, +}; + +export const CATEGORY_ORDER: FeedItemCategory[] = [ + "security", + "email", + "scheduling", + "background", + "system", +]; + +function FilterPill({ + icon: Icon, + iconColor, + bgColor, + isSelected, + label, + onClick, +}: { + icon: LucideIcon; + iconColor: string; + bgColor: string; + isSelected: boolean; + label: string; + onClick: () => void; +}) { + return ( + + ); +} + +export interface HomeFeedFilterBarProps { + categories: FeedItemCategory[]; + activeFilter: FeedItemCategory | null; + onFilterChange: (category: FeedItemCategory | null) => void; +} + +export function HomeFeedFilterBar({ + categories, + activeFilter, + onFilterChange, +}: HomeFeedFilterBarProps) { + const presentCategories = CATEGORY_ORDER.filter((c) => + categories.includes(c), + ); + + if (presentCategories.length === 0) return null; + + return ( +
+ + Filter: + + + onFilterChange(null)} + /> + + {presentCategories.map((category) => { + const style = CATEGORY_STYLES[category]; + return ( + onFilterChange(category)} + /> + ); + })} +
+ ); +} diff --git a/apps/web/src/domains/home/home-feed-list.tsx b/apps/web/src/domains/home/home-feed-list.tsx new file mode 100644 index 00000000000..26c883371d9 --- /dev/null +++ b/apps/web/src/domains/home/home-feed-list.tsx @@ -0,0 +1,89 @@ +import { useState } from "react"; + +import { Typography } from "@vellum/design-library/components/typography"; +import { HomeFeedFilterBar } from "./home-feed-filter-bar.js"; +import { HomeRecapRow } from "./home-recap-row.js"; +import { + excludeHighUrgency, + filterByCategory, + getPresentCategories, + groupByTime, + sortFeedItems, +} from "./utils/feed-utils.js"; +import type { FeedItem, FeedItemCategory, FeedTimeGroup } from "./types.js"; + +const TIME_GROUP_LABELS: Record = { + today: "Today", + yesterday: "Yesterday", + older: "Older", +}; + +export interface HomeFeedListProps { + items: FeedItem[]; + onSelectItem: (item: FeedItem) => void; + onDismissItem: (itemId: string) => void; +} + +export function HomeFeedList({ + items, + onSelectItem, + onDismissItem, +}: HomeFeedListProps) { + const [activeFilter, setActiveFilter] = useState( + null, + ); + + const visible = items.filter((item) => item.status !== "dismissed"); + const eligible = excludeHighUrgency(visible); + const presentCategories = getPresentCategories(eligible); + const filtered = filterByCategory(eligible, activeFilter); + const sorted = sortFeedItems(filtered); + const grouped = groupByTime(sorted); + + return ( +
+ + + {grouped.size === 0 ? ( + + {activeFilter + ? "No items match the selected filter." + : "No items to show."} + + ) : ( + [...grouped.entries()].map(([group, groupItems]) => ( +
+ + {TIME_GROUP_LABELS[group]} + + +
+ {groupItems.map((item) => ( + + ))} +
+
+ )) + )} +
+ ); +} diff --git a/apps/web/src/domains/home/home-greeting-header.tsx b/apps/web/src/domains/home/home-greeting-header.tsx new file mode 100644 index 00000000000..9015b12e3cb --- /dev/null +++ b/apps/web/src/domains/home/home-greeting-header.tsx @@ -0,0 +1,44 @@ +import { SquarePen } from "lucide-react"; + +import { Button } from "@vellum/design-library/components/button"; +import { Typography } from "@vellum/design-library/components/typography"; +import { ChatAvatar } from "@/components/avatar/chat-avatar.js"; +import type { CharacterComponents, CharacterTraits } from "@/domains/avatar/types.js"; + +interface HomeGreetingHeaderProps { + avatarComponents: CharacterComponents | null; + avatarTraits: CharacterTraits | null; + avatarImageUrl: string | null; + onStartNewChat: () => void; +} + +export function HomeGreetingHeader({ + avatarComponents, + avatarTraits, + avatarImageUrl, + onStartNewChat, +}: HomeGreetingHeaderProps) { + return ( +
+
+ + + Here's what's been going on + +
+ + +
+ ); +} diff --git a/apps/web/src/domains/home/home-page.tsx b/apps/web/src/domains/home/home-page.tsx new file mode 100644 index 00000000000..a0f7865ca85 --- /dev/null +++ b/apps/web/src/domains/home/home-page.tsx @@ -0,0 +1,180 @@ +import { useCallback, useState } from "react"; + +import { ResizablePanel } from "@vellum/design-library/components/resizable-panel"; +import { useAssistantAvatar } from "@/domains/avatar/use-assistant-avatar.js"; +import { useIsMobile } from "@/hooks/use-is-mobile.js"; +import { HomeDetailPanel } from "./detail-panel/home-detail-panel.js"; +import { HomeFeedList } from "./home-feed-list.js"; +import { HomeGreetingHeader } from "./home-greeting-header.js"; +import { HomeSuggestionPillBar } from "./home-suggestion-pill-bar.js"; +import { useHomeFeedQuery } from "./hooks/use-home-feed-query.js"; +import { useHomeStateQuery } from "./hooks/use-home-state-query.js"; +import type { FeedItem, FeedItemStatus, SuggestedPrompt } from "./types.js"; + +function HomePageSkeleton() { + return ( +
+
+
+
+
+
+
+
+ +
+
+
+
+
+ +
+ {Array.from({ length: 4 }, (_, i) => ( +
+ ))} +
+
+ ); +} + +export interface HomePageProps { + assistantId: string; + onStartNewChat: () => void; + onOpenConversation: (conversationId: string) => void; + onSuggestionSelected: (prompt: string) => void; +} + +export function HomePage({ + assistantId, + onStartNewChat, + onOpenConversation, + onSuggestionSelected, +}: HomePageProps) { + const isMobile = useIsMobile(); + const avatar = useAssistantAvatar(assistantId); + const feedQuery = useHomeFeedQuery(assistantId); + useHomeStateQuery(assistantId); + + const [selectedItem, setSelectedItem] = useState(null); + + const handleSelectItem = useCallback( + (item: FeedItem) => { + if (item.status === "new") { + setSelectedItem({ ...item, status: "seen" }); + feedQuery.updateStatus.mutate({ itemId: item.id, status: "seen" }); + } else { + setSelectedItem(item); + } + }, + [feedQuery.updateStatus], + ); + + const handleCloseDetail = useCallback(() => { + setSelectedItem(null); + }, []); + + const handleDismissItem = useCallback( + (itemId: string) => { + feedQuery.updateStatus.mutate({ itemId, status: "dismissed" }); + if (selectedItem?.id === itemId) { + setSelectedItem(null); + } + }, + [feedQuery.updateStatus, selectedItem?.id], + ); + + const handleUpdateStatus = useCallback( + (itemId: string, status: FeedItemStatus) => { + feedQuery.updateStatus.mutate({ itemId, status }); + setSelectedItem((prev) => + prev?.id === itemId ? { ...prev, status } : prev, + ); + }, + [feedQuery.updateStatus], + ); + + const handleGoToThread = useCallback( + (conversationId: string) => { + setSelectedItem(null); + onOpenConversation(conversationId); + }, + [onOpenConversation], + ); + + const handleSuggestionSelect = useCallback( + (prompt: SuggestedPrompt) => { + onSuggestionSelected(prompt.prompt); + }, + [onSuggestionSelected], + ); + + const feedContent = feedQuery.isLoading ? ( + + ) : ( + <> + + + + + ); + + if (selectedItem && isMobile) { + return ( +
+ +
+ ); + } + + if (selectedItem && !isMobile) { + return ( + + {feedContent} +
+ } + right={ + + } + /> + ); + } + + return ( +
+
+ {feedContent} +
+
+ ); +} diff --git a/apps/web/src/domains/home/home-recap-row.tsx b/apps/web/src/domains/home/home-recap-row.tsx new file mode 100644 index 00000000000..8c184eb27e1 --- /dev/null +++ b/apps/web/src/domains/home/home-recap-row.tsx @@ -0,0 +1,95 @@ +import { X } from "lucide-react"; +import { useState } from "react"; + +import { cn } from "@vellum/design-library/utils/cn"; +import { CATEGORY_STYLES } from "./home-feed-filter-bar.js"; +import type { FeedItem, FeedItemCategory } from "./types.js"; + +function resolveStyle(category?: FeedItemCategory) { + if (category && CATEGORY_STYLES[category]) { + return CATEGORY_STYLES[category]; + } + return CATEGORY_STYLES.system; +} + +export interface HomeRecapRowProps { + item: FeedItem; + onSelect: (item: FeedItem) => void; + onDismiss: (itemId: string) => void; +} + +export function HomeRecapRow({ item, onSelect, onDismiss }: HomeRecapRowProps) { + const [isHovering, setIsHovering] = useState(false); + const style = resolveStyle(item.category); + const Icon = style.icon; + const isUnread = item.status === "new"; + + return ( + + ); +} diff --git a/apps/web/src/domains/home/home-suggestion-pill-bar.tsx b/apps/web/src/domains/home/home-suggestion-pill-bar.tsx new file mode 100644 index 00000000000..9f4c916f736 --- /dev/null +++ b/apps/web/src/domains/home/home-suggestion-pill-bar.tsx @@ -0,0 +1,94 @@ +import { icons, Sparkles, X } from "lucide-react"; +import { useState } from "react"; + +import { Typography } from "@vellum/design-library/components/typography"; +import type { SuggestedPrompt } from "./types.js"; + +function toPascalCase(s: string): string { + return s.charAt(0).toUpperCase() + s.slice(1); +} + +/** + * Resolves a daemon icon key (bare Lucide camelCase like "mail", "fileText") + * to a lucide-react component. Matches the macOS resolveIcon(_:) algorithm: + * try direct PascalCase lookup, then strip "lucide-" prefix and retry. + */ +function resolveIcon(iconName: string | undefined) { + if (!iconName) return Sparkles; + + const pascal = toPascalCase(iconName); + if (icons[pascal as keyof typeof icons]) { + return icons[pascal as keyof typeof icons]; + } + + const stripped = iconName.replace(/^lucide-/, ""); + if (stripped !== iconName) { + const strippedPascal = toPascalCase(stripped); + if (icons[strippedPascal as keyof typeof icons]) { + return icons[strippedPascal as keyof typeof icons]; + } + } + + return Sparkles; +} + +interface HomeSuggestionPillBarProps { + suggestions: SuggestedPrompt[]; + onSelect: (prompt: SuggestedPrompt) => void; +} + +export function HomeSuggestionPillBar({ + suggestions, + onSelect, +}: HomeSuggestionPillBarProps) { + const [dismissed, setDismissed] = useState(false); + + if (dismissed || suggestions.length === 0) return null; + + const visible = suggestions.slice(0, 3); + + return ( +
+
+ + By the way, have you tried one of these: + + +
+
+ {visible.map((suggestion) => { + const Icon = resolveIcon(suggestion.icon); + return ( + + ); + })} +
+
+ ); +} diff --git a/apps/web/src/domains/home/hooks/use-home-feed-query.ts b/apps/web/src/domains/home/hooks/use-home-feed-query.ts new file mode 100644 index 00000000000..11c50653cd7 --- /dev/null +++ b/apps/web/src/domains/home/hooks/use-home-feed-query.ts @@ -0,0 +1,176 @@ +import { useCallback, useEffect, useRef } from "react"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; + +import { + fetchHomeFeed, + triggerFeedAction, + updateFeedItemStatus, +} from "../api.js"; +import type { + FeedItem, + FeedItemStatus, + HomeFeedResponse, +} from "../types.js"; + +const QUERY_KEY_PREFIX = "home-feed" as const; + +function homeFeedQueryKey(assistantId: string) { + return [QUERY_KEY_PREFIX, assistantId] as const; +} + +/** + * React Query hook for the home feed. + * + * Tracks time-away via `document.visibilitychange` so the daemon can + * personalise the greeting and decide which items to surface. + */ +export function useHomeFeedQuery(assistantId: string | null) { + const queryClient = useQueryClient(); + + const hiddenAtRef = useRef(null); + const timeAwaySecondsRef = useRef(0); + + useEffect(() => { + function handleVisibilityChange() { + if (document.hidden) { + hiddenAtRef.current = Date.now(); + } else if (hiddenAtRef.current !== null) { + const elapsed = Math.round( + (Date.now() - hiddenAtRef.current) / 1000, + ); + timeAwaySecondsRef.current = elapsed; + hiddenAtRef.current = null; + + if (assistantId) { + void queryClient.invalidateQueries({ + queryKey: homeFeedQueryKey(assistantId), + }); + } + } + } + + document.addEventListener("visibilitychange", handleVisibilityChange); + return () => { + document.removeEventListener("visibilitychange", handleVisibilityChange); + }; + }, [assistantId, queryClient]); + + const query = useQuery({ + queryKey: homeFeedQueryKey(assistantId ?? ""), + queryFn: () => + fetchHomeFeed(assistantId!, timeAwaySecondsRef.current), + enabled: Boolean(assistantId), + staleTime: 30_000, + }); + + const updateStatus = useMutation({ + mutationFn: ({ + itemId, + status, + }: { + itemId: string; + status: FeedItemStatus; + }) => updateFeedItemStatus(assistantId!, itemId, status), + + onMutate: async ({ itemId, status }) => { + const key = homeFeedQueryKey(assistantId!); + await queryClient.cancelQueries({ queryKey: key }); + + const previous = queryClient.getQueryData(key); + + queryClient.setQueryData(key, (old) => { + if (!old) return old; + return { + ...old, + items: + status === "dismissed" + ? old.items.filter((item: FeedItem) => item.id !== itemId) + : old.items.map((item: FeedItem) => + item.id === itemId ? { ...item, status } : item, + ), + }; + }); + + return { previous }; + }, + + onError: (_err, _vars, context) => { + if (context?.previous && assistantId) { + queryClient.setQueryData( + homeFeedQueryKey(assistantId), + context.previous, + ); + } + }, + + onSettled: () => { + if (assistantId) { + void queryClient.invalidateQueries({ + queryKey: homeFeedQueryKey(assistantId), + }); + } + }, + }); + + const triggerAction = useMutation({ + mutationFn: ({ + itemId, + actionId, + }: { + itemId: string; + actionId: string; + }) => triggerFeedAction(assistantId!, itemId, actionId), + + onMutate: async ({ itemId }) => { + const key = homeFeedQueryKey(assistantId!); + await queryClient.cancelQueries({ queryKey: key }); + + const previous = queryClient.getQueryData(key); + + queryClient.setQueryData(key, (old) => { + if (!old) return old; + return { + ...old, + items: old.items.map((item: FeedItem) => + item.id === itemId + ? { ...item, status: "acted_on" as const } + : item, + ), + }; + }); + + return { previous }; + }, + + onError: (_err, _vars, context) => { + if (context?.previous && assistantId) { + queryClient.setQueryData( + homeFeedQueryKey(assistantId), + context.previous, + ); + } + }, + + onSettled: () => { + if (assistantId) { + void queryClient.invalidateQueries({ + queryKey: homeFeedQueryKey(assistantId), + }); + } + }, + }); + + const invalidate = useCallback(() => { + if (!assistantId) return; + void queryClient.invalidateQueries({ + queryKey: homeFeedQueryKey(assistantId), + }); + }, [assistantId, queryClient]); + + return { + ...query, + updateStatus, + triggerAction, + invalidate, + }; +} diff --git a/apps/web/src/domains/home/hooks/use-home-state-query.ts b/apps/web/src/domains/home/hooks/use-home-state-query.ts new file mode 100644 index 00000000000..b2ce4dc0270 --- /dev/null +++ b/apps/web/src/domains/home/hooks/use-home-state-query.ts @@ -0,0 +1,38 @@ +import { useCallback } from "react"; +import { useQuery, useQueryClient } from "@tanstack/react-query"; + +import { fetchRelationshipState } from "../api.js"; +import type { RelationshipState } from "../types.js"; + +const QUERY_KEY_PREFIX = "home-state" as const; + +function homeStateQueryKey(assistantId: string) { + return [QUERY_KEY_PREFIX, assistantId] as const; +} + +/** + * React Query hook for the assistant relationship state (tier, facts, + * capabilities, conversation count, etc.). + */ +export function useHomeStateQuery(assistantId: string | null) { + const queryClient = useQueryClient(); + + const query = useQuery({ + queryKey: homeStateQueryKey(assistantId ?? ""), + queryFn: () => fetchRelationshipState(assistantId!), + enabled: Boolean(assistantId), + staleTime: 60_000, + }); + + const invalidate = useCallback(() => { + if (!assistantId) return; + void queryClient.invalidateQueries({ + queryKey: homeStateQueryKey(assistantId), + }); + }, [assistantId, queryClient]); + + return { + ...query, + invalidate, + }; +} diff --git a/apps/web/src/domains/home/types.ts b/apps/web/src/domains/home/types.ts new file mode 100644 index 00000000000..c6630eb3deb --- /dev/null +++ b/apps/web/src/domains/home/types.ts @@ -0,0 +1,108 @@ +export type FeedItemType = "notification"; +export type FeedItemStatus = "new" | "seen" | "acted_on" | "dismissed"; +export type FeedItemUrgency = "low" | "medium" | "high" | "critical"; +export type FeedItemCategory = + | "security" + | "scheduling" + | "background" + | "email" + | "system"; + +export type FeedItemDetailPanelKind = + | "emailDraft" + | "documentPreview" + | "permissionChat" + | "paymentAuth" + | "toolPermission" + | "updatesList"; + +export interface FeedAction { + id: string; + label: string; + prompt: string; +} + +export interface FeedItemDetailPanel { + kind: FeedItemDetailPanelKind; +} + +export interface FeedItem { + id: string; + type: FeedItemType; + priority: number; + title: string; + summary: string; + timestamp: string; + status: FeedItemStatus; + expiresAt?: string; + actions?: FeedAction[]; + urgency?: FeedItemUrgency; + conversationId?: string; + detailPanel?: FeedItemDetailPanel; + category?: FeedItemCategory; + metadata?: Record; + createdAt: string; +} + +export type SuggestedPromptSource = "deterministic" | "assistant"; +export interface SuggestedPrompt { + id: string; + label: string; + icon?: string; + prompt: string; + source: SuggestedPromptSource; +} + +export interface ContextBanner { + greeting: string; + timeAwayLabel: string; + newCount: number; +} + +export interface HomeFeedResponse { + items: FeedItem[]; + updatedAt: string; + contextBanner: ContextBanner; + suggestedPrompts: SuggestedPrompt[]; +} + +export type RelationshipTier = 1 | 2 | 3 | 4; +export type FactCategory = "voice" | "world" | "priorities"; +export type FactConfidence = "strong" | "uncertain"; +export type FactSource = "onboarding" | "inferred"; + +export interface Fact { + id: string; + category: FactCategory; + text: string; + confidence: FactConfidence; + source: FactSource; +} + +export type CapabilityTier = "unlocked" | "next-up" | "earned"; + +export interface Capability { + id: string; + name: string; + description: string; + tier: CapabilityTier; + gate: string; + unlockHint?: string; + ctaLabel?: string; +} + +export interface RelationshipState { + version: number; + assistantId: string; + tier: RelationshipTier; + progressPercent: number; + facts: Fact[]; + capabilities: Capability[]; + conversationCount: number; + hatchedDate: string; + assistantName: string; + userName?: string; + updatedAt: string; +} + +export type FeedTimeGroup = "today" | "yesterday" | "older"; diff --git a/apps/web/src/domains/home/utils/feed-utils.ts b/apps/web/src/domains/home/utils/feed-utils.ts new file mode 100644 index 00000000000..c9b4f8dd5b4 --- /dev/null +++ b/apps/web/src/domains/home/utils/feed-utils.ts @@ -0,0 +1,91 @@ +import type { + FeedItem, + FeedItemCategory, + FeedTimeGroup, +} from "../types.js"; + +/** + * Sort feed items by priority descending, then by createdAt descending. + */ +export function sortFeedItems(items: FeedItem[]): FeedItem[] { + return [...items].sort((a, b) => { + if (a.priority !== b.priority) return b.priority - a.priority; + return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(); + }); +} + +/** + * Bucket items into "today", "yesterday", or "older" based on createdAt + * in the local timezone. Returns a Map preserving order. Empty groups + * are omitted. + */ +export function groupByTime( + items: FeedItem[], +): Map { + const now = new Date(); + const todayStart = new Date( + now.getFullYear(), + now.getMonth(), + now.getDate(), + ); + const yesterdayStart = new Date( + now.getFullYear(), + now.getMonth(), + now.getDate() - 1, + ); + + const groups: Record = { + today: [], + yesterday: [], + older: [], + }; + + for (const item of items) { + const created = new Date(item.createdAt); + if (created >= todayStart) { + groups.today.push(item); + } else if (created >= yesterdayStart) { + groups.yesterday.push(item); + } else { + groups.older.push(item); + } + } + + const result = new Map(); + if (groups.today.length > 0) result.set("today", groups.today); + if (groups.yesterday.length > 0) result.set("yesterday", groups.yesterday); + if (groups.older.length > 0) result.set("older", groups.older); + + return result; +} + +/** + * Filter items by category. If category is null, return all items. + */ +export function filterByCategory( + items: FeedItem[], + category: FeedItemCategory | null, +): FeedItem[] { + if (category === null) return items; + return items.filter((item) => (item.category ?? "system") === category); +} + +/** + * Exclude items with urgency "high" or "critical". + */ +export function excludeHighUrgency(items: FeedItem[]): FeedItem[] { + return items.filter( + (item) => item.urgency !== "high" && item.urgency !== "critical", + ); +} + +/** + * Return deduplicated list of categories present in the items. + */ +export function getPresentCategories(items: FeedItem[]): FeedItemCategory[] { + const categories = new Set(); + for (const item of items) { + categories.add(item.category ?? "system"); + } + return [...categories]; +} diff --git a/apps/web/src/hooks/use-is-mobile.ts b/apps/web/src/hooks/use-is-mobile.ts new file mode 100644 index 00000000000..c0c1ee97758 --- /dev/null +++ b/apps/web/src/hooks/use-is-mobile.ts @@ -0,0 +1,26 @@ +import { useSyncExternalStore } from "react"; + +/** + * Media query that marks viewports narrow enough to swap a sidebar rail + * for an overlay drawer. Mirrors `SidebarPageLayout`'s `md:` breakpoint + * (768px). + */ +export const MOBILE_MEDIA_QUERY = "(max-width: 767px)"; + +function subscribe(onChange: () => void): () => void { + const mql = window.matchMedia(MOBILE_MEDIA_QUERY); + mql.addEventListener("change", onChange); + return () => mql.removeEventListener("change", onChange); +} + +function getSnapshot(): boolean { + return window.matchMedia(MOBILE_MEDIA_QUERY).matches; +} + +/** + * Returns `true` while the viewport matches `MOBILE_MEDIA_QUERY` + * (`max-width: 767px`). + */ +export function useIsMobile(): boolean { + return useSyncExternalStore(subscribe, getSnapshot); +} diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts new file mode 100644 index 00000000000..c1781bf74e2 --- /dev/null +++ b/apps/web/src/lib/api-client.ts @@ -0,0 +1,15 @@ +/** + * Configured HeyAPI client for daemon API requests. + * + * All hand-written API wrappers (home feed, avatar, etc.) import this + * singleton instead of depending on generated code. The generated + * client (from codegen) uses its own inline-bundled instance; this one + * is for endpoints that aren't in the OpenAPI spec. + * + * Reference: https://heyapi.dev/openapi-ts/clients/fetch + */ +import { createClient } from "@hey-api/client-fetch"; + +export const client = createClient({ + baseUrl: "", +}); diff --git a/apps/web/src/lib/api-errors.ts b/apps/web/src/lib/api-errors.ts new file mode 100644 index 00000000000..6060b8ff46b --- /dev/null +++ b/apps/web/src/lib/api-errors.ts @@ -0,0 +1,17 @@ +/** + * Assert that a Response object is present. + * + * HeyAPI client calls return `{ data, error, response }` where `response` + * can be `undefined` when the request never reached the server (e.g. network + * error). This helper narrows the type and throws a descriptive error when + * it is missing. + */ +export function assertHasResponse( + response: Response | undefined, + error: unknown, + fallbackMessage: string, +): asserts response is Response { + if (response) return; + if (error instanceof Error) throw error; + throw new Error(fallbackMessage); +} diff --git a/apps/web/src/lib/query-client.ts b/apps/web/src/lib/query-client.ts new file mode 100644 index 00000000000..6bc5929d29a --- /dev/null +++ b/apps/web/src/lib/query-client.ts @@ -0,0 +1,10 @@ +import { QueryClient } from "@tanstack/react-query"; + +export const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: 1, + refetchOnWindowFocus: false, + }, + }, +}); diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx index 9b9b9bedb0f..32ca78393fc 100644 --- a/apps/web/src/main.tsx +++ b/apps/web/src/main.tsx @@ -1,6 +1,8 @@ +import { QueryClientProvider } from "@tanstack/react-query"; import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import { RouterProvider } from "react-router"; +import { queryClient } from "./lib/query-client.js"; import { router } from "./routes.js"; import "./index.css"; @@ -9,6 +11,8 @@ if (!rootEl) throw new Error("Root element #root not found"); createRoot(rootEl).render( - + + + ); diff --git a/apps/web/src/routes.tsx b/apps/web/src/routes.tsx index b97953c02d4..3a0f2a3a59a 100644 --- a/apps/web/src/routes.tsx +++ b/apps/web/src/routes.tsx @@ -1,11 +1,28 @@ -import { createBrowserRouter } from "react-router"; +import { createBrowserRouter, useNavigate } from "react-router"; import { App } from "./App.js"; import { ChatPage } from "./domains/chat/chat-page.js"; +import { HomePage } from "./domains/home/home-page.js"; import { LibraryPage } from "./domains/library/library-page.js"; import { LibraryDetailPage } from "./domains/library/library-detail-page.js"; import { NotFound } from "./components/not-found.js"; import { SettingsTabPage } from "./domains/settings/settings-tab-page.js"; +function HomePageRoute() { + const navigate = useNavigate(); + return ( + navigate("/")} + onOpenConversation={(conversationId) => + navigate(`/conversations/${conversationId}`) + } + onSuggestionSelected={(prompt) => + navigate(`/?prompt=${encodeURIComponent(prompt)}`) + } + /> + ); +} + export const router = createBrowserRouter( [ { @@ -13,6 +30,10 @@ export const router = createBrowserRouter( element: , children: [ { index: true, element: }, + { + path: "home", + element: , + }, { path: "settings/:tab", element: }, { path: "library", element: }, { path: "library/:appId", element: }, diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index 0fe39019558..6a6a4344ef4 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -8,6 +8,7 @@ "strict": true, "esModuleInterop": true, "skipLibCheck": true, + "preserveSymlinks": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "noEmit": true, diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index c908fd432db..10bac33f6eb 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -10,6 +10,7 @@ export default defineConfig({ alias: { "@/": path.resolve(import.meta.dirname, "src") + "/", }, + preserveSymlinks: true, }, server: { port: 3001,