From 47befc8653c915dc340ac9a5c6ff24bae05d5429 Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Mon, 30 Mar 2026 15:47:11 -0300 Subject: [PATCH 01/11] feat(tokens): design token foundation with JSON source of truth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Single source of truth: common/theme/tokens.json defines all 109 design tokens (41 colour + 68 non-colour) with values, dark mode overrides, and usage descriptions. Codegen script (scripts/generate-tokens.mjs) generates: - common/theme/tokens.ts — TypeScript exports - web/styles/_tokens.scss — CSS custom properties (:root + .dark) - documentation/TokenReference.generated.stories.tsx — flat JSX for MCP Token categories: - Colour: surface, text, border, icon (with light/dark variants) - Spacing: Tailwind naming convention (--space-1 = 4px, --space-4 = 16px) - Border radius: none through full (8 values) - Typography: headings h1-h6, body, caption, label, font weights - Elevation: 4 shadow levels with dark mode overrides - Motion: 3 durations + 3 easing curves Primitive colour palette (_primitives.scss) provides 7 families (slate, purple, red, green, gold, blue, orange) with 50-950 scales. lint-staged auto-runs codegen when tokens.json is committed. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/frontend-chromatic.yml | 45 ++++ frontend/common/theme/index.ts | 9 + frontend/common/theme/tokens.json | 133 ++++++++++ frontend/common/theme/tokens.ts | 217 ++++++++++++++++ frontend/scripts/generate-tokens.mjs | 318 +++++++++++++++++++++++ frontend/web/styles/_primitives.scss | 104 ++++++++ frontend/web/styles/_tokens.scss | 171 ++++++++++++ frontend/web/styles/_variables.scss | 16 +- frontend/web/styles/styles.scss | 1 + 9 files changed, 1006 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/frontend-chromatic.yml create mode 100644 frontend/common/theme/index.ts create mode 100644 frontend/common/theme/tokens.json create mode 100644 frontend/common/theme/tokens.ts create mode 100644 frontend/scripts/generate-tokens.mjs create mode 100644 frontend/web/styles/_primitives.scss create mode 100644 frontend/web/styles/_tokens.scss diff --git a/.github/workflows/frontend-chromatic.yml b/.github/workflows/frontend-chromatic.yml new file mode 100644 index 000000000000..d68c1fd68dce --- /dev/null +++ b/.github/workflows/frontend-chromatic.yml @@ -0,0 +1,45 @@ +name: Frontend Chromatic + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + paths: + - frontend/** + - .github/workflows/frontend-chromatic.yml + +permissions: + contents: read + +jobs: + chromatic: + name: Chromatic + runs-on: ubuntu-latest + if: github.event.pull_request.draft == false + + defaults: + run: + working-directory: frontend + + steps: + - uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: frontend/.nvmrc + cache: npm + cache-dependency-path: frontend/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Publish to Chromatic + uses: chromaui/action@v11 + with: + workingDir: frontend + projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} + exitZeroOnChanges: true + exitOnceUploaded: true + onlyChanged: true diff --git a/frontend/common/theme/index.ts b/frontend/common/theme/index.ts new file mode 100644 index 000000000000..a8f1c200c490 --- /dev/null +++ b/frontend/common/theme/index.ts @@ -0,0 +1,9 @@ +export { tokens, space, radius, shadow, duration, easing } from './tokens' +export type { + TokenCategory, + TokenEntry, + TokenName, + SpaceScale, + RadiusScale, + ShadowScale, +} from './tokens' diff --git a/frontend/common/theme/tokens.json b/frontend/common/theme/tokens.json new file mode 100644 index 000000000000..97c75ca086a8 --- /dev/null +++ b/frontend/common/theme/tokens.json @@ -0,0 +1,133 @@ +{ + "color": { + "surface": { + "default": { "cssVar": "--color-surface-default", "light": "#ffffff", "dark": "#101628" }, + "subtle": { "cssVar": "--color-surface-subtle", "light": "#fafafb", "dark": "#15192b" }, + "muted": { "cssVar": "--color-surface-muted", "light": "#eff1f4", "dark": "#161d30" }, + "emphasis": { "cssVar": "--color-surface-emphasis", "light": "#e0e3e9", "dark": "#202839" }, + "hover": { "cssVar": "--color-surface-hover", "light": "rgba(0, 0, 0, 0.08)", "dark": "rgba(255, 255, 255, 0.08)" }, + "active": { "cssVar": "--color-surface-active", "light": "rgba(0, 0, 0, 0.16)", "dark": "rgba(255, 255, 255, 0.16)" }, + "action": { "cssVar": "--color-surface-action", "light": "#6837fc", "dark": "#906af6" }, + "action-hover": { "cssVar": "--color-surface-action-hover", "light": "#4e25db", "dark": "#6837fc" }, + "action-active": { "cssVar": "--color-surface-action-active", "light": "#3919b7", "dark": "#4e25db" }, + "action-subtle": { "cssVar": "--color-surface-action-subtle", "light": "rgba(104, 55, 252, 0.08)", "dark": "rgba(255, 255, 255, 0.08)" }, + "action-muted": { "cssVar": "--color-surface-action-muted", "light": "rgba(104, 55, 252, 0.16)", "dark": "rgba(255, 255, 255, 0.16)" }, + "danger": { "cssVar": "--color-surface-danger", "light": "rgba(239, 77, 86, 0.08)", "dark": "rgba(34, 23, 40, 1)" }, + "success": { "cssVar": "--color-surface-success", "light": "rgba(39, 171, 149, 0.08)", "dark": "rgba(17, 32, 46, 1)" }, + "warning": { "cssVar": "--color-surface-warning", "light": "rgba(255, 159, 67, 0.08)", "dark": "rgba(34, 31, 39, 1)" }, + "info": { "cssVar": "--color-surface-info", "light": "rgba(10, 173, 223, 0.08)", "dark": "rgba(15, 32, 52, 1)" } + }, + "text": { + "default": { "cssVar": "--color-text-default", "light": "#1a2634", "dark": "#ffffff" }, + "secondary": { "cssVar": "--color-text-secondary", "light": "#656d7b", "dark": "#9da4ae" }, + "tertiary": { "cssVar": "--color-text-tertiary", "light": "#9da4ae", "dark": "rgba(255, 255, 255, 0.48)" }, + "disabled": { "cssVar": "--color-text-disabled", "light": "#9da4ae", "dark": "rgba(255, 255, 255, 0.32)" }, + "on-fill": { "cssVar": "--color-text-on-fill", "light": "#ffffff", "dark": "#ffffff" }, + "action": { "cssVar": "--color-text-action", "light": "#6837fc", "dark": "#906af6" }, + "danger": { "cssVar": "--color-text-danger", "light": "#ef4d56", "dark": "#ef4d56" }, + "success": { "cssVar": "--color-text-success", "light": "#27ab95", "dark": "#27ab95" }, + "warning": { "cssVar": "--color-text-warning", "light": "#ff9f43", "dark": "#ff9f43" }, + "info": { "cssVar": "--color-text-info", "light": "#0aaddf", "dark": "#0aaddf" } + }, + "border": { + "default": { "cssVar": "--color-border-default", "light": "rgba(101, 109, 123, 0.16)", "dark": "rgba(255, 255, 255, 0.16)" }, + "strong": { "cssVar": "--color-border-strong", "light": "rgba(101, 109, 123, 0.24)", "dark": "rgba(255, 255, 255, 0.24)" }, + "disabled": { "cssVar": "--color-border-disabled", "light": "rgba(101, 109, 123, 0.08)", "dark": "rgba(255, 255, 255, 0.08)" }, + "action": { "cssVar": "--color-border-action", "light": "#6837fc", "dark": "#906af6" }, + "danger": { "cssVar": "--color-border-danger", "light": "#ef4d56", "dark": "#ef4d56" }, + "success": { "cssVar": "--color-border-success", "light": "#27ab95", "dark": "#27ab95" }, + "warning": { "cssVar": "--color-border-warning", "light": "#ff9f43", "dark": "#ff9f43" }, + "info": { "cssVar": "--color-border-info", "light": "#0aaddf", "dark": "#0aaddf" } + }, + "icon": { + "default": { "cssVar": "--color-icon-default", "light": "#1a2634", "dark": "#ffffff" }, + "secondary": { "cssVar": "--color-icon-secondary", "light": "#656d7b", "dark": "#9da4ae" }, + "disabled": { "cssVar": "--color-icon-disabled", "light": "#9da4ae", "dark": "rgba(255, 255, 255, 0.32)" }, + "action": { "cssVar": "--color-icon-action", "light": "#6837fc", "dark": "#906af6" }, + "danger": { "cssVar": "--color-icon-danger", "light": "#ef4d56", "dark": "#ef4d56" }, + "success": { "cssVar": "--color-icon-success", "light": "#27ab95", "dark": "#27ab95" }, + "warning": { "cssVar": "--color-icon-warning", "light": "#ff9f43", "dark": "#ff9f43" }, + "info": { "cssVar": "--color-icon-info", "light": "#0aaddf", "dark": "#0aaddf" } + } + }, + "space": { + "0": { "cssVar": "--space-0", "value": "0px", "description": "No spacing. Use for flush edges between elements." }, + "0.5": { "cssVar": "--space-0_5", "value": "2px", "description": "Fine optical adjustment only. Never use for component padding." }, + "1": { "cssVar": "--space-1", "value": "4px", "description": "Tight inline spacing. Icon-to-text gap, badge padding." }, + "2": { "cssVar": "--space-2", "value": "8px", "description": "Default inline gap. Space between icon and label, between chips." }, + "3": { "cssVar": "--space-3", "value": "12px", "description": "Comfortable inner padding. Input padding, small card padding." }, + "4": { "cssVar": "--space-4", "value": "16px", "description": "Standard component padding. Card padding, section gap, button horizontal padding." }, + "5": { "cssVar": "--space-5", "value": "20px", "description": "Button horizontal padding (large). Modal body padding." }, + "6": { "cssVar": "--space-6", "value": "24px", "description": "Section spacing. Gap between card groups, modal header/body padding." }, + "8": { "cssVar": "--space-8", "value": "32px", "description": "Large section spacing. Page section margins." }, + "10": { "cssVar": "--space-10", "value": "40px", "description": "Extra-large spacing. Major page sections." }, + "12": { "cssVar": "--space-12", "value": "48px", "description": "Page-level vertical spacing between major sections." }, + "16": { "cssVar": "--space-16", "value": "64px", "description": "Maximum spacing. Hero sections, page-level separation." } + }, + "radius": { + "none": { "cssVar": "--radius-none", "value": "0px", "description": "Sharp corners. Tables, dividers." }, + "xs": { "cssVar": "--radius-xs", "value": "2px", "description": "Barely rounded. Badges, tags." }, + "sm": { "cssVar": "--radius-sm", "value": "4px", "description": "Buttons, inputs, small interactive elements." }, + "md": { "cssVar": "--radius-md", "value": "6px", "description": "Default component radius. Cards, dropdowns, tooltips." }, + "lg": { "cssVar": "--radius-lg", "value": "8px", "description": "Large cards, panels." }, + "xl": { "cssVar": "--radius-xl", "value": "10px", "description": "Extra-large containers." }, + "2xl": { "cssVar": "--radius-2xl", "value": "18px", "description": "Modals." }, + "full": { "cssVar": "--radius-full", "value": "9999px", "description": "Pill shapes, avatars, circular elements." } + }, + "shadow": { + "sm": { "cssVar": "--shadow-sm", "light": "0px 1px 2px rgba(0, 0, 0, 0.05)", "dark": "0px 1px 2px rgba(0, 0, 0, 0.20)", "description": "Subtle lift. Buttons on hover, input focus ring companion." }, + "md": { "cssVar": "--shadow-md", "light": "0px 4px 8px rgba(0, 0, 0, 0.12)", "dark": "0px 4px 8px rgba(0, 0, 0, 0.30)", "description": "Cards, dropdowns, popovers. Default elevation for floating elements." }, + "lg": { "cssVar": "--shadow-lg", "light": "0px 8px 16px rgba(0, 0, 0, 0.15)", "dark": "0px 8px 16px rgba(0, 0, 0, 0.35)", "description": "Modals, drawers, slide-over panels. High elevation for overlay content." }, + "xl": { "cssVar": "--shadow-xl", "light": "0px 12px 24px rgba(0, 0, 0, 0.20)", "dark": "0px 12px 24px rgba(0, 0, 0, 0.40)", "description": "Toast notifications, command palettes. Maximum elevation for urgent content." } + }, + "font": { + "family": { "cssVar": "--font-family", "value": "'OpenSans', sans-serif" }, + "h1-size": { "cssVar": "--font-h1-size", "value": "42px" }, + "h1-line-height": { "cssVar": "--font-h1-line-height", "value": "46px" }, + "h1-weight": { "cssVar": "--font-h1-weight", "value": "700" }, + "h2-size": { "cssVar": "--font-h2-size", "value": "34px" }, + "h2-line-height": { "cssVar": "--font-h2-line-height", "value": "40px" }, + "h2-weight": { "cssVar": "--font-h2-weight", "value": "700" }, + "h3-size": { "cssVar": "--font-h3-size", "value": "30px" }, + "h3-line-height": { "cssVar": "--font-h3-line-height", "value": "40px" }, + "h3-weight": { "cssVar": "--font-h3-weight", "value": "600" }, + "h4-size": { "cssVar": "--font-h4-size", "value": "24px" }, + "h4-line-height": { "cssVar": "--font-h4-line-height", "value": "32px" }, + "h4-weight": { "cssVar": "--font-h4-weight", "value": "600" }, + "h5-size": { "cssVar": "--font-h5-size", "value": "18px" }, + "h5-line-height": { "cssVar": "--font-h5-line-height", "value": "28px" }, + "h5-weight": { "cssVar": "--font-h5-weight", "value": "600" }, + "h6-size": { "cssVar": "--font-h6-size", "value": "16px" }, + "h6-line-height": { "cssVar": "--font-h6-line-height", "value": "24px" }, + "h6-weight": { "cssVar": "--font-h6-weight", "value": "600" }, + "body-size": { "cssVar": "--font-body-size", "value": "14px" }, + "body-line-height": { "cssVar": "--font-body-line-height", "value": "20px" }, + "body-weight": { "cssVar": "--font-body-weight", "value": "400" }, + "body-sm-size": { "cssVar": "--font-body-sm-size", "value": "13px" }, + "body-sm-line-height": { "cssVar": "--font-body-sm-line-height", "value": "18px" }, + "body-sm-weight": { "cssVar": "--font-body-sm-weight", "value": "400" }, + "caption-size": { "cssVar": "--font-caption-size", "value": "12px" }, + "caption-line-height": { "cssVar": "--font-caption-line-height", "value": "16px" }, + "caption-weight": { "cssVar": "--font-caption-weight", "value": "400" }, + "caption-xs-size": { "cssVar": "--font-caption-xs-size", "value": "11px" }, + "caption-xs-line-height": { "cssVar": "--font-caption-xs-line-height", "value": "14px" }, + "caption-xs-weight": { "cssVar": "--font-caption-xs-weight", "value": "400" }, + "label-size": { "cssVar": "--font-label-size", "value": "12px" }, + "label-line-height": { "cssVar": "--font-label-line-height", "value": "16px" }, + "label-weight": { "cssVar": "--font-label-weight", "value": "600" }, + "weight-regular": { "cssVar": "--font-weight-regular", "value": "400" }, + "weight-medium": { "cssVar": "--font-weight-medium", "value": "500" }, + "weight-semibold": { "cssVar": "--font-weight-semibold", "value": "600" }, + "weight-bold": { "cssVar": "--font-weight-bold", "value": "700" } + }, + "duration": { + "fast": { "cssVar": "--duration-fast", "value": "100ms", "description": "Quick feedback. Hover states, toggle switches, checkbox ticks." }, + "normal": { "cssVar": "--duration-normal", "value": "200ms", "description": "Standard transitions. Dropdown open, tooltip appear, tab switch." }, + "slow": { "cssVar": "--duration-slow", "value": "300ms", "description": "Deliberate emphasis. Modal enter, drawer slide, accordion expand." } + }, + "easing": { + "standard": { "cssVar": "--easing-standard", "value": "cubic-bezier(0.2, 0, 0.38, 0.9)", "description": "Default for most transitions. Smooth deceleration. Use for elements moving within the page." }, + "entrance": { "cssVar": "--easing-entrance", "value": "cubic-bezier(0.0, 0, 0.38, 0.9)", "description": "Elements entering the viewport. Decelerates into resting position. Modals, toasts, slide-ins." }, + "exit": { "cssVar": "--easing-exit", "value": "cubic-bezier(0.2, 0, 1, 0.9)", "description": "Elements leaving the viewport. Accelerates out of view. Closing modals, dismissing toasts." } + } +} diff --git a/frontend/common/theme/tokens.ts b/frontend/common/theme/tokens.ts new file mode 100644 index 000000000000..c92e80daa9aa --- /dev/null +++ b/frontend/common/theme/tokens.ts @@ -0,0 +1,217 @@ +// ============================================================================= +// Design Tokens — AUTO-GENERATED from common/theme/tokens.json +// Do not edit manually. Run: npm run generate:tokens +// ============================================================================= + +export const tokens = { + border: { + action: 'var(--color-border-action, #6837fc)', + danger: 'var(--color-border-danger, #ef4d56)', + default: 'var(--color-border-default)', + disabled: 'var(--color-border-disabled)', + info: 'var(--color-border-info, #0aaddf)', + strong: 'var(--color-border-strong)', + success: 'var(--color-border-success, #27ab95)', + warning: 'var(--color-border-warning, #ff9f43)', + }, + icon: { + action: 'var(--color-icon-action, #6837fc)', + danger: 'var(--color-icon-danger, #ef4d56)', + default: 'var(--color-icon-default, #1a2634)', + disabled: 'var(--color-icon-disabled, #9da4ae)', + info: 'var(--color-icon-info, #0aaddf)', + secondary: 'var(--color-icon-secondary, #656d7b)', + success: 'var(--color-icon-success, #27ab95)', + warning: 'var(--color-icon-warning, #ff9f43)', + }, + surface: { + action: 'var(--color-surface-action, #6837fc)', + actionActive: 'var(--color-surface-action-active, #3919b7)', + actionHover: 'var(--color-surface-action-hover, #4e25db)', + actionMuted: 'var(--color-surface-action-muted)', + actionSubtle: 'var(--color-surface-action-subtle)', + active: 'var(--color-surface-active)', + danger: 'var(--color-surface-danger)', + default: 'var(--color-surface-default, #ffffff)', + emphasis: 'var(--color-surface-emphasis, #e0e3e9)', + hover: 'var(--color-surface-hover)', + info: 'var(--color-surface-info)', + muted: 'var(--color-surface-muted, #eff1f4)', + subtle: 'var(--color-surface-subtle, #fafafb)', + success: 'var(--color-surface-success)', + warning: 'var(--color-surface-warning)', + }, + text: { + action: 'var(--color-text-action, #6837fc)', + danger: 'var(--color-text-danger, #ef4d56)', + default: 'var(--color-text-default, #1a2634)', + disabled: 'var(--color-text-disabled, #9da4ae)', + info: 'var(--color-text-info, #0aaddf)', + onFill: 'var(--color-text-on-fill, #ffffff)', + secondary: 'var(--color-text-secondary, #656d7b)', + success: 'var(--color-text-success, #27ab95)', + tertiary: 'var(--color-text-tertiary, #9da4ae)', + warning: 'var(--color-text-warning, #ff9f43)', + }, +} as const + +export type TokenEntry = { + value: string + description: string +} + +// Space +export const space: Record = { + '0': { + description: 'No spacing. Use for flush edges between elements.', + value: 'var(--space-0, 0px)', + }, + '0.5': { + description: + 'Fine optical adjustment only. Never use for component padding.', + value: 'var(--space-0_5, 2px)', + }, + '1': { + description: 'Tight inline spacing. Icon-to-text gap, badge padding.', + value: 'var(--space-1, 4px)', + }, + '10': { + description: 'Extra-large spacing. Major page sections.', + value: 'var(--space-10, 40px)', + }, + '12': { + description: 'Page-level vertical spacing between major sections.', + value: 'var(--space-12, 48px)', + }, + '16': { + description: 'Maximum spacing. Hero sections, page-level separation.', + value: 'var(--space-16, 64px)', + }, + '2': { + description: + 'Default inline gap. Space between icon and label, between chips.', + value: 'var(--space-2, 8px)', + }, + '3': { + description: + 'Comfortable inner padding. Input padding, small card padding.', + value: 'var(--space-3, 12px)', + }, + '4': { + description: + 'Standard component padding. Card padding, section gap, button horizontal padding.', + value: 'var(--space-4, 16px)', + }, + '5': { + description: 'Button horizontal padding (large). Modal body padding.', + value: 'var(--space-5, 20px)', + }, + '6': { + description: + 'Section spacing. Gap between card groups, modal header/body padding.', + value: 'var(--space-6, 24px)', + }, + '8': { + description: 'Large section spacing. Page section margins.', + value: 'var(--space-8, 32px)', + }, +} +// Radius +export const radius: Record = { + '2xl': { + description: 'Modals.', + value: 'var(--radius-2xl, 18px)', + }, + 'full': { + description: 'Pill shapes, avatars, circular elements.', + value: 'var(--radius-full, 9999px)', + }, + 'lg': { + description: 'Large cards, panels.', + value: 'var(--radius-lg, 8px)', + }, + 'md': { + description: 'Default component radius. Cards, dropdowns, tooltips.', + value: 'var(--radius-md, 6px)', + }, + 'none': { + description: 'Sharp corners. Tables, dividers.', + value: 'var(--radius-none, 0px)', + }, + 'sm': { + description: 'Buttons, inputs, small interactive elements.', + value: 'var(--radius-sm, 4px)', + }, + 'xl': { + description: 'Extra-large containers.', + value: 'var(--radius-xl, 10px)', + }, + 'xs': { + description: 'Barely rounded. Badges, tags.', + value: 'var(--radius-xs, 2px)', + }, +} +// Shadow +export const shadow: Record = { + 'lg': { + description: + 'Modals, drawers, slide-over panels. High elevation for overlay content.', + value: 'var(--shadow-lg, 0px 8px 16px rgba(0, 0, 0, 0.15))', + }, + 'md': { + description: + 'Cards, dropdowns, popovers. Default elevation for floating elements.', + value: 'var(--shadow-md, 0px 4px 8px rgba(0, 0, 0, 0.12))', + }, + 'sm': { + description: 'Subtle lift. Buttons on hover, input focus ring companion.', + value: 'var(--shadow-sm, 0px 1px 2px rgba(0, 0, 0, 0.05))', + }, + 'xl': { + description: + 'Toast notifications, command palettes. Maximum elevation for urgent content.', + value: 'var(--shadow-xl, 0px 12px 24px rgba(0, 0, 0, 0.20))', + }, +} +// Duration +export const duration: Record = { + 'fast': { + description: + 'Quick feedback. Hover states, toggle switches, checkbox ticks.', + value: 'var(--duration-fast, 100ms)', + }, + 'normal': { + description: + 'Standard transitions. Dropdown open, tooltip appear, tab switch.', + value: 'var(--duration-normal, 200ms)', + }, + 'slow': { + description: + 'Deliberate emphasis. Modal enter, drawer slide, accordion expand.', + value: 'var(--duration-slow, 300ms)', + }, +} +// Easing +export const easing: Record = { + 'entrance': { + description: + 'Elements entering the viewport. Decelerates into resting position. Modals, toasts, slide-ins.', + value: 'var(--easing-entrance)', + }, + 'exit': { + description: + 'Elements leaving the viewport. Accelerates out of view. Closing modals, dismissing toasts.', + value: 'var(--easing-exit)', + }, + 'standard': { + description: + 'Default for most transitions. Smooth deceleration. Use for elements moving within the page.', + value: 'var(--easing-standard)', + }, +} + +export type TokenCategory = keyof typeof tokens +export type TokenName = keyof (typeof tokens)[C] +export type SpaceScale = keyof typeof space +export type RadiusScale = keyof typeof radius +export type ShadowScale = keyof typeof shadow diff --git a/frontend/scripts/generate-tokens.mjs b/frontend/scripts/generate-tokens.mjs new file mode 100644 index 000000000000..d63497175cb9 --- /dev/null +++ b/frontend/scripts/generate-tokens.mjs @@ -0,0 +1,318 @@ +/** + * Generate all token files from tokens.json (single source of truth). + * + * Outputs: + * 1. common/theme/tokens.ts — TypeScript exports + * 2. web/styles/_tokens.scss — CSS custom properties + * 3. documentation/TokenReference.generated.stories.tsx — flat JSX for Storybook MCP + * + * Usage: + * node scripts/generate-tokens.mjs + * npm run generate:tokens + */ + +import { readFileSync, writeFileSync } from 'node:fs' +import { resolve, dirname } from 'node:path' +import { fileURLToPath } from 'node:url' +import { execSync } from 'node:child_process' + +const __dirname = dirname(fileURLToPath(import.meta.url)) +const ROOT = resolve(__dirname, '..') +const json = JSON.parse( + readFileSync(resolve(ROOT, 'common/theme/tokens.json'), 'utf-8'), +) + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +const kebabToCamel = (s) => + s.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase()) + +const sorted = (obj) => + Object.entries(obj).sort(([a], [b]) => a.localeCompare(b)) + +const esc = (s) => s.replace(/\\/g, '\\\\').replace(/'/g, "\\'") +const lightVal = (e) => e.light ?? e.value +const cap = (s) => s.charAt(0).toUpperCase() + s.slice(1) + +const NON_COLOUR = ['space', 'radius', 'font', 'shadow', 'duration', 'easing'] +const DESCRIBED = ['space', 'radius', 'shadow', 'duration', 'easing'] + +function makeCssVar(cssVarName, fallback) { + if ( + !fallback || + fallback.startsWith('rgba') || + fallback.startsWith('cubic-bezier') + ) { + return `var(${cssVarName})` + } + return `var(${cssVarName}, ${fallback})` +} + +// --------------------------------------------------------------------------- +// Step 1: Build flat data from JSON +// --------------------------------------------------------------------------- + +function buildScssLines() { + const rootLines = [] + const darkLines = [] + + // Colour tokens + for (const [category, entries] of Object.entries(json.color)) { + rootLines.push(` // ${cap(category)}`) + for (const [, e] of sorted(entries)) { + rootLines.push(` ${e.cssVar}: ${e.light};`) + if (e.dark && e.dark !== e.light) { + darkLines.push(` ${e.cssVar}: ${e.dark};`) + } + } + rootLines.push('') + } + + // Non-colour tokens + for (const cat of NON_COLOUR) { + if (!json[cat]) continue + rootLines.push(` // ${cap(cat)}`) + for (const [, e] of sorted(json[cat])) { + const val = lightVal(e) + rootLines.push(` ${e.cssVar}: ${val};`) + if (e.dark && e.dark !== val) { + darkLines.push(` ${e.cssVar}: ${e.dark};`) + } + } + rootLines.push('') + } + + return { rootLines, darkLines } +} + +function buildTsColourLines() { + const lines = [] + for (const [category, entries] of sorted(json.color)) { + lines.push(` ${category}: {`) + for (const [key, e] of sorted(entries)) { + const v = esc(makeCssVar(e.cssVar, e.light)) + lines.push(` ${kebabToCamel(key)}: '${v}',`) + } + lines.push(' },') + } + return lines +} + +function buildTsDescribedLines() { + const blocks = [] + for (const cat of DESCRIBED) { + if (!json[cat]) continue + const lines = [] + lines.push(`// ${cap(cat)}`) + lines.push(`export const ${cat}: Record = {`) + for (const [key, e] of sorted(json[cat])) { + const v = esc(makeCssVar(e.cssVar, lightVal(e))) + const d = esc(e.description || '') + lines.push(` '${esc(key)}': {`) + lines.push(` description: '${d}',`) + lines.push(` value: '${v}',`) + lines.push(' },') + } + lines.push('}') + blocks.push(lines.join('\n')) + } + return blocks +} + +function buildTableRows(title, entries, opts = {}) { + const rows = [] + rows.push(`

${title}

`) + rows.push(" ") + rows.push(' ') + rows.push(' ') + rows.push(' ') + rows.push(' ') + if (opts.showDescription) rows.push(' ') + rows.push(' ') + rows.push(' ') + rows.push(' ') + for (const e of entries) { + rows.push(' ') + rows.push(` `) + rows.push(` `) + if (opts.showDescription && e.description) { + rows.push(` `) + } + rows.push(' ') + } + rows.push(' ') + rows.push('
TokenValueUsage
${e.cssVar}${e.value}${e.description}
') + return rows +} + +// --------------------------------------------------------------------------- +// Step 2: Render to strings +// --------------------------------------------------------------------------- + +function generateScss() { + const { rootLines, darkLines } = buildScssLines() + + const header = [ + '// =============================================================================', + '// Design Tokens — AUTO-GENERATED from common/theme/tokens.json', + '// Do not edit manually. Run: npm run generate:tokens', + '// =============================================================================', + '', + ] + + const output = [...header, ':root {', ...rootLines, '}'] + + if (darkLines.length > 0) { + output.push('', '.dark {', ...darkLines, '}') + } + + output.push('') + return output.join('\n') +} + +function generateTs() { + const colourLines = buildTsColourLines() + const describedBlocks = buildTsDescribedLines() + + const output = [ + '// =============================================================================', + '// Design Tokens — AUTO-GENERATED from common/theme/tokens.json', + '// Do not edit manually. Run: npm run generate:tokens', + '// =============================================================================', + '', + 'export const tokens = {', + ...colourLines, + '} as const', + '', + 'export type TokenEntry = {', + ' value: string', + ' description: string', + '}', + '', + ...describedBlocks, + '', + 'export type TokenCategory = keyof typeof tokens', + 'export type TokenName = keyof (typeof tokens)[C]', + 'export type SpaceScale = keyof typeof space', + 'export type RadiusScale = keyof typeof radius', + 'export type ShadowScale = keyof typeof shadow', + '', + ] + + return output.join('\n') +} + +function generateMcpStory() { + // Build all table rows + const tables = [] + + for (const [cat, entries] of Object.entries(json.color)) { + const data = Object.values(entries).map((e) => ({ + cssVar: e.cssVar, + value: e.light, + })) + tables.push(...buildTableRows(`Colour: ${cat}`, data)) + } + + for (const cat of DESCRIBED) { + if (!json[cat]) continue + const data = Object.values(json[cat]).map((e) => ({ + cssVar: e.cssVar, + value: lightVal(e), + description: e.description || '', + })) + tables.push(...buildTableRows(cap(cat), data, { showDescription: true })) + } + + const output = [ + '// AUTO-GENERATED from common/theme/tokens.json — do not edit manually', + '// Run: npm run generate:tokens', + '', + "import React from 'react'", + "import type { Meta, StoryObj } from 'storybook'", + '', + "import './docs.scss'", + "import DocPage from './components/DocPage'", + '', + 'const meta: Meta = {', + " parameters: { chromatic: { disableSnapshot: true }, layout: 'padded' },", + " title: 'Design System/Token Reference (MCP)',", + '}', + 'export default meta', + '', + 'export const AllTokens: StoryObj = {', + " name: 'All tokens (MCP reference)',", + ' render: () => (', + ' ', + ...tables, + '', + "

Pairing rules

", + '
    ', + '
  • Icon-to-text gap: --space-1 (4px) or --space-2 (8px)
  • ', + '
  • Component inner padding: --space-3 (12px) to --space-4 (16px)
  • ', + '
  • Between components: --space-4 (16px) to --space-6 (24px)
  • ', + '
  • Page sections: --space-8 (32px) and above
  • ', + '
', + '', + '

Dark mode shadows

', + '

', + ' Dark mode overrides use stronger opacity (0.20-0.40 vs 0.05-0.20).', + ' Higher elevation surfaces should use lighter backgrounds', + ' (--color-surface-emphasis) rather than relying solely on shadows.', + '

', + '', + '

Motion pairing

', + '

', + ' Combine a duration with an easing: transition: all var(--duration-normal) var(--easing-standard).', + ' Use --easing-entrance for elements appearing, --easing-exit for elements leaving.', + '

', + ' ', + ' ),', + '}', + '', + ] + + return output.join('\n') +} + +// --------------------------------------------------------------------------- +// Write and format +// --------------------------------------------------------------------------- + +const scssPath = resolve(ROOT, 'web/styles/_tokens.scss') +const tsPath = resolve(ROOT, 'common/theme/tokens.ts') +const storyPath = resolve(ROOT, 'documentation/TokenReference.generated.stories.tsx') + +writeFileSync(scssPath, generateScss(), 'utf-8') +writeFileSync(tsPath, generateTs(), 'utf-8') +writeFileSync(storyPath, generateMcpStory(), 'utf-8') + +try { + execSync(`npx prettier --write "${tsPath}" "${storyPath}"`, { + cwd: ROOT, + stdio: 'pipe', + }) +} catch { + // prettier may not be available in all environments +} + +const colorCount = Object.values(json.color).reduce( + (sum, cat) => sum + Object.keys(cat).length, + 0, +) +const nonColorCount = NON_COLOUR.reduce( + (sum, cat) => sum + (json[cat] ? Object.keys(json[cat]).length : 0), + 0, +) +console.log('Generated from tokens.json:') +console.log(` ${scssPath}`) +console.log(` ${tsPath}`) +console.log(` ${storyPath}`) +console.log( + ` ${colorCount} colour + ${nonColorCount} non-colour = ${colorCount + nonColorCount} tokens`, +) diff --git a/frontend/web/styles/_primitives.scss b/frontend/web/styles/_primitives.scss new file mode 100644 index 000000000000..faa72002e6e6 --- /dev/null +++ b/frontend/web/styles/_primitives.scss @@ -0,0 +1,104 @@ +// ============================================================================= +// Primitive Colour Palette +// ============================================================================= +// Full tonal scales (50–950) for every colour family. +// These are NOT for direct use in components — use semantic tokens instead. +// See _tokens.scss for the semantic layer. +// +// Scale structure matches Tailwind convention (11 steps per hue). +// Anchored values are locked to existing Flagsmith colours; generated steps +// were interpolated in HSL to fill the gaps. +// ============================================================================= + +// Slate (neutrals) — custom scale, not Tailwind-aligned +$slate-0: #ffffff; +$slate-50: #fafafb; +$slate-100: #eff1f4; +$slate-200: #e0e3e9; +$slate-300: #9da4ae; +$slate-400: #767d85; +$slate-500: #656d7b; +$slate-600: #1a2634; +$slate-700: #2d3443; +$slate-800: #202839; +$slate-850: #161d30; +$slate-900: #15192b; +$slate-950: #101628; + +// Purple (brand) +$purple-50: #f5f0ff; +$purple-100: #e8dbff; +$purple-200: #d4bcff; +$purple-300: #b794ff; +$purple-400: #906af6; +$purple-500: #7a4dfc; +$purple-600: #6837fc; +$purple-700: #4e25db; +$purple-800: #3919b7; +$purple-900: #2a2054; +$purple-950: #1e163e; + +// Red (danger) +$red-50: #fef2f1; +$red-100: #fce5e4; +$red-200: #f9cbc9; +$red-300: #f5a5a2; +$red-400: #f57c78; +$red-500: #ef4d56; +$red-600: #e61b26; +$red-700: #bb1720; +$red-800: #90141b; +$red-900: #701116; +$red-950: #500d11; + +// Green (success) +$green-50: #eef9f6; +$green-100: #d6f1eb; +$green-200: #b5e5da; +$green-300: #87d4c4; +$green-400: #56ccad; +$green-500: #27ab95; +$green-600: #13787b; +$green-700: #116163; +$green-800: #0e4a4c; +$green-900: #0c3a3b; +$green-950: #09292a; + +// Gold (secondary) +$gold-50: #fefbf0; +$gold-100: #fdf6e0; +$gold-200: #faeec5; +$gold-300: #fae392; +$gold-400: #f9dc80; +$gold-500: #f7d56e; +$gold-600: #e5c55f; +$gold-700: #d4b050; +$gold-800: #b38f30; +$gold-900: #8b7027; +$gold-950: #64511e; + +// Blue (info) +$blue-50: #eef8fb; +$blue-100: #d6eef5; +$blue-200: #b3e0ed; +$blue-300: #7ecde2; +$blue-400: #45bce0; +$blue-500: #0aaddf; +$blue-600: #0b8bb2; +$blue-700: #0b7190; +$blue-800: #0b576e; +$blue-900: #094456; +$blue-950: #07313e; + +// Orange (warning) +$orange-50: #fff5ec; +$orange-100: #ffe9d4; +$orange-200: #ffd7b5; +$orange-300: #ffc08a; +$orange-400: #efb47c; +$orange-500: #ff9f43; +$orange-600: #fa810c; +$orange-700: #d06907; +$orange-800: #9f5208; +$orange-900: #7b4008; +$orange-950: #592f07; diff --git a/frontend/web/styles/_tokens.scss b/frontend/web/styles/_tokens.scss new file mode 100644 index 000000000000..4766e489caeb --- /dev/null +++ b/frontend/web/styles/_tokens.scss @@ -0,0 +1,171 @@ +// ============================================================================= +// Design Tokens — AUTO-GENERATED from common/theme/tokens.json +// Do not edit manually. Run: npm run generate:tokens +// ============================================================================= + +:root { + // Surface + --color-surface-action: #6837fc; + --color-surface-action-active: #3919b7; + --color-surface-action-hover: #4e25db; + --color-surface-action-muted: rgba(104, 55, 252, 0.16); + --color-surface-action-subtle: rgba(104, 55, 252, 0.08); + --color-surface-active: rgba(0, 0, 0, 0.16); + --color-surface-danger: rgba(239, 77, 86, 0.08); + --color-surface-default: #ffffff; + --color-surface-emphasis: #e0e3e9; + --color-surface-hover: rgba(0, 0, 0, 0.08); + --color-surface-info: rgba(10, 173, 223, 0.08); + --color-surface-muted: #eff1f4; + --color-surface-subtle: #fafafb; + --color-surface-success: rgba(39, 171, 149, 0.08); + --color-surface-warning: rgba(255, 159, 67, 0.08); + + // Text + --color-text-action: #6837fc; + --color-text-danger: #ef4d56; + --color-text-default: #1a2634; + --color-text-disabled: #9da4ae; + --color-text-info: #0aaddf; + --color-text-on-fill: #ffffff; + --color-text-secondary: #656d7b; + --color-text-success: #27ab95; + --color-text-tertiary: #9da4ae; + --color-text-warning: #ff9f43; + + // Border + --color-border-action: #6837fc; + --color-border-danger: #ef4d56; + --color-border-default: rgba(101, 109, 123, 0.16); + --color-border-disabled: rgba(101, 109, 123, 0.08); + --color-border-info: #0aaddf; + --color-border-strong: rgba(101, 109, 123, 0.24); + --color-border-success: #27ab95; + --color-border-warning: #ff9f43; + + // Icon + --color-icon-action: #6837fc; + --color-icon-danger: #ef4d56; + --color-icon-default: #1a2634; + --color-icon-disabled: #9da4ae; + --color-icon-info: #0aaddf; + --color-icon-secondary: #656d7b; + --color-icon-success: #27ab95; + --color-icon-warning: #ff9f43; + + // Space + --space-0: 0px; + --space-0_5: 2px; + --space-1: 4px; + --space-10: 40px; + --space-12: 48px; + --space-16: 64px; + --space-2: 8px; + --space-3: 12px; + --space-4: 16px; + --space-5: 20px; + --space-6: 24px; + --space-8: 32px; + + // Radius + --radius-2xl: 18px; + --radius-full: 9999px; + --radius-lg: 8px; + --radius-md: 6px; + --radius-none: 0px; + --radius-sm: 4px; + --radius-xl: 10px; + --radius-xs: 2px; + + // Font + --font-body-line-height: 20px; + --font-body-size: 14px; + --font-body-sm-line-height: 18px; + --font-body-sm-size: 13px; + --font-body-sm-weight: 400; + --font-body-weight: 400; + --font-caption-line-height: 16px; + --font-caption-size: 12px; + --font-caption-weight: 400; + --font-caption-xs-line-height: 14px; + --font-caption-xs-size: 11px; + --font-caption-xs-weight: 400; + --font-family: 'OpenSans', sans-serif; + --font-h1-line-height: 46px; + --font-h1-size: 42px; + --font-h1-weight: 700; + --font-h2-line-height: 40px; + --font-h2-size: 34px; + --font-h2-weight: 700; + --font-h3-line-height: 40px; + --font-h3-size: 30px; + --font-h3-weight: 600; + --font-h4-line-height: 32px; + --font-h4-size: 24px; + --font-h4-weight: 600; + --font-h5-line-height: 28px; + --font-h5-size: 18px; + --font-h5-weight: 600; + --font-h6-line-height: 24px; + --font-h6-size: 16px; + --font-h6-weight: 600; + --font-label-line-height: 16px; + --font-label-size: 12px; + --font-label-weight: 600; + --font-weight-bold: 700; + --font-weight-medium: 500; + --font-weight-regular: 400; + --font-weight-semibold: 600; + + // Shadow + --shadow-lg: 0px 8px 16px rgba(0, 0, 0, 0.15); + --shadow-md: 0px 4px 8px rgba(0, 0, 0, 0.12); + --shadow-sm: 0px 1px 2px rgba(0, 0, 0, 0.05); + --shadow-xl: 0px 12px 24px rgba(0, 0, 0, 0.20); + + // Duration + --duration-fast: 100ms; + --duration-normal: 200ms; + --duration-slow: 300ms; + + // Easing + --easing-entrance: cubic-bezier(0.0, 0, 0.38, 0.9); + --easing-exit: cubic-bezier(0.2, 0, 1, 0.9); + --easing-standard: cubic-bezier(0.2, 0, 0.38, 0.9); + +} + +.dark { + --color-surface-action: #906af6; + --color-surface-action-active: #4e25db; + --color-surface-action-hover: #6837fc; + --color-surface-action-muted: rgba(255, 255, 255, 0.16); + --color-surface-action-subtle: rgba(255, 255, 255, 0.08); + --color-surface-active: rgba(255, 255, 255, 0.16); + --color-surface-danger: rgba(34, 23, 40, 1); + --color-surface-default: #101628; + --color-surface-emphasis: #202839; + --color-surface-hover: rgba(255, 255, 255, 0.08); + --color-surface-info: rgba(15, 32, 52, 1); + --color-surface-muted: #161d30; + --color-surface-subtle: #15192b; + --color-surface-success: rgba(17, 32, 46, 1); + --color-surface-warning: rgba(34, 31, 39, 1); + --color-text-action: #906af6; + --color-text-default: #ffffff; + --color-text-disabled: rgba(255, 255, 255, 0.32); + --color-text-secondary: #9da4ae; + --color-text-tertiary: rgba(255, 255, 255, 0.48); + --color-border-action: #906af6; + --color-border-default: rgba(255, 255, 255, 0.16); + --color-border-disabled: rgba(255, 255, 255, 0.08); + --color-border-strong: rgba(255, 255, 255, 0.24); + --color-icon-action: #906af6; + --color-icon-default: #ffffff; + --color-icon-disabled: rgba(255, 255, 255, 0.32); + --color-icon-secondary: #9da4ae; + --shadow-lg: 0px 8px 16px rgba(0, 0, 0, 0.35); + --shadow-md: 0px 4px 8px rgba(0, 0, 0, 0.30); + --shadow-sm: 0px 1px 2px rgba(0, 0, 0, 0.20); + --shadow-xl: 0px 12px 24px rgba(0, 0, 0, 0.40); +} diff --git a/frontend/web/styles/_variables.scss b/frontend/web/styles/_variables.scss index b91e7b92c8ee..aa17beed42d1 100644 --- a/frontend/web/styles/_variables.scss +++ b/frontend/web/styles/_variables.scss @@ -81,13 +81,13 @@ $danger-solid-dark-alert: rgba(34, 23, 40); // Alphas $info-alfa-8: rgba(10, 173, 223, 0.08); $info-alfa-24: rgba(10, 173, 223, 0.24); -$danger-alfa-8: rgba(255, 66, 75, 0.08); -$danger-alfa-16: rgba(255, 66, 75, 0.16); -$warning-alfa-8: rgba(255, 159, 0, 0.08); -$primary-alfa-8: rgba(149, 108, 255, 0.08); -$primary-alfa-16: rgba(149, 108, 255, 0.16); -$primary-alfa-24: rgba(149, 108, 255, 0.24); -$primary-alfa-32: rgba(149, 108, 255, 0.32); +$danger-alfa-8: rgba(239, 77, 86, 0.08); +$danger-alfa-16: rgba(239, 77, 86, 0.16); +$warning-alfa-8: rgba(255, 159, 67, 0.08); +$primary-alfa-8: rgba(104, 55, 252, 0.08); +$primary-alfa-16: rgba(104, 55, 252, 0.16); +$primary-alfa-24: rgba(104, 55, 252, 0.24); +$primary-alfa-32: rgba(104, 55, 252, 0.32); $basic-alpha-8: rgba(101, 109, 123, 0.08); $basic-alpha-16: rgba(101, 109, 123, 0.16); $basic-alpha-24: rgba(101, 109, 123, 0.24); @@ -278,7 +278,7 @@ $switcher-dark-mode-width: 22px; $checkbox-width: 20px; $checkbox-border-color: rgba(101, 109, 123, 0.24); $checkbox-border-color-dark: rgba(255, 255, 255, 0.24); -$checkbox-focus-bg: rgba(149, 108, 255, 0.08); +$checkbox-focus-bg: rgba(104, 55, 252, 0.08); $checkbox-focus-border-color: $primary400; $checkbox-hover-border-color: $primary; $checkbox-checked-hover-border-color: $primary600; diff --git a/frontend/web/styles/styles.scss b/frontend/web/styles/styles.scss index 674a2fcefaef..1380ea685d9e 100644 --- a/frontend/web/styles/styles.scss +++ b/frontend/web/styles/styles.scss @@ -1,4 +1,5 @@ @import "variables"; +@import "tokens"; @import "3rdParty/index"; @import "components/index"; @import "flexbox/index"; From 95278ef30942e8ee2e8735ea65f528ffd8e62f74 Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Mon, 30 Mar 2026 15:47:44 -0300 Subject: [PATCH 02/11] feat(storybook): Storybook 10 with component stories and Chromatic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set up Storybook 10 with webpack5, SWC compiler, and a11y addon. Chromatic CI workflow captures visual regression on every PR. Storybook UI respects OS colour scheme preference (light/dark). Chromatic modes capture every component story in both themes. Components with stories: - Banner — 4 variants (success, warning, danger, info) with CTA - Button — 7 themes, 4 sizes, disabled states, icon variants - Switch — on, off, disabled, controlled - Skeleton — text, badge, circle variants - SettingRow — pattern component using React 19 useId() - Icons — categorised searchable catalogue of all 61 icons New components: - Banner — variant-driven alert with semantic colour tokens - SettingRow — accessible settings toggle with ARIA labels - Skeleton — loading placeholder with reduced motion support - RequireFeatureOwnershipSetting — project setting (#4432) Shared Storybook utilities: - DocPage, ScaleRow, Swatch, TokenGroup helper components - docs.scss dogfooding 79 token references (spacing, colour, font, radius, motion) Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/.eslintrc.js | 1 + frontend/.gitignore | 3 + frontend/.storybook/docs-theme.scss | 104 + frontend/.storybook/main.js | 70 + frontend/.storybook/manager-head.html | 6 + frontend/.storybook/manager.js | 61 + frontend/.storybook/modes.js | 9 + frontend/.storybook/preview.js | 57 + frontend/common/types/requests.ts | 1 + frontend/common/types/responses.ts | 1 + .../components/Banner.stories.tsx | 179 + .../components/Button.stories.tsx | 194 + frontend/documentation/components/DocPage.tsx | 17 + .../components/Icons.stories.tsx | 169 + .../documentation/components/ScaleRow.tsx | 38 + .../components/Setting.stories.tsx | 151 + .../documentation/components/Skeleton.mdx | 93 + .../components/Skeleton.stories.tsx | 179 + frontend/documentation/components/Swatch.tsx | 21 + .../components/Switch.stories.tsx | 72 + .../documentation/components/TokenGroup.tsx | 35 + frontend/documentation/docs.scss | 297 + frontend/package-lock.json | 5651 ++++++++++++----- frontend/package.json | 15 +- frontend/web/components/Banner/Banner.tsx | 30 + frontend/web/components/Banner/banner.scss | 28 + frontend/web/components/Banner/index.ts | 2 + .../web/components/SettingRow/SettingRow.tsx | 53 + frontend/web/components/SettingRow/index.ts | 2 + .../components/SettingRow/setting-row.scss | 28 + frontend/web/components/Skeleton/Skeleton.tsx | 35 + frontend/web/components/Skeleton/index.ts | 2 + .../RequireFeatureOwnershipSetting.tsx | 36 + .../sections/additional-settings/index.tsx | 3 + 34 files changed, 6075 insertions(+), 1568 deletions(-) create mode 100644 frontend/.storybook/docs-theme.scss create mode 100644 frontend/.storybook/main.js create mode 100644 frontend/.storybook/manager-head.html create mode 100644 frontend/.storybook/manager.js create mode 100644 frontend/.storybook/modes.js create mode 100644 frontend/.storybook/preview.js create mode 100644 frontend/documentation/components/Banner.stories.tsx create mode 100644 frontend/documentation/components/Button.stories.tsx create mode 100644 frontend/documentation/components/DocPage.tsx create mode 100644 frontend/documentation/components/Icons.stories.tsx create mode 100644 frontend/documentation/components/ScaleRow.tsx create mode 100644 frontend/documentation/components/Setting.stories.tsx create mode 100644 frontend/documentation/components/Skeleton.mdx create mode 100644 frontend/documentation/components/Skeleton.stories.tsx create mode 100644 frontend/documentation/components/Swatch.tsx create mode 100644 frontend/documentation/components/Switch.stories.tsx create mode 100644 frontend/documentation/components/TokenGroup.tsx create mode 100644 frontend/documentation/docs.scss create mode 100644 frontend/web/components/Banner/Banner.tsx create mode 100644 frontend/web/components/Banner/banner.scss create mode 100644 frontend/web/components/Banner/index.ts create mode 100644 frontend/web/components/SettingRow/SettingRow.tsx create mode 100644 frontend/web/components/SettingRow/index.ts create mode 100644 frontend/web/components/SettingRow/setting-row.scss create mode 100644 frontend/web/components/Skeleton/Skeleton.tsx create mode 100644 frontend/web/components/Skeleton/index.ts create mode 100644 frontend/web/components/pages/project-settings/tabs/general-tab/sections/additional-settings/RequireFeatureOwnershipSetting.tsx diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 7cb3d28f9bcb..4d19d9f28f5b 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -10,6 +10,7 @@ module.exports = { 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', 'plugin:@dword-design/import-alias/recommended', + 'plugin:storybook/recommended', ], 'globals': { '$': true, diff --git a/frontend/.gitignore b/frontend/.gitignore index 29f737c9d966..37a1bdaaf119 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -33,3 +33,6 @@ common/project.js # Playwright e2e/playwright-report/ e2e/test-results/ + +*storybook.log +storybook-static diff --git a/frontend/.storybook/docs-theme.scss b/frontend/.storybook/docs-theme.scss new file mode 100644 index 000000000000..9b11ad002b94 --- /dev/null +++ b/frontend/.storybook/docs-theme.scss @@ -0,0 +1,104 @@ +// ============================================================================= +// Storybook Docs Theme Overrides +// ============================================================================= +// The Storybook docs renderer uses its own styling that doesn't respond to +// our theme toggle. These overrides ensure documentation pages are readable +// in both light and dark mode. +// ============================================================================= + +// Light mode — fix invisible inline code +.sbdocs code { + color: #1a2634; +} + +.dark { + // Docs page background + .sbdocs-wrapper { + background-color: var(--color-surface-default, #101628); + color: var(--color-text-default, #ffffff); + } + + .sbdocs-content { + color: var(--color-text-default, #ffffff); + } + + // Headings + .sbdocs h1, + .sbdocs h2, + .sbdocs h3, + .sbdocs h4, + .sbdocs h5 { + color: var(--color-text-default, #ffffff); + } + + // Body text + .sbdocs p, + .sbdocs li, + .sbdocs td, + .sbdocs th { + color: var(--color-text-default, #ffffff); + } + + // Code — inline and blocks + .sbdocs code { + background-color: var(--color-surface-emphasis, #202839); + color: var(--color-text-default, #ffffff); + } + + .sbdocs pre { + background-color: var(--color-surface-emphasis, #202839); + color: var(--color-text-default, #ffffff); + } + + // Storybook Source/code block container and all its children + .docblock-source, + .docblock-source > * { + background-color: var(--color-surface-emphasis, #202839) !important; + color: var(--color-text-default, #ffffff) !important; + } + + // Copy button inside code blocks + .docblock-source button { + background-color: var(--color-surface-muted, #161d30) !important; + color: var(--color-text-default, #ffffff) !important; + border-color: var(--color-border-default, rgba(255, 255, 255, 0.16)) !important; + } + + // Syntax highlighting tokens + .docblock-source .token { + color: var(--color-text-default, #ffffff) !important; + } + + // Horizontal rules + .sbdocs hr { + border-color: var(--color-border-default, rgba(255, 255, 255, 0.16)); + } + + // Canvas (story preview) background + .docs-story { + background-color: var(--color-surface-default, #101628); + } + + // Table borders + .sbdocs table { + border-color: var(--color-border-default, rgba(255, 255, 255, 0.16)); + } + + .sbdocs th, + .sbdocs td { + border-color: var(--color-border-default, rgba(255, 255, 255, 0.16)); + } + + // Table row backgrounds (override Storybook's alternating white rows) + .sbdocs tr { + background-color: var(--color-surface-default, #101628); + } + + .sbdocs tr:nth-child(even) { + background-color: var(--color-surface-subtle, #15192b); + } + + .sbdocs thead tr { + background-color: var(--color-surface-muted, #161d30); + } +} diff --git a/frontend/.storybook/main.js b/frontend/.storybook/main.js new file mode 100644 index 000000000000..3ad653c70ab4 --- /dev/null +++ b/frontend/.storybook/main.js @@ -0,0 +1,70 @@ +const path = require('path') +const webpack = require('webpack') + +/** @type { import('storybook').StorybookConfig } */ +const config = { + stories: [ + '../documentation/**/*.mdx', + '../documentation/**/*.stories.@(js|jsx|ts|tsx)', + ], + staticDirs: ['../web'], + addons: [ + '@storybook/addon-webpack5-compiler-swc', + '@storybook/addon-docs', + '@storybook/addon-a11y', + ], + framework: { + name: '@storybook/react-webpack5', + options: {}, + }, + swc: () => ({ + jsc: { + transform: { + react: { + runtime: 'automatic', + }, + }, + parser: { + syntax: 'typescript', + tsx: true, + }, + }, + }), + webpackFinal: async (config) => { + config.resolve = config.resolve || {} + config.resolve.alias = { + ...config.resolve.alias, + common: path.resolve(__dirname, '../common'), + components: path.resolve(__dirname, '../web/components'), + project: path.resolve(__dirname, '../web/project'), + } + + config.module = config.module || {} + config.module.rules = config.module.rules || [] + config.module.rules.push({ + test: /\.scss$/, + use: [ + 'style-loader', + { loader: 'css-loader', options: { importLoaders: 1 } }, + { + loader: 'sass-loader', + options: { + sassOptions: { + silenceDeprecations: ['slash-div'], + }, + }, + }, + ], + }) + + config.plugins = config.plugins || [] + config.plugins.push( + new webpack.DefinePlugin({ + E2E: false, + }), + ) + + return config + }, +} +module.exports = config diff --git a/frontend/.storybook/manager-head.html b/frontend/.storybook/manager-head.html new file mode 100644 index 000000000000..26112dc381c5 --- /dev/null +++ b/frontend/.storybook/manager-head.html @@ -0,0 +1,6 @@ + diff --git a/frontend/.storybook/manager.js b/frontend/.storybook/manager.js new file mode 100644 index 000000000000..21b88523b8eb --- /dev/null +++ b/frontend/.storybook/manager.js @@ -0,0 +1,61 @@ +import { addons } from 'storybook/manager-api' +import { create } from 'storybook/theming' + +// Primitive palette — mirrors _primitives.scss +// Storybook manager runs outside the app, so CSS vars aren't available. +const slate = { + 0: '#ffffff', + 50: '#fafafb', + 100: '#eff1f4', + 200: '#e0e3e9', + 300: '#9da4ae', + 500: '#656d7b', + 600: '#1a2634', + 850: '#161d30', + 900: '#15192b', +} +const purple = { 600: '#6837fc' } + +const shared = { + brandTitle: 'Flagsmith', + brandUrl: 'https://flagsmith.com', + brandImage: '/static/images/nav-logo.png', + brandTarget: '_blank', + fontBase: '"Inter", sans-serif', + colorPrimary: purple[600], + colorSecondary: purple[600], +} + +const dark = create({ + base: 'dark', + ...shared, + appBg: slate[900], + appContentBg: slate[850], + appBorderColor: 'rgba(255, 255, 255, 0.1)', + barBg: slate[900], + barTextColor: slate[300], + barSelectedColor: purple[600], + textColor: slate[200], + textMutedColor: slate[300], + textInverseColor: slate[900], +}) + +const light = create({ + base: 'light', + ...shared, + appBg: slate[50], + appContentBg: slate[0], + appBorderColor: slate[100], + barBg: slate[0], + barTextColor: slate[500], + barSelectedColor: purple[600], + textColor: slate[600], + textMutedColor: slate[300], + textInverseColor: slate[0], +}) + +const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches + +addons.setConfig({ + theme: prefersDark ? dark : light, +}) diff --git a/frontend/.storybook/modes.js b/frontend/.storybook/modes.js new file mode 100644 index 000000000000..01033618c933 --- /dev/null +++ b/frontend/.storybook/modes.js @@ -0,0 +1,9 @@ +/** @type { Record } */ +export const allModes = { + light: { + theme: 'light', + }, + dark: { + theme: 'dark', + }, +} diff --git a/frontend/.storybook/preview.js b/frontend/.storybook/preview.js new file mode 100644 index 000000000000..e01818066f34 --- /dev/null +++ b/frontend/.storybook/preview.js @@ -0,0 +1,57 @@ +import '../web/styles/styles.scss' +import './docs-theme.scss' +import { allModes } from './modes' + +/** @type { import('storybook').Preview } */ +const preview = { + globalTypes: { + theme: { + description: 'Dark mode toggle', + toolbar: { + title: 'Theme', + icon: 'moon', + items: [ + { value: 'light', title: 'Light', icon: 'sun' }, + { value: 'dark', title: 'Dark', icon: 'moon' }, + ], + dynamicTitle: true, + }, + }, + }, + initialGlobals: { + theme: window.matchMedia('(prefers-color-scheme: dark)').matches + ? 'dark' + : 'light', + }, + decorators: [ + (Story, context) => { + const theme = context.globals.theme || 'light' + const isDark = theme === 'dark' + + document.documentElement.setAttribute( + 'data-bs-theme', + isDark ? 'dark' : 'light', + ) + document.body.classList.toggle('dark', isDark) + + return Story() + }, + ], + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + backgrounds: { disable: true }, + chromatic: { + modes: { + light: allModes.light, + dark: allModes.dark, + }, + }, + }, +} + +export default preview diff --git a/frontend/common/types/requests.ts b/frontend/common/types/requests.ts index fc48e4e17841..65743f977b50 100644 --- a/frontend/common/types/requests.ts +++ b/frontend/common/types/requests.ts @@ -38,6 +38,7 @@ export type UpdateProjectBody = { stale_flags_limit_days?: number | null only_allow_lower_case_feature_names?: boolean feature_name_regex?: string | null + require_feature_owners?: boolean } export type UpdateOrganisationBody = { diff --git a/frontend/common/types/responses.ts b/frontend/common/types/responses.ts index 2265891e198c..64e7d8fea531 100644 --- a/frontend/common/types/responses.ts +++ b/frontend/common/types/responses.ts @@ -226,6 +226,7 @@ export type Project = { total_segments?: number only_allow_lower_case_feature_names?: boolean feature_name_regex?: string | null + require_feature_owners?: boolean environments: Environment[] } export type ImportStrategy = 'SKIP' | 'OVERWRITE_DESTRUCTIVE' diff --git a/frontend/documentation/components/Banner.stories.tsx b/frontend/documentation/components/Banner.stories.tsx new file mode 100644 index 000000000000..6e6dbc31764b --- /dev/null +++ b/frontend/documentation/components/Banner.stories.tsx @@ -0,0 +1,179 @@ +import React from 'react' +import type { Meta, StoryObj } from 'storybook' + +import Banner from 'components/Banner' +import type { BannerProps } from 'components/Banner' +import { Button } from 'components/base/forms/Button' + +const meta: Meta = { + argTypes: { + children: { + control: 'text', + description: + 'Banner message content. Can include a CTA button as a child.', + }, + variant: { + control: 'select', + description: 'Feedback colour variant.', + options: ['success', 'warning', 'danger', 'info'], + }, + }, + args: { + children: 'This is a banner message.', + variant: 'info', + }, + component: Banner, + parameters: { layout: 'padded' }, + title: 'Components/Banner', +} + +export default meta + +type Story = StoryObj + +// --------------------------------------------------------------------------- +// Default — interactive playground +// --------------------------------------------------------------------------- + +export const Default: Story = {} + +// --------------------------------------------------------------------------- +// Individual variants +// --------------------------------------------------------------------------- + +export const Success: Story = { + args: { + children: 'Your changes have been saved successfully.', + variant: 'success', + }, + parameters: { + docs: { + description: { + story: 'Use `success` for confirming a completed action.', + }, + }, + }, +} + +export const Warning: Story = { + args: { + children: 'Your trial is ending in 3 days.', + variant: 'warning', + }, + parameters: { + docs: { + description: { + story: + 'Use `warning` for cautionary messages that need attention but are not critical.', + }, + }, + }, +} + +export const Danger: Story = { + args: { + children: 'Your API key has been revoked.', + variant: 'danger', + }, + parameters: { + docs: { + description: { + story: 'Use `danger` for errors or critical issues.', + }, + }, + }, +} + +export const Info: Story = { + args: { + children: 'A new version of Flagsmith is available.', + variant: 'info', + }, + parameters: { + docs: { + description: { + story: 'Use `info` for neutral informational messages.', + }, + }, + }, +} + +// --------------------------------------------------------------------------- +// With CTA (passed as children) +// --------------------------------------------------------------------------- + +export const WithCTA: Story = { + name: 'With CTA button', + parameters: { + docs: { + description: { + story: + 'Add a CTA by passing a `Button` as part of `children`. This keeps the Banner API simple — the banner renders whatever you give it.', + }, + }, + }, + render: () => ( + + Your trial is ending in 3 days. + + + ), +} + +export const DangerWithCTA: Story = { + name: 'Danger with CTA', + parameters: { + docs: { + description: { + story: + 'For danger banners, use `theme="danger"` on the CTA button for visual consistency.', + }, + }, + }, + render: () => ( + + Your API key has been revoked. + + + ), +} + +// --------------------------------------------------------------------------- +// All variants +// --------------------------------------------------------------------------- + +export const AllVariants: Story = { + name: 'All variants', + parameters: { + docs: { + description: { + story: + 'All four banner variants. Each has a default icon that matches the variant. Banners are persistent — not closable or dismissable.', + }, + }, + }, + render: () => ( +
+ + Your changes have been saved successfully. + + + Your trial is ending in 3 days. + + + + Your API key has been revoked. + + + A new version of Flagsmith is available. +
+ ), +} diff --git a/frontend/documentation/components/Button.stories.tsx b/frontend/documentation/components/Button.stories.tsx new file mode 100644 index 000000000000..e4be8e2ffdab --- /dev/null +++ b/frontend/documentation/components/Button.stories.tsx @@ -0,0 +1,194 @@ +import React from 'react' +import type { Meta, StoryObj } from 'storybook' + +import { + Button, + themeClassNames, + sizeClassNames, +} from 'components/base/forms/Button' +import type { ButtonType } from 'components/base/forms/Button' + +const themeOptions = Object.keys(themeClassNames) as Array< + keyof typeof themeClassNames +> +const sizeOptions = Object.keys(sizeClassNames) as Array< + keyof typeof sizeClassNames +> + +const meta: Meta = { + argTypes: { + children: { + control: 'text', + description: 'Button label content.', + }, + disabled: { + control: 'boolean', + description: 'Disables the button, preventing interaction.', + }, + size: { + control: 'select', + description: 'Size of the button.', + options: sizeOptions, + table: { defaultValue: { summary: 'default' } }, + }, + theme: { + control: 'select', + description: 'Visual variant of the button.', + options: themeOptions, + table: { defaultValue: { summary: 'primary' } }, + }, + }, + args: { + children: 'Button', + disabled: false, + size: 'default', + theme: 'primary', + }, + component: Button, + parameters: { layout: 'centered' }, + title: 'Components/Button', +} + +export default meta + +type Story = StoryObj + +// --------------------------------------------------------------------------- +// Default — interactive playground +// --------------------------------------------------------------------------- + +export const Default: Story = {} + +// --------------------------------------------------------------------------- +// All Variants +// --------------------------------------------------------------------------- + +export const Variants: Story = { + parameters: { + docs: { + description: { + story: + 'All available button themes. Use `primary` for main actions, `secondary` for alternatives, `outline` for low-emphasis actions, `danger` for destructive actions, and `success` for positive confirmations.', + }, + }, + }, + render: () => ( +
+ + + + + + + +
+ ), +} + +// --------------------------------------------------------------------------- +// Sizes +// --------------------------------------------------------------------------- + +export const Sizes: Story = { + parameters: { + docs: { + description: { + story: 'Button sizes from large to extra small.', + }, + }, + }, + render: () => ( +
+ + + + +
+ ), +} + +// --------------------------------------------------------------------------- +// Disabled +// --------------------------------------------------------------------------- + +export const Disabled: Story = { + parameters: { + docs: { + description: { + story: 'Disabled buttons are non-interactive and visually muted.', + }, + }, + }, + render: () => ( +
+ + + + +
+ ), +} + +// --------------------------------------------------------------------------- +// With Icons +// --------------------------------------------------------------------------- + +export const WithIcons: Story = { + parameters: { + docs: { + description: { + story: + 'Buttons support `iconLeft` and `iconRight` props. Pass any `IconName` from the icon system.', + }, + }, + }, + render: () => ( +
+ + + +
+ ), +} diff --git a/frontend/documentation/components/DocPage.tsx b/frontend/documentation/components/DocPage.tsx new file mode 100644 index 000000000000..6a50ffd68514 --- /dev/null +++ b/frontend/documentation/components/DocPage.tsx @@ -0,0 +1,17 @@ +import React from 'react' + +type DocPageProps = { + children: React.ReactNode + description: React.ReactNode + title: string +} + +const DocPage: React.FC = ({ children, description, title }) => ( +
+

{title}

+

{description}

+ {children} +
+) + +export default DocPage diff --git a/frontend/documentation/components/Icons.stories.tsx b/frontend/documentation/components/Icons.stories.tsx new file mode 100644 index 000000000000..c659a3ecabbc --- /dev/null +++ b/frontend/documentation/components/Icons.stories.tsx @@ -0,0 +1,169 @@ +import React, { useState } from 'react' +import type { Meta, StoryObj } from 'storybook' +import Icon from 'components/Icon' +import type { IconName } from 'components/Icon' + +// eslint-disable-next-line @dword-design/import-alias/prefer-alias +import '../docs.scss' + +const meta: Meta = { + parameters: { layout: 'padded' }, + title: 'Components/Icons', +} +export default meta + +type IconCategory = { + label: string + icons: IconName[] +} + +const CATEGORIES: IconCategory[] = [ + { + icons: [ + 'arrow-left', + 'arrow-right', + 'chevron-down', + 'chevron-left', + 'chevron-right', + 'chevron-up', + 'expand', + 'link', + 'open-external-link', + ], + label: 'Navigation', + }, + { + icons: [ + 'close-circle', + 'copy', + 'copy-outlined', + 'edit', + 'minus-circle', + 'more-vertical', + 'options-2', + 'plus', + 'refresh', + 'search', + 'trash-2', + ], + label: 'Actions', + }, + { + icons: [ + 'checkmark', + 'checkmark-circle', + 'checkmark-square', + 'eye', + 'eye-off', + 'info', + 'info-outlined', + 'lock', + 'shield', + 'warning', + ], + label: 'Status', + }, + { + icons: [ + 'github', + 'issue-closed', + 'issue-linked', + 'pr-closed', + 'pr-draft', + 'pr-linked', + 'pr-merged', + ], + label: 'GitHub', + }, + { + icons: [ + 'bar-chart', + 'features', + 'flash', + 'flask', + 'layers', + 'list', + 'pie-chart', + 'rocket', + 'setting', + ], + label: 'Features', + }, + { + icons: ['people', 'person'], + label: 'People', + }, + { + icons: ['code', 'file-text', 'height', 'radio', 'request'], + label: 'Content', + }, + { + icons: ['bell', 'calendar', 'clock', 'timer'], + label: 'Time', + }, + { + icons: ['moon', 'sun'], + label: 'Theme', + }, + { + icons: ['award', 'google'], + label: 'Brand', + }, +] + +const ALL_ICONS = CATEGORIES.flatMap((c) => c.icons) + +const IconCard: React.FC<{ name: IconName }> = ({ name }) => ( +
+ + {name} +
+) + +const IconCatalogue: React.FC = () => { + const [search, setSearch] = useState('') + const query = search.toLowerCase() + + return ( +
+

Icon Catalogue

+

+ {ALL_ICONS.length} icons from {''}. Use:{' '} + {''} +

+ setSearch(e.target.value)} + placeholder='Search icons...' + type='text' + value={search} + /> + {CATEGORIES.map(({ icons, label }) => { + const filtered = icons.filter((name) => + name.toLowerCase().includes(query), + ) + if (filtered.length === 0) return null + return ( +
+

{label}

+
+ {filtered.map((name) => ( + + ))} +
+
+ ) + })} + {ALL_ICONS.filter((n) => n.includes(query)).length === 0 && ( +

+ No icons match “{search}” +

+ )} +
+ ) +} + +export const Catalogue: StoryObj = { + name: 'All icons', + render: () => , +} diff --git a/frontend/documentation/components/ScaleRow.tsx b/frontend/documentation/components/ScaleRow.tsx new file mode 100644 index 000000000000..b8877dc9eb4b --- /dev/null +++ b/frontend/documentation/components/ScaleRow.tsx @@ -0,0 +1,38 @@ +import React from 'react' + +type Swatch = { step: string; hex: string; variable: string } +type Scale = { name: string; swatches: Swatch[] } + +const SwatchCard: React.FC<{ swatch: Swatch }> = ({ swatch }) => { + const r = parseInt(swatch.hex.slice(1, 3), 16) + const g = parseInt(swatch.hex.slice(3, 5), 16) + const b = parseInt(swatch.hex.slice(5, 7), 16) + const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255 + const textColor = luminance > 0.5 ? '#1a2634' : '#ffffff' + + return ( +
+
+ {swatch.step} +
+ {swatch.hex} +
+ ) +} + +const ScaleRow: React.FC<{ scale: Scale }> = ({ scale }) => ( +
+

{scale.name}

+
+ {scale.swatches.map((s) => ( + + ))} +
+
+) + +export default ScaleRow +export type { Scale, Swatch } diff --git a/frontend/documentation/components/Setting.stories.tsx b/frontend/documentation/components/Setting.stories.tsx new file mode 100644 index 000000000000..aa6ecdbba2a4 --- /dev/null +++ b/frontend/documentation/components/Setting.stories.tsx @@ -0,0 +1,151 @@ +import React, { useState } from 'react' +import type { Meta, StoryObj } from 'storybook' + +import SettingRow from 'components/SettingRow' +import type { SettingRowProps } from 'components/SettingRow' + +const meta: Meta = { + argTypes: { + checked: { + control: 'boolean', + description: 'Whether the setting is enabled.', + }, + description: { + control: 'text', + description: 'Explanation of what the setting does.', + }, + disabled: { + control: 'boolean', + description: 'Disables the toggle during save operations.', + }, + title: { + control: 'text', + description: 'Setting name displayed as a heading.', + }, + }, + args: { + checked: false, + description: 'Description of what this setting controls.', + disabled: false, + title: 'Setting name', + }, + component: SettingRow, + parameters: { layout: 'padded' }, + title: 'Patterns/SettingRow', +} + +export default meta + +type Story = StoryObj + +// --------------------------------------------------------------------------- +// Default — interactive playground +// --------------------------------------------------------------------------- + +const InteractiveSetting = (args: SettingRowProps) => { + const [checked, setChecked] = useState(args.checked) + return +} + +export const Default: Story = { + render: (args) => , +} + +// --------------------------------------------------------------------------- +// Real examples from the codebase +// --------------------------------------------------------------------------- + +const PreventFlagDefaultsExample = () => { + const [checked, setChecked] = useState(false) + return ( + + ) +} + +export const PreventFlagDefaults: Story = { + name: 'Prevent flag defaults', + parameters: { + docs: { + description: { + story: + 'Existing project setting. Prevents defaults from being set across all environments when creating a feature.', + }, + }, + }, + render: () => , +} + +const RequireFeatureOwnershipExample = () => { + const [checked, setChecked] = useState(true) + return ( + + ) +} + +export const RequireFeatureOwnership: Story = { + name: 'Require feature ownership', + parameters: { + docs: { + description: { + story: + 'Proposed setting for issue #4432. When enabled, users must assign at least one owner when creating a feature flag.', + }, + }, + }, + render: () => , +} + +// --------------------------------------------------------------------------- +// Multiple settings (as they appear in project settings) +// --------------------------------------------------------------------------- + +const SettingsGroupExample = () => { + const [preventDefaults, setPreventDefaults] = useState(false) + const [caseSensitivity, setCaseSensitivity] = useState(false) + const [requireOwnership, setRequireOwnership] = useState(true) + return ( +
+ + + +
+ ) +} + +export const SettingsGroup: Story = { + name: 'Settings group', + parameters: { + docs: { + description: { + story: + 'Multiple settings stacked vertically, as they appear in Project Settings → Additional Settings.', + }, + }, + }, + render: () => , +} diff --git a/frontend/documentation/components/Skeleton.mdx b/frontend/documentation/components/Skeleton.mdx new file mode 100644 index 000000000000..1953ea5f3486 --- /dev/null +++ b/frontend/documentation/components/Skeleton.mdx @@ -0,0 +1,93 @@ +{/* Skeleton.mdx */} +import { Meta, Canvas, Story } from '@storybook/addon-docs/blocks' +import * as SkeletonStories from './Skeleton.stories' + + + +# Skeleton + +A skeleton is a placeholder that approximates the shape of content while it loads. It reduces perceived loading time by showing users the structure of the page before data arrives — instead of a blank screen or a spinner. + +Skeletons work best when the layout is **predictable** (list rows, cards, settings). They signal to the user that content is coming and *where* it will appear. The brain processes the skeleton shape as "almost loaded" rather than "waiting", which makes the load feel faster even when it isn't. + +--- + +## When to use + +- **Loading data from an API** and you want to show the layout shape before content arrives. +- **The content area has a predictable structure** — list rows, cards, form fields, settings. +- **You want to reduce perceived loading time** by showing a placeholder instead of a spinner. + +## When not to use + +- **The loading state is brief** (<200ms) — use a spinner instead. A skeleton that flashes and disappears is worse than no skeleton. +- **The content structure is unpredictable** — use a full-page spinner or a progress bar. +- **You're loading a single value** — inline spinners or a simple "Loading..." text work better. + +--- + +## How it works + +The `.skeleton` CSS class applies a **shimmer gradient animation** — a light band sweeps across the placeholder from left to right, creating the impression of activity. + +- **Dark mode:** The gradient automatically switches to lighter alpha values (`white-alpha-8` / `white-alpha-16`). No extra code needed. +- **Reduced motion:** The animation respects `prefers-reduced-motion: reduce`. Users who disable motion see a static grey placeholder instead of the shimmer. +- **Screen readers:** Skeleton elements are purely decorative — they carry no semantic meaning and screen readers skip them. + +--- + +## Variants + +Three shape variants for different content types: + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VariantShapeUse for
text (default)Rectangular bar, 16px tall, 4px radiusText lines, labels, values
badgePill shape, 12px radiusTags, status badges, chips
circleFully roundAvatars, icon placeholders, toggle switches
+ +--- + +## Composing skeletons + +The goal is that the skeleton and the loaded content **occupy the same space** — no layout shift when data arrives. Compose multiple Skeleton elements to match your content layout. + +### Feature row + + + +### Settings section + + + +--- + +## Best practices + +1. **Match the real content dimensions.** If the loaded text is ~180px wide, make the skeleton ~180px wide. Close is good enough — exact pixel matching isn't needed. +2. **Don't skeleton everything.** Static UI (headers, sidebars, navigation) should render immediately. Only skeleton the dynamic content area. +3. **Keep skeleton structure simple.** Two or three shapes per row is enough. Over-detailed skeletons are harder to maintain and don't improve the user experience. +4. **Avoid layout shift.** The skeleton should occupy the same height as the loaded content. If the loaded list has 65px rows, the skeleton rows should be 65px too. diff --git a/frontend/documentation/components/Skeleton.stories.tsx b/frontend/documentation/components/Skeleton.stories.tsx new file mode 100644 index 000000000000..0fcc0aaddad1 --- /dev/null +++ b/frontend/documentation/components/Skeleton.stories.tsx @@ -0,0 +1,179 @@ +import React from 'react' +import type { Meta, StoryObj } from 'storybook' + +import Skeleton from 'components/Skeleton' +import type { SkeletonProps } from 'components/Skeleton' + +const meta: Meta = { + argTypes: { + height: { + control: 'number', + description: 'Height of the skeleton element.', + }, + variant: { + control: 'select', + description: + 'Shape variant. `text` for inline text placeholders, `badge` for pill shapes, `circle` for avatars and icons.', + options: ['text', 'badge', 'circle'], + }, + width: { + control: 'number', + description: 'Width of the skeleton element.', + }, + }, + args: { + variant: 'text', + width: 200, + }, + component: Skeleton, + parameters: { layout: 'padded' }, + title: 'Components/Skeleton', +} + +export default meta + +type Story = StoryObj + +// --------------------------------------------------------------------------- +// Default — interactive playground +// --------------------------------------------------------------------------- + +export const Default: Story = {} + +// --------------------------------------------------------------------------- +// Variants +// --------------------------------------------------------------------------- + +export const Variants: Story = { + parameters: { + docs: { + description: { + story: + 'Three shape variants for different content types. All include a shimmer animation that respects `prefers-reduced-motion`.', + }, + }, + }, + render: () => ( +
+
+ + text — default, 16px height, 4px radius +
+
+ + badge — pill shape, 12px radius +
+
+ + circle — round, for avatars and icons +
+
+ ), +} + +// --------------------------------------------------------------------------- +// When to use +// --------------------------------------------------------------------------- + +export const WhenToUse: Story = { + name: 'When to use', + parameters: { + docs: { + description: { + story: ` +**Use Skeleton when:** +- Loading data from an API and you want to show the layout shape before content arrives +- The content area has a predictable structure (list rows, cards, form fields) +- You want to reduce perceived loading time by showing a placeholder + +**Don't use Skeleton when:** +- The loading state is brief (<200ms) — use a spinner instead +- The content structure is unpredictable — use a full-page spinner +- You're loading a single value — inline spinners work better + +**Accessibility:** +- Shimmer animation respects \`prefers-reduced-motion: reduce\` +- Skeleton elements are decorative — screen readers skip them + `, + }, + }, + }, + render: () => ( +
+ + + +
+ ), +} + +// --------------------------------------------------------------------------- +// Composition: Feature row skeleton +// --------------------------------------------------------------------------- + +export const FeatureRowExample: Story = { + name: 'Feature row example', + parameters: { + docs: { + description: { + story: + 'Compose multiple Skeleton elements to match the layout of a feature row. This mirrors what `FeatureRowSkeleton` renders.', + }, + }, + }, + render: () => ( +
+
+
+ + +
+
+ + + +
+ ), +} + +// --------------------------------------------------------------------------- +// Composition: Settings skeleton +// --------------------------------------------------------------------------- + +export const SettingsExample: Story = { + name: 'Settings example', + parameters: { + docs: { + description: { + story: + 'Compose Skeleton elements to match the layout of a settings section while data loads.', + }, + }, + }, + render: () => ( +
+ {[1, 2, 3].map((i) => ( +
+
+ + +
+ +
+ ))} +
+ ), +} diff --git a/frontend/documentation/components/Swatch.tsx b/frontend/documentation/components/Swatch.tsx new file mode 100644 index 000000000000..2a97fca32ea6 --- /dev/null +++ b/frontend/documentation/components/Swatch.tsx @@ -0,0 +1,21 @@ +import React from 'react' + +type SwatchProps = { + colour: string + label?: string + size?: number +} + +const Swatch: React.FC = ({ colour, label, size = 40 }) => ( +
+
+ {label || colour} +
+) + +export default Swatch +export type { SwatchProps } diff --git a/frontend/documentation/components/Switch.stories.tsx b/frontend/documentation/components/Switch.stories.tsx new file mode 100644 index 000000000000..9c2068c004e3 --- /dev/null +++ b/frontend/documentation/components/Switch.stories.tsx @@ -0,0 +1,72 @@ +import React, { useState } from 'react' +import type { Meta, StoryObj } from 'storybook' + +import Switch from 'components/Switch' +import type { SwitchProps } from 'components/Switch' + +const meta: Meta = { + argTypes: { + checked: { + control: 'boolean', + description: 'Whether the switch is on or off.', + }, + disabled: { + control: 'boolean', + description: 'Disables the switch, preventing interaction.', + }, + }, + args: { + checked: false, + disabled: false, + }, + component: Switch, + parameters: { layout: 'centered' }, + title: 'Components/Switch', +} + +export default meta + +type Story = StoryObj + +// --------------------------------------------------------------------------- +// Default — interactive +// --------------------------------------------------------------------------- + +const InteractiveSwitch = () => { + const [checked, setChecked] = useState(false) + return +} + +export const Default: Story = { + render: () => , +} + +// --------------------------------------------------------------------------- +// States +// --------------------------------------------------------------------------- + +export const States: Story = { + parameters: { + docs: { + description: { + story: 'Switch in all visual states: off, on, and disabled.', + }, + }, + }, + render: () => ( +
+
+ + Off +
+
+ + On +
+
+ + Disabled +
+
+ ), +} diff --git a/frontend/documentation/components/TokenGroup.tsx b/frontend/documentation/components/TokenGroup.tsx new file mode 100644 index 000000000000..0b48e0a01a1a --- /dev/null +++ b/frontend/documentation/components/TokenGroup.tsx @@ -0,0 +1,35 @@ +import React from 'react' + +type TokenEntry = { cssVar: string; computed: string } +type TokenGroupData = { title: string; tokens: TokenEntry[] } + +const TokenSwatch: React.FC<{ token: TokenEntry }> = ({ token }) => ( +
+
+ {token.cssVar} + {token.computed} +
+) + +const TokenGroup: React.FC<{ group: TokenGroupData }> = ({ group }) => ( +
+

{group.title}

+
+ + {['Token', 'Computed value'].map((h) => ( + + {h} + + ))} +
+ {group.tokens.map((token) => ( + + ))} +
+) + +export default TokenGroup +export type { TokenEntry, TokenGroupData } diff --git a/frontend/documentation/docs.scss b/frontend/documentation/docs.scss new file mode 100644 index 000000000000..21c0443f7c32 --- /dev/null +++ b/frontend/documentation/docs.scss @@ -0,0 +1,297 @@ +// ============================================================================= +// Storybook Documentation Styles +// ============================================================================= +// Shared styles for documentation stories. Uses design token custom properties +// for spacing, radius, and colours — dogfooding the token system. +// ============================================================================= + +// --------------------------------------------------------------------------- +// Page layout +// --------------------------------------------------------------------------- + +.docs-page { + font-family: 'OpenSans', sans-serif; + max-width: 960px; + + code { + color: var(--color-text-default, #1a2634); + } +} + +.docs-page__title { + color: var(--color-text-default, #1a2634); + margin-bottom: var(--space-1, 4px); +} + +.docs-page__description { + color: var(--color-text-secondary, #656d7b); + font-size: var(--font-body-size, 14px); + margin-bottom: var(--space-6, 24px); + + code { + color: inherit; + } +} + +// --------------------------------------------------------------------------- +// Colour palette (swatches) +// --------------------------------------------------------------------------- + +.scale-row { + margin-bottom: var(--space-8, 32px); +} + +.scale-row__title { + color: var(--color-text-default, #1a2634); + font-size: var(--font-h6-size, 16px); + font-weight: var(--font-weight-bold, 700); + margin-bottom: var(--space-3, 12px); +} + +.scale-row__swatches { + display: flex; + gap: 6px; +} + +.swatch-card { + display: flex; + flex: 1; + flex-direction: column; + gap: var(--space-1, 4px); + min-width: 0; +} + +.swatch-card__colour { + align-items: flex-end; + border: 1px solid rgba(128, 128, 128, 0.2); + border-radius: var(--radius-lg, 8px); + display: flex; + font-size: 11px; + font-weight: var(--font-weight-semibold, 600); + height: 56px; + padding: 6px; +} + +.swatch-card__hex { + color: var(--color-text-secondary, #656d7b); + font-size: 10px; +} + +// --------------------------------------------------------------------------- +// Token table +// --------------------------------------------------------------------------- + +.token-group { + margin-bottom: var(--space-8, 32px); +} + +.token-group__title { + color: var(--color-text-default, #1a2634); + font-size: var(--font-h6-size, 16px); + font-weight: var(--font-weight-bold, 700); + margin-bottom: var(--space-3, 12px); +} + +.token-group__header { + border-bottom: 2px solid var(--color-border-strong, rgba(101, 109, 123, 0.24)); + display: grid; + gap: var(--space-3, 12px); + grid-template-columns: 48px 1fr 1fr; + padding-bottom: 6px; +} + +.token-group__header-label { + color: var(--color-text-secondary, #656d7b); + font-size: 10px; + font-weight: var(--font-weight-bold, 700); + letter-spacing: 0.06em; + text-transform: uppercase; +} + +.token-swatch { + align-items: center; + border-bottom: 1px solid var(--color-border-default, rgba(101, 109, 123, 0.16)); + display: grid; + gap: var(--space-3, 12px); + grid-template-columns: 48px 1fr 1fr; + padding: var(--space-2, 8px) 0; +} + +.token-swatch__preview { + border: 1px solid rgba(128, 128, 128, 0.2); + border-radius: var(--radius-lg, 8px); + height: 40px; + width: 40px; +} + +.token-swatch__name { + color: var(--color-text-default, #1a2634); + font-size: var(--font-caption-size, 12px); + font-weight: var(--font-weight-semibold, 600); +} + +.token-swatch__value { + color: var(--color-text-secondary, #656d7b); + font-size: 11px; +} + +// --------------------------------------------------------------------------- +// Categorical palette (swatches with labels) +// --------------------------------------------------------------------------- + +.cat-swatch { + align-items: center; + display: flex; + flex-direction: column; + gap: var(--space-1, 4px); +} + +.cat-swatch__colour { + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: var(--radius-md, 6px); + height: 40px; + width: 40px; +} + +.cat-swatch__label { + color: var(--color-text-default, #1a2634); + font-size: 11px; + text-align: center; + white-space: pre-line; +} + +.cat-grid { + display: flex; + flex-wrap: wrap; + gap: var(--space-4, 16px); +} + +.cat-health-row { + display: flex; + gap: var(--space-6, 24px); +} + +.cat-health-item { + align-items: center; + display: flex; + gap: var(--space-2, 8px); +} + +.cat-health-item__migration { + color: var(--color-text-secondary, #656d7b); + font-size: var(--font-caption-size, 12px); + + code { + color: var(--color-text-default, #1a2634); + } +} + +.cat-note { + color: var(--color-text-secondary, #656d7b); + margin-top: var(--space-4, 16px); +} + +// --------------------------------------------------------------------------- +// Icon catalogue +// --------------------------------------------------------------------------- + +.icon-catalogue__header { + margin-bottom: var(--space-0_5, 2px); +} + +.icon-catalogue__description { + color: var(--color-text-secondary, #656d7b); + font-size: var(--font-body-sm-size, 13px); + margin-bottom: var(--space-3, 12px); +} + +.icon-catalogue__search { + background: var(--color-surface-subtle, #fafafb); + border: 1px solid var(--color-border-default, #e0e3e9); + border-radius: var(--radius-md, 6px); + color: var(--color-text-default, #1a2634); + font-size: var(--font-body-sm-size, 13px); + margin-bottom: var(--space-4, 16px); + padding: var(--space-1, 4px) var(--space-3, 12px); + width: 100%; +} + +.icon-catalogue__category { + margin-bottom: var(--space-4, 16px); +} + +.icon-catalogue__category-label { + border-bottom: 1px solid var(--color-border-default, #e0e3e9); + color: var(--color-text-secondary, #656d7b); + font-size: var(--font-caption-size, 12px); + font-weight: var(--font-weight-semibold, 600); + letter-spacing: 0.04em; + margin-bottom: var(--space-2, 8px); + padding-bottom: var(--space-1, 4px); + text-transform: uppercase; +} + +.icon-catalogue__grid { + display: grid; + gap: var(--space-2, 8px); + grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); +} + +.icon-catalogue__card { + align-items: center; + background: var(--color-surface-muted, #eff1f4); + border: 1px solid var(--color-border-default, #e0e3e9); + border-radius: var(--radius-sm, 4px); + color: var(--color-text-default, #1a2634); + cursor: default; + display: flex; + flex-direction: column; + gap: var(--space-1, 4px); + padding: var(--space-3, 12px) var(--space-2, 8px) var(--space-2, 8px); + transition: background var(--duration-fast, 100ms); +} + +.icon-catalogue__card:hover { + background: var(--color-surface-emphasis, #e0e3e9); +} + +.icon-catalogue__card code { + color: var(--color-text-secondary, #656d7b); + font-size: 10px; + text-align: center; + word-break: break-word; +} + +.icon-catalogue__empty { + color: var(--color-text-tertiary, #9da4ae); + margin-top: var(--space-4, 16px); +} + +// --------------------------------------------------------------------------- +// Documentation table (Decision Framework, etc.) +// --------------------------------------------------------------------------- + +.docs-table { + border-collapse: collapse; + font-size: var(--font-body-size, 14px); + width: 100%; + + th, + td { + border-bottom: 1px solid var(--color-border-default, rgba(101, 109, 123, 0.16)); + padding: var(--space-2, 8px) var(--space-3, 12px); + text-align: left; + } + + th { + color: var(--color-text-secondary, #656d7b); + font-size: 11px; + font-weight: var(--font-weight-bold, 700); + letter-spacing: 0.04em; + text-transform: uppercase; + } + + code { + font-size: var(--font-caption-size, 12px); + } +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ace2e625103e..fb4c7a483900 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -120,6 +120,10 @@ "@dword-design/eslint-plugin-import-alias": "^2.0.7", "@playwright/test": "^1.58.2", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15", + "@storybook/addon-a11y": "^10.3.3", + "@storybook/addon-docs": "^10.3.3", + "@storybook/addon-webpack5-compiler-swc": "^4.0.2", + "@storybook/react-webpack5": "^10.3.3", "@types/archiver": "^6.0.2", "@types/classnames": "^2.3.1", "@types/color": "^3.0.3", @@ -135,6 +139,7 @@ "@typescript-eslint/eslint-plugin": "5.4.0", "@typescript-eslint/parser": "5.4.0", "archiver": "^7.0.1", + "chromatic": "^15.2.0", "eslint": "^8.0.1", "eslint-config-prettier": "^8.3.0", "eslint-plugin-import": "2.27.5", @@ -144,6 +149,7 @@ "eslint-plugin-react-hooks": "4.3.0", "eslint-plugin-sort-destructure-keys": "^1.4.0", "eslint-plugin-sort-keys-fix": "^1.1.2", + "eslint-plugin-storybook": "^10.3.3", "eslint-plugin-unused-imports": "^4.3.0", "husky": "^8.0.2", "jest": "^30.2.0", @@ -155,6 +161,7 @@ "raw-loader": "0.5.1", "react-refresh": "^0.14.2", "ssgrtk": "^0.3.5", + "storybook": "^10.3.3", "ts-jest": "^29.4.6", "typescript": "5.9.3" }, @@ -163,6 +170,13 @@ "npm": "10.x" } }, + "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/@amplitude/analytics-browser": { "version": "2.22.0", "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-2.22.0.tgz", @@ -485,12 +499,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -499,9 +513,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -547,13 +561,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", - "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -575,12 +589,12 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.2", + "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -694,27 +708,27 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -833,25 +847,25 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" }, "engines": { "node": ">=6.9.0" } }, "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.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.28.5" + "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -2314,31 +2328,31 @@ } }, "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", - "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", "debug": "^4.3.1" }, "engines": { @@ -2346,9 +2360,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==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -2700,1169 +2714,1157 @@ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", "license": "MIT" }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", + "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "node": ">=18" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "node_modules/@esbuild/android-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", + "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=18" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "node_modules/@esbuild/android-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", + "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=18" } }, - "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "node_modules/@esbuild/android-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", + "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=18" } }, - "node_modules/@flagsmith/flagsmith": { - "version": "11.0.0-internal.6", - "resolved": "https://registry.npmjs.org/@flagsmith/flagsmith/-/flagsmith-11.0.0-internal.6.tgz", - "integrity": "sha512-Vz719LOLC6h6KDHqlULOJTCth5RuX2iT4GPxcTZTGxOrxM9q4XwE8AxxLqoExgucXrd2neKUMU0PDmB1Q3LE0Q==", - "license": "BSD-3-Clause" - }, - "node_modules/@floating-ui/core": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", - "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", + "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.10" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@floating-ui/dom": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.3.tgz", - "integrity": "sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", + "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.7.3", - "@floating-ui/utils": "^0.2.10" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.5.tgz", - "integrity": "sha512-HDO/1/1oH9fjj4eLgegrlH3dklZpHtUYYFiVwMUwfGvk9jWDRWqkklA2/NFScknrcNSspbV868WjXORvreDX+Q==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", + "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.7.3" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@floating-ui/utils": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", - "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", - "license": "MIT" - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", + "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=10.10.0" + "node": ">=18" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@esbuild/linux-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", + "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "cpu": [ + "arm" + ], "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=18" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", + "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "BSD-3-Clause" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@inquirer/external-editor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", - "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", + "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "chardet": "^2.1.1", - "iconv-lite": "^0.7.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } } }, - "node_modules/@ionic/core": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.8.6.tgz", - "integrity": "sha512-HAYZdEmeJgOdo2kDlZkcCGHb+zs/vjU6iv4skbVBL7y+OnSv/oC2u83Yee8S3/aY0YAxkyBgu7hLTYH13Zc2Aw==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", + "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "cpu": [ + "loong64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "@stencil/core": "^4.12.2", - "ionicons": "^7.2.2", - "tslib": "^2.1.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@ionic/react": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@ionic/react/-/react-7.8.6.tgz", - "integrity": "sha512-IIlcdOW2OmcrjC3nqGqQCcJdHDnEbGIfyzpKR0FDaRQ6M/a7Mz6IlIG/cgdGP0RBBadECykBFDfa6XgRAGwWoA==", - "license": "MIT", - "dependencies": { - "@ionic/core": "7.8.6", - "ionicons": "^7.0.0", - "tslib": "*" - }, - "peerDependencies": { - "react": ">=16.8.6", - "react-dom": ">=16.8.6" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", + "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", + "cpu": [ + "mips64el" + ], "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", + "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", + "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", + "cpu": [ + "riscv64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", + "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "node_modules/@esbuild/linux-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", + "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=18" } }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", + "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", + "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", + "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", + "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", + "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, + "optional": true, + "os": [ + "openharmony" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", + "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", + "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", + "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "node_modules/@esbuild/win32-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", + "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@jest/console": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", - "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@jest/core": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz", - "integrity": "sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==", + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.2.0", - "@jest/pattern": "30.0.1", - "@jest/reporters": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-changed-files": "30.2.0", - "jest-config": "30.2.0", - "jest-haste-map": "30.2.0", - "jest-message-util": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.2.0", - "jest-resolve-dependencies": "30.2.0", - "jest-runner": "30.2.0", - "jest-runtime": "30.2.0", - "jest-snapshot": "30.2.0", - "jest-util": "30.2.0", - "jest-validate": "30.2.0", - "jest-watcher": "30.2.0", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@jest/core/node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], "license": "MIT", "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@jest/diff-sequences": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", - "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } + "node_modules/@flagsmith/flagsmith": { + "version": "11.0.0-internal.6", + "resolved": "https://registry.npmjs.org/@flagsmith/flagsmith/-/flagsmith-11.0.0-internal.6.tgz", + "integrity": "sha512-Vz719LOLC6h6KDHqlULOJTCth5RuX2iT4GPxcTZTGxOrxM9q4XwE8AxxLqoExgucXrd2neKUMU0PDmB1Q3LE0Q==", + "license": "BSD-3-Clause" }, - "node_modules/@jest/environment": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", - "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", - "dev": true, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", "license": "MIT", "dependencies": { - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-mock": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "@floating-ui/utils": "^0.2.10" } }, - "node_modules/@jest/expect": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", - "dev": true, + "node_modules/@floating-ui/dom": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.3.tgz", + "integrity": "sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==", "license": "MIT", "dependencies": { - "expect": "30.2.0", - "jest-snapshot": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" } }, - "node_modules/@jest/expect-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", - "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", - "dev": true, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.5.tgz", + "integrity": "sha512-HDO/1/1oH9fjj4eLgegrlH3dklZpHtUYYFiVwMUwfGvk9jWDRWqkklA2/NFScknrcNSspbV868WjXORvreDX+Q==", "license": "MIT", "dependencies": { - "@jest/get-type": "30.1.0" + "@floating-ui/dom": "^1.7.3" }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" } }, - "node_modules/@jest/fake-timers": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", - "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@jest/types": "30.2.0", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=10.10.0" } }, - "node_modules/@jest/get-type": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", - "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@jest/globals": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz", - "integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==", + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.2.0", - "@jest/expect": "30.2.0", - "@jest/types": "30.2.0", - "jest-mock": "30.2.0" + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@jest/pattern": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", - "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", - "dev": true, + "node_modules/@ionic/core": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.8.6.tgz", + "integrity": "sha512-HAYZdEmeJgOdo2kDlZkcCGHb+zs/vjU6iv4skbVBL7y+OnSv/oC2u83Yee8S3/aY0YAxkyBgu7hLTYH13Zc2Aw==", "license": "MIT", "dependencies": { - "@types/node": "*", - "jest-regex-util": "30.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "@stencil/core": "^4.12.2", + "ionicons": "^7.2.2", + "tslib": "^2.1.0" } }, - "node_modules/@jest/reporters": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz", - "integrity": "sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==", - "dev": true, + "node_modules/@ionic/react": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@ionic/react/-/react-7.8.6.tgz", + "integrity": "sha512-IIlcdOW2OmcrjC3nqGqQCcJdHDnEbGIfyzpKR0FDaRQ6M/a7Mz6IlIG/cgdGP0RBBadECykBFDfa6XgRAGwWoA==", "license": "MIT", "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.0", - "@jridgewell/trace-mapping": "^0.3.25", - "@types/node": "*", - "chalk": "^4.1.2", - "collect-v8-coverage": "^1.0.2", - "exit-x": "^0.2.2", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^5.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "jest-worker": "30.2.0", - "slash": "^3.0.0", - "string-length": "^4.0.2", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "@ionic/core": "7.8.6", + "ionicons": "^7.0.0", + "tslib": "*" }, "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "react": ">=16.8.6", + "react-dom": ">=16.8.6" } }, - "node_modules/@jest/reporters/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "balanced-match": "^1.0.0" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/@jest/reporters/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "node_modules/@isaacs/cliui/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": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "license": "MIT", + "engines": { + "node": ">=12" }, - "bin": { - "glob": "dist/esm/bin.mjs" + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@jest/reporters/node_modules/jest-worker": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", - "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.2.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" + "ansi-regex": "^6.0.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@jest/reporters/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.2" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@jest/reporters/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=8" } }, - "node_modules/@jest/reporters/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "sprintf-js": "~1.0.2" } }, - "node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/@jest/snapshot-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz", - "integrity": "sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "natural-compare": "^1.4.0" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@jest/source-map": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", - "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "callsites": "^3.1.0", - "graceful-fs": "^4.2.11" + "p-locate": "^4.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/@jest/test-result": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", - "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.2.0", - "@jest/types": "30.2.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" + "p-try": "^2.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@jest/test-sequencer": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz", - "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "30.2.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "slash": "^3.0.0" + "p-limit": "^2.2.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/@jest/transform": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", - "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/types": "30.2.0", - "@jridgewell/trace-mapping": "^0.3.25", - "babel-plugin-istanbul": "^7.0.1", - "chalk": "^4.1.2", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-util": "30.2.0", - "micromatch": "^4.0.8", - "pirates": "^4.0.7", - "slash": "^3.0.0", - "write-file-atomic": "^5.0.1" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/@jest/transform/node_modules/@babel/core": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", - "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "node": ">=8" } }, - "node_modules/@jest/transform/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/@jest/transform/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/@jest/console": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", + "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/types": { + "node_modules/@jest/core": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz", + "integrity": "sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==", "dev": true, "license": "MIT", "dependencies": { + "@jest/console": "30.2.0", "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", + "@jest/reporters": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.2.0", + "jest-config": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-message-util": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-resolve-dependencies": "30.2.0", + "jest-runner": "30.2.0", + "jest-runtime": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "jest-watcher": "30.2.0", + "micromatch": "^4.0.8", + "pretty-format": "30.2.0", + "slash": "^3.0.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "node_modules/@jest/core/node_modules/ci-info": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", + "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" + "engines": { + "node": ">=8" } }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "node_modules/@jest/diff-sequences": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", + "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "node_modules/@jest/environment": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", + "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", + "dev": true, "license": "MIT", + "dependencies": { + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0" + }, "engines": { - "node": ">=6.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.10.tgz", - "integrity": "sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==", + "node_modules/@jest/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", + "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" + "expect": "30.2.0", + "jest-snapshot": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "node_modules/@jest/expect-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", + "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", + "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@jest/get-type": "30.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@jest/fake-timers": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", + "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", "dev": true, "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@jest/types": "30.2.0", + "@sinonjs/fake-timers": "^13.0.0", + "@types/node": "*", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" }, "engines": { - "node": ">= 8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@jest/globals": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz", + "integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==", "dev": true, "license": "MIT", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/types": "30.2.0", + "jest-mock": "30.2.0" }, "engines": { - "node": ">= 8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@oclif/color": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/@oclif/color/-/color-1.0.13.tgz", - "integrity": "sha512-/2WZxKCNjeHlQogCs1VBtJWlPXjwWke/9gMrwsVsrUt00g2V6LUBvwgwrxhrXepjOmq4IZ5QeNbpDMEOUlx/JA==", + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.2.1", - "chalk": "^4.1.0", - "strip-ansi": "^6.0.1", - "supports-color": "^8.1.1", - "tslib": "^2" + "@types/node": "*", + "jest-regex-util": "30.0.1" }, "engines": { - "node": ">=12.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@oclif/color/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/@jest/reporters": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz", + "integrity": "sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "jest-worker": "30.2.0", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" }, "engines": { - "node": ">=10" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/@oclif/core": { - "version": "1.26.2", - "resolved": "https://registry.npmjs.org/@oclif/core/-/core-1.26.2.tgz", - "integrity": "sha512-6jYuZgXvHfOIc9GIaS4T3CIKGTjPmfAxuMcbCbMRKJJl4aq/4xeRlEz0E8/hz8HxvxZBGvN2GwAUHlrGWQVrVw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@oclif/linewrap": "^1.0.0", - "@oclif/screen": "^3.0.4", - "ansi-escapes": "^4.3.2", - "ansi-styles": "^4.3.0", - "cardinal": "^2.1.1", - "chalk": "^4.1.2", - "clean-stack": "^3.0.1", - "cli-progress": "^3.10.0", - "debug": "^4.3.4", - "ejs": "^3.1.6", - "fs-extra": "^9.1.0", - "get-package-type": "^0.1.0", - "globby": "^11.1.0", - "hyperlinker": "^1.0.0", - "indent-string": "^4.0.0", - "is-wsl": "^2.2.0", - "js-yaml": "^3.14.1", - "natural-orderby": "^2.0.3", - "object-treeify": "^1.1.33", - "password-prompt": "^1.1.2", - "semver": "^7.3.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "supports-color": "^8.1.1", - "supports-hyperlinks": "^2.2.0", - "tslib": "^2.4.1", - "widest-line": "^3.1.0", - "wrap-ansi": "^7.0.0" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, - "engines": { - "node": ">=14.0.0" + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/@oclif/core/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@jest/reporters/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { - "sprintf-js": "~1.0.2" + "balanced-match": "^1.0.0" } }, - "node_modules/@oclif/core/node_modules/clean-stack": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", - "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", + "node_modules/@jest/reporters/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "escape-string-regexp": "4.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=10" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@oclif/core/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/@oclif/core/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "node_modules/@jest/reporters/node_modules/jest-worker": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", + "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", "dev": true, "license": "MIT", "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.2.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" }, "engines": { - "node": ">=10" - } - }, - "node_modules/@oclif/core/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": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@oclif/core/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "node_modules/@jest/reporters/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "brace-expansion": "^2.0.2" }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@oclif/core/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" + "engines": { + "node": ">=16 || 14 >=14.17" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@oclif/core/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==", + "node_modules/@jest/reporters/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, + "license": "ISC", "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/@oclif/core/node_modules/supports-color": { + "node_modules/@jest/reporters/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", @@ -3878,754 +3880,2173 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/@oclif/core/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, "engines": { - "node": ">= 10.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@oclif/linewrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@oclif/linewrap/-/linewrap-1.0.0.tgz", - "integrity": "sha512-Ups2dShK52xXa8w6iBWLgcjPJWjais6KPJQq3gQ/88AY6BXoTX+MIGFPrWQO1KLMiQfoTpcLnUwloN4brrVUHw==", - "dev": true, - "license": "ISC" - }, - "node_modules/@oclif/plugin-help": { - "version": "5.2.20", - "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-5.2.20.tgz", - "integrity": "sha512-u+GXX/KAGL9S10LxAwNUaWdzbEBARJ92ogmM7g3gDVud2HioCmvWQCDohNRVZ9GYV9oKwZ/M8xwd6a1d95rEKQ==", + "node_modules/@jest/snapshot-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz", + "integrity": "sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==", "dev": true, "license": "MIT", "dependencies": { - "@oclif/core": "^2.15.0" + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" }, "engines": { - "node": ">=12.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@oclif/plugin-help/node_modules/@oclif/core": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/@oclif/core/-/core-2.16.0.tgz", - "integrity": "sha512-dL6atBH0zCZl1A1IXCKJgLPrM/wR7K+Wi401E/IvqsK8m2iCHW+0TEOGrans/cuN3oTW+uxIyJFHJ8Im0k4qBw==", + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", "dev": true, "license": "MIT", "dependencies": { - "@types/cli-progress": "^3.11.0", - "ansi-escapes": "^4.3.2", - "ansi-styles": "^4.3.0", - "cardinal": "^2.1.1", - "chalk": "^4.1.2", - "clean-stack": "^3.0.1", - "cli-progress": "^3.12.0", - "debug": "^4.3.4", - "ejs": "^3.1.8", - "get-package-type": "^0.1.0", - "globby": "^11.1.0", - "hyperlinker": "^1.0.0", - "indent-string": "^4.0.0", - "is-wsl": "^2.2.0", - "js-yaml": "^3.14.1", - "natural-orderby": "^2.0.3", - "object-treeify": "^1.1.33", - "password-prompt": "^1.1.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "supports-color": "^8.1.1", - "supports-hyperlinks": "^2.2.0", - "ts-node": "^10.9.1", - "tslib": "^2.5.0", - "widest-line": "^3.1.0", - "wordwrap": "^1.0.0", - "wrap-ansi": "^7.0.0" + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" }, "engines": { - "node": ">=14.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@oclif/plugin-help/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@jest/test-result": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", + "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", "dev": true, "license": "MIT", "dependencies": { - "sprintf-js": "~1.0.2" + "@jest/console": "30.2.0", + "@jest/types": "30.2.0", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@oclif/plugin-help/node_modules/clean-stack": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", - "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", + "node_modules/@jest/test-sequencer": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz", + "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==", "dev": true, "license": "MIT", "dependencies": { - "escape-string-regexp": "4.0.0" + "@jest/test-result": "30.2.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "slash": "^3.0.0" }, "engines": { - "node": ">=10" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", + "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/types": "30.2.0", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-util": "30.2.0", + "micromatch": "^4.0.8", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/transform/node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@oclif/plugin-help/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==", + "node_modules/@jest/transform/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT" }, - "node_modules/@oclif/plugin-help/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==", + "node_modules/@jest/transform/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@jest/types": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", + "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.10.tgz", + "integrity": "sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "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/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@oclif/color": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@oclif/color/-/color-1.0.13.tgz", + "integrity": "sha512-/2WZxKCNjeHlQogCs1VBtJWlPXjwWke/9gMrwsVsrUt00g2V6LUBvwgwrxhrXepjOmq4IZ5QeNbpDMEOUlx/JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.2.1", + "chalk": "^4.1.0", + "strip-ansi": "^6.0.1", + "supports-color": "^8.1.1", + "tslib": "^2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@oclif/color/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@oclif/core": { + "version": "1.26.2", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-1.26.2.tgz", + "integrity": "sha512-6jYuZgXvHfOIc9GIaS4T3CIKGTjPmfAxuMcbCbMRKJJl4aq/4xeRlEz0E8/hz8HxvxZBGvN2GwAUHlrGWQVrVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oclif/linewrap": "^1.0.0", + "@oclif/screen": "^3.0.4", + "ansi-escapes": "^4.3.2", + "ansi-styles": "^4.3.0", + "cardinal": "^2.1.1", + "chalk": "^4.1.2", + "clean-stack": "^3.0.1", + "cli-progress": "^3.10.0", + "debug": "^4.3.4", + "ejs": "^3.1.6", + "fs-extra": "^9.1.0", + "get-package-type": "^0.1.0", + "globby": "^11.1.0", + "hyperlinker": "^1.0.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "js-yaml": "^3.14.1", + "natural-orderby": "^2.0.3", + "object-treeify": "^1.1.33", + "password-prompt": "^1.1.2", + "semver": "^7.3.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "supports-color": "^8.1.1", + "supports-hyperlinks": "^2.2.0", + "tslib": "^2.4.1", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oclif/core/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@oclif/core/node_modules/clean-stack": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", + "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@oclif/core/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/@oclif/core/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@oclif/core/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/@oclif/core/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@oclif/core/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@oclif/core/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/@oclif/core/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@oclif/linewrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@oclif/linewrap/-/linewrap-1.0.0.tgz", + "integrity": "sha512-Ups2dShK52xXa8w6iBWLgcjPJWjais6KPJQq3gQ/88AY6BXoTX+MIGFPrWQO1KLMiQfoTpcLnUwloN4brrVUHw==", + "dev": true, + "license": "ISC" + }, + "node_modules/@oclif/plugin-help": { + "version": "5.2.20", + "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-5.2.20.tgz", + "integrity": "sha512-u+GXX/KAGL9S10LxAwNUaWdzbEBARJ92ogmM7g3gDVud2HioCmvWQCDohNRVZ9GYV9oKwZ/M8xwd6a1d95rEKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oclif/core": "^2.15.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@oclif/plugin-help/node_modules/@oclif/core": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-2.16.0.tgz", + "integrity": "sha512-dL6atBH0zCZl1A1IXCKJgLPrM/wR7K+Wi401E/IvqsK8m2iCHW+0TEOGrans/cuN3oTW+uxIyJFHJ8Im0k4qBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cli-progress": "^3.11.0", + "ansi-escapes": "^4.3.2", + "ansi-styles": "^4.3.0", + "cardinal": "^2.1.1", + "chalk": "^4.1.2", + "clean-stack": "^3.0.1", + "cli-progress": "^3.12.0", + "debug": "^4.3.4", + "ejs": "^3.1.8", + "get-package-type": "^0.1.0", + "globby": "^11.1.0", + "hyperlinker": "^1.0.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "js-yaml": "^3.14.1", + "natural-orderby": "^2.0.3", + "object-treeify": "^1.1.33", + "password-prompt": "^1.1.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "supports-color": "^8.1.1", + "supports-hyperlinks": "^2.2.0", + "ts-node": "^10.9.1", + "tslib": "^2.5.0", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oclif/plugin-help/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@oclif/plugin-help/node_modules/clean-stack": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", + "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@oclif/plugin-help/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/@oclif/plugin-help/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/@oclif/plugin-help/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@oclif/plugin-help/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/@oclif/plugin-help/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/@oclif/plugin-help/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@oclif/plugin-plugins": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/@oclif/plugin-plugins/-/plugin-plugins-2.4.7.tgz", + "integrity": "sha512-6fzUDLWrSK7n6+EBrEekEEYrYTCneRoOF9TzojkjuFn1+ailvUlr98G90bblxKOyy8fqMe7QjvqwTgIDQ9ZIzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oclif/color": "^1.0.4", + "@oclif/core": "^2.8.2", + "chalk": "^4.1.2", + "debug": "^4.3.4", + "fs-extra": "^9.0", + "http-call": "^5.2.2", + "load-json-file": "^5.3.0", + "npm-run-path": "^4.0.1", + "semver": "^7.5.0", + "tslib": "^2.4.1", + "yarn": "^1.22.18" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@oclif/plugin-plugins/node_modules/@oclif/core": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-2.16.0.tgz", + "integrity": "sha512-dL6atBH0zCZl1A1IXCKJgLPrM/wR7K+Wi401E/IvqsK8m2iCHW+0TEOGrans/cuN3oTW+uxIyJFHJ8Im0k4qBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cli-progress": "^3.11.0", + "ansi-escapes": "^4.3.2", + "ansi-styles": "^4.3.0", + "cardinal": "^2.1.1", + "chalk": "^4.1.2", + "clean-stack": "^3.0.1", + "cli-progress": "^3.12.0", + "debug": "^4.3.4", + "ejs": "^3.1.8", + "get-package-type": "^0.1.0", + "globby": "^11.1.0", + "hyperlinker": "^1.0.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "js-yaml": "^3.14.1", + "natural-orderby": "^2.0.3", + "object-treeify": "^1.1.33", + "password-prompt": "^1.1.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "supports-color": "^8.1.1", + "supports-hyperlinks": "^2.2.0", + "ts-node": "^10.9.1", + "tslib": "^2.5.0", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oclif/plugin-plugins/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@oclif/plugin-plugins/node_modules/clean-stack": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", + "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@oclif/plugin-plugins/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/@oclif/plugin-plugins/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@oclif/plugin-plugins/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/@oclif/plugin-plugins/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@oclif/plugin-plugins/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@oclif/plugin-plugins/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/@oclif/plugin-plugins/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/@oclif/plugin-plugins/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@oclif/screen": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@oclif/screen/-/screen-3.0.8.tgz", + "integrity": "sha512-yx6KAqlt3TAHBduS2fMQtJDL2ufIHnDRArrJEOoTTuizxqmjLT+psGYOHpmMl3gvQpFJ11Hs76guUUktzAF9Bg==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@playwright/test": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz", + "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.58.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.17.tgz", + "integrity": "sha512-tXDyE1/jzFsHXjhRZQ3hMl0IVhYe5qula43LDWIhVfjp9G/nT5OQY5AORVOrkEGAUltBJOfOWeETbmhm6kHhuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-html": "^0.0.9", + "core-js-pure": "^3.23.3", + "error-stack-parser": "^2.0.6", + "html-entities": "^2.1.0", + "loader-utils": "^2.0.4", + "schema-utils": "^4.2.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "@types/webpack": "4.x || 5.x", + "react-refresh": ">=0.10.0 <1.0.0", + "sockjs-client": "^1.4.0", + "type-fest": ">=0.17.0 <5.0.0", + "webpack": ">=4.43.0 <6.0.0", + "webpack-dev-server": "3.x || 4.x || 5.x", + "webpack-hot-middleware": "2.x", + "webpack-plugin-serve": "0.x || 1.x" + }, + "peerDependenciesMeta": { + "@types/webpack": { + "optional": true + }, + "sockjs-client": { + "optional": true + }, + "type-fest": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + }, + "webpack-hot-middleware": { + "optional": true + }, + "webpack-plugin-serve": { + "optional": true + } + } + }, + "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==", + "license": "MIT" + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@preact/signals-core": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.14.0.tgz", + "integrity": "sha512-AowtCcCU/33lFlh1zRFf/u+12rfrhtNakj7UpaGEsmMwUKpKWMVvcktOGcwBBNiB4lWrZWc01LhiyyzVklJyaQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/@react-oauth/google": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.2.8.tgz", + "integrity": "sha512-W3sRcU6kSZMGUOk10Vy5kPZPzvsi7+UpM2MxnT6fMVp+whDMKCVope5R01gwRydK9OI+0rozAARCD2NgrbkV7w==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@reduxjs/toolkit": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.1.tgz", + "integrity": "sha512-HikrdY+IDgRfRYlCTGUQaiCxxDDgM1mQrRbZ6S1HFZX5ZYuJ4o8EstNmhTwHdPl2rTmLxzwSu0b3AyeyTlR+RA==", + "license": "MIT", + "dependencies": { + "immer": "^9.0.16", + "redux": "^4.2.0", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.7" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18", + "react-redux": "^7.2.1 || ^8.0.2" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.2.tgz", + "integrity": "sha512-7QaYCf8bqF04dOy7w/eHmJeNExxTYwvKAmlSAH/EaWWUzbT0h5sbF6bktFoX/0F/0qwng5/dWFMyf3gzaM8DsQ==", + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "magic-string": "^0.30.3" + }, + "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/pluginutils": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", + "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", + "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-darwin-arm64": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.9.tgz", + "integrity": "sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@sentry-internal/feedback": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.120.4.tgz", + "integrity": "sha512-eSwgvTdrh03zYYaI6UVOjI9p4VmKg6+c2+CBQfRZX++6wwnCVsNv7XF7WUIpVGBAkJ0N2oapjQmCzJKGKBRWQg==", + "license": "MIT", + "dependencies": { + "@sentry/core": "7.120.4", + "@sentry/types": "7.120.4", + "@sentry/utils": "7.120.4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@sentry-internal/replay-canvas": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-7.120.4.tgz", + "integrity": "sha512-2+W4CgUL1VzrPjArbTid4WhKh7HH21vREVilZdvffQPVwOEpgNTPAb69loQuTlhJVveh9hWTj2nE5UXLbLP+AA==", + "license": "MIT", + "dependencies": { + "@sentry/core": "7.120.4", + "@sentry/replay": "7.120.4", + "@sentry/types": "7.120.4", + "@sentry/utils": "7.120.4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@sentry-internal/tracing": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.120.4.tgz", + "integrity": "sha512-Fz5+4XCg3akeoFK+K7g+d7HqGMjmnLoY2eJlpONJmaeT9pXY7yfUyXKZMmMajdE2LxxKJgQ2YKvSCaGVamTjHw==", + "license": "MIT", + "dependencies": { + "@sentry/core": "7.120.4", + "@sentry/types": "7.120.4", + "@sentry/utils": "7.120.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/babel-plugin-component-annotate": { + "version": "2.22.7", + "resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.22.7.tgz", + "integrity": "sha512-aa7XKgZMVl6l04NY+3X7BP7yvQ/s8scn8KzQfTLrGRarziTlMGrsCOBQtCNWXOPEbtxAIHpZ9dsrAn5EJSivOQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@sentry/browser": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.120.4.tgz", + "integrity": "sha512-ymlNtIPG6HAKzM/JXpWVGCzCNufZNADfy+O/olZuVJW5Be1DtOFyRnBvz0LeKbmxJbXb2lX/XMhuen6PXPdoQw==", + "license": "MIT", + "dependencies": { + "@sentry-internal/feedback": "7.120.4", + "@sentry-internal/replay-canvas": "7.120.4", + "@sentry-internal/tracing": "7.120.4", + "@sentry/core": "7.120.4", + "@sentry/integrations": "7.120.4", + "@sentry/replay": "7.120.4", + "@sentry/types": "7.120.4", + "@sentry/utils": "7.120.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/bundler-plugin-core": { + "version": "2.22.7", + "resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.22.7.tgz", + "integrity": "sha512-ouQh5sqcB8vsJ8yTTe0rf+iaUkwmeUlGNFi35IkCFUQlWJ22qS6OfvNjOqFI19e6eGUXks0c/2ieFC4+9wJ+1g==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.18.5", + "@sentry/babel-plugin-component-annotate": "2.22.7", + "@sentry/cli": "2.39.1", + "dotenv": "^16.3.1", + "find-up": "^5.0.0", + "glob": "^9.3.2", + "magic-string": "0.30.8", + "unplugin": "1.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@sentry/bundler-plugin-core/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/@sentry/bundler-plugin-core/node_modules/magic-string": { + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@sentry/cli": { + "version": "2.39.1", + "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.39.1.tgz", + "integrity": "sha512-JIb3e9vh0+OmQ0KxmexMXg9oZsR/G7HMwxt5BUIKAXZ9m17Xll4ETXTRnRUBT3sf7EpNGAmlQk1xEmVN9pYZYQ==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.7", + "progress": "^2.0.3", + "proxy-from-env": "^1.1.0", + "which": "^2.0.2" + }, + "bin": { + "sentry-cli": "bin/sentry-cli" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@sentry/cli-darwin": "2.39.1", + "@sentry/cli-linux-arm": "2.39.1", + "@sentry/cli-linux-arm64": "2.39.1", + "@sentry/cli-linux-i686": "2.39.1", + "@sentry/cli-linux-x64": "2.39.1", + "@sentry/cli-win32-i686": "2.39.1", + "@sentry/cli-win32-x64": "2.39.1" + } + }, + "node_modules/@sentry/cli-darwin": { + "version": "2.39.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.39.1.tgz", + "integrity": "sha512-kiNGNSAkg46LNGatfNH5tfsmI/kCAaPA62KQuFZloZiemTNzhy9/6NJP8HZ/GxGs8GDMxic6wNrV9CkVEgFLJQ==", + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/cli-linux-arm": { + "version": "2.39.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.39.1.tgz", + "integrity": "sha512-DkENbxyRxUrfLnJLXTA4s5UL/GoctU5Cm4ER1eB7XN7p9WsamFJd/yf2KpltkjEyiTuplv0yAbdjl1KX3vKmEQ==", + "cpu": [ + "arm" + ], + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "linux", + "freebsd" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/cli-linux-arm64": { + "version": "2.39.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.39.1.tgz", + "integrity": "sha512-5VbVJDatolDrWOgaffsEM7znjs0cR8bHt9Bq0mStM3tBolgAeSDHE89NgHggfZR+DJ2VWOy4vgCwkObrUD6NQw==", + "cpu": [ + "arm64" + ], + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "linux", + "freebsd" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/cli-linux-i686": { + "version": "2.39.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.39.1.tgz", + "integrity": "sha512-pXWVoKXCRrY7N8vc9H7mETiV9ZCz+zSnX65JQCzZxgYrayQPJTc+NPRnZTdYdk5RlAupXaFicBI2GwOCRqVRkg==", + "cpu": [ + "x86", + "ia32" + ], + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "linux", + "freebsd" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/cli-linux-x64": { + "version": "2.39.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.39.1.tgz", + "integrity": "sha512-IwayNZy+it7FWG4M9LayyUmG1a/8kT9+/IEm67sT5+7dkMIMcpmHDqL8rWcPojOXuTKaOBBjkVdNMBTXy0mXlA==", + "cpu": [ + "x64" + ], + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "linux", + "freebsd" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/cli-win32-i686": { + "version": "2.39.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.39.1.tgz", + "integrity": "sha512-NglnNoqHSmE+Dz/wHeIVRnV2bLMx7tIn3IQ8vXGO5HWA2f8zYJGktbkLq1Lg23PaQmeZLPGlja3gBQfZYSG10Q==", + "cpu": [ + "x86", + "ia32" + ], + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/cli-win32-x64": { + "version": "2.39.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.39.1.tgz", + "integrity": "sha512-xv0R2CMf/X1Fte3cMWie1NXuHmUyQPDBfCyIt6k6RPFPxAYUgcqgMPznYwVMwWEA1W43PaOkSn3d8ZylsDaETw==", + "cpu": [ + "x64" + ], + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/core": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.120.4.tgz", + "integrity": "sha512-TXu3Q5kKiq8db9OXGkWyXUbIxMMuttB5vJ031yolOl5T/B69JRyAoKuojLBjRv1XX583gS1rSSoX8YXX7ATFGA==", + "license": "MIT", + "dependencies": { + "@sentry/types": "7.120.4", + "@sentry/utils": "7.120.4" + }, "engines": { "node": ">=8" } }, - "node_modules/@oclif/plugin-help/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "dev": true, + "node_modules/@sentry/integrations": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-7.120.4.tgz", + "integrity": "sha512-kkBTLk053XlhDCg7OkBQTIMF4puqFibeRO3E3YiVc4PGLnocXMaVpOSCkMqAc1k1kZ09UgGi8DxfQhnFEjUkpA==", "license": "MIT", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "@sentry/core": "7.120.4", + "@sentry/types": "7.120.4", + "@sentry/utils": "7.120.4", + "localforage": "^1.8.1" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=8" } }, - "node_modules/@oclif/plugin-help/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, + "node_modules/@sentry/replay": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.120.4.tgz", + "integrity": "sha512-FW8sPenNFfnO/K7sncsSTX4rIVak9j7VUiLIagJrcqZIC7d1dInFNjy8CdVJUlyz3Y3TOgIl3L3+ZpjfyMnaZg==", "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "@sentry-internal/tracing": "7.120.4", + "@sentry/core": "7.120.4", + "@sentry/types": "7.120.4", + "@sentry/utils": "7.120.4" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "node": ">=12" } }, - "node_modules/@oclif/plugin-help/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, + "node_modules/@sentry/types": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.120.4.tgz", + "integrity": "sha512-cUq2hSSe6/qrU6oZsEP4InMI5VVdD86aypE+ENrQ6eZEVLTCYm1w6XhW1NvIu3UuWh7gZec4a9J7AFpYxki88Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/utils": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.120.4.tgz", + "integrity": "sha512-zCKpyDIWKHwtervNK2ZlaK8mMV7gVUijAgFeJStH+CU/imcdquizV3pFLlSQYRswG+Lbyd6CT/LGRh3IbtkCFw==", "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "@sentry/types": "7.120.4" }, "engines": { "node": ">=8" } }, - "node_modules/@oclif/plugin-help/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, + "node_modules/@sentry/webpack-plugin": { + "version": "2.22.7", + "resolved": "https://registry.npmjs.org/@sentry/webpack-plugin/-/webpack-plugin-2.22.7.tgz", + "integrity": "sha512-j5h5LZHWDlm/FQCCmEghQ9FzYXwfZdlOf3FE/X6rK6lrtx0JCAkq+uhMSasoyP4XYKL4P4vRS6WFSos4jxf/UA==", "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@sentry/bundler-plugin-core": "2.22.7", + "unplugin": "1.0.1", + "uuid": "^9.0.0" }, "engines": { - "node": ">=10" + "node": ">= 14" }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "peerDependencies": { + "webpack": ">=4.40.0" } }, - "node_modules/@oclif/plugin-plugins": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/@oclif/plugin-plugins/-/plugin-plugins-2.4.7.tgz", - "integrity": "sha512-6fzUDLWrSK7n6+EBrEekEEYrYTCneRoOF9TzojkjuFn1+ailvUlr98G90bblxKOyy8fqMe7QjvqwTgIDQ9ZIzg==", + "node_modules/@sinclair/typebox": { + "version": "0.34.46", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.46.tgz", + "integrity": "sha512-kiW7CtS/NkdvTUjkjUJo7d5JsFfbJ14YjdhDk9KoEgK6nFjKNXZPrX0jfLA8ZlET4cFLHxOZ/0vFKOP+bOxIOQ==", "dev": true, - "license": "MIT", + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@oclif/color": "^1.0.4", - "@oclif/core": "^2.8.2", - "chalk": "^4.1.2", - "debug": "^4.3.4", - "fs-extra": "^9.0", - "http-call": "^5.2.2", - "load-json-file": "^5.3.0", - "npm-run-path": "^4.0.1", - "semver": "^7.5.0", - "tslib": "^2.4.1", - "yarn": "^1.22.18" - }, - "engines": { - "node": ">=12.0.0" + "type-detect": "4.0.8" } }, - "node_modules/@oclif/plugin-plugins/node_modules/@oclif/core": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/@oclif/core/-/core-2.16.0.tgz", - "integrity": "sha512-dL6atBH0zCZl1A1IXCKJgLPrM/wR7K+Wi401E/IvqsK8m2iCHW+0TEOGrans/cuN3oTW+uxIyJFHJ8Im0k4qBw==", + "node_modules/@sinonjs/commons/node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, "license": "MIT", - "dependencies": { - "@types/cli-progress": "^3.11.0", - "ansi-escapes": "^4.3.2", - "ansi-styles": "^4.3.0", - "cardinal": "^2.1.1", - "chalk": "^4.1.2", - "clean-stack": "^3.0.1", - "cli-progress": "^3.12.0", - "debug": "^4.3.4", - "ejs": "^3.1.8", - "get-package-type": "^0.1.0", - "globby": "^11.1.0", - "hyperlinker": "^1.0.0", - "indent-string": "^4.0.0", - "is-wsl": "^2.2.0", - "js-yaml": "^3.14.1", - "natural-orderby": "^2.0.3", - "object-treeify": "^1.1.33", - "password-prompt": "^1.1.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "supports-color": "^8.1.1", - "supports-hyperlinks": "^2.2.0", - "ts-node": "^10.9.1", - "tslib": "^2.5.0", - "widest-line": "^3.1.0", - "wordwrap": "^1.0.0", - "wrap-ansi": "^7.0.0" - }, "engines": { - "node": ">=14.0.0" + "node": ">=4" } }, - "node_modules/@oclif/plugin-plugins/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "sprintf-js": "~1.0.2" + "@sinonjs/commons": "^3.0.1" } }, - "node_modules/@oclif/plugin-plugins/node_modules/clean-stack": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", - "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", - "dev": true, + "node_modules/@slack/logger": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-3.0.0.tgz", + "integrity": "sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA==", "license": "MIT", "dependencies": { - "escape-string-regexp": "4.0.0" + "@types/node": ">=12.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 12.13.0", + "npm": ">= 6.12.0" } }, - "node_modules/@oclif/plugin-plugins/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/@slack/types": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/@slack/types/-/types-2.15.0.tgz", + "integrity": "sha512-livb1gyG3J8ATLBJ3KjZfjHpTRz9btY1m5cgNuXxWJbhwRB1Gwb8Ly6XLJm2Sy1W6h+vLgqIHg7IwKrF1C1Szg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0", + "npm": ">= 6.12.0" + } }, - "node_modules/@oclif/plugin-plugins/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, + "node_modules/@slack/web-api": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-6.13.0.tgz", + "integrity": "sha512-dv65crIgdh9ZYHrevLU6XFHTQwTyDmNqEqzuIrV+Vqe/vgiG6w37oex5ePDU1RGm2IJ90H8iOvHFvzdEO/vB+g==", "license": "MIT", "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "@slack/logger": "^3.0.0", + "@slack/types": "^2.11.0", + "@types/is-stream": "^1.1.0", + "@types/node": ">=12.0.0", + "axios": "^1.7.4", + "eventemitter3": "^3.1.0", + "form-data": "^2.5.0", + "is-electron": "2.2.2", + "is-stream": "^1.1.0", + "p-queue": "^6.6.1", + "p-retry": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">= 12.13.0", + "npm": ">= 6.12.0" } }, - "node_modules/@oclif/plugin-plugins/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, + "node_modules/@stencil/core": { + "version": "4.36.2", + "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.36.2.tgz", + "integrity": "sha512-PRFSpxNzX9Oi0Wfh02asztN9Sgev/MacfZwmd+VVyE6ZxW+a/kEpAYZhzGAmE+/aKVOGYuug7R9SulanYGxiDQ==", "license": "MIT", + "bin": { + "stencil": "bin/stencil" + }, "engines": { - "node": ">=8" + "node": ">=16.0.0", + "npm": ">=7.10.0" + }, + "optionalDependencies": { + "@rollup/rollup-darwin-arm64": "4.34.9", + "@rollup/rollup-darwin-x64": "4.34.9", + "@rollup/rollup-linux-arm64-gnu": "4.34.9", + "@rollup/rollup-linux-arm64-musl": "4.34.9", + "@rollup/rollup-linux-x64-gnu": "4.34.9", + "@rollup/rollup-linux-x64-musl": "4.34.9", + "@rollup/rollup-win32-arm64-msvc": "4.34.9", + "@rollup/rollup-win32-x64-msvc": "4.34.9" } }, - "node_modules/@oclif/plugin-plugins/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "node_modules/@storybook/addon-a11y": { + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-10.3.3.tgz", + "integrity": "sha512-1yELCE8NXUJKcfS2k97pujtVw4z95PCwyoy2I6VAPiG/nRnJI8M6ned08YmCMEJhLBgGA1+GBh9HO4uk+xPcYA==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "@storybook/global": "^5.0.0", + "axe-core": "^4.2.0" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^10.3.3" } }, - "node_modules/@oclif/plugin-plugins/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "node_modules/@storybook/addon-docs": { + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.3.3.tgz", + "integrity": "sha512-trJQTpOtuOEuNv1Rn8X2Sopp5hSPpb0u0soEJ71BZAbxe4d2Y1d/1MYcxBdRKwncum6sCTsnxTpqQ/qvSJKlTQ==", "dev": true, "license": "MIT", "dependencies": { - "universalify": "^2.0.0" + "@mdx-js/react": "^3.0.0", + "@storybook/csf-plugin": "10.3.3", + "@storybook/icons": "^2.0.1", + "@storybook/react-dom-shim": "10.3.3", + "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" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^10.3.3" } }, - "node_modules/@oclif/plugin-plugins/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "node_modules/@storybook/addon-docs/node_modules/@storybook/csf-plugin": { + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-10.3.3.tgz", + "integrity": "sha512-Utlh7zubm+4iOzBBfzLW4F4vD99UBtl2Do4edlzK2F7krQIcFvR2ontjAE8S1FQVLZAC3WHalCOS+Ch8zf3knA==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" + "unplugin": "^2.3.5" }, "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "esbuild": "*", + "rollup": "*", + "storybook": "^10.3.3", + "vite": "*", + "webpack": "*" + }, + "peerDependenciesMeta": { + "esbuild": { + "optional": true + }, + "rollup": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } } }, - "node_modules/@oclif/plugin-plugins/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==", + "node_modules/@storybook/addon-docs/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": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" }, "engines": { - "node": ">=8" + "node": ">=18.12.0" } }, - "node_modules/@oclif/plugin-plugins/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/@storybook/addon-docs/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/@storybook/addon-webpack5-compiler-swc": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@storybook/addon-webpack5-compiler-swc/-/addon-webpack5-compiler-swc-4.0.3.tgz", + "integrity": "sha512-REJZBArIBcqzxmhQY9R1br9hjfcFYdl4FeWD/okx1eRwPZkl49aUhTYqZPrA+MWXfKJkuuNQ5vnfSoR0c9HyvA==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@swc/core": "^1.13.5", + "swc-loader": "^0.2.6" }, "engines": { - "node": ">=10" + "node": ">=18" }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "peerDependencies": { + "storybook": "^9.0.0 || ^10.0.0-0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0" } }, - "node_modules/@oclif/plugin-plugins/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "node_modules/@storybook/builder-webpack5": { + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-10.3.3.tgz", + "integrity": "sha512-A7hop0VXG/06EZ7l2WIuhsrnpiV6NOOcOiVqjYDLplbVelkiiL98LTL+Om87u0n32sAfXWgFk2jIhSc3bbXlsQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 10.0.0" + "dependencies": { + "@storybook/core-webpack": "10.3.3", + "case-sensitive-paths-webpack-plugin": "^2.4.0", + "cjs-module-lexer": "^1.2.3", + "css-loader": "^7.1.2", + "es-module-lexer": "^1.5.0", + "fork-ts-checker-webpack-plugin": "^9.1.0", + "html-webpack-plugin": "^5.5.0", + "magic-string": "^0.30.5", + "style-loader": "^4.0.0", + "terser-webpack-plugin": "^5.3.14", + "ts-dedent": "^2.0.0", + "webpack": "5", + "webpack-dev-middleware": "^6.1.2", + "webpack-hot-middleware": "^2.25.1", + "webpack-virtual-modules": "^0.6.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^10.3.3" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@oclif/screen": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@oclif/screen/-/screen-3.0.8.tgz", - "integrity": "sha512-yx6KAqlt3TAHBduS2fMQtJDL2ufIHnDRArrJEOoTTuizxqmjLT+psGYOHpmMl3gvQpFJ11Hs76guUUktzAF9Bg==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "node_modules/@storybook/builder-webpack5/node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - } + "license": "MIT" }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/@storybook/builder-webpack5/node_modules/css-loader": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.4.tgz", + "integrity": "sha512-vv3J9tlOl04WjiMvHQI/9tmIrCxVrj6PFbHemBB1iihpeRbi/I4h033eoFIhwxBBqLhI0KYFS7yvynBFhIZfTw==", "dev": true, "license": "MIT", - "optional": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.40", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.6.3" + }, "engines": { - "node": ">=14" + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, - "node_modules/@pkgr/core": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", - "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "node_modules/@storybook/builder-webpack5/node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@storybook/builder-webpack5/node_modules/style-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-4.0.0.tgz", + "integrity": "sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==", "dev": true, "license": "MIT", "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + "node": ">= 18.12.0" }, "funding": { - "url": "https://opencollective.com/pkgr" - } - }, - "node_modules/@playwright/test": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz", - "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright": "1.58.2" - }, - "bin": { - "playwright": "cli.js" + "type": "opencollective", + "url": "https://opencollective.com/webpack" }, - "engines": { - "node": ">=18" + "peerDependencies": { + "webpack": "^5.27.0" } }, - "node_modules/@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.17", - "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.17.tgz", - "integrity": "sha512-tXDyE1/jzFsHXjhRZQ3hMl0IVhYe5qula43LDWIhVfjp9G/nT5OQY5AORVOrkEGAUltBJOfOWeETbmhm6kHhuQ==", + "node_modules/@storybook/builder-webpack5/node_modules/webpack-dev-middleware": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-6.1.3.tgz", + "integrity": "sha512-A4ChP0Qj8oGociTs6UdlRUGANIGrCDL3y+pmQMc+dSsraXHCatFpmMey4mYELA+juqwUqwQsUgJJISXl1KWmiw==", "dev": true, "license": "MIT", "dependencies": { - "ansi-html": "^0.0.9", - "core-js-pure": "^3.23.3", - "error-stack-parser": "^2.0.6", - "html-entities": "^2.1.0", - "loader-utils": "^2.0.4", - "schema-utils": "^4.2.0", - "source-map": "^0.7.3" + "colorette": "^2.0.10", + "memfs": "^3.4.12", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" }, "engines": { - "node": ">= 10.13" + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "@types/webpack": "4.x || 5.x", - "react-refresh": ">=0.10.0 <1.0.0", - "sockjs-client": "^1.4.0", - "type-fest": ">=0.17.0 <5.0.0", - "webpack": ">=4.43.0 <6.0.0", - "webpack-dev-server": "3.x || 4.x || 5.x", - "webpack-hot-middleware": "2.x", - "webpack-plugin-serve": "0.x || 1.x" + "webpack": "^5.0.0" }, "peerDependenciesMeta": { - "@types/webpack": { - "optional": true - }, - "sockjs-client": { - "optional": true - }, - "type-fest": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - }, - "webpack-hot-middleware": { - "optional": true - }, - "webpack-plugin-serve": { + "webpack": { "optional": true } } }, - "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==", + "node_modules/@storybook/builder-webpack5/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/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "node_modules/@storybook/core-webpack": { + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-10.3.3.tgz", + "integrity": "sha512-ESRM2k9m1V0qXaqEM+bvtCjv9+gYVE3PMuoNZMyIYNdGA4Pdc2PvQsUrKQNVByVbEGwjt+h0RE6b20bnBkdYsg==", + "dev": true, "license": "MIT", + "dependencies": { + "ts-dedent": "^2.0.0" + }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/popperjs" + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "storybook": "^10.3.3" } }, - "node_modules/@preact/signals-core": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.14.0.tgz", - "integrity": "sha512-AowtCcCU/33lFlh1zRFf/u+12rfrhtNakj7UpaGEsmMwUKpKWMVvcktOGcwBBNiB4lWrZWc01LhiyyzVklJyaQ==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/preact" - } + "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/@react-oauth/google": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.2.8.tgz", - "integrity": "sha512-W3sRcU6kSZMGUOk10Vy5kPZPzvsi7+UpM2MxnT6fMVp+whDMKCVope5R01gwRydK9OI+0rozAARCD2NgrbkV7w==", + "node_modules/@storybook/icons": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-2.0.1.tgz", + "integrity": "sha512-/smVjw88yK3CKsiuR71vNgWQ9+NuY2L+e8X7IMrFjexjm6ZR8ULrV2DRkTA61aV6ryefslzHEGDInGpnNeIocg==", + "dev": true, "license": "MIT", "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "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/@reduxjs/toolkit": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.1.tgz", - "integrity": "sha512-HikrdY+IDgRfRYlCTGUQaiCxxDDgM1mQrRbZ6S1HFZX5ZYuJ4o8EstNmhTwHdPl2rTmLxzwSu0b3AyeyTlR+RA==", + "node_modules/@storybook/preset-react-webpack": { + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@storybook/preset-react-webpack/-/preset-react-webpack-10.3.3.tgz", + "integrity": "sha512-R8WeGUo062VpIx+i+5/Cv8dVGM+YSgCGZ0STPANijmOHATvfWpslTAIjMkq0me/BoDT5zxzCnYvAyRrBcjOW8A==", + "dev": true, "license": "MIT", "dependencies": { - "immer": "^9.0.16", - "redux": "^4.2.0", - "redux-thunk": "^2.4.2", - "reselect": "^4.1.7" + "@storybook/core-webpack": "10.3.3", + "@storybook/react-docgen-typescript-plugin": "1.0.6--canary.9.0c3f3b7.0", + "@types/semver": "^7.7.1", + "magic-string": "^0.30.5", + "react-docgen": "^7.1.1", + "resolve": "^1.22.8", + "semver": "^7.7.3", + "tsconfig-paths": "^4.2.0", + "webpack": "5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18", - "react-redux": "^7.2.1 || ^8.0.2" + "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.3" }, "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-redux": { + "typescript": { "optional": true } } }, - "node_modules/@rollup/plugin-replace": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.2.tgz", - "integrity": "sha512-7QaYCf8bqF04dOy7w/eHmJeNExxTYwvKAmlSAH/EaWWUzbT0h5sbF6bktFoX/0F/0qwng5/dWFMyf3gzaM8DsQ==", + "node_modules/@storybook/preset-react-webpack/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": { - "@rollup/pluginutils": "^5.0.1", - "magic-string": "^0.30.3" + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=6" + } + }, + "node_modules/@storybook/react": { + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-10.3.3.tgz", + "integrity": "sha512-cGG5TbR8Tdx9zwlpsWyBEfWrejm5iWdYF26EwIhwuKq9GFUTAVrQzo0Rs7Tqc3ZyVhRS/YfsRiWSEH+zmq2JiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/global": "^5.0.0", + "@storybook/react-dom-shim": "10.3.3", + "react-docgen": "^8.0.2", + "react-docgen-typescript": "^2.2.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + "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.3", + "typescript": ">= 4.9.x" }, "peerDependenciesMeta": { - "rollup": { + "typescript": { "optional": true } } }, - "node_modules/@rollup/pluginutils": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", - "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", + "node_modules/@storybook/react-docgen-typescript-plugin": { + "version": "1.0.6--canary.9.0c3f3b7.0", + "resolved": "https://registry.npmjs.org/@storybook/react-docgen-typescript-plugin/-/react-docgen-typescript-plugin-1.0.6--canary.9.0c3f3b7.0.tgz", + "integrity": "sha512-KUqXC3oa9JuQ0kZJLBhVdS4lOneKTOopnNBK4tUAgoxWQ3u/IjzdueZjFr7gyBrXMoU6duutk3RQR9u8ZpYJ4Q==", + "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" + "debug": "^4.1.1", + "endent": "^2.0.1", + "find-cache-dir": "^3.3.1", + "flat-cache": "^3.0.4", + "micromatch": "^4.0.2", + "react-docgen-typescript": "^2.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "typescript": ">= 4.x", + "webpack": ">= 4" + } + }, + "node_modules/@storybook/react-docgen-typescript-plugin/node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=8" }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/@storybook/react-docgen-typescript-plugin/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } + "engines": { + "node": ">=8" } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.34.9", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.9.tgz", - "integrity": "sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==", - "cpu": [ - "arm64" - ], + "node_modules/@storybook/react-docgen-typescript-plugin/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/@sentry-internal/feedback": { - "version": "7.120.4", - "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.120.4.tgz", - "integrity": "sha512-eSwgvTdrh03zYYaI6UVOjI9p4VmKg6+c2+CBQfRZX++6wwnCVsNv7XF7WUIpVGBAkJ0N2oapjQmCzJKGKBRWQg==", + "node_modules/@storybook/react-docgen-typescript-plugin/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, "license": "MIT", "dependencies": { - "@sentry/core": "7.120.4", - "@sentry/types": "7.120.4", - "@sentry/utils": "7.120.4" + "semver": "^6.0.0" }, "engines": { - "node": ">=12" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@sentry-internal/replay-canvas": { - "version": "7.120.4", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-7.120.4.tgz", - "integrity": "sha512-2+W4CgUL1VzrPjArbTid4WhKh7HH21vREVilZdvffQPVwOEpgNTPAb69loQuTlhJVveh9hWTj2nE5UXLbLP+AA==", + "node_modules/@storybook/react-docgen-typescript-plugin/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, "license": "MIT", "dependencies": { - "@sentry/core": "7.120.4", - "@sentry/replay": "7.120.4", - "@sentry/types": "7.120.4", - "@sentry/utils": "7.120.4" + "p-try": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@sentry-internal/tracing": { - "version": "7.120.4", - "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.120.4.tgz", - "integrity": "sha512-Fz5+4XCg3akeoFK+K7g+d7HqGMjmnLoY2eJlpONJmaeT9pXY7yfUyXKZMmMajdE2LxxKJgQ2YKvSCaGVamTjHw==", + "node_modules/@storybook/react-docgen-typescript-plugin/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, "license": "MIT", "dependencies": { - "@sentry/core": "7.120.4", - "@sentry/types": "7.120.4", - "@sentry/utils": "7.120.4" + "p-limit": "^2.2.0" }, "engines": { "node": ">=8" } }, - "node_modules/@sentry/babel-plugin-component-annotate": { - "version": "2.22.7", - "resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.22.7.tgz", - "integrity": "sha512-aa7XKgZMVl6l04NY+3X7BP7yvQ/s8scn8KzQfTLrGRarziTlMGrsCOBQtCNWXOPEbtxAIHpZ9dsrAn5EJSivOQ==", + "node_modules/@storybook/react-docgen-typescript-plugin/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, "license": "MIT", "engines": { - "node": ">= 14" + "node": ">=8" } }, - "node_modules/@sentry/browser": { - "version": "7.120.4", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.120.4.tgz", - "integrity": "sha512-ymlNtIPG6HAKzM/JXpWVGCzCNufZNADfy+O/olZuVJW5Be1DtOFyRnBvz0LeKbmxJbXb2lX/XMhuen6PXPdoQw==", + "node_modules/@storybook/react-docgen-typescript-plugin/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, "license": "MIT", "dependencies": { - "@sentry-internal/feedback": "7.120.4", - "@sentry-internal/replay-canvas": "7.120.4", - "@sentry-internal/tracing": "7.120.4", - "@sentry/core": "7.120.4", - "@sentry/integrations": "7.120.4", - "@sentry/replay": "7.120.4", - "@sentry/types": "7.120.4", - "@sentry/utils": "7.120.4" + "find-up": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/@sentry/bundler-plugin-core": { - "version": "2.22.7", - "resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.22.7.tgz", - "integrity": "sha512-ouQh5sqcB8vsJ8yTTe0rf+iaUkwmeUlGNFi35IkCFUQlWJ22qS6OfvNjOqFI19e6eGUXks0c/2ieFC4+9wJ+1g==", + "node_modules/@storybook/react-docgen-typescript-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@storybook/react-dom-shim": { + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.3.3.tgz", + "integrity": "sha512-lkhuh4G3UTreU9M3Iz5Dt32c6U+l/4XuvqLtbe1sDHENZH6aPj7y0b5FwnfHyvuTvYRhtbo29xZrF5Bp9kCC0w==", + "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.3" + } + }, + "node_modules/@storybook/react-webpack5": { + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/@storybook/react-webpack5/-/react-webpack5-10.3.3.tgz", + "integrity": "sha512-+x5/KrLmgv7VnWfzKxh0xftnDIjogiVksNuaLJAl4TRuY7/OsrlJBUckQ+ovMXTZpaAini6+7IVoilrzmdl4cA==", + "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.18.5", - "@sentry/babel-plugin-component-annotate": "2.22.7", - "@sentry/cli": "2.39.1", - "dotenv": "^16.3.1", - "find-up": "^5.0.0", - "glob": "^9.3.2", - "magic-string": "0.30.8", - "unplugin": "1.0.1" + "@storybook/builder-webpack5": "10.3.3", + "@storybook/preset-react-webpack": "10.3.3", + "@storybook/react": "10.3.3" }, - "engines": { - "node": ">= 14" + "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.3", + "typescript": ">= 4.9.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@sentry/bundler-plugin-core/node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "license": "BSD-2-Clause", + "node_modules/@storybook/react/node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, "engines": { - "node": ">=12" + "node": ">=6.9.0" }, "funding": { - "url": "https://dotenvx.com" + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@sentry/bundler-plugin-core/node_modules/magic-string": { - "version": "0.30.8", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", - "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "node_modules/@storybook/react/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@storybook/react/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": { - "@jridgewell/sourcemap-codec": "^1.4.15" + "@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": ">=12" + "node": "^20.9.0 || >=22" + } + }, + "node_modules/@storybook/react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@sentry/cli": { - "version": "2.39.1", - "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.39.1.tgz", - "integrity": "sha512-JIb3e9vh0+OmQ0KxmexMXg9oZsR/G7HMwxt5BUIKAXZ9m17Xll4ETXTRnRUBT3sf7EpNGAmlQk1xEmVN9pYZYQ==", + "node_modules/@swc/core": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.21.tgz", + "integrity": "sha512-fkk7NJcBscrR3/F8jiqlMptRHP650NxqDnspBMrRe5d8xOoCy9MLL5kOBLFXjFLfMo3KQQHhk+/jUULOMlR1uQ==", + "devOptional": true, "hasInstallScript": true, - "license": "BSD-3-Clause", + "license": "Apache-2.0", "dependencies": { - "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.7", - "progress": "^2.0.3", - "proxy-from-env": "^1.1.0", - "which": "^2.0.2" - }, - "bin": { - "sentry-cli": "bin/sentry-cli" + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.25" }, "engines": { - "node": ">= 10" + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@sentry/cli-darwin": "2.39.1", - "@sentry/cli-linux-arm": "2.39.1", - "@sentry/cli-linux-arm64": "2.39.1", - "@sentry/cli-linux-i686": "2.39.1", - "@sentry/cli-linux-x64": "2.39.1", - "@sentry/cli-win32-i686": "2.39.1", - "@sentry/cli-win32-x64": "2.39.1" + "@swc/core-darwin-arm64": "1.15.21", + "@swc/core-darwin-x64": "1.15.21", + "@swc/core-linux-arm-gnueabihf": "1.15.21", + "@swc/core-linux-arm64-gnu": "1.15.21", + "@swc/core-linux-arm64-musl": "1.15.21", + "@swc/core-linux-ppc64-gnu": "1.15.21", + "@swc/core-linux-s390x-gnu": "1.15.21", + "@swc/core-linux-x64-gnu": "1.15.21", + "@swc/core-linux-x64-musl": "1.15.21", + "@swc/core-win32-arm64-msvc": "1.15.21", + "@swc/core-win32-ia32-msvc": "1.15.21", + "@swc/core-win32-x64-msvc": "1.15.21" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } } }, - "node_modules/@sentry/cli-darwin": { - "version": "2.39.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.39.1.tgz", - "integrity": "sha512-kiNGNSAkg46LNGatfNH5tfsmI/kCAaPA62KQuFZloZiemTNzhy9/6NJP8HZ/GxGs8GDMxic6wNrV9CkVEgFLJQ==", - "license": "BSD-3-Clause", + "node_modules/@swc/core-darwin-arm64": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.21.tgz", + "integrity": "sha512-SA8SFg9dp0qKRH8goWsax6bptFE2EdmPf2YRAQW9WoHGf3XKM1bX0nd5UdwxmC5hXsBUZAYf7xSciCler6/oyA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "darwin" @@ -4634,293 +6055,297 @@ "node": ">=10" } }, - "node_modules/@sentry/cli-linux-arm": { - "version": "2.39.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.39.1.tgz", - "integrity": "sha512-DkENbxyRxUrfLnJLXTA4s5UL/GoctU5Cm4ER1eB7XN7p9WsamFJd/yf2KpltkjEyiTuplv0yAbdjl1KX3vKmEQ==", + "node_modules/@swc/core-darwin-x64": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.21.tgz", + "integrity": "sha512-//fOVntgowz9+V90lVsNCtyyrtbHp3jWH6Rch7MXHXbcvbLmbCTmssl5DeedUWLLGiAAW1wksBdqdGYOTjaNLw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.21.tgz", + "integrity": "sha512-meNI4Sh6h9h8DvIfEc0l5URabYMSuNvyisLmG6vnoYAS43s8ON3NJR8sDHvdP7NJTrLe0q/x2XCn6yL/BeHcZg==", "cpu": [ "arm" ], - "license": "BSD-3-Clause", + "license": "Apache-2.0", "optional": true, "os": [ - "linux", - "freebsd" + "linux" ], "engines": { "node": ">=10" } }, - "node_modules/@sentry/cli-linux-arm64": { - "version": "2.39.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.39.1.tgz", - "integrity": "sha512-5VbVJDatolDrWOgaffsEM7znjs0cR8bHt9Bq0mStM3tBolgAeSDHE89NgHggfZR+DJ2VWOy4vgCwkObrUD6NQw==", + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.21.tgz", + "integrity": "sha512-QrXlNQnHeXqU2EzLlnsPoWEh8/GtNJLvfMiPsDhk+ht6Xv8+vhvZ5YZ/BokNWSIZiWPKLAqR0M7T92YF5tmD3g==", "cpu": [ "arm64" ], - "license": "BSD-3-Clause", + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ - "linux", - "freebsd" + "linux" ], "engines": { "node": ">=10" } }, - "node_modules/@sentry/cli-linux-i686": { - "version": "2.39.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.39.1.tgz", - "integrity": "sha512-pXWVoKXCRrY7N8vc9H7mETiV9ZCz+zSnX65JQCzZxgYrayQPJTc+NPRnZTdYdk5RlAupXaFicBI2GwOCRqVRkg==", + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.21.tgz", + "integrity": "sha512-8/yGCMO333ultDaMQivE5CjO6oXDPeeg1IV4sphojPkb0Pv0i6zvcRIkgp60xDB+UxLr6VgHgt+BBgqS959E9g==", "cpu": [ - "x86", - "ia32" + "arm64" ], - "license": "BSD-3-Clause", + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ - "linux", - "freebsd" + "linux" ], "engines": { "node": ">=10" } }, - "node_modules/@sentry/cli-linux-x64": { - "version": "2.39.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.39.1.tgz", - "integrity": "sha512-IwayNZy+it7FWG4M9LayyUmG1a/8kT9+/IEm67sT5+7dkMIMcpmHDqL8rWcPojOXuTKaOBBjkVdNMBTXy0mXlA==", + "node_modules/@swc/core-linux-ppc64-gnu": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.21.tgz", + "integrity": "sha512-ucW0HzPx0s1dgRvcvuLSPSA/2Kk/VYTv9st8qe1Kc22Gu0Q0rH9+6TcBTmMuNIp0Xs4BPr1uBttmbO1wEGI49Q==", "cpu": [ - "x64" + "ppc64" ], - "license": "BSD-3-Clause", + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ - "linux", - "freebsd" + "linux" ], "engines": { "node": ">=10" } }, - "node_modules/@sentry/cli-win32-i686": { - "version": "2.39.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.39.1.tgz", - "integrity": "sha512-NglnNoqHSmE+Dz/wHeIVRnV2bLMx7tIn3IQ8vXGO5HWA2f8zYJGktbkLq1Lg23PaQmeZLPGlja3gBQfZYSG10Q==", + "node_modules/@swc/core-linux-s390x-gnu": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.21.tgz", + "integrity": "sha512-ulTnOGc5I7YRObE/9NreAhQg94QkiR5qNhhcUZ1iFAYjzg/JGAi1ch+s/Ixe61pMIr8bfVrF0NOaB0f8wjaAfA==", "cpu": [ - "x86", - "ia32" + "s390x" ], - "license": "BSD-3-Clause", + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ - "win32" + "linux" ], "engines": { "node": ">=10" } }, - "node_modules/@sentry/cli-win32-x64": { - "version": "2.39.1", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.39.1.tgz", - "integrity": "sha512-xv0R2CMf/X1Fte3cMWie1NXuHmUyQPDBfCyIt6k6RPFPxAYUgcqgMPznYwVMwWEA1W43PaOkSn3d8ZylsDaETw==", + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.21.tgz", + "integrity": "sha512-D0RokxtM+cPvSqJIKR6uja4hbD+scI9ezo95mBhfSyLUs9wnPPl26sLp1ZPR/EXRdYm3F3S6RUtVi+8QXhT24Q==", "cpu": [ "x64" ], - "license": "BSD-3-Clause", + "license": "Apache-2.0 AND MIT", "optional": true, "os": [ - "win32" + "linux" ], "engines": { "node": ">=10" } }, - "node_modules/@sentry/core": { - "version": "7.120.4", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.120.4.tgz", - "integrity": "sha512-TXu3Q5kKiq8db9OXGkWyXUbIxMMuttB5vJ031yolOl5T/B69JRyAoKuojLBjRv1XX583gS1rSSoX8YXX7ATFGA==", - "license": "MIT", - "dependencies": { - "@sentry/types": "7.120.4", - "@sentry/utils": "7.120.4" - }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.21.tgz", + "integrity": "sha512-nER8u7VeRfmU6fMDzl1NQAbbB/G7O2avmvCOwIul1uGkZ2/acbPH+DCL9h5+0yd/coNcxMBTL6NGepIew+7C2w==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/@sentry/integrations": { - "version": "7.120.4", - "resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-7.120.4.tgz", - "integrity": "sha512-kkBTLk053XlhDCg7OkBQTIMF4puqFibeRO3E3YiVc4PGLnocXMaVpOSCkMqAc1k1kZ09UgGi8DxfQhnFEjUkpA==", - "license": "MIT", - "dependencies": { - "@sentry/core": "7.120.4", - "@sentry/types": "7.120.4", - "@sentry/utils": "7.120.4", - "localforage": "^1.8.1" - }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.21.tgz", + "integrity": "sha512-+/AgNBnjYugUA8C0Do4YzymgvnGbztv7j8HKSQLvR/DQgZPoXQ2B3PqB2mTtGh/X5DhlJWiqnunN35JUgWcAeQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/@sentry/replay": { - "version": "7.120.4", - "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.120.4.tgz", - "integrity": "sha512-FW8sPenNFfnO/K7sncsSTX4rIVak9j7VUiLIagJrcqZIC7d1dInFNjy8CdVJUlyz3Y3TOgIl3L3+ZpjfyMnaZg==", - "license": "MIT", - "dependencies": { - "@sentry-internal/tracing": "7.120.4", - "@sentry/core": "7.120.4", - "@sentry/types": "7.120.4", - "@sentry/utils": "7.120.4" - }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.21.tgz", + "integrity": "sha512-IkSZj8PX/N4HcaFhMQtzmkV8YSnuNoJ0E6OvMwFiOfejPhiKXvl7CdDsn1f4/emYEIDO3fpgZW9DTaCRMDxaDA==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=12" + "node": ">=10" } }, - "node_modules/@sentry/types": { - "version": "7.120.4", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.120.4.tgz", - "integrity": "sha512-cUq2hSSe6/qrU6oZsEP4InMI5VVdD86aypE+ENrQ6eZEVLTCYm1w6XhW1NvIu3UuWh7gZec4a9J7AFpYxki88Q==", - "license": "MIT", + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.15.21", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.21.tgz", + "integrity": "sha512-zUyWso7OOENB6e1N1hNuNn8vbvLsTdKQ5WKLgt/JcBNfJhKy/6jmBmqI3GXk/MyvQKd5SLvP7A0F36p7TeDqvw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/@sentry/utils": { - "version": "7.120.4", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.120.4.tgz", - "integrity": "sha512-zCKpyDIWKHwtervNK2ZlaK8mMV7gVUijAgFeJStH+CU/imcdquizV3pFLlSQYRswG+Lbyd6CT/LGRh3IbtkCFw==", - "license": "MIT", + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@swc/types": { + "version": "0.1.26", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.26.tgz", + "integrity": "sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw==", + "devOptional": true, + "license": "Apache-2.0", "dependencies": { - "@sentry/types": "7.120.4" - }, - "engines": { - "node": ">=8" + "@swc/counter": "^0.1.3" } }, - "node_modules/@sentry/webpack-plugin": { - "version": "2.22.7", - "resolved": "https://registry.npmjs.org/@sentry/webpack-plugin/-/webpack-plugin-2.22.7.tgz", - "integrity": "sha512-j5h5LZHWDlm/FQCCmEghQ9FzYXwfZdlOf3FE/X6rK6lrtx0JCAkq+uhMSasoyP4XYKL4P4vRS6WFSos4jxf/UA==", + "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": { - "@sentry/bundler-plugin-core": "2.22.7", - "unplugin": "1.0.1", - "uuid": "^9.0.0" + "@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": ">= 14" - }, - "peerDependencies": { - "webpack": ">=4.40.0" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.34.46", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.46.tgz", - "integrity": "sha512-kiW7CtS/NkdvTUjkjUJo7d5JsFfbJ14YjdhDk9KoEgK6nFjKNXZPrX0jfLA8ZlET4cFLHxOZ/0vFKOP+bOxIOQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" + "node": ">=18" } }, - "node_modules/@sinonjs/commons/node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "node_modules/@testing-library/dom/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": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@sinonjs/fake-timers": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", - "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "node_modules/@testing-library/dom/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": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/@slack/logger": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-3.0.0.tgz", - "integrity": "sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA==", "license": "MIT", + "peer": true, "dependencies": { - "@types/node": ">=12.0.0" + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" }, "engines": { - "node": ">= 12.13.0", - "npm": ">= 6.12.0" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/@slack/types": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/@slack/types/-/types-2.15.0.tgz", - "integrity": "sha512-livb1gyG3J8ATLBJ3KjZfjHpTRz9btY1m5cgNuXxWJbhwRB1Gwb8Ly6XLJm2Sy1W6h+vLgqIHg7IwKrF1C1Szg==", + "node_modules/@testing-library/dom/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", - "engines": { - "node": ">= 12.13.0", - "npm": ">= 6.12.0" - } + "peer": true }, - "node_modules/@slack/web-api": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-6.13.0.tgz", - "integrity": "sha512-dv65crIgdh9ZYHrevLU6XFHTQwTyDmNqEqzuIrV+Vqe/vgiG6w37oex5ePDU1RGm2IJ90H8iOvHFvzdEO/vB+g==", + "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": { - "@slack/logger": "^3.0.0", - "@slack/types": "^2.11.0", - "@types/is-stream": "^1.1.0", - "@types/node": ">=12.0.0", - "axios": "^1.7.4", - "eventemitter3": "^3.1.0", - "form-data": "^2.5.0", - "is-electron": "2.2.2", - "is-stream": "^1.1.0", - "p-queue": "^6.6.1", - "p-retry": "^4.0.0" + "@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": ">= 12.13.0", - "npm": ">= 6.12.0" + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" } }, - "node_modules/@stencil/core": { - "version": "4.36.2", - "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.36.2.tgz", - "integrity": "sha512-PRFSpxNzX9Oi0Wfh02asztN9Sgev/MacfZwmd+VVyE6ZxW+a/kEpAYZhzGAmE+/aKVOGYuug7R9SulanYGxiDQ==", + "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", - "bin": { - "stencil": "bin/stencil" - }, "engines": { - "node": ">=16.0.0", - "npm": ">=7.10.0" + "node": ">=12", + "npm": ">=6" }, - "optionalDependencies": { - "@rollup/rollup-darwin-arm64": "4.34.9", - "@rollup/rollup-darwin-x64": "4.34.9", - "@rollup/rollup-linux-arm64-gnu": "4.34.9", - "@rollup/rollup-linux-arm64-musl": "4.34.9", - "@rollup/rollup-linux-x64-gnu": "4.34.9", - "@rollup/rollup-linux-x64-musl": "4.34.9", - "@rollup/rollup-win32-arm64-msvc": "4.34.9", - "@rollup/rollup-win32-x64-msvc": "4.34.9" + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" } }, "node_modules/@tsconfig/node10": { @@ -4957,6 +6382,14 @@ "@types/readdir-glob": "*" } }, + "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", @@ -5002,6 +6435,17 @@ "@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/classnames": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.3.4.tgz", @@ -5128,6 +6572,20 @@ "@types/ms": "*" } }, + "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/dompurify": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.2.0.tgz", @@ -5284,6 +6742,13 @@ "@types/unist": "^2" } }, + "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/ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", @@ -5331,7 +6796,6 @@ "version": "19.2.14", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", - "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -5341,7 +6805,7 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "dev": true, + "devOptional": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.2.0" @@ -5394,7 +6858,6 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, "license": "MIT" }, "node_modules/@types/readdir-glob": { @@ -5407,12 +6870,26 @@ "@types/node": "*" } }, + "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/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "license": "MIT" }, + "node_modules/@types/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -5542,6 +7019,42 @@ } } }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", + "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.58.0", + "@typescript-eslint/types": "^8.58.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", + "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/scope-manager": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.4.0.tgz", @@ -5560,6 +7073,23 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", + "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, "node_modules/@typescript-eslint/types": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.4.0.tgz", @@ -5602,6 +7132,160 @@ } } }, + "node_modules/@typescript-eslint/utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", + "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", + "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", + "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", + "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.58.0", + "@typescript-eslint/tsconfig-utils": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", + "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/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/@typescript-eslint/utils/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/visitor-keys": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.4.0.tgz", @@ -5641,6 +7325,64 @@ "darwin" ] }, + "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/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/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/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/@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", @@ -6346,6 +8088,16 @@ "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/array-buffer-byte-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", @@ -6471,12 +8223,35 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "license": "MIT" - }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" + }, + "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/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -6545,6 +8320,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axe-core": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.1.tgz", + "integrity": "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, "node_modules/axios": { "version": "1.13.5", "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", @@ -7269,6 +9054,22 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "license": "MIT" }, + "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/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -7389,6 +9190,33 @@ "cdl": "bin/cdl.js" } }, + "node_modules/case-sensitive-paths-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "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", @@ -7453,6 +9281,16 @@ "dev": true, "license": "MIT" }, + "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/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -7489,6 +9327,30 @@ "node": ">= 6" } }, + "node_modules/chromatic": { + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-15.3.1.tgz", + "integrity": "sha512-2u4ow+Hf2xNusaTj9xkfp7pHMJ2pMnmOQxZPWyB+4SNEqjrMz813E6CJJzczBSlZzeY0waYq56uvpK/CkiUNvQ==", + "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/chrome-trace-event": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", @@ -8229,6 +10091,13 @@ "url": "https://github.com/sponsors/fb55" } }, + "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/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -8542,6 +10411,16 @@ "node": ">=0.10.0" } }, + "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", @@ -8559,6 +10438,36 @@ "node": ">=0.10.0" } }, + "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/defaults": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", @@ -8590,6 +10499,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/define-properties": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", @@ -8703,6 +10625,14 @@ "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-align": { "version": "1.12.4", "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.4.tgz", @@ -8930,6 +10860,18 @@ "node": ">= 0.8" } }, + "node_modules/endent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/endent/-/endent-2.1.0.tgz", + "integrity": "sha512-r8VyPX7XL8U01Xgnb1CjZ3XV+z90cXIJ9JPE/R9SEC9vpw2P6CfsRPJmp20DppC5N7ZAMCmjYkJIa744Iyg96w==", + "dev": true, + "license": "MIT", + "dependencies": { + "dedent": "^0.7.0", + "fast-json-parse": "^1.0.3", + "objectorarray": "^1.0.5" + } + }, "node_modules/enhanced-resolve": { "version": "5.19.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", @@ -9140,6 +11082,48 @@ "integrity": "sha512-8/2Juj7DeNMwxkbcruBmErKcgPSTPgaGIB08nQvlBzaqliW1vMliNOaBQaMzzQAxX9k0vGy+8wwWlgiLuKqZIw==", "license": "MIT" }, + "node_modules/esbuild": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", + "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.4", + "@esbuild/android-arm": "0.27.4", + "@esbuild/android-arm64": "0.27.4", + "@esbuild/android-x64": "0.27.4", + "@esbuild/darwin-arm64": "0.27.4", + "@esbuild/darwin-x64": "0.27.4", + "@esbuild/freebsd-arm64": "0.27.4", + "@esbuild/freebsd-x64": "0.27.4", + "@esbuild/linux-arm": "0.27.4", + "@esbuild/linux-arm64": "0.27.4", + "@esbuild/linux-ia32": "0.27.4", + "@esbuild/linux-loong64": "0.27.4", + "@esbuild/linux-mips64el": "0.27.4", + "@esbuild/linux-ppc64": "0.27.4", + "@esbuild/linux-riscv64": "0.27.4", + "@esbuild/linux-s390x": "0.27.4", + "@esbuild/linux-x64": "0.27.4", + "@esbuild/netbsd-arm64": "0.27.4", + "@esbuild/netbsd-x64": "0.27.4", + "@esbuild/openbsd-arm64": "0.27.4", + "@esbuild/openbsd-x64": "0.27.4", + "@esbuild/openharmony-arm64": "0.27.4", + "@esbuild/sunos-x64": "0.27.4", + "@esbuild/win32-arm64": "0.27.4", + "@esbuild/win32-ia32": "0.27.4", + "@esbuild/win32-x64": "0.27.4" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -9551,6 +11535,20 @@ "node": ">=6.0.0" } }, + "node_modules/eslint-plugin-storybook": { + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-10.3.3.tgz", + "integrity": "sha512-jo8wZvKaJlxxrNvf4hCsROJP3CdlpaLiYewAs5Ww+PJxCrLelIi5XVHWOAgBvvr3H9WDKvUw8xuvqPYqAlpkFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.48.0" + }, + "peerDependencies": { + "eslint": ">=8", + "storybook": "^10.3.3" + } + }, "node_modules/eslint-plugin-unused-imports": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.3.0.tgz", @@ -10183,6 +12181,24 @@ "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==", "license": "MIT" }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", @@ -10600,6 +12616,138 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.1.0.tgz", + "integrity": "sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "chalk": "^4.1.2", + "chokidar": "^4.0.1", + "cosmiconfig": "^8.2.0", + "deepmerge": "^4.2.2", + "fs-extra": "^10.0.0", + "memfs": "^3.4.1", + "minimatch": "^3.0.4", + "node-abort-controller": "^3.0.1", + "schema-utils": "^3.1.1", + "semver": "^7.3.5", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "typescript": ">3.6.0", + "webpack": "^5.11.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/form-data": { "version": "2.5.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz", @@ -12268,17 +14416,52 @@ "is-extglob": "^2.1.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "node_modules/is-inside-container/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": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-interactive": { @@ -14366,6 +16549,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/lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -14384,6 +16574,17 @@ "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.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", @@ -15094,6 +17295,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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/mini-create-react-context": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", @@ -15288,6 +17499,13 @@ "tslib": "^2.0.3" } }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "dev": true, + "license": "MIT" + }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -15603,6 +17821,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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/opener": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", @@ -15958,6 +18195,16 @@ "node": ">=8" } }, + "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": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -16932,6 +19179,38 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/react-docgen": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-7.1.1.tgz", + "integrity": "sha512-hlSJDQ2synMPKFZOsKo9Hi8WWZTC7POR8EmWvTSjow+VDgKzkmjQvFm2fk0tmRw+f0vTOIYKlarR0iL4996pdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.18.9", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9", + "@types/babel__core": "^7.18.0", + "@types/babel__traverse": "^7.18.0", + "@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": ">=16.14.0" + } + }, + "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.4", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", @@ -17372,6 +19651,33 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "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/recast/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/recharts": { "version": "2.15.4", "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", @@ -17431,6 +19737,33 @@ "node": ">= 0.10" } }, + "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/redeyed": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", @@ -17856,6 +20189,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "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/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -18109,9 +20455,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -18616,6 +20962,64 @@ "node": ">= 0.4" } }, + "node_modules/storybook": { + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-10.3.3.tgz", + "integrity": "sha512-tMoRAts9EVqf+mEMPLC6z1DPyHbcPe+CV1MhLN55IKsl0HxNjvVGK44rVPSePbltPE6vIsn4bdRj6CCUt8SJwQ==", + "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", + "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" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + } + } + }, + "node_modules/storybook/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/streamx": { "version": "2.23.0", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", @@ -18874,6 +21278,19 @@ "node": ">=6" } }, + "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": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -18991,6 +21408,20 @@ "suppress-exit-code": "index.js" } }, + "node_modules/swc-loader": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/swc-loader/-/swc-loader-0.2.7.tgz", + "integrity": "sha512-nwYWw3Fh9ame3Rtm7StS9SBLpHRRnYcK7bnpF3UKZmesAK0gw2/ADvlURFAINmPvKtDLzp+GBiP9yLoEjg6S9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@swc/counter": "^0.1.3" + }, + "peerDependencies": { + "@swc/core": "^1.2.147", + "webpack": ">=2" + } + }, "node_modules/synckit": { "version": "0.11.11", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", @@ -19237,6 +21668,43 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "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/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/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -19316,6 +21784,29 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "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/ts-jest": { "version": "29.4.6", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz", @@ -19369,19 +21860,6 @@ } } }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/ts-jest/node_modules/type-fest": { "version": "4.41.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", @@ -19636,7 +22114,6 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -19866,6 +22343,16 @@ "url": "https://opencollective.com/unified" } }, + "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/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -20747,6 +23234,38 @@ } } }, + "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/wsl-utils/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/x-frame-options": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/x-frame-options/-/x-frame-options-1.0.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 27106b6514ee..e9a3a674b399 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -26,7 +26,10 @@ "dev": "cross-env npm run env && npx nodemon --watch ./api --ignore server/config/config.json --watch webpack --exec node ./api", "dev:local": "ENV=local cross-env npm run env && npx nodemon --watch ./api --ignore server/config/config.json --watch webpack --exec node ./api", "bundle": "npx webpack --config ./webpack/webpack.config.prod.js", - "bundledjango": "npx webpack --config ./webpack/webpack.config.django.prod.js" + "bundledjango": "npx webpack --config ./webpack/webpack.config.django.prod.js", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build", + "generate:tokens": "node scripts/generate-tokens.mjs" }, "engines": { "node": "22.x", @@ -150,6 +153,10 @@ "@dword-design/eslint-plugin-import-alias": "^2.0.7", "@playwright/test": "^1.58.2", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15", + "@storybook/addon-a11y": "^10.3.3", + "@storybook/addon-docs": "^10.3.3", + "@storybook/addon-webpack5-compiler-swc": "^4.0.2", + "@storybook/react-webpack5": "^10.3.3", "@types/archiver": "^6.0.2", "@types/classnames": "^2.3.1", "@types/color": "^3.0.3", @@ -165,6 +172,7 @@ "@typescript-eslint/eslint-plugin": "5.4.0", "@typescript-eslint/parser": "5.4.0", "archiver": "^7.0.1", + "chromatic": "^15.2.0", "eslint": "^8.0.1", "eslint-config-prettier": "^8.3.0", "eslint-plugin-import": "2.27.5", @@ -174,6 +182,7 @@ "eslint-plugin-react-hooks": "4.3.0", "eslint-plugin-sort-destructure-keys": "^1.4.0", "eslint-plugin-sort-keys-fix": "^1.1.2", + "eslint-plugin-storybook": "^10.3.3", "eslint-plugin-unused-imports": "^4.3.0", "husky": "^8.0.2", "jest": "^30.2.0", @@ -185,10 +194,12 @@ "raw-loader": "0.5.1", "react-refresh": "^0.14.2", "ssgrtk": "^0.3.5", + "storybook": "^10.3.3", "ts-jest": "^29.4.6", "typescript": "5.9.3" }, "lint-staged": { - "*.{js,ts,tsx}": "eslint --fix" + "*.{js,ts,tsx}": "eslint --fix", + "common/theme/tokens.json": "npm run generate:tokens && git add common/theme/tokens.ts web/styles/_tokens.scss documentation/TokenReference.generated.stories.tsx" } } diff --git a/frontend/web/components/Banner/Banner.tsx b/frontend/web/components/Banner/Banner.tsx new file mode 100644 index 000000000000..50f65b3b0c67 --- /dev/null +++ b/frontend/web/components/Banner/Banner.tsx @@ -0,0 +1,30 @@ +import React, { FC, ReactNode } from 'react' +import cn from 'classnames' +import Icon, { IconName } from 'components/Icon' +import './banner.scss' + +type BannerVariant = 'success' | 'warning' | 'danger' | 'info' + +type BannerProps = { + variant: BannerVariant + children: ReactNode +} + +const variantIcons: Record = { + danger: 'close-circle', + info: 'info', + success: 'checkmark-circle', + warning: 'warning', +} + +const Banner: FC = ({ children, variant }) => ( +
+ + {children} +
+) + +Banner.displayName = 'Banner' + +export default Banner +export type { BannerProps, BannerVariant } diff --git a/frontend/web/components/Banner/banner.scss b/frontend/web/components/Banner/banner.scss new file mode 100644 index 000000000000..f759771bada4 --- /dev/null +++ b/frontend/web/components/Banner/banner.scss @@ -0,0 +1,28 @@ +.banner { + align-items: center; + border-radius: 8px; + color: var(--color-text-default); + display: flex; + gap: 12px; + padding: 12px 16px; + + &--success { + background: var(--color-surface-success); + border: 1px solid var(--color-border-success); + } + + &--warning { + background: var(--color-surface-warning); + border: 1px solid var(--color-border-warning); + } + + &--danger { + background: var(--color-surface-danger); + border: 1px solid var(--color-border-danger); + } + + &--info { + background: var(--color-surface-info); + border: 1px solid var(--color-border-info); + } +} diff --git a/frontend/web/components/Banner/index.ts b/frontend/web/components/Banner/index.ts new file mode 100644 index 000000000000..644a4e9f8bb0 --- /dev/null +++ b/frontend/web/components/Banner/index.ts @@ -0,0 +1,2 @@ +export { default } from './Banner' +export type { BannerProps, BannerVariant } from './Banner' diff --git a/frontend/web/components/SettingRow/SettingRow.tsx b/frontend/web/components/SettingRow/SettingRow.tsx new file mode 100644 index 000000000000..65d85b679519 --- /dev/null +++ b/frontend/web/components/SettingRow/SettingRow.tsx @@ -0,0 +1,53 @@ +import React, { FC, HTMLAttributes, ReactNode, useId } from 'react' +import cn from 'classnames' +import Switch from 'components/Switch' +import './setting-row.scss' + +type SettingRowProps = HTMLAttributes & { + title: ReactNode + description: ReactNode + checked: boolean + disabled?: boolean + onChange?: (checked: boolean) => void +} + +const SettingRow: FC = ({ + checked, + className, + description, + disabled = false, + onChange, + title, + ...rest +}) => { + const id = useId() + const titleId = `${id}-title` + const descId = `${id}-desc` + + return ( +
+
+
+ +
+
+ {title} +
+
+ + {description} + +
+ ) +} + +SettingRow.displayName = 'SettingRow' + +export default SettingRow +export type { SettingRowProps } diff --git a/frontend/web/components/SettingRow/index.ts b/frontend/web/components/SettingRow/index.ts new file mode 100644 index 000000000000..2bcad68f2ba8 --- /dev/null +++ b/frontend/web/components/SettingRow/index.ts @@ -0,0 +1,2 @@ +export { default } from './SettingRow' +export type { SettingRowProps } from './SettingRow' diff --git a/frontend/web/components/SettingRow/setting-row.scss b/frontend/web/components/SettingRow/setting-row.scss new file mode 100644 index 000000000000..6d708fa04925 --- /dev/null +++ b/frontend/web/components/SettingRow/setting-row.scss @@ -0,0 +1,28 @@ +.setting-row { + display: flex; + flex-direction: column; +} + +.setting-row__header { + align-items: center; + display: flex; + gap: 12px; + margin-bottom: 8px; +} + +.setting-row__switch { + flex-shrink: 0; +} + +.setting-row__title { + color: var(--color-text-default); + display: flex; + gap: 8px; + margin-bottom: 0; +} + +.setting-row__description { + color: var(--color-text-secondary); + font-size: 14px; + line-height: 1.4; +} diff --git a/frontend/web/components/Skeleton/Skeleton.tsx b/frontend/web/components/Skeleton/Skeleton.tsx new file mode 100644 index 000000000000..fe2751f81df1 --- /dev/null +++ b/frontend/web/components/Skeleton/Skeleton.tsx @@ -0,0 +1,35 @@ +import React, { FC, HTMLAttributes } from 'react' +import cn from 'classnames' + +type SkeletonVariant = 'text' | 'badge' | 'circle' + +type SkeletonProps = HTMLAttributes & { + width?: number | string + height?: number | string + variant?: SkeletonVariant +} + +const variantClassNames: Record = { + badge: 'skeleton-badge', + circle: 'skeleton-circle', + text: 'skeleton-text', +} + +const Skeleton: FC = ({ + className, + height, + variant = 'text', + width, + ...rest +}) => ( +
+) + +Skeleton.displayName = 'Skeleton' + +export default Skeleton +export type { SkeletonProps, SkeletonVariant } diff --git a/frontend/web/components/Skeleton/index.ts b/frontend/web/components/Skeleton/index.ts new file mode 100644 index 000000000000..132a9b852b5f --- /dev/null +++ b/frontend/web/components/Skeleton/index.ts @@ -0,0 +1,2 @@ +export { default } from './Skeleton' +export type { SkeletonProps, SkeletonVariant } from './Skeleton' diff --git a/frontend/web/components/pages/project-settings/tabs/general-tab/sections/additional-settings/RequireFeatureOwnershipSetting.tsx b/frontend/web/components/pages/project-settings/tabs/general-tab/sections/additional-settings/RequireFeatureOwnershipSetting.tsx new file mode 100644 index 000000000000..8dfbd0370d64 --- /dev/null +++ b/frontend/web/components/pages/project-settings/tabs/general-tab/sections/additional-settings/RequireFeatureOwnershipSetting.tsx @@ -0,0 +1,36 @@ +import React from 'react' +import SettingRow from 'components/SettingRow' +import { Project } from 'common/types/responses' +import { useUpdateProjectWithToast } from 'components/pages/project-settings/hooks' + +type RequireFeatureOwnershipSettingProps = { + project: Project +} + +export const RequireFeatureOwnershipSetting = ({ + project, +}: RequireFeatureOwnershipSettingProps) => { + const [updateProjectWithToast, { isLoading: isSaving }] = + useUpdateProjectWithToast() + + const handleToggle = async () => { + await updateProjectWithToast( + { + name: project.name, + require_feature_owners: !project.require_feature_owners, + }, + project.id, + ) + } + + return ( + + ) +} diff --git a/frontend/web/components/pages/project-settings/tabs/general-tab/sections/additional-settings/index.tsx b/frontend/web/components/pages/project-settings/tabs/general-tab/sections/additional-settings/index.tsx index 8b8bf15865f0..64fd368742a7 100644 --- a/frontend/web/components/pages/project-settings/tabs/general-tab/sections/additional-settings/index.tsx +++ b/frontend/web/components/pages/project-settings/tabs/general-tab/sections/additional-settings/index.tsx @@ -4,6 +4,7 @@ import { ChangeRequestsApprovalsSetting } from './ChangeRequestsApprovalsSetting import { PreventFlagDefaultsSetting } from './PreventFlagDefaultsSetting' import { CaseSensitivitySetting } from './CaseSensitivitySetting' import { FeatureNameValidation } from './FeatureNameValidation' +import { RequireFeatureOwnershipSetting } from './RequireFeatureOwnershipSetting' type AdditionalSettingsProps = { project: Project @@ -19,6 +20,8 @@ export const AdditionalSettings = ({ project }: AdditionalSettingsProps) => { + +
) } From 94766f6cee8af35ac972338dafbe2983de9cb68b Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Mon, 30 Mar 2026 15:48:02 -0300 Subject: [PATCH 03/11] docs(storybook): add token documentation stories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Visual stories for each token category: - Palette — primitive colour scales (auto-generated from _primitives.scss) - Semantic Colour Tokens — runtime CSS var reading with theme toggle - Tag & Project Colours — categorical swatches - Spacing, Border Radius, Elevation, Motion — import from tokens.ts Token Reference (MCP) — auto-generated flat JSX with all 109 tokens inlined for Storybook MCP consumption by AI agents. All documentation stories have Chromatic snapshots disabled. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../CategoricalPalette.stories.tsx | 152 ++++ .../documentation/ColourPalette.stories.tsx | 79 ++ .../documentation/ElevationTokens.stories.tsx | 84 ++ .../documentation/MotionTokens.stories.tsx | 167 ++++ .../documentation/RadiusTokens.stories.tsx | 76 ++ .../documentation/SemanticTokens.stories.tsx | 131 +++ .../documentation/SpacingTokens.stories.tsx | 99 +++ .../TokenReference.generated.stories.tsx | 795 ++++++++++++++++++ 8 files changed, 1583 insertions(+) create mode 100644 frontend/documentation/CategoricalPalette.stories.tsx create mode 100644 frontend/documentation/ColourPalette.stories.tsx create mode 100644 frontend/documentation/ElevationTokens.stories.tsx create mode 100644 frontend/documentation/MotionTokens.stories.tsx create mode 100644 frontend/documentation/RadiusTokens.stories.tsx create mode 100644 frontend/documentation/SemanticTokens.stories.tsx create mode 100644 frontend/documentation/SpacingTokens.stories.tsx create mode 100644 frontend/documentation/TokenReference.generated.stories.tsx diff --git a/frontend/documentation/CategoricalPalette.stories.tsx b/frontend/documentation/CategoricalPalette.stories.tsx new file mode 100644 index 000000000000..2bcfcadc6a99 --- /dev/null +++ b/frontend/documentation/CategoricalPalette.stories.tsx @@ -0,0 +1,152 @@ +import React from 'react' +import type { Meta, StoryObj } from 'storybook' + +import './docs.scss' +import DocPage from './components/DocPage' +import Swatch from './components/Swatch' + +// --------------------------------------------------------------------------- +// Colour data — inlined to avoid importing Constants (which pulls in the +// full app dependency tree and breaks Storybook's ESM context). +// Source of truth: common/constants.ts +// --------------------------------------------------------------------------- + +const TAG_COLOURS = [ + { hex: '#3d4db6', name: 'Indigo' }, + { hex: '#ea5a45', name: 'Coral' }, + { hex: '#c6b215', name: 'Gold' }, + { hex: '#60bd4e', name: 'Green' }, + { hex: '#fe5505', name: 'Orange' }, + { hex: '#1492f4', name: 'Blue' }, + { hex: '#14c0f4', name: 'Cyan' }, + { hex: '#c277e0', name: 'Lavender' }, + { hex: '#039587', name: 'Teal' }, + { hex: '#344562', name: 'Navy' }, + { hex: '#ffa500', name: 'Amber' }, + { hex: '#3cb371', name: 'Mint' }, + { hex: '#d3d3d3', name: 'Silver' }, + { hex: '#5D6D7E', name: 'Slate' }, + { hex: '#641E16', name: 'Maroon' }, + { hex: '#5B2C6F', name: 'Plum' }, + { hex: '#D35400', name: 'Burnt Orange' }, + { hex: '#F08080', name: 'Salmon' }, + { hex: '#AAC200', name: 'Lime' }, + { hex: '#DE3163', name: 'Cerise' }, +] + +const DEFAULT_TAG_COLOUR = '#dedede' + +const PROJECT_COLOURS = [ + '#906AF6', + '#FAE392', + '#42D0EB', + '#56CCAD', + '#FFBE71', + '#F57C78', +] + +const FEATURE_HEALTH = { + healthyColor: '#60bd4e', + unhealthyColor: '#D35400', +} + +const meta: Meta = { + parameters: { chromatic: { disableSnapshot: true }, layout: 'padded' }, + title: 'Design System/Tag & Project Colours', +} +export default meta + +// --------------------------------------------------------------------------- +// Stories +// --------------------------------------------------------------------------- + +export const TagColours: StoryObj = { + name: 'Tag colours', + render: () => ( + + 20 decorative colours users pick from when creating tags. Will be + defined in _categorical.scss as CSS custom properties ( + --color-tag-1 through --color-tag-20). + Currently in constants.ts pending migration. These are + NOT semantic tokens — they are categorical identifiers that need + to be visually distinct from each other. + + } + > +
+ {TAG_COLOURS.map(({ hex, name }) => ( + + ))} +
+

