From fb0357090155c6dd6ed0c7850d25982d0ad91526 Mon Sep 17 00:00:00 2001 From: Ben Zhou Date: Tue, 29 Sep 2026 14:30:19 -0400 Subject: [PATCH 1/5] Set up Next.js with Bun, Tailwind, and Fluid Functionalism --- .eslintignore | 3 - .eslintrc.js | 77 - .gitignore | 10 +- bun.lock | 1186 ++++++++++++ components.json | 20 + eslint.config.mjs | 10 + next.config.ts | 10 + package.json | 107 +- postcss.config.mjs | 1 + src/app/globals.css | 373 ++++ src/app/layout.tsx | 12 + src/app/page.tsx | 3 + src/components/fluid-hover-highlight.tsx | 138 ++ src/components/ui/accordion.tsx | 961 ++++++++++ src/components/ui/ask-user-questions.tsx | 1886 +++++++++++++++++++ src/components/ui/badge.tsx | 140 ++ src/components/ui/button.tsx | 283 +++ src/components/ui/card.tsx | 1026 ++++++++++ src/components/ui/carousel-dots.tsx | 243 +++ src/components/ui/chat-message.tsx | 141 ++ src/components/ui/checkbox-group.tsx | 380 ++++ src/components/ui/color-picker.tsx | 2158 ++++++++++++++++++++++ src/components/ui/combobox.tsx | 1526 +++++++++++++++ src/components/ui/command-menu.tsx | 1575 ++++++++++++++++ src/components/ui/dialog.tsx | 279 +++ src/components/ui/dropdown-search.tsx | 370 ++++ src/components/ui/dropdown.tsx | 760 ++++++++ src/components/ui/file-thumbnail.tsx | 179 ++ src/components/ui/input-copy.tsx | 397 ++++ src/components/ui/input-group.tsx | 281 +++ src/components/ui/input-message.tsx | 1385 ++++++++++++++ src/components/ui/menu-item.tsx | 315 ++++ src/components/ui/mobile-drawer.tsx | 123 ++ src/components/ui/radio-group.tsx | 424 +++++ src/components/ui/scroll-area.tsx | 212 +++ src/components/ui/select.tsx | 836 +++++++++ src/components/ui/sidebar-core.tsx | 1621 ++++++++++++++++ src/components/ui/sidebar-menu.tsx | 1442 +++++++++++++++ src/components/ui/sidebar.tsx | 331 ++++ src/components/ui/slider.tsx | 1744 +++++++++++++++++ src/components/ui/switch.tsx | 284 +++ src/components/ui/table.tsx | 205 ++ src/components/ui/tabs-subtle.tsx | 452 +++++ src/components/ui/tabs.tsx | 537 ++++++ src/components/ui/thinking-indicator.tsx | 126 ++ src/components/ui/thinking-steps.tsx | 617 +++++++ src/components/ui/tooltip.tsx | 254 +++ src/hooks/use-fluid-hover.ts | 552 ++++++ src/hooks/use-keyboard-nav-gate.ts | 37 + src/hooks/use-merge-split.tsx | 421 +++++ src/hooks/use-touch-primary.tsx | 35 + src/lib/elevated.tsx | 56 + src/lib/font-weight.ts | 6 + src/lib/icon-context.tsx | 186 ++ src/lib/popup.ts | 43 + src/lib/shape-context.tsx | 163 ++ src/lib/size-context.tsx | 194 ++ src/lib/springs.ts | 31 + src/lib/surface-classes.ts | 63 + src/lib/surface-context.tsx | 23 + src/lib/utils.ts | 1 + tsconfig.json | 21 + 62 files changed, 27142 insertions(+), 133 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc.js create mode 100644 bun.lock create mode 100644 components.json create mode 100644 eslint.config.mjs create mode 100644 next.config.ts create mode 100644 postcss.config.mjs create mode 100644 src/app/globals.css create mode 100644 src/app/layout.tsx create mode 100644 src/app/page.tsx create mode 100644 src/components/fluid-hover-highlight.tsx create mode 100644 src/components/ui/accordion.tsx create mode 100644 src/components/ui/ask-user-questions.tsx create mode 100644 src/components/ui/badge.tsx create mode 100644 src/components/ui/button.tsx create mode 100644 src/components/ui/card.tsx create mode 100644 src/components/ui/carousel-dots.tsx create mode 100644 src/components/ui/chat-message.tsx create mode 100644 src/components/ui/checkbox-group.tsx create mode 100644 src/components/ui/color-picker.tsx create mode 100644 src/components/ui/combobox.tsx create mode 100644 src/components/ui/command-menu.tsx create mode 100644 src/components/ui/dialog.tsx create mode 100644 src/components/ui/dropdown-search.tsx create mode 100644 src/components/ui/dropdown.tsx create mode 100644 src/components/ui/file-thumbnail.tsx create mode 100644 src/components/ui/input-copy.tsx create mode 100644 src/components/ui/input-group.tsx create mode 100644 src/components/ui/input-message.tsx create mode 100644 src/components/ui/menu-item.tsx create mode 100644 src/components/ui/mobile-drawer.tsx create mode 100644 src/components/ui/radio-group.tsx create mode 100644 src/components/ui/scroll-area.tsx create mode 100644 src/components/ui/select.tsx create mode 100644 src/components/ui/sidebar-core.tsx create mode 100644 src/components/ui/sidebar-menu.tsx create mode 100644 src/components/ui/sidebar.tsx create mode 100644 src/components/ui/slider.tsx create mode 100644 src/components/ui/switch.tsx create mode 100644 src/components/ui/table.tsx create mode 100644 src/components/ui/tabs-subtle.tsx create mode 100644 src/components/ui/tabs.tsx create mode 100644 src/components/ui/thinking-indicator.tsx create mode 100644 src/components/ui/thinking-steps.tsx create mode 100644 src/components/ui/tooltip.tsx create mode 100644 src/hooks/use-fluid-hover.ts create mode 100644 src/hooks/use-keyboard-nav-gate.ts create mode 100644 src/hooks/use-merge-split.tsx create mode 100644 src/hooks/use-touch-primary.tsx create mode 100644 src/lib/elevated.tsx create mode 100644 src/lib/font-weight.ts create mode 100644 src/lib/icon-context.tsx create mode 100644 src/lib/popup.ts create mode 100644 src/lib/shape-context.tsx create mode 100644 src/lib/size-context.tsx create mode 100644 src/lib/springs.ts create mode 100644 src/lib/surface-classes.ts create mode 100644 src/lib/surface-context.tsx create mode 100644 src/lib/utils.ts create mode 100644 tsconfig.json diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index ada4f49..0000000 --- a/.eslintignore +++ /dev/null @@ -1,3 +0,0 @@ -assets/css/style.css -assets/vendors -assets/js \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 20af983..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,77 +0,0 @@ -module.exports = { - root: true, - extends: [ - 'eslint:recommended', - ], - rules: { - indent: ['error', 'tab', { - SwitchCase: 1, - flatTernaryExpressions: true, - offsetTernaryExpressions: true, - }], - 'comma-dangle': ['error', 'always-multiline'], - 'comma-spacing': ['error', { before: false, after: true }], - 'func-call-spacing': ['error', 'never'], - 'keyword-spacing': ['error'], - 'no-extra-parens': ['error', 'all'], - 'no-extra-semi': ['error'], - 'no-trailing-spaces': ['error'], - 'object-curly-spacing': ['error', 'always'], - semi: ['error', 'always'], - 'space-before-blocks': ['error', 'always'], - 'space-before-function-paren': ['error', { - anonymous: 'always', - named: 'never', - asyncArrow: 'always', - }], - 'no-non-null-assertion': 'off', - quotes: ['error', 'single'], - 'no-tabs': 0, - 'max-len': ['warn', 120], - 'arrow-parens': ['error', 'as-needed'], - 'array-bracket-spacing': ['error', 'never'], - 'quote-props': ['error', 'as-needed'], - 'sort-imports': ['error', { - allowSeparatedGroups: true, - }], - 'max-statements-per-line': ['error', { - max: 1, - }], - 'object-curly-newline': ['error', { - multiline: true, - consistent: true, - }], - 'object-property-newline': ['error', { - allowAllPropertiesOnSameLine: true, - }], - 'operator-linebreak': ['error', 'before'], - 'multiline-ternary': ['error', 'always-multiline'], - 'space-in-parens': ['error', 'never'], - 'space-infix-ops': ['error'], - 'space-unary-ops': [ - 2, { - words: true, - nonwords: false, - }], - eqeqeq: ['error', 'always'], - 'no-var-requires': 'off', - 'no-empty': ['error', { allowEmptyCatch: true }], - 'prefer-const': ['error', { - destructuring: 'all', - }], - }, - parserOptions: { - ecmaVersion: 2022, - }, - env: { - node: true, - commonjs: true, - es2021: true, - browser: true, - }, - globals: { - CodeMirror: 'readonly', - editor: 'writable', - }, - -}; \ No newline at end of file diff --git a/.gitignore b/.gitignore index 119a2a5..2e5c575 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ # Add any directories, files, or patterns you don't want to be tracked by version control node_modules/ -package-lock.json .DS_Store .vscode *documentation/ @@ -18,4 +17,11 @@ oldindex.html .vercel .output vite.config.js.timestamp-* -vite.config.ts.timestamp-* \ No newline at end of file +vite.config.ts.timestamp-* + +# Next.js and test output +.next/ +out/ +*.tsbuildinfo +playwright-report/ +test-results/ diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..ae8c38c --- /dev/null +++ b/bun.lock @@ -0,0 +1,1186 @@ +{ + "lockfileVersion": 1, + "configVersion": 0, + "workspaces": { + "": { + "name": "btagscript-playground", + "dependencies": { + "@base-ui/react": "^1.8.0", + "@codemirror/language": "^6.12.4", + "@codemirror/view": "^6.43.13", + "@fontsource-variable/inter": "^5.3.0", + "@lezer/highlight": "^1.2.5", + "@radix-ui/react-accordion": "^1.2.20", + "@radix-ui/react-checkbox": "^1.3.11", + "@radix-ui/react-collapsible": "^1.1.20", + "@radix-ui/react-dialog": "^1.1.23", + "@radix-ui/react-dropdown-menu": "^2.1.24", + "@radix-ui/react-popover": "^1.1.23", + "@radix-ui/react-radio-group": "^1.4.7", + "@radix-ui/react-scroll-area": "^1.2.18", + "@radix-ui/react-select": "^2.3.7", + "@radix-ui/react-slider": "^1.4.7", + "@radix-ui/react-slot": "^1.3.3", + "@radix-ui/react-switch": "^1.3.7", + "@radix-ui/react-tabs": "^1.1.21", + "@radix-ui/react-tooltip": "^1.2.16", + "@uiw/react-codemirror": "^4.25.12", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cn": "^0.4.0", + "framer-motion": "^13.4.6", + "lucide-react": "^1.48.0", + "next": "^16.3.7", + "pdfjs-dist": "^6.3.289", + "react": "^19.3.0", + "react-dom": "^19.3.0", + "tailwind-merge": "^3.7.0", + "tw-animate-css": "^1.4.0", + }, + "devDependencies": { + "@eslint/compat": "^2.1.1", + "@playwright/test": "^1.63.0", + "@tailwindcss/postcss": "^4.3.3", + "@types/node": "^26.6.3", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", + "@typescript/native": "^7.0.2", + "eslint": "^10.11.0", + "eslint-config-next": "^16.3.7", + "tailwindcss": "^4.3.3", + "typescript": "^6.0.2", + }, + }, + }, + "packages": { + "@alloc/quick-lru": ["@alloc/quick-lru@5.3.0", "", {}, "sha512-U4+70Pc5ZS9osnCBCE5Jha/ciHM+Yp+CNMNC/7HvYbNRk1Ldd+f7qO65W5qfhu/TCv+/ozljlXXe9Nj8419DMA=="], + + "@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], + + "@babel/compat-data": ["@babel/compat-data@7.29.7", "", {}, "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg=="], + + "@babel/core": ["@babel/core@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-module-transforms": "^7.29.7", "@babel/helpers": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA=="], + + "@babel/generator": ["@babel/generator@7.29.8", "", { "dependencies": { "@babel/parser": "^7.29.8", "@babel/types": "^7.29.8", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.29.7", "", { "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.29.7", "", { "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.29.7", "", {}, "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw=="], + + "@babel/helpers": ["@babel/helpers@7.29.7", "", { "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg=="], + + "@babel/parser": ["@babel/parser@7.29.9", "", { "dependencies": { "@babel/types": "^7.29.8" }, "bin": "./bin/babel-parser.js" }, "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA=="], + + "@babel/runtime": ["@babel/runtime@7.29.7", "", {}, "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw=="], + + "@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], + + "@babel/traverse": ["@babel/traverse@7.29.8", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.8", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.8", "@babel/template": "^7.29.7", "@babel/types": "^7.29.8", "debug": "^4.3.1" } }, "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg=="], + + "@babel/types": ["@babel/types@7.29.8", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg=="], + + "@base-ui/react": ["@base-ui/react@1.8.0", "", { "dependencies": { "@babel/runtime": "^7.29.7", "@base-ui/utils": "0.4.0", "@floating-ui/react-dom": "^2.1.9", "@floating-ui/utils": "^0.2.12", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@date-fns/tz": "^1.2.0", "@types/react": "^17 || ^18 || ^19", "date-fns": "^4.0.0", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@date-fns/tz", "@types/react", "date-fns"] }, "sha512-P0/1sxo6SBVZOklKMIedvTWqw2s2IQzi9x5bIVsXu980cuSOD4NeuRSs+/L7LZQfDkZP/uRZyGPyfFl/B1oH+Q=="], + + "@base-ui/utils": ["@base-ui/utils@0.4.0", "", { "dependencies": { "@babel/runtime": "^7.29.7", "@floating-ui/utils": "^0.2.12", "reselect": "^5.2.0", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-bO9fz25kKtPf+aZVyfQrC0PDmJdmVni31W2hCS5/Owb+inwdIL3XU26pCPRPlt4LSxZrBgLwubXQXQlKaFEZzw=="], + + "@cacheable/memory": ["@cacheable/memory@2.2.0", "", { "dependencies": { "@cacheable/utils": "^2.5.0", "@keyv/bigmap": "^1.3.1", "hookified": "^1.15.1", "keyv": "^5.6.0" } }, "sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ=="], + + "@cacheable/utils": ["@cacheable/utils@2.5.0", "", { "dependencies": { "hashery": "^1.5.1", "keyv": "^5.6.0" } }, "sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA=="], + + "@codemirror/autocomplete": ["@codemirror/autocomplete@6.20.3", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0" } }, "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g=="], + + "@codemirror/commands": ["@codemirror/commands@6.11.1", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.7.0", "@codemirror/view": "^6.27.0", "@lezer/common": "^1.1.0" } }, "sha512-O/4hG3SC1YwcmQ0d2UVNDs+AsaNWd1iHVxbTeEBuqH+6bExAiPK3iS/BvpY6rZGURALv4ZD3sIgcCmRvw3ehBg=="], + + "@codemirror/language": ["@codemirror/language@6.12.4", "", { "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", "@lezer/common": "^1.5.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0", "style-mod": "^4.0.0" } }, "sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A=="], + + "@codemirror/lint": ["@codemirror/lint@6.9.7", "", { "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.42.0", "crelt": "^1.0.5" } }, "sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg=="], + + "@codemirror/search": ["@codemirror/search@6.7.2", "", { "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.37.0", "crelt": "^1.0.5" } }, "sha512-gUYkYhT2+n/+VGZ+8EzE5WFkYZUZYm1VOKDudIsNqh42uRVQJ0a6Yss9sdKT3MeOYfuL1N6AZA57oza0Oyr0LA=="], + + "@codemirror/state": ["@codemirror/state@6.7.6", "", { "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } }, "sha512-kAz+AncRtKuIknedxT1bq4XwXv4UowhbkHU1myPrtVb/jZtImWuV5BXzv5vK6i3kYACsdiZiQKFQQ5Mq7elW8w=="], + + "@codemirror/theme-one-dark": ["@codemirror/theme-one-dark@6.1.3", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", "@lezer/highlight": "^1.0.0" } }, "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA=="], + + "@codemirror/view": ["@codemirror/view@6.43.13", "", { "dependencies": { "@codemirror/state": "^6.7.0", "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, "sha512-sihaFrUzAsYBQsL9J2t69y8nfMQGwcYmggAZsk+kjPbjYZMyuf2hU8tUNTZ+P+isb6XRr8JE22TZlJxBoVdH1A=="], + + "@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], + + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.10.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], + + "@eslint/compat": ["@eslint/compat@2.1.1", "", { "dependencies": { "@eslint/core": "^1.2.1" }, "peerDependencies": { "eslint": "^8.40 || 9 || 10" }, "optionalPeers": ["eslint"] }, "sha512-rMcy8GSrwNzcISX/BlTDY/GLB4eCopEuy9woIls3To+15OLxykZrxxq+WUcylCPCQ6F4MujjBM1DX5V1aqI3Vw=="], + + "@eslint/config-array": ["@eslint/config-array@0.23.5", "", { "dependencies": { "@eslint/object-schema": "^3.0.5", "debug": "^4.3.1", "minimatch": "^10.2.4" } }, "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA=="], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.7.0", "", { "dependencies": { "@eslint/core": "^1.2.1" } }, "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw=="], + + "@eslint/core": ["@eslint/core@1.2.1", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ=="], + + "@eslint/object-schema": ["@eslint/object-schema@3.0.5", "", {}, "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw=="], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.7.3", "", { "dependencies": { "@eslint/core": "^1.2.1", "levn": "^0.4.1" } }, "sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q=="], + + "@floating-ui/core": ["@floating-ui/core@1.8.0", "", { "dependencies": { "@floating-ui/utils": "^0.2.12" } }, "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.8.0", "", { "dependencies": { "@floating-ui/core": "^1.8.0", "@floating-ui/utils": "^0.2.12" } }, "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg=="], + + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.9", "", { "dependencies": { "@floating-ui/dom": "^1.8.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.12", "", {}, "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww=="], + + "@fontsource-variable/inter": ["@fontsource-variable/inter@5.3.0", "", {}, "sha512-OupL48va4JNofb97w6NYeF9S7W/kHNKM0Er8Dem5nqi4jeOLrVJDoE8tZEpnMJmtkvNbB1EIPPwHcdkF6b1oUA=="], + + "@humanfs/core": ["@humanfs/core@0.19.2", "", { "dependencies": { "@humanfs/types": "^0.15.0" } }, "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA=="], + + "@humanfs/node": ["@humanfs/node@0.16.8", "", { "dependencies": { "@humanfs/core": "^0.19.2", "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ=="], + + "@humanfs/types": ["@humanfs/types@0.15.0", "", {}, "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + + "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], + + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg=="], + + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.4" }, "os": "darwin", "cpu": "x64" }, "sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw=="], + + "@img/sharp-freebsd-wasm32": ["@img/sharp-freebsd-wasm32@0.35.5", "", { "dependencies": { "@img/sharp-wasm32": "0.35.5" }, "os": "freebsd" }, "sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw=="], + + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw=="], + + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ=="], + + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.4", "", { "os": "linux", "cpu": "arm" }, "sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw=="], + + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A=="], + + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.3.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ=="], + + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.3.4", "", { "os": "linux", "cpu": "none" }, "sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q=="], + + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ=="], + + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw=="], + + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA=="], + + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg=="], + + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.4" }, "os": "linux", "cpu": "arm" }, "sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ=="], + + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.4" }, "os": "linux", "cpu": "arm64" }, "sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ=="], + + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.3.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA=="], + + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.3.4" }, "os": "linux", "cpu": "none" }, "sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ=="], + + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.4" }, "os": "linux", "cpu": "s390x" }, "sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA=="], + + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.4" }, "os": "linux", "cpu": "x64" }, "sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog=="], + + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.4" }, "os": "linux", "cpu": "arm64" }, "sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg=="], + + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.4" }, "os": "linux", "cpu": "x64" }, "sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A=="], + + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.5", "", { "dependencies": { "@emnapi/runtime": "^1.11.3" } }, "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ=="], + + "@img/sharp-webcontainers-wasm32": ["@img/sharp-webcontainers-wasm32@0.35.5", "", { "dependencies": { "@img/sharp-wasm32": "0.35.5" }, "cpu": "none" }, "sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA=="], + + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.35.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.5", "", { "os": "win32", "cpu": "x64" }, "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.6.0", "", {}, "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@keyv/bigmap": ["@keyv/bigmap@1.3.1", "", { "dependencies": { "hashery": "^1.4.0", "hookified": "^1.15.0" }, "peerDependencies": { "keyv": "^5.6.0" } }, "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ=="], + + "@keyv/serialize": ["@keyv/serialize@1.1.1", "", {}, "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA=="], + + "@lezer/common": ["@lezer/common@1.5.3", "", {}, "sha512-H0iErY4e43LpXbYDyBci5W4v/RwTgGV3YzYOlJPiGZ8RY8w51kVE+xLn1tg3Z6UmSfyETvtsUK3r7YsgDQEI7Q=="], + + "@lezer/highlight": ["@lezer/highlight@1.2.5", "", { "dependencies": { "@lezer/common": "^1.3.0" } }, "sha512-O1GMVKgtf5YspFaRzpmqkVgMtIk2HG9uMAbH6wdAIdNh3TVzCsch/+cRi+6l2//UGDKe6NB9BW3cvuR5ezMQ0w=="], + + "@lezer/lr": ["@lezer/lr@1.4.10", "", { "dependencies": { "@lezer/common": "^1.0.0" } }, "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A=="], + + "@marijn/find-cluster-break": ["@marijn/find-cluster-break@1.0.4", "", {}, "sha512-Wy0V7+SGUjnF9/TkiM1hKVDPj7jKXduPNboMVtHTA8dySMURWqfg/JZ9E2Sq8JgSJmkl7k7Qe9FLeMSrSraWmQ=="], + + "@napi-rs/canvas": ["@napi-rs/canvas@1.0.9", "", { "optionalDependencies": { "@napi-rs/canvas-android-arm64": "1.0.9", "@napi-rs/canvas-darwin-arm64": "1.0.9", "@napi-rs/canvas-darwin-x64": "1.0.9", "@napi-rs/canvas-linux-arm-gnueabihf": "1.0.9", "@napi-rs/canvas-linux-arm64-gnu": "1.0.9", "@napi-rs/canvas-linux-arm64-musl": "1.0.9", "@napi-rs/canvas-linux-riscv64-gnu": "1.0.9", "@napi-rs/canvas-linux-x64-gnu": "1.0.9", "@napi-rs/canvas-linux-x64-musl": "1.0.9", "@napi-rs/canvas-win32-arm64-msvc": "1.0.9", "@napi-rs/canvas-win32-x64-msvc": "1.0.9" } }, "sha512-QviPdJImDi/jMAvBfqaw+19BndMd/sizXVW3NnpMd3VJGz++QXkOHcP9kWR/smHG0hNjHeyuHFyrx/5lD0oNcQ=="], + + "@napi-rs/canvas-android-arm64": ["@napi-rs/canvas-android-arm64@1.0.9", "", { "os": "android", "cpu": "arm64" }, "sha512-4LGXk2/0HVzE29K8SzML5WubgCp++B1FH3qgl35XmSZE+lLdr6P9VRQEnZ0MCLZMTSuJP41yyhtoiVNEuvrTIA=="], + + "@napi-rs/canvas-darwin-arm64": ["@napi-rs/canvas-darwin-arm64@1.0.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-YNdfLBzY0W/Pep9fo2L6RmoNlNksnn05LRnX66W63R3ij58S25QOTcjdtEt2v8+PnCESzqZsYzUo+QPeIR44NA=="], + + "@napi-rs/canvas-darwin-x64": ["@napi-rs/canvas-darwin-x64@1.0.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-ceZQSknTEcy3dOXoekv59LTCkXjvnLsq+VW5PeNNDHEPQbRS5Ervkm1EaDa7WLAjiYWMLuSQTRTHao1dEX4prg=="], + + "@napi-rs/canvas-linux-arm-gnueabihf": ["@napi-rs/canvas-linux-arm-gnueabihf@1.0.9", "", { "os": "linux", "cpu": "arm" }, "sha512-XhfI0Wwv4llhd6nnWDtY3kQKjq0r+y1i91PlJlJI24ag2U9WrnwbG1qS3+fDLEyouwEVFchiKkTEHozK+5iUNA=="], + + "@napi-rs/canvas-linux-arm64-gnu": ["@napi-rs/canvas-linux-arm64-gnu@1.0.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-012oiYtKaE7i9oxc8q7nraT7kDOpLcaCmFLzVe9Ty34RHDdoDzbWLrVh827CNxYh/EADX1eSikA3ymLjo/nNuw=="], + + "@napi-rs/canvas-linux-arm64-musl": ["@napi-rs/canvas-linux-arm64-musl@1.0.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-Ls5UWYFFn63casTZEczbeyEg3vRDRkv9lscuGwfchtY5yLLQhgOB8SN4YGxmfJ5vTaBwZ2YUxBS3NtmjJmFXdA=="], + + "@napi-rs/canvas-linux-riscv64-gnu": ["@napi-rs/canvas-linux-riscv64-gnu@1.0.9", "", { "os": "linux", "cpu": "none" }, "sha512-hLKEGxV7ZiRHqndePTokgDMdBlo/rDfzg7P4p4QIv9pUhuYobnu3R2NIFLCRghG0nwfo+s2sw+c1xZFeCmEAsw=="], + + "@napi-rs/canvas-linux-x64-gnu": ["@napi-rs/canvas-linux-x64-gnu@1.0.9", "", { "os": "linux", "cpu": "x64" }, "sha512-6kaz3w0QMy77PDWk6rJ1ksIihdad3qzEyX2o2oGT8GwCaypfT5mhjr8buOO5hstyLxcWXDScuz56RsINLtBPIQ=="], + + "@napi-rs/canvas-linux-x64-musl": ["@napi-rs/canvas-linux-x64-musl@1.0.9", "", { "os": "linux", "cpu": "x64" }, "sha512-xrGvmS3v55hmZ86ls/kBLVNMUTYio3f6Ik0DireemG994VfPAwiA3ZXA0Uf1bByctkB3NQ1Sfb+H5bkdUnnzfQ=="], + + "@napi-rs/canvas-win32-arm64-msvc": ["@napi-rs/canvas-win32-arm64-msvc@1.0.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-yjmVS3ArZeRVCP7jqbPq4rpZa/BhTeI7ELE2XqJg3snICQBDevLZyArxswHkiTnT34KRic33/4fLirrHI+SY8A=="], + + "@napi-rs/canvas-win32-x64-msvc": ["@napi-rs/canvas-win32-x64-msvc@1.0.9", "", { "os": "win32", "cpu": "x64" }, "sha512-QlSYQdMQslB81nlABo9wNfQ6npFhE7/O+saCZdqVGueGanyRk4jCogD5EwQenfP3kIq9e+mm6GreQBjX5MrA8g=="], + + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.2.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" } }, "sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g=="], + + "@next/env": ["@next/env@16.3.7", "", {}, "sha512-/HuhBN1CorqNTmewTIh81yXOQri6B8Ye/c9D7+830XqwVaf8qMnJNrgNxDn6dLtihq8YzBbI6NSAuMKvpr+DvQ=="], + + "@next/eslint-plugin-next": ["@next/eslint-plugin-next@16.3.7", "", { "dependencies": { "@eslint-community/eslint-utils": "4.9.1", "fast-glob": "3.3.1" } }, "sha512-ZQwUKBxqKAasarxLHJkkIv0cuG80pJ7TKvCUcFvlKewkuzYkL+Lsxb++DQlcBnY4uT0kOazDye4j1pyDPlDcDQ=="], + + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.3.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-MDAd3woxfJOVFtfG2VAuaz5zyyVZTMVUPJAknotCNgeEPrPlRBlcV9YSd8S620P6TiSc2MWpfwO/UAHl9EWRug=="], + + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.3.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-Usd86QilBWl2G7JxfWskV9NLAxacVRrDDQ3WQIoPKRB0MXiVuVSdyu3wt5EA8SyjQpaVk/7VOzSkRB4LgJltyA=="], + + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.3.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-pVauSs1WomgtBgfJj/Q+846nBix4CdohLUIDafyJGdAvNw5kOusLawoX+rpc2P4U3Kd7tLwqFXsnHeSkLZrqyw=="], + + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.3.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-vY+iamd6cOfk29bGTgxeS/OXlrlfOfyFgyd34cibC7e25bXRMxuqNUfs2Z9Fxb/VHpV5dopffMSWnCeR2tc8rA=="], + + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.3.7", "", { "os": "linux", "cpu": "x64" }, "sha512-NWx0LRZ9IO9rTDXLc+Hi/bNcTKblT86du4OLMRm5Z62C2T5/+mNEhK2XeQxwoSmVFvU2rjMSjJMK4ttZAk88+w=="], + + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.3.7", "", { "os": "linux", "cpu": "x64" }, "sha512-v5Dk/iMB4JyQZwQ+UWOZGGAqA4HjkBewKwaCkXi5DqL1Dy1TjXpRmAh8N7VOuID0hLmouICSXozJrMBBG1Xeyw=="], + + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.3.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-G4BkB7AhfKJnaoIpIkoA66l5pXPGJXD3EF1EsuYj/sgM4rHA41dsa3CIIqjR4FQFZEjU+zGQRbIEPYZIOi5EoA=="], + + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.3.7", "", { "os": "win32", "cpu": "x64" }, "sha512-DuvRhf50tGU7leT9Ow0cs/9lo81X5lHdNoxSRk4ckSk2Ihn2dsM+BEe5j7ANpEFhD25p//SmV8ajdRtuaBIg3g=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="], + + "@playwright/test": ["@playwright/test@1.63.0", "", { "dependencies": { "playwright": "1.63.0" }, "bin": { "playwright": "cli.js" } }, "sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ=="], + + "@radix-ui/number": ["@radix-ui/number@1.1.3", "", {}, "sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA=="], + + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.7", "", {}, "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q=="], + + "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.20", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-collapsible": "1.1.20", "@radix-ui/react-collection": "1.1.15", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-direction": "1.1.4", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-jDhG9FvAEnlhnjrsINbNXcUa4G+L1KqSkJSunkbKEzFRcAb52jvM0PjPxPRvhe1HNc5F5yc0yzzWeeqlH4yBIg=="], + + "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.15", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA=="], + + "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-controllable-state": "1.2.6", "@radix-ui/react-use-size": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Gnptr9pDDQxD3hgq2dtPbtrp/c2qH1mBwIzw3X/ivrMb2e1t0jMTi606fVEqFPaQR1ggXIVQWKj3P2WW9v7zGQ=="], + + "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.20", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-controllable-state": "1.2.6", "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-mcGesGplBnzN2sbvJETzpCNfSMyPnb29q1GRLU+Ib7bJrpIG2ywmRoh2V5VbA2uNvKikKUlVbAPks7JDjz4A8Q=="], + + "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.15", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-slot": "1.3.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA=="], + + "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.5", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA=="], + + "@radix-ui/react-context": ["@radix-ui/react-context@1.2.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA=="], + + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.23", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-dismissable-layer": "1.1.19", "@radix-ui/react-focus-guards": "1.1.6", "@radix-ui/react-focus-scope": "1.1.16", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-portal": "1.1.17", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-slot": "1.3.3", "@radix-ui/react-use-controllable-state": "1.2.6", "@radix-ui/react-use-layout-effect": "1.1.4", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA=="], + + "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg=="], + + "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.19", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-callback-ref": "1.1.4", "@radix-ui/react-use-effect-event": "0.0.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w=="], + + "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.24", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-menu": "2.1.24", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-geq8l2rJkxvkXsT9RMgtUE3P8pITFpTsvYpbySi1IH4fZEABD/Gp85myayFgxk0ktljGMJnCbeFkyTusvSvv7g=="], + + "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.6", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ=="], + + "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.16", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-callback-ref": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ=="], + + "@radix-ui/react-id": ["@radix-ui/react-id@1.1.4", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA=="], + + "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.24", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-collection": "1.1.15", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-direction": "1.1.4", "@radix-ui/react-dismissable-layer": "1.1.19", "@radix-ui/react-focus-guards": "1.1.6", "@radix-ui/react-focus-scope": "1.1.16", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-popper": "1.3.7", "@radix-ui/react-portal": "1.1.17", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-roving-focus": "1.1.19", "@radix-ui/react-slot": "1.3.3", "@radix-ui/react-use-callback-ref": "1.1.4", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-uW7RVuU6Lp/ZtfeY4b3kL32zccgEWvPv1+cf17ubYzHa9cL8AHokmk36cG/XEiH/smbQvumnieXX9j/e9RqJWA=="], + + "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.23", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-dismissable-layer": "1.1.19", "@radix-ui/react-focus-guards": "1.1.6", "@radix-ui/react-focus-scope": "1.1.16", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-popper": "1.3.7", "@radix-ui/react-portal": "1.1.17", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-slot": "1.3.3", "@radix-ui/react-use-controllable-state": "1.2.6", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ=="], + + "@radix-ui/react-popper": ["@radix-ui/react-popper@1.3.7", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.15", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-callback-ref": "1.1.4", "@radix-ui/react-use-layout-effect": "1.1.4", "@radix-ui/react-use-rect": "1.1.4", "@radix-ui/react-use-size": "1.1.4", "@radix-ui/rect": "1.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg=="], + + "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.17", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ=="], + + "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.10", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw=="], + + "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.10", "", { "dependencies": { "@radix-ui/react-slot": "1.3.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg=="], + + "@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.4.7", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-direction": "1.1.4", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-roving-focus": "1.1.19", "@radix-ui/react-use-controllable-state": "1.2.6", "@radix-ui/react-use-size": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-cgYFEkntCxppHZgtSZ+7vh0wbZQ+IC7PPMw8DSnRG27B6kDd32/Zw0OJt7dGDigCoprMuWHjg2PvUn3PYvPFoQ=="], + + "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.19", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-collection": "1.1.15", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-direction": "1.1.4", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-callback-ref": "1.1.4", "@radix-ui/react-use-controllable-state": "1.2.6", "@radix-ui/react-use-is-hydrated": "0.1.3", "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ=="], + + "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.18", "", { "dependencies": { "@radix-ui/number": "1.1.3", "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-direction": "1.1.4", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-callback-ref": "1.1.4", "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Zn5Cd171wxsO3Dfg8HaW6RifTb9CYTKQJHs/G4+LN1GfmJpaQMZQyQxMprVPHpaz7QY4l9BxK2JwQuzHsXC8nA=="], + + "@radix-ui/react-select": ["@radix-ui/react-select@2.3.7", "", { "dependencies": { "@radix-ui/number": "1.1.3", "@radix-ui/primitive": "1.1.7", "@radix-ui/react-collection": "1.1.15", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-direction": "1.1.4", "@radix-ui/react-dismissable-layer": "1.1.19", "@radix-ui/react-focus-guards": "1.1.6", "@radix-ui/react-focus-scope": "1.1.16", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-popper": "1.3.7", "@radix-ui/react-portal": "1.1.17", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-slot": "1.3.3", "@radix-ui/react-use-callback-ref": "1.1.4", "@radix-ui/react-use-controllable-state": "1.2.6", "@radix-ui/react-use-layout-effect": "1.1.4", "@radix-ui/react-use-previous": "1.1.4", "@radix-ui/react-visually-hidden": "1.2.11", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-WFGImkmbzcfxeIwq/+4HvRN0pizBwbwQUED4I13ezQsDdfl38ZntN6TmR8XaSzPBqoCToe8rF75j6NPNDSzhbg=="], + + "@radix-ui/react-slider": ["@radix-ui/react-slider@1.4.7", "", { "dependencies": { "@radix-ui/number": "1.1.3", "@radix-ui/primitive": "1.1.7", "@radix-ui/react-collection": "1.1.15", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-direction": "1.1.4", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-controllable-state": "1.2.6", "@radix-ui/react-use-layout-effect": "1.1.4", "@radix-ui/react-use-previous": "1.1.4", "@radix-ui/react-use-size": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-mTSLf1GC/C0moWjTbvCM6Qn/gBjvlFt1azuWF2v7MN5C3Zq2U2J2lN3ZEYkpujuOU5Ro7A28wkviSxaKnG0BYg=="], + + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.5" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q=="], + + "@radix-ui/react-switch": ["@radix-ui/react-switch@1.3.7", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-controllable-state": "1.2.6", "@radix-ui/react-use-size": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-48tB/4dn2UVLBCYhTu9AuR63IHl73l/qLbLgxd86noTUor4/K4LFDAcYjK+isP5313qxaFpjPVogE7+Y0/V3Kw=="], + + "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.21", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-direction": "1.1.4", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-roving-focus": "1.1.19", "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-UKxJlZid7FVtsk/WTxj4i4uSEgj2Au+KBbS7SQyTlzMhhn+86Cz3tISZdTa87bfEfcuvZezf2ZsxD4xuEKtkog=="], + + "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-dismissable-layer": "1.1.19", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-popper": "1.3.7", "@radix-ui/react-portal": "1.1.17", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-slot": "1.3.3", "@radix-ui/react-use-controllable-state": "1.2.6", "@radix-ui/react-use-layout-effect": "1.1.4", "@radix-ui/react-visually-hidden": "1.2.11" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-6EamKFRRnlpdadndbZ6LMwycfwkwPte1B42hs6QA0gYhjaOKqW4PZ4pjaW9UrlDX5eVt/OjncE7BFTPL5nmZhg=="], + + "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ=="], + + "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-use-effect-event": "0.0.5", "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ=="], + + "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.5", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg=="], + + "@radix-ui/react-use-is-hydrated": ["@radix-ui/react-use-is-hydrated@0.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw=="], + + "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw=="], + + "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-XoSLhbRbqxFtgJoi2fNHA3C6pDlY34x508vUpUGoFZfvePfHXHbE1lC4FYFMnJWgiCRroSTw6fOsXQoVS9RwZg=="], + + "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.4", "", { "dependencies": { "@radix-ui/rect": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ=="], + + "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.4", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw=="], + + "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.11", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ=="], + + "@radix-ui/rect": ["@radix-ui/rect@1.1.3", "", {}, "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw=="], + + "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], + + "@swc/helpers": ["@swc/helpers@0.5.23", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.3.3", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.24.1", "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.3" } }, "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.3", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.3", "@tailwindcss/oxide-darwin-arm64": "4.3.3", "@tailwindcss/oxide-darwin-x64": "4.3.3", "@tailwindcss/oxide-freebsd-x64": "4.3.3", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", "@tailwindcss/oxide-linux-x64-musl": "4.3.3", "@tailwindcss/oxide-wasm32-wasi": "4.3.3", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" } }, "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.3", "", { "os": "android", "cpu": "arm64" }, "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.3.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.3.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3", "", { "os": "linux", "cpu": "arm" }, "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.3.3", "", { "dependencies": { "@emnapi/core": "^1.11.1", "@emnapi/runtime": "^1.11.1", "@emnapi/wasi-threads": "^1.2.2", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.2", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.3.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.3", "", { "os": "win32", "cpu": "x64" }, "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw=="], + + "@tailwindcss/postcss": ["@tailwindcss/postcss@4.3.3", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.3.3", "@tailwindcss/oxide": "4.3.3", "postcss": "^8.5.16", "tailwindcss": "4.3.3" } }, "sha512-JTSZZGQi1AyKirbLN3azmjVzef92tcX7h+iSqPdaeStyFpGpDlKvvpxeOE8njhbUanbRwr3z8DyzhICWnMtQeg=="], + + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.4", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A=="], + + "@types/esrecurse": ["@types/esrecurse@4.3.1", "", {}, "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw=="], + + "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], + + "@types/node": ["@types/node@26.6.3", "", { "dependencies": { "undici-types": "~8.9.0" } }, "sha512-dsqMQQoeTLqu9wynDD00q573mNzso3IdQOAfHRJqLCcmCFPoGo9A1bDpUcv/9tnKpErQWv9uKeGfl37EIS02Yg=="], + + "@types/react": ["@types/react@19.3.0", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg=="], + + "@types/react-dom": ["@types/react-dom@19.3.0", "", { "peerDependencies": { "@types/react": "^19.3.0" } }, "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.71.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.71.0", "@typescript-eslint/type-utils": "8.71.0", "@typescript-eslint/utils": "8.71.0", "@typescript-eslint/visitor-keys": "8.71.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.71.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-pqcS9c1HxZTHt7End4nXqd0s5lJrrFzrgCkKFJrsbUnaL6M3+6oBFZaslg6Gjsl3argl2DDRFROnXARaZ2e4Nw=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.71.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.71.0", "@typescript-eslint/types": "8.71.0", "@typescript-eslint/typescript-estree": "8.71.0", "@typescript-eslint/visitor-keys": "8.71.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-CG4nPk1f2zc8yw4pALqHsFYH2hdo+h1T9daSp21+Hnxi9LOE3GT9hAfTKJCBXVNM2GmYs1eMEP615wPoeOgk3A=="], + + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.71.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.71.0", "@typescript-eslint/types": "^8.71.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-aABjw5rjBacYONVPaPiWOCjJu0vEF4a25iQuodlmQYL1trtLZ0X/y+2Vzl3BKI1odM4LnwLE1oUDXYp1wzx1TQ=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.71.0", "", { "dependencies": { "@typescript-eslint/types": "8.71.0", "@typescript-eslint/visitor-keys": "8.71.0" } }, "sha512-gWF0BhUcnjZxSpLE8ngS/59n2SB0J3YqRxvX1+2aoRJk9hNtHSLOV+TcarFiOr5ipXm3yc1QrI4c9YZc8zyCxw=="], + + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.71.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-Z1UlWHADEK2Mlb9NpWfDeSjqoZ5EyrOv4R3eQpbkzqn/EwaIdOpXXupEA1+0ZIOSJSZZDBHG0BrQyN8zUG6Pwg=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.71.0", "", { "dependencies": { "@typescript-eslint/types": "8.71.0", "@typescript-eslint/typescript-estree": "8.71.0", "@typescript-eslint/utils": "8.71.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-i8uO1qbdxeKgRnS5sCRt6On3/nfo2d2DwQe3Yvjx543zLy7r8ySqRuPPiIIXAhS03U0v5NfAFx+rUgxFzKKwNw=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.71.0", "", {}, "sha512-cJ4OoxPGWvFnBTnSZyaU+qJzGTqPTGJY+gDchj6cRyLRdmIdt4rcsE4twj+zPfrNiWuVi38wijHzShL++Z9atQ=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.71.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.71.0", "@typescript-eslint/tsconfig-utils": "8.71.0", "@typescript-eslint/types": "8.71.0", "@typescript-eslint/visitor-keys": "8.71.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-PEEF4G5sLLWAS5BpPrUvms4ySZkiBQQZM4z+3ReI46axK5Vqr/vXBQatJQIZZOYdGyPUAKTtsrWzpqKuU+3DEw=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.71.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.71.0", "@typescript-eslint/types": "8.71.0", "@typescript-eslint/typescript-estree": "8.71.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-pKR/tEMVrXZG23UFKUn5BQf3zfmfk7KQceI2cGzywZ5nxM5Eu3hEJU1utjWzydtzBbcJAQhHN8iPCxobHpPcZQ=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.71.0", "", { "dependencies": { "@typescript-eslint/types": "8.71.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-8eQ9R218XORK+KLosnf4bu/QsUXvUyVwTbArg7/0NMB1Pu87OJKvj4nhFblkYE8gQV73mW1dx1ptlPCkwRGa7A=="], + + "@typescript/native": ["typescript@7.0.2", "", { "optionalDependencies": { "@typescript/typescript-aix-ppc64": "7.0.2", "@typescript/typescript-darwin-arm64": "7.0.2", "@typescript/typescript-darwin-x64": "7.0.2", "@typescript/typescript-freebsd-arm64": "7.0.2", "@typescript/typescript-freebsd-x64": "7.0.2", "@typescript/typescript-linux-arm": "7.0.2", "@typescript/typescript-linux-arm64": "7.0.2", "@typescript/typescript-linux-loong64": "7.0.2", "@typescript/typescript-linux-mips64el": "7.0.2", "@typescript/typescript-linux-ppc64": "7.0.2", "@typescript/typescript-linux-riscv64": "7.0.2", "@typescript/typescript-linux-s390x": "7.0.2", "@typescript/typescript-linux-x64": "7.0.2", "@typescript/typescript-netbsd-arm64": "7.0.2", "@typescript/typescript-netbsd-x64": "7.0.2", "@typescript/typescript-openbsd-arm64": "7.0.2", "@typescript/typescript-openbsd-x64": "7.0.2", "@typescript/typescript-sunos-x64": "7.0.2", "@typescript/typescript-win32-arm64": "7.0.2", "@typescript/typescript-win32-x64": "7.0.2" }, "bin": { "tsc": "bin/tsc" } }, "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA=="], + + "@typescript/old": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], + + "@typescript/typescript-aix-ppc64": ["@typescript/typescript-aix-ppc64@7.0.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ=="], + + "@typescript/typescript-darwin-arm64": ["@typescript/typescript-darwin-arm64@7.0.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA=="], + + "@typescript/typescript-darwin-x64": ["@typescript/typescript-darwin-x64@7.0.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA=="], + + "@typescript/typescript-freebsd-arm64": ["@typescript/typescript-freebsd-arm64@7.0.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ=="], + + "@typescript/typescript-freebsd-x64": ["@typescript/typescript-freebsd-x64@7.0.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw=="], + + "@typescript/typescript-linux-arm": ["@typescript/typescript-linux-arm@7.0.2", "", { "os": "linux", "cpu": "arm" }, "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ=="], + + "@typescript/typescript-linux-arm64": ["@typescript/typescript-linux-arm64@7.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ=="], + + "@typescript/typescript-linux-loong64": ["@typescript/typescript-linux-loong64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ=="], + + "@typescript/typescript-linux-mips64el": ["@typescript/typescript-linux-mips64el@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA=="], + + "@typescript/typescript-linux-ppc64": ["@typescript/typescript-linux-ppc64@7.0.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA=="], + + "@typescript/typescript-linux-riscv64": ["@typescript/typescript-linux-riscv64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ=="], + + "@typescript/typescript-linux-s390x": ["@typescript/typescript-linux-s390x@7.0.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw=="], + + "@typescript/typescript-linux-x64": ["@typescript/typescript-linux-x64@7.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A=="], + + "@typescript/typescript-netbsd-arm64": ["@typescript/typescript-netbsd-arm64@7.0.2", "", { "os": "none", "cpu": "arm64" }, "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA=="], + + "@typescript/typescript-netbsd-x64": ["@typescript/typescript-netbsd-x64@7.0.2", "", { "os": "none", "cpu": "x64" }, "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA=="], + + "@typescript/typescript-openbsd-arm64": ["@typescript/typescript-openbsd-arm64@7.0.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ=="], + + "@typescript/typescript-openbsd-x64": ["@typescript/typescript-openbsd-x64@7.0.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg=="], + + "@typescript/typescript-sunos-x64": ["@typescript/typescript-sunos-x64@7.0.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g=="], + + "@typescript/typescript-win32-arm64": ["@typescript/typescript-win32-arm64@7.0.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ=="], + + "@typescript/typescript-win32-x64": ["@typescript/typescript-win32-x64@7.0.2", "", { "os": "win32", "cpu": "x64" }, "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g=="], + + "@uiw/codemirror-extensions-basic-setup": ["@uiw/codemirror-extensions-basic-setup@4.25.12", "", { "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/commands": "^6.0.0", "@codemirror/language": "^6.0.0", "@codemirror/lint": "^6.0.0", "@codemirror/search": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0" } }, "sha512-wR5lz7jbVcBGyiiLEObAmDikcc39KNqrrd4bdUhav85WZu48ttgnSf1YV+grHuMsj5l/WCw6u+r9ZuSfURJgiA=="], + + "@uiw/react-codemirror": ["@uiw/react-codemirror@4.25.12", "", { "dependencies": { "@babel/runtime": "^7.18.6", "@codemirror/commands": "^6.1.0", "@codemirror/state": "^6.1.1", "@codemirror/theme-one-dark": "^6.0.0", "@uiw/codemirror-extensions-basic-setup": "4.25.12", "codemirror": "^6.0.0" }, "peerDependencies": { "@codemirror/view": ">=6.0.0", "react": ">=17.0.0", "react-dom": ">=17.0.0" } }, "sha512-RE0Gj/QesD7/JWPnJREG8Kq8brGv2NAH17iUECcBrLkqXzzEAgsj/4Qqvz/OlK8XWKmzlsenLRkObT9H+gYQ0w=="], + + "@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.12.2", "", { "os": "android", "cpu": "arm" }, "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w=="], + + "@unrs/resolver-binding-android-arm64": ["@unrs/resolver-binding-android-arm64@1.12.2", "", { "os": "android", "cpu": "arm64" }, "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ=="], + + "@unrs/resolver-binding-darwin-arm64": ["@unrs/resolver-binding-darwin-arm64@1.12.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w=="], + + "@unrs/resolver-binding-darwin-x64": ["@unrs/resolver-binding-darwin-x64@1.12.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA=="], + + "@unrs/resolver-binding-freebsd-x64": ["@unrs/resolver-binding-freebsd-x64@1.12.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg=="], + + "@unrs/resolver-binding-linux-arm-gnueabihf": ["@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2", "", { "os": "linux", "cpu": "arm" }, "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A=="], + + "@unrs/resolver-binding-linux-arm-musleabihf": ["@unrs/resolver-binding-linux-arm-musleabihf@1.12.2", "", { "os": "linux", "cpu": "arm" }, "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g=="], + + "@unrs/resolver-binding-linux-arm64-gnu": ["@unrs/resolver-binding-linux-arm64-gnu@1.12.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg=="], + + "@unrs/resolver-binding-linux-arm64-musl": ["@unrs/resolver-binding-linux-arm64-musl@1.12.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA=="], + + "@unrs/resolver-binding-linux-loong64-gnu": ["@unrs/resolver-binding-linux-loong64-gnu@1.12.2", "", { "os": "linux", "cpu": "none" }, "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q=="], + + "@unrs/resolver-binding-linux-loong64-musl": ["@unrs/resolver-binding-linux-loong64-musl@1.12.2", "", { "os": "linux", "cpu": "none" }, "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew=="], + + "@unrs/resolver-binding-linux-ppc64-gnu": ["@unrs/resolver-binding-linux-ppc64-gnu@1.12.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg=="], + + "@unrs/resolver-binding-linux-riscv64-gnu": ["@unrs/resolver-binding-linux-riscv64-gnu@1.12.2", "", { "os": "linux", "cpu": "none" }, "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A=="], + + "@unrs/resolver-binding-linux-riscv64-musl": ["@unrs/resolver-binding-linux-riscv64-musl@1.12.2", "", { "os": "linux", "cpu": "none" }, "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w=="], + + "@unrs/resolver-binding-linux-s390x-gnu": ["@unrs/resolver-binding-linux-s390x-gnu@1.12.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw=="], + + "@unrs/resolver-binding-linux-x64-gnu": ["@unrs/resolver-binding-linux-x64-gnu@1.12.2", "", { "os": "linux", "cpu": "x64" }, "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ=="], + + "@unrs/resolver-binding-linux-x64-musl": ["@unrs/resolver-binding-linux-x64-musl@1.12.2", "", { "os": "linux", "cpu": "x64" }, "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A=="], + + "@unrs/resolver-binding-openharmony-arm64": ["@unrs/resolver-binding-openharmony-arm64@1.12.2", "", { "os": "none", "cpu": "arm64" }, "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ=="], + + "@unrs/resolver-binding-wasm32-wasi": ["@unrs/resolver-binding-wasm32-wasi@1.12.2", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A=="], + + "@unrs/resolver-binding-win32-arm64-msvc": ["@unrs/resolver-binding-win32-arm64-msvc@1.12.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g=="], + + "@unrs/resolver-binding-win32-ia32-msvc": ["@unrs/resolver-binding-win32-ia32-msvc@1.12.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g=="], + + "@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.12.2", "", { "os": "win32", "cpu": "x64" }, "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA=="], + + "acorn": ["acorn@8.18.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], + + "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], + + "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], + + "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="], + + "array-includes": ["array-includes@3.2.0", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.2", "es-object-atoms": "^1.1.2", "es-shim-unscopables": "^1.1.0", "is-string": "^1.1.1", "math-intrinsics": "^1.1.0" } }, "sha512-VXY5eFRarnXcYxwBjJzPmEhH55+rmP79/+ueDhi0F+TuqfHCItagIHqxeUZrmgrOPa31QTh9H85DjX3FfJ0FTg=="], + + "array.prototype.findlast": ["array.prototype.findlast@1.2.5", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ=="], + + "array.prototype.findlastindex": ["array.prototype.findlastindex@1.2.6", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-shim-unscopables": "^1.1.0" } }, "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ=="], + + "array.prototype.flat": ["array.prototype.flat@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg=="], + + "array.prototype.flatmap": ["array.prototype.flatmap@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg=="], + + "array.prototype.tosorted": ["array.prototype.tosorted@1.1.4", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3", "es-errors": "^1.3.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA=="], + + "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="], + + "ast-types-flow": ["ast-types-flow@0.0.8", "", {}, "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ=="], + + "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="], + + "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], + + "axe-core": ["axe-core@4.13.0", "", {}, "sha512-UzGt8zg7Ny8djbYMhxl2zuEevVa7r2gJjYY5Lwr1xM7+XU2nd6CkIWFTVcCIbAP63vSz71NaVyyuSk9lHKcy0A=="], + + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], + + "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.11.26", "", { "bin": "dist/cli.cjs" }, "sha512-GLQdD3y6UF8iVuMJl5fHgE4jdn/ua7n+toKfLgNlg3BqQtOZjpy68T8Tup8/wGWZCDlm7KMg7tPb4MPn7oN0TQ=="], + + "brace-expansion": ["brace-expansion@5.0.12", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "browserslist": ["browserslist@4.29.3", "", { "dependencies": { "baseline-browser-mapping": "^2.11.26", "caniuse-lite": "^1.0.30001813", "electron-to-chromium": "^1.5.439", "node-releases": "^2.0.57", "update-browserslist-db": "^1.3.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-1R4kiYKXGViqEN0CnoDrXc1StD9niAwu+j2dukWzrD4bJgsD4lDmEp0CRbc6E/vYJIfTHwPmwyaKtVSudICdPA=="], + + "cacheable": ["cacheable@2.5.0", "", { "dependencies": { "@cacheable/memory": "^2.2.0", "@cacheable/utils": "^2.5.0", "hookified": "^1.15.0", "keyv": "^5.6.0", "qified": "^0.10.1" } }, "sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g=="], + + "call-bind": ["call-bind@1.0.9", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" } }, "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001813", "", {}, "sha512-zfjJo4rM0+fUomGDBW/xcDjhIwz/210DGvip2MAMDZ8KHcRPnOHmEgHPZP0UHlZoxr8fYKVJOqcORhYQcG4FKQ=="], + + "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + + "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "cn": ["cn@0.4.0", "", { "bin": { "cn": "bin/cn.mjs" } }, "sha512-qOAeUhPwPzBxx6jaeQdo646XIKliKHc2j1ZF7Ngb1o0pJZKH5gq8FkA0aVdUdPFUDXNXyUCXpCtoJISo6dhizg=="], + + "codemirror": ["codemirror@6.0.2", "", { "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/commands": "^6.0.0", "@codemirror/language": "^6.0.0", "@codemirror/lint": "^6.0.0", "@codemirror/search": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0" } }, "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "crelt": ["crelt@1.0.7", "", {}, "sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "damerau-levenshtein": ["damerau-levenshtein@1.0.8", "", {}, "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="], + + "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="], + + "data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="], + + "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], + + "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], + + "doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.440", "", {}, "sha512-SghDzqdJokdz8zP8YNlvS74+CwLRoUPDGvP/gFA+HrKVw+pOshUAgx8pXR37xl/u16zGae746rFNWvZ/22kU2Q=="], + + "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "enhanced-resolve": ["enhanced-resolve@5.26.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw=="], + + "es-abstract": ["es-abstract@1.24.2", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg=="], + + "es-abstract-get": ["es-abstract-get@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "es-object-atoms": "^1.1.2", "is-callable": "^1.2.7", "object-inspect": "^1.13.4" } }, "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-iterator-helpers": ["es-iterator-helpers@1.4.0", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.2", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.3.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.5", "math-intrinsics": "^1.1.0" } }, "sha512-c/A0P0oxkACDc+cKWw8evLXK83oBKgn0qPOqCYT4x9uolpCIJAcYvJC9QYKNDRPsTeGyCrQ326jrvgZWdCdK5Q=="], + + "es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "es-shim-unscopables": ["es-shim-unscopables@1.1.0", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw=="], + + "es-to-primitive": ["es-to-primitive@1.3.4", "", { "dependencies": { "es-abstract-get": "^1.0.0", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "is-callable": "^1.2.7", "is-date-object": "^1.1.0", "is-symbol": "^1.1.1" } }, "sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@10.11.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", "@eslint/config-helpers": "^0.7.0", "@eslint/core": "^1.2.1", "@eslint/plugin-kit": "^0.7.3", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", "espree": "^11.2.0", "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "11.1.5 || >11.1.6 <12", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "minimatch": "^10.2.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-P7a6UEEqb9G95MYAtqkmsTbVXIYyzIfl6NGOIJk162PaahFxFyeGcrlXYFSiagECg4sEm8IseJdZBKR3rx6MsQ=="], + + "eslint-config-next": ["eslint-config-next@16.3.7", "", { "dependencies": { "@next/eslint-plugin-next": "16.3.7", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", "eslint-plugin-react-hooks": "^7.0.0", "globals": "16.4.0", "typescript-eslint": "^8.46.0" }, "peerDependencies": { "eslint": ">=9.0.0", "typescript": ">=3.3.1" }, "optionalPeers": ["typescript"] }, "sha512-/a7OkjM1dBK3CY159QlqXI2d3DS6F9CjTF8iwAACas2keulTTpPG0ap7ClRstBhPCr0OH1yBlRX9tpvlLZzqzg=="], + + "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.10", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.16.1", "resolve": "^2.0.0-next.6" } }, "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ=="], + + "eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@3.10.1", "", { "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "^4.4.0", "get-tsconfig": "^4.10.0", "is-bun-module": "^2.0.0", "stable-hash": "^0.0.5", "tinyglobby": "^0.2.13", "unrs-resolver": "^1.6.2" }, "peerDependencies": { "eslint": "*", "eslint-plugin-import": "*", "eslint-plugin-import-x": "*" }, "optionalPeers": ["eslint-plugin-import", "eslint-plugin-import-x"] }, "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ=="], + + "eslint-module-utils": ["eslint-module-utils@2.14.0", "", { "dependencies": { "debug": "^3.2.7" }, "peerDependencies": { "eslint": "*" }, "optionalPeers": ["eslint"] }, "sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig=="], + + "eslint-plugin-import": ["eslint-plugin-import@2.32.0", "", { "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", "array.prototype.findlastindex": "^1.2.6", "array.prototype.flat": "^1.3.3", "array.prototype.flatmap": "^1.3.3", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.12.1", "hasown": "^2.0.2", "is-core-module": "^2.16.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", "object.values": "^1.2.1", "semver": "^6.3.1", "string.prototype.trimend": "^1.0.9", "tsconfig-paths": "^3.15.0" }, "peerDependencies": { "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA=="], + + "eslint-plugin-jsx-a11y": ["eslint-plugin-jsx-a11y@6.10.2", "", { "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", "array.prototype.flatmap": "^1.3.2", "ast-types-flow": "^0.0.8", "axe-core": "^4.10.0", "axobject-query": "^4.1.0", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", "hasown": "^2.0.2", "jsx-ast-utils": "^3.3.5", "language-tags": "^1.0.9", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "safe-regex-test": "^1.0.3", "string.prototype.includes": "^2.0.1" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q=="], + + "eslint-plugin-react": ["eslint-plugin-react@7.37.5", "", { "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA=="], + + "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@7.1.1", "", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "hermes-parser": "^0.25.1", "zod": "^3.25.0 || ^4.0.0", "zod-validation-error": "^3.5.0 || ^4.0.0" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" } }, "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g=="], + + "eslint-scope": ["eslint-scope@9.1.2", "", { "dependencies": { "@types/esrecurse": "^4.3.1", "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="], + + "espree": ["espree@11.2.0", "", { "dependencies": { "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^5.0.1" } }, "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw=="], + + "esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-glob": ["fast-glob@3.3.1", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fastq": ["fastq@1.20.3", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "file-entry-cache": ["file-entry-cache@11.1.5", "", { "dependencies": { "flat-cache": "^6.1.23" } }, "sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat-cache": ["flat-cache@6.1.23", "", { "dependencies": { "cacheable": "^2.5.0", "flatted": "^3.4.2", "hookified": "^1.15.0" } }, "sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA=="], + + "flatted": ["flatted@3.4.4", "", {}, "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q=="], + + "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], + + "framer-motion": ["framer-motion@13.4.6", "", { "dependencies": { "motion-dom": "^13.4.5", "motion-utils": "^13.3.0", "tslib": "^2.4.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-TwKWt2CC8rjhARzwV00TZIh04ipumSAqyKM/JZmS+urdncSHg6wTF/mfj/Fd6Oi2W01TZSWsiA3uxXDrXZtKyw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "function.prototype.name": ["function.prototype.name@1.2.0", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2", "hasown": "^2.0.4", "is-callable": "^1.2.7", "is-document.all": "^1.0.0" } }, "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew=="], + + "functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="], + + "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], + + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="], + + "get-tsconfig": ["get-tsconfig@4.14.3", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="], + + "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], + + "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], + + "has-proto": ["has-proto@1.2.0", "", { "dependencies": { "dunder-proto": "^1.0.0" } }, "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hashery": ["hashery@1.5.1", "", { "dependencies": { "hookified": "^1.15.0" } }, "sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ=="], + + "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], + + "hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="], + + "hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="], + + "hookified": ["hookified@1.15.1", "", {}, "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg=="], + + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], + + "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], + + "is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="], + + "is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="], + + "is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="], + + "is-bun-module": ["is-bun-module@2.0.0", "", { "dependencies": { "semver": "^7.7.1" } }, "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ=="], + + "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], + + "is-core-module": ["is-core-module@2.17.0", "", { "dependencies": { "hasown": "^2.0.4" } }, "sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA=="], + + "is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="], + + "is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="], + + "is-document.all": ["is-document.all@1.0.0", "", { "dependencies": { "call-bound": "^1.0.4" } }, "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="], + + "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="], + + "is-negative-zero": ["is-negative-zero@2.0.3", "", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="], + + "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], + + "is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="], + + "is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="], + + "is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="], + + "is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="], + + "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], + + "is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="], + + "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="], + + "is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="], + + "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="], + + "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="], + + "jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="], + + "keyv": ["keyv@5.6.0", "", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw=="], + + "language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="], + + "language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + + "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "lucide-react": ["lucide-react@1.48.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-R0CIKY/fXiC6y9xRBADgsK+VW2p/pcTJOMhLZf1T+uG+vJUvyUR02nGOgmIIC7MPkiNK4Ox8QDnbqF8rJYWPZQ=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "minimatch": ["minimatch@10.2.6", "", { "dependencies": { "brace-expansion": "^5.0.8" } }, "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A=="], + + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + + "motion-dom": ["motion-dom@13.4.5", "", { "dependencies": { "motion-utils": "^13.3.0" } }, "sha512-p63p2Doq+US/O5wADauIPUVx0/eONHMLPzsudBM9XMWRuBQLXogdxH0vwIA8WYQZw864pyzoV723xoDqakwWTg=="], + + "motion-utils": ["motion-utils@13.3.0", "", {}, "sha512-sgSschQp7EseHInIlR7hBbMuvet3RA0bs28KPZAXJcGKGdxHGvh1ogpYDilY3bOMtl73EqPNmp75sAKHYPU5sg=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nanoid": ["nanoid@3.3.19", "", { "bin": "bin/nanoid.cjs" }, "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug=="], + + "napi-postinstall": ["napi-postinstall@0.3.4", "", { "bin": { "napi-postinstall": "lib/cli.js" } }, "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "next": ["next@16.3.7", "", { "dependencies": { "@next/env": "16.3.7", "@swc/helpers": "0.5.23", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.5.23", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.3.7", "@next/swc-darwin-x64": "16.3.7", "@next/swc-linux-arm64-gnu": "16.3.7", "@next/swc-linux-arm64-musl": "16.3.7", "@next/swc-linux-x64-gnu": "16.3.7", "@next/swc-linux-x64-musl": "16.3.7", "@next/swc-win32-arm64-msvc": "16.3.7", "@next/swc-win32-x64-msvc": "16.3.7", "sharp": "^0.35.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": "dist/bin/next" }, "sha512-S4AlB0KMYcvVyEVjfD2Ze/3JsX9PWjpD3hJPcEJTVvkAQsMTYVs9DB1NtaBbs41ZZ18NxYwkmM8ljqtrFrmLsQ=="], + + "node-exports-info": ["node-exports-info@1.6.2", "", { "dependencies": { "array.prototype.flatmap": "^1.3.3", "es-errors": "^1.3.0", "object.entries": "^1.1.9", "semver": "^6.3.1" } }, "sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag=="], + + "node-releases": ["node-releases@2.0.57", "", {}, "sha512-kQK9LGGFiHtrWiNhZtA7Qbw17AQz+dmsEKODRIVTXA9+e5MS/2gZEBhYJt13GrAz5/IOZKddH/0Z3TP/Zgo+yw=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], + + "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], + + "object.entries": ["object.entries@1.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-object-atoms": "^1.1.1" } }, "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw=="], + + "object.fromentries": ["object.fromentries@2.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-object-atoms": "^1.0.0" } }, "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ=="], + + "object.groupby": ["object.groupby@1.0.3", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2" } }, "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ=="], + + "object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="], + + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "own-keys": ["own-keys@1.0.2", "", { "dependencies": { "call-bound": "^1.0.4", "get-intrinsic": "^1.3.0", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "pdfjs-dist": ["pdfjs-dist@6.3.289", "", { "optionalDependencies": { "@napi-rs/canvas": "^1.0.0" } }, "sha512-ZHjSVpDa3D6izMq8/04lvkhkATUmL9px6ChPaXc1k6nU2Mrhlg1/7F0bdUqCwUjw3NsPTfPZsMDUU6ZIcRaeQw=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="], + + "playwright": ["playwright@1.63.0", "", { "dependencies": { "playwright-core": "1.63.0" }, "bin": { "playwright": "cli.js" } }, "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg=="], + + "playwright-core": ["playwright-core@1.63.0", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg=="], + + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], + + "postcss": ["postcss@8.5.23", "", { "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "qified": ["qified@0.10.1", "", { "dependencies": { "hookified": "^2.1.1" } }, "sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "react": ["react@19.3.0", "", {}, "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog=="], + + "react-dom": ["react-dom@19.3.0", "", { "dependencies": { "scheduler": "^0.28.0" }, "peerDependencies": { "react": "^19.3.0" } }, "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q=="], + + "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="], + + "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], + + "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], + + "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="], + + "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="], + + "reselect": ["reselect@5.3.0", "", {}, "sha512-XGoLeRAVzUTcJ1qkxPQhDJyIZ5d6zzZD9nT7AEZOaaU9UbWclhycElmhO+VD5bFeLuzhPBaOV2oXC8uG35ZSpg=="], + + "resolve": ["resolve@2.0.0-next.7", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.2", "node-exports-info": "^1.6.0", "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ=="], + + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "safe-array-concat": ["safe-array-concat@1.1.4", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "get-intrinsic": "^1.3.0", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg=="], + + "safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="], + + "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], + + "scheduler": ["scheduler@0.28.0", "", {}, "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw=="], + + "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], + + "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="], + + "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="], + + "sharp": ["sharp@0.35.5", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.5", "@img/sharp-darwin-x64": "0.35.5", "@img/sharp-freebsd-wasm32": "0.35.5", "@img/sharp-libvips-darwin-arm64": "1.3.4", "@img/sharp-libvips-darwin-x64": "1.3.4", "@img/sharp-libvips-linux-arm": "1.3.4", "@img/sharp-libvips-linux-arm64": "1.3.4", "@img/sharp-libvips-linux-ppc64": "1.3.4", "@img/sharp-libvips-linux-riscv64": "1.3.4", "@img/sharp-libvips-linux-s390x": "1.3.4", "@img/sharp-libvips-linux-x64": "1.3.4", "@img/sharp-libvips-linuxmusl-arm64": "1.3.4", "@img/sharp-libvips-linuxmusl-x64": "1.3.4", "@img/sharp-linux-arm": "0.35.5", "@img/sharp-linux-arm64": "0.35.5", "@img/sharp-linux-ppc64": "0.35.5", "@img/sharp-linux-riscv64": "0.35.5", "@img/sharp-linux-s390x": "0.35.5", "@img/sharp-linux-x64": "0.35.5", "@img/sharp-linuxmusl-arm64": "0.35.5", "@img/sharp-linuxmusl-x64": "0.35.5", "@img/sharp-webcontainers-wasm32": "0.35.5", "@img/sharp-win32-arm64": "0.35.5", "@img/sharp-win32-ia32": "0.35.5", "@img/sharp-win32-x64": "0.35.5" } }, "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "side-channel": ["side-channel@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ=="], + + "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="], + + "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], + + "string.prototype.includes": ["string.prototype.includes@2.0.1", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3" } }, "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg=="], + + "string.prototype.matchall": ["string.prototype.matchall@4.1.0", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.2", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.2", "get-intrinsic": "^1.3.0", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.4", "set-function-name": "^2.0.2", "side-channel": "^1.1.1" } }, "sha512-tHNHTxInrYLCga9O9YGxWA3G9/nnzQw8UGAyqGx3Ar1pSTTzIuM4woFSq4SowkXCjJIwq5sIiQvEfRI9tCH1qQ=="], + + "string.prototype.repeat": ["string.prototype.repeat@1.0.0", "", { "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" } }, "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w=="], + + "string.prototype.trim": ["string.prototype.trim@1.2.11", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.2", "es-object-atoms": "^1.1.2", "has-property-descriptors": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w=="], + + "string.prototype.trimend": ["string.prototype.trimend@1.0.10", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-object-atoms": "^1.1.2" } }, "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw=="], + + "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="], + + "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], + + "style-mod": ["style-mod@4.1.4", "", {}, "sha512-XXWIQt633/EpAFx8aZDOTjBzrCaGmhvEQlQo6MVPfa2OzO2cWo+4hV9h+6UkHYlXGfy+ODXKUdP7Pthmcu5ATw=="], + + "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "tailwind-merge": ["tailwind-merge@3.7.0", "", {}, "sha512-XPPUyAc+cvspz3lHTcR/QgPfW2A0lv/xQNIjX3HGhLR+Nq2lHaLq5MtTesHn8GUr3W3DguT2KT5x3NVgRtYwmA=="], + + "tailwindcss": ["tailwindcss@4.3.3", "", {}, "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ=="], + + "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], + + "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="], + + "tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], + + "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="], + + "typed-array-byte-offset": ["typed-array-byte-offset@1.0.5", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.9", "for-each": "^0.3.5", "gopd": "^1.2.0", "is-typed-array": "^1.1.15", "reflect.getprototypeof": "^1.0.10" } }, "sha512-0FHJvLPqZ7KJzp17O13jfsAjsqazgrxBu2zEK95PmUz8lv2+GjRuxUInCr2Rk9Dms3ihN21zJ929ZO43yJ95QQ=="], + + "typed-array-length": ["typed-array-length@1.0.8", "", { "dependencies": { "call-bind": "^1.0.9", "for-each": "^0.3.5", "gopd": "^1.2.0", "is-typed-array": "^1.1.15", "possible-typed-array-names": "^1.1.0", "reflect.getprototypeof": "^1.0.10" } }, "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g=="], + + "typescript": ["@typescript/typescript6@6.0.2", "", { "dependencies": { "@typescript/old": "npm:typescript@^6" }, "bin": { "tsc6": "bin/tsc6" } }, "sha512-mbCddXd+jm7hfx7w2YU64/Av4/NqqeG3GoRZgxPcgoTxYjhrcfJRw9ULch71SS4G+Q3bOXFhRvPqjguN0Hyp5w=="], + + "typescript-eslint": ["typescript-eslint@8.71.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.71.0", "@typescript-eslint/parser": "8.71.0", "@typescript-eslint/typescript-estree": "8.71.0", "@typescript-eslint/utils": "8.71.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-fBdHYiqQ14RW6mOMXD14Svn82ZsCYAoQSzGRzyEjR59S5A2Krh/l7fGTOQ7iCr8gGy/mHVXtEF7s5fgjEdV0Pw=="], + + "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], + + "undici-types": ["undici-types@8.9.0", "", {}, "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg=="], + + "unrs-resolver": ["unrs-resolver@1.12.2", "", { "dependencies": { "napi-postinstall": "^0.3.4" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.12.2", "@unrs/resolver-binding-android-arm64": "1.12.2", "@unrs/resolver-binding-darwin-arm64": "1.12.2", "@unrs/resolver-binding-darwin-x64": "1.12.2", "@unrs/resolver-binding-freebsd-x64": "1.12.2", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", "@unrs/resolver-binding-linux-x64-musl": "1.12.2", "@unrs/resolver-binding-openharmony-arm64": "1.12.2", "@unrs/resolver-binding-wasm32-wasi": "1.12.2", "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" } }, "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ=="], + + "update-browserslist-db": ["update-browserslist-db@1.3.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], + + "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], + + "use-sync-external-store": ["use-sync-external-store@1.7.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-6L+EeigHMQhdaIPNIFUKwfWJSwWFQ8gJbJ2DLOs5sDIegTwR9fRxvnM3uciHKjIZhFz+KAv2emhWMRvDmMcY8A=="], + + "w3c-keyname": ["w3c-keyname@2.2.8", "", {}, "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="], + + "which-builtin-type": ["which-builtin-type@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", "is-date-object": "^1.1.0", "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", "which-typed-array": "^1.1.16" } }, "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q=="], + + "which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="], + + "which-typed-array": ["which-typed-array@1.1.24", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.9", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-wk4Mf4pR5mRP7eYuuTBCIQ9d0ud2Fv2jRLQpfgnRjbOxAFHmjKFValgTpitVKzJJS8ajnYQV2Du1SZ8j6b/EUQ=="], + + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "zod": ["zod@4.6.5", "", {}, "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q=="], + + "zod-validation-error": ["zod-validation-error@4.0.2", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ=="], + + "@babel/core/json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@img/sharp-wasm32/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], + + "@next/eslint-plugin-next/@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.3", "tslib": "^2.4.0" }, "bundled": true }, "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.2.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" }, "bundled": true }, "sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.4", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.10", "", {}, "sha512-HpbUakT7xp5miBUywCHf36ZEuAJNklBJDDsGpUIjMzOSmM8ELSfA9Sa/QDPeNeqeoN31u+UTCkL4klCOVvRm4Q=="], + + "@typescript-eslint/typescript-estree/semver": ["semver@7.8.5", "", { "bin": "bin/semver.js" }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], + + "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-plugin-import/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], + + "eslint-plugin-jsx-a11y/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], + + "eslint-plugin-react/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], + + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "is-bun-module/semver": ["semver@7.8.5", "", { "bin": "bin/semver.js" }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], + + "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + + "qified/hookified": ["hookified@2.2.0", "", {}, "sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA=="], + + "sharp/semver": ["semver@7.8.5", "", { "bin": "bin/semver.js" }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], + + "@next/eslint-plugin-next/@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "eslint-plugin-import/minimatch/brace-expansion": ["brace-expansion@1.1.21", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw=="], + + "eslint-plugin-jsx-a11y/minimatch/brace-expansion": ["brace-expansion@1.1.21", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw=="], + + "eslint-plugin-react/minimatch/brace-expansion": ["brace-expansion@1.1.21", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw=="], + + "eslint-plugin-import/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "eslint-plugin-jsx-a11y/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "eslint-plugin-react/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + } +} diff --git a/components.json b/components.json new file mode 100644 index 0000000..e61813a --- /dev/null +++ b/components.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/app/globals.css", + "baseColor": "neutral", + "cssVariables": true + }, + "aliases": { + "components": "@/components", + "ui": "@/components/ui", + "utils": "@/lib/utils", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "registries": { "@fluid": "https://www.fluidfunctionalism.com/r/{name}.json" } +} diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..670f290 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,10 @@ +import { defineConfig, globalIgnores } from "eslint/config"; +import nextVitals from "eslint-config-next/core-web-vitals"; +import nextTs from "eslint-config-next/typescript"; +import { fixupConfigRules } from "@eslint/compat"; + +export default defineConfig([ + ...fixupConfigRules([...nextVitals, ...nextTs]), + // Fluid registry sources are vendored as published; type checking includes them. + globalIgnores([".next/**", "out/**", "next-env.d.ts", "assets/**", "scripts/**", ".serena/**", "src/components/ui/**", "src/components/fluid-hover-highlight.tsx", "src/hooks/use-*.tsx", "src/hooks/use-fluid-hover.ts", "src/hooks/use-keyboard-nav-gate.ts", "src/lib/shape-context.tsx", "src/lib/icon-context.tsx", "src/lib/size-context.tsx", "src/lib/surface-context.tsx", "src/lib/elevated.tsx"]), +]); diff --git a/next.config.ts b/next.config.ts new file mode 100644 index 0000000..bcbcea1 --- /dev/null +++ b/next.config.ts @@ -0,0 +1,10 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + output: "export", + trailingSlash: true, + basePath: process.env.NEXT_PUBLIC_BASE_PATH || "", + images: { unoptimized: true }, +}; + +export default nextConfig; diff --git a/package.json b/package.json index a909268..f70589a 100644 --- a/package.json +++ b/package.json @@ -1,57 +1,62 @@ { - "name": "corona_admin", - "version": "2.0.0", - "description": "", - "main": "index.html", + "name": "btagscript-playground", + "version": "3.0.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "lint": "eslint .", + "typecheck": "tsc --noEmit", + "test": "bun test tests" + }, + "engines": { + "node": ">=22.0.0" + }, "dependencies": { - "@mdi/font": "^3.7.95", - "bootstrap": "^4.4.1", - "bootstrap-maxlength": "^1.6.0", - "chart.js": "^2.8.0", - "codemirror": "^5.48.0", - "flag-icon-css": "^3.3.0", - "jquery": "3.4.1", - "jvectormap": "^2.0.4", - "moment": "^2.24.0", - "owl.carousel": "^2.3.4", - "perfect-scrollbar": "^1.5.0", - "popper.js": "^1.16.1", - "progressbar.js": "^1.0.1", - "pwstabs": "^1.4.0", - "select2": "^4.0.7", - "select2-bootstrap-theme": "0.1.0-beta.10", - "twbs-pagination": "^1.4.1", - "typeahead.js": "^0.11.1" + "@base-ui/react": "^1.8.0", + "@codemirror/language": "^6.12.4", + "@codemirror/view": "^6.43.13", + "@fontsource-variable/inter": "^5.3.0", + "@lezer/highlight": "^1.2.5", + "@radix-ui/react-accordion": "^1.2.20", + "@radix-ui/react-checkbox": "^1.3.11", + "@radix-ui/react-collapsible": "^1.1.20", + "@radix-ui/react-dialog": "^1.1.23", + "@radix-ui/react-dropdown-menu": "^2.1.24", + "@radix-ui/react-popover": "^1.1.23", + "@radix-ui/react-radio-group": "^1.4.7", + "@radix-ui/react-scroll-area": "^1.2.18", + "@radix-ui/react-select": "^2.3.7", + "@radix-ui/react-slider": "^1.4.7", + "@radix-ui/react-slot": "^1.3.3", + "@radix-ui/react-switch": "^1.3.7", + "@radix-ui/react-tabs": "^1.1.21", + "@radix-ui/react-tooltip": "^1.2.16", + "@uiw/react-codemirror": "^4.25.12", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cn": "^0.4.0", + "framer-motion": "^13.4.6", + "lucide-react": "^1.48.0", + "next": "^16.3.7", + "pdfjs-dist": "^6.3.289", + "react": "^19.3.0", + "react-dom": "^19.3.0", + "tailwind-merge": "^3.7.0", + "tw-animate-css": "^1.4.0" }, "devDependencies": { - "browser-sync": "^2.26.7", - "compass-mixins": "^0.12.10", - "del": "^5.0.0", - "gulp": "^4.0.2", - "gulp-autoprefixer": "^6.1.0", - "gulp-clean-css": "^4.2.0", - "gulp-concat": "^2.6.1", - "gulp-htmlmin": "^5.0.1", - "gulp-inject": "5.0.2", - "gulp-inject-partials": "^1.0.5", - "gulp-notify": "^3.2.0", - "gulp-plumber": "^1.2.1", - "gulp-prettify": "^0.5.0", - "gulp-rename": "^1.4.0", - "gulp-replace": "^1.0.0", - "gulp-sass": "^4.0.2", - "gulp-sourcemaps": "^2.6.5", - "gulp-util": "^3.0.8", - "merge-stream": "^2.0.0", - "minimatch": "^3.0.4", - "require-dir": "^1.2.0", - "run-sequence": "^2.2.1" - }, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "@eslint/compat": "^2.1.1", + "@playwright/test": "^1.63.0", + "@tailwindcss/postcss": "^4.3.3", + "@types/node": "^26.6.3", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", + "@typescript/native": "^7.0.2", + "eslint": "^10.11.0", + "eslint-config-next": "^16.3.7", + "tailwindcss": "^4.3.3", + "typescript": "^6.0.2" }, - "author": "bootstrapdash", - "url": "http://www.bootstrapdash.com/", - "copyright": "", - "license": "" + "packageManager": "bun@1.3.14" } diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..a9b5c43 --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1 @@ +export default { plugins: { "@tailwindcss/postcss": {} } }; diff --git a/src/app/globals.css b/src/app/globals.css new file mode 100644 index 0000000..f9bbe96 --- /dev/null +++ b/src/app/globals.css @@ -0,0 +1,373 @@ +@import "tailwindcss"; +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); + +@theme inline { + --color-hover: var(--hover); + --color-active: var(--active); + --color-selected: var(--selected); + --color-destructive-light: var(--destructive-light); + --color-surface-1: var(--surface-1); + --color-surface-2: var(--surface-2); + --color-surface-3: var(--surface-3); + --color-surface-4: var(--surface-4); + --color-surface-5: var(--surface-5); + --color-surface-6: var(--surface-6); + --color-surface-7: var(--surface-7); + --color-surface-8: var(--surface-8); + --shadow-surface-1: var(--shadow-1); + --shadow-surface-2: var(--shadow-2); + --shadow-surface-3: var(--shadow-3); + --shadow-surface-4: var(--shadow-4); + --shadow-surface-5: var(--shadow-5); + --shadow-surface-6: var(--shadow-6); + --shadow-surface-7: var(--shadow-7); + --shadow-surface-8: var(--shadow-8); + --color-dm-drop: var(--dm-drop); + --color-dm-ring-high: var(--dm-ring-high); + --color-dm-ring-mid: var(--dm-ring-mid); + --color-dm-ring-base: var(--dm-ring-base); + --color-dm-hi-peak: var(--dm-hi-peak); + --color-dm-hi-high: var(--dm-hi-high); + --color-dm-hi-mid: var(--dm-hi-mid); + --color-dm-hi-base: var(--dm-hi-base); + --color-checker-b: var(--checker-b); + --color-checker-a: var(--checker-a); + --shadow-8: var(--shadow-8); + --shadow-7: var(--shadow-7); + --shadow-6: var(--shadow-6); + --shadow-5: var(--shadow-5); + --shadow-4: var(--shadow-4); + --shadow-3: var(--shadow-3); + --shadow-2: var(--shadow-2); + --shadow-1: var(--shadow-1); + --color-shadow-color: var(--shadow-color); + --color-focus-ring: var(--focus-ring); + --overlay: var(--overlay); + @keyframes shimmer { + 0% { + background-position: 0% 0; + } + 100% { + background-position: 100% 0; + } + } + @keyframes spinner-move { + to { + stroke-dashoffset: -100; + } + } + @keyframes spinner-dash { + 0%, 100% { + stroke-dasharray: 15 85; + } + 50% { + stroke-dasharray: 40 60; + } + } + @keyframes sf-reveal-start { + from { + --sf-start: 1; + } + to { + --sf-start: 0; + } + } + @keyframes sf-reveal-end { + from { + --sf-end: 0; + } + to { + --sf-end: 1; + } + } + @keyframes sf-divider-start { + from { + opacity: 0; + } + to { + opacity: 1; + } + } + @keyframes sf-divider-end { + from { + opacity: 1; + } + to { + opacity: 0; + } + } +} + +:root { + --hover: rgb(0 0 0 / 0.04); + --active: rgb(0 0 0 / 0.07); + --selected: #D4D4D4; + --destructive-light: #FEF2F2; + --overlay: 0 0 0; + --focus-ring: #6B97FF; + --surface-1: #FAFAFA; + --surface-2: #FCFCFC; + --surface-3: #FFFFFF; + --surface-4: #FFFFFF; + --surface-5: #FFFFFF; + --surface-6: #FFFFFF; + --surface-7: #FFFFFF; + --surface-8: #FFFFFF; + --shadow-color: rgb(0 0 0 / 0.06); + --shadow-1: 0 0 0 1px var(--shadow-color); + --shadow-2: 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color); + --shadow-3: 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color), 0 3px 3px -1.5px var(--shadow-color); + --shadow-4: 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color), 0 3px 3px -1.5px var(--shadow-color), 0 6px 6px -3px var(--shadow-color); + --shadow-5: 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color), 0 3px 3px -1.5px var(--shadow-color), 0 6px 6px -3px var(--shadow-color), 0 12px 12px -6px var(--shadow-color); + --shadow-6: 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color), 0 3px 3px -1.5px var(--shadow-color), 0 6px 6px -3px var(--shadow-color), 0 12px 12px -6px var(--shadow-color), 0 24px 24px -12px var(--shadow-color); + --shadow-7: 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color), 0 3px 3px -1.5px var(--shadow-color), 0 6px 6px -3px var(--shadow-color), 0 12px 12px -6px var(--shadow-color), 0 24px 24px -12px var(--shadow-color), 0 48px 48px -24px var(--shadow-color); + --shadow-8: 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color), 0 3px 3px -1.5px var(--shadow-color), 0 6px 6px -3px var(--shadow-color), 0 12px 12px -6px var(--shadow-color), 0 24px 24px -12px var(--shadow-color), 0 48px 48px -24px var(--shadow-color), 0 96px 96px -48px var(--shadow-color); + --checker-a: #bbbbbb; + --checker-b: #ffffff; +} + +.dark { + --hover: rgb(255 255 255 / 0.06); + --active: rgb(255 255 255 / 0.1); + --selected: #525252; + --destructive-light: #450A0A; + --overlay: 255 255 255; + --surface-1: #171717; + --surface-2: #1E1E1E; + --surface-3: #252525; + --surface-4: #2C2C2C; + --surface-5: #333333; + --surface-6: #3A3A3A; + --surface-7: #414141; + --surface-8: #484848; + --dm-hi-base: rgba(255,255,255,0.01); + --dm-hi-mid: rgba(255,255,255,0.02); + --dm-hi-high: rgba(255,255,255,0.04); + --dm-hi-peak: rgba(255,255,255,0.06); + --dm-ring-base: rgba(255,255,255,0.02); + --dm-ring-mid: rgba(255,255,255,0.04); + --dm-ring-high: rgba(255,255,255,0.06); + --dm-drop: rgba(0,0,0,0.18); + --shadow-1: inset 0 0 0 1px var(--dm-ring-base); + --shadow-2: inset 0 1px 0 0 var(--dm-hi-base), inset 0 0 0 1px var(--dm-ring-base), 0 1px 1px -0.5px var(--dm-drop); + --shadow-3: inset 0 1px 0 0 var(--dm-hi-mid), inset 0 0 0 1px var(--dm-ring-base), 0 0 0 1px rgba(0,0,0,0.12), 0 1px 1px -0.5px var(--dm-drop), 0 3px 3px -1.5px var(--dm-drop); + --shadow-4: inset 0 1px 0 0 var(--dm-hi-mid), inset 0 0 0 1px var(--dm-ring-mid), 0 0 0 1px rgba(0,0,0,0.14), 0 1px 1px -0.5px var(--dm-drop), 0 3px 3px -1.5px var(--dm-drop), 0 6px 6px -3px var(--dm-drop); + --shadow-5: inset 0 1px 0 0 var(--dm-hi-high), inset 0 0 0 1px var(--dm-ring-mid), 0 0 0 1px rgba(0,0,0,0.16), 0 1px 1px -0.5px var(--dm-drop), 0 3px 3px -1.5px var(--dm-drop), 0 6px 6px -3px var(--dm-drop), 0 12px 12px -6px var(--dm-drop); + --shadow-6: inset 0 1px 0 0 var(--dm-hi-high), inset 0 0 0 1px var(--dm-ring-high), 0 0 0 1px rgba(0,0,0,0.18), 0 1px 1px -0.5px var(--dm-drop), 0 3px 3px -1.5px var(--dm-drop), 0 6px 6px -3px var(--dm-drop), 0 12px 12px -6px var(--dm-drop), 0 24px 24px -12px var(--dm-drop); + --shadow-7: inset 0 1px 0 0 var(--dm-hi-peak), inset 0 0 0 1px var(--dm-ring-high), 0 0 0 1px rgba(0,0,0,0.20), 0 1px 1px -0.5px var(--dm-drop), 0 3px 3px -1.5px var(--dm-drop), 0 6px 6px -3px var(--dm-drop), 0 12px 12px -6px var(--dm-drop), 0 24px 24px -12px var(--dm-drop), 0 48px 48px -24px var(--dm-drop); + --shadow-8: inset 0 1px 0 0 var(--dm-hi-peak), inset 0 0 0 1px var(--dm-ring-high), 0 0 0 1px rgba(0,0,0,0.22), 0 1px 1px -0.5px var(--dm-drop), 0 3px 3px -1.5px var(--dm-drop), 0 6px 6px -3px var(--dm-drop), 0 12px 12px -6px var(--dm-drop), 0 24px 24px -12px var(--dm-drop), 0 48px 48px -24px var(--dm-drop), 0 96px 96px -48px var(--dm-drop); + --checker-a: #1f1f1f; + --checker-b: #2a2a2a; +} + +@layer base { + :focus-visible { + outline: 1px solid var(--focus-ring, #6B97FF); + outline-offset: 2px; + border-radius: var(--shape-input-radius, 8px); + } + a:focus-visible { + border-radius: 2px; + } +} + html.transitioning *, html.transitioning *::before, html.transitioning *::after { + transition: border-radius 180ms ease-in-out, background-color 180ms ease-in-out, color 180ms ease-in-out, border-color 180ms ease-in-out, fill 180ms ease-in-out, stroke 180ms ease-in-out !important; +} + .shimmer-text { + color: transparent; + background: linear-gradient(90deg, #a3a3a3 0%, #a3a3a3 35%, #525252 50%, #a3a3a3 65%, #a3a3a3 100%); + background-size: 300% 100%; + background-clip: text; + -webkit-background-clip: text; + animation: shimmer 1.5s ease-in-out infinite; +} + @property --sf-start { + syntax: ""; + inherits: false; + initial-value: 1; +} + @property --sf-end { + syntax: ""; + inherits: false; + initial-value: 1; +} + .scroll-fade, .scroll-fade-x { + --scroll-fade-size: 48px; +} + .scroll-fade { + -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--scroll-fade-size), #000 calc(100% - var(--scroll-fade-size)), transparent 100%); + mask-image: linear-gradient(to bottom, transparent 0, #000 var(--scroll-fade-size), #000 calc(100% - var(--scroll-fade-size)), transparent 100%); +} + .scroll-fade-x { + -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--scroll-fade-size), #000 calc(100% - var(--scroll-fade-size)), transparent 100%); + mask-image: linear-gradient(to right, transparent 0, #000 var(--scroll-fade-size), #000 calc(100% - var(--scroll-fade-size)), transparent 100%); +} + +@supports (timeline-scope: --sf) { + .scroll-divider { + position: relative; + timeline-scope: --sf-scroller; + --scroll-divider-size: 48px; + } + .scroll-divider .scroll-fade { + scroll-timeline-name: --sf-scroller; + } + .scroll-divider::before, .scroll-divider::after { + content: ""; + position: absolute; + left: var(--scroll-divider-inset, 0px); + right: var(--scroll-divider-inset, 0px); + height: 1px; + background-color: var(--border); + pointer-events: none; + z-index: 1; + opacity: 0; + } + .scroll-divider::before { + top: 0; + animation: sf-divider-start both linear; + animation-timeline: --sf-scroller; + animation-range: 0 var(--scroll-divider-size); + } + .scroll-divider:first-child::before { + content: none; + } + .scroll-divider::after { + bottom: 0; + animation: sf-divider-end both linear; + animation-timeline: --sf-scroller; + animation-range: calc(100% - var(--scroll-divider-size)) 100%; + } +} + +@supports (animation-timeline: scroll()) { + .scroll-fade { + mask-image: linear-gradient(to bottom, rgba(0, 0, 0, var(--sf-start)) 0, #000 var(--scroll-fade-size), #000 calc(100% - var(--scroll-fade-size)), rgba(0, 0, 0, var(--sf-end)) 100%); + -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, var(--sf-start)) 0, #000 var(--scroll-fade-size), #000 calc(100% - var(--scroll-fade-size)), rgba(0, 0, 0, var(--sf-end)) 100%); + animation: sf-reveal-start both linear, sf-reveal-end both linear; + animation-timeline: scroll(self), scroll(self); + animation-range-start: 0, calc(100% - var(--scroll-fade-size)); + animation-range-end: var(--scroll-fade-size), 100%; + } + .scroll-fade-x { + mask-image: linear-gradient(to right, rgba(0, 0, 0, var(--sf-start)) 0, #000 var(--scroll-fade-size), #000 calc(100% - var(--scroll-fade-size)), rgba(0, 0, 0, var(--sf-end)) 100%); + -webkit-mask-image: linear-gradient(to right, rgba(0, 0, 0, var(--sf-start)) 0, #000 var(--scroll-fade-size), #000 calc(100% - var(--scroll-fade-size)), rgba(0, 0, 0, var(--sf-end)) 100%); + animation: sf-reveal-start both linear, sf-reveal-end both linear; + animation-timeline: scroll(self inline), scroll(self inline); + animation-range-start: 0, calc(100% - var(--scroll-fade-size)); + animation-range-end: var(--scroll-fade-size), 100%; + } +} + +@media (pointer: fine) { + * { + scrollbar-width: thin; + scrollbar-color: rgb(var(--overlay) / 0.08) transparent; + } + ::-webkit-scrollbar { + width: 10px; + height: 10px; + } + ::-webkit-scrollbar-track, ::-webkit-scrollbar-corner { + background: transparent; + } + ::-webkit-scrollbar-thumb { + background-color: rgb(var(--overlay) / 0.08); + border: 3px solid transparent; + background-clip: content-box; + border-radius: 9999px; + } + ::-webkit-scrollbar-thumb:vertical { + border-left-width: 1px; + border-right-width: 5px; + } + ::-webkit-scrollbar-thumb:horizontal { + border-top-width: 1px; + border-bottom-width: 5px; + } + ::-webkit-scrollbar-thumb:hover { + background-color: rgb(var(--overlay) / 0.12); + } + ::-webkit-scrollbar-thumb:active { + background-color: rgb(var(--overlay) / 0.16); + } +} + .scrollbar-hide { + -ms-overflow-style: none; + scrollbar-width: none; +} + .scrollbar-hide::-webkit-scrollbar { + display: none; +} +/* Application palette layered over Fluid's published surface tokens. */ +@theme inline { + --font-sans: "Inter Variable", sans-serif; + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--foreground); + --color-border: var(--border); + --color-input: var(--border); + --color-ring: var(--focus-ring); + --color-destructive: #d14d41; + --color-destructive-foreground: #fff; + --color-card: var(--surface-2); + --color-card-foreground: var(--foreground); + --color-popover: var(--surface-3); + --color-popover-foreground: var(--foreground); +} +:root { + --background: #f5f5f2; + --foreground: #242723; + --primary: #303d2c; + --primary-foreground: #fff; + --secondary: #e8ebe4; + --muted: #eeefea; + --muted-foreground: #73786f; + --accent: #e7ebdf; + --border: #e1e4db; + --focus-ring: #7c9265; + --radius: 0.75rem; +} +.dark { + --background: #181b17; + --foreground: #eff1e9; + --primary: #c8d8b7; + --primary-foreground: #222a1e; + --secondary: #30372c; + --muted: #252b22; + --muted-foreground: #a2ab9b; + --accent: #343e2d; + --border: #363d31; +} +@layer base { + * { border-color: var(--border); } + body { margin: 0; background: var(--background); color: var(--foreground); font-family: "Inter Variable", sans-serif; font-size: 14px; -webkit-font-smoothing: antialiased; } + button, a, input, textarea { -webkit-tap-highlight-color: transparent; } + button:not(:disabled), [role="button"] { cursor: pointer; } + ::selection { background: #c9d8b9; color: #242723; } +} +.workspace-panel { border: 1px solid var(--border); border-radius: 18px; background: var(--surface-2); overflow: hidden; } +.panel-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 18px 22px; border-bottom: 1px solid var(--border); } +.eyebrow { font-size: 10px; font-weight: 600; letter-spacing: .15em; text-transform: uppercase; color: var(--muted-foreground); } +.code-editor .cm-editor { background: transparent; } +.code-editor .cm-scroller { min-height: 340px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; } +.code-editor .cm-focused { outline: none; } +.code-editor .cm-gutters { background: transparent; border: 0; color: #a3a99d; } +.code-editor .cm-content { padding: 22px 0; } +.code-editor .cm-line { padding-left: 16px; } +.code-editor .cm-gutterElement { padding-right: 12px; padding-left: 16px; } +.code-editor .cm-activeLine, .code-editor .cm-activeLineGutter { background: rgb(124 146 101 / .07); } +.dark .cm-editor { color: var(--foreground); } +.dark .cm-cursor { border-left-color: var(--foreground); } +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; } +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..38bf39e --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,12 @@ +import type { Metadata } from "next"; +import "@fontsource-variable/inter"; +import "./globals.css"; + +export const metadata: Metadata = { + title: "bTagScript — Playground", + description: "A workspace for writing, testing, and exploring TagScript.", +}; + +export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) { + return {children}; +} diff --git a/src/app/page.tsx b/src/app/page.tsx new file mode 100644 index 0000000..aa59d1a --- /dev/null +++ b/src/app/page.tsx @@ -0,0 +1,3 @@ +export default function Home() { + return
bTagScript Playground
; +} diff --git a/src/components/fluid-hover-highlight.tsx b/src/components/fluid-hover-highlight.tsx new file mode 100644 index 0000000..633cce2 --- /dev/null +++ b/src/components/fluid-hover-highlight.tsx @@ -0,0 +1,138 @@ +"use client"; + +import { + motion, + AnimatePresence, + useReducedMotion, + type Transition, +} from "framer-motion"; +import { cn } from "@/lib/utils"; +import { spring } from "@/lib/springs"; +import type { ItemRect, UseFluidHoverReturn } from "@/hooks/use-fluid-hover"; + +// --------------------------------------------------------------------------- +// The one hover highlight every fluid hover list renders: an absolutely +// positioned fill that springs between the rects `useFluidHover` measures. +// Consumers used to hand-roll this motion.div; this is that block, once. +// +// const hover = useFluidHover(ref); +// +// +// It owns no layout opinion beyond `absolute`: radius, z-index, and the +// offsetParent (the container must be `relative`) are the consumer's. +// --------------------------------------------------------------------------- + +/** What the highlight reads off the hook: the highlighted index, the + * measured rects, whether they are current, and the pointer session. */ +export type FluidHoverSource = Pick< + UseFluidHoverReturn, + "activeIndex" | "itemRects" | "isMeasured" | "sessionRef" +>; + +interface HighlightFromHook { + /** The hook's return value. The highlight sits on + * `itemRects[activeIndex]` once `isMeasured`, and re-keys on the session. */ + hover: FluidHoverSource; + /** Keep the list's state but show nothing (a closed popup, hover switched + * off). Runs the exit fade. */ + hidden?: boolean; + rect?: never; + session?: never; +} + +interface HighlightFromRect { + /** For lists that resolve their own rect (the sidebar's unified scope): + * the rect to sit on, in the container's coordinate space. `null` hides + * the highlight (it fades out on `spring.fast.exit`). */ + rect: ItemRect | null; + /** `sessionRef.current` from `useFluidHover`. It increments when the + * cursor enters the container, which re-keys the highlight so it fades in + * at `from ?? rect` instead of sliding over from wherever it was last. */ + session: number; + hover?: never; + hidden?: never; +} + +export type FluidHoverHighlightProps = (HighlightFromHook | HighlightFromRect) & { + /** Where a fresh session fades in from. A dropdown passes its checked row, + * a nav menu its active route. Defaults to the rect itself. */ + from?: ItemRect | null; + /** Radius, z-index, anything else. Merged onto + * `absolute bg-hover pointer-events-none`. */ + className?: string; + /** The positional spring. Defaults to `spring.fast`. Pass `false` to snap + * to the new rect with no travel (a layout reflow that moved the rows + * underneath, not a hover change). The opacity fade is always 0.08s. */ + transition?: Transition | false; +}; + +const fade: Transition = { duration: 0.08 }; +const snap: Transition = { duration: 0 }; + +/** A measured rect as animation targets: position as a transform, size as + * layout. Exported for the unit test. */ +export function toTarget(rect: ItemRect) { + return { x: rect.left, y: rect.top, width: rect.width, height: rect.height }; +} + +/** + * Resolves the positional transition. Reduced motion keeps the opacity fade + * and drops the travel, per the motion guidelines: fewer and gentler, not + * none. Exported for the unit test. + */ +export function resolveHighlightTransition( + transition: Transition | false | undefined, + reduceMotion: boolean +): Transition { + const positional = + transition === false || reduceMotion ? snap : (transition ?? spring.fast); + return { ...positional, opacity: fade }; +} + +/** The rect and session a set of props resolves to. Exported for the test. */ +export function resolveHighlightSource( + props: FluidHoverHighlightProps +): { rect: ItemRect | null; session: number } { + if (props.hover) { + const { activeIndex, itemRects, isMeasured, sessionRef } = props.hover; + const rect = + !props.hidden && isMeasured && activeIndex !== null + ? (itemRects[activeIndex] ?? null) + : null; + return { rect, session: sessionRef.current }; + } + return { rect: props.rect, session: props.session }; +} + +export function FluidHoverHighlight(props: FluidHoverHighlightProps) { + const { from, className, transition } = props; + const { rect, session } = resolveHighlightSource(props); + // Reads the OS media query directly, so an installed copy honours reduced + // motion without the app wrapping its tree in MotionConfig. A wrapped app + // gets the same result twice over: the travel is a transform, which + // MotionConfig reduces too. + const reduceMotion = useReducedMotion() ?? false; + return ( + + {rect && ( + + )} + + ); +} diff --git a/src/components/ui/accordion.tsx b/src/components/ui/accordion.tsx new file mode 100644 index 0000000..779f41a --- /dev/null +++ b/src/components/ui/accordion.tsx @@ -0,0 +1,961 @@ +"use client"; + +import { + useRef, + useState, + useEffect, + useLayoutEffect, + useCallback, + useMemo, + createContext, + useContext, + forwardRef, + type ReactNode, + type HTMLAttributes, +} from "react"; +import { motion, AnimatePresence, useReducedMotion } from "framer-motion"; +import * as AccordionPrimitive from "@radix-ui/react-accordion"; + +// SSR-safe layout effect (client components still server-render in Next). +const useIsoLayoutEffect = + typeof window !== "undefined" ? useLayoutEffect : useEffect; +import { cn } from "@/lib/utils"; +import { useIcon } from "@/lib/icon-context"; +import { spring } from "@/lib/springs"; +import { fontWeights } from "@/lib/font-weight"; +import { useFluidHover, useRegisterFluidHoverItem } from "@/hooks/use-fluid-hover"; +import { useShape } from "@/lib/shape-context"; +import { SizeProvider, useSize, type SizeVariant } from "@/lib/size-context"; +import { FluidHoverHighlight } from "@/components/fluid-hover-highlight"; + +// ─── Contexts ──────────────────────────────────────────────────────────────── + +interface ItemRect { + top: number; + left: number; + width: number; + height: number; +} + +interface AccordionGroupContextValue { + registerItem: (index: number, element: HTMLElement | null) => void; + registerFullItem: (index: number, element: HTMLElement | null) => void; + activeIndex: number | null; + grouped: true; + remeasure: () => void; + openValues: Set; + openItemRects: Map; +} + +const AccordionGroupContext = + createContext(null); + +function useAccordionGroup() { + return useContext(AccordionGroupContext); +} + +interface AccordionItemContextValue { + index?: number; + value: string; + isOpen: boolean; + triggerRef: React.MutableRefObject; + /** Standalone items carry the group's choice themselves. */ + highlight: "trigger" | "item"; +} + +const AccordionItemContext = + createContext(null); + +function useAccordionItemContext() { + const ctx = useContext(AccordionItemContext); + if (!ctx) + throw new Error( + "AccordionTrigger/AccordionContent must be used within an AccordionItem" + ); + return ctx; +} + +// ─── AccordionGroup ────────────────────────────────────────────────────────── + +type AccordionGroupSingleProps = { + type?: "single"; + value?: string; + defaultValue?: string; + onValueChange?: (value: string) => void; + collapsible?: boolean; +}; + +type AccordionGroupMultipleProps = { + type: "multiple"; + value?: string[]; + defaultValue?: string[]; + onValueChange?: (value: string[]) => void; +}; + +type AccordionGroupProps = HTMLAttributes & { + children: ReactNode; + /** Pins the group's rows to one step of the size ladder (default 36px, + * compact 28px — see /docs/sizes). Omitted, they follow the surrounding + * SizeProvider. */ + size?: SizeVariant; + /** What an open item tints. "item" paints the row and its panel as one + * block, and holds while it stays open. "trigger" scopes the fill to the + * row and shows it on hover only, leaving the panel on the page's own + * surface — the way a sidebar row highlights without colouring its + * sub-tree. @default "item" */ + highlight?: "trigger" | "item"; +} & (AccordionGroupSingleProps | AccordionGroupMultipleProps); + +const AccordionGroup = forwardRef( + (props, ref) => { + const { + children, + highlight = "item", + type = "single", + size, + className, + ...rest + } = props; + + const containerRef = useRef(null); + const fullItemElementsRef = useRef>(new Map()); + const [openItemRects, setOpenItemRects] = useState>( + new Map() + ); + const openItemRectsRef = useRef(openItemRects); + + const hover = useFluidHover(containerRef); + const { + activeIndex, + setActiveIndex, + itemRects, + handlers, + registerItem, + measureItems, + } = hover; + + const registerFullItem = useCallback( + (index: number, element: HTMLElement | null) => { + if (element) { + fullItemElementsRef.current.set(index, element); + } else { + fullItemElementsRef.current.delete(index); + } + }, + [] + ); + + const measureFullItems = useCallback(() => { + if (!containerRef.current) return; + const next = new Map(); + // Use offset* (layout coords) to match the fluid hover hook's items. + // getBoundingClientRect would return visual coords already scaled by + // any ancestor transform; once applied as CSS inside the same scaled + // container, the overlay would scale a second time. + fullItemElementsRef.current.forEach((el, idx) => { + next.set(idx, { + top: el.offsetTop, + left: el.offsetLeft, + width: el.offsetWidth, + height: el.offsetHeight, + }); + }); + // Skip the state update when nothing moved (mirrors the fluid hover + // hook's measureItems guard) — this runs per animation frame via + // onUpdate, and an unconditional set would invalidate the group + // context and re-render every item even on no-op remeasures. + const prev = openItemRectsRef.current; + let changed = prev.size !== next.size; + if (!changed) { + for (const [idx, r] of next) { + const p = prev.get(idx); + if ( + !p || + p.top !== r.top || + p.left !== r.left || + p.width !== r.width || + p.height !== r.height + ) { + changed = true; + break; + } + } + } + if (!changed) return; + openItemRectsRef.current = next; + setOpenItemRects(next); + }, []); + + // Track open values for context + const [internalSingleValue, setInternalSingleValue] = useState( + () => { + if (type === "single") { + const sp = props as AccordionGroupSingleProps; + return sp.defaultValue ?? ""; + } + return ""; + } + ); + const [internalMultipleValue, setInternalMultipleValue] = useState< + string[] + >(() => { + if (type === "multiple") { + const mp = props as AccordionGroupMultipleProps; + return mp.defaultValue ?? []; + } + return []; + }); + const singleOnValueChange = (props as AccordionGroupSingleProps).onValueChange; + const multipleOnValueChange = (props as AccordionGroupMultipleProps).onValueChange; + + const openValuesList: string[] = + type === "multiple" + ? (props as AccordionGroupMultipleProps).value ?? internalMultipleValue + : (() => { + const v = + (props as AccordionGroupSingleProps).value ?? internalSingleValue; + return v ? [v] : []; + })(); + + // Keyed on the joined values so the Set (and the group context value + // below) keeps a stable identity across re-renders where the open values + // haven't actually changed. + const openValuesKey = openValuesList.join(","); + + const openValues = useMemo( + () => new Set(openValuesList), + // Deliberately keyed on the joined string, not the (fresh) array. + [openValuesKey] + ); + + const handleSingleValueChange = useCallback( + (value: string) => { + const sp = props as AccordionGroupSingleProps; + if (sp.onValueChange) sp.onValueChange(value); + else setInternalSingleValue(value); + }, + [singleOnValueChange] + ); + + const handleMultipleValueChange = useCallback( + (value: string[]) => { + const mp = props as AccordionGroupMultipleProps; + if (mp.onValueChange) mp.onValueChange(value); + else setInternalMultipleValue(value); + }, + [multipleOnValueChange] + ); + + useEffect(() => { + measureItems(); + measureFullItems(); + }, [measureItems, measureFullItems, children]); + + // Remeasure when open values change so the first paint already + // reflects shifted trigger positions. + useEffect(() => { + measureItems(); + measureFullItems(); + }, [measureItems, measureFullItems, openValuesKey]); + + const [focusedIndex, setFocusedIndex] = useState(null); + + const focusRect = focusedIndex !== null ? itemRects[focusedIndex] : null; + // Dimming: reduce expanded BG opacity when hovering a non-expanded trigger + // An open item tints its trigger by default; "item" restores the older + // block treatment that spans the panel too. The trigger rects are the + // ones fluid hover already tracks, so this is a choice of source. + // "trigger" tints the open row only while you're on it: the panel below + // already says the item is open, so the fill goes back to being a hover + // affordance rather than a persistent state. + const expandedRects = + highlight === "item" + ? openItemRects + : new Map( + [...openItemRects.keys()].flatMap((idx) => { + const rect = idx === activeIndex ? itemRects[idx] : null; + return rect ? ([[idx, rect]] as [number, ItemRect][]) : []; + }) + ); + + const isHoveringNonOpen = + activeIndex !== null && !openItemRects.has(activeIndex); + const shape = useShape(); + + // Strip non-HTML props before spreading + const { + value: _value, + defaultValue: _defaultValue, + onValueChange: _onValueChange, + collapsible: _collapsible, + type: _type, + ...htmlProps + } = rest as Record; + + // Build Radix root props + const radixProps = + type === "multiple" + ? { + type: "multiple" as const, + value: + (props as AccordionGroupMultipleProps).value ?? + internalMultipleValue, + onValueChange: handleMultipleValueChange, + } + : { + type: "single" as const, + collapsible: + (props as AccordionGroupSingleProps).collapsible ?? true, + value: + (props as AccordionGroupSingleProps).value ?? internalSingleValue, + onValueChange: handleSingleValueChange, + }; + + const remeasure = useCallback(() => { + measureItems(); + measureFullItems(); + }, [measureItems, measureFullItems]); + + // Memoized: the group re-renders on every fluid-hover mousemove; a + // fresh context object each time would re-render every item with it. + const groupContextValue = useMemo( + () => ({ + registerItem, + registerFullItem, + activeIndex, + grouped: true, + remeasure, + openValues, + openItemRects, + }), + [ + registerItem, + registerFullItem, + activeIndex, + remeasure, + openValues, + openItemRects, + ] + ); + + const group = ( + + +
{ + ( + containerRef as React.MutableRefObject + ).current = node; + if (typeof ref === "function") ref(node); + else if (ref) + ( + ref as React.MutableRefObject + ).current = node; + }} + onMouseEnter={handlers.onMouseEnter} + onMouseMove={(e) => { + // Suppress fluid hover when cursor is over an expanded + // content area (below the item's trigger). This keeps trigger + // hover scoped to the trigger row only. + const container = containerRef.current; + if (container) { + const cRect = container.getBoundingClientRect(); + const layoutH = container.offsetHeight; + const visualH = cRect.height; + const scale = layoutH > 0 ? visualH / layoutH : 1; + const localY = + (e.clientY - cRect.top) / scale + container.scrollTop; + for (const [idx, full] of openItemRects) { + const trigger = itemRects[idx]; + if (!trigger) continue; + const contentTop = trigger.top + trigger.height; + const contentBottom = full.top + full.height; + if (localY >= contentTop && localY <= contentBottom) { + setActiveIndex(null); + return; + } + } + } + handlers.onMouseMove(e); + }} + onMouseLeave={handlers.onMouseLeave} + onFocus={(e) => { + const indexAttr = (e.target as HTMLElement) + .closest("[data-fluid-hover-index]") + ?.getAttribute("data-fluid-hover-index"); + if (indexAttr != null) { + const idx = Number(indexAttr); + setActiveIndex(idx); + setFocusedIndex( + (e.target as HTMLElement).matches(":focus-visible") + ? idx + : null + ); + } + }} + onBlur={(e) => { + if ( + containerRef.current?.contains(e.relatedTarget as Node) + ) + return; + setFocusedIndex(null); + setActiveIndex(null); + }} + className={cn( + "relative flex flex-col w-72 max-w-full", + className + )} + {...(htmlProps as HTMLAttributes)} + > + {/* Expanded item backgrounds */} + + {[...expandedRects.entries()].map(([idx, rect]) => ( + + ))} + + + {/* Hover background */} + + + {/* Focus ring */} + + {focusRect && ( + + )} + + + {children} +
+
+
+ ); + + // A size prop pins every row in the group to one ladder step. + return size ? {group} : group; + } +); + +AccordionGroup.displayName = "AccordionGroup"; + +// ─── Accordion (Standalone) ────────────────────────────────────────────────── + +interface AccordionProps extends HTMLAttributes { + children: ReactNode; + type?: "single" | "multiple"; + collapsible?: boolean; + defaultValue?: string | string[]; + value?: string | string[]; + onValueChange?: ((value: string) => void) | ((value: string[]) => void); + /** Pins the accordion's rows to one step of the size ladder (default 36px, + * compact 28px — see /docs/sizes). Omitted, they follow the surrounding + * SizeProvider. */ + size?: SizeVariant; +} + +const Accordion = forwardRef( + ( + { + children, + type = "single", + collapsible = true, + defaultValue, + value, + onValueChange, + size, + className, + ...props + }, + ref + ) => { + // Track open values for AccordionItemContext + const [internalSingleValue, setInternalSingleValue] = useState( + () => { + if (type === "single") { + return (defaultValue as string) ?? ""; + } + return ""; + } + ); + const [internalMultipleValue, setInternalMultipleValue] = useState< + string[] + >(() => { + if (type === "multiple") { + return (defaultValue as string[]) ?? []; + } + return []; + }); + + const openValues = new Set( + type === "multiple" + ? (value as string[] | undefined) ?? internalMultipleValue + : (() => { + const v = (value as string | undefined) ?? internalSingleValue; + return v ? [v] : []; + })() + ); + + const handleSingleChange = useCallback( + (v: string) => { + if (onValueChange) (onValueChange as (v: string) => void)(v); + else setInternalSingleValue(v); + }, + [onValueChange] + ); + + const handleMultipleChange = useCallback( + (v: string[]) => { + if (onValueChange) (onValueChange as (v: string[]) => void)(v); + else setInternalMultipleValue(v); + }, + [onValueChange] + ); + + // `value` is always defined here (internal state is seeded from + // `defaultValue` above), so the Radix root is permanently controlled and + // forwarding `defaultValue` would be dead. + const radixProps = + type === "multiple" + ? { + type: "multiple" as const, + value: (value as string[] | undefined) ?? internalMultipleValue, + onValueChange: handleMultipleChange, + } + : { + type: "single" as const, + collapsible, + value: (value as string | undefined) ?? internalSingleValue, + onValueChange: handleSingleChange, + }; + + const root = ( + +
+ + {children} + +
+
+ ); + + // A size prop pins every row to one ladder step. + return size ? {root} : root; + } +); + +Accordion.displayName = "Accordion"; + +// Standalone context to provide open values without AccordionGroup +const StandaloneOpenContext = createContext>(new Set()); + +// ─── AccordionItem ─────────────────────────────────────────────────────────── + +interface AccordionItemProps extends HTMLAttributes { + value: string; + index?: number; + disabled?: boolean; + /** Standalone equivalent of AccordionGroup's prop: what an open item + * tints. Ignored inside a group, which decides for all its rows. + * @default "item" */ + highlight?: "trigger" | "item"; + children: ReactNode; +} + +const AccordionItem = forwardRef( + ({ value, index, disabled, highlight = "item", children, className, ...props }, ref) => { + const internalRef = useRef(null); + const groupCtx = useAccordionGroup(); + const standaloneOpen = useContext(StandaloneOpenContext); + const shape = useShape(); + + const isOpen = groupCtx?.grouped + ? groupCtx.openValues.has(value) + : standaloneOpen.has(value); + + const triggerRef = useRef(null); + + // Register trigger element (not full item) for fluid hover + useRegisterFluidHoverItem( + groupCtx?.grouped ? groupCtx.registerItem : undefined, + index, + triggerRef + ); + + // Register full item element for expanded background measurement + useEffect(() => { + if (groupCtx?.grouped && index !== undefined) { + if (isOpen) { + groupCtx.registerFullItem(index, internalRef.current); + } else { + groupCtx.registerFullItem(index, null); + } + return () => groupCtx.registerFullItem(index, null); + } + }, [index, groupCtx, isOpen]); + + return ( + + { + ( + internalRef as React.MutableRefObject + ).current = node; + if (typeof ref === "function") ref(node); + else if (ref) + ( + ref as React.MutableRefObject + ).current = node; + }} + value={value} + disabled={disabled} + data-fluid-hover-index={index} + className={cn(!groupCtx?.grouped && "relative", className)} + {...props} + > + {/* Standalone expanded background. Under the default "trigger" + choice the tint lives inside AccordionTrigger, where it covers + the row and not the panel below it. */} + {!groupCtx?.grouped && highlight === "item" && ( + + {isOpen && ( + + )} + + )} + {children} + + + ); + } +); + +AccordionItem.displayName = "AccordionItem"; + +// ─── AccordionTrigger ──────────────────────────────────────────────────────── + +interface AccordionTriggerProps + extends HTMLAttributes { + children: ReactNode; +} + +const AccordionTrigger = forwardRef( + ({ children, className, ...props }, ref) => { + const ChevronRight = useIcon("chevron-right"); + const groupCtx = useAccordionGroup(); + const { index, isOpen, triggerRef, highlight } = useAccordionItemContext(); + const shape = useShape(); + const sizeClasses = useSize(); + const [isHovered, setIsHovered] = useState(false); + + const isActive = groupCtx?.grouped + ? groupCtx.activeIndex === index + : isHovered; + + const triggerContent = ( + +
+ )} + > + {/* Label with dual-layer text */} + + + + {children} + + + + {/* Chevron — right when collapsed, rotates 90° down when expanded */} + + + + +
+
+ ); + + // In grouped mode, wrap in a div for fluid hover registration + if (groupCtx?.grouped) { + return
{triggerContent}
; + } + + // Standalone mode: local hover with animated BG + return ( +
setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + > + {/* Open tint, scoped to this row: the panel below keeps the page's + own surface, the way a sidebar row highlights without colouring + its sub-tree. */} + + {isOpen && highlight === "trigger" && isHovered && ( + + )} + + + {isHovered && ( + + )} + + {triggerContent} +
+ ); + } +); + +AccordionTrigger.displayName = "AccordionTrigger"; + +// ─── AccordionContent ──────────────────────────────────────────────────────── + +interface AccordionContentProps extends HTMLAttributes { + children: ReactNode; +} + +const AccordionContent = forwardRef( + ({ children, className, ...props }, ref) => { + const groupCtx = useAccordionGroup(); + const { isOpen } = useAccordionItemContext(); + const sizeClasses = useSize(); + // Read here rather than relying on a MotionConfig the consumer may not + // have: height is a positional value, so framer would otherwise animate + // it for a reduced-motion user in any app that installs this component. + const reduceMotion = useReducedMotion() ?? false; + + // The open height is animated to a self-measured LAYOUT pixel value, not + // `height: "auto"`: framer resolves an "auto" target by measuring the + // element's *visual* (transformed) size, so under a scaled ancestor + // (e.g. /demo's 1.7x card) the animation overshoots to scale× the real + // height and snaps back when the final "auto" lands — a visible height + // reduction at the end of every open. offsetHeight and ResizeObserver + // are transform-immune. + const innerRef = useRef(null); + const roRef = useRef(null); + const [contentHeight, setContentHeight] = useState(null); + // Items open at mount render `initial: "auto"` and receive their first + // pixel target a commit later; that hand-off must SNAP (duration 0), not + // spring — framer would measure the spring's numeric start visually + // (scaled) and play a shrink. Items that open later spring normally. + const needsSnap = useRef(isOpen); + // Height springs only when THIS panel toggles. When contentHeight + // changes underneath it instead — anything collapsible nested inside + // the panel, another accordion included — it must snap: a spring + // re-targeted every frame chases the child's own animation, lands + // after it, and drags everything below the item along late. Same rule + // as SidebarGroup / SidebarMenuSub; see motion-guidelines.md. + const prevOpenRef = useRef(isOpen); + const togglingRef = useRef(false); + if (prevOpenRef.current !== isOpen) { + prevOpenRef.current = isOpen; + togglingRef.current = true; + } + + const measureRef = useCallback((el: HTMLDivElement | null) => { + roRef.current?.disconnect(); + roRef.current = null; + innerRef.current = el; + if (!el) return; + if (el.offsetHeight > 0) setContentHeight(el.offsetHeight); + const ro = new ResizeObserver(() => { + // Ignore the 0 that fires while the panel is display:none. + if (el.offsetHeight > 0) setContentHeight(el.offsetHeight); + }); + ro.observe(el); + roRef.current = ro; + }, []); + + // Re-measure synchronously (pre-paint) when opening, so the spring's + // target is the fresh layout height from its first frame. + useIsoLayoutEffect(() => { + if (isOpen && innerRef.current && innerRef.current.offsetHeight > 0) { + setContentHeight(innerRef.current.offsetHeight); + } + }, [isOpen]); + + useEffect(() => { + if (contentHeight !== null) needsSnap.current = false; + }, [contentHeight]); + + // Content stays mounted so it can be measured; fully-closed panels are + // display:none (hidden) once the exit finishes, keeping them out of the + // accessibility tree without cutting the animation short. + const [exitComplete, setExitComplete] = useState(!isOpen); + if (isOpen && exitComplete) { + // Reset during render so the panel is un-hidden before the opening + // animation's first paint. + setExitComplete(false); + } + + return ( + + + + ); + } +); + +AccordionContent.displayName = "AccordionContent"; + +// ─── Exports ───────────────────────────────────────────────────────────────── + +export { + Accordion, + AccordionGroup, + AccordionItem, + AccordionTrigger, + AccordionContent, +}; +export default Accordion; diff --git a/src/components/ui/ask-user-questions.tsx b/src/components/ui/ask-user-questions.tsx new file mode 100644 index 0000000..773f1de --- /dev/null +++ b/src/components/ui/ask-user-questions.tsx @@ -0,0 +1,1886 @@ +"use client"; + +import { + forwardRef, + useCallback, + useEffect, + useId, + useMemo, + useRef, + useState, + type HTMLAttributes, + type KeyboardEvent as ReactKeyboardEvent, +} from "react"; +import { AnimatePresence, motion } from "framer-motion"; +import { RadioGroup as RadioGroupPrimitive } from "@base-ui/react/radio-group"; +import { Radio as RadioPrimitive } from "@base-ui/react/radio"; +import { CheckboxGroup as CheckboxGroupPrimitive } from "@base-ui/react/checkbox-group"; +import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox"; +import { Field } from "@base-ui/react/field"; +import { cn } from "@/lib/utils"; +import { spring } from "@/lib/springs"; +import { fontWeights } from "@/lib/font-weight"; +import { useShape } from "@/lib/shape-context"; +import { SizeProvider, useSize, type SizeVariant } from "@/lib/size-context"; +import { useIcon } from "@/lib/icon-context"; +import { useFluidHover, useRegisterFluidHoverItem } from "@/hooks/use-fluid-hover"; +import { useMergeSplitBlocks, SelectionBackgrounds } from "@/hooks/use-merge-split"; +import { Button } from "@/components/ui/button"; +import { FluidHoverHighlight } from "@/components/fluid-hover-highlight"; + +export interface AskUserOption { + id?: string; + title: string; + description?: string; +} + +export interface AskUserQuestion { + id?: string; + title: string; + /** Options to choose from. Optional only because a `freeText` question + * has none; every other mode requires at least one. */ + options?: AskUserOption[]; + multiSelect?: boolean; + allowOther?: boolean; + otherPlaceholder?: string; + skippable?: boolean; + nextLabel?: string; + /** Visual layout for each option row. + * - "inline" (default): title and description on one line. + * - "stacked": title above, description below — useful when descriptions + * are long enough to wrap. */ + layout?: "inline" | "stacked"; + /** Which side of the row the numbered chip sits on. + * - "right" (default): chip on the right; the single-select submit + * arrow overlays it on hover/focus. + * - "left": chip on the left, before the body. The submit arrow + * still appears on the right edge of the row, so the action + * affordance stays where the eye expects it. + * Works with every other option (single/multi-select, allowOther, + * inline/stacked layout). */ + chipPosition?: "left" | "right"; + /** Render a single multi-line textarea as the *only* answer, with no + * option rows — for open-ended prompts (a name, a description, free + * comments). Distinct from `allowOther`, which appends a free-text row + * *alongside* options. The field auto-focuses when the question appears; + * ⌘/⌃+Enter (or the bottom submit button) commits, and the answer is + * returned in `otherText`. `options` is ignored when this is set. */ + freeText?: boolean; + /** Placeholder for the `freeText` textarea. */ + freeTextPlaceholder?: string; + /** Whether the `freeText` field starts at multi-line height. Defaults to + * `true` — a taller field that invites a few sentences. Set `false` for a + * single-line field (one row tall) where a short answer is expected; + * plain Enter then submits instead of inserting a newline. Either way the + * textarea still grows to fit longer content as it wraps. */ + freeTextMultiline?: boolean; + /** Validate the `freeText` value when the user tries to submit (button or + * ⌘/⌃+Enter). Return an error message to block submission and surface it in + * the footer; return null/undefined to allow it. The error clears as soon + * as the user edits the field. */ + freeTextValidate?: (value: string) => string | null | undefined; +} + +export interface AskUserAnswer { + questionId: string; + selectedIds: string[]; + otherText?: string; + skipped?: boolean; +} + +export interface AskUserQuestionsProps + extends Omit, "onChange"> { + questions: AskUserQuestion[]; + currentIndex?: number; + defaultCurrentIndex?: number; + onCurrentIndexChange?: (index: number) => void; + answers?: Record; + defaultAnswers?: Record; + onAnswersChange?: (answers: Record) => void; + onComplete?: (answers: Record) => void; + onSkip?: (questionId: string, currentIndex: number) => void; + skipLabel?: string; + /** Pins the flow to one step of the size ladder (default 36px, compact + * 28px — see /docs/sizes). Omitted, it follows the surrounding + * SizeProvider. */ + size?: SizeVariant; +} + +function questionKey(q: AskUserQuestion, i: number) { + return q.id ?? `q-${i}`; +} + +function optionKey(o: AskUserOption, i: number) { + return o.id ?? `o-${i}`; +} + +// Mounted-instance registry for the document-level 1-9 shortcut. The listener +// has to be global (digits should work without focus in the card), but only ONE +// instance may answer a keypress: the one containing focus, or — when focus is +// outside every instance — the most recently mounted one. Without this, +// stacked instances (e.g. docs demos) would all answer the same digit. +const mountedInstances: HTMLElement[] = []; + +// True while a Row's mousedown handler is programmatically redirecting focus +// to the row (see Row's onMouseDown). Chrome reports script-initiated focus as +// :focus-visible, so without this flag every mouse click would light up the +// keyboard focus ring. Set/cleared synchronously around the focus() call — +// focus events dispatch synchronously, so a module-level flag is safe. +let pointerFocusRedirect = false; + +const AskUserQuestions = forwardRef( + function AskUserQuestions( + { + questions, + currentIndex: controlledIndex, + defaultCurrentIndex = 0, + onCurrentIndexChange, + answers: controlledAnswers, + defaultAnswers, + onAnswersChange, + onComplete, + onSkip, + skipLabel = "Skip", + size, + className, + ...rest + }, + ref + ) { + // Resolve locally with the prop override — this component both consumes + // the ladder (its own rows and chrome) and re-provides it to nested + // ladder-aware children (Button etc.) via the SizeProvider wrap below. + const sizeClasses = useSize(size); + const compact = sizeClasses.variant === "compact"; + + // ── Controlled / uncontrolled state ────────────────────────── + const [internalIndex, setInternalIndex] = useState(defaultCurrentIndex); + const isIndexControlled = controlledIndex !== undefined; + const index = isIndexControlled ? (controlledIndex as number) : internalIndex; + const setIndex = useCallback( + (next: number) => { + if (!isIndexControlled) setInternalIndex(next); + onCurrentIndexChange?.(next); + }, + [isIndexControlled, onCurrentIndexChange] + ); + + const [internalAnswers, setInternalAnswers] = useState< + Record + >(defaultAnswers ?? {}); + const isAnswersControlled = controlledAnswers !== undefined; + const answers = isAnswersControlled + ? (controlledAnswers as Record) + : internalAnswers; + + const answersRef = useRef(answers); + useEffect(() => { + answersRef.current = answers; + }, [answers]); + + const writeAnswers = useCallback( + ( + updater: ( + prev: Record + ) => Record + ) => { + const next = updater(answersRef.current); + answersRef.current = next; + if (!isAnswersControlled) setInternalAnswers(next); + onAnswersChange?.(next); + return next; + }, + [isAnswersControlled, onAnswersChange] + ); + + const shape = useShape(); + const ArrowLeft = useIcon("arrow-left"); + const ArrowRight = useIcon("arrow-right"); + + // The footer ← / → icons hint at the ArrowLeft/ArrowRight keys, which + // mobile has no equivalent for, so render them desktop-only. (The inline + // submit arrows on option rows stay — those are tap affordances, not + // keyboard hints.) + const ArrowLeftKey = useMemo( + () => + function ArrowLeftKey(p: { + size?: number; + strokeWidth?: number; + className?: string; + }) { + return ; + }, + [ArrowLeft] + ); + const ArrowRightKey = useMemo( + () => + function ArrowRightKey(p: { + size?: number; + strokeWidth?: number; + className?: string; + }) { + return ; + }, + [ArrowRight] + ); + + // Detect the platform so the Continue shortcut hint shows the right + // modifier: ⌘ on macOS, ⌃ (Control) elsewhere. Computed in a lazy + // initializer (guarded for SSR, where `navigator` doesn't exist) rather + // than an effect — an effect resolves a frame late, so ⌘/⌃+Enter would + // check ctrlKey on Macs for the first frames. The server can't know the + // platform, so the ⌘/⌃ glyph alone may differ on hydration; ShortcutChip + // carries suppressHydrationWarning to absorb that one-character delta. + const [isMac] = useState(() => { + if (typeof navigator === "undefined") return false; + const nav = navigator as Navigator & { + userAgentData?: { platform?: string }; + }; + const platform = nav.userAgentData?.platform || nav.platform || ""; + return /mac/i.test(platform); + }); + + const reactId = useId(); + const total = questions.length; + const safeIndex = Math.max(0, Math.min(index, Math.max(0, total - 1))); + const question = questions[safeIndex]; + const qId = question ? questionKey(question, safeIndex) : ""; + const currentAnswer = answers[qId]; + + const isMulti = !!question?.multiSelect; + const isSkippable = question?.skippable !== false; + const isFreeText = !!question?.freeText; + // Multi-line is the default; single-line is opt-out via freeTextMultiline. + const isFreeTextMultiline = question?.freeTextMultiline !== false; + // freeText owns the whole answer area, so the inline Other row is + // suppressed even if a caller sets both. + const allowOther = !isFreeText && !!question?.allowOther; + const selectedIds = useMemo( + () => currentAnswer?.selectedIds ?? [], + [currentAnswer] + ); + const otherText = currentAnswer?.otherText ?? ""; + + const options = question?.options ?? []; + const otherIndex = allowOther ? options.length : -1; + const rowCount = options.length + (allowOther ? 1 : 0); + + // ── Refs & fluid hover ─────────────────────────────────── + const rootRef = useRef(null); + const hasQuestion = !!question; + useEffect(() => { + if (!hasQuestion) return; + const el = rootRef.current; + if (!el) return; + mountedInstances.push(el); + return () => { + const i = mountedInstances.indexOf(el); + if (i !== -1) mountedInstances.splice(i, 1); + }; + }, [hasQuestion]); + const rowsContainerRef = useRef(null); + // The Other field is a multi-line textarea — it auto-resizes to fit + // wrapped content and lets users press Enter for a newline. + const otherInputRef = useRef(null); + // Stable IDs for contiguous-selection runs (see selectedGroups below). + const groupIdCounterRef = useRef(0); + const prevGroupMapRef = useRef(new Map()); + const hover = useFluidHover(rowsContainerRef); + const { + activeIndex, + setActiveIndex, + itemRects, + handlers, + registerItem, + } = hover; + + // ── Other-row textarea auto-resize ────────────────────────── + // The Other field is a textarea so users can write a multi-line answer. + // Browsers don't auto-fit textarea height to content, so we set it + // manually: reset to 0 (so the field can shrink when lines are deleted), + // then expand to scrollHeight. Remeasure the fluid hover rows after — the + // hover, selected and focus indicators absolutely-position against + // itemRects, so they need fresh rects when the row's height changes. + // + // We also track whether the textarea is currently displaying more than + // one line (either via explicit \n or text that wraps). Only then do + // we switch the Other row to `topAlign`; in the 1-line state the row + // stays `items-center` so a single line sits at the row's optical + // centre, matching the surrounding option rows. + const [isOtherMultiline, setIsOtherMultiline] = useState(false); + // Reset the multi-line flag when the question changes so the new + // question's first paint of an empty Other row doesn't inherit a + // stale `true` from the previous question's multi-line draft (which + // would briefly apply `items-start` + the -5px chip nudge on an + // empty single-line row before the resize effect below corrects it). + useEffect(() => { + setIsOtherMultiline(false); + }, [qId]); + useEffect(() => { + const el = otherInputRef.current; + if (!el) return; + el.style.height = "0px"; + el.style.height = `${el.scrollHeight}px`; + // Threshold against the textarea's *measured* line-height, not a + // hard-coded 22px — so the flag stays correct at high browser + // font-size / zoom settings where line-height grows past 22 even + // for a single line. 1.5× line-height is a generous fudge below + // a true second wrapped line (2× line-height) but well above any + // single-line rounding artefact. + const lineHeight = + parseFloat(window.getComputedStyle(el).lineHeight) || 18; + setIsOtherMultiline(el.scrollHeight > lineHeight * 1.5); + }, [otherText, qId]); + + // ── freeText auto-focus ───────────────────────────────────── + // A freeText question is a single open-ended field, so drop the caret + // straight into it when the question appears — the user can start typing + // without a click. Re-runs on qId so each freeText step in a flow gets + // focused as it slides in. + useEffect(() => { + if (!isFreeText) return; + // preventScroll so mounting the card (or advancing to the next freeText + // step) drops the caret in without yanking the viewport to the field. + otherInputRef.current?.focus({ preventScroll: true }); + }, [isFreeText, qId]); + + // ── Animated height ────────────────────────────────────────── + // Track the natural height of the Q/A content and animate the wrapper's + // REAL height to it. Animating the actual height (not a layout transform) + // means the card border and the footer below reflow frame-by-frame, so the + // height morph and the footer move together. A ResizeObserver keeps the + // target in sync across question swaps, shape changes, and text wrapping. + const contentMeasureRef = useRef(null); + const [contentHeight, setContentHeight] = useState("auto"); + useEffect(() => { + const el = contentMeasureRef.current; + if (!el) return; + const update = () => setContentHeight(el.offsetHeight); + update(); + const ro = new ResizeObserver(update); + ro.observe(el); + return () => ro.disconnect(); + }, []); + + const [focusedIndex, setFocusedIndex] = useState(null); + // Ref mirror so callbacks (markFocusRestore) can read the latest value + // without re-subscribing on every focus move. + const focusedIndexRef = useRef(null); + useEffect(() => { + focusedIndexRef.current = focusedIndex; + }, [focusedIndex]); + // Validation message for the current freeText question (null = valid). + const [freeTextError, setFreeTextError] = useState(null); + + // Reset transient state when question changes + useEffect(() => { + setActiveIndex(null); + setFocusedIndex(null); + setFreeTextError(null); + }, [safeIndex, setActiveIndex]); + + // ── Keyboard focus restoration across question changes ─────── + // The question content remounts on qId, which destroys the focused row and + // drops focus to . If we navigated *from within* the rows (i.e. the + // user was driving with the keyboard), refocus the new question's first row + // so focus-within is kept and arrows keep routing here instead of falling + // through to page-level navigation. + const restoreFocusRef = useRef(false); + const markFocusRestore = useCallback(() => { + // Only restore when the user was keyboard-driving. A mouse click also + // parks focus on the clicked row (Row's onMouseDown redirect), and the + // browser reports that script focus as :focus-visible — so we can't ask + // the DOM. focusedIndexRef is the component's own modality signal: it's + // non-null only when the morphing ring is showing, i.e. focus genuinely + // came from the keyboard. Restoring after a click would keyboard-focus + // the next question's first row and leave the ring stuck on screen. + if ( + rowsContainerRef.current?.contains(document.activeElement) && + focusedIndexRef.current !== null + ) { + restoreFocusRef.current = true; + } + }, []); + useEffect(() => { + if (!restoreFocusRef.current) return; + restoreFocusRef.current = false; + const firstRow = rowsContainerRef.current?.querySelector( + '[data-fluid-hover-index="0"]' + ) as HTMLElement | null; + firstRow?.focus(); + }, [safeIndex]); + + // ── Answer actions ─────────────────────────────────────────── + const goNext = useCallback( + (snapshot: Record) => { + if (safeIndex >= total - 1) { + onComplete?.(snapshot); + } else { + markFocusRestore(); + setIndex(safeIndex + 1); + } + }, + [safeIndex, total, onComplete, setIndex, markFocusRestore] + ); + + const handleSingleSelect = useCallback( + (optId: string) => { + if (!question) return; + // Read through answersRef — the same source writeAnswers mutates — so + // a write earlier in the same tick is never missed the way a stale + // render-scope `answers` capture could be. + const text = answersRef.current[qId]?.otherText; + const snapshot = writeAnswers((prev) => ({ + ...prev, + [qId]: { + questionId: qId, + selectedIds: [optId], + otherText: text || undefined, + skipped: false, + }, + })); + goNext(snapshot); + }, + [question, qId, writeAnswers, goNext] + ); + + const handleMultiToggle = useCallback( + (optId: string) => { + if (!question) return; + writeAnswers((prev) => { + const existing = prev[qId]; + const set = new Set(existing?.selectedIds ?? []); + if (set.has(optId)) set.delete(optId); + else set.add(optId); + return { + ...prev, + [qId]: { + questionId: qId, + selectedIds: Array.from(set), + otherText: existing?.otherText, + skipped: false, + }, + }; + }); + }, + [question, qId, writeAnswers] + ); + + // Base UI Checkbox.Group reports value changes coming from its (hidden) + // per-row checkbox primitives. Row clicks go through handleMultiToggle + // directly, so in practice this only fires if a hidden control is toggled + // by other means — mirror it into the answer so the two never disagree. + const handleGroupValueChange = useCallback( + (vals: string[]) => { + if (!question) return; + writeAnswers((prev) => ({ + ...prev, + [qId]: { + questionId: qId, + selectedIds: vals, + otherText: prev[qId]?.otherText, + skipped: false, + }, + })); + }, + [question, qId, writeAnswers] + ); + + const handleOtherChange = useCallback( + (text: string) => { + if (!question) return; + // Editing clears a standing validation error — the user is fixing it. + setFreeTextError(null); + writeAnswers((prev) => ({ + ...prev, + [qId]: { + questionId: qId, + selectedIds: prev[qId]?.selectedIds ?? [], + otherText: text, + skipped: false, + }, + })); + }, + [question, qId, writeAnswers] + ); + + const handleOtherSubmit = useCallback(() => { + if (!question) return; + // answersRef (not render-scope `answers`) keeps this read consistent + // with writeAnswers below — see handleSingleSelect. + const text = (answersRef.current[qId]?.otherText ?? "").trim(); + if (!text) return; + // freeText questions may validate on submit; a returned message blocks + // navigation and surfaces in the footer. + if (question.freeText && question.freeTextValidate) { + const message = question.freeTextValidate(text); + if (message) { + setFreeTextError(message); + return; + } + } + setFreeTextError(null); + const snapshot = writeAnswers((prev) => ({ + ...prev, + [qId]: { + questionId: qId, + selectedIds: prev[qId]?.selectedIds ?? [], + otherText: text, + skipped: false, + }, + })); + goNext(snapshot); + }, [question, qId, writeAnswers, goNext]); + + const handleSkip = useCallback(() => { + if (!question) return; + const snapshot = writeAnswers((prev) => ({ + ...prev, + [qId]: { + questionId: qId, + selectedIds: prev[qId]?.selectedIds ?? [], + otherText: prev[qId]?.otherText, + skipped: true, + }, + })); + onSkip?.(qId, safeIndex); + goNext(snapshot); + }, [question, qId, writeAnswers, onSkip, safeIndex, goNext]); + + const handleMultiNext = useCallback(() => { + goNext(answers); + }, [goNext, answers]); + + const handleBack = useCallback(() => { + if (safeIndex > 0) { + markFocusRestore(); + setIndex(safeIndex - 1); + } + }, [safeIndex, setIndex, markFocusRestore]); + + // ── Keyboard shortcuts: 1-9 ────────────────────────────────── + useEffect(() => { + if (!question) return; + const handler = (e: KeyboardEvent) => { + if (e.metaKey || e.ctrlKey || e.altKey) return; + const target = e.target as HTMLElement | null; + if (!target) return; + const tag = target.tagName; + if (tag === "INPUT" || tag === "TEXTAREA" || target.isContentEditable) return; + // Only one mounted instance may answer this keypress (see + // mountedInstances): the one containing focus, or the most recently + // mounted one when focus sits outside every instance. + const root = rootRef.current; + if (!root) return; + if (!root.contains(target)) { + if (mountedInstances.some((el) => el !== root && el.contains(target))) + return; + // A focused element that WRAPS instances (a docs preview frame + // holding keyboard scope — see click-to-focus) narrows the pick to + // the instances inside it; focus on wraps them all, which + // degenerates to the original most-recently-mounted rule. + const wrapped = mountedInstances.filter((el) => target.contains(el)); + const pool = wrapped.length > 0 ? wrapped : mountedInstances; + if (pool[pool.length - 1] !== root) return; + } + const code = e.key; + if (code < "1" || code > "9") return; + const idx = parseInt(code, 10) - 1; + if (idx >= 0 && idx < options.length) { + e.preventDefault(); + const oid = optionKey(options[idx], idx); + if (isMulti) handleMultiToggle(oid); + else handleSingleSelect(oid); + } else if (idx === options.length && allowOther) { + e.preventDefault(); + otherInputRef.current?.focus(); + } + }; + document.addEventListener("keydown", handler); + return () => document.removeEventListener("keydown", handler); + }, [ + question, + options, + isMulti, + allowOther, + handleSingleSelect, + handleMultiToggle, + ]); + + // ── Keyboard navigation ────────────────────────────────────── + // Up/Down move the highlight between rows using the SAME indicator as + // mouse hover (activeIndex → bg-hover), so keyboard and pointer focus look + // identical. Left = Back, Right = Skip. We stopPropagation on the arrows we + // handle so the doc page's ←/→ page-change nav (a window listener) doesn't + // also fire — important for multi-select, whose container is role="group" + // (not "radiogroup") and so isn't auto-skipped by that handler. + const focusRow = (idx: number) => { + const el = rowsContainerRef.current?.querySelector( + `[data-fluid-hover-index="${idx}"]` + ) as HTMLElement | null; + el?.focus(); + }; + + const moveActive = useCallback( + (next: number) => { + setActiveIndex(next); + // The Other row is a text field — focus the input directly so typing + // works; everything else focuses the row for Enter/Space selection. + if (allowOther && next === otherIndex) otherInputRef.current?.focus(); + else focusRow(next); + }, + [allowOther, otherIndex, setActiveIndex] + ); + + const handleNavKey = (e: ReactKeyboardEvent) => { + const target = e.target as HTMLElement; + const isTextInput = + target.tagName === "INPUT" || + target.tagName === "TEXTAREA" || + target.isContentEditable; + + // Inside the Other text field, ←/→ and Home/End move the caret natively. + // ↑/↓ are dual-purpose in the textarea: when the caret has more lines + // to move to in that direction (there's a \n before/after it), let the + // browser handle native caret movement; only steal the keystroke to + // navigate to an adjacent option row when the caret is already at the + // first / last line — otherwise the user can't edit a multi-line draft + // without focus jumping out of the field. + if (isTextInput && e.key !== "ArrowDown" && e.key !== "ArrowUp") return; + if ( + isTextInput && + (e.key === "ArrowUp" || e.key === "ArrowDown") && + target.tagName === "TEXTAREA" + ) { + // Position-bounds check — works for BOTH explicit `\n` AND visual + // line wraps. Only steal the key when the caret has nowhere left + // to go inside the textarea: ArrowUp at the very start, or + // ArrowDown at the very end. Anywhere else, let the textarea + // handle native caret movement (line-by-line up/down, including + // through wrapped lines without `\n`). + const ta = target as HTMLTextAreaElement; + if (e.key === "ArrowUp" && ta.selectionStart > 0) return; + if (e.key === "ArrowDown" && ta.selectionEnd < ta.value.length) return; + } + + // Our keydown handler and Base UI's RadioGroup composite are merged onto + // the SAME rows container (via the primitive's render prop), and the + // composite's roving focus targets the hidden sr-only radios. Base UI + // runs our (external) handler first and skips its own when we call + // preventBaseUIHandler — without it, every arrow press would land focus + // on an invisible control right after we move it to the next row. + const preventBaseUI = ( + e as unknown as { preventBaseUIHandler?: () => void } + ).preventBaseUIHandler; + + if (e.key === "ArrowLeft" || e.key === "ArrowRight") { + e.preventDefault(); + e.stopPropagation(); + preventBaseUI?.(); + if (e.key === "ArrowLeft") { + if (safeIndex > 0) handleBack(); + } else if (isSkippable && total > 1) { + handleSkip(); + } + return; + } + + if (rowCount === 0) return; + if ( + e.key === "ArrowDown" || + e.key === "ArrowUp" || + e.key === "Home" || + e.key === "End" + ) { + e.preventDefault(); + e.stopPropagation(); + preventBaseUI?.(); + let next: number; + if (e.key === "Home") next = 0; + else if (e.key === "End") next = rowCount - 1; + else { + // When focus is in the Other field, treat it as the Other row. + const base = isTextInput ? otherIndex : activeIndex ?? -1; + next = e.key === "ArrowDown" ? base + 1 : base - 1; + next = (next + rowCount) % rowCount; + } + moveActive(next); + } + }; + + // Cmd+Enter (macOS) / Ctrl+Enter (Windows/Linux) commits a multi-select + // question, mirroring the Continue button. Handled at the root so it works + // wherever focus sits inside the card, and scoped to this instance because + // the event has to bubble up from a focused descendant (no global listener, + // so stacked demos don't all fire at once). + const handleRootKey = (e: ReactKeyboardEvent) => { + if (e.key !== "Enter") return; + const mod = isMac ? e.metaKey : e.ctrlKey; + // ⌘/⌃+Enter commits multi-select and freeText alike — both use the + // bottom submit button rather than an inline arrow. + if (!mod || !(isMulti || isFreeText)) return; + e.preventDefault(); // keep a focused button/row from also activating + if (isFreeText) { + if (otherText.trim().length > 0) handleOtherSubmit(); + return; + } + const hasAnswer = selectedIds.length > 0 || otherText.trim().length > 0; + if (hasAnswer) handleMultiNext(); + }; + + if (!question) { + return ( +
+

