diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000000..994397f1c5
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,99 @@
+# Hi robots, welcome to the Grida project.
+
+Grida is a open source Design tool that aims to provide high-performance, configurable canvas-based editor.
+
+**Mission**
+
+- Build a high performance interactive graphics engine and its ecosystem
+
+Currently, we have below features / modules.
+
+- canvas
+- forms
+- database
+
+## Project Structure
+
+- [docs](./docs) - the docs directory
+- [editor](./editor) - the editor directory
+- [packages](./packages) - shared packages
+- [desktop](./desktop) - the electron desktop app
+- [supabase](./supabase) - the supabase project
+- [apps](./apps) - micro sites for Grida
+- [library](./library) - hosted library workers
+- [jobs](./jobs) - hosted jobs
+- [.legacy](./legacy) - will be removed
+
+## Languages, Frameworks, Tools, Infrastructures
+
+**Languages**
+
+- Node.js 22 - main runtime for most apps
+- TypeScript 5 - main language for most apps
+- Python 3.12 - partially used for tasks / jobs, that are independent, e.g. `/library`
+- Deno - partially used for tasks / jobs, that shares the codebase, e.g. `/jobs`
+
+**Database**
+
+Grida heavily relies on Supabase.
+
+- Supabase
+
+**Web**
+
+- React.js 19
+- Next.js 15
+
+**UI**
+
+- Tailwind CSS 4
+- Shadcn UI
+- Lucide / Radix Icons
+
+**Desktop**
+
+- electron with electron-forge
+- vite
+
+## Documentation
+
+Documentation files are located in the `./docs` directory.
+
+This directory contains the docs as-is, the deployment of the docs are handled by [apps/docs](./apps/docs). A docusaurus project that syncs the docs content to its directory. When writing docs, the root `./docs` directory is the source of truth.
+
+## /editor
+
+The editor is a monorepo project that contains the codebase for the editor.
+
+grida.co and \[tenant\].grida.site domains are connected.
+
+- `app` the nextjs app directory, no shared root layout, each has its own root layout.
+ - `(api)/(public)/v1` contains the public api routes.
+ - `(api)/private` contains the private, editor only api routes.
+ - `(auth)` contains the auth specific flow routes. do not modify.
+ - `(insiders)` contains the insiders, local-only routes. e.g. Grida does not allow email signups, the insiders locally can.
+ - `(library)` contains the Grida Library (open assets) specific pages.
+ - `(preview)` contains the embed-purpose slave preview pages, maily used by the playground.
+ - `(site)` similar to `(www)`, but contains pages that are not seo-purpose.
+ - `(tenant)` contains the tenant-site rendered pages.
+ - `(tools)` contains the standalone tools and editor pages, like playground, etc.
+ - `(workbench)` contains the workbench, the main editor page.
+ - `(workspace)` similar to `(workbench)`, but contains the dashboard, not the actual editor.
+ - `(www)` contains the landing page, seo-purpose static pages.
+
+## /desktop
+
+The desktop is a electron app that runs a hosted version of the editor. we choose this way to make things maintainable.
+We choose electron for stability, consistency, and relies on chrome-specific functions.
+
+## /supabase
+
+We use supabase for database, auth, and storage.
+
+## /jobs
+
+Jobs are hosted on railway.com
+
+## /library
+
+Library workers are hosted on railway.com
diff --git a/README.md b/README.md
index f2743dfa15..25bf2d0b37 100644
--- a/README.md
+++ b/README.md
@@ -164,6 +164,8 @@ Since 2020, I’ve dedicated myself full-time to building Grida, often facing ch
- [cors.sh](https://cors.sh) - A simple CORS proxy service
- We're ~~hiring!~~ - But I am actively looking for co-founder who excels in engineering and market reach / sales.
+_If you are a robot trying to contribute, please refer to the [AGENTS.md](./AGENTS.md)_
+
---
## License
diff --git a/apps/backgrounds/README.md b/apps/backgrounds/README.md
index c4033664f8..04c8ebf9f9 100644
--- a/apps/backgrounds/README.md
+++ b/apps/backgrounds/README.md
@@ -1,36 +1,51 @@
-This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
+# [bg.grida.co](https://bg.grida.co)
-## Getting Started
+A lightweight, zero-dependency solution for adding beautiful, interactive backgrounds to your website.
-First, run the development server:
+## Why Grida Backgrounds?
-```bash
-npm run dev
-# or
-yarn dev
-# or
-pnpm dev
-# or
-bun dev
+- **Zero Dependencies**: No need to manage complex libraries like Three.js or worry about version conflicts
+- **Performance Optimized**: Backgrounds run in an isolated iframe, ensuring minimal impact on your main application
+- **Easy Integration**: Just add an iframe - no complex setup required
+- **Maintenance Free**: We handle all updates and optimizations
+
+## Quick Start
+
+Add this iframe to your HTML:
+
+```html
+
```
-Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+That's it! The background will automatically adapt to your viewport size.
-You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+## Available Backgrounds
-This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
+Visit [bg.grida.co](https://bg.grida.co) to browse and preview available backgrounds. Each background has a unique ID that you can use in the iframe URL.
-## Learn More
+## Customization
-To learn more about Next.js, take a look at the following resources:
+You can customize the background behavior by adding URL parameters:
+
+```html
+
+```
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
-- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+Available parameters vary by background - check the documentation for each specific background.
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
+## Performance
-## Deploy on Vercel
+- Backgrounds are served from a global CDN
+- Automatic quality adjustment based on device performance
+- Minimal memory footprint
+- No impact on your main application's bundle size
-The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+## Support
-Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
+For questions or custom background requests, please contact us at [grida.co/contact](https://grida.co/contact)
diff --git a/apps/blog/README.md b/apps/blog/README.md
index 0c6c2c27be..a47692b8c4 100644
--- a/apps/blog/README.md
+++ b/apps/blog/README.md
@@ -1,41 +1,12 @@
-# Website
+# [grida.co/blog](https://grida.co/blog)
-This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
+This website is built using [Docusaurus](https://docusaurus.io/).
-### Installation
+### Running locally
+```sh
+pnpm install
+pnpm start
+# pnpm build
+# visit http://localhost:3000/blog
```
-$ yarn
-```
-
-### Local Development
-
-```
-$ yarn start
-```
-
-This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
-
-### Build
-
-```
-$ yarn build
-```
-
-This command generates static content into the `build` directory and can be served using any static contents hosting service.
-
-### Deployment
-
-Using SSH:
-
-```
-$ USE_SSH=true yarn deploy
-```
-
-Not using SSH:
-
-```
-$ GIT_USER= yarn deploy
-```
-
-If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
diff --git a/apps/docs/README.md b/apps/docs/README.md
index aaba2fa1e1..4aef5e2bc2 100644
--- a/apps/docs/README.md
+++ b/apps/docs/README.md
@@ -1,41 +1,18 @@
-# Website
+# [grida.co/docs](https://grida.co/docs)
-This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.
+This website is built using [Docusaurus](https://docusaurus.io/).
-### Installation
+### Running locally
+```sh
+pnpm install
+pnpm start
+# pnpm build
+# visit http://localhost:3000/docs
```
-$ yarn
-```
-
-### Local Development
-
-```
-$ yarn start
-```
-
-This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
-
-### Build
-
-```
-$ yarn build
-```
-
-This command generates static content into the `build` directory and can be served using any static contents hosting service.
-
-### Deployment
-Using SSH:
+## Contents
-```
-$ USE_SSH=true yarn deploy
-```
-
-Not using SSH:
-
-```
-$ GIT_USER= yarn deploy
-```
+The docs content IS NOT SERVED under this directory.
-If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
+Docs are synced from the root `./docs` directory.
diff --git a/apps/posts/README.md b/apps/posts/README.md
index b12f3e33e7..63dd022f90 100644
--- a/apps/posts/README.md
+++ b/apps/posts/README.md
@@ -1,34 +1,3 @@
-This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
+# Grida Posts (Legacy CMS)
-## Getting Started
-
-First, run the development server:
-
-```bash
-npm run dev
-# or
-yarn dev
-```
-
-Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
-
-You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
-
-[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
-
-The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
-
-## Learn More
-
-To learn more about Next.js, take a look at the following resources:
-
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
-- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
-
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
-
-## Deploy on Vercel
-
-The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
-
-Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
+This project will be replaced with Grida Database CMS feature.
diff --git a/apps/viewer/README.md b/apps/viewer/README.md
index 4dabf8f605..b7e4c6280e 100644
--- a/apps/viewer/README.md
+++ b/apps/viewer/README.md
@@ -12,19 +12,10 @@ First, run the development server:
```bash
pnpm dev
+# visit http://localhost:3000
```
-Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+## Supported file types and Viewers
-You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
-
-This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
-
-## Learn More
-
-To learn more about Next.js, take a look at the following resources:
-
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
-- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
-
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
+- PDF
+- PDF - Flipbook
diff --git a/editor/.gitignore b/editor/.gitignore
index 35c540663e..32fb4abafc 100644
--- a/editor/.gitignore
+++ b/editor/.gitignore
@@ -7,7 +7,11 @@
.yarn/install-state.gz
# testing
+/blob-report/
/coverage
+/playwright/.cache/
+/playwright-report/
+/test-results/
# next.js
/.next/
@@ -40,3 +44,6 @@ supabase/.temp
# typescript
*.tsbuildinfo
next-env.d.ts
+
+# Playwright
+node_modules/
diff --git a/editor/app/(site)/(packages)/layout.tsx b/editor/app/(tools)/(packages)/layout.tsx
similarity index 100%
rename from editor/app/(site)/(packages)/layout.tsx
rename to editor/app/(tools)/(packages)/layout.tsx
diff --git a/editor/app/(site)/(packages)/packages/[%40grida]/pixel-grid/page.tsx b/editor/app/(tools)/(packages)/packages/[%40grida]/pixel-grid/page.tsx
similarity index 100%
rename from editor/app/(site)/(packages)/packages/[%40grida]/pixel-grid/page.tsx
rename to editor/app/(tools)/(packages)/packages/[%40grida]/pixel-grid/page.tsx
diff --git a/editor/app/(site)/(packages)/packages/[%40grida]/ruler/page.tsx b/editor/app/(tools)/(packages)/packages/[%40grida]/ruler/page.tsx
similarity index 100%
rename from editor/app/(site)/(packages)/packages/[%40grida]/ruler/page.tsx
rename to editor/app/(tools)/(packages)/packages/[%40grida]/ruler/page.tsx
diff --git a/editor/app/(site)/(packages)/packages/[%40grida]/transparency-grid/page.tsx b/editor/app/(tools)/(packages)/packages/[%40grida]/transparency-grid/page.tsx
similarity index 100%
rename from editor/app/(site)/(packages)/packages/[%40grida]/transparency-grid/page.tsx
rename to editor/app/(tools)/(packages)/packages/[%40grida]/transparency-grid/page.tsx
diff --git a/editor/app/(site)/(packages)/packages/data.ts b/editor/app/(tools)/(packages)/packages/data.ts
similarity index 100%
rename from editor/app/(site)/(packages)/packages/data.ts
rename to editor/app/(tools)/(packages)/packages/data.ts
diff --git a/editor/app/(site)/(packages)/packages/page.tsx b/editor/app/(tools)/(packages)/packages/page.tsx
similarity index 100%
rename from editor/app/(site)/(packages)/packages/page.tsx
rename to editor/app/(tools)/(packages)/packages/page.tsx
diff --git a/editor/app/(site)/(packages)/packages/sitemap.ts b/editor/app/(tools)/(packages)/packages/sitemap.ts
similarity index 100%
rename from editor/app/(site)/(packages)/packages/sitemap.ts
rename to editor/app/(tools)/(packages)/packages/sitemap.ts
diff --git a/editor/app/(site)/(playground)/playground/forms/[slug]/page.tsx b/editor/app/(tools)/(playground)/playground/forms/[slug]/page.tsx
similarity index 100%
rename from editor/app/(site)/(playground)/playground/forms/[slug]/page.tsx
rename to editor/app/(tools)/(playground)/playground/forms/[slug]/page.tsx
diff --git a/editor/app/(site)/(playground)/playground/forms/layout.tsx b/editor/app/(tools)/(playground)/playground/forms/layout.tsx
similarity index 100%
rename from editor/app/(site)/(playground)/playground/forms/layout.tsx
rename to editor/app/(tools)/(playground)/playground/forms/layout.tsx
diff --git a/editor/app/(site)/(playground)/playground/forms/page.tsx b/editor/app/(tools)/(playground)/playground/forms/page.tsx
similarity index 100%
rename from editor/app/(site)/(playground)/playground/forms/page.tsx
rename to editor/app/(tools)/(playground)/playground/forms/page.tsx
diff --git a/editor/app/(site)/(playground)/playground/forms/publish/route.ts b/editor/app/(tools)/(playground)/playground/forms/publish/route.ts
similarity index 100%
rename from editor/app/(site)/(playground)/playground/forms/publish/route.ts
rename to editor/app/(tools)/(playground)/playground/forms/publish/route.ts
diff --git a/editor/app/(site)/(playground)/playground/forms/share/route.ts b/editor/app/(tools)/(playground)/playground/forms/share/route.ts
similarity index 100%
rename from editor/app/(site)/(playground)/playground/forms/share/route.ts
rename to editor/app/(tools)/(playground)/playground/forms/share/route.ts
diff --git a/editor/app/(site)/(playground)/playground/forms/with-ai/route.ts b/editor/app/(tools)/(playground)/playground/forms/with-ai/route.ts
similarity index 100%
rename from editor/app/(site)/(playground)/playground/forms/with-ai/route.ts
rename to editor/app/(tools)/(playground)/playground/forms/with-ai/route.ts
diff --git a/editor/app/(site)/(playground)/playground/image/_components/image-frame.tsx b/editor/app/(tools)/(playground)/playground/image/_components/image-frame.tsx
similarity index 100%
rename from editor/app/(site)/(playground)/playground/image/_components/image-frame.tsx
rename to editor/app/(tools)/(playground)/playground/image/_components/image-frame.tsx
diff --git a/editor/app/(site)/(playground)/playground/image/_page.tsx b/editor/app/(tools)/(playground)/playground/image/_page.tsx
similarity index 100%
rename from editor/app/(site)/(playground)/playground/image/_page.tsx
rename to editor/app/(tools)/(playground)/playground/image/_page.tsx
diff --git a/editor/app/(site)/(playground)/playground/image/page.tsx b/editor/app/(tools)/(playground)/playground/image/page.tsx
similarity index 100%
rename from editor/app/(site)/(playground)/playground/image/page.tsx
rename to editor/app/(tools)/(playground)/playground/image/page.tsx
diff --git a/editor/app/(site)/(playground)/playground/layout.tsx b/editor/app/(tools)/(playground)/playground/layout.tsx
similarity index 100%
rename from editor/app/(site)/(playground)/playground/layout.tsx
rename to editor/app/(tools)/(playground)/playground/layout.tsx
diff --git a/editor/app/(site)/(playground)/playground/page.tsx b/editor/app/(tools)/(playground)/playground/page.tsx
similarity index 100%
rename from editor/app/(site)/(playground)/playground/page.tsx
rename to editor/app/(tools)/(playground)/playground/page.tsx
diff --git a/editor/app/(tools)/layout.tsx b/editor/app/(tools)/layout.tsx
index bdead4c714..1cf2187799 100644
--- a/editor/app/(tools)/layout.tsx
+++ b/editor/app/(tools)/layout.tsx
@@ -4,6 +4,7 @@ import { GoogleAnalytics } from "@next/third-parties/google";
import { Analytics } from "@vercel/analytics/react";
import { SpeedInsights } from "@vercel/speed-insights/next";
import { ThemeProvider } from "@/components/theme-provider";
+import { Toaster } from "@/components/ui/sonner";
import "../globals.css";
const inter = Inter({ subsets: ["latin"] });
@@ -21,13 +22,16 @@ export default function RootLayout({
return (
- {children}
+
+
+ {children}
+
+ {process.env.NEXT_PUBLIC_GAID && (
+
+ )}
+
+
- {process.env.NEXT_PUBLIC_GAID && (
-
- )}
-
-
);
}
diff --git a/editor/e2e/canvas.spec.ts b/editor/e2e/canvas.spec.ts
new file mode 100644
index 0000000000..4ae05e94bc
--- /dev/null
+++ b/editor/e2e/canvas.spec.ts
@@ -0,0 +1,51 @@
+import { test, expect } from "@playwright/test";
+
+test("canvas basic features", async ({ page }) => {
+ // Navigate to canvas page
+ await page.goto("/canvas");
+
+ // wait for the page to load
+ await page.waitForLoadState("load");
+ // wait for extra 3 seconds
+ await page.waitForTimeout(3000);
+
+ // [r for rect]
+ // Press 'r' to select rectangle tool
+ await page.keyboard.press("r");
+
+ // Get the viewport dimensions
+ const viewportWidth = page.viewportSize()?.width || 0;
+ const viewportHeight = page.viewportSize()?.height || 0;
+
+ // Click in the center of the viewport to insert a rectangle
+ await page.mouse.click(viewportWidth / 2, viewportHeight / 2);
+
+ // Add a small wait to ensure the rectangle is rendered
+ await page.waitForTimeout(100);
+
+ // croll to right 100px
+ await page.mouse.wheel(100, 0);
+
+ // [o for circle]
+ await page.keyboard.press("o");
+
+ // Click in the center of the viewport to insert a circle
+ await page.mouse.click(viewportWidth / 2, viewportHeight / 2);
+
+ // Add a small wait to ensure the circle is rendered
+ await page.waitForTimeout(100);
+
+ // croll to right 100px
+ await page.mouse.wheel(100, 0);
+
+ // [l for line]
+ await page.keyboard.press("l");
+
+ // drag a-b
+ await page.mouse.down();
+ await page.mouse.move(viewportWidth / 2 + 100, viewportHeight / 2);
+ await page.mouse.up();
+
+ // Add a small wait to ensure the line is rendered
+ await page.waitForTimeout(100);
+});
diff --git a/editor/package.json b/editor/package.json
index bd896c68a3..ed985c03f9 100644
--- a/editor/package.json
+++ b/editor/package.json
@@ -10,13 +10,14 @@
"lint": "next lint",
"lint:fix": "next lint --fix",
"test": "jest",
+ "test:e2e": "playwright test",
"typecheck": "tsc --noEmit"
},
"packageManager": "pnpm@10.10.0",
"dependencies": {
- "@app/database": "workspace:*",
"@ai-sdk/openai": "^1.3.20",
"@ai-sdk/replicate": "^0.2.7",
+ "@app/database": "workspace:*",
"@blocknote/core": "^0.29.1",
"@blocknote/mantine": "^0.29.1",
"@blocknote/react": "^0.29.1",
@@ -182,8 +183,8 @@
"react-i18next": "^13.5.0",
"react-map-gl": "^7.1.7",
"react-medium-image-zoom": "^5.2.11",
- "react-pdf": "^9.2.1",
"react-p-queue": "workspace:*",
+ "react-pdf": "^9.2.1",
"react-phone-number-input": "^3.4.12",
"react-player": "^2.14.1",
"react-resizable-panels": "^3.0.1",
@@ -215,6 +216,7 @@
"devDependencies": {
"@eslint/eslintrc": "^3.3.1",
"@figma/rest-api-spec": "^0.21.0",
+ "@playwright/test": "^1.52.0",
"@supabase/storage-js": "^2.7.3",
"@tailwindcss/postcss": "^4.1.6",
"@tailwindcss/typography": "^0.5.16",
diff --git a/editor/playwright.config.ts b/editor/playwright.config.ts
new file mode 100644
index 0000000000..2da6a4b7d6
--- /dev/null
+++ b/editor/playwright.config.ts
@@ -0,0 +1,59 @@
+import { defineConfig, devices } from "@playwright/test";
+import path from "path";
+
+// Use process.env.PORT by default and fallback to port 3000
+const PORT = process.env.PORT || 3000;
+
+// Set webServer.url and use.baseURL with the location of the WebServer respecting the correct set port
+const baseURL = `http://localhost:${PORT}`;
+
+// Reference: https://playwright.dev/docs/test-configuration
+export default defineConfig({
+ // Timeout per test
+ timeout: 30 * 1000,
+ // Test directory
+ testDir: path.join(__dirname, "e2e"),
+ // If a test fails, retry it additional 2 times
+ retries: 2,
+ // Artifacts folder where screenshots, videos, and traces are stored.
+ outputDir: "test-results/",
+
+ // Run your local dev server before starting the tests:
+ // https://playwright.dev/docs/test-advanced#launching-a-development-web-server-during-the-tests
+ webServer: {
+ command: "npm run dev",
+ url: baseURL,
+ timeout: 120 * 1000,
+ reuseExistingServer: !process.env.CI,
+ },
+
+ use: {
+ // Use baseURL so to make navigations relative.
+ // More information: https://playwright.dev/docs/api/class-testoptions#test-options-base-url
+ baseURL,
+
+ // Retry a test if its failing with enabled tracing. This allows you to analyze the DOM, console logs, network traffic etc.
+ // More information: https://playwright.dev/docs/trace-viewer
+ trace: "retry-with-trace",
+
+ // All available context options: https://playwright.dev/docs/api/class-browser#browser-new-context
+ // contextOptions: {
+ // ignoreHTTPSErrors: true,
+ // },
+ },
+
+ projects: [
+ {
+ name: "Desktop Chrome",
+ use: {
+ ...devices["Desktop Chrome"],
+ },
+ },
+ {
+ name: "Mobile Chrome",
+ use: {
+ ...devices["Pixel 5"],
+ },
+ },
+ ],
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 57e550a7e1..443dad1229 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -49,7 +49,7 @@ importers:
dependencies:
'@next/third-parties':
specifier: 15.3.2
- version: 15.3.2(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
+ version: 15.3.2(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
'@react-three/drei':
specifier: ^10.0.7
version: 10.0.8(@react-three/fiber@9.1.2(@types/react@19.1.3)(immer@9.0.21)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.170.0))(@types/react@19.1.3)(@types/three@0.170.0)(immer@9.0.21)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.170.0)
@@ -64,7 +64,7 @@ importers:
version: 12.12.1(@emotion/is-prop-valid@1.3.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
next:
specifier: 15.3.2
- version: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ version: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react:
specifier: 19.1.0
version: 19.1.0
@@ -211,7 +211,7 @@ importers:
version: 0.367.0(react@19.1.0)
next:
specifier: 15.3.2
- version: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ version: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
pdfjs-dist:
specifier: 4.8.69
version: 4.8.69
@@ -359,7 +359,7 @@ importers:
version: 15.3.2(@mdx-js/loader@3.1.0(acorn@8.14.1)(webpack@5.98.0(esbuild@0.25.4)))(@mdx-js/react@3.1.0(@types/react@19.1.3)(react@19.0.0))
'@next/third-parties':
specifier: 15.3.2
- version: 15.3.2(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)
+ version: 15.3.2(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)
'@number-flow/react':
specifier: ^0.5.7
version: 0.5.9(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -458,7 +458,7 @@ importers:
version: 0.0.38(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@sentry/nextjs':
specifier: ^9.17.0
- version: 9.19.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(webpack@5.98.0(esbuild@0.25.4))
+ version: 9.19.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(webpack@5.98.0(esbuild@0.25.4))
'@stepperize/react':
specifier: ^3.1.1
version: 3.1.1(react@19.0.0)
@@ -539,7 +539,7 @@ importers:
version: 10.3.1(react@19.0.0)
'@vercel/analytics':
specifier: ^1.3.1
- version: 1.5.0(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.19)(vue@3.5.13(typescript@5.8.3))
+ version: 1.5.0(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.19)(vue@3.5.13(typescript@5.8.3))
'@vercel/edge-config':
specifier: ^1.2.1
version: 1.4.0(@opentelemetry/api@1.9.0)
@@ -551,7 +551,7 @@ importers:
version: 1.7.4(zod@3.24.4)
'@vercel/speed-insights':
specifier: ^1.0.12
- version: 1.2.0(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.19)(vue@3.5.13(typescript@5.8.3))
+ version: 1.2.0(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.19)(vue@3.5.13(typescript@5.8.3))
'@visx/responsive':
specifier: ^3.10.2
version: 3.12.0(react@19.0.0)
@@ -713,7 +713,7 @@ importers:
version: 1.0.0
next:
specifier: 15.3.2
- version: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ version: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
next-themes:
specifier: ^0.4.6
version: 0.4.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -862,6 +862,9 @@ importers:
'@figma/rest-api-spec':
specifier: ^0.21.0
version: 0.21.0
+ '@playwright/test':
+ specifier: ^1.52.0
+ version: 1.52.0
'@supabase/storage-js':
specifier: ^2.7.3
version: 2.7.3
@@ -3500,6 +3503,11 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
+ '@playwright/test@1.52.0':
+ resolution: {integrity: sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g==}
+ engines: {node: '>=18'}
+ hasBin: true
+
'@pnpm/config.env-replace@1.1.0':
resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==}
engines: {node: '>=12.22.0'}
@@ -8190,6 +8198,11 @@ packages:
fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+ fsevents@2.3.2:
+ resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -10493,6 +10506,16 @@ packages:
resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==}
engines: {node: '>=8'}
+ playwright-core@1.52.0:
+ resolution: {integrity: sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ playwright@1.52.0:
+ resolution: {integrity: sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==}
+ engines: {node: '>=18'}
+ hasBin: true
+
point-in-polygon-hao@1.2.4:
resolution: {integrity: sha512-x2pcvXeqhRHlNRdhLs/tgFapAbSSe86wa/eqmj1G6pWftbEs5aVRJhRGM6FYSUERKu0PjekJzMq0gsI2XyiclQ==}
@@ -16532,15 +16555,15 @@ snapshots:
'@next/swc-win32-x64-msvc@15.3.2':
optional: true
- '@next/third-parties@15.3.2(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)':
+ '@next/third-parties@15.3.2(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)':
dependencies:
- next: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ next: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react: 19.0.0
third-party-capital: 1.0.20
- '@next/third-parties@15.3.2(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)':
+ '@next/third-parties@15.3.2(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)':
dependencies:
- next: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ next: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react: 19.1.0
third-party-capital: 1.0.20
@@ -16872,6 +16895,10 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
+ '@playwright/test@1.52.0':
+ dependencies:
+ playwright: 1.52.0
+
'@pnpm/config.env-replace@1.1.0': {}
'@pnpm/network.ca-file@1.0.2':
@@ -17981,7 +18008,7 @@ snapshots:
'@sentry/core@9.19.0': {}
- '@sentry/nextjs@9.19.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(webpack@5.98.0(esbuild@0.25.4))':
+ '@sentry/nextjs@9.19.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(webpack@5.98.0(esbuild@0.25.4))':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/semantic-conventions': 1.33.0
@@ -17994,7 +18021,7 @@ snapshots:
'@sentry/vercel-edge': 9.19.0
'@sentry/webpack-plugin': 3.3.1(encoding@0.1.13)(webpack@5.98.0(esbuild@0.25.4))
chalk: 3.0.0
- next: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ next: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
resolve: 1.22.8
rollup: 4.35.0
stacktrace-parser: 0.1.11
@@ -19283,9 +19310,9 @@ snapshots:
'@use-gesture/core': 10.3.1
react: 19.1.0
- '@vercel/analytics@1.5.0(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.19)(vue@3.5.13(typescript@5.8.3))':
+ '@vercel/analytics@1.5.0(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.19)(vue@3.5.13(typescript@5.8.3))':
optionalDependencies:
- next: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ next: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react: 19.0.0
svelte: 4.2.19
vue: 3.5.13(typescript@5.8.3)
@@ -19304,9 +19331,9 @@ snapshots:
dependencies:
zod: 3.24.4
- '@vercel/speed-insights@1.2.0(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.19)(vue@3.5.13(typescript@5.8.3))':
+ '@vercel/speed-insights@1.2.0(next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.19)(vue@3.5.13(typescript@5.8.3))':
optionalDependencies:
- next: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ next: 15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react: 19.0.0
svelte: 4.2.19
vue: 3.5.13(typescript@5.8.3)
@@ -22367,6 +22394,9 @@ snapshots:
fs.realpath@1.0.0: {}
+ fsevents@2.3.2:
+ optional: true
+
fsevents@2.3.3:
optional: true
@@ -24873,7 +24903,7 @@ snapshots:
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
- next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
+ next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies:
'@next/env': 15.3.2
'@swc/counter': 0.1.3
@@ -24894,12 +24924,13 @@ snapshots:
'@next/swc-win32-arm64-msvc': 15.3.2
'@next/swc-win32-x64-msvc': 15.3.2
'@opentelemetry/api': 1.9.0
+ '@playwright/test': 1.52.0
sharp: 0.34.1
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
- next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+ next@15.3.2(@babel/core@7.27.1)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
'@next/env': 15.3.2
'@swc/counter': 0.1.3
@@ -24920,6 +24951,7 @@ snapshots:
'@next/swc-win32-arm64-msvc': 15.3.2
'@next/swc-win32-x64-msvc': 15.3.2
'@opentelemetry/api': 1.9.0
+ '@playwright/test': 1.52.0
sharp: 0.34.1
transitivePeerDependencies:
- '@babel/core'
@@ -25332,6 +25364,14 @@ snapshots:
dependencies:
find-up: 3.0.0
+ playwright-core@1.52.0: {}
+
+ playwright@1.52.0:
+ dependencies:
+ playwright-core: 1.52.0
+ optionalDependencies:
+ fsevents: 2.3.2
+
point-in-polygon-hao@1.2.4:
dependencies:
robust-predicates: 3.0.2