+ Default tag colour: {DEFAULT_TAG_COLOUR} +

+
+ ), +} + +export const ProjectColours: StoryObj = { + name: 'Project colours', + render: () => ( + + 6 colours assigned by index for project avatar badges. Will be defined + in _categorical.scss as --color-project-1{' '} + through --color-project-6. Currently in{' '} + constants.ts pending migration. Decorative — not + tied to any UI role or theme. + + } + > +
+ {PROJECT_COLOURS.map((colour: string, i: number) => ( + + ))} +
+
+ ), +} + +export const FeatureHealthColours: StoryObj = { + name: 'Feature health colours', + render: () => ( + + Status colours for feature health indicators. Currently hardcoded in{' '} + common/constants.ts as{' '} + Constants.featureHealth. These should migrate to semantic + feedback tokens: var(--color-success-default) and{' '} + var(--color-warning-default). + + } + > +
+
+ +
+ Healthy +
+ Should use var(--color-success-default) +
+
+
+
+ +
+ Unhealthy +
+ Should use var(--color-warning-default) +
+
+
+
+
+ ), +} diff --git a/frontend/documentation/ColourPalette.stories.tsx b/frontend/documentation/ColourPalette.stories.tsx new file mode 100644 index 000000000000..f6dd6c6b0e08 --- /dev/null +++ b/frontend/documentation/ColourPalette.stories.tsx @@ -0,0 +1,79 @@ +import React, { useEffect, useState } from 'react' +import type { Meta, StoryObj } from 'storybook' + +import './docs.scss' +import DocPage from './components/DocPage' +import ScaleRow from './components/ScaleRow' +import type { Scale } from './components/ScaleRow' + +// @ts-expect-error raw-loader import +import primitivesSource from '!!raw-loader!../web/styles/_primitives.scss' + +const meta: Meta = { + parameters: { chromatic: { disableSnapshot: true }, layout: 'padded' }, + title: 'Design System/Palette', +} +export default meta + +// --------------------------------------------------------------------------- +// Parse _primitives.scss at build time +// --------------------------------------------------------------------------- + +function parsePrimitives(source: string): Scale[] { + const scales: Scale[] = [] + let current: Scale | null = null + + for (const line of source.split('\n')) { + // Section comment: "// Slate (neutrals)" → name = "Slate" + const sectionMatch = line.match(/^\/\/\s+(\w+)\s+\(/) + if (sectionMatch) { + current = { name: sectionMatch[1], swatches: [] } + scales.push(current) + continue + } + + // Variable: "$slate-50: #fafafb;" → step=50, hex=#fafafb + const varMatch = line.match(/^\$(\w+)-(\d+):\s*(#[0-9a-fA-F]{6});/) + if (varMatch && current) { + current.swatches.push({ + hex: varMatch[3], + step: varMatch[2], + variable: `$${varMatch[1]}-${varMatch[2]}`, + }) + } + } + + return scales +} + +// --------------------------------------------------------------------------- +// Story +// --------------------------------------------------------------------------- + +const PalettePage: React.FC = () => { + const [scales, setScales] = useState([]) + + useEffect(() => { + setScales(parsePrimitives(primitivesSource)) + }, []) + + return ( + + Auto-generated from web/styles/_primitives.scss. Add a + new variable to the SCSS file and it will appear here automatically. + + } + > + {scales.map((scale) => ( + + ))} + + ) +} + +export const Primitives: StoryObj = { + render: () => , +} diff --git a/frontend/documentation/ElevationTokens.stories.tsx b/frontend/documentation/ElevationTokens.stories.tsx new file mode 100644 index 000000000000..c23dc989835b --- /dev/null +++ b/frontend/documentation/ElevationTokens.stories.tsx @@ -0,0 +1,84 @@ +import React from 'react' +import type { Meta, StoryObj } from 'storybook' + +import './docs.scss' +import DocPage from './components/DocPage' +import { shadow } from 'common/theme/tokens' + +const meta: Meta = { + parameters: { chromatic: { disableSnapshot: true }, layout: 'padded' }, + title: 'Design System/Elevation', +} +export default meta + +const SHADOW_SCALE = Object.entries(shadow).map(([, entry]) => ({ + description: entry.description, + token: entry.value.match(/var\((--[^,)]+)/)?.[1] ?? '', + value: entry.value.match(/,\s*(.+)\)$/)?.[1]?.trim() ?? entry.value, +})) + +export const Overview: StoryObj = { + render: () => ( + + Four elevation levels. Dark mode uses stronger shadows since surface + colour differentiation replaces visual lift. Defined in{' '} + _tokens.scss with .dark overrides. + + } + > +
+ {SHADOW_SCALE.map(({ description, token, value }) => ( +
+ {token} + + {value} + + + {description} + +
+ ))} +
+ +