No questions.

+
+ ); + } + + // ── Layout calculations for hover/focus indicators ─────────── + // focusedIndex comes from the rows container's onFocus (see rowsContent), + // set only when the focused row matches :focus-visible — so the blue + // morphing ring tracks keyboard focus across option rows. It is + // intentionally suppressed for the Other field: that row has its own + // input-field treatment (the "type here" hint when empty, the merged + // selected bg once it has text), so the ring is redundant there and reads + // as noise while typing. focusedIndex is still tracked for the hint and + // submit-arrow visibility — we just don't draw a ring around it. + const focusRect = + focusedIndex !== null && !(allowOther && focusedIndex === otherIndex) + ? itemRects[focusedIndex] + : null; + + // ── Selected-row grouping (merges contiguous selections) ───── + // Mirrors the CheckboxGroup pattern: contiguous selected indices + // collapse into a single rounded background block; stable IDs let + // framer-motion morph block size/position when neighbours toggle. + // The Other row gets its own input-field-style indicator (see below) and + // is intentionally excluded here so it doesn't merge into a contiguous + // bg-accent block with adjacent selected options. + // Include the Other row in selectedIndices when it has text. This lets + // it merge into the same morphing bg block as adjacent selected options + // (instead of looking like a disconnected input field next to them). + const selectedIndices = useMemo(() => { + const set = new Set(); + options.forEach((opt, i) => { + if (selectedIds.includes(optionKey(opt, i))) set.add(i); + }); + if (allowOther && otherText.length > 0) set.add(otherIndex); + return set; + }, [options, selectedIds, allowOther, otherText, otherIndex]); + + const selectedGroups = useMemo(() => { + const runs: { start: number; end: number }[] = []; + const sorted = [...selectedIndices].sort((a, b) => a - b); + for (const idx of sorted) { + const last = runs[runs.length - 1]; + if (last && idx === last.end + 1) last.end = idx; + else runs.push({ start: idx, end: idx }); + } + + // Stable run IDs so a growing/shrinking run animates instead of + // exit+re-enter when neighbours flip. + const usedIds = new Set(); + const nextGroupMap = new Map(); + const groups = runs.map((run) => { + let stableId: number | null = null; + for (let i = run.start; i <= run.end; i++) { + const prev = prevGroupMapRef.current.get(i); + if (prev !== undefined && !usedIds.has(prev)) { + stableId = prev; + break; + } + } + const id = stableId ?? ++groupIdCounterRef.current; + usedIds.add(id); + for (let i = run.start; i <= run.end; i++) nextGroupMap.set(i, id); + return { ...run, id }; + }); + prevGroupMapRef.current = nextGroupMap; + return groups; + }, [selectedIndices]); + + // Selected backgrounds, with the merge/split boundary animation when one + // unselected row bridges or splits two selected runs. Selected backgrounds + // use shape.bg, so corners animate around its radius. + const blocks = useMergeSplitBlocks(selectedGroups, itemRects, shape.bgRadius); + + const showBack = total > 1 && safeIndex > 0; + const showSkip = total > 1 && isSkippable; + // freeText commits through the same bottom submit button as multi-select. + const showSubmit = isMulti || isFreeText; + const showFooter = showBack || showSkip || showSubmit; + + // ── Roving tabindex ────────────────────────────────────────── + // One tab stop for the whole group, single- AND multi-select alike: the + // first selected row, or — when the question is unanswered — the first + // row, so the group stays keyboard-reachable (the hasSelection-style + // fallback from registry/base). Arrows handle row-to-row movement; Tab + // moves on past the group. The Other row never takes the stop — its + // textarea is natively focusable on its own. + const firstSelectedRow = options.findIndex((opt, i) => + selectedIds.includes(optionKey(opt, i)) + ); + + // ── Option rows ────────────────────────────────────────────── + // The rows container is handed to a Base UI group primitive via `render` + // (Radio.Group for single-select, Checkbox.Group for multi-select — see + // the JSX below), so group semantics and selection plumbing come from + // Base UI while the rows keep their custom fluid-hover treatment. + // Each option Row hosts a hidden sr-only Radio/Checkbox primitive; the + // visible wrapper carries role/aria-checked. CAUTION: every keyboard-nav + // query in here is scoped to [data-fluid-hover-index] — a bare + // [role="radio"] / [role="checkbox"] selector would ALSO match the hidden + // primitive inside each row (two hits per row) and land arrow-key focus + // on invisible controls. + const rowsContent = ( +
{ + // Track focus at the container (React's onFocus is focusin, so row + // and Other-textarea focus both bubble here). activeIndex mirrors + // the hover highlight onto the focused row; focusedIndex feeds the + // morphing blue ring, gated to keyboard focus via :focus-visible — + // mouse clicks focus rows without drawing the ring. + const indexAttr = (e.target as HTMLElement) + .closest("[data-fluid-hover-index]") + ?.getAttribute("data-fluid-hover-index"); + if (indexAttr != null) { + const idx = Number(indexAttr); + setActiveIndex(idx); + setFocusedIndex( + !pointerFocusRedirect && + (e.target as HTMLElement).matches(":focus-visible") + ? idx + : null + ); + } + }} + onBlur={(e) => { + // Only clear when focus leaves the whole group — row-to-row moves + // keep the indicators mounted so they morph instead of exiting and + // re-entering. + if (rowsContainerRef.current?.contains(e.relatedTarget as Node)) + return; + setFocusedIndex(null); + setActiveIndex(null); + }} + onKeyDown={handleNavKey} + className="relative flex flex-col -mx-3" + > + {/* Other-row input hint — shown only when the Other input is + focused and still empty, to signal "type here". As soon as + text exists, the row joins selectedIndices and inherits the + selected merged bg, so it visually integrates with adjacent + selected options instead of looking like a standalone field. */} + + {(() => { + if (!allowOther) return null; + const otherRect = itemRects[otherIndex]; + const isEmptyFocused = + focusedIndex === otherIndex && otherText.length === 0; + if (!otherRect || !isEmptyFocused) return null; + return ( + + ); + })()} + + + {/* Single morphing hover indicator (rendered below selected bg + so a hovered+selected row still reads as clearly selected) */} + + + {/* Selected-row backgrounds (merged for contiguous selections). + A run is normally one block; mid merge/split it is drawn as two + abutting halves — see useMergeSplitBlocks. Uses bg-active + (overlay-aware) and renders ABOVE the hover indicator so the + selected state stays readable when mousing over a row. Corners + are driven numerically (around shape.bg's radius) so a single + selected row matches its hover. */} + + + {/* Single morphing focus ring — fed by the container onFocus above, + so keyboard focus on any option row draws it. */} + + {focusRect && ( + + )} + + + {options.map((opt, i) => { + const oid = optionKey(opt, i); + const isSelected = selectedIds.includes(oid); + const isHover = activeIndex === i; + const showArrow = !isMulti && isHover; + return ( + + isMulti ? handleMultiToggle(oid) : handleSingleSelect(oid) + } + onKeyDown={(e) => { + // Let ⌘/Ctrl+Enter fall through to the root handler + // (Continue) instead of toggling the focused row. + if ( + (e.key === " " || e.key === "Enter") && + !e.metaKey && + !e.ctrlKey + ) { + e.preventDefault(); + if (isMulti) handleMultiToggle(oid); + else handleSingleSelect(oid); + } + }} + shape={shape} + aria-checked={isSelected} + chipContent={i + 1} + chipFilled={isSelected} + isMulti={isMulti} + showArrow={showArrow} + bodyLayout={question.layout === "stacked" ? "stacked" : "inline"} + // Anchor the chip to the first text line whenever the + // body can wrap to multiple lines (stacked layouts + // pair a title with a description that often wraps). + topAlign={question.layout === "stacked"} + chipPosition={question.chipPosition ?? "right"} + arrowIcon={ + + } + // Hidden Base UI primitive (sr-only): carries the group's + // selection plumbing while the visible row wrapper handles + // all interaction and styling. aria-hidden + tabIndex -1 keep + // it out of the a11y tree and the tab order — the row itself + // is the radio/checkbox as far as AT is concerned. + hiddenControl={ + isMulti ? ( + + ) : ( + + ) + } + > + {question.layout === "stacked" ? ( + <> + + + + {opt.title} + + + {opt.description && ( + + {opt.description} + + )} + + ) : ( + + + + + {opt.title} + + + {opt.description && ( + <> + {" "} + + {opt.description} + + + )} + + )} + + ); + })} + + {allowOther && ( + 0} + tabIndex={-1} + onClick={() => otherInputRef.current?.focus()} + shape={shape} + chipContent={otherIndex + 1} + chipFilled={otherText.length > 0} + isMulti={isMulti} + // Other body is a textarea that may grow past one line; + // only switch to top-aligned when it actually wraps, so + // the 1-line empty / single-line state stays visually + // centred like the surrounding option rows. + topAlign={isOtherMultiline} + chipPosition={question.chipPosition ?? "right"} + ariaLabel={ + question.otherPlaceholder ?? "Describe in your own words" + } + showArrow={ + !isMulti && + (focusedIndex === otherIndex || + activeIndex === otherIndex) && + otherText.trim().length > 0 + } + arrowIcon={ + + } + onArrowClick={ + !isMulti && otherText.trim().length > 0 + ? handleOtherSubmit + : undefined + } + > + + ",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===k.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.fn[t]=function(e){return _(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return _(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){k.fn[n]=function(e,t){return 0=o.clientWidth&&n>=o.clientHeight}),l=0a[e]&&!t.escapeWithReference&&(n=Q(f[o],a[e]-('right'===e?f.width:f.height))),ae({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=le({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!K(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-us[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,w=t(e.instance.popper),y=parseFloat(w['margin'+f]),E=parseFloat(w['border'+f+'Width']),v=b-e.offsets.popper[m]-y-E;return v=ee(Q(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,$(v)),ae(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case ce.FLIP:p=[n,i];break;case ce.CLOCKWISE:p=G(n);break;case ce.COUNTERCLOCKWISE:p=G(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)f(l.top)||'bottom'===n&&f(a.top)f(o.right),g=f(a.top)f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,w=-1!==['top','bottom'].indexOf(n),y=!!t.flipVariations&&(w&&'start'===r&&h||w&&'end'===r&&c||!w&&'start'===r&&g||!w&&'end'===r&&u),E=!!t.flipVariationsByContent&&(w&&'start'===r&&c||w&&'end'===r&&h||!w&&'start'===r&&u||!w&&'end'===r&&g),v=y||E;(m||b||v)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),v&&(r=z(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=le({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport',flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!K(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottomo.right||t.top>o.bottom||t.rightwindow.devicePixelRatio||!fe),c='bottom'===o?'top':'bottom',g='right'===n?'left':'right',b=B('transform');if(d='bottom'==c?'HTML'===l.nodeName?-l.clientHeight+h.bottom:-f.height+h.bottom:h.top,s='right'==g?'HTML'===l.nodeName?-l.clientWidth+h.right:-f.width+h.right:h.left,a&&b)m[b]='translate3d('+s+'px, '+d+'px, 0)',m[c]=0,m[g]=0,m.willChange='transform';else{var w='bottom'==c?-1:1,y='right'==g?-1:1;m[c]=d*w,m[g]=s*y,m.willChange=c+', '+g}var E={"x-placement":e.placement};return e.attributes=le({},E,e.attributes),e.styles=le({},m,e.styles),e.arrowStyles=le({},e.offsets.arrow,e.arrowStyles),e},gpuAcceleration:!0,x:'bottom',y:'right'},applyStyle:{order:900,enabled:!0,fn:function(e){return V(e.instance.popper,e.styles),j(e.instance.popper,e.attributes),e.arrowElement&&Object.keys(e.arrowStyles).length&&V(e.arrowElement,e.arrowStyles),e},onLoad:function(e,t,o,n,i){var r=L(i,t,e,o.positionFixed),p=O(o.placement,r,t,e,o.modifiers.flip.boundariesElement,o.modifiers.flip.padding);return t.setAttribute('x-placement',p),V(t,{position:o.positionFixed?'fixed':'absolute'}),o},gpuAcceleration:void 0}}},ge}); -//# sourceMappingURL=popper.min.js.map - -/*! - * Bootstrap v4.4.1 (https://getbootstrap.com/) - * Copyright 2011-2019 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e((t=t||self).bootstrap={},t.jQuery,t.Popper)}(this,function(t,g,u){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)g(this._element).one(Y.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:Se,popperConfig:null},Fe="show",Ue="out",We={HIDE:"hide"+Oe,HIDDEN:"hidden"+Oe,SHOW:"show"+Oe,SHOWN:"shown"+Oe,INSERTED:"inserted"+Oe,CLICK:"click"+Oe,FOCUSIN:"focusin"+Oe,FOCUSOUT:"focusout"+Oe,MOUSEENTER:"mouseenter"+Oe,MOUSELEAVE:"mouseleave"+Oe},qe="fade",Me="show",Ke=".tooltip-inner",Qe=".arrow",Be="hover",Ve="focus",Ye="click",ze="manual",Xe=function(){function i(t,e){if("undefined"==typeof u)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=g(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(g(this.getTipElement()).hasClass(Me))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),g.removeData(this.element,this.constructor.DATA_KEY),g(this.element).off(this.constructor.EVENT_KEY),g(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&g(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===g(this.element).css("display"))throw new Error("Please use show on visible elements");var t=g.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){g(this.element).trigger(t);var n=_.findShadowRoot(this.element),i=g.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!i)return;var o=this.getTipElement(),r=_.getUID(this.constructor.NAME);o.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&g(o).addClass(qe);var s="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,a=this._getAttachment(s);this.addAttachmentClass(a);var l=this._getContainer();g(o).data(this.constructor.DATA_KEY,this),g.contains(this.element.ownerDocument.documentElement,this.tip)||g(o).appendTo(l),g(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new u(this.element,o,this._getPopperConfig(a)),g(o).addClass(Me),"ontouchstart"in document.documentElement&&g(document.body).children().on("mouseover",null,g.noop);var c=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,g(e.element).trigger(e.constructor.Event.SHOWN),t===Ue&&e._leave(null,e)};if(g(this.tip).hasClass(qe)){var h=_.getTransitionDurationFromElement(this.tip);g(this.tip).one(_.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},t.hide=function(t){function e(){n._hoverState!==Fe&&i.parentNode&&i.parentNode.removeChild(i),n._cleanTipClass(),n.element.removeAttribute("aria-describedby"),g(n.element).trigger(n.constructor.Event.HIDDEN),null!==n._popper&&n._popper.destroy(),t&&t()}var n=this,i=this.getTipElement(),o=g.Event(this.constructor.Event.HIDE);if(g(this.element).trigger(o),!o.isDefaultPrevented()){if(g(i).removeClass(Me),"ontouchstart"in document.documentElement&&g(document.body).children().off("mouseover",null,g.noop),this._activeTrigger[Ye]=!1,this._activeTrigger[Ve]=!1,this._activeTrigger[Be]=!1,g(this.tip).hasClass(qe)){var r=_.getTransitionDurationFromElement(i);g(i).one(_.TRANSITION_END,e).emulateTransitionEnd(r)}else e();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){g(this.getTipElement()).addClass(Pe+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(g(t.querySelectorAll(Ke)),this.getTitle()),g(t).removeClass(qe+" "+Me)},t.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=we(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?g(e).parent().is(t)||t.empty().append(e):t.text(g(e).text())},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t=t||("function"==typeof this.config.title?this.config.title.call(this.element):this.config.title)},t._getPopperConfig=function(t){var e=this;return l({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:Qe},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},{},this.config.popperConfig)},t._getOffset=function(){var e=this,t={};return"function"==typeof this.config.offset?t.fn=function(t){return t.offsets=l({},t.offsets,{},e.config.offset(t.offsets,e.element)||{}),t}:t.offset=this.config.offset,t},t._getContainer=function(){return!1===this.config.container?document.body:_.isElement(this.config.container)?g(this.config.container):g(document).find(this.config.container)},t._getAttachment=function(t){return Re[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)g(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==ze){var e=t===Be?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===Be?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;g(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}}),this._hideModalHandler=function(){i.element&&i.hide()},g(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");!this.element.getAttribute("title")&&"string"==t||(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Ve:Be]=!0),g(e.getTipElement()).hasClass(Me)||e._hoverState===Fe?e._hoverState=Fe:(clearTimeout(e._timeout),e._hoverState=Fe,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===Fe&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Ve:Be]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=Ue,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===Ue&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){var e=g(this.element).data();return Object.keys(e).forEach(function(t){-1!==je.indexOf(t)&&delete e[t]}),"number"==typeof(t=l({},this.constructor.Default,{},e,{},"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),_.typeCheckConfig(Ae,t,this.constructor.DefaultType),t.sanitize&&(t.template=we(t.template,t.whiteList,t.sanitizeFn)),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(Le);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(g(t).removeClass(qe),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=g(this).data(Ne),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),g(this).data(Ne,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.4.1"}},{key:"Default",get:function(){return xe}},{key:"NAME",get:function(){return Ae}},{key:"DATA_KEY",get:function(){return Ne}},{key:"Event",get:function(){return We}},{key:"EVENT_KEY",get:function(){return Oe}},{key:"DefaultType",get:function(){return He}}]),i}();g.fn[Ae]=Xe._jQueryInterface,g.fn[Ae].Constructor=Xe,g.fn[Ae].noConflict=function(){return g.fn[Ae]=ke,Xe._jQueryInterface};var $e="popover",Ge="bs.popover",Je="."+Ge,Ze=g.fn[$e],tn="bs-popover",en=new RegExp("(^|\\s)"+tn+"\\S+","g"),nn=l({},Xe.Default,{placement:"right",trigger:"click",content:"",template:''}),on=l({},Xe.DefaultType,{content:"(string|element|function)"}),rn="fade",sn="show",an=".popover-header",ln=".popover-body",cn={HIDE:"hide"+Je,HIDDEN:"hidden"+Je,SHOW:"show"+Je,SHOWN:"shown"+Je,INSERTED:"inserted"+Je,CLICK:"click"+Je,FOCUSIN:"focusin"+Je,FOCUSOUT:"focusout"+Je,MOUSEENTER:"mouseenter"+Je,MOUSELEAVE:"mouseleave"+Je},hn=function(t){function i(){return t.apply(this,arguments)||this}!function(t,e){t.prototype=Object.create(e.prototype),(t.prototype.constructor=t).__proto__=e}(i,t);var e=i.prototype;return e.isWithContent=function(){return this.getTitle()||this._getContent()},e.addAttachmentClass=function(t){g(this.getTipElement()).addClass(tn+"-"+t)},e.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},e.setContent=function(){var t=g(this.getTipElement());this.setElementContent(t.find(an),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(ln),e),t.removeClass(rn+" "+sn)},e._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},e._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(en);null!==e&&0=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||tn[h]&&(a.reach[i]="start"),n[h]>a[f]-a[g]-1&&(a.reach[i]="end"),b&&(n.dispatchEvent(k("ps-scroll-"+i)),0>b?n.dispatchEvent(k("ps-scroll-"+l)):0=a.railXWidth-a.scrollbarXWidth&&(a.scrollbarXLeft=a.railXWidth-a.scrollbarXWidth),a.scrollbarYTop>=a.railYHeight-a.scrollbarYHeight&&(a.scrollbarYTop=a.railYHeight-a.scrollbarYHeight),s(c,a),a.scrollbarXActive?c.classList.add(z.state.active("x")):(c.classList.remove(z.state.active("x")),a.scrollbarXWidth=0,a.scrollbarXLeft=0,c.scrollLeft=!0===a.isRtl?a.contentWidth:0),a.scrollbarYActive?c.classList.add(z.state.active("y")):(c.classList.remove(z.state.active("y")),a.scrollbarYHeight=0,a.scrollbarYTop=0,c.scrollTop=0)}function r(a,b){var c=Math.min,d=Math.max;return a.settings.minScrollbarLength&&(b=d(b,a.settings.minScrollbarLength)),a.settings.maxScrollbarLength&&(b=c(b,a.settings.maxScrollbarLength)),b}function s(a,c){var d={width:c.railXWidth},e=v(a.scrollTop);d.left=c.isRtl?c.negativeScrollAdjustment+a.scrollLeft+c.containerWidth-c.contentWidth:a.scrollLeft,c.isScrollbarXUsingBottom?d.bottom=c.scrollbarXBottom-e:d.top=c.scrollbarXTop+e,b(c.scrollbarXRail,d);var f={top:e,height:c.railYHeight};c.isScrollbarYUsingRight?c.isRtl?f.right=c.contentWidth-(c.negativeScrollAdjustment+a.scrollLeft)-c.scrollbarYRight-c.scrollbarYOuterWidth-9:f.right=c.scrollbarYRight-a.scrollLeft:c.isRtl?f.left=c.negativeScrollAdjustment+a.scrollLeft+2*c.containerWidth-c.contentWidth-c.scrollbarYLeft-c.scrollbarYOuterWidth:f.left=c.scrollbarYLeft+a.scrollLeft,b(c.scrollbarYRail,f),b(c.scrollbarX,{left:c.scrollbarXLeft,width:c.scrollbarXWidth-c.railBorderXWidth}),b(c.scrollbarY,{top:c.scrollbarYTop,height:c.scrollbarYHeight-c.railBorderYWidth})}function t(a,b){function c(b){b.touches&&b.touches[0]&&(b[k]=b.touches[0].pageY),s[o]=t+v*(b[k]-u),g(a,p),q(a),b.stopPropagation(),b.preventDefault()}function d(){h(a,p),a[r].classList.remove(z.state.clicking),a.event.unbind(a.ownerDocument,"mousemove",c)}function f(b,e){t=s[o],e&&b.touches&&(b[k]=b.touches[0].pageY),u=b[k],v=(a[j]-a[i])/(a[l]-a[n]),e?a.event.bind(a.ownerDocument,"touchmove",c):(a.event.bind(a.ownerDocument,"mousemove",c),a.event.once(a.ownerDocument,"mouseup",d),b.preventDefault()),a[r].classList.add(z.state.clicking),b.stopPropagation()}var i=b[0],j=b[1],k=b[2],l=b[3],m=b[4],n=b[5],o=b[6],p=b[7],r=b[8],s=a.element,t=null,u=null,v=null;a.event.bind(a[m],"mousedown",function(a){f(a)}),a.event.bind(a[m],"touchstart",function(a){f(a,!0)})}var w="undefined"!=typeof Element&&(Element.prototype.matches||Element.prototype.webkitMatchesSelector||Element.prototype.mozMatchesSelector||Element.prototype.msMatchesSelector),z={main:"ps",rtl:"ps__rtl",element:{thumb:function(a){return"ps__thumb-"+a},rail:function(a){return"ps__rail-"+a},consuming:"ps__child--consume"},state:{focus:"ps--focus",clicking:"ps--clicking",active:function(a){return"ps--active-"+a},scrolling:function(a){return"ps--scrolling-"+a}}},A={x:null,y:null},B=function(a){this.element=a,this.handlers={}},C={isEmpty:{configurable:!0}};B.prototype.bind=function(a,b){"undefined"==typeof this.handlers[a]&&(this.handlers[a]=[]),this.handlers[a].push(b),this.element.addEventListener(a,b,!1)},B.prototype.unbind=function(a,b){var c=this;this.handlers[a]=this.handlers[a].filter(function(d){return!!(b&&d!==b)||(c.element.removeEventListener(a,d,!1),!1)})},B.prototype.unbindAll=function(){for(var a in this.handlers)this.unbind(a)},C.isEmpty.get=function(){var a=this;return Object.keys(this.handlers).every(function(b){return 0===a.handlers[b].length})},Object.defineProperties(B.prototype,C);var D=function(){this.eventElements=[]};D.prototype.eventElement=function(a){var b=this.eventElements.filter(function(b){return b.element===a})[0];return b||(b=new B(a),this.eventElements.push(b)),b},D.prototype.bind=function(a,b,c){this.eventElement(a).bind(b,c)},D.prototype.unbind=function(a,b,c){var d=this.eventElement(a);d.unbind(b,c),d.isEmpty&&this.eventElements.splice(this.eventElements.indexOf(d),1)},D.prototype.unbindAll=function(){this.eventElements.forEach(function(a){return a.unbindAll()}),this.eventElements=[]},D.prototype.once=function(a,b,c){var d=this.eventElement(a),e=function(a){d.unbind(b,e),c(a)};d.bind(b,e)};var E={isWebKit:"undefined"!=typeof document&&"WebkitAppearance"in document.documentElement.style,supportsTouch:"undefined"!=typeof window&&("ontouchstart"in window||"maxTouchPoints"in window.navigator&&0a.scrollbarYTop?1:-1;a.element.scrollTop+=d*a.containerHeight,q(a),b.stopPropagation()}),a.event.bind(a.scrollbarX,"mousedown",function(a){return a.stopPropagation()}),a.event.bind(a.scrollbarXRail,"mousedown",function(b){var c=b.pageX-window.pageXOffset-a.scrollbarXRail.getBoundingClientRect().left,d=c>a.scrollbarXLeft?1:-1;a.element.scrollLeft+=d*a.containerWidth,q(a),b.stopPropagation()})},"drag-thumb":function(a){t(a,["containerWidth","contentWidth","pageX","railXWidth","scrollbarX","scrollbarXWidth","scrollLeft","x","scrollbarXRail"]),t(a,["containerHeight","contentHeight","pageY","railYHeight","scrollbarY","scrollbarYHeight","scrollTop","y","scrollbarYRail"])},keyboard:function(a){function b(b,d){var e=v(c.scrollTop);if(0===b){if(!a.scrollbarYActive)return!1;if(0===e&&0=a.contentHeight-a.containerHeight&&0>d)return!a.settings.wheelPropagation}var f=c.scrollLeft;if(0===d){if(!a.scrollbarXActive)return!1;if(0===f&&0>b||f>=a.contentWidth-a.containerWidth&&0u(a)?f||g:i||j,!d||!b.settings.wheelPropagation}function d(a){var b=a.deltaX,c=-1*a.deltaY;return("undefined"==typeof b||"undefined"==typeof c)&&(b=-1*a.wheelDeltaX/6,c=a.wheelDeltaY/6),a.deltaMode&&1===a.deltaMode&&(b*=10,c*=10),b!==b&&c!==c/* NaN checks */&&(b=0,c=a.wheelDelta),a.shiftKey?[-c,-b]:[b,c]}function f(b,c,d){// FIXME: this is a workaround for - - - - - - - -
-
- -
-
-
-
-

Documentation

-

Tagscript documentation can be viewed here, or alternatively for Carl-bot, here.

-
-
-
-
-
- - -
-
- Copyright © benz206 2022 - Not affiliated with Carl-bot or Botlabs -
-
- -
- - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/eslint.config.mjs b/eslint.config.mjs index 670f290..0b2affc 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -4,7 +4,24 @@ import nextTs from "eslint-config-next/typescript"; import { fixupConfigRules } from "@eslint/compat"; export default defineConfig([ - ...fixupConfigRules([...nextVitals, ...nextTs]), - // Fluid registry sources are vendored as published; type checking includes them. - globalIgnores([".next/**", "out/**", "next-env.d.ts", "assets/**", "scripts/**", ".serena/**", "src/components/ui/**", "src/components/fluid-hover-highlight.tsx", "src/hooks/use-*.tsx", "src/hooks/use-fluid-hover.ts", "src/hooks/use-keyboard-nav-gate.ts", "src/lib/shape-context.tsx", "src/lib/icon-context.tsx", "src/lib/size-context.tsx", "src/lib/surface-context.tsx", "src/lib/elevated.tsx"]), + ...fixupConfigRules([...nextVitals, ...nextTs]), + // Fluid registry sources are vendored as published; type checking includes them. + globalIgnores([ + ".next/**", + "out/**", + "next-env.d.ts", + "assets/**", + "scripts/**", + ".serena/**", + "src/components/ui/**", + "src/components/fluid-hover-highlight.tsx", + "src/hooks/use-*.tsx", + "src/hooks/use-fluid-hover.ts", + "src/hooks/use-keyboard-nav-gate.ts", + "src/lib/shape-context.tsx", + "src/lib/icon-context.tsx", + "src/lib/size-context.tsx", + "src/lib/surface-context.tsx", + "src/lib/elevated.tsx", + ]), ]); diff --git a/index.html b/index.html deleted file mode 100644 index 5027841..0000000 --- a/index.html +++ /dev/null @@ -1,933 +0,0 @@ - - - - - - - - - Playground - _Leg3ndary - - - - - - - - - - - - " - - - - - - - - - - - - - - - - - - - -
- - - -
- - - -
-
- -
-
-
-
-

Editor

-

- Put your tagscript here! Please be - patient, this may take a while to - process the first time around. -

-
- -
-
-
- -
- -
-
-
- -
-
-
-
-
-
-

Output

-

- Your tagscript output will show - here! Please note that this runs on - a different engine from carl-bot, - that means new blocks are added and - you can see them - here, an example of a common variable - name is count. -

-
-
- -
-
-
-
-
-
-
-
-
-
-

Actions

-

- Any and all actions that your - tagscript may do will show up here! -

-
- - - - - - - - -
ActionValues
-
-
-

- Errors: None -

-

- Warnings: None -

- -
-
-
-
-
-
-

Debug

-

- All variables used in your tag will - appear here, along with their final - value. -

-
- - - - - - - - -
VariableValue
-
- -
-
-
-
-
-
-
-
-

Seeds

-

- Seeds are predefined variables that - are passed to the engine, set them - here. -

-
- {args} - -
- -
-

Channel

-
- Name - -
-
- ID - -
-
- NSFW - -
-
- Mention - -
-
- Topic - -
-
- Slowmode - -
- -
-
-
-
-
-
-

User

-

- The user who "invoked the tag", - remember that this runs on a - different engine and so {user} is - technically not a valid block, - consider using {user(name)}. -

-
- Name - -
-
- Username - -
-
- ID - -
-
- Created At - -
-
- Joined At - -
-
- Mention - -
-
- Color - -
-
- Role IDs - -
- -
-
-
-
-
-
-

Target

-
- -
-
- Name - -
-
- Username - -
-
- ID - -
-
- Created At - -
-
- Joined At - -
-
- Mention - -
-
- Color - -
-
- Role IDs - -
- -
-
-
-
-
-
-
-
-

- Control Panel -

-

- Here are some other things you may - want to edit. -

- -
-
- -
-
- -
-
- -
-
-
-
-
- - -
-

- Syntax Highlighting brought to you by - Asty8926 -

-
- Copyright © _Leg3ndary 2022 - - Not affiliated with - Carl-bot - or - Botlabs -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - diff --git a/next-env.d.ts b/next-env.d.ts new file mode 100644 index 0000000..ce4e94a --- /dev/null +++ b/next-env.d.ts @@ -0,0 +1,7 @@ +/// +/// +import "./.next/types/routes.d.ts"; +import "./.next/types/root-params.d.ts"; + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/next.config.ts b/next.config.ts index bcbcea1..eb60c65 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,10 +1,10 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - output: "export", - trailingSlash: true, - basePath: process.env.NEXT_PUBLIC_BASE_PATH || "", - images: { unoptimized: true }, + output: "export", + trailingSlash: true, + basePath: process.env.NEXT_PUBLIC_BASE_PATH || "", + images: { unoptimized: true }, }; export default nextConfig; diff --git a/package.json b/package.json index f70589a..53bbe65 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,10 @@ "build": "next build", "lint": "eslint .", "typecheck": "tsc --noEmit", - "test": "bun test tests" + "test": "bun test tests", + "preview": "bun scripts/preview.ts", + "test:e2e": "playwright test", + "format": "prettier --write src/components/playground.tsx src/components/component-gallery.tsx src/components/script-editor.tsx src/components/seed-panel.tsx src/components/results-panel.tsx src/lib/tagscript.ts src/app tests e2e scripts/preview.ts *.ts *.mjs .github/workflows/*.yml README.md" }, "engines": { "node": ">=22.0.0" @@ -49,14 +52,16 @@ "@eslint/compat": "^2.1.1", "@playwright/test": "^1.63.0", "@tailwindcss/postcss": "^4.3.3", + "@types/bun": "^1.4.2", "@types/node": "^26.6.3", "@types/react": "^19.3.0", "@types/react-dom": "^19.3.0", - "@typescript/native": "^7.0.2", + "@typescript/native": "npm:typescript@^7.0.2", "eslint": "^10.11.0", "eslint-config-next": "^16.3.7", + "prettier": "^3.9.9", "tailwindcss": "^4.3.3", - "typescript": "^6.0.2" + "typescript": "npm:@typescript/typescript6@^6.0.2" }, - "packageManager": "bun@1.3.14" + "packageManager": "bun@1.4.2" } diff --git a/partials/_footer.html b/partials/_footer.html deleted file mode 100644 index 6d356fc..0000000 --- a/partials/_footer.html +++ /dev/null @@ -1,6 +0,0 @@ - \ No newline at end of file diff --git a/partials/_navbar.html b/partials/_navbar.html deleted file mode 100644 index a98790c..0000000 --- a/partials/_navbar.html +++ /dev/null @@ -1,192 +0,0 @@ - \ No newline at end of file diff --git a/partials/_sidebar.html b/partials/_sidebar.html deleted file mode 100644 index 43f2511..0000000 --- a/partials/_sidebar.html +++ /dev/null @@ -1,142 +0,0 @@ - \ No newline at end of file diff --git a/postcss.config.mjs b/postcss.config.mjs index a9b5c43..d062fcd 100644 --- a/postcss.config.mjs +++ b/postcss.config.mjs @@ -1 +1,2 @@ -export default { plugins: { "@tailwindcss/postcss": {} } }; +const config = { plugins: { "@tailwindcss/postcss": {} } }; +export default config; diff --git a/scripts/importCarlScript.js b/scripts/importCarlScript.js deleted file mode 100644 index b87a03e..0000000 --- a/scripts/importCarlScript.js +++ /dev/null @@ -1,76 +0,0 @@ -function isInt(str) { - if (typeof str !== "string") { - return false; - } - - const num = Number(str); - - if (Number.isInteger(num) && num > 0) { - return true; - } - - return false; -} - -const CARL_API_URL = - "https://mighty-sea-55702.herokuapp.com/https://carl.gg/api/v1/tags/"; - -const carlImportURL = document.getElementById("carlImportURL"); -const carlImportBtn = document.getElementById("carlImport"); - -function parseID(str) { - let tagID; - if (isInt(str) === true) { - tagID = str; - } else { - tagID = str.split("/").pop(); - if (isInt(tagID) === false) { - console.log("Add something here when tag isn't found idiot"); - // Custom tag I made lul - tagID = 1479390; - } - } - return tagID; -} - -let isLoading = false; - -async function importTag() { - if (isLoading) return; - if (useLocal.checked) { - window.localStorage.setItem("tagscript", editor.getValue()); - } else if (useSession.checked) { - window.sessionStorage.setItem("tagscript", editor.getValue()); - } - carlImportBtn.setAttribute("disabled", true); - isLoading = true; - - let importURL = carlImportURL.value; - - let tagID = parseID(importURL); - - let headers = new Headers(); - headers.append("origin", "btagscriptplayground"); - - try { - let resp = await fetch(CARL_API_URL + tagID, { - method: "GET", - headers: headers, - }).then((response) => response.json()); - editor.setValue(resp.content); - carlImportURL.innerText = ""; - } catch (err) { - let resp = await fetch(CARL_API_URL + "1479390", { - method: "GET", - headers: headers, - }).then((response) => response.json()); - editor.setValue(resp.content); - } - carlImportBtn.removeAttribute("disabled"); - isLoading = false; -} - -carlImportBtn.addEventListener("click", importTag); -carlImportURL.addEventListener("keydown", (e) => { - if (e.key === "Enter") importTag(); -}); diff --git a/scripts/manageDA.js b/scripts/manageDA.js deleted file mode 100644 index 965648a..0000000 --- a/scripts/manageDA.js +++ /dev/null @@ -1,29 +0,0 @@ -let actionButton = document.getElementById("clearActions"); -let debugButton = document.getElementById("clearDebug"); - -function clearActions(actions) { - let table = document.getElementById("actionBody"); - let errorsText = document.getElementById("errors"); - let warningsText = document.getElementById("warnings"); - - while (table.hasChildNodes()) { - table.removeChild(table.firstChild); - } - errorsText.innerText = "Errors: None"; - warningsText.innerText = "Warnings: None"; -} - -function clearDebug(actions) { - let table = document.getElementById("debugTable"); - let errorsText = document.getElementById("errors"); - let warningsText = document.getElementById("warnings"); - - while (table.hasChildNodes()) { - table.removeChild(table.firstChild); - } - errorsText.innerText = "Errors: None"; - warningsText.innerText = "Warnings: None"; -} - -actionButton.addEventListener("click", clearActions); -debugButton.addEventListener("click", clearDebug); diff --git a/scripts/manageStorage.js b/scripts/manageStorage.js deleted file mode 100644 index 7a7d65a..0000000 --- a/scripts/manageStorage.js +++ /dev/null @@ -1,74 +0,0 @@ -let useLocal = document.getElementById("useLocal"); -let useSession = document.getElementById("useSession"); - -if (window.localStorage.getItem("useLocal") === true) { - useLocal.checked = true; - useSession.checked = false; -} - -if (useLocal.checked === true) { - if (window.localStorage.getItem("tagscript") === null) { - editor.setValue(""); - } else { - editor.setValue(window.localStorage.getItem("tagscript")); - } -} else if (useSession.checked === true) { - if (window.sessionStorage.getItem("tagscript") === null) { - editor.setValue(""); - } else { - editor.setValue(window.sessionStorage.getItem("tagscript")); - } -} - -useLocal.addEventListener("change", (event) => { - if (event.currentTarget.checked) { - useSession.checked = false; - window.localStorage.setItem("useLocal", true); - window.localStorage.setItem("useSession", false); - window.localStorage.setItem("tagscript", editor.getValue()); - window.sessionStorage.removeItem("tagscript"); - } -}); - -useSession.addEventListener("change", (event) => { - if (event.currentTarget.checked) { - useLocal.checked = false; - window.localStorage.setItem("useLocal", false); - window.localStorage.setItem("useSession", true); - window.sessionStorage.setItem("tagscript", editor.getValue()); - window.localStorage.removeItem("tagscript"); - } -}); - -editor.on("change", function () { - if (useLocal.checked) { - window.localStorage.setItem("tagscript", editor.getValue()); - } else if (useSession.checked) { - window.sessionStorage.setItem("tagscript", editor.getValue()); - } -}); - -let clearLocal = document.getElementById("clearLocal"); -clearLocal.addEventListener("click", function () { - window.localStorage.removeItem("tagscript"); - window.localStorage.removeItem("useLocal"); - window.localStorage.removeItem("useSession"); - editor.setValue(""); -}); -let clearSession = document.getElementById("clearSession"); -clearSession.addEventListener("click", function () { - window.sessionStorage.removeItem("tagscript"); - window.sessionStorage.removeItem("useLocal"); - window.sessionStorage.removeItem("useSession"); - editor.setValue(""); -}); -let clearAll = document.getElementById("clearAll"); -clearAll.addEventListener("click", function () { - window.localStorage.removeItem("tagscript"); - window.localStorage.removeItem("useLocal"); - window.localStorage.removeItem("useSession"); - window.sessionStorage.removeItem("tagscript"); - window.sessionStorage.removeItem("useLocal"); - window.sessionStorage.removeItem("useSession"); - editor.setValue(""); -}); diff --git a/scripts/processTagScript.js b/scripts/processTagScript.js deleted file mode 100644 index 45cc013..0000000 --- a/scripts/processTagScript.js +++ /dev/null @@ -1,308 +0,0 @@ -// Out of respect, please don't go spamming my API :( - -const API_URL = "https://leg3ndary.pythonanywhere.com/v2/process/"; - -class ApiResponse { - constructor(data) { - this.body = data.body; - this.actions = data.actions; - this.extras = data.extras; - this.uses = data.uses; - } -} - -function escapeHtml(unsafe) { - return unsafe - .replace(/&/g, "&") - .replace(//g, ">") - .replace(/"/g, """) - .replace(/'/g, "'"); -} - -function decodeTagScript(tagscript) { - return tagscript - .replace(/Ꜳ/g, "\\") - .replace(/ꜳ/g, "<") - .replace(/₩/g, "/") - .replace(/ꜵ/g, ">") - .replace(/Ꜷ/g, "."); -} - -function encodeTagScript(tagscript) { - return tagscript - .replace(/\\/g, "Ꜳ") - .replace(/\//g, "₩") - .replace(//g, "ꜵ") - .replace(/\./g, "Ꜷ"); -} - -function genSeedString() { - // these are left here in case I need to quickly edit them. - const ARGS = document.getElementById("tagscriptArgs").value; - - const CHANNEL_NAME = document.getElementById("channelName").value; - const CHANNEL_ID = document.getElementById("channelID").value; - const CHANNEL_NSFW = document.getElementById("channelNSFW").value; - const CHANNEL_MENTION = document.getElementById("channelMention").value; - const CHANNEL_TOPIC = document.getElementById("channelTopic").value; - const CHANNEL_SLOWMODE = document.getElementById("channelSlowmode").value; - - const USER_NAME = document.getElementById("userName").value; - const USER_USERNAME = document.getElementById("userUsername").value; - const USER_ID = document.getElementById("userID").value; - const USER_CREATEDAT = document.getElementById("userCreatedAt").value; - const USER_JOINEDAT = document.getElementById("userJoinedAt").value; - const USER_MENTION = document.getElementById("userMention").value; - const USER_COLOR = document.getElementById("userColor").value; - const USER_ROLEIDS = document.getElementById("userRoleIDs").value; - - const USE_TARGET = document.getElementById("useTarget").checked; - - const TARGET_NAME = document.getElementById("targetName").value; - const TARGET_USERNAME = document.getElementById("targetUsername").value; - const TARGET_ID = document.getElementById("targetID").value; - const TARGET_CREATEDAT = document.getElementById("targetCreatedAt").value; - const TARGET_JOINEDAT = document.getElementById("targetJoinedAt").value; - const TARGET_MENTION = document.getElementById("targetMention").value; - const TARGET_COLOR = document.getElementById("targetColor").value; - const TARGET_ROLEIDS = document.getElementById("targetRoleIDs").value; - - let target; - let user; - - user = { - name: USER_NAME, - username: USER_USERNAME, - id: USER_ID, - createdAt: USER_CREATEDAT, - joinedAt: USER_JOINEDAT, - mention: USER_MENTION, - color: USER_COLOR, - roleIDs: USER_ROLEIDS, - }; - - if (USE_TARGET === true) { - target = { - name: TARGET_NAME, - username: TARGET_USERNAME, - id: TARGET_ID, - createdAt: TARGET_CREATEDAT, - joinedAt: TARGET_JOINEDAT, - mention: TARGET_MENTION, - color: TARGET_COLOR, - roleIDs: TARGET_ROLEIDS, - }; - } else { - target = user; - } - - let seed = { - user: user, - target: target, - args: ARGS, - channel: { - name: CHANNEL_NAME, - id: CHANNEL_ID, - nsfw: CHANNEL_NSFW, - mention: CHANNEL_MENTION, - topic: CHANNEL_TOPIC, - slowmode: CHANNEL_SLOWMODE, - }, - }; - return seed; -} - -function loadActionTable(actions) { - const table = document.getElementById("actionBody"); - const errorsText = document.getElementById("errors"); - const warningsText = document.getElementById("warnings"); - - while (table.hasChildNodes()) { - table.removeChild(table.firstChild); - } - errorsText.innerText = "Errors: None"; - warningsText.innerText = "Warnings: None"; - - const errors = []; - const warnings = []; - - for (const [action, value] of Object.entries(actions)) { - const row = table.insertRow(); - const actionRow = row.insertCell(0); - const valuesRow = row.insertCell(1); - const badge = row.insertCell(2); - - let actionContent = "Error"; - let valueContent = "Error"; - let badgeContent = '
Error
'; - - if (action === "blacklist") { - if (actions.requires) { - errors.push( - "You cannot have both a blacklist and a require block." - ); - actionContent = action; - valueContent = `Blacklisting the following IDS: ${value.items.join( - ", " - )}, - replying with "${value.response}" when the user/role/channel is blacklisted.`; - badgeContent = '
Error
'; - } else { - valueContent = `Blacklisting the following IDS: ${value.items.join( - ", " - )}, - replying with "${value.response}" when the user/role/channel is blacklisted.`; - badgeContent = - '
Succewass
'; - } - } else if (action === "commands") { - actionContent = "command"; - valueContent = `The following commands are used with their associated values: ${value.join( - ", " - )}`; - badgeContent = '
Success
'; - } else if (action === "delete") { - actionContent = "delete"; - valueContent = "Deleting the message"; - badgeContent = '
Success
'; - } else if (action === "embed") { - actionContent = "embed"; - valueContent = `Embedding the following embed json: ${JSON.stringify( - value - )}`; - badgeContent = '
Success
'; - } else if (action === "overrides") { - actionContent = "override"; - valueContent = "Overriding command permissions."; - badgeContent = '
Success
'; - } else if (action === "reactions") { - actionContent = "reaction"; - valueContent = `Adding the following reactions: ${value.join( - ", " - )}`; - badgeContent = '
Success
'; - } else if (action === "requires") { - if (actions.requires) { - actionContent = "require"; - valueContent = `Requiring the following IDS: ${value.items.join( - ", " - )}, - replying with "${value.response}" when the requirements aren't met.`; - badgeContent = '
Error
'; - } else { - actionContent = "require"; - valueContent = `Requiring the following IDS: ${value.items.join( - ", " - )}, - replying with "${value.response}" when the requirements aren't met.`; - badgeContent = '
Success
'; - } - } else if (action === "target") { - actionContent = "redirect"; - valueContent = `Redirecting the message to the following channel: ${value.channel}`; - badgeContent = '
Success
'; - } else { - console.log("Unknown action: " + action); - } - - actionRow.innerHTML = escapeHtml(actionContent); - valuesRow.innerHTML = escapeHtml(valueContent); - badge.innerHTML = badgeContent; - } - - if (errors.length > 0) { - errorsText.innerText = "Errors: " + errors.join(", "); - } - if (warnings.length > 0) { - warningsText.innerText = "Warnings: " + warnings.join(", "); - } -} - -function loadDebugTable(debug) { - const table = document.getElementById("debugTable"); - - while (table.hasChildNodes()) { - table.removeChild(table.firstChild); - } - - for (const [name, value] of Object.entries(debug)) { - if (["channel", "user", "target", "args"].includes(name) !== true) { - const row = table.insertRow(); - - const nameRow = row.insertCell(0); - nameRow.innerHTML = escapeHtml(name); - - const valuesRow = row.insertCell(1); - valuesRow.innerHTML = escapeHtml(value); - } - } -} - -const BUTTON = document.getElementById("processButton"); -let isProcessing = false; - -async function processTagScript() { - if (isProcessing) return; - - BUTTON.setAttribute("disabled", true); - isProcessing = true; - - // Generating the seed str - const TAGSCRIPT = editor.getValue(); - const SEED = genSeedString(); - let cleanedSeed = encodeTagScript(JSON.stringify(SEED)); - - let headers = new Headers(); - headers.append("Content-Type", "application/x-www-form-urlencoded"); - - let body = new URLSearchParams(); - body.set("tagscript", TAGSCRIPT); - body.append("seeds", cleanedSeed); - - if (TAGSCRIPT.trim() === "") { - let response = new ApiResponse({ - output: "", - actions: {}, - extras: { - debug: {}, - }, - }); - document.getElementById("output").value = " "; - - loadActionTable(response.actions); - loadDebugTable(response.extras.debug); - } else { - let response = fetch(API_URL, { - method: "POST", - headers: headers, - body: body, - origin: "https://benz206.github.io:443", - }).then((res) => res.json()); - - response.then(function (resp) { - console.log(resp); - response = new ApiResponse(resp); - document.getElementById("output").value = decodeTagScript( - response.body - ); - - loadActionTable(response.actions); - loadDebugTable(response.extras.debug); - - const counter = document.getElementById("counter"); - counter.innerHTML = `Playground - Processed ${response.uses[1].toLocaleString()} different Tags`; - }); - } - - BUTTON.removeAttribute("disabled"); - isProcessing = false; -} - -BUTTON.addEventListener("click", processTagScript); - -editor.addKeyMap({ - "Cmd-Enter": processTagScript, - "Ctrl-Enter": processTagScript, -}); diff --git a/scripts/randomizeSeeds.js b/scripts/randomizeSeeds.js deleted file mode 100644 index b8917a0..0000000 --- a/scripts/randomizeSeeds.js +++ /dev/null @@ -1,188 +0,0 @@ -let randomizeArgs = document.getElementById("randomizeArgs"); -randomizeArgs.addEventListener("click", randomArgs); - -let randomizeChannel = document.getElementById("randomizeChannel"); -randomizeChannel.addEventListener("click", randomChannel); - -let randomizeUser = document.getElementById("randomizeUser"); -randomizeUser.addEventListener("click", randomUser); - -let randomizeTarget = document.getElementById("randomizeTarget"); -randomizeTarget.addEventListener("click", randomTarget); - -let randomizeAll = document.getElementById("randomizeAll"); -randomizeAll.addEventListener("click", randomAll); - -function randomID() { - return Math.floor( - Math.random() * (9999999999999999999 - 100000000000000000 + 1) + - 100000000000000000 - ); -} - -function randomArgs() { - let argsValues = [ - "Hello world", - "Boy do I love tagscript!", - "Imagine", - "Consider putting something here", - "args!!!", - "kiwis", - "discord", - "botlabs", - ]; - document.getElementById("tagscriptArgs").value = - argsValues[Math.floor(Math.random() * argsValues.length)]; -} - -function randomChannel() { - let channelNameValues = [ - "tagscript-chat", - "tagscript-is-so-cool", - "general", - "support", - "carl-bot", - "btagscript", - "main-chat", - ]; - document.getElementById("channelName").value = - channelNameValues[Math.floor(Math.random() * channelNameValues.length)]; - - let channelID = randomID(); - document.getElementById("channelID").value = channelID; - - let channelNSFWValues = ["true", "false"]; - document.getElementById("channelNSFW").value = - channelNSFWValues[Math.floor(Math.random() * channelNSFWValues.length)]; - - document.getElementById("channelMention").value = "<#" + channelID + ">"; - - let channelTopicValues = [ - "Shameless advertising", - "This channel is for tagscript", - "Tagscript is the coolest language to ever exist!", - "Javascript is pretty fun to learn!", - "Python is a pretty cool language", - ]; - document.getElementById("channelTopic").value = - channelTopicValues[ - Math.floor(Math.random() * channelTopicValues.length) - ]; - - let channelSlowmodeValues = [ - "0", - "5", - "30", - "60", - "120", - "240", - "480", - "960", - "1440", - ]; - document.getElementById("channelSlowmode").value = - channelSlowmodeValues[ - Math.floor(Math.random() * channelSlowmodeValues.length) - ]; -} - -function randomUser() { - let userNameValues = [ - "tagscript", - "tagscript-bot", - "tagscript-bot-2", - "carl-bot", - "btagscript", - "bot", - "turtle101", - "sentinelsfan", - ]; - document.getElementById("userName").value = - userNameValues[Math.floor(Math.random() * userNameValues.length)]; - - document.getElementById("userUsername").value = - userNameValues[Math.floor(Math.random() * userNameValues.length)] + - "#" + - Math.floor(Math.random() * (9999 - 0 + 1) + 0); - - let userID = randomID(); - document.getElementById("userID").value = userID; - - document.getElementById("userCreatedAt").value = Math.floor( - Math.random() * (Math.floor(Date.now() / 1000) - 0 + 1) + 0 - ); - - document.getElementById("userJoinedAt").value = Math.floor( - Math.random() * (Math.floor(Date.now() / 1000) - 0 + 1) + 0 - ); - - document.getElementById("userMention").value = "<@" + userID + ">"; - - document.getElementById("userColor").value = Math.floor( - Math.random() * 16777215 - ) - .toString(16) - .toUpperCase(); - - let userRoleIDS = []; - for (let i = 0; i < Math.floor(Math.random() * (10 - 1 + 1) + 1); i++) { - userRoleIDS.push(randomID()); - } - document.getElementById("userRoleIDs").value = userRoleIDS.join(" "); -} - -function randomTarget() { - let targetNameValues = [ - "Jerry", - "Ben", - "Asty", - "Asport", - "Arthur", - "Elise", - "Raffael", - "Deafiore", - "Panda", - "Oriel", - ]; - document.getElementById("targetName").value = - targetNameValues[Math.floor(Math.random() * targetNameValues.length)]; - - document.getElementById("targetUsername").value = - targetNameValues[Math.floor(Math.random() * targetNameValues.length)] + - "#" + - Math.floor(Math.random() * (9999 - 0 + 1) + 0); - - let targetID = randomID(); - document.getElementById("targetID").value = targetID; - - document.getElementById("targetCreatedAt").value = Math.floor( - Math.random() * (Math.floor(Date.now() / 1000) - 0 + 1) + 0 - ); - - document.getElementById("targetJoinedAt").value = Math.floor( - Math.random() * (Math.floor(Date.now() / 1000) - 0 + 1) + 0 - ); - - document.getElementById("targetMention").value = "<@" + targetID + ">"; - - document.getElementById("targetColor").value = Math.floor( - Math.random() * 16777215 - ) - .toString(16) - .toUpperCase(); - - let targetRoleIDS = []; - for (let i = 0; i < Math.floor(Math.random() * (10 - 1 + 1) + 1); i++) { - targetRoleIDS.push(randomID()); - } - document.getElementById("targetRoleIDs").value = targetRoleIDS.join(" "); -} - -function randomAll() { - randomArgs(); - randomChannel(); - randomUser(); - randomTarget(); -} - -randomAll(); diff --git a/scripts/syntaxHighlighting.js b/scripts/syntaxHighlighting.js deleted file mode 100644 index 0158990..0000000 --- a/scripts/syntaxHighlighting.js +++ /dev/null @@ -1,81 +0,0 @@ -/* eslint-disable max-len */ - -CodeMirror.defineSimpleMode("tse", { - start: [ - { - regex: /\d*\.?\d+/g, - token: "numbers", - }, - { - regex: /({=\(comment\):).*}/gi, - token: "comment", - }, - { - regex: /\{/g, - token: "lb brackets", - }, - { - regex: /\}/g, - token: "rb brackets", - }, - { - regex: /\(/g, - token: "lp operators", - }, - { - regex: /\)/g, - token: "rp operators", - }, - { - regex: /:/g, - token: "colon operators", - }, - { - regex: /(==|!=|>=?|<=?|[+\-*/%^])/g, - token: "operators", - }, - { - regex: /(m|math|\+|m)(?=(:|\(|\}))/g, - token: "blocks mathblock", - }, - { - regex: /(unix|uses|args|message|join|replace|if|any|all|and|or|break|contains|strf|#|random|rand|urlencode|td|index|list|cycle|=|let|assign|in|upper|lower|50:|c|cmd|redirect|require|blacklist|react|reactu|dm|delete|silent|silence|override|lvl|range)(?=(:|\(|\}))/g, - token: "blocks", - }, - { - regex: /(user|target|server)(?=(:|\(|\}))/g, - token: "blocks canparam", - }, - { - regex: /(avatar|id|created_at|joined_at|roleids|color|name|proper|position|icon|owner|randomonline|randomoffline|members|bots|humans|roles|channels|topic|slowmode|mention)(?=\))/g, - token: "parameters", - }, - { - regex: /(trunc|round|abs)/g, - token: "math", - }, - { - regex: /(true|false)/g, - token: "boolean", - }, - { - regex: /(https?)/g, - token: "http", - }, - { - regex: /(%a|%A|%w|%d|%-d|%b|%B|%m|%-m|%y|%Y|%H|%-H|%I|%-I|%p|%M|%-M|%S|%-S|%f|%z|%Z|%j|%-j|%U|%W|%c|%x|%X|%u|%n|%i|%s)/g, - token: "strf", - }, - { - regex: /.+?/, - token: "text", - }, - ], -}); - -// eslint-disable-next-line no-unused-vars -const editor = CodeMirror.fromTextArea(document.getElementById("tagscript"), { - mode: "tse", - theme: "tse", - lineWrapping: true, -}); diff --git a/src/app/globals.css b/src/app/globals.css index f9bbe96..71135f3 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -45,57 +45,58 @@ --color-shadow-color: var(--shadow-color); --color-focus-ring: var(--focus-ring); --overlay: var(--overlay); - @keyframes shimmer { - 0% { - background-position: 0% 0; + @keyframes shimmer { + 0% { + background-position: 0% 0; } - 100% { - background-position: 100% 0; + 100% { + background-position: 100% 0; } } - @keyframes spinner-move { - to { - stroke-dashoffset: -100; + @keyframes spinner-move { + to { + stroke-dashoffset: -100; } } - @keyframes spinner-dash { - 0%, 100% { - stroke-dasharray: 15 85; + @keyframes spinner-dash { + 0%, + 100% { + stroke-dasharray: 15 85; } - 50% { - stroke-dasharray: 40 60; + 50% { + stroke-dasharray: 40 60; } } - @keyframes sf-reveal-start { - from { - --sf-start: 1; + @keyframes sf-reveal-start { + from { + --sf-start: 1; } - to { - --sf-start: 0; + to { + --sf-start: 0; } } - @keyframes sf-reveal-end { - from { - --sf-end: 0; + @keyframes sf-reveal-end { + from { + --sf-end: 0; } - to { - --sf-end: 1; + to { + --sf-end: 1; } } - @keyframes sf-divider-start { - from { - opacity: 0; + @keyframes sf-divider-start { + from { + opacity: 0; } - to { - opacity: 1; + to { + opacity: 1; } } - @keyframes sf-divider-end { - from { - opacity: 1; + @keyframes sf-divider-end { + from { + opacity: 1; } - to { - opacity: 0; + to { + opacity: 0; } } } @@ -103,27 +104,53 @@ :root { --hover: rgb(0 0 0 / 0.04); --active: rgb(0 0 0 / 0.07); - --selected: #D4D4D4; - --destructive-light: #FEF2F2; + --selected: #d4d4d4; + --destructive-light: #fef2f2; --overlay: 0 0 0; - --focus-ring: #6B97FF; - --surface-1: #FAFAFA; - --surface-2: #FCFCFC; - --surface-3: #FFFFFF; - --surface-4: #FFFFFF; - --surface-5: #FFFFFF; - --surface-6: #FFFFFF; - --surface-7: #FFFFFF; - --surface-8: #FFFFFF; + --focus-ring: #6b97ff; + --surface-1: #fafafa; + --surface-2: #fcfcfc; + --surface-3: #ffffff; + --surface-4: #ffffff; + --surface-5: #ffffff; + --surface-6: #ffffff; + --surface-7: #ffffff; + --surface-8: #ffffff; --shadow-color: rgb(0 0 0 / 0.06); --shadow-1: 0 0 0 1px var(--shadow-color); - --shadow-2: 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color); - --shadow-3: 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color), 0 3px 3px -1.5px var(--shadow-color); - --shadow-4: 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color), 0 3px 3px -1.5px var(--shadow-color), 0 6px 6px -3px var(--shadow-color); - --shadow-5: 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color), 0 3px 3px -1.5px var(--shadow-color), 0 6px 6px -3px var(--shadow-color), 0 12px 12px -6px var(--shadow-color); - --shadow-6: 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color), 0 3px 3px -1.5px var(--shadow-color), 0 6px 6px -3px var(--shadow-color), 0 12px 12px -6px var(--shadow-color), 0 24px 24px -12px var(--shadow-color); - --shadow-7: 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color), 0 3px 3px -1.5px var(--shadow-color), 0 6px 6px -3px var(--shadow-color), 0 12px 12px -6px var(--shadow-color), 0 24px 24px -12px var(--shadow-color), 0 48px 48px -24px var(--shadow-color); - --shadow-8: 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color), 0 3px 3px -1.5px var(--shadow-color), 0 6px 6px -3px var(--shadow-color), 0 12px 12px -6px var(--shadow-color), 0 24px 24px -12px var(--shadow-color), 0 48px 48px -24px var(--shadow-color), 0 96px 96px -48px var(--shadow-color); + --shadow-2: + 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color); + --shadow-3: + 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color), + 0 3px 3px -1.5px var(--shadow-color); + --shadow-4: + 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color), + 0 3px 3px -1.5px var(--shadow-color), 0 6px 6px -3px var(--shadow-color); + --shadow-5: + 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color), + 0 3px 3px -1.5px var(--shadow-color), + 0 6px 6px -3px var(--shadow-color), 0 12px 12px -6px var(--shadow-color); + --shadow-6: + 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color), + 0 3px 3px -1.5px var(--shadow-color), + 0 6px 6px -3px var(--shadow-color), + 0 12px 12px -6px var(--shadow-color), + 0 24px 24px -12px var(--shadow-color); + --shadow-7: + 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color), + 0 3px 3px -1.5px var(--shadow-color), + 0 6px 6px -3px var(--shadow-color), + 0 12px 12px -6px var(--shadow-color), + 0 24px 24px -12px var(--shadow-color), + 0 48px 48px -24px var(--shadow-color); + --shadow-8: + 0 0 0 1px var(--shadow-color), 0 1px 1px -0.5px var(--shadow-color), + 0 3px 3px -1.5px var(--shadow-color), + 0 6px 6px -3px var(--shadow-color), + 0 12px 12px -6px var(--shadow-color), + 0 24px 24px -12px var(--shadow-color), + 0 48px 48px -24px var(--shadow-color), + 0 96px 96px -48px var(--shadow-color); --checker-a: #bbbbbb; --checker-b: #ffffff; } @@ -132,242 +159,408 @@ --hover: rgb(255 255 255 / 0.06); --active: rgb(255 255 255 / 0.1); --selected: #525252; - --destructive-light: #450A0A; + --destructive-light: #450a0a; --overlay: 255 255 255; --surface-1: #171717; - --surface-2: #1E1E1E; + --surface-2: #1e1e1e; --surface-3: #252525; - --surface-4: #2C2C2C; + --surface-4: #2c2c2c; --surface-5: #333333; - --surface-6: #3A3A3A; + --surface-6: #3a3a3a; --surface-7: #414141; --surface-8: #484848; - --dm-hi-base: rgba(255,255,255,0.01); - --dm-hi-mid: rgba(255,255,255,0.02); - --dm-hi-high: rgba(255,255,255,0.04); - --dm-hi-peak: rgba(255,255,255,0.06); - --dm-ring-base: rgba(255,255,255,0.02); - --dm-ring-mid: rgba(255,255,255,0.04); - --dm-ring-high: rgba(255,255,255,0.06); - --dm-drop: rgba(0,0,0,0.18); + --dm-hi-base: rgba(255, 255, 255, 0.01); + --dm-hi-mid: rgba(255, 255, 255, 0.02); + --dm-hi-high: rgba(255, 255, 255, 0.04); + --dm-hi-peak: rgba(255, 255, 255, 0.06); + --dm-ring-base: rgba(255, 255, 255, 0.02); + --dm-ring-mid: rgba(255, 255, 255, 0.04); + --dm-ring-high: rgba(255, 255, 255, 0.06); + --dm-drop: rgba(0, 0, 0, 0.18); --shadow-1: inset 0 0 0 1px var(--dm-ring-base); - --shadow-2: inset 0 1px 0 0 var(--dm-hi-base), inset 0 0 0 1px var(--dm-ring-base), 0 1px 1px -0.5px var(--dm-drop); - --shadow-3: inset 0 1px 0 0 var(--dm-hi-mid), inset 0 0 0 1px var(--dm-ring-base), 0 0 0 1px rgba(0,0,0,0.12), 0 1px 1px -0.5px var(--dm-drop), 0 3px 3px -1.5px var(--dm-drop); - --shadow-4: inset 0 1px 0 0 var(--dm-hi-mid), inset 0 0 0 1px var(--dm-ring-mid), 0 0 0 1px rgba(0,0,0,0.14), 0 1px 1px -0.5px var(--dm-drop), 0 3px 3px -1.5px var(--dm-drop), 0 6px 6px -3px var(--dm-drop); - --shadow-5: inset 0 1px 0 0 var(--dm-hi-high), inset 0 0 0 1px var(--dm-ring-mid), 0 0 0 1px rgba(0,0,0,0.16), 0 1px 1px -0.5px var(--dm-drop), 0 3px 3px -1.5px var(--dm-drop), 0 6px 6px -3px var(--dm-drop), 0 12px 12px -6px var(--dm-drop); - --shadow-6: inset 0 1px 0 0 var(--dm-hi-high), inset 0 0 0 1px var(--dm-ring-high), 0 0 0 1px rgba(0,0,0,0.18), 0 1px 1px -0.5px var(--dm-drop), 0 3px 3px -1.5px var(--dm-drop), 0 6px 6px -3px var(--dm-drop), 0 12px 12px -6px var(--dm-drop), 0 24px 24px -12px var(--dm-drop); - --shadow-7: inset 0 1px 0 0 var(--dm-hi-peak), inset 0 0 0 1px var(--dm-ring-high), 0 0 0 1px rgba(0,0,0,0.20), 0 1px 1px -0.5px var(--dm-drop), 0 3px 3px -1.5px var(--dm-drop), 0 6px 6px -3px var(--dm-drop), 0 12px 12px -6px var(--dm-drop), 0 24px 24px -12px var(--dm-drop), 0 48px 48px -24px var(--dm-drop); - --shadow-8: inset 0 1px 0 0 var(--dm-hi-peak), inset 0 0 0 1px var(--dm-ring-high), 0 0 0 1px rgba(0,0,0,0.22), 0 1px 1px -0.5px var(--dm-drop), 0 3px 3px -1.5px var(--dm-drop), 0 6px 6px -3px var(--dm-drop), 0 12px 12px -6px var(--dm-drop), 0 24px 24px -12px var(--dm-drop), 0 48px 48px -24px var(--dm-drop), 0 96px 96px -48px var(--dm-drop); + --shadow-2: + inset 0 1px 0 0 var(--dm-hi-base), inset 0 0 0 1px var(--dm-ring-base), + 0 1px 1px -0.5px var(--dm-drop); + --shadow-3: + inset 0 1px 0 0 var(--dm-hi-mid), inset 0 0 0 1px var(--dm-ring-base), + 0 0 0 1px rgba(0, 0, 0, 0.12), 0 1px 1px -0.5px var(--dm-drop), + 0 3px 3px -1.5px var(--dm-drop); + --shadow-4: + inset 0 1px 0 0 var(--dm-hi-mid), inset 0 0 0 1px var(--dm-ring-mid), + 0 0 0 1px rgba(0, 0, 0, 0.14), 0 1px 1px -0.5px var(--dm-drop), + 0 3px 3px -1.5px var(--dm-drop), 0 6px 6px -3px var(--dm-drop); + --shadow-5: + inset 0 1px 0 0 var(--dm-hi-high), inset 0 0 0 1px var(--dm-ring-mid), + 0 0 0 1px rgba(0, 0, 0, 0.16), 0 1px 1px -0.5px var(--dm-drop), + 0 3px 3px -1.5px var(--dm-drop), 0 6px 6px -3px var(--dm-drop), + 0 12px 12px -6px var(--dm-drop); + --shadow-6: + inset 0 1px 0 0 var(--dm-hi-high), inset 0 0 0 1px var(--dm-ring-high), + 0 0 0 1px rgba(0, 0, 0, 0.18), 0 1px 1px -0.5px var(--dm-drop), + 0 3px 3px -1.5px var(--dm-drop), 0 6px 6px -3px var(--dm-drop), + 0 12px 12px -6px var(--dm-drop), 0 24px 24px -12px var(--dm-drop); + --shadow-7: + inset 0 1px 0 0 var(--dm-hi-peak), inset 0 0 0 1px var(--dm-ring-high), + 0 0 0 1px rgba(0, 0, 0, 0.2), 0 1px 1px -0.5px var(--dm-drop), + 0 3px 3px -1.5px var(--dm-drop), 0 6px 6px -3px var(--dm-drop), + 0 12px 12px -6px var(--dm-drop), 0 24px 24px -12px var(--dm-drop), + 0 48px 48px -24px var(--dm-drop); + --shadow-8: + inset 0 1px 0 0 var(--dm-hi-peak), inset 0 0 0 1px var(--dm-ring-high), + 0 0 0 1px rgba(0, 0, 0, 0.22), 0 1px 1px -0.5px var(--dm-drop), + 0 3px 3px -1.5px var(--dm-drop), 0 6px 6px -3px var(--dm-drop), + 0 12px 12px -6px var(--dm-drop), 0 24px 24px -12px var(--dm-drop), + 0 48px 48px -24px var(--dm-drop), 0 96px 96px -48px var(--dm-drop); --checker-a: #1f1f1f; --checker-b: #2a2a2a; } @layer base { - :focus-visible { - outline: 1px solid var(--focus-ring, #6B97FF); - outline-offset: 2px; - border-radius: var(--shape-input-radius, 8px); + :focus-visible { + outline: 1px solid var(--focus-ring, #6b97ff); + outline-offset: 2px; + border-radius: var(--shape-input-radius, 8px); } - a:focus-visible { - border-radius: 2px; + a:focus-visible { + border-radius: 2px; } } - html.transitioning *, html.transitioning *::before, html.transitioning *::after { - transition: border-radius 180ms ease-in-out, background-color 180ms ease-in-out, color 180ms ease-in-out, border-color 180ms ease-in-out, fill 180ms ease-in-out, stroke 180ms ease-in-out !important; +html.transitioning *, +html.transitioning *::before, +html.transitioning *::after { + transition: + border-radius 180ms ease-in-out, + background-color 180ms ease-in-out, + color 180ms ease-in-out, + border-color 180ms ease-in-out, + fill 180ms ease-in-out, + stroke 180ms ease-in-out !important; } - .shimmer-text { +.shimmer-text { color: transparent; - background: linear-gradient(90deg, #a3a3a3 0%, #a3a3a3 35%, #525252 50%, #a3a3a3 65%, #a3a3a3 100%); + background: linear-gradient( + 90deg, + #a3a3a3 0%, + #a3a3a3 35%, + #525252 50%, + #a3a3a3 65%, + #a3a3a3 100% + ); background-size: 300% 100%; background-clip: text; -webkit-background-clip: text; animation: shimmer 1.5s ease-in-out infinite; } - @property --sf-start { +@property --sf-start { syntax: ""; inherits: false; initial-value: 1; } - @property --sf-end { +@property --sf-end { syntax: ""; inherits: false; initial-value: 1; } - .scroll-fade, .scroll-fade-x { +.scroll-fade, +.scroll-fade-x { --scroll-fade-size: 48px; } - .scroll-fade { - -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--scroll-fade-size), #000 calc(100% - var(--scroll-fade-size)), transparent 100%); - mask-image: linear-gradient(to bottom, transparent 0, #000 var(--scroll-fade-size), #000 calc(100% - var(--scroll-fade-size)), transparent 100%); +.scroll-fade { + -webkit-mask-image: linear-gradient( + to bottom, + transparent 0, + #000 var(--scroll-fade-size), + #000 calc(100% - var(--scroll-fade-size)), + transparent 100% + ); + mask-image: linear-gradient( + to bottom, + transparent 0, + #000 var(--scroll-fade-size), + #000 calc(100% - var(--scroll-fade-size)), + transparent 100% + ); } - .scroll-fade-x { - -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--scroll-fade-size), #000 calc(100% - var(--scroll-fade-size)), transparent 100%); - mask-image: linear-gradient(to right, transparent 0, #000 var(--scroll-fade-size), #000 calc(100% - var(--scroll-fade-size)), transparent 100%); +.scroll-fade-x { + -webkit-mask-image: linear-gradient( + to right, + transparent 0, + #000 var(--scroll-fade-size), + #000 calc(100% - var(--scroll-fade-size)), + transparent 100% + ); + mask-image: linear-gradient( + to right, + transparent 0, + #000 var(--scroll-fade-size), + #000 calc(100% - var(--scroll-fade-size)), + transparent 100% + ); } @supports (timeline-scope: --sf) { - .scroll-divider { - position: relative; - timeline-scope: --sf-scroller; - --scroll-divider-size: 48px; + .scroll-divider { + position: relative; + timeline-scope: --sf-scroller; + --scroll-divider-size: 48px; } - .scroll-divider .scroll-fade { - scroll-timeline-name: --sf-scroller; + .scroll-divider .scroll-fade { + scroll-timeline-name: --sf-scroller; } - .scroll-divider::before, .scroll-divider::after { - content: ""; - position: absolute; - left: var(--scroll-divider-inset, 0px); - right: var(--scroll-divider-inset, 0px); - height: 1px; - background-color: var(--border); - pointer-events: none; - z-index: 1; - opacity: 0; + .scroll-divider::before, + .scroll-divider::after { + content: ""; + position: absolute; + left: var(--scroll-divider-inset, 0px); + right: var(--scroll-divider-inset, 0px); + height: 1px; + background-color: var(--border); + pointer-events: none; + z-index: 1; + opacity: 0; } - .scroll-divider::before { - top: 0; - animation: sf-divider-start both linear; - animation-timeline: --sf-scroller; - animation-range: 0 var(--scroll-divider-size); + .scroll-divider::before { + top: 0; + animation: sf-divider-start both linear; + animation-timeline: --sf-scroller; + animation-range: 0 var(--scroll-divider-size); } - .scroll-divider:first-child::before { - content: none; + .scroll-divider:first-child::before { + content: none; } - .scroll-divider::after { - bottom: 0; - animation: sf-divider-end both linear; - animation-timeline: --sf-scroller; - animation-range: calc(100% - var(--scroll-divider-size)) 100%; + .scroll-divider::after { + bottom: 0; + animation: sf-divider-end both linear; + animation-timeline: --sf-scroller; + animation-range: calc(100% - var(--scroll-divider-size)) 100%; } } @supports (animation-timeline: scroll()) { - .scroll-fade { - mask-image: linear-gradient(to bottom, rgba(0, 0, 0, var(--sf-start)) 0, #000 var(--scroll-fade-size), #000 calc(100% - var(--scroll-fade-size)), rgba(0, 0, 0, var(--sf-end)) 100%); - -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, var(--sf-start)) 0, #000 var(--scroll-fade-size), #000 calc(100% - var(--scroll-fade-size)), rgba(0, 0, 0, var(--sf-end)) 100%); - animation: sf-reveal-start both linear, sf-reveal-end both linear; - animation-timeline: scroll(self), scroll(self); - animation-range-start: 0, calc(100% - var(--scroll-fade-size)); - animation-range-end: var(--scroll-fade-size), 100%; + .scroll-fade { + mask-image: linear-gradient( + to bottom, + rgba(0, 0, 0, var(--sf-start)) 0, + #000 var(--scroll-fade-size), + #000 calc(100% - var(--scroll-fade-size)), + rgba(0, 0, 0, var(--sf-end)) 100% + ); + -webkit-mask-image: linear-gradient( + to bottom, + rgba(0, 0, 0, var(--sf-start)) 0, + #000 var(--scroll-fade-size), + #000 calc(100% - var(--scroll-fade-size)), + rgba(0, 0, 0, var(--sf-end)) 100% + ); + animation: + sf-reveal-start both linear, + sf-reveal-end both linear; + animation-timeline: scroll(self), scroll(self); + animation-range-start: 0, calc(100% - var(--scroll-fade-size)); + animation-range-end: var(--scroll-fade-size), 100%; } - .scroll-fade-x { - mask-image: linear-gradient(to right, rgba(0, 0, 0, var(--sf-start)) 0, #000 var(--scroll-fade-size), #000 calc(100% - var(--scroll-fade-size)), rgba(0, 0, 0, var(--sf-end)) 100%); - -webkit-mask-image: linear-gradient(to right, rgba(0, 0, 0, var(--sf-start)) 0, #000 var(--scroll-fade-size), #000 calc(100% - var(--scroll-fade-size)), rgba(0, 0, 0, var(--sf-end)) 100%); - animation: sf-reveal-start both linear, sf-reveal-end both linear; - animation-timeline: scroll(self inline), scroll(self inline); - animation-range-start: 0, calc(100% - var(--scroll-fade-size)); - animation-range-end: var(--scroll-fade-size), 100%; + .scroll-fade-x { + mask-image: linear-gradient( + to right, + rgba(0, 0, 0, var(--sf-start)) 0, + #000 var(--scroll-fade-size), + #000 calc(100% - var(--scroll-fade-size)), + rgba(0, 0, 0, var(--sf-end)) 100% + ); + -webkit-mask-image: linear-gradient( + to right, + rgba(0, 0, 0, var(--sf-start)) 0, + #000 var(--scroll-fade-size), + #000 calc(100% - var(--scroll-fade-size)), + rgba(0, 0, 0, var(--sf-end)) 100% + ); + animation: + sf-reveal-start both linear, + sf-reveal-end both linear; + animation-timeline: scroll(self inline), scroll(self inline); + animation-range-start: 0, calc(100% - var(--scroll-fade-size)); + animation-range-end: var(--scroll-fade-size), 100%; } } @media (pointer: fine) { - * { - scrollbar-width: thin; - scrollbar-color: rgb(var(--overlay) / 0.08) transparent; + * { + scrollbar-width: thin; + scrollbar-color: rgb(var(--overlay) / 0.08) transparent; } - ::-webkit-scrollbar { - width: 10px; - height: 10px; + ::-webkit-scrollbar { + width: 10px; + height: 10px; } - ::-webkit-scrollbar-track, ::-webkit-scrollbar-corner { - background: transparent; + ::-webkit-scrollbar-track, + ::-webkit-scrollbar-corner { + background: transparent; } - ::-webkit-scrollbar-thumb { - background-color: rgb(var(--overlay) / 0.08); - border: 3px solid transparent; - background-clip: content-box; - border-radius: 9999px; + ::-webkit-scrollbar-thumb { + background-color: rgb(var(--overlay) / 0.08); + border: 3px solid transparent; + background-clip: content-box; + border-radius: 9999px; } - ::-webkit-scrollbar-thumb:vertical { - border-left-width: 1px; - border-right-width: 5px; + ::-webkit-scrollbar-thumb:vertical { + border-left-width: 1px; + border-right-width: 5px; } - ::-webkit-scrollbar-thumb:horizontal { - border-top-width: 1px; - border-bottom-width: 5px; + ::-webkit-scrollbar-thumb:horizontal { + border-top-width: 1px; + border-bottom-width: 5px; } - ::-webkit-scrollbar-thumb:hover { - background-color: rgb(var(--overlay) / 0.12); + ::-webkit-scrollbar-thumb:hover { + background-color: rgb(var(--overlay) / 0.12); } - ::-webkit-scrollbar-thumb:active { - background-color: rgb(var(--overlay) / 0.16); + ::-webkit-scrollbar-thumb:active { + background-color: rgb(var(--overlay) / 0.16); } } - .scrollbar-hide { +.scrollbar-hide { -ms-overflow-style: none; scrollbar-width: none; } - .scrollbar-hide::-webkit-scrollbar { +.scrollbar-hide::-webkit-scrollbar { display: none; } /* Application palette layered over Fluid's published surface tokens. */ @theme inline { - --font-sans: "Inter Variable", sans-serif; - --color-background: var(--background); - --color-foreground: var(--foreground); - --color-primary: var(--primary); - --color-primary-foreground: var(--primary-foreground); - --color-secondary: var(--secondary); - --color-secondary-foreground: var(--foreground); - --color-muted: var(--muted); - --color-muted-foreground: var(--muted-foreground); - --color-accent: var(--accent); - --color-accent-foreground: var(--foreground); - --color-border: var(--border); - --color-input: var(--border); - --color-ring: var(--focus-ring); - --color-destructive: #d14d41; - --color-destructive-foreground: #fff; - --color-card: var(--surface-2); - --color-card-foreground: var(--foreground); - --color-popover: var(--surface-3); - --color-popover-foreground: var(--foreground); + --font-sans: "Inter Variable", sans-serif; + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--foreground); + --color-border: var(--border); + --color-input: var(--border); + --color-ring: var(--focus-ring); + --color-destructive: #d14d41; + --color-destructive-foreground: #fff; + --color-card: var(--surface-2); + --color-card-foreground: var(--foreground); + --color-popover: var(--surface-3); + --color-popover-foreground: var(--foreground); } :root { - --background: #f5f5f2; - --foreground: #242723; - --primary: #303d2c; - --primary-foreground: #fff; - --secondary: #e8ebe4; - --muted: #eeefea; - --muted-foreground: #73786f; - --accent: #e7ebdf; - --border: #e1e4db; - --focus-ring: #7c9265; - --radius: 0.75rem; + --background: #f5f5f2; + --foreground: #242723; + --primary: #303d2c; + --primary-foreground: #fff; + --secondary: #e8ebe4; + --muted: #eeefea; + --muted-foreground: #73786f; + --accent: #e7ebdf; + --border: #e1e4db; + --focus-ring: #7c9265; + --radius: 0.75rem; } .dark { - --background: #181b17; - --foreground: #eff1e9; - --primary: #c8d8b7; - --primary-foreground: #222a1e; - --secondary: #30372c; - --muted: #252b22; - --muted-foreground: #a2ab9b; - --accent: #343e2d; - --border: #363d31; + --background: #181b17; + --foreground: #eff1e9; + --primary: #c8d8b7; + --primary-foreground: #222a1e; + --secondary: #30372c; + --muted: #252b22; + --muted-foreground: #a2ab9b; + --accent: #343e2d; + --border: #363d31; } @layer base { - * { border-color: var(--border); } - body { margin: 0; background: var(--background); color: var(--foreground); font-family: "Inter Variable", sans-serif; font-size: 14px; -webkit-font-smoothing: antialiased; } - button, a, input, textarea { -webkit-tap-highlight-color: transparent; } - button:not(:disabled), [role="button"] { cursor: pointer; } - ::selection { background: #c9d8b9; color: #242723; } -} -.workspace-panel { border: 1px solid var(--border); border-radius: 18px; background: var(--surface-2); overflow: hidden; } -.panel-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 18px 22px; border-bottom: 1px solid var(--border); } -.eyebrow { font-size: 10px; font-weight: 600; letter-spacing: .15em; text-transform: uppercase; color: var(--muted-foreground); } -.code-editor .cm-editor { background: transparent; } -.code-editor .cm-scroller { min-height: 340px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; } -.code-editor .cm-focused { outline: none; } -.code-editor .cm-gutters { background: transparent; border: 0; color: #a3a99d; } -.code-editor .cm-content { padding: 22px 0; } -.code-editor .cm-line { padding-left: 16px; } -.code-editor .cm-gutterElement { padding-right: 12px; padding-left: 16px; } -.code-editor .cm-activeLine, .code-editor .cm-activeLineGutter { background: rgb(124 146 101 / .07); } -.dark .cm-editor { color: var(--foreground); } -.dark .cm-cursor { border-left-color: var(--foreground); } + * { + border-color: var(--border); + } + body { + margin: 0; + background: var(--background); + color: var(--foreground); + font-family: "Inter Variable", sans-serif; + font-size: 14px; + -webkit-font-smoothing: antialiased; + } + button, + a, + input, + textarea { + -webkit-tap-highlight-color: transparent; + } + button:not(:disabled), + [role="button"] { + cursor: pointer; + } + ::selection { + background: #c9d8b9; + color: #242723; + } +} +.workspace-panel { + border: 1px solid var(--border); + border-radius: 18px; + background: var(--surface-2); + overflow: hidden; +} +.panel-heading { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 18px 22px; + border-bottom: 1px solid var(--border); +} +.eyebrow { + font-size: 10px; + font-weight: 600; + letter-spacing: 0.15em; + text-transform: uppercase; + color: var(--muted-foreground); +} +.code-editor .cm-editor { + background: transparent; +} +.code-editor .cm-scroller { + min-height: 340px; + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; +} +.code-editor .cm-focused { + outline: none; +} +.code-editor .cm-gutters { + background: transparent; + border: 0; + color: #a3a99d; +} +.code-editor .cm-content { + padding: 22px 0; +} +.code-editor .cm-line { + padding-left: 16px; +} +.code-editor .cm-gutterElement { + padding-right: 12px; + padding-left: 16px; +} +.code-editor .cm-activeLine, +.code-editor .cm-activeLineGutter { + background: rgb(124 146 101 / 0.07); +} +.dark .cm-editor { + color: var(--foreground); +} +.dark .cm-cursor { + border-left-color: var(--foreground); +} @media (prefers-reduced-motion: reduce) { - *, *::before, *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; } + *, + *::before, + *::after { + scroll-behavior: auto !important; + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } } diff --git a/src/app/icon.svg b/src/app/icon.svg new file mode 100644 index 0000000..ca66788 --- /dev/null +++ b/src/app/icon.svg @@ -0,0 +1 @@ + diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 38bf39e..5b31c0f 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -3,10 +3,16 @@ import "@fontsource-variable/inter"; import "./globals.css"; export const metadata: Metadata = { - title: "bTagScript — Playground", - description: "A workspace for writing, testing, and exploring TagScript.", + title: "bTagScript — Playground", + description: "A workspace for writing, testing, and exploring TagScript.", }; -export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) { - return {children}; +export default function RootLayout({ + children, +}: Readonly<{ children: React.ReactNode }>) { + return ( + + {children} + + ); } diff --git a/src/app/page.tsx b/src/app/page.tsx index aa59d1a..e3808e0 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,3 +1,5 @@ +import { Playground } from "@/components/playground"; + export default function Home() { - return
bTagScript Playground
; + return ; } diff --git a/src/components/component-gallery.tsx b/src/components/component-gallery.tsx new file mode 100644 index 0000000..f28086e --- /dev/null +++ b/src/components/component-gallery.tsx @@ -0,0 +1,737 @@ +"use client"; + +import { useState, type ReactNode } from "react"; +import { ArrowUpRight, Check, FileCode2, Leaf, Plus } from "lucide-react"; +import { + Accordion, + AccordionItem, + AccordionTrigger, + AccordionContent, +} from "@/components/ui/accordion"; +import { AskUserQuestions } from "@/components/ui/ask-user-questions"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + CardGroup, + Card, + CardHeader, + CardTitle, + CardDescription, +} from "@/components/ui/card"; +import { CarouselDots } from "@/components/ui/carousel-dots"; +import { ChatMessage } from "@/components/ui/chat-message"; +import { CheckboxGroup, CheckboxItem } from "@/components/ui/checkbox-group"; +import { ColorPickerPopover } from "@/components/ui/color-picker"; +import { + Combobox, + ComboboxInput, + ComboboxContent, + ComboboxList, + ComboboxItem, + ComboboxEmpty, +} from "@/components/ui/combobox"; +import { + CommandMenu, + CommandMenuInput, + CommandMenuList, + CommandMenuEmpty, +} from "@/components/ui/command-menu"; +import { + Dialog, + DialogTrigger, + DialogContent, + DialogTitle, + DialogDescription, + DialogHeader, +} from "@/components/ui/dialog"; +import { + DropdownMenu, + DropdownTrigger, + DropdownContent, +} from "@/components/ui/dropdown"; +import { MenuItem } from "@/components/ui/menu-item"; +import { FileThumbnail } from "@/components/ui/file-thumbnail"; +import { InputCopy } from "@/components/ui/input-copy"; +import { InputGroup, InputField } from "@/components/ui/input-group"; +import { InputMessage } from "@/components/ui/input-message"; +import MobileDrawer from "@/components/ui/mobile-drawer"; +import { RadioGroup, RadioItem } from "@/components/ui/radio-group"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { + Select, + SelectTrigger, + SelectContent, + SelectItem, +} from "@/components/ui/select"; +import { + Sidebar, + SidebarProvider, + SidebarContent, + SidebarMenu, + SidebarMenuItem, + SidebarMenuButton, +} from "@/components/ui/sidebar"; +import { Slider } from "@/components/ui/slider"; +import { Switch } from "@/components/ui/switch"; +import { + Table, + TableHeader, + TableBody, + TableRow, + TableHead, + TableCell, +} from "@/components/ui/table"; +import { Tabs, TabsList, TabItem, TabPanel } from "@/components/ui/tabs"; +import { + TabsSubtle, + TabsSubtleItem, + TabsSubtlePanel, +} from "@/components/ui/tabs-subtle"; +import { ThinkingIndicator } from "@/components/ui/thinking-indicator"; +import { + ThinkingSteps, + ThinkingStepsHeader, + ThinkingStepsContent, + ThinkingStep, +} from "@/components/ui/thinking-steps"; +import { Tooltip } from "@/components/ui/tooltip"; +import { Elevated } from "@/lib/elevated"; + +const commandItems = [ + { value: "welcome", label: "Welcome tag", icon: FileCode2 }, + { value: "roll", label: "Dice roll", icon: Plus }, +]; +const slides = [ + "Start with an idea.", + "Give it some context.", + "See where it goes.", +]; + +function Demo({ + name, + slug, + children, +}: { + name: string; + slug?: string; + children: ReactNode; +}) { + return ( +
+
+

{name}

+ + + +
+
+ {children} +
+
+ ); +} + +export function ComponentGallery() { + const [checked, setChecked] = useState(new Set([0])); + const [color, setColor] = useState("#7c9265"); + const [choice, setChoice] = useState("Welcome"); + const [command, setCommand] = useState(""); + const [dropdown, setDropdown] = useState("Choose an action"); + const [name, setName] = useState("My first tag"); + const [draft, setDraft] = useState(""); + const [message, setMessage] = useState("Your message will appear here."); + const [drawer, setDrawer] = useState(false); + const [radio, setRadio] = useState("local"); + const [select, setSelect] = useState("User"); + const [slider, setSlider] = useState(60); + const [enabled, setEnabled] = useState(true); + const [subtle, setSubtle] = useState(0); + const [slide, setSlide] = useState(0); + const [answer, setAnswer] = useState(""); + const [file, setFile] = useState(null); + const [card, setCard] = useState(0); + const [clicks, setClicks] = useState(0); + const [navigation, setNavigation] = useState(0); + return ( + <> +
+ 30 components +

+ Live examples · Official registry sources · Radix primitives +

+ + Explore Fluid Functionalism + +
+
+ +
+ + + +
+
+ +
+ Ready + + Draft + + Needs a little work +
+
+ + setEnabled(!enabled)} + /> + + + setSlider(Number(value))} + showValue + formatValue={(value) => `${value}%`} + /> + + + + {[ + "Syntax highlighting", + "Browser storage", + "Keyboard shortcuts", + ].map((label, index) => ( + + setChecked((previous) => { + const next = new Set(previous); + if (next.has(index)) next.delete(index); + else next.add(index); + return next; + }) + } + /> + ))} + + + + + + + + + + + + + + + + + + + + + + + + + No matching tags. + + {(item) => { + const value = + typeof item === "string" + ? item + : item.value; + return ( + + {typeof item === "string" + ? item + : item.label} + + ); + }} + + + + + + + {dropdown} + } + /> + + {[ + "Duplicate tag", + "Move to collection", + "Archive tag", + ].map((label, index) => ( + setDropdown(label)} + /> + ))} + + + + + + + + + + + + + + {["write", "run", "refine"].map((value, index) => ( + +

+ {slides[index]} +

+
+ ))} +
+
+ + + + + + {[ + "A quieter way to navigate.", + "A moving highlight follows your selection.", + ].map((text, index) => ( + +

+ {text} +

+
+ ))} +
+ +
+ + + +
+
+ + + + + What makes it fluid? + + +

