diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..c91991c --- /dev/null +++ b/.eslintignore @@ -0,0 +1,5 @@ +.next +.vercel +.git +node_modules +.deepsource.toml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5447156 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "yearly" diff --git a/.github/workflows/lint-typecheck.yaml b/.github/workflows/lint-typecheck.yaml index a7a31a3..8493006 100644 --- a/.github/workflows/lint-typecheck.yaml +++ b/.github/workflows/lint-typecheck.yaml @@ -7,6 +7,7 @@ on: pull_request: branches: - main + - preview jobs: linting: diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..e06e1c8 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,7 @@ +node_modules +public +.vercel +.next +.deepsource.toml +.pnpm-lock.yaml +.vercel diff --git a/README.md b/README.md index 2150d48..c9f1cec 100644 --- a/README.md +++ b/README.md @@ -1 +1,46 @@ -# A work in progress... \ No newline at end of file +# Miracle Diagram + +## Introduction + +Miracle Diagram is a tool for creating database diagrams in a simple and intuitive way. It uses simple code to represent database +tables and relationships between them. Don't draw your diagrams, Just code them! + +![Demo](./public/miracle-diagram-light.png) + +![Demo](./public/miracle-diagram-demo-dark.png) + +![Demo](./public/miracle-diagram-demo-light.png) + +## Conbritutions + +This is an open source project, so if you want to contribute, you can do it in the following ways: + +- Create an issue with a bug or feature request. +- Share the project with your friends. +- Fork the project and create a pull request. + +## RoadMap + +Done +- [x] Code to diagram +- [x] Code to relationships +- [x] Import and export diagrams +- [x] Light and dark theme +- [x] Table with Icon +- [x] Save to browser storage +- [x] Nicer auto complete +- [x] Multi projects + +Todo +- [ ] Landing and Documentation page +- [ ] Table with color +- [ ] Table and column with notes +- [ ] Export to image +- [ ] Share project with link (public and private) +- [ ] Better error description +- [ ] Better Nodes and edges positioning + + + + + diff --git a/package.json b/package.json index 6c206b2..e78bb20 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "private": false, "scripts": { - "dev": "next dev --turbo", + "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint", @@ -12,32 +12,42 @@ }, "dependencies": { "@codemirror/autocomplete": "^6.9.0", + "@codemirror/commands": "^6.2.5", "@codemirror/language": "^6.9.0", "@codemirror/lint": "^6.4.0", - "@lezer/common": "^1.0.4", - "@lezer/generator": "^1.5.0", + "@codemirror/view": "^6.16.0", + "@lezer/common": "^1.1.1", + "@lezer/generator": "^1.5.1", "@lezer/highlight": "^1.1.6", "@lezer/lr": "^1.3.10", "@mantine/hooks": "^6.0.19", + "@radix-ui/react-alert-dialog": "^1.0.4", "@radix-ui/react-dialog": "^1.0.4", "@radix-ui/react-dropdown-menu": "^2.0.5", + "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-popover": "^1.0.6", "@radix-ui/react-select": "^1.2.2", "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-tabs": "^1.0.4", "@uiw/codemirror-theme-material": "^4.21.9", "@uiw/codemirror-theme-tokyo-night": "^4.21.9", - "@uiw/react-codemirror": "^4.21.9", + "@uiw/react-codemirror": "^4.21.21", "@vercel/analytics": "^1.0.2", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", + "cmdk": "^0.2.0", + "dexie": "^3.2.4", + "dexie-react-hooks": "^1.1.6", + "html-to-image": "^1.11.11", "jotai": "^2.4.0", "lodash": "^4.17.21", "lucide-react": "0.268.0", - "next": "13.4.12", + "next": "13.4.19", "next-themes": "^0.2.1", "react": "18.2.0", "react-dom": "18.2.0", + "react-hot-toast": "^2.4.1", "reactflow": "^11.8.3", "split-pane-react": "^0.1.3", "tailwind-merge": "^1.14.0", @@ -45,14 +55,15 @@ "zod": "^3.22.2" }, "devDependencies": { + "@tailwindcss/typography": "^0.5.10", "@types/lodash": "^4.14.197", "@types/node": "20.5.0", "@types/react": "18.2.20", - "@types/react-dom": "18.2.7", + "@types/react-dom": "18.2.17", "autoprefixer": "10.4.15", "eslint": "8.47.0", "eslint-config-next": "13.4.16", - "postcss": "8.4.27", + "postcss": "8.4.31", "tailwindcss": "3.3.3", "typescript": "5.1.6" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0747106..c3315d2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.1' +lockfileVersion: '6.0' settings: autoInstallPeers: true @@ -7,19 +7,25 @@ settings: dependencies: '@codemirror/autocomplete': specifier: ^6.9.0 - version: 6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4) + version: 6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.1.1) + '@codemirror/commands': + specifier: ^6.2.5 + version: 6.2.5 '@codemirror/language': specifier: ^6.9.0 version: 6.9.0 '@codemirror/lint': specifier: ^6.4.0 version: 6.4.0 + '@codemirror/view': + specifier: ^6.16.0 + version: 6.16.0 '@lezer/common': - specifier: ^1.0.4 - version: 1.0.4 + specifier: ^1.1.1 + version: 1.1.1 '@lezer/generator': - specifier: ^1.5.0 - version: 1.5.0 + specifier: ^1.5.1 + version: 1.5.1 '@lezer/highlight': specifier: ^1.1.6 version: 1.1.6 @@ -29,24 +35,33 @@ dependencies: '@mantine/hooks': specifier: ^6.0.19 version: 6.0.19(react@18.2.0) + '@radix-ui/react-alert-dialog': + specifier: ^1.0.4 + version: 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-dialog': specifier: ^1.0.4 - version: 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-dropdown-menu': specifier: ^2.0.5 - version: 2.0.5(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + version: 2.0.5(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-label': + specifier: ^2.0.2 + version: 2.0.2(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-popover': + specifier: ^1.0.6 + version: 1.0.6(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-select': specifier: ^1.2.2 - version: 1.2.2(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + version: 1.2.2(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-separator': specifier: ^1.0.3 - version: 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-slot': specifier: ^1.0.2 version: 1.0.2(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-tabs': specifier: ^1.0.4 - version: 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + version: 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@uiw/codemirror-theme-material': specifier: ^4.21.9 version: 4.21.9(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0) @@ -54,8 +69,8 @@ dependencies: specifier: ^4.21.9 version: 4.21.9(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0) '@uiw/react-codemirror': - specifier: ^4.21.9 - version: 4.21.9(@babel/runtime@7.22.11)(@codemirror/autocomplete@6.9.0)(@codemirror/language@6.9.0)(@codemirror/lint@6.4.0)(@codemirror/search@6.5.1)(@codemirror/state@6.2.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.16.0)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0) + specifier: ^4.21.21 + version: 4.21.21(@babel/runtime@7.22.11)(@codemirror/autocomplete@6.9.0)(@codemirror/language@6.9.0)(@codemirror/lint@6.4.0)(@codemirror/search@6.5.1)(@codemirror/state@6.2.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.16.0)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0) '@vercel/analytics': specifier: ^1.0.2 version: 1.0.2 @@ -65,6 +80,18 @@ dependencies: clsx: specifier: ^2.0.0 version: 2.0.0 + cmdk: + specifier: ^0.2.0 + version: 0.2.0(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + dexie: + specifier: ^3.2.4 + version: 3.2.4 + dexie-react-hooks: + specifier: ^1.1.6 + version: 1.1.6(@types/react@18.2.20)(dexie@3.2.4)(react@18.2.0) + html-to-image: + specifier: ^1.11.11 + version: 1.11.11 jotai: specifier: ^2.4.0 version: 2.4.0(@types/react@18.2.20)(react@18.2.0) @@ -75,17 +102,20 @@ dependencies: specifier: 0.268.0 version: 0.268.0(react@18.2.0) next: - specifier: 13.4.12 - version: 13.4.12(react-dom@18.2.0)(react@18.2.0) + specifier: 13.4.19 + version: 13.4.19(react-dom@18.2.0)(react@18.2.0) next-themes: specifier: ^0.2.1 - version: 0.2.1(next@13.4.12)(react-dom@18.2.0)(react@18.2.0) + version: 0.2.1(next@13.4.19)(react-dom@18.2.0)(react@18.2.0) react: specifier: 18.2.0 version: 18.2.0 react-dom: specifier: 18.2.0 version: 18.2.0(react@18.2.0) + react-hot-toast: + specifier: ^2.4.1 + version: 2.4.1(csstype@3.1.2)(react-dom@18.2.0)(react@18.2.0) reactflow: specifier: ^11.8.3 version: 11.8.3(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) @@ -103,6 +133,9 @@ dependencies: version: 3.22.2 devDependencies: + '@tailwindcss/typography': + specifier: ^0.5.10 + version: 0.5.10(tailwindcss@3.3.3) '@types/lodash': specifier: ^4.14.197 version: 4.14.197 @@ -113,11 +146,11 @@ devDependencies: specifier: 18.2.20 version: 18.2.20 '@types/react-dom': - specifier: 18.2.7 - version: 18.2.7 + specifier: 18.2.17 + version: 18.2.17 autoprefixer: specifier: 10.4.15 - version: 10.4.15(postcss@8.4.27) + version: 10.4.15(postcss@8.4.31) eslint: specifier: 8.47.0 version: 8.47.0 @@ -125,8 +158,8 @@ devDependencies: specifier: 13.4.16 version: 13.4.16(eslint@8.47.0)(typescript@5.1.6) postcss: - specifier: 8.4.27 - version: 8.4.27 + specifier: 8.4.31 + version: 8.4.31 tailwindcss: specifier: 3.3.3 version: 3.3.3 @@ -151,7 +184,7 @@ packages: dependencies: regenerator-runtime: 0.14.0 - /@codemirror/autocomplete@6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4): + /@codemirror/autocomplete@6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.1.1): resolution: {integrity: sha512-Fbwm0V/Wn3BkEJZRhr0hi5BhCo5a7eBL6LYaliPjOSwCyfOpnjXY59HruSxOUNV+1OYer0Tgx1zRNQttjXyDog==} peerDependencies: '@codemirror/language': ^6.0.0 @@ -162,16 +195,16 @@ packages: '@codemirror/language': 6.9.0 '@codemirror/state': 6.2.1 '@codemirror/view': 6.16.0 - '@lezer/common': 1.0.4 + '@lezer/common': 1.1.1 dev: false - /@codemirror/commands@6.2.4: - resolution: {integrity: sha512-42lmDqVH0ttfilLShReLXsDfASKLXzfyC36bzwcqzox9PlHulMcsUOfHXNo2X2aFMVNUoQ7j+d4q5bnfseYoOA==} + /@codemirror/commands@6.2.5: + resolution: {integrity: sha512-dSi7ow2P2YgPBZflR9AJoaTHvqmeGIgkhignYMd5zK5y6DANTvxKxp6eMEpIDUJkRAaOY/TFZ4jP1ADIO/GLVA==} dependencies: '@codemirror/language': 6.9.0 '@codemirror/state': 6.2.1 '@codemirror/view': 6.16.0 - '@lezer/common': 1.0.4 + '@lezer/common': 1.1.1 dev: false /@codemirror/language@6.9.0: @@ -179,7 +212,7 @@ packages: dependencies: '@codemirror/state': 6.2.1 '@codemirror/view': 6.16.0 - '@lezer/common': 1.0.4 + '@lezer/common': 1.1.1 '@lezer/highlight': 1.1.6 '@lezer/lr': 1.3.10 style-mod: 4.1.0 @@ -332,28 +365,28 @@ packages: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 - /@lezer/common@1.0.4: - resolution: {integrity: sha512-lZHlk8p67x4aIDtJl6UQrXSOP6oi7dQR3W/geFVrENdA1JDaAJWldnVqVjPMJupbTKbzDfFcePfKttqVidS/dg==} + /@lezer/common@1.1.1: + resolution: {integrity: sha512-aAPB9YbvZHqAW+bIwiuuTDGB4DG0sYNRObGLxud8cW7osw1ZQxfDuTZ8KQiqfZ0QJGcR34CvpTMDXEyo/+Htgg==} dev: false - /@lezer/generator@1.5.0: - resolution: {integrity: sha512-RhZtwyAzsnqC+p6N4ptbBZ/PZR+0OxpYfHdB1OO5jJ6as05H+FXD+KMGXEtDq8LPZfoTuekJaJrXEcOeNrds2g==} + /@lezer/generator@1.5.1: + resolution: {integrity: sha512-vodJv2JPwsFsiBBHE463yBhvUI9TmhIu5duF/8MH304xNS6FyWH/vTyG61pjhERm5f+VBP94co0eiN+afWcvXw==} hasBin: true dependencies: - '@lezer/common': 1.0.4 + '@lezer/common': 1.1.1 '@lezer/lr': 1.3.10 dev: false /@lezer/highlight@1.1.6: resolution: {integrity: sha512-cmSJYa2us+r3SePpRCjN5ymCqCPv+zyXmDl0ciWtVaNiORT/MxM7ZgOMQZADD0o51qOaOg24qc/zBViOIwAjJg==} dependencies: - '@lezer/common': 1.0.4 + '@lezer/common': 1.1.1 dev: false /@lezer/lr@1.3.10: resolution: {integrity: sha512-BZfVvf7Re5BIwJHlZXbJn9L8lus5EonxQghyn+ih8Wl36XMFBPTXC0KM0IdUtj9w/diPHsKlXVgL+AlX2jYJ0Q==} dependencies: - '@lezer/common': 1.0.4 + '@lezer/common': 1.1.1 dev: false /@mantine/hooks@6.0.19(react@18.2.0): @@ -364,8 +397,8 @@ packages: react: 18.2.0 dev: false - /@next/env@13.4.12: - resolution: {integrity: sha512-RmHanbV21saP/6OEPBJ7yJMuys68cIf8OBBWd7+uj40LdpmswVAwe1uzeuFyUsd6SfeITWT3XnQfn6wULeKwDQ==} + /@next/env@13.4.19: + resolution: {integrity: sha512-FsAT5x0jF2kkhNkKkukhsyYOrRqtSxrEhfliniIq0bwWbuXLgyt3Gv0Ml+b91XwjwArmuP7NxCiGd++GGKdNMQ==} dev: false /@next/eslint-plugin-next@13.4.16: @@ -374,8 +407,8 @@ packages: glob: 7.1.7 dev: true - /@next/swc-darwin-arm64@13.4.12: - resolution: {integrity: sha512-deUrbCXTMZ6ZhbOoloqecnUeNpUOupi8SE2tx4jPfNS9uyUR9zK4iXBvH65opVcA/9F5I/p8vDXSYbUlbmBjZg==} + /@next/swc-darwin-arm64@13.4.19: + resolution: {integrity: sha512-vv1qrjXeGbuF2mOkhkdxMDtv9np7W4mcBtaDnHU+yJG+bBwa6rYsYSCI/9Xm5+TuF5SbZbrWO6G1NfTh1TMjvQ==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] @@ -383,8 +416,8 @@ packages: dev: false optional: true - /@next/swc-darwin-x64@13.4.12: - resolution: {integrity: sha512-WRvH7RxgRHlC1yb5oG0ZLx8F7uci9AivM5/HGGv9ZyG2Als8Ij64GC3d+mQ5sJhWjusyU6T6V1WKTUoTmOB0zQ==} + /@next/swc-darwin-x64@13.4.19: + resolution: {integrity: sha512-jyzO6wwYhx6F+7gD8ddZfuqO4TtpJdw3wyOduR4fxTUCm3aLw7YmHGYNjS0xRSYGAkLpBkH1E0RcelyId6lNsw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] @@ -392,8 +425,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-gnu@13.4.12: - resolution: {integrity: sha512-YEKracAWuxp54tKiAvvq73PUs9lok57cc8meYRibTWe/VdPB2vLgkTVWFcw31YDuRXdEhdX0fWS6Q+ESBhnEig==} + /@next/swc-linux-arm64-gnu@13.4.19: + resolution: {integrity: sha512-vdlnIlaAEh6H+G6HrKZB9c2zJKnpPVKnA6LBwjwT2BTjxI7e0Hx30+FoWCgi50e+YO49p6oPOtesP9mXDRiiUg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -401,8 +434,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-musl@13.4.12: - resolution: {integrity: sha512-LhJR7/RAjdHJ2Isl2pgc/JaoxNk0KtBgkVpiDJPVExVWA1c6gzY57+3zWuxuyWzTG+fhLZo2Y80pLXgIJv7g3g==} + /@next/swc-linux-arm64-musl@13.4.19: + resolution: {integrity: sha512-aU0HkH2XPgxqrbNRBFb3si9Ahu/CpaR5RPmN2s9GiM9qJCiBBlZtRTiEca+DC+xRPyCThTtWYgxjWHgU7ZkyvA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -410,8 +443,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-gnu@13.4.12: - resolution: {integrity: sha512-1DWLL/B9nBNiQRng+1aqs3OaZcxC16Nf+mOnpcrZZSdyKHek3WQh6j/fkbukObgNGwmCoVevLUa/p3UFTTqgqg==} + /@next/swc-linux-x64-gnu@13.4.19: + resolution: {integrity: sha512-htwOEagMa/CXNykFFeAHHvMJeqZfNQEoQvHfsA4wgg5QqGNqD5soeCer4oGlCol6NGUxknrQO6VEustcv+Md+g==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -419,8 +452,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-musl@13.4.12: - resolution: {integrity: sha512-kEAJmgYFhp0VL+eRWmUkVxLVunn7oL9Mdue/FS8yzRBVj7Z0AnIrHpTIeIUl1bbdQq1VaoOztnKicAjfkLTRCQ==} + /@next/swc-linux-x64-musl@13.4.19: + resolution: {integrity: sha512-4Gj4vvtbK1JH8ApWTT214b3GwUh9EKKQjY41hH/t+u55Knxi/0wesMzwQRhppK6Ddalhu0TEttbiJ+wRcoEj5Q==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -428,8 +461,8 @@ packages: dev: false optional: true - /@next/swc-win32-arm64-msvc@13.4.12: - resolution: {integrity: sha512-GMLuL/loR6yIIRTnPRY6UGbLL9MBdw2anxkOnANxvLvsml4F0HNIgvnU3Ej4BjbqMTNjD4hcPFdlEow4XHPdZA==} + /@next/swc-win32-arm64-msvc@13.4.19: + resolution: {integrity: sha512-bUfDevQK4NsIAHXs3/JNgnvEY+LRyneDN788W2NYiRIIzmILjba7LaQTfihuFawZDhRtkYCv3JDC3B4TwnmRJw==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] @@ -437,8 +470,8 @@ packages: dev: false optional: true - /@next/swc-win32-ia32-msvc@13.4.12: - resolution: {integrity: sha512-PhgNqN2Vnkm7XaMdRmmX0ZSwZXQAtamBVSa9A/V1dfKQCV1rjIZeiy/dbBnVYGdj63ANfsOR/30XpxP71W0eww==} + /@next/swc-win32-ia32-msvc@13.4.19: + resolution: {integrity: sha512-Y5kikILFAr81LYIFaw6j/NrOtmiM4Sf3GtOc0pn50ez2GCkr+oejYuKGcwAwq3jiTKuzF6OF4iT2INPoxRycEA==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] @@ -446,8 +479,8 @@ packages: dev: false optional: true - /@next/swc-win32-x64-msvc@13.4.12: - resolution: {integrity: sha512-Z+56e/Ljt0bUs+T+jPjhFyxYBcdY2RIq9ELFU+qAMQMteHo7ymbV7CKmlcX59RI9C4YzN8PgMgLyAoi916b5HA==} + /@next/swc-win32-x64-msvc@13.4.19: + resolution: {integrity: sha512-YzA78jBDXMYiINdPdJJwGgPNT3YqBNNGhsthsDoWHL9p24tEJn9ViQf/ZqTbwSpX/RrkPupLfuuTH2sf73JBAw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -479,13 +512,45 @@ packages: '@babel/runtime': 7.22.11 dev: false + /@radix-ui/primitive@1.0.0: + resolution: {integrity: sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==} + dependencies: + '@babel/runtime': 7.22.11 + dev: false + /@radix-ui/primitive@1.0.1: resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} dependencies: '@babel/runtime': 7.22.11 dev: false - /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-alert-dialog@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-jbfBCRlKYlhbitueOAv7z74PXYeIQmWpKwm3jllsdkw7fGWNkxqP3v0nY9WmOzcPqpQuoorNtvViBgL46n5gVg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.22.11 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.20)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.20)(react@18.2.0) + '@radix-ui/react-dialog': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.20)(react@18.2.0) + '@types/react': 18.2.20 + '@types/react-dom': 18.2.17 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} peerDependencies: '@types/react': '*' @@ -499,14 +564,14 @@ packages: optional: true dependencies: '@babel/runtime': 7.22.11 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@types/react': 18.2.20 - '@types/react-dom': 18.2.7 + '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} peerDependencies: '@types/react': '*' @@ -522,14 +587,23 @@ packages: '@babel/runtime': 7.22.11 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.20)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-slot': 1.0.2(@types/react@18.2.20)(react@18.2.0) '@types/react': 18.2.20 - '@types/react-dom': 18.2.7 + '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false + /@radix-ui/react-compose-refs@1.0.0(react@18.2.0): + resolution: {integrity: sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.22.11 + react: 18.2.0 + dev: false + /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.20)(react@18.2.0): resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} peerDependencies: @@ -544,6 +618,15 @@ packages: react: 18.2.0 dev: false + /@radix-ui/react-context@1.0.0(react@18.2.0): + resolution: {integrity: sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.22.11 + react: 18.2.0 + dev: false + /@radix-ui/react-context@1.0.1(@types/react@18.2.20)(react@18.2.0): resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} peerDependencies: @@ -558,7 +641,34 @@ packages: react: 18.2.0 dev: false - /@radix-ui/react-dialog@1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-dialog@1.0.0(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Yn9YU+QlHYLWwV1XfKiqnGVpWYWk6MeBVM6x/bcoyPvxgjQGoeT35482viLPctTMWoMw0PoHgqfSox7Ig+957Q==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.22.11 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + '@radix-ui/react-context': 1.0.0(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.0(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.0(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.0(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.0(react@18.2.0) + '@radix-ui/react-portal': 1.0.0(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.0(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.0(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.0(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.0(react@18.2.0) + aria-hidden: 1.2.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.4(@types/react@18.2.20)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + dev: false + + /@radix-ui/react-dialog@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-hJtRy/jPULGQZceSAP2Re6/4NpKo8im6V8P2hUqZsdFiSL8l35kYsw3qbRI6Ay5mQd2+wlLqje770eq+RJ3yZg==} peerDependencies: '@types/react': '*' @@ -575,17 +685,17 @@ packages: '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.20)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.20)(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-id': 1.0.1(@types/react@18.2.20)(react@18.2.0) - '@radix-ui/react-portal': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-slot': 1.0.2(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@types/react': 18.2.20 - '@types/react-dom': 18.2.7 + '@types/react-dom': 18.2.17 aria-hidden: 1.2.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -606,7 +716,23 @@ packages: react: 18.2.0 dev: false - /@radix-ui/react-dismissable-layer@1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-dismissable-layer@1.0.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-n7kDRfx+LB1zLueRDvZ1Pd0bxdJWDUZNQ/GWoxDn2prnuJKRdxsjulejX/ePkOsLi2tTm6P24mDqlMSgQpsT6g==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.22.11 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + '@radix-ui/react-primitive': 1.0.0(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) + '@radix-ui/react-use-escape-keydown': 1.0.0(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-dismissable-layer@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==} peerDependencies: '@types/react': '*' @@ -622,16 +748,16 @@ packages: '@babel/runtime': 7.22.11 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.20)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.20)(react@18.2.0) '@types/react': 18.2.20 - '@types/react-dom': 18.2.7 + '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-dropdown-menu@2.0.5(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-dropdown-menu@2.0.5(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-xdOrZzOTocqqkCkYo8yRPCib5OkTkqN7lqNCdxwPOdE466DOaNl4N8PkUIlsXthQvW5Wwkd+aEmWpfWlBoDPEw==} peerDependencies: '@types/react': '*' @@ -649,15 +775,24 @@ packages: '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-id': 1.0.1(@types/react@18.2.20)(react@18.2.0) - '@radix-ui/react-menu': 2.0.5(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-menu': 2.0.5(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@types/react': 18.2.20 - '@types/react-dom': 18.2.7 + '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false + /@radix-ui/react-focus-guards@1.0.0(react@18.2.0): + resolution: {integrity: sha512-UagjDk4ijOAnGu4WMUPj9ahi7/zJJqNZ9ZAiGPp7waUWJO0O1aWXi/udPphI0IUjvrhBsZJGSN66dR2dsueLWQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.22.11 + react: 18.2.0 + dev: false + /@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.20)(react@18.2.0): resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==} peerDependencies: @@ -672,7 +807,21 @@ packages: react: 18.2.0 dev: false - /@radix-ui/react-focus-scope@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-focus-scope@1.0.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-C4SWtsULLGf/2L4oGeIHlvWQx7Rf+7cX/vKOAD2dXW0A1b5QXwi3wWeaEgW+wn+SEVrraMUk05vLU9fZZz5HbQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.22.11 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + '@radix-ui/react-primitive': 1.0.0(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-focus-scope@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==} peerDependencies: '@types/react': '*' @@ -687,14 +836,24 @@ packages: dependencies: '@babel/runtime': 7.22.11 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.20)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@types/react': 18.2.20 - '@types/react-dom': 18.2.7 + '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false + /@radix-ui/react-id@1.0.0(react@18.2.0): + resolution: {integrity: sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.22.11 + '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0) + react: 18.2.0 + dev: false + /@radix-ui/react-id@1.0.1(@types/react@18.2.20)(react@18.2.0): resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} peerDependencies: @@ -710,7 +869,28 @@ packages: react: 18.2.0 dev: false - /@radix-ui/react-menu@2.0.5(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-label@2.0.2(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.22.11 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.20 + '@types/react-dom': 18.2.17 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-menu@2.0.5(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Gw4f9pwdH+w5w+49k0gLjN0PfRDHvxmAgG16AbyJZ7zhwZ6PBHKtWohvnSwfusfnK3L68dpBREHpVkj8wEM7ZA==} peerDependencies: '@types/react': '*' @@ -725,30 +905,65 @@ packages: dependencies: '@babel/runtime': 7.22.11 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-direction': 1.0.1(@types/react@18.2.20)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.20)(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-id': 1.0.1(@types/react@18.2.20)(react@18.2.0) - '@radix-ui/react-popper': 1.1.2(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-portal': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-popper': 1.1.2(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-slot': 1.0.2(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@types/react': 18.2.20 - '@types/react-dom': 18.2.7 + '@types/react-dom': 18.2.17 + aria-hidden: 1.2.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.2.20)(react@18.2.0) + dev: false + + /@radix-ui/react-popover@1.0.6(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-cZ4defGpkZ0qTRtlIBzJLSzL6ht7ofhhW4i1+pkemjV1IKXm0wgCRnee154qlV6r9Ttunmh2TNZhMfV2bavUyA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.22.11 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.20)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.20)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.20)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.20)(react@18.2.0) + '@radix-ui/react-popper': 1.1.2(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.20)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.20)(react@18.2.0) + '@types/react': 18.2.20 + '@types/react-dom': 18.2.17 aria-hidden: 1.2.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) react-remove-scroll: 2.5.5(@types/react@18.2.20)(react@18.2.0) dev: false - /@radix-ui/react-popper@1.1.2(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-popper@1.1.2(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==} peerDependencies: '@types/react': '*' @@ -763,22 +978,34 @@ packages: dependencies: '@babel/runtime': 7.22.11 '@floating-ui/react-dom': 2.0.1(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.20)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/rect': 1.0.1 '@types/react': 18.2.20 - '@types/react-dom': 18.2.7 + '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-portal@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-portal@1.0.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-a8qyFO/Xb99d8wQdu4o7qnigNjTPG123uADNecz0eX4usnQEj7o+cG4ZX4zkqq98NYekT7UoEQIjxBNWIFuqTA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.22.11 + '@radix-ui/react-primitive': 1.0.0(react-dom@18.2.0)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-portal@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==} peerDependencies: '@types/react': '*' @@ -792,14 +1019,27 @@ packages: optional: true dependencies: '@babel/runtime': 7.22.11 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@types/react': 18.2.20 - '@types/react-dom': 18.2.7 + '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-presence@1.0.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.22.11 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==} peerDependencies: '@types/react': '*' @@ -816,12 +1056,24 @@ packages: '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@types/react': 18.2.20 - '@types/react-dom': 18.2.7 + '@types/react-dom': 18.2.17 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-primitive@1.0.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-EyXe6mnRlHZ8b6f4ilTDrXmkLShICIuOTTj0GX4w1rp+wSxf3+TD05u1UOITC8VsJ2a9nwHvdXtOXEOl0Cw/zQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.22.11 + '@radix-ui/react-slot': 1.0.0(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} peerDependencies: '@types/react': '*' @@ -837,12 +1089,12 @@ packages: '@babel/runtime': 7.22.11 '@radix-ui/react-slot': 1.0.2(@types/react@18.2.20)(react@18.2.0) '@types/react': 18.2.20 - '@types/react-dom': 18.2.7 + '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==} peerDependencies: '@types/react': '*' @@ -857,21 +1109,21 @@ packages: dependencies: '@babel/runtime': 7.22.11 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-direction': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-id': 1.0.1(@types/react@18.2.20)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@types/react': 18.2.20 - '@types/react-dom': 18.2.7 + '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@radix-ui/react-select@1.2.2(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-select@1.2.2(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-zI7McXr8fNaSrUY9mZe4x/HC0jTLY9fWNhO1oLWYMQGDXuV4UCivIGTxwioSzO0ZCYX9iSLyWmAh/1TOmX3Cnw==} peerDependencies: '@types/react': '*' @@ -887,32 +1139,32 @@ packages: '@babel/runtime': 7.22.11 '@radix-ui/number': 1.0.1 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-context': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-direction': 1.0.1(@types/react@18.2.20)(react@18.2.0) - '@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.20)(react@18.2.0) - '@radix-ui/react-focus-scope': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-id': 1.0.1(@types/react@18.2.20)(react@18.2.0) - '@radix-ui/react-popper': 1.1.2(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-portal': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-popper': 1.1.2(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-slot': 1.0.2(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.20)(react@18.2.0) - '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@types/react': 18.2.20 - '@types/react-dom': 18.2.7 + '@types/react-dom': 18.2.17 aria-hidden: 1.2.3 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) react-remove-scroll: 2.5.5(@types/react@18.2.20)(react@18.2.0) dev: false - /@radix-ui/react-separator@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-separator@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==} peerDependencies: '@types/react': '*' @@ -926,13 +1178,23 @@ packages: optional: true dependencies: '@babel/runtime': 7.22.11 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@types/react': 18.2.20 - '@types/react-dom': 18.2.7 + '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false + /@radix-ui/react-slot@1.0.0(react@18.2.0): + resolution: {integrity: sha512-3mrKauI/tWXo1Ll+gN5dHcxDPdm/Df1ufcDLCecn+pnCIVcdWE7CujXo8QaXOWRJyZyQWWbpB8eFwHzWXlv5mQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.22.11 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) + react: 18.2.0 + dev: false + /@radix-ui/react-slot@1.0.2(@types/react@18.2.20)(react@18.2.0): resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} peerDependencies: @@ -948,7 +1210,7 @@ packages: react: 18.2.0 dev: false - /@radix-ui/react-tabs@1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-tabs@1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-egZfYY/+wRNCflXNHx+dePvnz9FbmssDTJBtgRfDY7e8SE5oIo3Py2eCB1ckAbh1Q7cQ/6yJZThJ++sgbxibog==} peerDependencies: '@types/react': '*' @@ -966,16 +1228,25 @@ packages: '@radix-ui/react-context': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-direction': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@radix-ui/react-id': 1.0.1(@types/react@18.2.20)(react@18.2.0) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.20)(react@18.2.0) '@types/react': 18.2.20 - '@types/react-dom': 18.2.7 + '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false + /@radix-ui/react-use-callback-ref@1.0.0(react@18.2.0): + resolution: {integrity: sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.22.11 + react: 18.2.0 + dev: false + /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.20)(react@18.2.0): resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==} peerDependencies: @@ -990,6 +1261,16 @@ packages: react: 18.2.0 dev: false + /@radix-ui/react-use-controllable-state@1.0.0(react@18.2.0): + resolution: {integrity: sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.22.11 + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) + react: 18.2.0 + dev: false + /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.20)(react@18.2.0): resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} peerDependencies: @@ -1005,6 +1286,16 @@ packages: react: 18.2.0 dev: false + /@radix-ui/react-use-escape-keydown@1.0.0(react@18.2.0): + resolution: {integrity: sha512-JwfBCUIfhXRxKExgIqGa4CQsiMemo1Xt0W/B4ei3fpzpvPENKpMKQ8mZSB6Acj3ebrAEgi2xiQvcI1PAAodvyg==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.22.11 + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) + react: 18.2.0 + dev: false + /@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.2.20)(react@18.2.0): resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} peerDependencies: @@ -1020,6 +1311,15 @@ packages: react: 18.2.0 dev: false + /@radix-ui/react-use-layout-effect@1.0.0(react@18.2.0): + resolution: {integrity: sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.22.11 + react: 18.2.0 + dev: false + /@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.20)(react@18.2.0): resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==} peerDependencies: @@ -1078,7 +1378,7 @@ packages: react: 18.2.0 dev: false - /@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + /@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==} peerDependencies: '@types/react': '*' @@ -1092,9 +1392,9 @@ packages: optional: true dependencies: '@babel/runtime': 7.22.11 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.17)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@types/react': 18.2.20 - '@types/react-dom': 18.2.7 + '@types/react-dom': 18.2.17 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false @@ -1223,6 +1523,18 @@ packages: tslib: 2.6.2 dev: false + /@tailwindcss/typography@0.5.10(tailwindcss@3.3.3): + resolution: {integrity: sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + dependencies: + lodash.castarray: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + postcss-selector-parser: 6.0.10 + tailwindcss: 3.3.3 + dev: true + /@types/d3-array@3.0.6: resolution: {integrity: sha512-NHkizg870sKYQn45oZT5ItoHqcgRgJD7KAiWZp4Udc6YdrFH2W0tZ2vv4shRHP+SXHoJ1G8B4I1GWb5oQSGypA==} dev: false @@ -1421,8 +1733,8 @@ packages: /@types/prop-types@15.7.5: resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} - /@types/react-dom@18.2.7: - resolution: {integrity: sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==} + /@types/react-dom@18.2.17: + resolution: {integrity: sha512-rvrT/M7Df5eykWFxn6MYt5Pem/Dbyc1N8Y0S9Mrkw2WFCRiqUgw9P7ul2NpwsXCSM1DVdENzdG9J5SreqfAIWg==} dependencies: '@types/react': 18.2.20 @@ -1499,8 +1811,8 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@uiw/codemirror-extensions-basic-setup@4.21.9(@codemirror/autocomplete@6.9.0)(@codemirror/commands@6.2.4)(@codemirror/language@6.9.0)(@codemirror/lint@6.4.0)(@codemirror/search@6.5.1)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0): - resolution: {integrity: sha512-TQT6aF8brxZpFnk/K4fm/K/9k9eF3PMav/KKjHlYrGUT8BTNk/qL+ximLtIzvTUhmBFchjM1lrqSJdvpVom7/w==} + /@uiw/codemirror-extensions-basic-setup@4.21.21(@codemirror/autocomplete@6.9.0)(@codemirror/commands@6.2.5)(@codemirror/language@6.9.0)(@codemirror/lint@6.4.0)(@codemirror/search@6.5.1)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0): + resolution: {integrity: sha512-+0i9dPrRSa8Mf0CvyrMvnAhajnqwsP3IMRRlaHDRgsSGL8igc4z7MhvUPn+7cWFAAqWzQRhMdMSWzo6/TEa3EA==} peerDependencies: '@codemirror/autocomplete': '>=6.0.0' '@codemirror/commands': '>=6.0.0' @@ -1510,8 +1822,8 @@ packages: '@codemirror/state': '>=6.0.0' '@codemirror/view': '>=6.0.0' dependencies: - '@codemirror/autocomplete': 6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4) - '@codemirror/commands': 6.2.4 + '@codemirror/autocomplete': 6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.1.1) + '@codemirror/commands': 6.2.5 '@codemirror/language': 6.9.0 '@codemirror/lint': 6.4.0 '@codemirror/search': 6.5.1 @@ -1551,8 +1863,8 @@ packages: '@codemirror/view': 6.16.0 dev: false - /@uiw/react-codemirror@4.21.9(@babel/runtime@7.22.11)(@codemirror/autocomplete@6.9.0)(@codemirror/language@6.9.0)(@codemirror/lint@6.4.0)(@codemirror/search@6.5.1)(@codemirror/state@6.2.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.16.0)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-aeLegPz2iCvqJjhzXp2WUMqpMZDqxsTnF3rX9kGRlfY6vQLsrjoctj0cQ29uxEtFYJChOVjtCOtnQUlyIuNAHQ==} + /@uiw/react-codemirror@4.21.21(@babel/runtime@7.22.11)(@codemirror/autocomplete@6.9.0)(@codemirror/language@6.9.0)(@codemirror/lint@6.4.0)(@codemirror/search@6.5.1)(@codemirror/state@6.2.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.16.0)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-PaxBMarufMWoR0qc5zuvBSt76rJ9POm9qoOaJbqRmnNL2viaF+d+Paf2blPSlm1JSnqn7hlRjio+40nZJ9TKzw==} peerDependencies: '@babel/runtime': '>=7.11.0' '@codemirror/state': '>=6.0.0' @@ -1563,12 +1875,12 @@ packages: react-dom: '>=16.8.0' dependencies: '@babel/runtime': 7.22.11 - '@codemirror/commands': 6.2.4 + '@codemirror/commands': 6.2.5 '@codemirror/state': 6.2.1 '@codemirror/theme-one-dark': 6.1.2 '@codemirror/view': 6.16.0 - '@uiw/codemirror-extensions-basic-setup': 4.21.9(@codemirror/autocomplete@6.9.0)(@codemirror/commands@6.2.4)(@codemirror/language@6.9.0)(@codemirror/lint@6.4.0)(@codemirror/search@6.5.1)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0) - codemirror: 6.0.1(@lezer/common@1.0.4) + '@uiw/codemirror-extensions-basic-setup': 4.21.21(@codemirror/autocomplete@6.9.0)(@codemirror/commands@6.2.5)(@codemirror/language@6.9.0)(@codemirror/lint@6.4.0)(@codemirror/search@6.5.1)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0) + codemirror: 6.0.1(@lezer/common@1.1.1) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) transitivePeerDependencies: @@ -1733,7 +2045,7 @@ packages: has-symbols: 1.0.3 dev: true - /autoprefixer@10.4.15(postcss@8.4.27): + /autoprefixer@10.4.15(postcss@8.4.31): resolution: {integrity: sha512-KCuPB8ZCIqFdA4HwKXsvz7j6gvSDNhDP7WnUjBleRkKjPdvCmHFuQ77ocavI8FT6NdvlBnE2UFr2H4Mycn8Vew==} engines: {node: ^10 || ^12 || >=14} hasBin: true @@ -1745,7 +2057,7 @@ packages: fraction.js: 4.2.1 normalize-range: 0.1.2 picocolors: 1.0.0 - postcss: 8.4.27 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: true @@ -1862,11 +2174,25 @@ packages: engines: {node: '>=6'} dev: false - /codemirror@6.0.1(@lezer/common@1.0.4): + /cmdk@0.2.0(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-JQpKvEOb86SnvMZbYaFKYhvzFntWBeSZdyii0rZPhKJj9uwJBxu4DaVYDrRN7r3mPop56oPhRw+JYWTKs66TYw==} + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + dependencies: + '@radix-ui/react-dialog': 1.0.0(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) + command-score: 0.1.2 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + dev: false + + /codemirror@6.0.1(@lezer/common@1.1.1): resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==} dependencies: - '@codemirror/autocomplete': 6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4) - '@codemirror/commands': 6.2.4 + '@codemirror/autocomplete': 6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.1.1) + '@codemirror/commands': 6.2.5 '@codemirror/language': 6.9.0 '@codemirror/lint': 6.4.0 '@codemirror/search': 6.5.1 @@ -1887,6 +2213,10 @@ packages: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true + /command-score@0.1.2: + resolution: {integrity: sha512-VtDvQpIJBvBatnONUsPzXYFVKQQAhuf3XTNOAsdBxCNO/QCtUUd8LSgjn0GVarBkCad6aJCZfXgrjYbl/KRr7w==} + dev: false + /commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -2028,6 +2358,23 @@ packages: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} dev: false + /dexie-react-hooks@1.1.6(@types/react@18.2.20)(dexie@3.2.4)(react@18.2.0): + resolution: {integrity: sha512-xSblWtmPwhafWNWMECsW7zMMmBu8goH3QqTxEfwBNoNG1mgsM0oFclippev7ss9HhKICqBwTjgqpscci5Ed4mA==} + peerDependencies: + '@types/react': '>=16' + dexie: ^3.2 || ^4.0.1-alpha + react: '>=16' + dependencies: + '@types/react': 18.2.20 + dexie: 3.2.4 + react: 18.2.0 + dev: false + + /dexie@3.2.4: + resolution: {integrity: sha512-VKoTQRSv7+RnffpOJ3Dh6ozknBqzWw/F3iqMdsZg958R0AS8AnY9x9d1lbwENr0gzeGJHXKcGhAMRaqys6SxqA==} + engines: {node: '>=6.0'} + dev: false + /didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} @@ -2642,6 +2989,14 @@ packages: slash: 3.0.0 dev: true + /goober@2.1.13(csstype@3.1.2): + resolution: {integrity: sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==} + peerDependencies: + csstype: ^3.0.10 + dependencies: + csstype: 3.1.2 + dev: false + /gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} dependencies: @@ -2693,6 +3048,10 @@ packages: dependencies: function-bind: 1.1.1 + /html-to-image@1.11.11: + resolution: {integrity: sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==} + dev: false + /ignore@5.2.4: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} engines: {node: '>= 4'} @@ -2996,6 +3355,14 @@ packages: p-locate: 5.0.0 dev: true + /lodash.castarray@4.4.0: + resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} + dev: true + + /lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + dev: true + /lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true @@ -3069,37 +3436,34 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /next-themes@0.2.1(next@13.4.12)(react-dom@18.2.0)(react@18.2.0): + /next-themes@0.2.1(next@13.4.19)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==} peerDependencies: next: '*' react: '*' react-dom: '*' dependencies: - next: 13.4.12(react-dom@18.2.0)(react@18.2.0) + next: 13.4.19(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /next@13.4.12(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-eHfnru9x6NRmTMcjQp6Nz0J4XH9OubmzOa7CkWL+AUrUxpibub3vWwttjduu9No16dug1kq04hiUUpo7J3m3Xw==} + /next@13.4.19(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-HuPSzzAbJ1T4BD8e0bs6B9C1kWQ6gv8ykZoRWs5AQoiIuqbGHHdQO7Ljuvg05Q0Z24E2ABozHe6FxDvI6HfyAw==} engines: {node: '>=16.8.0'} hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 - fibers: '>= 3.1.0' react: ^18.2.0 react-dom: ^18.2.0 sass: ^1.3.0 peerDependenciesMeta: '@opentelemetry/api': optional: true - fibers: - optional: true sass: optional: true dependencies: - '@next/env': 13.4.12 + '@next/env': 13.4.19 '@swc/helpers': 0.5.1 busboy: 1.6.0 caniuse-lite: 1.0.30001522 @@ -3110,15 +3474,15 @@ packages: watchpack: 2.4.0 zod: 3.21.4 optionalDependencies: - '@next/swc-darwin-arm64': 13.4.12 - '@next/swc-darwin-x64': 13.4.12 - '@next/swc-linux-arm64-gnu': 13.4.12 - '@next/swc-linux-arm64-musl': 13.4.12 - '@next/swc-linux-x64-gnu': 13.4.12 - '@next/swc-linux-x64-musl': 13.4.12 - '@next/swc-win32-arm64-msvc': 13.4.12 - '@next/swc-win32-ia32-msvc': 13.4.12 - '@next/swc-win32-x64-msvc': 13.4.12 + '@next/swc-darwin-arm64': 13.4.19 + '@next/swc-darwin-x64': 13.4.19 + '@next/swc-linux-arm64-gnu': 13.4.19 + '@next/swc-linux-arm64-musl': 13.4.19 + '@next/swc-linux-x64-gnu': 13.4.19 + '@next/swc-linux-x64-musl': 13.4.19 + '@next/swc-win32-arm64-msvc': 13.4.19 + '@next/swc-win32-ia32-msvc': 13.4.19 + '@next/swc-win32-x64-msvc': 13.4.19 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -3282,27 +3646,27 @@ packages: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} - /postcss-import@15.1.0(postcss@8.4.27): + /postcss-import@15.1.0(postcss@8.4.31): resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} peerDependencies: postcss: ^8.0.0 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.4 - /postcss-js@4.0.1(postcss@8.4.27): + /postcss-js@4.0.1(postcss@8.4.31): resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} engines: {node: ^12 || ^14 || >= 16} peerDependencies: postcss: ^8.4.21 dependencies: camelcase-css: 2.0.1 - postcss: 8.4.27 + postcss: 8.4.31 - /postcss-load-config@4.0.1(postcss@8.4.27): + /postcss-load-config@4.0.1(postcss@8.4.31): resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} engines: {node: '>= 14'} peerDependencies: @@ -3315,18 +3679,26 @@ packages: optional: true dependencies: lilconfig: 2.1.0 - postcss: 8.4.27 + postcss: 8.4.31 yaml: 2.3.1 - /postcss-nested@6.0.1(postcss@8.4.27): + /postcss-nested@6.0.1(postcss@8.4.31): resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} engines: {node: '>=12.0'} peerDependencies: postcss: ^8.2.14 dependencies: - postcss: 8.4.27 + postcss: 8.4.31 postcss-selector-parser: 6.0.13 + /postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + /postcss-selector-parser@6.0.13: resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==} engines: {node: '>=4'} @@ -3346,8 +3718,8 @@ packages: source-map-js: 1.0.2 dev: false - /postcss@8.4.27: - resolution: {integrity: sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==} + /postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.6 @@ -3385,6 +3757,20 @@ packages: scheduler: 0.23.0 dev: false + /react-hot-toast@2.4.1(csstype@3.1.2)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==} + engines: {node: '>=10'} + peerDependencies: + react: '>=16' + react-dom: '>=16' + dependencies: + goober: 2.1.13(csstype@3.1.2) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + transitivePeerDependencies: + - csstype + dev: false + /react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} dev: true @@ -3405,6 +3791,25 @@ packages: tslib: 2.6.2 dev: false + /react-remove-scroll@2.5.4(@types/react@18.2.20)(react@18.2.0): + resolution: {integrity: sha512-xGVKJJr0SJGQVirVFAUZ2k1QLyO6m+2fy0l8Qawbp5Jgrv3DeLalrfMNBFSlmz5kriGGzsVBtGVnf4pTKIhhWA==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.20 + react: 18.2.0 + react-remove-scroll-bar: 2.3.4(@types/react@18.2.20)(react@18.2.0) + react-style-singleton: 2.2.1(@types/react@18.2.20)(react@18.2.0) + tslib: 2.6.2 + use-callback-ref: 1.3.0(@types/react@18.2.20)(react@18.2.0) + use-sidecar: 1.1.2(@types/react@18.2.20)(react@18.2.0) + dev: false + /react-remove-scroll@2.5.5(@types/react@18.2.20)(react@18.2.0): resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==} engines: {node: '>=10'} @@ -3756,11 +4161,11 @@ packages: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.0.0 - postcss: 8.4.27 - postcss-import: 15.1.0(postcss@8.4.27) - postcss-js: 4.0.1(postcss@8.4.27) - postcss-load-config: 4.0.1(postcss@8.4.27) - postcss-nested: 6.0.1(postcss@8.4.27) + postcss: 8.4.31 + postcss-import: 15.1.0(postcss@8.4.31) + postcss-js: 4.0.1(postcss@8.4.31) + postcss-load-config: 4.0.1(postcss@8.4.31) + postcss-nested: 6.0.1(postcss@8.4.31) postcss-selector-parser: 6.0.13 resolve: 1.22.4 sucrase: 3.34.0 diff --git a/src/app/layout.tsx b/src/app/layout.tsx index b5c4529..bafeb71 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -5,6 +5,8 @@ import '@/styles/globals.css'; import type { Metadata } from 'next'; import { Inter } from 'next/font/google'; import { Analytics } from '@vercel/analytics/react'; +import ConfirmDialog from '@/components/confirm-dialog'; +import { Toaster } from 'react-hot-toast'; const inter = Inter({ subsets: ['latin'] }); @@ -75,6 +77,8 @@ export default function RootLayout({ {children} + + ); diff --git a/src/app/page.tsx b/src/app/page.tsx index 5ef5998..26e1f54 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -3,28 +3,31 @@ import { CodeEditor } from '@/components/code-editor'; import { Flow } from '@/components/flow'; import { Sash } from '@/components/sash'; -import { Metadata } from 'next'; -import { useState } from 'react'; +import { useProject } from '@/hooks/use-project'; +import React, { useState } from 'react'; import SplitPane from 'split-pane-react'; import 'split-pane-react/esm/themes/default.css'; export default function Home() { const [sizes, setSizes] = useState<(number | string)[]>(['25%', 'auto']); + return ( -
- - -
- -
-
-
+ +
+ + +
+ +
+
+
+
); } diff --git a/src/components/app-header/settings.tsx b/src/components/app-header/import-export.tsx similarity index 79% rename from src/components/app-header/settings.tsx rename to src/components/app-header/import-export.tsx index b96ac6b..baa9134 100644 --- a/src/components/app-header/settings.tsx +++ b/src/components/app-header/import-export.tsx @@ -12,8 +12,8 @@ import { import { Icons } from '../icons'; import { useImportExport } from '@/hooks/use-import-export'; -export function AppHeaderSettings() { - const { exportToJson, importFromJson } = useImportExport(); +export function ImportExport() { + const { exportToJson, importFromJson, exportToPng } = useImportExport(); return ( @@ -33,6 +33,10 @@ export function AppHeaderSettings() { Export Json + + + Export PNG + ); diff --git a/src/components/app-header/index.tsx b/src/components/app-header/index.tsx index f732bbc..8e28c7c 100644 --- a/src/components/app-header/index.tsx +++ b/src/components/app-header/index.tsx @@ -2,7 +2,11 @@ import React from 'react'; import { Icons } from '../icons'; import Text from '../ui/text'; import { ThemeToggle } from '../theme/toggle'; -import { AppHeaderSettings } from './settings'; +import { ImportExport } from './import-export'; +import { ProjectSelector } from './project-selector'; +import { DocsSheet } from '../docs-sheet'; +import { Button } from '../ui/button'; +import Link from 'next/link'; export function AppHeader() { return ( @@ -22,8 +26,18 @@ export function AppHeader() {
+ + - + +
); diff --git a/src/components/app-header/project-selector.tsx b/src/components/app-header/project-selector.tsx new file mode 100644 index 0000000..ac97c05 --- /dev/null +++ b/src/components/app-header/project-selector.tsx @@ -0,0 +1,187 @@ +'use client'; + +import * as React from 'react'; + +import { cn } from '@/lib/utils'; +import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover'; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandSeparator, +} from '../ui/command'; +import { CheckIcon, ChevronsUpDown, PlusCircle, Trash2 } from 'lucide-react'; +import { Button } from '../ui/button'; +import { useAtom } from 'jotai'; +import { currentProjectAtom } from '@/jotai/project-atom'; +import { useAllProjects } from '@/hooks/use-all-projects'; +import { + DialogFooter, + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogTrigger, +} from '../ui/dialog'; +import { Label } from '../ui/label'; +import { Input } from '../ui/input'; +import { db } from '@/lib/db'; +import useConfirmation from '@/hooks/use-confirmation'; +import { toast } from 'react-hot-toast'; + +export function ProjectSelector() { + const allProjects = useAllProjects(); + const [currentProject, setCurrentProject] = useAtom(currentProjectAtom); + + const [open, setOpen] = React.useState(false); + const [newProjectDialogOpen, setNewProjectDialogOpen] = React.useState(false); + const [newProjectName, setNewProjectName] = React.useState(''); + + const { createConfirmation } = useConfirmation(); + + const handleNewProjectSubmit = React.useCallback(() => { + try { + db.projects.add({ + id: newProjectName, + code: '', + edges: [], + nodes: [], + tables: [], + }); + + setCurrentProject(newProjectName); + setNewProjectDialogOpen(false); + toast.success(`Project ${newProjectName} has been created.`); + } catch (error) { + toast.error('Failed to create project.'); + console.log(error); + } + }, [setCurrentProject, newProjectName, setNewProjectDialogOpen]); + + const handleDeleteProject = React.useCallback(async () => { + if (!allProjects) return; + + if (allProjects?.length < 2) { + toast.error('You only have one project. 🤨'); + return; + } + + try { + await createConfirmation({ + title: `Are you sure you want to delete ${currentProject} project?`, + description: `This is a permanent action, your project will be deleted forever.`, + type: 'destructive', + onConfirm: () => { + toast.promise(db.projects.delete(currentProject), { + loading: 'Deleting project...', + success: () => { + setCurrentProject(allProjects[0]); + return `Project ${currentProject} has been deleted.`; + }, + error: 'Failed to delete project.', + }); + }, + }); + } catch (error) { + console.log(error); + } + }, [allProjects, createConfirmation, currentProject, setCurrentProject]); + + return ( + + + + + + + + + No project found. + + {allProjects?.map(temProject => ( + { + setCurrentProject(temProject); + setOpen(false); + }} + > + {temProject} + + + ))} + + + + + { + setNewProjectDialogOpen(true); + setOpen(false); + }} + > + + Add Project + + + + + Delete current project + + + + + + + + Create Project + + Add a new diagram project to your workspace. + + +
+
+
+ + setNewProjectName(e.target.value)} + /> +
+
+
+ + + + +
+
+ ); +} diff --git a/src/components/code-editor.tsx b/src/components/code-editor.tsx index 03ece44..19c269b 100644 --- a/src/components/code-editor.tsx +++ b/src/components/code-editor.tsx @@ -1,24 +1,16 @@ 'use client'; -import React, { useEffect } from 'react'; +import React from 'react'; import CodeMirror from '@uiw/react-codemirror'; import { tokyoNight } from '@uiw/codemirror-theme-tokyo-night'; import { materialLight } from '@uiw/codemirror-theme-material'; import { useTheme } from 'next-themes'; -import { useTables } from '@/hooks/use-tables'; import { miro } from '@/lib/lang-miro'; -import { MiroLang } from '@/lib/lang-miro/parser'; import { useMirolang } from '@/hooks/use-mirolang'; export function CodeEditor() { const { resolvedTheme } = useTheme(); const [code, setCode] = useMirolang(); - const { setTables } = useTables(); - - useEffect(() => { - const parsedTables = new MiroLang(code).parse(); - setTables(parsedTables); - }, [code, setTables]); return ( diff --git a/src/components/confirm-dialog.tsx b/src/components/confirm-dialog.tsx new file mode 100644 index 0000000..7f49f96 --- /dev/null +++ b/src/components/confirm-dialog.tsx @@ -0,0 +1,54 @@ +"use client"; + +import React from "react"; + +import useConfirmation from "@/hooks/use-confirmation"; + +import { + AlertDialogAction, + AlertDialog as AlertDialogBase, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; +import { buttonVariants } from "@/components/ui/button"; + +export default function ConfirmDialog() { + const { confirmation, hideConfirmation } = useConfirmation(); + + if (!confirmation) return null; + + const handleConfirm = async () => { + await confirmation.onConfirm?.(); + confirmation.resolve?.(); + }; + + return ( + + + + {confirmation.title} + + {confirmation.description} + + + + + {confirmation.cancelText || "Cancel"} + + + {confirmation.confirmText || "Confirm"} + + + + + ); +} diff --git a/src/components/docs-sheet.tsx b/src/components/docs-sheet.tsx new file mode 100644 index 0000000..b5d839e --- /dev/null +++ b/src/components/docs-sheet.tsx @@ -0,0 +1,149 @@ +import { Button } from '@/components/ui/button'; +import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'; +import { Icons } from './icons'; +import { Alert, AlertDescription, AlertTitle } from './ui/alert'; +import Link from 'next/link'; +import { Globe2 } from 'lucide-react'; + +const simpleSyntax = `users { + id uuid, + name string, + email string, +}`; + +const simpleContraints = `users { + id uuid pk, + name string unique, + email string unique nullable, + gender string nullable, +}`; + +const simpleRelationship = `users { + id uuid pk, + name string, + email string, +} + +posts { + id uuid pk, + title string, + user_id uuid -> users.id, +}`; + +const simpleIcon = `users i-user { + id uuid pk, + name string, + email string, +}`; + +export function DocsSheet() { + return ( + + + + + +
+

+ + Getting Started +

+ + + Had a bug? + + You can report bugs and issues on the github repository. + + + Github + + + + +

+ Miracle Diagram comes with its own simple scripting language called + MiroLang, which is used to define the structure of the diagram. +

+
+            {simpleSyntax}
+          
+

Constraints

+

+ You can define constraints by just adding it after the type. + Contraits are user defined, which means you can define your own + contraints. +

+

Note: You can have multiple constraints on a single column.

+
+            {simpleContraints}
+          
+

Relationships

+

+ You can define relationship by either dragging one column to another + or by using the following syntax. +

+

+ Syntax: -> tableName.columnName +

+
+            {simpleRelationship}
+          
+

Icons

+

+ Miracle Diagram uses icons from + + Lucide Icon + + and you can use them by just adding the icon name after the table + name. Note that the icon name should be in kebab case and always + prefixed with i-. +

+

+ Example: i-user, i-clock +

+
+            {simpleIcon}
+          
+

Support the project

+

+ Miracle Diagram is an open source project and is free to use. If you + like the project, please consider supporting the project by giving a + star on github. +

+
+ + +
+
+
+
+ ); +} diff --git a/src/components/edges/button-edge.tsx b/src/components/edges/button-edge.tsx index 057bf59..ac8e31d 100644 --- a/src/components/edges/button-edge.tsx +++ b/src/components/edges/button-edge.tsx @@ -3,13 +3,13 @@ import { BaseEdge, EdgeLabelRenderer, EdgeProps, - getBezierPath, + getSmoothStepPath, useStore, } from 'reactflow'; import { Button } from '../ui/button'; import { Icons } from '../icons'; -import { useEdgesAtom } from '@/hooks/use-edges-atom'; import { getEdgeParams } from '@/lib/floating'; +import { useProject } from '@/hooks/use-project'; export function ButtonEdgeRaw({ id, @@ -17,12 +17,6 @@ export function ButtonEdgeRaw({ style = {}, source, target, - sourceX, - sourceY, - sourcePosition, - targetX, - targetY, - targetPosition, sourceHandleId, targetHandleId, markerEnd, @@ -35,7 +29,11 @@ export function ButtonEdgeRaw({ useCallback(store => store.nodeInternals.get(target), [target]), ); - const { removeEdge } = useEdgesAtom(); + const { handldeRemoveEdge } = useProject(); + + const handleRemoveEdgeClick = useCallback(() => { + handldeRemoveEdge(id); + }, [id, handldeRemoveEdge]); if (!sourceNode || !targetNode) return null; @@ -48,7 +46,7 @@ export function ButtonEdgeRaw({ }, ); - const [edgePath, labelX, labelY] = getBezierPath({ + const [edgePath, labelX, labelY] = getSmoothStepPath({ sourceX: sx, sourceY: sy, sourcePosition: sourcePos, @@ -80,7 +78,7 @@ export function ButtonEdgeRaw({ size="icon" variant="outline" className="w-6 h-6 rounded-full" - onClick={event => removeEdge(id)} + onClick={handleRemoveEdgeClick} > diff --git a/src/components/flow.tsx b/src/components/flow.tsx index c6b27c4..4f48184 100644 --- a/src/components/flow.tsx +++ b/src/components/flow.tsx @@ -1,7 +1,7 @@ 'use client'; import 'reactflow/dist/style.css'; -import React, { useCallback, MouseEvent } from 'react'; +import React, { useCallback, MouseEvent, useEffect } from 'react'; import ReactFlow, { Background, BackgroundVariant, @@ -10,28 +10,25 @@ import ReactFlow, { Edge, } from 'reactflow'; import { ButtonEdge } from './edges/button-edge'; -import { useNodesAtom } from '@/hooks/use-nodes-atom'; -import { useEdgesAtom } from '@/hooks/use-edges-atom'; -import { CustomDefaultNode } from './nodes/custom-default'; import { FloatingNode } from './nodes/floating-nodes'; +import { useProject } from '@/hooks/use-project'; const edgeTypes = { 'button-edge': ButtonEdge, }; const nodeTypes = { - 'custom-default': CustomDefaultNode, 'floating-node': FloatingNode, }; export function Flow() { - const { nodes, onNodesChange } = useNodesAtom(); - const { edges, onEdgesChange, setEdges, onDragConnect } = useEdgesAtom(); + const { project, onDragConnect, onEdgesChange, onNodesChange, setProject } = + useProject(); const onEdgeMouseEnter = useCallback( (event: MouseEvent, edge: Edge) => { // show the label on mouse enter - setEdges(es => + setProject('edges', es => es.map(e => { if (e.id === edge.id) { e.label = 'x'; @@ -40,14 +37,14 @@ export function Flow() { }), ); }, - [setEdges], + [setProject], ); const onEdgeMouseLeave = useCallback( (event: MouseEvent, edge: Edge) => { // hide the label on mouse leave setTimeout(() => { - setEdges(es => + setProject('edges', es => es.map(e => { if (e.id === edge.id) { e.label = undefined; @@ -57,14 +54,14 @@ export function Flow() { ); }, 1250); }, - [setEdges], + [setProject], ); return ( ( ), GitHub: (props: LucideProps) => ( - + { - const table = tableSchema.safeParse(props.data); - - if (!table.success) return null; - - return ( - -
-

{table.data.name}

- -
    - {table.data.columns.map(column => ( -
  • - - - {column.name} {column.type} -
  • - ))} -
-
- - ); -}); - -CustomDefaultNode.displayName = 'CustomDefaultNode'; diff --git a/src/components/sash.tsx b/src/components/sash.tsx index 412f766..7bd9428 100644 --- a/src/components/sash.tsx +++ b/src/components/sash.tsx @@ -1,4 +1,4 @@ -export function Sash(index: number, active: boolean) { +export function Sash(index: number) { return (
( + +) +AlertDialogPortal.displayName = AlertDialogPrimitive.Portal.displayName + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +)) +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)) +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + +const AlertDialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogHeader.displayName = "AlertDialogHeader" + +const AlertDialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogFooter.displayName = "AlertDialogFooter" + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName + +export { + AlertDialog, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/src/components/ui/alert.tsx b/src/components/ui/alert.tsx new file mode 100644 index 0000000..41fa7e0 --- /dev/null +++ b/src/components/ui/alert.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)) +Alert.displayName = "Alert" + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertDescription.displayName = "AlertDescription" + +export { Alert, AlertTitle, AlertDescription } diff --git a/src/components/ui/command.tsx b/src/components/ui/command.tsx new file mode 100644 index 0000000..7b46cf9 --- /dev/null +++ b/src/components/ui/command.tsx @@ -0,0 +1,159 @@ +'use client'; + +import * as React from 'react'; +import { DialogProps } from '@radix-ui/react-dialog'; +import { Command as CommandPrimitive } from 'cmdk'; +import { Search } from 'lucide-react'; + +import { cn } from '@/lib/utils'; +import { Dialog, DialogContent } from '@/components/ui/dialog'; + +const Command = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +Command.displayName = CommandPrimitive.displayName; + +interface CommandDialogProps extends DialogProps {} + +const CommandDialog = ({ children, ...props }: CommandDialogProps) => { + return ( + + + + {children} + + + + ); +}; + +const CommandInput = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( +
+ + +
+)); + +CommandInput.displayName = CommandPrimitive.Input.displayName; + +const CommandList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); + +CommandList.displayName = CommandPrimitive.List.displayName; + +const CommandEmpty = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>((props, ref) => ( + +)); + +CommandEmpty.displayName = CommandPrimitive.Empty.displayName; + +const CommandGroup = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); + +CommandGroup.displayName = CommandPrimitive.Group.displayName; + +const CommandSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +CommandSeparator.displayName = CommandPrimitive.Separator.displayName; + +const CommandItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + isDestructive?: boolean; + } +>(({ className, isDestructive, ...props }, ref) => ( + +)); + +CommandItem.displayName = CommandPrimitive.Item.displayName; + +const CommandShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ); +}; +CommandShortcut.displayName = 'CommandShortcut'; + +export { + Command, + CommandDialog, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +}; diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx new file mode 100644 index 0000000..2f33766 --- /dev/null +++ b/src/components/ui/dialog.tsx @@ -0,0 +1,123 @@ +"use client" + +import * as React from "react" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import { X } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Dialog = DialogPrimitive.Root + +const DialogTrigger = DialogPrimitive.Trigger + +const DialogPortal = ({ + className, + ...props +}: DialogPrimitive.DialogPortalProps) => ( + +) +DialogPortal.displayName = DialogPrimitive.Portal.displayName + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName + +const DialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)) +DialogContent.displayName = DialogPrimitive.Content.displayName + +const DialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogHeader.displayName = "DialogHeader" + +const DialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogFooter.displayName = "DialogFooter" + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogTitle.displayName = DialogPrimitive.Title.displayName + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogDescription.displayName = DialogPrimitive.Description.displayName + +export { + Dialog, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +} diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx new file mode 100644 index 0000000..677d05f --- /dev/null +++ b/src/components/ui/input.tsx @@ -0,0 +1,25 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +export interface InputProps + extends React.InputHTMLAttributes {} + +const Input = React.forwardRef( + ({ className, type, ...props }, ref) => { + return ( + + ) + } +) +Input.displayName = "Input" + +export { Input } diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx new file mode 100644 index 0000000..5341821 --- /dev/null +++ b/src/components/ui/label.tsx @@ -0,0 +1,26 @@ +"use client" + +import * as React from "react" +import * as LabelPrimitive from "@radix-ui/react-label" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const labelVariants = cva( + "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" +) + +const Label = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, ...props }, ref) => ( + +)) +Label.displayName = LabelPrimitive.Root.displayName + +export { Label } diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx new file mode 100644 index 0000000..a0ec48b --- /dev/null +++ b/src/components/ui/popover.tsx @@ -0,0 +1,31 @@ +"use client" + +import * as React from "react" +import * as PopoverPrimitive from "@radix-ui/react-popover" + +import { cn } from "@/lib/utils" + +const Popover = PopoverPrimitive.Root + +const PopoverTrigger = PopoverPrimitive.Trigger + +const PopoverContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( + + + +)) +PopoverContent.displayName = PopoverPrimitive.Content.displayName + +export { Popover, PopoverTrigger, PopoverContent } diff --git a/src/components/ui/selector.tsx b/src/components/ui/selector.tsx new file mode 100644 index 0000000..d513537 --- /dev/null +++ b/src/components/ui/selector.tsx @@ -0,0 +1,74 @@ +'use client'; + +import * as React from 'react'; +import { PopoverProps } from '@radix-ui/react-popover'; + +import { cn } from '@/lib/utils'; +import { Popover, PopoverContent, PopoverTrigger } from './popover'; +import { Button } from './button'; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, +} from './command'; +import { CheckIcon, ChevronsUpDown } from 'lucide-react'; + +interface Preset { + id: string; + name: string; +} + +interface PresetSelectorProps extends PopoverProps { + presets: Preset[]; +} + +export function PresetSelector({ presets, ...props }: PresetSelectorProps) { + const [open, setOpen] = React.useState(false); + const [selectedPreset, setSelectedPreset] = React.useState(); + + return ( + + + + + + + + No presets found. + + {presets.map(preset => ( + { + setSelectedPreset(preset); + setOpen(false); + }} + > + {preset.name} + + + ))} + + + + + ); +} diff --git a/src/hooks/use-all-projects.ts b/src/hooks/use-all-projects.ts new file mode 100644 index 0000000..d116892 --- /dev/null +++ b/src/hooks/use-all-projects.ts @@ -0,0 +1,8 @@ +import { db } from '@/lib/db'; +import { useLiveQuery } from 'dexie-react-hooks'; + +export function useAllProjects() { + const allProjects = useLiveQuery(() => db.projects.toCollection().keys(), []); + + return allProjects as (string[] | undefined); +} diff --git a/src/hooks/use-confirmation.ts b/src/hooks/use-confirmation.ts new file mode 100644 index 0000000..2eaf8a4 --- /dev/null +++ b/src/hooks/use-confirmation.ts @@ -0,0 +1,62 @@ +import { atom, useAtom } from 'jotai'; +import { z } from 'zod'; + +const createConfirmationSchema = z.object({ + open: z.boolean().default(true), + title: z.string(), + description: z.string().optional(), + type: z + .union([z.literal('default'), z.literal('destructive')]) + .default('default'), + cancelText: z.string().default('Cancel'), + confirmText: z.string().default('Confirm'), + onConfirm: z.function().returns(z.promise(z.void()).or(z.void())).optional(), + onCancel: z + .function() + .returns(z.void()) + .default(() => () => {}), + resolve: z.function().returns(z.void()).optional(), +}); + +export type ConfirmationAtom = z.infer; + +const confirmationAtom = atom(null); + +export default function useConfirmation() { + const [confirmation, setConfirmation] = useAtom(confirmationAtom); + + const createConfirmation = async ( + confirmation: z.input, + ) => { + await new Promise(resolve => { + const confirmationData = createConfirmationSchema.parse({ + ...confirmation, + resolve, + }); + setConfirmation(confirmationData); + }); + }; + + const showConfirmation = () => { + if (!confirmation) return; + setConfirmation({ + ...confirmation, + open: true, + }); + }; + + const hideConfirmation = () => { + if (!confirmation) return; + setConfirmation({ + ...confirmation, + open: false, + }); + }; + + return { + confirmation, + showConfirmation, + hideConfirmation, + createConfirmation, + }; +} diff --git a/src/hooks/use-edges-atom.ts b/src/hooks/use-edges-atom.ts index cf60e7f..2bd8375 100644 --- a/src/hooks/use-edges-atom.ts +++ b/src/hooks/use-edges-atom.ts @@ -78,8 +78,15 @@ export function useEdgesAtom() { const removeEdge = useCallback( (edgeId: string) => { setEdges(prev => prev.filter(edge => edge.id !== edgeId)); + + const mirolang = new MiroLang(code); + + const updatedWithoutRelationship = mirolang.removeRelationship(edgeId); + + setCode(prev => updatedWithoutRelationship ?? prev); + }, - [setEdges], + [setEdges, setCode, code], ); const onEdgesChange = useCallback( @@ -91,7 +98,6 @@ export function useEdgesAtom() { useEffect(() => { console.log('use-edges-atom.ts'); - console.log('edges', edges); }, [edges, tables]); return { diff --git a/src/hooks/use-import-export.ts b/src/hooks/use-import-export.ts index 7f1d3ef..9a93d74 100644 --- a/src/hooks/use-import-export.ts +++ b/src/hooks/use-import-export.ts @@ -1,29 +1,28 @@ -import { edgesAtom } from '@/jotai/edges-atom'; -import { mirolangAtom } from '@/jotai/miro-lang-atom'; -import { nodesAtom } from '@/jotai/nodes-atom'; +import { projectAtom } from '@/jotai/project-atom'; import { useAtom } from 'jotai'; import { useCallback } from 'react'; +import { getRectOfNodes, getTransformForBounds } from 'reactflow'; +import { toPng } from 'html-to-image'; +import { shallowCheckProject } from '@/lib/utils'; export function useImportExport() { - const [code, setCode] = useAtom(mirolangAtom); - const [nodes, setNode] = useAtom(nodesAtom); - const [edges, setEdge] = useAtom(edgesAtom); + const [project, setProjectRaw] = useAtom(projectAtom); const exportToJson = useCallback(() => { - const data = JSON.stringify({ code, nodes, edges }); + const data = JSON.stringify(project); - const a = document.createElement('a'); + const anchor = document.createElement('a'); - a.href = URL.createObjectURL(new Blob([data], { type: 'text/plain' })); + anchor.href = URL.createObjectURL(new Blob([data], { type: 'text/plain' })); - a.setAttribute('download', 'miro-lang.json'); + anchor.setAttribute('download', 'miro-lang.json'); - document.body.appendChild(a); + document.body.appendChild(anchor); - a.click(); + anchor.click(); - document.body.removeChild(a); - }, [code, nodes, edges]); + document.body.removeChild(anchor); + }, [project]); const importFromJson = useCallback(() => { const input = document.createElement('input'); @@ -43,10 +42,23 @@ export function useImportExport() { const data = reader.result as string; try { - const { code, nodes, edges } = JSON.parse(data); - setCode(code); - setNode(nodes); - setEdge(edges); + const project = JSON.parse(data); + + if (!shallowCheckProject(project)) return; + + setProjectRaw(prev => { + if (!prev) { + return project; + } else { + return { + ...prev, + code: project.code, + edges: project.edges, + nodes: project.nodes, + tables: project.tables, + }; + } + }); } catch (error) { console.error(error); } @@ -60,7 +72,59 @@ export function useImportExport() { input.click(); document.body.removeChild(input); - }, [setCode, setNode, setEdge]); + }, [setProjectRaw]); + + const exportToPng = useCallback(() => { + const getDownloadTime = () => { + const now = new Date(Date.now()); + const date_now = now + .toLocaleTimeString('en-US', { + hour: '2-digit', + minute: '2-digit', + }) + .replaceAll(':', '-') + .replaceAll(' ', ''); + + return date_now; + }; + + const downloadImage = (dataUrl: string) => { + const anchor = document.createElement('a'); + anchor.setAttribute( + 'download', + `miracle-diagram-${getDownloadTime()}.png`, + ); + anchor.setAttribute('href', dataUrl); + anchor.click(); + }; - return { exportToJson, importFromJson }; + const imgWidth = 1024; + const imgHeight = 768; + + // calculate transform so that all nodes will be visible when using `getPng()` + if (project) { + const nodes = project.nodes; + const nodesBounds = getRectOfNodes(nodes); + const transform = getTransformForBounds( + nodesBounds, + imgWidth, + imgHeight, + 0.5, + 2, + ); + + toPng(document.querySelector('.react-flow__viewport') as HTMLElement, { + backgroundColor: '#222222', + width: imgWidth, + height: imgHeight, + style: { + width: `${imgWidth}`, + height: `${imgHeight}`, + transform: `translate(${transform[0]}px, ${transform[1]}px) scale(${transform[2]})`, + }, + }).then(dataUrl => downloadImage(dataUrl)); + } + }, [project]); + + return { exportToJson, importFromJson, exportToPng }; } diff --git a/src/hooks/use-mirolang.ts b/src/hooks/use-mirolang.ts index efc3680..56be938 100644 --- a/src/hooks/use-mirolang.ts +++ b/src/hooks/use-mirolang.ts @@ -12,6 +12,10 @@ export function useMirolang(options = { leading: false, ms: 275 }) { useEffect(() => clearTimeout, []); + useEffect(() => { + console.log('use-mirolang.ts'); + }, [code]); + const debouncedSetValue = (newValue: string) => { clearTimeout(); if (leadingRef.current && options.leading) { diff --git a/src/hooks/use-nodes-atom.ts b/src/hooks/use-nodes-atom.ts index 552d08a..f073c84 100644 --- a/src/hooks/use-nodes-atom.ts +++ b/src/hooks/use-nodes-atom.ts @@ -1,7 +1,7 @@ import { nodesAtom } from '@/jotai/nodes-atom'; import { Table } from '@/lib/table'; import { useAtom } from 'jotai'; -import { useCallback } from 'react'; +import { useCallback, useEffect } from 'react'; import { Node, NodeChange, applyNodeChanges } from 'reactflow'; export function useNodesAtom() { @@ -46,6 +46,10 @@ export function useNodesAtom() { [nodes, setNodes], ); + useEffect(() => { + console.log('use-nodes-atom.ts'); + }, [nodes]); + return { nodes, setNodes, diff --git a/src/hooks/use-project.ts b/src/hooks/use-project.ts new file mode 100644 index 0000000..ba8c8f2 --- /dev/null +++ b/src/hooks/use-project.ts @@ -0,0 +1,352 @@ +import { mirolangAtom } from '@/jotai/miro-lang-atom'; +import { Project, currentProjectAtom, projectAtom } from '@/jotai/project-atom'; +import { MiroLang } from '@/lib/lang-miro/parser'; +import { + Relationship, + Table, + extractRelationshipsFromEdges, + extractRelationshipsFromTables, + generateRelationshipId, + getRelationshipsChanges, + getTablesChanges, + isValidRelationship, +} from '@/lib/table'; +import { useAtom } from 'jotai'; +import { useCallback, useEffect } from 'react'; +import { + Connection, + Edge, + EdgeChange, + Node, + NodeChange, + addEdge, + applyEdgeChanges, + applyNodeChanges, +} from 'reactflow'; +import { db } from '@/lib/db'; + +type ProjectKey = keyof Project; + +export function useProject() { + const [project, setProjectRaw] = useAtom(projectAtom); + const [code, setCode] = useAtom(mirolangAtom); + const [currentProject] = useAtom(currentProjectAtom); + + useEffect(() => { + if (!currentProject) return; + + db.projects + .get(currentProject) + .then(found => { + if (found) { + setProjectRaw(found); + setCode(found.code); + } else { + setProjectRaw({ + id: 'example', + code: '', + nodes: [], + edges: [], + tables: [], + }); + } + }) + .catch(err => { + console.error(err); + }); + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [currentProject]); + + useEffect(() => { + if (!project) return; + + db.projects.put(project); + }, [project]); + + // ---- Nodes Handlers ---- // + const setProject = useCallback( + ( + projectKey: T, + handler: ((prev: Project[T]) => Project[T]) | Project[T], + ) => { + setProjectRaw(prev => { + if (!prev) return prev; + + if (typeof handler === 'function') { + const data = handler(prev[projectKey]); + return { + ...prev, + [projectKey]: data, + }; + } else { + return { + ...prev, + [projectKey]: handler, + }; + } + }); + }, + [setProjectRaw], + ); + + const addNode = useCallback( + (input: Node) => { + setProject('nodes', prev => [ + ...prev, + { ...input, type: 'floating-node' }, + ]); + }, + [setProject], + ); + + const removeNode = useCallback( + (id: string) => { + setProject('nodes', prev => prev.filter(node => node.id !== id)); + }, + [setProject], + ); + + const onNodesChange = useCallback( + (changes: NodeChange[]) => { + setProject('nodes', prev => applyNodeChanges(changes, prev)); + }, + [setProject], + ); + + const updateNodesFromTables = useCallback( + (tables: Array) => { + if (!project) return; + + const updatedNodes = project.nodes.map(node => { + const updatedTable = tables.find(table => table.name === node.id); + if (updatedTable) { + return { + ...node, + data: updatedTable, + }; + } + return node; + }); + + setProject('nodes', updatedNodes); + }, + [project, setProject], + ); + + // ---- Edges Handlers ---- // + const connectEdge = useCallback( + (params: Edge | Connection) => { + if (!project) return; + + const relationship: Relationship = { + source: { + tableName: params.source!, + columnName: params.sourceHandle?.split('-')[0] ?? '', + }, + target: { + tableName: params.target!, + columnName: params.targetHandle?.split('-')[0] ?? '', + }, + }; + + if (!isValidRelationship({ relationship, tables: project.tables })) + return; + + const id = generateRelationshipId(relationship); + + setProject('edges', prev => + addEdge( + { ...params, type: 'button-edge', id, data: relationship }, + prev, + ), + ); + }, + [setProject, project], + ); + + const onDragConnect = useCallback( + (params: Edge | Connection) => { + if (!project) return; + + const relationship: Relationship = { + source: { + tableName: params.source!, + columnName: params.sourceHandle?.split('-')[0] ?? '', + }, + target: { + tableName: params.target!, + columnName: params.targetHandle?.split('-')[0] ?? '', + }, + }; + + if (!isValidRelationship({ relationship, tables: project.tables })) + return; + + const mirolang = new MiroLang(code); + + const updatedWithRelationship = mirolang.addRelationship(relationship); + + setCode(prev => updatedWithRelationship ?? prev); + }, + [project, setCode, code], + ); + + const removeEdge = useCallback( + (edgeId: string) => { + if (!project) return; + + // const mirolang = new MiroLang(code); + + // const updatedWithoutRelationship = mirolang.removeRelationship(edgeId); + + // setCode(prev => updatedWithoutRelationship ?? prev); + + setProject('edges', prev => prev.filter(edge => edge.id !== edgeId)); + }, + [project, setProject], + ); + + const handldeRemoveEdge = useCallback( + (edgeId: string) => { + if (!project) return; + + const mirolang = new MiroLang(code); + + const updatedWithoutRelationship = mirolang.removeRelationship(edgeId); + console.log('updatedWithoutRelationship', updatedWithoutRelationship); + + setCode(prev => updatedWithoutRelationship ?? prev); + }, + [project, code, setCode], + ); + + const onEdgesChange = useCallback( + (changes: EdgeChange[]) => { + setProject('edges', prev => applyEdgeChanges(changes, prev)); + }, + [setProject], + ); + + // ---- Tables Handlers ---- // + const addTable = useCallback( + (table: Table) => { + setProject('tables', prev => [...prev, table]); + addNode({ + id: table.name, + position: { + x: 200, + y: 200, + }, + data: table, + }); + }, + [addNode, setProject], + ); + + const removeTable = useCallback( + (tableName: string) => { + setProject('tables', prev => + prev.filter(table => table.name !== tableName), + ); + removeNode(tableName); + }, + [setProject, removeNode], + ); + + const handleTableChanges = useCallback( + (newTables: Table[]) => { + if (!project) return; + + const { addedTables, updatedTables, removedTables } = getTablesChanges({ + newTableArray: newTables, + oldTableArray: project.tables, + }); + + addedTables.forEach(table => { + addNode({ + id: table.name, + data: table, + position: { + x: 200, + y: 200, + }, + }); + }); + + removedTables.forEach(table => { + removeNode(table.name); + }); + + updatedTables.forEach(table => { + updateNodesFromTables([table]); + }); + + const newRelationships = extractRelationshipsFromTables(newTables); + const oldRelationships = extractRelationshipsFromEdges(project.edges); + + const { addedRelationships, removedRelationships } = + getRelationshipsChanges({ + newRelationships, + oldRelationships, + }); + + removedRelationships.forEach(relationship => { + removeEdge(generateRelationshipId(relationship)); + }); + + addedRelationships.forEach(relationship => { + connectEdge({ + id: generateRelationshipId(relationship), + source: relationship.source.tableName, + sourceHandle: `${relationship.source.columnName}-right`, + target: relationship.target.tableName, + targetHandle: `${relationship.target.columnName}-left`, + data: relationship, + type: 'button-edge', + }); + }); + + setProject('tables', newTables); + }, + [ + removeEdge, + project, + addNode, + removeNode, + updateNodesFromTables, + connectEdge, + setProject, + ], + ); + + useEffect(() => { + console.log('use-table.ts'); + + // Parse new tables + const tables = new MiroLang(code).parse(); + + // Handle changes in tables + handleTableChanges(tables); + + // Update project code + setProject('code', code); + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [code]); + + return { + project, + setProject, + addNode, + removeNode, + onNodesChange, + connectEdge, + onDragConnect, + removeEdge, + onEdgesChange, + addTable, + removeTable, + handldeRemoveEdge, + }; +} diff --git a/src/hooks/use-tables.ts b/src/hooks/use-tables.ts index db08682..d2071fc 100644 --- a/src/hooks/use-tables.ts +++ b/src/hooks/use-tables.ts @@ -1,11 +1,9 @@ import { tablesAtom } from '@/jotai/table-atom'; import { - Relationship, Table, extractRelationshipsFromEdges, extractRelationshipsFromTables, generateRelationshipId, - getRelationshipsChanges, getTablesChanges, } from '@/lib/table'; import { useAtom } from 'jotai'; diff --git a/src/jotai/project-atom.ts b/src/jotai/project-atom.ts new file mode 100644 index 0000000..d1611ce --- /dev/null +++ b/src/jotai/project-atom.ts @@ -0,0 +1,16 @@ +import { atom } from 'jotai'; +import { Edge, Node } from 'reactflow'; +import { atomWithStorage } from 'jotai/utils'; +import { Table } from '@/lib/table'; + +export type Project = { + id: string; + code: string; + nodes: Array; + edges: Array; + tables: Array
; +}; + +export const projectAtom = atom(undefined); + +export const currentProjectAtom = atomWithStorage('currentProject', 'example'); diff --git a/src/lib/db.ts b/src/lib/db.ts new file mode 100644 index 0000000..90e60d5 --- /dev/null +++ b/src/lib/db.ts @@ -0,0 +1,15 @@ +import { Project } from '@/jotai/project-atom'; +import Dexie, { Table } from 'dexie'; + +export class MiracleDiagramDB extends Dexie { + projects!: Table; + + constructor() { + super('MiracleDiagramDB'); + this.version(1).stores({ + projects: 'id, code, nodes, edges, tables', + }); + } +} + +export const db = new MiracleDiagramDB(); diff --git a/src/lib/lang-miro/completions.ts b/src/lib/lang-miro/completions.ts new file mode 100644 index 0000000..0a0c24f --- /dev/null +++ b/src/lib/lang-miro/completions.ts @@ -0,0 +1,149 @@ +import { allIconNames } from '@/components/icons'; +import { CompletionContext, CompletionResult } from '@codemirror/autocomplete'; +import { syntaxTree } from '@codemirror/language'; +import { SyntaxNode } from '@lezer/common'; + +export const completion = ( + handler: ( + context: CompletionContext, + node: SyntaxNode, + ) => CompletionResult | null, +) => { + return (context: CompletionContext) => { + const { state, pos } = context; + + const node = syntaxTree(state).resolve(pos, -1); + + return handler(context, node); + }; +}; + +export const iconCompletion = completion((context, node) => { + if (node.nextSibling?.name === '{' || node.parent?.name === 'Icon') { + const options = allIconNames.map(icon => ({ + label: `i-${icon}`, + type: 'icon', + })); + + const from = node.name === 'IconName' ? node.from - 2 : node.from; + + return { + from, + options, + }; + } + + return null; +}); + +const defaultTypes = [ + { label: 'string', type: 'type' }, + { label: 'number', type: 'type' }, + { label: 'boolean', type: 'type' }, + { label: 'int', type: 'type' }, + { label: 'float', type: 'type' }, + { label: 'date', type: 'type' }, +]; + +export const miroTypeCompletion = completion((context, node) => { + if (!['Column', 'ColumnType'].includes(node.type.name)) return null; + + const filter = node.type.name === 'ColumnType'; + + const from = node.type.name === 'ColumnType' ? node.from : context.pos; + + const tree = syntaxTree(context.state).resolveInner(0); + + const tables = tree.getChildren('Table'); + + const columns = tables.flatMap(table => table.getChildren('Column')); + + const columnTypes = columns.flatMap(column => + column.getChildren('ColumnType'), + ); + + const typeStrings = columnTypes.map(type => + context.state.sliceDoc(type.from, type.to), + ); + + // remove duplicates and filter the last one out + const removedDuplicates = Array.from(new Set(typeStrings)).slice(0, -1); + + const types = removedDuplicates.map(type => ({ + label: type, + type: 'type', + })); + + return { + from, + filter, + options: [defaultTypes, types].flat(), + }; +}); + +export const miroRelationshipCompletion = completion((context, node) => { + if (node.parent?.name !== 'Relationship' && node.name !== 'Relationship') + return null; + + const tree = syntaxTree(context.state).resolveInner(0); + + const tables = tree.getChildren('Table').map(tableNode => { + const tableNameNode = tableNode.getChild('TableName'); + const tableName = context.state.sliceDoc( + tableNameNode?.from, + tableNameNode?.to, + ); + const columns = tableNode + .getChildren('Column') + .map(columnNode => + context.state.sliceDoc( + columnNode.firstChild?.from, + columnNode.firstChild?.to, + ), + ); + + return { + name: tableName, + columns, + }; + }); + + let options: Array<{ label: string; type: string }> = []; + let from: number; + + if (node.name === 'TableName' || node.name === 'Relationship') { + from = node.name === 'TableName' ? node.from : context.pos; + options = tables.map(table => ({ + label: table.name, + type: 'table', + })); + } else if (node.name === 'ColumnName' || node.name === '.') { + from = node.name === 'ColumnName' ? node.from : context.pos; + + const tableNameNode = + node.name === 'ColumnName' + ? node.prevSibling?.prevSibling + : node.prevSibling; + + const tableName = context.state.sliceDoc( + tableNameNode?.from, + tableNameNode?.to, + ); + + const table = tables.find(table => table.name === tableName); + + if (table) { + options = table.columns.map(column => ({ + label: column, + type: 'column', + })); + } + } else { + return null; + } + + return { + from, + options, + }; +}); diff --git a/src/lib/lang-miro/grammar/miro.grammar b/src/lib/lang-miro/grammar/miro.grammar index a0e2606..5c52e20 100644 --- a/src/lib/lang-miro/grammar/miro.grammar +++ b/src/lib/lang-miro/grammar/miro.grammar @@ -24,8 +24,9 @@ Relationship { "->" TableName "." ColumnName } space { $[\r\t\n ] } Identifier { $[\[A-Za-z_?\]]+ } Name { $[A-Za-z_]+ } - IconName { $[\[A-Za-z_?\-\]]+ } + IconName { $[\[A-Za-z0-9_?\-\]]+ } "{" "}" + "." } @skip { space } diff --git a/src/lib/lang-miro/grammar/miro.ts b/src/lib/lang-miro/grammar/miro.ts index a9efe01..7359de6 100644 --- a/src/lib/lang-miro/grammar/miro.ts +++ b/src/lib/lang-miro/grammar/miro.ts @@ -3,15 +3,15 @@ import {LRParser} from "@lezer/lr" import {MiroStyleTags} from "../highlight" export const parser = LRParser.deserialize({ version: 14, - states: "%QOVQPOOO[QQO'#C_QOQPOOOdQSO'#CaOiQWO,58yOqQPO,58yOOQO,58{,58{OvQ`O'#CdO{QPO'#CoOOQO1G.e1G.eO!TQPO1G.eO!YQWO1G.eO!bQpO,59OO!pQWO'#CjO!{QPO,59ZOOQO7+$P7+$PO#TQPO7+$PO#YQpO'#CpO#hQPO'#ChOOQO1G.j1G.jO#mQPO1G.jOOQO,59U,59UOOQO-E6h-E6hOOQO< ({ - label: `i-${icon}`, - type: 'icon', - })), -); - -const miroTypeCompletion = ( - context: CompletionContext, -): CompletionResult | null => { - // match if there is a word and space - const match = context.matchBefore(/\w\s+\w+/); - - if (match) { - return { - from: context.pos, - options: [ - { label: 'string', type: 'type' }, - { label: 'number', type: 'type' }, - { label: 'boolean', type: 'type' }, - { label: 'int', type: 'type' }, - { label: 'float', type: 'type' }, - { label: 'date', type: 'type' }, - ], - }; - } - - return null; -}; - -const MiroLinter = linter(view => { +export const MiroLinter = linter(view => { let diagnostics: Diagnostic[] = []; syntaxTree(view.state) .cursor() @@ -93,16 +65,9 @@ const MiroLinter = linter(view => { to: node.to, severity: 'error', message: 'Syntax Error!', - actions: [ - { - name: 'Remove', - apply(view, from, to) { - view.dispatch({ changes: { from, to } }); - }, - }, - ], }); }); + return diagnostics; }); @@ -111,7 +76,44 @@ export function miro() { MiroHighlighting, MiroLinter, autocompletion({ - override: [miroTypeCompletion, completeAnyWord, iconCompletion], + override: [ + miroTypeCompletion, + iconCompletion, + miroRelationshipCompletion, + ], }), + keymap.of([ + { + key: 'Tab', + preventDefault: true, + run: target => { + if (completionStatus(target.state) === 'active') { + moveCompletionSelection(true)(target); + } else { + insertTab(target); + } + return true; + }, + }, + { + key: 'Enter', + preventDefault: true, + run: target => { + if (completionStatus(target.state) === 'active') { + acceptCompletion(target); + } else { + insertNewlineAndIndent(target); + } + return true; + }, + }, + { + key: 'Ctrl-Space', + mac: 'Cmd-i', + preventDefault: true, + run: startCompletion, + }, + ...defaultKeymap, + ]), ]); } diff --git a/src/lib/lang-miro/parser.ts b/src/lib/lang-miro/parser.ts index 450e565..713d9c1 100644 --- a/src/lib/lang-miro/parser.ts +++ b/src/lib/lang-miro/parser.ts @@ -1,4 +1,9 @@ -import { Relationship, Table, relationshipSchema } from '@/lib/table'; +import { + Relationship, + Table, + getRelationshipFromEdgeId, + relationshipSchema, +} from '@/lib/table'; import { SyntaxNode, Tree } from '@lezer/common'; import { parser } from './grammar/miro'; @@ -115,4 +120,39 @@ export class MiroLang { return updatedColumnCode; } } + + removeRelationship(edgeId: string) { + const relationship = getRelationshipFromEdgeId(edgeId); + + const tableNode = this.tree + .resolveInner(0) + .getChildren('Table') + .find(tableNode => { + return ( + this.nodeToString(tableNode.firstChild!) === + relationship.source.tableName + ); + }); + + if (!tableNode) return; + + const columnNode = tableNode.getChildren('Column').find(columnNode => { + return ( + this.nodeToString(columnNode.firstChild!) === + relationship.source.columnName + ); + }); + + if (!columnNode) return; + + const relationshipNode = columnNode.getChild('Relationship'); + + if (!relationshipNode) return; + + const updatedColumnCode = + this.miroLang.slice(0, relationshipNode.from) + + this.miroLang.slice(relationshipNode.to); + + return updatedColumnCode; + } } diff --git a/src/lib/table.ts b/src/lib/table.ts index 9d6b96a..4b2f910 100644 --- a/src/lib/table.ts +++ b/src/lib/table.ts @@ -66,7 +66,24 @@ export function extractRelationshipsFromEdges( export function generateRelationshipId(relationship: Relationship): string { console.log('relationship', relationship); - return `${relationship.source.tableName}_${relationship.source.columnName}-${relationship.target.tableName}_${relationship.target.columnName}`; + return `${relationship.source.tableName}#${relationship.source.columnName}&${relationship.target.tableName}#${relationship.target.columnName}`; +} + +export function getRelationshipFromEdgeId(edgeId: string): Relationship { + const [source, target] = edgeId.split('&'); + const [sourceTable, sourceColumn] = source.split('#'); + const [targetTable, targetColumn] = target.split('#'); + + return { + source: { + tableName: sourceTable, + columnName: sourceColumn, + }, + target: { + tableName: targetTable, + columnName: targetColumn, + }, + }; } /** @@ -133,22 +150,23 @@ export function getRelationshipsChanges({ oldRelationships: Relationship[]; newRelationships: Relationship[]; }): RelationshipChanges { - const relationshipMap = new Map(); - - // Create a map of old relationships for efficient lookup - for (const relationship of oldRelationships) { - relationshipMap.set(generateRelationshipId(relationship), relationship); - } - - const addedRelationships: Relationship[] = newRelationships.filter( - newRelationship => { - return !relationshipMap.has(generateRelationshipId(newRelationship)); - }, + // get the deleted relationships and added relationships + const addedRelationships = newRelationships.filter( + newRelationship => + !oldRelationships.find( + oldRelationship => + generateRelationshipId(oldRelationship) === + generateRelationshipId(newRelationship), + ), ); - const removedRelationships: Relationship[] = oldRelationships.filter( - oldRelationship => { - return !relationshipMap.has(generateRelationshipId(oldRelationship)); - }, + + const removedRelationships = oldRelationships.filter( + oldRelationship => + !newRelationships.find( + newRelationship => + generateRelationshipId(oldRelationship) === + generateRelationshipId(newRelationship), + ), ); return { addedRelationships, removedRelationships }; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 4b03467..68850c9 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,3 +1,4 @@ +import { Project } from '@/jotai/project-atom'; import { type ClassValue, clsx } from 'clsx'; import { twMerge } from 'tailwind-merge'; @@ -16,3 +17,19 @@ export function isEqual(arr1: any[], arr2: any[]) { return true; } + +export function shallowCheckProject(project: unknown): project is Project { + if (!project) return false; + + if (typeof project !== 'object') return false; + + if (!('code' in project)) return false; + + if (!('nodes' in project)) return false; + + if (!('edges' in project)) return false; + + if (!('tables' in project)) return false; + + return true; +} diff --git a/src/styles/globals.css b/src/styles/globals.css index 34f819e..35914bc 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -75,6 +75,67 @@ } } +.no-scrollbar::-webkit-scrollbar { + display: none; +} + +.mac-style-scrollbar::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +.mac-style-scrollbar::-webkit-scrollbar-thumb { + background-color: rgba(0, 0, 0, 0.2); + border-radius: 8px; +} + +.mac-style-scrollbar::-webkit-scrollbar-track { + background-color: transparent; +} + .cm-lintRange-error { @apply bg-destructive/25 rounded; } + +/* style code mirror suggestion */ +.cm-tooltip-autocomplete { + @apply rounded-lg p-1 !border-border; +} + +.cm-tooltip-autocomplete > ul { + @apply no-scrollbar; +} + +.cm-tooltip-autocomplete > ul > li { + @apply rounded; +} + +.cm-completionMatchedText { + @apply !no-underline font-semibold; +} + +.cm-tooltip-autocomplete ul li[aria-selected] { + @apply !bg-gradient-to-br from-blue-400 to-blue-600 dark:from-blue-700 dark:to-blue-900; +} + +/* Icon for completion option */ +.cm-completionIcon-icon::after { + content: '✨'; +} + +.cm-completionIcon-type::after { + content: '#️⃣' !important; +} + +.cm-completionIcon-text::after { + content: '🔥' !important; + font-size: 1rem !important; +} + +.cm-completionIcon-table::after { + content: '📂' !important; +} + +.cm-completionIcon-column::after { + content: '🏷️' !important; +} diff --git a/tailwind.config.js b/tailwind.config.js index 6bfb6a3..44b7b4d 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,73 +1,71 @@ /** @type {import('tailwindcss').Config} */ module.exports = { - darkMode: ["class"], - content: [ - './src/**/*.{ts,tsx}', - ], + darkMode: ['class'], + content: ['./src/**/*.{ts,tsx}'], theme: { container: { center: true, - padding: "2rem", + padding: '2rem', screens: { - "2xl": "1400px", + '2xl': '1400px', }, }, extend: { colors: { - border: "hsl(var(--border))", - input: "hsl(var(--input))", - ring: "hsl(var(--ring))", - background: "hsl(var(--background))", - foreground: "hsl(var(--foreground))", + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', primary: { - DEFAULT: "hsl(var(--primary))", - foreground: "hsl(var(--primary-foreground))", + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))', }, secondary: { - DEFAULT: "hsl(var(--secondary))", - foreground: "hsl(var(--secondary-foreground))", + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))', }, destructive: { - DEFAULT: "hsl(var(--destructive))", - foreground: "hsl(var(--destructive-foreground))", + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))', }, muted: { - DEFAULT: "hsl(var(--muted))", - foreground: "hsl(var(--muted-foreground))", + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))', }, accent: { - DEFAULT: "hsl(var(--accent))", - foreground: "hsl(var(--accent-foreground))", + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))', }, popover: { - DEFAULT: "hsl(var(--popover))", - foreground: "hsl(var(--popover-foreground))", + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))', }, card: { - DEFAULT: "hsl(var(--card))", - foreground: "hsl(var(--card-foreground))", + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))', }, }, borderRadius: { - lg: "var(--radius)", - md: "calc(var(--radius) - 2px)", - sm: "calc(var(--radius) - 4px)", + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)', }, keyframes: { - "accordion-down": { + 'accordion-down': { from: { height: 0 }, - to: { height: "var(--radix-accordion-content-height)" }, + to: { height: 'var(--radix-accordion-content-height)' }, }, - "accordion-up": { - from: { height: "var(--radix-accordion-content-height)" }, + 'accordion-up': { + from: { height: 'var(--radix-accordion-content-height)' }, to: { height: 0 }, }, }, animation: { - "accordion-down": "accordion-down 0.2s ease-out", - "accordion-up": "accordion-up 0.2s ease-out", + 'accordion-down': 'accordion-down 0.2s ease-out', + 'accordion-up': 'accordion-up 0.2s ease-out', }, }, }, - plugins: [require("tailwindcss-animate")], -} \ No newline at end of file + plugins: [require('tailwindcss-animate'), require('@tailwindcss/typography')], +};