Dark mode strategy

+

+ In dark mode, shadows are harder to perceive. The dark overrides use + stronger opacity values (0.20–0.40 vs 0.05–0.20). Additionally, higher + elevation surfaces should use progressively lighter background colours + (e.g. --color-surface-emphasis for modals) to maintain + visual hierarchy without relying solely on shadows. +

+
+ ), +} diff --git a/frontend/documentation/MotionTokens.stories.tsx b/frontend/documentation/MotionTokens.stories.tsx new file mode 100644 index 000000000000..1892330274cd --- /dev/null +++ b/frontend/documentation/MotionTokens.stories.tsx @@ -0,0 +1,167 @@ +import React, { useState } from 'react' +import type { Meta, StoryObj } from 'storybook' + +import './docs.scss' +import DocPage from './components/DocPage' +import { duration, easing } from 'common/theme/tokens' + +const meta: Meta = { + parameters: { chromatic: { disableSnapshot: true }, layout: 'padded' }, + title: 'Design System/Motion', +} +export default meta + +const DURATION_SCALE = Object.entries(duration).map(([, entry]) => ({ + description: entry.description, + token: entry.value.match(/var\((--[^,)]+)/)?.[1] ?? '', + value: entry.value.match(/,\s*(.+)\)$/)?.[1]?.trim() ?? '', +})) + +const EASING_SCALE = Object.entries(easing).map(([, entry]) => ({ + description: entry.description, + token: entry.value.match(/var\((--[^,)]+)/)?.[1] ?? '', + value: entry.value.match(/var\((--[^,)]+)\)/)?.[0] ?? entry.value, +})) + +const DemoBox: React.FC<{ + duration: string + easing: string + label: string +}> = ({ duration, easing, label }) => { + const [active, setActive] = useState(false) + + return ( +
setActive((v) => !v)} + role='button' + style={{ + background: active + ? 'var(--color-surface-action, #6837fc)' + : 'var(--color-surface-muted, #eff1f4)', + borderRadius: 'var(--radius-md, 6px)', + color: active + ? 'var(--color-text-on-fill, #fff)' + : 'var(--color-text-default)', + cursor: 'pointer', + fontSize: 13, + fontWeight: 600, + padding: '12px 16px', + textAlign: 'center', + transform: active ? 'scale(1.05)' : 'scale(1)', + transition: `all ${duration} ${easing}`, + userSelect: 'none', + }} + tabIndex={0} + > + {label} — click me +
+ ) +} + +export const Overview: StoryObj = { + render: () => ( + + Duration and easing tokens for consistent animation. Defined in{' '} + _tokens.scss. Pair a duration with an easing:{' '} + + {'transition: all var(--duration-normal) var(--easing-standard)'} + + + } + > +

Duration

+ + + + + + + + + + {DURATION_SCALE.map(({ description, token, value }) => ( + + + + + + ))} + +
TokenValueUsage
+ {token} + + {value} + + {description} +
+ +

Easing

+ + + + + + + + + + {EASING_SCALE.map(({ description, token, value }) => ( + + + + + + ))} + +
TokenValueUsage
+ {token} + + {value} + + {description} +
+ +

Interactive demo

+

+ Click each box to see the duration + easing combination in action. +

+
+ + + +
+
+ ), +} diff --git a/frontend/documentation/RadiusTokens.stories.tsx b/frontend/documentation/RadiusTokens.stories.tsx new file mode 100644 index 000000000000..610cffd4bf75 --- /dev/null +++ b/frontend/documentation/RadiusTokens.stories.tsx @@ -0,0 +1,76 @@ +import React from 'react' +import type { Meta, StoryObj } from 'storybook' + +import './docs.scss' +import DocPage from './components/DocPage' +import { radius } from 'common/theme/tokens' + +const meta: Meta = { + parameters: { chromatic: { disableSnapshot: true }, layout: 'padded' }, + title: 'Design System/Border Radius', +} +export default meta + +const RADIUS_SCALE = Object.entries(radius).map(([, entry]) => ({ + description: entry.description, + token: entry.value.match(/var\((--[^,)]+)/)?.[1] ?? '', + value: entry.value.match(/,\s*(.+)\)$/)?.[1]?.trim() ?? '', +})) + +export const Overview: StoryObj = { + render: () => ( + + Defined in _tokens.scss. Use{' '} + {'border-radius: var(--radius-md)'} in SCSS or{' '} + {'radius.md'} from common/theme/tokens in + TypeScript. + + } + > + + + + + + + + + + + {RADIUS_SCALE.map(({ description, token, value }) => ( + + + + + + + ))} + +
PreviewTokenValueUsage
+
+
+ {token} + + {value} + + {description} +
+
+ ), +} diff --git a/frontend/documentation/SemanticTokens.stories.tsx b/frontend/documentation/SemanticTokens.stories.tsx new file mode 100644 index 000000000000..cf39f1aad396 --- /dev/null +++ b/frontend/documentation/SemanticTokens.stories.tsx @@ -0,0 +1,131 @@ +import React, { useEffect, useState } from 'react' +import type { Meta, StoryObj } from 'storybook' + +import './docs.scss' +import DocPage from './components/DocPage' +import TokenGroup from './components/TokenGroup' +import type { TokenGroupData } from './components/TokenGroup' + +const meta: Meta = { + parameters: { chromatic: { disableSnapshot: true }, layout: 'padded' }, + title: 'Design System/Semantic Colour Tokens', +} +export default meta + +// --------------------------------------------------------------------------- +// Helpers — read --color-* custom properties from the document +// --------------------------------------------------------------------------- + +const TOKEN_PREFIX = '--color-' + +/** Group labels derived from the second segment: --color-{group}-* */ +const GROUP_LABELS: Record = { + border: 'Border', + brand: 'Brand', + danger: 'Danger', + info: 'Info', + success: 'Success', + surface: 'Surface', + text: 'Text', + warning: 'Warning', +} + +/** Extract the group key from a CSS variable name. */ +function groupKey(cssVar: string): string { + return cssVar.replace(TOKEN_PREFIX, '').split('-')[0] +} + +/** + * Read all --color-* custom properties defined on :root. + * We iterate the stylesheet rules rather than computed styles so we only + * pick up tokens we explicitly defined (not inherited browser defaults). + */ +function readTokens(): TokenGroupData[] { + const tokenVars = new Set() + + for (const sheet of Array.from(document.styleSheets)) { + try { + for (const rule of Array.from(sheet.cssRules)) { + if ( + rule instanceof CSSStyleRule && + (rule.selectorText === ':root' || rule.selectorText === '.dark') + ) { + for (let i = 0; i < rule.style.length; i++) { + const prop = rule.style[i] + if (prop.startsWith(TOKEN_PREFIX)) { + tokenVars.add(prop) + } + } + } + } + } catch { + // cross-origin stylesheets throw — skip them + } + } + + const computed = getComputedStyle(document.documentElement) + const grouped: Record = {} + + Array.from(tokenVars) + .sort() + .forEach((cssVar) => { + const key = groupKey(cssVar) + if (!grouped[key]) grouped[key] = [] + grouped[key].push({ + computed: computed.getPropertyValue(cssVar).trim(), + cssVar, + }) + }) + + // Order groups to match the SCSS file + const ORDER = [ + 'brand', + 'surface', + 'text', + 'border', + 'danger', + 'success', + 'warning', + 'info', + ] + return ORDER.filter((key) => grouped[key]).map((key) => ({ + title: GROUP_LABELS[key] || key, + tokens: grouped[key], + })) +} + +// --------------------------------------------------------------------------- +// Story +// --------------------------------------------------------------------------- + +const TokensPage: React.FC = () => { + const [groups, setGroups] = useState([]) + + // No dependency array — re-runs on every render so computed values + // update immediately when the theme is toggled via the toolbar. + useEffect(() => { + const timer = setTimeout(() => setGroups(readTokens()), 50) + return () => clearTimeout(timer) + }) + + return ( + + Auto-generated from CSS custom properties. Source of truth:{' '} + common/theme/tokens.json. Toggle the theme in the toolbar + to see computed values update. + + } + > + {groups.map((group) => ( + + ))} + + ) +} + +export const Overview: StoryObj = { + render: () => , +} diff --git a/frontend/documentation/SpacingTokens.stories.tsx b/frontend/documentation/SpacingTokens.stories.tsx new file mode 100644 index 000000000000..5dcd6b2c405e --- /dev/null +++ b/frontend/documentation/SpacingTokens.stories.tsx @@ -0,0 +1,99 @@ +import React from 'react' +import type { Meta, StoryObj } from 'storybook' + +import './docs.scss' +import DocPage from './components/DocPage' +import { space } from 'common/theme/tokens' + +const meta: Meta = { + parameters: { chromatic: { disableSnapshot: true }, layout: 'padded' }, + title: 'Design System/Spacing', +} +export default meta + +const SPACING_SCALE = Object.entries(space).map(([, entry]) => ({ + description: entry.description, + token: entry.value.match(/var\((--[^,)]+)/)?.[1] ?? '', + value: entry.value.match(/,\s*(.+)\)$/)?.[1]?.trim() ?? '', +})) + +// --------------------------------------------------------------------------- +// Stories +// --------------------------------------------------------------------------- + +export const Overview: StoryObj = { + render: () => ( + + 4px base grid. Defined in _tokens.scss as CSS custom + properties. Use var(--space-4) in SCSS or{' '} + space[4] from common/theme/tokens in + TypeScript. Names follow the Tailwind convention. + + } + > + + + + + + + + + + + {SPACING_SCALE.map(({ description, token, value }) => ( + + + + + + + ))} + +
PreviewTokenValueUsage
+
+
+ {token} + + {value} + + {description} +
+ +