+ Spring motion and proximity highlights + respond to your next move. +

+
+
+ + + Can I use the keyboard? + + +

+ Yes. Tab to a control, then use Enter, + Space, or arrow keys. +

+
+
+
+
+ + + + + + + + + A little room to focus. + + + Press Escape or use the close button when + you’re done. + + +

+ Dialogs keep the task in focus while preserving + the workspace behind it. +

+
+
+
+ + + setDrawer(false)} + > +
+

+ A little more space. +

+

+ A navigation surface for compact screens. +

+ +
+
+
+ + + + + Tag + Status + + + + {["Welcome", "Dice roll"].map((tag) => ( + + {tag} + + + Ready + + + + ))} + +
+
+ + + {["An idea", "A possibility"].map((title, index) => ( + setCard(index)} + > + + {title} + + Click to make it yours. + + + + ))} + + + +

{slides[slide]}

+ +
+ + +
+ {Array.from({ length: 12 }, (_, index) => ( +

+ {String(index + 1).padStart(2, "0")} — + There’s always room for another idea. +

+ ))} +
+
+
+ + + + + + {["Workspace", "Your tags", "Settings"].map( + (label, index) => ( + + + setNavigation(index) + } + > + {label} + + + ), + )} + + + + + + + setCommand(item.label)} + > + + + No tags found. + + +

