From f3eca4fe3b3f266164d306e3118f96ce68fe11a9 Mon Sep 17 00:00:00 2001 From: chandan-m Date: Fri, 8 May 2026 03:24:28 -0700 Subject: [PATCH 01/31] change task creation polling to once 15 secs --- frontend/src/pages/AIAnnotationPage/hooks/useSamplingStatus.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/AIAnnotationPage/hooks/useSamplingStatus.ts b/frontend/src/pages/AIAnnotationPage/hooks/useSamplingStatus.ts index e84f4f8..38e21bb 100644 --- a/frontend/src/pages/AIAnnotationPage/hooks/useSamplingStatus.ts +++ b/frontend/src/pages/AIAnnotationPage/hooks/useSamplingStatus.ts @@ -60,7 +60,7 @@ export function useSamplingStatus({ taskId, taskStatus, refreshTaskData }: UseSa }; void pollStatus(); - const intervalId = window.setInterval(() => void pollStatus(), 60_000); + const intervalId = window.setInterval(() => void pollStatus(), 15_000); return () => { mounted = false; From b5d72aa2f5921f2d0fa3b495584b41b8b798bfce Mon Sep 17 00:00:00 2001 From: chandan-m Date: Fri, 8 May 2026 04:27:00 -0700 Subject: [PATCH 02/31] storybook demo - wip - 2 --- frontend/.gitignore | 3 + frontend/.storybook/main.ts | 17 + frontend/.storybook/preview.ts | 32 + frontend/eslint.config.js | 40 +- frontend/package-lock.json | 2882 +++++++++++++++++++- frontend/package.json | 27 +- frontend/public/mockServiceWorker.js | 349 +++ frontend/src/stories/DemoFlows.stories.tsx | 173 ++ frontend/src/stories/demoData.ts | 51 + frontend/src/stories/mswHandlers.ts | 183 ++ frontend/vite.config.ts | 36 +- frontend/vitest.shims.d.ts | 1 + 12 files changed, 3671 insertions(+), 123 deletions(-) create mode 100644 frontend/.storybook/main.ts create mode 100644 frontend/.storybook/preview.ts create mode 100644 frontend/public/mockServiceWorker.js create mode 100644 frontend/src/stories/DemoFlows.stories.tsx create mode 100644 frontend/src/stories/demoData.ts create mode 100644 frontend/src/stories/mswHandlers.ts create mode 100644 frontend/vitest.shims.d.ts diff --git a/frontend/.gitignore b/frontend/.gitignore index a547bf3..f52343a 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -22,3 +22,6 @@ dist-ssr *.njsproj *.sln *.sw? + +*storybook.log +storybook-static diff --git a/frontend/.storybook/main.ts b/frontend/.storybook/main.ts new file mode 100644 index 0000000..78cf7b3 --- /dev/null +++ b/frontend/.storybook/main.ts @@ -0,0 +1,17 @@ +import type { StorybookConfig } from '@storybook/react-vite'; + +const config: StorybookConfig = { + "stories": [ + "../src/**/*.mdx", + "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)" + ], + "addons": [ + "@chromatic-com/storybook", + "@storybook/addon-vitest", + "@storybook/addon-a11y", + "@storybook/addon-docs", + "@storybook/addon-onboarding" + ], + "framework": "@storybook/react-vite" +}; +export default config; \ No newline at end of file diff --git a/frontend/.storybook/preview.ts b/frontend/.storybook/preview.ts new file mode 100644 index 0000000..3e8dbcf --- /dev/null +++ b/frontend/.storybook/preview.ts @@ -0,0 +1,32 @@ +import type { Preview } from "@storybook/react-vite"; +import "@mantine/core/styles.css"; +import "@mantine/dropzone/styles.css"; +import "@mantine/notifications/styles.css"; +import "../src/index.css"; +import { initialize, mswLoader } from "msw-storybook-addon"; + +// Storybook-only runtime override for AI annotation demo pacing. +(globalThis as { __ANNOTATION_BATCH_SIZE__?: number }).__ANNOTATION_BATCH_SIZE__ = 3; + +initialize(); + +const preview: Preview = { + loaders: [mswLoader], + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + + a11y: { + // 'todo' - show a11y violations in the test UI only + // 'error' - fail CI on a11y violations + // 'off' - skip a11y checks entirely + test: "todo", + }, + }, +}; + +export default preview; diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js index 0615933..7358be5 100644 --- a/frontend/eslint.config.js +++ b/frontend/eslint.config.js @@ -1,3 +1,6 @@ +// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format +import storybook from "eslint-plugin-storybook"; + import js from '@eslint/js' import globals from 'globals' import reactHooks from 'eslint-plugin-react-hooks' @@ -5,24 +8,21 @@ import reactRefresh from 'eslint-plugin-react-refresh' import tseslint from 'typescript-eslint' import { defineConfig, globalIgnores } from 'eslint/config' -export default defineConfig([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - js.configs.recommended, - tseslint.configs.recommended, - reactHooks.configs.flat.recommended, - reactRefresh.configs.vite, - ], - languageOptions: { - ecmaVersion: 2020, - globals: globals.browser, - }, - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-unused-vars': 'warn', - 'no-unused-vars': 'warn', - }, +export default defineConfig([globalIgnores(['dist']), { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + rules: { + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unused-vars': 'warn', + 'no-unused-vars': 'warn', }, -]) +}, ...storybook.configs["flat/recommended"]]) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d746aaa..ace9879 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -25,21 +25,42 @@ "uuid": "^13.0.0" }, "devDependencies": { + "@chromatic-com/storybook": "^5.1.2", "@eslint/js": "^9.39.1", + "@storybook/addon-a11y": "^10.3.6", + "@storybook/addon-docs": "^10.3.6", + "@storybook/addon-onboarding": "^10.3.6", + "@storybook/addon-vitest": "^10.3.6", + "@storybook/react-vite": "^10.3.6", "@types/node": "^24.10.1", "@types/papaparse": "^5.5.2", "@types/react": "^19.2.5", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.1", + "@vitest/browser-playwright": "^4.1.5", + "@vitest/coverage-v8": "^4.1.5", "eslint": "^9.39.1", "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.4.24", + "eslint-plugin-storybook": "^10.3.6", "globals": "^16.5.0", + "msw": "^2.14.5", + "msw-storybook-addon": "^2.0.7", + "playwright": "^1.59.1", + "storybook": "^10.3.6", "typescript": "~5.9.3", "typescript-eslint": "^8.46.4", - "vite": "^7.2.4" + "vite": "^7.2.4", + "vitest": "^4.1.5" } }, + "node_modules/@adobe/css-tools": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", + "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", + "dev": true, + "license": "MIT" + }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -227,13 +248,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.5" + "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -318,9 +339,9 @@ } }, "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dev": true, "license": "MIT", "dependencies": { @@ -331,6 +352,44 @@ "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@blazediff/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@blazediff/core/-/core-1.9.1.tgz", + "integrity": "sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@chromatic-com/storybook": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@chromatic-com/storybook/-/storybook-5.1.2.tgz", + "integrity": "sha512-H/hgvwC3E+OtseP2OT2QYUJH2VfnzT6wM3pWOkaNV6g7QI+VUdWJbeJ3o2jFqvEPQNqzhQKWDOlvM4lu+7is6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@neoconfetti/react": "^1.0.0", + "chromatic": "^13.3.4", + "filesize": "^10.0.12", + "jsonfile": "^6.1.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=20.0.0", + "yarn": ">=1.22.18" + }, + "peerDependencies": { + "storybook": "^0.0.0-0 || ^10.1.0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", @@ -1035,6 +1094,113 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@inquirer/ansi": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.5.tgz", + "integrity": "sha512-doc2sWgJpbFQ64UflSVd17ibMGDuxO1yKgOgLMwavzESnXjFWJqUeG8saYosqKpHp4kWiM5x1nXvEjbpx90gzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + } + }, + "node_modules/@inquirer/confirm": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.0.12.tgz", + "integrity": "sha512-h9FgGun3QwVYNj5TWIZZ+slii73bMoBFjPfVIGtnFuL4t8gBiNDV9PcSfIzkuxvgquJKt9nr1QzszpBzTbH8Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^11.1.9", + "@inquirer/type": "^4.0.5" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "11.1.9", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.1.9.tgz", + "integrity": "sha512-BDE4fG22uYh1bGSifcj7JSx119TVYNViMhMu85usp4Fswrzh6M0DV3yld64jA98uOAa2GSQ4Bg4bZRm2d2cwSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.5", + "@inquirer/figures": "^2.0.5", + "@inquirer/type": "^4.0.5", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.5.tgz", + "integrity": "sha512-NsSs4kzfm12lNetHwAn3GEuH317IzpwrMCbOuMIVytpjnJ90YYHNwdRgYGuKmVxwuIqSgqk3M5qqQt1cDk0tGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + } + }, + "node_modules/@inquirer/type": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.5.tgz", + "integrity": "sha512-aetVUNeKNc/VriqXlw1NRSW0zhMBB0W4bNbWRJgzRl/3d0QNDQFfk0GO5SDdtjMZVg6o8ZKEiadd7SCCzoOn5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@joshwooding/vite-plugin-react-docgen-typescript": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@joshwooding/vite-plugin-react-docgen-typescript/-/vite-plugin-react-docgen-typescript-0.7.0.tgz", + "integrity": "sha512-qvsTEwEFefhdirGOPnu9Wp6ChfIwy2dBCRuETU3uE+4cC+PFoxMSiiEhxk4lOluA34eARHA0OxqsEUYDqRMgeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^13.0.1", + "react-docgen-typescript": "^2.2.2" + }, + "peerDependencies": { + "typescript": ">= 4.3.x", + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -1166,6 +1332,88 @@ "react": "^18.x || ^19.x" } }, + "node_modules/@mdx-js/react": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz", + "integrity": "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdx": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" + } + }, + "node_modules/@mswjs/interceptors": { + "version": "0.41.8", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.41.8.tgz", + "integrity": "sha512-pRLMNKTSGRoLq+KnEB/7OY5vijw1XmcheAAOiv6pj7W1FG32kAGqj1C/RK/cqxRGr1Fh+zBi8sDur8kj3EQv6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@mswjs/interceptors/node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@neoconfetti/react": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@neoconfetti/react/-/react-1.0.0.tgz", + "integrity": "sha512-klcSooChXXOzIm+SE5IISIAn3bYzYfPjbX7D7HoqZL84oAfgREeSg5vSIaSFH+DaGzzvImTyWe1OyrJ67vik4A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@open-draft/deferred-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-3.0.0.tgz", + "integrity": "sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, "node_modules/@reduxjs/toolkit": { "version": "2.11.2", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", @@ -1199,6 +1447,29 @@ "dev": true, "license": "MIT" }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.53.5", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.5.tgz", @@ -1519,109 +1790,446 @@ "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", "license": "MIT" }, - "node_modules/@tabler/icons": { - "version": "3.36.0", - "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.36.0.tgz", - "integrity": "sha512-z9OfTEG6QbaQWM9KBOxxUdpgvMUn0atageXyiaSc2gmYm51ORO8Ua7eUcjlks+Dc0YMK4rrodAFdK9SfjJ4ZcA==", + "node_modules/@storybook/addon-a11y": { + "version": "10.3.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-10.3.6.tgz", + "integrity": "sha512-cbwXIT5CeHZ9AFbTKQ6YB7Ct6TAl/kKOgALbvzzVtFfRvm51JYygGaiJaB7PbPWn9wgJP2olJcFt+erlEc6cRw==", + "dev": true, "license": "MIT", + "dependencies": { + "@storybook/global": "^5.0.0", + "axe-core": "^4.2.0" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/codecalm" + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^10.3.6" } }, - "node_modules/@tabler/icons-react": { - "version": "3.36.0", - "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-3.36.0.tgz", - "integrity": "sha512-sSZ00bEjTdTTskVFykq294RJq+9cFatwy4uYa78HcYBCXU1kSD1DIp5yoFsQXmybkIOKCjp18OnhAYk553UIfQ==", + "node_modules/@storybook/addon-docs": { + "version": "10.3.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.3.6.tgz", + "integrity": "sha512-TvIdADVPtauxW0LzXIpIv7X6GxwetorhyNh+6+7MHC27XSBCWVxxRUwL63YeLlHTuXsIk0quG3b1xgwVRzWOJA==", + "dev": true, "license": "MIT", "dependencies": { - "@tabler/icons": "3.36.0" + "@mdx-js/react": "^3.0.0", + "@storybook/csf-plugin": "10.3.6", + "@storybook/icons": "^2.0.1", + "@storybook/react-dom-shim": "10.3.6", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "ts-dedent": "^2.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/codecalm" + "type": "opencollective", + "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "react": ">= 16" + "storybook": "^10.3.6" } }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "node_modules/@storybook/addon-onboarding": { + "version": "10.3.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-onboarding/-/addon-onboarding-10.3.6.tgz", + "integrity": "sha512-Tys9eOFzCkBygfDWVRa2hpTQI5Y1HQt9ybkJIHdR16GASQ3fhyXVULHVRmGQLEGN+3n84JGvlU8CN9S/OBB1IQ==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^10.3.6" } }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "node_modules/@storybook/addon-vitest": { + "version": "10.3.6", + "resolved": "https://registry.npmjs.org/@storybook/addon-vitest/-/addon-vitest-10.3.6.tgz", + "integrity": "sha512-HXj7RrPJY+xzoNjL+xZu2oLw1fI5BA87Noh1NAXMPuECHR5R5fuRM/tTsJuIGXHFMO06FjSi/rekDIfCj1fL4w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.0.0" + "@storybook/global": "^5.0.0", + "@storybook/icons": "^2.0.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "@vitest/browser": "^3.0.0 || ^4.0.0", + "@vitest/browser-playwright": "^4.0.0", + "@vitest/runner": "^3.0.0 || ^4.0.0", + "storybook": "^10.3.6", + "vitest": "^3.0.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/runner": { + "optional": true + }, + "vitest": { + "optional": true + } } }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "node_modules/@storybook/builder-vite": { + "version": "10.3.6", + "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-10.3.6.tgz", + "integrity": "sha512-gpvR/sE4BcrFtmQZ+Ker7zD23oQzoVeqD9nF6cK6yzY+Q0svJXyX2EPmFG4y+EwygD5/vNzDpP84gGMut8VRwg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" + "@storybook/csf-plugin": "10.3.6", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^10.3.6", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "node_modules/@storybook/csf-plugin": { + "version": "10.3.6", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-10.3.6.tgz", + "integrity": "sha512-9kBf7VRdRqTSIYo+rPtVn5yjYYyK8kP2QhEYx3oiXvfwy4RexmbJnhk/tXa/lNiTqukA1TqaWQ2+5MqF4fu6YQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.2" + "unplugin": "^2.3.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "esbuild": "*", + "rollup": "*", + "storybook": "^10.3.6", + "vite": "*", + "webpack": "*" + }, + "peerDependenciesMeta": { + "esbuild": { + "optional": true + }, + "rollup": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } } }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "node_modules/@storybook/global": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@storybook/global/-/global-5.0.0.tgz", + "integrity": "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==", "dev": true, "license": "MIT" }, - "node_modules/@types/node": { - "version": "24.10.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.4.tgz", - "integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==", + "node_modules/@storybook/icons": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-2.0.2.tgz", + "integrity": "sha512-KZBCpXsshAIjczYNXR/rlxEtCUX/eAbpFNwKi8bcOomrLA4t/SyPz5RF+lVPO2oZBUE4sAkt43mfJUevQDSEEw==", "dev": true, "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/@types/papaparse": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.5.2.tgz", - "integrity": "sha512-gFnFp/JMzLHCwRf7tQHrNnfhN4eYBVYYI897CGX4MY1tzY9l2aLkVyx2IlKZ/SAqDbB3I1AOZW5gTMGGsqWliA==", + "node_modules/@storybook/react": { + "version": "10.3.6", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-10.3.6.tgz", + "integrity": "sha512-oZQZ6xayWe5IdHmFUTL0TL8rX/gpNNh9gWhT2vzW5eeUvlkVG/RBKdsja6Ndrk2s1D9vcnwiI6r6CNXy3IEEmg==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*" + "@storybook/global": "^5.0.0", + "@storybook/react-dom-shim": "10.3.6", + "react-docgen": "^8.0.2", + "react-docgen-typescript": "^2.2.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "storybook": "^10.3.6", + "typescript": ">= 4.9.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@storybook/react-dom-shim": { + "version": "10.3.6", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.3.6.tgz", + "integrity": "sha512-/Tu1gPu+Fw+zOnAGmxRmOD30FX3a04LxcTAKflEtdpmtIMVR5bA3qpjy+f5YhoyDCecbXyKmL1OeIU2FIIZHqQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "storybook": "^10.3.6" + } + }, + "node_modules/@storybook/react-vite": { + "version": "10.3.6", + "resolved": "https://registry.npmjs.org/@storybook/react-vite/-/react-vite-10.3.6.tgz", + "integrity": "sha512-tySQRc+8q7V2NkylQMNJjDV8zXy6tkxb8oDqw/DIhHhI9Xn77MTKVZ8Cihbo5NMm7HYTB6xDKr6wqdSMgdufYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@joshwooding/vite-plugin-react-docgen-typescript": "^0.7.0", + "@rollup/pluginutils": "^5.0.2", + "@storybook/builder-vite": "10.3.6", + "@storybook/react": "10.3.6", + "empathic": "^2.0.0", + "magic-string": "^0.30.0", + "react-docgen": "^8.0.0", + "resolve": "^1.22.8", + "tsconfig-paths": "^4.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "storybook": "^10.3.6", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@tabler/icons": { + "version": "3.36.0", + "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.36.0.tgz", + "integrity": "sha512-z9OfTEG6QbaQWM9KBOxxUdpgvMUn0atageXyiaSc2gmYm51ORO8Ua7eUcjlks+Dc0YMK4rrodAFdK9SfjJ4ZcA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/codecalm" + } + }, + "node_modules/@tabler/icons-react": { + "version": "3.36.0", + "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-3.36.0.tgz", + "integrity": "sha512-sSZ00bEjTdTTskVFykq294RJq+9cFatwy4uYa78HcYBCXU1kSD1DIp5yoFsQXmybkIOKCjp18OnhAYk553UIfQ==", + "license": "MIT", + "dependencies": { + "@tabler/icons": "3.36.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/codecalm" + }, + "peerDependencies": { + "react": ">= 16" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/user-event": { + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/doctrine": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.9.tgz", + "integrity": "sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mdx": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", + "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.10.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.4.tgz", + "integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/papaparse": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.5.2.tgz", + "integrity": "sha512-gFnFp/JMzLHCwRf7tQHrNnfhN4eYBVYYI897CGX4MY1tzY9l2aLkVyx2IlKZ/SAqDbB3I1AOZW5gTMGGsqWliA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, "node_modules/@types/react": { @@ -1644,6 +2252,30 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/resolve": { + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.6.tgz", + "integrity": "sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/set-cookie-parser": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.10.tgz", + "integrity": "sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/statuses": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.6.tgz", + "integrity": "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/use-sync-external-store": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", @@ -1940,6 +2572,264 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, + "node_modules/@vitest/browser": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.1.5.tgz", + "integrity": "sha512-iCDGI8c4yg+xmjUg2VsygdAUSIIB4x5Rht/P68OXy1hPELKXHDkzh87lkuTcdYmemRChDkEpB426MmDjzC0ziA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@blazediff/core": "1.9.1", + "@vitest/mocker": "4.1.5", + "@vitest/utils": "4.1.5", + "magic-string": "^0.30.21", + "pngjs": "^7.0.0", + "sirv": "^3.0.2", + "tinyrainbow": "^3.1.0", + "ws": "^8.19.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "4.1.5" + } + }, + "node_modules/@vitest/browser-playwright": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/browser-playwright/-/browser-playwright-4.1.5.tgz", + "integrity": "sha512-CWy0lBQJq97nionyJJdnaU4961IXTl43a7UCu5nHy51IoKxAt6PVIJLo+76rVl7KOOgcWHNkG4kbJu/pW7knvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/browser": "4.1.5", + "@vitest/mocker": "4.1.5", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "playwright": "*", + "vitest": "4.1.5" + }, + "peerDependenciesMeta": { + "playwright": { + "optional": false + } + } + }, + "node_modules/@vitest/coverage-v8": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.5.tgz", + "integrity": "sha512-38C0/Ddb7HcRG0Z4/DUem8x57d2p9jYgp18mkaYswEOQBGsI1CG4f/hjm0ZCeaJfWhSZ4k7jgs29V1Zom7Ki9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.1.5", + "ast-v8-to-istanbul": "^1.0.0", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.1.5", + "vitest": "4.1.5" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/expect/node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/expect/node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/expect/node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/expect/node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.5.tgz", + "integrity": "sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.5", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.5.tgz", + "integrity": "sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.5.tgz", + "integrity": "sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.5", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.5.tgz", + "integrity": "sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.5", + "@vitest/utils": "4.1.5", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.5.tgz", + "integrity": "sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.5.tgz", + "integrity": "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.5", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@webcontainer/env": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@webcontainer/env/-/env-1.1.1.tgz", + "integrity": "sha512-6aN99yL695Hi9SuIk1oC88l9o0gmxL1nGWWQ/kNy81HigJ0FoaoTXpytCj6ItzgyCEwA9kF1wixsTuv5cjsgng==", + "dev": true, + "license": "MIT" + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -1980,6 +2870,16 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -1987,21 +2887,83 @@ "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-types": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ast-v8-to-istanbul": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.0.tgz", + "integrity": "sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", "dev": true, - "license": "Python-2.0" + "license": "MIT" }, "node_modules/asynckit": { "version": "0.4.0", @@ -2018,6 +2980,16 @@ "node": ">=4" } }, + "node_modules/axe-core": { + "version": "4.11.4", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.4.tgz", + "integrity": "sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, "node_modules/axios": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", @@ -2091,6 +3063,22 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -2135,6 +3123,23 @@ ], "license": "CC-BY-4.0" }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2152,6 +3157,78 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/chromatic": { + "version": "13.3.5", + "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-13.3.5.tgz", + "integrity": "sha512-MzPhxpl838qJUo0A55osCF2ifwPbjcIPeElr1d4SHcjnHoIcg7l1syJDrAYK/a+PcCBrOGi06jPNpQAln5hWgw==", + "dev": true, + "license": "MIT", + "bin": { + "chroma": "dist/bin.js", + "chromatic": "dist/bin.js", + "chromatic-cli": "dist/bin.js" + }, + "peerDependencies": { + "@chromatic-com/cypress": "^0.*.* || ^1.0.0", + "@chromatic-com/playwright": "^0.*.* || ^1.0.0" + }, + "peerDependenciesMeta": { + "@chromatic-com/cypress": { + "optional": true + }, + "@chromatic-com/playwright": { + "optional": true + } + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2235,6 +3312,13 @@ "node": ">= 8" } }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -2259,6 +3343,16 @@ } } }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2266,6 +3360,49 @@ "dev": true, "license": "MIT" }, + "node_modules/default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2275,12 +3412,43 @@ "node": ">=0.4.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", "license": "MIT" }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/dom-helpers": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", @@ -2312,6 +3480,23 @@ "dev": true, "license": "ISC" }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/empathic": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.1.tgz", + "integrity": "sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -2330,6 +3515,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -2512,6 +3704,20 @@ "eslint": ">=8.40" } }, + "node_modules/eslint-plugin-storybook": { + "version": "10.3.6", + "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-10.3.6.tgz", + "integrity": "sha512-8udrL+Rmp5LFaZvgRe4J226X1MYls25bWCyHuzR5X8s2qbFTryX+wKC+o/0Ato4A1AvwnDg8OOMPc6yWJ9JpcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.48.0" + }, + "peerDependencies": { + "eslint": ">=8", + "storybook": "^10.3.6" + } + }, "node_modules/eslint-scope": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", @@ -2560,6 +3766,20 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -2596,6 +3816,13 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -2606,6 +3833,16 @@ "node": ">=0.10.0" } }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2626,6 +3863,33 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.0.tgz", + "integrity": "sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -2669,6 +3933,16 @@ "node": ">= 12" } }, + "node_modules/filesize": { + "version": "10.1.6", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.1.6.tgz", + "integrity": "sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 10.4.0" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -2777,6 +4051,16 @@ "node": ">=6.9.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -2823,6 +4107,24 @@ "node": ">= 0.4" } }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2836,6 +4138,45 @@ "node": ">=10.13.0" } }, + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globals": { "version": "16.5.0", "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", @@ -2861,6 +4202,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/graphql": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.14.0.tgz", + "integrity": "sha512-BBvQ/406p+4CZbTpCbVPSxfzrZrbnuWSP1ELYgyS6B+hNeKzgrdB4JczCa5VZUBQrDa9hUngm0KnexY6pJRN5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2899,9 +4258,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -2910,6 +4269,24 @@ "node": ">= 0.4" } }, + "node_modules/headers-polyfill": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-5.0.1.tgz", + "integrity": "sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/set-cookie-parser": "^2.4.10", + "set-cookie-parser": "^3.0.1" + } + }, + "node_modules/headers-polyfill/node_modules/set-cookie-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.0.tgz", + "integrity": "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==", + "dev": true, + "license": "MIT" + }, "node_modules/hermes-estree": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", @@ -2927,6 +4304,13 @@ "hermes-estree": "0.25.1" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2974,6 +4358,48 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2984,6 +4410,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -2997,6 +4433,48 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -3004,6 +4482,45 @@ "dev": true, "license": "ISC" }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3070,6 +4587,19 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -3138,6 +4668,13 @@ "loose-envify": "cli.js" } }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -3148,6 +4685,68 @@ "yallist": "^3.0.2" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", + "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -3178,6 +4777,16 @@ "node": ">= 0.6" } }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3191,6 +4800,36 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3198,6 +4837,90 @@ "dev": true, "license": "MIT" }, + "node_modules/msw": { + "version": "2.14.5", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.14.5.tgz", + "integrity": "sha512-X6G05oX4x0e+CNI55KMdhMmwHCBKf2iwazGr+iwsdoJ94JA1ED7wSXb6V+lLPdqFkmIlPiGYvayqnaNcOzobDA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@inquirer/confirm": "^6.0.11", + "@mswjs/interceptors": "^0.41.3", + "@open-draft/deferred-promise": "^3.0.0", + "@types/statuses": "^2.0.6", + "cookie": "^1.1.1", + "graphql": "^16.13.2", + "headers-polyfill": "^5.0.1", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "path-to-regexp": "^6.3.0", + "picocolors": "^1.1.1", + "rettime": "^0.11.11", + "statuses": "^2.0.2", + "strict-event-emitter": "^0.5.1", + "tough-cookie": "^6.0.1", + "type-fest": "^5.5.0", + "until-async": "^3.0.2", + "yargs": "^17.7.2" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.8.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/msw-storybook-addon": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/msw-storybook-addon/-/msw-storybook-addon-2.0.7.tgz", + "integrity": "sha512-TGmlxXy2TsaB6QcClVKRxqvay5f93xoLguHOihRFQ+gIEIyiyvcoQjkEeuOe7Y9qvddzGB1LyFomzPo9/EpnuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-node-process": "^1.0.1" + }, + "peerDependencies": { + "msw": "^2.0.0" + } + }, + "node_modules/msw/node_modules/type-fest": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.6.0.tgz", + "integrity": "sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mute-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz", + "integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -3240,6 +4963,36 @@ "node": ">=0.10.0" } }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -3258,6 +5011,13 @@ "node": ">= 0.8.0" } }, + "node_modules/outvariant": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", + "dev": true, + "license": "MIT" + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -3326,7 +5086,65 @@ "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", + "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" } }, "node_modules/picocolors": { @@ -3349,6 +5167,63 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/playwright": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.19.0" + } + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -3388,6 +5263,44 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -3424,6 +5337,38 @@ "node": ">=0.10.0" } }, + "node_modules/react-docgen": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-8.0.3.tgz", + "integrity": "sha512-aEZ9qP+/M+58x2qgfSFEWH1BxLyHe5+qkLNJOZQb5iGS017jpbRnoKhNRrXPeA6RfBrZO5wZrT9DMC1UqE1f1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.2", + "@types/babel__core": "^7.20.5", + "@types/babel__traverse": "^7.20.7", + "@types/doctrine": "^0.0.9", + "@types/resolve": "^1.20.2", + "doctrine": "^3.0.0", + "resolve": "^1.22.1", + "strip-indent": "^4.0.0" + }, + "engines": { + "node": "^20.9.0 || >=22" + } + }, + "node_modules/react-docgen-typescript": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.4.0.tgz", + "integrity": "sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">= 4.3.x" + } + }, "node_modules/react-dom": { "version": "19.2.3", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", @@ -3642,6 +5587,50 @@ "react-dom": ">=16.6.0" } }, + "node_modules/recast": { + "version": "0.23.11", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz", + "integrity": "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-types": "^0.16.1", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tiny-invariant": "^1.3.3", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/redent/node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/redux": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", @@ -3657,12 +5646,44 @@ "redux": "^5.0.0" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/reselect": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", "license": "MIT" }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3673,6 +5694,13 @@ "node": ">=4" } }, + "node_modules/rettime": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/rettime/-/rettime-0.11.11.tgz", + "integrity": "sha512-ILJRqVWBCTlg9r42fFgwVZx1gnFAcQF8mRoMkbgQfIrjEDf9nbBFDFx00oloOa+Q869FUtaYDXZvEfnecQSCoQ==", + "dev": true, + "license": "MIT" + }, "node_modules/rollup": { "version": "4.53.5", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.5.tgz", @@ -3715,6 +5743,19 @@ "fsevents": "~2.3.2" } }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -3747,27 +5788,250 @@ "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/storybook": { + "version": "10.3.6", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-10.3.6.tgz", + "integrity": "sha512-vbSz7g/1rGMC1uAULqMZjALkIuLu2QABqfhRYhyr/11kzyesi+vAmwyJLukZP1FfecxGOgMwOh6GS0YsGpHAvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/global": "^5.0.0", + "@storybook/icons": "^2.0.1", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/user-event": "^14.6.1", + "@vitest/expect": "3.2.4", + "@vitest/spy": "3.2.4", + "@webcontainer/env": "^1.1.1", + "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0", + "open": "^10.2.0", + "recast": "^0.23.5", + "semver": "^7.7.3", + "use-sync-external-store": "^1.5.0", + "ws": "^8.18.0" + }, + "bin": { + "storybook": "dist/bin/dispatcher.js" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "prettier": "^2 || ^3", + "vite-plus": "^0.1.15" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + }, + "vite-plus": { + "optional": true + } + } + }, + "node_modules/storybook/node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/storybook/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/shebang-regex": { + "node_modules/strip-bom": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "node_modules/strip-indent": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.1.1.tgz", + "integrity": "sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/strip-json-comments": { @@ -3796,12 +6060,62 @@ "node": ">=8" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/tabbable": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.3.0.tgz", "integrity": "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==", "license": "MIT" }, + "node_modules/tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -3819,6 +6133,69 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.0.30", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.30.tgz", + "integrity": "sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.30" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.30", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.30.tgz", + "integrity": "sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -3832,6 +6209,31 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.10" + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -3908,6 +6310,42 @@ "dev": true, "license": "MIT" }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unplugin": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", + "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/until-async": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/until-async/-/until-async-3.0.2.tgz", + "integrity": "sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/kettanaito" + } + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", @@ -4134,6 +6572,131 @@ } } }, + "node_modules/vitest": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.5.tgz", + "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.5", + "@vitest/mocker": "4.1.5", + "@vitest/pretty-format": "4.1.5", + "@vitest/runner": "4.1.5", + "@vitest/snapshot": "4.1.5", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.5", + "@vitest/browser-preview": "4.1.5", + "@vitest/browser-webdriverio": "4.1.5", + "@vitest/coverage-istanbul": "4.1.5", + "@vitest/coverage-v8": "4.1.5", + "@vitest/ui": "4.1.5", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/vitest/node_modules/@vitest/expect": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz", + "integrity": "sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest/node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true, + "license": "MIT" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4150,6 +6713,23 @@ "node": ">= 8" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -4160,6 +6740,85 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -4167,6 +6826,35 @@ "dev": true, "license": "ISC" }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index e3d04c6..8b5f701 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,7 +7,9 @@ "dev": "vite", "build": "tsc -b && vite build", "lint": "eslint .", - "preview": "vite preview" + "preview": "vite preview", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build" }, "dependencies": { "@mantine/core": "^8.3.10", @@ -27,18 +29,37 @@ "uuid": "^13.0.0" }, "devDependencies": { + "@chromatic-com/storybook": "^5.1.2", "@eslint/js": "^9.39.1", + "@storybook/addon-a11y": "^10.3.6", + "@storybook/addon-docs": "^10.3.6", + "@storybook/addon-onboarding": "^10.3.6", + "@storybook/addon-vitest": "^10.3.6", + "@storybook/react-vite": "^10.3.6", "@types/node": "^24.10.1", "@types/papaparse": "^5.5.2", "@types/react": "^19.2.5", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.1", + "@vitest/browser-playwright": "^4.1.5", + "@vitest/coverage-v8": "^4.1.5", "eslint": "^9.39.1", "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.4.24", + "eslint-plugin-storybook": "^10.3.6", "globals": "^16.5.0", + "msw": "^2.14.5", + "msw-storybook-addon": "^2.0.7", + "playwright": "^1.59.1", + "storybook": "^10.3.6", "typescript": "~5.9.3", "typescript-eslint": "^8.46.4", - "vite": "^7.2.4" + "vite": "^7.2.4", + "vitest": "^4.1.5" + }, + "msw": { + "workerDirectory": [ + "public" + ] } -} +} \ No newline at end of file diff --git a/frontend/public/mockServiceWorker.js b/frontend/public/mockServiceWorker.js new file mode 100644 index 0000000..9cd8401 --- /dev/null +++ b/frontend/public/mockServiceWorker.js @@ -0,0 +1,349 @@ +/* eslint-disable */ +/* tslint:disable */ + +/** + * Mock Service Worker. + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + */ + +const PACKAGE_VERSION = '2.14.5' +const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' +const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') +const activeClientIds = new Set() + +addEventListener('install', function () { + self.skipWaiting() +}) + +addEventListener('activate', function (event) { + event.waitUntil(self.clients.claim()) +}) + +addEventListener('message', async function (event) { + const clientId = Reflect.get(event.source || {}, 'id') + + if (!clientId || !self.clients) { + return + } + + const client = await self.clients.get(clientId) + + if (!client) { + return + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }) + break + } + + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: { + packageVersion: PACKAGE_VERSION, + checksum: INTEGRITY_CHECKSUM, + }, + }) + break + } + + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId) + + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: { + client: { + id: client.id, + frameType: client.frameType, + }, + }, + }) + break + } + + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId) + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister() + } + + break + } + } +}) + +addEventListener('fetch', function (event) { + const requestInterceptedAt = Date.now() + + // Bypass navigation requests. + if (event.request.mode === 'navigate') { + return + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if ( + event.request.cache === 'only-if-cached' && + event.request.mode !== 'same-origin' + ) { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been terminated (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + const requestId = crypto.randomUUID() + event.respondWith(handleRequest(event, requestId, requestInterceptedAt)) +}) + +/** + * @param {FetchEvent} event + * @param {string} requestId + * @param {number} requestInterceptedAt + */ +async function handleRequest(event, requestId, requestInterceptedAt) { + const client = await resolveMainClient(event) + const requestCloneForEvents = event.request.clone() + const response = await getResponse( + event, + client, + requestId, + requestInterceptedAt, + ) + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + const serializedRequest = await serializeRequest(requestCloneForEvents) + + // Clone the response so both the client and the library could consume it. + const responseClone = response.clone() + + sendToClient( + client, + { + type: 'RESPONSE', + payload: { + isMockedResponse: IS_MOCKED_RESPONSE in response, + request: { + id: requestId, + ...serializedRequest, + }, + response: { + type: responseClone.type, + status: responseClone.status, + statusText: responseClone.statusText, + headers: Object.fromEntries(responseClone.headers.entries()), + body: responseClone.body, + }, + }, + }, + responseClone.body ? [serializedRequest.body, responseClone.body] : [], + ) + } + + return response +} + +/** + * Resolve the main client for the given event. + * Client that issues a request doesn't necessarily equal the client + * that registered the worker. It's with the latter the worker should + * communicate with during the response resolving phase. + * @param {FetchEvent} event + * @returns {Promise} + */ +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId) + + if (activeClientIds.has(event.clientId)) { + return client + } + + if (client?.frameType === 'top-level') { + return client + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible' + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) +} + +/** + * @param {FetchEvent} event + * @param {Client | undefined} client + * @param {string} requestId + * @param {number} requestInterceptedAt + * @returns {Promise} + */ +async function getResponse(event, client, requestId, requestInterceptedAt) { + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const requestClone = event.request.clone() + + function passthrough() { + // Cast the request headers to a new Headers instance + // so the headers can be manipulated with. + const headers = new Headers(requestClone.headers) + + // Remove the "accept" header value that marked this request as passthrough. + // This prevents request alteration and also keeps it compliant with the + // user-defined CORS policies. + const acceptHeader = headers.get('accept') + if (acceptHeader) { + const values = acceptHeader.split(',').map((value) => value.trim()) + const filteredValues = values.filter( + (value) => value !== 'msw/passthrough', + ) + + if (filteredValues.length > 0) { + headers.set('accept', filteredValues.join(', ')) + } else { + headers.delete('accept') + } + } + + return fetch(requestClone, { headers }) + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough() + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough() + } + + // Notify the client that a request has been intercepted. + const serializedRequest = await serializeRequest(event.request) + const clientMessage = await sendToClient( + client, + { + type: 'REQUEST', + payload: { + id: requestId, + interceptedAt: requestInterceptedAt, + ...serializedRequest, + }, + }, + [serializedRequest.body], + ) + + switch (clientMessage.type) { + case 'MOCK_RESPONSE': { + return respondWithMock(clientMessage.data) + } + + case 'PASSTHROUGH': { + return passthrough() + } + } + + return passthrough() +} + +/** + * @param {Client} client + * @param {any} message + * @param {Array} transferrables + * @returns {Promise} + */ +function sendToClient(client, message, transferrables = []) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel() + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error) + } + + resolve(event.data) + } + + client.postMessage(message, [ + channel.port2, + ...transferrables.filter(Boolean), + ]) + }) +} + +/** + * @param {Response} response + * @returns {Response} + */ +function respondWithMock(response) { + // Setting response status code to 0 is a no-op. + // However, when responding with a "Response.error()", the produced Response + // instance will have status code set to 0. Since it's not possible to create + // a Response instance with status code 0, handle that use-case separately. + if (response.status === 0) { + return Response.error() + } + + const mockedResponse = new Response(response.body, response) + + Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { + value: true, + enumerable: true, + }) + + return mockedResponse +} + +/** + * @param {Request} request + */ +async function serializeRequest(request) { + return { + url: request.url, + mode: request.mode, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: await request.arrayBuffer(), + keepalive: request.keepalive, + } +} diff --git a/frontend/src/stories/DemoFlows.stories.tsx b/frontend/src/stories/DemoFlows.stories.tsx new file mode 100644 index 0000000..acea1dd --- /dev/null +++ b/frontend/src/stories/DemoFlows.stories.tsx @@ -0,0 +1,173 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; +import type { ReactNode } from "react"; +import { MantineProvider } from "@mantine/core"; +import { Notifications } from "@mantine/notifications"; +import { Provider } from "react-redux"; +import { configureStore } from "@reduxjs/toolkit"; +import { createMemoryRouter, RouterProvider } from "react-router-dom"; +import userReducer from "../store/userSlice"; +import { AppLayout } from "../components/layout/AppLayout"; +import AnnotationPage from "../pages/AIAnnotationPage"; +import NewAnnotationTaskPage from "../pages/NewAnnotationTaskPage"; +import LandingPage from "../pages/LandingPage"; +import LoginPage from "../pages/LoginPage"; +import CodebookLandingPage from "../pages/CodebookLandingPage"; +import DatasetUploadPage from "../pages/DatasetUploadPage"; +import AnnotateDatasetLandingPage from "../pages/AnnotateDatasetLandingPage"; +import AnnotateDatasetPage from "../pages/AnnotateDatasetPage"; +import { ProtectedRoute } from "../components/auth/ProtectedRoute"; +import { + handlersEmpty, + handlersPermissionDenied, + handlersReady, + handlersSamplingError, + handlersSamplingPending, + handlersSamplingTransition, + handlersServerError, +} from "./mswHandlers"; + +const makeStore = () => + configureStore({ + reducer: { user: userReducer }, + preloadedState: { + user: { + user: { id: "demo-user", username: "demo", email: "demo@example.com" }, + accessToken: "demo-token", + refreshToken: "demo-refresh", + }, + }, + }); + +function DemoApp({ route = "/", element }: { route?: string; element?: ReactNode }) { + const router = createMemoryRouter( + [ + { + path: "/", + element: , + children: [ + { index: true, element: }, + { path: "codebook-landing", element: }, + { path: "new-codebook", element: }, + { path: "annotate-dataset-landing", element: }, + { path: "new-annotation", element: }, + { path: "annotate-dataset/:id", element: }, + { path: "codebook-creation/:taskId", element: }, + ], + }, + ], + { initialEntries: [route] }, + ); + + return ( + + + + {element ?? } + + + ); +} + +const meta: Meta = { + title: "Demo/App Flows", + component: DemoApp, + parameters: { layout: "fullscreen" }, +}; + +export default meta; +type Story = StoryObj; + +export const Landing: Story = { + render: () => , + parameters: { msw: { handlers: handlersReady } }, +}; + +export const TaskCreationPage: Story = { + render: () => , + parameters: { msw: { handlers: handlersReady } }, +}; + +export const CodebookLanding: Story = { + render: () => , + parameters: { msw: { handlers: handlersReady } }, +}; + +export const CodebookTaskUploadPage: Story = { + render: () => , + parameters: { msw: { handlers: handlersReady } }, +}; + +export const SamplingPending: Story = { + render: () => , + parameters: { msw: { handlers: handlersSamplingPending } }, +}; + +export const AIReview_ReadyImmediate: Story = { + render: () => , + parameters: { msw: { handlers: handlersReady } }, +}; + +export const AIReview_PendingThenReady: Story = { + render: () => , + parameters: { msw: { handlers: handlersSamplingTransition() } }, +}; + +export const SamplingErrorState: Story = { + render: () => , + parameters: { msw: { handlers: handlersSamplingError } }, +}; + +export const EmptyState: Story = { + render: () => , + parameters: { msw: { handlers: handlersEmpty } }, +}; + +export const PermissionDeniedState: Story = { + render: () => , + parameters: { msw: { handlers: handlersPermissionDenied } }, +}; + +export const ServerErrorState: Story = { + render: () => , + parameters: { msw: { handlers: handlersServerError } }, +}; + +function SessionExpiredApp() { + const expiredJwt = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJ1IiwiZXhwIjoxMDAwLCJ1c2VybmFtZSI6ImRlbW8iLCJlbWFpbCI6ImRlbW9AZXhhbXBsZS5jb20ifQ.sig"; + const store = configureStore({ + reducer: { user: userReducer }, + preloadedState: { + user: { + user: { id: "demo-user", username: "demo", email: "demo@example.com" }, + accessToken: expiredJwt, + refreshToken: "demo-refresh", + }, + }, + }); + + const router = createMemoryRouter( + [ + { + element: , + children: [{ path: "/", element: }], + }, + { path: "/login", element: }, + ], + { initialEntries: ["/"] }, + ); + + return ( + + + + + + + ); +} + +export const SessionExpiredRedirect: Story = { + render: () => , + parameters: { msw: { handlers: handlersReady } }, +}; diff --git a/frontend/src/stories/demoData.ts b/frontend/src/stories/demoData.ts new file mode 100644 index 0000000..fb9f981 --- /dev/null +++ b/frontend/src/stories/demoData.ts @@ -0,0 +1,51 @@ +import type { Task } from "@common/types/tasks"; +import type { AnnotationItem } from "@common/types/annotations"; + +export const demoTask: Task = { + _id: "demo-task-1", + name: "Customer Support Intent Classification", + description: "Classify support tickets by intent for triage automation.", + type: "Multiclass", + labels: [ + { name: "Billing", description: "Payments, invoices, and charges" }, + { name: "Technical", description: "Bugs, outages, feature failures" }, + { name: "Account", description: "Login, profile, access, permissions" }, + ], + columns: ["text"], + file: "demo.csv", + status: "ready", + codebook: [ + "Billing: payment failed, chargeback, invoice mismatch", + "Technical: app crashed, timeout, bug report", + "Account: password reset, MFA lockout, account recovery", + "Billing: duplicate renewal or unexpected subscription charge", + "Technical: export/reporting errors and stale loading states", + ], + userID: "demo-user", +}; + +const samples = [ + "I was charged twice for last month.", + "The app crashes every time I open reports.", + "I cannot reset my account password.", + "My invoice total does not match my contract.", + "Dashboard is stuck on loading for 10 minutes.", + "Please update my account email and phone number.", +]; + +export const demoAnnotations: AnnotationItem[] = samples.map((text, idx) => { + const id = idx + 1; + const label = idx % 3 === 0 ? "Billing" : idx % 3 === 1 ? "Technical" : "Account"; + return { + _id: `a${String(id)}`, + taskId: "demo-task-1", + sampleId: id, + sampleContent: { text }, + labels: [label], + createdBy: "demo-user", + source: "guide", + aiAnnotation: null, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + } as AnnotationItem; +}); diff --git a/frontend/src/stories/mswHandlers.ts b/frontend/src/stories/mswHandlers.ts new file mode 100644 index 0000000..ee2738b --- /dev/null +++ b/frontend/src/stories/mswHandlers.ts @@ -0,0 +1,183 @@ +import { http, HttpResponse } from "msw"; +import { demoAnnotations, demoTask } from "./demoData"; + +const API = "http://localhost:8080"; + +export const handlersReady = [ + http.get(`${API}/api/tasks/getTask/:taskId`, () => { + return HttpResponse.json({ success: true, task: demoTask }); + }), + http.get(`${API}/api/annotate/get-annotations/:taskId`, () => { + return HttpResponse.json({ success: true, annotations: demoAnnotations }); + }), + http.get(`${API}/api/tasks/getTasks`, () => { + return HttpResponse.json({ success: true, tasks: [demoTask], count: 1 }); + }), + http.post(`${API}/api/inference`, () => { + return HttpResponse.json({ + label: ["Billing"], + reason: "Payment or invoice related issue detected in the text.", + span_text: "charged twice", + raw_response: "mocked", + system_prompt: "mocked-system-prompt", + user_prompt: "mocked-user-prompt", + }); + }), + http.post(`${API}/api/inference/`, () => { + return HttpResponse.json({ + label: ["Billing"], + reason: "Payment or invoice related issue detected in the text.", + span_text: "charged twice", + raw_response: "mocked", + system_prompt: "mocked-system-prompt", + user_prompt: "mocked-user-prompt", + }); + }), + http.post(`${API}/api/annotate/update-guide`, () => { + return HttpResponse.json({ success: true, message: "Guide annotation updated" }); + }), + http.post(`${API}/api/inference/rule-synthesis`, () => { + return HttpResponse.json({ + success: true, + rules: [ + "If text mentions duplicated or unexpected charges, label Billing.", + "If text mentions crashes, timeouts, or loading loops, label Technical.", + ], + }); + }), + http.post(`${API}/api/tasks/saveCodebook`, () => { + return HttpResponse.json({ success: true, message: "Codebook saved" }); + }), + http.post(`${API}/api/tasks/exportCodebook`, () => { + return HttpResponse.json({ success: true, message: "Codebook exported" }); + }), + http.post(`${API}/api/metrics/samples`, () => { + return HttpResponse.json({ success: true, filename: "sample_metrics_demo.csv" }); + }), + http.post(`${API}/api/metrics/metadata`, () => { + return HttpResponse.json({ success: true, filename: "metadata_metrics_demo.csv" }); + }), + http.post(`${API}/api/metrics/batches`, () => { + return HttpResponse.json({ success: true, filename: "batch_metrics_demo.csv" }); + }), +]; + +export const handlersSamplingPending = [ + http.get(`${API}/api/tasks/getTask/:taskId`, () => { + return HttpResponse.json({ success: true, task: { ...demoTask, status: "sampling_pending" } }); + }), + http.get(`${API}/api/annotate/get-annotations/:taskId`, () => { + return HttpResponse.json({ success: true, annotations: [] }); + }), + http.get(`${API}/api/tasks/getTasks`, () => { + return HttpResponse.json({ success: true, tasks: [{ ...demoTask, status: "sampling_pending" }], count: 1 }); + }), +]; + +export const handlersSamplingError = [ + http.get(`${API}/api/tasks/getTask/:taskId`, () => { + return HttpResponse.json({ + success: true, + task: { ...demoTask, status: "sampling_error" }, + }); + }), + http.get(`${API}/api/annotate/get-annotations/:taskId`, () => { + return HttpResponse.json({ success: true, annotations: [] }); + }), + http.get(`${API}/api/tasks/getTasks`, () => { + return HttpResponse.json({ + success: true, + tasks: [{ ...demoTask, status: "sampling_error" }], + count: 1, + }); + }), +]; + +export const handlersEmpty = [ + http.get(`${API}/api/tasks/getTask/:taskId`, () => { + return HttpResponse.json({ success: true, task: null }); + }), + http.get(`${API}/api/annotate/get-annotations/:taskId`, () => { + return HttpResponse.json({ success: true, annotations: [] }); + }), + http.get(`${API}/api/tasks/getTasks`, () => { + return HttpResponse.json({ success: true, tasks: [], count: 0 }); + }), +]; + +export const handlersPermissionDenied = [ + http.get(`${API}/api/tasks/getTask/:taskId`, () => { + return HttpResponse.json( + { success: false, message: "Forbidden" }, + { status: 403 }, + ); + }), + http.get(`${API}/api/annotate/get-annotations/:taskId`, () => { + return HttpResponse.json( + { success: false, message: "Forbidden" }, + { status: 403 }, + ); + }), + http.get(`${API}/api/tasks/getTasks`, () => { + return HttpResponse.json( + { success: false, message: "Forbidden" }, + { status: 403 }, + ); + }), +]; + +export const handlersServerError = [ + http.get(`${API}/api/tasks/getTask/:taskId`, () => { + return HttpResponse.json( + { success: false, message: "Internal server error" }, + { status: 500 }, + ); + }), + http.get(`${API}/api/annotate/get-annotations/:taskId`, () => { + return HttpResponse.json( + { success: false, message: "Internal server error" }, + { status: 500 }, + ); + }), + http.get(`${API}/api/tasks/getTasks`, () => { + return HttpResponse.json( + { success: false, message: "Internal server error" }, + { status: 500 }, + ); + }), +]; + +export function handlersSamplingTransition() { + let reads = 0; + + return [ + http.get(`${API}/api/tasks/getTask/:taskId`, () => { + reads += 1; + const status = reads < 6 ? "sampling_pending" : "ready"; + return HttpResponse.json({ + success: true, + task: { ...demoTask, status }, + }); + }), + http.get(`${API}/api/annotate/get-annotations/:taskId`, () => { + return HttpResponse.json({ success: true, annotations: demoAnnotations }); + }), + http.get(`${API}/api/tasks/getTasks`, () => { + return HttpResponse.json({ success: true, tasks: [demoTask], count: 1 }); + }), + http.post(`${API}/api/tasks/upload`, () => { + return HttpResponse.json({ + success: true, + message: "Upload successful", + filePath: "demo.csv", + }); + }), + http.post(`${API}/api/tasks/createTask`, () => { + return HttpResponse.json({ + success: true, + message: "Task created", + taskId: "demo-task-1", + }); + }), + ]; +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 8b0f57b..db4f551 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,7 +1,37 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +/// +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; // https://vite.dev/config/ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'; +import { playwright } from '@vitest/browser-playwright'; +const dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)); + +// More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon export default defineConfig({ plugins: [react()], -}) + test: { + projects: [{ + extends: true, + plugins: [ + // The plugin will run tests for the stories defined in your Storybook config + // See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest + storybookTest({ + configDir: path.join(dirname, '.storybook') + })], + test: { + name: 'storybook', + browser: { + enabled: true, + headless: true, + provider: playwright({}), + instances: [{ + browser: 'chromium' + }] + } + } + }] + } +}); \ No newline at end of file diff --git a/frontend/vitest.shims.d.ts b/frontend/vitest.shims.d.ts new file mode 100644 index 0000000..7782f28 --- /dev/null +++ b/frontend/vitest.shims.d.ts @@ -0,0 +1 @@ +/// \ No newline at end of file From b54e4fe53ff0a242e006480949fd318c6b792257 Mon Sep 17 00:00:00 2001 From: chandan-m Date: Fri, 8 May 2026 04:38:36 -0700 Subject: [PATCH 03/31] storybook demo - wip - 2 --- frontend/.storybook/preview.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/frontend/.storybook/preview.ts b/frontend/.storybook/preview.ts index 3e8dbcf..336b0d6 100644 --- a/frontend/.storybook/preview.ts +++ b/frontend/.storybook/preview.ts @@ -5,9 +5,6 @@ import "@mantine/notifications/styles.css"; import "../src/index.css"; import { initialize, mswLoader } from "msw-storybook-addon"; -// Storybook-only runtime override for AI annotation demo pacing. -(globalThis as { __ANNOTATION_BATCH_SIZE__?: number }).__ANNOTATION_BATCH_SIZE__ = 3; - initialize(); const preview: Preview = { From 21a4e8c40f94eb9c885f0853e56a9a2bf9d5cbf5 Mon Sep 17 00:00:00 2001 From: chandan-m Date: Fri, 8 May 2026 04:50:06 -0700 Subject: [PATCH 04/31] fix - end of review flow --- .../AIAnnotationPage/AIAnnotationPage.tsx | 2 ++ .../components/AIReviewPanel.tsx | 8 ++++--- .../components/MetricsModal.tsx | 16 +++++++++++-- .../hooks/useAIAnnotationController.ts | 24 +++++++++++++++++++ .../hooks/useAnnotationReviewFlow.ts | 3 ++- 5 files changed, 47 insertions(+), 6 deletions(-) diff --git a/frontend/src/pages/AIAnnotationPage/AIAnnotationPage.tsx b/frontend/src/pages/AIAnnotationPage/AIAnnotationPage.tsx index bb9d354..e30f36e 100644 --- a/frontend/src/pages/AIAnnotationPage/AIAnnotationPage.tsx +++ b/frontend/src/pages/AIAnnotationPage/AIAnnotationPage.tsx @@ -128,6 +128,7 @@ export default function AnnotationPage() { files={controller.metricsFiles} onClose={() => controller.setMetricsModalOpen(false)} onDownload={controller.handleDownloadMetrics} + onExportCodebook={controller.handleExportCodebookFromModal} /> void; onNext: () => void; isCommitStep: boolean; + isCompleteStep: boolean; nextDisabled: boolean; onSetCorrect: (value: boolean) => void; onSetSpanFeedback: (value: boolean) => void; @@ -87,6 +88,7 @@ export function AIReviewPanel({ onPrev, onNext, isCommitStep, + isCompleteStep, nextDisabled, onSetCorrect, onSetSpanFeedback, @@ -117,13 +119,13 @@ export function AIReviewPanel({ Previous diff --git a/frontend/src/pages/AIAnnotationPage/components/MetricsModal.tsx b/frontend/src/pages/AIAnnotationPage/components/MetricsModal.tsx index 7a9540b..611dbb5 100644 --- a/frontend/src/pages/AIAnnotationPage/components/MetricsModal.tsx +++ b/frontend/src/pages/AIAnnotationPage/components/MetricsModal.tsx @@ -7,9 +7,17 @@ interface MetricsModalProps { files: MetricsFiles; onClose: () => void; onDownload: (filename?: string) => void; + onExportCodebook: () => void; } -export function MetricsModal({ opened, isLight, files, onClose, onDownload }: MetricsModalProps) { +export function MetricsModal({ + opened, + isLight, + files, + onClose, + onDownload, + onExportCodebook, +}: MetricsModalProps) { return ( - Review completed! You can download the captured metrics below. + Review completed! You can download the generated codebook metrics here. + + You can download the captured metrics below. diff --git a/frontend/src/pages/AIAnnotationPage/hooks/useAIAnnotationController.ts b/frontend/src/pages/AIAnnotationPage/hooks/useAIAnnotationController.ts index f70c695..3da13ee 100644 --- a/frontend/src/pages/AIAnnotationPage/hooks/useAIAnnotationController.ts +++ b/frontend/src/pages/AIAnnotationPage/hooks/useAIAnnotationController.ts @@ -42,6 +42,7 @@ export const useAIAnnotationController = () => { const [metricsModalOpen, setMetricsModalOpen] = useState(false); const [metricsFiles, setMetricsFiles] = useState({}); + const [reviewCompleted, setReviewCompleted] = useState(false); const [totalCorrect, setTotalCorrect] = useState(0); const [totalAttempted, setTotalAttempted] = useState(0); const [predictedAccuracy] = useState(null); @@ -143,6 +144,11 @@ export const useAIAnnotationController = () => { }; const handleNextOrCommit = async () => { + if (reviewCompleted) { + setMetricsModalOpen(true); + return; + } + const shouldGenerateMetrics = reviewState.isLastBatch; let committedCodebook: string[] = []; @@ -184,6 +190,7 @@ export const useAIAnnotationController = () => { } setMetricsModalOpen(true); + setReviewCompleted(true); } }; @@ -232,8 +239,25 @@ export const useAIAnnotationController = () => { actualBatchSize: reviewState.actualBatchSize, currentBatchStartIndex: reviewState.currentBatchStartIndex, isCommitStep: reviewState.currentBatchProgress === reviewState.actualBatchSize, + isCompleteStep: + reviewCompleted && + reviewState.currentIndex === Math.max(reviewState.totalSamples - 1, 0), nextDisabled: reviewState.nextDisabled, + handleExportCodebookFromModal: async () => { + if (!task?._id) return; + try { + await exportCodebookSnapshot( + task._id, + codebookState.codebook, + codebookState.lastPromptUsed, + ); + toast.success("Codebook exported."); + } catch (error: any) { + toast.error(error?.message || "Failed to export codebook."); + } + }, + handleNextOrCommit, goPrev: reviewState.goPrev, goHome, diff --git a/frontend/src/pages/AIAnnotationPage/hooks/useAnnotationReviewFlow.ts b/frontend/src/pages/AIAnnotationPage/hooks/useAnnotationReviewFlow.ts index 3232228..ca18ff3 100644 --- a/frontend/src/pages/AIAnnotationPage/hooks/useAnnotationReviewFlow.ts +++ b/frontend/src/pages/AIAnnotationPage/hooks/useAnnotationReviewFlow.ts @@ -355,7 +355,8 @@ export function useAnnotationReviewFlow({ if (currentIndex < totalSamples - 1) { setCurrentIndex(currentIndex + 1); } else { - setCurrentIndex(totalSamples); + // Keep focus on last sample after completion to avoid an empty n+1/n state. + setCurrentIndex(Math.max(totalSamples - 1, 0)); } }; From 1d4f3e9164fde5af6d7171a249a44ad26f0b8375 Mon Sep 17 00:00:00 2001 From: chandan-m Date: Fri, 8 May 2026 04:55:07 -0700 Subject: [PATCH 05/31] fix - type errors --- frontend/src/lib/auth.ts | 1 + frontend/src/pages/SubsamplingPage.tsx | 2 +- frontend/src/stories/DemoFlows.stories.tsx | 4 ++-- frontend/src/stories/demoData.ts | 9 ++++++--- frontend/tsconfig.node.json | 2 +- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/frontend/src/lib/auth.ts b/frontend/src/lib/auth.ts index 0bdc1fd..29800c5 100644 --- a/frontend/src/lib/auth.ts +++ b/frontend/src/lib/auth.ts @@ -37,6 +37,7 @@ export function buildUserFromToken(token: string): User | null { } return { id: payload.userId, + name: payload.username, username: payload.username, email: payload.email, }; diff --git a/frontend/src/pages/SubsamplingPage.tsx b/frontend/src/pages/SubsamplingPage.tsx index c88338c..775739c 100644 --- a/frontend/src/pages/SubsamplingPage.tsx +++ b/frontend/src/pages/SubsamplingPage.tsx @@ -29,7 +29,6 @@ import { useState, useEffect, useMemo } from "react"; import { useNavigate } from "react-router-dom"; import { createTask, - checkValFileExists, getUserTasks, } from "../services/tasks.service"; import { embedDataset } from "../services/embedding.service"; @@ -402,6 +401,7 @@ export default function SubsamplingPage() { const payload: EmbedDatasetRequest = { file_path: fileName, text_col: chosenCol, + label_col: "task_label", taskId: task?._id ?? "", userId: user?.id || "00000", labels: taskLabels.filter( diff --git a/frontend/src/stories/DemoFlows.stories.tsx b/frontend/src/stories/DemoFlows.stories.tsx index acea1dd..35b4472 100644 --- a/frontend/src/stories/DemoFlows.stories.tsx +++ b/frontend/src/stories/DemoFlows.stories.tsx @@ -31,7 +31,7 @@ const makeStore = () => reducer: { user: userReducer }, preloadedState: { user: { - user: { id: "demo-user", username: "demo", email: "demo@example.com" }, + user: { id: "demo-user", name: "Demo User", username: "demo", email: "demo@example.com" }, accessToken: "demo-token", refreshToken: "demo-refresh", }, @@ -139,7 +139,7 @@ function SessionExpiredApp() { reducer: { user: userReducer }, preloadedState: { user: { - user: { id: "demo-user", username: "demo", email: "demo@example.com" }, + user: { id: "demo-user", name: "Demo User", username: "demo", email: "demo@example.com" }, accessToken: expiredJwt, refreshToken: "demo-refresh", }, diff --git a/frontend/src/stories/demoData.ts b/frontend/src/stories/demoData.ts index fb9f981..248aa6e 100644 --- a/frontend/src/stories/demoData.ts +++ b/frontend/src/stories/demoData.ts @@ -7,10 +7,12 @@ export const demoTask: Task = { description: "Classify support tickets by intent for triage automation.", type: "Multiclass", labels: [ - { name: "Billing", description: "Payments, invoices, and charges" }, - { name: "Technical", description: "Bugs, outages, feature failures" }, - { name: "Account", description: "Login, profile, access, permissions" }, + { name: "Billing", definition: "Payments, invoices, and charges", keywords: ["payment", "invoice", "charge"] }, + { name: "Technical", definition: "Bugs, outages, feature failures", keywords: ["bug", "crash", "timeout"] }, + { name: "Account", definition: "Login, profile, access, permissions", keywords: ["login", "access", "password"] }, ], + labelColumn: "task_label", + modelName: "mistral:7b", columns: ["text"], file: "demo.csv", status: "ready", @@ -22,6 +24,7 @@ export const demoTask: Task = { "Technical: export/reporting errors and stale loading states", ], userID: "demo-user", + createdAt: new Date().toISOString(), }; const samples = [ diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json index 2a6ae99..00e70f9 100644 --- a/frontend/tsconfig.node.json +++ b/frontend/tsconfig.node.json @@ -26,5 +26,5 @@ "noFallthroughCasesInSwitch": true, "noUncheckedSideEffectImports": true }, - "include": ["vite.config.ts", "src", "../common/src"] + "include": ["vite.config.ts"] } From a2a50380c12045d3b7c2b6d3d23ab191e82af1c0 Mon Sep 17 00:00:00 2001 From: chandan-m Date: Fri, 8 May 2026 05:01:32 -0700 Subject: [PATCH 06/31] change stories --- frontend/src/stories/DemoFlows.stories.tsx | 52 +++++----------------- 1 file changed, 12 insertions(+), 40 deletions(-) diff --git a/frontend/src/stories/DemoFlows.stories.tsx b/frontend/src/stories/DemoFlows.stories.tsx index 35b4472..4949bea 100644 --- a/frontend/src/stories/DemoFlows.stories.tsx +++ b/frontend/src/stories/DemoFlows.stories.tsx @@ -17,13 +17,10 @@ import AnnotateDatasetLandingPage from "../pages/AnnotateDatasetLandingPage"; import AnnotateDatasetPage from "../pages/AnnotateDatasetPage"; import { ProtectedRoute } from "../components/auth/ProtectedRoute"; import { - handlersEmpty, - handlersPermissionDenied, handlersReady, handlersSamplingError, handlersSamplingPending, handlersSamplingTransition, - handlersServerError, } from "./mswHandlers"; const makeStore = () => @@ -77,61 +74,36 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Landing: Story = { +export const HappyPath_MainJourney: Story = { render: () => , parameters: { msw: { handlers: handlersReady } }, }; -export const TaskCreationPage: Story = { - render: () => , - parameters: { msw: { handlers: handlersReady } }, -}; - -export const CodebookLanding: Story = { - render: () => , - parameters: { msw: { handlers: handlersReady } }, -}; - -export const CodebookTaskUploadPage: Story = { - render: () => , - parameters: { msw: { handlers: handlersReady } }, +HappyPath_MainJourney.parameters = { + ...HappyPath_MainJourney.parameters, + docs: { + description: { + story: + "Start at landing page and use in-app navigation to explore the full happy path: codebook flow, upload page, annotation landing, and review workflow.", + }, + }, }; -export const SamplingPending: Story = { +export const State_Pending_Sampling: Story = { render: () => , parameters: { msw: { handlers: handlersSamplingPending } }, }; -export const AIReview_ReadyImmediate: Story = { - render: () => , - parameters: { msw: { handlers: handlersReady } }, -}; - -export const AIReview_PendingThenReady: Story = { +export const State_Pending_ToReadyTransition: Story = { render: () => , parameters: { msw: { handlers: handlersSamplingTransition() } }, }; -export const SamplingErrorState: Story = { +export const State_Error_SamplingFailed: Story = { render: () => , parameters: { msw: { handlers: handlersSamplingError } }, }; -export const EmptyState: Story = { - render: () => , - parameters: { msw: { handlers: handlersEmpty } }, -}; - -export const PermissionDeniedState: Story = { - render: () => , - parameters: { msw: { handlers: handlersPermissionDenied } }, -}; - -export const ServerErrorState: Story = { - render: () => , - parameters: { msw: { handlers: handlersServerError } }, -}; - function SessionExpiredApp() { const expiredJwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJ1IiwiZXhwIjoxMDAwLCJ1c2VybmFtZSI6ImRlbW8iLCJlbWFpbCI6ImRlbW9AZXhhbXBsZS5jb20ifQ.sig"; From 69c2a2c72c986beaf2547064449a23ef2e036ef6 Mon Sep 17 00:00:00 2001 From: chandan-m Date: Fri, 8 May 2026 05:13:21 -0700 Subject: [PATCH 07/31] storybook demo - wip - 3 --- frontend/src/stories/demoData.ts | 3 -- frontend/src/stories/mswHandlers.ts | 46 ++++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/frontend/src/stories/demoData.ts b/frontend/src/stories/demoData.ts index 248aa6e..ea77ed8 100644 --- a/frontend/src/stories/demoData.ts +++ b/frontend/src/stories/demoData.ts @@ -19,9 +19,6 @@ export const demoTask: Task = { codebook: [ "Billing: payment failed, chargeback, invoice mismatch", "Technical: app crashed, timeout, bug report", - "Account: password reset, MFA lockout, account recovery", - "Billing: duplicate renewal or unexpected subscription charge", - "Technical: export/reporting errors and stale loading states", ], userID: "demo-user", createdAt: new Date().toISOString(), diff --git a/frontend/src/stories/mswHandlers.ts b/frontend/src/stories/mswHandlers.ts index ee2738b..d58daee 100644 --- a/frontend/src/stories/mswHandlers.ts +++ b/frontend/src/stories/mswHandlers.ts @@ -4,6 +4,17 @@ import { demoAnnotations, demoTask } from "./demoData"; const API = "http://localhost:8080"; export const handlersReady = [ + http.post(`${API}/api/tasks/create`, () => { + return HttpResponse.json({ + success: true, + taskId: "demo-task-1", + task: demoTask, + fileName: "demo.csv", + message: "Task created. Sampling is pending.", + valSummary: { rows: 120, columns: ["text", "task_label"] }, + restSummary: { rows: 880, columns: ["text"] }, + }); + }), http.get(`${API}/api/tasks/getTask/:taskId`, () => { return HttpResponse.json({ success: true, task: demoTask }); }), @@ -36,15 +47,34 @@ export const handlersReady = [ http.post(`${API}/api/annotate/update-guide`, () => { return HttpResponse.json({ success: true, message: "Guide annotation updated" }); }), - http.post(`${API}/api/inference/rule-synthesis`, () => { - return HttpResponse.json({ - success: true, - rules: [ - "If text mentions duplicated or unexpected charges, label Billing.", - "If text mentions crashes, timeouts, or loading loops, label Technical.", - ], + (() => { + let commitCount = 0; + return http.post(`${API}/api/inference/rule-synthesis`, () => { + commitCount += 1; + if (commitCount === 1) { + return HttpResponse.json({ + success: true, + rules: [ + "If text mentions duplicated or unexpected charges, label Billing.", + "If text mentions crashes, timeouts, or loading loops, label Technical.", + ], + }); + } + if (commitCount === 2) { + return HttpResponse.json({ + success: true, + rules: [ + "If text mentions password reset or lockout, label Account.", + "If text mentions profile/email/access changes, label Account.", + ], + }); + } + return HttpResponse.json({ + success: true, + rules: [], + }); }); - }), + })(), http.post(`${API}/api/tasks/saveCodebook`, () => { return HttpResponse.json({ success: true, message: "Codebook saved" }); }), From 40e566cb2973311465e2b44347fc61f8e738df05 Mon Sep 17 00:00:00 2001 From: chandan-m Date: Fri, 8 May 2026 05:50:33 -0700 Subject: [PATCH 08/31] storybook demo - wip - 3 --- frontend/.storybook/preview.ts | 2 ++ frontend/.storybook/storybook-overrides.css | 24 +++++++++++++++++ frontend/src/stories/DemoFlows.stories.tsx | 10 ++++++- frontend/src/stories/mswHandlers.ts | 30 +++++++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 frontend/.storybook/storybook-overrides.css diff --git a/frontend/.storybook/preview.ts b/frontend/.storybook/preview.ts index 336b0d6..cc29859 100644 --- a/frontend/.storybook/preview.ts +++ b/frontend/.storybook/preview.ts @@ -3,6 +3,7 @@ import "@mantine/core/styles.css"; import "@mantine/dropzone/styles.css"; import "@mantine/notifications/styles.css"; import "../src/index.css"; +import "./storybook-overrides.css"; import { initialize, mswLoader } from "msw-storybook-addon"; initialize(); @@ -10,6 +11,7 @@ initialize(); const preview: Preview = { loaders: [mswLoader], parameters: { + layout: "fullscreen", controls: { matchers: { color: /(background|color)$/i, diff --git a/frontend/.storybook/storybook-overrides.css b/frontend/.storybook/storybook-overrides.css new file mode 100644 index 0000000..d1b4d4d --- /dev/null +++ b/frontend/.storybook/storybook-overrides.css @@ -0,0 +1,24 @@ +/* Storybook-only layout normalization for full-app page stories */ +#storybook-root [class*="_page_"] [class*="_hero_"], +#storybook-root [class*="_page_"] [class*="_tableSection_"] { + --container-size: 100% !important; + max-width: 100% !important; + width: 100% !important; + margin-left: 0 !important; + margin-right: 0 !important; +} + +#storybook-root [class*="_page_"] [class*="mantine-Container-root"] { + --container-size: 100% !important; + max-width: 100% !important; + width: 100% !important; +} + +/* Force Storybook centered mode to behave like fullscreen for app stories */ +.sb-show-main.sb-main-centered #storybook-root { + margin: 0 !important; + padding: 0 !important; + max-width: none !important; + width: 100% !important; + height: 100% !important; +} diff --git a/frontend/src/stories/DemoFlows.stories.tsx b/frontend/src/stories/DemoFlows.stories.tsx index 4949bea..6010b4f 100644 --- a/frontend/src/stories/DemoFlows.stories.tsx +++ b/frontend/src/stories/DemoFlows.stories.tsx @@ -38,6 +38,7 @@ const makeStore = () => function DemoApp({ route = "/", element }: { route?: string; element?: ReactNode }) { const router = createMemoryRouter( [ + { path: "/login", element: }, { path: "/", element: , @@ -68,7 +69,14 @@ function DemoApp({ route = "/", element }: { route?: string; element?: ReactNode const meta: Meta = { title: "Demo/App Flows", component: DemoApp, - parameters: { layout: "fullscreen" }, + parameters: { + layout: "fullscreen", + docs: { + canvas: { + layout: "fullscreen", + }, + }, + }, }; export default meta; diff --git a/frontend/src/stories/mswHandlers.ts b/frontend/src/stories/mswHandlers.ts index d58daee..25c2bca 100644 --- a/frontend/src/stories/mswHandlers.ts +++ b/frontend/src/stories/mswHandlers.ts @@ -4,6 +4,26 @@ import { demoAnnotations, demoTask } from "./demoData"; const API = "http://localhost:8080"; export const handlersReady = [ + http.post(`${API}/api/account/login`, async ({ request }) => { + const body = (await request.json().catch(() => ({}))) as { + email?: string; + }; + const email = body?.email || "demo@example.com"; + const username = email.split("@")[0] || "demo"; + + return HttpResponse.json({ + success: true, + jwtToken: "demo-jwt-token", + jwtRefreshToken: "demo-refresh-token", + user: { + id: "demo-user", + name: "Demo User", + username, + email, + }, + message: "Login successful", + }); + }), http.post(`${API}/api/tasks/create`, () => { return HttpResponse.json({ success: true, @@ -24,6 +44,16 @@ export const handlersReady = [ http.get(`${API}/api/tasks/getTasks`, () => { return HttpResponse.json({ success: true, tasks: [demoTask], count: 1 }); }), + http.delete(`${API}/api/tasks/delete/:taskId`, () => { + // Storybook no-op: keep demo task list stable after delete action. + return HttpResponse.json({ + success: true, + message: "No-op in demo mode", + deletedTaskId: null, + deletedFilesCount: 0, + deletedAnnotationsCount: 0, + }); + }), http.post(`${API}/api/inference`, () => { return HttpResponse.json({ label: ["Billing"], From 7753f3cbbb363def68c9bf040607d0b336a9a44f Mon Sep 17 00:00:00 2001 From: chandan-m Date: Fri, 8 May 2026 05:53:56 -0700 Subject: [PATCH 09/31] default light mode --- frontend/src/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 9dd6358..98eb624 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -17,7 +17,7 @@ export default function App() { From 83c18e79b3a489b9131571dda55d85a8e209bef4 Mon Sep 17 00:00:00 2001 From: chandan-m Date: Fri, 8 May 2026 06:00:07 -0700 Subject: [PATCH 10/31] fix light mode --- .../src/pages/AnnotateDatasetLandingPage.module.css | 11 +++++++++++ frontend/src/pages/CodebookLandingPage.module.css | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/frontend/src/pages/AnnotateDatasetLandingPage.module.css b/frontend/src/pages/AnnotateDatasetLandingPage.module.css index 3bf702d..2f4765a 100644 --- a/frontend/src/pages/AnnotateDatasetLandingPage.module.css +++ b/frontend/src/pages/AnnotateDatasetLandingPage.module.css @@ -42,7 +42,13 @@ border-color: rgba(15, 20, 24, 0.1); } +:global([data-mantine-color-scheme="light"]) .mockPanel { + background: #ffffff; + border-color: rgba(15, 20, 24, 0.12); +} + :global([data-mantine-color-scheme="light"]) .subtitle, +:global([data-mantine-color-scheme="light"]) .secondaryCta, :global([data-mantine-color-scheme="light"]) .sectionHint, :global([data-mantine-color-scheme="light"]) .walkthroughText, :global([data-mantine-color-scheme="light"]) .walkthroughFootnote { @@ -58,6 +64,11 @@ color: #0f1418; } +:global([data-mantine-color-scheme="light"]) .summaryChip { + background: rgba(15, 20, 24, 0.04); + border-color: rgba(15, 20, 24, 0.14); +} + .page::before { content: ""; position: absolute; diff --git a/frontend/src/pages/CodebookLandingPage.module.css b/frontend/src/pages/CodebookLandingPage.module.css index f7970a1..d638a52 100644 --- a/frontend/src/pages/CodebookLandingPage.module.css +++ b/frontend/src/pages/CodebookLandingPage.module.css @@ -43,6 +43,11 @@ border-color: rgba(15, 20, 24, 0.1); } +:global([data-mantine-color-scheme="light"]) .mockPanel { + background: #ffffff; + border-color: rgba(15, 20, 24, 0.12); +} + :global([data-mantine-color-scheme="light"]) .subtitle, :global([data-mantine-color-scheme="light"]) .secondaryCta, :global([data-mantine-color-scheme="light"]) .sectionHint, @@ -61,6 +66,11 @@ color: #0f1418; } +:global([data-mantine-color-scheme="light"]) .summaryChip { + background: rgba(15, 20, 24, 0.04); + border-color: rgba(15, 20, 24, 0.14); +} + .page::before { content: ""; position: absolute; From bffaf8b8d852a54ec9e3f22a712d6d6e6790d62e Mon Sep 17 00:00:00 2001 From: chandan-m Date: Fri, 8 May 2026 06:21:05 -0700 Subject: [PATCH 11/31] storybook deployment --- frontend/.storybook/demo.html | 32 ++++++++++++++++++++++++++++++++ frontend/package.json | 5 +++-- 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 frontend/.storybook/demo.html diff --git a/frontend/.storybook/demo.html b/frontend/.storybook/demo.html new file mode 100644 index 0000000..30295ea --- /dev/null +++ b/frontend/.storybook/demo.html @@ -0,0 +1,32 @@ + + + + + + Annotation Tool Demo + + + + + + diff --git a/frontend/package.json b/frontend/package.json index 8b5f701..4f52fe9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,7 +9,8 @@ "lint": "eslint .", "preview": "vite preview", "storybook": "storybook dev -p 6006", - "build-storybook": "storybook build" + "build-storybook": "storybook build", + "build-storybook-demo": "npm run build-storybook && cp .storybook/demo.html storybook-static/demo.html" }, "dependencies": { "@mantine/core": "^8.3.10", @@ -62,4 +63,4 @@ "public" ] } -} \ No newline at end of file +} From b2a4ace6c27847dbd9b256973b148ea1da4d643c Mon Sep 17 00:00:00 2001 From: chandan-m Date: Fri, 8 May 2026 06:24:18 -0700 Subject: [PATCH 12/31] storybook deployment - gh actions cicd --- .github/workflows/deploy-storybook-pages.yml | 51 ++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 .github/workflows/deploy-storybook-pages.yml diff --git a/.github/workflows/deploy-storybook-pages.yml b/.github/workflows/deploy-storybook-pages.yml new file mode 100644 index 0000000..3298d24 --- /dev/null +++ b/.github/workflows/deploy-storybook-pages.yml @@ -0,0 +1,51 @@ +name: Deploy Storybook Demo to GitHub Pages + +on: + push: + branches: + - static-ui + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: true + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: frontend/package-lock.json + + - name: Install dependencies + run: npm ci + working-directory: frontend + + - name: Build Storybook demo static site + run: npm run build-storybook-demo + working-directory: frontend + + - name: Setup Pages + uses: actions/configure-pages@v5 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: frontend/storybook-static + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 From 5a66410012c8e2dc8ee8527cbd25c62d5332caf6 Mon Sep 17 00:00:00 2001 From: chandan-m Date: Wed, 13 May 2026 00:41:00 -0700 Subject: [PATCH 13/31] Remove unimplemented Annotate Dataset task pages --- frontend/src/components/layout/Sidebar.tsx | 126 ++------------------- frontend/src/pages/CodebookLandingPage.tsx | 9 -- frontend/src/pages/LandingPage.tsx | 17 +-- frontend/src/router.tsx | 20 ---- frontend/src/stories/DemoFlows.stories.tsx | 14 +-- 5 files changed, 14 insertions(+), 172 deletions(-) diff --git a/frontend/src/components/layout/Sidebar.tsx b/frontend/src/components/layout/Sidebar.tsx index 469b6c5..990f2dc 100644 --- a/frontend/src/components/layout/Sidebar.tsx +++ b/frontend/src/components/layout/Sidebar.tsx @@ -15,7 +15,6 @@ import { IconLayoutSidebar, IconUserCircle, IconBook2, - IconPencil, IconDots, IconTrash, IconMoon, @@ -52,7 +51,6 @@ type TaskRouteBuilder = (task: Task) => string; export const SideBar = ({ collapsed, toggleCollapsed }: SideBarProps) => { const TASK_SECTION_HEADER_HEIGHT = 32; - const TASK_SECTION_GAP = 6; const TASK_SECTION_TOP_PADDING = 4; const user = useSelector((state: IRootState) => state.user.user); const accessToken = useSelector( @@ -76,36 +74,26 @@ export const SideBar = ({ collapsed, toggleCollapsed }: SideBarProps) => { const [error, setError] = useState(); const [sectionsOpen, setSectionsOpen] = useState({ codebook: true, - annotation: true, }); const [overflowState, setOverflowState] = useState({ codebook: false, - annotation: false, }); const [fadeState, setFadeState] = useState({ codebook: false, - annotation: false, }); const [listHeights, setListHeights] = useState({ codebook: 0, - annotation: 0, }); const taskSectionsRef = useRef(null); const codebookListRef = useRef(null); - const annotationListRef = useRef(null); const codebookTasks = useMemo( () => tasks.filter((task) => !task.codebookSourceTaskId), [tasks], ); - const annotationOnlyTasks = useMemo( - () => tasks.filter((task) => Boolean(task.codebookSourceTaskId)), - [tasks], - ); - const updateListOverflow = useCallback((list: "codebook" | "annotation") => { - const target = - list === "codebook" ? codebookListRef.current : annotationListRef.current; + const updateListOverflow = useCallback((list: "codebook") => { + const target = codebookListRef.current; if (!target) return; const hasOverflow = target.scrollHeight > target.clientHeight + 1; const canScrollMore = @@ -125,64 +113,24 @@ export const SideBar = ({ collapsed, toggleCollapsed }: SideBarProps) => { if (!container) return; const codebookOpen = sectionsOpen.codebook; - const annotationOpen = sectionsOpen.annotation; - const available = container.clientHeight - TASK_SECTION_TOP_PADDING - - TASK_SECTION_HEADER_HEIGHT - - TASK_SECTION_HEADER_HEIGHT - - TASK_SECTION_GAP; + TASK_SECTION_HEADER_HEIGHT; if (available <= 0) { - setListHeights({ codebook: 0, annotation: 0 }); + setListHeights({ codebook: 0 }); return; } - if (codebookOpen && !annotationOpen) { - setListHeights({ codebook: available, annotation: 0 }); - return; - } - if (!codebookOpen && annotationOpen) { - setListHeights({ codebook: 0, annotation: available }); - return; - } - if (!codebookOpen && !annotationOpen) { - setListHeights({ codebook: 0, annotation: 0 }); + if (!codebookOpen) { + setListHeights({ codebook: 0 }); return; } const codebookNeed = codebookListRef.current?.scrollHeight ?? 0; - const annotationNeed = annotationListRef.current?.scrollHeight ?? 0; - - const base = available / 2; - let codebookAlloc = Math.min(base, codebookNeed); - let annotationAlloc = Math.min(base, annotationNeed); - - let remainder = available - codebookAlloc - annotationAlloc; - if (remainder > 0) { - const codebookMissing = Math.max(0, codebookNeed - codebookAlloc); - const annotationMissing = Math.max(0, annotationNeed - annotationAlloc); - const totalMissing = codebookMissing + annotationMissing; - - if (totalMissing > 0) { - const codebookExtra = Math.min( - codebookMissing, - (remainder * codebookMissing) / totalMissing, - ); - codebookAlloc += codebookExtra; - remainder -= codebookExtra; - - const annotationExtra = Math.min(annotationMissing, remainder); - annotationAlloc += annotationExtra; - } - } - - setListHeights({ - codebook: Math.max(0, Math.floor(codebookAlloc)), - annotation: Math.max(0, Math.floor(annotationAlloc)), - }); - }, [sectionsOpen.annotation, sectionsOpen.codebook]); + setListHeights({ codebook: Math.max(0, Math.min(available, codebookNeed)) }); + }, [sectionsOpen.codebook]); const fetchTasks = useCallback(async () => { if (!accessToken) return; @@ -214,8 +162,7 @@ export const SideBar = ({ collapsed, toggleCollapsed }: SideBarProps) => { const currentPath = window.location.pathname; const isOnDeletedTaskPath = - currentPath.endsWith(`/codebook-creation/${task._id}`) || - currentPath.endsWith(`/annotate-dataset/${task._id}`); + currentPath.endsWith(`/codebook-creation/${task._id}`); if (isOnDeletedTaskPath) { navigate("/"); } @@ -246,12 +193,10 @@ export const SideBar = ({ collapsed, toggleCollapsed }: SideBarProps) => { useEffect(() => { updateListOverflow("codebook"); - updateListOverflow("annotation"); updateSectionLayout(); }, [ tasks, sectionsOpen.codebook, - sectionsOpen.annotation, updateListOverflow, updateSectionLayout, ]); @@ -260,7 +205,6 @@ export const SideBar = ({ collapsed, toggleCollapsed }: SideBarProps) => { const handleResize = () => { updateSectionLayout(); updateListOverflow("codebook"); - updateListOverflow("annotation"); }; window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); @@ -336,7 +280,6 @@ export const SideBar = ({ collapsed, toggleCollapsed }: SideBarProps) => { ); const isCreateCodebookActive = pathname === "/new-codebook"; - const isAnnotateDatasetActive = pathname === "/new-annotation"; const renderTaskSection = useCallback( ({ @@ -347,7 +290,7 @@ export const SideBar = ({ collapsed, toggleCollapsed }: SideBarProps) => { listHeight, routeBuilder, }: { - keyName: "codebook" | "annotation"; + keyName: "codebook"; title: string; tasksList: Task[]; listRef: MutableRefObject; @@ -467,26 +410,6 @@ export const SideBar = ({ collapsed, toggleCollapsed }: SideBarProps) => { - - { - navigate("/new-annotation"); - }} - w="100%" - size="xl" - radius="md" - c="var(--app-sidebar-text)" - variant="subtle" - classNames={{ - root: `sidebar-button-collapsed ${ - isAnnotateDatasetActive ? "sidebar-button-collapsed-active" : "" - }`, - }} - > - - - - { Create Codebook - - Your Tasks @@ -641,14 +543,6 @@ export const SideBar = ({ collapsed, toggleCollapsed }: SideBarProps) => { routeBuilder: (task) => `/codebook-creation/${task._id}`, })} - {renderTaskSection({ - keyName: "annotation", - title: "Data Annotation", - tasksList: annotationOnlyTasks, - listRef: annotationListRef, - listHeight: listHeights.annotation, - routeBuilder: (task) => `/annotate-dataset/${task._id}`, - })} )} diff --git a/frontend/src/pages/CodebookLandingPage.tsx b/frontend/src/pages/CodebookLandingPage.tsx index d4da8c3..3ccba52 100644 --- a/frontend/src/pages/CodebookLandingPage.tsx +++ b/frontend/src/pages/CodebookLandingPage.tsx @@ -48,15 +48,6 @@ export default function CodebookLandingPage() { > Start a new task - diff --git a/frontend/src/pages/LandingPage.tsx b/frontend/src/pages/LandingPage.tsx index cd70a36..8eac022 100644 --- a/frontend/src/pages/LandingPage.tsx +++ b/frontend/src/pages/LandingPage.tsx @@ -14,7 +14,6 @@ import { import { IconArrowRight, IconChecklist, - IconFileText, IconFolderOpen, } from "@tabler/icons-react"; import { useNavigate } from "react-router-dom"; @@ -35,20 +34,6 @@ const options = [ cta: "Go to codebook task", path: "/codebook-landing", }, - { - title: "Annotate Dataset", - description: - "Use an existing codebook to label a fresh unlabeled dataset in a dedicated annotation workflow.", - requirements: [ - "Codebook file", - "Unlabeled dataset to annotate", - "Task definition file", - "Label definitions file", - ], - icon: IconFileText, - cta: "Go to annotation task", - path: "/annotate-dataset-landing", - }, ]; export default function LandingPage() { @@ -68,7 +53,7 @@ export default function LandingPage() { - + {options.map((option) => { const Icon = option.icon; diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx index 4092119..71f0c07 100644 --- a/frontend/src/router.tsx +++ b/frontend/src/router.tsx @@ -2,15 +2,11 @@ import { createBrowserRouter } from "react-router-dom"; import LoginPage from "./pages/LoginPage"; import SignUpPage from "./pages/SignUpPage"; import { AppLayout } from "./components/layout/AppLayout"; -import LandingPage from "./pages/LandingPage"; import DatasetUploadPage from "./pages/DatasetUploadPage"; import { ProtectedRoute } from "./components/auth/ProtectedRoute"; import { PublicRoute } from "./components/auth/PublicRoute"; import AnnotationPage from "./pages/AIAnnotationPage"; -import NewAnnotationTaskPage from "./pages/NewAnnotationTaskPage"; -import AnnotateDatasetPage from "./pages/AnnotateDatasetPage"; import CodebookLandingPage from "./pages/CodebookLandingPage"; -import AnnotateDatasetLandingPage from "./pages/AnnotateDatasetLandingPage"; export const router = createBrowserRouter([ { @@ -37,10 +33,6 @@ export const router = createBrowserRouter([ children: [ { path: "", - element: , - }, - { - path: "/codebook-landing", element: , }, { @@ -51,18 +43,6 @@ export const router = createBrowserRouter([ path: "/codebook-creation/:taskId?", element: , }, - { - path: "/new-annotation", - element: , - }, - { - path: "/annotate-dataset/:id", - element: , - }, - { - path: "/annotate-dataset-landing", - element: , - }, ], }, ], diff --git a/frontend/src/stories/DemoFlows.stories.tsx b/frontend/src/stories/DemoFlows.stories.tsx index 6010b4f..02b4908 100644 --- a/frontend/src/stories/DemoFlows.stories.tsx +++ b/frontend/src/stories/DemoFlows.stories.tsx @@ -8,13 +8,9 @@ import { createMemoryRouter, RouterProvider } from "react-router-dom"; import userReducer from "../store/userSlice"; import { AppLayout } from "../components/layout/AppLayout"; import AnnotationPage from "../pages/AIAnnotationPage"; -import NewAnnotationTaskPage from "../pages/NewAnnotationTaskPage"; -import LandingPage from "../pages/LandingPage"; import LoginPage from "../pages/LoginPage"; import CodebookLandingPage from "../pages/CodebookLandingPage"; import DatasetUploadPage from "../pages/DatasetUploadPage"; -import AnnotateDatasetLandingPage from "../pages/AnnotateDatasetLandingPage"; -import AnnotateDatasetPage from "../pages/AnnotateDatasetPage"; import { ProtectedRoute } from "../components/auth/ProtectedRoute"; import { handlersReady, @@ -43,12 +39,8 @@ function DemoApp({ route = "/", element }: { route?: string; element?: ReactNode path: "/", element: , children: [ - { index: true, element: }, - { path: "codebook-landing", element: }, + { index: true, element: }, { path: "new-codebook", element: }, - { path: "annotate-dataset-landing", element: }, - { path: "new-annotation", element: }, - { path: "annotate-dataset/:id", element: }, { path: "codebook-creation/:taskId", element: }, ], }, @@ -92,7 +84,7 @@ HappyPath_MainJourney.parameters = { docs: { description: { story: - "Start at landing page and use in-app navigation to explore the full happy path: codebook flow, upload page, annotation landing, and review workflow.", + "Start at landing page and use in-app navigation to explore the happy path: codebook flow, upload page, and review workflow.", }, }, }; @@ -130,7 +122,7 @@ function SessionExpiredApp() { [ { element: , - children: [{ path: "/", element: }], + children: [{ path: "/", element: }], }, { path: "/login", element: }, ], From 52ca9d8f0b3de74401c03b9b64ffa7893789aea9 Mon Sep 17 00:00:00 2001 From: chandan-m Date: Wed, 13 May 2026 01:55:51 -0700 Subject: [PATCH 14/31] Cleanup --- .../src/pages/CodebookLandingPage.module.css | 402 +++++++++--------- frontend/src/pages/CodebookLandingPage.tsx | 40 +- 2 files changed, 212 insertions(+), 230 deletions(-) diff --git a/frontend/src/pages/CodebookLandingPage.module.css b/frontend/src/pages/CodebookLandingPage.module.css index d638a52..2047f96 100644 --- a/frontend/src/pages/CodebookLandingPage.module.css +++ b/frontend/src/pages/CodebookLandingPage.module.css @@ -1,234 +1,233 @@ .page { - --accent: #7ce7e1; - --accent-strong: #36c2b3; - --accent-warm: #f3a578; + --accent: #1fa68a; + --accent-strong: #0d7f69; + --ink: #102027; + --paper: #f5fbf8; position: relative; min-height: 100dvh; + color: var(--ink); background: - radial-gradient( - 1200px 800px at 10% 20%, - rgba(124, 231, 225, 0.18), - transparent 60% - ), - radial-gradient( - 900px 600px at 90% 0%, - rgba(243, 165, 120, 0.2), - transparent 55% - ), - linear-gradient(180deg, #11171c 0%, #080c10 100%); - color: #e8eef1; + radial-gradient(900px 600px at 85% -5%, rgba(31, 166, 138, 0.2), transparent 60%), + radial-gradient(700px 520px at -5% 20%, rgba(255, 206, 122, 0.22), transparent 55%), + linear-gradient(180deg, #fafdff 0%, #eef6f2 100%); overflow-x: hidden; - font-family: "Space Grotesk", system-ui, sans-serif; + font-family: "Sora", "Space Grotesk", system-ui, sans-serif; } -:global([data-mantine-color-scheme="light"]) .page { +:global([data-mantine-color-scheme="dark"]) .page { + --ink: #e7f1ee; + --paper: #101a18; background: - radial-gradient( - 1200px 800px at 10% 20%, - rgba(54, 194, 179, 0.18), - transparent 60% - ), - radial-gradient( - 900px 600px at 90% 0%, - rgba(243, 165, 120, 0.22), - transparent 55% - ), - linear-gradient(180deg, #f7fafb 0%, #eef3f5 100%); - color: #0f1418; -} - -:global([data-mantine-color-scheme="light"]) .walkthroughSection, -:global([data-mantine-color-scheme="light"]) .walkthroughCard { - background: rgba(255, 255, 255, 0.9); - border-color: rgba(15, 20, 24, 0.1); -} - -:global([data-mantine-color-scheme="light"]) .mockPanel { - background: #ffffff; - border-color: rgba(15, 20, 24, 0.12); -} - -:global([data-mantine-color-scheme="light"]) .subtitle, -:global([data-mantine-color-scheme="light"]) .secondaryCta, -:global([data-mantine-color-scheme="light"]) .sectionHint, -:global([data-mantine-color-scheme="light"]) .walkthroughText, -:global([data-mantine-color-scheme="light"]) .walkthroughFootnote { - color: rgba(15, 20, 24, 0.78); -} - -:global([data-mantine-color-scheme="light"]) .fileChip, -:global([data-mantine-color-scheme="light"]) .exampleLabel, -:global([data-mantine-color-scheme="light"]) .walkthroughTitle, -:global([data-mantine-color-scheme="light"]) .mockPrompt, -:global([data-mantine-color-scheme="light"]) .summaryChip, -:global([data-mantine-color-scheme="light"]) .predictionPill, -:global([data-mantine-color-scheme="light"]) .statusPill { - color: #0f1418; -} - -:global([data-mantine-color-scheme="light"]) .summaryChip { - background: rgba(15, 20, 24, 0.04); - border-color: rgba(15, 20, 24, 0.14); + radial-gradient(900px 600px at 85% -5%, rgba(58, 196, 166, 0.22), transparent 60%), + radial-gradient(700px 520px at -5% 20%, rgba(246, 185, 93, 0.18), transparent 55%), + linear-gradient(180deg, #0a1110 0%, #0d1514 100%); } .page::before { content: ""; position: absolute; inset: 0; - opacity: 0.25; - background-image: - linear-gradient(120deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px), - linear-gradient(0deg, rgba(255, 255, 255, 0.04) 1px, transparent 1px); - background-size: 48px 48px; pointer-events: none; + opacity: 0.18; + background-image: linear-gradient(rgba(16, 32, 39, 0.07) 1px, transparent 1px), + linear-gradient(90deg, rgba(16, 32, 39, 0.07) 1px, transparent 1px); + background-size: 36px 36px; } -.orbOne { - position: absolute; - border-radius: 999px; - filter: blur(0.5px); - opacity: 0.5; +:global([data-mantine-color-scheme="dark"]) .page::before { + opacity: 0.16; + background-image: linear-gradient(rgba(233, 245, 242, 0.07) 1px, transparent 1px), + linear-gradient(90deg, rgba(233, 245, 242, 0.07) 1px, transparent 1px); } -.orbOne { - width: 360px; - height: 360px; - background: radial-gradient( - circle at 30% 30%, - rgba(124, 231, 225, 0.6), - transparent 70% - ); - top: -120px; - left: -80px; -} - -.hero { +.hero, +.flowSection { position: relative; z-index: 1; - padding: 32px 24px 14px; - max-width: 1200px; + max-width: 1180px; margin: 0 auto; - animation: fadeUp 700ms ease both; +} + +.hero { + padding: 44px 24px 10px; + animation: fadeUp 600ms ease both; } .kicker { font-family: "IBM Plex Mono", ui-monospace, monospace; - letter-spacing: 0.2em; + letter-spacing: 0.16em; text-transform: uppercase; } +.heroGrid { + display: grid; + grid-template-columns: 1.15fr 0.85fr; + gap: 18px; + margin-top: 16px; +} + +.heroCard, +.sideCard { + border-radius: 20px; + padding: 24px; + border: 1px solid rgba(16, 32, 39, 0.12); + background: rgba(255, 255, 255, 0.76); + backdrop-filter: blur(4px); +} + +:global([data-mantine-color-scheme="dark"]) .heroCard, +:global([data-mantine-color-scheme="dark"]) .sideCard { + border-color: rgba(231, 241, 238, 0.14); + background: rgba(14, 24, 22, 0.72); +} + .title { - font-size: clamp(30px, 3vw, 46px); - font-weight: 650; - line-height: 1.05; + font-size: clamp(34px, 4.4vw, 58px); + font-weight: 700; + line-height: 0.98; + letter-spacing: -0.03em; } .subtitle { - max-width: 720px; - color: rgba(232, 238, 241, 0.7); - font-size: 1rem; + color: rgba(16, 32, 39, 0.8); + max-width: 62ch; + font-size: 1.02rem; +} + +:global([data-mantine-color-scheme="dark"]) .subtitle { + color: rgba(231, 241, 238, 0.82); } .primaryCta { background: linear-gradient(135deg, var(--accent), var(--accent-strong)); - color: #031016; - font-weight: 600; + color: #f4fffc; + font-weight: 700; } -.secondaryCta { - color: rgba(232, 238, 241, 0.7); +.statGrid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 10px; +} + +.statChip { + background: rgba(16, 32, 39, 0.04); + border: 1px solid rgba(16, 32, 39, 0.1); + border-radius: 12px; + padding: 10px; +} + +:global([data-mantine-color-scheme="dark"]) .statChip { + background: rgba(231, 241, 238, 0.04); + border-color: rgba(231, 241, 238, 0.14); +} + +.statLabel { + font-size: 0.72rem; + text-transform: uppercase; + letter-spacing: 0.08em; + color: rgba(16, 32, 39, 0.62); +} + +:global([data-mantine-color-scheme="dark"]) .statLabel { + color: rgba(231, 241, 238, 0.62); +} + +.statValue { + font-size: 1.1rem; + font-weight: 700; +} + +.sideCardTitle { + font-size: 0.9rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.1em; + color: rgba(16, 32, 39, 0.62); +} + +:global([data-mantine-color-scheme="dark"]) .sideCardTitle { + color: rgba(231, 241, 238, 0.62); } .flowSection { - position: relative; - z-index: 1; - padding: 14px 24px 28px; - max-width: 1200px; - margin: 0 auto; + padding: 14px 24px 30px; +} + +.walkthroughSection { + border-radius: 20px; + padding: 18px; + border: 1px solid rgba(16, 32, 39, 0.12); + background: rgba(255, 255, 255, 0.7); +} + +:global([data-mantine-color-scheme="dark"]) .walkthroughSection { + border-color: rgba(231, 241, 238, 0.14); + background: rgba(14, 24, 22, 0.72); } .sectionTitle { - font-size: 1.1rem; - font-weight: 600; + font-size: 1.15rem; } .sectionHint { - color: rgba(232, 238, 241, 0.55); - font-size: 0.92rem; + font-size: 0.9rem; + color: rgba(16, 32, 39, 0.66); } -.walkthroughSection { - background: rgba(14, 20, 26, 0.68); - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 16px; - padding: 16px; +:global([data-mantine-color-scheme="dark"]) .sectionHint { + color: rgba(231, 241, 238, 0.65); } .walkthroughRow { + margin-top: 10px; display: grid; grid-template-columns: 1fr auto 1fr auto 1fr; gap: 14px; - align-items: stretch; - margin-top: 8px; } .walkthroughCard { - background: rgba(18, 24, 29, 0.78); - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 14px; + border: 1px solid rgba(16, 32, 39, 0.12); + background: var(--paper); + border-radius: 16px; padding: 14px; - min-height: 220px; +} + +:global([data-mantine-color-scheme="dark"]) .walkthroughCard { + border-color: rgba(231, 241, 238, 0.12); + background: rgba(16, 26, 24, 0.94); } .walkthroughArrow { display: flex; align-items: center; - justify-content: center; - color: rgba(124, 231, 225, 0.84); -} - -.walkthroughIcon { - color: rgba(124, 231, 225, 0.92); -} - -.exampleIcon { - color: rgba(124, 231, 225, 0.86); + color: rgba(13, 127, 105, 0.92); } .walkthroughTitle { font-size: 1rem; - font-weight: 600; - color: var(--app-text); + font-weight: 700; } .walkthroughText { - color: rgba(232, 238, 241, 0.7); + color: rgba(16, 32, 39, 0.78); font-size: 0.9rem; - line-height: 1.45; } -.fileChipGroup { - margin-top: 4px; +:global([data-mantine-color-scheme="dark"]) .walkthroughText { + color: rgba(231, 241, 238, 0.8); } -.fileChip { - display: inline-flex; - align-items: center; - gap: 5px; - padding: 3px 8px; - border-radius: 999px; - border: 1px solid rgba(124, 231, 225, 0.35); - background: rgba(124, 231, 225, 0.11); - font-size: 0.76rem; - color: #c9f6f2; +.mockPanel { + border: 1px solid rgba(16, 32, 39, 0.12); + background: #fff; + border-radius: 12px; + padding: 10px; } -.mockPanel { - margin-top: 4px; - padding: 8px; - border-radius: 10px; - border: 1px solid rgba(255, 255, 255, 0.08); - background: rgba(8, 13, 18, 0.44); +:global([data-mantine-color-scheme="dark"]) .mockPanel { + border-color: rgba(231, 241, 238, 0.14); + background: rgba(9, 15, 14, 0.95); } .mockRow { @@ -242,82 +241,66 @@ margin-bottom: 0; } -.mockPrompt { - font-size: 0.8rem; - line-height: 1.35; - color: rgba(232, 238, 241, 0.84); - margin-bottom: 8px; -} - +.fileChip, .predictionPill, -.statusPill { +.statusPill, +.summaryChip { display: inline-flex; - padding: 3px 7px; + align-items: center; + gap: 5px; + padding: 3px 8px; border-radius: 999px; - font-size: 0.72rem; - border: 1px solid rgba(255, 255, 255, 0.1); + font-size: 0.74rem; } +:global([data-mantine-color-scheme="dark"]) .summaryChip { + border-color: rgba(231, 241, 238, 0.18); + background: rgba(231, 241, 238, 0.06); +} + +.fileChip, .predictionPill { - background: rgba(124, 231, 225, 0.13); - color: #c9f6f2; - border-color: rgba(124, 231, 225, 0.3); + border: 1px solid rgba(31, 166, 138, 0.34); + background: rgba(31, 166, 138, 0.14); } .statusPill { - background: rgba(139, 230, 163, 0.14); - color: #d2f5dc; - border-color: rgba(139, 230, 163, 0.35); + border: 1px solid rgba(96, 179, 112, 0.35); + background: rgba(96, 179, 112, 0.14); } .summaryChip { - display: inline-flex; - align-items: center; - padding: 2px 7px; - border-radius: 999px; - font-size: 0.72rem; - color: rgba(232, 238, 241, 0.82); - border: 1px solid rgba(255, 255, 255, 0.1); - background: rgba(255, 255, 255, 0.06); + border: 1px solid rgba(16, 32, 39, 0.14); + background: rgba(16, 32, 39, 0.04); +} + +.mockPrompt, +.exampleLabel, +.walkthroughFootnote { + color: rgba(16, 32, 39, 0.74); +} + +:global([data-mantine-color-scheme="dark"]) .mockPrompt, +:global([data-mantine-color-scheme="dark"]) .exampleLabel, +:global([data-mantine-color-scheme="dark"]) .walkthroughFootnote { + color: rgba(231, 241, 238, 0.76); } .exportButtonMock { margin-top: 8px; width: fit-content; - padding: 5px 11px; border-radius: 999px; - font-size: 0.76rem; - font-weight: 600; - color: #031016; + padding: 6px 11px; + font-size: 0.78rem; + font-weight: 700; + color: #f4fffc; background: linear-gradient(135deg, var(--accent), var(--accent-strong)); } -.exampleLabel { - font-size: 0.84rem; - color: rgba(232, 238, 241, 0.86); -} - -.walkthroughFootnote { - margin-top: 10px; - color: rgba(232, 238, 241, 0.58); - font-size: 0.82rem; -} - @keyframes fadeUp { from { opacity: 0; - transform: translateY(12px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -@keyframes riseIn { - from { - opacity: 0; - transform: translateY(16px); + transform: translateY(10px); } to { opacity: 1; @@ -325,9 +308,9 @@ } } -@media (max-width: 768px) { - .hero { - padding-top: 28px; +@media (max-width: 960px) { + .heroGrid { + grid-template-columns: 1fr; } .walkthroughRow { @@ -338,8 +321,7 @@ display: none; } - .primaryCta, - .secondaryCta { - width: 100%; + .statGrid { + grid-template-columns: 1fr; } } diff --git a/frontend/src/pages/CodebookLandingPage.tsx b/frontend/src/pages/CodebookLandingPage.tsx index 3ccba52..628ac4d 100644 --- a/frontend/src/pages/CodebookLandingPage.tsx +++ b/frontend/src/pages/CodebookLandingPage.tsx @@ -32,23 +32,26 @@ export default function CodebookLandingPage() { Guided Annotation Flow - - Annotation Assistant - - - Build and refine a codebook from labeled and unlabeled datasets. - - - - +
+ + Annotation Assistant + + Build, test, and refine a production-ready codebook from labeled and + unlabeled datasets in one streamlined workflow. + + + + + +
@@ -142,9 +145,6 @@ export default function CodebookLandingPage() { - - Existing tasks remain accessible from the left sidebar. - From a00ca671add252f952b70fe461130b3dda3e58ab Mon Sep 17 00:00:00 2001 From: chandan-m Date: Wed, 13 May 2026 02:02:00 -0700 Subject: [PATCH 15/31] Cleanup --- .../src/pages/CodebookLandingPage.module.css | 32 +++++++++++++++++++ frontend/src/pages/CodebookLandingPage.tsx | 20 ++++++++---- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/frontend/src/pages/CodebookLandingPage.module.css b/frontend/src/pages/CodebookLandingPage.module.css index 2047f96..c8b25b5 100644 --- a/frontend/src/pages/CodebookLandingPage.module.css +++ b/frontend/src/pages/CodebookLandingPage.module.css @@ -241,6 +241,28 @@ margin-bottom: 0; } +.sampleCard, +.ruleCard { + border: 1px solid rgba(16, 32, 39, 0.14); + background: rgba(16, 32, 39, 0.03); + border-radius: 10px; + padding: 8px; + margin-bottom: 8px; +} + +.ruleCard { + margin-top: 8px; +} + +.sampleCardLabel { + font-size: 0.68rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.08em; + color: rgba(16, 32, 39, 0.62); + margin-bottom: 4px; +} + .fileChip, .predictionPill, .statusPill, @@ -258,6 +280,16 @@ background: rgba(231, 241, 238, 0.06); } +:global([data-mantine-color-scheme="dark"]) .sampleCard, +:global([data-mantine-color-scheme="dark"]) .ruleCard { + border-color: rgba(231, 241, 238, 0.16); + background: rgba(231, 241, 238, 0.04); +} + +:global([data-mantine-color-scheme="dark"]) .sampleCardLabel { + color: rgba(231, 241, 238, 0.68); +} + .fileChip, .predictionPill { border: 1px solid rgba(31, 166, 138, 0.34); diff --git a/frontend/src/pages/CodebookLandingPage.tsx b/frontend/src/pages/CodebookLandingPage.tsx index 628ac4d..2859dcf 100644 --- a/frontend/src/pages/CodebookLandingPage.tsx +++ b/frontend/src/pages/CodebookLandingPage.tsx @@ -103,16 +103,22 @@ export default function CodebookLandingPage() { Check AI predictions, mark correctness, and update rules in the codebook panel.
- - “Client reports improved sleep after two weeks.” - +
+ Sample post + + "Ad on Facebook Marketplace showing one belt made with pangolin leather. On sale." + +
- Predicted: Sleep improvement + Predicted: negative Marked: Correct - - Rule added: If note mentions improved sleep -> Sleep improvement - +
+ Rule added + + If a post promotes pangolin products or sales, label it as negative toward conservation. + +
From e1ff5b05b90bf5c6f54353f60c75ed3292116e26 Mon Sep 17 00:00:00 2001 From: chandan-m Date: Wed, 13 May 2026 02:27:24 -0700 Subject: [PATCH 16/31] Enter details directly on Create task page --- backend/src/routes/tasks.ts | 4 +- backend/src/services/tasks.service.ts | 44 ++++- .../src/pages/DatasetUploadPage.module.css | 41 ++++ frontend/src/pages/DatasetUploadPage.tsx | 177 +++++++++++++----- frontend/src/services/tasks.service.ts | 18 +- 5 files changed, 224 insertions(+), 60 deletions(-) diff --git a/backend/src/routes/tasks.ts b/backend/src/routes/tasks.ts index 4fe5357..41d056a 100644 --- a/backend/src/routes/tasks.ts +++ b/backend/src/routes/tasks.ts @@ -29,8 +29,8 @@ router.post( uploadBundle.fields([ { name: "d_val", maxCount: 1 }, { name: "d_all", maxCount: 1 }, - { name: "task_json", maxCount: 1 }, { name: "labels_json", maxCount: 1 }, + { name: "task_json", maxCount: 1 }, ]), uploadTaskBundle, ); @@ -41,8 +41,8 @@ router.post( uploadBundle.fields([ { name: "d_val", maxCount: 1 }, { name: "d_all", maxCount: 1 }, - { name: "task_json", maxCount: 1 }, { name: "labels_json", maxCount: 1 }, + { name: "task_json", maxCount: 1 }, ]), uploadTaskBundle, ); diff --git a/backend/src/services/tasks.service.ts b/backend/src/services/tasks.service.ts index 4077df4..9435cf4 100644 --- a/backend/src/services/tasks.service.ts +++ b/backend/src/services/tasks.service.ts @@ -762,6 +762,9 @@ export async function uploadTaskBundle(req: AuthRequest, res: Response) { const dAllFile = files?.d_all?.[0]; const taskJsonFile = files?.task_json?.[0]; const labelsJsonFile = files?.labels_json?.[0]; + const taskNameField = req.body.task_name as string | undefined; + const taskDescriptionField = req.body.task_description as string | undefined; + const taskTypeField = req.body.task_type as string | undefined; const textColumn = req.body.text_column as string | undefined; const labelColumn = req.body.label_column as string | undefined; const modelName = req.body.model_name as string | undefined; @@ -769,7 +772,6 @@ export async function uploadTaskBundle(req: AuthRequest, res: Response) { if ( !dValFile || !dAllFile || - !taskJsonFile || !labelsJsonFile || !labelColumn ) { @@ -777,21 +779,49 @@ export async function uploadTaskBundle(req: AuthRequest, res: Response) { d_val: !!dValFile, d_all: !!dAllFile, task_json: !!taskJsonFile, + task_name: !!taskNameField, + task_description: !!taskDescriptionField, labels_json: !!labelsJsonFile, labelColumn: !!labelColumn, }); return res.status(400).json({ success: false, message: - "Missing files. Expected d_val, d_all, task_json, labels_json and label column", + "Missing fields. Expected d_val, d_all, labels_json, label column, and either task_json or task_name + task_description", }); } - // Parse Task JSON - console.log("[uploadTaskBundle] Parsing task_json..."); - const taskJsonRaw = taskJsonFile.buffer.toString("utf-8"); - const taskInfo = parseTaskJson(taskJsonFile.buffer); - console.log("[uploadTaskBundle] Task Info parsed:", taskInfo.name); + let taskJsonRaw = ""; + let taskInfo: { + name: string; + description: string; + type: Task["type"]; + }; + if (taskJsonFile) { + console.log("[uploadTaskBundle] Parsing task_json..."); + taskJsonRaw = taskJsonFile.buffer.toString("utf-8"); + taskInfo = parseTaskJson(taskJsonFile.buffer); + console.log("[uploadTaskBundle] Task Info parsed from file:", taskInfo.name); + } else { + const name = String(taskNameField ?? "").trim(); + const description = String(taskDescriptionField ?? "").trim(); + if (!name || !description) { + return res.status(400).json({ + success: false, + message: + "Task name and description are required when task_json is not provided.", + }); + } + const type: Task["type"] = + taskTypeField === "Single-class" ? "Single-class" : "Multiclass"; + taskInfo = { name, description, type }; + taskJsonRaw = JSON.stringify( + { taskname: name, description, type }, + null, + 2, + ); + console.log("[uploadTaskBundle] Task Info parsed from form fields:", taskInfo.name); + } // Parse Labels JSON console.log("[uploadTaskBundle] Parsing labels_json..."); diff --git a/frontend/src/pages/DatasetUploadPage.module.css b/frontend/src/pages/DatasetUploadPage.module.css index b32fd54..fbef9bf 100644 --- a/frontend/src/pages/DatasetUploadPage.module.css +++ b/frontend/src/pages/DatasetUploadPage.module.css @@ -57,16 +57,30 @@ color: rgba(15, 20, 24, 0.65); } +:global([data-mantine-color-scheme="light"]) .fileHint { + color: rgba(15, 20, 24, 0.7); +} + :global([data-mantine-color-scheme="light"]) .fileButton { background: #ffffff; color: #0f1418; border-color: rgba(124, 231, 225, 0.55); } +:global([data-mantine-color-scheme="light"]) .fileButtonAction { + background: #ffffff; + color: #0f1418; + border-color: rgba(124, 231, 225, 0.55); +} + :global([data-mantine-color-scheme="light"]) .fileButton:hover { background: #f4f7f9; } +:global([data-mantine-color-scheme="light"]) .fileButtonAction:hover { + background: #f4f7f9; +} + :global([data-mantine-color-scheme="light"]) .tableCard { background: #ffffff; border-color: rgba(15, 20, 24, 0.08); @@ -237,6 +251,33 @@ background: rgba(26, 38, 45, 0.95); } +.fileButtonAction { + appearance: none; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; + padding: 4px 10px; + min-height: 28px; + border-radius: 8px; + border: 1px solid rgba(124, 231, 225, 0.4); + color: #e8eef1; + background: rgba(20, 30, 36, 0.9); + font-size: 0.78rem; + font-weight: 600; + cursor: pointer; + transition: + transform 0.2s ease, + border-color 0.2s ease, + background 0.2s ease; +} + +.fileButtonAction:hover { + transform: translateY(-1px); + border-color: rgba(124, 231, 225, 0.7); + background: rgba(26, 38, 45, 0.95); +} + .fileName { align-self: flex-start; text-align: left; diff --git a/frontend/src/pages/DatasetUploadPage.tsx b/frontend/src/pages/DatasetUploadPage.tsx index ecf33b4..e31f2cb 100644 --- a/frontend/src/pages/DatasetUploadPage.tsx +++ b/frontend/src/pages/DatasetUploadPage.tsx @@ -12,6 +12,7 @@ import { Stack, Switch, Text, + Textarea, TextInput, Title, useMantineColorScheme, @@ -57,6 +58,12 @@ interface RequiredFileCardProps { label: string; onClick: () => void; }; + preSelectAction?: { + label: string; + onClick: () => void; + }; + hideMeta?: boolean; + children?: ReactNode; } function RequiredFileCard({ @@ -69,6 +76,9 @@ function RequiredFileCard({ file, onFileChange, templateAction, + preSelectAction, + hideMeta, + children, }: RequiredFileCardProps) { return (
@@ -86,15 +96,27 @@ function RequiredFileCard({ {label} - + + {preSelectAction ? ( + + ) : null} + + {hint} - {templateAction ? ( + {children} + {!hideMeta && templateAction ? ( - ) : ( + ) : !hideMeta ? ( {file ? file.name : "No file selected"} - )} + ) : null + }
); } @@ -131,6 +154,9 @@ export default function DatasetUploadPage() { const [dValFile, setDValFile] = useState(null); const [dAllFile, setDAllFile] = useState(null); const [taskJsonFile, setTaskJsonFile] = useState(null); + const [taskDetailsMode, setTaskDetailsMode] = useState<"file" | "manual">("file"); + const [manualTaskName, setManualTaskName] = useState(""); + const [manualTaskDescription, setManualTaskDescription] = useState(""); const [labelsJsonFile, setLabelsJsonFile] = useState(null); const [config, setConfig] = useState(defaultUploadConfig); const [isUploading, setIsUploading] = useState(false); @@ -145,7 +171,10 @@ export default function DatasetUploadPage() { openHelpIntro, } = usePageIntroTour("hideStep1Intro"); - const isReady = Boolean(dValFile && dAllFile && taskJsonFile && labelsJsonFile); + const hasManualTaskDetails = + manualTaskName.trim().length > 0 && manualTaskDescription.trim().length > 0; + const hasTaskDetails = taskDetailsMode === "file" ? Boolean(taskJsonFile) : hasManualTaskDetails; + const isReady = Boolean(dValFile && dAllFile && hasTaskDetails && labelsJsonFile); const handleHelp = openHelpIntro; @@ -160,6 +189,9 @@ export default function DatasetUploadPage() { setDValFile(null); setDAllFile(null); setTaskJsonFile(null); + setTaskDetailsMode("file"); + setManualTaskName(""); + setManualTaskDescription(""); setLabelsJsonFile(null); setConfig(defaultUploadConfig); setError(null); @@ -171,13 +203,13 @@ export default function DatasetUploadPage() { if ( !dValFile || !dAllFile || - !taskJsonFile || + !hasTaskDetails || !labelsJsonFile || !config.labelColumn || !config.selectedModel ) { setError( - "Please provide all four files and label column as well as preferred model before continuing.", + "Please provide both CSVs, labels JSON, task details (JSON upload or manual entry), label column, and preferred model before continuing.", ); return; } @@ -189,7 +221,11 @@ export default function DatasetUploadPage() { const response = await uploadTaskBundle({ dValFile, dAllFile, - taskJsonFile, + taskJsonFile: taskDetailsMode === "file" ? taskJsonFile ?? undefined : undefined, + taskName: taskDetailsMode === "manual" ? manualTaskName.trim() : undefined, + taskDescription: + taskDetailsMode === "manual" ? manualTaskDescription.trim() : undefined, + taskType: "Multiclass", labelsJsonFile, textColumn: config.textColumn, labelColumn: config.labelColumn, @@ -233,8 +269,12 @@ export default function DatasetUploadPage() { config, dAllFile, dValFile, + hasTaskDetails, labelsJsonFile, + manualTaskDescription, + manualTaskName, navigate, + taskDetailsMode, taskJsonFile, ]); @@ -360,40 +400,6 @@ export default function DatasetUploadPage() { Upload both CSVs and both JSON files to continue. - - } - label="Labeled dataset" - selectLabel="Select CSV" - hint="CSV with text + task_label columns" - file={dValFile} - onFileChange={setDValFile} - /> - - - - } - label="Unlabeled dataset" - selectLabel="Select CSV" - hint="CSV with text column only" - file={dAllFile} - onFileChange={setDAllFile} - /> - - downloadContent("task.json", TASK_TEMPLATE), + hideMeta={taskDetailsMode === "manual"} + templateAction={ + taskDetailsMode === "file" + ? { + label: "Download template", + onClick: () => downloadContent("task.json", TASK_TEMPLATE), + } + : undefined + } + preSelectAction={{ + label: taskDetailsMode === "file" ? "Enter details" : "Use JSON", + onClick: () => + setTaskDetailsMode((prev) => (prev === "file" ? "manual" : "file")), }} - /> + > + {taskDetailsMode === "manual" && ( + + setManualTaskName(event.currentTarget.value)} + classNames={{ + label: styles.configLabel, + input: styles.configInput, + }} + /> +