Pairing rules

+
    +
  • + Icon-to-text gap: --space-1 (4px) or{' '} + --space-2 (8px) +
  • +
  • + Component inner padding: --space-3{' '} + (12px) to --space-4 (16px) +
  • +
  • + Between components: --space-4 (16px) to{' '} + --space-6 (24px) +
  • +
  • + Page sections: --space-8 (32px) and + above +
  • +
+
+ ), +} diff --git a/frontend/documentation/TokenReference.generated.stories.tsx b/frontend/documentation/TokenReference.generated.stories.tsx new file mode 100644 index 000000000000..c00be63a03f0 --- /dev/null +++ b/frontend/documentation/TokenReference.generated.stories.tsx @@ -0,0 +1,795 @@ +// AUTO-GENERATED from common/theme/tokens.json — do not edit manually +// Run: npm run generate:tokens + +import React from 'react' +import type { Meta, StoryObj } from 'storybook' + +import './docs.scss' +import DocPage from './components/DocPage' + +const meta: Meta = { + parameters: { chromatic: { disableSnapshot: true }, layout: 'padded' }, + title: 'Design System/Token Reference (MCP)', +} +export default meta + +export const AllTokens: StoryObj = { + name: 'All tokens (MCP reference)', + render: () => ( + +

Colour: surface

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TokenValue
+ --color-surface-default + + #ffffff +
+ --color-surface-subtle + + #fafafb +
+ --color-surface-muted + + #eff1f4 +
+ --color-surface-emphasis + + #e0e3e9 +
+ --color-surface-hover + + rgba(0, 0, 0, 0.08) +
+ --color-surface-active + + rgba(0, 0, 0, 0.16) +
+ --color-surface-action + + #6837fc +
+ --color-surface-action-hover + + #4e25db +
+ --color-surface-action-active + + #3919b7 +
+ --color-surface-action-subtle + + rgba(104, 55, 252, 0.08) +
+ --color-surface-action-muted + + rgba(104, 55, 252, 0.16) +
+ --color-surface-danger + + rgba(239, 77, 86, 0.08) +
+ --color-surface-success + + rgba(39, 171, 149, 0.08) +
+ --color-surface-warning + + rgba(255, 159, 67, 0.08) +
+ --color-surface-info + + rgba(10, 173, 223, 0.08) +
+