+ {command + ? `Selected: ${command}` + : "Type to filter. Enter to select."} +

+
+ +
+ Hello, TagScript. + + Hello! Let’s make something useful. + +
+
+ + { + setMessage(value); + setDraft(""); + }} + placeholder="Try the message composer…" + textareaProps={{ "aria-label": "Gallery message" }} + /> +

+ {message} +

+
+ +
+ {file && } + +
+
+ + +

+ An activity indicator demo. No request is running. +

+
+ + + + Example execution + + + + + + + + + + {answer ? ( +
+ + + {answer} + + +
+ ) : ( + + setAnswer( + answers.idea.selectedIds[0] ?? + "Ready to begin", + ) + } + /> + )} +
+
+
+ +

+ Built on a shared surface system. +

+

+ All 30 components use the same theme, typography, and + elevation. Try dark mode in workspace settings. +

+
+
+ + ); +} diff --git a/src/components/playground.tsx b/src/components/playground.tsx new file mode 100644 index 0000000..9791104 --- /dev/null +++ b/src/components/playground.tsx @@ -0,0 +1,999 @@ +"use client"; + +import { useCallback, useEffect, useRef, useState } from "react"; +import dynamic from "next/dynamic"; +import { MotionConfig } from "framer-motion"; +import { + ArrowDownToLine, + ArrowUpRight, + BookOpen, + Braces, + Check, + FileCode2, + FlaskConical, + GitFork, + Grid2X2, + Leaf, + MoreHorizontal, + Play, + Plus, + Search, + Settings2, + Sparkles, + Upload, +} from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/ui/badge"; +import { Tooltip, TooltipProvider } from "@/components/ui/tooltip"; +import { + Sidebar, + SidebarProvider, + useSidebar, + SidebarHeader, + SidebarContent, + SidebarFooter, + SidebarGroup, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuItem, + SidebarMenuButton, + SidebarTrigger, +} from "@/components/ui/sidebar"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, +} from "@/components/ui/dialog"; +import { + DropdownMenu, + DropdownTrigger, + DropdownContent, +} from "@/components/ui/dropdown"; +import { MenuItem } from "@/components/ui/menu-item"; +import { InputGroup, InputField } from "@/components/ui/input-group"; +import { InputCopy } from "@/components/ui/input-copy"; +import { RadioGroup, RadioItem } from "@/components/ui/radio-group"; +import { + Select, + SelectTrigger, + SelectContent, + SelectItem, +} from "@/components/ui/select"; +import { Slider } from "@/components/ui/slider"; +import { + CardGroup, + Card, + CardHeader, + CardTitle, + CardDescription, + CardContent, +} from "@/components/ui/card"; +import { + Accordion, + AccordionItem, + AccordionTrigger, + AccordionContent, +} from "@/components/ui/accordion"; +import { + CommandMenu, + CommandMenuDialog, + CommandMenuInput, + CommandMenuList, + CommandMenuEmpty, + CommandMenuFooter, +} from "@/components/ui/command-menu"; +import { ShapeProvider } from "@/lib/shape-context"; +import { + examples, + initialSeeds, + importCarlTag, + runTag, + type TagResult, +} from "@/lib/tagscript"; +import { SeedPanel } from "./seed-panel"; +import { ResultsPanel } from "./results-panel"; + +const ScriptEditor = dynamic( + () => import("./script-editor").then((m) => m.ScriptEditor), + { + ssr: false, + loading: () => ( +
+ Loading editor… +
+ ), + }, +); +const ComponentGallery = dynamic(() => + import("./component-gallery").then((m) => m.ComponentGallery), +); +type View = "playground" | "examples" | "guide" | "components"; +type StorageMode = "local" | "session" | "off"; + +export function Playground() { + return ( + + + + + + + + + + ); +} + +function Workspace() { + const { setOpenMobile } = useSidebar(); + const [view, updateView] = useState("playground"); + function setView(next: View) { + updateView(next); + setOpenMobile(false); + } + const [script, setScript] = useState(examples[0].code); + const [seeds, setSeeds] = useState(initialSeeds); + const [useTarget, setUseTarget] = useState(false); + const [result, setResult] = useState(null); + const [busy, setBusy] = useState(false); + const running = useRef(false); + const [error, setError] = useState(""); + const [elapsed, setElapsed] = useState(null); + const [settings, setSettings] = useState(false); + const [importOpen, setImportOpen] = useState(false); + const [importValue, setImportValue] = useState(""); + const [importError, setImportError] = useState(""); + const [importing, setImporting] = useState(false); + const [palette, setPalette] = useState(false); + const [storage, setStorage] = useState("local"); + const [ready, setReady] = useState(false); + const [notice, setNotice] = useState(""); + const [fontSize, setFontSize] = useState(14); + const [theme, setTheme] = useState("light"); + const fileInput = useRef(null); + + useEffect(() => { + // Browser storage is only available after hydration; restore the legacy script once. + /* eslint-disable react-hooks/set-state-in-effect */ + try { + const savedMode = localStorage.getItem("btag-storage"); + const mode: StorageMode = + savedMode === "session" || savedMode === "off" + ? savedMode + : savedMode === "local" + ? "local" + : localStorage.getItem("useSession") === "true" + ? "session" + : "local"; + const saved = + mode === "off" + ? null + : (mode === "local" + ? localStorage + : sessionStorage + ).getItem("tagscript"); + if (saved !== null) setScript(saved); + setStorage(mode); + } catch { + setNotice( + "Browser storage is unavailable. Export your script to keep a copy.", + ); + } + setReady(true); + /* eslint-enable react-hooks/set-state-in-effect */ + }, []); + useEffect(() => { + if (!ready) return; + try { + localStorage.setItem("btag-storage", storage); + if (storage !== "off") + (storage === "local" ? localStorage : sessionStorage).setItem( + "tagscript", + script, + ); + if (storage !== "local") localStorage.removeItem("tagscript"); + if (storage !== "session") sessionStorage.removeItem("tagscript"); + } catch { + // Report storage failures from this synchronization effect. + // eslint-disable-next-line react-hooks/set-state-in-effect + setNotice("Could not save your script. Export it to keep a copy."); + } + }, [script, storage, ready]); + useEffect(() => { + document.documentElement.classList.toggle("dark", theme === "dark"); + }, [theme]); + + const run = useCallback(async () => { + if (running.current) return; + if (!script.trim()) { + setResult(null); + setElapsed(null); + setError("Write a tag before running it."); + return; + } + running.current = true; + setBusy(true); + setError(""); + setResult(null); + setElapsed(null); + const started = performance.now(); + try { + setResult(await runTag(script, seeds, useTarget)); + setElapsed(Math.round(performance.now() - started)); + } catch (err) { + setError( + err instanceof TypeError + ? "The TagScript engine could not be reached. Check your connection and try again. Your script is still here." + : err instanceof Error + ? err.message + : "The tag could not be processed.", + ); + } finally { + running.current = false; + setBusy(false); + } + }, [script, seeds, useTarget]); + useEffect(() => { + const handler = (event: KeyboardEvent) => { + if ( + (event.metaKey || event.ctrlKey) && + event.key === "Enter" && + view === "playground" && + !importOpen && + !settings && + !palette + ) { + event.preventDefault(); + void run(); + } + }; + window.addEventListener("keydown", handler); + return () => window.removeEventListener("keydown", handler); + }, [run, view, importOpen, settings, palette]); + + function loadExample(index: number) { + setScript(examples[index].code); + setResult(null); + setError(""); + setElapsed(null); + setView("playground"); + } + function exportScript() { + const url = URL.createObjectURL( + new Blob([script], { type: "text/plain" }), + ); + const anchor = document.createElement("a"); + anchor.href = url; + anchor.download = "my-tag.tagscript"; + anchor.click(); + setTimeout(() => URL.revokeObjectURL(url), 1000); + } + async function importTag() { + if (importing) return; + setImporting(true); + setImportError(""); + try { + setScript(await importCarlTag(importValue)); + setResult(null); + setError(""); + setElapsed(null); + setImportOpen(false); + setView("playground"); + setImportValue(""); + } catch (err) { + setImportError( + err instanceof TypeError + ? "Carl import could not be reached. Paste your script or import a file instead." + : err instanceof Error + ? err.message + : "Import failed.", + ); + } finally { + setImporting(false); + } + } + const commands = [ + { value: "playground", label: "Open playground", icon: Braces }, + { value: "examples", label: "Explore examples", icon: FileCode2 }, + { value: "guide", label: "Read the guide", icon: BookOpen }, + { + value: "components", + label: "Browse Fluid components", + icon: Grid2X2, + }, + { value: "settings", label: "Workspace settings", icon: Settings2 }, + { value: "import", label: "Import a Carl tag", icon: Upload }, + ]; + + return ( + <> + + Skip to workspace + + + + setView("playground")} + className="flex items-center gap-3" + > +
+ +
+
+ bTagScript + + PLAYGROUND + +
+
+
+ + + + Workspace + + {( + [ + { + id: "playground", + label: "Playground", + icon: Braces, + }, + { + id: "examples", + label: "Examples", + icon: FileCode2, + }, + { + id: "guide", + label: "Quick guide", + icon: BookOpen, + }, + { + id: "components", + label: "Components", + icon: Grid2X2, + }, + ] as const + ).map((item) => ( + + setView(item.id)} + > + {item.label} + + + ))} + + + + + Start with an idea + + + {examples.slice(0, 3).map((example, index) => ( + + loadExample(index)} + > + + {String(index + 1).padStart(2, "0")} + + {example.name} + + + ))} + + + + +
+ +

+ Room to experiment. +

+

+ Small ideas turn into useful tags. +
+ Make something yours. +

+
+ + + + View on GitHub + + +
+
+
+
+
+ + + Workspace + + / + + {view === "guide" ? "Quick guide" : view} + +
+
+ + A space for your tags + + + + +
+
+
+
+
+

+ {view === "playground" + ? "Write. Run. Refine." + : "Explore the possibilities"} +

+

+ {view === "playground" + ? "Good tags start here." + : view === "examples" + ? "A place to begin." + : view === "guide" + ? "Meet your playground." + : "Fluid, by design."} +

+

+ {view === "playground" + ? "Give your ideas a little room to run." + : view === "components" + ? "Every component in the current Fluid Functionalism registry, ready to try." + : "A little inspiration for whatever you’re building next."} +

+
+ {view === "playground" && ( +
+ + +
+ )} +
+ {notice && ( +
+ {notice} + +
+ )} + {view === "playground" && ( + <> +
+
+
+
+
+
+ +

+ my-tag.tagscript +

+
+ + + + + } + /> + + + + fileInput.current?.click() + } + /> + { + setScript(""); + setResult(null); + setError(""); + setElapsed( + null, + ); + }} + /> + + +
+
+ + TagScript + + + Your next good idea ↓ + +
+ +
+ + {script.split("\n").length}{" "} + lines · {script.length}{" "} + characters + + + + {storage === "off" + ? "Autosave off" + : storage === "local" + ? "Saved in browser" + : "Saved for session"} + +
+
+ + setResult((current) => + current + ? section === "actions" + ? { + ...current, + actions: {}, + } + : { + ...current, + extras: { + debug: {}, + }, + } + : null, + ) + } + /> +
+
+
+ +

