├── .env.example ├── .gitignore ├── README.md ├── next.config.mjs ├── package.json ├── pnpm-lock.yaml ├── postcss.config.mjs ├── public └── favicon.ico ├── resources ├── demo.mp4 └── instant-perms.json ├── src ├── __dev.tsx ├── components │ ├── InstantAuth.tsx │ └── UI.tsx ├── config.tsx ├── instant.perms.ts ├── instant.schema.ts ├── lib │ ├── clientDB.tsx │ ├── useInstantPresence.tsx │ └── useInstantStore.tsx ├── mutators.ts ├── pages │ ├── _app.tsx │ ├── drawings │ │ └── [id].tsx │ └── index.tsx ├── styles │ └── globals.css └── types.ts ├── tailwind.config.ts └── tsconfig.json /.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_INSTANT_APP_ID= -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | # env 39 | .env 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🎨 [instldraw](https://draw.instantdb.com/): Team-oriented tldraw built w/ InstantDB 2 | 3 | Welcome! We took [tldraw](https://tldraw.dev/)'s infinite canvas and added real-time team collaboration powered by Instant's [graph database](https://www.instantdb.com/docs/instaml) and [presence](https://www.instantdb.com/docs/presence-and-topics). 4 | 5 | https://github.com/jsventures/instldraw/assets/1624703/bde87c77-10b5-4267-9c82-20ac6755fabc 6 | 7 | ### 🎁 Features 8 | 9 | - Auth via ["magic code" login](https://www.instantdb.com/docs/auth#magic-codes). 10 | - A full-fledged teams/memberships/invites [data model](https://www.instantdb.com/docs/modeling-data) and secured by [permissions](https://www.instantdb.com/docs/permissions). 11 | - Multiplayer cursors via [presence](https://www.instantdb.com/docs/presence-and-topics). 12 | 13 | ## 🛣️ Getting Started 14 | 15 | ### ⚡ 1. Create a free account on [Instant](https://www.instantdb.com/) 16 | 17 | Head on over to the [Instant dashboard](https://www.instantdb.com/dash), grab your app's ID, and plop it into a `.env` file: 18 | 19 | ```bash 20 | NEXT_PUBLIC_INSTANT_APP_ID=__YOUR_APP_ID__ 21 | ``` 22 | 23 | ### 🔥 2. Install and Run 24 | 25 | 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). 26 | 27 | Install dependencies with your package manager of choice, then start the development server: 28 | 29 | ```bash 30 | npm run dev 31 | # or 32 | yarn dev 33 | # or 34 | pnpm dev 35 | # or 36 | bun dev 37 | ``` 38 | 39 | That's it! 🎉 Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. Next.js will live-update the page as you edit the app's code. 40 | 41 | Finally, to add a layer of security to your app, copy `resources/instant-perms.json` into the [Instant Permissions editor](https://www.instantdb.com/dash?s=main&t=perms). 42 | 43 | ## 📚 Reference 44 | 45 | ### 📂 Structure 46 | 47 | The app is broken up into two pages: 48 | 49 | - An index page `/`, which serves as a dashboard and directory of teams and drawings. 50 | - A drawing page `/drawings/:id` where we render the [tldraw](https://tldraw.dev/) canvas. 51 | 52 | Both pages load data from Instant with `db.useQuery` and write data using functions from `src/mutators.ts`. 53 | 54 | ### 📄 Notable Files 55 | 56 | - `src/instant.schema.ts`: Contains the schema for the app's data model. 57 | - `src/instant.perms.ts`: Contains the permissions for the app. 58 | - `src/pages/index.tsx`: The main dashboard: list and manage teams, teammates, and drawings. 59 | - `src/pages/drawings/[id].tsx`: The canvas! Uses `useInstantStore` and `useInstantPresence` to add multiplayer. 60 | - `src/lib/clientDB.tsx`: Exports our Instant database! 61 | - `src/lib/useInstantStore.tsx`: A collaborative backend for tldraw built on top of Instant's real-time database. Uses InstaML's [merge()](https://www.instantdb.com/docs/instaml#merge) for fine-grained updates to the drawing state. 62 | - `src/lib/useInstantPresence.tsx`: A React hook responsible for keeping tldraw's editor state in sync with [Instant's real-time presence API](https://www.instantdb.com/docs/presence-and-topics). 63 | - `src/mutators.ts`: All functions that update Instant's database live here. You can inspect and edit your database using the [Instant Explorer](https://www.instantdb.com/dash?s=main&t=explorer). 64 | 65 | ## Why Instant? 66 | 67 | Instant is a sync engine inspired by Firebase with support for relational data. To learn more, check out [this essay](https://www.instantdb.com/essays/next_firebase). 68 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | }; 5 | 6 | export default nextConfig; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "instldraw", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@instantdb/react": "^0.17.27", 13 | "lodash": "^4.17.21", 14 | "next": "14.2.4", 15 | "react": "^18", 16 | "react-dom": "^18", 17 | "tldraw": "^2.2.1" 18 | }, 19 | "devDependencies": { 20 | "@types/lodash": "^4.17.6", 21 | "@types/node": "^20", 22 | "@types/react": "^18", 23 | "@types/react-dom": "^18", 24 | "dotenv": "^16.4.5", 25 | "postcss": "^8", 26 | "tailwindcss": "^3.4.1", 27 | "typescript": "^5" 28 | }, 29 | "packageManager": "pnpm@8.15.6+sha512.77b89e9be77a2b06ad8f403a19cae5e22976f61023f98ad323d5c30194958ebc02ee0a6ae5d13ee454f6134e4e8caf29a05f0b1a0e1d2b17bca6b6a1f1159f86" 30 | } 31 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | dependencies: 8 | '@instantdb/react': 9 | specifier: ^0.17.27 10 | version: 0.17.27(react@18.3.1) 11 | lodash: 12 | specifier: ^4.17.21 13 | version: 4.17.21 14 | next: 15 | specifier: 14.2.4 16 | version: 14.2.4(react-dom@18.3.1)(react@18.3.1) 17 | react: 18 | specifier: ^18 19 | version: 18.3.1 20 | react-dom: 21 | specifier: ^18 22 | version: 18.3.1(react@18.3.1) 23 | tldraw: 24 | specifier: ^2.2.1 25 | version: 2.2.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 26 | 27 | devDependencies: 28 | '@types/lodash': 29 | specifier: ^4.17.6 30 | version: 4.17.6 31 | '@types/node': 32 | specifier: ^20 33 | version: 20.14.9 34 | '@types/react': 35 | specifier: ^18 36 | version: 18.3.3 37 | '@types/react-dom': 38 | specifier: ^18 39 | version: 18.3.0 40 | dotenv: 41 | specifier: ^16.4.5 42 | version: 16.4.5 43 | postcss: 44 | specifier: ^8 45 | version: 8.4.39 46 | tailwindcss: 47 | specifier: ^3.4.1 48 | version: 3.4.4 49 | typescript: 50 | specifier: ^5 51 | version: 5.5.3 52 | 53 | packages: 54 | 55 | /@alloc/quick-lru@5.2.0: 56 | resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} 57 | engines: {node: '>=10'} 58 | dev: true 59 | 60 | /@babel/runtime@7.24.7: 61 | resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==} 62 | engines: {node: '>=6.9.0'} 63 | dependencies: 64 | regenerator-runtime: 0.14.1 65 | dev: false 66 | 67 | /@floating-ui/core@1.6.4: 68 | resolution: {integrity: sha512-a4IowK4QkXl4SCWTGUR0INAfEOX3wtsYw3rKK5InQEHMGObkR8Xk44qYQD9P4r6HHw0iIfK6GUKECmY8sTkqRA==} 69 | dependencies: 70 | '@floating-ui/utils': 0.2.4 71 | dev: false 72 | 73 | /@floating-ui/dom@1.6.7: 74 | resolution: {integrity: sha512-wmVfPG5o2xnKDU4jx/m4w5qva9FWHcnZ8BvzEe90D/RpwsJaTAVYPEPdQ8sbr/N8zZTAHlZUTQdqg8ZUbzHmng==} 75 | dependencies: 76 | '@floating-ui/core': 1.6.4 77 | '@floating-ui/utils': 0.2.4 78 | dev: false 79 | 80 | /@floating-ui/react-dom@2.1.1(react-dom@18.3.1)(react@18.3.1): 81 | resolution: {integrity: sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==} 82 | peerDependencies: 83 | react: '>=16.8.0' 84 | react-dom: '>=16.8.0' 85 | dependencies: 86 | '@floating-ui/dom': 1.6.7 87 | react: 18.3.1 88 | react-dom: 18.3.1(react@18.3.1) 89 | dev: false 90 | 91 | /@floating-ui/utils@0.2.4: 92 | resolution: {integrity: sha512-dWO2pw8hhi+WrXq1YJy2yCuWoL20PddgGaqTgVe4cOS9Q6qklXCiA1tJEqX6BEwRNSCP84/afac9hd4MS+zEUA==} 93 | dev: false 94 | 95 | /@instantdb/core@0.17.27: 96 | resolution: {integrity: sha512-TuZMCDsxVEwRI8MGkz5Mzb6Dyo6C6xX5ifVWbNOUexY9CA9ZiarFhAd3k9q5ZDSqbq0aHye8zmeYiJgAkPmQjg==} 97 | dependencies: 98 | mutative: 1.0.10 99 | uuid: 9.0.1 100 | dev: false 101 | 102 | /@instantdb/react@0.17.27(react@18.3.1): 103 | resolution: {integrity: sha512-oUa8hn2coAIlUgeAvJ+vUMgcnLIyCpB10Uw8iQDL7NfdU/tzxk15OBCqfuB+434nMlEL6aQGHVyNHeATtmtXHg==} 104 | peerDependencies: 105 | react: '>=16' 106 | dependencies: 107 | '@instantdb/core': 0.17.27 108 | react: 18.3.1 109 | dev: false 110 | 111 | /@isaacs/cliui@8.0.2: 112 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} 113 | engines: {node: '>=12'} 114 | dependencies: 115 | string-width: 5.1.2 116 | string-width-cjs: /string-width@4.2.3 117 | strip-ansi: 7.1.0 118 | strip-ansi-cjs: /strip-ansi@6.0.1 119 | wrap-ansi: 8.1.0 120 | wrap-ansi-cjs: /wrap-ansi@7.0.0 121 | dev: true 122 | 123 | /@jridgewell/gen-mapping@0.3.5: 124 | resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} 125 | engines: {node: '>=6.0.0'} 126 | dependencies: 127 | '@jridgewell/set-array': 1.2.1 128 | '@jridgewell/sourcemap-codec': 1.4.15 129 | '@jridgewell/trace-mapping': 0.3.25 130 | dev: true 131 | 132 | /@jridgewell/resolve-uri@3.1.2: 133 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 134 | engines: {node: '>=6.0.0'} 135 | dev: true 136 | 137 | /@jridgewell/set-array@1.2.1: 138 | resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} 139 | engines: {node: '>=6.0.0'} 140 | dev: true 141 | 142 | /@jridgewell/sourcemap-codec@1.4.15: 143 | resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} 144 | dev: true 145 | 146 | /@jridgewell/trace-mapping@0.3.25: 147 | resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} 148 | dependencies: 149 | '@jridgewell/resolve-uri': 3.1.2 150 | '@jridgewell/sourcemap-codec': 1.4.15 151 | dev: true 152 | 153 | /@next/env@14.2.4: 154 | resolution: {integrity: sha512-3EtkY5VDkuV2+lNmKlbkibIJxcO4oIHEhBWne6PaAp+76J9KoSsGvNikp6ivzAT8dhhBMYrm6op2pS1ApG0Hzg==} 155 | dev: false 156 | 157 | /@next/swc-darwin-arm64@14.2.4: 158 | resolution: {integrity: sha512-AH3mO4JlFUqsYcwFUHb1wAKlebHU/Hv2u2kb1pAuRanDZ7pD/A/KPD98RHZmwsJpdHQwfEc/06mgpSzwrJYnNg==} 159 | engines: {node: '>= 10'} 160 | cpu: [arm64] 161 | os: [darwin] 162 | requiresBuild: true 163 | dev: false 164 | optional: true 165 | 166 | /@next/swc-darwin-x64@14.2.4: 167 | resolution: {integrity: sha512-QVadW73sWIO6E2VroyUjuAxhWLZWEpiFqHdZdoQ/AMpN9YWGuHV8t2rChr0ahy+irKX5mlDU7OY68k3n4tAZTg==} 168 | engines: {node: '>= 10'} 169 | cpu: [x64] 170 | os: [darwin] 171 | requiresBuild: true 172 | dev: false 173 | optional: true 174 | 175 | /@next/swc-linux-arm64-gnu@14.2.4: 176 | resolution: {integrity: sha512-KT6GUrb3oyCfcfJ+WliXuJnD6pCpZiosx2X3k66HLR+DMoilRb76LpWPGb4tZprawTtcnyrv75ElD6VncVamUQ==} 177 | engines: {node: '>= 10'} 178 | cpu: [arm64] 179 | os: [linux] 180 | requiresBuild: true 181 | dev: false 182 | optional: true 183 | 184 | /@next/swc-linux-arm64-musl@14.2.4: 185 | resolution: {integrity: sha512-Alv8/XGSs/ytwQcbCHwze1HmiIkIVhDHYLjczSVrf0Wi2MvKn/blt7+S6FJitj3yTlMwMxII1gIJ9WepI4aZ/A==} 186 | engines: {node: '>= 10'} 187 | cpu: [arm64] 188 | os: [linux] 189 | requiresBuild: true 190 | dev: false 191 | optional: true 192 | 193 | /@next/swc-linux-x64-gnu@14.2.4: 194 | resolution: {integrity: sha512-ze0ShQDBPCqxLImzw4sCdfnB3lRmN3qGMB2GWDRlq5Wqy4G36pxtNOo2usu/Nm9+V2Rh/QQnrRc2l94kYFXO6Q==} 195 | engines: {node: '>= 10'} 196 | cpu: [x64] 197 | os: [linux] 198 | requiresBuild: true 199 | dev: false 200 | optional: true 201 | 202 | /@next/swc-linux-x64-musl@14.2.4: 203 | resolution: {integrity: sha512-8dwC0UJoc6fC7PX70csdaznVMNr16hQrTDAMPvLPloazlcaWfdPogq+UpZX6Drqb1OBlwowz8iG7WR0Tzk/diQ==} 204 | engines: {node: '>= 10'} 205 | cpu: [x64] 206 | os: [linux] 207 | requiresBuild: true 208 | dev: false 209 | optional: true 210 | 211 | /@next/swc-win32-arm64-msvc@14.2.4: 212 | resolution: {integrity: sha512-jxyg67NbEWkDyvM+O8UDbPAyYRZqGLQDTPwvrBBeOSyVWW/jFQkQKQ70JDqDSYg1ZDdl+E3nkbFbq8xM8E9x8A==} 213 | engines: {node: '>= 10'} 214 | cpu: [arm64] 215 | os: [win32] 216 | requiresBuild: true 217 | dev: false 218 | optional: true 219 | 220 | /@next/swc-win32-ia32-msvc@14.2.4: 221 | resolution: {integrity: sha512-twrmN753hjXRdcrZmZttb/m5xaCBFa48Dt3FbeEItpJArxriYDunWxJn+QFXdJ3hPkm4u7CKxncVvnmgQMY1ag==} 222 | engines: {node: '>= 10'} 223 | cpu: [ia32] 224 | os: [win32] 225 | requiresBuild: true 226 | dev: false 227 | optional: true 228 | 229 | /@next/swc-win32-x64-msvc@14.2.4: 230 | resolution: {integrity: sha512-tkLrjBzqFTP8DVrAAQmZelEahfR9OxWpFR++vAI9FBhCiIxtwHwBHC23SBHCTURBtwB4kc/x44imVOnkKGNVGg==} 231 | engines: {node: '>= 10'} 232 | cpu: [x64] 233 | os: [win32] 234 | requiresBuild: true 235 | dev: false 236 | optional: true 237 | 238 | /@nodelib/fs.scandir@2.1.5: 239 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 240 | engines: {node: '>= 8'} 241 | dependencies: 242 | '@nodelib/fs.stat': 2.0.5 243 | run-parallel: 1.2.0 244 | dev: true 245 | 246 | /@nodelib/fs.stat@2.0.5: 247 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 248 | engines: {node: '>= 8'} 249 | dev: true 250 | 251 | /@nodelib/fs.walk@1.2.8: 252 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 253 | engines: {node: '>= 8'} 254 | dependencies: 255 | '@nodelib/fs.scandir': 2.1.5 256 | fastq: 1.17.1 257 | dev: true 258 | 259 | /@pkgjs/parseargs@0.11.0: 260 | resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} 261 | engines: {node: '>=14'} 262 | requiresBuild: true 263 | dev: true 264 | optional: true 265 | 266 | /@radix-ui/number@1.0.1: 267 | resolution: {integrity: sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==} 268 | dependencies: 269 | '@babel/runtime': 7.24.7 270 | dev: false 271 | 272 | /@radix-ui/number@1.1.0: 273 | resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} 274 | dev: false 275 | 276 | /@radix-ui/primitive@1.0.1: 277 | resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} 278 | dependencies: 279 | '@babel/runtime': 7.24.7 280 | dev: false 281 | 282 | /@radix-ui/primitive@1.1.0: 283 | resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==} 284 | dev: false 285 | 286 | /@radix-ui/react-alert-dialog@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 287 | resolution: {integrity: sha512-wmCoJwj7byuVuiLKqDLlX7ClSUU0vd9sdCeM+2Ls+uf13+cpSJoMgwysHq1SGVVkJj5Xn0XWi1NoRCdkMpr6Mw==} 288 | peerDependencies: 289 | '@types/react': '*' 290 | '@types/react-dom': '*' 291 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 292 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 293 | peerDependenciesMeta: 294 | '@types/react': 295 | optional: true 296 | '@types/react-dom': 297 | optional: true 298 | dependencies: 299 | '@radix-ui/primitive': 1.1.0 300 | '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) 301 | '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1) 302 | '@radix-ui/react-dialog': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 303 | '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 304 | '@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1) 305 | '@types/react': 18.3.3 306 | '@types/react-dom': 18.3.0 307 | react: 18.3.1 308 | react-dom: 18.3.1(react@18.3.1) 309 | dev: false 310 | 311 | /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 312 | resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} 313 | peerDependencies: 314 | '@types/react': '*' 315 | '@types/react-dom': '*' 316 | react: ^16.8 || ^17.0 || ^18.0 317 | react-dom: ^16.8 || ^17.0 || ^18.0 318 | peerDependenciesMeta: 319 | '@types/react': 320 | optional: true 321 | '@types/react-dom': 322 | optional: true 323 | dependencies: 324 | '@babel/runtime': 7.24.7 325 | '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 326 | '@types/react': 18.3.3 327 | '@types/react-dom': 18.3.0 328 | react: 18.3.1 329 | react-dom: 18.3.1(react@18.3.1) 330 | dev: false 331 | 332 | /@radix-ui/react-arrow@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 333 | resolution: {integrity: sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==} 334 | peerDependencies: 335 | '@types/react': '*' 336 | '@types/react-dom': '*' 337 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 338 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 339 | peerDependenciesMeta: 340 | '@types/react': 341 | optional: true 342 | '@types/react-dom': 343 | optional: true 344 | dependencies: 345 | '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 346 | '@types/react': 18.3.3 347 | '@types/react-dom': 18.3.0 348 | react: 18.3.1 349 | react-dom: 18.3.1(react@18.3.1) 350 | dev: false 351 | 352 | /@radix-ui/react-collection@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 353 | resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} 354 | peerDependencies: 355 | '@types/react': '*' 356 | '@types/react-dom': '*' 357 | react: ^16.8 || ^17.0 || ^18.0 358 | react-dom: ^16.8 || ^17.0 || ^18.0 359 | peerDependenciesMeta: 360 | '@types/react': 361 | optional: true 362 | '@types/react-dom': 363 | optional: true 364 | dependencies: 365 | '@babel/runtime': 7.24.7 366 | '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) 367 | '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) 368 | '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 369 | '@radix-ui/react-slot': 1.0.2(@types/react@18.3.3)(react@18.3.1) 370 | '@types/react': 18.3.3 371 | '@types/react-dom': 18.3.0 372 | react: 18.3.1 373 | react-dom: 18.3.1(react@18.3.1) 374 | dev: false 375 | 376 | /@radix-ui/react-collection@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 377 | resolution: {integrity: sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==} 378 | peerDependencies: 379 | '@types/react': '*' 380 | '@types/react-dom': '*' 381 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 382 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 383 | peerDependenciesMeta: 384 | '@types/react': 385 | optional: true 386 | '@types/react-dom': 387 | optional: true 388 | dependencies: 389 | '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) 390 | '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1) 391 | '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 392 | '@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1) 393 | '@types/react': 18.3.3 394 | '@types/react-dom': 18.3.0 395 | react: 18.3.1 396 | react-dom: 18.3.1(react@18.3.1) 397 | dev: false 398 | 399 | /@radix-ui/react-compose-refs@1.0.1(@types/react@18.3.3)(react@18.3.1): 400 | resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} 401 | peerDependencies: 402 | '@types/react': '*' 403 | react: ^16.8 || ^17.0 || ^18.0 404 | peerDependenciesMeta: 405 | '@types/react': 406 | optional: true 407 | dependencies: 408 | '@babel/runtime': 7.24.7 409 | '@types/react': 18.3.3 410 | react: 18.3.1 411 | dev: false 412 | 413 | /@radix-ui/react-compose-refs@1.1.0(@types/react@18.3.3)(react@18.3.1): 414 | resolution: {integrity: sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==} 415 | peerDependencies: 416 | '@types/react': '*' 417 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 418 | peerDependenciesMeta: 419 | '@types/react': 420 | optional: true 421 | dependencies: 422 | '@types/react': 18.3.3 423 | react: 18.3.1 424 | dev: false 425 | 426 | /@radix-ui/react-context-menu@2.2.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 427 | resolution: {integrity: sha512-wvMKKIeb3eOrkJ96s722vcidZ+2ZNfcYZWBPRHIB1VWrF+fiF851Io6LX0kmK5wTDQFKdulCCKJk2c3SBaQHvA==} 428 | peerDependencies: 429 | '@types/react': '*' 430 | '@types/react-dom': '*' 431 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 432 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 433 | peerDependenciesMeta: 434 | '@types/react': 435 | optional: true 436 | '@types/react-dom': 437 | optional: true 438 | dependencies: 439 | '@radix-ui/primitive': 1.1.0 440 | '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1) 441 | '@radix-ui/react-menu': 2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 442 | '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 443 | '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1) 444 | '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.3)(react@18.3.1) 445 | '@types/react': 18.3.3 446 | '@types/react-dom': 18.3.0 447 | react: 18.3.1 448 | react-dom: 18.3.1(react@18.3.1) 449 | dev: false 450 | 451 | /@radix-ui/react-context@1.0.1(@types/react@18.3.3)(react@18.3.1): 452 | resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} 453 | peerDependencies: 454 | '@types/react': '*' 455 | react: ^16.8 || ^17.0 || ^18.0 456 | peerDependenciesMeta: 457 | '@types/react': 458 | optional: true 459 | dependencies: 460 | '@babel/runtime': 7.24.7 461 | '@types/react': 18.3.3 462 | react: 18.3.1 463 | dev: false 464 | 465 | /@radix-ui/react-context@1.1.0(@types/react@18.3.3)(react@18.3.1): 466 | resolution: {integrity: sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==} 467 | peerDependencies: 468 | '@types/react': '*' 469 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 470 | peerDependenciesMeta: 471 | '@types/react': 472 | optional: true 473 | dependencies: 474 | '@types/react': 18.3.3 475 | react: 18.3.1 476 | dev: false 477 | 478 | /@radix-ui/react-dialog@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 479 | resolution: {integrity: sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==} 480 | peerDependencies: 481 | '@types/react': '*' 482 | '@types/react-dom': '*' 483 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 484 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 485 | peerDependenciesMeta: 486 | '@types/react': 487 | optional: true 488 | '@types/react-dom': 489 | optional: true 490 | dependencies: 491 | '@radix-ui/primitive': 1.1.0 492 | '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) 493 | '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1) 494 | '@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 495 | '@radix-ui/react-focus-guards': 1.1.0(@types/react@18.3.3)(react@18.3.1) 496 | '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 497 | '@radix-ui/react-id': 1.1.0(@types/react@18.3.3)(react@18.3.1) 498 | '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 499 | '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 500 | '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 501 | '@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1) 502 | '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.3)(react@18.3.1) 503 | '@types/react': 18.3.3 504 | '@types/react-dom': 18.3.0 505 | aria-hidden: 1.2.4 506 | react: 18.3.1 507 | react-dom: 18.3.1(react@18.3.1) 508 | react-remove-scroll: 2.5.7(@types/react@18.3.3)(react@18.3.1) 509 | dev: false 510 | 511 | /@radix-ui/react-direction@1.0.1(@types/react@18.3.3)(react@18.3.1): 512 | resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==} 513 | peerDependencies: 514 | '@types/react': '*' 515 | react: ^16.8 || ^17.0 || ^18.0 516 | peerDependenciesMeta: 517 | '@types/react': 518 | optional: true 519 | dependencies: 520 | '@babel/runtime': 7.24.7 521 | '@types/react': 18.3.3 522 | react: 18.3.1 523 | dev: false 524 | 525 | /@radix-ui/react-direction@1.1.0(@types/react@18.3.3)(react@18.3.1): 526 | resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==} 527 | peerDependencies: 528 | '@types/react': '*' 529 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 530 | peerDependenciesMeta: 531 | '@types/react': 532 | optional: true 533 | dependencies: 534 | '@types/react': 18.3.3 535 | react: 18.3.1 536 | dev: false 537 | 538 | /@radix-ui/react-dismissable-layer@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 539 | resolution: {integrity: sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==} 540 | peerDependencies: 541 | '@types/react': '*' 542 | '@types/react-dom': '*' 543 | react: ^16.8 || ^17.0 || ^18.0 544 | react-dom: ^16.8 || ^17.0 || ^18.0 545 | peerDependenciesMeta: 546 | '@types/react': 547 | optional: true 548 | '@types/react-dom': 549 | optional: true 550 | dependencies: 551 | '@babel/runtime': 7.24.7 552 | '@radix-ui/primitive': 1.0.1 553 | '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) 554 | '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 555 | '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) 556 | '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.3.3)(react@18.3.1) 557 | '@types/react': 18.3.3 558 | '@types/react-dom': 18.3.0 559 | react: 18.3.1 560 | react-dom: 18.3.1(react@18.3.1) 561 | dev: false 562 | 563 | /@radix-ui/react-dismissable-layer@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 564 | resolution: {integrity: sha512-/UovfmmXGptwGcBQawLzvn2jOfM0t4z3/uKffoBlj724+n3FvBbZ7M0aaBOmkp6pqFYpO4yx8tSVJjx3Fl2jig==} 565 | peerDependencies: 566 | '@types/react': '*' 567 | '@types/react-dom': '*' 568 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 569 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 570 | peerDependenciesMeta: 571 | '@types/react': 572 | optional: true 573 | '@types/react-dom': 574 | optional: true 575 | dependencies: 576 | '@radix-ui/primitive': 1.1.0 577 | '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) 578 | '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 579 | '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1) 580 | '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.3.3)(react@18.3.1) 581 | '@types/react': 18.3.3 582 | '@types/react-dom': 18.3.0 583 | react: 18.3.1 584 | react-dom: 18.3.1(react@18.3.1) 585 | dev: false 586 | 587 | /@radix-ui/react-dropdown-menu@2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 588 | resolution: {integrity: sha512-y8E+x9fBq9qvteD2Zwa4397pUVhYsh9iq44b5RD5qu1GMJWBCBuVg1hMyItbc6+zH00TxGRqd9Iot4wzf3OoBQ==} 589 | peerDependencies: 590 | '@types/react': '*' 591 | '@types/react-dom': '*' 592 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 593 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 594 | peerDependenciesMeta: 595 | '@types/react': 596 | optional: true 597 | '@types/react-dom': 598 | optional: true 599 | dependencies: 600 | '@radix-ui/primitive': 1.1.0 601 | '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) 602 | '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1) 603 | '@radix-ui/react-id': 1.1.0(@types/react@18.3.3)(react@18.3.1) 604 | '@radix-ui/react-menu': 2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 605 | '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 606 | '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.3)(react@18.3.1) 607 | '@types/react': 18.3.3 608 | '@types/react-dom': 18.3.0 609 | react: 18.3.1 610 | react-dom: 18.3.1(react@18.3.1) 611 | dev: false 612 | 613 | /@radix-ui/react-focus-guards@1.0.1(@types/react@18.3.3)(react@18.3.1): 614 | resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==} 615 | peerDependencies: 616 | '@types/react': '*' 617 | react: ^16.8 || ^17.0 || ^18.0 618 | peerDependenciesMeta: 619 | '@types/react': 620 | optional: true 621 | dependencies: 622 | '@babel/runtime': 7.24.7 623 | '@types/react': 18.3.3 624 | react: 18.3.1 625 | dev: false 626 | 627 | /@radix-ui/react-focus-guards@1.1.0(@types/react@18.3.3)(react@18.3.1): 628 | resolution: {integrity: sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==} 629 | peerDependencies: 630 | '@types/react': '*' 631 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 632 | peerDependenciesMeta: 633 | '@types/react': 634 | optional: true 635 | dependencies: 636 | '@types/react': 18.3.3 637 | react: 18.3.1 638 | dev: false 639 | 640 | /@radix-ui/react-focus-scope@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 641 | resolution: {integrity: sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==} 642 | peerDependencies: 643 | '@types/react': '*' 644 | '@types/react-dom': '*' 645 | react: ^16.8 || ^17.0 || ^18.0 646 | react-dom: ^16.8 || ^17.0 || ^18.0 647 | peerDependenciesMeta: 648 | '@types/react': 649 | optional: true 650 | '@types/react-dom': 651 | optional: true 652 | dependencies: 653 | '@babel/runtime': 7.24.7 654 | '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) 655 | '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 656 | '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) 657 | '@types/react': 18.3.3 658 | '@types/react-dom': 18.3.0 659 | react: 18.3.1 660 | react-dom: 18.3.1(react@18.3.1) 661 | dev: false 662 | 663 | /@radix-ui/react-focus-scope@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 664 | resolution: {integrity: sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==} 665 | peerDependencies: 666 | '@types/react': '*' 667 | '@types/react-dom': '*' 668 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 669 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 670 | peerDependenciesMeta: 671 | '@types/react': 672 | optional: true 673 | '@types/react-dom': 674 | optional: true 675 | dependencies: 676 | '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) 677 | '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 678 | '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1) 679 | '@types/react': 18.3.3 680 | '@types/react-dom': 18.3.0 681 | react: 18.3.1 682 | react-dom: 18.3.1(react@18.3.1) 683 | dev: false 684 | 685 | /@radix-ui/react-id@1.0.1(@types/react@18.3.3)(react@18.3.1): 686 | resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} 687 | peerDependencies: 688 | '@types/react': '*' 689 | react: ^16.8 || ^17.0 || ^18.0 690 | peerDependenciesMeta: 691 | '@types/react': 692 | optional: true 693 | dependencies: 694 | '@babel/runtime': 7.24.7 695 | '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.3)(react@18.3.1) 696 | '@types/react': 18.3.3 697 | react: 18.3.1 698 | dev: false 699 | 700 | /@radix-ui/react-id@1.1.0(@types/react@18.3.3)(react@18.3.1): 701 | resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} 702 | peerDependencies: 703 | '@types/react': '*' 704 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 705 | peerDependenciesMeta: 706 | '@types/react': 707 | optional: true 708 | dependencies: 709 | '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1) 710 | '@types/react': 18.3.3 711 | react: 18.3.1 712 | dev: false 713 | 714 | /@radix-ui/react-menu@2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 715 | resolution: {integrity: sha512-oa3mXRRVjHi6DZu/ghuzdylyjaMXLymx83irM7hTxutQbD+7IhPKdMdRHD26Rm+kHRrWcrUkkRPv5pd47a2xFQ==} 716 | peerDependencies: 717 | '@types/react': '*' 718 | '@types/react-dom': '*' 719 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 720 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 721 | peerDependenciesMeta: 722 | '@types/react': 723 | optional: true 724 | '@types/react-dom': 725 | optional: true 726 | dependencies: 727 | '@radix-ui/primitive': 1.1.0 728 | '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 729 | '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) 730 | '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1) 731 | '@radix-ui/react-direction': 1.1.0(@types/react@18.3.3)(react@18.3.1) 732 | '@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 733 | '@radix-ui/react-focus-guards': 1.1.0(@types/react@18.3.3)(react@18.3.1) 734 | '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 735 | '@radix-ui/react-id': 1.1.0(@types/react@18.3.3)(react@18.3.1) 736 | '@radix-ui/react-popper': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 737 | '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 738 | '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 739 | '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 740 | '@radix-ui/react-roving-focus': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 741 | '@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1) 742 | '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1) 743 | '@types/react': 18.3.3 744 | '@types/react-dom': 18.3.0 745 | aria-hidden: 1.2.4 746 | react: 18.3.1 747 | react-dom: 18.3.1(react@18.3.1) 748 | react-remove-scroll: 2.5.7(@types/react@18.3.3)(react@18.3.1) 749 | dev: false 750 | 751 | /@radix-ui/react-popover@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 752 | resolution: {integrity: sha512-3y1A3isulwnWhvTTwmIreiB8CF4L+qRjZnK1wYLO7pplddzXKby/GnZ2M7OZY3qgnl6p9AodUIHRYGXNah8Y7g==} 753 | peerDependencies: 754 | '@types/react': '*' 755 | '@types/react-dom': '*' 756 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 757 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 758 | peerDependenciesMeta: 759 | '@types/react': 760 | optional: true 761 | '@types/react-dom': 762 | optional: true 763 | dependencies: 764 | '@radix-ui/primitive': 1.1.0 765 | '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) 766 | '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1) 767 | '@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 768 | '@radix-ui/react-focus-guards': 1.1.0(@types/react@18.3.3)(react@18.3.1) 769 | '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 770 | '@radix-ui/react-id': 1.1.0(@types/react@18.3.3)(react@18.3.1) 771 | '@radix-ui/react-popper': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 772 | '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 773 | '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 774 | '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 775 | '@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1) 776 | '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.3)(react@18.3.1) 777 | '@types/react': 18.3.3 778 | '@types/react-dom': 18.3.0 779 | aria-hidden: 1.2.4 780 | react: 18.3.1 781 | react-dom: 18.3.1(react@18.3.1) 782 | react-remove-scroll: 2.5.7(@types/react@18.3.3)(react@18.3.1) 783 | dev: false 784 | 785 | /@radix-ui/react-popper@1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 786 | resolution: {integrity: sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==} 787 | peerDependencies: 788 | '@types/react': '*' 789 | '@types/react-dom': '*' 790 | react: ^16.8 || ^17.0 || ^18.0 791 | react-dom: ^16.8 || ^17.0 || ^18.0 792 | peerDependenciesMeta: 793 | '@types/react': 794 | optional: true 795 | '@types/react-dom': 796 | optional: true 797 | dependencies: 798 | '@babel/runtime': 7.24.7 799 | '@floating-ui/react-dom': 2.1.1(react-dom@18.3.1)(react@18.3.1) 800 | '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 801 | '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) 802 | '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) 803 | '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 804 | '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) 805 | '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.3)(react@18.3.1) 806 | '@radix-ui/react-use-rect': 1.0.1(@types/react@18.3.3)(react@18.3.1) 807 | '@radix-ui/react-use-size': 1.0.1(@types/react@18.3.3)(react@18.3.1) 808 | '@radix-ui/rect': 1.0.1 809 | '@types/react': 18.3.3 810 | '@types/react-dom': 18.3.0 811 | react: 18.3.1 812 | react-dom: 18.3.1(react@18.3.1) 813 | dev: false 814 | 815 | /@radix-ui/react-popper@1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 816 | resolution: {integrity: sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==} 817 | peerDependencies: 818 | '@types/react': '*' 819 | '@types/react-dom': '*' 820 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 821 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 822 | peerDependenciesMeta: 823 | '@types/react': 824 | optional: true 825 | '@types/react-dom': 826 | optional: true 827 | dependencies: 828 | '@floating-ui/react-dom': 2.1.1(react-dom@18.3.1)(react@18.3.1) 829 | '@radix-ui/react-arrow': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 830 | '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) 831 | '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1) 832 | '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 833 | '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1) 834 | '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1) 835 | '@radix-ui/react-use-rect': 1.1.0(@types/react@18.3.3)(react@18.3.1) 836 | '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.3)(react@18.3.1) 837 | '@radix-ui/rect': 1.1.0 838 | '@types/react': 18.3.3 839 | '@types/react-dom': 18.3.0 840 | react: 18.3.1 841 | react-dom: 18.3.1(react@18.3.1) 842 | dev: false 843 | 844 | /@radix-ui/react-portal@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 845 | resolution: {integrity: sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==} 846 | peerDependencies: 847 | '@types/react': '*' 848 | '@types/react-dom': '*' 849 | react: ^16.8 || ^17.0 || ^18.0 850 | react-dom: ^16.8 || ^17.0 || ^18.0 851 | peerDependenciesMeta: 852 | '@types/react': 853 | optional: true 854 | '@types/react-dom': 855 | optional: true 856 | dependencies: 857 | '@babel/runtime': 7.24.7 858 | '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 859 | '@types/react': 18.3.3 860 | '@types/react-dom': 18.3.0 861 | react: 18.3.1 862 | react-dom: 18.3.1(react@18.3.1) 863 | dev: false 864 | 865 | /@radix-ui/react-portal@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 866 | resolution: {integrity: sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g==} 867 | peerDependencies: 868 | '@types/react': '*' 869 | '@types/react-dom': '*' 870 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 871 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 872 | peerDependenciesMeta: 873 | '@types/react': 874 | optional: true 875 | '@types/react-dom': 876 | optional: true 877 | dependencies: 878 | '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 879 | '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1) 880 | '@types/react': 18.3.3 881 | '@types/react-dom': 18.3.0 882 | react: 18.3.1 883 | react-dom: 18.3.1(react@18.3.1) 884 | dev: false 885 | 886 | /@radix-ui/react-presence@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 887 | resolution: {integrity: sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==} 888 | peerDependencies: 889 | '@types/react': '*' 890 | '@types/react-dom': '*' 891 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 892 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 893 | peerDependenciesMeta: 894 | '@types/react': 895 | optional: true 896 | '@types/react-dom': 897 | optional: true 898 | dependencies: 899 | '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) 900 | '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1) 901 | '@types/react': 18.3.3 902 | '@types/react-dom': 18.3.0 903 | react: 18.3.1 904 | react-dom: 18.3.1(react@18.3.1) 905 | dev: false 906 | 907 | /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 908 | resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} 909 | peerDependencies: 910 | '@types/react': '*' 911 | '@types/react-dom': '*' 912 | react: ^16.8 || ^17.0 || ^18.0 913 | react-dom: ^16.8 || ^17.0 || ^18.0 914 | peerDependenciesMeta: 915 | '@types/react': 916 | optional: true 917 | '@types/react-dom': 918 | optional: true 919 | dependencies: 920 | '@babel/runtime': 7.24.7 921 | '@radix-ui/react-slot': 1.0.2(@types/react@18.3.3)(react@18.3.1) 922 | '@types/react': 18.3.3 923 | '@types/react-dom': 18.3.0 924 | react: 18.3.1 925 | react-dom: 18.3.1(react@18.3.1) 926 | dev: false 927 | 928 | /@radix-ui/react-primitive@2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 929 | resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==} 930 | peerDependencies: 931 | '@types/react': '*' 932 | '@types/react-dom': '*' 933 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 934 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 935 | peerDependenciesMeta: 936 | '@types/react': 937 | optional: true 938 | '@types/react-dom': 939 | optional: true 940 | dependencies: 941 | '@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1) 942 | '@types/react': 18.3.3 943 | '@types/react-dom': 18.3.0 944 | react: 18.3.1 945 | react-dom: 18.3.1(react@18.3.1) 946 | dev: false 947 | 948 | /@radix-ui/react-roving-focus@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 949 | resolution: {integrity: sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==} 950 | peerDependencies: 951 | '@types/react': '*' 952 | '@types/react-dom': '*' 953 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 954 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 955 | peerDependenciesMeta: 956 | '@types/react': 957 | optional: true 958 | '@types/react-dom': 959 | optional: true 960 | dependencies: 961 | '@radix-ui/primitive': 1.1.0 962 | '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 963 | '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) 964 | '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1) 965 | '@radix-ui/react-direction': 1.1.0(@types/react@18.3.3)(react@18.3.1) 966 | '@radix-ui/react-id': 1.1.0(@types/react@18.3.3)(react@18.3.1) 967 | '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 968 | '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1) 969 | '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.3)(react@18.3.1) 970 | '@types/react': 18.3.3 971 | '@types/react-dom': 18.3.0 972 | react: 18.3.1 973 | react-dom: 18.3.1(react@18.3.1) 974 | dev: false 975 | 976 | /@radix-ui/react-select@1.2.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 977 | resolution: {integrity: sha512-zI7McXr8fNaSrUY9mZe4x/HC0jTLY9fWNhO1oLWYMQGDXuV4UCivIGTxwioSzO0ZCYX9iSLyWmAh/1TOmX3Cnw==} 978 | peerDependencies: 979 | '@types/react': '*' 980 | '@types/react-dom': '*' 981 | react: ^16.8 || ^17.0 || ^18.0 982 | react-dom: ^16.8 || ^17.0 || ^18.0 983 | peerDependenciesMeta: 984 | '@types/react': 985 | optional: true 986 | '@types/react-dom': 987 | optional: true 988 | dependencies: 989 | '@babel/runtime': 7.24.7 990 | '@radix-ui/number': 1.0.1 991 | '@radix-ui/primitive': 1.0.1 992 | '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 993 | '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) 994 | '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) 995 | '@radix-ui/react-direction': 1.0.1(@types/react@18.3.3)(react@18.3.1) 996 | '@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 997 | '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.3.3)(react@18.3.1) 998 | '@radix-ui/react-focus-scope': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 999 | '@radix-ui/react-id': 1.0.1(@types/react@18.3.3)(react@18.3.1) 1000 | '@radix-ui/react-popper': 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 1001 | '@radix-ui/react-portal': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 1002 | '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 1003 | '@radix-ui/react-slot': 1.0.2(@types/react@18.3.3)(react@18.3.1) 1004 | '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) 1005 | '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.3)(react@18.3.1) 1006 | '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.3)(react@18.3.1) 1007 | '@radix-ui/react-use-previous': 1.0.1(@types/react@18.3.3)(react@18.3.1) 1008 | '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 1009 | '@types/react': 18.3.3 1010 | '@types/react-dom': 18.3.0 1011 | aria-hidden: 1.2.4 1012 | react: 18.3.1 1013 | react-dom: 18.3.1(react@18.3.1) 1014 | react-remove-scroll: 2.5.5(@types/react@18.3.3)(react@18.3.1) 1015 | dev: false 1016 | 1017 | /@radix-ui/react-slider@1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 1018 | resolution: {integrity: sha512-dAHCDA4/ySXROEPaRtaMV5WHL8+JB/DbtyTbJjYkY0RXmKMO2Ln8DFZhywG5/mVQ4WqHDBc8smc14yPXPqZHYA==} 1019 | peerDependencies: 1020 | '@types/react': '*' 1021 | '@types/react-dom': '*' 1022 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1023 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1024 | peerDependenciesMeta: 1025 | '@types/react': 1026 | optional: true 1027 | '@types/react-dom': 1028 | optional: true 1029 | dependencies: 1030 | '@radix-ui/number': 1.1.0 1031 | '@radix-ui/primitive': 1.1.0 1032 | '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 1033 | '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) 1034 | '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1) 1035 | '@radix-ui/react-direction': 1.1.0(@types/react@18.3.3)(react@18.3.1) 1036 | '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 1037 | '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.3)(react@18.3.1) 1038 | '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1) 1039 | '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.3)(react@18.3.1) 1040 | '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.3)(react@18.3.1) 1041 | '@types/react': 18.3.3 1042 | '@types/react-dom': 18.3.0 1043 | react: 18.3.1 1044 | react-dom: 18.3.1(react@18.3.1) 1045 | dev: false 1046 | 1047 | /@radix-ui/react-slot@1.0.2(@types/react@18.3.3)(react@18.3.1): 1048 | resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} 1049 | peerDependencies: 1050 | '@types/react': '*' 1051 | react: ^16.8 || ^17.0 || ^18.0 1052 | peerDependenciesMeta: 1053 | '@types/react': 1054 | optional: true 1055 | dependencies: 1056 | '@babel/runtime': 7.24.7 1057 | '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) 1058 | '@types/react': 18.3.3 1059 | react: 18.3.1 1060 | dev: false 1061 | 1062 | /@radix-ui/react-slot@1.1.0(@types/react@18.3.3)(react@18.3.1): 1063 | resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==} 1064 | peerDependencies: 1065 | '@types/react': '*' 1066 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1067 | peerDependenciesMeta: 1068 | '@types/react': 1069 | optional: true 1070 | dependencies: 1071 | '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) 1072 | '@types/react': 18.3.3 1073 | react: 18.3.1 1074 | dev: false 1075 | 1076 | /@radix-ui/react-toast@1.2.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 1077 | resolution: {integrity: sha512-5trl7piMXcZiCq7MW6r8YYmu0bK5qDpTWz+FdEPdKyft2UixkspheYbjbrLXVN5NGKHFbOP7lm8eD0biiSqZqg==} 1078 | peerDependencies: 1079 | '@types/react': '*' 1080 | '@types/react-dom': '*' 1081 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1082 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1083 | peerDependenciesMeta: 1084 | '@types/react': 1085 | optional: true 1086 | '@types/react-dom': 1087 | optional: true 1088 | dependencies: 1089 | '@radix-ui/primitive': 1.1.0 1090 | '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 1091 | '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) 1092 | '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1) 1093 | '@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 1094 | '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 1095 | '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 1096 | '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 1097 | '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1) 1098 | '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.3)(react@18.3.1) 1099 | '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1) 1100 | '@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 1101 | '@types/react': 18.3.3 1102 | '@types/react-dom': 18.3.0 1103 | react: 18.3.1 1104 | react-dom: 18.3.1(react@18.3.1) 1105 | dev: false 1106 | 1107 | /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.3.3)(react@18.3.1): 1108 | resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==} 1109 | peerDependencies: 1110 | '@types/react': '*' 1111 | react: ^16.8 || ^17.0 || ^18.0 1112 | peerDependenciesMeta: 1113 | '@types/react': 1114 | optional: true 1115 | dependencies: 1116 | '@babel/runtime': 7.24.7 1117 | '@types/react': 18.3.3 1118 | react: 18.3.1 1119 | dev: false 1120 | 1121 | /@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.3)(react@18.3.1): 1122 | resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} 1123 | peerDependencies: 1124 | '@types/react': '*' 1125 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1126 | peerDependenciesMeta: 1127 | '@types/react': 1128 | optional: true 1129 | dependencies: 1130 | '@types/react': 18.3.3 1131 | react: 18.3.1 1132 | dev: false 1133 | 1134 | /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.3.3)(react@18.3.1): 1135 | resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} 1136 | peerDependencies: 1137 | '@types/react': '*' 1138 | react: ^16.8 || ^17.0 || ^18.0 1139 | peerDependenciesMeta: 1140 | '@types/react': 1141 | optional: true 1142 | dependencies: 1143 | '@babel/runtime': 7.24.7 1144 | '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) 1145 | '@types/react': 18.3.3 1146 | react: 18.3.1 1147 | dev: false 1148 | 1149 | /@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.3.3)(react@18.3.1): 1150 | resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} 1151 | peerDependencies: 1152 | '@types/react': '*' 1153 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1154 | peerDependenciesMeta: 1155 | '@types/react': 1156 | optional: true 1157 | dependencies: 1158 | '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1) 1159 | '@types/react': 18.3.3 1160 | react: 18.3.1 1161 | dev: false 1162 | 1163 | /@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.3.3)(react@18.3.1): 1164 | resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} 1165 | peerDependencies: 1166 | '@types/react': '*' 1167 | react: ^16.8 || ^17.0 || ^18.0 1168 | peerDependenciesMeta: 1169 | '@types/react': 1170 | optional: true 1171 | dependencies: 1172 | '@babel/runtime': 7.24.7 1173 | '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) 1174 | '@types/react': 18.3.3 1175 | react: 18.3.1 1176 | dev: false 1177 | 1178 | /@radix-ui/react-use-escape-keydown@1.1.0(@types/react@18.3.3)(react@18.3.1): 1179 | resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==} 1180 | peerDependencies: 1181 | '@types/react': '*' 1182 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1183 | peerDependenciesMeta: 1184 | '@types/react': 1185 | optional: true 1186 | dependencies: 1187 | '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1) 1188 | '@types/react': 18.3.3 1189 | react: 18.3.1 1190 | dev: false 1191 | 1192 | /@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.3.3)(react@18.3.1): 1193 | resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==} 1194 | peerDependencies: 1195 | '@types/react': '*' 1196 | react: ^16.8 || ^17.0 || ^18.0 1197 | peerDependenciesMeta: 1198 | '@types/react': 1199 | optional: true 1200 | dependencies: 1201 | '@babel/runtime': 7.24.7 1202 | '@types/react': 18.3.3 1203 | react: 18.3.1 1204 | dev: false 1205 | 1206 | /@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.3.3)(react@18.3.1): 1207 | resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} 1208 | peerDependencies: 1209 | '@types/react': '*' 1210 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1211 | peerDependenciesMeta: 1212 | '@types/react': 1213 | optional: true 1214 | dependencies: 1215 | '@types/react': 18.3.3 1216 | react: 18.3.1 1217 | dev: false 1218 | 1219 | /@radix-ui/react-use-previous@1.0.1(@types/react@18.3.3)(react@18.3.1): 1220 | resolution: {integrity: sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==} 1221 | peerDependencies: 1222 | '@types/react': '*' 1223 | react: ^16.8 || ^17.0 || ^18.0 1224 | peerDependenciesMeta: 1225 | '@types/react': 1226 | optional: true 1227 | dependencies: 1228 | '@babel/runtime': 7.24.7 1229 | '@types/react': 18.3.3 1230 | react: 18.3.1 1231 | dev: false 1232 | 1233 | /@radix-ui/react-use-previous@1.1.0(@types/react@18.3.3)(react@18.3.1): 1234 | resolution: {integrity: sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==} 1235 | peerDependencies: 1236 | '@types/react': '*' 1237 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1238 | peerDependenciesMeta: 1239 | '@types/react': 1240 | optional: true 1241 | dependencies: 1242 | '@types/react': 18.3.3 1243 | react: 18.3.1 1244 | dev: false 1245 | 1246 | /@radix-ui/react-use-rect@1.0.1(@types/react@18.3.3)(react@18.3.1): 1247 | resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==} 1248 | peerDependencies: 1249 | '@types/react': '*' 1250 | react: ^16.8 || ^17.0 || ^18.0 1251 | peerDependenciesMeta: 1252 | '@types/react': 1253 | optional: true 1254 | dependencies: 1255 | '@babel/runtime': 7.24.7 1256 | '@radix-ui/rect': 1.0.1 1257 | '@types/react': 18.3.3 1258 | react: 18.3.1 1259 | dev: false 1260 | 1261 | /@radix-ui/react-use-rect@1.1.0(@types/react@18.3.3)(react@18.3.1): 1262 | resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==} 1263 | peerDependencies: 1264 | '@types/react': '*' 1265 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1266 | peerDependenciesMeta: 1267 | '@types/react': 1268 | optional: true 1269 | dependencies: 1270 | '@radix-ui/rect': 1.1.0 1271 | '@types/react': 18.3.3 1272 | react: 18.3.1 1273 | dev: false 1274 | 1275 | /@radix-ui/react-use-size@1.0.1(@types/react@18.3.3)(react@18.3.1): 1276 | resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==} 1277 | peerDependencies: 1278 | '@types/react': '*' 1279 | react: ^16.8 || ^17.0 || ^18.0 1280 | peerDependenciesMeta: 1281 | '@types/react': 1282 | optional: true 1283 | dependencies: 1284 | '@babel/runtime': 7.24.7 1285 | '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.3)(react@18.3.1) 1286 | '@types/react': 18.3.3 1287 | react: 18.3.1 1288 | dev: false 1289 | 1290 | /@radix-ui/react-use-size@1.1.0(@types/react@18.3.3)(react@18.3.1): 1291 | resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==} 1292 | peerDependencies: 1293 | '@types/react': '*' 1294 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1295 | peerDependenciesMeta: 1296 | '@types/react': 1297 | optional: true 1298 | dependencies: 1299 | '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1) 1300 | '@types/react': 18.3.3 1301 | react: 18.3.1 1302 | dev: false 1303 | 1304 | /@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 1305 | resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==} 1306 | peerDependencies: 1307 | '@types/react': '*' 1308 | '@types/react-dom': '*' 1309 | react: ^16.8 || ^17.0 || ^18.0 1310 | react-dom: ^16.8 || ^17.0 || ^18.0 1311 | peerDependenciesMeta: 1312 | '@types/react': 1313 | optional: true 1314 | '@types/react-dom': 1315 | optional: true 1316 | dependencies: 1317 | '@babel/runtime': 7.24.7 1318 | '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 1319 | '@types/react': 18.3.3 1320 | '@types/react-dom': 18.3.0 1321 | react: 18.3.1 1322 | react-dom: 18.3.1(react@18.3.1) 1323 | dev: false 1324 | 1325 | /@radix-ui/react-visually-hidden@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 1326 | resolution: {integrity: sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==} 1327 | peerDependencies: 1328 | '@types/react': '*' 1329 | '@types/react-dom': '*' 1330 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1331 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc 1332 | peerDependenciesMeta: 1333 | '@types/react': 1334 | optional: true 1335 | '@types/react-dom': 1336 | optional: true 1337 | dependencies: 1338 | '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 1339 | '@types/react': 18.3.3 1340 | '@types/react-dom': 18.3.0 1341 | react: 18.3.1 1342 | react-dom: 18.3.1(react@18.3.1) 1343 | dev: false 1344 | 1345 | /@radix-ui/rect@1.0.1: 1346 | resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==} 1347 | dependencies: 1348 | '@babel/runtime': 7.24.7 1349 | dev: false 1350 | 1351 | /@radix-ui/rect@1.1.0: 1352 | resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==} 1353 | dev: false 1354 | 1355 | /@swc/counter@0.1.3: 1356 | resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} 1357 | dev: false 1358 | 1359 | /@swc/helpers@0.5.5: 1360 | resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} 1361 | dependencies: 1362 | '@swc/counter': 0.1.3 1363 | tslib: 2.6.3 1364 | dev: false 1365 | 1366 | /@tldraw/editor@2.2.1(react-dom@18.3.1)(react@18.3.1): 1367 | resolution: {integrity: sha512-0ROLYP0YD4Kq0MapXfNMXfbigECL/3Fpe+GZRlp8El7LcHl8bctaq8ppc//azqtEt9DLbQKv6BMDDlC/obfe/g==} 1368 | peerDependencies: 1369 | react: ^18 1370 | react-dom: ^18 1371 | dependencies: 1372 | '@tldraw/state': 2.2.1(react@18.3.1) 1373 | '@tldraw/store': 2.2.1(react@18.3.1) 1374 | '@tldraw/tlschema': 2.2.1(react@18.3.1) 1375 | '@tldraw/utils': 2.2.1 1376 | '@tldraw/validate': 2.2.1 1377 | '@types/core-js': 2.5.8 1378 | '@use-gesture/react': 10.3.1(react@18.3.1) 1379 | classnames: 2.5.1 1380 | core-js: 3.37.1 1381 | eventemitter3: 4.0.7 1382 | idb: 7.1.1 1383 | is-plain-object: 5.0.0 1384 | lodash.throttle: 4.1.1 1385 | lodash.uniq: 4.5.0 1386 | nanoid: 4.0.2 1387 | react: 18.3.1 1388 | react-dom: 18.3.1(react@18.3.1) 1389 | dev: false 1390 | 1391 | /@tldraw/state@2.2.1(react@18.3.1): 1392 | resolution: {integrity: sha512-s57edBpiBCMxCkYI2Cpozx6O4xjh04eTpQaqCTNK0pKpVEXQz1y1br0390etYZeMorCkJDvzN2Z6QBGYKR/6Nw==} 1393 | peerDependencies: 1394 | react: ^18 1395 | dependencies: 1396 | react: 18.3.1 1397 | dev: false 1398 | 1399 | /@tldraw/store@2.2.1(react@18.3.1): 1400 | resolution: {integrity: sha512-2sLCOe+69z+4nOBgFsLVQDGEgOPy7BFzrQXQS7FvhS/5ZTew9xZMgbUfOKCqzDQ5aLKSNO6qaQazykwzoQrNbw==} 1401 | peerDependencies: 1402 | react: ^18 1403 | dependencies: 1404 | '@tldraw/state': 2.2.1(react@18.3.1) 1405 | '@tldraw/utils': 2.2.1 1406 | lodash.isequal: 4.5.0 1407 | nanoid: 4.0.2 1408 | react: 18.3.1 1409 | dev: false 1410 | 1411 | /@tldraw/tlschema@2.2.1(react@18.3.1): 1412 | resolution: {integrity: sha512-9r4b6mezcTbj1RYwYzFvwBMEtGkIMVXtD12EMG5JUwlZU8vWFFWo+Ab0PpmnraF5GGB24BQkcuiQ2AQsN1WFwQ==} 1413 | peerDependencies: 1414 | react: ^18 1415 | dependencies: 1416 | '@tldraw/state': 2.2.1(react@18.3.1) 1417 | '@tldraw/store': 2.2.1(react@18.3.1) 1418 | '@tldraw/utils': 2.2.1 1419 | '@tldraw/validate': 2.2.1 1420 | nanoid: 4.0.2 1421 | react: 18.3.1 1422 | dev: false 1423 | 1424 | /@tldraw/utils@2.2.1: 1425 | resolution: {integrity: sha512-n2Ko/x6gGi33zP+kaKKcgdofbHQYM+jZiXJlBebngpHLLK1DnkaGVVLH9Dxk+vwBZu217ecpMGWPDZKIFxFRFg==} 1426 | dev: false 1427 | 1428 | /@tldraw/validate@2.2.1: 1429 | resolution: {integrity: sha512-9kmmy6bKCu5PyqI44yY30xluduJiJAlrjwymtYZ103ByrAjASE/tkJ18SlDepetrHqOEjpk4G8iKFzZfH45SSw==} 1430 | dependencies: 1431 | '@tldraw/utils': 2.2.1 1432 | dev: false 1433 | 1434 | /@types/core-js@2.5.8: 1435 | resolution: {integrity: sha512-VgnAj6tIAhJhZdJ8/IpxdatM8G4OD3VWGlp6xIxUGENZlpbob9Ty4VVdC1FIEp0aK6DBscDDjyzy5FB60TuNqg==} 1436 | dev: false 1437 | 1438 | /@types/lodash@4.17.6: 1439 | resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} 1440 | dev: true 1441 | 1442 | /@types/node@20.14.9: 1443 | resolution: {integrity: sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==} 1444 | dependencies: 1445 | undici-types: 5.26.5 1446 | dev: true 1447 | 1448 | /@types/prop-types@15.7.12: 1449 | resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} 1450 | 1451 | /@types/react-dom@18.3.0: 1452 | resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} 1453 | dependencies: 1454 | '@types/react': 18.3.3 1455 | 1456 | /@types/react@18.3.3: 1457 | resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} 1458 | dependencies: 1459 | '@types/prop-types': 15.7.12 1460 | csstype: 3.1.3 1461 | 1462 | /@use-gesture/core@10.3.1: 1463 | resolution: {integrity: sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==} 1464 | dev: false 1465 | 1466 | /@use-gesture/react@10.3.1(react@18.3.1): 1467 | resolution: {integrity: sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==} 1468 | peerDependencies: 1469 | react: '>= 16.8.0' 1470 | dependencies: 1471 | '@use-gesture/core': 10.3.1 1472 | react: 18.3.1 1473 | dev: false 1474 | 1475 | /ansi-regex@5.0.1: 1476 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 1477 | engines: {node: '>=8'} 1478 | dev: true 1479 | 1480 | /ansi-regex@6.0.1: 1481 | resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} 1482 | engines: {node: '>=12'} 1483 | dev: true 1484 | 1485 | /ansi-styles@4.3.0: 1486 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 1487 | engines: {node: '>=8'} 1488 | dependencies: 1489 | color-convert: 2.0.1 1490 | dev: true 1491 | 1492 | /ansi-styles@6.2.1: 1493 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} 1494 | engines: {node: '>=12'} 1495 | dev: true 1496 | 1497 | /any-promise@1.3.0: 1498 | resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} 1499 | dev: true 1500 | 1501 | /anymatch@3.1.3: 1502 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} 1503 | engines: {node: '>= 8'} 1504 | dependencies: 1505 | normalize-path: 3.0.0 1506 | picomatch: 2.3.1 1507 | dev: true 1508 | 1509 | /arg@5.0.2: 1510 | resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} 1511 | dev: true 1512 | 1513 | /aria-hidden@1.2.4: 1514 | resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==} 1515 | engines: {node: '>=10'} 1516 | dependencies: 1517 | tslib: 2.6.3 1518 | dev: false 1519 | 1520 | /balanced-match@1.0.2: 1521 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 1522 | dev: true 1523 | 1524 | /binary-extensions@2.3.0: 1525 | resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} 1526 | engines: {node: '>=8'} 1527 | dev: true 1528 | 1529 | /brace-expansion@2.0.1: 1530 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 1531 | dependencies: 1532 | balanced-match: 1.0.2 1533 | dev: true 1534 | 1535 | /braces@3.0.3: 1536 | resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 1537 | engines: {node: '>=8'} 1538 | dependencies: 1539 | fill-range: 7.1.1 1540 | dev: true 1541 | 1542 | /busboy@1.6.0: 1543 | resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} 1544 | engines: {node: '>=10.16.0'} 1545 | dependencies: 1546 | streamsearch: 1.1.0 1547 | dev: false 1548 | 1549 | /camelcase-css@2.0.1: 1550 | resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} 1551 | engines: {node: '>= 6'} 1552 | dev: true 1553 | 1554 | /caniuse-lite@1.0.30001639: 1555 | resolution: {integrity: sha512-eFHflNTBIlFwP2AIKaYuBQN/apnUoKNhBdza8ZnW/h2di4LCZ4xFqYlxUxo+LQ76KFI1PGcC1QDxMbxTZpSCAg==} 1556 | dev: false 1557 | 1558 | /canvas-size@1.2.6: 1559 | resolution: {integrity: sha512-x2iVHOrZ5x9V0Hwx6kBz+Yxf/VCAII+jrD6WLjJbytJLozHq/oDJjEva432Os0eHxWMFR0vYlLJwTr6QxyxQqw==} 1560 | dev: false 1561 | 1562 | /chokidar@3.6.0: 1563 | resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} 1564 | engines: {node: '>= 8.10.0'} 1565 | dependencies: 1566 | anymatch: 3.1.3 1567 | braces: 3.0.3 1568 | glob-parent: 5.1.2 1569 | is-binary-path: 2.1.0 1570 | is-glob: 4.0.3 1571 | normalize-path: 3.0.0 1572 | readdirp: 3.6.0 1573 | optionalDependencies: 1574 | fsevents: 2.3.3 1575 | dev: true 1576 | 1577 | /classnames@2.5.1: 1578 | resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} 1579 | dev: false 1580 | 1581 | /client-only@0.0.1: 1582 | resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} 1583 | dev: false 1584 | 1585 | /color-convert@2.0.1: 1586 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 1587 | engines: {node: '>=7.0.0'} 1588 | dependencies: 1589 | color-name: 1.1.4 1590 | dev: true 1591 | 1592 | /color-name@1.1.4: 1593 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 1594 | dev: true 1595 | 1596 | /commander@4.1.1: 1597 | resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} 1598 | engines: {node: '>= 6'} 1599 | dev: true 1600 | 1601 | /core-js@3.37.1: 1602 | resolution: {integrity: sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==} 1603 | requiresBuild: true 1604 | dev: false 1605 | 1606 | /cross-spawn@7.0.3: 1607 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} 1608 | engines: {node: '>= 8'} 1609 | dependencies: 1610 | path-key: 3.1.1 1611 | shebang-command: 2.0.0 1612 | which: 2.0.2 1613 | dev: true 1614 | 1615 | /cssesc@3.0.0: 1616 | resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} 1617 | engines: {node: '>=4'} 1618 | hasBin: true 1619 | dev: true 1620 | 1621 | /csstype@3.1.3: 1622 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 1623 | 1624 | /detect-node-es@1.1.0: 1625 | resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} 1626 | dev: false 1627 | 1628 | /didyoumean@1.2.2: 1629 | resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} 1630 | dev: true 1631 | 1632 | /dlv@1.1.3: 1633 | resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} 1634 | dev: true 1635 | 1636 | /dotenv@16.4.5: 1637 | resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} 1638 | engines: {node: '>=12'} 1639 | dev: true 1640 | 1641 | /eastasianwidth@0.2.0: 1642 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 1643 | dev: true 1644 | 1645 | /emoji-regex@8.0.0: 1646 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 1647 | dev: true 1648 | 1649 | /emoji-regex@9.2.2: 1650 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 1651 | dev: true 1652 | 1653 | /eventemitter3@4.0.7: 1654 | resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} 1655 | dev: false 1656 | 1657 | /fast-glob@3.3.2: 1658 | resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} 1659 | engines: {node: '>=8.6.0'} 1660 | dependencies: 1661 | '@nodelib/fs.stat': 2.0.5 1662 | '@nodelib/fs.walk': 1.2.8 1663 | glob-parent: 5.1.2 1664 | merge2: 1.4.1 1665 | micromatch: 4.0.7 1666 | dev: true 1667 | 1668 | /fastq@1.17.1: 1669 | resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} 1670 | dependencies: 1671 | reusify: 1.0.4 1672 | dev: true 1673 | 1674 | /fill-range@7.1.1: 1675 | resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 1676 | engines: {node: '>=8'} 1677 | dependencies: 1678 | to-regex-range: 5.0.1 1679 | dev: true 1680 | 1681 | /foreground-child@3.2.1: 1682 | resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} 1683 | engines: {node: '>=14'} 1684 | dependencies: 1685 | cross-spawn: 7.0.3 1686 | signal-exit: 4.1.0 1687 | dev: true 1688 | 1689 | /fsevents@2.3.3: 1690 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 1691 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 1692 | os: [darwin] 1693 | requiresBuild: true 1694 | dev: true 1695 | optional: true 1696 | 1697 | /function-bind@1.1.2: 1698 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 1699 | dev: true 1700 | 1701 | /get-nonce@1.0.1: 1702 | resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} 1703 | engines: {node: '>=6'} 1704 | dev: false 1705 | 1706 | /glob-parent@5.1.2: 1707 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 1708 | engines: {node: '>= 6'} 1709 | dependencies: 1710 | is-glob: 4.0.3 1711 | dev: true 1712 | 1713 | /glob-parent@6.0.2: 1714 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 1715 | engines: {node: '>=10.13.0'} 1716 | dependencies: 1717 | is-glob: 4.0.3 1718 | dev: true 1719 | 1720 | /glob@10.4.2: 1721 | resolution: {integrity: sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==} 1722 | engines: {node: '>=16 || 14 >=14.18'} 1723 | hasBin: true 1724 | dependencies: 1725 | foreground-child: 3.2.1 1726 | jackspeak: 3.4.0 1727 | minimatch: 9.0.5 1728 | minipass: 7.1.2 1729 | package-json-from-dist: 1.0.0 1730 | path-scurry: 1.11.1 1731 | dev: true 1732 | 1733 | /graceful-fs@4.2.11: 1734 | resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 1735 | dev: false 1736 | 1737 | /hasown@2.0.2: 1738 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 1739 | engines: {node: '>= 0.4'} 1740 | dependencies: 1741 | function-bind: 1.1.2 1742 | dev: true 1743 | 1744 | /hotkeys-js@3.13.7: 1745 | resolution: {integrity: sha512-ygFIdTqqwG4fFP7kkiYlvayZppeIQX2aPpirsngkv1xM1lP0piDY5QEh68nQnIKvz64hfocxhBaD/uK3sSK1yQ==} 1746 | dev: false 1747 | 1748 | /idb@7.1.1: 1749 | resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} 1750 | dev: false 1751 | 1752 | /invariant@2.2.4: 1753 | resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} 1754 | dependencies: 1755 | loose-envify: 1.4.0 1756 | dev: false 1757 | 1758 | /is-binary-path@2.1.0: 1759 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 1760 | engines: {node: '>=8'} 1761 | dependencies: 1762 | binary-extensions: 2.3.0 1763 | dev: true 1764 | 1765 | /is-core-module@2.14.0: 1766 | resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} 1767 | engines: {node: '>= 0.4'} 1768 | dependencies: 1769 | hasown: 2.0.2 1770 | dev: true 1771 | 1772 | /is-extglob@2.1.1: 1773 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 1774 | engines: {node: '>=0.10.0'} 1775 | dev: true 1776 | 1777 | /is-fullwidth-code-point@3.0.0: 1778 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 1779 | engines: {node: '>=8'} 1780 | dev: true 1781 | 1782 | /is-glob@4.0.3: 1783 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 1784 | engines: {node: '>=0.10.0'} 1785 | dependencies: 1786 | is-extglob: 2.1.1 1787 | dev: true 1788 | 1789 | /is-number@7.0.0: 1790 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 1791 | engines: {node: '>=0.12.0'} 1792 | dev: true 1793 | 1794 | /is-plain-object@5.0.0: 1795 | resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} 1796 | engines: {node: '>=0.10.0'} 1797 | dev: false 1798 | 1799 | /isexe@2.0.0: 1800 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 1801 | dev: true 1802 | 1803 | /jackspeak@3.4.0: 1804 | resolution: {integrity: sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==} 1805 | engines: {node: '>=14'} 1806 | dependencies: 1807 | '@isaacs/cliui': 8.0.2 1808 | optionalDependencies: 1809 | '@pkgjs/parseargs': 0.11.0 1810 | dev: true 1811 | 1812 | /jiti@1.21.6: 1813 | resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} 1814 | hasBin: true 1815 | dev: true 1816 | 1817 | /js-tokens@4.0.0: 1818 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 1819 | dev: false 1820 | 1821 | /lilconfig@2.1.0: 1822 | resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} 1823 | engines: {node: '>=10'} 1824 | dev: true 1825 | 1826 | /lilconfig@3.1.2: 1827 | resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} 1828 | engines: {node: '>=14'} 1829 | dev: true 1830 | 1831 | /lines-and-columns@1.2.4: 1832 | resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} 1833 | dev: true 1834 | 1835 | /lodash.isequal@4.5.0: 1836 | resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} 1837 | dev: false 1838 | 1839 | /lodash.throttle@4.1.1: 1840 | resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} 1841 | dev: false 1842 | 1843 | /lodash.uniq@4.5.0: 1844 | resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} 1845 | dev: false 1846 | 1847 | /lodash@4.17.21: 1848 | resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} 1849 | dev: false 1850 | 1851 | /loose-envify@1.4.0: 1852 | resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} 1853 | hasBin: true 1854 | dependencies: 1855 | js-tokens: 4.0.0 1856 | dev: false 1857 | 1858 | /lru-cache@10.3.0: 1859 | resolution: {integrity: sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==} 1860 | engines: {node: 14 || >=16.14} 1861 | dev: true 1862 | 1863 | /lz-string@1.5.0: 1864 | resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} 1865 | hasBin: true 1866 | dev: false 1867 | 1868 | /merge2@1.4.1: 1869 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 1870 | engines: {node: '>= 8'} 1871 | dev: true 1872 | 1873 | /micromatch@4.0.7: 1874 | resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} 1875 | engines: {node: '>=8.6'} 1876 | dependencies: 1877 | braces: 3.0.3 1878 | picomatch: 2.3.1 1879 | dev: true 1880 | 1881 | /minimatch@9.0.5: 1882 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 1883 | engines: {node: '>=16 || 14 >=14.17'} 1884 | dependencies: 1885 | brace-expansion: 2.0.1 1886 | dev: true 1887 | 1888 | /minipass@7.1.2: 1889 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 1890 | engines: {node: '>=16 || 14 >=14.17'} 1891 | dev: true 1892 | 1893 | /mutative@1.0.10: 1894 | resolution: {integrity: sha512-YD4MEleW9vJyIVEItz6x71fWdJ9qea3+R8hxtNiei8cQ7BGLM1c9Q6zCJ7SuLzorBhd2PBHN+xUEwEahxMI34w==} 1895 | engines: {node: '>=14.0'} 1896 | dev: false 1897 | 1898 | /mz@2.7.0: 1899 | resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} 1900 | dependencies: 1901 | any-promise: 1.3.0 1902 | object-assign: 4.1.1 1903 | thenify-all: 1.6.0 1904 | dev: true 1905 | 1906 | /nanoid@3.3.7: 1907 | resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} 1908 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 1909 | hasBin: true 1910 | 1911 | /nanoid@4.0.2: 1912 | resolution: {integrity: sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==} 1913 | engines: {node: ^14 || ^16 || >=18} 1914 | hasBin: true 1915 | dev: false 1916 | 1917 | /next@14.2.4(react-dom@18.3.1)(react@18.3.1): 1918 | resolution: {integrity: sha512-R8/V7vugY+822rsQGQCjoLhMuC9oFj9SOi4Cl4b2wjDrseD0LRZ10W7R6Czo4w9ZznVSshKjuIomsRjvm9EKJQ==} 1919 | engines: {node: '>=18.17.0'} 1920 | hasBin: true 1921 | peerDependencies: 1922 | '@opentelemetry/api': ^1.1.0 1923 | '@playwright/test': ^1.41.2 1924 | react: ^18.2.0 1925 | react-dom: ^18.2.0 1926 | sass: ^1.3.0 1927 | peerDependenciesMeta: 1928 | '@opentelemetry/api': 1929 | optional: true 1930 | '@playwright/test': 1931 | optional: true 1932 | sass: 1933 | optional: true 1934 | dependencies: 1935 | '@next/env': 14.2.4 1936 | '@swc/helpers': 0.5.5 1937 | busboy: 1.6.0 1938 | caniuse-lite: 1.0.30001639 1939 | graceful-fs: 4.2.11 1940 | postcss: 8.4.31 1941 | react: 18.3.1 1942 | react-dom: 18.3.1(react@18.3.1) 1943 | styled-jsx: 5.1.1(react@18.3.1) 1944 | optionalDependencies: 1945 | '@next/swc-darwin-arm64': 14.2.4 1946 | '@next/swc-darwin-x64': 14.2.4 1947 | '@next/swc-linux-arm64-gnu': 14.2.4 1948 | '@next/swc-linux-arm64-musl': 14.2.4 1949 | '@next/swc-linux-x64-gnu': 14.2.4 1950 | '@next/swc-linux-x64-musl': 14.2.4 1951 | '@next/swc-win32-arm64-msvc': 14.2.4 1952 | '@next/swc-win32-ia32-msvc': 14.2.4 1953 | '@next/swc-win32-x64-msvc': 14.2.4 1954 | transitivePeerDependencies: 1955 | - '@babel/core' 1956 | - babel-plugin-macros 1957 | dev: false 1958 | 1959 | /normalize-path@3.0.0: 1960 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 1961 | engines: {node: '>=0.10.0'} 1962 | dev: true 1963 | 1964 | /object-assign@4.1.1: 1965 | resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} 1966 | engines: {node: '>=0.10.0'} 1967 | dev: true 1968 | 1969 | /object-hash@3.0.0: 1970 | resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} 1971 | engines: {node: '>= 6'} 1972 | dev: true 1973 | 1974 | /package-json-from-dist@1.0.0: 1975 | resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} 1976 | dev: true 1977 | 1978 | /path-key@3.1.1: 1979 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 1980 | engines: {node: '>=8'} 1981 | dev: true 1982 | 1983 | /path-parse@1.0.7: 1984 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 1985 | dev: true 1986 | 1987 | /path-scurry@1.11.1: 1988 | resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} 1989 | engines: {node: '>=16 || 14 >=14.18'} 1990 | dependencies: 1991 | lru-cache: 10.3.0 1992 | minipass: 7.1.2 1993 | dev: true 1994 | 1995 | /picocolors@1.0.1: 1996 | resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} 1997 | 1998 | /picomatch@2.3.1: 1999 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 2000 | engines: {node: '>=8.6'} 2001 | dev: true 2002 | 2003 | /pify@2.3.0: 2004 | resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} 2005 | engines: {node: '>=0.10.0'} 2006 | dev: true 2007 | 2008 | /pirates@4.0.6: 2009 | resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} 2010 | engines: {node: '>= 6'} 2011 | dev: true 2012 | 2013 | /postcss-import@15.1.0(postcss@8.4.39): 2014 | resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} 2015 | engines: {node: '>=14.0.0'} 2016 | peerDependencies: 2017 | postcss: ^8.0.0 2018 | dependencies: 2019 | postcss: 8.4.39 2020 | postcss-value-parser: 4.2.0 2021 | read-cache: 1.0.0 2022 | resolve: 1.22.8 2023 | dev: true 2024 | 2025 | /postcss-js@4.0.1(postcss@8.4.39): 2026 | resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} 2027 | engines: {node: ^12 || ^14 || >= 16} 2028 | peerDependencies: 2029 | postcss: ^8.4.21 2030 | dependencies: 2031 | camelcase-css: 2.0.1 2032 | postcss: 8.4.39 2033 | dev: true 2034 | 2035 | /postcss-load-config@4.0.2(postcss@8.4.39): 2036 | resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} 2037 | engines: {node: '>= 14'} 2038 | peerDependencies: 2039 | postcss: '>=8.0.9' 2040 | ts-node: '>=9.0.0' 2041 | peerDependenciesMeta: 2042 | postcss: 2043 | optional: true 2044 | ts-node: 2045 | optional: true 2046 | dependencies: 2047 | lilconfig: 3.1.2 2048 | postcss: 8.4.39 2049 | yaml: 2.4.5 2050 | dev: true 2051 | 2052 | /postcss-nested@6.0.1(postcss@8.4.39): 2053 | resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} 2054 | engines: {node: '>=12.0'} 2055 | peerDependencies: 2056 | postcss: ^8.2.14 2057 | dependencies: 2058 | postcss: 8.4.39 2059 | postcss-selector-parser: 6.1.0 2060 | dev: true 2061 | 2062 | /postcss-selector-parser@6.1.0: 2063 | resolution: {integrity: sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==} 2064 | engines: {node: '>=4'} 2065 | dependencies: 2066 | cssesc: 3.0.0 2067 | util-deprecate: 1.0.2 2068 | dev: true 2069 | 2070 | /postcss-value-parser@4.2.0: 2071 | resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} 2072 | dev: true 2073 | 2074 | /postcss@8.4.31: 2075 | resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} 2076 | engines: {node: ^10 || ^12 || >=14} 2077 | dependencies: 2078 | nanoid: 3.3.7 2079 | picocolors: 1.0.1 2080 | source-map-js: 1.2.0 2081 | dev: false 2082 | 2083 | /postcss@8.4.39: 2084 | resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} 2085 | engines: {node: ^10 || ^12 || >=14} 2086 | dependencies: 2087 | nanoid: 3.3.7 2088 | picocolors: 1.0.1 2089 | source-map-js: 1.2.0 2090 | dev: true 2091 | 2092 | /queue-microtask@1.2.3: 2093 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 2094 | dev: true 2095 | 2096 | /react-dom@18.3.1(react@18.3.1): 2097 | resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} 2098 | peerDependencies: 2099 | react: ^18.3.1 2100 | dependencies: 2101 | loose-envify: 1.4.0 2102 | react: 18.3.1 2103 | scheduler: 0.23.2 2104 | dev: false 2105 | 2106 | /react-remove-scroll-bar@2.3.6(@types/react@18.3.3)(react@18.3.1): 2107 | resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==} 2108 | engines: {node: '>=10'} 2109 | peerDependencies: 2110 | '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 2111 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 2112 | peerDependenciesMeta: 2113 | '@types/react': 2114 | optional: true 2115 | dependencies: 2116 | '@types/react': 18.3.3 2117 | react: 18.3.1 2118 | react-style-singleton: 2.2.1(@types/react@18.3.3)(react@18.3.1) 2119 | tslib: 2.6.3 2120 | dev: false 2121 | 2122 | /react-remove-scroll@2.5.5(@types/react@18.3.3)(react@18.3.1): 2123 | resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==} 2124 | engines: {node: '>=10'} 2125 | peerDependencies: 2126 | '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 2127 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 2128 | peerDependenciesMeta: 2129 | '@types/react': 2130 | optional: true 2131 | dependencies: 2132 | '@types/react': 18.3.3 2133 | react: 18.3.1 2134 | react-remove-scroll-bar: 2.3.6(@types/react@18.3.3)(react@18.3.1) 2135 | react-style-singleton: 2.2.1(@types/react@18.3.3)(react@18.3.1) 2136 | tslib: 2.6.3 2137 | use-callback-ref: 1.3.2(@types/react@18.3.3)(react@18.3.1) 2138 | use-sidecar: 1.1.2(@types/react@18.3.3)(react@18.3.1) 2139 | dev: false 2140 | 2141 | /react-remove-scroll@2.5.7(@types/react@18.3.3)(react@18.3.1): 2142 | resolution: {integrity: sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==} 2143 | engines: {node: '>=10'} 2144 | peerDependencies: 2145 | '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 2146 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 2147 | peerDependenciesMeta: 2148 | '@types/react': 2149 | optional: true 2150 | dependencies: 2151 | '@types/react': 18.3.3 2152 | react: 18.3.1 2153 | react-remove-scroll-bar: 2.3.6(@types/react@18.3.3)(react@18.3.1) 2154 | react-style-singleton: 2.2.1(@types/react@18.3.3)(react@18.3.1) 2155 | tslib: 2.6.3 2156 | use-callback-ref: 1.3.2(@types/react@18.3.3)(react@18.3.1) 2157 | use-sidecar: 1.1.2(@types/react@18.3.3)(react@18.3.1) 2158 | dev: false 2159 | 2160 | /react-style-singleton@2.2.1(@types/react@18.3.3)(react@18.3.1): 2161 | resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} 2162 | engines: {node: '>=10'} 2163 | peerDependencies: 2164 | '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 2165 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 2166 | peerDependenciesMeta: 2167 | '@types/react': 2168 | optional: true 2169 | dependencies: 2170 | '@types/react': 18.3.3 2171 | get-nonce: 1.0.1 2172 | invariant: 2.2.4 2173 | react: 18.3.1 2174 | tslib: 2.6.3 2175 | dev: false 2176 | 2177 | /react@18.3.1: 2178 | resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} 2179 | engines: {node: '>=0.10.0'} 2180 | dependencies: 2181 | loose-envify: 1.4.0 2182 | dev: false 2183 | 2184 | /read-cache@1.0.0: 2185 | resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} 2186 | dependencies: 2187 | pify: 2.3.0 2188 | dev: true 2189 | 2190 | /readdirp@3.6.0: 2191 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 2192 | engines: {node: '>=8.10.0'} 2193 | dependencies: 2194 | picomatch: 2.3.1 2195 | dev: true 2196 | 2197 | /regenerator-runtime@0.14.1: 2198 | resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} 2199 | dev: false 2200 | 2201 | /resolve@1.22.8: 2202 | resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} 2203 | hasBin: true 2204 | dependencies: 2205 | is-core-module: 2.14.0 2206 | path-parse: 1.0.7 2207 | supports-preserve-symlinks-flag: 1.0.0 2208 | dev: true 2209 | 2210 | /reusify@1.0.4: 2211 | resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} 2212 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 2213 | dev: true 2214 | 2215 | /run-parallel@1.2.0: 2216 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 2217 | dependencies: 2218 | queue-microtask: 1.2.3 2219 | dev: true 2220 | 2221 | /scheduler@0.23.2: 2222 | resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} 2223 | dependencies: 2224 | loose-envify: 1.4.0 2225 | dev: false 2226 | 2227 | /shebang-command@2.0.0: 2228 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 2229 | engines: {node: '>=8'} 2230 | dependencies: 2231 | shebang-regex: 3.0.0 2232 | dev: true 2233 | 2234 | /shebang-regex@3.0.0: 2235 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 2236 | engines: {node: '>=8'} 2237 | dev: true 2238 | 2239 | /signal-exit@4.1.0: 2240 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 2241 | engines: {node: '>=14'} 2242 | dev: true 2243 | 2244 | /source-map-js@1.2.0: 2245 | resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} 2246 | engines: {node: '>=0.10.0'} 2247 | 2248 | /streamsearch@1.1.0: 2249 | resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} 2250 | engines: {node: '>=10.0.0'} 2251 | dev: false 2252 | 2253 | /string-width@4.2.3: 2254 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 2255 | engines: {node: '>=8'} 2256 | dependencies: 2257 | emoji-regex: 8.0.0 2258 | is-fullwidth-code-point: 3.0.0 2259 | strip-ansi: 6.0.1 2260 | dev: true 2261 | 2262 | /string-width@5.1.2: 2263 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 2264 | engines: {node: '>=12'} 2265 | dependencies: 2266 | eastasianwidth: 0.2.0 2267 | emoji-regex: 9.2.2 2268 | strip-ansi: 7.1.0 2269 | dev: true 2270 | 2271 | /strip-ansi@6.0.1: 2272 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 2273 | engines: {node: '>=8'} 2274 | dependencies: 2275 | ansi-regex: 5.0.1 2276 | dev: true 2277 | 2278 | /strip-ansi@7.1.0: 2279 | resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} 2280 | engines: {node: '>=12'} 2281 | dependencies: 2282 | ansi-regex: 6.0.1 2283 | dev: true 2284 | 2285 | /styled-jsx@5.1.1(react@18.3.1): 2286 | resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} 2287 | engines: {node: '>= 12.0.0'} 2288 | peerDependencies: 2289 | '@babel/core': '*' 2290 | babel-plugin-macros: '*' 2291 | react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' 2292 | peerDependenciesMeta: 2293 | '@babel/core': 2294 | optional: true 2295 | babel-plugin-macros: 2296 | optional: true 2297 | dependencies: 2298 | client-only: 0.0.1 2299 | react: 18.3.1 2300 | dev: false 2301 | 2302 | /sucrase@3.35.0: 2303 | resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} 2304 | engines: {node: '>=16 || 14 >=14.17'} 2305 | hasBin: true 2306 | dependencies: 2307 | '@jridgewell/gen-mapping': 0.3.5 2308 | commander: 4.1.1 2309 | glob: 10.4.2 2310 | lines-and-columns: 1.2.4 2311 | mz: 2.7.0 2312 | pirates: 4.0.6 2313 | ts-interface-checker: 0.1.13 2314 | dev: true 2315 | 2316 | /supports-preserve-symlinks-flag@1.0.0: 2317 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 2318 | engines: {node: '>= 0.4'} 2319 | dev: true 2320 | 2321 | /tailwindcss@3.4.4: 2322 | resolution: {integrity: sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==} 2323 | engines: {node: '>=14.0.0'} 2324 | hasBin: true 2325 | dependencies: 2326 | '@alloc/quick-lru': 5.2.0 2327 | arg: 5.0.2 2328 | chokidar: 3.6.0 2329 | didyoumean: 1.2.2 2330 | dlv: 1.1.3 2331 | fast-glob: 3.3.2 2332 | glob-parent: 6.0.2 2333 | is-glob: 4.0.3 2334 | jiti: 1.21.6 2335 | lilconfig: 2.1.0 2336 | micromatch: 4.0.7 2337 | normalize-path: 3.0.0 2338 | object-hash: 3.0.0 2339 | picocolors: 1.0.1 2340 | postcss: 8.4.39 2341 | postcss-import: 15.1.0(postcss@8.4.39) 2342 | postcss-js: 4.0.1(postcss@8.4.39) 2343 | postcss-load-config: 4.0.2(postcss@8.4.39) 2344 | postcss-nested: 6.0.1(postcss@8.4.39) 2345 | postcss-selector-parser: 6.1.0 2346 | resolve: 1.22.8 2347 | sucrase: 3.35.0 2348 | transitivePeerDependencies: 2349 | - ts-node 2350 | dev: true 2351 | 2352 | /thenify-all@1.6.0: 2353 | resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} 2354 | engines: {node: '>=0.8'} 2355 | dependencies: 2356 | thenify: 3.3.1 2357 | dev: true 2358 | 2359 | /thenify@3.3.1: 2360 | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} 2361 | dependencies: 2362 | any-promise: 1.3.0 2363 | dev: true 2364 | 2365 | /tldraw@2.2.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): 2366 | resolution: {integrity: sha512-6BQdc2Rae1R8fCIF+q3OYqUMcqwLgTGbzYLXdMaKTmf+Rv/dsR3iO6p/Jq6Er6Cok4hE9RxIkyr/qilaYFBG4w==} 2367 | peerDependencies: 2368 | react: ^18 2369 | react-dom: ^18 2370 | dependencies: 2371 | '@radix-ui/react-alert-dialog': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 2372 | '@radix-ui/react-context-menu': 2.2.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 2373 | '@radix-ui/react-dialog': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 2374 | '@radix-ui/react-dropdown-menu': 2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 2375 | '@radix-ui/react-popover': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 2376 | '@radix-ui/react-select': 1.2.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 2377 | '@radix-ui/react-slider': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 2378 | '@radix-ui/react-toast': 1.2.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) 2379 | '@tldraw/editor': 2.2.1(react-dom@18.3.1)(react@18.3.1) 2380 | '@tldraw/store': 2.2.1(react@18.3.1) 2381 | canvas-size: 1.2.6 2382 | classnames: 2.5.1 2383 | hotkeys-js: 3.13.7 2384 | lz-string: 1.5.0 2385 | react: 18.3.1 2386 | react-dom: 18.3.1(react@18.3.1) 2387 | transitivePeerDependencies: 2388 | - '@types/react' 2389 | - '@types/react-dom' 2390 | dev: false 2391 | 2392 | /to-regex-range@5.0.1: 2393 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 2394 | engines: {node: '>=8.0'} 2395 | dependencies: 2396 | is-number: 7.0.0 2397 | dev: true 2398 | 2399 | /ts-interface-checker@0.1.13: 2400 | resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} 2401 | dev: true 2402 | 2403 | /tslib@2.6.3: 2404 | resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} 2405 | dev: false 2406 | 2407 | /typescript@5.5.3: 2408 | resolution: {integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==} 2409 | engines: {node: '>=14.17'} 2410 | hasBin: true 2411 | dev: true 2412 | 2413 | /undici-types@5.26.5: 2414 | resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} 2415 | dev: true 2416 | 2417 | /use-callback-ref@1.3.2(@types/react@18.3.3)(react@18.3.1): 2418 | resolution: {integrity: sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==} 2419 | engines: {node: '>=10'} 2420 | peerDependencies: 2421 | '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 2422 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 2423 | peerDependenciesMeta: 2424 | '@types/react': 2425 | optional: true 2426 | dependencies: 2427 | '@types/react': 18.3.3 2428 | react: 18.3.1 2429 | tslib: 2.6.3 2430 | dev: false 2431 | 2432 | /use-sidecar@1.1.2(@types/react@18.3.3)(react@18.3.1): 2433 | resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} 2434 | engines: {node: '>=10'} 2435 | peerDependencies: 2436 | '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0 2437 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 2438 | peerDependenciesMeta: 2439 | '@types/react': 2440 | optional: true 2441 | dependencies: 2442 | '@types/react': 18.3.3 2443 | detect-node-es: 1.1.0 2444 | react: 18.3.1 2445 | tslib: 2.6.3 2446 | dev: false 2447 | 2448 | /util-deprecate@1.0.2: 2449 | resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} 2450 | dev: true 2451 | 2452 | /uuid@9.0.1: 2453 | resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} 2454 | hasBin: true 2455 | dev: false 2456 | 2457 | /which@2.0.2: 2458 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 2459 | engines: {node: '>= 8'} 2460 | hasBin: true 2461 | dependencies: 2462 | isexe: 2.0.0 2463 | dev: true 2464 | 2465 | /wrap-ansi@7.0.0: 2466 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 2467 | engines: {node: '>=10'} 2468 | dependencies: 2469 | ansi-styles: 4.3.0 2470 | string-width: 4.2.3 2471 | strip-ansi: 6.0.1 2472 | dev: true 2473 | 2474 | /wrap-ansi@8.1.0: 2475 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} 2476 | engines: {node: '>=12'} 2477 | dependencies: 2478 | ansi-styles: 6.2.1 2479 | string-width: 5.1.2 2480 | strip-ansi: 7.1.0 2481 | dev: true 2482 | 2483 | /yaml@2.4.5: 2484 | resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} 2485 | engines: {node: '>= 14'} 2486 | hasBin: true 2487 | dev: true 2488 | -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsventures/instldraw/bb76163b13858dc359a3a458cec7b43bfaa4fffb/public/favicon.ico -------------------------------------------------------------------------------- /resources/demo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsventures/instldraw/bb76163b13858dc359a3a458cec7b43bfaa4fffb/resources/demo.mp4 -------------------------------------------------------------------------------- /resources/instant-perms.json: -------------------------------------------------------------------------------- 1 | { 2 | "teams": { 3 | "bind": [ 4 | "isCreator", 5 | "auth.id == data.creatorId", 6 | "isMember", 7 | "auth.id in data.ref('memberships.userId')" 8 | ], 9 | "allow": { 10 | "view": "isMember", 11 | "create": "isCreator", 12 | "delete": "isCreator", 13 | "update": "isCreator" 14 | } 15 | }, 16 | "invites": { 17 | "bind": [ 18 | "isMember", 19 | "auth.id in data.ref('team.memberships.userId')", 20 | "isInvitee", 21 | "auth.email == data.userEmail" 22 | ], 23 | "allow": { 24 | "view": "isInvitee", 25 | "create": "isMember", 26 | "delete": "isMember", 27 | "update": "false" 28 | } 29 | }, 30 | "drawings": { 31 | "bind": ["isMember", "auth.id in data.ref('team.memberships.userId')"], 32 | "allow": { 33 | "view": "isMember", 34 | "create": "isMember", 35 | "delete": "isMember", 36 | "update": "isMember" 37 | } 38 | }, 39 | "memberships": { 40 | "bind": [ 41 | "isMember", 42 | "auth.id in data.ref('team.memberships.userId')", 43 | "isInviteeOrCreator", 44 | "size(data.ref('team.invites.id')) == 0 ? auth.id in data.ref('team.creatorId') : auth.email in data.ref('team.invites.userEmail')", 45 | "isUser", 46 | "auth.id == data.userId" 47 | ], 48 | "allow": { 49 | "view": "isMember", 50 | "create": "isInviteeOrCreator", 51 | "delete": "isUser", 52 | "update": "false" 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/__dev.tsx: -------------------------------------------------------------------------------- 1 | import { isBrowser, isDev } from "@/config"; 2 | import clientDB from "./lib/clientDB"; 3 | 4 | // auth dev utils 5 | 6 | async function getUser() { 7 | return (await clientDB._core._reactor.getCurrentUser())?.user; 8 | } 9 | 10 | // dev-only! 11 | if (isBrowser && isDev) { 12 | const g = globalThis as any; 13 | 14 | g.$db = clientDB; 15 | g.$u = getUser; 16 | } 17 | -------------------------------------------------------------------------------- /src/components/InstantAuth.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import clientDB from "@/lib/clientDB"; 3 | 4 | // Instant auth copypasta https://www.instantdb.com/docs/auth#magic-codes 5 | 6 | export function InstantAuth() { 7 | const [sentEmail, setSentEmail] = useState(""); 8 | 9 | return ( 10 |
11 |
12 | {!sentEmail ? ( 13 | 14 | ) : ( 15 | 16 | )} 17 |
18 |
19 | ); 20 | } 21 | 22 | function EmailStep({ onSendEmail }: { onSendEmail: (email: string) => void }) { 23 | const inputRef = React.useRef(null); 24 | const handleSubmit = (e: React.FormEvent) => { 25 | e.preventDefault(); 26 | const inputEl = inputRef.current!; 27 | const email = inputEl.value; 28 | onSendEmail(email); 29 | clientDB.auth.sendMagicCode({ email }).catch((err) => { 30 | alert("Uh oh :" + err.body?.message); 31 | onSendEmail(""); 32 | }); 33 | }; 34 | return ( 35 |
40 |

Let's log you in

41 |

42 | Enter your email, and we'll send you a verification code. We'll create 43 | an account for you too if you don't already have one. 44 |

45 | 53 | 59 |
60 | ); 61 | } 62 | 63 | function CodeStep({ sentEmail }: { sentEmail: string }) { 64 | const inputRef = React.useRef(null); 65 | const handleSubmit = (e: React.FormEvent) => { 66 | e.preventDefault(); 67 | const inputEl = inputRef.current!; 68 | const code = inputEl.value; 69 | clientDB.auth 70 | .signInWithMagicCode({ email: sentEmail, code }) 71 | .catch((err) => { 72 | inputEl.value = ""; 73 | alert("Uh oh :" + err.body?.message); 74 | }); 75 | }; 76 | 77 | return ( 78 |
83 |

Enter your code

84 |

85 | We sent an email to {sentEmail}. Check your email, and 86 | paste the code you see. 87 |

88 | 96 | 102 |
103 | ); 104 | } 105 | -------------------------------------------------------------------------------- /src/components/UI.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | export function useDialog(open?: boolean) { 4 | const [isOpen, setIsOpen] = useState(open ?? false); 5 | 6 | return { 7 | isOpen, 8 | open: () => setIsOpen(true), 9 | close: () => setIsOpen(false), 10 | }; 11 | } 12 | 13 | export function PromptDialog({ 14 | title, 15 | onClose, 16 | onSubmit, 17 | }: { 18 | title: string; 19 | onClose: () => void; 20 | onSubmit: (v: string) => void; 21 | }) { 22 | const [value, setValue] = useState(""); 23 | 24 | return ( 25 |
{ 28 | if (e.target === e.currentTarget) onClose(); 29 | }} 30 | > 31 |
{ 34 | e.preventDefault(); 35 | onSubmit(value); 36 | onClose(); 37 | }} 38 | > 39 |