Colour: text

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TokenValue
+ --color-text-default + + #1a2634 +
+ --color-text-secondary + + #656d7b +
+ --color-text-tertiary + + #9da4ae +
+ --color-text-disabled + + #9da4ae +
+ --color-text-on-fill + + #ffffff +
+ --color-text-action + + #6837fc +
+ --color-text-danger + + #ef4d56 +
+ --color-text-success + + #27ab95 +
+ --color-text-warning + + #ff9f43 +
+ --color-text-info + + #0aaddf +
+

Colour: border

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TokenValue
+ --color-border-default + + rgba(101, 109, 123, 0.16) +
+ --color-border-strong + + rgba(101, 109, 123, 0.24) +
+ --color-border-disabled + + rgba(101, 109, 123, 0.08) +
+ --color-border-action + + #6837fc +
+ --color-border-danger + + #ef4d56 +
+ --color-border-success + + #27ab95 +
+ --color-border-warning + + #ff9f43 +
+ --color-border-info + + #0aaddf +
+

Colour: icon

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TokenValue
+ --color-icon-default + + #1a2634 +
+ --color-icon-secondary + + #656d7b +
+ --color-icon-disabled + + #9da4ae +
+ --color-icon-action + + #6837fc +
+ --color-icon-danger + + #ef4d56 +
+ --color-icon-success + + #27ab95 +
+ --color-icon-warning + + #ff9f43 +
+ --color-icon-info + + #0aaddf +
+