+ A blank page isn’t always the + best starting point. +

+
+ +
+
+ +
+
+ + Made for the little things that make your + server yours. + + + Powered by bTagScript · Crafted with Fluid + Functionalism + +
+ + )} + {view === "examples" && ( + + {examples.map((example, index) => ( + loadExample(index)} + > + + + {example.category} + + + {example.name} + + + {example.description} + + + +
+                                            {example.code}
+                                        
+ + Open in playground{" "} + + +
+
+ ))} +
+ )} + {view === "guide" && ( +
+
+ +

+ A safe place to test your ideas. +

+

+ Write a tag, give it some context, and run + it. The playground sends your script and + seeds to the bTagScript engine. It shows the + response, actions, and variables without + executing Discord commands. +

+
+ + {[ + [ + "seeds", + "What are seeds?", + "Seeds provide the user, channel, target, and arguments that a Discord bot would normally supply. Edit them in Set the scene. Unless enabled separately, target uses the same values as user.", + ], + [ + "save", + "Where does my script go?", + "Scripts autosave in this browser by default. Workspace settings lets you choose session storage or turn autosave off. Export a script from the editor menu for a portable copy. Running a tag sends its text and seeds to the external TagScript engine.", + ], + [ + "import", + "How do I import a tag?", + "Use Import tag with a Carl tag ID or link, or import a local .txt or .tagscript file. Carl imports depend on the existing external proxy. If it is unavailable, paste the script or use a file.", + ], + [ + "run", + "What happens when I run a tag?", + "The response appears in Output. Actions shows what the bot would do, and Debug lists the variables returned by the engine. Run with the button or Command/Control + Enter. Network and engine errors appear beside the editor.", + ], + ].map(([id, title, text], index) => ( + + + {title} + + +

+ {text} +

+
+
+ ))} +
+ +
+ )} + {view === "components" && } +
+
+ { + const file = event.target.files?.[0]; + if (!file) return; + try { + setScript(await file.text()); + setResult(null); + setError(""); + setElapsed(null); + setView("playground"); + setNotice(`Imported ${file.name}`); + } catch { + setNotice("This file could not be read."); + } + event.target.value = ""; + }} + /> + + + + Bring your tag along. + + Import an existing Carl tag by ID or public link. + + +
+ + { + if (event.key === "Enter") void importTag(); + }} + /> + + {importError && ( +

+ {importError} +

+ )} + +

