aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/web/components/graph-dialog.tsx2
-rw-r--r--bun.lock747
-rw-r--r--package.json1
-rw-r--r--packages/memory-graph-playground/.gitignore24
-rw-r--r--packages/memory-graph-playground/README.md47
-rw-r--r--packages/memory-graph-playground/eslint.config.js23
-rw-r--r--packages/memory-graph-playground/index.html13
-rw-r--r--packages/memory-graph-playground/package.json31
-rw-r--r--packages/memory-graph-playground/public/vite.svg1
-rw-r--r--packages/memory-graph-playground/src/App.css119
-rw-r--r--packages/memory-graph-playground/src/App.tsx71
-rw-r--r--packages/memory-graph-playground/src/assets/react.svg1
-rw-r--r--packages/memory-graph-playground/src/index.css31
-rw-r--r--packages/memory-graph-playground/src/main.tsx11
-rw-r--r--packages/memory-graph-playground/tsconfig.app.json28
-rw-r--r--packages/memory-graph-playground/tsconfig.json7
-rw-r--r--packages/memory-graph-playground/tsconfig.node.json26
-rw-r--r--packages/memory-graph-playground/vite.config.ts7
-rw-r--r--packages/memory-graph/.gitignore33
-rw-r--r--packages/memory-graph/.npmignore42
-rw-r--r--packages/memory-graph/README.md224
-rw-r--r--packages/memory-graph/package.json81
-rw-r--r--packages/memory-graph/src/api-types.ts79
-rw-r--r--packages/memory-graph/src/assets/icons.tsx208
-rw-r--r--packages/memory-graph/src/components/canvas-common.css.ts10
-rw-r--r--packages/memory-graph/src/components/graph-canvas.tsx764
-rw-r--r--packages/memory-graph/src/components/legend.css.ts345
-rw-r--r--packages/memory-graph/src/components/legend.tsx275
-rw-r--r--packages/memory-graph/src/components/loading-indicator.css.ts55
-rw-r--r--packages/memory-graph/src/components/loading-indicator.tsx40
-rw-r--r--packages/memory-graph/src/components/memory-graph-wrapper.tsx198
-rw-r--r--packages/memory-graph/src/components/memory-graph.css.ts75
-rw-r--r--packages/memory-graph/src/components/memory-graph.tsx448
-rw-r--r--packages/memory-graph/src/components/navigation-controls.css.ts77
-rw-r--r--packages/memory-graph/src/components/navigation-controls.tsx73
-rw-r--r--packages/memory-graph/src/components/node-detail-panel.css.ts170
-rw-r--r--packages/memory-graph/src/components/node-detail-panel.tsx266
-rw-r--r--packages/memory-graph/src/components/spaces-dropdown.css.ts158
-rw-r--r--packages/memory-graph/src/components/spaces-dropdown.tsx110
-rw-r--r--packages/memory-graph/src/constants.ts100
-rw-r--r--packages/memory-graph/src/hooks/use-documents-query.ts113
-rw-r--r--packages/memory-graph/src/hooks/use-graph-data.ts308
-rw-r--r--packages/memory-graph/src/hooks/use-graph-interactions.ts564
-rw-r--r--packages/memory-graph/src/hooks/use-mobile.ts19
-rw-r--r--packages/memory-graph/src/index.tsx46
-rw-r--r--packages/memory-graph/src/lib/api-client.ts213
-rw-r--r--packages/memory-graph/src/lib/similarity.ts115
-rw-r--r--packages/memory-graph/src/styles/animations.css.ts116
-rw-r--r--packages/memory-graph/src/styles/effects.css.ts120
-rw-r--r--packages/memory-graph/src/styles/global.css.ts71
-rw-r--r--packages/memory-graph/src/styles/index.ts20
-rw-r--r--packages/memory-graph/src/styles/sprinkles.css.ts204
-rw-r--r--packages/memory-graph/src/styles/theme.css.ts245
-rw-r--r--packages/memory-graph/src/types.ts120
-rw-r--r--packages/memory-graph/src/ui/badge.css.ts119
-rw-r--r--packages/memory-graph/src/ui/badge.tsx27
-rw-r--r--packages/memory-graph/src/ui/button.css.ts210
-rw-r--r--packages/memory-graph/src/ui/button.tsx30
-rw-r--r--packages/memory-graph/src/ui/collapsible.tsx33
-rw-r--r--packages/memory-graph/src/ui/glass-effect.css.ts58
-rw-r--r--packages/memory-graph/src/ui/glass-effect.tsx21
-rw-r--r--packages/memory-graph/src/ui/heading.css.ts24
-rw-r--r--packages/memory-graph/src/ui/heading.tsx18
-rw-r--r--packages/memory-graph/tsconfig.json26
-rw-r--r--packages/memory-graph/vite.config.ts56
-rw-r--r--packages/ui/memory-graph/legend.tsx23
-rw-r--r--packages/ui/memory-graph/memory-graph.tsx10
-rw-r--r--packages/ui/memory-graph/types.ts2
68 files changed, 7517 insertions, 435 deletions
diff --git a/apps/web/components/graph-dialog.tsx b/apps/web/components/graph-dialog.tsx
index 2e74b3b1..8849d4ce 100644
--- a/apps/web/components/graph-dialog.tsx
+++ b/apps/web/components/graph-dialog.tsx
@@ -243,4 +243,4 @@ export function GraphDialog() {
)}
</>
)
-} \ No newline at end of file
+}
diff --git a/bun.lock b/bun.lock
index c9a8ca1b..221c4bdc 100644
--- a/bun.lock
+++ b/bun.lock
@@ -13,6 +13,7 @@
"@google/generative-ai": "^0.24.1",
"@hono/zod-validator": "^0.7.1",
"@scalar/hono-api-reference": "^0.9.11",
+ "@vanilla-extract/recipes": "^0.5.7",
"ai": "^5.0.59",
"alchemy": "^0.55.2",
"atmn": "^0.0.16",
@@ -77,21 +78,6 @@
"typescript": "^5.9.2",
},
},
- "apps/raycast-extension": {
- "name": "supermemory",
- "dependencies": {
- "@raycast/api": "^1.103.2",
- "@raycast/utils": "^1.17.0",
- },
- "devDependencies": {
- "@raycast/eslint-config": "^2.0.4",
- "@types/node": "22.13.10",
- "@types/react": "19.0.10",
- "eslint": "^9.22.0",
- "prettier": "^3.5.3",
- "typescript": "^5.8.2",
- },
- },
"apps/web": {
"name": "@repo/web",
"version": "0.1.0",
@@ -210,6 +196,57 @@
"ai-gateway-provider": "^0.0.11",
},
},
+ "packages/memory-graph": {
+ "name": "@supermemory/memory-graph",
+ "version": "0.1.0",
+ "dependencies": {
+ "@emotion/is-prop-valid": "^1.4.0",
+ "@radix-ui/react-collapsible": "^1.1.12",
+ "@radix-ui/react-slot": "^1.2.4",
+ "@tanstack/react-query": "^5.90.7",
+ "@vanilla-extract/css": "^1.17.4",
+ "@vanilla-extract/recipes": "^0.5.7",
+ "@vanilla-extract/sprinkles": "^1.6.5",
+ "lucide-react": "^0.552.0",
+ "motion": "^12.23.24",
+ },
+ "devDependencies": {
+ "@types/react": "^19.2.2",
+ "@types/react-dom": "^19.2.2",
+ "@vanilla-extract/vite-plugin": "^5.1.1",
+ "@vitejs/plugin-react": "^5.1.0",
+ "typescript": "^5.9.3",
+ "vite": "^7.2.1",
+ "vite-plugin-lib-inject-css": "^2.2.2",
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0",
+ },
+ },
+ "packages/memory-graph-playground": {
+ "name": "memory-graph-playground",
+ "version": "0.0.0",
+ "dependencies": {
+ "@supermemory/memory-graph": "workspace:*",
+ "react": "^19.1.1",
+ "react-dom": "^19.1.1",
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.36.0",
+ "@types/node": "^24.6.0",
+ "@types/react": "^19.1.16",
+ "@types/react-dom": "^19.1.9",
+ "@vitejs/plugin-react": "^5.0.4",
+ "eslint": "^9.36.0",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-react-refresh": "^0.4.22",
+ "globals": "^16.4.0",
+ "typescript": "~5.9.3",
+ "typescript-eslint": "^8.45.0",
+ "vite": "^7.1.7",
+ },
+ },
"packages/tools": {
"name": "@supermemory/tools",
"version": "1.3.1",
@@ -319,25 +356,25 @@
"@ark/util": ["@ark/[email protected]", "", {}, "sha512-/BtnX7oCjNkxi2vi6y1399b+9xd1jnCrDYhZ61f0a+3X8x8DxlK52VgEEzyuC2UQMPACIfYrmHkhD3lGt2GaMA=="],
- "@ast-grep/napi": ["@ast-grep/[email protected]", "", { "optionalDependencies": { "@ast-grep/napi-darwin-arm64": "0.35.0", "@ast-grep/napi-darwin-x64": "0.35.0", "@ast-grep/napi-linux-arm64-gnu": "0.35.0", "@ast-grep/napi-linux-arm64-musl": "0.35.0", "@ast-grep/napi-linux-x64-gnu": "0.35.0", "@ast-grep/napi-linux-x64-musl": "0.35.0", "@ast-grep/napi-win32-arm64-msvc": "0.35.0", "@ast-grep/napi-win32-ia32-msvc": "0.35.0", "@ast-grep/napi-win32-x64-msvc": "0.35.0" } }, "sha512-3ucaaSxV6fxXoqHrE/rxAvP1THnDdY5jNzGlnvx+JvnY9C/dSRKc0jlRMRz59N3El572+/yNRUUpAV1T9aBJug=="],
+ "@ast-grep/napi": ["@ast-grep/[email protected]", "", { "optionalDependencies": { "@ast-grep/napi-darwin-arm64": "0.36.3", "@ast-grep/napi-darwin-x64": "0.36.3", "@ast-grep/napi-linux-arm64-gnu": "0.36.3", "@ast-grep/napi-linux-arm64-musl": "0.36.3", "@ast-grep/napi-linux-x64-gnu": "0.36.3", "@ast-grep/napi-linux-x64-musl": "0.36.3", "@ast-grep/napi-win32-arm64-msvc": "0.36.3", "@ast-grep/napi-win32-ia32-msvc": "0.36.3", "@ast-grep/napi-win32-x64-msvc": "0.36.3" } }, "sha512-ExypohE8L7FvKBHxu7UpwcV9XVfyS+AqNZKyKIfxYwJyD9l7Gw6pmMYd7J2uopJsPEIUf44/emEFds6nFUx/dw=="],
- "@ast-grep/napi-darwin-arm64": ["@ast-grep/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-T+MN4Oinc+sXjXCIHzfxDDWY7r2pKgPxM6zVeVlkMTrJV2mJtyKYBIS+CABhRM6kflps2T2I6l4DGaKV/8Ym9w=="],
+ "@ast-grep/napi-darwin-arm64": ["@ast-grep/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-uM0Hrm5gcHqaBL64ktmPBFMTorTlPKWsUfi0E2Cg09GJfeYWvZmicCqgd7qVtjURmQvFQdb4JSqHIkJvws6Uqw=="],
- "@ast-grep/napi-darwin-x64": ["@ast-grep/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-pEYiN6JI1HY2uWhMYJ9+3yIMyVYKuYdFzeD+dL7odA3qzK0o9N9AM3/NOt4ynU2EhufaWCJr0P5NoQ636qN6MQ=="],
+ "@ast-grep/napi-darwin-x64": ["@ast-grep/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-wEMeQw8lRL66puG2m8m0kDRQDtubygj59HA/cmut2V5SPx/13BN3wuEk6JPv97gqGUCUGhG2+5Z6UZ/Ll2q01Q=="],
- "@ast-grep/napi-linux-arm64-gnu": ["@ast-grep/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-NBuzQngABGKz7lhG08IQb+7nPqUx81Ol37xmS3ZhVSdSgM0mtp93rCbgFTkJcAFE8IMfCHQSg7G4g0Iotz4ABQ=="],
+ "@ast-grep/napi-linux-arm64-gnu": ["@ast-grep/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-sMsTMaUjW7SM8KPbLviCSBuM4zgJcwvie1yZI92HKSlFzC7ABe7X7UvyUREB+JwqccDVEL5yOJAjqB8eFSCizw=="],
- "@ast-grep/napi-linux-arm64-musl": ["@ast-grep/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-1EcvHPwyWpCL/96LuItBYGfeI5FaMTRvL+dHbO/hL5q1npqbb5qn+ppJwtNOjTPz8tayvgggxVk9T4C2O7taYA=="],
+ "@ast-grep/napi-linux-arm64-musl": ["@ast-grep/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-2XRmNYuovZu0Pa4J3or4PKMkQZnXXfpVcCrPwWB/2ytX7XUo+TWLgYE8rPVnJOyw5zujkveFb0XUrro9mQgLzw=="],
- "@ast-grep/napi-linux-x64-gnu": ["@ast-grep/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-FDzNdlqmQnsiWXhnLxusw5AOfEcEM+5xtmrnAf3SBRFr86JyWD9qsynnFYC2pnP9hlMfifNH2TTmMpyGJW49Xw=="],
+ "@ast-grep/napi-linux-x64-gnu": ["@ast-grep/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-mTwPRbBi1feGqR2b5TWC5gkEDeRi8wfk4euF5sKNihfMGHj6pdfINHQ3QvLVO4C7z0r/wgWLAvditFA0b997dg=="],
- "@ast-grep/napi-linux-x64-musl": ["@ast-grep/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-wlmndjfBafT8u5p4DBnoRQyoCSGNuVSz7rT3TqhvlHcPzUouRWMn95epU9B1LNLyjXvr9xHeRjSktyCN28w57Q=="],
+ "@ast-grep/napi-linux-x64-musl": ["@ast-grep/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-tMGPrT+zuZzJK6n1cD1kOii7HYZE9gUXjwtVNE/uZqXEaWP6lmkfoTMbLjnxEe74VQbmaoDGh1/cjrDBnqC6Uw=="],
- "@ast-grep/napi-win32-arm64-msvc": ["@ast-grep/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-gkhJeYc4rrZLX2icLxalPikTLMR57DuIYLwLr9g+StHYXIsGHrbfrE6Nnbdd8Izfs34ArFCrcwdaMrGlvOPSeg=="],
+ "@ast-grep/napi-win32-arm64-msvc": ["@ast-grep/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-7pFyr9+dyV+4cBJJ1I57gg6PDXP3GBQeVAsEEitzEruxx4Hb4cyNro54gGtlsS+6ty+N0t004tPQxYO2VrsPIg=="],
- "@ast-grep/napi-win32-ia32-msvc": ["@ast-grep/[email protected]", "", { "os": "win32", "cpu": "ia32" }, "sha512-OdUuRa3chHCZ65y+qALfkUjz0W0Eg21YZ9TyPquV5why07M6HAK38mmYGzLxFH6294SvRQhs+FA/rAfbKeH0jA=="],
+ "@ast-grep/napi-win32-ia32-msvc": ["@ast-grep/[email protected]", "", { "os": "win32", "cpu": "ia32" }, "sha512-MPAgccH9VscRaFuEBMzDGPS+3c4cKNVGIVJ7WSNa1nZtLQ0eFEaPJ7pyDnCezgVSxfNFVYBvKyyF/vcm7Qc9+A=="],
- "@ast-grep/napi-win32-x64-msvc": ["@ast-grep/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-pcQRUHqbroTN1oQ56V982a7IZTUUySQYWa2KEyksiifHGuBuitlzcyzFGjT96ThcqD9XW0UVJMvpoF2Qjh006Q=="],
+ "@ast-grep/napi-win32-x64-msvc": ["@ast-grep/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-TIVtuSbXhty9kaSEfr4ULWx5PAuUeGgUkFaR60lmOs7sGTWgpig+suwKfTmevoAblFknCW/aMHOwziwJoUZA6A=="],
"@asyncapi/parser": ["@asyncapi/[email protected]", "", { "dependencies": { "@asyncapi/specs": "^6.8.0", "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", "@stoplight/json": "3.21.0", "@stoplight/json-ref-readers": "^1.2.2", "@stoplight/json-ref-resolver": "^3.1.5", "@stoplight/spectral-core": "^1.18.3", "@stoplight/spectral-functions": "^1.7.2", "@stoplight/spectral-parsers": "^1.0.2", "@stoplight/spectral-ref-resolver": "^1.0.3", "@stoplight/types": "^13.12.0", "@types/json-schema": "^7.0.11", "@types/urijs": "^1.19.19", "ajv": "^8.17.1", "ajv-errors": "^3.0.0", "ajv-formats": "^2.1.1", "avsc": "^5.7.5", "js-yaml": "^4.1.0", "jsonpath-plus": "^10.0.0", "node-fetch": "2.6.7" } }, "sha512-Sxn74oHiZSU6+cVeZy62iPZMFMvKp4jupMFHelSICCMw1qELmUHPvuZSr+ZHDmNGgHcEpzJM5HN02kR7T4g+PQ=="],
@@ -613,6 +650,12 @@
"@emnapi/wasi-threads": ["@emnapi/[email protected]", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
+ "@emotion/hash": ["@emotion/[email protected]", "", {}, "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="],
+
+ "@emotion/is-prop-valid": ["@emotion/[email protected]", "", { "dependencies": { "@emotion/memoize": "^0.9.0" } }, "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw=="],
+
+ "@emotion/memoize": ["@emotion/[email protected]", "", {}, "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="],
+
"@esbuild-kit/core-utils": ["@esbuild-kit/[email protected]", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="],
"@esbuild-kit/esm-loader": ["@esbuild-kit/[email protected]", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="],
@@ -943,14 +986,6 @@
"@npmcli/promise-spawn": ["@npmcli/[email protected]", "", { "dependencies": { "which": "^3.0.0" } }, "sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg=="],
- "@oclif/core": ["@oclif/[email protected]", "", { "dependencies": { "ansi-escapes": "^4.3.2", "ansis": "^3.17.0", "clean-stack": "^3.0.1", "cli-spinners": "^2.9.2", "debug": "^4.4.3", "ejs": "^3.1.10", "get-package-type": "^0.1.0", "indent-string": "^4.0.0", "is-wsl": "^2.2.0", "lilconfig": "^3.1.3", "minimatch": "^9.0.5", "semver": "^7.7.3", "string-width": "^4.2.3", "supports-color": "^8", "tinyglobby": "^0.2.14", "widest-line": "^3.1.0", "wordwrap": "^1.0.0", "wrap-ansi": "^7.0.0" } }, "sha512-jteNUQKgJHLHFbbz806aGZqf+RJJ7t4gwF4MYa8fCwCxQ8/klJNWc0MvaJiBebk7Mc+J39mdlsB4XraaCKznFw=="],
-
- "@oclif/plugin-autocomplete": ["@oclif/[email protected]", "", { "dependencies": { "@oclif/core": "^4", "ansis": "^3.16.0", "debug": "^4.4.1", "ejs": "^3.1.10" } }, "sha512-OwAZNnSpuDjKyhAwoOJkFWxGswPFKBB4hpNIMsj6PUtbKwGBPmD+2wGGPgTsDioVwLmUELSb2bZ+1dxHfvXmvg=="],
-
- "@oclif/plugin-help": ["@oclif/[email protected]", "", { "dependencies": { "@oclif/core": "^4" } }, "sha512-ZMcQTsHaiCEOZIRZoynUQ+98fyM1Adoqx4LbOgYWRVKXKbavHPCZKm6F+/y0GpWscXVoeGnvJO6GIBsigrqaSA=="],
-
- "@oclif/plugin-not-found": ["@oclif/[email protected]", "", { "dependencies": { "@inquirer/prompts": "^7.9.0", "@oclif/core": "^4.8.0", "ansis": "^3.17.0", "fast-levenshtein": "^3.0.0" } }, "sha512-CRcqHGdcEL4l5cls5F9FvwKt04LkdG7WyFozOu2vP1/3w34S29zbw8Tx1gAzfBZDDme5ChSaqFXU5qbTLx5yYQ=="],
-
"@octokit/auth-token": ["@octokit/[email protected]", "", {}, "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw=="],
"@octokit/core": ["@octokit/[email protected]", "", { "dependencies": { "@octokit/auth-token": "^5.0.0", "@octokit/graphql": "^8.2.2", "@octokit/request": "^9.2.3", "@octokit/request-error": "^6.1.8", "@octokit/types": "^14.0.0", "before-after-hook": "^3.0.2", "universal-user-agent": "^7.0.0" } }, "sha512-kIU8SLQkYWGp3pVKiYzA5OSaNF5EE03P/R8zEmmrG6XwOg5oBjXyQVVIauQ0dgau4zYhpZEhJrvIYt6oM+zZZA=="],
@@ -1191,14 +1226,6 @@
"@radix-ui/rect": ["@radix-ui/[email protected]", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
- "@raycast/api": ["@raycast/[email protected]", "", { "dependencies": { "@oclif/core": "^4.5.4", "@oclif/plugin-autocomplete": "^3.2.35", "@oclif/plugin-help": "^6.2.33", "@oclif/plugin-not-found": "^3.2.68", "@types/node": "22.13.10", "@types/react": "19.0.10", "esbuild": "^0.25.10", "react": "19.0.0" }, "peerDependencies": { "react-devtools": "6.1.1" }, "optionalPeers": ["react-devtools"], "bin": { "ray": "bin/run.js" } }, "sha512-x34deBLt8BNCIztMjsYPU8eGz4xFp444PJzxybIvDt86ZCk6phlTLD11qtkyZKOURFIoncebNDVTJn5KdO09nA=="],
-
- "@raycast/eslint-config": ["@raycast/[email protected]", "", { "dependencies": { "@eslint/js": "^9.36.0", "@raycast/eslint-plugin": "^2.1.1", "eslint-config-prettier": "^10.1.8", "globals": "^16.4.0", "typescript-eslint": "^8.45.0" }, "peerDependencies": { "eslint": ">=8.23.0", "prettier": ">=2", "typescript": ">=4" } }, "sha512-W0kxF+FJ+BYQn0EKIV739j2ZrHEtjo/LclsoZgUWg3t364Dq75XKcjqYFYx+59/DBaamY0amdajlfuDAf6veAg=="],
-
- "@raycast/eslint-plugin": ["@raycast/[email protected]", "", { "dependencies": { "@typescript-eslint/utils": "^8.26.1" }, "peerDependencies": { "eslint": ">=8.23.0" } }, "sha512-r2gs8uIlNp6I2mLOyN/kReGlvigzEeuyQPl4yw7nwLy8Zxjfjhg8txMViaBux8juBWBxbSWq/IfW6ZA50oeOHQ=="],
-
- "@raycast/utils": ["@raycast/[email protected]", "", { "dependencies": { "cross-fetch": "^3.1.6", "dequal": "^2.0.3", "object-hash": "^3.0.0", "signal-exit": "^4.0.2", "stream-chain": "^2.2.5", "stream-json": "^1.8.0" }, "peerDependencies": { "@raycast/api": ">=1.69.0" } }, "sha512-/udUGcTZCgZZwzesmjBkqG5naQZTD/ZLHbqRwkWcF+W97vf9tr9raxKyQjKsdZ17OVllw2T3sHBQsVUdEmCm2g=="],
-
"@react-email/render": ["@react-email/[email protected]", "", { "dependencies": { "html-to-text": "^9.0.5", "prettier": "^3.5.3", "react-promise-suspense": "^0.3.4" }, "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-RnRehYN3v9gVlNMehHPHhyp2RQo7+pSkHDtXPvg3s0GbzM9SQMW4Qrf8GRNvtpLC4gsI+Wt0VatNRUFqjvevbw=="],
"@react-hook/debounce": ["@react-hook/[email protected]", "", { "dependencies": { "@react-hook/latest": "^1.0.2" }, "peerDependencies": { "react": ">=16.8" } }, "sha512-ir/kPrSfAzY12Gre0sOHkZ2rkEmM4fS5M5zFxCi4BnCeXh2nvx9Ujd+U4IGpKCuPA+EQD0pg1eK2NGLvfWejag=="],
@@ -1267,7 +1294,7 @@
"@rolldown/binding-win32-x64-msvc": ["@rolldown/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-UlpxKmFdik0Y2VjZrgUCgoYArZJiZllXgIipdBRV1hw6uK45UbQabSTW6Kp6enuOu7vouYWftwhuxfpE8J2JAg=="],
- "@rolldown/pluginutils": ["@rolldown/[email protected]", "", {}, "sha512-ycMEPrS3StOIeb87BT3/+bu+blEtyvwQ4zmo2IcJQy0Rd1DAAhKksA0iUZ3MYSpJtjlPhg0Eo6mvVS6ggPhRbw=="],
+ "@rolldown/pluginutils": ["@rolldown/[email protected]", "", {}, "sha512-5Uxg7fQUCmfhax7FJke2+8B6cqgeUJUD9o2uXIKXhD+mG0mL6NObmVoi9wXEU1tY89mZKgAYA6fTbftx3q2ZPQ=="],
"@rollup/plugin-commonjs": ["@rollup/[email protected]", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", "estree-walker": "^2.0.2", "fdir": "^6.2.0", "is-reference": "1.2.1", "magic-string": "^0.30.3", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^2.68.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA=="],
@@ -1543,6 +1570,8 @@
"@supermemory/ai-sdk": ["@supermemory/ai-sdk@workspace:packages/ai-sdk"],
+ "@supermemory/memory-graph": ["@supermemory/memory-graph@workspace:packages/memory-graph"],
+
"@supermemory/tools": ["@supermemory/tools@workspace:packages/tools"],
"@swc/core": ["@swc/[email protected]", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.24" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.13.5", "@swc/core-darwin-x64": "1.13.5", "@swc/core-linux-arm-gnueabihf": "1.13.5", "@swc/core-linux-arm64-gnu": "1.13.5", "@swc/core-linux-arm64-musl": "1.13.5", "@swc/core-linux-x64-gnu": "1.13.5", "@swc/core-linux-x64-musl": "1.13.5", "@swc/core-win32-arm64-msvc": "1.13.5", "@swc/core-win32-ia32-msvc": "1.13.5", "@swc/core-win32-x64-msvc": "1.13.5" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ=="],
@@ -1607,7 +1636,7 @@
"@tailwindcss/typography": ["@tailwindcss/[email protected]", "", { "dependencies": { "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg=="],
- "@tailwindcss/vite": ["@tailwindcss/[email protected]", "", { "dependencies": { "@tailwindcss/node": "4.1.14", "@tailwindcss/oxide": "4.1.14", "tailwindcss": "4.1.14" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-BoFUoU0XqgCUS1UXWhmDJroKKhNXeDzD7/XwabjkDIAbMnc4ULn5e2FuEuBbhZ6ENZoSYzKlzvZ44Yr6EUDUSA=="],
+ "@tailwindcss/vite": ["@tailwindcss/[email protected]", "", { "dependencies": { "@tailwindcss/node": "4.1.16", "@tailwindcss/oxide": "4.1.16", "tailwindcss": "4.1.16" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-bbguNBcDxsRmi9nnlWJxhfDWamY3lmcyACHcdO1crxfzuLpOhHLLtEIN/nCbbAtj5rchUgQD17QVAKi1f7IsKg=="],
"@tanstack/devtools-event-client": ["@tanstack/[email protected]", "", {}, "sha512-gkvph/YMCFUfAca75EsJBJnhbKitDGix7vdEcT/3lAV+eyGSv+uECYG43apVQN4yLJKnV6mzcNvGzOhDhb72gg=="],
@@ -1835,7 +1864,23 @@
"@ungap/structured-clone": ["@ungap/[email protected]", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
- "@vitejs/plugin-react": ["@vitejs/[email protected]", "", { "dependencies": { "@babel/core": "^7.28.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.38", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-La0KD0vGkVkSk6K+piWDKRUyg8Rl5iAIKRMH0vMJI0Eg47bq1eOxmoObAaQG37WMW9MSyk7Cs8EIWwJC1PtzKA=="],
+ "@vanilla-extract/babel-plugin-debug-ids": ["@vanilla-extract/[email protected]", "", { "dependencies": { "@babel/core": "^7.23.9" } }, "sha512-MeDWGICAF9zA/OZLOKwhoRlsUW+fiMwnfuOAqFVohL31Agj7Q/RBWAYweqjHLgFBCsdnr6XIfwjJnmb2znEWxw=="],
+
+ "@vanilla-extract/compiler": ["@vanilla-extract/[email protected]", "", { "dependencies": { "@vanilla-extract/css": "^1.17.4", "@vanilla-extract/integration": "^8.0.4", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", "vite-node": "^3.2.2" } }, "sha512-KZ67DZQu58dMo7Jv4PNMPG5TbMOXB68xxVYV2cRJvUdPeiOmX0FOaPgEsYBAZUgd/oLEx4IyV0AvlvsxJ1akfQ=="],
+
+ "@vanilla-extract/css": ["@vanilla-extract/[email protected]", "", { "dependencies": { "@emotion/hash": "^0.9.0", "@vanilla-extract/private": "^1.0.9", "css-what": "^6.1.0", "cssesc": "^3.0.0", "csstype": "^3.0.7", "dedent": "^1.5.3", "deep-object-diff": "^1.1.9", "deepmerge": "^4.2.2", "lru-cache": "^10.4.3", "media-query-parser": "^2.0.2", "modern-ahocorasick": "^1.0.0", "picocolors": "^1.0.0" } }, "sha512-m3g9nQDWPtL+sTFdtCGRMI1Vrp86Ay4PBYq1Bo7Bnchj5ElNtAJpOqD+zg+apthVA4fB7oVpMWNjwpa6ElDWFQ=="],
+
+ "@vanilla-extract/integration": ["@vanilla-extract/[email protected]", "", { "dependencies": { "@babel/core": "^7.23.9", "@babel/plugin-syntax-typescript": "^7.23.3", "@vanilla-extract/babel-plugin-debug-ids": "^1.2.2", "@vanilla-extract/css": "^1.17.4", "dedent": "^1.5.3", "esbuild": "npm:esbuild@>=0.17.6 <0.26.0", "eval": "0.1.8", "find-up": "^5.0.0", "javascript-stringify": "^2.0.1", "mlly": "^1.4.2" } }, "sha512-cmOb7tR+g3ulKvFtSbmdw3YUyIS1d7MQqN+FcbwNhdieyno5xzUyfDCMjeWJhmCSMvZ6WlinkrOkgs6SHB+FRg=="],
+
+ "@vanilla-extract/private": ["@vanilla-extract/[email protected]", "", {}, "sha512-gT2jbfZuaaCLrAxwXbRgIhGhcXbRZCG3v4TTUnjw0EJ7ArdBRxkq4msNJkbuRkCgfIK5ATmprB5t9ljvLeFDEA=="],
+
+ "@vanilla-extract/recipes": ["@vanilla-extract/[email protected]", "", { "peerDependencies": { "@vanilla-extract/css": "^1.0.0" } }, "sha512-Fvr+htdyb6LVUu+PhH61UFPhwkjgDEk8L4Zq9oIdte42sntpKrgFy90MyTRtGwjVALmrJ0pwRUVr8UoByYeW8A=="],
+
+ "@vanilla-extract/sprinkles": ["@vanilla-extract/[email protected]", "", { "peerDependencies": { "@vanilla-extract/css": "^1.0.0" } }, "sha512-HOYidLONR/SeGk8NBAeI64I4gYdsMX9vJmniL13ZcLVwawyK0s2GUENEAcGA+GYLIoeyQB61UqmhqPodJry7zA=="],
+
+ "@vanilla-extract/vite-plugin": ["@vanilla-extract/[email protected]", "", { "dependencies": { "@vanilla-extract/compiler": "^0.3.1", "@vanilla-extract/integration": "^8.0.4" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-Nd1worqkHrd8XED4ZAA7Wmkd3pCqCwpmzCBVF8v6T1BSLHGXQE5HYflVgygw0CsIAbFRMS6zQBIk4F4/r/YKIw=="],
+
+ "@vitejs/plugin-react": ["@vitejs/[email protected]", "", { "dependencies": { "@babel/core": "^7.28.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.43", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-4LuWrg7EKWgQaMJfnN+wcmbAW+VSsCmqGohftWjuct47bv8uE4n/nPpq4XjJPsxgq00GGG5J8dvBczp8uxScew=="],
"@vitest/expect": ["@vitest/[email protected]", "", { "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=="],
@@ -1945,7 +1990,7 @@
"ansi-colors": ["[email protected]", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="],
- "ansi-escapes": ["[email protected]", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
+ "ansi-escapes": ["[email protected]", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-Zhl0ErHcSRUaVfGUeUdDuLgpkEo8KIFjB4Y9uAc46ScOpdDiU1Dbyplh7qWJeJ/ZHpbyMSM26+X3BySgnIz40Q=="],
"ansi-regex": ["[email protected]", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
@@ -2157,7 +2202,7 @@
"class-variance-authority": ["[email protected]", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
- "clean-stack": ["[email protected]", "", { "dependencies": { "escape-string-regexp": "4.0.0" } }, "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg=="],
+ "clean-stack": ["[email protected]", "", { "dependencies": { "escape-string-regexp": "5.0.0" } }, "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg=="],
"cli-boxes": ["[email protected]", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="],
@@ -2249,8 +2294,6 @@
"cosmiconfig": ["[email protected]", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg=="],
- "cross-fetch": ["[email protected]", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q=="],
-
"cross-spawn": ["[email protected]", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"css-select": ["[email protected]", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="],
@@ -2367,6 +2410,8 @@
"deep-is": ["[email protected]", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
+ "deep-object-diff": ["[email protected]", "", {}, "sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA=="],
+
"deepmerge": ["[email protected]", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
"default-browser": ["[email protected]", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg=="],
@@ -2469,8 +2514,6 @@
"efrt": ["[email protected]", "", {}, "sha512-/RInbCy1d4P6Zdfa+TMVsf/ufZVotat5hCw3QXmWtjU+3pFEOvOQ7ibo3aIxyCJw2leIeAMjmPj+1SLJiCpdrQ=="],
- "ejs": ["[email protected]", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="],
-
"electron-to-chromium": ["[email protected]", "", {}, "sha512-nxkiyuqAn4MJ1QbobwqJILiDtu/jk14hEAWaMiJmNPh1Z+jqoFlBFZjdXwLWGeVSeu9hGLg6+2G9yJaW8rBIFA=="],
"embla-carousel": ["[email protected]", "", {}, "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA=="],
@@ -2549,7 +2592,9 @@
"eslint": ["[email protected]", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.1", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g=="],
- "eslint-config-prettier": ["[email protected]", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w=="],
+ "eslint-plugin-react-hooks": ["[email protected]", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="],
+
+ "eslint-plugin-react-refresh": ["[email protected]", "", { "peerDependencies": { "eslint": ">=8.40" } }, "sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w=="],
"eslint-scope": ["[email protected]", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
@@ -2583,6 +2628,8 @@
"etag": ["[email protected]", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
+ "eval": ["[email protected]", "", { "dependencies": { "@types/node": "*", "require-like": ">= 0.1.1" } }, "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw=="],
+
"event-target-shim": ["[email protected]", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
"eventemitter3": ["[email protected]", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="],
@@ -2633,8 +2680,6 @@
"fast-xml-parser": ["[email protected]", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
- "fastest-levenshtein": ["[email protected]", "", {}, "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg=="],
-
"fastq": ["[email protected]", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
"fault": ["[email protected]", "", { "dependencies": { "format": "^0.2.0" } }, "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ=="],
@@ -2655,8 +2700,6 @@
"file-type": ["[email protected]", "", { "dependencies": { "@tokenizer/inflate": "^0.2.7", "strtok3": "^10.2.2", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" } }, "sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg=="],
- "filelist": ["[email protected]", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q=="],
-
"filesize": ["[email protected]", "", {}, "sha512-mYJ/qXKvREuO0uH8LTQJ6v7GsUvVOguqxg2VTwQUkyTPXXRRWPdjuUPVqdBrJQhvci48OHlNGRnux+Slr2Rnvw=="],
"fill-range": ["[email protected]", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
@@ -2729,8 +2772,6 @@
"get-nonce": ["[email protected]", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
- "get-package-type": ["[email protected]", "", {}, "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q=="],
-
"get-port": ["[email protected]", "", {}, "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ=="],
"get-port-please": ["[email protected]", "", {}, "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A=="],
@@ -2905,7 +2946,7 @@
"imurmurhash": ["[email protected]", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
- "indent-string": ["[email protected]", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="],
+ "indent-string": ["[email protected]", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="],
"inherits": ["[email protected]", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
@@ -3051,7 +3092,7 @@
"jackspeak": ["[email protected]", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
- "jake": ["[email protected]", "", { "dependencies": { "async": "^3.2.6", "filelist": "^1.0.4", "picocolors": "^1.1.1" }, "bin": { "jake": "bin/cli.js" } }, "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA=="],
+ "javascript-stringify": ["[email protected]", "", {}, "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg=="],
"jest-worker": ["[email protected]", "", { "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" } }, "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg=="],
@@ -3147,6 +3188,8 @@
"lightningcss": ["[email protected]", "", { "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=="],
+ "lightningcss-android-arm64": ["[email protected]", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="],
+
"lightningcss-darwin-arm64": ["[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="],
"lightningcss-darwin-x64": ["[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="],
@@ -3269,8 +3312,12 @@
"mdast-util-to-string": ["[email protected]", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="],
+ "media-query-parser": ["[email protected]", "", { "dependencies": { "@babel/runtime": "^7.12.5" } }, "sha512-1N4qp+jE0pL5Xv4uEcwVUhIkwdUO3S/9gML90nqKA7v7FcOS5vUtatfzok9S9U1EJU8dHWlcv95WLnKmmxZI9w=="],
+
"media-typer": ["[email protected]", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="],
+ "memory-graph-playground": ["memory-graph-playground@workspace:packages/memory-graph-playground"],
+
"merge-descriptors": ["[email protected]", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="],
"merge-stream": ["[email protected]", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="],
@@ -3389,6 +3436,8 @@
"mnemonist": ["[email protected]", "", { "dependencies": { "obliterator": "^1.6.1" } }, "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw=="],
+ "modern-ahocorasick": ["[email protected]", "", {}, "sha512-sEKPVl2rM+MNVkGQt3ChdmD8YsigmXdn5NifZn6jiwn9LRJpWm8F3guhaqrJT/JOat6pwpbXEk6kv+b9DMIjsQ=="],
+
"module-details-from-path": ["[email protected]", "", {}, "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w=="],
"morgan": ["[email protected]", "", { "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", "depd": "~2.0.0", "on-finished": "~2.3.0", "on-headers": "~1.1.0" } }, "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A=="],
@@ -3751,7 +3800,7 @@
"react-reconciler": ["[email protected]", "", { "dependencies": { "scheduler": "^0.25.0" }, "peerDependencies": { "react": "^19.0.0" } }, "sha512-7Ob7Z+URmesIsIVRjnLoDGwBEG/tVitidU0nMsqX/eeJaLY89RISO/10ERe0MqmzuKUUB1rmY+h1itMbUHg9BQ=="],
- "react-refresh": ["[email protected]", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="],
+ "react-refresh": ["[email protected]", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="],
"react-remove-scroll": ["[email protected]", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="],
@@ -3841,6 +3890,8 @@
"require-in-the-middle": ["[email protected]", "", { "dependencies": { "debug": "^4.3.5", "module-details-from-path": "^1.0.3", "resolve": "^1.22.8" } }, "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ=="],
+ "require-like": ["[email protected]", "", {}, "sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A=="],
+
"resend": ["[email protected]", "", { "dependencies": { "@react-email/render": "1.1.2" } }, "sha512-R8eBOFQDO6dzRTDmaMEdpqrkmgSjPpVXt4nGfWsZdYOet0kqra0xgbvTES6HmCriZEXbmGk3e0DiGIaLFTFSHA=="],
"resolve": ["[email protected]", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="],
@@ -3969,7 +4020,7 @@
"siginfo": ["[email protected]", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
- "signal-exit": ["[email protected]", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
+ "signal-exit": ["[email protected]", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
"simple-eval": ["[email protected]", "", { "dependencies": { "jsep": "^1.3.6" } }, "sha512-LH7FpTAkeD+y5xQC4fzS+tFtaNlvt3Ib1zKzvhjv/Y+cioV4zIuw4IZr2yhRLu67CWL7FR9/6KXKnjRoZTvGGQ=="],
@@ -4045,10 +4096,6 @@
"stoppable": ["[email protected]", "", {}, "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw=="],
- "stream-chain": ["[email protected]", "", {}, "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA=="],
-
- "stream-json": ["[email protected]", "", { "dependencies": { "stream-chain": "^2.2.5" } }, "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw=="],
-
"streamdown": ["[email protected]", "", { "dependencies": { "clsx": "^2.1.1", "harden-react-markdown": "^1.0.5", "katex": "^0.16.22", "lucide-react": "^0.542.0", "marked": "^16.2.1", "mermaid": "^11.11.0", "react-markdown": "^10.1.0", "rehype-katex": "^7.0.1", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-math": "^6.0.0", "shiki": "^3.12.2", "tailwind-merge": "^3.3.1" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0" } }, "sha512-vFZdoWKUeagzKwGGOcEqkV1fcgXOJOQqrNBor5/hbaAE/e/ULxZoIHHJJd5KEuaSddCM9KuYtIuZi3WSttXTEA=="],
"streamx": ["[email protected]", "", { "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg=="],
@@ -4101,7 +4148,7 @@
"suffix-thumb": ["[email protected]", "", {}, "sha512-I5PWXAFKx3FYnI9a+dQMWNqTxoRt6vdBdb0O+BJ1sxXCWtSoQCusc13E58f+9p4MYx/qCnEMkD5jac6K2j3dgA=="],
- "supermemory": ["supermemory@workspace:apps/raycast-extension"],
+ "supermemory": ["[email protected]", "", {}, "sha512-acomTikn701JDhGnK7jGlsE/hTa+nYha1/Q4+4C8oB80BOIiQuyNufM3wXQ45z1YIqhL5m+JkgdTDGZH5zCIVw=="],
"supermemory-browser-extension": ["supermemory-browser-extension@workspace:apps/browser-extension"],
@@ -4339,10 +4386,12 @@
"victory-vendor": ["[email protected]", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ=="],
- "vite": ["[email protected]", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA=="],
+ "vite": ["[email protected]", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-qTl3VF7BvOupTR85Zc561sPEgxyUSNSvTQ9fit7DEMP7yPgvvIGm5Zfa1dOM+kOwWGNviK9uFM9ra77+OjK7lQ=="],
"vite-node": ["[email protected]", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="],
+ "vite-plugin-lib-inject-css": ["[email protected]", "", { "dependencies": { "@ast-grep/napi": "^0.36.2", "magic-string": "^0.30.17", "picocolors": "^1.1.1" }, "peerDependencies": { "vite": "*" } }, "sha512-NF30p0GwtfSAmVlxo2NgPXM2rEdtgV7LFi4lkzajKD7P3Ru/ZAFmI533M0Z5qyMZpvNMxVGkewzpjD0HOWtbDQ=="],
+
"vitest": ["[email protected]", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="],
"vscode-jsonrpc": ["[email protected]", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="],
@@ -4401,8 +4450,6 @@
"word-wrap": ["[email protected]", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
- "wordwrap": ["[email protected]", "", {}, "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="],
-
"workerd": ["[email protected]", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250617.0", "@cloudflare/workerd-darwin-arm64": "1.20250617.0", "@cloudflare/workerd-linux-64": "1.20250617.0", "@cloudflare/workerd-linux-arm64": "1.20250617.0", "@cloudflare/workerd-windows-64": "1.20250617.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-Uv6p0PYUHp/W/aWfUPLkZVAoAjapisM27JJlwcX9wCPTfCfnuegGOxFMvvlYpmNaX4YCwEdLCwuNn3xkpSkuZw=="],
"wrangler": ["[email protected]", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.3.3", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20250617.4", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.17", "workerd": "1.20250617.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250617.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-m8qVO3YxhUTII+4U889G/f5UuLSvMkUkCNatupV2f/SJ+iqaWtP1QbuQII8bs2J/O4rqxsz46Wu2S50u7tKB5Q=="],
@@ -4709,6 +4756,8 @@
"@iconify/utils/globals": ["[email protected]", "", {}, "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg=="],
+ "@inquirer/core/signal-exit": ["[email protected]", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
+
"@inquirer/core/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
"@inquirer/external-editor/iconv-lite": ["[email protected]", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ=="],
@@ -4767,32 +4816,12 @@
"@npmcli/promise-spawn/which": ["[email protected]", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/which.js" } }, "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg=="],
- "@oclif/core/ansis": ["[email protected]", "", {}, "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg=="],
-
- "@oclif/core/is-wsl": ["[email protected]", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
-
- "@oclif/core/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
-
- "@oclif/core/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "@oclif/core/supports-color": ["[email protected]", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
-
- "@oclif/core/widest-line": ["[email protected]", "", { "dependencies": { "string-width": "^4.0.0" } }, "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg=="],
-
- "@oclif/core/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
-
- "@oclif/plugin-autocomplete/ansis": ["[email protected]", "", {}, "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/checkbox": "^4.3.0", "@inquirer/confirm": "^5.1.19", "@inquirer/editor": "^4.2.21", "@inquirer/expand": "^4.0.21", "@inquirer/input": "^4.2.5", "@inquirer/number": "^3.0.21", "@inquirer/password": "^4.0.21", "@inquirer/rawlist": "^4.1.9", "@inquirer/search": "^3.2.0", "@inquirer/select": "^4.4.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-X7/+dG9SLpSzRkwgG5/xiIzW0oMrV3C0HOa7YHG1WnrLK+vCQHfte4k/T80059YBdei29RBC3s+pSMvPJDU9/A=="],
-
- "@oclif/plugin-not-found/ansis": ["[email protected]", "", {}, "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg=="],
-
- "@oclif/plugin-not-found/fast-levenshtein": ["[email protected]", "", { "dependencies": { "fastest-levenshtein": "^1.0.7" } }, "sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ=="],
-
"@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/[email protected]", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="],
"@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/[email protected]", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="],
+ "@opennextjs/aws/@ast-grep/napi": ["@ast-grep/[email protected]", "", { "optionalDependencies": { "@ast-grep/napi-darwin-arm64": "0.35.0", "@ast-grep/napi-darwin-x64": "0.35.0", "@ast-grep/napi-linux-arm64-gnu": "0.35.0", "@ast-grep/napi-linux-arm64-musl": "0.35.0", "@ast-grep/napi-linux-x64-gnu": "0.35.0", "@ast-grep/napi-linux-x64-musl": "0.35.0", "@ast-grep/napi-win32-arm64-msvc": "0.35.0", "@ast-grep/napi-win32-ia32-msvc": "0.35.0", "@ast-grep/napi-win32-x64-msvc": "0.35.0" } }, "sha512-3ucaaSxV6fxXoqHrE/rxAvP1THnDdY5jNzGlnvx+JvnY9C/dSRKc0jlRMRz59N3El572+/yNRUUpAV1T9aBJug=="],
+
"@opennextjs/aws/express": ["[email protected]", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.0.1", "content-disposition": "^1.0.0", "content-type": "~1.0.4", "cookie": "0.7.1", "cookie-signature": "^1.2.1", "debug": "4.3.6", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "^2.0.0", "fresh": "2.0.0", "http-errors": "2.0.0", "merge-descriptors": "^2.0.0", "methods": "~1.1.2", "mime-types": "^3.0.0", "on-finished": "2.4.1", "once": "1.4.0", "parseurl": "~1.3.3", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "router": "^2.0.0", "safe-buffer": "5.2.1", "send": "^1.1.0", "serve-static": "^2.1.0", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "^2.0.0", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ=="],
"@opennextjs/cloudflare/glob": ["[email protected]", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.0.3", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA=="],
@@ -4805,16 +4834,10 @@
"@puppeteer/browsers/yargs": ["[email protected]", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
- "@raycast/api/@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw=="],
-
- "@raycast/api/@types/react": ["@types/[email protected]", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g=="],
-
- "@raycast/api/esbuild": ["[email protected]", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
-
- "@raycast/api/react": ["[email protected]", "", {}, "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ=="],
-
"@react-router/dev/pathe": ["[email protected]", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="],
+ "@react-router/dev/react-refresh": ["[email protected]", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="],
+
"@react-router/dev/valibot": ["[email protected]", "", { "peerDependencies": { "typescript": ">=5" }, "optionalPeers": ["typescript"] }, "sha512-igDBb8CTYr8YTQlOKgaN9nSS0Be7z+WRuaeYqGf3Cjz3aKmSnqEmYnkfVjzIuumGqfHpa3fLIvMEAfhrpqN8ng=="],
"@repo/docs/typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
@@ -4865,18 +4888,24 @@
"@stoplight/yaml/@stoplight/types": ["@stoplight/[email protected]", "", { "dependencies": { "@types/json-schema": "^7.0.4", "utility-types": "^3.10.0" } }, "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g=="],
- "@supermemory/ai-sdk/supermemory": ["[email protected]", "", {}, "sha512-acomTikn701JDhGnK7jGlsE/hTa+nYha1/Q4+4C8oB80BOIiQuyNufM3wXQ45z1YIqhL5m+JkgdTDGZH5zCIVw=="],
-
"@supermemory/ai-sdk/typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"@supermemory/ai-sdk/zod": ["[email protected]", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
+ "@supermemory/memory-graph/@radix-ui/react-slot": ["@radix-ui/[email protected]", "", { "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=="],
+
+ "@supermemory/memory-graph/@tanstack/react-query": ["@tanstack/[email protected]", "", { "dependencies": { "@tanstack/query-core": "5.90.7" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-wAHc/cgKzW7LZNFloThyHnV/AX9gTg3w5yAv0gvQHPZoCnepwqCMtzbuPbb2UvfvO32XZ46e8bPOYbfZhzVnnQ=="],
+
+ "@supermemory/memory-graph/lucide-react": ["[email protected]", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-g9WCjmfwqbexSnZE+2cl21PCfXOcqnGeWeMTNAOGEfpPbm/ZF4YIq77Z8qWrxbu660EKuLB4nSLggoKnCb+isw=="],
+
+ "@supermemory/memory-graph/motion": ["[email protected]", "", { "dependencies": { "framer-motion": "^12.23.24", "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-Rc5E7oe2YZ72N//S3QXGzbnXgqNrTESv8KKxABR20q2FLch9gHLo0JLyYo2hZ238bZ9Gx6cWhj9VO0IgwbMjCw=="],
+
+ "@supermemory/memory-graph/typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
+
"@supermemory/tools/@ai-sdk/anthropic": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.11" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XuGg4Mk/hbbe34a27ElFbontKbQMi0lSIOw4iFpbzXowsvW/ld5tspFOcrjDrNcy+0z5iXSFVklwwQcNwe65SQ=="],
"@supermemory/tools/@anthropic-ai/sdk": ["@anthropic-ai/[email protected]", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-zIdPOcrCVEI8t3Di40nH4z9EoeyGZfXbYSvWdDLsB/KkaSYMnEgC7gmcgWu83g2NTn1ZTpbMvpdttWDGGIk6zw=="],
- "@supermemory/tools/supermemory": ["[email protected]", "", {}, "sha512-acomTikn701JDhGnK7jGlsE/hTa+nYha1/Q4+4C8oB80BOIiQuyNufM3wXQ45z1YIqhL5m+JkgdTDGZH5zCIVw=="],
-
"@supermemory/tools/typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"@supermemory/tools/zod": ["[email protected]", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
@@ -4893,23 +4922,23 @@
"@tailwindcss/oxide-wasm32-wasi/tslib": ["[email protected]", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
- "@tokenizer/inflate/fflate": ["[email protected]", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
+ "@tailwindcss/vite/@tailwindcss/node": ["@tailwindcss/[email protected]", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.19", "source-map-js": "^1.2.1", "tailwindcss": "4.1.16" } }, "sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw=="],
- "@typescript-eslint/eslint-plugin/ignore": ["[email protected]", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
+ "@tailwindcss/vite/@tailwindcss/oxide": ["@tailwindcss/[email protected]", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.16", "@tailwindcss/oxide-darwin-arm64": "4.1.16", "@tailwindcss/oxide-darwin-x64": "4.1.16", "@tailwindcss/oxide-freebsd-x64": "4.1.16", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.16", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.16", "@tailwindcss/oxide-linux-arm64-musl": "4.1.16", "@tailwindcss/oxide-linux-x64-gnu": "4.1.16", "@tailwindcss/oxide-linux-x64-musl": "4.1.16", "@tailwindcss/oxide-wasm32-wasi": "4.1.16", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.16", "@tailwindcss/oxide-win32-x64-msvc": "4.1.16" } }, "sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg=="],
- "@typescript-eslint/typescript-estree/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
+ "@tailwindcss/vite/tailwindcss": ["[email protected]", "", {}, "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA=="],
- "@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/[email protected]", "", {}, "sha512-N/ICGKleNhA5nc9XXQG/kkKHJ7S55u0x0XUJbbkmdCnFuoRkM1Il12q9q0eX19+M7KKUEPw/daUPIRnxhcxAIw=="],
+ "@tokenizer/inflate/fflate": ["[email protected]", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
- "@vitejs/plugin-react/react-refresh": ["[email protected]", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
+ "@typescript-eslint/eslint-plugin/ignore": ["[email protected]", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
- "@vitest/mocker/estree-walker": ["[email protected]", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
+ "@vanilla-extract/integration/esbuild": ["[email protected]", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
- "accepts/negotiator": ["[email protected]", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
+ "@vitest/mocker/estree-walker": ["[email protected]", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
- "aggregate-error/clean-stack": ["[email protected]", "", { "dependencies": { "escape-string-regexp": "5.0.0" } }, "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg=="],
+ "@wxt-dev/module-react/@vitejs/plugin-react": ["@vitejs/[email protected]", "", { "dependencies": { "@babel/core": "^7.28.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.38", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-La0KD0vGkVkSk6K+piWDKRUyg8Rl5iAIKRMH0vMJI0Eg47bq1eOxmoObAaQG37WMW9MSyk7Cs8EIWwJC1PtzKA=="],
- "aggregate-error/indent-string": ["[email protected]", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="],
+ "accepts/negotiator": ["[email protected]", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
"ai/@ai-sdk/provider-utils": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-T1gZ76gEIwffep6MWI0QNy9jgoybUHE7TRaHB5k54K8mF91ciGFlbtCGxDYhMH3nCRergKwYFIDeFF0hJSIQHQ=="],
@@ -4933,8 +4962,6 @@
"ansi-align/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
- "ansi-escapes/type-fest": ["[email protected]", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
-
"anymatch/picomatch": ["[email protected]", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"atmn/dotenv": ["[email protected]", "", {}, "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w=="],
@@ -4961,6 +4988,8 @@
"chromium-bidi/zod": ["[email protected]", "", {}, "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g=="],
+ "clean-stack/escape-string-regexp": ["[email protected]", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
+
"cli-highlight/chalk": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"cli-highlight/parse5": ["[email protected]", "", {}, "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug=="],
@@ -5017,8 +5046,6 @@
"estree-util-to-js/source-map": ["[email protected]", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
- "execa/signal-exit": ["[email protected]", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
-
"express/cookie": ["[email protected]", "", {}, "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="],
"express/debug": ["[email protected]", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
@@ -5035,10 +5062,10 @@
"fetch-blob/web-streams-polyfill": ["[email protected]", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
- "filelist/minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
-
"finalhandler/debug": ["[email protected]", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
+ "foreground-child/signal-exit": ["[email protected]", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
+
"fs-minipass/minipass": ["[email protected]", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
"fx-runner/commander": ["[email protected]", "", { "dependencies": { "graceful-readlink": ">= 1.0.0" } }, "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A=="],
@@ -5069,16 +5096,10 @@
"http-proxy-agent/agent-base": ["[email protected]", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
- "ink/ansi-escapes": ["[email protected]", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-Zhl0ErHcSRUaVfGUeUdDuLgpkEo8KIFjB4Y9uAc46ScOpdDiU1Dbyplh7qWJeJ/ZHpbyMSM26+X3BySgnIz40Q=="],
-
"ink/cli-cursor": ["[email protected]", "", { "dependencies": { "restore-cursor": "^4.0.0" } }, "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg=="],
- "ink/indent-string": ["[email protected]", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="],
-
"ink/react-reconciler": ["[email protected]", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-2NPMOzgTlG0ZWdIf3qG+dcbLSoAc/uLfOwckc3ofy5sSK0pLJqnQLpUFxvGcN2rlXSjnVtGeeFLNimCQEj5gOQ=="],
- "ink/signal-exit": ["[email protected]", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
-
"is-online/got": ["[email protected]", "", { "dependencies": { "@sindresorhus/is": "^5.2.0", "@szmarczak/http-timer": "^5.0.1", "cacheable-lookup": "^7.0.0", "cacheable-request": "^10.2.8", "decompress-response": "^6.0.0", "form-data-encoder": "^2.1.2", "get-stream": "^6.0.1", "http2-wrapper": "^2.1.10", "lowercase-keys": "^3.0.0", "p-cancelable": "^3.0.0", "responselike": "^3.0.0" } }, "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ=="],
"jest-worker/supports-color": ["[email protected]", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
@@ -5089,12 +5110,12 @@
"log-symbols/is-unicode-supported": ["[email protected]", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="],
- "log-update/ansi-escapes": ["[email protected]", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-Zhl0ErHcSRUaVfGUeUdDuLgpkEo8KIFjB4Y9uAc46ScOpdDiU1Dbyplh7qWJeJ/ZHpbyMSM26+X3BySgnIz40Q=="],
-
"mdast-util-find-and-replace/escape-string-regexp": ["[email protected]", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
"mdast-util-frontmatter/escape-string-regexp": ["[email protected]", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
+ "memory-graph-playground/typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
+
"mermaid/uuid": ["[email protected]", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
"micromark-extension-mdxjs/acorn": ["[email protected]", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
@@ -5171,6 +5192,10 @@
"restore-cursor/onetime": ["[email protected]", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="],
+ "restore-cursor/signal-exit": ["[email protected]", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
+
+ "rolldown/@rolldown/pluginutils": ["@rolldown/[email protected]", "", {}, "sha512-ycMEPrS3StOIeb87BT3/+bu+blEtyvwQ4zmo2IcJQy0Rd1DAAhKksA0iUZ3MYSpJtjlPhg0Eo6mvVS6ggPhRbw=="],
+
"router/path-to-regexp": ["[email protected]", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="],
"safe-array-concat/isarray": ["[email protected]", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
@@ -5225,11 +5250,9 @@
"sucrase/lines-and-columns": ["[email protected]", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
- "supermemory/@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw=="],
+ "supermemory-browser-extension/@tanstack/react-query": ["@tanstack/[email protected]", "", { "dependencies": { "@tanstack/query-core": "5.90.7" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-wAHc/cgKzW7LZNFloThyHnV/AX9gTg3w5yAv0gvQHPZoCnepwqCMtzbuPbb2UvfvO32XZ46e8bPOYbfZhzVnnQ=="],
- "supermemory/@types/react": ["@types/[email protected]", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g=="],
-
- "supermemory/typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
+ "supermemory-browser-extension/tailwindcss": ["[email protected]", "", {}, "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA=="],
"supermemory-browser-extension/typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
@@ -5257,8 +5280,14 @@
"update-notifier/is-in-ci": ["[email protected]", "", { "bin": { "is-in-ci": "cli.js" } }, "sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg=="],
+ "vite/esbuild": ["[email protected]", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
+
+ "vite-node/vite": ["[email protected]", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA=="],
+
"vitest/tinyexec": ["[email protected]", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
+ "vitest/vite": ["[email protected]", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA=="],
+
"web-ext-run/@babel/runtime": ["@babel/[email protected]", "", {}, "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA=="],
"web-ext-run/pino": ["[email protected]", "", { "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^3.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg=="],
@@ -5283,6 +5312,8 @@
"wxt/minimatch": ["[email protected]", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="],
+ "wxt/vite": ["[email protected]", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA=="],
+
"youch/cookie": ["[email protected]", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
"@ai-sdk/react/@ai-sdk/provider-utils/@ai-sdk/provider": ["@ai-sdk/[email protected]", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Z8SPncMtS3RsoXITmT7NVwrAq6M44dmw0DoUOYJqNNtCu8iMWuxB8Nxsoqpa0uEEy9R1V1ZThJAXTYgjTUxl3w=="],
@@ -5601,39 +5632,27 @@
"@node-minify/core/glob/minipass": ["[email protected]", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="],
- "@oclif/core/is-wsl/is-docker": ["[email protected]", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="],
-
- "@oclif/core/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "@oclif/core/string-width/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
-
- "@oclif/core/wrap-ansi/ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "@oclif/core/wrap-ansi/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/checkbox": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/ansi": "^1.0.1", "@inquirer/core": "^10.3.0", "@inquirer/figures": "^1.0.14", "@inquirer/type": "^3.0.9", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-5+Q3PKH35YsnoPTh75LucALdAxom6xh5D1oeY561x4cqBuH24ZFVyFREPe14xgnrtmGu3EEt1dIi60wRVSnGCw=="],
+ "@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/[email protected]", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/confirm": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/core": "^10.3.0", "@inquirer/type": "^3.0.9" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-wQNz9cfcxrtEnUyG5PndC8g3gZ7lGDBzmWiXZkX8ot3vfZ+/BLjR8EvyGX4YzQLeVqtAlY/YScZpW7CW8qMoDQ=="],
+ "@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/[email protected]", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/editor": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/core": "^10.3.0", "@inquirer/external-editor": "^1.0.2", "@inquirer/type": "^3.0.9" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-MjtjOGjr0Kh4BciaFShYpZ1s9400idOdvQ5D7u7lE6VztPFoyLcVNE5dXBmEEIQq5zi4B9h2kU+q7AVBxJMAkQ=="],
+ "@opennextjs/aws/@ast-grep/napi/@ast-grep/napi-darwin-arm64": ["@ast-grep/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-T+MN4Oinc+sXjXCIHzfxDDWY7r2pKgPxM6zVeVlkMTrJV2mJtyKYBIS+CABhRM6kflps2T2I6l4DGaKV/8Ym9w=="],
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/expand": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/core": "^10.3.0", "@inquirer/type": "^3.0.9", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-+mScLhIcbPFmuvU3tAGBed78XvYHSvCl6dBiYMlzCLhpr0bzGzd8tfivMMeqND6XZiaZ1tgusbUHJEfc6YzOdA=="],
+ "@opennextjs/aws/@ast-grep/napi/@ast-grep/napi-darwin-x64": ["@ast-grep/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-pEYiN6JI1HY2uWhMYJ9+3yIMyVYKuYdFzeD+dL7odA3qzK0o9N9AM3/NOt4ynU2EhufaWCJr0P5NoQ636qN6MQ=="],
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/input": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/core": "^10.3.0", "@inquirer/type": "^3.0.9" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-7GoWev7P6s7t0oJbenH0eQ0ThNdDJbEAEtVt9vsrYZ9FulIokvd823yLyhQlWHJPGce1wzP53ttfdCZmonMHyA=="],
+ "@opennextjs/aws/@ast-grep/napi/@ast-grep/napi-linux-arm64-gnu": ["@ast-grep/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-NBuzQngABGKz7lhG08IQb+7nPqUx81Ol37xmS3ZhVSdSgM0mtp93rCbgFTkJcAFE8IMfCHQSg7G4g0Iotz4ABQ=="],
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/number": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/core": "^10.3.0", "@inquirer/type": "^3.0.9" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-5QWs0KGaNMlhbdhOSCFfKsW+/dcAVC2g4wT/z2MCiZM47uLgatC5N20kpkDQf7dHx+XFct/MJvvNGy6aYJn4Pw=="],
+ "@opennextjs/aws/@ast-grep/napi/@ast-grep/napi-linux-arm64-musl": ["@ast-grep/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-1EcvHPwyWpCL/96LuItBYGfeI5FaMTRvL+dHbO/hL5q1npqbb5qn+ppJwtNOjTPz8tayvgggxVk9T4C2O7taYA=="],
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/password": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/ansi": "^1.0.1", "@inquirer/core": "^10.3.0", "@inquirer/type": "^3.0.9" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-xxeW1V5SbNFNig2pLfetsDb0svWlKuhmr7MPJZMYuDnCTkpVBI+X/doudg4pznc1/U+yYmWFFOi4hNvGgUo7EA=="],
+ "@opennextjs/aws/@ast-grep/napi/@ast-grep/napi-linux-x64-gnu": ["@ast-grep/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-FDzNdlqmQnsiWXhnLxusw5AOfEcEM+5xtmrnAf3SBRFr86JyWD9qsynnFYC2pnP9hlMfifNH2TTmMpyGJW49Xw=="],
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/rawlist": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/core": "^10.3.0", "@inquirer/type": "^3.0.9", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-AWpxB7MuJrRiSfTKGJ7Y68imYt8P9N3Gaa7ySdkFj1iWjr6WfbGAhdZvw/UnhFXTHITJzxGUI9k8IX7akAEBCg=="],
+ "@opennextjs/aws/@ast-grep/napi/@ast-grep/napi-linux-x64-musl": ["@ast-grep/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-wlmndjfBafT8u5p4DBnoRQyoCSGNuVSz7rT3TqhvlHcPzUouRWMn95epU9B1LNLyjXvr9xHeRjSktyCN28w57Q=="],
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/search": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/core": "^10.3.0", "@inquirer/figures": "^1.0.14", "@inquirer/type": "^3.0.9", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-a5SzB/qrXafDX1Z4AZW3CsVoiNxcIYCzYP7r9RzrfMpaLpB+yWi5U8BWagZyLmwR0pKbbL5umnGRd0RzGVI8bQ=="],
+ "@opennextjs/aws/@ast-grep/napi/@ast-grep/napi-win32-arm64-msvc": ["@ast-grep/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-gkhJeYc4rrZLX2icLxalPikTLMR57DuIYLwLr9g+StHYXIsGHrbfrE6Nnbdd8Izfs34ArFCrcwdaMrGlvOPSeg=="],
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/select": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/ansi": "^1.0.1", "@inquirer/core": "^10.3.0", "@inquirer/figures": "^1.0.14", "@inquirer/type": "^3.0.9", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-kaC3FHsJZvVyIjYBs5Ih8y8Bj4P/QItQWrZW22WJax7zTN+ZPXVGuOM55vzbdCP9zKUiBd9iEJVdesujfF+cAA=="],
+ "@opennextjs/aws/@ast-grep/napi/@ast-grep/napi-win32-ia32-msvc": ["@ast-grep/[email protected]", "", { "os": "win32", "cpu": "ia32" }, "sha512-OdUuRa3chHCZ65y+qALfkUjz0W0Eg21YZ9TyPquV5why07M6HAK38mmYGzLxFH6294SvRQhs+FA/rAfbKeH0jA=="],
- "@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/[email protected]", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
-
- "@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/[email protected]", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="],
+ "@opennextjs/aws/@ast-grep/napi/@ast-grep/napi-win32-x64-msvc": ["@ast-grep/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-pcQRUHqbroTN1oQ56V982a7IZTUUySQYWa2KEyksiifHGuBuitlzcyzFGjT96ThcqD9XW0UVJMvpoF2Qjh006Q=="],
"@opennextjs/aws/express/accepts": ["[email protected]", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
@@ -5677,105 +5696,135 @@
"@puppeteer/browsers/yargs/yargs-parser": ["[email protected]", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
- "@raycast/api/@types/node/undici-types": ["[email protected]", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
+ "@repo/web/@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-T1gZ76gEIwffep6MWI0QNy9jgoybUHE7TRaHB5k54K8mF91ciGFlbtCGxDYhMH3nCRergKwYFIDeFF0hJSIQHQ=="],
- "@raycast/api/esbuild/@esbuild/aix-ppc64": ["@esbuild/[email protected]", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="],
+ "@repo/web/@biomejs/biome/@biomejs/cli-darwin-arm64": ["@biomejs/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4LECm4kc3If0JISai4c3KWQzukoUdpxy4fRzlrPcrdMSRFksR9ZoXK7JBcPuLBmd2SoT4/d7CQS33VnZpgBjew=="],
- "@raycast/api/esbuild/@esbuild/android-arm": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="],
+ "@repo/web/@biomejs/biome/@biomejs/cli-darwin-x64": ["@biomejs/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-jNMnfwHT4N3wi+ypRfMTjLGnDmKYGzxVr1EYAPBcauRcDnICFXN81wD6wxJcSUrLynoyyYCdfW6vJHS/IAoTDA=="],
- "@raycast/api/esbuild/@esbuild/android-arm64": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="],
+ "@repo/web/@biomejs/biome/@biomejs/cli-linux-arm64": ["@biomejs/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-amnqvk+gWybbQleRRq8TMe0rIv7GHss8mFJEaGuEZYWg1Tw14YKOkeo8h6pf1c+d3qR+JU4iT9KXnBKGON4klw=="],
- "@raycast/api/esbuild/@esbuild/android-x64": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="],
+ "@repo/web/@biomejs/biome/@biomejs/cli-linux-arm64-musl": ["@biomejs/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-2Zz4usDG1GTTPQnliIeNx6eVGGP2ry5vE/v39nT73a3cKN6t5H5XxjcEoZZh62uVZvED7hXXikclvI64vZkYqw=="],
- "@raycast/api/esbuild/@esbuild/darwin-arm64": ["@esbuild/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="],
+ "@repo/web/@biomejs/biome/@biomejs/cli-linux-x64": ["@biomejs/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-8BG/vRAhFz1pmuyd24FQPhNeueLqPtwvZk6yblABY2gzL2H8fLQAF/Z2OPIc+BPIVPld+8cSiKY/KFh6k81xfA=="],
- "@raycast/api/esbuild/@esbuild/darwin-x64": ["@esbuild/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="],
+ "@repo/web/@biomejs/biome/@biomejs/cli-linux-x64-musl": ["@biomejs/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-gzB19MpRdTuOuLtPpFBGrV3Lq424gHyq2lFj8wfX9tvLMLdmA/R9C7k/mqBp/spcbWuHeIEKgEs3RviOPcWGBA=="],
- "@raycast/api/esbuild/@esbuild/freebsd-arm64": ["@esbuild/[email protected]", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="],
+ "@repo/web/@biomejs/biome/@biomejs/cli-win32-arm64": ["@biomejs/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCruqQlfWjhMlOdyf5pDHOxoNm4WoyY2vZ4YN33/nuZBRstVDuqPPjS0yBkbUlLEte11FbpW+wWSlfnZfSIZvg=="],
- "@raycast/api/esbuild/@esbuild/freebsd-x64": ["@esbuild/[email protected]", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="],
+ "@repo/web/@biomejs/biome/@biomejs/cli-win32-x64": ["@biomejs/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-6Ee9P26DTb4D8sN9nXxgbi9Dw5vSOfH98M7UlmkjKB2vtUbrRqCbZiNfryGiwnPIpd6YUoTl7rLVD2/x1CyEHQ=="],
- "@raycast/api/esbuild/@esbuild/linux-arm": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="],
+ "@repo/web/ai/@ai-sdk/gateway": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "2.0.0-beta.1", "@ai-sdk/provider-utils": "3.0.0-beta.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-v+LXXm8INLYAdxHnNMVAJ/B7k+Nejn5dCQMg/F8SRetB5dEQ4sbfimE+b6rawILJznnsy2fugUO1oFFXlUS5Yg=="],
- "@raycast/api/esbuild/@esbuild/linux-arm64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="],
+ "@repo/web/ai/@ai-sdk/provider": ["@ai-sdk/[email protected]", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Z8SPncMtS3RsoXITmT7NVwrAq6M44dmw0DoUOYJqNNtCu8iMWuxB8Nxsoqpa0uEEy9R1V1ZThJAXTYgjTUxl3w=="],
- "@raycast/api/esbuild/@esbuild/linux-ia32": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="],
+ "@repo/web/ai/@ai-sdk/provider-utils": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "2.0.0-beta.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-4Dv/wiGZrvO6fI7P0yMLa4XZru0XW8LPibTObbkHBdweLUVGIze7aCfxxQeY44Uqcbl/h6/yBTkx2XmPtwf/Ow=="],
- "@raycast/api/esbuild/@esbuild/linux-loong64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="],
+ "@repo/web/wrangler/@cloudflare/unenv-preset": ["@cloudflare/[email protected]", "", { "peerDependencies": { "unenv": "2.0.0-rc.21", "workerd": "^1.20250924.0" }, "optionalPeers": ["workerd"] }, "sha512-eB3UAIVhrvY+CMZrRXS/bAv5kWdNiH+dgwu+1M1S7keDeonxkfKIGVIrhcCLTkcqYlN30MPURPuVFUEzIWuuvg=="],
- "@raycast/api/esbuild/@esbuild/linux-mips64el": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="],
+ "@repo/web/wrangler/miniflare": ["[email protected]", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "7.14.0", "workerd": "1.20250927.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-CP0Q9Ytipid/Q6fJ2gAsVJ3yIMdx1+GoivA+EON68/ZLt66QwUFtpFeqdOUOKDmMbf/NFzjsKsce6h/8KjjYXg=="],
- "@raycast/api/esbuild/@esbuild/linux-ppc64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="],
+ "@repo/web/wrangler/unenv": ["[email protected]", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.7", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.6.1" } }, "sha512-Wj7/AMtE9MRnAXa6Su3Lk0LNCfqDYgfwVjwRFVum9U7wsto1imuHqk4kTm7Jni+5A0Hn7dttL6O/zjvUvoo+8A=="],
- "@raycast/api/esbuild/@esbuild/linux-riscv64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="],
+ "@repo/web/wrangler/workerd": ["[email protected]", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250927.0", "@cloudflare/workerd-darwin-arm64": "1.20250927.0", "@cloudflare/workerd-linux-64": "1.20250927.0", "@cloudflare/workerd-linux-arm64": "1.20250927.0", "@cloudflare/workerd-windows-64": "1.20250927.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-6kyAGPGYNvn5mbpCJJ48VebN7QGSrvU/WJXgd4EQz20PyqjJAxHcEGGAJ+0Da0u/ewrN1+6fuMKQ1ALLBPiTWg=="],
- "@raycast/api/esbuild/@esbuild/linux-s390x": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="],
+ "@sentry/bundler-plugin-core/glob/minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA=="],
- "@raycast/api/esbuild/@esbuild/linux-x64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="],
+ "@sentry/bundler-plugin-core/glob/minipass": ["[email protected]", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="],
- "@raycast/api/esbuild/@esbuild/netbsd-arm64": ["@esbuild/[email protected]", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="],
+ "@stoplight/better-ajv-errors/ajv/json-schema-traverse": ["[email protected]", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
- "@raycast/api/esbuild/@esbuild/netbsd-x64": ["@esbuild/[email protected]", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="],
+ "@stoplight/spectral-core/ajv/json-schema-traverse": ["[email protected]", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
- "@raycast/api/esbuild/@esbuild/openbsd-arm64": ["@esbuild/[email protected]", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="],
+ "@stoplight/spectral-core/minimatch/brace-expansion": ["[email protected]", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
- "@raycast/api/esbuild/@esbuild/openbsd-x64": ["@esbuild/[email protected]", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="],
+ "@stoplight/spectral-functions/ajv/json-schema-traverse": ["[email protected]", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
- "@raycast/api/esbuild/@esbuild/sunos-x64": ["@esbuild/[email protected]", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="],
+ "@supermemory/memory-graph/@tanstack/react-query/@tanstack/query-core": ["@tanstack/[email protected]", "", {}, "sha512-6PN65csiuTNfBMXqQUxQhCNdtm1rV+9kC9YwWAIKcaxAauq3Wu7p18j3gQY3YIBJU70jT/wzCCZ2uqto/vQgiQ=="],
- "@raycast/api/esbuild/@esbuild/win32-arm64": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="],
+ "@supermemory/memory-graph/motion/framer-motion": ["[email protected]", "", { "dependencies": { "motion-dom": "^12.23.23", "motion-utils": "^12.23.6", "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-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w=="],
- "@raycast/api/esbuild/@esbuild/win32-ia32": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="],
+ "@supermemory/tools/@ai-sdk/anthropic/@ai-sdk/provider-utils": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-4hgHj89VqyOHzGaV85TkcgvO8WjecVF35TOUVg+C56vnzpWSgdIZu/ZWZNdZ6BTrv8y0N1toBWW7XcWiRRicLg=="],
- "@raycast/api/esbuild/@esbuild/win32-x64": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="],
+ "@tailwindcss/vite/@tailwindcss/node/lightningcss": ["[email protected]", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="],
- "@repo/web/@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-T1gZ76gEIwffep6MWI0QNy9jgoybUHE7TRaHB5k54K8mF91ciGFlbtCGxDYhMH3nCRergKwYFIDeFF0hJSIQHQ=="],
+ "@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-android-arm64": ["@tailwindcss/[email protected]", "", { "os": "android", "cpu": "arm64" }, "sha512-8+ctzkjHgwDJ5caq9IqRSgsP70xhdhJvm+oueS/yhD5ixLhqTw9fSL1OurzMUhBwE5zK26FXLCz2f/RtkISqHA=="],
- "@repo/web/@biomejs/biome/@biomejs/cli-darwin-arm64": ["@biomejs/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4LECm4kc3If0JISai4c3KWQzukoUdpxy4fRzlrPcrdMSRFksR9ZoXK7JBcPuLBmd2SoT4/d7CQS33VnZpgBjew=="],
+ "@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-C3oZy5042v2FOALBZtY0JTDnGNdS6w7DxL/odvSny17ORUnaRKhyTse8xYi3yKGyfnTUOdavRCdmc8QqJYwFKA=="],
- "@repo/web/@biomejs/biome/@biomejs/cli-darwin-x64": ["@biomejs/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-jNMnfwHT4N3wi+ypRfMTjLGnDmKYGzxVr1EYAPBcauRcDnICFXN81wD6wxJcSUrLynoyyYCdfW6vJHS/IAoTDA=="],
+ "@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-darwin-x64": ["@tailwindcss/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-vjrl/1Ub9+JwU6BP0emgipGjowzYZMjbWCDqwA2Z4vCa+HBSpP4v6U2ddejcHsolsYxwL5r4bPNoamlV0xDdLg=="],
- "@repo/web/@biomejs/biome/@biomejs/cli-linux-arm64": ["@biomejs/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-amnqvk+gWybbQleRRq8TMe0rIv7GHss8mFJEaGuEZYWg1Tw14YKOkeo8h6pf1c+d3qR+JU4iT9KXnBKGON4klw=="],
+ "@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/[email protected]", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TSMpPYpQLm+aR1wW5rKuUuEruc/oOX3C7H0BTnPDn7W/eMw8W+MRMpiypKMkXZfwH8wqPIRKppuZoedTtNj2tg=="],
- "@repo/web/@biomejs/biome/@biomejs/cli-linux-arm64-musl": ["@biomejs/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-2Zz4usDG1GTTPQnliIeNx6eVGGP2ry5vE/v39nT73a3cKN6t5H5XxjcEoZZh62uVZvED7hXXikclvI64vZkYqw=="],
+ "@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/[email protected]", "", { "os": "linux", "cpu": "arm" }, "sha512-p0GGfRg/w0sdsFKBjMYvvKIiKy/LNWLWgV/plR4lUgrsxFAoQBFrXkZ4C0w8IOXfslB9vHK/JGASWD2IefIpvw=="],
- "@repo/web/@biomejs/biome/@biomejs/cli-linux-x64": ["@biomejs/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-8BG/vRAhFz1pmuyd24FQPhNeueLqPtwvZk6yblABY2gzL2H8fLQAF/Z2OPIc+BPIVPld+8cSiKY/KFh6k81xfA=="],
+ "@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-DoixyMmTNO19rwRPdqviTrG1rYzpxgyYJl8RgQvdAQUzxC1ToLRqtNJpU/ATURSKgIg6uerPw2feW0aS8SNr/w=="],
- "@repo/web/@biomejs/biome/@biomejs/cli-linux-x64-musl": ["@biomejs/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-gzB19MpRdTuOuLtPpFBGrV3Lq424gHyq2lFj8wfX9tvLMLdmA/R9C7k/mqBp/spcbWuHeIEKgEs3RviOPcWGBA=="],
+ "@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-H81UXMa9hJhWhaAUca6bU2wm5RRFpuHImrwXBUvPbYb+3jo32I9VIwpOX6hms0fPmA6f2pGVlybO6qU8pF4fzQ=="],
- "@repo/web/@biomejs/biome/@biomejs/cli-win32-arm64": ["@biomejs/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCruqQlfWjhMlOdyf5pDHOxoNm4WoyY2vZ4YN33/nuZBRstVDuqPPjS0yBkbUlLEte11FbpW+wWSlfnZfSIZvg=="],
+ "@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-ZGHQxDtFC2/ruo7t99Qo2TTIvOERULPl5l0K1g0oK6b5PGqjYMga+FcY1wIUnrUxY56h28FxybtDEla+ICOyew=="],
- "@repo/web/@biomejs/biome/@biomejs/cli-win32-x64": ["@biomejs/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-6Ee9P26DTb4D8sN9nXxgbi9Dw5vSOfH98M7UlmkjKB2vtUbrRqCbZiNfryGiwnPIpd6YUoTl7rLVD2/x1CyEHQ=="],
+ "@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-Oi1tAaa0rcKf1Og9MzKeINZzMLPbhxvm7rno5/zuP1WYmpiG0bEHq4AcRUiG2165/WUzvxkW4XDYCscZWbTLZw=="],
- "@repo/web/ai/@ai-sdk/gateway": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "2.0.0-beta.1", "@ai-sdk/provider-utils": "3.0.0-beta.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-v+LXXm8INLYAdxHnNMVAJ/B7k+Nejn5dCQMg/F8SRetB5dEQ4sbfimE+b6rawILJznnsy2fugUO1oFFXlUS5Yg=="],
+ "@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/[email protected]", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.0.7", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-B01u/b8LteGRwucIBmCQ07FVXLzImWESAIMcUU6nvFt/tYsQ6IHz8DmZ5KtvmwxD+iTYBtM1xwoGXswnlu9v0Q=="],
- "@repo/web/ai/@ai-sdk/provider": ["@ai-sdk/[email protected]", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Z8SPncMtS3RsoXITmT7NVwrAq6M44dmw0DoUOYJqNNtCu8iMWuxB8Nxsoqpa0uEEy9R1V1ZThJAXTYgjTUxl3w=="],
+ "@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-zX+Q8sSkGj6HKRTMJXuPvOcP8XfYON24zJBRPlszcH1Np7xuHXhWn8qfFjIujVzvH3BHU+16jBXwgpl20i+v9A=="],
- "@repo/web/ai/@ai-sdk/provider-utils": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "2.0.0-beta.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-4Dv/wiGZrvO6fI7P0yMLa4XZru0XW8LPibTObbkHBdweLUVGIze7aCfxxQeY44Uqcbl/h6/yBTkx2XmPtwf/Ow=="],
+ "@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-m5dDFJUEejbFqP+UXVstd4W/wnxA4F61q8SoL+mqTypId2T2ZpuxosNSgowiCnLp2+Z+rivdU0AqpfgiD7yCBg=="],
- "@repo/web/wrangler/@cloudflare/unenv-preset": ["@cloudflare/[email protected]", "", { "peerDependencies": { "unenv": "2.0.0-rc.21", "workerd": "^1.20250924.0" }, "optionalPeers": ["workerd"] }, "sha512-eB3UAIVhrvY+CMZrRXS/bAv5kWdNiH+dgwu+1M1S7keDeonxkfKIGVIrhcCLTkcqYlN30MPURPuVFUEzIWuuvg=="],
+ "@vanilla-extract/integration/esbuild/@esbuild/aix-ppc64": ["@esbuild/[email protected]", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="],
- "@repo/web/wrangler/miniflare": ["[email protected]", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "7.14.0", "workerd": "1.20250927.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-CP0Q9Ytipid/Q6fJ2gAsVJ3yIMdx1+GoivA+EON68/ZLt66QwUFtpFeqdOUOKDmMbf/NFzjsKsce6h/8KjjYXg=="],
+ "@vanilla-extract/integration/esbuild/@esbuild/android-arm": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="],
- "@repo/web/wrangler/unenv": ["[email protected]", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.7", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.6.1" } }, "sha512-Wj7/AMtE9MRnAXa6Su3Lk0LNCfqDYgfwVjwRFVum9U7wsto1imuHqk4kTm7Jni+5A0Hn7dttL6O/zjvUvoo+8A=="],
+ "@vanilla-extract/integration/esbuild/@esbuild/android-arm64": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="],
- "@repo/web/wrangler/workerd": ["[email protected]", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250927.0", "@cloudflare/workerd-darwin-arm64": "1.20250927.0", "@cloudflare/workerd-linux-64": "1.20250927.0", "@cloudflare/workerd-linux-arm64": "1.20250927.0", "@cloudflare/workerd-windows-64": "1.20250927.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-6kyAGPGYNvn5mbpCJJ48VebN7QGSrvU/WJXgd4EQz20PyqjJAxHcEGGAJ+0Da0u/ewrN1+6fuMKQ1ALLBPiTWg=="],
+ "@vanilla-extract/integration/esbuild/@esbuild/android-x64": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="],
- "@sentry/bundler-plugin-core/glob/minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA=="],
+ "@vanilla-extract/integration/esbuild/@esbuild/darwin-arm64": ["@esbuild/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="],
- "@sentry/bundler-plugin-core/glob/minipass": ["[email protected]", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="],
+ "@vanilla-extract/integration/esbuild/@esbuild/darwin-x64": ["@esbuild/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="],
- "@stoplight/better-ajv-errors/ajv/json-schema-traverse": ["[email protected]", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
+ "@vanilla-extract/integration/esbuild/@esbuild/freebsd-arm64": ["@esbuild/[email protected]", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="],
- "@stoplight/spectral-core/ajv/json-schema-traverse": ["[email protected]", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
+ "@vanilla-extract/integration/esbuild/@esbuild/freebsd-x64": ["@esbuild/[email protected]", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="],
- "@stoplight/spectral-core/minimatch/brace-expansion": ["[email protected]", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
+ "@vanilla-extract/integration/esbuild/@esbuild/linux-arm": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="],
- "@stoplight/spectral-functions/ajv/json-schema-traverse": ["[email protected]", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
+ "@vanilla-extract/integration/esbuild/@esbuild/linux-arm64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="],
- "@supermemory/tools/@ai-sdk/anthropic/@ai-sdk/provider-utils": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-4hgHj89VqyOHzGaV85TkcgvO8WjecVF35TOUVg+C56vnzpWSgdIZu/ZWZNdZ6BTrv8y0N1toBWW7XcWiRRicLg=="],
+ "@vanilla-extract/integration/esbuild/@esbuild/linux-ia32": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="],
+
+ "@vanilla-extract/integration/esbuild/@esbuild/linux-loong64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="],
+
+ "@vanilla-extract/integration/esbuild/@esbuild/linux-mips64el": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="],
+
+ "@vanilla-extract/integration/esbuild/@esbuild/linux-ppc64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="],
+
+ "@vanilla-extract/integration/esbuild/@esbuild/linux-riscv64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="],
+
+ "@vanilla-extract/integration/esbuild/@esbuild/linux-s390x": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="],
+
+ "@vanilla-extract/integration/esbuild/@esbuild/linux-x64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="],
+
+ "@vanilla-extract/integration/esbuild/@esbuild/netbsd-arm64": ["@esbuild/[email protected]", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="],
+
+ "@vanilla-extract/integration/esbuild/@esbuild/netbsd-x64": ["@esbuild/[email protected]", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="],
+
+ "@vanilla-extract/integration/esbuild/@esbuild/openbsd-arm64": ["@esbuild/[email protected]", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="],
+
+ "@vanilla-extract/integration/esbuild/@esbuild/openbsd-x64": ["@esbuild/[email protected]", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="],
- "aggregate-error/clean-stack/escape-string-regexp": ["[email protected]", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
+ "@vanilla-extract/integration/esbuild/@esbuild/sunos-x64": ["@esbuild/[email protected]", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="],
+
+ "@vanilla-extract/integration/esbuild/@esbuild/win32-arm64": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="],
+
+ "@vanilla-extract/integration/esbuild/@esbuild/win32-ia32": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="],
+
+ "@vanilla-extract/integration/esbuild/@esbuild/win32-x64": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="],
+
+ "@wxt-dev/module-react/@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/[email protected]", "", {}, "sha512-N/ICGKleNhA5nc9XXQG/kkKHJ7S55u0x0XUJbbkmdCnFuoRkM1Il12q9q0eX19+M7KKUEPw/daUPIRnxhcxAIw=="],
+
+ "@wxt-dev/module-react/@vitejs/plugin-react/react-refresh": ["[email protected]", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
"ai-gateway-provider/ai/@ai-sdk/react": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider-utils": "2.2.8", "@ai-sdk/ui-utils": "1.2.11", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.23.8" }, "optionalPeers": ["zod"] }, "sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g=="],
@@ -5983,7 +6032,7 @@
"string-width-cjs/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
- "supermemory/@types/node/undici-types": ["[email protected]", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
+ "supermemory-browser-extension/@tanstack/react-query/@tanstack/query-core": ["@tanstack/[email protected]", "", {}, "sha512-6PN65csiuTNfBMXqQUxQhCNdtm1rV+9kC9YwWAIKcaxAauq3Wu7p18j3gQY3YIBJU70jT/wzCCZ2uqto/vQgiQ=="],
"terser-webpack-plugin/terser/acorn": ["[email protected]", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
@@ -5995,6 +6044,56 @@
"unplugin/chokidar/readdirp": ["[email protected]", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
+ "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/[email protected]", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="],
+
+ "vite/esbuild/@esbuild/android-arm": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="],
+
+ "vite/esbuild/@esbuild/android-arm64": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="],
+
+ "vite/esbuild/@esbuild/android-x64": ["@esbuild/[email protected]", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="],
+
+ "vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="],
+
+ "vite/esbuild/@esbuild/darwin-x64": ["@esbuild/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="],
+
+ "vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/[email protected]", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="],
+
+ "vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/[email protected]", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="],
+
+ "vite/esbuild/@esbuild/linux-arm": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="],
+
+ "vite/esbuild/@esbuild/linux-arm64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="],
+
+ "vite/esbuild/@esbuild/linux-ia32": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="],
+
+ "vite/esbuild/@esbuild/linux-loong64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="],
+
+ "vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="],
+
+ "vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="],
+
+ "vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="],
+
+ "vite/esbuild/@esbuild/linux-s390x": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="],
+
+ "vite/esbuild/@esbuild/linux-x64": ["@esbuild/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="],
+
+ "vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/[email protected]", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="],
+
+ "vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/[email protected]", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="],
+
+ "vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/[email protected]", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="],
+
+ "vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/[email protected]", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="],
+
+ "vite/esbuild/@esbuild/sunos-x64": ["@esbuild/[email protected]", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="],
+
+ "vite/esbuild/@esbuild/win32-arm64": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="],
+
+ "vite/esbuild/@esbuild/win32-ia32": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="],
+
+ "vite/esbuild/@esbuild/win32-x64": ["@esbuild/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="],
+
"webpack/eslint-scope/estraverse": ["[email protected]", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="],
"wrap-ansi-cjs/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
@@ -6095,62 +6194,6 @@
"@mintlify/scraping/yargs/string-width/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
- "@oclif/core/string-width/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
-
- "@oclif/core/wrap-ansi/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/checkbox/@inquirer/ansi": ["@inquirer/[email protected]", "", {}, "sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/checkbox/@inquirer/core": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/ansi": "^1.0.1", "@inquirer/figures": "^1.0.14", "@inquirer/type": "^3.0.9", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/checkbox/@inquirer/figures": ["@inquirer/[email protected]", "", {}, "sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/checkbox/@inquirer/type": ["@inquirer/[email protected]", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/confirm/@inquirer/core": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/ansi": "^1.0.1", "@inquirer/figures": "^1.0.14", "@inquirer/type": "^3.0.9", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/confirm/@inquirer/type": ["@inquirer/[email protected]", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/editor/@inquirer/core": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/ansi": "^1.0.1", "@inquirer/figures": "^1.0.14", "@inquirer/type": "^3.0.9", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/editor/@inquirer/type": ["@inquirer/[email protected]", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/expand/@inquirer/core": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/ansi": "^1.0.1", "@inquirer/figures": "^1.0.14", "@inquirer/type": "^3.0.9", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/expand/@inquirer/type": ["@inquirer/[email protected]", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/input/@inquirer/core": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/ansi": "^1.0.1", "@inquirer/figures": "^1.0.14", "@inquirer/type": "^3.0.9", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/input/@inquirer/type": ["@inquirer/[email protected]", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/number/@inquirer/core": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/ansi": "^1.0.1", "@inquirer/figures": "^1.0.14", "@inquirer/type": "^3.0.9", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/number/@inquirer/type": ["@inquirer/[email protected]", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/password/@inquirer/ansi": ["@inquirer/[email protected]", "", {}, "sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/password/@inquirer/core": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/ansi": "^1.0.1", "@inquirer/figures": "^1.0.14", "@inquirer/type": "^3.0.9", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/password/@inquirer/type": ["@inquirer/[email protected]", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/rawlist/@inquirer/core": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/ansi": "^1.0.1", "@inquirer/figures": "^1.0.14", "@inquirer/type": "^3.0.9", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/rawlist/@inquirer/type": ["@inquirer/[email protected]", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/search/@inquirer/core": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/ansi": "^1.0.1", "@inquirer/figures": "^1.0.14", "@inquirer/type": "^3.0.9", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/search/@inquirer/figures": ["@inquirer/[email protected]", "", {}, "sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/search/@inquirer/type": ["@inquirer/[email protected]", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/select/@inquirer/ansi": ["@inquirer/[email protected]", "", {}, "sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/select/@inquirer/core": ["@inquirer/[email protected]", "", { "dependencies": { "@inquirer/ansi": "^1.0.1", "@inquirer/figures": "^1.0.14", "@inquirer/type": "^3.0.9", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/select/@inquirer/figures": ["@inquirer/[email protected]", "", {}, "sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/select/@inquirer/type": ["@inquirer/[email protected]", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w=="],
-
"@opennextjs/aws/express/accepts/negotiator": ["[email protected]", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
"@opennextjs/aws/express/body-parser/debug": ["[email protected]", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
@@ -6199,6 +6242,40 @@
"@repo/web/wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-+m124IiM149QvvzAOrO766uTdILqXJZqzZjqTaMTaWXegjjsJwGSL6v9d71TSFntEwxeXnpJPBkVWyKZFjqrvg=="],
+ "@supermemory/memory-graph/motion/framer-motion/motion-dom": ["[email protected]", "", { "dependencies": { "motion-utils": "^12.23.6" } }, "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA=="],
+
+ "@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-darwin-arm64": ["[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="],
+
+ "@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-darwin-x64": ["[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="],
+
+ "@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-freebsd-x64": ["[email protected]", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="],
+
+ "@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-linux-arm-gnueabihf": ["[email protected]", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="],
+
+ "@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-linux-arm64-gnu": ["[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="],
+
+ "@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-linux-arm64-musl": ["[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="],
+
+ "@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-linux-x64-gnu": ["[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="],
+
+ "@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-linux-x64-musl": ["[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="],
+
+ "@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-win32-arm64-msvc": ["[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="],
+
+ "@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-win32-x64-msvc": ["[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="],
+
+ "@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/[email protected]", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="],
+
+ "@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/[email protected]", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="],
+
+ "@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/[email protected]", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
+
+ "@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/[email protected]", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw=="],
+
+ "@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/[email protected]", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
+
+ "@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/tslib": ["[email protected]", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
+
"alchemy/@cloudflare/unenv-preset/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-rFtXu/qhZziGOltjhHUCdlqP9wLUhf/CmnjJS0hXffGRAVxsCXhJw+7Vlr+hyRSHjHRhEV+gBFc4pHzT10Stzw=="],
"alchemy/@cloudflare/unenv-preset/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BcNlLVfPyctLjFeIJENhK7OZFkfaysHVA6G6KT1lwum+BaVOutebweLo2zOrH7UQCMDYdpkQOeb5nLDctvs8YA=="],
@@ -6315,54 +6392,6 @@
"@mintlify/scraping/yargs/string-width/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/checkbox/@inquirer/core/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/confirm/@inquirer/core/@inquirer/ansi": ["@inquirer/[email protected]", "", {}, "sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/confirm/@inquirer/core/@inquirer/figures": ["@inquirer/[email protected]", "", {}, "sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/confirm/@inquirer/core/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/editor/@inquirer/core/@inquirer/ansi": ["@inquirer/[email protected]", "", {}, "sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/editor/@inquirer/core/@inquirer/figures": ["@inquirer/[email protected]", "", {}, "sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/editor/@inquirer/core/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/expand/@inquirer/core/@inquirer/ansi": ["@inquirer/[email protected]", "", {}, "sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/expand/@inquirer/core/@inquirer/figures": ["@inquirer/[email protected]", "", {}, "sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/expand/@inquirer/core/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/input/@inquirer/core/@inquirer/ansi": ["@inquirer/[email protected]", "", {}, "sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/input/@inquirer/core/@inquirer/figures": ["@inquirer/[email protected]", "", {}, "sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/input/@inquirer/core/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/number/@inquirer/core/@inquirer/ansi": ["@inquirer/[email protected]", "", {}, "sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/number/@inquirer/core/@inquirer/figures": ["@inquirer/[email protected]", "", {}, "sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/number/@inquirer/core/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/password/@inquirer/core/@inquirer/figures": ["@inquirer/[email protected]", "", {}, "sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/password/@inquirer/core/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/rawlist/@inquirer/core/@inquirer/ansi": ["@inquirer/[email protected]", "", {}, "sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/rawlist/@inquirer/core/@inquirer/figures": ["@inquirer/[email protected]", "", {}, "sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/rawlist/@inquirer/core/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/search/@inquirer/core/@inquirer/ansi": ["@inquirer/[email protected]", "", {}, "sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/search/@inquirer/core/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/select/@inquirer/core/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
-
"@opennextjs/aws/express/body-parser/raw-body/iconv-lite": ["[email protected]", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ=="],
"@puppeteer/browsers/yargs/cliui/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
@@ -6414,107 +6443,5 @@
"cli-highlight/yargs/cliui/wrap-ansi/ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"cli-highlight/yargs/string-width/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
-
- "publish-browser-extension/ora/cli-cursor/restore-cursor/signal-exit": ["[email protected]", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/checkbox/@inquirer/core/wrap-ansi/ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/checkbox/@inquirer/core/wrap-ansi/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/checkbox/@inquirer/core/wrap-ansi/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/confirm/@inquirer/core/wrap-ansi/ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/confirm/@inquirer/core/wrap-ansi/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/confirm/@inquirer/core/wrap-ansi/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/editor/@inquirer/core/wrap-ansi/ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/editor/@inquirer/core/wrap-ansi/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/editor/@inquirer/core/wrap-ansi/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/expand/@inquirer/core/wrap-ansi/ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/expand/@inquirer/core/wrap-ansi/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/expand/@inquirer/core/wrap-ansi/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/input/@inquirer/core/wrap-ansi/ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/input/@inquirer/core/wrap-ansi/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/input/@inquirer/core/wrap-ansi/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/number/@inquirer/core/wrap-ansi/ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/number/@inquirer/core/wrap-ansi/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/number/@inquirer/core/wrap-ansi/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/password/@inquirer/core/wrap-ansi/ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/password/@inquirer/core/wrap-ansi/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/password/@inquirer/core/wrap-ansi/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/rawlist/@inquirer/core/wrap-ansi/ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/rawlist/@inquirer/core/wrap-ansi/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/rawlist/@inquirer/core/wrap-ansi/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/search/@inquirer/core/wrap-ansi/ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/search/@inquirer/core/wrap-ansi/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/search/@inquirer/core/wrap-ansi/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/select/@inquirer/core/wrap-ansi/ansi-styles": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/select/@inquirer/core/wrap-ansi/string-width": ["[email protected]", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/select/@inquirer/core/wrap-ansi/strip-ansi": ["[email protected]", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/checkbox/@inquirer/core/wrap-ansi/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/checkbox/@inquirer/core/wrap-ansi/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/confirm/@inquirer/core/wrap-ansi/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/confirm/@inquirer/core/wrap-ansi/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/editor/@inquirer/core/wrap-ansi/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/editor/@inquirer/core/wrap-ansi/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/expand/@inquirer/core/wrap-ansi/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/expand/@inquirer/core/wrap-ansi/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/input/@inquirer/core/wrap-ansi/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/input/@inquirer/core/wrap-ansi/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/number/@inquirer/core/wrap-ansi/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/number/@inquirer/core/wrap-ansi/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/password/@inquirer/core/wrap-ansi/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/password/@inquirer/core/wrap-ansi/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/rawlist/@inquirer/core/wrap-ansi/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/rawlist/@inquirer/core/wrap-ansi/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/search/@inquirer/core/wrap-ansi/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/search/@inquirer/core/wrap-ansi/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/select/@inquirer/core/wrap-ansi/string-width/emoji-regex": ["[email protected]", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
-
- "@oclif/plugin-not-found/@inquirer/prompts/@inquirer/select/@inquirer/core/wrap-ansi/strip-ansi/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
}
}
diff --git a/package.json b/package.json
index 791c4a2b..95d9d7e7 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
"@google/generative-ai": "^0.24.1",
"@hono/zod-validator": "^0.7.1",
"@scalar/hono-api-reference": "^0.9.11",
+ "@vanilla-extract/recipes": "^0.5.7",
"ai": "^5.0.59",
"alchemy": "^0.55.2",
"atmn": "^0.0.16",
diff --git a/packages/memory-graph-playground/.gitignore b/packages/memory-graph-playground/.gitignore
new file mode 100644
index 00000000..a547bf36
--- /dev/null
+++ b/packages/memory-graph-playground/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/packages/memory-graph-playground/README.md b/packages/memory-graph-playground/README.md
new file mode 100644
index 00000000..0480a389
--- /dev/null
+++ b/packages/memory-graph-playground/README.md
@@ -0,0 +1,47 @@
+# Memory Graph Playground
+
+A local development playground for testing the `@supermemory/memory-graph` package.
+
+## Getting Started
+
+1. Make sure the memory-graph package is built:
+ ```bash
+ cd ../memory-graph
+ bun run build
+ ```
+
+2. Start the dev server:
+ ```bash
+ cd ../memory-graph-playground
+ bun run dev
+ ```
+
+3. Open your browser to the URL shown (usually http://localhost:5173)
+
+4. Enter your Supermemory API key and click "Load Graph"
+
+## Features to Test
+
+- ✅ API key authentication
+- ✅ Data fetching with React Query
+- ✅ Graph rendering with PixiJS
+- ✅ Interactive pan and zoom
+- ✅ Node selection and details
+- ✅ Space filtering
+- ✅ Legend with statistics
+- ✅ CSS auto-injection
+- ✅ Error handling
+
+## Development
+
+When making changes to the memory-graph package:
+
+1. Make your changes in `packages/memory-graph/src`
+2. Rebuild: `cd packages/memory-graph && bun run build`
+3. The playground will hot-reload automatically
+
+## Notes
+
+- This playground uses the workspace version of `@supermemory/memory-graph`
+- CSS is automatically injected from the package
+- All dependencies are bundled except React/ReactDOM
diff --git a/packages/memory-graph-playground/eslint.config.js b/packages/memory-graph-playground/eslint.config.js
new file mode 100644
index 00000000..b19330b1
--- /dev/null
+++ b/packages/memory-graph-playground/eslint.config.js
@@ -0,0 +1,23 @@
+import js from '@eslint/js'
+import globals from 'globals'
+import reactHooks from 'eslint-plugin-react-hooks'
+import reactRefresh from 'eslint-plugin-react-refresh'
+import tseslint from 'typescript-eslint'
+import { defineConfig, globalIgnores } from 'eslint/config'
+
+export default defineConfig([
+ globalIgnores(['dist']),
+ {
+ files: ['**/*.{ts,tsx}'],
+ extends: [
+ js.configs.recommended,
+ tseslint.configs.recommended,
+ reactHooks.configs['recommended-latest'],
+ reactRefresh.configs.vite,
+ ],
+ languageOptions: {
+ ecmaVersion: 2020,
+ globals: globals.browser,
+ },
+ },
+])
diff --git a/packages/memory-graph-playground/index.html b/packages/memory-graph-playground/index.html
new file mode 100644
index 00000000..7542b7d5
--- /dev/null
+++ b/packages/memory-graph-playground/index.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>memory-graph-playground</title>
+ </head>
+ <body>
+ <div id="root"></div>
+ <script type="module" src="/src/main.tsx"></script>
+ </body>
+</html>
diff --git a/packages/memory-graph-playground/package.json b/packages/memory-graph-playground/package.json
new file mode 100644
index 00000000..203b066d
--- /dev/null
+++ b/packages/memory-graph-playground/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "memory-graph-playground",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "lint": "eslint .",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@supermemory/memory-graph": "workspace:*",
+ "react": "^19.1.1",
+ "react-dom": "^19.1.1"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.36.0",
+ "@types/node": "^24.6.0",
+ "@types/react": "^19.1.16",
+ "@types/react-dom": "^19.1.9",
+ "@vitejs/plugin-react": "^5.0.4",
+ "eslint": "^9.36.0",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-react-refresh": "^0.4.22",
+ "globals": "^16.4.0",
+ "typescript": "~5.9.3",
+ "typescript-eslint": "^8.45.0",
+ "vite": "^7.1.7"
+ }
+}
diff --git a/packages/memory-graph-playground/public/vite.svg b/packages/memory-graph-playground/public/vite.svg
new file mode 100644
index 00000000..e7b8dfb1
--- /dev/null
+++ b/packages/memory-graph-playground/public/vite.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg> \ No newline at end of file
diff --git a/packages/memory-graph-playground/src/App.css b/packages/memory-graph-playground/src/App.css
new file mode 100644
index 00000000..9e07ed68
--- /dev/null
+++ b/packages/memory-graph-playground/src/App.css
@@ -0,0 +1,119 @@
+.app {
+ width: 100vw;
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ background: #0f1419;
+ color: #e2e8f0;
+}
+
+.header {
+ padding: 1.5rem;
+ background: #1a1f29;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.header h1 {
+ margin: 0;
+ font-size: 2rem;
+ color: #fff;
+}
+
+.header p {
+ margin: 0.5rem 0 0 0;
+ color: #94a3b8;
+}
+
+.controls {
+ padding: 1rem 1.5rem;
+ display: flex;
+ gap: 1rem;
+ background: #1a1f29;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.api-key-input {
+ flex: 1;
+ padding: 0.75rem 1rem;
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ border-radius: 0.5rem;
+ background: rgba(255, 255, 255, 0.05);
+ color: #e2e8f0;
+ font-size: 1rem;
+}
+
+.api-key-input::placeholder {
+ color: #64748b;
+}
+
+.api-key-input:focus {
+ outline: none;
+ border-color: #60a5fa;
+}
+
+.load-button {
+ padding: 0.75rem 2rem;
+ border: none;
+ border-radius: 0.5rem;
+ background: #3b82f6;
+ color: white;
+ font-size: 1rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition: background 0.2s;
+}
+
+.load-button:hover:not(:disabled) {
+ background: #2563eb;
+}
+
+.load-button:disabled {
+ background: #334155;
+ cursor: not-allowed;
+ opacity: 0.5;
+}
+
+.graph-container {
+ flex: 1;
+ position: relative;
+ overflow: hidden;
+ padding: 1rem;
+}
+
+.instructions {
+ flex: 1;
+ padding: 3rem;
+ max-width: 800px;
+ margin: 0 auto;
+}
+
+.instructions h2 {
+ color: #fff;
+ margin-bottom: 1.5rem;
+}
+
+.instructions h3 {
+ color: #e2e8f0;
+ margin-top: 2rem;
+ margin-bottom: 1rem;
+}
+
+.instructions ol,
+.instructions ul {
+ text-align: left;
+ line-height: 2;
+}
+
+.instructions li {
+ margin-bottom: 0.5rem;
+ color: #cbd5e1;
+}
+
+.instructions a {
+ color: #60a5fa;
+ text-decoration: none;
+}
+
+.instructions a:hover {
+ text-decoration: underline;
+}
diff --git a/packages/memory-graph-playground/src/App.tsx b/packages/memory-graph-playground/src/App.tsx
new file mode 100644
index 00000000..3ab8c82c
--- /dev/null
+++ b/packages/memory-graph-playground/src/App.tsx
@@ -0,0 +1,71 @@
+import { useState } from 'react'
+import { MemoryGraph } from '@supermemory/memory-graph'
+import './App.css'
+
+function App() {
+ const [apiKey, setApiKey] = useState('')
+ const [showGraph, setShowGraph] = useState(false)
+
+ return (
+ <div className="app">
+ <div className="header">
+ <h1>Memory Graph Playground</h1>
+ <p>Test the @supermemory/memory-graph package</p>
+ </div>
+
+ <div className="controls">
+ <input
+ type="password"
+ placeholder="Enter your Supermemory API key"
+ value={apiKey}
+ onChange={(e) => setApiKey(e.target.value)}
+ className="api-key-input"
+ />
+ <button
+ onClick={() => setShowGraph(true)}
+ disabled={!apiKey}
+ className="load-button"
+ >
+ Load Graph
+ </button>
+ </div>
+
+ {showGraph && apiKey && (
+ <div className="graph-container">
+ <MemoryGraph
+ apiKey={apiKey}
+ variant="console"
+ onError={(error) => {
+ console.error('Graph error:', error)
+ alert(`Error: ${error.message}`)
+ }}
+ onSuccess={(total) => {
+ console.log(`Loaded ${total} documents`)
+ }}
+ />
+ </div>
+ )}
+
+ {!showGraph && (
+ <div className="instructions">
+ <h2>Instructions</h2>
+ <ol>
+ <li>Get your API key from <a href="https://supermemory.ai" target="_blank" rel="noopener noreferrer">supermemory.ai</a></li>
+ <li>Enter your API key above</li>
+ <li>Click "Load Graph" to visualize your memories</li>
+ </ol>
+ <h3>Features to test:</h3>
+ <ul>
+ <li>Pan and zoom the graph</li>
+ <li>Click on nodes to see details</li>
+ <li>Drag nodes around</li>
+ <li>Use the space selector to filter by space</li>
+ <li>Check the legend for statistics</li>
+ </ul>
+ </div>
+ )}
+ </div>
+ )
+}
+
+export default App
diff --git a/packages/memory-graph-playground/src/assets/react.svg b/packages/memory-graph-playground/src/assets/react.svg
new file mode 100644
index 00000000..6c87de9b
--- /dev/null
+++ b/packages/memory-graph-playground/src/assets/react.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg> \ No newline at end of file
diff --git a/packages/memory-graph-playground/src/index.css b/packages/memory-graph-playground/src/index.css
new file mode 100644
index 00000000..b2876d8a
--- /dev/null
+++ b/packages/memory-graph-playground/src/index.css
@@ -0,0 +1,31 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+:root {
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color-scheme: dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: #0f1419;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+body {
+ margin: 0;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+#root {
+ width: 100%;
+ height: 100vh;
+}
diff --git a/packages/memory-graph-playground/src/main.tsx b/packages/memory-graph-playground/src/main.tsx
new file mode 100644
index 00000000..cd61d6d1
--- /dev/null
+++ b/packages/memory-graph-playground/src/main.tsx
@@ -0,0 +1,11 @@
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import './index.css'
+import App from './App.tsx'
+
+createRoot(document.getElementById('root')!).render(
+ <StrictMode>
+ <App />
+ {/*<div>hi</div>*/}
+ </StrictMode>,
+)
diff --git a/packages/memory-graph-playground/tsconfig.app.json b/packages/memory-graph-playground/tsconfig.app.json
new file mode 100644
index 00000000..a9b5a59c
--- /dev/null
+++ b/packages/memory-graph-playground/tsconfig.app.json
@@ -0,0 +1,28 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "ES2022",
+ "useDefineForClassFields": true,
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "types": ["vite/client"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["src"]
+}
diff --git a/packages/memory-graph-playground/tsconfig.json b/packages/memory-graph-playground/tsconfig.json
new file mode 100644
index 00000000..1ffef600
--- /dev/null
+++ b/packages/memory-graph-playground/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "files": [],
+ "references": [
+ { "path": "./tsconfig.app.json" },
+ { "path": "./tsconfig.node.json" }
+ ]
+}
diff --git a/packages/memory-graph-playground/tsconfig.node.json b/packages/memory-graph-playground/tsconfig.node.json
new file mode 100644
index 00000000..8a67f62f
--- /dev/null
+++ b/packages/memory-graph-playground/tsconfig.node.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "target": "ES2023",
+ "lib": ["ES2023"],
+ "module": "ESNext",
+ "types": ["node"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/packages/memory-graph-playground/vite.config.ts b/packages/memory-graph-playground/vite.config.ts
new file mode 100644
index 00000000..8b0f57b9
--- /dev/null
+++ b/packages/memory-graph-playground/vite.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react()],
+})
diff --git a/packages/memory-graph/.gitignore b/packages/memory-graph/.gitignore
new file mode 100644
index 00000000..37e17b72
--- /dev/null
+++ b/packages/memory-graph/.gitignore
@@ -0,0 +1,33 @@
+# Dependencies
+node_modules
+
+# Build output
+dist
+*.tsbuildinfo
+
+# Environment variables
+.env
+.env.local
+.env.production
+
+# Editor
+.vscode
+.idea
+*.swp
+*.swo
+*~
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Testing
+coverage
+.nyc_output
diff --git a/packages/memory-graph/.npmignore b/packages/memory-graph/.npmignore
new file mode 100644
index 00000000..da9b208a
--- /dev/null
+++ b/packages/memory-graph/.npmignore
@@ -0,0 +1,42 @@
+# Source files (only publish dist)
+src
+*.ts
+!*.d.ts
+
+# Config files
+tsconfig.json
+vite.config.ts
+.gitignore
+.npmignore
+
+# Development
+node_modules
+*.log
+.env*
+
+# Editor
+.vscode
+.idea
+*.swp
+*.swo
+*~
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Testing
+coverage
+.nyc_output
+*.test.*
+*.spec.*
+
+# CI/CD
+.github
+.gitlab-ci.yml
+.travis.yml
+
+# Docs (except README)
+docs
+examples
+.changeset
diff --git a/packages/memory-graph/README.md b/packages/memory-graph/README.md
new file mode 100644
index 00000000..3dd6be60
--- /dev/null
+++ b/packages/memory-graph/README.md
@@ -0,0 +1,224 @@
+# @supermemory/memory-graph
+
+> Interactive graph visualization component for Supermemory - visualize and explore your memory connections
+
+[![npm version](https://img.shields.io/npm/v/@supermemory/memory-graph.svg)](https://www.npmjs.com/package/@supermemory/memory-graph)
+[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
+
+## Features
+
+- 🎨 **WebGL-powered rendering** - Smooth performance with hundreds of nodes using PixiJS
+- 🔍 **Interactive exploration** - Pan, zoom, drag nodes, and explore connections
+- 🧠 **Semantic connections** - Visualizes relationships based on content similarity
+- 📱 **Responsive design** - Works seamlessly on mobile and desktop
+- 🎯 **Zero configuration** - Works out of the box with automatic CSS injection
+- 📦 **Lightweight** - Tree-shakeable and optimized bundle
+- 🎭 **TypeScript** - Full TypeScript support with exported types
+
+## Installation
+
+```bash
+npm install @supermemory/memory-graph
+# or
+yarn add @supermemory/memory-graph
+# or
+pnpm add @supermemory/memory-graph
+# or
+bun add @supermemory/memory-graph
+```
+
+## Quick Start
+
+```tsx
+import { MemoryGraph } from '@supermemory/memory-graph'
+
+function App() {
+ return (
+ <MemoryGraph
+ apiKey="your-api-key"
+ id="optional-document-id"
+ />
+ )
+}
+```
+
+That's it! The CSS is automatically injected, no manual imports needed.
+
+## Usage
+
+### Basic Usage
+
+```tsx
+import { MemoryGraph } from '@supermemory/memory-graph'
+
+<MemoryGraph
+ apiKey="your-supermemory-api-key"
+ variant="console"
+/>
+```
+
+### Advanced Usage
+
+```tsx
+import { MemoryGraph } from '@supermemory/memory-graph'
+
+<MemoryGraph
+ apiKey="your-api-key"
+ id="document-123"
+ baseUrl="https://api.supermemory.ai"
+ variant="consumer"
+ showSpacesSelector={true}
+ onError={(error) => {
+ console.error('Failed to load graph:', error)
+ }}
+ onSuccess={(data) => {
+ console.log('Graph loaded:', data)
+ }}
+/>
+```
+
+## API Reference
+
+### Props
+
+| Prop | Type | Default | Description |
+|------|------|---------|-------------|
+| `apiKey` | `string` | **required** | Your Supermemory API key |
+| `id` | `string` | `undefined` | Optional document ID to filter the graph |
+| `baseUrl` | `string` | `"https://api.supermemory.ai"` | API base URL |
+| `variant` | `"console" \| "consumer"` | `"console"` | Visual variant - console for full view, consumer for embedded |
+| `showSpacesSelector` | `boolean` | `true` | Show/hide the spaces filter dropdown |
+| `onError` | `(error: Error) => void` | `undefined` | Callback when data fetching fails |
+| `onSuccess` | `(data: any) => void` | `undefined` | Callback when data is successfully loaded |
+
+## Framework Integration
+
+### Next.js
+
+```tsx
+// app/graph/page.tsx
+'use client'
+
+import { MemoryGraph } from '@supermemory/memory-graph'
+
+export default function GraphPage() {
+ return (
+ <div className="w-full h-screen">
+ <MemoryGraph apiKey={process.env.NEXT_PUBLIC_SUPERMEMORY_API_KEY!} />
+ </div>
+ )
+}
+```
+
+### Vite/React
+
+```tsx
+// src/App.tsx
+import { MemoryGraph } from '@supermemory/memory-graph'
+
+function App() {
+ return (
+ <div style={{ width: '100vw', height: '100vh' }}>
+ <MemoryGraph apiKey={import.meta.env.VITE_SUPERMEMORY_API_KEY} />
+ </div>
+ )
+}
+```
+
+### Create React App
+
+```tsx
+// src/App.tsx
+import { MemoryGraph } from '@supermemory/memory-graph'
+
+function App() {
+ return (
+ <div style={{ width: '100vw', height: '100vh' }}>
+ <MemoryGraph apiKey={process.env.REACT_APP_SUPERMEMORY_API_KEY} />
+ </div>
+ )
+}
+```
+
+## Getting an API Key
+
+1. Visit [supermemory.ai](https://supermemory.ai)
+2. Sign up or log in to your account
+3. Navigate to Settings > API Keys
+4. Generate a new API key
+5. Copy and use it in your application
+
+⚠️ **Security Note**: Never commit API keys to version control. Use environment variables.
+
+## Features in Detail
+
+### WebGL Rendering
+
+The graph uses PixiJS for hardware-accelerated WebGL rendering, enabling smooth interaction with hundreds of nodes and connections.
+
+### Semantic Similarity
+
+Connections between memories are visualized based on semantic similarity, with stronger connections appearing more prominent.
+
+### Interactive Controls
+
+- **Pan**: Click and drag the background
+- **Zoom**: Mouse wheel or pinch on mobile
+- **Select Node**: Click on any document or memory
+- **Drag Nodes**: Click and drag individual nodes
+- **Fit to View**: Auto-fit button to center all content
+
+### Touch Support
+
+Full support for touch gestures including pinch-to-zoom and touch-drag for mobile devices.
+
+## Browser Support
+
+- Chrome/Edge (latest)
+- Firefox (latest)
+- Safari (latest)
+- Mobile browsers with WebGL support
+
+## Requirements
+
+- React 18+
+- Modern browser with WebGL support
+
+## Development
+
+```bash
+# Install dependencies
+bun install
+
+# Build the package
+bun run build
+
+# Watch mode for development
+bun run dev
+
+# Type checking
+bun run check-types
+```
+
+## License
+
+MIT © [Supermemory](https://supermemory.ai)
+
+## Support
+
+- 📧 Email: [email protected]
+- 🐛 Issues: [GitHub Issues](https://github.com/supermemoryai/supermemory/issues)
+- 💬 Discord: [Join our community](https://discord.gg/supermemory)
+
+## Roadmap
+
+- [ ] Custom theme support
+- [ ] Export graph as image
+- [ ] Advanced filtering options
+- [ ] Graph animation presets
+- [ ] Accessibility improvements
+- [ ] Collaboration features
+
+---
+
+Made with ❤️ by the Supermemory team
diff --git a/packages/memory-graph/package.json b/packages/memory-graph/package.json
new file mode 100644
index 00000000..6ffd7317
--- /dev/null
+++ b/packages/memory-graph/package.json
@@ -0,0 +1,81 @@
+{
+ "name": "@supermemory/memory-graph",
+ "version": "0.1.0",
+ "description": "Interactive graph visualization component for Supermemory - visualize and explore your memory connections",
+ "type": "module",
+ "main": "./dist/memory-graph.cjs",
+ "module": "./dist/memory-graph.js",
+ "types": "./dist/index.d.ts",
+ "exports": {
+ ".": {
+ "import": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/memory-graph.js"
+ },
+ "require": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/memory-graph.cjs"
+ }
+ },
+ "./styles.css": "./dist/memory-graph.css",
+ "./package.json": "./package.json"
+ },
+ "files": [
+ "dist",
+ "README.md"
+ ],
+ "sideEffects": [
+ "**/*.css"
+ ],
+ "scripts": {
+ "dev": "vite build --watch",
+ "build": "vite build && tsc --emitDeclarationOnly",
+ "check-types": "tsc --noEmit",
+ "prepublishOnly": "bun run build"
+ },
+ "keywords": [
+ "supermemory",
+ "graph",
+ "visualization",
+ "memory",
+ "interactive",
+ "canvas",
+ "react",
+ "component"
+ ],
+ "author": "Supermemory",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/supermemoryai/supermemory",
+ "directory": "packages/memory-graph"
+ },
+ "bugs": {
+ "url": "https://github.com/supermemoryai/supermemory/issues"
+ },
+ "homepage": "https://github.com/supermemoryai/supermemory/tree/main/packages/memory-graph#readme",
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ },
+ "dependencies": {
+ "@emotion/is-prop-valid": "^1.4.0",
+ "@radix-ui/react-collapsible": "^1.1.12",
+ "@radix-ui/react-slot": "^1.2.4",
+ "@tanstack/react-query": "^5.90.7",
+ "@vanilla-extract/css": "^1.17.4",
+ "@vanilla-extract/recipes": "^0.5.7",
+ "@vanilla-extract/sprinkles": "^1.6.5",
+ "lucide-react": "^0.552.0",
+ "motion": "^12.23.24"
+ },
+ "devDependencies": {
+ "@types/react": "^19.2.2",
+ "@types/react-dom": "^19.2.2",
+ "@vanilla-extract/vite-plugin": "^5.1.1",
+ "@vitejs/plugin-react": "^5.1.0",
+ "typescript": "^5.9.3",
+ "vite": "^7.2.1",
+ "vite-plugin-lib-inject-css": "^2.2.2"
+ }
+}
diff --git a/packages/memory-graph/src/api-types.ts b/packages/memory-graph/src/api-types.ts
new file mode 100644
index 00000000..7742e39f
--- /dev/null
+++ b/packages/memory-graph/src/api-types.ts
@@ -0,0 +1,79 @@
+// Standalone TypeScript types for Memory Graph
+// These mirror the API response types from @repo/validation/api
+
+export interface MemoryEntry {
+ id: string;
+ customId?: string | null;
+ documentId: string;
+ content: string | null;
+ summary?: string | null;
+ title?: string | null;
+ url?: string | null;
+ type?: string | null;
+ metadata?: Record<string, string | number | boolean> | null;
+ embedding?: number[] | null;
+ embeddingModel?: string | null;
+ tokenCount?: number | null;
+ createdAt: string | Date;
+ updatedAt: string | Date;
+ // Fields from join relationship
+ sourceAddedAt?: Date | null;
+ sourceRelevanceScore?: number | null;
+ sourceMetadata?: Record<string, unknown> | null;
+ spaceContainerTag?: string | null;
+ // Version chain fields
+ updatesMemoryId?: string | null;
+ nextVersionId?: string | null;
+ relation?: "updates" | "extends" | "derives" | null;
+ // Memory status fields
+ isForgotten?: boolean;
+ forgetAfter?: Date | string | null;
+ isLatest?: boolean;
+ // Space/container fields
+ spaceId?: string | null;
+ // Legacy fields
+ memory?: string | null;
+ memoryRelations?: Array<{
+ relationType: "updates" | "extends" | "derives";
+ targetMemoryId: string;
+ }> | null;
+ parentMemoryId?: string | null;
+}
+
+export interface DocumentWithMemories {
+ id: string;
+ customId?: string | null;
+ contentHash: string | null;
+ orgId: string;
+ userId: string;
+ connectionId?: string | null;
+ title?: string | null;
+ content?: string | null;
+ summary?: string | null;
+ url?: string | null;
+ source?: string | null;
+ type?: string | null;
+ status: "pending" | "processing" | "done" | "failed";
+ metadata?: Record<string, string | number | boolean> | null;
+ processingMetadata?: Record<string, unknown> | null;
+ raw?: string | null;
+ tokenCount?: number | null;
+ wordCount?: number | null;
+ chunkCount?: number | null;
+ averageChunkSize?: number | null;
+ summaryEmbedding?: number[] | null;
+ summaryEmbeddingModel?: string | null;
+ createdAt: string | Date;
+ updatedAt: string | Date;
+ memoryEntries: MemoryEntry[];
+}
+
+export interface DocumentsResponse {
+ documents: DocumentWithMemories[];
+ pagination: {
+ currentPage: number;
+ limit: number;
+ totalItems: number;
+ totalPages: number;
+ };
+}
diff --git a/packages/memory-graph/src/assets/icons.tsx b/packages/memory-graph/src/assets/icons.tsx
new file mode 100644
index 00000000..5383f690
--- /dev/null
+++ b/packages/memory-graph/src/assets/icons.tsx
@@ -0,0 +1,208 @@
+export const OneDrive = ({ className }: { className?: string }) => (
+ <svg
+ className={className}
+ viewBox="0 0 256 165"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <title>OneDrive</title>
+ <path
+ d="m154.66 110.682l52.842-50.534c-10.976-42.8-54.57-68.597-97.37-57.62a80 80 0 0 0-46.952 33.51c.817-.02 91.48 74.644 91.48 74.644"
+ fill="#0364B8"
+ />
+ <path
+ d="m97.618 45.552l-.002.009a63.7 63.7 0 0 0-33.619-9.543c-.274 0-.544.017-.818.02C27.852 36.476-.432 65.47.005 100.798a63.97 63.97 0 0 0 11.493 35.798l79.165-9.915l60.694-48.94z"
+ fill="#0078D4"
+ />
+ <path
+ d="M207.502 60.148a53 53 0 0 0-3.51-.131a51.8 51.8 0 0 0-20.61 4.254l-.002-.005l-32.022 13.475l35.302 43.607l63.11 15.341c13.62-25.283 4.164-56.82-21.12-70.44a52 52 0 0 0-21.148-6.1"
+ fill="#1490DF"
+ />
+ <path
+ d="M11.498 136.596a63.91 63.91 0 0 0 52.5 27.417h139.994a51.99 51.99 0 0 0 45.778-27.323l-98.413-58.95z"
+ fill="#28A8EA"
+ />
+ </svg>
+);
+
+export const GoogleDrive = ({ className }: { className?: string }) => (
+ <svg
+ className={className}
+ viewBox="0 0 256 229"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <title>Google Drive</title>
+ <path
+ d="m19.354 196.034l11.29 19.5c2.346 4.106 5.718 7.332 9.677 9.678q17.009-21.591 23.68-33.137q6.77-11.717 16.641-36.655q-26.604-3.502-40.32-3.502q-13.165 0-40.322 3.502c0 4.545 1.173 9.09 3.519 13.196z"
+ fill="#0066DA"
+ />
+ <path
+ d="M215.681 225.212c3.96-2.346 7.332-5.572 9.677-9.677l4.692-8.064l22.434-38.855a26.57 26.57 0 0 0 3.518-13.196q-27.315-3.502-40.247-3.502q-13.899 0-40.248 3.502q9.754 25.075 16.422 36.655q6.724 11.683 23.752 33.137"
+ fill="#EA4335"
+ />
+ <path
+ d="M128.001 73.311q19.68-23.768 27.125-36.655q5.996-10.377 13.196-33.137C164.363 1.173 159.818 0 155.126 0h-54.25C96.184 0 91.64 1.32 87.68 3.519q9.16 26.103 15.544 37.154q7.056 12.213 24.777 32.638"
+ fill="#00832D"
+ />
+ <path
+ d="M175.36 155.42H80.642l-40.32 69.792c3.958 2.346 8.503 3.519 13.195 3.519h148.968c4.692 0 9.238-1.32 13.196-3.52z"
+ fill="#2684FC"
+ />
+ <path
+ d="M128.001 73.311L87.681 3.52c-3.96 2.346-7.332 5.571-9.678 9.677L3.519 142.224A26.57 26.57 0 0 0 0 155.42h80.642z"
+ fill="#00AC47"
+ />
+ <path
+ d="m215.242 77.71l-37.243-64.514c-2.345-4.106-5.718-7.331-9.677-9.677l-40.32 69.792l47.358 82.109h80.496c0-4.546-1.173-9.09-3.519-13.196z"
+ fill="#FFBA00"
+ />
+ </svg>
+);
+
+export const Notion = ({ className }: { className?: string }) => (
+ <svg
+ className={className}
+ viewBox="0 0 256 268"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <title>Notion</title>
+ <path
+ d="M16.092 11.538L164.09.608c18.179-1.56 22.85-.508 34.28 7.801l47.243 33.282C253.406 47.414 256 48.975 256 55.207v182.527c0 11.439-4.155 18.205-18.696 19.24L65.44 267.378c-10.913.517-16.11-1.043-21.825-8.327L8.826 213.814C2.586 205.487 0 199.254 0 191.97V29.726c0-9.352 4.155-17.153 16.092-18.188"
+ fill="#FFF"
+ />
+ <path d="M164.09.608L16.092 11.538C4.155 12.573 0 20.374 0 29.726v162.245c0 7.284 2.585 13.516 8.826 21.843l34.789 45.237c5.715 7.284 10.912 8.844 21.825 8.327l171.864-10.404c14.532-1.035 18.696-7.801 18.696-19.24V55.207c0-5.911-2.336-7.614-9.21-12.66l-1.185-.856L198.37 8.409C186.94.1 182.27-.952 164.09.608M69.327 52.22c-14.033.945-17.216 1.159-25.186-5.323L23.876 30.778c-2.06-2.086-1.026-4.69 4.163-5.207l142.274-10.395c11.947-1.043 18.17 3.12 22.842 6.758l24.401 17.68c1.043.525 3.638 3.637.517 3.637L71.146 52.095zm-16.36 183.954V81.222c0-6.767 2.077-9.887 8.3-10.413L230.02 60.93c5.724-.517 8.31 3.12 8.31 9.879v153.917c0 6.767-1.044 12.49-10.387 13.008l-161.487 9.361c-9.343.517-13.489-2.594-13.489-10.921M212.377 89.53c1.034 4.681 0 9.362-4.681 9.897l-7.783 1.542v114.404c-6.758 3.637-12.981 5.715-18.18 5.715c-8.308 0-10.386-2.604-16.609-10.396l-50.898-80.079v77.476l16.1 3.646s0 9.362-12.989 9.362l-35.814 2.077c-1.043-2.086 0-7.284 3.63-8.318l9.351-2.595V109.823l-12.98-1.052c-1.044-4.68 1.55-11.439 8.826-11.965l38.426-2.585l52.958 81.113v-71.76l-13.498-1.552c-1.043-5.733 3.111-9.896 8.3-10.404z" />
+ </svg>
+);
+
+export const GoogleDocs = ({ className }: { className?: string }) => (
+ <svg
+ className={className}
+ viewBox="0 0 24 24"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <title>Google Docs</title>
+ <path
+ d="M14.727 6.727H14V0H4.91c-.905 0-1.637.732-1.637 1.636v20.728c0 .904.732 1.636 1.636 1.636h14.182c.904 0 1.636-.732 1.636-1.636V6.727zm-.545 10.455H7.09v-1.364h7.09v1.364zm2.727-3.273H7.091v-1.364h9.818zm0-3.273H7.091V9.273h9.818zM14.727 6h6l-6-6z"
+ fill="currentColor"
+ />
+ </svg>
+);
+
+export const GoogleSheets = ({ className }: { className?: string }) => (
+ <svg
+ className={className}
+ viewBox="0 0 24 24"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <title>Google Sheets</title>
+ <path
+ d="M11.318 12.545H7.91v-1.909h3.41v1.91zM14.728 0v6h6zm1.363 10.636h-3.41v1.91h3.41zm0 3.273h-3.41v1.91h3.41zM20.727 6.5v15.864c0 .904-.732 1.636-1.636 1.636H4.909a1.636 1.636 0 0 1-1.636-1.636V1.636C3.273.732 4.005 0 4.909 0h9.318v6.5zm-3.273 2.773H6.545v7.909h10.91v-7.91zm-6.136 4.636H7.91v1.91h3.41v-1.91z"
+ fill="currentColor"
+ />
+ </svg>
+);
+
+export const GoogleSlides = ({ className }: { className?: string }) => (
+ <svg
+ className={className}
+ viewBox="0 0 24 24"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <title>Google Slides</title>
+ <path
+ d="M16.09 15.273H7.91v-4.637h8.18zm1.728-8.523h2.91v15.614c0 .904-.733 1.636-1.637 1.636H4.909a1.636 1.636 0 0 1-1.636-1.636V1.636C3.273.732 4.005 0 4.909 0h9.068v6.75zm-.363 2.523H6.545v7.363h10.91zm-2.728-5.979V6h6.001l-6-6v3.294z"
+ fill="currentColor"
+ />
+ </svg>
+);
+
+export const NotionDoc = ({ className }: { className?: string }) => (
+ <svg
+ className={className}
+ viewBox="0 0 24 24"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <title>Notion Doc</title>
+ <path
+ d="M4.459 4.208c.746.606 1.026.56 2.428.466l13.215-.793c.28 0 .047-.28-.046-.326L17.86 1.968c-.42-.326-.981-.7-2.055-.607L3.01 2.295c-.466.046-.56.28-.374.466zm.793 3.08v13.904c0 .747.373 1.027 1.214.98l14.523-.84c.841-.046.935-.56.935-1.167V6.354c0-.606-.233-.933-.748-.887l-15.177.887c-.56.047-.747.327-.747.933zm14.337.745c.093.42 0 .84-.42.888l-.7.14v10.264c-.608.327-1.168.514-1.635.514c-.748 0-.935-.234-1.495-.933l-4.577-7.186v6.952L12.21 19s0 .84-1.168.84l-3.222.186c-.093-.186 0-.653.327-.746l.84-.233V9.854L7.822 9.76c-.094-.42.14-1.026.793-1.073l3.456-.233l4.764 7.279v-6.44l-1.215-.139c-.093-.514.28-.887.747-.933zM1.936 1.035l13.31-.98c1.634-.14 2.055-.047 3.082.7l4.249 2.986c.7.513.934.653.934 1.213v16.378c0 1.026-.373 1.634-1.68 1.726l-15.458.934c-.98.047-1.448-.093-1.962-.747l-3.129-4.06c-.56-.747-.793-1.306-.793-1.96V2.667c0-.839.374-1.54 1.447-1.632"
+ fill="currentColor"
+ />
+ </svg>
+);
+
+export const MicrosoftWord = ({ className }: { className?: string }) => (
+ <svg
+ className={className}
+ viewBox="0 0 24 24"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <title>Microsoft Word</title>
+ <path
+ d="M23.004 1.5q.41 0 .703.293t.293.703v19.008q0 .41-.293.703t-.703.293H6.996q-.41 0-.703-.293T6 21.504V18H.996q-.41 0-.703-.293T0 17.004V6.996q0-.41.293-.703T.996 6H6V2.496q0-.41.293-.703t.703-.293zM6.035 11.203l1.442 4.735h1.64l1.57-7.876H9.036l-.937 4.653l-1.325-4.5H5.38l-1.406 4.523l-.938-4.675H1.312l1.57 7.874h1.641zM22.5 21v-3h-15v3zm0-4.5v-3.75H12v3.75zm0-5.25V7.5H12v3.75zm0-5.25V3h-15v3Z"
+ fill="currentColor"
+ />
+ </svg>
+);
+
+export const MicrosoftExcel = ({ className }: { className?: string }) => (
+ <svg
+ className={className}
+ viewBox="0 0 24 24"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <title>Microsoft Excel</title>
+ <path
+ d="M23 1.5q.41 0 .7.3q.3.29.3.7v19q0 .41-.3.7q-.29.3-.7.3H7q-.41 0-.7-.3q-.3-.29-.3-.7V18H1q-.41 0-.7-.3q-.3-.29-.3-.7V7q0-.41.3-.7Q.58 6 1 6h5V2.5q0-.41.3-.7q.29-.3.7-.3zM6 13.28l1.42 2.66h2.14l-2.38-3.87l2.34-3.8H7.46l-1.3 2.4l-.05.08l-.04.09l-.64-1.28l-.66-1.29H2.59l2.27 3.82l-2.48 3.85h2.16zM14.25 21v-3H7.5v3zm0-4.5v-3.75H12v3.75zm0-5.25V7.5H12v3.75zm0-5.25V3H7.5v3zm8.25 15v-3h-6.75v3zm0-4.5v-3.75h-6.75v3.75zm0-5.25V7.5h-6.75v3.75zm0-5.25V3h-6.75v3Z"
+ fill="currentColor"
+ />
+ </svg>
+);
+
+export const MicrosoftPowerpoint = ({ className }: { className?: string }) => (
+ <svg
+ className={className}
+ viewBox="0 0 24 24"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <title>Microsoft PowerPoint</title>
+ <path
+ d="M13.5 1.5q1.453 0 2.795.375t2.508 1.06t2.12 1.641q.956.955 1.641 2.121q.686 1.166 1.061 2.508T24 12t-.375 2.795t-1.06 2.508q-.686 1.166-1.641 2.12q-.955.956-2.121 1.641q-1.166.686-2.508 1.061T13.5 22.5q-1.29 0-2.52-.305q-1.23-.304-2.337-.884T6.58 19.893Q5.625 19.055 4.887 18H.997q-.411 0-.704-.293T0 17.004V6.996q0-.41.293-.703T.996 6h3.89q.739-1.055 1.694-1.893q.955-.837 2.063-1.418q1.107-.58 2.337-.884T13.5 1.5m.75 1.535v8.215h8.215q-.14-1.64-.826-3.076t-1.782-2.531q-1.095-1.096-2.537-1.782t-3.07-.826m-5.262 7.57q0-.68-.228-1.166q-.229-.486-.627-.79q-.399-.305-.938-.446q-.539-.14-1.172-.14H2.848v7.863h1.84v-2.742H5.93q.574 0 1.119-.17t.978-.493q.434-.322.698-.802t.263-1.114M13.5 21q1.172 0 2.262-.287t2.056-.82t1.776-1.278q.808-.744 1.418-1.664t.984-1.986q.375-1.067.469-2.227h-9.703V3.035q-1.735.14-3.27.908T6.797 6h4.207q.41 0 .703.293t.293.703v10.008q0 .41-.293.703t-.703.293H6.797q.644.715 1.412 1.271q.768.557 1.623.944t1.781.586T13.5 21M5.812 9.598q.575 0 .915.228q.34.229.34.838q0 .27-.124.44q-.123.17-.31.275q-.188.105-.422.146t-.445.041H4.687V9.598Z"
+ fill="currentColor"
+ />
+ </svg>
+);
+
+export const MicrosoftOneNote = ({ className }: { className?: string }) => (
+ <svg
+ className={className}
+ viewBox="0 0 24 24"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <title>Microsoft OneNote</title>
+ <path
+ d="M23 1.5q.41 0 .7.3q.3.29.3.7v19q0 .41-.3.7q-.29.3-.7.3H7q-.41 0-.7-.3q-.3-.29-.3-.7V18H1q-.41 0-.7-.3q-.3-.29-.3-.7V7q0-.41.3-.7Q.58 6 1 6h5V2.5q0-.41.3-.7q.29-.3.7-.3ZM4.56 11l2.83 4.93h1.79V8.07H7.44v5.03L4.71 8.07H2.82v7.86h1.74ZM22.5 21v-3h-3v3Zm0-4.5v-3h-3v3Zm0-4.5V9h-3v3Zm0-4.5V3h-15v3H11q.41 0 .7.3q.3.29.3.7v10q0 .41-.3.7q-.29.3-.7.3H7.5v3H18V7.5Z"
+ fill="currentColor"
+ />
+ </svg>
+);
+
+export const PDF = ({ className }: { className?: string }) => (
+ <svg
+ className={className}
+ viewBox="0 0 15 16"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <title>PDF</title>
+ <path
+ d="M3 13h.86v-.9h.39c.62 0 1.14-.45 1.14-1.06s-.5-1.05-1.14-1.05H3v3Zm.86-1.59v-.72h.3c.2 0 .37.13.37.35s-.16.36-.37.36h-.3ZM6.19 13h1.19c1 0 1.62-.59 1.62-1.52C9 10.61 8.38 10 7.38 10H6.19zm.86-.71V10.7h.29c.33 0 .78.16.78.78c0 .65-.45.81-.78.81zM10 13h.86v-1.07h1.06v-.69h-1.06v-.54h1.21v-.69h-2.06v3Z"
+ fill="currentColor"
+ />
+ <path
+ d="M12.5 16h-10c-.83 0-1.5-.67-1.5-1.5v-13C1 .67 1.67 0 2.5 0h7.09c.4 0 .78.16 1.06.44l2.91 2.91c.28.28.44.66.44 1.06V14.5c0 .83-.67 1.5-1.5 1.5M2.5 1c-.28 0-.5.22-.5.5v13c0 .28.22.5.5.5h10c.28 0 .5-.22.5-.5V4.41a.47.47 0 0 0-.15-.35L9.94 1.15A.5.5 0 0 0 9.59 1z"
+ fill="currentColor"
+ />
+ <path
+ d="M13.38 5h-2.91C9.66 5 9 4.34 9 3.53V.62c0-.28.22-.5.5-.5s.5.22.5.5v2.91c0 .26.21.47.47.47h2.91c.28 0 .5.22.5.5s-.22.5-.5.5"
+ fill="currentColor"
+ />
+ </svg>
+);
diff --git a/packages/memory-graph/src/components/canvas-common.css.ts b/packages/memory-graph/src/components/canvas-common.css.ts
new file mode 100644
index 00000000..91005488
--- /dev/null
+++ b/packages/memory-graph/src/components/canvas-common.css.ts
@@ -0,0 +1,10 @@
+import { style } from "@vanilla-extract/css";
+
+/**
+ * Canvas wrapper/container that fills its parent
+ * Used by both graph-canvas and graph-webgl-canvas
+ */
+export const canvasWrapper = style({
+ position: "absolute",
+ inset: 0,
+});
diff --git a/packages/memory-graph/src/components/graph-canvas.tsx b/packages/memory-graph/src/components/graph-canvas.tsx
new file mode 100644
index 00000000..59efa74d
--- /dev/null
+++ b/packages/memory-graph/src/components/graph-canvas.tsx
@@ -0,0 +1,764 @@
+"use client";
+
+import {
+ memo,
+ useCallback,
+ useEffect,
+ useLayoutEffect,
+ useMemo,
+ useRef,
+} from "react";
+import { colors } from "@/constants";
+import type {
+ DocumentWithMemories,
+ GraphCanvasProps,
+ GraphNode,
+ MemoryEntry,
+} from "@/types";
+import { canvasWrapper } from "./canvas-common.css";
+
+export const GraphCanvas = memo<GraphCanvasProps>(
+ ({
+ nodes,
+ edges,
+ panX,
+ panY,
+ zoom,
+ width,
+ height,
+ onNodeHover,
+ onNodeClick,
+ onNodeDragStart,
+ onNodeDragMove,
+ onNodeDragEnd,
+ onPanStart,
+ onPanMove,
+ onPanEnd,
+ onWheel,
+ onDoubleClick,
+ onTouchStart,
+ onTouchMove,
+ onTouchEnd,
+ draggingNodeId,
+ highlightDocumentIds,
+ }) => {
+ const canvasRef = useRef<HTMLCanvasElement>(null);
+ const animationRef = useRef<number>(0);
+ const startTimeRef = useRef<number>(Date.now());
+ const mousePos = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
+ const currentHoveredNode = useRef<string | null>(null);
+
+ // Initialize start time once
+ useEffect(() => {
+ startTimeRef.current = Date.now();
+ }, []);
+
+ // Efficient hit detection
+ const getNodeAtPosition = useCallback(
+ (x: number, y: number): string | null => {
+ // Check from top-most to bottom-most: memory nodes are drawn after documents
+ for (let i = nodes.length - 1; i >= 0; i--) {
+ const node = nodes[i]!;
+ const screenX = node.x * zoom + panX;
+ const screenY = node.y * zoom + panY;
+ const nodeSize = node.size * zoom;
+
+ const dx = x - screenX;
+ const dy = y - screenY;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+
+ if (distance <= nodeSize / 2) {
+ return node.id;
+ }
+ }
+ return null;
+ },
+ [nodes, panX, panY, zoom],
+ );
+
+ // Handle mouse events
+ const handleMouseMove = useCallback(
+ (e: React.MouseEvent) => {
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+
+ const rect = canvas.getBoundingClientRect();
+ const x = e.clientX - rect.left;
+ const y = e.clientY - rect.top;
+
+ mousePos.current = { x, y };
+
+ const nodeId = getNodeAtPosition(x, y);
+ if (nodeId !== currentHoveredNode.current) {
+ currentHoveredNode.current = nodeId;
+ onNodeHover(nodeId);
+ }
+
+ // Handle node dragging
+ if (draggingNodeId) {
+ onNodeDragMove(e);
+ }
+ },
+ [getNodeAtPosition, onNodeHover, draggingNodeId, onNodeDragMove],
+ );
+
+ const handleMouseDown = useCallback(
+ (e: React.MouseEvent) => {
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+
+ const rect = canvas.getBoundingClientRect();
+ const x = e.clientX - rect.left;
+ const y = e.clientY - rect.top;
+
+ const nodeId = getNodeAtPosition(x, y);
+ if (nodeId) {
+ // When starting a node drag, prevent initiating pan
+ e.stopPropagation();
+ onNodeDragStart(nodeId, e);
+ return;
+ }
+ onPanStart(e);
+ },
+ [getNodeAtPosition, onNodeDragStart, onPanStart],
+ );
+
+ const handleClick = useCallback(
+ (e: React.MouseEvent) => {
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+
+ const rect = canvas.getBoundingClientRect();
+ const x = e.clientX - rect.left;
+ const y = e.clientY - rect.top;
+
+ const nodeId = getNodeAtPosition(x, y);
+ if (nodeId) {
+ onNodeClick(nodeId);
+ }
+ },
+ [getNodeAtPosition, onNodeClick],
+ );
+
+ // Professional rendering function with LOD
+ const render = useCallback(() => {
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+
+ const ctx = canvas.getContext("2d");
+ if (!ctx) return;
+
+ const currentTime = Date.now();
+ const _elapsed = currentTime - startTimeRef.current;
+
+ // Level-of-detail optimization based on zoom
+ const useSimplifiedRendering = zoom < 0.3;
+
+ // Clear canvas
+ ctx.clearRect(0, 0, width, height);
+
+ // Set high quality rendering
+ ctx.imageSmoothingEnabled = true;
+ ctx.imageSmoothingQuality = "high";
+
+ // Draw minimal background grid
+ ctx.strokeStyle = "rgba(148, 163, 184, 0.03)"; // Very subtle grid
+ ctx.lineWidth = 1;
+ const gridSpacing = 100 * zoom;
+ const offsetX = panX % gridSpacing;
+ const offsetY = panY % gridSpacing;
+
+ // Simple, clean grid lines
+ for (let x = offsetX; x < width; x += gridSpacing) {
+ ctx.beginPath();
+ ctx.moveTo(x, 0);
+ ctx.lineTo(x, height);
+ ctx.stroke();
+ }
+ for (let y = offsetY; y < height; y += gridSpacing) {
+ ctx.beginPath();
+ ctx.moveTo(0, y);
+ ctx.lineTo(width, y);
+ ctx.stroke();
+ }
+
+ // Create node lookup map
+ const nodeMap = new Map(nodes.map((node) => [node.id, node]));
+
+ // Draw enhanced edges with sophisticated styling
+ ctx.lineCap = "round";
+ edges.forEach((edge) => {
+ const sourceNode = nodeMap.get(edge.source);
+ const targetNode = nodeMap.get(edge.target);
+
+ if (sourceNode && targetNode) {
+ const sourceX = sourceNode.x * zoom + panX;
+ const sourceY = sourceNode.y * zoom + panY;
+ const targetX = targetNode.x * zoom + panX;
+ const targetY = targetNode.y * zoom + panY;
+
+ // Enhanced viewport culling with edge type considerations
+ if (
+ sourceX < -100 ||
+ sourceX > width + 100 ||
+ targetX < -100 ||
+ targetX > width + 100
+ ) {
+ return;
+ }
+
+ // Skip very weak connections when zoomed out for performance
+ if (useSimplifiedRendering) {
+ if (
+ edge.edgeType === "doc-memory" &&
+ edge.visualProps.opacity < 0.3
+ ) {
+ return; // Skip very weak doc-memory edges when zoomed out
+ }
+ }
+
+ // Enhanced connection styling based on edge type
+ let connectionColor = colors.connection.weak;
+ let dashPattern: number[] = [];
+ let opacity = edge.visualProps.opacity;
+ let lineWidth = Math.max(1, edge.visualProps.thickness * zoom);
+
+ if (edge.edgeType === "doc-memory") {
+ // Doc-memory: Solid thin lines, subtle
+ dashPattern = [];
+ connectionColor = colors.connection.memory;
+ opacity = 0.9;
+ lineWidth = 1;
+ } else if (edge.edgeType === "doc-doc") {
+ // Doc-doc: Thick dashed lines with strong similarity emphasis
+ dashPattern = useSimplifiedRendering ? [] : [10, 5]; // Solid lines when zoomed out
+ opacity = Math.max(0, edge.similarity * 0.5);
+ lineWidth = Math.max(1, edge.similarity * 2); // Thicker for stronger similarity
+
+ if (edge.similarity > 0.85)
+ connectionColor = colors.connection.strong;
+ else if (edge.similarity > 0.725)
+ connectionColor = colors.connection.medium;
+ } else if (edge.edgeType === "version") {
+ // Version chains: Double line effect with relation-specific colors
+ dashPattern = [];
+ connectionColor = edge.color || colors.relations.updates;
+ opacity = 0.8;
+ lineWidth = 2;
+ }
+
+ ctx.strokeStyle = connectionColor;
+ ctx.lineWidth = lineWidth;
+ ctx.globalAlpha = opacity;
+ ctx.setLineDash(dashPattern);
+
+ if (edge.edgeType === "version") {
+ // Special double-line rendering for version chains
+ // First line (outer)
+ ctx.lineWidth = 3;
+ ctx.globalAlpha = opacity * 0.3;
+ ctx.beginPath();
+ ctx.moveTo(sourceX, sourceY);
+ ctx.lineTo(targetX, targetY);
+ ctx.stroke();
+
+ // Second line (inner)
+ ctx.lineWidth = 1;
+ ctx.globalAlpha = opacity;
+ ctx.beginPath();
+ ctx.moveTo(sourceX, sourceY);
+ ctx.lineTo(targetX, targetY);
+ ctx.stroke();
+ } else {
+ // Simplified lines when zoomed out, curved when zoomed in
+ if (useSimplifiedRendering) {
+ // Straight lines for performance
+ ctx.beginPath();
+ ctx.moveTo(sourceX, sourceY);
+ ctx.lineTo(targetX, targetY);
+ ctx.stroke();
+ } else {
+ // Regular curved line for doc-memory and doc-doc
+ const midX = (sourceX + targetX) / 2;
+ const midY = (sourceY + targetY) / 2;
+ const dx = targetX - sourceX;
+ const dy = targetY - sourceY;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+ const controlOffset =
+ edge.edgeType === "doc-memory"
+ ? 15
+ : Math.min(30, distance * 0.2);
+
+ ctx.beginPath();
+ ctx.moveTo(sourceX, sourceY);
+ ctx.quadraticCurveTo(
+ midX + controlOffset * (dy / distance),
+ midY - controlOffset * (dx / distance),
+ targetX,
+ targetY,
+ );
+ ctx.stroke();
+ }
+ }
+
+ // Subtle arrow head for version edges
+ if (edge.edgeType === "version") {
+ const angle = Math.atan2(targetY - sourceY, targetX - sourceX);
+ const arrowLength = Math.max(6, 8 * zoom); // Shorter, more subtle
+ const arrowWidth = Math.max(8, 12 * zoom);
+
+ // Calculate arrow position offset from node edge
+ const nodeRadius = (targetNode.size * zoom) / 2;
+ const offsetDistance = nodeRadius + 2;
+ const arrowX = targetX - Math.cos(angle) * offsetDistance;
+ const arrowY = targetY - Math.sin(angle) * offsetDistance;
+
+ ctx.save();
+ ctx.translate(arrowX, arrowY);
+ ctx.rotate(angle);
+ ctx.setLineDash([]);
+
+ // Simple outlined arrow (not filled)
+ ctx.strokeStyle = connectionColor;
+ ctx.lineWidth = Math.max(1, 1.5 * zoom);
+ ctx.globalAlpha = opacity;
+
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(-arrowLength, arrowWidth / 2);
+ ctx.moveTo(0, 0);
+ ctx.lineTo(-arrowLength, -arrowWidth / 2);
+ ctx.stroke();
+
+ ctx.restore();
+ }
+ }
+ });
+
+ ctx.globalAlpha = 1;
+ ctx.setLineDash([]);
+
+ // Prepare highlight set from provided document IDs (customId or internal)
+ const highlightSet = new Set<string>(highlightDocumentIds ?? []);
+
+ // Draw nodes with enhanced styling and LOD optimization
+ nodes.forEach((node) => {
+ const screenX = node.x * zoom + panX;
+ const screenY = node.y * zoom + panY;
+ const nodeSize = node.size * zoom;
+
+ // Enhanced viewport culling
+ const margin = nodeSize + 50;
+ if (
+ screenX < -margin ||
+ screenX > width + margin ||
+ screenY < -margin ||
+ screenY > height + margin
+ ) {
+ return;
+ }
+
+ const isHovered = currentHoveredNode.current === node.id;
+ const isDragging = node.isDragging;
+ const isHighlightedDocument = (() => {
+ if (node.type !== "document" || highlightSet.size === 0) return false;
+ const doc = node.data as DocumentWithMemories;
+ if (doc.customId && highlightSet.has(doc.customId)) return true;
+ return highlightSet.has(doc.id);
+ })();
+
+ if (node.type === "document") {
+ // Enhanced glassmorphism document styling
+ const docWidth = nodeSize * 1.4;
+ const docHeight = nodeSize * 0.9;
+
+ // Multi-layer glass effect
+ ctx.fillStyle = isDragging
+ ? colors.document.accent
+ : isHovered
+ ? colors.document.secondary
+ : colors.document.primary;
+ ctx.globalAlpha = 1;
+
+ // Enhanced border with subtle glow
+ ctx.strokeStyle = isDragging
+ ? colors.document.glow
+ : isHovered
+ ? colors.document.accent
+ : colors.document.border;
+ ctx.lineWidth = isDragging ? 3 : isHovered ? 2 : 1;
+
+ // Rounded rectangle with enhanced styling
+ const radius = useSimplifiedRendering ? 6 : 12;
+ ctx.beginPath();
+ ctx.roundRect(
+ screenX - docWidth / 2,
+ screenY - docHeight / 2,
+ docWidth,
+ docHeight,
+ radius,
+ );
+ ctx.fill();
+ ctx.stroke();
+
+ // Subtle inner highlight for glass effect (skip when zoomed out)
+ if (!useSimplifiedRendering && (isHovered || isDragging)) {
+ ctx.strokeStyle = "rgba(255, 255, 255, 0.1)";
+ ctx.lineWidth = 1;
+ ctx.beginPath();
+ ctx.roundRect(
+ screenX - docWidth / 2 + 1,
+ screenY - docHeight / 2 + 1,
+ docWidth - 2,
+ docHeight - 2,
+ radius - 1,
+ );
+ ctx.stroke();
+ }
+
+ // Highlight ring for search hits
+ if (isHighlightedDocument) {
+ ctx.save();
+ ctx.globalAlpha = 0.9;
+ ctx.strokeStyle = colors.accent.primary;
+ ctx.lineWidth = 3;
+ ctx.setLineDash([6, 4]);
+ const ringPadding = 10;
+ ctx.beginPath();
+ ctx.roundRect(
+ screenX - docWidth / 2 - ringPadding,
+ screenY - docHeight / 2 - ringPadding,
+ docWidth + ringPadding * 2,
+ docHeight + ringPadding * 2,
+ radius + 6,
+ );
+ ctx.stroke();
+ ctx.setLineDash([]);
+ ctx.restore();
+ }
+ } else {
+ // Enhanced memory styling with status indicators
+ const mem = node.data as MemoryEntry;
+ const isForgotten =
+ mem.isForgotten ||
+ (mem.forgetAfter &&
+ new Date(mem.forgetAfter).getTime() < Date.now());
+ const isLatest = mem.isLatest;
+
+ // Check if memory is expiring soon (within 7 days)
+ const expiringSoon =
+ mem.forgetAfter &&
+ !isForgotten &&
+ new Date(mem.forgetAfter).getTime() - Date.now() <
+ 1000 * 60 * 60 * 24 * 7;
+
+ // Check if memory is new (created within last 24 hours)
+ const isNew =
+ !isForgotten &&
+ new Date(mem.createdAt).getTime() >
+ Date.now() - 1000 * 60 * 60 * 24;
+
+ // Determine colors based on status
+ let fillColor = colors.memory.primary;
+ let borderColor = colors.memory.border;
+ let glowColor = colors.memory.glow;
+
+ if (isForgotten) {
+ fillColor = colors.status.forgotten;
+ borderColor = "rgba(220,38,38,0.3)";
+ glowColor = "rgba(220,38,38,0.2)";
+ } else if (expiringSoon) {
+ borderColor = colors.status.expiring;
+ glowColor = colors.accent.amber;
+ } else if (isNew) {
+ borderColor = colors.status.new;
+ glowColor = colors.accent.emerald;
+ }
+
+ if (isDragging) {
+ fillColor = colors.memory.accent;
+ borderColor = glowColor;
+ } else if (isHovered) {
+ fillColor = colors.memory.secondary;
+ }
+
+ const radius = nodeSize / 2;
+
+ ctx.fillStyle = fillColor;
+ ctx.globalAlpha = isLatest ? 1 : 0.4;
+ ctx.strokeStyle = borderColor;
+ ctx.lineWidth = isDragging ? 3 : isHovered ? 2 : 1.5;
+
+ if (useSimplifiedRendering) {
+ // Simple circles when zoomed out for performance
+ ctx.beginPath();
+ ctx.arc(screenX, screenY, radius, 0, 2 * Math.PI);
+ ctx.fill();
+ ctx.stroke();
+ } else {
+ // HEXAGONAL memory nodes when zoomed in
+ const sides = 6;
+ ctx.beginPath();
+ for (let i = 0; i < sides; i++) {
+ const angle = (i * 2 * Math.PI) / sides - Math.PI / 2; // Start from top
+ const x = screenX + radius * Math.cos(angle);
+ const y = screenY + radius * Math.sin(angle);
+ if (i === 0) {
+ ctx.moveTo(x, y);
+ } else {
+ ctx.lineTo(x, y);
+ }
+ }
+ ctx.closePath();
+ ctx.fill();
+ ctx.stroke();
+
+ // Inner highlight for glass effect
+ if (isHovered || isDragging) {
+ ctx.strokeStyle = "rgba(147, 197, 253, 0.3)";
+ ctx.lineWidth = 1;
+ const innerRadius = radius - 2;
+ ctx.beginPath();
+ for (let i = 0; i < sides; i++) {
+ const angle = (i * 2 * Math.PI) / sides - Math.PI / 2;
+ const x = screenX + innerRadius * Math.cos(angle);
+ const y = screenY + innerRadius * Math.sin(angle);
+ if (i === 0) {
+ ctx.moveTo(x, y);
+ } else {
+ ctx.lineTo(x, y);
+ }
+ }
+ ctx.closePath();
+ ctx.stroke();
+ }
+ }
+
+ // Status indicators overlay (always preserve these as required)
+ if (isForgotten) {
+ // Cross for forgotten memories
+ ctx.strokeStyle = "rgba(220,38,38,0.4)";
+ ctx.lineWidth = 2;
+ const r = nodeSize * 0.25;
+ ctx.beginPath();
+ ctx.moveTo(screenX - r, screenY - r);
+ ctx.lineTo(screenX + r, screenY + r);
+ ctx.moveTo(screenX + r, screenY - r);
+ ctx.lineTo(screenX - r, screenY + r);
+ ctx.stroke();
+ } else if (isNew) {
+ // Small dot for new memories
+ ctx.fillStyle = colors.status.new;
+ ctx.beginPath();
+ ctx.arc(
+ screenX + nodeSize * 0.25,
+ screenY - nodeSize * 0.25,
+ Math.max(2, nodeSize * 0.15), // Scale with node size, minimum 2px
+ 0,
+ 2 * Math.PI,
+ );
+ ctx.fill();
+ }
+ }
+
+ // Enhanced hover glow effect (skip when zoomed out for performance)
+ if (!useSimplifiedRendering && (isHovered || isDragging)) {
+ const glowColor =
+ node.type === "document"
+ ? colors.document.glow
+ : colors.memory.glow;
+
+ ctx.strokeStyle = glowColor;
+ ctx.lineWidth = 1;
+ ctx.setLineDash([3, 3]);
+ ctx.globalAlpha = 0.6;
+
+ ctx.beginPath();
+ const glowSize = nodeSize * 0.7;
+ if (node.type === "document") {
+ ctx.roundRect(
+ screenX - glowSize,
+ screenY - glowSize / 1.4,
+ glowSize * 2,
+ glowSize * 1.4,
+ 15,
+ );
+ } else {
+ // Hexagonal glow for memory nodes
+ const glowRadius = glowSize;
+ const sides = 6;
+ for (let i = 0; i < sides; i++) {
+ const angle = (i * 2 * Math.PI) / sides - Math.PI / 2;
+ const x = screenX + glowRadius * Math.cos(angle);
+ const y = screenY + glowRadius * Math.sin(angle);
+ if (i === 0) {
+ ctx.moveTo(x, y);
+ } else {
+ ctx.lineTo(x, y);
+ }
+ }
+ ctx.closePath();
+ }
+ ctx.stroke();
+ ctx.setLineDash([]);
+ }
+ });
+
+ ctx.globalAlpha = 1;
+ }, [nodes, edges, panX, panY, zoom, width, height, highlightDocumentIds]);
+
+ // Change-based rendering instead of continuous animation
+ const lastRenderParams = useRef<string>("");
+
+ // Create a render key that changes when visual state changes
+ const renderKey = useMemo(() => {
+ const nodePositions = nodes
+ .map(
+ (n) =>
+ `${n.id}:${n.x}:${n.y}:${n.isDragging ? "1" : "0"}:${currentHoveredNode.current === n.id ? "1" : "0"}`,
+ )
+ .join("|");
+ const highlightKey = (highlightDocumentIds ?? []).join("|");
+ return `${nodePositions}-${edges.length}-${panX}-${panY}-${zoom}-${width}-${height}-${highlightKey}`;
+ }, [
+ nodes,
+ edges.length,
+ panX,
+ panY,
+ zoom,
+ width,
+ height,
+ highlightDocumentIds,
+ ]);
+
+ // Only render when something actually changed
+ useEffect(() => {
+ if (renderKey !== lastRenderParams.current) {
+ lastRenderParams.current = renderKey;
+ render();
+ }
+ }, [renderKey, render]);
+
+ // Cleanup any existing animation frames
+ useEffect(() => {
+ return () => {
+ if (animationRef.current) {
+ cancelAnimationFrame(animationRef.current);
+ }
+ };
+ }, []);
+
+ // Add native wheel event listener to prevent browser zoom
+ useEffect(() => {
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+
+ const handleNativeWheel = (e: WheelEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ // Call the onWheel handler with a synthetic-like event
+ // @ts-expect-error - partial WheelEvent object
+ onWheel({
+ deltaY: e.deltaY,
+ deltaX: e.deltaX,
+ clientX: e.clientX,
+ clientY: e.clientY,
+ currentTarget: canvas,
+ nativeEvent: e,
+ preventDefault: () => {},
+ stopPropagation: () => {},
+ } as React.WheelEvent);
+ };
+
+ // Add listener with passive: false to ensure preventDefault works
+ canvas.addEventListener("wheel", handleNativeWheel, { passive: false });
+
+ // Also prevent gesture events for touch devices
+ const handleGesture = (e: Event) => {
+ e.preventDefault();
+ };
+
+ canvas.addEventListener("gesturestart", handleGesture, {
+ passive: false,
+ });
+ canvas.addEventListener("gesturechange", handleGesture, {
+ passive: false,
+ });
+ canvas.addEventListener("gestureend", handleGesture, { passive: false });
+
+ return () => {
+ canvas.removeEventListener("wheel", handleNativeWheel);
+ canvas.removeEventListener("gesturestart", handleGesture);
+ canvas.removeEventListener("gesturechange", handleGesture);
+ canvas.removeEventListener("gestureend", handleGesture);
+ };
+ }, [onWheel]);
+
+ // High-DPI handling --------------------------------------------------
+ const dpr =
+ typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
+
+ useLayoutEffect(() => {
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+
+ // upscale backing store
+ canvas.style.width = `${width}px`;
+ canvas.style.height = `${height}px`;
+ canvas.width = width * dpr;
+ canvas.height = height * dpr;
+
+ const ctx = canvas.getContext("2d");
+ ctx?.scale(dpr, dpr);
+ }, [width, height, dpr]);
+ // -----------------------------------------------------------------------
+
+ return (
+ <canvas
+ className={canvasWrapper}
+ height={height}
+ onClick={handleClick}
+ onDoubleClick={onDoubleClick}
+ onMouseDown={handleMouseDown}
+ onMouseLeave={() => {
+ if (draggingNodeId) {
+ onNodeDragEnd();
+ } else {
+ onPanEnd();
+ }
+ }}
+ onMouseMove={(e) => {
+ handleMouseMove(e);
+ if (!draggingNodeId) {
+ onPanMove(e);
+ }
+ }}
+ onMouseUp={() => {
+ if (draggingNodeId) {
+ onNodeDragEnd();
+ } else {
+ onPanEnd();
+ }
+ }}
+ onTouchStart={onTouchStart}
+ onTouchMove={onTouchMove}
+ onTouchEnd={onTouchEnd}
+ ref={canvasRef}
+ style={{
+ cursor: draggingNodeId
+ ? "grabbing"
+ : currentHoveredNode.current
+ ? "grab"
+ : "move",
+ touchAction: "none",
+ userSelect: "none",
+ WebkitUserSelect: "none",
+ }}
+ width={width}
+ />
+ );
+ },
+);
+
+GraphCanvas.displayName = "GraphCanvas";
diff --git a/packages/memory-graph/src/components/legend.css.ts b/packages/memory-graph/src/components/legend.css.ts
new file mode 100644
index 00000000..b758cf9d
--- /dev/null
+++ b/packages/memory-graph/src/components/legend.css.ts
@@ -0,0 +1,345 @@
+import { style, styleVariants, globalStyle } from "@vanilla-extract/css";
+import { themeContract } from "../styles/theme.css";
+
+/**
+ * Legend container base
+ */
+const legendContainerBase = style({
+ position: "absolute",
+ zIndex: 20, // Above most elements but below node detail panel
+ borderRadius: themeContract.radii.xl,
+ overflow: "hidden",
+ width: "fit-content",
+ height: "fit-content",
+ maxHeight: "calc(100vh - 2rem)", // Prevent overflow
+});
+
+/**
+ * Legend container variants for positioning
+ * Console: Bottom-right (doesn't conflict with anything)
+ * Consumer: Bottom-right (moved from top to avoid conflicts)
+ */
+export const legendContainer = styleVariants({
+ consoleDesktop: [
+ legendContainerBase,
+ {
+ bottom: themeContract.space[4],
+ right: themeContract.space[4],
+ },
+ ],
+ consoleMobile: [
+ legendContainerBase,
+ {
+ bottom: themeContract.space[4],
+ right: themeContract.space[4],
+ "@media": {
+ "screen and (max-width: 767px)": {
+ display: "none",
+ },
+ },
+ },
+ ],
+ consumerDesktop: [
+ legendContainerBase,
+ {
+ // Changed from top to bottom to avoid overlap with node detail panel
+ bottom: themeContract.space[4],
+ right: themeContract.space[4],
+ },
+ ],
+ consumerMobile: [
+ legendContainerBase,
+ {
+ bottom: themeContract.space[4],
+ right: themeContract.space[4],
+ "@media": {
+ "screen and (max-width: 767px)": {
+ display: "none",
+ },
+ },
+ },
+ ],
+});
+
+/**
+ * Mobile size variants
+ */
+export const mobileSize = styleVariants({
+ expanded: {
+ maxWidth: "20rem", // max-w-xs
+ },
+ collapsed: {
+ width: "4rem", // w-16
+ height: "3rem", // h-12
+ },
+});
+
+/**
+ * Legend content wrapper
+ */
+export const legendContent = style({
+ position: "relative",
+ zIndex: 10,
+});
+
+/**
+ * Collapsed trigger button
+ */
+export const collapsedTrigger = style({
+ width: "100%",
+ height: "100%",
+ padding: themeContract.space[2],
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "center",
+ transition: themeContract.transitions.normal,
+
+ selectors: {
+ "&:hover": {
+ backgroundColor: "rgba(255, 255, 255, 0.05)",
+ },
+ },
+});
+
+export const collapsedContent = style({
+ display: "flex",
+ flexDirection: "column",
+ alignItems: "center",
+ gap: themeContract.space[1],
+});
+
+export const collapsedText = style({
+ fontSize: themeContract.typography.fontSize.xs,
+ color: themeContract.colors.text.secondary,
+ fontWeight: themeContract.typography.fontWeight.medium,
+});
+
+export const collapsedIcon = style({
+ width: "0.75rem",
+ height: "0.75rem",
+ color: themeContract.colors.text.muted,
+});
+
+/**
+ * Header
+ */
+export const legendHeader = style({
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "space-between",
+ paddingLeft: themeContract.space[4],
+ paddingRight: themeContract.space[4],
+ paddingTop: themeContract.space[3],
+ paddingBottom: themeContract.space[3],
+ borderBottom: "1px solid rgba(71, 85, 105, 0.5)", // slate-600/50
+});
+
+export const legendTitle = style({
+ fontSize: themeContract.typography.fontSize.sm,
+ fontWeight: themeContract.typography.fontWeight.medium,
+ color: themeContract.colors.text.primary,
+});
+
+export const headerTrigger = style({
+ padding: themeContract.space[1],
+ borderRadius: themeContract.radii.sm,
+ transition: themeContract.transitions.normal,
+
+ selectors: {
+ "&:hover": {
+ backgroundColor: "rgba(255, 255, 255, 0.1)",
+ },
+ },
+});
+
+export const headerIcon = style({
+ width: "1rem",
+ height: "1rem",
+ color: themeContract.colors.text.muted,
+});
+
+/**
+ * Content sections
+ */
+export const sectionsContainer = style({
+ fontSize: themeContract.typography.fontSize.xs,
+ color: themeContract.colors.text.secondary,
+ paddingLeft: themeContract.space[4],
+ paddingRight: themeContract.space[4],
+ paddingTop: themeContract.space[3],
+ paddingBottom: themeContract.space[3],
+});
+
+export const sectionWrapper = style({
+ marginTop: themeContract.space[3],
+ selectors: {
+ "&:first-child": {
+ marginTop: 0,
+ },
+ },
+});
+
+export const sectionTitle = style({
+ fontSize: themeContract.typography.fontSize.xs,
+ fontWeight: themeContract.typography.fontWeight.medium,
+ color: themeContract.colors.text.secondary,
+ textTransform: "uppercase",
+ letterSpacing: "0.05em",
+ marginBottom: themeContract.space[2],
+});
+
+export const itemsList = style({
+ display: "flex",
+ flexDirection: "column",
+ gap: "0.375rem", // gap-1.5
+});
+
+export const legendItem = style({
+ display: "flex",
+ alignItems: "center",
+ gap: themeContract.space[2],
+});
+
+export const legendIcon = style({
+ width: "0.75rem",
+ height: "0.75rem",
+ flexShrink: 0,
+});
+
+export const legendText = style({
+ fontSize: themeContract.typography.fontSize.xs,
+});
+
+/**
+ * Shape styles
+ */
+export const hexagon = style({
+ clipPath: "polygon(50% 0%, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%)",
+});
+
+export const documentNode = style({
+ width: "1rem",
+ height: "0.75rem",
+ background: "rgba(255, 255, 255, 0.08)",
+ border: "1px solid rgba(255, 255, 255, 0.25)",
+ borderRadius: themeContract.radii.sm,
+ flexShrink: 0,
+});
+
+export const memoryNode = style([
+ hexagon,
+ {
+ width: "0.75rem",
+ height: "0.75rem",
+ background: "rgba(147, 197, 253, 0.1)",
+ border: "1px solid rgba(147, 197, 253, 0.35)",
+ flexShrink: 0,
+ },
+]);
+
+export const memoryNodeOlder = style([
+ memoryNode,
+ {
+ opacity: 0.4,
+ },
+]);
+
+export const forgottenNode = style([
+ hexagon,
+ {
+ width: "0.75rem",
+ height: "0.75rem",
+ background: "rgba(239, 68, 68, 0.3)",
+ border: "1px solid rgba(239, 68, 68, 0.8)",
+ position: "relative",
+ flexShrink: 0,
+ },
+]);
+
+export const forgottenIcon = style({
+ position: "absolute",
+ inset: 0,
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "center",
+ color: "rgb(248, 113, 113)",
+ fontSize: themeContract.typography.fontSize.xs,
+ lineHeight: "1",
+});
+
+export const expiringNode = style([
+ hexagon,
+ {
+ width: "0.75rem",
+ height: "0.75rem",
+ background: "rgba(147, 197, 253, 0.1)",
+ border: "2px solid rgb(245, 158, 11)",
+ flexShrink: 0,
+ },
+]);
+
+export const newNode = style([
+ hexagon,
+ {
+ width: "0.75rem",
+ height: "0.75rem",
+ background: "rgba(147, 197, 253, 0.1)",
+ border: "2px solid rgb(16, 185, 129)",
+ position: "relative",
+ flexShrink: 0,
+ },
+]);
+
+export const newBadge = style({
+ position: "absolute",
+ top: "-0.25rem",
+ right: "-0.25rem",
+ width: "0.5rem",
+ height: "0.5rem",
+ backgroundColor: "rgb(16, 185, 129)",
+ borderRadius: themeContract.radii.full,
+});
+
+export const connectionLine = style({
+ width: "1rem",
+ height: 0,
+ borderTop: "1px solid rgb(148, 163, 184)",
+ flexShrink: 0,
+});
+
+export const similarityLine = style({
+ width: "1rem",
+ height: 0,
+ borderTop: "2px dashed rgb(148, 163, 184)",
+ flexShrink: 0,
+});
+
+export const relationLine = style({
+ width: "1rem",
+ height: 0,
+ borderTop: "2px solid",
+ flexShrink: 0,
+});
+
+export const weakSimilarity = style({
+ width: "0.75rem",
+ height: "0.75rem",
+ borderRadius: themeContract.radii.full,
+ background: "rgba(148, 163, 184, 0.2)",
+ flexShrink: 0,
+});
+
+export const strongSimilarity = style({
+ width: "0.75rem",
+ height: "0.75rem",
+ borderRadius: themeContract.radii.full,
+ background: "rgba(148, 163, 184, 0.6)",
+ flexShrink: 0,
+});
+
+export const gradientCircle = style({
+ width: "0.75rem",
+ height: "0.75rem",
+ background: "linear-gradient(to right, rgb(148, 163, 184), rgb(96, 165, 250))",
+ borderRadius: themeContract.radii.full,
+});
diff --git a/packages/memory-graph/src/components/legend.tsx b/packages/memory-graph/src/components/legend.tsx
new file mode 100644
index 00000000..16f588a9
--- /dev/null
+++ b/packages/memory-graph/src/components/legend.tsx
@@ -0,0 +1,275 @@
+"use client";
+
+import { useIsMobile } from "@/hooks/use-mobile";
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/ui/collapsible";
+import { GlassMenuEffect } from "@/ui/glass-effect";
+import { Brain, ChevronDown, ChevronUp, FileText } from "lucide-react";
+import { memo, useEffect, useState } from "react";
+import { colors } from "@/constants";
+import type { GraphEdge, GraphNode, LegendProps } from "@/types";
+import * as styles from "./legend.css";
+
+// Cookie utility functions for legend state
+const setCookie = (name: string, value: string, days = 365) => {
+ if (typeof document === "undefined") return;
+ const expires = new Date();
+ expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000);
+ document.cookie = `${name}=${value};expires=${expires.toUTCString()};path=/`;
+};
+
+const getCookie = (name: string): string | null => {
+ if (typeof document === "undefined") return null;
+ const nameEQ = `${name}=`;
+ const ca = document.cookie.split(";");
+ for (let i = 0; i < ca.length; i++) {
+ let c = ca[i];
+ if (!c) continue;
+ while (c.charAt(0) === " ") c = c.substring(1, c.length);
+ if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
+ }
+ return null;
+};
+
+interface ExtendedLegendProps extends LegendProps {
+ id?: string;
+ nodes?: GraphNode[];
+ edges?: GraphEdge[];
+ isLoading?: boolean;
+}
+
+export const Legend = memo(function Legend({
+ variant = "console",
+ id,
+ nodes = [],
+ edges = [],
+ isLoading = false,
+}: ExtendedLegendProps) {
+ const isMobile = useIsMobile();
+ const [isExpanded, setIsExpanded] = useState(true);
+ const [isInitialized, setIsInitialized] = useState(false);
+
+ // Load saved preference on client side
+ useEffect(() => {
+ if (!isInitialized) {
+ const savedState = getCookie("legendCollapsed");
+ if (savedState === "true") {
+ setIsExpanded(false);
+ } else if (savedState === "false") {
+ setIsExpanded(true);
+ } else {
+ // Default: collapsed on mobile, expanded on desktop
+ setIsExpanded(!isMobile);
+ }
+ setIsInitialized(true);
+ }
+ }, [isInitialized, isMobile]);
+
+ // Save to cookie when state changes
+ const handleToggleExpanded = (expanded: boolean) => {
+ setIsExpanded(expanded);
+ setCookie("legendCollapsed", expanded ? "false" : "true");
+ };
+
+ // Get container class based on variant and mobile state
+ const getContainerClass = () => {
+ if (variant === "console") {
+ return isMobile ? styles.legendContainer.consoleMobile : styles.legendContainer.consoleDesktop;
+ }
+ return isMobile ? styles.legendContainer.consumerMobile : styles.legendContainer.consumerDesktop;
+ };
+
+ // Calculate stats
+ const memoryCount = nodes.filter((n) => n.type === "memory").length;
+ const documentCount = nodes.filter((n) => n.type === "document").length;
+
+ const containerClass = isMobile && !isExpanded
+ ? `${getContainerClass()} ${styles.mobileSize.collapsed}`
+ : isMobile
+ ? `${getContainerClass()} ${styles.mobileSize.expanded}`
+ : getContainerClass();
+
+ return (
+ <div
+ className={containerClass}
+ id={id}
+ >
+ <Collapsible onOpenChange={handleToggleExpanded} open={isExpanded}>
+ {/* Glass effect background */}
+ <GlassMenuEffect rounded="xl" />
+
+ <div className={styles.legendContent}>
+ {/* Mobile and Desktop collapsed state */}
+ {!isExpanded && (
+ <CollapsibleTrigger className={styles.collapsedTrigger}>
+ <div className={styles.collapsedContent}>
+ <div className={styles.collapsedText}>?</div>
+ <ChevronUp className={styles.collapsedIcon} />
+ </div>
+ </CollapsibleTrigger>
+ )}
+
+ {/* Expanded state */}
+ {isExpanded && (
+ <>
+ {/* Header with toggle */}
+ <div className={styles.legendHeader}>
+ <div className={styles.legendTitle}>Legend</div>
+ <CollapsibleTrigger className={styles.headerTrigger}>
+ <ChevronDown className={styles.headerIcon} />
+ </CollapsibleTrigger>
+ </div>
+
+ <CollapsibleContent>
+ <div className={styles.sectionsContainer}>
+ {/* Stats Section */}
+ {!isLoading && (
+ <div className={styles.sectionWrapper}>
+ <div className={styles.sectionTitle}>
+ Statistics
+ </div>
+ <div className={styles.itemsList}>
+ <div className={styles.legendItem}>
+ <Brain className={styles.legendIcon} style={{ color: "rgb(96, 165, 250)" }} />
+ <span className={styles.legendText}>
+ {memoryCount} memories
+ </span>
+ </div>
+ <div className={styles.legendItem}>
+ <FileText className={styles.legendIcon} style={{ color: "rgb(203, 213, 225)" }} />
+ <span className={styles.legendText}>
+ {documentCount} documents
+ </span>
+ </div>
+ <div className={styles.legendItem}>
+ <div className={styles.gradientCircle} />
+ <span className={styles.legendText}>
+ {edges.length} connections
+ </span>
+ </div>
+ </div>
+ </div>
+ )}
+
+ {/* Node Types */}
+ <div className={styles.sectionWrapper}>
+ <div className={styles.sectionTitle}>
+ Nodes
+ </div>
+ <div className={styles.itemsList}>
+ <div className={styles.legendItem}>
+ <div className={styles.documentNode} />
+ <span className={styles.legendText}>Document</span>
+ </div>
+ <div className={styles.legendItem}>
+ <div className={styles.memoryNode} />
+ <span className={styles.legendText}>Memory (latest)</span>
+ </div>
+ <div className={styles.legendItem}>
+ <div className={styles.memoryNodeOlder} />
+ <span className={styles.legendText}>Memory (older)</span>
+ </div>
+ </div>
+ </div>
+
+ {/* Status Indicators */}
+ <div className={styles.sectionWrapper}>
+ <div className={styles.sectionTitle}>
+ Status
+ </div>
+ <div className={styles.itemsList}>
+ <div className={styles.legendItem}>
+ <div className={styles.forgottenNode}>
+ <div className={styles.forgottenIcon}>
+ ✕
+ </div>
+ </div>
+ <span className={styles.legendText}>Forgotten</span>
+ </div>
+ <div className={styles.legendItem}>
+ <div className={styles.expiringNode} />
+ <span className={styles.legendText}>Expiring soon</span>
+ </div>
+ <div className={styles.legendItem}>
+ <div className={styles.newNode}>
+ <div className={styles.newBadge} />
+ </div>
+ <span className={styles.legendText}>New memory</span>
+ </div>
+ </div>
+ </div>
+
+ {/* Connection Types */}
+ <div className={styles.sectionWrapper}>
+ <div className={styles.sectionTitle}>
+ Connections
+ </div>
+ <div className={styles.itemsList}>
+ <div className={styles.legendItem}>
+ <div className={styles.connectionLine} />
+ <span className={styles.legendText}>Doc → Memory</span>
+ </div>
+ <div className={styles.legendItem}>
+ <div className={styles.similarityLine} />
+ <span className={styles.legendText}>Doc similarity</span>
+ </div>
+ </div>
+ </div>
+
+ {/* Relation Types */}
+ <div className={styles.sectionWrapper}>
+ <div className={styles.sectionTitle}>
+ Relations
+ </div>
+ <div className={styles.itemsList}>
+ {[
+ ["updates", colors.relations.updates],
+ ["extends", colors.relations.extends],
+ ["derives", colors.relations.derives],
+ ].map(([label, color]) => (
+ <div className={styles.legendItem} key={label}>
+ <div
+ className={styles.relationLine}
+ style={{ borderColor: color }}
+ />
+ <span
+ className={styles.legendText}
+ style={{ color: color, textTransform: "capitalize" }}
+ >
+ {label}
+ </span>
+ </div>
+ ))}
+ </div>
+ </div>
+
+ {/* Similarity Strength */}
+ <div className={styles.sectionWrapper}>
+ <div className={styles.sectionTitle}>
+ Similarity
+ </div>
+ <div className={styles.itemsList}>
+ <div className={styles.legendItem}>
+ <div className={styles.weakSimilarity} />
+ <span className={styles.legendText}>Weak</span>
+ </div>
+ <div className={styles.legendItem}>
+ <div className={styles.strongSimilarity} />
+ <span className={styles.legendText}>Strong</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </CollapsibleContent>
+ </>
+ )}
+ </div>
+ </Collapsible>
+ </div>
+ );
+});
+
+Legend.displayName = "Legend";
diff --git a/packages/memory-graph/src/components/loading-indicator.css.ts b/packages/memory-graph/src/components/loading-indicator.css.ts
new file mode 100644
index 00000000..09010f28
--- /dev/null
+++ b/packages/memory-graph/src/components/loading-indicator.css.ts
@@ -0,0 +1,55 @@
+import { style } from "@vanilla-extract/css";
+import { themeContract } from "../styles/theme.css";
+import { animations } from "../styles";
+
+/**
+ * Loading indicator container
+ * Positioned top-left, below spaces dropdown
+ */
+export const loadingContainer = style({
+ position: "absolute",
+ zIndex: 30, // High priority so it's visible when loading
+ borderRadius: themeContract.radii.xl,
+ overflow: "hidden",
+ top: "5.5rem", // Below spaces dropdown (~88px)
+ left: themeContract.space[4],
+});
+
+/**
+ * Content wrapper
+ */
+export const loadingContent = style({
+ position: "relative",
+ zIndex: 10,
+ color: themeContract.colors.text.secondary,
+ paddingLeft: themeContract.space[4],
+ paddingRight: themeContract.space[4],
+ paddingTop: themeContract.space[3],
+ paddingBottom: themeContract.space[3],
+});
+
+/**
+ * Flex container for icon and text
+ */
+export const loadingFlex = style({
+ display: "flex",
+ alignItems: "center",
+ gap: themeContract.space[2],
+});
+
+/**
+ * Spinning icon
+ */
+export const loadingIcon = style({
+ width: "1rem",
+ height: "1rem",
+ animation: `${animations.spin} 1s linear infinite`,
+ color: themeContract.colors.memory.border,
+});
+
+/**
+ * Loading text
+ */
+export const loadingText = style({
+ fontSize: themeContract.typography.fontSize.sm,
+});
diff --git a/packages/memory-graph/src/components/loading-indicator.tsx b/packages/memory-graph/src/components/loading-indicator.tsx
new file mode 100644
index 00000000..be31430b
--- /dev/null
+++ b/packages/memory-graph/src/components/loading-indicator.tsx
@@ -0,0 +1,40 @@
+"use client";
+
+import { GlassMenuEffect } from "@/ui/glass-effect";
+import { Sparkles } from "lucide-react";
+import { memo } from "react";
+import type { LoadingIndicatorProps } from "@/types";
+import {
+ loadingContainer,
+ loadingContent,
+ loadingFlex,
+ loadingIcon,
+ loadingText,
+} from "./loading-indicator.css";
+
+export const LoadingIndicator = memo<LoadingIndicatorProps>(
+ ({ isLoading, isLoadingMore, totalLoaded, variant = "console" }) => {
+ if (!isLoading && !isLoadingMore) return null;
+
+ return (
+ <div className={loadingContainer}>
+ {/* Glass effect background */}
+ <GlassMenuEffect rounded="xl" />
+
+ <div className={loadingContent}>
+ <div className={loadingFlex}>
+ {/*@ts-ignore */}
+ <Sparkles className={loadingIcon} />
+ <span className={loadingText}>
+ {isLoading
+ ? "Loading memory graph..."
+ : `Loading more documents... (${totalLoaded})`}
+ </span>
+ </div>
+ </div>
+ </div>
+ );
+ },
+);
+
+LoadingIndicator.displayName = "LoadingIndicator";
diff --git a/packages/memory-graph/src/components/memory-graph-wrapper.tsx b/packages/memory-graph/src/components/memory-graph-wrapper.tsx
new file mode 100644
index 00000000..cfc8e148
--- /dev/null
+++ b/packages/memory-graph/src/components/memory-graph-wrapper.tsx
@@ -0,0 +1,198 @@
+"use client";
+
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import { useEffect, useMemo, useRef } from "react";
+import {
+ flattenDocuments,
+ getLoadedCount,
+ getTotalDocuments,
+ useInfiniteDocumentsQuery,
+} from "@/hooks/use-documents-query";
+import { MemoryGraph } from "./memory-graph";
+import { defaultTheme } from "@/styles/theme.css";
+import type { ApiClientError } from "@/lib/api-client";
+
+export interface MemoryGraphWrapperProps {
+ /** API key for authentication */
+ apiKey: string;
+ /** Optional base URL for the API (defaults to https://api.supermemory.ai) */
+ baseUrl?: string;
+ /** Optional document ID to filter by */
+ id?: string;
+ /** Visual variant - console for full view, consumer for embedded */
+ variant?: "console" | "consumer";
+ /** Show/hide the spaces filter dropdown */
+ showSpacesSelector?: boolean;
+ /** Optional container tags to filter documents */
+ containerTags?: string[];
+ /** Callback when data fetching fails */
+ onError?: (error: ApiClientError) => void;
+ /** Callback when data is successfully loaded */
+ onSuccess?: (totalDocuments: number) => void;
+ /** Empty state content */
+ children?: React.ReactNode;
+ /** Documents to highlight */
+ highlightDocumentIds?: string[];
+ /** Whether highlights are visible */
+ highlightsVisible?: boolean;
+ /** Pixels occluded on the right side of the viewport */
+ occludedRightPx?: number;
+}
+
+/**
+ * Internal component that uses the query hooks
+ */
+function MemoryGraphWithQuery(props: MemoryGraphWrapperProps) {
+ const {
+ apiKey,
+ baseUrl,
+ containerTags,
+ variant = "console",
+ showSpacesSelector,
+ onError,
+ onSuccess,
+ children,
+ highlightDocumentIds,
+ highlightsVisible,
+ occludedRightPx,
+ } = props;
+
+ // Derive showSpacesSelector from variant if not explicitly provided
+ // console variant shows spaces selector, consumer variant hides it
+ const finalShowSpacesSelector = showSpacesSelector ?? (variant === "console");
+
+ // Use infinite query for automatic pagination
+ const {
+ data,
+ isLoading,
+ isFetchingNextPage,
+ hasNextPage,
+ fetchNextPage,
+ error,
+ } = useInfiniteDocumentsQuery({
+ apiKey,
+ baseUrl,
+ containerTags,
+ enabled: !!apiKey,
+ });
+
+ // Flatten documents from all pages
+ const documents = useMemo(() => flattenDocuments(data), [data]);
+ const totalLoaded = useMemo(() => getLoadedCount(data), [data]);
+ const totalDocuments = useMemo(() => getTotalDocuments(data), [data]);
+
+ // Eagerly load all pages to ensure complete graph data
+ const isLoadingAllPages = useRef(false);
+
+ useEffect(() => {
+ // Only start loading once, when initial data is loaded
+ if (isLoading || isLoadingAllPages.current || !data?.pages?.[0]) return;
+
+ const abortController = new AbortController();
+
+ // Start recursive page loading
+ const loadAllPages = async () => {
+ isLoadingAllPages.current = true;
+
+ try {
+ // Keep fetching until no more pages or aborted
+ let shouldContinue = hasNextPage;
+
+ while (shouldContinue && !abortController.signal.aborted) {
+ const result = await fetchNextPage();
+ shouldContinue = result.hasNextPage ?? false;
+
+ // Throttle requests to avoid overwhelming server (50ms delay like console app)
+ if (shouldContinue && !abortController.signal.aborted) {
+ await new Promise(resolve => setTimeout(resolve, 50));
+ }
+ }
+ } catch (error) {
+ if (!abortController.signal.aborted) {
+ console.error('[MemoryGraph] Error loading pages:', error);
+ }
+ }
+ };
+
+ if (hasNextPage) {
+ loadAllPages();
+ }
+
+ // Cleanup on unmount
+ return () => {
+ abortController.abort();
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []); // Only run once on mount
+
+ // Call callbacks
+ if (error && onError) {
+ onError(error as ApiClientError);
+ }
+
+ if (data && onSuccess && totalDocuments > 0) {
+ onSuccess(totalDocuments);
+ }
+
+ // Load more function
+ const loadMoreDocuments = async () => {
+ if (hasNextPage && !isFetchingNextPage) {
+ await fetchNextPage();
+ }
+ };
+
+ return (
+ <MemoryGraph
+ documents={documents}
+ isLoading={isLoading}
+ isLoadingMore={isFetchingNextPage}
+ error={error as Error | null}
+ totalLoaded={totalLoaded}
+ hasMore={hasNextPage ?? false}
+ loadMoreDocuments={loadMoreDocuments}
+ variant={variant}
+ showSpacesSelector={finalShowSpacesSelector}
+ highlightDocumentIds={highlightDocumentIds}
+ highlightsVisible={highlightsVisible}
+ occludedRightPx={occludedRightPx}
+ autoLoadOnViewport={true}
+ themeClassName={defaultTheme}
+ >
+ {children}
+ </MemoryGraph>
+ );
+}
+
+// Create a default query client for the wrapper
+const defaultQueryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ refetchOnWindowFocus: false,
+ refetchOnMount: false,
+ retry: 2,
+ },
+ },
+});
+
+/**
+ * MemoryGraph component with built-in data fetching
+ *
+ * This component handles all data fetching internally using the provided API key.
+ * Simply pass your API key and it will fetch and render the graph automatically.
+ *
+ * @example
+ * ```tsx
+ * <MemoryGraphWrapper
+ * apiKey="your-api-key"
+ * variant="console"
+ * onError={(error) => console.error(error)}
+ * />
+ * ```
+ */
+export function MemoryGraphWrapper(props: MemoryGraphWrapperProps) {
+ return (
+ <QueryClientProvider client={defaultQueryClient}>
+ <MemoryGraphWithQuery {...props} />
+ </QueryClientProvider>
+ );
+}
diff --git a/packages/memory-graph/src/components/memory-graph.css.ts b/packages/memory-graph/src/components/memory-graph.css.ts
new file mode 100644
index 00000000..f5b38273
--- /dev/null
+++ b/packages/memory-graph/src/components/memory-graph.css.ts
@@ -0,0 +1,75 @@
+import { style } from "@vanilla-extract/css";
+import { themeContract } from "../styles/theme.css";
+
+/**
+ * Error state container
+ */
+export const errorContainer = style({
+ height: "100%",
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "center",
+ backgroundColor: themeContract.colors.background.primary,
+});
+
+export const errorCard = style({
+ borderRadius: themeContract.radii.xl,
+ overflow: "hidden",
+});
+
+export const errorContent = style({
+ position: "relative",
+ zIndex: 10,
+ color: themeContract.colors.text.secondary,
+ paddingLeft: themeContract.space[6],
+ paddingRight: themeContract.space[6],
+ paddingTop: themeContract.space[4],
+ paddingBottom: themeContract.space[4],
+});
+
+/**
+ * Main graph container
+ * Position relative so absolutely positioned children position relative to this container
+ */
+export const mainContainer = style({
+ position: "relative",
+ height: "100%",
+ borderRadius: themeContract.radii.xl,
+ overflow: "hidden",
+ backgroundColor: themeContract.colors.background.primary,
+});
+
+/**
+ * Spaces selector positioning
+ * Top-left corner, below most overlays
+ */
+export const spacesSelectorContainer = style({
+ position: "absolute",
+ top: themeContract.space[4],
+ left: themeContract.space[4],
+ zIndex: 15, // Above base elements, below loading/panels
+});
+
+/**
+ * Graph canvas container
+ */
+export const graphContainer = style({
+ width: "100%",
+ height: "100%",
+ position: "relative",
+ overflow: "hidden",
+ touchAction: "none",
+ userSelect: "none",
+ WebkitUserSelect: "none",
+});
+
+/**
+ * Navigation controls positioning
+ * Bottom-left corner
+ */
+export const navControlsContainer = style({
+ position: "absolute",
+ bottom: themeContract.space[4],
+ left: themeContract.space[4],
+ zIndex: 15, // Same level as spaces dropdown
+});
diff --git a/packages/memory-graph/src/components/memory-graph.tsx b/packages/memory-graph/src/components/memory-graph.tsx
new file mode 100644
index 00000000..3eeed37b
--- /dev/null
+++ b/packages/memory-graph/src/components/memory-graph.tsx
@@ -0,0 +1,448 @@
+"use client";
+
+import { GlassMenuEffect } from "@/ui/glass-effect";
+import { AnimatePresence } from "motion/react";
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { GraphCanvas } from "./graph-canvas";
+import { useGraphData } from "@/hooks/use-graph-data";
+import { useGraphInteractions } from "@/hooks/use-graph-interactions";
+import { Legend } from "./legend";
+import { LoadingIndicator } from "./loading-indicator";
+import { NavigationControls } from "./navigation-controls";
+import { NodeDetailPanel } from "./node-detail-panel";
+import { SpacesDropdown } from "./spaces-dropdown";
+import * as styles from "./memory-graph.css";
+
+import type { MemoryGraphProps } from "@/types";
+
+export const MemoryGraph = ({
+ children,
+ documents,
+ isLoading,
+ isLoadingMore,
+ error,
+ totalLoaded,
+ hasMore,
+ loadMoreDocuments,
+ showSpacesSelector,
+ variant = "console",
+ legendId,
+ highlightDocumentIds = [],
+ highlightsVisible = true,
+ occludedRightPx = 0,
+ autoLoadOnViewport = true,
+ themeClassName,
+}: MemoryGraphProps) => {
+ // Derive showSpacesSelector from variant if not explicitly provided
+ // console variant shows spaces selector, consumer variant hides it
+ const finalShowSpacesSelector = showSpacesSelector ?? (variant === "console");
+
+ const [selectedSpace, setSelectedSpace] = useState<string>("all");
+ const [containerSize, setContainerSize] = useState({ width: 0, height: 0 });
+ const containerRef = useRef<HTMLDivElement>(null);
+
+ // Create data object with pagination to satisfy type requirements
+ const data = useMemo(() => {
+ return documents && documents.length > 0
+ ? {
+ documents,
+ pagination: {
+ currentPage: 1,
+ limit: documents.length,
+ totalItems: documents.length,
+ totalPages: 1,
+ },
+ }
+ : null;
+ }, [documents]);
+
+ // Graph interactions with variant-specific settings
+ const {
+ panX,
+ panY,
+ zoom,
+ /** hoveredNode currently unused within this component */
+ hoveredNode: _hoveredNode,
+ selectedNode,
+ draggingNodeId,
+ nodePositions,
+ handlePanStart,
+ handlePanMove,
+ handlePanEnd,
+ handleWheel,
+ handleNodeHover,
+ handleNodeClick,
+ handleNodeDragStart,
+ handleNodeDragMove,
+ handleNodeDragEnd,
+ handleDoubleClick,
+ handleTouchStart,
+ handleTouchMove,
+ handleTouchEnd,
+ setSelectedNode,
+ autoFitToViewport,
+ centerViewportOn,
+ zoomIn,
+ zoomOut,
+ } = useGraphInteractions(variant);
+
+ // Graph data
+ const { nodes, edges } = useGraphData(
+ data,
+ selectedSpace,
+ nodePositions,
+ draggingNodeId,
+ );
+
+ // Auto-fit once per unique highlight set to show the full graph for context
+ const lastFittedHighlightKeyRef = useRef<string>("");
+ useEffect(() => {
+ const highlightKey = highlightsVisible
+ ? highlightDocumentIds.join("|")
+ : "";
+ if (
+ highlightKey &&
+ highlightKey !== lastFittedHighlightKeyRef.current &&
+ containerSize.width > 0 &&
+ containerSize.height > 0 &&
+ nodes.length > 0
+ ) {
+ autoFitToViewport(nodes, containerSize.width, containerSize.height, {
+ occludedRightPx,
+ animate: true,
+ });
+ lastFittedHighlightKeyRef.current = highlightKey;
+ }
+ }, [
+ highlightsVisible,
+ highlightDocumentIds,
+ containerSize.width,
+ containerSize.height,
+ nodes.length,
+ occludedRightPx,
+ autoFitToViewport,
+ ]);
+
+ // Auto-fit graph when component mounts or nodes change significantly
+ const hasAutoFittedRef = useRef(false);
+ useEffect(() => {
+ // Only auto-fit once when we have nodes and container size
+ if (
+ !hasAutoFittedRef.current &&
+ nodes.length > 0 &&
+ containerSize.width > 0 &&
+ containerSize.height > 0
+ ) {
+ // Auto-fit to show all content for both variants
+ // Add a small delay to ensure the canvas is fully initialized
+ const timer = setTimeout(() => {
+ autoFitToViewport(nodes, containerSize.width, containerSize.height);
+ hasAutoFittedRef.current = true;
+ }, 100);
+
+ return () => clearTimeout(timer);
+ }
+ }, [
+ nodes,
+ containerSize.width,
+ containerSize.height,
+ autoFitToViewport,
+ ]);
+
+ // Reset auto-fit flag when nodes array becomes empty (switching views)
+ useEffect(() => {
+ if (nodes.length === 0) {
+ hasAutoFittedRef.current = false;
+ }
+ }, [nodes.length]);
+
+ // Extract unique spaces from memories and calculate counts
+ const { availableSpaces, spaceMemoryCounts } = useMemo(() => {
+ if (!data?.documents) return { availableSpaces: [], spaceMemoryCounts: {} };
+
+ const spaceSet = new Set<string>();
+ const counts: Record<string, number> = {};
+
+ data.documents.forEach((doc) => {
+ doc.memoryEntries.forEach((memory) => {
+ const spaceId = memory.spaceContainerTag || memory.spaceId || "default";
+ spaceSet.add(spaceId);
+ counts[spaceId] = (counts[spaceId] || 0) + 1;
+ });
+ });
+
+ return {
+ availableSpaces: Array.from(spaceSet).sort(),
+ spaceMemoryCounts: counts,
+ };
+ }, [data]);
+
+ // Handle container resize
+ useEffect(() => {
+ const updateSize = () => {
+ if (containerRef.current) {
+ const newWidth = containerRef.current.clientWidth;
+ const newHeight = containerRef.current.clientHeight;
+
+ // Only update if size actually changed and is valid
+ setContainerSize((prev) => {
+ if (prev.width !== newWidth || prev.height !== newHeight) {
+ return { width: newWidth, height: newHeight };
+ }
+ return prev;
+ });
+ }
+ };
+
+ // Use a slight delay to ensure DOM is fully rendered
+ const timer = setTimeout(updateSize, 0);
+ updateSize(); // Also call immediately
+
+ window.addEventListener("resize", updateSize);
+
+ // Use ResizeObserver for more accurate container size detection
+ const resizeObserver = new ResizeObserver(updateSize);
+ if (containerRef.current) {
+ resizeObserver.observe(containerRef.current);
+ }
+
+ return () => {
+ clearTimeout(timer);
+ window.removeEventListener("resize", updateSize);
+ resizeObserver.disconnect();
+ };
+ }, []);
+
+ // Enhanced node drag start that includes nodes data
+ const handleNodeDragStartWithNodes = useCallback(
+ (nodeId: string, e: React.MouseEvent) => {
+ handleNodeDragStart(nodeId, e, nodes);
+ },
+ [handleNodeDragStart, nodes],
+ );
+
+ // Navigation callbacks
+ const handleCenter = useCallback(() => {
+ if (nodes.length > 0) {
+ // Calculate center of all nodes
+ let sumX = 0
+ let sumY = 0
+ let count = 0
+
+ nodes.forEach((node) => {
+ sumX += node.x
+ sumY += node.y
+ count++
+ })
+
+ if (count > 0) {
+ const centerX = sumX / count
+ const centerY = sumY / count
+ centerViewportOn(centerX, centerY, containerSize.width, containerSize.height)
+ }
+ }
+ }, [nodes, centerViewportOn, containerSize.width, containerSize.height])
+
+ const handleAutoFit = useCallback(() => {
+ if (nodes.length > 0 && containerSize.width > 0 && containerSize.height > 0) {
+ autoFitToViewport(nodes, containerSize.width, containerSize.height, {
+ occludedRightPx,
+ animate: true,
+ })
+ }
+ }, [nodes, containerSize.width, containerSize.height, occludedRightPx, autoFitToViewport])
+
+ // Get selected node data
+ const selectedNodeData = useMemo(() => {
+ if (!selectedNode) return null;
+ return nodes.find((n) => n.id === selectedNode) || null;
+ }, [selectedNode, nodes]);
+
+ // Viewport-based loading: load more when most documents are visible (optional)
+ const checkAndLoadMore = useCallback(() => {
+ if (
+ isLoadingMore ||
+ !hasMore ||
+ !data?.documents ||
+ data.documents.length === 0
+ )
+ return;
+
+ // Calculate viewport bounds
+ const viewportBounds = {
+ left: -panX / zoom - 200,
+ right: (-panX + containerSize.width) / zoom + 200,
+ top: -panY / zoom - 200,
+ bottom: (-panY + containerSize.height) / zoom + 200,
+ };
+
+ // Count visible documents
+ const visibleDocuments = data.documents.filter((doc) => {
+ const docNodes = nodes.filter(
+ (node) => node.type === "document" && node.data.id === doc.id,
+ );
+ return docNodes.some(
+ (node) =>
+ node.x >= viewportBounds.left &&
+ node.x <= viewportBounds.right &&
+ node.y >= viewportBounds.top &&
+ node.y <= viewportBounds.bottom,
+ );
+ });
+
+ // If 80% or more of documents are visible, load more
+ const visibilityRatio = visibleDocuments.length / data.documents.length;
+ if (visibilityRatio >= 0.8) {
+ loadMoreDocuments();
+ }
+ }, [
+ isLoadingMore,
+ hasMore,
+ data,
+ panX,
+ panY,
+ zoom,
+ containerSize.width,
+ containerSize.height,
+ nodes,
+ loadMoreDocuments,
+ ]);
+
+ // Throttled version to avoid excessive checks
+ const lastLoadCheckRef = useRef(0);
+ const throttledCheckAndLoadMore = useCallback(() => {
+ const now = Date.now();
+ if (now - lastLoadCheckRef.current > 1000) {
+ // Check at most once per second
+ lastLoadCheckRef.current = now;
+ checkAndLoadMore();
+ }
+ }, [checkAndLoadMore]);
+
+ // Monitor viewport changes to trigger loading
+ useEffect(() => {
+ if (!autoLoadOnViewport) return;
+ throttledCheckAndLoadMore();
+ }, [throttledCheckAndLoadMore, autoLoadOnViewport]);
+
+ // Initial load trigger when graph is first rendered
+ useEffect(() => {
+ if (!autoLoadOnViewport) return;
+ if (data?.documents && data.documents.length > 0 && hasMore) {
+ // Start loading more documents after initial render
+ setTimeout(() => {
+ throttledCheckAndLoadMore();
+ }, 500); // Small delay to allow initial layout
+ }
+ }, [data, hasMore, throttledCheckAndLoadMore, autoLoadOnViewport]);
+
+ if (error) {
+ return (
+ <div className={styles.errorContainer}>
+ <div className={styles.errorCard}>
+ {/* Glass effect background */}
+ <GlassMenuEffect rounded="xl" />
+
+ <div className={styles.errorContent}>
+ Error loading documents: {error.message}
+ </div>
+ </div>
+ </div>
+ );
+ }
+
+ return (
+ <div className={themeClassName ? `${themeClassName} ${styles.mainContainer}` : styles.mainContainer}>
+ {/* Spaces selector - only shown for console */}
+ {finalShowSpacesSelector && availableSpaces.length > 0 && (
+ <div className={styles.spacesSelectorContainer}>
+ <SpacesDropdown
+ availableSpaces={availableSpaces}
+ onSpaceChange={setSelectedSpace}
+ selectedSpace={selectedSpace}
+ spaceMemoryCounts={spaceMemoryCounts}
+ />
+ </div>
+ )}
+
+ {/* Loading indicator */}
+ <LoadingIndicator
+ isLoading={isLoading}
+ isLoadingMore={isLoadingMore}
+ totalLoaded={totalLoaded}
+ variant={variant}
+ />
+
+ {/* Legend */}
+ <Legend
+ edges={edges}
+ id={legendId}
+ isLoading={isLoading}
+ nodes={nodes}
+ variant={variant}
+ />
+
+ {/* Node detail panel */}
+ <AnimatePresence>
+ {selectedNodeData && (
+ <NodeDetailPanel
+ node={selectedNodeData}
+ onClose={() => setSelectedNode(null)}
+ variant={variant}
+ />
+ )}
+ </AnimatePresence>
+
+ {/* Show welcome screen when no memories exist */}
+ {!isLoading &&
+ (!data || nodes.filter((n) => n.type === "document").length === 0) && (
+ <>{children}</>
+ )}
+
+ {/* Graph container */}
+ <div
+ className={styles.graphContainer}
+ ref={containerRef}
+ >
+ {(containerSize.width > 0 && containerSize.height > 0) && (
+ <GraphCanvas
+ draggingNodeId={draggingNodeId}
+ edges={edges}
+ height={containerSize.height}
+ nodes={nodes}
+ highlightDocumentIds={highlightsVisible ? highlightDocumentIds : []}
+ onDoubleClick={handleDoubleClick}
+ onNodeClick={handleNodeClick}
+ onNodeDragEnd={handleNodeDragEnd}
+ onNodeDragMove={handleNodeDragMove}
+ onNodeDragStart={handleNodeDragStartWithNodes}
+ onNodeHover={handleNodeHover}
+ onPanEnd={handlePanEnd}
+ onPanMove={handlePanMove}
+ onPanStart={handlePanStart}
+ onTouchStart={handleTouchStart}
+ onTouchMove={handleTouchMove}
+ onTouchEnd={handleTouchEnd}
+ onWheel={handleWheel}
+ panX={panX}
+ panY={panY}
+ width={containerSize.width}
+ zoom={zoom}
+ />
+ )}
+
+ {/* Navigation controls */}
+ {containerSize.width > 0 && (
+ <NavigationControls
+ onCenter={handleCenter}
+ onZoomIn={() => zoomIn(containerSize.width / 2, containerSize.height / 2)}
+ onZoomOut={() => zoomOut(containerSize.width / 2, containerSize.height / 2)}
+ onAutoFit={handleAutoFit}
+ nodes={nodes}
+ className={styles.navControlsContainer}
+ />
+ )}
+ </div>
+ </div>
+ );
+};
diff --git a/packages/memory-graph/src/components/navigation-controls.css.ts b/packages/memory-graph/src/components/navigation-controls.css.ts
new file mode 100644
index 00000000..3a4094bd
--- /dev/null
+++ b/packages/memory-graph/src/components/navigation-controls.css.ts
@@ -0,0 +1,77 @@
+import { style } from "@vanilla-extract/css";
+import { themeContract } from "../styles/theme.css";
+
+/**
+ * Navigation controls container
+ */
+export const navContainer = style({
+ display: "flex",
+ flexDirection: "column",
+ gap: themeContract.space[1],
+});
+
+/**
+ * Base button styles for navigation controls
+ */
+const navButtonBase = style({
+ backgroundColor: "rgba(0, 0, 0, 0.2)",
+ backdropFilter: "blur(8px)",
+ WebkitBackdropFilter: "blur(8px)",
+ border: `1px solid rgba(255, 255, 255, 0.1)`,
+ borderRadius: themeContract.radii.lg,
+ padding: themeContract.space[2],
+ color: "rgba(255, 255, 255, 0.7)",
+ fontSize: themeContract.typography.fontSize.xs,
+ fontWeight: themeContract.typography.fontWeight.medium,
+ minWidth: "64px",
+ cursor: "pointer",
+ transition: themeContract.transitions.normal,
+
+ selectors: {
+ "&:hover": {
+ backgroundColor: "rgba(0, 0, 0, 0.3)",
+ borderColor: "rgba(255, 255, 255, 0.2)",
+ color: "rgba(255, 255, 255, 1)",
+ },
+ },
+});
+
+/**
+ * Standard navigation button
+ */
+export const navButton = navButtonBase;
+
+/**
+ * Zoom controls container
+ */
+export const zoomContainer = style({
+ display: "flex",
+ flexDirection: "column",
+});
+
+/**
+ * Zoom in button (top rounded)
+ */
+export const zoomInButton = style([
+ navButtonBase,
+ {
+ borderTopLeftRadius: themeContract.radii.lg,
+ borderTopRightRadius: themeContract.radii.lg,
+ borderBottomLeftRadius: 0,
+ borderBottomRightRadius: 0,
+ borderBottom: 0,
+ },
+]);
+
+/**
+ * Zoom out button (bottom rounded)
+ */
+export const zoomOutButton = style([
+ navButtonBase,
+ {
+ borderTopLeftRadius: 0,
+ borderTopRightRadius: 0,
+ borderBottomLeftRadius: themeContract.radii.lg,
+ borderBottomRightRadius: themeContract.radii.lg,
+ },
+]);
diff --git a/packages/memory-graph/src/components/navigation-controls.tsx b/packages/memory-graph/src/components/navigation-controls.tsx
new file mode 100644
index 00000000..19caa888
--- /dev/null
+++ b/packages/memory-graph/src/components/navigation-controls.tsx
@@ -0,0 +1,73 @@
+"use client";
+
+import { memo } from "react";
+import type { GraphNode } from "@/types";
+import {
+ navContainer,
+ navButton,
+ zoomContainer,
+ zoomInButton,
+ zoomOutButton,
+} from "./navigation-controls.css";
+
+interface NavigationControlsProps {
+ onCenter: () => void;
+ onZoomIn: () => void;
+ onZoomOut: () => void;
+ onAutoFit: () => void;
+ nodes: GraphNode[];
+ className?: string;
+}
+
+export const NavigationControls = memo<NavigationControlsProps>(
+ ({ onCenter, onZoomIn, onZoomOut, onAutoFit, nodes, className = "" }) => {
+ if (nodes.length === 0) {
+ return null;
+ }
+
+ const containerClassName = className
+ ? `${navContainer} ${className}`
+ : navContainer;
+
+ return (
+ <div className={containerClassName}>
+ <button
+ type="button"
+ onClick={onAutoFit}
+ className={navButton}
+ title="Auto-fit graph to viewport"
+ >
+ Fit
+ </button>
+ <button
+ type="button"
+ onClick={onCenter}
+ className={navButton}
+ title="Center view on graph"
+ >
+ Center
+ </button>
+ <div className={zoomContainer}>
+ <button
+ type="button"
+ onClick={onZoomIn}
+ className={zoomInButton}
+ title="Zoom in"
+ >
+ +
+ </button>
+ <button
+ type="button"
+ onClick={onZoomOut}
+ className={zoomOutButton}
+ title="Zoom out"
+ >
+ −
+ </button>
+ </div>
+ </div>
+ );
+ },
+);
+
+NavigationControls.displayName = "NavigationControls";
diff --git a/packages/memory-graph/src/components/node-detail-panel.css.ts b/packages/memory-graph/src/components/node-detail-panel.css.ts
new file mode 100644
index 00000000..a3c30e06
--- /dev/null
+++ b/packages/memory-graph/src/components/node-detail-panel.css.ts
@@ -0,0 +1,170 @@
+import { style } from "@vanilla-extract/css";
+import { themeContract } from "../styles/theme.css";
+
+/**
+ * Main container (positioned absolutely)
+ * Highest z-index so it appears above everything when open
+ */
+export const container = style({
+ position: "absolute",
+ width: "20rem", // w-80 = 320px = 20rem
+ borderRadius: themeContract.radii.xl,
+ overflow: "hidden",
+ zIndex: 40, // Highest priority - always on top when open
+ maxHeight: "calc(100vh - 2rem)", // Leave some breathing room
+ top: themeContract.space[4],
+ right: themeContract.space[4],
+
+ // Add shadow for depth
+ boxShadow: "0 20px 25px -5px rgb(0 0 0 / 0.3), 0 8px 10px -6px rgb(0 0 0 / 0.3)",
+});
+
+/**
+ * Content wrapper with scrolling
+ */
+export const content = style({
+ position: "relative",
+ zIndex: 10,
+ padding: themeContract.space[4],
+ overflowY: "auto",
+ maxHeight: "80vh",
+});
+
+/**
+ * Header section
+ */
+export const header = style({
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "space-between",
+ marginBottom: themeContract.space[3],
+});
+
+export const headerLeft = style({
+ display: "flex",
+ alignItems: "center",
+ gap: themeContract.space[2],
+});
+
+export const headerIcon = style({
+ width: "1.25rem",
+ height: "1.25rem",
+ color: themeContract.colors.text.secondary,
+});
+
+export const headerIconMemory = style({
+ width: "1.25rem",
+ height: "1.25rem",
+ color: "rgb(96, 165, 250)", // blue-400
+});
+
+export const closeButton = style({
+ height: "32px",
+ width: "32px",
+ padding: 0,
+ color: themeContract.colors.text.secondary,
+
+ selectors: {
+ "&:hover": {
+ color: themeContract.colors.text.primary,
+ },
+ },
+});
+
+export const closeIcon = style({
+ width: "1rem",
+ height: "1rem",
+});
+
+/**
+ * Content sections
+ */
+export const sections = style({
+ display: "flex",
+ flexDirection: "column",
+ gap: themeContract.space[3],
+});
+
+export const section = style({});
+
+export const sectionLabel = style({
+ fontSize: themeContract.typography.fontSize.xs,
+ color: themeContract.colors.text.muted,
+ textTransform: "uppercase",
+ letterSpacing: "0.05em",
+});
+
+export const sectionValue = style({
+ fontSize: themeContract.typography.fontSize.sm,
+ color: themeContract.colors.text.secondary,
+ marginTop: themeContract.space[1],
+});
+
+export const sectionValueTruncated = style({
+ fontSize: themeContract.typography.fontSize.sm,
+ color: themeContract.colors.text.secondary,
+ marginTop: themeContract.space[1],
+ overflow: "hidden",
+ display: "-webkit-box",
+ WebkitLineClamp: 3,
+ WebkitBoxOrient: "vertical",
+});
+
+export const link = style({
+ fontSize: themeContract.typography.fontSize.sm,
+ color: "rgb(129, 140, 248)", // indigo-400
+ marginTop: themeContract.space[1],
+ display: "flex",
+ alignItems: "center",
+ gap: themeContract.space[1],
+ textDecoration: "none",
+ transition: themeContract.transitions.normal,
+
+ selectors: {
+ "&:hover": {
+ color: "rgb(165, 180, 252)", // indigo-300
+ },
+ },
+});
+
+export const linkIcon = style({
+ width: "0.75rem",
+ height: "0.75rem",
+});
+
+export const badge = style({
+ marginTop: themeContract.space[2],
+});
+
+export const expiryText = style({
+ fontSize: themeContract.typography.fontSize.xs,
+ color: themeContract.colors.text.muted,
+ marginTop: themeContract.space[1],
+});
+
+/**
+ * Footer section (metadata)
+ */
+export const footer = style({
+ paddingTop: themeContract.space[2],
+ borderTop: "1px solid rgba(71, 85, 105, 0.5)", // slate-700/50
+});
+
+export const metadata = style({
+ display: "flex",
+ alignItems: "center",
+ gap: themeContract.space[4],
+ fontSize: themeContract.typography.fontSize.xs,
+ color: themeContract.colors.text.muted,
+});
+
+export const metadataItem = style({
+ display: "flex",
+ alignItems: "center",
+ gap: themeContract.space[1],
+});
+
+export const metadataIcon = style({
+ width: "0.75rem",
+ height: "0.75rem",
+});
diff --git a/packages/memory-graph/src/components/node-detail-panel.tsx b/packages/memory-graph/src/components/node-detail-panel.tsx
new file mode 100644
index 00000000..e2ae0133
--- /dev/null
+++ b/packages/memory-graph/src/components/node-detail-panel.tsx
@@ -0,0 +1,266 @@
+"use client";
+
+import { Badge } from "@/ui/badge";
+import { Button } from "@/ui/button";
+import { GlassMenuEffect } from "@/ui/glass-effect";
+import { Brain, Calendar, ExternalLink, FileText, Hash, X } from "lucide-react";
+import { motion } from "motion/react";
+import { memo } from "react";
+import {
+ GoogleDocs,
+ GoogleDrive,
+ GoogleSheets,
+ GoogleSlides,
+ MicrosoftExcel,
+ MicrosoftOneNote,
+ MicrosoftPowerpoint,
+ MicrosoftWord,
+ NotionDoc,
+ OneDrive,
+ PDF,
+} from "@/assets/icons";
+import { HeadingH3Bold } from "@/ui/heading";
+import type {
+ DocumentWithMemories,
+ MemoryEntry,
+} from "@/types";
+import type { NodeDetailPanelProps } from "@/types";
+import * as styles from "./node-detail-panel.css";
+
+const formatDocumentType = (type: string) => {
+ // Special case for PDF
+ if (type.toLowerCase() === "pdf") return "PDF";
+
+ // Replace underscores with spaces and capitalize each word
+ return type
+ .split("_")
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
+ .join(" ");
+};
+
+const getDocumentIcon = (type: string) => {
+ const iconProps = { className: "w-5 h-5 text-slate-300" };
+
+ switch (type) {
+ case "google_doc":
+ return <GoogleDocs {...iconProps} />;
+ case "google_sheet":
+ return <GoogleSheets {...iconProps} />;
+ case "google_slide":
+ return <GoogleSlides {...iconProps} />;
+ case "google_drive":
+ return <GoogleDrive {...iconProps} />;
+ case "notion":
+ case "notion_doc":
+ return <NotionDoc {...iconProps} />;
+ case "word":
+ case "microsoft_word":
+ return <MicrosoftWord {...iconProps} />;
+ case "excel":
+ case "microsoft_excel":
+ return <MicrosoftExcel {...iconProps} />;
+ case "powerpoint":
+ case "microsoft_powerpoint":
+ return <MicrosoftPowerpoint {...iconProps} />;
+ case "onenote":
+ case "microsoft_onenote":
+ return <MicrosoftOneNote {...iconProps} />;
+ case "onedrive":
+ return <OneDrive {...iconProps} />;
+ case "pdf":
+ return <PDF {...iconProps} />;
+ default:
+ {/*@ts-ignore */}
+ return <FileText {...iconProps} />;
+ }
+};
+
+export const NodeDetailPanel = memo(
+ function NodeDetailPanel({ node, onClose, variant = "console" }: NodeDetailPanelProps) {
+ if (!node) return null;
+
+ const isDocument = node.type === "document";
+ const data = node.data;
+
+ return (
+ <motion.div
+ animate={{ opacity: 1 }}
+ className={styles.container}
+ exit={{ opacity: 0 }}
+ initial={{ opacity: 0 }}
+ transition={{
+ duration: 0.2,
+ ease: "easeInOut",
+ }}
+ >
+ {/* Glass effect background */}
+ <GlassMenuEffect rounded="xl" />
+
+ <motion.div
+ animate={{ opacity: 1 }}
+ className={styles.content}
+ initial={{ opacity: 0 }}
+ transition={{ delay: 0.05, duration: 0.15 }}
+ >
+ <div className={styles.header}>
+ <div className={styles.headerLeft}>
+ {isDocument ? (
+ getDocumentIcon((data as DocumentWithMemories).type ?? "")
+ ) : (
+ // @ts-ignore
+ <Brain className={styles.headerIconMemory} />
+ )}
+ <HeadingH3Bold>
+ {isDocument ? "Document" : "Memory"}
+ </HeadingH3Bold>
+ </div>
+ <motion.div whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }}>
+ <Button
+ className={styles.closeButton}
+ onClick={onClose}
+ size="sm"
+ variant="ghost"
+ >
+ {/* @ts-ignore */}
+ <X className={styles.closeIcon} />
+ </Button>
+ </motion.div>
+ </div>
+
+ <div className={styles.sections}>
+ {isDocument ? (
+ <>
+ <div className={styles.section}>
+ <span className={styles.sectionLabel}>
+ Title
+ </span>
+ <p className={styles.sectionValue}>
+ {(data as DocumentWithMemories).title ||
+ "Untitled Document"}
+ </p>
+ </div>
+
+ {(data as DocumentWithMemories).summary && (
+ <div className={styles.section}>
+ <span className={styles.sectionLabel}>
+ Summary
+ </span>
+ <p className={styles.sectionValueTruncated}>
+ {(data as DocumentWithMemories).summary}
+ </p>
+ </div>
+ )}
+
+ <div className={styles.section}>
+ <span className={styles.sectionLabel}>
+ Type
+ </span>
+ <p className={styles.sectionValue}>
+ {formatDocumentType((data as DocumentWithMemories).type ?? "")}
+ </p>
+ </div>
+
+ <div className={styles.section}>
+ <span className={styles.sectionLabel}>
+ Memory Count
+ </span>
+ <p className={styles.sectionValue}>
+ {(data as DocumentWithMemories).memoryEntries.length}{" "}
+ memories
+ </p>
+ </div>
+
+ {((data as DocumentWithMemories).url ||
+ (data as DocumentWithMemories).customId) && (
+ <div className={styles.section}>
+ <span className={styles.sectionLabel}>
+ URL
+ </span>
+ <a
+ className={styles.link}
+ href={(() => {
+ const doc = data as DocumentWithMemories;
+ if (doc.type === "google_doc" && doc.customId) {
+ return `https://docs.google.com/document/d/${doc.customId}`;
+ }
+ if (doc.type === "google_sheet" && doc.customId) {
+ return `https://docs.google.com/spreadsheets/d/${doc.customId}`;
+ }
+ if (doc.type === "google_slide" && doc.customId) {
+ return `https://docs.google.com/presentation/d/${doc.customId}`;
+ }
+ return doc.url ?? undefined;
+ })()}
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ {/* @ts-ignore */}
+ <ExternalLink className={styles.linkIcon} />
+ View Document
+ </a>
+ </div>
+ )}
+ </>
+ ) : (
+ <>
+ <div className={styles.section}>
+ <span className={styles.sectionLabel}>
+ Memory
+ </span>
+ <p className={styles.sectionValue}>
+ {(data as MemoryEntry).memory}
+ </p>
+ {(data as MemoryEntry).isForgotten && (
+ <Badge className={styles.badge} variant="destructive">
+ Forgotten
+ </Badge>
+ )}
+ {(data as MemoryEntry).forgetAfter && (
+ <p className={styles.expiryText}>
+ Expires:{" "}
+ {(data as MemoryEntry).forgetAfter
+ ? new Date(
+ (data as MemoryEntry).forgetAfter!,
+ ).toLocaleDateString()
+ : ""}{" "}
+ {("forgetReason" in data &&
+ (data as any).forgetReason
+ ? `- ${(data as any).forgetReason}`
+ : null)}
+ </p>
+ )}
+ </div>
+
+ <div className={styles.section}>
+ <span className={styles.sectionLabel}>
+ Space
+ </span>
+ <p className={styles.sectionValue}>
+ {(data as MemoryEntry).spaceId || "Default"}
+ </p>
+ </div>
+ </>
+ )}
+
+ <div className={styles.footer}>
+ <div className={styles.metadata}>
+ <span className={styles.metadataItem}>
+ {/* @ts-ignore */}
+ <Calendar className={styles.metadataIcon} />
+ {new Date(data.createdAt).toLocaleDateString()}
+ </span>
+ <span className={styles.metadataItem}>
+ {/* @ts-ignore */}
+ <Hash className={styles.metadataIcon} />
+ {node.id}
+ </span>
+ </div>
+ </div>
+ </div>
+ </motion.div>
+ </motion.div>
+ );
+ },
+);
+
+NodeDetailPanel.displayName = "NodeDetailPanel";
diff --git a/packages/memory-graph/src/components/spaces-dropdown.css.ts b/packages/memory-graph/src/components/spaces-dropdown.css.ts
new file mode 100644
index 00000000..d7af2258
--- /dev/null
+++ b/packages/memory-graph/src/components/spaces-dropdown.css.ts
@@ -0,0 +1,158 @@
+import { style } from "@vanilla-extract/css";
+import { themeContract } from "../styles/theme.css";
+
+/**
+ * Dropdown container
+ */
+export const container = style({
+ position: "relative",
+});
+
+/**
+ * Main trigger button with gradient border effect
+ */
+export const trigger = style({
+ display: "flex",
+ alignItems: "center",
+ gap: themeContract.space[3],
+ paddingLeft: themeContract.space[4],
+ paddingRight: themeContract.space[4],
+ paddingTop: themeContract.space[3],
+ paddingBottom: themeContract.space[3],
+ borderRadius: themeContract.radii.xl,
+ border: "2px solid transparent",
+ backgroundImage:
+ "linear-gradient(#1a1f29, #1a1f29), linear-gradient(150.262deg, #A4E8F5 0%, #267FFA 26%, #464646 49%, #747474 70%, #A4E8F5 100%)",
+ backgroundOrigin: "border-box",
+ backgroundClip: "padding-box, border-box",
+ boxShadow: "inset 0px 2px 1px rgba(84, 84, 84, 0.15)",
+ backdropFilter: "blur(12px)",
+ WebkitBackdropFilter: "blur(12px)",
+ transition: themeContract.transitions.normal,
+ cursor: "pointer",
+ minWidth: "15rem", // min-w-60 = 240px = 15rem
+
+ selectors: {
+ "&:hover": {
+ boxShadow: "inset 0px 2px 1px rgba(84, 84, 84, 0.25)",
+ },
+ },
+});
+
+export const triggerIcon = style({
+ width: "1rem",
+ height: "1rem",
+ color: themeContract.colors.text.secondary,
+});
+
+export const triggerContent = style({
+ flex: 1,
+ textAlign: "left",
+});
+
+export const triggerLabel = style({
+ fontSize: themeContract.typography.fontSize.sm,
+ color: themeContract.colors.text.secondary,
+ fontWeight: themeContract.typography.fontWeight.medium,
+});
+
+export const triggerSubtext = style({
+ fontSize: themeContract.typography.fontSize.xs,
+ color: themeContract.colors.text.muted,
+});
+
+export const triggerChevron = style({
+ width: "1rem",
+ height: "1rem",
+ color: themeContract.colors.text.secondary,
+ transition: "transform 200ms ease",
+});
+
+export const triggerChevronOpen = style({
+ transform: "rotate(180deg)",
+});
+
+/**
+ * Dropdown menu
+ */
+export const dropdown = style({
+ position: "absolute",
+ top: "100%",
+ left: 0,
+ right: 0,
+ marginTop: themeContract.space[2],
+ background: "rgba(15, 23, 42, 0.95)", // slate-900/95
+ backdropFilter: "blur(12px)",
+ WebkitBackdropFilter: "blur(12px)",
+ border: "1px solid rgba(71, 85, 105, 0.4)", // slate-700/40
+ borderRadius: themeContract.radii.xl,
+ boxShadow:
+ "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)", // shadow-xl
+ zIndex: 20,
+ overflow: "hidden",
+});
+
+export const dropdownInner = style({
+ padding: themeContract.space[1],
+});
+
+/**
+ * Dropdown items
+ */
+const dropdownItemBase = style({
+ width: "100%",
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "space-between",
+ paddingLeft: themeContract.space[3],
+ paddingRight: themeContract.space[3],
+ paddingTop: themeContract.space[2],
+ paddingBottom: themeContract.space[2],
+ borderRadius: themeContract.radii.lg,
+ textAlign: "left",
+ transition: themeContract.transitions.normal,
+ cursor: "pointer",
+ border: "none",
+ background: "transparent",
+});
+
+export const dropdownItem = style([
+ dropdownItemBase,
+ {
+ color: themeContract.colors.text.secondary,
+
+ selectors: {
+ "&:hover": {
+ backgroundColor: "rgba(51, 65, 85, 0.5)", // slate-700/50
+ },
+ },
+ },
+]);
+
+export const dropdownItemActive = style([
+ dropdownItemBase,
+ {
+ backgroundColor: "rgba(59, 130, 246, 0.2)", // blue-500/20
+ color: "rgb(147, 197, 253)", // blue-300
+ },
+]);
+
+export const dropdownItemLabel = style({
+ fontSize: themeContract.typography.fontSize.sm,
+ flex: 1,
+});
+
+export const dropdownItemLabelTruncate = style({
+ fontSize: themeContract.typography.fontSize.sm,
+ flex: 1,
+ overflow: "hidden",
+ textOverflow: "ellipsis",
+ whiteSpace: "nowrap",
+});
+
+export const dropdownItemBadge = style({
+ backgroundColor: "rgba(51, 65, 85, 0.5)", // slate-700/50
+ color: themeContract.colors.text.secondary,
+ fontSize: themeContract.typography.fontSize.xs,
+ marginLeft: themeContract.space[2],
+});
diff --git a/packages/memory-graph/src/components/spaces-dropdown.tsx b/packages/memory-graph/src/components/spaces-dropdown.tsx
new file mode 100644
index 00000000..b70059f5
--- /dev/null
+++ b/packages/memory-graph/src/components/spaces-dropdown.tsx
@@ -0,0 +1,110 @@
+"use client";
+
+import { Badge } from "@/ui/badge";
+import { ChevronDown, Eye } from "lucide-react";
+import { memo, useEffect, useRef, useState } from "react";
+import type { SpacesDropdownProps } from "@/types";
+import * as styles from "./spaces-dropdown.css";
+
+export const SpacesDropdown = memo<SpacesDropdownProps>(
+ ({ selectedSpace, availableSpaces, spaceMemoryCounts, onSpaceChange }) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const dropdownRef = useRef<HTMLDivElement>(null);
+
+ // Close dropdown when clicking outside
+ useEffect(() => {
+ const handleClickOutside = (event: MouseEvent) => {
+ if (
+ dropdownRef.current &&
+ !dropdownRef.current.contains(event.target as Node)
+ ) {
+ setIsOpen(false);
+ }
+ };
+
+ document.addEventListener("mousedown", handleClickOutside);
+ return () =>
+ document.removeEventListener("mousedown", handleClickOutside);
+ }, []);
+
+ const totalMemories = Object.values(spaceMemoryCounts).reduce(
+ (sum, count) => sum + count,
+ 0,
+ );
+
+ return (
+ <div className={styles.container} ref={dropdownRef}>
+ <button
+ className={styles.trigger}
+ onClick={() => setIsOpen(!isOpen)}
+ type="button"
+ >
+ {/*@ts-ignore */}
+ <Eye className={styles.triggerIcon} />
+ <div className={styles.triggerContent}>
+ <span className={styles.triggerLabel}>
+ {selectedSpace === "all"
+ ? "All Spaces"
+ : selectedSpace || "Select space"}
+ </span>
+ <div className={styles.triggerSubtext}>
+ {selectedSpace === "all"
+ ? `${totalMemories} total memories`
+ : `${spaceMemoryCounts[selectedSpace] || 0} memories`}
+ </div>
+ </div>
+ {/*@ts-ignore */}
+ <ChevronDown
+ className={`${styles.triggerChevron} ${isOpen ? styles.triggerChevronOpen : ""}`}
+ />
+ </button>
+
+ {isOpen && (
+ <div className={styles.dropdown}>
+ <div className={styles.dropdownInner}>
+ <button
+ className={
+ selectedSpace === "all"
+ ? styles.dropdownItemActive
+ : styles.dropdownItem
+ }
+ onClick={() => {
+ onSpaceChange("all");
+ setIsOpen(false);
+ }}
+ type="button"
+ >
+ <span className={styles.dropdownItemLabel}>All Spaces</span>
+ <Badge className={styles.dropdownItemBadge}>
+ {totalMemories}
+ </Badge>
+ </button>
+ {availableSpaces.map((space) => (
+ <button
+ className={
+ selectedSpace === space
+ ? styles.dropdownItemActive
+ : styles.dropdownItem
+ }
+ key={space}
+ onClick={() => {
+ onSpaceChange(space);
+ setIsOpen(false);
+ }}
+ type="button"
+ >
+ <span className={styles.dropdownItemLabelTruncate}>{space}</span>
+ <Badge className={styles.dropdownItemBadge}>
+ {spaceMemoryCounts[space] || 0}
+ </Badge>
+ </button>
+ ))}
+ </div>
+ </div>
+ )}
+ </div>
+ );
+ },
+);
+
+SpacesDropdown.displayName = "SpacesDropdown";
diff --git a/packages/memory-graph/src/constants.ts b/packages/memory-graph/src/constants.ts
new file mode 100644
index 00000000..23193601
--- /dev/null
+++ b/packages/memory-graph/src/constants.ts
@@ -0,0 +1,100 @@
+// Enhanced glass-morphism color palette
+export const colors = {
+ background: {
+ primary: "#0f1419", // Deep dark blue-gray
+ secondary: "#1a1f29", // Slightly lighter
+ accent: "#252a35", // Card backgrounds
+ },
+ document: {
+ primary: "rgba(255, 255, 255, 0.06)", // Subtle glass white
+ secondary: "rgba(255, 255, 255, 0.12)", // More visible
+ accent: "rgba(255, 255, 255, 0.18)", // Hover state
+ border: "rgba(255, 255, 255, 0.25)", // Sharp borders
+ glow: "rgba(147, 197, 253, 0.4)", // Blue glow for interaction
+ },
+ memory: {
+ primary: "rgba(147, 197, 253, 0.08)", // Subtle glass blue
+ secondary: "rgba(147, 197, 253, 0.16)", // More visible
+ accent: "rgba(147, 197, 253, 0.24)", // Hover state
+ border: "rgba(147, 197, 253, 0.35)", // Sharp borders
+ glow: "rgba(147, 197, 253, 0.5)", // Blue glow for interaction
+ },
+ connection: {
+ weak: "rgba(148, 163, 184, 0)", // Very subtle
+ memory: "rgba(148, 163, 184, 0.3)", // Very subtle
+ medium: "rgba(148, 163, 184, 0.125)", // Medium visibility
+ strong: "rgba(148, 163, 184, 0.4)", // Strong connection
+ },
+ text: {
+ primary: "#ffffff", // Pure white
+ secondary: "#e2e8f0", // Light gray
+ muted: "#94a3b8", // Medium gray
+ },
+ accent: {
+ primary: "rgba(59, 130, 246, 0.7)", // Clean blue
+ secondary: "rgba(99, 102, 241, 0.6)", // Clean purple
+ glow: "rgba(147, 197, 253, 0.6)", // Subtle glow
+ amber: "rgba(251, 165, 36, 0.8)", // Amber for expiring
+ emerald: "rgba(16, 185, 129, 0.4)", // Emerald for new
+ },
+ status: {
+ forgotten: "rgba(220, 38, 38, 0.15)", // Red for forgotten
+ expiring: "rgba(251, 165, 36, 0.8)", // Amber for expiring soon
+ new: "rgba(16, 185, 129, 0.4)", // Emerald for new memories
+ },
+ relations: {
+ updates: "rgba(147, 77, 253, 0.5)", // purple
+ extends: "rgba(16, 185, 129, 0.5)", // green
+ derives: "rgba(147, 197, 253, 0.5)", // blue
+ },
+};
+
+export const LAYOUT_CONSTANTS = {
+ centerX: 400,
+ centerY: 300,
+ clusterRadius: 300, // Memory "bubble" size around a doc - smaller bubble
+ spaceSpacing: 1600, // How far apart the *spaces* (groups of docs) sit - push spaces way out
+ documentSpacing: 1000, // How far the first doc in a space sits from its space-centre - push docs way out
+ minDocDist: 900, // Minimum distance two documents in the **same space** are allowed to be - sets repulsion radius
+ memoryClusterRadius: 300,
+};
+
+// Graph view settings
+export const GRAPH_SETTINGS = {
+ console: {
+ initialZoom: 0.8, // Higher zoom for console - better overview
+ initialPanX: 0,
+ initialPanY: 0,
+ },
+ consumer: {
+ initialZoom: 0.5, // Changed from 0.1 to 0.5 for better initial visibility
+ initialPanX: 400, // Pan towards center to compensate for larger layout
+ initialPanY: 300, // Pan towards center to compensate for larger layout
+ },
+};
+
+// Responsive positioning for different app variants
+export const POSITIONING = {
+ console: {
+ legend: {
+ desktop: "bottom-4 right-4",
+ mobile: "bottom-4 right-4",
+ },
+ loadingIndicator: "top-20 right-4",
+
+ spacesSelector: "top-4 left-4",
+ viewToggle: "", // Not used in console
+ nodeDetail: "top-4 right-4",
+ },
+ consumer: {
+ legend: {
+ desktop: "top-18 right-4",
+ mobile: "bottom-[180px] left-4",
+ },
+ loadingIndicator: "top-20 right-4",
+
+ spacesSelector: "", // Hidden in consumer
+ viewToggle: "top-4 right-4", // Consumer has view toggle
+ nodeDetail: "top-4 right-4",
+ },
+};
diff --git a/packages/memory-graph/src/hooks/use-documents-query.ts b/packages/memory-graph/src/hooks/use-documents-query.ts
new file mode 100644
index 00000000..eb9ab892
--- /dev/null
+++ b/packages/memory-graph/src/hooks/use-documents-query.ts
@@ -0,0 +1,113 @@
+"use client";
+
+import { useInfiniteQuery, useQuery } from "@tanstack/react-query";
+import { fetchDocuments, type FetchDocumentsOptions } from "@/lib/api-client";
+import type { DocumentsResponse } from "@/api-types";
+
+export interface UseDocumentsQueryOptions {
+ apiKey: string;
+ baseUrl?: string;
+ id?: string; // Optional document ID to filter by
+ containerTags?: string[];
+ limit?: number;
+ sort?: "createdAt" | "updatedAt";
+ order?: "asc" | "desc";
+ enabled?: boolean; // Whether to enable the query
+}
+
+/**
+ * Hook for fetching a single page of documents
+ * Useful when you don't need pagination
+ */
+export function useDocumentsQuery(options: UseDocumentsQueryOptions) {
+ const {
+ apiKey,
+ baseUrl,
+ containerTags,
+ limit = 50,
+ sort = "createdAt",
+ order = "desc",
+ enabled = true,
+ } = options;
+
+ return useQuery({
+ queryKey: ["documents", { apiKey, baseUrl, containerTags, limit, sort, order }],
+ queryFn: async () => {
+ return fetchDocuments({
+ apiKey,
+ baseUrl,
+ page: 1,
+ limit,
+ sort,
+ order,
+ containerTags,
+ });
+ },
+ enabled: enabled && !!apiKey,
+ staleTime: 1000 * 60 * 5, // 5 minutes
+ retry: 2,
+ });
+}
+
+/**
+ * Hook for fetching documents with infinite scroll/pagination support
+ * Automatically handles loading more pages
+ */
+export function useInfiniteDocumentsQuery(options: UseDocumentsQueryOptions) {
+ const {
+ apiKey,
+ baseUrl,
+ containerTags,
+ limit = 500,
+ sort = "createdAt",
+ order = "desc",
+ enabled = true,
+ } = options;
+
+ return useInfiniteQuery({
+ queryKey: ["documents", "infinite", { apiKey, baseUrl, containerTags, limit, sort, order }],
+ queryFn: async ({ pageParam = 1 }) => {
+ return fetchDocuments({
+ apiKey,
+ baseUrl,
+ page: pageParam,
+ limit,
+ sort,
+ order,
+ containerTags,
+ });
+ },
+ initialPageParam: 1,
+ getNextPageParam: (lastPage: DocumentsResponse) => {
+ const { currentPage, totalPages } = lastPage.pagination;
+ return currentPage < totalPages ? currentPage + 1 : undefined;
+ },
+ enabled: enabled && !!apiKey,
+ staleTime: 1000 * 60 * 5, // 5 minutes
+ retry: 2,
+ });
+}
+
+/**
+ * Helper to flatten infinite query results into a single documents array
+ */
+export function flattenDocuments(data: { pages: DocumentsResponse[] } | undefined) {
+ if (!data?.pages) return [];
+ return data.pages.flatMap((page) => page.documents);
+}
+
+/**
+ * Helper to get total documents count from infinite query
+ */
+export function getTotalDocuments(data: { pages: DocumentsResponse[] } | undefined) {
+ if (!data?.pages?.[0]) return 0;
+ return data.pages[0].pagination.totalItems;
+}
+
+/**
+ * Helper to get current loaded count from infinite query
+ */
+export function getLoadedCount(data: { pages: DocumentsResponse[] } | undefined) {
+ if (!data?.pages) return 0;
+ return data.pages.reduce((sum, page) => sum + page.documents.length, 0);
+}
diff --git a/packages/memory-graph/src/hooks/use-graph-data.ts b/packages/memory-graph/src/hooks/use-graph-data.ts
new file mode 100644
index 00000000..030eea61
--- /dev/null
+++ b/packages/memory-graph/src/hooks/use-graph-data.ts
@@ -0,0 +1,308 @@
+"use client";
+
+import {
+ calculateSemanticSimilarity,
+ getConnectionVisualProps,
+ getMagicalConnectionColor,
+} from "@/lib/similarity";
+import { useMemo } from "react";
+import { colors, LAYOUT_CONSTANTS } from "@/constants";
+import type {
+ DocumentsResponse,
+ DocumentWithMemories,
+ GraphEdge,
+ GraphNode,
+ MemoryEntry,
+ MemoryRelation,
+} from "@/types";
+
+export function useGraphData(
+ data: DocumentsResponse | null,
+ selectedSpace: string,
+ nodePositions: Map<string, { x: number; y: number }>,
+ draggingNodeId: string | null,
+) {
+ return useMemo(() => {
+ if (!data?.documents) return { nodes: [], edges: [] };
+
+ const allNodes: GraphNode[] = [];
+ const allEdges: GraphEdge[] = [];
+
+ // Filter documents that have memories in selected space
+ const filteredDocuments = data.documents
+ .map((doc) => ({
+ ...doc,
+ memoryEntries:
+ selectedSpace === "all"
+ ? doc.memoryEntries
+ : doc.memoryEntries.filter(
+ (memory) =>
+ (memory.spaceContainerTag ?? memory.spaceId ?? "default") ===
+ selectedSpace,
+ ),
+ }))
+ .filter((doc) => doc.memoryEntries.length > 0);
+
+ // Group documents by space for better clustering
+ const documentsBySpace = new Map<string, typeof filteredDocuments>();
+ filteredDocuments.forEach((doc) => {
+ const docSpace =
+ doc.memoryEntries[0]?.spaceContainerTag ??
+ doc.memoryEntries[0]?.spaceId ??
+ "default";
+ if (!documentsBySpace.has(docSpace)) {
+ documentsBySpace.set(docSpace, []);
+ }
+ const spaceDocsArr = documentsBySpace.get(docSpace);
+ if (spaceDocsArr) {
+ spaceDocsArr.push(doc);
+ }
+ });
+
+ // Enhanced Layout with Space Separation
+ const { centerX, centerY, clusterRadius, spaceSpacing, documentSpacing } =
+ LAYOUT_CONSTANTS;
+
+ /* 1. Build DOCUMENT nodes with space-aware clustering */
+ const documentNodes: GraphNode[] = [];
+ let spaceIndex = 0;
+
+ documentsBySpace.forEach((spaceDocs) => {
+ const spaceAngle = (spaceIndex / documentsBySpace.size) * Math.PI * 2;
+ const spaceOffsetX = Math.cos(spaceAngle) * spaceSpacing;
+ const spaceOffsetY = Math.sin(spaceAngle) * spaceSpacing;
+ const spaceCenterX = centerX + spaceOffsetX;
+ const spaceCenterY = centerY + spaceOffsetY;
+
+ spaceDocs.forEach((doc, docIndex) => {
+ // Create proper circular layout with concentric rings
+ const docsPerRing = 6; // Start with 6 docs in inner ring
+ let currentRing = 0;
+ let docsInCurrentRing = docsPerRing;
+ let totalDocsInPreviousRings = 0;
+
+ // Find which ring this document belongs to
+ while (totalDocsInPreviousRings + docsInCurrentRing <= docIndex) {
+ totalDocsInPreviousRings += docsInCurrentRing;
+ currentRing++;
+ docsInCurrentRing = docsPerRing + currentRing * 4; // Each ring has more docs
+ }
+
+ // Position within the ring
+ const positionInRing = docIndex - totalDocsInPreviousRings;
+ const angleInRing = (positionInRing / docsInCurrentRing) * Math.PI * 2;
+
+ // Radius increases significantly with each ring
+ const baseRadius = documentSpacing * 0.8;
+ const radius =
+ currentRing === 0
+ ? baseRadius
+ : baseRadius + currentRing * documentSpacing * 1.2;
+
+ const defaultX = spaceCenterX + Math.cos(angleInRing) * radius;
+ const defaultY = spaceCenterY + Math.sin(angleInRing) * radius;
+
+ const customPos = nodePositions.get(doc.id);
+
+ documentNodes.push({
+ id: doc.id,
+ type: "document",
+ x: customPos?.x ?? defaultX,
+ y: customPos?.y ?? defaultY,
+ data: doc,
+ size: 58,
+ color: colors.document.primary,
+ isHovered: false,
+ isDragging: draggingNodeId === doc.id,
+ } satisfies GraphNode);
+ });
+
+ spaceIndex++;
+ });
+
+ /* 2. Gentle document collision avoidance with dampening */
+ const minDocDist = LAYOUT_CONSTANTS.minDocDist;
+
+ // Reduced iterations and gentler repulsion for smoother movement
+ for (let iter = 0; iter < 2; iter++) {
+ documentNodes.forEach((nodeA) => {
+ documentNodes.forEach((nodeB) => {
+ if (nodeA.id >= nodeB.id) return;
+
+ // Only repel documents in the same space
+ const spaceA =
+ (nodeA.data as DocumentWithMemories).memoryEntries[0]
+ ?.spaceContainerTag ??
+ (nodeA.data as DocumentWithMemories).memoryEntries[0]?.spaceId ??
+ "default";
+ const spaceB =
+ (nodeB.data as DocumentWithMemories).memoryEntries[0]
+ ?.spaceContainerTag ??
+ (nodeB.data as DocumentWithMemories).memoryEntries[0]?.spaceId ??
+ "default";
+
+ if (spaceA !== spaceB) return;
+
+ const dx = nodeB.x - nodeA.x;
+ const dy = nodeB.y - nodeA.y;
+ const dist = Math.sqrt(dx * dx + dy * dy) || 1;
+
+ if (dist < minDocDist) {
+ // Much gentler push with dampening
+ const push = (minDocDist - dist) / 8;
+ const dampening = Math.max(0.1, Math.min(1, dist / minDocDist));
+ const smoothPush = push * dampening * 0.5;
+
+ const nx = dx / dist;
+ const ny = dy / dist;
+ nodeA.x -= nx * smoothPush;
+ nodeA.y -= ny * smoothPush;
+ nodeB.x += nx * smoothPush;
+ nodeB.y += ny * smoothPush;
+ }
+ });
+ });
+ }
+
+ allNodes.push(...documentNodes);
+
+ /* 3. Add memories around documents WITH doc-memory connections */
+ documentNodes.forEach((docNode) => {
+ const memoryNodeMap = new Map<string, GraphNode>();
+ const doc = docNode.data as DocumentWithMemories;
+
+ doc.memoryEntries.forEach((memory, memIndex) => {
+ const memoryId = `${memory.id}`;
+ const customMemPos = nodePositions.get(memoryId);
+
+ const clusterAngle =
+ (memIndex / doc.memoryEntries.length) * Math.PI * 2;
+ const variation = Math.sin(memIndex * 2.5) * 0.3 + 0.7;
+ const distance = clusterRadius * variation;
+
+ const seed =
+ memIndex * 12345 + Number.parseInt(docNode.id.slice(0, 6), 36);
+ const offsetX = Math.sin(seed) * 0.5 * 40;
+ const offsetY = Math.cos(seed) * 0.5 * 40;
+
+ const defaultMemX =
+ docNode.x + Math.cos(clusterAngle) * distance + offsetX;
+ const defaultMemY =
+ docNode.y + Math.sin(clusterAngle) * distance + offsetY;
+
+ if (!memoryNodeMap.has(memoryId)) {
+ const memoryNode: GraphNode = {
+ id: memoryId,
+ type: "memory",
+ x: customMemPos?.x ?? defaultMemX,
+ y: customMemPos?.y ?? defaultMemY,
+ data: memory,
+ size: Math.max(
+ 32,
+ Math.min(48, (memory.memory?.length || 50) * 0.5),
+ ),
+ color: colors.memory.primary,
+ isHovered: false,
+ isDragging: draggingNodeId === memoryId,
+ };
+ memoryNodeMap.set(memoryId, memoryNode);
+ allNodes.push(memoryNode);
+ }
+
+ // Create doc-memory edge with similarity
+ allEdges.push({
+ id: `edge-${docNode.id}-${memory.id}`,
+ source: docNode.id,
+ target: memoryId,
+ similarity: 1,
+ visualProps: getConnectionVisualProps(1),
+ color: colors.connection.memory,
+ edgeType: "doc-memory",
+ });
+ });
+ });
+
+ // Build mapping of memoryId -> nodeId for version chains
+ const memNodeIdMap = new Map<string, string>();
+ allNodes.forEach((n) => {
+ if (n.type === "memory") {
+ memNodeIdMap.set((n.data as MemoryEntry).id, n.id);
+ }
+ });
+
+ // Add version-chain edges (old -> new)
+ data.documents.forEach((doc) => {
+ doc.memoryEntries.forEach((mem: MemoryEntry) => {
+ // Support both new object structure and legacy array/single parent fields
+ let parentRelations: Record<string, MemoryRelation> = {};
+
+ if (
+ mem.memoryRelations &&
+ Array.isArray(mem.memoryRelations) &&
+ mem.memoryRelations.length > 0
+ ) {
+ // Convert array to Record
+ parentRelations = mem.memoryRelations.reduce((acc, rel) => {
+ acc[rel.targetMemoryId] = rel.relationType;
+ return acc;
+ }, {} as Record<string, MemoryRelation>);
+ } else if (mem.parentMemoryId) {
+ parentRelations = {
+ [mem.parentMemoryId]: "updates" as MemoryRelation,
+ };
+ }
+ Object.entries(parentRelations).forEach(([pid, relationType]) => {
+ const fromId = memNodeIdMap.get(pid);
+ const toId = memNodeIdMap.get(mem.id);
+ if (fromId && toId) {
+ allEdges.push({
+ id: `version-${fromId}-${toId}`,
+ source: fromId,
+ target: toId,
+ similarity: 1,
+ visualProps: {
+ opacity: 0.8,
+ thickness: 1,
+ glow: 0,
+ pulseDuration: 3000,
+ },
+ // choose color based on relation type
+ color: colors.relations[relationType] ?? colors.relations.updates,
+ edgeType: "version",
+ relationType: relationType as MemoryRelation,
+ });
+ }
+ });
+ });
+ });
+
+ // Document-to-document similarity edges
+ for (let i = 0; i < filteredDocuments.length; i++) {
+ const docI = filteredDocuments[i];
+ if (!docI) continue;
+
+ for (let j = i + 1; j < filteredDocuments.length; j++) {
+ const docJ = filteredDocuments[j];
+ if (!docJ) continue;
+
+ const sim = calculateSemanticSimilarity(
+ docI.summaryEmbedding ? Array.from(docI.summaryEmbedding) : null,
+ docJ.summaryEmbedding ? Array.from(docJ.summaryEmbedding) : null,
+ );
+ if (sim > 0.725) {
+ allEdges.push({
+ id: `doc-doc-${docI.id}-${docJ.id}`,
+ source: docI.id,
+ target: docJ.id,
+ similarity: sim,
+ visualProps: getConnectionVisualProps(sim),
+ color: getMagicalConnectionColor(sim, 200),
+ edgeType: "doc-doc",
+ });
+ }
+ }
+ }
+
+ return { nodes: allNodes, edges: allEdges };
+ }, [data, selectedSpace, nodePositions, draggingNodeId]);
+}
diff --git a/packages/memory-graph/src/hooks/use-graph-interactions.ts b/packages/memory-graph/src/hooks/use-graph-interactions.ts
new file mode 100644
index 00000000..fa794397
--- /dev/null
+++ b/packages/memory-graph/src/hooks/use-graph-interactions.ts
@@ -0,0 +1,564 @@
+"use client";
+
+import { useCallback, useRef, useState } from "react";
+import { GRAPH_SETTINGS } from "@/constants";
+import type { GraphNode } from "@/types";
+
+export function useGraphInteractions(
+ variant: "console" | "consumer" = "console",
+) {
+ const settings = GRAPH_SETTINGS[variant];
+
+ const [panX, setPanX] = useState(settings.initialPanX);
+ const [panY, setPanY] = useState(settings.initialPanY);
+ const [zoom, setZoom] = useState(settings.initialZoom);
+ const [isPanning, setIsPanning] = useState(false);
+ const [panStart, setPanStart] = useState({ x: 0, y: 0 });
+ const [hoveredNode, setHoveredNode] = useState<string | null>(null);
+ const [selectedNode, setSelectedNode] = useState<string | null>(null);
+ const [draggingNodeId, setDraggingNodeId] = useState<string | null>(null);
+ const [dragStart, setDragStart] = useState({
+ x: 0,
+ y: 0,
+ nodeX: 0,
+ nodeY: 0,
+ });
+ const [nodePositions, setNodePositions] = useState<
+ Map<string, { x: number; y: number }>
+ >(new Map());
+
+ // Touch gesture state
+ const [touchState, setTouchState] = useState<{
+ touches: { id: number; x: number; y: number }[];
+ lastDistance: number;
+ lastCenter: { x: number; y: number };
+ isGesturing: boolean;
+ }>({
+ touches: [],
+ lastDistance: 0,
+ lastCenter: { x: 0, y: 0 },
+ isGesturing: false,
+ });
+
+ // Animation state for smooth transitions
+ const animationRef = useRef<number | null>(null);
+ const [isAnimating, setIsAnimating] = useState(false);
+
+ // Smooth animation helper
+ const animateToViewState = useCallback(
+ (
+ targetPanX: number,
+ targetPanY: number,
+ targetZoom: number,
+ duration = 300,
+ ) => {
+ if (animationRef.current) {
+ cancelAnimationFrame(animationRef.current);
+ }
+
+ const startPanX = panX;
+ const startPanY = panY;
+ const startZoom = zoom;
+ const startTime = Date.now();
+
+ setIsAnimating(true);
+
+ const animate = () => {
+ const elapsed = Date.now() - startTime;
+ const progress = Math.min(elapsed / duration, 1);
+
+ // Ease out cubic function for smooth transitions
+ const easeOut = 1 - (1 - progress) ** 3;
+
+ const currentPanX = startPanX + (targetPanX - startPanX) * easeOut;
+ const currentPanY = startPanY + (targetPanY - startPanY) * easeOut;
+ const currentZoom = startZoom + (targetZoom - startZoom) * easeOut;
+
+ setPanX(currentPanX);
+ setPanY(currentPanY);
+ setZoom(currentZoom);
+
+ if (progress < 1) {
+ animationRef.current = requestAnimationFrame(animate);
+ } else {
+ setIsAnimating(false);
+ animationRef.current = null;
+ }
+ };
+
+ animate();
+ },
+ [panX, panY, zoom],
+ );
+
+ // Node drag handlers
+ const handleNodeDragStart = useCallback(
+ (nodeId: string, e: React.MouseEvent, nodes?: GraphNode[]) => {
+ const node = nodes?.find((n) => n.id === nodeId);
+ if (!node) return;
+
+ setDraggingNodeId(nodeId);
+ setDragStart({
+ x: e.clientX,
+ y: e.clientY,
+ nodeX: node.x,
+ nodeY: node.y,
+ });
+ },
+ [],
+ );
+
+ const handleNodeDragMove = useCallback(
+ (e: React.MouseEvent) => {
+ if (!draggingNodeId) return;
+
+ const deltaX = (e.clientX - dragStart.x) / zoom;
+ const deltaY = (e.clientY - dragStart.y) / zoom;
+
+ const newX = dragStart.nodeX + deltaX;
+ const newY = dragStart.nodeY + deltaY;
+
+ setNodePositions((prev) =>
+ new Map(prev).set(draggingNodeId, { x: newX, y: newY }),
+ );
+ },
+ [draggingNodeId, dragStart, zoom],
+ );
+
+ const handleNodeDragEnd = useCallback(() => {
+ setDraggingNodeId(null);
+ }, []);
+
+ // Pan handlers
+ const handlePanStart = useCallback(
+ (e: React.MouseEvent) => {
+ setIsPanning(true);
+ setPanStart({ x: e.clientX - panX, y: e.clientY - panY });
+ },
+ [panX, panY],
+ );
+
+ const handlePanMove = useCallback(
+ (e: React.MouseEvent) => {
+ if (!isPanning || draggingNodeId) return;
+
+ const newPanX = e.clientX - panStart.x;
+ const newPanY = e.clientY - panStart.y;
+ setPanX(newPanX);
+ setPanY(newPanY);
+ },
+ [isPanning, panStart, draggingNodeId],
+ );
+
+ const handlePanEnd = useCallback(() => {
+ setIsPanning(false);
+ }, []);
+
+ // Zoom handlers
+ const handleWheel = useCallback(
+ (e: React.WheelEvent) => {
+ // Always prevent default to stop browser navigation
+ e.preventDefault();
+ e.stopPropagation();
+
+ // Handle horizontal scrolling (trackpad swipe) by converting to pan
+ if (Math.abs(e.deltaX) > Math.abs(e.deltaY)) {
+ // Horizontal scroll - pan the graph instead of zooming
+ const panDelta = e.deltaX * 0.5;
+ setPanX((prev) => prev - panDelta);
+ return;
+ }
+
+ // Vertical scroll - zoom behavior
+ const delta = e.deltaY > 0 ? 0.97 : 1.03;
+ const newZoom = Math.max(0.05, Math.min(3, zoom * delta));
+
+ // Get mouse position relative to the viewport
+ let mouseX = e.clientX;
+ let mouseY = e.clientY;
+
+ // Try to get the container bounds to make coordinates relative to the graph container
+ const target = e.currentTarget;
+ if (target && "getBoundingClientRect" in target) {
+ const rect = target.getBoundingClientRect();
+ mouseX = e.clientX - rect.left;
+ mouseY = e.clientY - rect.top;
+ }
+
+ // Calculate the world position of the mouse cursor
+ const worldX = (mouseX - panX) / zoom;
+ const worldY = (mouseY - panY) / zoom;
+
+ // Calculate new pan to keep the mouse position stationary
+ const newPanX = mouseX - worldX * newZoom;
+ const newPanY = mouseY - worldY * newZoom;
+
+ setZoom(newZoom);
+ setPanX(newPanX);
+ setPanY(newPanY);
+ },
+ [zoom, panX, panY],
+ );
+
+ const zoomIn = useCallback(
+ (centerX?: number, centerY?: number, animate = true) => {
+ const zoomFactor = 1.2;
+ const newZoom = Math.min(3, zoom * zoomFactor); // Increased max zoom to 3x
+
+ if (centerX !== undefined && centerY !== undefined) {
+ // Mouse-centered zoom for programmatic zoom in
+ const worldX = (centerX - panX) / zoom;
+ const worldY = (centerY - panY) / zoom;
+ const newPanX = centerX - worldX * newZoom;
+ const newPanY = centerY - worldY * newZoom;
+
+ if (animate && !isAnimating) {
+ animateToViewState(newPanX, newPanY, newZoom, 200);
+ } else {
+ setZoom(newZoom);
+ setPanX(newPanX);
+ setPanY(newPanY);
+ }
+ } else {
+ if (animate && !isAnimating) {
+ animateToViewState(panX, panY, newZoom, 200);
+ } else {
+ setZoom(newZoom);
+ }
+ }
+ },
+ [zoom, panX, panY, isAnimating, animateToViewState],
+ );
+
+ const zoomOut = useCallback(
+ (centerX?: number, centerY?: number, animate = true) => {
+ const zoomFactor = 0.8;
+ const newZoom = Math.max(0.05, zoom * zoomFactor); // Decreased min zoom to 0.05x
+
+ if (centerX !== undefined && centerY !== undefined) {
+ // Mouse-centered zoom for programmatic zoom out
+ const worldX = (centerX - panX) / zoom;
+ const worldY = (centerY - panY) / zoom;
+ const newPanX = centerX - worldX * newZoom;
+ const newPanY = centerY - worldY * newZoom;
+
+ if (animate && !isAnimating) {
+ animateToViewState(newPanX, newPanY, newZoom, 200);
+ } else {
+ setZoom(newZoom);
+ setPanX(newPanX);
+ setPanY(newPanY);
+ }
+ } else {
+ if (animate && !isAnimating) {
+ animateToViewState(panX, panY, newZoom, 200);
+ } else {
+ setZoom(newZoom);
+ }
+ }
+ },
+ [zoom, panX, panY, isAnimating, animateToViewState],
+ );
+
+ const resetView = useCallback(() => {
+ setPanX(settings.initialPanX);
+ setPanY(settings.initialPanY);
+ setZoom(settings.initialZoom);
+ setNodePositions(new Map());
+ }, [settings]);
+
+ // Auto-fit graph to viewport
+ const autoFitToViewport = useCallback(
+ (
+ nodes: GraphNode[],
+ viewportWidth: number,
+ viewportHeight: number,
+ options?: { occludedRightPx?: number; animate?: boolean },
+ ) => {
+ if (nodes.length === 0) return;
+
+ // Find the bounds of all nodes
+ let minX = Number.POSITIVE_INFINITY;
+ let maxX = Number.NEGATIVE_INFINITY;
+ let minY = Number.POSITIVE_INFINITY;
+ let maxY = Number.NEGATIVE_INFINITY;
+
+ nodes.forEach((node) => {
+ minX = Math.min(minX, node.x - node.size / 2);
+ maxX = Math.max(maxX, node.x + node.size / 2);
+ minY = Math.min(minY, node.y - node.size / 2);
+ maxY = Math.max(maxY, node.y + node.size / 2);
+ });
+
+ // Calculate the center of the content
+ const contentCenterX = (minX + maxX) / 2;
+ const contentCenterY = (minY + maxY) / 2;
+
+ // Calculate the size of the content
+ const contentWidth = maxX - minX;
+ const contentHeight = maxY - minY;
+
+ // Add padding (20% on each side)
+ const paddingFactor = 1.4;
+ const paddedWidth = contentWidth * paddingFactor;
+ const paddedHeight = contentHeight * paddingFactor;
+
+ // Account for occluded area on the right (e.g., chat panel)
+ const occludedRightPx = Math.max(0, options?.occludedRightPx ?? 0);
+ const availableWidth = Math.max(1, viewportWidth - occludedRightPx);
+
+ // Calculate the zoom needed to fit the content within available width
+ const zoomX = availableWidth / paddedWidth;
+ const zoomY = viewportHeight / paddedHeight;
+ const newZoom = Math.min(Math.max(0.05, Math.min(zoomX, zoomY)), 3);
+
+ // Calculate pan to center the content within available area
+ const availableCenterX = availableWidth / 2;
+ const newPanX = availableCenterX - contentCenterX * newZoom;
+ const newPanY = viewportHeight / 2 - contentCenterY * newZoom;
+
+ // Apply the new view (optional animation)
+ if (options?.animate) {
+ const steps = 8;
+ const durationMs = 160; // snappy
+ const intervalMs = Math.max(1, Math.floor(durationMs / steps));
+ const startZoom = zoom;
+ const startPanX = panX;
+ const startPanY = panY;
+ let i = 0;
+ const ease = (t: number) => 1 - (1 - t) ** 2; // ease-out quad
+ const timer = setInterval(() => {
+ i++;
+ const t = ease(i / steps);
+ setZoom(startZoom + (newZoom - startZoom) * t);
+ setPanX(startPanX + (newPanX - startPanX) * t);
+ setPanY(startPanY + (newPanY - startPanY) * t);
+ if (i >= steps) clearInterval(timer);
+ }, intervalMs);
+ } else {
+ setZoom(newZoom);
+ setPanX(newPanX);
+ setPanY(newPanY);
+ }
+ },
+ [zoom, panX, panY],
+ );
+
+ // Touch gesture handlers for mobile pinch-to-zoom
+ const handleTouchStart = useCallback((e: React.TouchEvent) => {
+ const touches = Array.from(e.touches).map((touch) => ({
+ id: touch.identifier,
+ x: touch.clientX,
+ y: touch.clientY,
+ }));
+
+ if (touches.length >= 2) {
+ // Start gesture with two or more fingers
+ const touch1 = touches[0]!;
+ const touch2 = touches[1]!;
+
+ const distance = Math.sqrt(
+ (touch2.x - touch1.x) ** 2 + (touch2.y - touch1.y) ** 2,
+ );
+
+ const center = {
+ x: (touch1.x + touch2.x) / 2,
+ y: (touch1.y + touch2.y) / 2,
+ };
+
+ setTouchState({
+ touches,
+ lastDistance: distance,
+ lastCenter: center,
+ isGesturing: true,
+ });
+ } else {
+ setTouchState((prev) => ({ ...prev, touches, isGesturing: false }));
+ }
+ }, []);
+
+ const handleTouchMove = useCallback(
+ (e: React.TouchEvent) => {
+ e.preventDefault();
+
+ const touches = Array.from(e.touches).map((touch) => ({
+ id: touch.identifier,
+ x: touch.clientX,
+ y: touch.clientY,
+ }));
+
+ if (touches.length >= 2 && touchState.isGesturing) {
+ const touch1 = touches[0]!;
+ const touch2 = touches[1]!;
+
+ const distance = Math.sqrt(
+ (touch2.x - touch1.x) ** 2 + (touch2.y - touch1.y) ** 2,
+ );
+
+ const center = {
+ x: (touch1.x + touch2.x) / 2,
+ y: (touch1.y + touch2.y) / 2,
+ };
+
+ // Calculate zoom change based on pinch distance change
+ const distanceChange = distance / touchState.lastDistance;
+ const newZoom = Math.max(0.05, Math.min(3, zoom * distanceChange));
+
+ // Get canvas bounds for center calculation
+ const canvas = e.currentTarget as HTMLElement;
+ const rect = canvas.getBoundingClientRect();
+ const centerX = center.x - rect.left;
+ const centerY = center.y - rect.top;
+
+ // Calculate the world position of the pinch center
+ const worldX = (centerX - panX) / zoom;
+ const worldY = (centerY - panY) / zoom;
+
+ // Calculate new pan to keep the pinch center stationary
+ const newPanX = centerX - worldX * newZoom;
+ const newPanY = centerY - worldY * newZoom;
+
+ // Calculate pan change based on center movement
+ const centerDx = center.x - touchState.lastCenter.x;
+ const centerDy = center.y - touchState.lastCenter.y;
+
+ setZoom(newZoom);
+ setPanX(newPanX + centerDx);
+ setPanY(newPanY + centerDy);
+
+ setTouchState({
+ touches,
+ lastDistance: distance,
+ lastCenter: center,
+ isGesturing: true,
+ });
+ } else if (touches.length === 1 && !touchState.isGesturing && isPanning) {
+ // Single finger pan (only if not in gesture mode)
+ const touch = touches[0]!;
+ const newPanX = touch.x - panStart.x;
+ const newPanY = touch.y - panStart.y;
+ setPanX(newPanX);
+ setPanY(newPanY);
+ }
+ },
+ [touchState, zoom, panX, panY, isPanning, panStart],
+ );
+
+ const handleTouchEnd = useCallback((e: React.TouchEvent) => {
+ const touches = Array.from(e.touches).map((touch) => ({
+ id: touch.identifier,
+ x: touch.clientX,
+ y: touch.clientY,
+ }));
+
+ if (touches.length < 2) {
+ setTouchState((prev) => ({ ...prev, touches, isGesturing: false }));
+ } else {
+ setTouchState((prev) => ({ ...prev, touches }));
+ }
+
+ if (touches.length === 0) {
+ setIsPanning(false);
+ }
+ }, []);
+
+ // Center viewport on a specific world position (with animation)
+ const centerViewportOn = useCallback(
+ (
+ worldX: number,
+ worldY: number,
+ viewportWidth: number,
+ viewportHeight: number,
+ animate = true,
+ ) => {
+ const newPanX = viewportWidth / 2 - worldX * zoom;
+ const newPanY = viewportHeight / 2 - worldY * zoom;
+
+ if (animate && !isAnimating) {
+ animateToViewState(newPanX, newPanY, zoom, 400);
+ } else {
+ setPanX(newPanX);
+ setPanY(newPanY);
+ }
+ },
+ [zoom, isAnimating, animateToViewState],
+ );
+
+ // Node interaction handlers
+ const handleNodeHover = useCallback((nodeId: string | null) => {
+ setHoveredNode(nodeId);
+ }, []);
+
+ const handleNodeClick = useCallback(
+ (nodeId: string) => {
+ setSelectedNode(selectedNode === nodeId ? null : nodeId);
+ },
+ [selectedNode],
+ );
+
+ const handleDoubleClick = useCallback(
+ (e: React.MouseEvent) => {
+ // Calculate new zoom (zoom in by 1.5x)
+ const zoomFactor = 1.5;
+ const newZoom = Math.min(3, zoom * zoomFactor);
+
+ // Get mouse position relative to the container
+ let mouseX = e.clientX;
+ let mouseY = e.clientY;
+
+ // Try to get the container bounds to make coordinates relative to the graph container
+ const target = e.currentTarget;
+ if (target && "getBoundingClientRect" in target) {
+ const rect = target.getBoundingClientRect();
+ mouseX = e.clientX - rect.left;
+ mouseY = e.clientY - rect.top;
+ }
+
+ // Calculate the world position of the clicked point
+ const worldX = (mouseX - panX) / zoom;
+ const worldY = (mouseY - panY) / zoom;
+
+ // Calculate new pan to keep the clicked point in the same screen position
+ const newPanX = mouseX - worldX * newZoom;
+ const newPanY = mouseY - worldY * newZoom;
+
+ setZoom(newZoom);
+ setPanX(newPanX);
+ setPanY(newPanY);
+ },
+ [zoom, panX, panY],
+ );
+
+ return {
+ // State
+ panX,
+ panY,
+ zoom,
+ hoveredNode,
+ selectedNode,
+ draggingNodeId,
+ nodePositions,
+ // Handlers
+ handlePanStart,
+ handlePanMove,
+ handlePanEnd,
+ handleWheel,
+ handleNodeHover,
+ handleNodeClick,
+ handleNodeDragStart,
+ handleNodeDragMove,
+ handleNodeDragEnd,
+ handleDoubleClick,
+ // Touch handlers
+ handleTouchStart,
+ handleTouchMove,
+ handleTouchEnd,
+ // Controls
+ zoomIn,
+ zoomOut,
+ resetView,
+ autoFitToViewport,
+ centerViewportOn,
+ setSelectedNode,
+ };
+}
diff --git a/packages/memory-graph/src/hooks/use-mobile.ts b/packages/memory-graph/src/hooks/use-mobile.ts
new file mode 100644
index 00000000..283bbb4c
--- /dev/null
+++ b/packages/memory-graph/src/hooks/use-mobile.ts
@@ -0,0 +1,19 @@
+import * as React from "react"
+
+const MOBILE_BREAKPOINT = 768
+
+export function useIsMobile() {
+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
+
+ React.useEffect(() => {
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
+ const onChange = () => {
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
+ }
+ mql.addEventListener("change", onChange)
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
+ return () => mql.removeEventListener("change", onChange)
+ }, [])
+
+ return !!isMobile
+}
diff --git a/packages/memory-graph/src/index.tsx b/packages/memory-graph/src/index.tsx
new file mode 100644
index 00000000..1e413e00
--- /dev/null
+++ b/packages/memory-graph/src/index.tsx
@@ -0,0 +1,46 @@
+// Auto-inject global styles (side effect import)
+import "./styles";
+
+// Export the main component
+export { MemoryGraphWrapper as MemoryGraph } from "./components/memory-graph-wrapper";
+
+// Export types for consumers
+export type {
+ MemoryGraphWrapperProps as MemoryGraphProps,
+} from "./components/memory-graph-wrapper";
+
+export type {
+ DocumentWithMemories,
+ MemoryEntry,
+ DocumentsResponse,
+} from "./api-types";
+
+export type {
+ GraphNode,
+ GraphEdge,
+ MemoryRelation,
+} from "./types";
+
+// Export API client for advanced usage
+export {
+ fetchDocuments,
+ fetchDocumentsPage,
+ validateApiKey,
+ type FetchDocumentsOptions,
+ type ApiClientError,
+} from "./lib/api-client";
+
+// Export hooks for advanced usage (if users want to bring their own QueryClient)
+export {
+ useDocumentsQuery,
+ useInfiniteDocumentsQuery,
+ flattenDocuments,
+ getTotalDocuments,
+ getLoadedCount,
+ type UseDocumentsQueryOptions,
+} from "./hooks/use-documents-query";
+
+// Export theme system for custom theming
+export { themeContract, defaultTheme } from "./styles/theme.css";
+export { sprinkles } from "./styles/sprinkles.css";
+export type { Sprinkles } from "./styles/sprinkles.css";
diff --git a/packages/memory-graph/src/lib/api-client.ts b/packages/memory-graph/src/lib/api-client.ts
new file mode 100644
index 00000000..faef4d06
--- /dev/null
+++ b/packages/memory-graph/src/lib/api-client.ts
@@ -0,0 +1,213 @@
+import type { DocumentsResponse } from "@/api-types";
+
+export interface FetchDocumentsOptions {
+ apiKey: string;
+ baseUrl?: string;
+ page?: number;
+ limit?: number;
+ sort?: "createdAt" | "updatedAt";
+ order?: "asc" | "desc";
+ containerTags?: string[];
+ signal?: AbortSignal;
+}
+
+export interface ApiClientError extends Error {
+ status?: number;
+ statusText?: string;
+ response?: unknown;
+}
+
+/**
+ * Creates an API client error with additional context
+ */
+function createApiError(
+ message: string,
+ status?: number,
+ statusText?: string,
+ response?: unknown,
+): ApiClientError {
+ const error = new Error(message) as ApiClientError;
+ error.name = "ApiClientError";
+ error.status = status;
+ error.statusText = statusText;
+ error.response = response;
+ return error;
+}
+
+/**
+ * Fetches documents with their memory entries from the Supermemory API
+ *
+ * @param options - Configuration options for the API request
+ * @returns Promise resolving to the documents response
+ * @throws ApiClientError if the request fails
+ */
+export async function fetchDocuments(
+ options: FetchDocumentsOptions,
+): Promise<DocumentsResponse> {
+ const {
+ apiKey,
+ baseUrl = "https://api.supermemory.ai",
+ page = 1,
+ limit = 50,
+ sort = "createdAt",
+ order = "desc",
+ containerTags,
+ signal,
+ } = options;
+
+ // Validate required parameters
+ if (!apiKey) {
+ throw createApiError("API key is required");
+ }
+
+ // Construct the full URL
+ const url = `${baseUrl}/v3/documents/documents`;
+
+ // Build request body
+ const body: {
+ page: number;
+ limit: number;
+ sort: string;
+ order: string;
+ containerTags?: string[];
+ } = {
+ page,
+ limit,
+ sort,
+ order,
+ };
+
+ if (containerTags && containerTags.length > 0) {
+ body.containerTags = containerTags;
+ }
+
+ try {
+ const response = await fetch(url, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${apiKey}`,
+ },
+ body: JSON.stringify(body),
+ signal,
+ });
+
+ // Handle non-OK responses
+ if (!response.ok) {
+ let errorMessage = `Failed to fetch documents: ${response.status} ${response.statusText}`;
+ let errorResponse: unknown;
+
+ try {
+ errorResponse = await response.json();
+ if (
+ errorResponse &&
+ typeof errorResponse === "object" &&
+ "message" in errorResponse
+ ) {
+ errorMessage = `API Error: ${(errorResponse as { message: string }).message}`;
+ }
+ } catch {
+ // If response is not JSON, use default error message
+ }
+
+ throw createApiError(
+ errorMessage,
+ response.status,
+ response.statusText,
+ errorResponse,
+ );
+ }
+
+ // Parse and validate response
+ const data = await response.json();
+
+ // Basic validation of response structure
+ if (!data || typeof data !== "object") {
+ throw createApiError("Invalid response format: expected an object");
+ }
+
+ if (!("documents" in data) || !Array.isArray(data.documents)) {
+ throw createApiError(
+ "Invalid response format: missing documents array",
+ );
+ }
+
+ if (!("pagination" in data) || typeof data.pagination !== "object") {
+ throw createApiError(
+ "Invalid response format: missing pagination object",
+ );
+ }
+
+ return data as DocumentsResponse;
+ } catch (error) {
+ // Re-throw ApiClientError as-is
+ if ((error as ApiClientError).name === "ApiClientError") {
+ throw error;
+ }
+
+ // Handle network errors
+ if (error instanceof TypeError && error.message.includes("fetch")) {
+ throw createApiError(
+ `Network error: Unable to connect to ${baseUrl}. Please check your internet connection.`,
+ );
+ }
+
+ // Handle abort errors
+ if (error instanceof Error && error.name === "AbortError") {
+ throw createApiError("Request was aborted");
+ }
+
+ // Handle other errors
+ throw createApiError(
+ `Unexpected error: ${error instanceof Error ? error.message : "Unknown error"}`,
+ );
+ }
+}
+
+/**
+ * Fetches a single page of documents (convenience wrapper)
+ */
+export async function fetchDocumentsPage(
+ apiKey: string,
+ page: number,
+ baseUrl?: string,
+ signal?: AbortSignal,
+): Promise<DocumentsResponse> {
+ return fetchDocuments({
+ apiKey,
+ baseUrl,
+ page,
+ limit: 50,
+ signal,
+ });
+}
+
+/**
+ * Validates an API key by making a test request
+ *
+ * @param apiKey - The API key to validate
+ * @param baseUrl - Optional base URL for the API
+ * @returns Promise resolving to true if valid, false otherwise
+ */
+export async function validateApiKey(
+ apiKey: string,
+ baseUrl?: string,
+): Promise<boolean> {
+ try {
+ await fetchDocuments({
+ apiKey,
+ baseUrl,
+ page: 1,
+ limit: 1,
+ });
+ return true;
+ } catch (error) {
+ // Check if it's an authentication error
+ if ((error as ApiClientError).status === 401) {
+ return false;
+ }
+ // Other errors might indicate valid key but other issues
+ // We'll return true in those cases to not block the user
+ return true;
+ }
+}
diff --git a/packages/memory-graph/src/lib/similarity.ts b/packages/memory-graph/src/lib/similarity.ts
new file mode 100644
index 00000000..09d3a2cc
--- /dev/null
+++ b/packages/memory-graph/src/lib/similarity.ts
@@ -0,0 +1,115 @@
+// Utility functions for calculating semantic similarity between documents and memories
+
+/**
+ * Calculate cosine similarity between two normalized vectors (unit vectors)
+ * Since all embeddings in this system are normalized using normalizeEmbeddingFast,
+ * cosine similarity equals dot product for unit vectors.
+ */
+export const cosineSimilarity = (
+ vectorA: number[],
+ vectorB: number[],
+): number => {
+ if (vectorA.length !== vectorB.length) {
+ throw new Error("Vectors must have the same length")
+ }
+
+ let dotProduct = 0
+
+ for (let i = 0; i < vectorA.length; i++) {
+ const vectorAi = vectorA[i]
+ const vectorBi = vectorB[i]
+ if (
+ typeof vectorAi !== "number" ||
+ typeof vectorBi !== "number" ||
+ isNaN(vectorAi) ||
+ isNaN(vectorBi)
+ ) {
+ throw new Error("Vectors must contain only numbers")
+ }
+ dotProduct += vectorAi * vectorBi
+ }
+
+ return dotProduct
+}
+
+/**
+ * Calculate semantic similarity between two documents
+ * Returns a value between 0 and 1, where 1 is most similar
+ */
+export const calculateSemanticSimilarity = (
+ document1Embedding: number[] | null,
+ document2Embedding: number[] | null,
+): number => {
+ // If we have both embeddings, use cosine similarity
+ if (
+ document1Embedding &&
+ document2Embedding &&
+ document1Embedding.length > 0 &&
+ document2Embedding.length > 0
+ ) {
+ const similarity = cosineSimilarity(document1Embedding, document2Embedding)
+ // Convert from [-1, 1] to [0, 1] range
+ return similarity >= 0 ? similarity : 0
+ }
+
+ return 0
+}
+
+/**
+ * Calculate semantic similarity between a document and memory entry
+ * Returns a value between 0 and 1, where 1 is most similar
+ */
+export const calculateDocumentMemorySimilarity = (
+ documentEmbedding: number[] | null,
+ memoryEmbedding: number[] | null,
+ relevanceScore?: number | null,
+): number => {
+ // If we have both embeddings, use cosine similarity
+ if (
+ documentEmbedding &&
+ memoryEmbedding &&
+ documentEmbedding.length > 0 &&
+ memoryEmbedding.length > 0
+ ) {
+ const similarity = cosineSimilarity(documentEmbedding, memoryEmbedding)
+ // Convert from [-1, 1] to [0, 1] range
+ return similarity >= 0 ? similarity : 0
+ }
+
+ // Fall back to relevance score from database (0-100 scale)
+ if (relevanceScore !== null && relevanceScore !== undefined) {
+ return Math.max(0, Math.min(1, relevanceScore / 100))
+ }
+
+ // Default similarity for connections without embeddings or relevance scores
+ return 0.5
+}
+
+/**
+ * Get visual properties for connection based on similarity
+ */
+export const getConnectionVisualProps = (similarity: number) => {
+ // Ensure similarity is between 0 and 1
+ const normalizedSimilarity = Math.max(0, Math.min(1, similarity))
+
+ return {
+ opacity: Math.max(0, normalizedSimilarity), // 0 to 1 range
+ thickness: Math.max(1, normalizedSimilarity * 4), // 1 to 4 pixels
+ glow: normalizedSimilarity * 0.6, // Glow intensity
+ pulseDuration: 2000 + (1 - normalizedSimilarity) * 3000, // Faster pulse for higher similarity
+ }
+}
+
+/**
+ * Generate magical color based on similarity and connection type
+ */
+export const getMagicalConnectionColor = (
+ similarity: number,
+ hue = 220,
+): string => {
+ const normalizedSimilarity = Math.max(0, Math.min(1, similarity))
+ const saturation = 60 + normalizedSimilarity * 40 // 60% to 100%
+ const lightness = 40 + normalizedSimilarity * 30 // 40% to 70%
+
+ return `hsl(${hue}, ${saturation}%, ${lightness}%)`
+}
diff --git a/packages/memory-graph/src/styles/animations.css.ts b/packages/memory-graph/src/styles/animations.css.ts
new file mode 100644
index 00000000..d9430ec4
--- /dev/null
+++ b/packages/memory-graph/src/styles/animations.css.ts
@@ -0,0 +1,116 @@
+import { keyframes } from "@vanilla-extract/css";
+
+/**
+ * Animation keyframes
+ * Used throughout the component library for consistent motion
+ */
+
+export const fadeIn = keyframes({
+ from: { opacity: 0 },
+ to: { opacity: 1 },
+});
+
+export const fadeOut = keyframes({
+ from: { opacity: 1 },
+ to: { opacity: 0 },
+});
+
+export const slideInFromRight = keyframes({
+ from: {
+ transform: "translateX(100%)",
+ opacity: 0,
+ },
+ to: {
+ transform: "translateX(0)",
+ opacity: 1,
+ },
+});
+
+export const slideInFromLeft = keyframes({
+ from: {
+ transform: "translateX(-100%)",
+ opacity: 0,
+ },
+ to: {
+ transform: "translateX(0)",
+ opacity: 1,
+ },
+});
+
+export const slideInFromTop = keyframes({
+ from: {
+ transform: "translateY(-100%)",
+ opacity: 0,
+ },
+ to: {
+ transform: "translateY(0)",
+ opacity: 1,
+ },
+});
+
+export const slideInFromBottom = keyframes({
+ from: {
+ transform: "translateY(100%)",
+ opacity: 0,
+ },
+ to: {
+ transform: "translateY(0)",
+ opacity: 1,
+ },
+});
+
+export const spin = keyframes({
+ from: { transform: "rotate(0deg)" },
+ to: { transform: "rotate(360deg)" },
+});
+
+export const pulse = keyframes({
+ "0%, 100%": {
+ opacity: 1,
+ },
+ "50%": {
+ opacity: 0.5,
+ },
+});
+
+export const bounce = keyframes({
+ "0%, 100%": {
+ transform: "translateY(-25%)",
+ animationTimingFunction: "cubic-bezier(0.8, 0, 1, 1)",
+ },
+ "50%": {
+ transform: "translateY(0)",
+ animationTimingFunction: "cubic-bezier(0, 0, 0.2, 1)",
+ },
+});
+
+export const scaleIn = keyframes({
+ from: {
+ transform: "scale(0.95)",
+ opacity: 0,
+ },
+ to: {
+ transform: "scale(1)",
+ opacity: 1,
+ },
+});
+
+export const scaleOut = keyframes({
+ from: {
+ transform: "scale(1)",
+ opacity: 1,
+ },
+ to: {
+ transform: "scale(0.95)",
+ opacity: 0,
+ },
+});
+
+export const shimmer = keyframes({
+ "0%": {
+ backgroundPosition: "-1000px 0",
+ },
+ "100%": {
+ backgroundPosition: "1000px 0",
+ },
+});
diff --git a/packages/memory-graph/src/styles/effects.css.ts b/packages/memory-graph/src/styles/effects.css.ts
new file mode 100644
index 00000000..2a290d32
--- /dev/null
+++ b/packages/memory-graph/src/styles/effects.css.ts
@@ -0,0 +1,120 @@
+import { style, styleVariants } from "@vanilla-extract/css";
+import { themeContract } from "./theme.css";
+
+/**
+ * Base glass-morphism effect
+ * Provides the signature frosted glass look
+ */
+const glassBase = style({
+ backdropFilter: "blur(12px)",
+ WebkitBackdropFilter: "blur(12px)",
+ border: `1px solid ${themeContract.colors.document.border}`,
+ borderRadius: themeContract.radii.lg,
+});
+
+/**
+ * Glass effect variants
+ */
+export const glass = styleVariants({
+ /**
+ * Light glass effect - subtle background
+ */
+ light: [
+ glassBase,
+ {
+ background: "rgba(255, 255, 255, 0.05)",
+ },
+ ],
+
+ /**
+ * Medium glass effect - more visible
+ */
+ medium: [
+ glassBase,
+ {
+ background: "rgba(255, 255, 255, 0.08)",
+ },
+ ],
+
+ /**
+ * Dark glass effect - prominent
+ */
+ dark: [
+ glassBase,
+ {
+ background: "rgba(15, 20, 25, 0.8)",
+ backdropFilter: "blur(20px)",
+ WebkitBackdropFilter: "blur(20px)",
+ },
+ ],
+});
+
+/**
+ * Glass panel styles for larger containers
+ */
+export const glassPanel = styleVariants({
+ default: {
+ background: "rgba(15, 20, 25, 0.8)",
+ backdropFilter: "blur(20px)",
+ WebkitBackdropFilter: "blur(20px)",
+ border: `1px solid ${themeContract.colors.document.border}`,
+ borderRadius: themeContract.radii.xl,
+ },
+ bordered: {
+ background: "rgba(15, 20, 25, 0.8)",
+ backdropFilter: "blur(20px)",
+ WebkitBackdropFilter: "blur(20px)",
+ border: `2px solid ${themeContract.colors.document.border}`,
+ borderRadius: themeContract.radii.xl,
+ },
+});
+
+/**
+ * Focus ring styles for accessibility
+ */
+export const focusRing = style({
+ outline: "none",
+ selectors: {
+ "&:focus-visible": {
+ outline: `2px solid ${themeContract.colors.accent.primary}`,
+ outlineOffset: "2px",
+ },
+ },
+});
+
+/**
+ * Transition presets
+ */
+export const transition = styleVariants({
+ fast: {
+ transition: themeContract.transitions.fast,
+ },
+ normal: {
+ transition: themeContract.transitions.normal,
+ },
+ slow: {
+ transition: themeContract.transitions.slow,
+ },
+ all: {
+ transition: `all ${themeContract.transitions.normal}`,
+ },
+ colors: {
+ transition: `background-color ${themeContract.transitions.normal}, color ${themeContract.transitions.normal}, border-color ${themeContract.transitions.normal}`,
+ },
+ transform: {
+ transition: `transform ${themeContract.transitions.normal}`,
+ },
+});
+
+/**
+ * Hover glow effect
+ */
+export const hoverGlow = style({
+ position: "relative",
+ transition: themeContract.transitions.normal,
+ selectors: {
+ "&:hover": {
+ boxShadow: `0 0 20px ${themeContract.colors.document.glow}`,
+ },
+ },
+});
diff --git a/packages/memory-graph/src/styles/global.css.ts b/packages/memory-graph/src/styles/global.css.ts
new file mode 100644
index 00000000..cbe37913
--- /dev/null
+++ b/packages/memory-graph/src/styles/global.css.ts
@@ -0,0 +1,71 @@
+import { globalStyle } from "@vanilla-extract/css";
+
+/**
+ * Global CSS reset and base styles
+ */
+
+// Box sizing reset
+globalStyle("*, *::before, *::after", {
+ boxSizing: "border-box",
+});
+
+// Remove default margins
+globalStyle("body, h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd", {
+ margin: 0,
+});
+
+// Remove list styles
+globalStyle("ul[role='list'], ol[role='list']", {
+ listStyle: "none",
+});
+
+// Core body defaults
+globalStyle("html, body", {
+ height: "100%",
+});
+
+globalStyle("body", {
+ lineHeight: 1.5,
+ WebkitFontSmoothing: "antialiased",
+ MozOsxFontSmoothing: "grayscale",
+});
+
+// Typography defaults
+globalStyle("h1, h2, h3, h4, h5, h6", {
+ fontWeight: 500,
+ lineHeight: 1.25,
+});
+
+// Inherit fonts for inputs and buttons
+globalStyle("input, button, textarea, select", {
+ font: "inherit",
+});
+
+// Remove default button styles
+globalStyle("button", {
+ background: "none",
+ border: "none",
+ padding: 0,
+ cursor: "pointer",
+});
+
+// Improve media defaults
+globalStyle("img, picture, video, canvas, svg", {
+ display: "block",
+ maxWidth: "100%",
+});
+
+// Remove built-in form typography styles
+globalStyle("input, button, textarea, select", {
+ font: "inherit",
+});
+
+// Avoid text overflows
+globalStyle("p, h1, h2, h3, h4, h5, h6", {
+ overflowWrap: "break-word",
+});
+
+// Improve text rendering
+globalStyle("#root, #__next", {
+ isolation: "isolate",
+});
diff --git a/packages/memory-graph/src/styles/index.ts b/packages/memory-graph/src/styles/index.ts
new file mode 100644
index 00000000..f619c689
--- /dev/null
+++ b/packages/memory-graph/src/styles/index.ts
@@ -0,0 +1,20 @@
+/**
+ * Style system exports
+ * Provides theme, sprinkles, animations, and effects for the memory-graph package
+ */
+
+// Import global styles (side effect)
+import "./global.css";
+
+// Theme
+export { themeContract, defaultTheme } from "./theme.css";
+
+// Sprinkles utilities
+export { sprinkles } from "./sprinkles.css";
+export type { Sprinkles } from "./sprinkles.css";
+
+// Animations
+export * as animations from "./animations.css";
+
+// Glass-morphism effects
+export { glass, glassPanel, focusRing, transition, hoverGlow } from "./effects.css";
diff --git a/packages/memory-graph/src/styles/sprinkles.css.ts b/packages/memory-graph/src/styles/sprinkles.css.ts
new file mode 100644
index 00000000..ecd7a024
--- /dev/null
+++ b/packages/memory-graph/src/styles/sprinkles.css.ts
@@ -0,0 +1,204 @@
+import { defineProperties, createSprinkles } from "@vanilla-extract/sprinkles";
+import { themeContract } from "./theme.css";
+
+/**
+ * Responsive conditions for mobile-first design
+ */
+const responsiveProperties = defineProperties({
+ conditions: {
+ mobile: {},
+ tablet: { "@media": "screen and (min-width: 768px)" },
+ desktop: { "@media": "screen and (min-width: 1024px)" },
+ },
+ defaultCondition: "mobile",
+ properties: {
+ // Display
+ display: ["none", "flex", "block", "inline", "inline-flex", "grid"],
+
+ // Flexbox
+ flexDirection: ["row", "column", "row-reverse", "column-reverse"],
+ justifyContent: [
+ "stretch",
+ "flex-start",
+ "center",
+ "flex-end",
+ "space-between",
+ "space-around",
+ "space-evenly",
+ ],
+ alignItems: ["stretch", "flex-start", "center", "flex-end", "baseline"],
+ flexWrap: ["nowrap", "wrap", "wrap-reverse"],
+ gap: themeContract.space,
+
+ // Spacing
+ padding: themeContract.space,
+ paddingTop: themeContract.space,
+ paddingBottom: themeContract.space,
+ paddingLeft: themeContract.space,
+ paddingRight: themeContract.space,
+ margin: themeContract.space,
+ marginTop: themeContract.space,
+ marginBottom: themeContract.space,
+ marginLeft: themeContract.space,
+ marginRight: themeContract.space,
+
+ // Sizing
+ width: {
+ auto: "auto",
+ full: "100%",
+ screen: "100vw",
+ min: "min-content",
+ max: "max-content",
+ fit: "fit-content",
+ },
+ height: {
+ auto: "auto",
+ full: "100%",
+ screen: "100vh",
+ min: "min-content",
+ max: "max-content",
+ fit: "fit-content",
+ },
+ minWidth: {
+ 0: "0",
+ full: "100%",
+ min: "min-content",
+ max: "max-content",
+ fit: "fit-content",
+ },
+ minHeight: {
+ 0: "0",
+ full: "100%",
+ screen: "100vh",
+ },
+ maxWidth: {
+ none: "none",
+ full: "100%",
+ min: "min-content",
+ max: "max-content",
+ fit: "fit-content",
+ },
+ maxHeight: {
+ none: "none",
+ full: "100%",
+ screen: "100vh",
+ },
+
+ // Position
+ position: ["static", "relative", "absolute", "fixed", "sticky"],
+ top: themeContract.space,
+ bottom: themeContract.space,
+ left: themeContract.space,
+ right: themeContract.space,
+ inset: themeContract.space,
+
+ // Border radius
+ borderRadius: themeContract.radii,
+ borderTopLeftRadius: themeContract.radii,
+ borderTopRightRadius: themeContract.radii,
+ borderBottomLeftRadius: themeContract.radii,
+ borderBottomRightRadius: themeContract.radii,
+
+ // Text
+ fontSize: themeContract.typography.fontSize,
+ fontWeight: themeContract.typography.fontWeight,
+ lineHeight: themeContract.typography.lineHeight,
+ textAlign: ["left", "center", "right", "justify"],
+
+ // Overflow
+ overflow: ["visible", "hidden", "scroll", "auto"],
+ overflowX: ["visible", "hidden", "scroll", "auto"],
+ overflowY: ["visible", "hidden", "scroll", "auto"],
+
+ // Z-index
+ zIndex: themeContract.zIndex,
+
+ // Cursor
+ cursor: ["auto", "pointer", "not-allowed", "grab", "grabbing"],
+
+ // Pointer events
+ pointerEvents: ["auto", "none"],
+
+ // User select
+ userSelect: ["auto", "none", "text", "all"],
+ },
+});
+
+/**
+ * Color properties (non-responsive)
+ */
+const colorProperties = defineProperties({
+ properties: {
+ color: {
+ primary: themeContract.colors.text.primary,
+ secondary: themeContract.colors.text.secondary,
+ muted: themeContract.colors.text.muted,
+ },
+ backgroundColor: {
+ transparent: "transparent",
+ primary: themeContract.colors.background.primary,
+ secondary: themeContract.colors.background.secondary,
+ accent: themeContract.colors.background.accent,
+ documentPrimary: themeContract.colors.document.primary,
+ documentSecondary: themeContract.colors.document.secondary,
+ documentAccent: themeContract.colors.document.accent,
+ memoryPrimary: themeContract.colors.memory.primary,
+ memorySecondary: themeContract.colors.memory.secondary,
+ memoryAccent: themeContract.colors.memory.accent,
+ },
+ borderColor: {
+ transparent: "transparent",
+ documentBorder: themeContract.colors.document.border,
+ memoryBorder: themeContract.colors.memory.border,
+ },
+ },
+});
+
+/**
+ * Border properties
+ */
+const borderProperties = defineProperties({
+ properties: {
+ borderWidth: {
+ 0: "0",
+ 1: "1px",
+ 2: "2px",
+ 4: "4px",
+ },
+ borderStyle: ["none", "solid", "dashed", "dotted"],
+ },
+});
+
+/**
+ * Opacity properties
+ */
+const opacityProperties = defineProperties({
+ properties: {
+ opacity: {
+ 0: "0",
+ 10: "0.1",
+ 20: "0.2",
+ 30: "0.3",
+ 40: "0.4",
+ 50: "0.5",
+ 60: "0.6",
+ 70: "0.7",
+ 80: "0.8",
+ 90: "0.9",
+ 100: "1",
+ },
+ },
+});
+
+/**
+ * Combined sprinkles system
+ * Provides Tailwind-like utility classes with full type safety
+ */
+export const sprinkles = createSprinkles(
+ responsiveProperties,
+ colorProperties,
+ borderProperties,
+ opacityProperties,
+);
+
+export type Sprinkles = Parameters<typeof sprinkles>[0];
diff --git a/packages/memory-graph/src/styles/theme.css.ts b/packages/memory-graph/src/styles/theme.css.ts
new file mode 100644
index 00000000..bf08e3eb
--- /dev/null
+++ b/packages/memory-graph/src/styles/theme.css.ts
@@ -0,0 +1,245 @@
+import { createTheme, createThemeContract } from "@vanilla-extract/css";
+
+/**
+ * Theme contract defines the structure of the design system.
+ * Consumers can provide custom themes that match this contract.
+ */
+export const themeContract = createThemeContract({
+ colors: {
+ // Background colors
+ background: {
+ primary: null,
+ secondary: null,
+ accent: null,
+ },
+ // Document node colors
+ document: {
+ primary: null,
+ secondary: null,
+ accent: null,
+ border: null,
+ glow: null,
+ },
+ // Memory node colors
+ memory: {
+ primary: null,
+ secondary: null,
+ accent: null,
+ border: null,
+ glow: null,
+ },
+ // Connection strengths
+ connection: {
+ weak: null,
+ memory: null,
+ medium: null,
+ strong: null,
+ },
+ // Text colors
+ text: {
+ primary: null,
+ secondary: null,
+ muted: null,
+ },
+ // Accent colors
+ accent: {
+ primary: null,
+ secondary: null,
+ glow: null,
+ amber: null,
+ emerald: null,
+ },
+ // Status indicators
+ status: {
+ forgotten: null,
+ expiring: null,
+ new: null,
+ },
+ // Relation types
+ relations: {
+ updates: null,
+ extends: null,
+ derives: null,
+ },
+ },
+ space: {
+ 0: null,
+ 1: null,
+ 2: null,
+ 3: null,
+ 4: null,
+ 5: null,
+ 6: null,
+ 8: null,
+ 10: null,
+ 12: null,
+ 16: null,
+ 20: null,
+ 24: null,
+ 32: null,
+ 40: null,
+ 48: null,
+ 64: null,
+ },
+ radii: {
+ none: null,
+ sm: null,
+ md: null,
+ lg: null,
+ xl: null,
+ "2xl": null,
+ full: null,
+ },
+ typography: {
+ fontSize: {
+ xs: null,
+ sm: null,
+ base: null,
+ lg: null,
+ xl: null,
+ "2xl": null,
+ "3xl": null,
+ },
+ fontWeight: {
+ normal: null,
+ medium: null,
+ semibold: null,
+ bold: null,
+ },
+ lineHeight: {
+ tight: null,
+ normal: null,
+ relaxed: null,
+ },
+ },
+ transitions: {
+ fast: null,
+ normal: null,
+ slow: null,
+ },
+ zIndex: {
+ base: null,
+ dropdown: null,
+ overlay: null,
+ modal: null,
+ tooltip: null,
+ },
+});
+
+/**
+ * Default theme implementation based on the original constants.ts colors
+ * This provides the glass-morphism dark theme used throughout the app.
+ */
+export const defaultTheme = createTheme(themeContract, {
+ colors: {
+ background: {
+ primary: "#0f1419", // Deep dark blue-gray
+ secondary: "#1a1f29", // Slightly lighter
+ accent: "#252a35", // Card backgrounds
+ },
+ document: {
+ primary: "rgba(255, 255, 255, 0.06)", // Subtle glass white
+ secondary: "rgba(255, 255, 255, 0.12)", // More visible
+ accent: "rgba(255, 255, 255, 0.18)", // Hover state
+ border: "rgba(255, 255, 255, 0.25)", // Sharp borders
+ glow: "rgba(147, 197, 253, 0.4)", // Blue glow for interaction
+ },
+ memory: {
+ primary: "rgba(147, 197, 253, 0.08)", // Subtle glass blue
+ secondary: "rgba(147, 197, 253, 0.16)", // More visible
+ accent: "rgba(147, 197, 253, 0.24)", // Hover state
+ border: "rgba(147, 197, 253, 0.35)", // Sharp borders
+ glow: "rgba(147, 197, 253, 0.5)", // Blue glow for interaction
+ },
+ connection: {
+ weak: "rgba(148, 163, 184, 0)", // Very subtle
+ memory: "rgba(148, 163, 184, 0.3)", // Very subtle
+ medium: "rgba(148, 163, 184, 0.125)", // Medium visibility
+ strong: "rgba(148, 163, 184, 0.4)", // Strong connection
+ },
+ text: {
+ primary: "#ffffff", // Pure white
+ secondary: "#e2e8f0", // Light gray
+ muted: "#94a3b8", // Medium gray
+ },
+ accent: {
+ primary: "rgba(59, 130, 246, 0.7)", // Clean blue
+ secondary: "rgba(99, 102, 241, 0.6)", // Clean purple
+ glow: "rgba(147, 197, 253, 0.6)", // Subtle glow
+ amber: "rgba(251, 165, 36, 0.8)", // Amber for expiring
+ emerald: "rgba(16, 185, 129, 0.4)", // Emerald for new
+ },
+ status: {
+ forgotten: "rgba(220, 38, 38, 0.15)", // Red for forgotten
+ expiring: "rgba(251, 165, 36, 0.8)", // Amber for expiring soon
+ new: "rgba(16, 185, 129, 0.4)", // Emerald for new memories
+ },
+ relations: {
+ updates: "rgba(147, 77, 253, 0.5)", // purple
+ extends: "rgba(16, 185, 129, 0.5)", // green
+ derives: "rgba(147, 197, 253, 0.5)", // blue
+ },
+ },
+ space: {
+ 0: "0",
+ 1: "0.25rem", // 4px
+ 2: "0.5rem", // 8px
+ 3: "0.75rem", // 12px
+ 4: "1rem", // 16px
+ 5: "1.25rem", // 20px
+ 6: "1.5rem", // 24px
+ 8: "2rem", // 32px
+ 10: "2.5rem", // 40px
+ 12: "3rem", // 48px
+ 16: "4rem", // 64px
+ 20: "5rem", // 80px
+ 24: "6rem", // 96px
+ 32: "8rem", // 128px
+ 40: "10rem", // 160px
+ 48: "12rem", // 192px
+ 64: "16rem", // 256px
+ },
+ radii: {
+ none: "0",
+ sm: "0.125rem", // 2px
+ md: "0.375rem", // 6px
+ lg: "0.5rem", // 8px
+ xl: "0.75rem", // 12px
+ "2xl": "1rem", // 16px
+ full: "9999px",
+ },
+ typography: {
+ fontSize: {
+ xs: "0.75rem", // 12px
+ sm: "0.875rem", // 14px
+ base: "1rem", // 16px
+ lg: "1.125rem", // 18px
+ xl: "1.25rem", // 20px
+ "2xl": "1.5rem", // 24px
+ "3xl": "1.875rem", // 30px
+ },
+ fontWeight: {
+ normal: "400",
+ medium: "500",
+ semibold: "600",
+ bold: "700",
+ },
+ lineHeight: {
+ tight: "1.25",
+ normal: "1.5",
+ relaxed: "1.75",
+ },
+ },
+ transitions: {
+ fast: "150ms ease-in-out",
+ normal: "200ms ease-in-out",
+ slow: "300ms ease-in-out",
+ },
+ zIndex: {
+ base: "0",
+ dropdown: "10",
+ overlay: "20",
+ modal: "30",
+ tooltip: "40",
+ },
+});
diff --git a/packages/memory-graph/src/types.ts b/packages/memory-graph/src/types.ts
new file mode 100644
index 00000000..796c8d01
--- /dev/null
+++ b/packages/memory-graph/src/types.ts
@@ -0,0 +1,120 @@
+import type { DocumentsResponse, DocumentWithMemories, MemoryEntry } from "./api-types";
+
+// Re-export for convenience
+export type { DocumentsResponse, DocumentWithMemories, MemoryEntry };
+
+export interface GraphNode {
+ id: string;
+ type: "document" | "memory";
+ x: number;
+ y: number;
+ data: DocumentWithMemories | MemoryEntry;
+ size: number;
+ color: string;
+ isHovered: boolean;
+ isDragging: boolean;
+}
+
+export type MemoryRelation = "updates" | "extends" | "derives";
+
+export interface GraphEdge {
+ id: string;
+ source: string;
+ target: string;
+ similarity: number;
+ visualProps: {
+ opacity: number;
+ thickness: number;
+ glow: number;
+ pulseDuration: number;
+ };
+ color: string;
+ edgeType: "doc-memory" | "doc-doc" | "version";
+ relationType?: MemoryRelation;
+}
+
+export interface SpacesDropdownProps {
+ selectedSpace: string;
+ availableSpaces: string[];
+ spaceMemoryCounts: Record<string, number>;
+ onSpaceChange: (space: string) => void;
+}
+
+export interface NodeDetailPanelProps {
+ node: GraphNode | null;
+ onClose: () => void;
+ variant?: "console" | "consumer";
+}
+
+export interface GraphCanvasProps {
+ nodes: GraphNode[];
+ edges: GraphEdge[];
+ panX: number;
+ panY: number;
+ zoom: number;
+ width: number;
+ height: number;
+ onNodeHover: (nodeId: string | null) => void;
+ onNodeClick: (nodeId: string) => void;
+ onNodeDragStart: (nodeId: string, e: React.MouseEvent) => void;
+ onNodeDragMove: (e: React.MouseEvent) => void;
+ onNodeDragEnd: () => void;
+ onPanStart: (e: React.MouseEvent) => void;
+ onPanMove: (e: React.MouseEvent) => void;
+ onPanEnd: () => void;
+ onWheel: (e: React.WheelEvent) => void;
+ onDoubleClick: (e: React.MouseEvent) => void;
+ onTouchStart?: (e: React.TouchEvent) => void;
+ onTouchMove?: (e: React.TouchEvent) => void;
+ onTouchEnd?: (e: React.TouchEvent) => void;
+ draggingNodeId: string | null;
+ // Optional list of document IDs (customId or internal id) to highlight
+ highlightDocumentIds?: string[];
+}
+
+export interface MemoryGraphProps {
+ children?: React.ReactNode;
+ documents: DocumentWithMemories[];
+ isLoading: boolean;
+ isLoadingMore: boolean;
+ error: Error | null;
+ totalLoaded: number;
+ hasMore: boolean;
+ loadMoreDocuments: () => Promise<void>;
+ // App-specific props
+ showSpacesSelector?: boolean; // true for console, false for consumer
+ variant?: "console" | "consumer"; // for different positioning and styling
+ legendId?: string; // Optional ID for the legend component
+ // Optional document highlight list (document custom IDs)
+ highlightDocumentIds?: string[];
+ // Whether highlights are currently visible (e.g., chat open)
+ highlightsVisible?: boolean;
+ // Pixels occluded on the right side of the viewport (e.g., chat panel)
+ occludedRightPx?: number;
+ // Whether to auto-load more documents based on viewport visibility
+ autoLoadOnViewport?: boolean;
+ // Theme class name to apply
+ themeClassName?: string;
+}
+
+export interface LegendProps {
+ variant?: "console" | "consumer";
+ nodes?: GraphNode[];
+ edges?: GraphEdge[];
+ isLoading?: boolean;
+ hoveredNode?: string | null;
+}
+
+export interface LoadingIndicatorProps {
+ isLoading: boolean;
+ isLoadingMore: boolean;
+ totalLoaded: number;
+ variant?: "console" | "consumer";
+}
+
+export interface ControlsProps {
+ onZoomIn: () => void;
+ onZoomOut: () => void;
+ onResetView: () => void;
+ variant?: "console" | "consumer";
+}
diff --git a/packages/memory-graph/src/ui/badge.css.ts b/packages/memory-graph/src/ui/badge.css.ts
new file mode 100644
index 00000000..1af96c1d
--- /dev/null
+++ b/packages/memory-graph/src/ui/badge.css.ts
@@ -0,0 +1,119 @@
+import { recipe, type RecipeVariants } from "@vanilla-extract/recipes";
+import { style, globalStyle } from "@vanilla-extract/css";
+import { themeContract } from "../styles/theme.css";
+
+/**
+ * Base styles for SVG icons inside badges
+ */
+export const badgeIcon = style({
+ width: "0.75rem",
+ height: "0.75rem",
+ pointerEvents: "none",
+});
+
+/**
+ * Badge recipe with variants
+ * Replaces CVA-based badge variants with vanilla-extract recipes
+ */
+const badgeBase = style({
+ display: "inline-flex",
+ alignItems: "center",
+ justifyContent: "center",
+ borderRadius: themeContract.radii.md,
+ border: "1px solid",
+ paddingLeft: themeContract.space[2],
+ paddingRight: themeContract.space[2],
+ paddingTop: "0.125rem",
+ paddingBottom: "0.125rem",
+ fontSize: themeContract.typography.fontSize.xs,
+ fontWeight: themeContract.typography.fontWeight.medium,
+ width: "fit-content",
+ whiteSpace: "nowrap",
+ flexShrink: 0,
+ gap: themeContract.space[1],
+ transition: "color 200ms ease-in-out, box-shadow 200ms ease-in-out",
+ overflow: "hidden",
+
+ selectors: {
+ "&:focus-visible": {
+ borderColor: themeContract.colors.accent.primary,
+ boxShadow: `0 0 0 2px ${themeContract.colors.accent.primary}33`,
+ },
+ "&[aria-invalid='true']": {
+ boxShadow: `0 0 0 2px ${themeContract.colors.status.forgotten}33`,
+ borderColor: themeContract.colors.status.forgotten,
+ },
+ },
+});
+
+// Global style for SVG children
+globalStyle(`${badgeBase} > svg`, {
+ width: "0.75rem",
+ height: "0.75rem",
+ pointerEvents: "none",
+});
+
+export const badge = recipe({
+ base: badgeBase,
+
+ variants: {
+ variant: {
+ default: {
+ borderColor: "transparent",
+ backgroundColor: themeContract.colors.accent.primary,
+ color: themeContract.colors.text.primary,
+
+ selectors: {
+ "a&:hover": {
+ opacity: 0.9,
+ },
+ },
+ },
+
+ secondary: {
+ borderColor: "transparent",
+ backgroundColor: themeContract.colors.background.secondary,
+ color: themeContract.colors.text.secondary,
+
+ selectors: {
+ "a&:hover": {
+ backgroundColor: themeContract.colors.background.accent,
+ },
+ },
+ },
+
+ destructive: {
+ borderColor: "transparent",
+ backgroundColor: themeContract.colors.status.forgotten,
+ color: themeContract.colors.text.primary,
+
+ selectors: {
+ "a&:hover": {
+ opacity: 0.9,
+ },
+ "&:focus-visible": {
+ boxShadow: `0 0 0 2px ${themeContract.colors.status.forgotten}33`,
+ },
+ },
+ },
+
+ outline: {
+ borderColor: themeContract.colors.document.border,
+ backgroundColor: "transparent",
+ color: themeContract.colors.text.primary,
+
+ selectors: {
+ "a&:hover": {
+ backgroundColor: themeContract.colors.document.primary,
+ },
+ },
+ },
+ },
+ },
+
+ defaultVariants: {
+ variant: "default",
+ },
+});
+
+export type BadgeVariants = RecipeVariants<typeof badge>;
diff --git a/packages/memory-graph/src/ui/badge.tsx b/packages/memory-graph/src/ui/badge.tsx
new file mode 100644
index 00000000..0708888f
--- /dev/null
+++ b/packages/memory-graph/src/ui/badge.tsx
@@ -0,0 +1,27 @@
+import { Slot } from "@radix-ui/react-slot";
+import type * as React from "react";
+import { badge, type BadgeVariants } from "./badge.css";
+
+function Badge({
+ className,
+ variant,
+ asChild = false,
+ ...props
+}: React.ComponentProps<"span"> &
+ BadgeVariants & { asChild?: boolean }) {
+ const Comp = asChild ? Slot : "span";
+
+ const combinedClassName = className
+ ? `${badge({ variant })} ${className}`
+ : badge({ variant });
+
+ return (
+ <Comp
+ className={combinedClassName}
+ data-slot="badge"
+ {...props}
+ />
+ );
+}
+
+export { Badge, badge as badgeVariants };
diff --git a/packages/memory-graph/src/ui/button.css.ts b/packages/memory-graph/src/ui/button.css.ts
new file mode 100644
index 00000000..ad9cce8c
--- /dev/null
+++ b/packages/memory-graph/src/ui/button.css.ts
@@ -0,0 +1,210 @@
+import { recipe, type RecipeVariants } from "@vanilla-extract/recipes";
+import { style } from "@vanilla-extract/css";
+import { themeContract } from "../styles/theme.css";
+
+/**
+ * Base styles for SVG icons inside buttons
+ */
+export const buttonIcon = style({
+ pointerEvents: "none",
+ flexShrink: 0,
+ selectors: {
+ "&:not([class*='size-'])": {
+ width: "1rem",
+ height: "1rem",
+ },
+ },
+});
+
+/**
+ * Button recipe with variants
+ * Replaces CVA-based button variants with vanilla-extract recipes
+ */
+export const button = recipe({
+ base: {
+ display: "inline-flex",
+ alignItems: "center",
+ justifyContent: "center",
+ gap: themeContract.space[2],
+ whiteSpace: "nowrap",
+ borderRadius: themeContract.radii.md,
+ fontSize: themeContract.typography.fontSize.sm,
+ fontWeight: themeContract.typography.fontWeight.medium,
+ transition: themeContract.transitions.normal,
+ flexShrink: 0,
+ outline: "none",
+ border: "1px solid transparent",
+ cursor: "pointer",
+
+ // SVG sizing
+ selectors: {
+ [`&:has(${buttonIcon})`]: {
+ // Buttons with icons get adjusted padding
+ },
+ "&:disabled": {
+ pointerEvents: "none",
+ opacity: 0.5,
+ },
+ "&:focus-visible": {
+ borderColor: themeContract.colors.accent.primary,
+ boxShadow: `0 0 0 2px ${themeContract.colors.accent.primary}33`,
+ },
+ "&[aria-invalid='true']": {
+ boxShadow: `0 0 0 2px ${themeContract.colors.status.forgotten}`,
+ borderColor: themeContract.colors.status.forgotten,
+ },
+ },
+ },
+
+ variants: {
+ variant: {
+ default: {
+ backgroundColor: themeContract.colors.accent.primary,
+ color: themeContract.colors.text.primary,
+ boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)",
+
+ selectors: {
+ "&:hover:not(:disabled)": {
+ backgroundColor: themeContract.colors.accent.secondary,
+ },
+ },
+ },
+
+ destructive: {
+ backgroundColor: themeContract.colors.status.forgotten,
+ color: themeContract.colors.text.primary,
+ boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)",
+
+ selectors: {
+ "&:hover:not(:disabled)": {
+ opacity: 0.9,
+ },
+ "&:focus-visible": {
+ boxShadow: `0 0 0 2px ${themeContract.colors.status.forgotten}33`,
+ },
+ },
+ },
+
+ outline: {
+ backgroundColor: themeContract.colors.background.primary,
+ borderColor: themeContract.colors.document.border,
+ color: themeContract.colors.text.primary,
+ boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)",
+
+ selectors: {
+ "&:hover:not(:disabled)": {
+ backgroundColor: themeContract.colors.document.primary,
+ },
+ },
+ },
+
+ secondary: {
+ backgroundColor: themeContract.colors.background.secondary,
+ color: themeContract.colors.text.secondary,
+ boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)",
+
+ selectors: {
+ "&:hover:not(:disabled)": {
+ backgroundColor: themeContract.colors.background.accent,
+ },
+ },
+ },
+
+ ghost: {
+ backgroundColor: "transparent",
+ color: themeContract.colors.text.primary,
+
+ selectors: {
+ "&:hover:not(:disabled)": {
+ backgroundColor: themeContract.colors.document.primary,
+ },
+ },
+ },
+
+ link: {
+ backgroundColor: "transparent",
+ color: themeContract.colors.accent.primary,
+ textDecoration: "underline",
+ textUnderlineOffset: "4px",
+
+ selectors: {
+ "&:hover:not(:disabled)": {
+ textDecoration: "underline",
+ },
+ },
+ },
+
+ settingsNav: {
+ cursor: "pointer",
+ borderRadius: themeContract.radii.sm,
+ backgroundColor: "transparent",
+ color: themeContract.colors.text.primary,
+ },
+ },
+
+ size: {
+ default: {
+ height: "36px",
+ paddingLeft: themeContract.space[4],
+ paddingRight: themeContract.space[4],
+ paddingTop: themeContract.space[2],
+ paddingBottom: themeContract.space[2],
+
+ selectors: {
+ "&:has(svg)": {
+ paddingLeft: themeContract.space[3],
+ paddingRight: themeContract.space[3],
+ },
+ },
+ },
+
+ sm: {
+ height: "32px",
+ borderRadius: themeContract.radii.md,
+ gap: themeContract.space[1],
+ paddingLeft: themeContract.space[3],
+ paddingRight: themeContract.space[3],
+
+ selectors: {
+ "&:has(svg)": {
+ paddingLeft: themeContract.space[2],
+ paddingRight: themeContract.space[2],
+ },
+ },
+ },
+
+ lg: {
+ height: "40px",
+ borderRadius: themeContract.radii.md,
+ paddingLeft: themeContract.space[6],
+ paddingRight: themeContract.space[6],
+
+ selectors: {
+ "&:has(svg)": {
+ paddingLeft: themeContract.space[4],
+ paddingRight: themeContract.space[4],
+ },
+ },
+ },
+
+ icon: {
+ width: "36px",
+ height: "36px",
+ padding: 0,
+ },
+
+ settingsNav: {
+ height: "32px",
+ gap: 0,
+ padding: 0,
+ },
+ },
+ },
+
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+});
+
+export type ButtonVariants = RecipeVariants<typeof button>;
diff --git a/packages/memory-graph/src/ui/button.tsx b/packages/memory-graph/src/ui/button.tsx
new file mode 100644
index 00000000..031f2cc8
--- /dev/null
+++ b/packages/memory-graph/src/ui/button.tsx
@@ -0,0 +1,30 @@
+import { Slot } from "@radix-ui/react-slot";
+import type * as React from "react";
+import { button, type ButtonVariants } from "./button.css";
+
+function Button({
+ className,
+ variant,
+ size,
+ asChild = false,
+ ...props
+}: React.ComponentProps<"button"> &
+ ButtonVariants & {
+ asChild?: boolean;
+ }) {
+ const Comp = asChild ? Slot : "button";
+
+ const combinedClassName = className
+ ? `${button({ variant, size })} ${className}`
+ : button({ variant, size });
+
+ return (
+ <Comp
+ className={combinedClassName}
+ data-slot="button"
+ {...props}
+ />
+ );
+}
+
+export { Button, button as buttonVariants };
diff --git a/packages/memory-graph/src/ui/collapsible.tsx b/packages/memory-graph/src/ui/collapsible.tsx
new file mode 100644
index 00000000..0551ffdd
--- /dev/null
+++ b/packages/memory-graph/src/ui/collapsible.tsx
@@ -0,0 +1,33 @@
+"use client";
+
+import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
+
+function Collapsible({
+ ...props
+}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
+ return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />;
+}
+
+function CollapsibleTrigger({
+ ...props
+}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
+ return (
+ <CollapsiblePrimitive.CollapsibleTrigger
+ data-slot="collapsible-trigger"
+ {...props}
+ />
+ );
+}
+
+function CollapsibleContent({
+ ...props
+}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
+ return (
+ <CollapsiblePrimitive.CollapsibleContent
+ data-slot="collapsible-content"
+ {...props}
+ />
+ );
+}
+
+export { Collapsible, CollapsibleTrigger, CollapsibleContent };
diff --git a/packages/memory-graph/src/ui/glass-effect.css.ts b/packages/memory-graph/src/ui/glass-effect.css.ts
new file mode 100644
index 00000000..16e0fcdc
--- /dev/null
+++ b/packages/memory-graph/src/ui/glass-effect.css.ts
@@ -0,0 +1,58 @@
+import { style } from "@vanilla-extract/css";
+import { recipe } from "@vanilla-extract/recipes";
+import { themeContract } from "../styles/theme.css";
+
+/**
+ * Glass menu effect container
+ */
+export const glassMenuContainer = style({
+ position: "absolute",
+ inset: 0,
+});
+
+/**
+ * Glass menu effect with customizable border radius
+ */
+export const glassMenuEffect = recipe({
+ base: {
+ position: "absolute",
+ inset: 0,
+ backdropFilter: "blur(12px)",
+ WebkitBackdropFilter: "blur(12px)",
+ background: "rgba(255, 255, 255, 0.05)",
+ border: `1px solid ${themeContract.colors.document.border}`,
+ },
+
+ variants: {
+ rounded: {
+ none: {
+ borderRadius: themeContract.radii.none,
+ },
+ sm: {
+ borderRadius: themeContract.radii.sm,
+ },
+ md: {
+ borderRadius: themeContract.radii.md,
+ },
+ lg: {
+ borderRadius: themeContract.radii.lg,
+ },
+ xl: {
+ borderRadius: themeContract.radii.xl,
+ },
+ "2xl": {
+ borderRadius: themeContract.radii["2xl"],
+ },
+ "3xl": {
+ borderRadius: "1.5rem", // Tailwind's rounded-3xl
+ },
+ full: {
+ borderRadius: themeContract.radii.full,
+ },
+ },
+ },
+
+ defaultVariants: {
+ rounded: "3xl",
+ },
+});
diff --git a/packages/memory-graph/src/ui/glass-effect.tsx b/packages/memory-graph/src/ui/glass-effect.tsx
new file mode 100644
index 00000000..e1908f52
--- /dev/null
+++ b/packages/memory-graph/src/ui/glass-effect.tsx
@@ -0,0 +1,21 @@
+import {
+ glassMenuContainer,
+ glassMenuEffect,
+} from "./glass-effect.css";
+
+interface GlassMenuEffectProps {
+ rounded?: "none" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "full";
+ className?: string;
+}
+
+export function GlassMenuEffect({
+ rounded = "3xl",
+ className = "",
+}: GlassMenuEffectProps) {
+ return (
+ <div className={`${glassMenuContainer} ${className}`}>
+ {/* Frosted glass effect with translucent border */}
+ <div className={glassMenuEffect({ rounded })} />
+ </div>
+ );
+}
diff --git a/packages/memory-graph/src/ui/heading.css.ts b/packages/memory-graph/src/ui/heading.css.ts
new file mode 100644
index 00000000..128d97a6
--- /dev/null
+++ b/packages/memory-graph/src/ui/heading.css.ts
@@ -0,0 +1,24 @@
+import { style } from "@vanilla-extract/css";
+import { themeContract } from "../styles/theme.css";
+
+/**
+ * Responsive heading style with bold weight
+ */
+export const headingH3Bold = style({
+ fontSize: "0.625rem", // 10px
+ fontWeight: themeContract.typography.fontWeight.bold,
+ lineHeight: "28px",
+ letterSpacing: "-0.4px",
+
+ "@media": {
+ "screen and (min-width: 640px)": {
+ fontSize: themeContract.typography.fontSize.xs, // 12px
+ },
+ "screen and (min-width: 768px)": {
+ fontSize: themeContract.typography.fontSize.sm, // 14px
+ },
+ "screen and (min-width: 1024px)": {
+ fontSize: themeContract.typography.fontSize.base, // 16px
+ },
+ },
+});
diff --git a/packages/memory-graph/src/ui/heading.tsx b/packages/memory-graph/src/ui/heading.tsx
new file mode 100644
index 00000000..65e8abc8
--- /dev/null
+++ b/packages/memory-graph/src/ui/heading.tsx
@@ -0,0 +1,18 @@
+import { Root } from "@radix-ui/react-slot";
+import { headingH3Bold } from "./heading.css";
+
+export function HeadingH3Bold({
+ className,
+ asChild,
+ ...props
+}: React.ComponentProps<"h3"> & { asChild?: boolean }) {
+ const Comp = asChild ? Root : "h3";
+
+ const combinedClassName = className
+ ? `${headingH3Bold} ${className}`
+ : headingH3Bold;
+
+ return (
+ <Comp className={combinedClassName} {...props} />
+ );
+}
diff --git a/packages/memory-graph/tsconfig.json b/packages/memory-graph/tsconfig.json
new file mode 100644
index 00000000..c8878527
--- /dev/null
+++ b/packages/memory-graph/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "extends": "@total-typescript/tsconfig/bundler/dom/library",
+ "compilerOptions": {
+ "jsx": "react-jsx",
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ },
+ "outDir": "./dist",
+ "declaration": true,
+ "declarationMap": true,
+ "emitDeclarationOnly": false,
+ "skipLibCheck": true,
+ "strict": true,
+ "esModuleInterop": true,
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "allowSyntheticDefaultImports": true,
+ "forceConsistentCasingInFileNames": true,
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noEmit": false
+ },
+ "include": ["src/**/*"],
+ "exclude": ["node_modules", "dist", "**/*.test.*", "**/*.spec.*"]
+}
diff --git a/packages/memory-graph/vite.config.ts b/packages/memory-graph/vite.config.ts
new file mode 100644
index 00000000..3098067f
--- /dev/null
+++ b/packages/memory-graph/vite.config.ts
@@ -0,0 +1,56 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+import { resolve } from 'path'
+import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';
+import { libInjectCss } from 'vite-plugin-lib-inject-css';
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [react(), vanillaExtractPlugin(), libInjectCss()],
+ build: {
+ lib: {
+ entry: resolve(__dirname, 'src/index.tsx'),
+ name: 'MemoryGraph',
+ formats: ['es', 'cjs'],
+ fileName: (format) => {
+ if (format === 'es') return 'memory-graph.js'
+ if (format === 'cjs') return 'memory-graph.cjs'
+ return 'memory-graph.js'
+ }
+ },
+ rollupOptions: {
+ // Externalize only peer dependencies (React)
+ external: ['react', 'react-dom', 'react/jsx-runtime'],
+ output: {
+ // Provide global variables for UMD build (if needed later)
+ globals: {
+ react: 'React',
+ 'react-dom': 'ReactDOM',
+ 'react/jsx-runtime': 'react/jsx-runtime'
+ },
+ // Preserve CSS as separate file
+ assetFileNames: (assetInfo) => {
+ // Vanilla-extract generates index.css, rename to memory-graph.css
+ if (assetInfo.name === 'index.css' || assetInfo.name === 'style.css') {
+ return 'memory-graph.css'
+ }
+ return assetInfo.name || 'asset'
+ },
+ // Don't preserve modules - bundle everything except externals
+ preserveModules: false,
+ }
+ },
+ // Ensure CSS is extracted
+ cssCodeSplit: false,
+ // Generate sourcemaps for debugging
+ sourcemap: true,
+ // Optimize deps
+ minify: 'esbuild',
+ target: 'esnext'
+ },
+ resolve: {
+ alias: {
+ '@': resolve(__dirname, './src')
+ }
+ }
+})
diff --git a/packages/ui/memory-graph/legend.tsx b/packages/ui/memory-graph/legend.tsx
index 81c634f3..db2495cc 100644
--- a/packages/ui/memory-graph/legend.tsx
+++ b/packages/ui/memory-graph/legend.tsx
@@ -39,7 +39,6 @@ interface ExtendedLegendProps extends LegendProps {
nodes?: GraphNode[];
edges?: GraphEdge[];
isLoading?: boolean;
- isExperimental?: boolean;
}
export const Legend = memo(function Legend({
@@ -48,20 +47,11 @@ export const Legend = memo(function Legend({
nodes = [],
edges = [],
isLoading = false,
- isExperimental = false,
}: ExtendedLegendProps) {
const isMobile = useIsMobile();
const [isExpanded, setIsExpanded] = useState(true);
const [isInitialized, setIsInitialized] = useState(false);
- const relationData = isExperimental
- ? [
- ["updates", colors.relations.updates],
- ["extends", colors.relations.extends],
- ["derives", colors.relations.derives],
- ]
- : [["updates", colors.relations.updates]];
-
// Load saved preference on client side
useEffect(() => {
if (!isInitialized) {
@@ -271,14 +261,11 @@ export const Legend = memo(function Legend({
Relations
</div>
<div className="space-y-1.5">
- {(isExperimental
- ? [
- ["updates", colors.relations.updates],
- ["extends", colors.relations.extends],
- ["derives", colors.relations.derives],
- ]
- : [["updates", colors.relations.updates]]
- ).map(([label, color]) => (
+ {[
+ ["updates", colors.relations.updates],
+ ["extends", colors.relations.extends],
+ ["derives", colors.relations.derives],
+ ].map(([label, color]) => (
<div className="flex items-center gap-2" key={label}>
<div
className="w-4 h-0 border-t-2 flex-shrink-0"
diff --git a/packages/ui/memory-graph/memory-graph.tsx b/packages/ui/memory-graph/memory-graph.tsx
index 69c544f3..8c1ad3c2 100644
--- a/packages/ui/memory-graph/memory-graph.tsx
+++ b/packages/ui/memory-graph/memory-graph.tsx
@@ -24,15 +24,18 @@ export const MemoryGraph = ({
totalLoaded,
hasMore,
loadMoreDocuments,
- showSpacesSelector = true,
+ showSpacesSelector,
variant = "console",
legendId,
highlightDocumentIds = [],
highlightsVisible = true,
occludedRightPx = 0,
autoLoadOnViewport = true,
- isExperimental = false,
}: MemoryGraphProps) => {
+ // Derive showSpacesSelector from variant if not explicitly provided
+ // console variant shows spaces selector, consumer variant hides it
+ const finalShowSpacesSelector = showSpacesSelector ?? (variant === "console");
+
const [selectedSpace, setSelectedSpace] = useState<string>("all");
const [containerSize, setContainerSize] = useState({ width: 0, height: 0 });
const containerRef = useRef<HTMLDivElement>(null);
@@ -356,7 +359,7 @@ export const MemoryGraph = ({
style={{ backgroundColor: colors.background.primary }}
>
{/* Spaces selector - only shown for console */}
- {showSpacesSelector && availableSpaces.length > 0 && (
+ {finalShowSpacesSelector && availableSpaces.length > 0 && (
<div className="absolute top-4 left-4 z-10">
<SpacesDropdown
availableSpaces={availableSpaces}
@@ -382,7 +385,6 @@ export const MemoryGraph = ({
isLoading={isLoading}
nodes={nodes}
variant={variant}
- isExperimental={isExperimental}
/>
{/* Node detail panel */}
diff --git a/packages/ui/memory-graph/types.ts b/packages/ui/memory-graph/types.ts
index 4692d2c0..a939c619 100644
--- a/packages/ui/memory-graph/types.ts
+++ b/packages/ui/memory-graph/types.ts
@@ -74,7 +74,6 @@ export interface GraphCanvasProps {
draggingNodeId: string | null;
// Optional list of document IDs (customId or internal id) to highlight
highlightDocumentIds?: string[];
- isExperimental?: boolean;
}
export interface MemoryGraphProps {
@@ -98,7 +97,6 @@ export interface MemoryGraphProps {
occludedRightPx?: number;
// Whether to auto-load more documents based on viewport visibility
autoLoadOnViewport?: boolean;
- isExperimental?: boolean;
}
export interface LegendProps {