Space

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TokenValueUsage
+ --space-0 + + 0px + No spacing. Use for flush edges between elements.
+ --space-1 + + 4px + Tight inline spacing. Icon-to-text gap, badge padding.
+ --space-2 + + 8px + + Default inline gap. Space between icon and label, between chips. +
+ --space-3 + + 12px + + Comfortable inner padding. Input padding, small card padding. +
+ --space-4 + + 16px + + Standard component padding. Card padding, section gap, button + horizontal padding. +
+ --space-5 + + 20px + Button horizontal padding (large). Modal body padding.
+ --space-6 + + 24px + + Section spacing. Gap between card groups, modal header/body + padding. +
+ --space-8 + + 32px + Large section spacing. Page section margins.
+ --space-10 + + 40px + Extra-large spacing. Major page sections.
+ --space-12 + + 48px + Page-level vertical spacing between major sections.
+ --space-16 + + 64px + Maximum spacing. Hero sections, page-level separation.
+ --space-0_5 + + 2px + + Fine optical adjustment only. Never use for component padding. +
+

Radius

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TokenValueUsage
+ --radius-none + + 0px + Sharp corners. Tables, dividers.
+ --radius-xs + + 2px + Barely rounded. Badges, tags.
+ --radius-sm + + 4px + Buttons, inputs, small interactive elements.
+ --radius-md + + 6px + Default component radius. Cards, dropdowns, tooltips.
+ --radius-lg + + 8px + Large cards, panels.
+ --radius-xl + + 10px + Extra-large containers.
+ --radius-2xl + + 18px + Modals.
+ --radius-full + + 9999px + Pill shapes, avatars, circular elements.
+

Shadow

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TokenValueUsage
+ --shadow-sm + + 0px 1px 2px rgba(0, 0, 0, 0.05) + Subtle lift. Buttons on hover, input focus ring companion.
+ --shadow-md + + 0px 4px 8px rgba(0, 0, 0, 0.12) + + Cards, dropdowns, popovers. Default elevation for floating + elements. +
+ --shadow-lg + + 0px 8px 16px rgba(0, 0, 0, 0.15) + + Modals, drawers, slide-over panels. High elevation for overlay + content. +
+ --shadow-xl + + 0px 12px 24px rgba(0, 0, 0, 0.20) + + Toast notifications, command palettes. Maximum elevation for + urgent content. +
+

Duration

+ + + + + + + + + + + + + + + + + + + + + + + + + +
TokenValueUsage
+ --duration-fast + + 100ms + + Quick feedback. Hover states, toggle switches, checkbox ticks. +
+ --duration-normal + + 200ms + + Standard transitions. Dropdown open, tooltip appear, tab switch. +
+ --duration-slow + + 300ms + + Deliberate emphasis. Modal enter, drawer slide, accordion expand. +
+

Easing

+ + + + + + + + + + + + + + + + + + + + + + + + + +
TokenValueUsage
+ --easing-standard + + cubic-bezier(0.2, 0, 0.38, 0.9) + + Default for most transitions. Smooth deceleration. Use for + elements moving within the page. +
+ --easing-entrance + + cubic-bezier(0.0, 0, 0.38, 0.9) + + Elements entering the viewport. Decelerates into resting position. + Modals, toasts, slide-ins. +
+ --easing-exit + + cubic-bezier(0.2, 0, 1, 0.9) + + Elements leaving the viewport. Accelerates out of view. Closing + modals, dismissing toasts. +
+ +

Pairing rules

+
    +
  • + Icon-to-text gap: --space-1 (4px) or --space-2 (8px) +
  • +
  • + Component inner padding: --space-3 (12px) to + --space-4 (16px) +
  • +
  • + Between components: --space-4 (16px) to --space-6 + (24px) +
  • +
  • + Page sections: --space-8 (32px) and above +
  • +
+ +

Dark mode shadows

+

+ Dark mode overrides use stronger opacity (0.20-0.40 vs 0.05-0.20). + Higher elevation surfaces should use lighter backgrounds + (--color-surface-emphasis) rather than relying solely on shadows. +

+ +

Motion pairing

+

+ Combine a duration with an easing: transition: all + var(--duration-normal) var(--easing-standard). Use --easing-entrance for + elements appearing, --easing-exit for elements leaving. +