+ Uses the existing Carl import service. You can also + import a local file from the editor menu. +

+
+
+
+ + + + Make yourself at home. + + Your workspace, your way. + + +
+
+

Save your work

+ + setStorage(value as StorageMode) + } + > + + + + +
+
+ + +
+ setFontSize(Number(value))} + showValue + formatValue={(value) => `${value}px`} + /> + + +
+
+
+ + { + if (item.value === "settings") setSettings(true); + else if (item.value === "import") setImportOpen(true); + else setView(item.value as View); + }} + > + + + + No matching commands. + + + + + + + ); +} diff --git a/src/components/results-panel.tsx b/src/components/results-panel.tsx new file mode 100644 index 0000000..3ffea32 --- /dev/null +++ b/src/components/results-panel.tsx @@ -0,0 +1,265 @@ +"use client"; + +import { useState } from "react"; +import { Braces, Check, Copy, Terminal, Trash2 } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/ui/badge"; +import { Tabs, TabsList, TabItem, TabPanel } from "@/components/ui/tabs"; +import { + Table, + TableHeader, + TableBody, + TableRow, + TableHead, + TableCell, +} from "@/components/ui/table"; +import { ChatMessage } from "@/components/ui/chat-message"; +import { ThinkingIndicator } from "@/components/ui/thinking-indicator"; +import { + ThinkingSteps, + ThinkingStepsHeader, + ThinkingStepsContent, + ThinkingStep, +} from "@/components/ui/thinking-steps"; +import { displayValue, type TagResult } from "@/lib/tagscript"; + +export function ResultsPanel({ + result, + busy, + error, + elapsed, + clear, +}: { + result: TagResult | null; + busy: boolean; + error: string; + elapsed: number | null; + clear: (section: "actions" | "debug") => void; +}) { + const [copied, setCopied] = useState(false); + const [copyError, setCopyError] = useState(""); + async function copy() { + try { + await navigator.clipboard.writeText(result?.body ?? ""); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + } catch { + setCopyError( + "Copy is unavailable. Select the output text to copy it manually.", + ); + } + } + const actions = Object.entries(result?.actions ?? {}); + const debug = Object.entries(result?.extras.debug ?? {}).filter( + ([key]) => !["user", "target", "channel", "args"].includes(key), + ); + return ( +
+
+
+ +