{title}

40 | setValue(e.target.value)} 45 | className="border px-3 py-0.5" 46 | /> 47 |
48 | 54 | 63 |
64 |
65 |
66 | ); 67 | } 68 | -------------------------------------------------------------------------------- /src/config.tsx: -------------------------------------------------------------------------------- 1 | import { uniqueId } from "tldraw"; 2 | 3 | export const isDev = process.env.NODE_ENV === "development"; 4 | export const isBrowser = typeof window != "undefined"; 5 | 6 | export const localSourceId = uniqueId(); 7 | 8 | export const colorNames = [ 9 | "magenta", 10 | "red", 11 | "green", 12 | "blue", 13 | "yellow", 14 | "purple", 15 | ]; 16 | -------------------------------------------------------------------------------- /src/instant.perms.ts: -------------------------------------------------------------------------------- 1 | import { InstantRules } from "@instantdb/react"; 2 | 3 | const rules = { 4 | teams: { 5 | bind: [ 6 | "isCreator", 7 | "auth.id == data.creatorId", 8 | "isMember", 9 | "auth.id in data.ref('memberships.userId')", 10 | ], 11 | allow: { 12 | view: "isMember", 13 | create: "isCreator", 14 | delete: "isCreator", 15 | update: "isCreator", 16 | }, 17 | }, 18 | invites: { 19 | bind: [ 20 | "isMember", 21 | "auth.id in data.ref('teams.memberships.userId')", 22 | "isInvitee", 23 | "auth.email == data.userEmail", 24 | ], 25 | allow: { 26 | view: "isInvitee", 27 | create: "isMember", 28 | delete: "isMember", 29 | update: "false", 30 | }, 31 | }, 32 | drawings: { 33 | bind: ["isMember", "auth.id in data.ref('teams.memberships.userId')"], 34 | allow: { 35 | view: "isMember", 36 | create: "isMember", 37 | delete: "isMember", 38 | update: "isMember", 39 | }, 40 | }, 41 | memberships: { 42 | bind: [ 43 | "isMember", 44 | "auth.id in data.ref('teams.memberships.userId')", 45 | "isInviteeOrCreator", 46 | "size(data.ref('teams.invites.id')) == 0 ? auth.id in data.ref('teams.creatorId') : auth.email in data.ref('teams.invites.userEmail')", 47 | "isUser", 48 | "auth.id == data.userId", 49 | ], 50 | allow: { 51 | view: "isMember", 52 | create: "isInviteeOrCreator", 53 | delete: "isUser", 54 | update: "false", 55 | }, 56 | }, 57 | } satisfies InstantRules; 58 | 59 | export default rules; 60 | -------------------------------------------------------------------------------- /src/instant.schema.ts: -------------------------------------------------------------------------------- 1 | import { i } from "@instantdb/react"; 2 | import { TLInstancePresence } from "tldraw"; 3 | 4 | const schema = i.schema({ 5 | entities: { 6 | drawings: i.entity({ 7 | name: i.string(), 8 | state: i.json(), 9 | }), 10 | invites: i.entity({ 11 | teamId: i.string(), 12 | teamName: i.string(), 13 | userEmail: i.string(), 14 | }), 15 | memberships: i.entity({ 16 | teamId: i.string(), 17 | userEmail: i.string(), 18 | userId: i.string(), 19 | }), 20 | teams: i.entity({ 21 | creatorId: i.string(), 22 | name: i.string(), 23 | }), 24 | }, 25 | links: { 26 | drawingsTeams: { 27 | forward: { 28 | on: "drawings", 29 | has: "one", 30 | label: "teams", 31 | }, 32 | reverse: { 33 | on: "teams", 34 | has: "many", 35 | label: "drawings", 36 | }, 37 | }, 38 | invitesTeams: { 39 | forward: { 40 | on: "invites", 41 | has: "one", 42 | label: "teams", 43 | }, 44 | reverse: { 45 | on: "teams", 46 | has: "many", 47 | label: "invites", 48 | }, 49 | }, 50 | membershipsTeams: { 51 | forward: { 52 | on: "memberships", 53 | has: "one", 54 | label: "teams", 55 | }, 56 | reverse: { 57 | on: "teams", 58 | has: "many", 59 | label: "memberships", 60 | }, 61 | }, 62 | }, 63 | rooms: { 64 | drawings: { 65 | presence: i.entity({ 66 | tldraw: i.json(), 67 | }), 68 | }, 69 | }, 70 | }); 71 | 72 | export default schema; 73 | -------------------------------------------------------------------------------- /src/lib/clientDB.tsx: -------------------------------------------------------------------------------- 1 | import schema from "@/instant.schema"; 2 | import { init } from "@instantdb/react"; 3 | 4 | const appId = process.env.NEXT_PUBLIC_INSTANT_APP_ID!; 5 | 6 | const clientDB = init({ 7 | appId, 8 | schema, 9 | }); 10 | 11 | export default clientDB; 12 | -------------------------------------------------------------------------------- /src/lib/useInstantPresence.tsx: -------------------------------------------------------------------------------- 1 | import clientDB from "@/lib/clientDB"; 2 | 3 | import { useEffect, useRef } from "react"; 4 | import { 5 | atom, 6 | createPresenceStateDerivation, 7 | react, 8 | TLInstancePresence, 9 | TLInstancePresenceID, 10 | Editor, 11 | } from "tldraw"; 12 | 13 | export function useInstantPresence({ 14 | editor, 15 | drawingId, 16 | user, 17 | }: { 18 | editor: Editor; 19 | drawingId: string; 20 | user: { id: string; color: string; name: string }; 21 | }) { 22 | const room = clientDB.room("drawings", drawingId); 23 | const presence = clientDB.rooms.usePresence(room); 24 | const prevPeersRef = useRef>( 25 | {} 26 | ); 27 | 28 | useEffect(() => { 29 | if (presence.isLoading) return; 30 | const prevPeers = prevPeersRef.current; 31 | 32 | const peers = Object.entries(presence.peers).filter(([k, p]) => p.tldraw); 33 | 34 | const updates = peers.map(([k, p]) => p.tldraw); 35 | const removals = Object.entries(prevPeers) 36 | .filter(([k, v]) => !presence.peers[k] && v.tldraw) 37 | .map(([k, v]) => v.tldraw.id) as TLInstancePresenceID[]; 38 | 39 | if (updates) editor.store.put(updates); 40 | if (removals) editor.store.remove(removals); 41 | 42 | prevPeersRef.current = presence.peers; 43 | }, [presence.peers]); 44 | 45 | useEffect(() => { 46 | const userAtom = atom<{ id: string; color: string; name: string }>("user", { 47 | ...user, 48 | }); 49 | 50 | const tldrawPresenceSignal = createPresenceStateDerivation(userAtom)( 51 | editor.store 52 | ); 53 | 54 | const stop = react("publish presence", () => { 55 | const userPresence = tldrawPresenceSignal.get(); 56 | if (!userPresence) return; 57 | 58 | presence.publishPresence({ tldraw: userPresence }); 59 | }); 60 | 61 | return stop; 62 | }, [user.id, user.color, user.name]); 63 | } 64 | -------------------------------------------------------------------------------- /src/lib/useInstantStore.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { omitBy, throttle } from "lodash"; 3 | import { 4 | createTLSchema, 5 | loadSnapshot, 6 | HistoryEntry, 7 | TLRecord, 8 | TLStoreWithStatus, 9 | createTLStore, 10 | defaultShapeUtils, 11 | uniqueId, 12 | TLShapeId, 13 | TLStore, 14 | } from "tldraw"; 15 | 16 | import type { DrawingState } from "@/types"; 17 | import clientDB from "./clientDB"; 18 | import { updateDrawingState } from "@/mutators"; 19 | 20 | export function useInstantStore({ 21 | drawingId, 22 | localSourceId, 23 | }: { 24 | drawingId: string | null; 25 | localSourceId: string; 26 | }) { 27 | const [storeWithStatus, setStoreWithStatus] = useState({ 28 | status: "loading", 29 | }); 30 | 31 | useEffect(() => { 32 | if (!drawingId) return; 33 | const _drawingId = drawingId; 34 | 35 | // --- begin: throttling 36 | // We can set a throttle wait time by adding `?x_throttle=100` to the URL 37 | // We default to 200ms 38 | // Setting `x_throttle=0` will bypass throttling 39 | let pendingState: DrawingState = {}; 40 | const sp = new URL(location.href).searchParams; 41 | const throttleWaitMs = sp.has("x_throttle") 42 | ? parseInt(String(sp.get("x_throttle"))) || 0 43 | : 200; 44 | const enqueueSync = throttleWaitMs 45 | ? throttle(runSync, throttleWaitMs, { 46 | leading: true, 47 | trailing: true, 48 | }) 49 | : runSync; 50 | 51 | function sync(state: DrawingState) { 52 | pendingState = { ...pendingState, ...state }; 53 | enqueueSync(); 54 | } 55 | 56 | function runSync() { 57 | updateDrawingState({ drawingId: _drawingId, state: pendingState }); 58 | pendingState = {}; 59 | } 60 | 61 | // --- end: throttling 62 | let lifecycleState: "pending" | "ready" | "closed" = "pending"; 63 | const unsubs: (() => void)[] = []; 64 | const tlStore = createTLStore({ 65 | shapeUtils: [...defaultShapeUtils], 66 | }); 67 | 68 | clientDB._core.subscribeQuery( 69 | { 70 | drawings: { 71 | $: { 72 | where: { 73 | id: drawingId, 74 | }, 75 | }, 76 | }, 77 | }, 78 | (res) => { 79 | const state = 80 | res.data?.drawings?.find((c) => c.id === drawingId)?.state ?? {}; 81 | 82 | if (lifecycleState === "pending") { 83 | initDrawing(state); 84 | } else if (lifecycleState === "ready") { 85 | syncInstantStateToTldrawStore(tlStore, state, localSourceId); 86 | } 87 | } 88 | ); 89 | 90 | return teardown; 91 | 92 | function handleLocalChange(event: HistoryEntry) { 93 | if (event.source !== "user") return; 94 | sync(tldrawEventToStateSlice(event, localSourceId)); 95 | } 96 | 97 | function initDrawing(state: DrawingState) { 98 | unsubs.push( 99 | tlStore.listen(handleLocalChange, { 100 | source: "user", 101 | scope: "document", 102 | }) 103 | ); 104 | 105 | tlStore.mergeRemoteChanges(() => { 106 | loadSnapshot(tlStore, { 107 | document: { 108 | store: omitBy(state, (v) => v === null || v.meta.deleted) as Record< 109 | string, 110 | TLRecord 111 | >, 112 | schema: createTLSchema().serialize(), 113 | }, 114 | }); 115 | }); 116 | 117 | setStoreWithStatus({ 118 | status: "synced-remote", 119 | connectionStatus: "online", 120 | store: tlStore, 121 | }); 122 | 123 | lifecycleState = "ready"; 124 | } 125 | 126 | function teardown() { 127 | setStoreWithStatus({ 128 | status: "not-synced", 129 | store: tlStore, 130 | }); 131 | 132 | unsubs.forEach((u) => u()); 133 | 134 | lifecycleState = "closed"; 135 | } 136 | }, [drawingId]); 137 | 138 | return storeWithStatus; 139 | } 140 | 141 | function tldrawEventToStateSlice( 142 | event: HistoryEntry, 143 | localSourceId: string 144 | ) { 145 | const state: DrawingState = {}; 146 | 147 | const items = [ 148 | ...Object.values(event.changes.added), 149 | ...Object.values(event.changes.updated).map(([_, next]) => next), 150 | ]; 151 | 152 | for (const item of items) { 153 | state[item.id] = { 154 | ...item, 155 | meta: { 156 | source: localSourceId, 157 | version: uniqueId(), 158 | }, 159 | }; 160 | } 161 | 162 | for (const item of Object.values(event.changes.removed)) { 163 | state[item.id] = { 164 | ...item, 165 | meta: { 166 | source: localSourceId, 167 | version: uniqueId(), 168 | deleted: true, 169 | }, 170 | }; 171 | } 172 | 173 | return state; 174 | } 175 | 176 | function syncInstantStateToTldrawStore( 177 | store: TLStore, 178 | state: DrawingState, 179 | localSourceId: string 180 | ) { 181 | // Calling `put` or `remove` on the store would trigger our `handleLocalChange` listener. 182 | // TLDraw offers a handy `mergeRemoteChanges` method to apply changes without triggering listeners, 183 | // allowing us to avoid an infinite loop of syncing changes back and forth. :) 184 | store.mergeRemoteChanges(() => { 185 | const removeIds = Object.values(state) 186 | .filter((e) => e?.meta.deleted && store.has(e.id)) 187 | .map((e) => e!.id); 188 | 189 | const updates = Object.values(state).filter((item) => { 190 | if (!item) return false; 191 | if (item.meta.deleted) return false; 192 | 193 | const tlItem = store.get(item?.id as TLShapeId); 194 | // We add a unique id to each version of an item to avoid updating it with the same data 195 | const diffVersion = tlItem?.meta.version !== item?.meta.version; 196 | // If the item was not created by the local user, update it 197 | const diffSource = item?.meta.source !== localSourceId; 198 | 199 | return diffSource && diffVersion; 200 | }); 201 | 202 | if (updates.length) { 203 | store.put(updates as TLRecord[]); 204 | } 205 | 206 | if (removeIds.length) { 207 | store.remove(removeIds as TLShapeId[]); 208 | } 209 | }); 210 | } 211 | -------------------------------------------------------------------------------- /src/mutators.ts: -------------------------------------------------------------------------------- 1 | import type { DrawingState } from "@/types"; 2 | import { id } from "@instantdb/react"; 3 | import clientDB from "@/lib/clientDB"; 4 | 5 | export async function createDrawingForTeam({ 6 | teamId, 7 | drawingName, 8 | }: { 9 | teamId: string; 10 | drawingName: string; 11 | }) { 12 | const drawingId = id(); 13 | 14 | const result = await clientDB.transact([ 15 | clientDB.tx.drawings[drawingId].merge({ name: drawingName ?? "Untitled" }), 16 | clientDB.tx.drawings[drawingId].link({ teams: teamId }), 17 | clientDB.tx.teams[teamId].link({ drawings: drawingId }), 18 | ]); 19 | 20 | return { result, vars: { drawingId } }; 21 | } 22 | 23 | export async function createTeamWithMember({ 24 | teamName, 25 | userEmail, 26 | userId, 27 | }: { 28 | teamName: string; 29 | userEmail: string; 30 | userId: string; 31 | }) { 32 | const teamId = id(); 33 | const membershipId = id(); 34 | 35 | const result = await clientDB.transact([ 36 | clientDB.tx.teams[teamId].update({ name: teamName, creatorId: userId }), 37 | clientDB.tx.memberships[membershipId].update({ teamId, userId, userEmail }), 38 | clientDB.tx.memberships[membershipId].link({ teams: teamId }), 39 | ]); 40 | 41 | return { 42 | result, 43 | vars: { 44 | teamId, 45 | membershipId, 46 | }, 47 | }; 48 | } 49 | 50 | export async function inviteMemberToTeam({ 51 | teamId, 52 | userEmail, 53 | teamName, 54 | }: { 55 | teamId: string; 56 | userEmail: string; 57 | teamName: string; 58 | }) { 59 | const inviteId = id(); 60 | 61 | const result = await clientDB.transact([ 62 | clientDB.tx.invites[inviteId].update({ userEmail, teamId, teamName }), 63 | clientDB.tx.invites[inviteId].link({ teams: teamId }), 64 | ]); 65 | 66 | return { 67 | result, 68 | vars: { inviteId }, 69 | }; 70 | } 71 | 72 | export async function acceptInvite({ 73 | teamId, 74 | userEmail, 75 | userId, 76 | }: { 77 | teamId: string; 78 | userEmail: string; 79 | userId: string; 80 | }) { 81 | const membershipId = id(); 82 | 83 | const result = await clientDB.transact([ 84 | clientDB.tx.memberships[membershipId].update({ teamId, userId, userEmail }), 85 | clientDB.tx.memberships[membershipId].link({ teams: teamId }), 86 | ]); 87 | 88 | return { 89 | result, 90 | }; 91 | } 92 | export async function declineInvite({ inviteId }: { inviteId: string }) { 93 | const result = await clientDB.transact([ 94 | clientDB.tx.invites[inviteId].merge({ status: "declined" }), 95 | ]); 96 | 97 | return { 98 | result, 99 | }; 100 | } 101 | 102 | export function updateDrawingState({ 103 | drawingId, 104 | state, 105 | }: { 106 | drawingId: string; 107 | state: DrawingState; 108 | }) { 109 | return clientDB.transact(clientDB.tx.drawings[drawingId].merge({ state })); 110 | } 111 | 112 | export function updateDrawingName({ 113 | drawingId, 114 | name, 115 | }: { 116 | drawingId: string; 117 | name: string; 118 | }) { 119 | return clientDB.transact(clientDB.tx.drawings[drawingId].merge({ name })); 120 | } 121 | 122 | export function deleteDrawing({ drawingId }: { drawingId: string }) { 123 | return clientDB.transact(clientDB.tx.drawings[drawingId].delete()); 124 | } 125 | -------------------------------------------------------------------------------- /src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | // adds Tailwind to app 2 | import "@/styles/globals.css"; 3 | 4 | import Head from "next/head"; 5 | import type { AppProps } from "next/app"; 6 | 7 | export default function App({ Component, pageProps }: AppProps) { 8 | return ( 9 | <> 10 | 11 | instldraw 12 | 13 | 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/pages/drawings/[id].tsx: -------------------------------------------------------------------------------- 1 | import "tldraw/tldraw.css"; 2 | import { useEffect, useState } from "react"; 3 | import { useRouter } from "next/router"; 4 | import { Tldraw, useEditor } from "tldraw"; 5 | 6 | import { updateDrawingName } from "@/mutators"; 7 | import { useInstantStore } from "@/lib/useInstantStore"; 8 | import { useInstantPresence } from "@/lib/useInstantPresence"; 9 | import { colorNames, localSourceId } from "@/config"; 10 | import clientDB from "@/lib/clientDB"; 11 | 12 | export default function Page() { 13 | const auth = clientDB.useAuth(); 14 | 15 | const router = useRouter(); 16 | const drawingId = router.query.id as string; 17 | 18 | useEffect(() => { 19 | if (auth.isLoading) return; 20 | if (!auth.user) { 21 | router.push("/"); 22 | } 23 | }, [auth.isLoading]); 24 | 25 | return ( 26 |
27 | {auth.isLoading ? ( 28 | Loading... 29 | ) : auth.error ? ( 30 | Oops, an error occurred! 31 | ) : auth.user ? ( 32 | 33 | ) : null} 34 |
35 | ); 36 | } 37 | 38 | function InstantTldraw({ drawingId }: { drawingId: string }) { 39 | const store = useInstantStore({ drawingId, localSourceId }); 40 | const [displayName, setDisplayName] = useState(""); 41 | const [color, setColor] = useState("blue"); 42 | 43 | const { data, isLoading: isDrawingLoading } = clientDB.useQuery({ 44 | drawings: { 45 | $: { 46 | where: { 47 | id: drawingId, 48 | }, 49 | }, 50 | }, 51 | }); 52 | 53 | const [drawingName, setDrawingName] = useState(""); 54 | 55 | useEffect(() => { 56 | if (isDrawingLoading) return; 57 | const drawing = data?.drawings.find((d) => d.id === drawingId); 58 | if (!drawing) return; 59 | 60 | setDrawingName(drawing.name); 61 | }, [isDrawingLoading]); 62 | 63 | return ( 64 |
65 |
66 |
67 | 68 | {``} 69 | 70 |
{ 73 | e.preventDefault(); 74 | updateDrawingName({ drawingId, name: drawingName }); 75 | }} 76 | > 77 | setDrawingName(e.currentTarget.value)} 84 | /> 85 | 91 |
92 |
93 |
94 | setDisplayName(e.currentTarget.value)} 100 | /> 101 | 102 | 116 |
117 |
118 | 123 | {drawingId ? ( 124 | 132 | ) : null} 133 | 134 |
135 | ); 136 | } 137 | 138 | function InstantTldrawCursors({ 139 | drawingId, 140 | user, 141 | }: { 142 | drawingId: string; 143 | user: { id: string; color: string; name: string }; 144 | }) { 145 | const editor = useEditor(); 146 | 147 | useInstantPresence({ 148 | editor, 149 | drawingId, 150 | user, 151 | }); 152 | 153 | return null; 154 | } 155 | -------------------------------------------------------------------------------- /src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useMemo, useState } from "react"; 2 | import Link from "next/link"; 3 | import { User } from "@instantdb/react"; 4 | import { useRouter } from "next/router"; 5 | import clientDB from "@/lib/clientDB"; 6 | import { InstantAuth } from "@/components/InstantAuth"; 7 | import { useDialog, PromptDialog } from "@/components/UI"; 8 | 9 | import { 10 | createTeamWithMember, 11 | acceptInvite, 12 | declineInvite, 13 | inviteMemberToTeam, 14 | createDrawingForTeam, 15 | deleteDrawing, 16 | } from "@/mutators"; 17 | 18 | import "@/__dev"; 19 | 20 | export const drawingsPerPage = 5; 21 | 22 | export default function Page() { 23 | const auth = clientDB.useAuth(); 24 | 25 | return ( 26 |
27 | 28 | {``} 29 | 30 | {auth.isLoading ? ( 31 | Loading... 32 | ) : auth.error ? ( 33 | Oops, an error occurred! 34 | ) : !auth.user ? ( 35 | 36 | ) : ( 37 | 38 | )} 39 |
40 | ); 41 | } 42 | 43 | function Index({ user }: { user: User }) { 44 | const teamDialog = useDialog(); 45 | const [selectedTeamId, setSelectedTeamId] = useState(null); 46 | const result = clientDB.useQuery({ 47 | teams: { 48 | $: { 49 | where: { 50 | "memberships.userId": user.id, 51 | }, 52 | }, 53 | }, 54 | invites: { 55 | $: { 56 | where: { 57 | userEmail: user.email, 58 | }, 59 | }, 60 | }, 61 | }); 62 | 63 | const { invites, teams } = result.data ?? {}; 64 | 65 | const team = useMemo(() => { 66 | return selectedTeamId 67 | ? teams?.find((t) => t.id === selectedTeamId) 68 | : undefined; 69 | }, [selectedTeamId, teams]); 70 | 71 | const pendingInvites = useMemo(() => { 72 | const teamIds = new Set(teams?.map((t) => t.id) ?? []); 73 | return invites?.filter((invite) => !teamIds.has(invite.teamId)) ?? []; 74 | }, [invites, teams]); 75 | 76 | useEffect(() => { 77 | if (team && teams?.find((t) => t.id === team.id)) return; 78 | 79 | const firstTeam = teams?.at(0); 80 | if (!firstTeam) return; 81 | 82 | setSelectedTeamId(firstTeam.id); 83 | }, [teams]); 84 | 85 | return ( 86 |
87 | {teamDialog.isOpen ? ( 88 | { 92 | const response = await createTeamWithMember({ 93 | teamName, 94 | userEmail: user.email, 95 | userId: user.id, 96 | }); 97 | 98 | setSelectedTeamId(response.vars.teamId); 99 | }} 100 | /> 101 | ) : null} 102 | {result.isLoading ? ( 103 | Loading... 104 | ) : result.error ? ( 105 | Oops, an error occurred! 106 | ) : result.data.teams.length === 0 && result.data.invites.length === 0 ? ( 107 |
108 |

Welcome!

109 |

Create your first team to get started.

110 | 116 |
117 | ) : ( 118 |
119 | {pendingInvites.length ? ( 120 |
121 |

Team invites

122 | {pendingInvites.map((invite) => { 123 | return ( 124 |
125 | {invite.teamName} 126 | 138 | 146 |
147 | ); 148 | })} 149 |
150 | ) : null} 151 |
152 |
153 | 154 | 165 | 171 |
172 | {team ? : null} 173 |
174 |
175 | )} 176 |
177 | ); 178 | } 179 | 180 | function Team({ 181 | team, 182 | }: { 183 | team: { 184 | id: string; 185 | name: string; 186 | }; 187 | }) { 188 | const router = useRouter(); 189 | const [pageNumber, setPageNumber] = useState(1); 190 | const inviteMemberDialog = useDialog(); 191 | const newDrawingDialog = useDialog(); 192 | 193 | const { isLoading, error, data } = clientDB.useQuery({ 194 | drawings: { 195 | $: { 196 | where: { 197 | "teams.id": team.id, 198 | }, 199 | limit: drawingsPerPage, 200 | offset: drawingsPerPage * (pageNumber - 1), 201 | }, 202 | }, 203 | memberships: { 204 | $: { 205 | where: { 206 | "teams.id": team.id, 207 | }, 208 | }, 209 | }, 210 | }); 211 | 212 | const drawings = data?.drawings ?? []; 213 | const memberships = data?.memberships ?? []; 214 | 215 | // begin: pagination 216 | // hack to check if there is a next page 217 | // by fetching one more item than the page size 218 | // if there is a next page, we will enable a "next" button 219 | // in the future, useQuery should give us a way to check if there is a next page 220 | const _pageLookaheadQuery = clientDB.useQuery({ 221 | drawings: { 222 | $: { 223 | where: { 224 | "teams.id": team.id, 225 | }, 226 | limit: drawingsPerPage + 1, 227 | offset: drawingsPerPage * (pageNumber - 1), 228 | }, 229 | }, 230 | }); 231 | 232 | const hasNextPage = 233 | data && 234 | _pageLookaheadQuery.data && 235 | _pageLookaheadQuery.data?.drawings.length > drawings.length; 236 | 237 | const loadNextPage = () => { 238 | if (!hasNextPage) return; 239 | setPageNumber(pageNumber + 1); 240 | }; 241 | 242 | const loadPreviousPage = () => { 243 | if (pageNumber <= 1) return; 244 | setPageNumber(pageNumber - 1); 245 | }; 246 | // end: pagination 247 | 248 | return ( 249 |
250 | {inviteMemberDialog.isOpen ? ( 251 | { 255 | inviteMemberToTeam({ 256 | teamId: team.id, 257 | userEmail, 258 | teamName: team.name, 259 | }); 260 | }} 261 | /> 262 | ) : null} 263 | {newDrawingDialog.isOpen ? ( 264 | { 268 | const response = await createDrawingForTeam({ 269 | teamId: team.id, 270 | drawingName, 271 | }); 272 | 273 | await router.push(`/drawings/${response.vars.drawingId}`); 274 | }} 275 | /> 276 | ) : null} 277 |
278 |
279 | 285 | 291 |
292 |

Drawings

293 |
294 | {isLoading ? ( 295 | Loading... 296 | ) : error ? ( 297 | Failed to load drawings 298 | ) : drawings.length ? ( 299 |
300 | {drawings.map((drawing) => ( 301 |
305 | 310 | {drawing.name} 311 | 312 | 320 |
321 | ))} 322 |
323 | ) : ( 324 | No drawings 325 | )} 326 |
327 |
328 | 335 | 342 |
343 |
344 |
Members
345 | {memberships.map((m) => ( 346 |
347 | {m.userEmail} 348 |
349 | ))} 350 |
351 |
352 |
353 | ); 354 | } 355 | -------------------------------------------------------------------------------- /src/styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { TLRecord } from "tldraw"; 2 | 3 | type InstantTLRecord = TLRecord & { 4 | meta: { source: string; version: string; deleted?: boolean }; 5 | }; 6 | 7 | export type DrawingState = Record; 8 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config: Config = { 4 | content: [ 5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}", 8 | ], 9 | }; 10 | 11 | export default config; 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "paths": { 16 | "@/*": ["./src/*"] 17 | } 18 | }, 19 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 20 | "exclude": ["node_modules"] 21 | } 22 | --------------------------------------------------------------------------------