+
+ ), +} From 0ec54858df23c9ddeb8651dd3b921c1fb2b210bf Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Mon, 30 Mar 2026 15:48:20 -0300 Subject: [PATCH 04/11] docs(storybook): add MDX documentation pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Introduction — welcome page with getting started guide - Decision Framework — where does a new colour go? + how to create a semantic token (HTML tables for MDX3 compat) - Token Maintenance — codegen workflow, adding/modifying/removing tokens - Typography — token reference for --font-* tokens (audit findings moved to Notion/Slides, issue #7077 for future components) Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/documentation/DecisionFramework.mdx | 141 +++++++++++++++++++ frontend/documentation/Introduction.mdx | 72 ++++++++++ frontend/documentation/TokenMaintenance.mdx | 105 ++++++++++++++ frontend/documentation/Typography.mdx | 63 +++++++++ 4 files changed, 381 insertions(+) create mode 100644 frontend/documentation/DecisionFramework.mdx create mode 100644 frontend/documentation/Introduction.mdx create mode 100644 frontend/documentation/TokenMaintenance.mdx create mode 100644 frontend/documentation/Typography.mdx diff --git a/frontend/documentation/DecisionFramework.mdx b/frontend/documentation/DecisionFramework.mdx new file mode 100644 index 000000000000..c0755f415328 --- /dev/null +++ b/frontend/documentation/DecisionFramework.mdx @@ -0,0 +1,141 @@ +import { Meta } from '@storybook/addon-docs/blocks' + + + +# Where does a new colour go? + +Use this decision framework when you need to add a colour to the codebase. It ensures colours end up in the right layer. + +### Is it for tags, projects, or data visualisation? + +→ Add to `_categorical.scss` as a CSS custom property. +→ Does NOT need a primitive scale. +→ These are decorative identifiers, not UI roles. + +### Does it fit an existing colour family? + +→ Use the closest primitive from `_primitives.scss`. +→ Pick the nearest step in the 50–950 scale. +→ Create or use a semantic token in `tokens.json` for the UI role. +→ Available families: **slate, purple, red, green, gold, blue, orange**. + +### Is it a completely new hue not covered by any family? + +→ This is rare. Discuss with the team first. +→ If approved: add a full 50–950 scale to `_primitives.scss`. +→ Then create semantic tokens that reference it. +→ Example: product adds a pink/magenta brand element. + +--- + +## Semantic token categories + + + + + + + + + + + +
CategoryToken prefixUse forExamples
Surface{'--color-surface-*'}Backgrounds — page, card, panel, input, hover. Includes action (brand purple) and feedback surfaces.Page bg, card bg, button bg (surface-action), banner bg (surface-danger, surface-success).
Text{'--color-text-*'}Body text, headings, muted/subtle text. Includes feedback text colours.Body copy, headings, error text (text-danger), success text (text-success).
Border{'--color-border-*'}Dividers, input borders, card outlines. Includes feedback borders.Input borders, card outlines, error borders (border-danger).
Icon{'--color-icon-*'}Icon fills — default, secondary, disabled, and feedback variants.Navigation icons, status icons (icon-danger, icon-success).
+ +--- + +## What is NOT a semantic token + +- **Tag colours** — 20 decorative values defined in `_categorical.scss` as CSS custom properties. Users pick these when creating tags. Many don't map to any primitive family (indigo, lime, cerise). Forcing them into scales would reduce visual distinction. +- **Project colours** — 6 values in `_categorical.scss`, assigned by index for project avatar badges. Decorative, not semantic. +- **Chart/data viz palettes** — future work. When needed, add `{'--color-chart-*'}` variables to `_categorical.scss`. +- **Third-party brand colours** — GitHub, Google, etc. Use their official values directly. + +--- + +## How to create a semantic token + +### 1. Name it + +Follow the naming convention: `--color-[category]-[variant]` + + + + + + + + + +
PartOptionsExample
categorysurface, text, border, icon--color-text-
variantdefault, secondary, disabled, danger, success, warning, info. Surface also has: subtle, muted, emphasis, hover, active. Text also has: tertiary, on-fill.--color-text-danger
+ +### 2. Pick the primitive values + +Open `_primitives.scss` and choose the right step from the 50–950 scale. The pattern for light vs dark: + + + + + + + + + + + +
VariantLight modeDark modeWhy
default500–600400–500Needs to read well on light/dark surfaces
hover600–700 (darker)400–600 (lighter)Light: darken on hover. Dark: lighten on hover.
active700–800 (darkest)300–400 (lightest)More intense than hover in both modes
subtle8% opacity of defaultOpaque dark tintLight: transparent tint. Dark: opaque to avoid stacking issues.
+ +### 3. Add to `tokens.json` + +Add the token entry to the appropriate colour category with `light` and `dark` values: + +```json +{ + "color": { + "text": { + "danger": { + "cssVar": "--color-text-danger", + "light": "#ef4d56", + "dark": "#ef4d56" + } + } + } +} +``` + +Run `git commit` — the pre-commit hook generates `_tokens.scss` and `tokens.ts` automatically. + +### 4. Use it + +```scss +// In SCSS — each element type gets its own token +.banner--danger { + background: var(--color-surface-danger); + color: var(--color-text-danger); + border: 1px solid var(--color-border-danger); +} +``` + +```tsx +// Icons reference the icon token + + +// Or via TS exports +import { tokens } from 'common/theme/tokens' + +``` + +Dark mode works automatically — no extra overrides needed. + +### 5. Verify + +- Run Storybook and check the **Design System / Semantic Tokens** story — your new token should appear automatically. +- Toggle light/dark in the toolbar and confirm both values look correct. +- Check contrast: text tokens on surface tokens should meet WCAG AA (4.5:1 for normal text, 3:1 for large text). + +### Common mistakes + +- **Using a hex value instead of the JSON** — always add tokens to `tokens.json`, never edit `_tokens.scss` or `tokens.ts` directly. +- **Forgetting the `dark` value** — every colour token needs both `light` and `dark` in the JSON. Missing it means the light mode value shows in dark mode. +- **Using transparency in dark mode subtle tokens** — use opaque values in dark mode to avoid stacking/blending issues with layered surfaces. +- **Using one token for multiple element types** — don't use `--color-text-danger` for a background. Text, surface, border, and icon each have their own danger token with values optimised for that context. diff --git a/frontend/documentation/Introduction.mdx b/frontend/documentation/Introduction.mdx new file mode 100644 index 000000000000..f2df79553edc --- /dev/null +++ b/frontend/documentation/Introduction.mdx @@ -0,0 +1,72 @@ +{/* Introduction.mdx */} +import { Meta } from '@storybook/addon-docs/blocks' + + + +# Flagsmith Frontend + +Welcome to the Flagsmith Storybook — the single source of truth for UI components, design tokens, and visual patterns used across the frontend. + +If a component doesn't have a story, it doesn't exist. + +## Getting started + +```bash +npm run storybook +``` + +Launches Storybook at [http://localhost:6006](http://localhost:6006). Browse the sidebar to explore components and documentation. + +## Why Storybook + +- **Develop in isolation** — build and test components without spinning up the full app +- **Visual documentation** — every component variant is visible and interactive +- **Dark mode validation** — toggle themes in the toolbar to verify both modes +- **Accessibility** — the a11y addon runs checks automatically on every story + +## Dark mode + +Use the **Theme** toggle in the toolbar (moon/sun icon) to switch between light and dark mode. Every story should look correct in both themes. + +## Writing stories + +Create a `*.stories.tsx` file in the `documentation/` directory. Use the existing stories as a reference. + +A good story should: + +- Cover all meaningful variants of the component +- Work in both light and dark mode +- Include representative content, not just placeholder text + +## Project structure + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathPurpose
documentation/Storybook stories and documentation (self-contained, portable)
web/components/React components
web/styles/SCSS styles and design tokens
common/Shared state, services, types, and utilities
+ +## Links + +- [Frontend README](https://github.com/Flagsmith/flagsmith/blob/main/frontend/README.md) diff --git a/frontend/documentation/TokenMaintenance.mdx b/frontend/documentation/TokenMaintenance.mdx new file mode 100644 index 000000000000..e332f898649b --- /dev/null +++ b/frontend/documentation/TokenMaintenance.mdx @@ -0,0 +1,105 @@ +import { Meta } from '@storybook/addon-docs/blocks' + + + +# Token Maintenance Guide + +How to add, modify, or remove design tokens. Follow this process to keep the token system consistent. + +## Where tokens live + + + + + + + + + + + + +
FilePurposeWhen to edit
common/theme/tokens.jsonSingle source of truth. All token values and descriptions.Every token change starts here.
common/theme/tokens.tsGenerated. TypeScript exports with var(--token, fallback) pattern.Never — auto-generated.
web/styles/_tokens.scssGenerated. CSS custom properties with :root and .dark.Never — auto-generated.
documentation/TokenReference.generated.stories.tsxGenerated. Flat JSX for Storybook MCP.Never — auto-generated.
web/styles/_primitives.scssRaw colour scales (50-950).Only when adding a new colour family or adjusting a scale.
+ +--- + +## Adding a new non-colour token + +Example: adding a new spacing value `--space-20` (80px). + +1. Add the entry to `tokens.json` in the `space` object: + +```json +"20": { "cssVar": "--space-20", "value": "80px", "description": "Page hero spacing." } +``` + +2. Run `git commit`. The pre-commit hook automatically regenerates `tokens.ts`, `_tokens.scss`, and `TokenReference.generated.stories.tsx`, then stages them. No manual step needed. + +--- + +## Adding a new colour token + +Example: adding `--color-text-link`. + +1. Add the entry to `tokens.json` under the `color.text` object: + +```json +"link": { "cssVar": "--color-text-link", "light": "#6837fc", "dark": "#906af6" } +``` + +2. Run `git commit`. The pre-commit hook regenerates all three files. The colour also appears in the Semantic Tokens visual story (auto-read from CSS vars). + +--- + +## Modifying a token value + +1. Change the value in `tokens.json`. +2. Run `git commit` — all generated files update automatically. + +--- + +## Removing a token + +1. Search the codebase for `var(--token-name)` usages — remove or replace all references first. +2. Remove the entry from `tokens.json`. +3. Run `git commit` — the generated files update without the removed token. + +--- + +## The one rule + +**Token values must only be defined in `tokens.json`.** No hardcoded hex values, no hardcoded pixel values anywhere else. SCSS files use `var(--token)`. TypeScript uses the exports from `common/theme/tokens`. Future Tailwind config references tokens via `var()` in `theme.extend`. + +--- + +## How the codegen works + + + + + + + + + + + +
StepWhat happensTriggered by
1scripts/generate-tokens.mjs reads tokens.jsonPre-commit hook (lint-staged) when tokens.json is staged
2Generates tokens.ts, _tokens.scss, and TokenReference.generated.stories.tsxAutomatic
3Generated files are auto-staged and included in the commitAutomatic
4Chromatic deploys the updated Storybook. Storybook MCP serves the flat JSX — AI agents can read all token data.CI (on PR)
+ +You can also run `npm run generate:tokens` manually at any time. + +--- + +## Story types + + + + + + + + + + + +
StoryTypePurpose
Token ReferenceAuto-generated (codegen)MCP-optimised. Every token inlined as flat JSX. AI agents read this.
Semantic Colour TokensAuto-generated (runtime)Visual. Reads --color-* CSS vars from the DOM. Shows computed colours with theme toggle.
Colour PaletteAuto-generated (build time)Visual. Parses _primitives.scss and renders swatch grids.
Spacing, Radius, Elevation, MotionImports from tokens.tsVisual. Shows previews (bars, rounded boxes, shadow cards, interactive demos). Data derived from tokens.ts.
diff --git a/frontend/documentation/Typography.mdx b/frontend/documentation/Typography.mdx new file mode 100644 index 000000000000..f1f0fea36d07 --- /dev/null +++ b/frontend/documentation/Typography.mdx @@ -0,0 +1,63 @@ +import { Meta } from '@storybook/addon-docs/blocks' + + + +# Typography Tokens + +Font tokens defined in `common/theme/tokens.json`. Generated into CSS custom properties in `_tokens.scss`. + +## Headings + + + + + + + + + + + + + +
TokenSizeLine heightWeight
--font-h1-size42px46px700
--font-h2-size34px40px700
--font-h3-size30px40px600
--font-h4-size24px32px600
--font-h5-size18px28px600
--font-h6-size16px24px600
+ +## Body text + + + + + + + + + + + + +
TokenSizeLine heightWeightUsage
--font-body-size14px20px400Default body text
--font-body-sm-size13px18px400Table headers, subtitles
--font-caption-size12px16px400Helper text, small labels
--font-caption-xs-size11px14px400Badges, minimal text
--font-label-size12px16px600Form labels, section labels
+ +## Font weights + + + + + + + + + + + +
TokenValueUsage
--font-weight-regular400Body text, form inputs
--font-weight-medium500Buttons, inputs, alerts, tabs
--font-weight-semibold600Active tab states, sidebar links
--font-weight-bold700Headings, unread badges
+ +## Font family + + + + + + + + +
TokenValue
--font-family'OpenSans', sans-serif
From a1b64f2a8006eca930f0b7a1b11558dcc186394a Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Tue, 31 Mar 2026 12:34:18 -0300 Subject: [PATCH 05/11] feat(tokens): add token utility classes and remove spacing tokens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add _token-utilities.scss with utility classes consuming the design tokens for colours, borders, icons, radius, shadows, font weights, and transitions. These fill the gap Bootstrap doesn't cover — semantic surface colours, icon colours, border radius tokens, etc. Remove spacing tokens entirely — Bootstrap handles spacing via its existing utility classes. This avoids two competing sources of truth for the same concern. - Create web/styles/_token-utilities.scss (no !important, no spacing) - Remove space key from tokens.json - Update generate-tokens.mjs to skip spacing generation - Delete SpacingTokens.stories.tsx - Regenerate _tokens.scss, tokens.ts, and MCP reference story - Update TokenMaintenance.mdx and docs.scss Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/common/theme/tokens.json | 14 -- frontend/common/theme/tokens.ts | 57 ------- .../documentation/SpacingTokens.stories.tsx | 99 ------------ frontend/documentation/TokenMaintenance.mdx | 10 +- .../TokenReference.generated.stories.tsx | 150 ------------------ frontend/documentation/docs.scss | 56 +++---- frontend/scripts/generate-tokens.mjs | 15 +- frontend/web/styles/_token-utilities.scss | 109 +++++++++++++ frontend/web/styles/_tokens.scss | 14 -- frontend/web/styles/styles.scss | 1 + 10 files changed, 147 insertions(+), 378 deletions(-) delete mode 100644 frontend/documentation/SpacingTokens.stories.tsx create mode 100644 frontend/web/styles/_token-utilities.scss diff --git a/frontend/common/theme/tokens.json b/frontend/common/theme/tokens.json index 97c75ca086a8..c5385e64aa11 100644 --- a/frontend/common/theme/tokens.json +++ b/frontend/common/theme/tokens.json @@ -50,20 +50,6 @@ "info": { "cssVar": "--color-icon-info", "light": "#0aaddf", "dark": "#0aaddf" } } }, - "space": { - "0": { "cssVar": "--space-0", "value": "0px", "description": "No spacing. Use for flush edges between elements." }, - "0.5": { "cssVar": "--space-0_5", "value": "2px", "description": "Fine optical adjustment only. Never use for component padding." }, - "1": { "cssVar": "--space-1", "value": "4px", "description": "Tight inline spacing. Icon-to-text gap, badge padding." }, - "2": { "cssVar": "--space-2", "value": "8px", "description": "Default inline gap. Space between icon and label, between chips." }, - "3": { "cssVar": "--space-3", "value": "12px", "description": "Comfortable inner padding. Input padding, small card padding." }, - "4": { "cssVar": "--space-4", "value": "16px", "description": "Standard component padding. Card padding, section gap, button horizontal padding." }, - "5": { "cssVar": "--space-5", "value": "20px", "description": "Button horizontal padding (large). Modal body padding." }, - "6": { "cssVar": "--space-6", "value": "24px", "description": "Section spacing. Gap between card groups, modal header/body padding." }, - "8": { "cssVar": "--space-8", "value": "32px", "description": "Large section spacing. Page section margins." }, - "10": { "cssVar": "--space-10", "value": "40px", "description": "Extra-large spacing. Major page sections." }, - "12": { "cssVar": "--space-12", "value": "48px", "description": "Page-level vertical spacing between major sections." }, - "16": { "cssVar": "--space-16", "value": "64px", "description": "Maximum spacing. Hero sections, page-level separation." } - }, "radius": { "none": { "cssVar": "--radius-none", "value": "0px", "description": "Sharp corners. Tables, dividers." }, "xs": { "cssVar": "--radius-xs", "value": "2px", "description": "Barely rounded. Badges, tags." }, diff --git a/frontend/common/theme/tokens.ts b/frontend/common/theme/tokens.ts index c92e80daa9aa..19ee6559d505 100644 --- a/frontend/common/theme/tokens.ts +++ b/frontend/common/theme/tokens.ts @@ -60,62 +60,6 @@ export type TokenEntry = { description: string } -// Space -export const space: Record = { - '0': { - description: 'No spacing. Use for flush edges between elements.', - value: 'var(--space-0, 0px)', - }, - '0.5': { - description: - 'Fine optical adjustment only. Never use for component padding.', - value: 'var(--space-0_5, 2px)', - }, - '1': { - description: 'Tight inline spacing. Icon-to-text gap, badge padding.', - value: 'var(--space-1, 4px)', - }, - '10': { - description: 'Extra-large spacing. Major page sections.', - value: 'var(--space-10, 40px)', - }, - '12': { - description: 'Page-level vertical spacing between major sections.', - value: 'var(--space-12, 48px)', - }, - '16': { - description: 'Maximum spacing. Hero sections, page-level separation.', - value: 'var(--space-16, 64px)', - }, - '2': { - description: - 'Default inline gap. Space between icon and label, between chips.', - value: 'var(--space-2, 8px)', - }, - '3': { - description: - 'Comfortable inner padding. Input padding, small card padding.', - value: 'var(--space-3, 12px)', - }, - '4': { - description: - 'Standard component padding. Card padding, section gap, button horizontal padding.', - value: 'var(--space-4, 16px)', - }, - '5': { - description: 'Button horizontal padding (large). Modal body padding.', - value: 'var(--space-5, 20px)', - }, - '6': { - description: - 'Section spacing. Gap between card groups, modal header/body padding.', - value: 'var(--space-6, 24px)', - }, - '8': { - description: 'Large section spacing. Page section margins.', - value: 'var(--space-8, 32px)', - }, -} // Radius export const radius: Record = { '2xl': { @@ -212,6 +156,5 @@ export const easing: Record = { export type TokenCategory = keyof typeof tokens export type TokenName = keyof (typeof tokens)[C] -export type SpaceScale = keyof typeof space export type RadiusScale = keyof typeof radius export type ShadowScale = keyof typeof shadow diff --git a/frontend/documentation/SpacingTokens.stories.tsx b/frontend/documentation/SpacingTokens.stories.tsx deleted file mode 100644 index 5dcd6b2c405e..000000000000 --- a/frontend/documentation/SpacingTokens.stories.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import React from 'react' -import type { Meta, StoryObj } from 'storybook' - -import './docs.scss' -import DocPage from './components/DocPage' -import { space } from 'common/theme/tokens' - -const meta: Meta = { - parameters: { chromatic: { disableSnapshot: true }, layout: 'padded' }, - title: 'Design System/Spacing', -} -export default meta - -const SPACING_SCALE = Object.entries(space).map(([, entry]) => ({ - description: entry.description, - token: entry.value.match(/var\((--[^,)]+)/)?.[1] ?? '', - value: entry.value.match(/,\s*(.+)\)$/)?.[1]?.trim() ?? '', -})) - -// --------------------------------------------------------------------------- -// Stories -// --------------------------------------------------------------------------- - -export const Overview: StoryObj = { - render: () => ( - - 4px base grid. Defined in _tokens.scss as CSS custom - properties. Use var(--space-4) in SCSS or{' '} - space[4] from common/theme/tokens in - TypeScript. Names follow the Tailwind convention. - - } - > - - - - - - - - - - - {SPACING_SCALE.map(({ description, token, value }) => ( - - - - - - - ))} - -
PreviewTokenValueUsage
-
-
- {token} - - {value} - - {description} -
- -

Pairing rules

-
    -
  • - Icon-to-text gap: --space-1 (4px) or{' '} - --space-2 (8px) -
  • -
  • - Component inner padding: --space-3{' '} - (12px) to --space-4 (16px) -
  • -
  • - Between components: --space-4 (16px) to{' '} - --space-6 (24px) -
  • -
  • - Page sections: --space-8 (32px) and - above -
  • -
-
- ), -} diff --git a/frontend/documentation/TokenMaintenance.mdx b/frontend/documentation/TokenMaintenance.mdx index e332f898649b..ad263b0f8699 100644 --- a/frontend/documentation/TokenMaintenance.mdx +++ b/frontend/documentation/TokenMaintenance.mdx @@ -25,16 +25,18 @@ How to add, modify, or remove design tokens. Follow this process to keep the tok ## Adding a new non-colour token -Example: adding a new spacing value `--space-20` (80px). +Example: adding a new radius value `--radius-3xl` (24px). -1. Add the entry to `tokens.json` in the `space` object: +1. Add the entry to `tokens.json` in the `radius` object: ```json -"20": { "cssVar": "--space-20", "value": "80px", "description": "Page hero spacing." } +"3xl": { "cssVar": "--radius-3xl", "value": "24px", "description": "Extra-large container corners." } ``` 2. Run `git commit`. The pre-commit hook automatically regenerates `tokens.ts`, `_tokens.scss`, and `TokenReference.generated.stories.tsx`, then stages them. No manual step needed. +**Note:** Spacing is handled by Bootstrap utility classes (`gap-2`, `p-3`, `m-4`, etc.) — there are no custom spacing tokens. + --- ## Adding a new colour token @@ -100,6 +102,6 @@ You can also run `npm run generate:tokens` manually at any time. Token ReferenceAuto-generated (codegen)MCP-optimised. Every token inlined as flat JSX. AI agents read this. Semantic Colour TokensAuto-generated (runtime)Visual. Reads --color-* CSS vars from the DOM. Shows computed colours with theme toggle. Colour PaletteAuto-generated (build time)Visual. Parses _primitives.scss and renders swatch grids. - Spacing, Radius, Elevation, MotionImports from tokens.tsVisual. Shows previews (bars, rounded boxes, shadow cards, interactive demos). Data derived from tokens.ts. + Radius, Elevation, MotionImports from tokens.tsVisual. Shows previews (rounded boxes, shadow cards, interactive demos). Data derived from tokens.ts. diff --git a/frontend/documentation/TokenReference.generated.stories.tsx b/frontend/documentation/TokenReference.generated.stories.tsx index c00be63a03f0..337508ad1971 100644 --- a/frontend/documentation/TokenReference.generated.stories.tsx +++ b/frontend/documentation/TokenReference.generated.stories.tsx @@ -392,138 +392,6 @@ export const AllTokens: StoryObj = { -

Space

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TokenValueUsage
- --space-0 - - 0px - No spacing. Use for flush edges between elements.
- --space-1 - - 4px - Tight inline spacing. Icon-to-text gap, badge padding.
- --space-2 - - 8px - - Default inline gap. Space between icon and label, between chips. -
- --space-3 - - 12px - - Comfortable inner padding. Input padding, small card padding. -
- --space-4 - - 16px - - Standard component padding. Card padding, section gap, button - horizontal padding. -
- --space-5 - - 20px - Button horizontal padding (large). Modal body padding.
- --space-6 - - 24px - - Section spacing. Gap between card groups, modal header/body - padding. -
- --space-8 - - 32px - Large section spacing. Page section margins.
- --space-10 - - 40px - Extra-large spacing. Major page sections.
- --space-12 - - 48px - Page-level vertical spacing between major sections.
- --space-16 - - 64px - Maximum spacing. Hero sections, page-level separation.
- --space-0_5 - - 2px - - Fine optical adjustment only. Never use for component padding. -

Radius