Output

+
+ + {busy + ? "Running" + : error + ? "Failed" + : result + ? "Complete" + : "Ready"} + +
+ +
+ + + + + +
+
+ {busy && ( +
+ +

+ Sending your script to the TagScript engine… +

+
+ )} + {error && ( +

+ {error} +

+ )} + + {result ? ( +
+
+
+ b. +
+ + TagScript + + BOT +
+ +
+                                        {result.body ||
+                                            "This tag returned an empty response."}
+                                    
+
+
+ ) : ( + !busy && + !error && ( +
+
+ +
+

+ A little code. A little possibility. +

+

+ Run your tag to see what it says. +
+ Your response will appear right here. +

+
+ ) + )} +
+ {( + [ + ["actions", actions], + ["debug", debug], + ] as const + ).map(([name, rows]) => ( + + {name === "actions" && + result?.actions.blacklist && + result?.actions.requires ? ( +

+ A tag cannot use both blacklist and require + blocks. +

+ ) : null} + {rows.length ? ( + <> +
+ + + + + {name === "actions" + ? "Action" + : "Variable"} + + Value + + + + {rows.map(([key, value]) => ( + + + {key} + + +
+                                                                {displayValue(
+                                                                    value,
+                                                                )}
+                                                            
+
+
+ ))} +
+
+
+ + + ) : ( +

+ {result + ? `No ${name === "actions" ? "actions" : "variables"} returned by this tag.` + : `Run a tag to inspect its ${name}.`} +

+ )} +
+ ))} +
+
+ {result && ( +
+ + + Execution details + + + + + + +
+ )} +
+ + {elapsed === null + ? "Waiting for your first run" + : `Completed in ${elapsed} ms`} + + +
+ {copyError && ( +

+ {copyError} +

+ )} +
+ ); +} diff --git a/src/components/script-editor.tsx b/src/components/script-editor.tsx new file mode 100644 index 0000000..21bd0ca --- /dev/null +++ b/src/components/script-editor.tsx @@ -0,0 +1,67 @@ +"use client"; + +import CodeMirror from "@uiw/react-codemirror"; +import { + StreamLanguage, + HighlightStyle, + syntaxHighlighting, +} from "@codemirror/language"; +import { EditorView } from "@codemirror/view"; +import { tags } from "@lezer/highlight"; + +const language = StreamLanguage.define({ + token(stream) { + if (stream.match(/\{=\(comment\):[^}]*\}/)) return "comment"; + if (stream.match(/[{}]/)) return "bracket"; + if (stream.match(/[():|=+*/<>!-]/)) return "operator"; + if (stream.match(/\d+(\.\d+)?/)) return "number"; + if ( + stream.match( + /\b(?:args|user|target|channel|if|math|range|random|let|assign|upper|lower)\b/, + ) + ) + return "keyword"; + stream.next(); + return null; + }, +}); +const extensions = [ + language, + EditorView.lineWrapping, + syntaxHighlighting( + HighlightStyle.define([ + { tag: tags.keyword, color: "#78935b" }, + { tag: tags.bracket, color: "#bc785d" }, + { tag: tags.operator, color: "#b0926a" }, + { tag: tags.number, color: "#9980b3" }, + { tag: tags.comment, color: "#858d7d", fontStyle: "italic" }, + ]), + ), +]; + +export function ScriptEditor({ + value, + onChange, + fontSize, +}: { + value: string; + onChange: (value: string) => void; + fontSize: number; +}) { + return ( +
+ +
+ ); +} diff --git a/src/components/seed-panel.tsx b/src/components/seed-panel.tsx new file mode 100644 index 0000000..94150d0 --- /dev/null +++ b/src/components/seed-panel.tsx @@ -0,0 +1,186 @@ +"use client"; + +import { useState } from "react"; +import { Shuffle } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { InputGroup, InputField } from "@/components/ui/input-group"; +import { + TabsSubtle, + TabsSubtleItem, + TabsSubtlePanel, +} from "@/components/ui/tabs-subtle"; +import { Switch } from "@/components/ui/switch"; +import { ColorPickerPopover } from "@/components/ui/color-picker"; +import { randomPerson, randomChannel, type Seeds } from "@/lib/tagscript"; + +const labels: Record = { + name: "Display name", + username: "Username", + id: "ID", + createdAt: "Created at (Unix)", + joinedAt: "Joined at (Unix)", + mention: "Mention", + color: "Role color", + roleIDs: "Role IDs", + nsfw: "NSFW", + topic: "Topic", + slowmode: "Slowmode (seconds)", +}; +export function SeedPanel({ + seeds, + setSeeds, + useTarget, + setUseTarget, +}: { + seeds: Seeds; + setSeeds: (value: Seeds) => void; + useTarget: boolean; + setUseTarget: (value: boolean) => void; +}) { + const [tab, setTab] = useState(0); + const groups = ["user", "channel", "target"] as const; + const group = groups[tab]; + const update = (key: string, value: string) => + setSeeds({ ...seeds, [group]: { ...seeds[group], [key]: value } }); + return ( +
+
+
+

Set the scene

+

+ The people and place behind your tag. +

+
+ +
+
+ + setSeeds({ ...seeds, args })} + /> + +
+
+ + {["User", "Channel", "Target"].map((label, index) => ( + + ))} + +
+ {groups.map((name, index) => ( + +
+ {name === "target" && ( + setUseTarget(!useTarget)} + /> + )} + {name === "target" && !useTarget ? ( +

+ Your target follows the user. Enable a separate + target to customize it. +

+ ) : ( + <> + + {Object.entries(seeds[name]) + .filter( + ([key]) => + key !== "color" && + key !== "nsfw", + ) + .map(([key, value], i) => ( + + update(key, next) + } + /> + ))} + + {name !== "channel" && ( + + update( + "color", + parsed.hex.replace("#", ""), + ) + } + triggerLabel="Role color" + hideEyedropper + /> + )} + {name === "channel" && ( + + update( + "nsfw", + seeds.channel.nsfw === "true" + ? "false" + : "true", + ) + } + /> + )} + + + )} +
+
+ ))} +
+ ); +} diff --git a/src/lib/tagscript.ts b/src/lib/tagscript.ts new file mode 100644 index 0000000..929a5ce --- /dev/null +++ b/src/lib/tagscript.ts @@ -0,0 +1,242 @@ +export const PROCESS_URL = "https://leg3ndary.pythonanywhere.com/v2/process/"; +export const CARL_URL = + "https://mighty-sea-55702.herokuapp.com/https://carl.gg/api/v1/tags/"; + +export interface PersonSeed { + name: string; + username: string; + id: string; + createdAt: string; + joinedAt: string; + mention: string; + color: string; + roleIDs: string; +} +export interface Seeds { + args: string; + user: PersonSeed; + target: PersonSeed; + channel: { + name: string; + id: string; + nsfw: string; + mention: string; + topic: string; + slowmode: string; + }; +} +export interface TagResult { + body: string; + actions: Record; + extras: { debug: Record }; + uses?: number[]; +} + +const person: PersonSeed = { + name: "Alex", + username: "alex", + id: "123456789012345678", + createdAt: "1609459200", + joinedAt: "1640995200", + mention: "<@123456789012345678>", + color: "7C9265", + roleIDs: "987654321012345678", +}; +export const initialSeeds: Seeds = { + args: "Hello world", + user: person, + target: { ...person, name: "Sam", username: "sam" }, + channel: { + name: "general", + id: "234567890123456789", + nsfw: "false", + mention: "<#234567890123456789>", + topic: "A place to try things out.", + slowmode: "0", + }, +}; +export const examples = [ + { + name: "A warm welcome", + description: "Make your first tag feel personal.", + category: "The basics", + code: "{=(greeting):Hello, {user(name)}!}\n{greeting}\n\nWelcome to {channel(name)}.\nYou said: {args}", + }, + { + name: "A little logic", + description: "Give your tag a different response.", + category: "Conditions", + code: "{if({args}==Hello):Hey, {user(name)}!|Try sending Hello as your argument.}", + }, + { + name: "Roll the dice", + description: "Leave the result up to chance.", + category: "Randomness", + code: "{user(name)} rolled a {range:1-6}!", + }, + { + name: "Do the math", + description: "Turn an expression into an answer.", + category: "Math", + code: "{=(price):24}\n{=(quantity):3}\nYour total is {math:{price}*{quantity}}.", + }, +]; + +export function encodeTagScript(value: string) { + return value + .replace(/\\/g, "Ꜳ") + .replace(/\//g, "₩") + .replace(//g, "ꜵ") + .replace(/\./g, "Ꜷ"); +} +export function decodeTagScript(value: string) { + return value + .replace(/Ꜳ/g, "\\") + .replace(/ꜳ/g, "<") + .replace(/₩/g, "/") + .replace(/ꜵ/g, ">") + .replace(/Ꜷ/g, "."); +} +export function createRequest( + script: string, + seeds: Seeds, + useTarget: boolean, +) { + return new URLSearchParams({ + tagscript: script, + seeds: encodeTagScript( + JSON.stringify({ + ...seeds, + target: useTarget ? seeds.target : seeds.user, + }), + ), + }); +} +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null && !Array.isArray(value); +} +export function parseResult(data: unknown): TagResult { + if ( + !isRecord(data) || + typeof data.body !== "string" || + !isRecord(data.actions) + ) { + throw new Error( + "The engine returned an unexpected response. Please try again.", + ); + } + const debug = + isRecord(data.extras) && isRecord(data.extras.debug) + ? data.extras.debug + : {}; + return { + body: decodeTagScript(data.body), + actions: data.actions, + extras: { debug }, + uses: Array.isArray(data.uses) + ? data.uses.filter( + (value): value is number => typeof value === "number", + ) + : undefined, + }; +} +export async function runTag( + script: string, + seeds: Seeds, + useTarget: boolean, +): Promise { + const response = await fetch(PROCESS_URL, { + method: "POST", + body: createRequest(script, seeds, useTarget), + signal: AbortSignal.timeout(30000), + }); + if (!response.ok) + throw new Error( + `The TagScript engine returned ${response.status}. Please try again later.`, + ); + return parseResult(await response.json()); +} +export function parseCarlId(value: string) { + const trimmed = value.trim(); + if (/^[1-9]\d*$/.test(trimmed)) return trimmed; + try { + const url = new URL(trimmed); + if ( + url.protocol !== "https:" || + !["carl.gg", "www.carl.gg"].includes(url.hostname) + ) + return null; + return ( + url.pathname.match(/^\/(?:t|tag|tags)\/([1-9]\d*)\/?$/)?.[1] ?? null + ); + } catch { + return null; + } +} +export async function importCarlTag(value: string) { + const id = parseCarlId(value); + if (!id) + throw new Error( + "Enter a numeric tag ID or a https://carl.gg/t/… link.", + ); + const response = await fetch(`${CARL_URL}${id}`, { + signal: AbortSignal.timeout(15000), + }); + if (!response.ok) + throw new Error( + "Carl import is unavailable. You can still paste your tag into the editor or import a file.", + ); + const data: unknown = await response.json(); + if (!isRecord(data) || typeof data.content !== "string") + throw new Error("That tag did not contain a script."); + return data.content; +} +export function randomPerson(): PersonSeed { + const name = ["Alex", "Sam", "River", "Charlie", "Morgan"][ + Math.floor(Math.random() * 5) + ]; + const id = randomId(); + const createdAt = + Math.floor(Date.now() / 1000) - + 86400 * (365 + Math.floor(Math.random() * 365)); + return { + name, + username: name.toLowerCase(), + id, + mention: `<@${id}>`, + createdAt: String(createdAt), + joinedAt: String(createdAt + 86400 * 100), + color: Math.floor(Math.random() * 0xffffff) + .toString(16) + .padStart(6, "0") + .toUpperCase(), + roleIDs: randomId(), + }; +} +export function randomId() { + return ( + "1" + + Array.from({ length: 17 }, () => Math.floor(Math.random() * 10)).join( + "", + ) + ); +} +export function randomChannel(): Seeds["channel"] { + const id = randomId(); + return { + name: ["general", "bot-testing", "tagscript-chat"][ + Math.floor(Math.random() * 3) + ], + id, + mention: `<#${id}>`, + nsfw: "false", + topic: "Testing something new.", + slowmode: "0", + }; +} +export function displayValue(value: unknown) { + return typeof value === "string" + ? value + : (JSON.stringify(value, null, 2) ?? ""); +} From 2417e8c0ec7315ee344ab73d20a38834071abd61 Mon Sep 17 00:00:00 2001 From: Ben Zhou Date: Tue, 29 Sep 2026 14:41:40 -0400 Subject: [PATCH 3/5] Add Bun verification and GitHub Pages deployment workflow --- .github/workflows/pages.yml | 67 +++++++++++++++++ README.md | 83 +++++++++++---------- docs/fluid-components.md | 23 ++++++ e2e/playground.spec.ts | 145 ++++++++++++++++++++++++++++++++++++ playwright.config.ts | 17 +++++ public/.nojekyll | 0 scripts/preview.ts | 37 +++++++++ tests/tagscript.test.ts | 73 ++++++++++++++++++ 8 files changed, 407 insertions(+), 38 deletions(-) create mode 100644 .github/workflows/pages.yml create mode 100644 docs/fluid-components.md create mode 100644 e2e/playground.spec.ts create mode 100644 playwright.config.ts create mode 100644 public/.nojekyll create mode 100644 scripts/preview.ts create mode 100644 tests/tagscript.test.ts diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..b4fac29 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,67 @@ +name: Build and deploy GitHub Pages + +on: + push: + branches: [master, feat/fluid-next-playground] + pull_request: + branches: [master] + workflow_dispatch: + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + pages: read + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 + with: + node-version: 24 + - uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.4.2 + - run: bun install --frozen-lockfile + - run: bun run lint + - run: bun run typecheck + - run: bun run test + - name: Configure Pages + id: pages + if: github.ref == 'refs/heads/master' + uses: actions/configure-pages@v6 + - name: Set the static export path + env: + PAGES_BASE_PATH: ${{ steps.pages.outputs.base_path }} + run: | + if [ "$GITHUB_REF" = "refs/heads/master" ]; then + echo "NEXT_PUBLIC_BASE_PATH=$PAGES_BASE_PATH" >> "$GITHUB_ENV" + else + echo "NEXT_PUBLIC_BASE_PATH=/${GITHUB_REPOSITORY#*/}" >> "$GITHUB_ENV" + fi + - run: bun run build + - run: bunx playwright install --with-deps chromium + - run: bun run test:e2e + - uses: actions/upload-pages-artifact@v5 + with: + path: out + + deploy: + if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' + needs: build + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + concurrency: + group: pages + cancel-in-progress: false + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy + id: deployment + uses: actions/deploy-pages@v5 diff --git a/README.md b/README.md index 39c325b..6ffdf72 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,70 @@ # bTagScript Playground -![bTagScript Playground](https://i.imgur.com/Mth9WKC.png) +A TagScript workspace rebuilt with Next.js App Router, TypeScript, Tailwind CSS, and [Fluid Functionalism](https://www.fluidfunctionalism.com/). The app exports as static HTML/CSS/JavaScript for GitHub Pages. -Welcome to the bTagScript Playground! This is the ultimate online tool for testing and experimenting with TagScript before implementing it in your projects. Avoid the hassle of creating and running tags repeatedly by using our user-friendly platform designed for seamless TagScript testing. +## Run locally -## What is TagScript? +Use **Bun 1.4.2** and **Node.js 24 LTS**. Bun manages dependencies and runs scripts; Next.js also requires Node.js. -TagScript is a templating language primarily used in Discord bots to create dynamic responses based on user input and conditions. It allows you to define complex logic and handle various scenarios effortlessly within your bot's commands. +```sh +bun install --frozen-lockfile +bun run dev +``` -## Features +Open http://localhost:3000. -- **Real-Time Testing**: Instantly see the results of your TagScript as you type. No more waiting to run tags on your bot. -- **Comprehensive Toolset**: Access a wide range of TagScript functions and operators to simulate different conditions and responses. -- **User-Friendly Interface**: An intuitive and easy-to-navigate interface that makes TagScript testing simple and efficient. -- **Save and Load Scripts**: Save your scripts for future use and load them whenever needed, ensuring you don't lose your work. -- **Syntax Highlighting**: Clearly see different elements of your TagScript with our syntax highlighting feature, making it easier to debug and understand your code. +## Build and preview GitHub Pages -## Getting Started +For this repository's project site: -### Accessing the Playground +```sh +NEXT_PUBLIC_BASE_PATH=/bTagScriptPlayground bun run build +NEXT_PUBLIC_BASE_PATH=/bTagScriptPlayground bun run preview +``` -You can start using the bTagScript Playground by visiting the following link: [bTagScript Playground](https://benz206.github.io/bTagScriptPlayground) +Open http://127.0.0.1:3000/bTagScriptPlayground/. The production files are in `out/`, including `.nojekyll`. The preview server serves these files directly, so missing assets and incorrect paths aren't hidden by an application fallback. -### Using the Playground +For a custom domain or an `owner.github.io` repository hosted at the domain root, omit `NEXT_PUBLIC_BASE_PATH` in both commands. Rebuild when changing the base path; Next.js embeds it into the client assets. -1. **Input Area**: Write your TagScript code in the input area. This is where you define the logic and structure of your tags. -2. **Process Script**: Click the "Process" button to execute your TagScript and see the output in real time. -3. **Output Area**: View the results of your TagScript in the output area. This section displays the responses generated by your script based on the input and conditions you defined. -4. **Import Script**: Use the "Import" button to load previously saved TagScript code from Carl Bot's tag system into the input area. +### Deploy -### Example Usage +1. In GitHub **Settings → Pages → Build and deployment**, select **GitHub Actions** as the source. +2. Merge the feature branch into `master` and push. +3. The **Build and deploy GitHub Pages** workflow installs the frozen Bun lockfile, runs lint/type/unit checks, builds the static export, runs Chromium tests, and uploads `out/`. +4. Only a successful build on `master` deploys. Pull requests and pushes to `feat/fluid-next-playground` validate the site without replacing the deployed site. After the workflow exists on `master`, it can also be run manually from Actions. -Here’s a simple example to demonstrate how to use the bTagScript Playground: +The deployment reads the real Pages base path from `actions/configure-pages`, supporting project sites and configured custom domains. The expected project URL is https://leg3ndary.github.io/bTagScriptPlayground/. Environment protection rules may require a GitHub approval before the deployment job runs. -1. **Write a Script**: - ```tagscript - {if(Hello=={args}):Hi there!|I don't understand.} - ``` - This script checks if the user input is "Hello" and responds with "Hi there!". Otherwise, it responds with "I don't understand." +## What's included -2. **Run the Script**: Click "Run Script" to see the output. - - Input: `Hello` - - Output: `Hi there!` +- CodeMirror 6 editor with TagScript highlighting, line numbers, and Command/Control + Enter to run. +- Editable arguments, channel, user, and optional target seeds, with randomization and role color selection. +- Response preview, action inspection, structured debug variables, execution duration, and copy controls. +- Browser or session autosave, migration of the old `tagscript` storage key, local file import/export, and Carl tag imports. +- Example tags, a quick guide, light/dark themes, editor font-size settings, and a command palette. +- All **30 current Fluid component types**, with working examples in the Components gallery. See [the source inventory](docs/fluid-components.md). - - Input: `Goodbye` - - Output: `I don't understand.` +## External services -## Contributing +Processing uses the existing `https://leg3ndary.pythonanywhere.com/v2/process/` endpoint and its form/seed encoding. Clicking **Run tag** sends the script and seed context to that service. The playground displays Discord actions; it does not execute them. -We welcome contributions from the community to improve the bTagScript Playground. If you have suggestions, bug reports, or want to contribute code, please visit our GitHub repository: [bTagScript Playground GitHub](https://github.com/benz206/bTagScriptPlayground) +Carl imports use the project's existing Heroku proxy for `carl.gg`. Both services must be online and permit requests from the deployed origin. A static GitHub Pages site cannot provide a server-side CORS proxy. Failures display an error, preserve the editor text, and re-enable the controls. If Carl import is unavailable, paste the script or import a `.txt`/`.tagscript` file. -## Support +## Verification -If you encounter any issues or have questions about using the bTagScript Playground, feel free to reach out for support. You can open an issue on our GitHub repository. +```sh +bun run lint +bun run typecheck +bun run test +NEXT_PUBLIC_BASE_PATH=/bTagScriptPlayground bun run build +bunx playwright install chromium +NEXT_PUBLIC_BASE_PATH=/bTagScriptPlayground bun run test:e2e +``` -## License +Unit tests cover encoding, seed payloads, target selection, result validation, Carl URL parsing, and randomized seed consistency. Browser tests run against the static export and mock external APIs to verify successful runs, network failures, duplicate request prevention, saved scripts, imports/exports, the full gallery, and mobile layout without depending on external service uptime. -The bTagScript Playground is open-source and available under the MIT License. Feel free to use, modify, and distribute it as per the terms of the license. +The current stack is Next.js 16.3.7, React 19.3, Tailwind 4.3, and TypeScript 7.0.2. TypeScript 7's native compiler is installed as `@typescript/native`; the `typescript` alias provides Microsoft's TypeScript 6 compatibility API for Next.js/ESLint tooling. ESLint 10 uses its official compatibility adapter for the Next.js React plugin. Exact resolved dependencies are recorded in `bun.lock`. ---- +## License -Thank you for using the bTagScript Playground! We hope it makes your TagScript development process easier and more efficient. Happy coding! +The original project [MIT license](LICENSE) is retained. Fluid component provenance and update instructions are in [docs/fluid-components.md](docs/fluid-components.md). diff --git a/docs/fluid-components.md b/docs/fluid-components.md new file mode 100644 index 0000000..6fc6d39 --- /dev/null +++ b/docs/fluid-components.md @@ -0,0 +1,23 @@ +# Fluid Functionalism sources + +Installed from the official [Fluid Functionalism registry](https://www.fluidfunctionalism.com/r/registry.json) on 2026-09-29 using the current shadcn CLI. The project uses the Radix implementation of each component; Base UI alternatives are duplicate implementations, not additional component types. + +All 30 registry UI component types are rendered in the Components gallery: + +Accordion, AskUserQuestions, Badge, Button, Card, CarouselDots, ChatMessage, CheckboxGroup, ColorPicker, Combobox, CommandMenu, Dialog, Dropdown, FileThumbnail, InputCopy, InputGroup, InputMessage, MobileDrawer, RadioGroup, ScrollArea, Select, Sidebar, Slider, Switch, Table, Tabs, TabsSubtle, ThinkingIndicator, ThinkingSteps, Tooltip. + +Shared hooks, motion utilities, contexts, elevation primitives, and theme tokens are also installed. Registry blocks are compositions of these components and are not additional primitives. + +## Updating + +The source lives in `src/components/ui`, `src/components/fluid-hover-highlight.tsx`, the shared `src/hooks` files, and the registry utility files in `src/lib`. Application code imports these sources directly. The registry doesn't publish semantic component versions; `bun.lock` records their package dependencies, and Git records the installed component snapshot. + +```sh +bunx --bun shadcn@latest add @fluid/button --overwrite +``` + +Replace `button` with the desired registry name. Review the diff before committing. `components.json` contains the registry URL and installation aliases. Use the same Radix flavor when updating. + +The registry source is kept as published and excluded from app lint rules. It remains part of strict TypeScript checks and the production build. The gallery browser test verifies that all 30 component types render without uncaught browser errors. + +The Inter variable font is bundled locally through `@fontsource-variable/inter`, including the weight axis used by Fluid's hover animations. No font CDN is required at build or runtime. diff --git a/e2e/playground.spec.ts b/e2e/playground.spec.ts new file mode 100644 index 0000000..f81af19 --- /dev/null +++ b/e2e/playground.spec.ts @@ -0,0 +1,145 @@ +import { expect, test } from "@playwright/test"; + +test("processes a tag with the original API contract and inspects the result", async ({ + page, +}) => { + let requests = 0; + await page.route("**/v2/process/", async (route) => { + requests++; + const body = new URLSearchParams(route.request().postData()!); + expect(body.get("tagscript")).toContain("greeting"); + expect(body.get("seeds")).toContain("Alex"); + await route.fulfill({ + json: { + body: "Hello, Alex!", + actions: { commands: ["ping"] }, + extras: { debug: { greeting: "Hello, Alex!" } }, + }, + }); + }); + await page.goto("./"); + await page.getByRole("button", { name: /^Run tag/ }).click(); + await expect(page.getByText("Hello, Alex!", { exact: true })).toBeVisible(); + await page.getByRole("tab", { name: "Actions · 1" }).click(); + await expect(page.getByRole("cell", { name: "commands" })).toBeVisible(); + await page.getByRole("tab", { name: "Debug · 1" }).click(); + await expect(page.getByRole("cell", { name: "greeting" })).toBeVisible(); + expect(requests).toBe(1); +}); + +test("shows network failures and prevents duplicate in-flight requests", async ({ + page, +}) => { + let requests = 0; + await page.route("**/v2/process/", async (route) => { + requests++; + await new Promise((resolve) => setTimeout(resolve, 500)); + await route.abort(); + }); + await page.goto("./"); + const run = page.getByRole("button", { name: /^Run tag/ }); + await run.click(); + await expect(run).toBeDisabled(); + await page.keyboard.press("Control+Enter"); + await expect( + page.getByRole("alert").filter({ hasText: "could not be reached" }), + ).toBeVisible(); + await expect(run).toBeEnabled(); + expect(requests).toBe(1); +}); + +test("restores scripts and switches to session storage", async ({ page }) => { + await page.goto("./"); + await page.evaluate(() => + localStorage.setItem("tagscript", "My saved tag"), + ); + await page.reload(); + await expect(page.locator(".cm-content")).toContainText("My saved tag"); + await page + .getByRole("button", { name: "Workspace settings", exact: true }) + .first() + .click(); + await page.getByRole("radio", { name: "For this session" }).click(); + await page.keyboard.press("Escape"); + await expect + .poll(() => page.evaluate(() => sessionStorage.getItem("tagscript"))) + .toBe("My saved tag"); + expect( + await page.evaluate(() => localStorage.getItem("tagscript")), + ).toBeNull(); + await page.reload(); + await expect(page.locator(".cm-content")).toContainText("My saved tag"); +}); + +test("validates Carl imports, loads a valid import, and exports files", async ({ + page, +}) => { + await page.route("**/https://carl.gg/api/v1/tags/123", (route) => + route.fulfill({ json: { content: "Hello from Carl" } }), + ); + await page.goto("./"); + await page.getByRole("button", { name: "Import tag", exact: true }).click(); + const dialog = page.getByRole("dialog"); + await dialog.getByLabel("Carl tag ID or URL").fill("invalid"); + await dialog + .getByRole("button", { name: "Import tag", exact: true }) + .click(); + await expect(dialog.getByRole("alert")).toContainText( + "Enter a numeric tag ID", + ); + await dialog.getByLabel("Carl tag ID or URL").fill("123"); + await dialog + .getByRole("button", { name: "Import tag", exact: true }) + .click(); + await expect(page.locator(".cm-content")).toContainText("Hello from Carl"); + await page.getByRole("button", { name: "Script options" }).click(); + const download = page.waitForEvent("download"); + await page.getByRole("menuitem", { name: "Export script" }).click(); + expect((await download).suggestedFilename()).toBe("my-tag.tagscript"); +}); + +test("renders all 30 Fluid components without browser errors", async ({ + page, +}) => { + const errors: string[] = []; + page.on("pageerror", (error) => errors.push(error.message)); + await page.goto("./"); + await page.getByRole("button", { name: "Components", exact: true }).click(); + await expect(page.locator("[data-component]")).toHaveCount(30); + await page.getByRole("button", { name: "Try me", exact: true }).click(); + await expect( + page.getByRole("button", { name: "Try me · 1" }), + ).toBeVisible(); + await page.getByRole("button", { name: "Open a little space" }).click(); + await expect(page.getByRole("dialog")).toBeVisible(); + await page.keyboard.press("Escape"); + await page + .getByRole("textbox", { name: "Gallery message" }) + .fill("A tiny idea"); + await page.getByRole("textbox", { name: "Gallery message" }).press("Enter"); + await expect(page.getByText("A tiny idea", { exact: true })).toBeVisible(); + expect(errors).toEqual([]); +}); + +test("keeps the mobile workspace within the viewport", async ({ page }) => { + await page.setViewportSize({ width: 390, height: 844 }); + await page.goto("./"); + await expect( + page.getByRole("heading", { name: "Good tags start here." }), + ).toBeVisible(); + expect( + await page.evaluate(() => document.documentElement.scrollWidth), + ).toBeLessThanOrEqual(390); + await page.getByRole("button", { name: /toggle sidebar/i }).click(); + await expect( + page.getByRole("button", { name: "Components", exact: true }), + ).toBeVisible(); + await page.getByRole("button", { name: "Components", exact: true }).click(); + await expect( + page.getByRole("heading", { name: "Fluid, by design." }), + ).toBeVisible(); + await expect(page.getByRole("dialog")).not.toBeVisible(); + expect( + await page.evaluate(() => document.documentElement.scrollWidth), + ).toBeLessThanOrEqual(390); +}); diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..9d9b9bd --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,17 @@ +import { defineConfig, devices } from "@playwright/test"; + +const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? ""; +export default defineConfig({ + testDir: "./e2e", + fullyParallel: true, + use: { + baseURL: `http://127.0.0.1:3000${basePath}/`, + trace: "retain-on-failure", + }, + projects: [{ name: "chromium", use: { ...devices["Desktop Chrome"] } }], + webServer: { + command: "bun run preview", + url: `http://127.0.0.1:3000${basePath}/`, + reuseExistingServer: !process.env.CI, + }, +}); diff --git a/public/.nojekyll b/public/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/scripts/preview.ts b/scripts/preview.ts new file mode 100644 index 0000000..f3047f9 --- /dev/null +++ b/scripts/preview.ts @@ -0,0 +1,37 @@ +import { resolve, sep } from "node:path"; + +const root = resolve("out"); +const basePath = (process.env.NEXT_PUBLIC_BASE_PATH ?? "").replace(/\/$/, ""); +const server = Bun.serve({ + hostname: "127.0.0.1", + port: Number(process.env.PORT ?? 3000), + async fetch(request) { + let pathname: string; + try { + pathname = decodeURIComponent(new URL(request.url).pathname); + } catch { + return new Response("Bad request", { status: 400 }); + } + if (basePath && pathname === basePath) + return Response.redirect( + `${new URL(request.url).origin}${basePath}/`, + 308, + ); + if (basePath && !pathname.startsWith(`${basePath}/`)) + return new Response("Not found", { status: 404 }); + const relative = pathname.slice(basePath.length).replace(/^\//, ""); + const path = resolve( + root, + relative.endsWith("/") || !relative + ? `${relative}index.html` + : relative, + ); + if (!path.startsWith(`${root}${sep}`)) + return new Response("Forbidden", { status: 403 }); + const file = Bun.file(path); + return (await file.exists()) + ? new Response(file) + : new Response("Not found", { status: 404 }); + }, +}); +console.log(`Static preview: http://127.0.0.1:${server.port}${basePath}/`); diff --git a/tests/tagscript.test.ts b/tests/tagscript.test.ts new file mode 100644 index 0000000..942b433 --- /dev/null +++ b/tests/tagscript.test.ts @@ -0,0 +1,73 @@ +import { describe, expect, test } from "bun:test"; +import { + createRequest, + decodeTagScript, + encodeTagScript, + initialSeeds, + parseCarlId, + parseResult, + randomPerson, +} from "../src/lib/tagscript"; + +describe("the existing TagScript API contract", () => { + test("encodes reserved characters and round-trips seed data", () => { + const value = "<@123> / path\\tag.txt"; + expect(encodeTagScript(value)).toBe("ꜳ@123ꜵ ₩ pathꜲtagꜶtxt"); + expect(decodeTagScript(encodeTagScript(value))).toBe(value); + }); + test("sends form data and mirrors user into target by default", () => { + const body = createRequest("Hello {user}", initialSeeds, false); + expect(body.get("tagscript")).toBe("Hello {user}"); + const seeds = JSON.parse(decodeTagScript(body.get("seeds")!)); + expect(seeds.target).toEqual(initialSeeds.user); + expect(seeds.channel).toEqual(initialSeeds.channel); + }); + test("uses a separate target when enabled", () => { + const body = createRequest("{target}", initialSeeds, true); + expect(JSON.parse(decodeTagScript(body.get("seeds")!)).target).toEqual( + initialSeeds.target, + ); + }); + test("decodes output and retains structured action/debug values", () => { + const result = parseResult({ + body: "Hello ꜳ@123ꜵꜶ", + actions: { embed: { title: "Hello" } }, + extras: { debug: { count: 3 } }, + }); + expect(result.body).toBe("Hello <@123>."); + expect(result.actions.embed).toEqual({ title: "Hello" }); + expect(result.extras.debug.count).toBe(3); + }); + test("rejects malformed engine responses instead of showing fake output", () => { + expect(() => parseResult({ error: "Unavailable" })).toThrow(); + expect(() => parseResult(null)).toThrow(); + expect(parseResult({ body: "", actions: {} }).extras.debug).toEqual({}); + }); +}); + +describe("Carl import", () => { + test("accepts IDs and public Carl tag URLs without losing integer precision", () => { + expect(parseCarlId("1234567890123456789")).toBe("1234567890123456789"); + expect(parseCarlId(" https://carl.gg/t/1479390/ ")).toBe("1479390"); + }); + test("rejects invalid links without silently loading an unrelated tag", () => { + for (const value of [ + "", + "0", + "-1", + "1.5", + "https://example.com/t/12", + "https://carl.gg/nope", + "javascript:alert(1)", + ]) + expect(parseCarlId(value)).toBeNull(); + }); +}); + +test("random seeds preserve string snowflakes, mentions, and timestamp order", () => { + const seed = randomPerson(); + expect(seed.id).toMatch(/^\d{18}$/); + expect(seed.mention).toBe(`<@${seed.id}>`); + expect(seed.color).toMatch(/^[\dA-F]{6}$/); + expect(Number(seed.joinedAt)).toBeGreaterThan(Number(seed.createdAt)); +}); From 036c305b3b7d68c62e4c5d1620658d3baea035c2 Mon Sep 17 00:00:00 2001 From: Ben Zhou Date: Tue, 29 Sep 2026 14:42:15 -0400 Subject: [PATCH 4/5] Point project links to the canonical GitHub repository --- README.md | 2 +- src/components/playground.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6ffdf72..c67b20e 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ For a custom domain or an `owner.github.io` repository hosted at the domain root 3. The **Build and deploy GitHub Pages** workflow installs the frozen Bun lockfile, runs lint/type/unit checks, builds the static export, runs Chromium tests, and uploads `out/`. 4. Only a successful build on `master` deploys. Pull requests and pushes to `feat/fluid-next-playground` validate the site without replacing the deployed site. After the workflow exists on `master`, it can also be run manually from Actions. -The deployment reads the real Pages base path from `actions/configure-pages`, supporting project sites and configured custom domains. The expected project URL is https://leg3ndary.github.io/bTagScriptPlayground/. Environment protection rules may require a GitHub approval before the deployment job runs. +The deployment reads the real Pages base path from `actions/configure-pages`, supporting project sites and configured custom domains. The expected project URL is https://benz206.github.io/bTagScriptPlayground/. Environment protection rules may require a GitHub approval before the deployment job runs. ## What's included diff --git a/src/components/playground.tsx b/src/components/playground.tsx index 9791104..324cf47 100644 --- a/src/components/playground.tsx +++ b/src/components/playground.tsx @@ -433,7 +433,7 @@ function Workspace() { Workspace settings Date: Tue, 29 Sep 2026 15:42:45 -0400 Subject: [PATCH 5/5] Add persistent dark mode and remove the component showcase --- README.md | 6 +- bun.lock | 6 - docs/fluid-components.md | 8 +- e2e/playground.spec.ts | 60 +- package.json | 4 +- src/app/globals.css | 2 + src/app/layout.tsx | 9 +- src/components/component-gallery.tsx | 737 --------- src/components/playground.tsx | 76 +- src/components/ui/ask-user-questions.tsx | 1886 ---------------------- src/components/ui/carousel-dots.tsx | 243 --- src/components/ui/checkbox-group.tsx | 380 ----- src/components/ui/combobox.tsx | 1526 ----------------- src/components/ui/input-message.tsx | 1385 ---------------- src/components/ui/mobile-drawer.tsx | 123 -- 15 files changed, 100 insertions(+), 6351 deletions(-) delete mode 100644 src/components/component-gallery.tsx delete mode 100644 src/components/ui/ask-user-questions.tsx delete mode 100644 src/components/ui/carousel-dots.tsx delete mode 100644 src/components/ui/checkbox-group.tsx delete mode 100644 src/components/ui/combobox.tsx delete mode 100644 src/components/ui/input-message.tsx delete mode 100644 src/components/ui/mobile-drawer.tsx diff --git a/README.md b/README.md index c67b20e..866a52f 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,8 @@ The deployment reads the real Pages base path from `actions/configure-pages`, su - Editable arguments, channel, user, and optional target seeds, with randomization and role color selection. - Response preview, action inspection, structured debug variables, execution duration, and copy controls. - Browser or session autosave, migration of the old `tagscript` storage key, local file import/export, and Carl tag imports. -- Example tags, a quick guide, light/dark themes, editor font-size settings, and a command palette. -- All **30 current Fluid component types**, with working examples in the Components gallery. See [the source inventory](docs/fluid-components.md). +- Example tags, a quick guide, a header light/dark toggle with a saved preference, editor font-size settings, and a command palette. +- Fluid Functionalism controls throughout the playground. See [component source notes](docs/fluid-components.md). ## External services @@ -61,7 +61,7 @@ bunx playwright install chromium NEXT_PUBLIC_BASE_PATH=/bTagScriptPlayground bun run test:e2e ``` -Unit tests cover encoding, seed payloads, target selection, result validation, Carl URL parsing, and randomized seed consistency. Browser tests run against the static export and mock external APIs to verify successful runs, network failures, duplicate request prevention, saved scripts, imports/exports, the full gallery, and mobile layout without depending on external service uptime. +Unit tests cover encoding, seed payloads, target selection, result validation, Carl URL parsing, and randomized seed consistency. Browser tests run against the static export and mock external APIs to verify successful runs, network failures, duplicate request prevention, saved scripts, imports/exports, theme persistence, and mobile layout without depending on external service uptime. The current stack is Next.js 16.3.7, React 19.3, Tailwind 4.3, and TypeScript 7.0.2. TypeScript 7's native compiler is installed as `@typescript/native`; the `typescript` alias provides Microsoft's TypeScript 6 compatibility API for Next.js/ESLint tooling. ESLint 10 uses its official compatibility adapter for the Next.js React plugin. Exact resolved dependencies are recorded in `bun.lock`. diff --git a/bun.lock b/bun.lock index 288ad28..2f3db2a 100644 --- a/bun.lock +++ b/bun.lock @@ -11,11 +11,9 @@ "@fontsource-variable/inter": "^5.3.0", "@lezer/highlight": "^1.2.5", "@radix-ui/react-accordion": "^1.2.20", - "@radix-ui/react-checkbox": "^1.3.11", "@radix-ui/react-collapsible": "^1.1.20", "@radix-ui/react-dialog": "^1.1.23", "@radix-ui/react-dropdown-menu": "^2.1.24", - "@radix-ui/react-popover": "^1.1.23", "@radix-ui/react-radio-group": "^1.4.7", "@radix-ui/react-scroll-area": "^1.2.18", "@radix-ui/react-select": "^2.3.7", @@ -297,8 +295,6 @@ "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.15", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA=="], - "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-controllable-state": "1.2.6", "@radix-ui/react-use-size": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Gnptr9pDDQxD3hgq2dtPbtrp/c2qH1mBwIzw3X/ivrMb2e1t0jMTi606fVEqFPaQR1ggXIVQWKj3P2WW9v7zGQ=="], - "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.20", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-controllable-state": "1.2.6", "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-mcGesGplBnzN2sbvJETzpCNfSMyPnb29q1GRLU+Ib7bJrpIG2ywmRoh2V5VbA2uNvKikKUlVbAPks7JDjz4A8Q=="], "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.15", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-slot": "1.3.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA=="], @@ -323,8 +319,6 @@ "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.24", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-collection": "1.1.15", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-direction": "1.1.4", "@radix-ui/react-dismissable-layer": "1.1.19", "@radix-ui/react-focus-guards": "1.1.6", "@radix-ui/react-focus-scope": "1.1.16", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-popper": "1.3.7", "@radix-ui/react-portal": "1.1.17", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-roving-focus": "1.1.19", "@radix-ui/react-slot": "1.3.3", "@radix-ui/react-use-callback-ref": "1.1.4", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-uW7RVuU6Lp/ZtfeY4b3kL32zccgEWvPv1+cf17ubYzHa9cL8AHokmk36cG/XEiH/smbQvumnieXX9j/e9RqJWA=="], - "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.23", "", { "dependencies": { "@radix-ui/primitive": "1.1.7", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-dismissable-layer": "1.1.19", "@radix-ui/react-focus-guards": "1.1.6", "@radix-ui/react-focus-scope": "1.1.16", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-popper": "1.3.7", "@radix-ui/react-portal": "1.1.17", "@radix-ui/react-presence": "1.1.10", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-slot": "1.3.3", "@radix-ui/react-use-controllable-state": "1.2.6", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ=="], - "@radix-ui/react-popper": ["@radix-ui/react-popper@1.3.7", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.15", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.2.2", "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-callback-ref": "1.1.4", "@radix-ui/react-use-layout-effect": "1.1.4", "@radix-ui/react-use-rect": "1.1.4", "@radix-ui/react-use-size": "1.1.4", "@radix-ui/rect": "1.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg=="], "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.17", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.10", "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ=="], diff --git a/docs/fluid-components.md b/docs/fluid-components.md index 6fc6d39..7b3d548 100644 --- a/docs/fluid-components.md +++ b/docs/fluid-components.md @@ -2,11 +2,7 @@ Installed from the official [Fluid Functionalism registry](https://www.fluidfunctionalism.com/r/registry.json) on 2026-09-29 using the current shadcn CLI. The project uses the Radix implementation of each component; Base UI alternatives are duplicate implementations, not additional component types. -All 30 registry UI component types are rendered in the Components gallery: - -Accordion, AskUserQuestions, Badge, Button, Card, CarouselDots, ChatMessage, CheckboxGroup, ColorPicker, Combobox, CommandMenu, Dialog, Dropdown, FileThumbnail, InputCopy, InputGroup, InputMessage, MobileDrawer, RadioGroup, ScrollArea, Select, Sidebar, Slider, Switch, Table, Tabs, TabsSubtle, ThinkingIndicator, ThinkingSteps, Tooltip. - -Shared hooks, motion utilities, contexts, elevation primitives, and theme tokens are also installed. Registry blocks are compositions of these components and are not additional primitives. +The playground uses Fluid components for its existing controls, seed forms, output, navigation, and dialogs, together with the shared motion, elevation, and theme utilities. Components used only by the removed showcase are not included. ## Updating @@ -18,6 +14,6 @@ bunx --bun shadcn@latest add @fluid/button --overwrite Replace `button` with the desired registry name. Review the diff before committing. `components.json` contains the registry URL and installation aliases. Use the same Radix flavor when updating. -The registry source is kept as published and excluded from app lint rules. It remains part of strict TypeScript checks and the production build. The gallery browser test verifies that all 30 component types render without uncaught browser errors. +The registry source is kept as published and excluded from app lint rules. It remains part of strict TypeScript checks and the production build. The Inter variable font is bundled locally through `@fontsource-variable/inter`, including the weight axis used by Fluid's hover animations. No font CDN is required at build or runtime. diff --git a/e2e/playground.spec.ts b/e2e/playground.spec.ts index f81af19..c3ab6cd 100644 --- a/e2e/playground.spec.ts +++ b/e2e/playground.spec.ts @@ -98,29 +98,6 @@ test("validates Carl imports, loads a valid import, and exports files", async ({ expect((await download).suggestedFilename()).toBe("my-tag.tagscript"); }); -test("renders all 30 Fluid components without browser errors", async ({ - page, -}) => { - const errors: string[] = []; - page.on("pageerror", (error) => errors.push(error.message)); - await page.goto("./"); - await page.getByRole("button", { name: "Components", exact: true }).click(); - await expect(page.locator("[data-component]")).toHaveCount(30); - await page.getByRole("button", { name: "Try me", exact: true }).click(); - await expect( - page.getByRole("button", { name: "Try me · 1" }), - ).toBeVisible(); - await page.getByRole("button", { name: "Open a little space" }).click(); - await expect(page.getByRole("dialog")).toBeVisible(); - await page.keyboard.press("Escape"); - await page - .getByRole("textbox", { name: "Gallery message" }) - .fill("A tiny idea"); - await page.getByRole("textbox", { name: "Gallery message" }).press("Enter"); - await expect(page.getByText("A tiny idea", { exact: true })).toBeVisible(); - expect(errors).toEqual([]); -}); - test("keeps the mobile workspace within the viewport", async ({ page }) => { await page.setViewportSize({ width: 390, height: 844 }); await page.goto("./"); @@ -132,14 +109,45 @@ test("keeps the mobile workspace within the viewport", async ({ page }) => { ).toBeLessThanOrEqual(390); await page.getByRole("button", { name: /toggle sidebar/i }).click(); await expect( - page.getByRole("button", { name: "Components", exact: true }), + page.getByRole("button", { name: "Quick guide", exact: true }), ).toBeVisible(); - await page.getByRole("button", { name: "Components", exact: true }).click(); + await page + .getByRole("button", { name: "Quick guide", exact: true }) + .click(); await expect( - page.getByRole("heading", { name: "Fluid, by design." }), + page.getByRole("heading", { name: "Meet your playground." }), ).toBeVisible(); await expect(page.getByRole("dialog")).not.toBeVisible(); expect( await page.evaluate(() => document.documentElement.scrollWidth), ).toBeLessThanOrEqual(390); }); + +test("persists dark mode and keeps the header and settings in sync", async ({ + page, +}) => { + await page.goto("./"); + await page.getByRole("button", { name: "Switch to dark mode" }).click(); + await expect(page.locator("html")).toHaveClass("dark"); + await expect(page.locator("html")).toHaveCSS("color-scheme", "dark"); + await page.reload(); + await expect( + page.getByRole("button", { name: "Switch to light mode" }), + ).toBeEnabled(); + await expect(page.locator("html")).toHaveClass("dark"); + await page + .getByRole("button", { name: "Workspace settings", exact: true }) + .first() + .click(); + await page.getByRole("combobox", { name: "Appearance" }).click(); + await page.getByRole("option", { name: "Light", exact: true }).click(); + await expect(page.locator("html")).not.toHaveClass("dark"); + await page.reload(); + await expect( + page.getByRole("button", { name: "Switch to dark mode" }), + ).toBeEnabled(); + await expect(page.locator("html")).toHaveCSS("color-scheme", "light"); + await page.setViewportSize({ width: 390, height: 844 }); + await page.getByRole("button", { name: "Switch to dark mode" }).click(); + await expect(page.locator("html")).toHaveClass("dark"); +}); diff --git a/package.json b/package.json index 53bbe65..b33ec54 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "test": "bun test tests", "preview": "bun scripts/preview.ts", "test:e2e": "playwright test", - "format": "prettier --write src/components/playground.tsx src/components/component-gallery.tsx src/components/script-editor.tsx src/components/seed-panel.tsx src/components/results-panel.tsx src/lib/tagscript.ts src/app tests e2e scripts/preview.ts *.ts *.mjs .github/workflows/*.yml README.md" + "format": "prettier --write src/components/playground.tsx src/components/script-editor.tsx src/components/seed-panel.tsx src/components/results-panel.tsx src/lib/tagscript.ts src/app tests e2e scripts/preview.ts *.ts *.mjs .github/workflows/*.yml README.md" }, "engines": { "node": ">=22.0.0" @@ -22,11 +22,9 @@ "@fontsource-variable/inter": "^5.3.0", "@lezer/highlight": "^1.2.5", "@radix-ui/react-accordion": "^1.2.20", - "@radix-ui/react-checkbox": "^1.3.11", "@radix-ui/react-collapsible": "^1.1.20", "@radix-ui/react-dialog": "^1.1.23", "@radix-ui/react-dropdown-menu": "^2.1.24", - "@radix-ui/react-popover": "^1.1.23", "@radix-ui/react-radio-group": "^1.4.7", "@radix-ui/react-scroll-area": "^1.2.18", "@radix-ui/react-select": "^2.3.7", diff --git a/src/app/globals.css b/src/app/globals.css index 71135f3..37387b5 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -102,6 +102,7 @@ } :root { + color-scheme: light; --hover: rgb(0 0 0 / 0.04); --active: rgb(0 0 0 / 0.07); --selected: #d4d4d4; @@ -156,6 +157,7 @@ } .dark { + color-scheme: dark; --hover: rgb(255 255 255 / 0.06); --active: rgb(255 255 255 / 0.1); --selected: #525252; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 5b31c0f..5b373a8 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -11,7 +11,14 @@ export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode }>) { return ( - + + +