@@ -759,24 +627,6 @@ export const AllTokens: StoryObj = {
-

Pairing rules

-
    -
  • - Icon-to-text gap: --space-1 (4px) or --space-2 (8px) -
  • -
  • - Component inner padding: --space-3 (12px) to - --space-4 (16px) -
  • -
  • - Between components: --space-4 (16px) to --space-6 - (24px) -
  • -
  • - Page sections: --space-8 (32px) and above -
  • -
-

Dark mode shadows

Dark mode overrides use stronger opacity (0.20-0.40 vs 0.05-0.20). diff --git a/frontend/documentation/docs.scss b/frontend/documentation/docs.scss index 21c0443f7c32..7868e9860d6c 100644 --- a/frontend/documentation/docs.scss +++ b/frontend/documentation/docs.scss @@ -2,7 +2,7 @@ // Storybook Documentation Styles // ============================================================================= // Shared styles for documentation stories. Uses design token custom properties -// for spacing, radius, and colours — dogfooding the token system. +// for radius and colours — dogfooding the token system. // ============================================================================= // --------------------------------------------------------------------------- @@ -20,13 +20,13 @@ .docs-page__title { color: var(--color-text-default, #1a2634); - margin-bottom: var(--space-1, 4px); + margin-bottom: 4px; } .docs-page__description { color: var(--color-text-secondary, #656d7b); font-size: var(--font-body-size, 14px); - margin-bottom: var(--space-6, 24px); + margin-bottom: 24px; code { color: inherit; @@ -38,14 +38,14 @@ // --------------------------------------------------------------------------- .scale-row { - margin-bottom: var(--space-8, 32px); + margin-bottom: 32px; } .scale-row__title { color: var(--color-text-default, #1a2634); font-size: var(--font-h6-size, 16px); font-weight: var(--font-weight-bold, 700); - margin-bottom: var(--space-3, 12px); + margin-bottom: 12px; } .scale-row__swatches { @@ -57,7 +57,7 @@ display: flex; flex: 1; flex-direction: column; - gap: var(--space-1, 4px); + gap: 4px; min-width: 0; } @@ -82,20 +82,20 @@ // --------------------------------------------------------------------------- .token-group { - margin-bottom: var(--space-8, 32px); + margin-bottom: 32px; } .token-group__title { color: var(--color-text-default, #1a2634); font-size: var(--font-h6-size, 16px); font-weight: var(--font-weight-bold, 700); - margin-bottom: var(--space-3, 12px); + margin-bottom: 12px; } .token-group__header { border-bottom: 2px solid var(--color-border-strong, rgba(101, 109, 123, 0.24)); display: grid; - gap: var(--space-3, 12px); + gap: 12px; grid-template-columns: 48px 1fr 1fr; padding-bottom: 6px; } @@ -112,9 +112,9 @@ align-items: center; border-bottom: 1px solid var(--color-border-default, rgba(101, 109, 123, 0.16)); display: grid; - gap: var(--space-3, 12px); + gap: 12px; grid-template-columns: 48px 1fr 1fr; - padding: var(--space-2, 8px) 0; + padding: 8px 0; } .token-swatch__preview { @@ -143,7 +143,7 @@ align-items: center; display: flex; flex-direction: column; - gap: var(--space-1, 4px); + gap: 4px; } .cat-swatch__colour { @@ -163,18 +163,18 @@ .cat-grid { display: flex; flex-wrap: wrap; - gap: var(--space-4, 16px); + gap: 16px; } .cat-health-row { display: flex; - gap: var(--space-6, 24px); + gap: 24px; } .cat-health-item { align-items: center; display: flex; - gap: var(--space-2, 8px); + gap: 8px; } .cat-health-item__migration { @@ -188,7 +188,7 @@ .cat-note { color: var(--color-text-secondary, #656d7b); - margin-top: var(--space-4, 16px); + margin-top: 16px; } // --------------------------------------------------------------------------- @@ -196,13 +196,13 @@ // --------------------------------------------------------------------------- .icon-catalogue__header { - margin-bottom: var(--space-0_5, 2px); + margin-bottom: 2px; } .icon-catalogue__description { color: var(--color-text-secondary, #656d7b); font-size: var(--font-body-sm-size, 13px); - margin-bottom: var(--space-3, 12px); + margin-bottom: 12px; } .icon-catalogue__search { @@ -211,13 +211,13 @@ border-radius: var(--radius-md, 6px); color: var(--color-text-default, #1a2634); font-size: var(--font-body-sm-size, 13px); - margin-bottom: var(--space-4, 16px); - padding: var(--space-1, 4px) var(--space-3, 12px); + margin-bottom: 16px; + padding: 4px 12px; width: 100%; } .icon-catalogue__category { - margin-bottom: var(--space-4, 16px); + margin-bottom: 16px; } .icon-catalogue__category-label { @@ -226,14 +226,14 @@ font-size: var(--font-caption-size, 12px); font-weight: var(--font-weight-semibold, 600); letter-spacing: 0.04em; - margin-bottom: var(--space-2, 8px); - padding-bottom: var(--space-1, 4px); + margin-bottom: 8px; + padding-bottom: 4px; text-transform: uppercase; } .icon-catalogue__grid { display: grid; - gap: var(--space-2, 8px); + gap: 8px; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); } @@ -246,8 +246,8 @@ cursor: default; display: flex; flex-direction: column; - gap: var(--space-1, 4px); - padding: var(--space-3, 12px) var(--space-2, 8px) var(--space-2, 8px); + gap: 4px; + padding: 12px 8px 8px; transition: background var(--duration-fast, 100ms); } @@ -264,7 +264,7 @@ .icon-catalogue__empty { color: var(--color-text-tertiary, #9da4ae); - margin-top: var(--space-4, 16px); + margin-top: 16px; } // --------------------------------------------------------------------------- @@ -279,7 +279,7 @@ th, td { border-bottom: 1px solid var(--color-border-default, rgba(101, 109, 123, 0.16)); - padding: var(--space-2, 8px) var(--space-3, 12px); + padding: 8px 12px; text-align: left; } diff --git a/frontend/scripts/generate-tokens.mjs b/frontend/scripts/generate-tokens.mjs index d63497175cb9..99c9e6bdca9f 100644 --- a/frontend/scripts/generate-tokens.mjs +++ b/frontend/scripts/generate-tokens.mjs @@ -36,8 +36,8 @@ const esc = (s) => s.replace(/\\/g, '\\\\').replace(/'/g, "\\'") const lightVal = (e) => e.light ?? e.value const cap = (s) => s.charAt(0).toUpperCase() + s.slice(1) -const NON_COLOUR = ['space', 'radius', 'font', 'shadow', 'duration', 'easing'] -const DESCRIBED = ['space', 'radius', 'shadow', 'duration', 'easing'] +const NON_COLOUR = ['radius', 'font', 'shadow', 'duration', 'easing'] +const DESCRIBED = ['radius', 'shadow', 'duration', 'easing'] function makeCssVar(cssVarName, fallback) { if ( @@ -195,7 +195,6 @@ function generateTs() { '', 'export type TokenCategory = keyof typeof tokens', 'export type TokenName = keyof (typeof tokens)[C]', - 'export type SpaceScale = keyof typeof space', 'export type RadiusScale = keyof typeof radius', 'export type ShadowScale = keyof typeof shadow', '', @@ -251,15 +250,7 @@ function generateMcpStory() { ' >', ...tables, '', - "

Pairing rules

", - '
    ', - '
  • Icon-to-text gap: --space-1 (4px) or --space-2 (8px)
  • ', - '
  • Component inner padding: --space-3 (12px) to --space-4 (16px)
  • ', - '
  • Between components: --space-4 (16px) to --space-6 (24px)
  • ', - '
  • Page sections: --space-8 (32px) and above
  • ', - '
', - '', - '

Dark mode shadows

', + "

Dark mode shadows

", '

', ' Dark mode overrides use stronger opacity (0.20-0.40 vs 0.05-0.20).', ' Higher elevation surfaces should use lighter backgrounds', diff --git a/frontend/web/styles/_token-utilities.scss b/frontend/web/styles/_token-utilities.scss new file mode 100644 index 000000000000..4f80a4720a36 --- /dev/null +++ b/frontend/web/styles/_token-utilities.scss @@ -0,0 +1,109 @@ +// ============================================================================= +// Token Utility Classes +// Consumes the design tokens defined in _tokens.scss. +// Dark mode is automatic — CSS custom properties resolve differently under +// :root vs .dark, so no dark-prefixed classes are needed. +// ============================================================================= + +// --------------------------------------------------------------------------- +// Background colours (surface tokens) +// Usage: className="bg-surface-subtle" +// --------------------------------------------------------------------------- +.bg-surface-default { background-color: var(--color-surface-default); } +.bg-surface-subtle { background-color: var(--color-surface-subtle); } +.bg-surface-muted { background-color: var(--color-surface-muted); } +.bg-surface-emphasis { background-color: var(--color-surface-emphasis); } +.bg-surface-hover { background-color: var(--color-surface-hover); } +.bg-surface-active { background-color: var(--color-surface-active); } +.bg-surface-action { background-color: var(--color-surface-action); } +.bg-surface-action-hover { background-color: var(--color-surface-action-hover); } +.bg-surface-action-active { background-color: var(--color-surface-action-active); } +.bg-surface-action-subtle { background-color: var(--color-surface-action-subtle); } +.bg-surface-action-muted { background-color: var(--color-surface-action-muted); } +.bg-surface-danger { background-color: var(--color-surface-danger); } +.bg-surface-success { background-color: var(--color-surface-success); } +.bg-surface-warning { background-color: var(--color-surface-warning); } +.bg-surface-info { background-color: var(--color-surface-info); } + +// --------------------------------------------------------------------------- +// Text colours +// Usage: className="text-default" or className="text-secondary" +// Colour-only — typography (size, weight, line-height) is handled by +// components (, ) not utility classes. +// --------------------------------------------------------------------------- +.text-default { color: var(--color-text-default); } +.text-secondary { color: var(--color-text-secondary); } +.text-tertiary { color: var(--color-text-tertiary); } +.text-disabled { color: var(--color-text-disabled); } +.text-on-fill { color: var(--color-text-on-fill); } +.text-action { color: var(--color-text-action); } +.text-danger { color: var(--color-text-danger); } +.text-success { color: var(--color-text-success); } +.text-warning { color: var(--color-text-warning); } +.text-info { color: var(--color-text-info); } + +// --------------------------------------------------------------------------- +// Border colours +// Usage: className="border-default" or className="border-action" +// --------------------------------------------------------------------------- +.border-default { border-color: var(--color-border-default); } +.border-strong { border-color: var(--color-border-strong); } +.border-disabled { border-color: var(--color-border-disabled); } +.border-action { border-color: var(--color-border-action); } +.border-danger { border-color: var(--color-border-danger); } +.border-success { border-color: var(--color-border-success); } +.border-warning { border-color: var(--color-border-warning); } +.border-info { border-color: var(--color-border-info); } + +// --------------------------------------------------------------------------- +// Icon colours (fill + color for SVG icons) +// Usage: className="icon-secondary" on an icon wrapper +// --------------------------------------------------------------------------- +.icon-default { color: var(--color-icon-default); fill: var(--color-icon-default); } +.icon-secondary { color: var(--color-icon-secondary); fill: var(--color-icon-secondary); } +.icon-disabled { color: var(--color-icon-disabled); fill: var(--color-icon-disabled); } +.icon-action { color: var(--color-icon-action); fill: var(--color-icon-action); } +.icon-danger { color: var(--color-icon-danger); fill: var(--color-icon-danger); } +.icon-success { color: var(--color-icon-success); fill: var(--color-icon-success); } +.icon-warning { color: var(--color-icon-warning); fill: var(--color-icon-warning); } +.icon-info { color: var(--color-icon-info); fill: var(--color-icon-info); } + +// --------------------------------------------------------------------------- +// Border radius +// Usage: className="rounded-md" for 6px radius +// --------------------------------------------------------------------------- +.rounded-none { border-radius: var(--radius-none); } +.rounded-xs { border-radius: var(--radius-xs); } +.rounded-sm { border-radius: var(--radius-sm); } +.rounded-md { border-radius: var(--radius-md); } +.rounded-lg { border-radius: var(--radius-lg); } +.rounded-xl { border-radius: var(--radius-xl); } +.rounded-2xl { border-radius: var(--radius-2xl); } +.rounded-full { border-radius: var(--radius-full); } + +// --------------------------------------------------------------------------- +// Box shadow +// Usage: className="shadow-md" +// --------------------------------------------------------------------------- +.shadow-sm { box-shadow: var(--shadow-sm); } +.shadow-md { box-shadow: var(--shadow-md); } +.shadow-lg { box-shadow: var(--shadow-lg); } +.shadow-xl { box-shadow: var(--shadow-xl); } +.shadow-none { box-shadow: none; } + +// --------------------------------------------------------------------------- +// Font weight (standalone modifiers, not type scale) +// Usage: className="font-semibold" +// --------------------------------------------------------------------------- +.font-regular { font-weight: var(--font-weight-regular); } +.font-medium { font-weight: var(--font-weight-medium); } +.font-semibold { font-weight: var(--font-weight-semibold); } +.font-bold { font-weight: var(--font-weight-bold); } + +// --------------------------------------------------------------------------- +// Transitions +// Usage: className="transition-normal" +// --------------------------------------------------------------------------- +.transition-fast { transition-duration: var(--duration-fast); transition-timing-function: var(--easing-standard); } +.transition-normal { transition-duration: var(--duration-normal); transition-timing-function: var(--easing-standard); } +.transition-slow { transition-duration: var(--duration-slow); transition-timing-function: var(--easing-standard); } diff --git a/frontend/web/styles/_tokens.scss b/frontend/web/styles/_tokens.scss index 4766e489caeb..9279cf5f8859 100644 --- a/frontend/web/styles/_tokens.scss +++ b/frontend/web/styles/_tokens.scss @@ -53,20 +53,6 @@ --color-icon-success: #27ab95; --color-icon-warning: #ff9f43; - // Space - --space-0: 0px; - --space-0_5: 2px; - --space-1: 4px; - --space-10: 40px; - --space-12: 48px; - --space-16: 64px; - --space-2: 8px; - --space-3: 12px; - --space-4: 16px; - --space-5: 20px; - --space-6: 24px; - --space-8: 32px; - // Radius --radius-2xl: 18px; --radius-full: 9999px; diff --git a/frontend/web/styles/styles.scss b/frontend/web/styles/styles.scss index 1380ea685d9e..89e6a95a64ba 100644 --- a/frontend/web/styles/styles.scss +++ b/frontend/web/styles/styles.scss @@ -1,5 +1,6 @@ @import "variables"; @import "tokens"; +@import "token-utilities"; @import "3rdParty/index"; @import "components/index"; @import "flexbox/index"; From bef1c94f16e1425f3737776bfab3ea3689964cb6 Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Tue, 31 Mar 2026 18:10:06 -0300 Subject: [PATCH 06/11] feat(tokens): auto-generate utility classes from tokens.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add utility class generation to the codegen pipeline. When tokens.json changes, the pre-commit hook now generates _token-utilities.scss alongside _tokens.scss, tokens.ts, and the MCP reference story. Adding a new token to tokens.json automatically creates its utility class — no manual SCSS to write. - Add generateUtilities() to generate-tokens.mjs - Update lint-staged to auto-stage _token-utilities.scss - Update TokenMaintenance.mdx to document the 4-file pipeline Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/documentation/TokenMaintenance.mdx | 9 +- frontend/package.json | 2 +- frontend/scripts/generate-tokens.mjs | 89 +++++++++++++++- frontend/web/styles/_token-utilities.scss | 106 ++++++++------------ 4 files changed, 134 insertions(+), 72 deletions(-) diff --git a/frontend/documentation/TokenMaintenance.mdx b/frontend/documentation/TokenMaintenance.mdx index ad263b0f8699..4954be5a5a27 100644 --- a/frontend/documentation/TokenMaintenance.mdx +++ b/frontend/documentation/TokenMaintenance.mdx @@ -16,6 +16,7 @@ How to add, modify, or remove design tokens. Follow this process to keep the tok common/theme/tokens.jsonSingle source of truth. All token values and descriptions.Every token change starts here. common/theme/tokens.tsGenerated. TypeScript exports with var(--token, fallback) pattern.Never — auto-generated. web/styles/_tokens.scssGenerated. CSS custom properties with :root and .dark.Never — auto-generated. + web/styles/_token-utilities.scssGenerated. Utility classes consuming tokens (e.g. .bg-surface-subtle, .icon-secondary).Never — auto-generated. documentation/TokenReference.generated.stories.tsxGenerated. Flat JSX for Storybook MCP.Never — auto-generated. web/styles/_primitives.scssRaw colour scales (50-950).Only when adding a new colour family or adjusting a scale. @@ -33,7 +34,7 @@ Example: adding a new radius value `--radius-3xl` (24px). "3xl": { "cssVar": "--radius-3xl", "value": "24px", "description": "Extra-large container corners." } ``` -2. Run `git commit`. The pre-commit hook automatically regenerates `tokens.ts`, `_tokens.scss`, and `TokenReference.generated.stories.tsx`, then stages them. No manual step needed. +2. Run `git commit`. The pre-commit hook automatically regenerates `tokens.ts`, `_tokens.scss`, `_token-utilities.scss`, and `TokenReference.generated.stories.tsx`, then stages them. No manual step needed. The new radius utility class (e.g. `.rounded-3xl`) is created automatically. **Note:** Spacing is handled by Bootstrap utility classes (`gap-2`, `p-3`, `m-4`, etc.) — there are no custom spacing tokens. @@ -49,7 +50,7 @@ Example: adding `--color-text-link`. "link": { "cssVar": "--color-text-link", "light": "#6837fc", "dark": "#906af6" } ``` -2. Run `git commit`. The pre-commit hook regenerates all three files. The colour also appears in the Semantic Tokens visual story (auto-read from CSS vars). +2. Run `git commit`. The pre-commit hook regenerates all four files. The colour also appears in the Semantic Tokens visual story (auto-read from CSS vars) and a utility class is created (e.g. `.text-link`). --- @@ -70,7 +71,7 @@ Example: adding `--color-text-link`. ## The one rule -**Token values must only be defined in `tokens.json`.** No hardcoded hex values, no hardcoded pixel values anywhere else. SCSS files use `var(--token)`. TypeScript uses the exports from `common/theme/tokens`. Future Tailwind config references tokens via `var()` in `theme.extend`. +**Token values must only be defined in `tokens.json`.** No hardcoded hex values, no hardcoded pixel values anywhere else. SCSS files use `var(--token)`. TypeScript uses the exports from `common/theme/tokens`. Utility classes are auto-generated — use `className="bg-surface-subtle"` in JSX instead of writing SCSS. --- @@ -82,7 +83,7 @@ Example: adding `--color-text-link`. 1scripts/generate-tokens.mjs reads tokens.jsonPre-commit hook (lint-staged) when tokens.json is staged - 2Generates tokens.ts, _tokens.scss, and TokenReference.generated.stories.tsxAutomatic + 2Generates tokens.ts, _tokens.scss, _token-utilities.scss, and TokenReference.generated.stories.tsxAutomatic 3Generated files are auto-staged and included in the commitAutomatic 4Chromatic deploys the updated Storybook. Storybook MCP serves the flat JSX — AI agents can read all token data.CI (on PR) diff --git a/frontend/package.json b/frontend/package.json index e9a3a674b399..5dfc700b5e06 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -200,6 +200,6 @@ }, "lint-staged": { "*.{js,ts,tsx}": "eslint --fix", - "common/theme/tokens.json": "npm run generate:tokens && git add common/theme/tokens.ts web/styles/_tokens.scss documentation/TokenReference.generated.stories.tsx" + "common/theme/tokens.json": "npm run generate:tokens && git add common/theme/tokens.ts web/styles/_tokens.scss web/styles/_token-utilities.scss documentation/TokenReference.generated.stories.tsx" } } diff --git a/frontend/scripts/generate-tokens.mjs b/frontend/scripts/generate-tokens.mjs index 99c9e6bdca9f..05e82560f2c9 100644 --- a/frontend/scripts/generate-tokens.mjs +++ b/frontend/scripts/generate-tokens.mjs @@ -4,7 +4,8 @@ * Outputs: * 1. common/theme/tokens.ts — TypeScript exports * 2. web/styles/_tokens.scss — CSS custom properties - * 3. documentation/TokenReference.generated.stories.tsx — flat JSX for Storybook MCP + * 3. web/styles/_token-utilities.scss — utility classes consuming tokens + * 4. documentation/TokenReference.generated.stories.tsx — flat JSX for Storybook MCP * * Usage: * node scripts/generate-tokens.mjs @@ -271,15 +272,100 @@ function generateMcpStory() { return output.join('\n') } +function generateUtilities() { + const lines = [ + '// =============================================================================', + '// Token Utility Classes — AUTO-GENERATED from common/theme/tokens.json', + '// Do not edit manually. Run: npm run generate:tokens', + '// Dark mode is automatic — CSS custom properties resolve differently under', + '// :root vs .dark, so no dark-prefixed classes are needed.', + '// =============================================================================', + '', + ] + + // Colour utilities + const colourMappings = { + surface: { prefix: 'bg-surface', property: 'background-color' }, + text: { prefix: 'text', property: 'color' }, + border: { prefix: 'border', property: 'border-color' }, + icon: { prefix: 'icon', property: null }, // special: color + fill + } + + for (const [category, entries] of Object.entries(json.color)) { + const mapping = colourMappings[category] + if (!mapping) continue + + lines.push(`// ${cap(category)}`) + for (const [key, e] of sorted(entries)) { + const cls = `${mapping.prefix}-${key}` + if (category === 'icon') { + lines.push(`.${cls} { color: var(${e.cssVar}); fill: var(${e.cssVar}); }`) + } else { + lines.push(`.${cls} { ${mapping.property}: var(${e.cssVar}); }`) + } + } + lines.push('') + } + + // Radius utilities + if (json.radius) { + lines.push('// Radius') + for (const [key, e] of sorted(json.radius)) { + lines.push(`.rounded-${key} { border-radius: var(${e.cssVar}); }`) + } + lines.push('') + } + + // Shadow utilities + if (json.shadow) { + lines.push('// Shadow') + for (const [key, e] of sorted(json.shadow)) { + lines.push(`.shadow-${key} { box-shadow: var(${e.cssVar}); }`) + } + lines.push('.shadow-none { box-shadow: none; }') + lines.push('') + } + + // Font weight utilities + if (json.font) { + const weights = Object.entries(json.font).filter(([k]) => + k.startsWith('weight-'), + ) + if (weights.length) { + lines.push('// Font weight') + for (const [key, e] of weights.sort(([a], [b]) => a.localeCompare(b))) { + const cls = key.replace('weight-', '') + lines.push(`.font-${cls} { font-weight: var(${e.cssVar}); }`) + } + lines.push('') + } + } + + // Transition utilities + if (json.duration && json.easing) { + lines.push('// Transitions') + for (const [key, e] of sorted(json.duration)) { + lines.push( + `.transition-${key} { transition-duration: var(${e.cssVar}); transition-timing-function: var(--easing-standard); }`, + ) + } + lines.push('') + } + + return lines.join('\n') +} + // --------------------------------------------------------------------------- // Write and format // --------------------------------------------------------------------------- const scssPath = resolve(ROOT, 'web/styles/_tokens.scss') +const utilitiesPath = resolve(ROOT, 'web/styles/_token-utilities.scss') const tsPath = resolve(ROOT, 'common/theme/tokens.ts') const storyPath = resolve(ROOT, 'documentation/TokenReference.generated.stories.tsx') writeFileSync(scssPath, generateScss(), 'utf-8') +writeFileSync(utilitiesPath, generateUtilities(), 'utf-8') writeFileSync(tsPath, generateTs(), 'utf-8') writeFileSync(storyPath, generateMcpStory(), 'utf-8') @@ -302,6 +388,7 @@ const nonColorCount = NON_COLOUR.reduce( ) console.log('Generated from tokens.json:') console.log(` ${scssPath}`) +console.log(` ${utilitiesPath}`) console.log(` ${tsPath}`) console.log(` ${storyPath}`) console.log( diff --git a/frontend/web/styles/_token-utilities.scss b/frontend/web/styles/_token-utilities.scss index 4f80a4720a36..8e2cc0ccfdfc 100644 --- a/frontend/web/styles/_token-utilities.scss +++ b/frontend/web/styles/_token-utilities.scss @@ -1,109 +1,83 @@ // ============================================================================= -// Token Utility Classes -// Consumes the design tokens defined in _tokens.scss. +// Token Utility Classes — AUTO-GENERATED from common/theme/tokens.json +// Do not edit manually. Run: npm run generate:tokens // Dark mode is automatic — CSS custom properties resolve differently under // :root vs .dark, so no dark-prefixed classes are needed. // ============================================================================= -// --------------------------------------------------------------------------- -// Background colours (surface tokens) -// Usage: className="bg-surface-subtle" -// --------------------------------------------------------------------------- -.bg-surface-default { background-color: var(--color-surface-default); } -.bg-surface-subtle { background-color: var(--color-surface-subtle); } -.bg-surface-muted { background-color: var(--color-surface-muted); } -.bg-surface-emphasis { background-color: var(--color-surface-emphasis); } -.bg-surface-hover { background-color: var(--color-surface-hover); } -.bg-surface-active { background-color: var(--color-surface-active); } +// Surface .bg-surface-action { background-color: var(--color-surface-action); } -.bg-surface-action-hover { background-color: var(--color-surface-action-hover); } .bg-surface-action-active { background-color: var(--color-surface-action-active); } -.bg-surface-action-subtle { background-color: var(--color-surface-action-subtle); } +.bg-surface-action-hover { background-color: var(--color-surface-action-hover); } .bg-surface-action-muted { background-color: var(--color-surface-action-muted); } +.bg-surface-action-subtle { background-color: var(--color-surface-action-subtle); } +.bg-surface-active { background-color: var(--color-surface-active); } .bg-surface-danger { background-color: var(--color-surface-danger); } +.bg-surface-default { background-color: var(--color-surface-default); } +.bg-surface-emphasis { background-color: var(--color-surface-emphasis); } +.bg-surface-hover { background-color: var(--color-surface-hover); } +.bg-surface-info { background-color: var(--color-surface-info); } +.bg-surface-muted { background-color: var(--color-surface-muted); } +.bg-surface-subtle { background-color: var(--color-surface-subtle); } .bg-surface-success { background-color: var(--color-surface-success); } .bg-surface-warning { background-color: var(--color-surface-warning); } -.bg-surface-info { background-color: var(--color-surface-info); } -// --------------------------------------------------------------------------- -// Text colours -// Usage: className="text-default" or className="text-secondary" -// Colour-only — typography (size, weight, line-height) is handled by -// components (, ) not utility classes. -// --------------------------------------------------------------------------- +// Text +.text-action { color: var(--color-text-action); } +.text-danger { color: var(--color-text-danger); } .text-default { color: var(--color-text-default); } -.text-secondary { color: var(--color-text-secondary); } -.text-tertiary { color: var(--color-text-tertiary); } .text-disabled { color: var(--color-text-disabled); } +.text-info { color: var(--color-text-info); } .text-on-fill { color: var(--color-text-on-fill); } -.text-action { color: var(--color-text-action); } -.text-danger { color: var(--color-text-danger); } +.text-secondary { color: var(--color-text-secondary); } .text-success { color: var(--color-text-success); } +.text-tertiary { color: var(--color-text-tertiary); } .text-warning { color: var(--color-text-warning); } -.text-info { color: var(--color-text-info); } -// --------------------------------------------------------------------------- -// Border colours -// Usage: className="border-default" or className="border-action" -// --------------------------------------------------------------------------- -.border-default { border-color: var(--color-border-default); } -.border-strong { border-color: var(--color-border-strong); } -.border-disabled { border-color: var(--color-border-disabled); } +// Border .border-action { border-color: var(--color-border-action); } .border-danger { border-color: var(--color-border-danger); } +.border-default { border-color: var(--color-border-default); } +.border-disabled { border-color: var(--color-border-disabled); } +.border-info { border-color: var(--color-border-info); } +.border-strong { border-color: var(--color-border-strong); } .border-success { border-color: var(--color-border-success); } .border-warning { border-color: var(--color-border-warning); } -.border-info { border-color: var(--color-border-info); } -// --------------------------------------------------------------------------- -// Icon colours (fill + color for SVG icons) -// Usage: className="icon-secondary" on an icon wrapper -// --------------------------------------------------------------------------- -.icon-default { color: var(--color-icon-default); fill: var(--color-icon-default); } -.icon-secondary { color: var(--color-icon-secondary); fill: var(--color-icon-secondary); } -.icon-disabled { color: var(--color-icon-disabled); fill: var(--color-icon-disabled); } +// Icon .icon-action { color: var(--color-icon-action); fill: var(--color-icon-action); } .icon-danger { color: var(--color-icon-danger); fill: var(--color-icon-danger); } +.icon-default { color: var(--color-icon-default); fill: var(--color-icon-default); } +.icon-disabled { color: var(--color-icon-disabled); fill: var(--color-icon-disabled); } +.icon-info { color: var(--color-icon-info); fill: var(--color-icon-info); } +.icon-secondary { color: var(--color-icon-secondary); fill: var(--color-icon-secondary); } .icon-success { color: var(--color-icon-success); fill: var(--color-icon-success); } .icon-warning { color: var(--color-icon-warning); fill: var(--color-icon-warning); } -.icon-info { color: var(--color-icon-info); fill: var(--color-icon-info); } -// --------------------------------------------------------------------------- -// Border radius -// Usage: className="rounded-md" for 6px radius -// --------------------------------------------------------------------------- +// Radius +.rounded-2xl { border-radius: var(--radius-2xl); } +.rounded-full { border-radius: var(--radius-full); } +.rounded-lg { border-radius: var(--radius-lg); } +.rounded-md { border-radius: var(--radius-md); } .rounded-none { border-radius: var(--radius-none); } -.rounded-xs { border-radius: var(--radius-xs); } .rounded-sm { border-radius: var(--radius-sm); } -.rounded-md { border-radius: var(--radius-md); } -.rounded-lg { border-radius: var(--radius-lg); } .rounded-xl { border-radius: var(--radius-xl); } -.rounded-2xl { border-radius: var(--radius-2xl); } -.rounded-full { border-radius: var(--radius-full); } +.rounded-xs { border-radius: var(--radius-xs); } -// --------------------------------------------------------------------------- -// Box shadow -// Usage: className="shadow-md" -// --------------------------------------------------------------------------- -.shadow-sm { box-shadow: var(--shadow-sm); } -.shadow-md { box-shadow: var(--shadow-md); } +// Shadow .shadow-lg { box-shadow: var(--shadow-lg); } +.shadow-md { box-shadow: var(--shadow-md); } +.shadow-sm { box-shadow: var(--shadow-sm); } .shadow-xl { box-shadow: var(--shadow-xl); } .shadow-none { box-shadow: none; } -// --------------------------------------------------------------------------- -// Font weight (standalone modifiers, not type scale) -// Usage: className="font-semibold" -// --------------------------------------------------------------------------- -.font-regular { font-weight: var(--font-weight-regular); } +// Font weight +.font-bold { font-weight: var(--font-weight-bold); } .font-medium { font-weight: var(--font-weight-medium); } +.font-regular { font-weight: var(--font-weight-regular); } .font-semibold { font-weight: var(--font-weight-semibold); } -.font-bold { font-weight: var(--font-weight-bold); } -// --------------------------------------------------------------------------- // Transitions -// Usage: className="transition-normal" -// --------------------------------------------------------------------------- .transition-fast { transition-duration: var(--duration-fast); transition-timing-function: var(--easing-standard); } .transition-normal { transition-duration: var(--duration-normal); transition-timing-function: var(--easing-standard); } .transition-slow { transition-duration: var(--duration-slow); transition-timing-function: var(--easing-standard); } From 34d2d5435e01be02f0063c4f0e28f622c0c1e372 Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Tue, 31 Mar 2026 18:38:13 -0300 Subject: [PATCH 07/11] fix(charts): use semantic colour tokens for dark mode (#6889) Replace hardcoded hex colours in Recharts components with resolved semantic design tokens via useChartTheme hook. Fixes chart axis labels, grid lines, tick fills, and tooltip text being invisible or low contrast in dark mode. Also updates recharts tooltip SCSS to use CSS custom properties instead of .dark class overrides. Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/common/hooks/useChartTheme.ts | 41 +++++++++ .../FeatureNavTab/FeatureAnalytics.tsx | 12 +-- .../ExperimentResultsTab.tsx | 76 +++++++++------- .../usage/OrganisationUsage.container.tsx | 12 +-- .../usage/components/SingleSDKLabelsChart.tsx | 90 ++++++++++--------- .../components/UsageTrendsChart.tsx | 12 +-- frontend/web/styles/project/_tooltips.scss | 28 +++--- 7 files changed, 169 insertions(+), 102 deletions(-) create mode 100644 frontend/common/hooks/useChartTheme.ts diff --git a/frontend/common/hooks/useChartTheme.ts b/frontend/common/hooks/useChartTheme.ts new file mode 100644 index 000000000000..7185514cfaae --- /dev/null +++ b/frontend/common/hooks/useChartTheme.ts @@ -0,0 +1,41 @@ +function resolveToken(cssVar: string): string { + return getComputedStyle(document.documentElement) + .getPropertyValue(cssVar) + .trim() +} + +type ChartTheme = { + gridStroke: string + tickFill: string + axisStroke: string + tooltipLabelColour: string + lineInfo: string + lineSuccess: string + lineWarning: string + lineDanger: string + lineAction: string + variantColours: string[] + winnerColour: string +} + +export default function useChartTheme(): ChartTheme { + const action = resolveToken('--color-text-action') + const info = resolveToken('--color-text-info') + const success = resolveToken('--color-text-success') + const warning = resolveToken('--color-text-warning') + const danger = resolveToken('--color-text-danger') + + return { + axisStroke: resolveToken('--color-border-default'), + gridStroke: resolveToken('--color-border-default'), + lineAction: action, + lineDanger: danger, + lineInfo: info, + lineSuccess: success, + lineWarning: warning, + tickFill: resolveToken('--color-text-secondary'), + tooltipLabelColour: resolveToken('--color-text-default'), + variantColours: [action, info, warning, danger, success], + winnerColour: success, + } +} diff --git a/frontend/web/components/feature-page/FeatureNavTab/FeatureAnalytics.tsx b/frontend/web/components/feature-page/FeatureNavTab/FeatureAnalytics.tsx index 24a97f9b3cdc..20121c4bb2cd 100644 --- a/frontend/web/components/feature-page/FeatureNavTab/FeatureAnalytics.tsx +++ b/frontend/web/components/feature-page/FeatureNavTab/FeatureAnalytics.tsx @@ -15,6 +15,7 @@ import EnvironmentTagSelect from 'components/EnvironmentTagSelect' import { useGetFeatureAnalyticsQuery } from 'common/services/useFeatureAnalytics' import { useGetEnvironmentsQuery } from 'common/services/useEnvironment' import Utils from 'common/utils/utils' +import useChartTheme from 'common/hooks/useChartTheme' type FlagAnalyticsType = { projectId: string @@ -27,6 +28,7 @@ const FlagAnalytics: FC = ({ featureId, projectId, }) => { + const chartTheme = useChartTheme() const [environmentIds, setEnvironmentIds] = useState(defaultEnvironmentIds) const { data, isLoading } = useGetFeatureAnalyticsQuery( { @@ -98,17 +100,17 @@ const FlagAnalytics: FC = ({ height={100} angle={-90} textAnchor='end' - tick={{ dx: -4, fill: '#656D7B' }} + tick={{ dx: -4, fill: chartTheme.tickFill }} tickLine={false} - axisLine={{ stroke: '#656D7B' }} + axisLine={{ stroke: chartTheme.axisStroke }} /> {sortBy(environmentIds, (id) => environments?.results?.findIndex((env) => `${env.id}` === id), diff --git a/frontend/web/components/modals/create-experiment/ExperimentResultsTab.tsx b/frontend/web/components/modals/create-experiment/ExperimentResultsTab.tsx index a38bf165c2ba..1927b2e3af3a 100644 --- a/frontend/web/components/modals/create-experiment/ExperimentResultsTab.tsx +++ b/frontend/web/components/modals/create-experiment/ExperimentResultsTab.tsx @@ -14,23 +14,18 @@ import { YAxis, } from 'recharts' import { useGetExperimentResultsQuery } from 'common/services/useExperimentResults' +import useChartTheme from 'common/hooks/useChartTheme' -const WINNER_COLOUR = 'rgba(22, 163, 74, 0.8)' -const VARIANT_COLOURS = [ - 'rgba(37, 99, 235, 0.8)', - 'rgba(234, 88, 12, 0.8)', - 'rgba(124, 58, 237, 0.8)', - 'rgba(8, 145, 178, 0.8)', - 'rgba(219, 39, 119, 0.8)', - 'rgba(220, 38, 38, 0.8)', - 'rgba(132, 204, 22, 0.8)', - 'rgba(245, 158, 11, 0.8)', -] - -const getVariantColour = (variant: string, index: number, winner?: string) => +const getVariantColour = ( + variant: string, + index: number, + variantColours: string[], + winnerColour: string, + winner?: string, +) => variant === winner - ? WINNER_COLOUR - : VARIANT_COLOURS[index % VARIANT_COLOURS.length] + ? winnerColour + : variantColours[index % variantColours.length] type ExperimentResultsTabProps = { environmentId: string @@ -41,6 +36,7 @@ const ExperimentResultsTab: FC = ({ environmentId, featureName, }) => { + const chartTheme = useChartTheme() const { data, error, isLoading } = useGetExperimentResultsQuery({ environmentId, featureName, @@ -78,31 +74,37 @@ const ExperimentResultsTab: FC = ({

Conversion Rate (%)
- + `${v.toFixed(1)}%`} /> {data.variants.map( (entry: { variant: string }, index: number) => ( ), )} @@ -116,22 +118,30 @@ const ExperimentResultsTab: FC = ({
Evaluations & Conversions
- + - - + +
@@ -156,7 +166,13 @@ const ExperimentResultsTab: FC = ({ {chanceToWinData.map((entry, index) => ( ))} diff --git a/frontend/web/components/organisation-settings/usage/OrganisationUsage.container.tsx b/frontend/web/components/organisation-settings/usage/OrganisationUsage.container.tsx index ee79c463c04c..ef97c6e36717 100644 --- a/frontend/web/components/organisation-settings/usage/OrganisationUsage.container.tsx +++ b/frontend/web/components/organisation-settings/usage/OrganisationUsage.container.tsx @@ -16,6 +16,7 @@ import { ValueType, } from 'recharts/types/component/DefaultTooltipContent' import { AggregateUsageDataItem } from 'common/types/responses' +import useChartTheme from 'common/hooks/useChartTheme' import UsageAPIDefinitions from './components/UsageAPIDefinitions' type OrganisationUsageProps = { @@ -31,6 +32,7 @@ const OrganisationUsage: FC = ({ isError, selection, }) => { + const chartTheme = useChartTheme() return chartData || isError ? ( <> {isError || chartData?.length === 0 ? ( @@ -42,7 +44,7 @@ const OrganisationUsage: FC = ({ ) : ( - + = ({ angle={-90} textAnchor='end' tickFormatter={(v) => moment(v).format('D MMM')} - axisLine={{ stroke: '#EFF1F4' }} - tick={{ dx: -4, fill: '#656D7B' }} + axisLine={{ stroke: chartTheme.axisStroke }} + tick={{ dx: -4, fill: chartTheme.tickFill }} tickLine={false} /> <_Tooltip cursor={{ fill: 'transparent' }} diff --git a/frontend/web/components/organisation-settings/usage/components/SingleSDKLabelsChart.tsx b/frontend/web/components/organisation-settings/usage/components/SingleSDKLabelsChart.tsx index a83f447ff378..5981f18fecf8 100644 --- a/frontend/web/components/organisation-settings/usage/components/SingleSDKLabelsChart.tsx +++ b/frontend/web/components/organisation-settings/usage/components/SingleSDKLabelsChart.tsx @@ -17,6 +17,7 @@ import { } from 'recharts/types/component/DefaultTooltipContent' import { ChartDataPoint } from 'components/organisation-settings/usage/OrganisationUsageMetrics.container' import Utils from 'common/utils/utils' +import useChartTheme from 'common/hooks/useChartTheme' interface UsageChartProps { colours: string[] @@ -34,50 +35,53 @@ const UsageChart: React.FC = ({ title, userAgents = [], userAgentsColorMap, -}) => ( -
-
{title}
- - - - <_Tooltip - cursor={{ fill: 'transparent' }} - content={} - /> - moment(v).format('D MMM')} - interval={data?.length > 31 ? 7 : 0} - textAnchor='end' - axisLine={{ stroke: '#EFF1F4' }} - tick={{ dx: -4, fill: '#656D7B' }} - angle={-90} - tickLine={false} - allowDataOverflow={false} - /> - - - {userAgents?.map((userAgent, index) => ( - { + const chartTheme = useChartTheme() + return ( +
+
{title}
+ + + + <_Tooltip + cursor={{ fill: 'transparent' }} + content={} + /> + moment(v).format('D MMM')} + interval={data?.length > 31 ? 7 : 0} + textAnchor='end' + axisLine={{ stroke: chartTheme.axisStroke }} + tick={{ dx: -4, fill: chartTheme.tickFill }} + angle={-90} + tickLine={false} + allowDataOverflow={false} + /> + - ))} - - -
-) + + {userAgents?.map((userAgent, index) => ( + + ))} +
+
+
+ ) +} const RechartsTooltip: FC> = ({ active, diff --git a/frontend/web/components/pages/admin-dashboard/components/UsageTrendsChart.tsx b/frontend/web/components/pages/admin-dashboard/components/UsageTrendsChart.tsx index 336a818ea729..8b3383d14ee7 100644 --- a/frontend/web/components/pages/admin-dashboard/components/UsageTrendsChart.tsx +++ b/frontend/web/components/pages/admin-dashboard/components/UsageTrendsChart.tsx @@ -13,6 +13,7 @@ import moment from 'moment' import { UsageTrend } from 'common/types/responses' import Utils from 'common/utils/utils' import Card from 'components/Card' +import useChartTheme from 'common/hooks/useChartTheme' interface UsageTrendsChartProps { trends: UsageTrend[] @@ -20,6 +21,7 @@ interface UsageTrendsChartProps { } const UsageTrendsChart: FC = ({ days = 30, trends }) => { + const chartTheme = useChartTheme() const chartData = trends.map((trend) => ({ ...trend, date: moment(trend.date).format('MMM DD'), @@ -37,12 +39,12 @@ const UsageTrendsChart: FC = ({ days = 30, trends }) => { Utils.numberWithCommas(value)} - tick={{ fontSize: 12 }} + tick={{ fill: chartTheme.tickFill, fontSize: 12 }} /> Utils.numberWithCommas(value)} @@ -52,7 +54,7 @@ const UsageTrendsChart: FC = ({ days = 30, trends }) => { type='monotone' dataKey='api_calls' name='API Calls' - stroke='#0AADDF' + stroke={chartTheme.lineInfo} strokeWidth={2} dot={false} /> @@ -60,7 +62,7 @@ const UsageTrendsChart: FC = ({ days = 30, trends }) => { type='monotone' dataKey='flag_evaluations' name='Flag Evaluations' - stroke='#27AB95' + stroke={chartTheme.lineSuccess} strokeWidth={2} dot={false} /> @@ -68,7 +70,7 @@ const UsageTrendsChart: FC = ({ days = 30, trends }) => { type='monotone' dataKey='identity_requests' name='Identity Requests' - stroke='#FF9F43' + stroke={chartTheme.lineWarning} strokeWidth={2} dot={false} /> diff --git a/frontend/web/styles/project/_tooltips.scss b/frontend/web/styles/project/_tooltips.scss index eec5071b455c..3b8a931e2657 100644 --- a/frontend/web/styles/project/_tooltips.scss +++ b/frontend/web/styles/project/_tooltips.scss @@ -40,8 +40,17 @@ $shadow-dark: 0 4px 4px 0 #00000029; } .recharts-tooltip { - background-color: $text-icon-light; + background-color: var(--color-surface-default); + border: 1px solid var(--color-border-strong); border-radius: $border-radius-lg; + + .text-muted { + color: var(--color-text-secondary) !important; + } +} + +.recharts-tooltip-header { + color: var(--color-text-default); } .yAxis { @@ -54,20 +63,11 @@ $shadow-dark: 0 4px 4px 0 #00000029; font-size: $font-caption-xs; line-height: $line-height-xxsm; font-weight: 500; + color: var(--color-text-secondary); } -.dark { - .recharts-tooltip-header { - color: $body-color; - } - - .xAxis { - color: $text-icon-light-grey; - } - - .recharts-wrapper { - line { - stroke: $bg-dark100; - } +.recharts-wrapper { + line { + stroke: var(--color-border-default); } } From f905564fa56cc3f06efcf8adf9836e7787cd9fb2 Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Tue, 31 Mar 2026 18:40:47 -0300 Subject: [PATCH 08/11] refactor(charts): make useChartTheme generic Remove experiment-specific winnerColour, rename variantColours to palette. Consumers pick from semantic colours directly (e.g. lineSuccess for winner). Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/common/hooks/useChartTheme.ts | 6 ++---- .../ExperimentResultsTab.tsx | 19 ++++++++----------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/frontend/common/hooks/useChartTheme.ts b/frontend/common/hooks/useChartTheme.ts index 7185514cfaae..9c17112eeea9 100644 --- a/frontend/common/hooks/useChartTheme.ts +++ b/frontend/common/hooks/useChartTheme.ts @@ -14,8 +14,7 @@ type ChartTheme = { lineWarning: string lineDanger: string lineAction: string - variantColours: string[] - winnerColour: string + palette: string[] } export default function useChartTheme(): ChartTheme { @@ -33,9 +32,8 @@ export default function useChartTheme(): ChartTheme { lineInfo: info, lineSuccess: success, lineWarning: warning, + palette: [action, info, warning, danger, success], tickFill: resolveToken('--color-text-secondary'), tooltipLabelColour: resolveToken('--color-text-default'), - variantColours: [action, info, warning, danger, success], - winnerColour: success, } } diff --git a/frontend/web/components/modals/create-experiment/ExperimentResultsTab.tsx b/frontend/web/components/modals/create-experiment/ExperimentResultsTab.tsx index 1927b2e3af3a..0f38fdaf237b 100644 --- a/frontend/web/components/modals/create-experiment/ExperimentResultsTab.tsx +++ b/frontend/web/components/modals/create-experiment/ExperimentResultsTab.tsx @@ -19,13 +19,10 @@ import useChartTheme from 'common/hooks/useChartTheme' const getVariantColour = ( variant: string, index: number, - variantColours: string[], + palette: string[], winnerColour: string, winner?: string, -) => - variant === winner - ? winnerColour - : variantColours[index % variantColours.length] +) => (variant === winner ? winnerColour : palette[index % palette.length]) type ExperimentResultsTabProps = { environmentId: string @@ -101,8 +98,8 @@ const ExperimentResultsTab: FC = ({ fill={getVariantColour( entry.variant, index, - chartTheme.variantColours, - chartTheme.winnerColour, + chartTheme.palette, + chartTheme.lineSuccess, winner, )} /> @@ -134,12 +131,12 @@ const ExperimentResultsTab: FC = ({
@@ -169,8 +166,8 @@ const ExperimentResultsTab: FC = ({ fill={getVariantColour( entry.variant, index, - chartTheme.variantColours, - chartTheme.winnerColour, + chartTheme.palette, + chartTheme.lineSuccess, winner, )} /> From 9bddc025b1201c28adc3503a5d025cf4d69f4454 Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Tue, 31 Mar 2026 19:22:31 -0300 Subject: [PATCH 09/11] feat(charts): add shared ChartTooltip component with colour swatch Extract the duplicated tooltip pattern (colour swatch + readable text) into a reusable ChartTooltip component. Add CSS classes for tooltip swatch and Recharts default tooltip dark mode styling using semantic tokens. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../web/components/charts/ChartTooltip.tsx | 47 +++++++++++++++++++ frontend/web/styles/project/_tooltips.scss | 17 +++++++ 2 files changed, 64 insertions(+) create mode 100644 frontend/web/components/charts/ChartTooltip.tsx diff --git a/frontend/web/components/charts/ChartTooltip.tsx b/frontend/web/components/charts/ChartTooltip.tsx new file mode 100644 index 000000000000..2378fc6780d7 --- /dev/null +++ b/frontend/web/components/charts/ChartTooltip.tsx @@ -0,0 +1,47 @@ +import React, { FC } from 'react' +import { TooltipProps } from 'recharts' +import { + NameType, + ValueType, +} from 'recharts/types/component/DefaultTooltipContent' +import Utils from 'common/utils/utils' + +type ChartTooltipProps = TooltipProps & { + formatLabel?: (label: string) => string +} + +const ChartTooltip: FC = ({ + active, + formatLabel, + label, + payload, +}) => { + if (!active || !payload || payload.length === 0) { + return null + } + + const displayLabel = formatLabel ? formatLabel(String(label)) : String(label) + + return ( +
+
+ {displayLabel} +
+
+ {payload.map((entry) => ( + + + + {entry.name || entry.dataKey}:{' '} + {Utils.numberWithCommas(Number(entry.value))} + + + ))} +
+ ) +} + +export default ChartTooltip diff --git a/frontend/web/styles/project/_tooltips.scss b/frontend/web/styles/project/_tooltips.scss index 3b8a931e2657..4735bd53b05b 100644 --- a/frontend/web/styles/project/_tooltips.scss +++ b/frontend/web/styles/project/_tooltips.scss @@ -49,10 +49,27 @@ $shadow-dark: 0 4px 4px 0 #00000029; } } +.recharts-default-tooltip { + background-color: var(--color-surface-default) !important; + border: 1px solid var(--color-border-strong) !important; + border-radius: $border-radius-lg; + + .recharts-tooltip-label { + color: var(--color-text-default) !important; + } +} + .recharts-tooltip-header { color: var(--color-text-default); } +.recharts-tooltip-swatch { + display: inline-block; + width: 16px; + height: 16px; + border-radius: 2px; +} + .yAxis { font-size: $font-caption-sm; line-height: $line-height-xxsm; From 5d7109403de06ac72e7805a5708ebe3f32bb5375 Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Tue, 31 Mar 2026 19:22:39 -0300 Subject: [PATCH 10/11] refactor(charts): remove tooltipLabelColour from useChartTheme Tooltip styling is now handled entirely via CSS classes targeting .recharts-tooltip and .recharts-default-tooltip, so the JS-resolved tooltip colour is no longer needed. Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/common/hooks/useChartTheme.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/common/hooks/useChartTheme.ts b/frontend/common/hooks/useChartTheme.ts index 9c17112eeea9..2feec0aecf1a 100644 --- a/frontend/common/hooks/useChartTheme.ts +++ b/frontend/common/hooks/useChartTheme.ts @@ -8,7 +8,6 @@ type ChartTheme = { gridStroke: string tickFill: string axisStroke: string - tooltipLabelColour: string lineInfo: string lineSuccess: string lineWarning: string @@ -34,6 +33,5 @@ export default function useChartTheme(): ChartTheme { lineWarning: warning, palette: [action, info, warning, danger, success], tickFill: resolveToken('--color-text-secondary'), - tooltipLabelColour: resolveToken('--color-text-default'), } } From 34560a8fc21b205fffa3879f7759dc39ff9b81ca Mon Sep 17 00:00:00 2001 From: Talisson Costa Date: Tue, 31 Mar 2026 19:22:49 -0300 Subject: [PATCH 11/11] refactor(charts): use shared ChartTooltip across all chart components Replace duplicated inline RechartsTooltip components in OrganisationUsage and SingleSDKLabelsChart, and replace Recharts default tooltips in FeatureAnalytics, UsageTrendsChart, and ExperimentResultsTab with the shared ChartTooltip. This gives all charts a consistent swatch + readable text tooltip pattern that works in both light and dark mode. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../FeatureNavTab/FeatureAnalytics.tsx | 3 +- .../ExperimentResultsTab.tsx | 24 +++- .../usage/OrganisationUsage.container.tsx | 111 +----------------- .../usage/components/SingleSDKLabelsChart.tsx | 51 +------- .../components/UsageTrendsChart.tsx | 5 +- 5 files changed, 33 insertions(+), 161 deletions(-) diff --git a/frontend/web/components/feature-page/FeatureNavTab/FeatureAnalytics.tsx b/frontend/web/components/feature-page/FeatureNavTab/FeatureAnalytics.tsx index 20121c4bb2cd..29c87df2dc28 100644 --- a/frontend/web/components/feature-page/FeatureNavTab/FeatureAnalytics.tsx +++ b/frontend/web/components/feature-page/FeatureNavTab/FeatureAnalytics.tsx @@ -16,6 +16,7 @@ import { useGetFeatureAnalyticsQuery } from 'common/services/useFeatureAnalytics import { useGetEnvironmentsQuery } from 'common/services/useEnvironment' import Utils from 'common/utils/utils' import useChartTheme from 'common/hooks/useChartTheme' +import ChartTooltip from 'components/charts/ChartTooltip' type FlagAnalyticsType = { projectId: string @@ -110,7 +111,7 @@ const FlagAnalytics: FC = ({ /> } /> {sortBy(environmentIds, (id) => environments?.results?.findIndex((env) => `${env.id}` === id), diff --git a/frontend/web/components/modals/create-experiment/ExperimentResultsTab.tsx b/frontend/web/components/modals/create-experiment/ExperimentResultsTab.tsx index 0f38fdaf237b..f3ff668f79df 100644 --- a/frontend/web/components/modals/create-experiment/ExperimentResultsTab.tsx +++ b/frontend/web/components/modals/create-experiment/ExperimentResultsTab.tsx @@ -15,6 +15,7 @@ import { } from 'recharts' import { useGetExperimentResultsQuery } from 'common/services/useExperimentResults' import useChartTheme from 'common/hooks/useChartTheme' +import ChartTooltip from 'components/charts/ChartTooltip' const getVariantColour = ( variant: string, @@ -47,7 +48,16 @@ const ExperimentResultsTab: FC = ({ ) } - if (error || !data?.variants?.length) { + if (!data?.variants?.length) { + return ( +
+ Experiment is on-going, data should start appearing as soon as your + users use it. +
+ ) + } + + if (error) { return (
Experiment is on-going, data should start appearing as soon as your @@ -83,7 +93,10 @@ const ExperimentResultsTab: FC = ({ tickLine={false} axisLine={{ stroke: chartTheme.axisStroke }} /> - + } + /> = ({ tickLine={false} axisLine={{ stroke: chartTheme.axisStroke }} /> - + } + /> = ({ /> ))} - + } /> diff --git a/frontend/web/components/organisation-settings/usage/OrganisationUsage.container.tsx b/frontend/web/components/organisation-settings/usage/OrganisationUsage.container.tsx index ef97c6e36717..6ed42c673533 100644 --- a/frontend/web/components/organisation-settings/usage/OrganisationUsage.container.tsx +++ b/frontend/web/components/organisation-settings/usage/OrganisationUsage.container.tsx @@ -1,4 +1,3 @@ -import Utils from 'common/utils/utils' import React, { FC } from 'react' import { Bar, @@ -8,15 +7,11 @@ import { XAxis, YAxis, CartesianGrid, - TooltipProps, } from 'recharts' import moment from 'moment' -import { - NameType, - ValueType, -} from 'recharts/types/component/DefaultTooltipContent' import { AggregateUsageDataItem } from 'common/types/responses' import useChartTheme from 'common/hooks/useChartTheme' +import ChartTooltip from 'components/charts/ChartTooltip' import UsageAPIDefinitions from './components/UsageAPIDefinitions' type OrganisationUsageProps = { @@ -66,7 +61,9 @@ const OrganisationUsage: FC = ({ /> <_Tooltip cursor={{ fill: 'transparent' }} - content={} + content={ + moment(v).format('D MMM')} /> + } /> {selection.includes('Flags') && ( @@ -108,104 +105,4 @@ const OrganisationUsage: FC = ({ ) } -const RechartsTooltip: FC> = ({ - active, - label, - payload, -}) => { - if (!active || !payload || payload.length === 0) { - return null - } - - return ( -
-
- {moment(label).format('D MMM')} -
-
- {payload.map((el: any) => { - const { dataKey, fill, payload } = el - switch (dataKey) { - case 'traits': { - return ( - - - - Traits: {Utils.numberWithCommas(payload[dataKey])} - - - ) - } - case 'flags': { - return ( - - - - Flags: {Utils.numberWithCommas(payload[dataKey])} - - - ) - } - case 'identities': { - return ( - - - - Identities: {Utils.numberWithCommas(payload[dataKey])} - - - ) - } - case 'environment_document': { - return ( - - - - Environment Document:{' '} - {Utils.numberWithCommas(payload[dataKey])} - - - ) - } - default: { - return null - } - } - })} -
- ) -} - export default OrganisationUsage diff --git a/frontend/web/components/organisation-settings/usage/components/SingleSDKLabelsChart.tsx b/frontend/web/components/organisation-settings/usage/components/SingleSDKLabelsChart.tsx index 5981f18fecf8..41ed5f15725c 100644 --- a/frontend/web/components/organisation-settings/usage/components/SingleSDKLabelsChart.tsx +++ b/frontend/web/components/organisation-settings/usage/components/SingleSDKLabelsChart.tsx @@ -1,4 +1,4 @@ -import React, { FC } from 'react' +import React from 'react' import { Bar, BarChart, @@ -7,17 +7,12 @@ import { XAxis, YAxis, CartesianGrid, - TooltipProps, Legend, } from 'recharts' import moment from 'moment' -import { - NameType, - ValueType, -} from 'recharts/types/component/DefaultTooltipContent' import { ChartDataPoint } from 'components/organisation-settings/usage/OrganisationUsageMetrics.container' -import Utils from 'common/utils/utils' import useChartTheme from 'common/hooks/useChartTheme' +import ChartTooltip from 'components/charts/ChartTooltip' interface UsageChartProps { colours: string[] @@ -45,7 +40,9 @@ const UsageChart: React.FC = ({ <_Tooltip cursor={{ fill: 'transparent' }} - content={} + content={ + moment(v).format('D MMM')} /> + } /> = ({ ) } -const RechartsTooltip: FC> = ({ - active, - label, - payload, -}) => { - if (!active || !payload || payload.length === 0) { - return null - } - - return ( -
-
- {moment(label).format('D MMM')} -
-
- {payload.map((el: any) => { - const { dataKey, fill, value } = el - return ( - - - - {dataKey}: {Utils.numberWithCommas(value)} - - - ) - })} -
- ) -} - export default UsageChart diff --git a/frontend/web/components/pages/admin-dashboard/components/UsageTrendsChart.tsx b/frontend/web/components/pages/admin-dashboard/components/UsageTrendsChart.tsx index 8b3383d14ee7..ba71195c5423 100644 --- a/frontend/web/components/pages/admin-dashboard/components/UsageTrendsChart.tsx +++ b/frontend/web/components/pages/admin-dashboard/components/UsageTrendsChart.tsx @@ -14,6 +14,7 @@ import { UsageTrend } from 'common/types/responses' import Utils from 'common/utils/utils' import Card from 'components/Card' import useChartTheme from 'common/hooks/useChartTheme' +import ChartTooltip from 'components/charts/ChartTooltip' interface UsageTrendsChartProps { trends: UsageTrend[] @@ -46,9 +47,7 @@ const UsageTrendsChart: FC = ({ days = 30, trends }) => { tickFormatter={(value: number) => Utils.numberWithCommas(value)} tick={{ fill: chartTheme.tickFill, fontSize: 12 }} /> - Utils.numberWithCommas(value)} - /> + } />