diff --git a/.claude/commands/enforce-charter.md b/.claude/commands/enforce-charter.md new file mode 100644 index 00000000..7a225031 --- /dev/null +++ b/.claude/commands/enforce-charter.md @@ -0,0 +1,165 @@ +# Graphic Charter Enforcement + +Audit and fix graphic charter violations for the package: **$ARGUMENTS** + +## Style System Architecture + +The design system uses a **3-layer token architecture**: + +1. **Primitives** (`variables.scss`) — OKLCH-derived shade scales. Components rarely reference these directly. +2. **Semantic tokens** (`variables.scss`) — Purpose-based names (`--color-bg-app`, `--color-text`, `--color-error`). **This is what components use.** +3. **Domain tokens** (package SCSS files) — Module-specific colors defined in the package that owns them. + +### Which layer to use? + +| Situation | Layer | Example | +| ----------------------------------------- | --------- | ---------------------------------------------- | +| Standard background, text, border | Semantic | `var(--color-bg-surface)`, `var(--color-text)` | +| Status indication | Semantic | `var(--color-error)`, `var(--color-success)` | +| Module-specific color (kernel, chat, LED) | Domain | `var(--color-kernel)`, `var(--c-led-blue)` | +| Building a new semantic/domain token | Primitive | `var(--primary-500)`, `var(--surface-800)` | + +## Token Reference + +Design tokens are defined in `packages/ui-base/src/lib/assets/css/variables.scss`. +Domain tokens are in each package's SCSS files (see `doc/guides/STYLE_SYSTEM.md`). + +### Color Tokens — Primitives + +| Scale | Shades | Role | +| ----------------------------- | --------- | ----------------------- | +| `--primary-100..900` | 9 shades | Brand purple/magenta | +| `--surface-100..900` | 9 shades | Dark theme surfaces | +| `--cyan-100..900` | 9 shades | Data/interactive accent | +| `--red-100..900` | 9 shades | Error/danger | +| `--orange-100..900` | 9 shades | Warning | +| `--yellow-100..900` | 9 shades | Debug/info | +| `--green-100..900` | 9 shades | Success | +| `--neutral-1..12` | 12 shades | Grays (Radix mauve) | +| `--alpha-white-*` | 5 values | White transparency | +| `--alpha-black-*` | 8 values | Black transparency | +| `--white`, `--black-600..900` | 5 values | Solids | + +### Color Tokens — Semantic + +| Token | Purpose | +| -------------------------- | --------------------------- | +| `--color-bg-app` | App background | +| `--color-bg-surface` | Surface/card background | +| `--color-bg-elevated` | Elevated surface | +| `--color-bg-input` | Input background | +| `--color-bg-hover` | Hover state background | +| `--color-bg-node` | Whiteboard node background | +| `--color-text` | Primary text | +| `--color-text-muted` | Secondary text | +| `--color-text-faint` | Tertiary text | +| `--color-text-on-color` | Text on colored backgrounds | +| `--color-text-placeholder` | Placeholder text | +| `--color-border` | Default border | +| `--color-border-muted` | Subtle border | +| `--color-border-focus` | Focus ring / form border | +| `--color-accent` | Primary accent | +| `--color-accent-hover` | Accent hover state | +| `--color-accent-muted` | Muted accent | +| `--color-accent-strong` | Strong accent | +| `--color-success` | Success state | +| `--color-error` | Error state | +| `--color-warning` | Warning state | +| `--color-info` | Info state | +| `--color-selection` | Selection highlight | +| `--color-link` | Link color | +| `--color-debug` | Debug overlay | +| `--color-button-blue` | Blue button accent | +| `--color-vault` | Vault/sensitive indicator | + +### Font Size Tokens + +| Hardcoded | Token | +| --------- | ---------------------- | +| 9px | `var(--font-size-2xs)` | +| 11px | `var(--font-size-xs)` | +| 12px | `var(--font-size-sm)` | +| 13px-14px | `var(--font-size-md)` | +| 15px-16px | `var(--font-size-lg)` | +| 17px-18px | `var(--font-size-xl)` | +| 20px-21px | `var(--font-size-2xl)` | +| 24px-25px | `var(--font-size-3xl)` | +| 28px-30px | `var(--font-size-4xl)` | + +### Shadow Tokens + +| Hardcoded Pattern | Token | +| ------------------- | ------------------ | +| Small/subtle shadow | `var(--shadow-sm)` | +| Medium shadow | `var(--shadow-md)` | +| Large shadow | `var(--shadow-lg)` | +| Extra-large shadow | `var(--shadow-xl)` | + +### Z-Index Tokens + +| Hardcoded Range | Token | +| --------------- | ------------------- | +| 0-10 | `var(--z-base)` | +| 50-150 | `var(--z-dropdown)` | +| 150-250 | `var(--z-sticky)` | +| 250-350 | `var(--z-modal)` | +| 350-450 | `var(--z-tooltip)` | +| 450+ | `var(--z-toast)` | + +### Spacing Tokens + +| Value | Token | +| ----- | ------------------- | +| 0px | `var(--spacing-0)` | +| 2px | `var(--spacing-1)` | +| 4px | `var(--spacing-2)` | +| 6px | `var(--spacing-3)` | +| 8px | `var(--spacing-4)` | +| 12px | `var(--spacing-5)` | +| 16px | `var(--spacing-6)` | +| 20px | `var(--spacing-7)` | +| 24px | `var(--spacing-8)` | +| 32px | `var(--spacing-10)` | +| 40px | `var(--spacing-12)` | +| 48px | `var(--spacing-14)` | +| 64px | `var(--spacing-16)` | + +## Workflow + +1. **Audit** the package for violations: + + - Search all `.scss` and `.css` files in `packages/$ARGUMENTS/` for hardcoded hex colors, font-size values, z-index values, and box-shadow values + - Search all `.tsx` and `.ts` files for inline `style={{}}` props with hardcoded color/font-size/z-index/shadow values + - Check for references to old token names (`--c-gray-*`, `--c-pink-*`, `--c-blue-gray-*`, `--c-alt-blue-*`, `--ca-white-*`, `--ca-black-*`, `--c-white-1`, `--c-black-*`, `--mauve-*`) + - Count violations by category + +2. **Fix** violations: + + - Replace hardcoded values with the closest matching token + - For colors: prefer semantic tokens first, then domain tokens, then primitives + - For font-size: use the mapping table above + - For z-index: use the layer that matches the component's purpose + - For box-shadow: use the closest shadow token + - For inline styles in TSX: prefer SCSS class-based approaches where possible + - For old token names: use the mapping in `doc/guides/STYLE_SYSTEM.md` + +3. **Validate** fixes: + + - Run `npx nx run $ARGUMENTS:lint` + - Run `npx nx run $ARGUMENTS:typecheck` + - Run `npx nx run $ARGUMENTS:test` (if tests exist) + - Run `npm run lint:tokens` to check for undefined/unused variables + - Fix any errors introduced + +4. **Report** results: + - List files changed + - Count violations fixed per category + - List any remaining violations that need manual review + +## Important Notes + +- `variables.scss` and `utilities.scss` are EXCLUDED from enforcement — they define the raw values +- Third-party library styles are excluded +- Some values are legitimate exceptions (e.g., CSS calculations, SVG-specific values, animation keyframes, conic-gradient colors) +- All values must map to a token — no `/* charter-exception */` needed if the color is genuinely unique (use a `/* charter-exception: */` comment only for truly one-off values) +- Domain tokens belong in their package's SCSS file, not in `variables.scss` diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ee53e65..f972d1be 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,6 +64,9 @@ jobs: - name: Validate frontend build run: npm run test:build + - name: Run Stylelint (graphic charter) + run: npm run stylelint + - name: Run all tests run: npx nx run-many -t test --parallel=3 env: @@ -97,3 +100,153 @@ jobs: **/coverage **/test-results retention-days: 7 + + visual-regression: + name: Visual Regression Tests + runs-on: ubuntu-24.04 + needs: validate + + permissions: + contents: read + actions: read + pull-requests: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '24.x' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Install Playwright Chromium + run: npx playwright install chromium --with-deps + + - name: Build library packages + run: npx nx run-many -t build --parallel=5 + env: + NODE_OPTIONS: --max_old_space_size=4096 + + - name: Build all Storybooks + run: npx nx run-many -t build-storybook --parallel=3 + env: + NODE_OPTIONS: --max_old_space_size=4096 + + - name: Run visual regression tests + id: visual-tests + run: | + FAILED_PACKAGES="" + DIFF_FILES="" + HAS_FAILURES="false" + + for dir in packages/*/storybook-static packages/modules/*/storybook-static; do + if [ -d "$dir" ]; then + pkg=$(echo "$dir" | sed 's|/storybook-static||') + pkg_name=$(basename "$pkg") + echo "Testing $pkg..." + npx http-server "$dir" -p 6006 --silent & + SERVER_PID=$! + sleep 3 + + if ! npx test-storybook --url http://127.0.0.1:6006 --config-dir "$pkg/.storybook" 2>&1; then + HAS_FAILURES="true" + FAILED_PACKAGES="$FAILED_PACKAGES- **$pkg_name**\n" + + # Collect diff images for this package + if [ -d "$pkg/__diff_output__" ]; then + for diff in "$pkg"/__diff_output__/*.png; do + if [ -f "$diff" ]; then + DIFF_FILES="$DIFF_FILES - \`$(basename "$diff")\`\n" + fi + done + fi + fi + + kill $SERVER_PID 2>/dev/null || true + wait $SERVER_PID 2>/dev/null || true + fi + done + + echo "has_failures=$HAS_FAILURES" >> $GITHUB_OUTPUT + + # Write failures to file for the comment step + { + echo "FAILED_PACKAGES<> $GITHUB_OUTPUT + + { + echo "DIFF_FILES<> $GITHUB_OUTPUT + + - name: Upload visual diff artifacts + if: steps.visual-tests.outputs.has_failures == 'true' + uses: actions/upload-artifact@v4 + with: + name: visual-diffs + path: | + **/__diff_output__ + retention-days: 7 + + - name: Comment on PR with visual regression results + if: github.event_name == 'pull_request' && steps.visual-tests.outputs.has_failures == 'true' + uses: actions/github-script@v7 + with: + script: | + const failedPackages = `${{ steps.visual-tests.outputs.FAILED_PACKAGES }}`; + const diffFiles = `${{ steps.visual-tests.outputs.DIFF_FILES }}`; + const runUrl = `${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`; + + const body = `## 📸 Visual Regression Detected + + The following packages have screenshot differences: + + ${failedPackages} +
+ Changed screenshots + + ${diffFiles} +
+ + **[Download visual-diffs artifact](${runUrl})** to review the differences. + + If these changes are intentional, update the baselines locally: + \`\`\`bash + npx nx run :test-storybook -- -u + \`\`\` + `; + + // Find and update existing comment or create new one + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + const botComment = comments.find(c => + c.user.type === 'Bot' && c.body.includes('📸 Visual Regression Detected') + ); + + if (botComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body: body + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: body + }); + } diff --git a/.gitignore b/.gitignore index 21562c35..adf46de9 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,7 @@ test-output storybook-static +__diff_output__ .env diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..521a9f7c --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +legacy-peer-deps=true diff --git a/.stylelintrc.json b/.stylelintrc.json new file mode 100644 index 00000000..54abe662 --- /dev/null +++ b/.stylelintrc.json @@ -0,0 +1,93 @@ +{ + "extends": ["stylelint-config-standard-scss"], + "plugins": [ + "stylelint-declaration-strict-value", + "stylelint-value-no-unknown-custom-properties" + ], + "ignoreFiles": [ + "**/node_modules/**", + "**/dist/**", + "**/storybook-static/**", + "**/variables.scss", + "**/utilities.scss" + ], + "rules": { + "scss/at-rule-no-unknown": true, + "no-descending-specificity": null, + "selector-class-pattern": null, + "scss/double-slash-comment-empty-line-before": null, + "declaration-empty-line-before": null, + "comment-empty-line-before": null, + "scss/dollar-variable-empty-line-before": null, + "rule-empty-line-before": null, + "at-rule-empty-line-before": null, + "no-empty-source": null, + "scss/comment-no-empty": null, + "color-function-notation": null, + "color-function-alias-notation": null, + "alpha-value-notation": null, + "length-zero-no-unit": null, + "comment-whitespace-inside": null, + "declaration-block-no-redundant-longhand-properties": null, + "declaration-block-no-duplicate-properties": null, + "shorthand-property-no-redundant-values": null, + "scss/no-global-function-names": null, + "scss/operator-no-unspaced": null, + "font-family-no-missing-generic-family-keyword": null, + "property-no-vendor-prefix": null, + "value-no-vendor-prefix": null, + "selector-no-vendor-prefix": null, + "media-feature-range-notation": null, + "import-notation": null, + "scss/at-import-partial-extension": null, + "scss/load-partial-extension": null, + "color-hex-length": null, + "number-max-precision": null, + "keyframes-name-pattern": null, + "custom-property-pattern": null, + "scss/dollar-variable-pattern": null, + "scss/percent-placeholder-pattern": null, + "scss/at-mixin-pattern": null, + "selector-pseudo-element-no-unknown": [ + true, + { "ignorePseudoElements": ["v-deep"] } + ], + "selector-pseudo-class-no-unknown": [ + true, + { "ignorePseudoClasses": ["global", "local", "export"] } + ], + "selector-pseudo-element-colon-notation": null, + "hue-degree-notation": null, + "custom-property-empty-line-before": null, + "scss/operator-no-newline-after": null, + "scss/double-slash-comment-whitespace-inside": null, + "function-name-case": null, + "selector-not-notation": null, + "font-family-name-quotes": null, + "scss/at-extend-no-missing-placeholder": null, + "declaration-block-no-shorthand-property-overrides": null, + "selector-id-pattern": null, + "value-keyword-case": null, + "function-no-unknown": null, + "scss/function-no-unknown": null, + "no-duplicate-selectors": null, + "csstools/value-no-unknown-custom-properties": null, + "scale-unlimited/declaration-strict-value": [ + ["/color$/", "font-size", "z-index", "box-shadow"], + { + "ignoreValues": [ + "transparent", + "inherit", + "initial", + "unset", + "currentColor", + "none", + "0" + ], + "disableFix": true, + "severity": "error", + "message": "Use a CSS variable (var(--...)) instead of a hardcoded value for '${property}'. See packages/ui-base/src/lib/assets/css/variables.scss for available tokens." + } + ] + } +} diff --git a/doc/guides/STYLE_SYSTEM.md b/doc/guides/STYLE_SYSTEM.md new file mode 100644 index 00000000..e9b863b5 --- /dev/null +++ b/doc/guides/STYLE_SYSTEM.md @@ -0,0 +1,307 @@ +# Style System + +This document describes the 3-layer color token architecture used across the platform. + +## Architecture Overview + +``` +┌─────────────────────────────────────────────────────────┐ +│ Layer 3: Domain Tokens (package SCSS files) │ +│ Module-specific colors: --color-kernel, --c-led-blue │ +│ References primitives or semantic tokens │ +├─────────────────────────────────────────────────────────┤ +│ Layer 2: Semantic Tokens (variables.scss) │ +│ Purpose-based: --color-bg-app, --color-text, --color-error │ +│ ★ Components use this layer for standard UI ★ │ +├─────────────────────────────────────────────────────────┤ +│ Layer 1: Primitives (variables.scss) │ +│ OKLCH-derived shades: --primary-500, --surface-800 │ +│ Components rarely reference these directly │ +└─────────────────────────────────────────────────────────┘ +``` + +## When to Use Each Layer + +| Situation | Use | Example | +| --------------------- | ------------------------ | ----------------------------- | +| Background color | Semantic | `var(--color-bg-surface)` | +| Text color | Semantic | `var(--color-text)` | +| Border color | Semantic | `var(--color-border)` | +| Error/success/warning | Semantic | `var(--color-error)` | +| Accent / brand | Semantic | `var(--color-accent)` | +| Kernel indicator | Domain (jupyter) | `var(--color-kernel)` | +| Chat bubble color | Domain (chats) | `var(--color-chat-default)` | +| LED status light | Domain (user-containers) | `var(--c-led-blue)` | +| Edge type color | Domain (whiteboard) | `var(--color-edge-reference)` | +| Building a new token | Primitive | `var(--primary-500)` | + +## Primitive Palette Reference + +All primitives are OKLCH-derived with consistent lightness/chroma curves. + +### Shade Scale + +| Shade | L | C | Role | +| ----- | ---- | ---- | ------------------------- | +| 100 | 0.93 | 0.04 | Lightest, near-white tint | +| 200 | 0.82 | 0.08 | Light | +| 300 | 0.72 | 0.14 | Light accent | +| 400 | 0.62 | 0.18 | Medium | +| 500 | 0.52 | 0.20 | Base (most saturated) | +| 600 | 0.42 | 0.16 | Dark accent | +| 700 | 0.32 | 0.12 | Dark | +| 800 | 0.22 | 0.07 | Very dark | +| 900 | 0.13 | 0.04 | Darkest | + +### Primary (H=300, brand purple/magenta) + +| Token | Hex | OKLCH | +| --------------- | ------- | ------------------- | +| `--primary-100` | #ece2ff | L=0.93 C=0.04 H=300 | +| `--primary-200` | #ccb9f1 | L=0.82 C=0.08 H=300 | +| `--primary-300` | #b28fef | L=0.72 C=0.14 H=300 | +| `--primary-400` | #9867e1 | L=0.62 C=0.18 H=300 | +| `--primary-500` | #7d40c8 | L=0.52 C=0.20 H=300 | +| `--primary-600` | #5c2f95 | L=0.42 C=0.16 H=300 | +| `--primary-700` | #3e1e65 | L=0.32 C=0.12 H=300 | +| `--primary-800` | #201136 | L=0.22 C=0.07 H=300 | +| `--primary-900` | #0a0415 | L=0.13 C=0.04 H=300 | + +### Surface (H=280, dark-theme surfaces) + +| Token | Hex | +| --------------- | ------- | +| `--surface-100` | #e2e6ff | +| `--surface-200` | #b9bff8 | +| `--surface-300` | #9499fa | +| `--surface-400` | #7474ef | +| `--surface-500` | #594fd7 | +| `--surface-600` | #413aa0 | +| `--surface-700` | #2a266d | +| `--surface-800` | #15153a | +| `--surface-900` | #050517 | + +### Cyan (H=230, data/interactive) + +| Token | Hex | +| ------------ | ------- | +| `--cyan-100` | #ceeefe | +| `--cyan-200` | #8dceee | +| `--cyan-300` | #16b3eb | +| `--cyan-400` | #0095dc | +| `--cyan-500` | #0076c4 | +| `--cyan-600` | #005792 | +| `--cyan-700` | #003a63 | +| `--cyan-800` | #001f34 | +| `--cyan-900` | #000914 | + +### Red (H=25, error/danger) + +| Token | Hex | +| ----------- | ------- | +| `--red-100` | #ffdedb | +| `--red-200` | #f4b0aa | +| `--red-300` | #f07f77 | +| `--red-400` | #de4e4b | +| `--red-500` | #c21725 | +| `--red-600` | #90101a | +| `--red-700` | #62090f | +| `--red-800` | #340909 | +| `--red-900` | #140202 | + +### Orange (H=50, warning) + +| Token | Hex | +| -------------- | ------- | +| `--orange-100` | #ffe1d1 | +| `--orange-200` | #efb696 | +| `--orange-300` | #e9884d | +| `--orange-400` | #d75c00 | +| `--orange-500` | #bc3000 | +| `--orange-600` | #8c2300 | +| `--orange-700` | #5f1600 | +| `--orange-800` | #330d00 | +| `--orange-900` | #130300 | + +### Yellow (H=85, debug/info) + +| Token | Hex | +| -------------- | ------- | +| `--yellow-100` | #f4e6ca | +| `--yellow-200` | #dcc188 | +| `--yellow-300` | #cd9c1f | +| `--yellow-400` | #b77900 | +| `--yellow-500` | #9c5700 | +| `--yellow-600` | #744000 | +| `--yellow-700` | #4e2a00 | +| `--yellow-800` | #291600 | +| `--yellow-900` | #0e0600 | + +### Green (H=160, success) + +| Token | Hex | +| ------------- | ------- | +| `--green-100` | #d2f1df | +| `--green-200` | #96d5b2 | +| `--green-300` | #3fbf86 | +| `--green-400` | #00a55e | +| `--green-500` | #00883c | +| `--green-600` | #00652b | +| `--green-700` | #00431b | +| `--green-800` | #00240e | +| `--green-900` | #000c03 | + +### Neutrals (Radix mauve, perceptually uniform) + +| Token | Hex | +| -------------- | ------- | +| `--neutral-1` | #fdfcfd | +| `--neutral-2` | #faf9fb | +| `--neutral-3` | #f2eff3 | +| `--neutral-4` | #eae7ec | +| `--neutral-5` | #e3dfe6 | +| `--neutral-6` | #dbd8e0 | +| `--neutral-7` | #d0cdd7 | +| `--neutral-8` | #bcbac7 | +| `--neutral-9` | #8e8c99 | +| `--neutral-10` | #84828e | +| `--neutral-11` | #65636d | +| `--neutral-12` | #211f26 | + +### Alpha Scales + +| Token | Value | +| ------------------ | ------------------------- | +| `--alpha-white-90` | rgba(255, 255, 255, 0.9) | +| `--alpha-white-35` | rgba(255, 255, 255, 0.35) | +| `--alpha-white-25` | rgba(255, 255, 255, 0.25) | +| `--alpha-white-15` | rgba(255, 255, 255, 0.15) | +| `--alpha-white-10` | rgba(255, 255, 255, 0.1) | +| `--alpha-black-75` | rgba(0, 0, 0, 0.75) | +| `--alpha-black-65` | rgba(0, 0, 0, 0.65) | +| `--alpha-black-55` | rgba(0, 0, 0, 0.55) | +| `--alpha-black-45` | rgba(0, 0, 0, 0.45) | +| `--alpha-black-25` | rgba(0, 0, 0, 0.25) | +| `--alpha-black-17` | rgba(0, 0, 0, 0.17) | +| `--alpha-black-10` | rgba(0, 0, 0, 0.1) | +| `--alpha-black-5` | rgba(0, 0, 0, 0.05) | + +### Solids + +| Token | Hex | +| ------------- | ------- | +| `--white` | #ffffff | +| `--black-600` | #333333 | +| `--black-700` | #222222 | +| `--black-800` | #111111 | +| `--black-900` | #000000 | + +## Semantic Token Reference + +All semantic tokens are defined in `variables.scss` and can be overridden for theming. + +| Token | Purpose | Dark Theme Value | +| -------------------------- | ---------------------- | ------------------ | +| **Backgrounds** | | | +| `--color-bg-app` | App background | `--surface-900` | +| `--color-bg-surface` | Card/panel background | `--surface-800` | +| `--color-bg-elevated` | Elevated surface | `--surface-700` | +| `--color-bg-input` | Input field background | `--surface-800` | +| `--color-bg-hover` | Hover state | `--alpha-white-10` | +| `--color-bg-node` | Whiteboard node | `--alpha-white-10` | +| **Text** | | | +| `--color-text` | Primary text | `--neutral-8` | +| `--color-text-muted` | Secondary text | `--neutral-9` | +| `--color-text-faint` | Tertiary text | `--neutral-11` | +| `--color-text-on-color` | On colored bg | `--white` | +| `--color-text-placeholder` | Placeholder | `--alpha-white-35` | +| **Borders** | | | +| `--color-border` | Default border | `--surface-600` | +| `--color-border-muted` | Subtle border | `--surface-700` | +| `--color-border-focus` | Focus ring | `--primary-300` | +| **Accent** | | | +| `--color-accent` | Primary accent | `--primary-500` | +| `--color-accent-hover` | Accent hover | `--primary-400` | +| `--color-accent-muted` | Muted accent | `--primary-600` | +| `--color-accent-strong` | Strong accent | `--primary-300` | +| **Status** | | | +| `--color-success` | Success | `--green-500` | +| `--color-error` | Error | `--red-500` | +| `--color-warning` | Warning | `--orange-500` | +| `--color-info` | Info | `--cyan-500` | +| **Interactive** | | | +| `--color-selection` | Selection | `--cyan-500` | +| `--color-link` | Link color | `--white` | + +## Domain Token Locations + +| Domain | File | Tokens | +| --------------- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | +| Jupyter | `packages/modules/jupyter/src/lib/index.scss` | `--color-kernel`, `--color-kernel-state-*`, `--jp-collaborator-color*` | +| Chats | `packages/modules/chats/src/lib/index.scss` | `--color-chat-new`, `--color-chat-default` | +| Whiteboard | `packages/modules/whiteboard/src/lib/components/css/edges.scss` | `--color-edge-reference`, `--color-edge-sequence`, `--color-scene` | +| Collab | `packages/collab-engine/src/lib/frontend/context.scss` | `--color-yjs-awareness-default` | +| Socials | `packages/modules/socials/src/lib/components/node-video.scss` | `--color-youtube` | +| Containers | `packages/modules/user-containers/src/lib/index.scss` | `--c-led-*` | +| Users | `packages/ui-base/src/lib/users/users.scss` | `--c-live-*`, `--c-plus-n-*`, `--c-host-*`, `--c-role-*`, `--c-brand-*` | +| Airtable/Notion | `packages/modules/airtable/src/lib/components/node-airtable/airtable-table.scss` | `--c-status-*`, `--c-highlight-*`, `--c-neutral-*` | + +## How to Add a New Color + +1. **Does a semantic token cover it?** Use it. (e.g., `--color-bg-surface`, `--color-error`) +2. **Is it module-specific?** Add a domain token in the module's SCSS file, referencing a primitive. +3. **Is it a new shade of an existing hue?** Use the nearest primitive (e.g., `--primary-400`). +4. **Is it a genuinely new color?** Discuss with the team. Add to primitives only if justified. + +## How to Add a Light Theme + +Override semantic tokens in a `[data-theme="light"]` selector: + +```scss +[data-theme='light'] { + --color-bg-app: var(--neutral-1); + --color-bg-surface: var(--neutral-2); + --color-bg-elevated: var(--white); + --color-text: var(--neutral-12); + --color-text-muted: var(--neutral-11); + --color-border: var(--neutral-6); + // ... override all semantic tokens +} +``` + +Primitives stay the same. Only semantic tokens change. + +## Tooling + +| Tool | Command | Purpose | +| --------------- | ---------------------------- | ---------------------------------------------- | +| Stylelint | `npm run stylelint` | Catches hardcoded values, enforces token usage | +| Token lint | `npm run lint:tokens` | Detects unused tokens in `variables.scss` | +| Enforce charter | `/enforce-charter ` | Claude skill to audit a package | + +### Stylelint Rules + +- **`scale-unlimited/declaration-strict-value`** — Enforces `var(--...)` for color, font-size, z-index, box-shadow +- **`csstools/value-no-unknown-custom-properties`** — Catches references to undefined custom properties + +## Migration from Old Token Names + +| Old Pattern | New Pattern | +| -------------------------- | --------------------------- | +| `--c-gray-*` / `--mauve-*` | `--neutral-*` | +| `--c-pink-*` | `--primary-*` | +| `--c-blue-gray-*` | `--surface-*` | +| `--c-alt-blue-*` | `--cyan-*` | +| `--c-blue-*` | `--surface-*` or `--cyan-*` | +| `--c-red-*` | `--red-*` | +| `--c-orange-*` | `--orange-*` | +| `--c-yellow-*` | `--yellow-*` | +| `--c-green-*` | `--green-*` | +| `--c-white-1` | `--white` | +| `--c-black-*` | `--black-*` | +| `--ca-white-*` | `--alpha-white-*` | +| `--ca-black-*` | `--alpha-black-*` | +| `--color-background` | `--color-bg-app` | +| `--color-node-background` | `--color-bg-node` | +| `--color-good` | `--color-success` | +| `--color-form-border` | `--color-border-focus` | diff --git a/package-lock.json b/package-lock.json index 147494e5..35e42b9e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -161,17 +161,22 @@ "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "jest-environment-node": "^29.7.0", + "jest-image-snapshot": "^6.5.1", "jiti": "2.4.2", "jsdom": "~22.1.0", "jsonc-eslint-parser": "^2.1.0", "lint-staged": "^16.2.7", "nx": "^20.8.2", + "postcss-selector-parser": "^7.1.1", "prettier": "^2.6.2", "sass": "^1.83.4", "sass-embedded": "^1.83.4", "storybook": "8.5.0", + "stylelint": "^17.1.0", + "stylelint-config-standard-scss": "^14.0.0", + "stylelint-declaration-strict-value": "^1.10.11", + "stylelint-value-no-unknown-custom-properties": "^6.1.1", "supertest": "^7.1.4", - "tailwindcss": "^3.4.17", "ts-jest": "^29.1.0", "ts-node": "10.9.1", "tsc-files": "^1.1.4", @@ -191,19 +196,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@apidevtools/json-schema-ref-parser": { "version": "14.2.1", "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-14.2.1.tgz", @@ -1037,7 +1029,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -3038,6 +3029,50 @@ "dev": true, "license": "(Apache-2.0 AND BSD-3-Clause)" }, + "node_modules/@cacheable/memory": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.0.7.tgz", + "integrity": "sha512-RbxnxAMf89Tp1dLhXMS7ceft/PGsDl1Ip7T20z5nZ+pwIAsQ1p2izPjVG69oCLv/jfQ7HDPHTWK0c9rcAWXN3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/utils": "^2.3.3", + "@keyv/bigmap": "^1.3.0", + "hookified": "^1.14.0", + "keyv": "^5.5.5" + } + }, + "node_modules/@cacheable/memory/node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/@cacheable/utils": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.3.3.tgz", + "integrity": "sha512-JsXDL70gQ+1Vc2W/KUFfkAJzgb4puKwwKehNLuB+HrNKWf91O736kGfxn4KujXCCSuh6mRRL4XEB0PkAFjWS0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.3.0", + "keyv": "^5.5.5" + } + }, + "node_modules/@cacheable/utils/node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -3062,6 +3097,136 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.0.26", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.26.tgz", + "integrity": "sha512-6boXK0KkzT5u5xOgF6TKB+CLq9SOpEGmkZw0g5n9/7yg85wab3UzSxB8TxhLJ31L4SGJ6BCFRw/iftTha1CJXA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0" + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/media-query-list-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-5.0.0.tgz", + "integrity": "sha512-T9lXmZOfnam3eMERPsszjY5NK0jX8RmThmmm99FZ8b7z8yMaFZWKwLWGZuTwdO3ddRY5fy13GmmEYZXB4I98Eg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/selector-resolve-nested": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-4.0.0.tgz", + "integrity": "sha512-9vAPxmp+Dx3wQBIUwc1v7Mdisw1kbbaGqXUM8QLTgWg7SoPGYtXBsMXvsFs/0Bn5yoFhcktzxNZGNaUt0VjgjA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.1.1" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-6.0.0.tgz", + "integrity": "sha512-4sSgl78OtOXEX/2d++8A83zHNTgwCJMaR24FvsYL7Uf/VS8HZk9PTwR51elTbGqMuwH3szLvvOXEaVnqn0Z3zA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.1.1" + } + }, "node_modules/@emnapi/core": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.0.tgz", @@ -4700,7 +4865,6 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.1.tgz", "integrity": "sha512-sPxgEWtPUR3EnRJCEtbGZG2iX8LQDUls2wUS3o27jg07KqJFMq6YDeWvMo1wfpmy3rqRdS0rivpLwhqQtEyCuQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@grpc/proto-loader": "^0.8.0", @@ -4714,7 +4878,6 @@ "version": "0.8.0", "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz", "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "lodash.camelcase": "^4.3.0", @@ -4949,102 +5112,6 @@ "node": "20 || >=22" } }, - "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": "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" - }, - "engines": { - "node": ">=12" - } - }, - "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": "MIT", - "engines": { - "node": ">=12" - }, - "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/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "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": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "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", @@ -5677,7 +5744,6 @@ "version": "4.4.2", "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", - "dev": true, "license": "MIT", "funding": { "type": "opencollective", @@ -6243,6 +6309,30 @@ "react": "^18.2.0" } }, + "node_modules/@keyv/bigmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", + "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.4.0", + "hookified": "^1.15.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "keyv": "^5.6.0" + } + }, + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "dev": true, + "license": "MIT" + }, "node_modules/@lumino/algorithm": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@lumino/algorithm/-/algorithm-2.0.3.tgz", @@ -7676,7 +7766,6 @@ "integrity": "sha512-vDnF/CjWq2ssrS3FgrZSrF6r/60S59+NKriLFdZiR42ykk4meY7XjrApn0l8RcBt3135/uO94VedCSF6jSfsJQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@module-federation/runtime": "0.21.3", "@module-federation/webpack-bundler-runtime": "0.21.3" @@ -7900,7 +7989,6 @@ "integrity": "sha512-JQZ//ab+lEXoU2DHAH+JtYASGzxEjXB0s4rU+6VJXc8c+oUPxH3kWIwzjdncg2mcWBmC1140DCk+K+kDfOZ5CQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@module-federation/runtime": "0.9.1", "@module-federation/webpack-bundler-runtime": "0.9.1" @@ -9601,7 +9689,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "@napi-rs/wasm-runtime": "0.2.4", "@nrwl/tao": "19.8.4", @@ -9685,7 +9772,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -9694,7 +9780,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.208.0.tgz", "integrity": "sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.3.0" @@ -9772,7 +9857,6 @@ "version": "0.54.0", "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-web/-/auto-instrumentations-web-0.54.0.tgz", "integrity": "sha512-lfOtVCB0MrWiuSg1RhndgqbQyiJDYNTaceIzBfeFWhlkTDdu8fFr5VXytbjt9u+oVOBVohuLYmYRNJWqq1iqpw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/instrumentation": "^0.208.0", @@ -9793,7 +9877,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.2.0.tgz", "integrity": "sha512-qRkLWiUEZNAmYapZ7KGS5C4OmBLcP/H2foXeOEaowYCR0wi89fHejrfYfbuLVCMLp/dWZXKvQusdbUEZjERfwQ==", - "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.19.0 || >=20.6.0" @@ -9807,7 +9890,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.2.0.tgz", "integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, @@ -9822,7 +9904,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.208.0.tgz", "integrity": "sha512-AmZDKFzbq/idME/yq68M155CJW1y056MNBekH9OZewiZKaqgwYN4VYfn3mXVPftYsfrCM2r4V6tS8H2LmfiDCg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.7.1", @@ -9843,7 +9924,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.208.0.tgz", "integrity": "sha512-jOv40Bs9jy9bZVLo/i8FwUiuCvbjWDI+ZW13wimJm4LjnlwJxGgB+N/VWOZUTpM+ah/awXeQqKdNlpLf2EjvYg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/api-logs": "0.208.0", @@ -9863,7 +9943,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.208.0.tgz", "integrity": "sha512-Wy8dZm16AOfM7yddEzSFzutHZDZ6HspKUODSUJVjyhnZFMBojWDjSNgduyCMlw6qaxJYz0dlb0OEcb4Eme+BfQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/api-logs": "0.208.0", @@ -9885,7 +9964,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.208.0.tgz", "integrity": "sha512-YbEnk7jjYmvhIwp2xJGkEvdgnayrA2QSr28R1LR1klDPvCxsoQPxE6TokDbQpoCEhD3+KmJVEXfb4EeEQxjymg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.7.1", @@ -9908,7 +9986,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.208.0.tgz", "integrity": "sha512-QZ3TrI90Y0i1ezWQdvreryjY0a5TK4J9gyDLIyhLBwV+EQUvyp5wR7TFPKCAexD4TDSWM0t3ulQDbYYjVtzTyA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.2.0", @@ -9928,7 +10005,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.208.0.tgz", "integrity": "sha512-CvvVD5kRDmRB/uSMalvEF6kiamY02pB46YAqclHtfjJccNZFxbkkXkMMmcJ7NgBFa5THmQBNVQ2AHyX29nRxOw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.2.0", @@ -9949,7 +10025,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.208.0.tgz", "integrity": "sha512-Rgws8GfIfq2iNWCD3G1dTD9xwYsCof1+tc5S5X0Ahdb5CrAPE+k5P70XCWHqrFFurVCcKaHLJ/6DjIBHWVfLiw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.2.0", @@ -9967,7 +10042,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.208.0.tgz", "integrity": "sha512-E/eNdcqVUTAT7BC+e8VOw/krqb+5rjzYkztMZ/o+eyJl+iEY6PfczPXpwWuICwvsm0SIhBoh9hmYED5Vh5RwIw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.7.1", @@ -9989,7 +10063,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.208.0.tgz", "integrity": "sha512-jbzDw1q+BkwKFq9yxhjAJ9rjKldbt5AgIy1gmEIJjEV/WRxQ3B6HcLVkwbjJ3RcMif86BDNKR846KJ0tY0aOJA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.2.0", @@ -10009,7 +10082,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.208.0.tgz", "integrity": "sha512-q844Jc3ApkZVdWYd5OAl+an3n1XXf3RWHa3Zgmnhw3HpsM3VluEKHckUUEqHPzbwDUx2lhPRVkqK7LsJ/CbDzA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.2.0", @@ -10029,7 +10101,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.2.0.tgz", "integrity": "sha512-VV4QzhGCT7cWrGasBWxelBjqbNBbyHicWWS/66KoZoe9BzYwFB72SH2/kkc4uAviQlO8iwv2okIJy+/jqqEHTg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.2.0", @@ -10048,7 +10119,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.208.0.tgz", "integrity": "sha512-Eju0L4qWcQS+oXxi6pgh7zvE2byogAkcsVv0OjHF/97iOz1N/aKE6etSGowYkie+YA1uo6DNwdSxaaNnLvcRlA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/api-logs": "0.208.0", @@ -10221,7 +10291,6 @@ "version": "0.54.0", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-document-load/-/instrumentation-document-load-0.54.0.tgz", "integrity": "sha512-fmWSUtgemLRZOUFHfCnUgFyG75yxwgT8rvfMN4lyESnh3cdStiI4SVKKNBjsnkfceqDQPLSG7G+qXfZjBIscuQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -10240,7 +10309,6 @@ "version": "0.57.0", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.57.0.tgz", "integrity": "sha512-HAdx/o58+8tSR5iW+ru4PHnEejyKrAy9fYFhlEI81o10nYxrGahnMAHWiSjhDC7UQSY3I4gjcPgSKQz4rm/asg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -10276,7 +10344,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fetch/-/instrumentation-fetch-0.208.0.tgz", "integrity": "sha512-zgStoUfNF1xH9bCq539k1aeieKxPiAvBo5gKipQ9fIt+eJsFvqGcSzrrDX+OYgpIPW/IVNgWBoOw6zVmKwgNwQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.2.0", @@ -10379,7 +10446,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.208.0.tgz", "integrity": "sha512-rhmK46DRWEbQQB77RxmVXGyjs6783crXCnFjYQj+4tDH/Kpv9Rbg3h2kaNyp5Vz2emF1f9HOQQvZoHzwMWOFZQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.2.0", @@ -10797,7 +10863,6 @@ "version": "0.53.0", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-user-interaction/-/instrumentation-user-interaction-0.53.0.tgz", "integrity": "sha512-yttf3dZNRSUFO/Hc3bZXlH/3rQlvJEN0rN+2EOLqOg0cJze61WPTe4kQerl2WH+bwOyO3rEOJExnCfv9moyK+g==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -10833,7 +10898,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-xml-http-request/-/instrumentation-xml-http-request-0.208.0.tgz", "integrity": "sha512-me0knebxJxnzis73p5/ZQgdLNG6nsUXMsDR/dZk+BPOiNyd3Me9ye2wVM06JlcLA54w4JESw6wMTNi4lMhowFQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.2.0", @@ -10852,7 +10916,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.208.0.tgz", "integrity": "sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.2.0", @@ -10869,7 +10932,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.208.0.tgz", "integrity": "sha512-fGvAg3zb8fC0oJAzfz7PQppADI2HYB7TSt/XoCaBJFi1mSquNUjtHXEoviMgObLAa1NRIgOC1lsV1OUKi+9+lQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.7.1", @@ -10888,7 +10950,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.208.0.tgz", "integrity": "sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/api-logs": "0.208.0", @@ -10910,7 +10971,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.2.0.tgz", "integrity": "sha512-9CrbTLFi5Ee4uepxg2qlpQIozoJuoAZU5sKMx0Mn7Oh+p7UrgCiEV6C02FOxxdYVRRFQVCinYR8Kf6eMSQsIsw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.2.0" @@ -10926,7 +10986,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.2.0.tgz", "integrity": "sha512-FfeOHOrdhiNzecoB1jZKp2fybqmqMPJUXe2ZOydP7QzmTPYcfPeuaclTLYVhK3HyJf71kt8sTl92nV4YIaLaKA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.2.0" @@ -11056,7 +11115,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.208.0.tgz", "integrity": "sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/api-logs": "0.208.0", @@ -11074,7 +11132,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.2.0.tgz", "integrity": "sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.2.0", @@ -11091,7 +11148,6 @@ "version": "0.208.0", "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.208.0.tgz", "integrity": "sha512-pbAqpZ7zTMFuTf3YecYsecsto/mheuvnK2a/jgstsE5ynWotBjgF5bnz5500W9Xl2LeUfg04WMt63TWtAgzRMw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/api-logs": "0.208.0", @@ -11145,7 +11201,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.2.0.tgz", "integrity": "sha512-+OaRja3f0IqGG2kptVeYsrZQK9nKRSpfFrKtRBq4uh6nIB8bTBgaGvYQrQoRrQWQMA5dK5yLhDMDc0dvYvCOIQ==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/context-async-hooks": "2.2.0", @@ -11163,7 +11218,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-web/-/sdk-trace-web-2.2.0.tgz", "integrity": "sha512-x/LHsDBO3kfqaFx5qSzBljJ5QHsRXrvS4MybBDy1k7Svidb8ZyIPudWVzj3s5LpPkYZIgi9e+7tdsNCnptoelw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "2.2.0", @@ -11534,17 +11588,6 @@ "typescript": "^3 || ^4 || ^5" } }, - "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/@polka/url": { "version": "1.0.0-next.29", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", @@ -11556,35 +11599,30 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/codegen": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "@protobufjs/aspromise": "^1.1.1", @@ -11595,35 +11633,30 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/inquire": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/@radix-ui/colors": { @@ -14588,7 +14621,6 @@ "resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-5.24.13.tgz", "integrity": "sha512-rNF8tDxIwTtXzz5O/U23QU73nlhgQNYJ+Sv5BAwQOIyhIE2Z3S5tUiSVMwZHt0julkv/Ryfwi+qsD4FiE5rOuw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "json-schema-merge-allof": "^0.8.1", "jsonpointer": "^5.0.1", @@ -15129,7 +15161,6 @@ "integrity": "sha512-hZVrmiZoBTchWUdh/XbeJ5z+GqHW5aPYeufBigmtUeyzul8uJtHlWKmQhpG+lplMf6o1RESTjjxl632TP/Cfhg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@module-federation/runtime-tools": "0.21.2", "@rspack/binding": "1.6.1", @@ -15445,6 +15476,19 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -17237,7 +17281,6 @@ "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -17346,7 +17389,6 @@ "integrity": "sha512-BBjg0QNuEEmJSoU/++JOXhrjWdu3PTyYeJWsvchsI0Aqtj8ICkz/DqlwtXbmZVZ5vuDPpTfFlwDBZe81zgShMA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@swc-node/core": "^1.13.1", "@swc-node/sourcemap-support": "^0.5.0", @@ -17469,7 +17511,6 @@ "dev": true, "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.8" @@ -17713,7 +17754,6 @@ "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@swc/counter": "^0.1.3" } @@ -17763,7 +17803,6 @@ "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -18175,7 +18214,6 @@ "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.5.tgz", "integrity": "sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ==", "license": "MIT", - "peer": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", @@ -18328,8 +18366,8 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/@types/json5": { "version": "0.0.29", @@ -18444,7 +18482,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.24.tgz", "integrity": "sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -18617,7 +18654,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz", "integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==", "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -18627,9 +18663,8 @@ "version": "18.3.0", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", - "devOptional": true, + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/react": "*" } @@ -18854,7 +18889,6 @@ "integrity": "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.3", "@typescript-eslint/types": "8.46.3", @@ -19735,8 +19769,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", @@ -19874,9 +19907,7 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -19899,7 +19930,6 @@ "version": "1.9.5", "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^8" @@ -19993,7 +20023,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -20099,7 +20128,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -20109,7 +20137,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -20121,13 +20148,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true, - "license": "MIT" - }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -20200,13 +20220,6 @@ "dev": true, "license": "MIT" }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true, - "license": "MIT" - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -20471,6 +20484,16 @@ "dev": true, "license": "MIT" }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -21404,7 +21427,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", @@ -21557,6 +21579,20 @@ "node": ">= 6.0.0" } }, + "node_modules/cacheable": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.3.2.tgz", + "integrity": "sha512-w+ZuRNmex9c1TR9RcsxbfTKCjSL0rh1WA5SABbrWprIHeNBdmyQLSYonlDy9gpD+63XT8DgZ/wNh1Smvc9WnJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/memory": "^2.0.7", + "@cacheable/utils": "^2.3.3", + "hookified": "^1.15.0", + "keyv": "^5.5.5", + "qified": "^0.6.0" + } + }, "node_modules/cacheable-lookup": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", @@ -21602,6 +21638,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cacheable/node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, "node_modules/caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -21718,16 +21764,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/caniuse-lite": { "version": "1.0.30001754", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001754.tgz", @@ -21921,7 +21957,6 @@ "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" }, "node_modules/classcat": { @@ -22039,7 +22074,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -22104,7 +22138,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -22117,6 +22150,12 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", "dev": true, "license": "MIT" }, @@ -22605,6 +22644,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/css-functions-list": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.3.tgz", + "integrity": "sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12 || >=16" + } + }, "node_modules/css-select": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", @@ -22916,7 +22965,6 @@ "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10" } @@ -23299,7 +23347,6 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -23840,13 +23887,6 @@ "wrappy": "1" } }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -23896,13 +23936,6 @@ "dev": true, "license": "MIT" }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true, - "license": "MIT" - }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -24089,13 +24122,6 @@ "dev": true, "license": "MIT" }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -24221,7 +24247,7 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "iconv-lite": "^0.6.2" @@ -24293,6 +24319,16 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/environment": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", @@ -24553,7 +24589,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -24603,7 +24638,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -24666,7 +24700,6 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -24727,7 +24760,6 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -25466,7 +25498,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -25840,6 +25871,16 @@ "fxparser": "src/cli/cli.js" } }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -26314,7 +26355,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", - "dev": true, "license": "MIT" }, "node_modules/fraction.js": { @@ -26581,7 +26621,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -26666,6 +26705,16 @@ "node": ">= 0.4" } }, + "node_modules/get-stdin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", + "integrity": "sha512-jZV7n6jGE3Gt7fgSTJoz91Ak5MuTLwMwkoYdjxuJ/AmjIsE1UC03y/IWkZCQGEvVNS9qoRNwy5BCqxImv0FVeA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", @@ -26835,6 +26884,57 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/globby": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-16.1.0.tgz", + "integrity": "sha512-+A4Hq7m7Ze592k9gZRy4gJ27DrXRNnC1vPjxTt1qQxEY8RxagBkBxivkCwg7FxSTG0iLLEMaUx13oOr0R2/qcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.5", + "is-path-inside": "^4.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/globby/node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", + "dev": true, + "license": "MIT" + }, "node_modules/glur": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/glur/-/glur-1.1.2.tgz", @@ -27119,6 +27219,19 @@ "node": ">=8" } }, + "node_modules/hashery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.4.0.tgz", + "integrity": "sha512-Wn2i1In6XFxl8Az55kkgnFRiAlIAushzh26PTjL2AKtQcEfXrcLa7Hn5QOWGZEf3LU057P9TwwZjFyxfS1VuvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^1.14.0" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -27166,6 +27279,13 @@ "node": ">=0.10.0" } }, + "node_modules/hookified": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", + "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", + "dev": true, + "license": "MIT" + }, "node_modules/hosted-git-info": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", @@ -27206,6 +27326,19 @@ "dev": true, "license": "MIT" }, + "node_modules/html-tags": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-5.1.0.tgz", + "integrity": "sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/htmlparser2": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", @@ -27549,7 +27682,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.0.tgz", "integrity": "sha512-yNZhyQYqXpkT0AKq3F3KLasUSK4fHvebNH5hOsKQw2dhGSALvQ4U0BqUc5suziKvydO5u5hgN2hy1RJaho8U5A==", - "dev": true, "license": "Apache-2.0", "dependencies": { "acorn": "^8.14.0", @@ -27588,6 +27720,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -27891,7 +28034,6 @@ "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" @@ -28018,6 +28160,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-path-inside": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", + "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -28438,22 +28593,6 @@ "node": ">= 0.4" } }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, "node_modules/jake": { "version": "10.9.4", "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", @@ -28478,7 +28617,6 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -28571,7 +28709,6 @@ "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", @@ -28993,7 +29130,6 @@ "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", @@ -29057,6 +29193,33 @@ "fsevents": "^2.3.2" } }, + "node_modules/jest-image-snapshot": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/jest-image-snapshot/-/jest-image-snapshot-6.5.1.tgz", + "integrity": "sha512-xlJFufgfY2Z4DsRsjcnTwxuynvo1bKdhf4OfcEftNuUAK+BwSCUtPmwlBGJhQ0XJXfm9JMAi/4BhQiHbaV8HrA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "chalk": "^4.0.0", + "get-stdin": "^5.0.1", + "glur": "^1.1.2", + "lodash": "^4.17.4", + "pixelmatch": "^5.1.0", + "pngjs": "^3.4.0", + "ssim.js": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "jest": ">=20 <=29" + }, + "peerDependenciesMeta": { + "jest": { + "optional": true + } + } + }, "node_modules/jest-junit": { "version": "16.0.0", "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-16.0.0.tgz", @@ -29380,7 +29543,6 @@ "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/console": "^29.7.0", "@jest/environment": "^29.7.0", @@ -29813,7 +29975,6 @@ "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", "dev": true, "license": "MIT", - "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -29844,7 +30005,6 @@ "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.11.0.tgz", "integrity": "sha512-zKfoBBD1uDw3rljwHkt0fWuja1B76R7CjznuBO+mSX6jpsO1EBeWNRKpeaQho9yPI/pvCv4recGfgOXGxwPZvQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=12.20.0" }, @@ -29932,7 +30092,6 @@ "integrity": "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "abab": "^2.0.6", "cssstyle": "^3.0.0", @@ -30250,6 +30409,16 @@ "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -30260,6 +30429,13 @@ "node": ">=6" } }, + "node_modules/known-css-properties": { + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.37.0.tgz", + "integrity": "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==", + "dev": true, + "license": "MIT" + }, "node_modules/koa": { "version": "2.15.4", "resolved": "https://registry.npmjs.org/koa/-/koa-2.15.4.tgz", @@ -30615,19 +30791,6 @@ "url": "https://github.com/sponsors/dmonad" } }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, "node_modules/lines-and-columns": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", @@ -30862,7 +31025,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true, "license": "MIT" }, "node_modules/lodash.clonedeep": { @@ -30973,6 +31135,13 @@ "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", "license": "MIT" }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -31208,7 +31377,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", - "dev": true, "license": "Apache-2.0" }, "node_modules/long-timeout": { @@ -31383,6 +31551,17 @@ "node": ">= 0.4" } }, + "node_modules/mathml-tag-names": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-4.0.0.tgz", + "integrity": "sha512-aa6AU2Pcx0VP/XWnh8IGL0SYSgQHDT6Ucror2j2mXeFAlN3ahaNs8EZtG1YiticMkSLj3Gt6VPFfZogt7G5iFQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -31458,6 +31637,19 @@ "map-or-similar": "^1.5.0" } }, + "node_modules/meow": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-14.0.0.tgz", + "integrity": "sha512-JhC3R1f6dbspVtmF3vKjAWz1EVIvwFrGGPLSdU6rK79xBwHWTuHoLnRX/t1/zHS1Ch1Y2UtIrih7DAHuH9JFJA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -32135,16 +32327,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -32201,15 +32383,13 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", - "dev": true, "license": "MIT" }, "node_modules/monaco-editor": { "version": "0.52.2", "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz", "integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/mri": { "version": "1.2.0", @@ -32305,18 +32485,6 @@ "object-assign": "^4.1.1" } }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, "node_modules/nano-spawn": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz", @@ -32692,7 +32860,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "@napi-rs/wasm-runtime": "0.2.4", "@yarnpkg/lockfile": "^1.1.0", @@ -33026,16 +33193,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -33441,13 +33598,6 @@ "node": ">=8" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, "node_modules/pako": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pako/-/pako-2.0.3.tgz", @@ -33747,30 +33897,6 @@ "dev": true, "license": "MIT" }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, "node_modules/path-to-regexp": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", @@ -33873,7 +33999,6 @@ "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", "license": "MIT", - "peer": true, "dependencies": { "pg-connection-string": "^2.9.1", "pg-pool": "^3.10.1", @@ -34033,6 +34158,29 @@ "@napi-rs/nice": "^1.0.1" } }, + "node_modules/pixelmatch": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-5.3.0.tgz", + "integrity": "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "pngjs": "^6.0.0" + }, + "bin": { + "pixelmatch": "bin/pixelmatch" + } + }, + "node_modules/pixelmatch/node_modules/pngjs": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", + "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.13.0" + } + }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -34193,6 +34341,16 @@ "crc-32": "^0.3.0" } }, + "node_modules/pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/points-on-curve": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-1.0.1.tgz", @@ -34271,7 +34429,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -34281,123 +34438,78 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } + "license": "MIT" }, - "node_modules/postcss-js": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", - "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "node_modules/postcss-resolve-nested-selector": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz", + "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } + "license": "MIT" }, - "node_modules/postcss-load-config": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", - "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "node_modules/postcss-safe-parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", + "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==", "dev": true, "funding": [ { "type": "opencollective", "url": "https://opencollective.com/postcss/" }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" + }, { "type": "github", "url": "https://github.com/sponsors/ai" } ], "license": "MIT", - "dependencies": { - "lilconfig": "^3.1.1" - }, "engines": { - "node": ">= 18" + "node": ">=18.0" }, "peerDependencies": { - "jiti": ">=1.21.0", - "postcss": ">=8.0.9", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - }, - "postcss": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } + "postcss": "^8.4.31" } }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "node_modules/postcss-scss": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz", + "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", "dev": true, "funding": [ { "type": "opencollective", "url": "https://opencollective.com/postcss/" }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-scss" + }, { "type": "github", "url": "https://github.com/sponsors/ai" } ], "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, "engines": { "node": ">=12.0" }, "peerDependencies": { - "postcss": "^8.2.14" + "postcss": "^8.4.29" } }, "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, "license": "MIT", "dependencies": { @@ -34488,7 +34600,6 @@ "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin-prettier.js" }, @@ -34609,7 +34720,6 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", - "dev": true, "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { @@ -34742,6 +34852,19 @@ "integrity": "sha512-6Du7IZdIy7cHiv7AhtDy4X2QRM8IAD5DII69mt5qWibC2d15ZU8DmBG1WdZKekG11cChSu4zkSUGPF9sweOl6w==", "license": "Apache-2.0" }, + "node_modules/qified": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/qified/-/qified-0.6.0.tgz", + "integrity": "sha512-tsSGN1x3h569ZSU1u6diwhltLyfUWDp3YbFHedapTmpBl0B3P6U3+Qptg7xu+v+1io1EwhdPyyRHYbEw0KN2FA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^1.14.0" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/qr.js": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz", @@ -34848,8 +34971,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/quill-cursors/-/quill-cursors-4.0.4.tgz", "integrity": "sha512-beHOYwRZ/I+Ift3bsvMnNWZ7gX25upW3b0aREpklUTR273MFJgxsCYmlgd/6otBE0FtFefOfh2/xU6xbkkxgIg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/quill-delta": { "version": "5.1.0", @@ -35091,7 +35213,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -35149,7 +35270,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -35329,16 +35449,6 @@ "react-dom": ">=16.6.0" } }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -35615,7 +35725,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -35634,7 +35743,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "dev": true, "license": "MIT", "dependencies": { "debug": "^4.3.5", @@ -36168,7 +36276,6 @@ "integrity": "sha512-elOcIZRTM76dvxNAjqYrucTSI0teAF/L2Lv0s6f6b7FOwcwIuA357bIE871580AjHJuSvLIRUosgV+lIWx6Rgg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -36190,7 +36297,6 @@ "integrity": "sha512-+VUy01yfDqNmIVMd/LLKl2TTtY0ovZN0rTonh+FhKr65mFwIYgU9WzgIZKS7U9/SPCQvWTsTGx9jyt+qRm/XFw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@bufbuild/protobuf": "^2.5.0", "buffer-builder": "^0.2.0", @@ -36627,7 +36733,6 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -37243,6 +37348,13 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/ssim.js": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/ssim.js/-/ssim.js-3.5.0.tgz", + "integrity": "sha512-Aj6Jl2z6oDmgYFFbQqK7fght19bXdOxY7Tj03nF+03M9gCBAjeIiO8/PlEGMfKDwYpw4q6iBqVq2YuREorGg/g==", + "dev": true, + "license": "MIT" + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -37309,7 +37421,6 @@ "integrity": "sha512-cEx42OlCetManF+cONVJVYP7SYsnI2K922DfWKmZhebP0it0n6TUof4y5/XzJ8YUruwPgyclGLdX8TvdRuNSfw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@storybook/core": "8.5.0" }, @@ -37470,7 +37581,6 @@ "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", @@ -37481,34 +37591,10 @@ "node": ">=8" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/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/string-width/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/string.prototype.includes": { @@ -37628,21 +37714,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -37788,107 +37859,391 @@ "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/stylis": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", - "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", - "license": "MIT" + "node_modules/stylelint": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.1.0.tgz", + "integrity": "sha512-+cUX1FxkkbLX5qJRAPapUv/+v+YU3pGbWu+pHVqTXpiY0mYh3Dxfxa0bLBtVtYgOC8hIWIyX2H/3Y3LWlAevDg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-syntax-patches-for-csstree": "^1.0.25", + "@csstools/css-tokenizer": "^4.0.0", + "@csstools/media-query-list-parser": "^5.0.0", + "@csstools/selector-resolve-nested": "^4.0.0", + "@csstools/selector-specificity": "^6.0.0", + "balanced-match": "^3.0.1", + "colord": "^2.9.3", + "cosmiconfig": "^9.0.0", + "css-functions-list": "^3.2.3", + "css-tree": "^3.1.0", + "debug": "^4.4.3", + "fast-glob": "^3.3.3", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^11.1.2", + "global-modules": "^2.0.0", + "globby": "^16.1.0", + "globjoin": "^0.1.4", + "html-tags": "^5.1.0", + "ignore": "^7.0.5", + "import-meta-resolve": "^4.2.0", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.37.0", + "mathml-tag-names": "^4.0.0", + "meow": "^14.0.0", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.5.6", + "postcss-safe-parser": "^7.0.1", + "postcss-selector-parser": "^7.1.1", + "postcss-value-parser": "^4.2.0", + "string-width": "^8.1.0", + "supports-hyperlinks": "^4.4.0", + "svg-tags": "^1.0.0", + "table": "^6.9.0", + "write-file-atomic": "^7.0.0" + }, + "bin": { + "stylelint": "bin/stylelint.mjs" + }, + "engines": { + "node": ">=20.19.0" + } }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "node_modules/stylelint-config-recommended": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.1.tgz", + "integrity": "sha512-bLvc1WOz/14aPImu/cufKAZYfXs/A/owZfSMZ4N+16WGXLoX5lOir53M6odBxvhgmgdxCVnNySJmZKx73T93cg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": "^16.1.0" + } + }, + "node_modules/stylelint-config-recommended-scss": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-14.1.0.tgz", + "integrity": "sha512-bhaMhh1u5dQqSsf6ri2GVWWQW5iUjBYgcHkh7SgDDn92ijoItC/cfO/W+fpXshgTQWhwFkP1rVcewcv4jaftRg==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" + "postcss-scss": "^4.0.9", + "stylelint-config-recommended": "^14.0.1", + "stylelint-scss": "^6.4.0" }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "postcss": "^8.3.3", + "stylelint": "^16.6.1" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + } + } + }, + "node_modules/stylelint-config-standard": { + "version": "36.0.1", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-36.0.1.tgz", + "integrity": "sha512-8aX8mTzJ6cuO8mmD5yon61CWuIM4UD8Q5aBcWKGSf6kg+EC3uhB+iOywpTK4ca6ZL7B49en8yanOFtUW0qNzyw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "license": "MIT", + "dependencies": { + "stylelint-config-recommended": "^14.0.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": "^16.1.0" } }, - "node_modules/sucrase/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "node_modules/stylelint-config-standard-scss": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard-scss/-/stylelint-config-standard-scss-14.0.0.tgz", + "integrity": "sha512-6Pa26D9mHyi4LauJ83ls3ELqCglU6VfCXchovbEqQUiEkezvKdv6VgsIoMy58i00c854wVmOw0k8W5FTpuaVqg==", "dev": true, "license": "MIT", + "dependencies": { + "stylelint-config-recommended-scss": "^14.1.0", + "stylelint-config-standard": "^36.0.1" + }, "engines": { - "node": ">= 6" + "node": ">=18.12.0" + }, + "peerDependencies": { + "postcss": "^8.3.3", + "stylelint": "^16.11.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + } } }, - "node_modules/sucrase/node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "node_modules/stylelint-declaration-strict-value": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/stylelint-declaration-strict-value/-/stylelint-declaration-strict-value-1.10.11.tgz", + "integrity": "sha512-oVQvhZlFZAiDz9r2BPFZLtTGm1A2JVhdKObKAJoTjFfR4F/NpApC4bMBTxf4sZS76Na3njYKVOaAaKSZ4+FU+g==", "dev": true, - "license": "ISC", + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": ">=7 <=16" + } + }, + "node_modules/stylelint-scss": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-6.14.0.tgz", + "integrity": "sha512-ZKmHMZolxeuYsnB+PCYrTpFce0/QWX9i9gh0hPXzp73WjuIMqUpzdQaBCrKoLWh6XtCFSaNDErkMPqdjy1/8aA==", + "dev": true, + "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" + "css-tree": "^3.0.1", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.37.0", + "mdn-data": "^2.25.0", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.6", + "postcss-selector-parser": "^7.1.1", + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">=14" + "node": ">=18.12.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "stylelint": "^16.8.2" } }, - "node_modules/sucrase/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "node_modules/stylelint-scss/node_modules/css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", "dev": true, - "license": "ISC", + "license": "MIT", "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" + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" }, - "bin": { - "glob": "dist/esm/bin.mjs" + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/stylelint-scss/node_modules/css-tree/node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/stylelint-scss/node_modules/mdn-data": { + "version": "2.27.0", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.0.tgz", + "integrity": "sha512-/pUmP9UebM48q5BTqZd0yPnDjyRGhITbKh8cwa6/ZwjuDu8xq+VzmugLF7QNxpdaqqNH3J5nnv3yc8oARv096A==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/stylelint-value-no-unknown-custom-properties": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/stylelint-value-no-unknown-custom-properties/-/stylelint-value-no-unknown-custom-properties-6.1.1.tgz", + "integrity": "sha512-eQ1zidKD5t9zMEaskjGUY4W47lH76qMlmsDSmCAPEwtaGzB4Ls7ORTfysC1D6hamp2zFC+vN1vpQ+GFz3Tw3lw==", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": ">=16" + } + }, + "node_modules/stylelint/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/sucrase/node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "node_modules/stylelint/node_modules/balanced-match": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-3.0.1.tgz", + "integrity": "sha512-vjtV3hiLqYDNRoiAv0zC4QaGAMPomEoq83PRmYIofPswwZurCeWR5LByXm7SyoL0Zh5+2z0+HC7jG8gSZJUh0w==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 16" + } }, - "node_modules/sucrase/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/stylelint/node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/stylelint/node_modules/css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/stylelint/node_modules/file-entry-cache": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.2.tgz", + "integrity": "sha512-N2WFfK12gmrK1c1GXOqiAJ1tc5YE+R53zvQ+t5P8S5XhnmKYVB5eZEiLNZKDSmoG8wqqbF9EXYBBW/nef19log==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^6.1.20" + } + }, + "node_modules/stylelint/node_modules/flat-cache": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.20.tgz", + "integrity": "sha512-AhHYqwvN62NVLp4lObVXGVluiABTHapoB57EyegZVmazN+hhGhLTn3uZbOofoTw4DSDvVCadzzyChXhOAvy8uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cacheable": "^2.3.2", + "flatted": "^3.3.3", + "hookified": "^1.15.0" + } + }, + "node_modules/stylelint/node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/stylelint/node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/stylelint/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/stylelint/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/sucrase/node_modules/signal-exit": { + "node_modules/stylelint/node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/stylelint/node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", @@ -37901,6 +38256,72 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/stylelint/node_modules/string-width": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.1.tgz", + "integrity": "sha512-KpqHIdDL9KwYk22wEOg/VIqYbrnLeSApsKT/bSj6Ez7pn3CftUiLAv2Lccpq1ALcpLV9UX1Ppn92npZWu2w/aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylelint/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": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/stylelint/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/stylelint/node_modules/write-file-atomic": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-7.0.0.tgz", + "integrity": "sha512-YnlPC6JqnZl6aO4uRc+dx5PHguiR9S6WeoLtpxNT9wIG+BDya7ZNE1q7KOjVgaA73hKhKLpVPgJ5QA9THQ5BRg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT" + }, "node_modules/superagent": { "version": "10.2.3", "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.3.tgz", @@ -37962,6 +38383,49 @@ "node": ">=8" } }, + "node_modules/supports-hyperlinks": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-4.4.0.tgz", + "integrity": "sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^5.0.1", + "supports-color": "^10.2.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-5.0.1.tgz", + "integrity": "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -37982,6 +38446,12 @@ "dev": true, "license": "MIT" }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, "node_modules/svgo": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", @@ -38054,52 +38524,39 @@ "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==", "license": "MIT" }, - "node_modules/tailwindcss": { - "version": "3.4.18", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.18.tgz", - "integrity": "sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==", + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.6.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.2", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.7", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.47", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=14.0.0" + "node": ">=10.0.0" } }, - "node_modules/tailwindcss/node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "node_modules/table/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", - "bin": { - "jiti": "bin/jiti.js" + "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/tapable": { @@ -38323,29 +38780,6 @@ "node": "*" } }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/thirty-two": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz", @@ -38661,13 +39095,6 @@ } } }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/ts-jest": { "version": "29.4.5", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.5.tgz", @@ -38753,7 +39180,6 @@ "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -39029,9 +39455,8 @@ "version": "5.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -39194,6 +39619,19 @@ "node": ">=4" } }, + "node_modules/unicorn-magic": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.4.0.tgz", + "integrity": "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/union": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", @@ -39559,7 +39997,6 @@ "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -40659,7 +41096,6 @@ "integrity": "sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "1.6.1", "@vitest/runner": "1.6.1", @@ -41780,7 +42216,6 @@ "integrity": "sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -42095,26 +42530,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -42154,7 +42569,6 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", - "peer": true, "engines": { "node": ">=10.0.0" }, @@ -42322,7 +42736,6 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -42352,7 +42765,6 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -42371,7 +42783,6 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -42382,7 +42793,6 @@ "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.27.tgz", "integrity": "sha512-OIDwaflOaq4wC6YlPBy2L6ceKeKuF7DeTxx+jPzv1FHn9tCZ0ZwSRnUBxD05E3yed46fv/FWJbvR+Ud7x0L7zw==", "license": "MIT", - "peer": true, "dependencies": { "lib0": "^0.2.99" }, @@ -42428,14 +42838,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/zone.js": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.1.tgz", - "integrity": "sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/zustand": { "version": "4.5.7", "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", @@ -42468,6 +42870,8 @@ "name": "@holistix-forge/api-fetch", "version": "0.0.1", "dependencies": { + "@holistix-forge/simple-types": "0.0.1", + "event-source-polyfill": "^1.0.31", "tslib": "^2.3.0" } }, @@ -42494,6 +42898,17 @@ "name": "@holistix-forge/backend-engine", "version": "0.0.1", "dependencies": { + "@holistix-forge/log": "0.0.1", + "@holistix-forge/simple-types": "0.0.1", + "@opentelemetry/api": "^1.9.0", + "cookie-parser": "^1.4.7", + "express": "^4.21.2", + "express-openapi-validator": "^5.6.0", + "form-data": "^4.0.1", + "jsonwebtoken": "^9.0.2", + "nocache": "^4.0.0", + "pg": "^8.13.1", + "ts-essentials": "^10.0.4", "tslib": "^2.3.0" } }, @@ -42513,6 +42928,12 @@ "name": "@holistix-forge/log", "version": "0.0.1", "dependencies": { + "@holistix-forge/simple-types": "0.0.1", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/exporter-logs-otlp-proto": "^0.208.0", + "@opentelemetry/resources": "^2.2.0", + "@opentelemetry/sdk-logs": "^0.208.0", + "@opentelemetry/semantic-conventions": "^1.38.0", "tslib": "^2.3.0" } }, @@ -42589,6 +43010,18 @@ "name": "@holistix-forge/observability", "version": "0.0.1", "dependencies": { + "@opentelemetry/auto-instrumentations-web": "^0.54.0", + "@opentelemetry/exporter-logs-otlp-proto": "^0.208.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.208.0", + "@opentelemetry/exporter-trace-otlp-proto": "^0.208.0", + "@opentelemetry/instrumentation": "^0.208.0", + "@opentelemetry/instrumentation-express": "^0.57.0", + "@opentelemetry/instrumentation-http": "^0.208.0", + "@opentelemetry/resources": "^2.2.0", + "@opentelemetry/sdk-node": "^0.208.0", + "@opentelemetry/sdk-trace-base": "^2.2.0", + "@opentelemetry/sdk-trace-web": "^2.2.0", + "@opentelemetry/semantic-conventions": "^1.38.0", "tslib": "^2.3.0" } }, @@ -42596,7 +43029,8 @@ "name": "@holistix-forge/simple-types", "version": "0.0.1", "dependencies": { - "tslib": "^2.3.0" + "tslib": "^2.3.0", + "uuid": "^9.0.1" } }, "packages/types": { diff --git a/package.json b/package.json index af269efa..7c87cd3a 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,12 @@ "test": "npx nx run-many -t test --parallel=3", "validate": "npm run lint && npm run typecheck && npm run test", "validate:affected": "npx nx affected -t lint,typecheck,test --parallel=3", - "pre-push": "npm run validate:affected" + "pre-push": "npm run validate:affected", + "lint:tokens": "bash scripts/lint-unused-tokens.sh", + "stylelint": "stylelint 'packages/**/*.{scss,css}' --allow-empty-input", + "stylelint:fix": "stylelint 'packages/**/*.{scss,css}' --fix --allow-empty-input", + "test:visual": "npx nx run-many -t test-storybook --parallel=1", + "test:visual:update": "npx nx run-many -t test-storybook --parallel=1 -- --updateSnapshot" }, "private": true, "devDependencies": { @@ -91,17 +96,22 @@ "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "jest-environment-node": "^29.7.0", + "jest-image-snapshot": "^6.5.1", "jiti": "2.4.2", "jsdom": "~22.1.0", "jsonc-eslint-parser": "^2.1.0", "lint-staged": "^16.2.7", "nx": "^20.8.2", + "postcss-selector-parser": "^7.1.1", "prettier": "^2.6.2", "sass": "^1.83.4", "sass-embedded": "^1.83.4", "storybook": "8.5.0", + "stylelint": "^17.1.0", + "stylelint-config-standard-scss": "^14.0.0", + "stylelint-declaration-strict-value": "^1.10.11", + "stylelint-value-no-unknown-custom-properties": "^6.1.1", "supertest": "^7.1.4", - "tailwindcss": "^3.4.17", "ts-jest": "^29.1.0", "ts-node": "10.9.1", "tsc-files": "^1.1.4", diff --git a/packages/app-frontend/src/app/MobileBlockOverlay.tsx b/packages/app-frontend/src/app/MobileBlockOverlay.tsx index b5b1a420..bf4ca93b 100644 --- a/packages/app-frontend/src/app/MobileBlockOverlay.tsx +++ b/packages/app-frontend/src/app/MobileBlockOverlay.tsx @@ -80,7 +80,15 @@ export function MobileBlockOverlay({ {!fullscreen && (
- - + + - + Organization - + Projects - + Actions @@ -130,28 +182,55 @@ const ProjectsList = () => { return ( - - + + {org.name} - + {projectCount} - -
+ +
- - @@ -167,39 +246,83 @@ const ProjectsList = () => { {/* Projects Section */}
-
-

My Projects

+
+

+ My Projects +

{(!orgsData || orgsData.length === 0) && ( )}
- - + + - + Name - + Organization - + Owner - + Public - + Link - + Actions @@ -208,30 +331,70 @@ const ProjectsList = () => { {data?.map((p) => ( ))} - - + + - -
+ +
- -
+ +
- -
+ +
- -
+ +
- -
+ +
@@ -273,7 +436,7 @@ const ProjectsListItem = ({ // Parse organization name: if matches "xxxx:yyyyy-org", extract username const renderOrganization = () => { - if (!orgData) return ...; + if (!orgData) return ...; const orgName = orgData.name; const match = orgName.match(/^(.+)-org$/); @@ -281,62 +444,69 @@ const ProjectsListItem = ({ const content = match && ownerStatus === 'success' && ownerData ? ( // Pattern matches: "xxxx:yyyyy-org" → show UserInline" - - + + ) : ( // Regular organization name - {orgName} + {orgName} ); // Wrap in link to org dashboard - return ( - - {content} - - ); + return {content}; }; return ( - - + + {project.name} - + {renderOrganization()} - + {ownerStatus === 'success' && ownerData ? ( - + ) : ( - ... + ... )} - + {project.public ? 'Public' : 'Private'} - + Open - + diff --git a/packages/app-frontend/src/app/pages/organization/dashboard.tsx b/packages/app-frontend/src/app/pages/organization/dashboard.tsx index 6ba4c556..04b89b67 100644 --- a/packages/app-frontend/src/app/pages/organization/dashboard.tsx +++ b/packages/app-frontend/src/app/pages/organization/dashboard.tsx @@ -42,9 +42,14 @@ export const OrganizationDashboard = () => { return (
-
- -

Loading organization...

+
+ +

+ Loading organization... +

); @@ -54,8 +59,18 @@ export const OrganizationDashboard = () => { return (
-
-

Organization not found

+
+

+ Organization not found +

); @@ -67,11 +82,30 @@ export const OrganizationDashboard = () => {
-
+
{/* Organization Header */} -
-

{org.name}

-
+
+

+ {org.name} +

+
{orgProjects.length} project{orgProjects.length !== 1 ? 's' : ''} @@ -79,7 +113,7 @@ export const OrganizationDashboard = () => {
{/* Quick Actions */} -
+
{
{/* Projects Table */} -
-
-

Projects

+
+
+

+ Projects +

{orgProjects.length > 0 ? ( - - + + - + Project Name - + Public - + Actions @@ -125,28 +183,52 @@ export const OrganizationDashboard = () => { {orgProjects.map((project) => ( - + {project.name} - + {project.public ? 'Public' : 'Private'} - + Open @@ -156,8 +238,18 @@ export const OrganizationDashboard = () => { ) : ( -
-

No projects yet

+
+

+ No projects yet +

diff --git a/packages/app-frontend/src/app/pages/organization/organization-context.tsx b/packages/app-frontend/src/app/pages/organization/organization-context.tsx index 6a766dbe..120eedd0 100644 --- a/packages/app-frontend/src/app/pages/organization/organization-context.tsx +++ b/packages/app-frontend/src/app/pages/organization/organization-context.tsx @@ -23,18 +23,33 @@ const StartOrganizationBox = ({ ); return ( -
-
- -

+

+
+ +

Organization has been shut down due to inactivity.

-

+

Click the button below to allocate a gateway and start the organization.

-
+
@@ -88,9 +103,14 @@ export const OrganizationContext = ({ // Show loading state if (gatewayStatus === 'pending') { return ( -
- -

Loading gateway information...

+
+ +

+ Loading gateway information... +

); } diff --git a/packages/app-frontend/src/app/pages/project/editor/node-editor/node-editor-view.tsx b/packages/app-frontend/src/app/pages/project/editor/node-editor/node-editor-view.tsx index 7e6bed4f..ab30ad83 100644 --- a/packages/app-frontend/src/app/pages/project/editor/node-editor/node-editor-view.tsx +++ b/packages/app-frontend/src/app/pages/project/editor/node-editor/node-editor-view.tsx @@ -12,7 +12,7 @@ export const NodeEditorView = ({ viewId }: { viewId: string }) => { const project = useProject(); return ( -
+
); diff --git a/packages/app-frontend/src/app/pages/project/editor/node-editor/node-editor.scss b/packages/app-frontend/src/app/pages/project/editor/node-editor/node-editor.scss index 68317217..fddbc737 100644 --- a/packages/app-frontend/src/app/pages/project/editor/node-editor/node-editor.scss +++ b/packages/app-frontend/src/app/pages/project/editor/node-editor/node-editor.scss @@ -1,9 +1,9 @@ .terminal-container { - box-shadow: 0 0 6px -1px var(--ca-black-6), 0 2px 4px -1px var(--ca-black-8); + box-shadow: var(--shadow-node-editor); width: fit-content; /* .xterm-viewport { - // background-color: var(--color-node-background) !important; + // background-color: var(--color-bg-node) !important; } .xterm-text-layer { // opacity: 0.5; diff --git a/packages/app-frontend/src/app/pages/project/editor/resources-page.tsx b/packages/app-frontend/src/app/pages/project/editor/resources-page.tsx index bf2a4514..72ab5702 100644 --- a/packages/app-frontend/src/app/pages/project/editor/resources-page.tsx +++ b/packages/app-frontend/src/app/pages/project/editor/resources-page.tsx @@ -43,7 +43,7 @@ export const ResourcePage = () => { }} > -
+
setDisplayNewServerForm(true)}> {array.map((ucid) => ( ( -
-
- +
+
+ {progress || 0}% -

{message}

+

{message}

); @@ -21,8 +31,15 @@ type ErrorProps = { }; export const ProjectError = ({ message }: ErrorProps) => ( -
- -

{message}

+
+ +

+ {message} +

); diff --git a/packages/app-frontend/src/app/pages/project/project-wrapper.tsx b/packages/app-frontend/src/app/pages/project/project-wrapper.tsx index 93f62229..6f6b4688 100644 --- a/packages/app-frontend/src/app/pages/project/project-wrapper.tsx +++ b/packages/app-frontend/src/app/pages/project/project-wrapper.tsx @@ -49,18 +49,33 @@ const StartOrganizationBox = ({ ); return ( -
-
- -

+

+
+ +

Organization has been shut down due to inactivity.

-

+

Click the button below to allocate a gateway and start the organization.

-
+
@@ -109,9 +124,14 @@ export const ProjectWrapper = ({ children }: { children: ReactNode }) => { // Check if user is authenticated if (userStatus === 'success' && !currentUserData?.user?.user_id) { return ( -
- -

Please login first, then come back here

+
+ +

+ Please login first, then come back here +

 

diff --git a/packages/app-frontend/src/index.scss b/packages/app-frontend/src/index.scss index a5803a02..fedf1fbb 100644 --- a/packages/app-frontend/src/index.scss +++ b/packages/app-frontend/src/index.scss @@ -1,12 +1,8 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - .go-fullscreen-button { position: fixed; bottom: 40px; right: 16px; - z-index: 10000; + z-index: var(--z-toast); display: none; } diff --git a/packages/collab-engine/src/lib/frontend/YjsAwareness.css b/packages/collab-engine/src/lib/frontend/YjsAwareness.css index a8d6eb10..9c39c365 100644 --- a/packages/collab-engine/src/lib/frontend/YjsAwareness.css +++ b/packages/collab-engine/src/lib/frontend/YjsAwareness.css @@ -1,5 +1,5 @@ .yRemoteSelection { - background-color: var(--ca-orange-45); + background-color: rgba(250, 129, 0, 0.5); } .yRemoteSelectionHead { position: absolute; diff --git a/packages/collab-engine/src/lib/frontend/YjsCssStylesheet.ts b/packages/collab-engine/src/lib/frontend/YjsCssStylesheet.ts index b5f7aa18..d39236a1 100644 --- a/packages/collab-engine/src/lib/frontend/YjsCssStylesheet.ts +++ b/packages/collab-engine/src/lib/frontend/YjsCssStylesheet.ts @@ -6,7 +6,7 @@ const userCss: { } = {}; export const buildUserCss = (key: number, color: string | undefined) => { - color = color || 'var(--c-orange-4)'; + color = color || 'var(--orange-400)'; const k = `${key}`; if (!userCss[k]) { diff --git a/packages/collab-engine/src/lib/frontend/context.scss b/packages/collab-engine/src/lib/frontend/context.scss index 2e74c190..21ff1b46 100644 --- a/packages/collab-engine/src/lib/frontend/context.scss +++ b/packages/collab-engine/src/lib/frontend/context.scss @@ -1,3 +1,8 @@ +:root { + // --- Collaboration domain tokens --- + --color-yjs-awareness-default: var(--orange-500); +} + .collab-error-overlay { position: fixed; top: 0; @@ -5,34 +10,34 @@ right: 0; bottom: 0; backdrop-filter: blur(4px); - background-color: rgba(0, 0, 0, 0.5); - z-index: 1000; + background-color: var(--alpha-black-55); + z-index: var(--z-toast); display: flex; align-items: center; justify-content: center; .error-content { - background-color: var(--color-background); + background-color: var(--color-bg-app); border-radius: 8px; padding: 24px; max-width: 400px; text-align: center; .error-title { - font-size: 18px; + font-size: var(--font-size-xl); font-weight: 600; margin-bottom: 12px; color: var(--color-error); } .error-message { - font-size: 14px; + font-size: var(--font-size-md); color: var(--color-text); margin-bottom: 8px; } .error-count { - font-size: 12px; + font-size: var(--font-size-sm); color: var(--color-text-muted); } } diff --git a/packages/modules/airtable/.storybook/preview.ts b/packages/modules/airtable/.storybook/preview.ts index 78709de6..7a586e21 100644 --- a/packages/modules/airtable/.storybook/preview.ts +++ b/packages/modules/airtable/.storybook/preview.ts @@ -6,25 +6,25 @@ import '@holistix-forge/whiteboard/style'; // import '../src/lib/index.scss'; const preview: Preview = { - parameters: { - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/, - }, - }, - backgrounds: { - default: 'dark', - values: [ - { - name: 'dark', - value: 'var(--color-background)', - }, - ], + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, + backgrounds: { + default: 'dark', + values: [ + { + name: 'dark', + value: 'var(--color-bg-app)', }, + ], }, - decorators: [GlobalWrapper], - tags: [], + }, + decorators: [GlobalWrapper], + tags: [], }; export default preview; diff --git a/packages/modules/airtable/.storybook/test-runner.js b/packages/modules/airtable/.storybook/test-runner.js new file mode 100644 index 00000000..4b458ca7 --- /dev/null +++ b/packages/modules/airtable/.storybook/test-runner.js @@ -0,0 +1,108 @@ +const path = require('path'); +const { toMatchImageSnapshot } = require('jest-image-snapshot'); + +const PKG_ROOT = path.resolve(__dirname, '..'); + +const SNAPSHOT_OPTS = { + failureThreshold: 0.002, // 0.2% pixel difference allowed + failureThresholdType: 'percent', + customSnapshotsDir: + process.env['SNAPSHOT_DIR'] || path.join(PKG_ROOT, '__screenshots__'), + customDiffDir: + process.env['DIFF_DIR'] || path.join(PKG_ROOT, '__diff_output__'), +}; + +/** + * Wait for the page to be ready without relying on networkidle + * (which times out when stories load external resources like avatars). + */ +async function waitUntilReady(page) { + await page.waitForLoadState('domcontentloaded'); + await page.waitForLoadState('load'); + await page.evaluate(() => document.fonts.ready); + // Small settle time for React re-renders + await page.waitForTimeout(300); +} + +module.exports = { + setup() { + expect.extend({ toMatchImageSnapshot }); + }, + + async preVisit(page) { + // Block external image requests to prevent navigation context destruction. + // Stories load random avatars from pravatar.cc which cause flaky test failures. + await page.route('**/*pravatar*/**', (route) => route.abort()); + await page.route('**/*.pravatar.*/**', (route) => route.abort()); + }, + + async postVisit(page, context) { + // Disable CSS animations/transitions to avoid flaky diffs + await page.addStyleTag({ + content: ` + *, *::before, *::after { + animation-duration: 0s !important; + animation-delay: 0s !important; + transition-duration: 0s !important; + transition-delay: 0s !important; + } + `, + }); + + // Wait for page to be ready (DOM, resources, fonts — skip networkidle) + await waitUntilReady(page); + + const root = page.locator('#storybook-root'); + + try { + await root.waitFor({ state: 'visible', timeout: 5000 }); + } catch { + // Root not visible — fall back to full page screenshot + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + // Check if a Radix dialog portal is present (renders outside #storybook-root) + const dialogContent = page.locator('[data-radix-portal] [role="dialog"]'); + const hasDialog = await dialogContent.count().then((c) => c > 0); + + if (hasDialog) { + // Screenshot the dialog content instead of the empty root + try { + await dialogContent + .first() + .waitFor({ state: 'visible', timeout: 3000 }); + const screenshot = await dialogContent.first().screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } catch { + // Dialog not visible, fall through to page screenshot + } + } + + // Check if root content is too small (likely clipped by overflow) + const box = await root.boundingBox(); + if (box && (box.width < 10 || box.height < 10)) { + // Component is too small to be meaningful — screenshot full page + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + const screenshot = await root.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + }, +}; diff --git a/packages/modules/airtable/src/lib/components/forms/new-base.scss b/packages/modules/airtable/src/lib/components/forms/new-base.scss index b507cce7..07d69f9c 100644 --- a/packages/modules/airtable/src/lib/components/forms/new-base.scss +++ b/packages/modules/airtable/src/lib/components/forms/new-base.scss @@ -24,11 +24,11 @@ background-color: var(--success-background, rgba(34, 197, 94, 0.1)); border: 1px solid var(--success-border, rgba(34, 197, 94, 0.3)); border-radius: 0.375rem; - font-size: 0.875rem; + font-size: var(--font-size-sm); color: var(--success-text, #22c55e); .back-button { - font-size: 0.75rem; + font-size: var(--font-size-sm); padding: 0.25rem 0.5rem; } } @@ -61,7 +61,7 @@ .base-item { padding: 0.75rem; - border: 1px solid #e0e0e0; + border: 1px solid var(--c-neutral-border-form); border-radius: 0.375rem; cursor: pointer; transition: all 0.2s ease; @@ -71,7 +71,7 @@ } &.selected { - border-color: #007acc; + border-color: var(--c-brand-highlight-blue); &:hover { background-color: rgba(255, 255, 255, 0.1); @@ -84,14 +84,14 @@ } .base-description { - font-size: 0.875rem; + font-size: var(--font-size-sm); color: var(--text-muted); margin-bottom: 0.25rem; } .base-id, .base-permission { - font-size: 0.75rem; + font-size: var(--font-size-sm); color: var(--text-muted); font-family: monospace; } diff --git a/packages/modules/airtable/src/lib/components/node-airtable/airtable-base-table-list.scss b/packages/modules/airtable/src/lib/components/node-airtable/airtable-base-table-list.scss index e632792e..f26c73a5 100644 --- a/packages/modules/airtable/src/lib/components/node-airtable/airtable-base-table-list.scss +++ b/packages/modules/airtable/src/lib/components/node-airtable/airtable-base-table-list.scss @@ -28,7 +28,7 @@ .table-item { background: rgba(255, 255, 255, 0.1); - border: 1px solid #404040; + border: 1px solid var(--c-neutral-border); border-radius: 8px; padding: 16px; cursor: grab; @@ -43,8 +43,8 @@ &:hover { background: rgba(54, 54, 54, 0.2) !important; transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); - border-color: #4dabf7; + box-shadow: var(--shadow-card-hover); + border-color: var(--c-highlight-blue); } &.dragging { @@ -54,8 +54,8 @@ .table-item-header { .table-item-title { - color: #ffffff; - font-size: 16px; + color: var(--white); + font-size: var(--font-size-lg); font-weight: 600; margin: 0 0 8px 0; line-height: 1.4; @@ -69,10 +69,10 @@ .table-count, .table-fields { background: rgba(77, 171, 247, 0.2); - color: #4dabf7; + color: var(--c-highlight-blue); padding: 2px 6px; border-radius: 10px; - font-size: 11px; + font-size: var(--font-size-xs); font-weight: 500; } } @@ -93,23 +93,23 @@ border-radius: 4px; .field-label { - color: #868e96; - font-size: 12px; + color: var(--c-neutral-text-muted); + font-size: var(--font-size-sm); font-weight: 500; min-width: 60px; } .field-name { - color: #ffffff; - font-size: 13px; + color: var(--white); + font-size: var(--font-size-md); font-weight: 500; } } .table-sample-records { .sample-label { - color: #868e96; - font-size: 12px; + color: var(--c-neutral-text-muted); + font-size: var(--font-size-sm); font-weight: 500; display: block; margin-bottom: 4px; @@ -121,12 +121,12 @@ gap: 4px; .sample-record { - color: #ddd; - font-size: 12px; + color: var(--c-neutral-text-light); + font-size: var(--font-size-sm); padding: 4px 8px; background: rgba(255, 255, 255, 0.05); border-radius: 3px; - border-left: 3px solid #4dabf7; + border-left: 3px solid var(--c-highlight-blue); } } } @@ -145,10 +145,10 @@ .view-badge { background: rgba(255, 255, 255, 0.1); - color: #ffffff; + color: var(--white); padding: 2px 6px; border-radius: 10px; - font-size: 10px; + font-size: var(--font-size-2xs); font-weight: 500; text-transform: capitalize; } @@ -159,15 +159,15 @@ // Color variations for different table types .table-item { &.has-records { - border-left: 4px solid #51cf66; + border-left: 4px solid var(--c-highlight-green); } &.empty-table { - border-left: 4px solid #868e96; + border-left: 4px solid var(--c-neutral-text-muted); opacity: 0.7; } &.many-fields { - border-left: 4px solid #ffd43b; + border-left: 4px solid var(--c-highlight-yellow); } } diff --git a/packages/modules/airtable/src/lib/components/node-airtable/airtable-right-panel.scss b/packages/modules/airtable/src/lib/components/node-airtable/airtable-right-panel.scss index 3ff54778..4a76328c 100644 --- a/packages/modules/airtable/src/lib/components/node-airtable/airtable-right-panel.scss +++ b/packages/modules/airtable/src/lib/components/node-airtable/airtable-right-panel.scss @@ -6,10 +6,10 @@ display: flex; flex-direction: column; background: rgba(54, 54, 54, 0.1); - border: 1px solid #404040; + border: 1px solid var(--c-neutral-border); .airtable-header { - border-bottom: 1px solid #404040; + border-bottom: 1px solid var(--c-neutral-border); margin-bottom: 20px; display: flex; align-items: center; @@ -24,7 +24,7 @@ display: flex; align-items: center; justify-content: center; - font-size: 18px; + font-size: var(--font-size-xl); background: rgba(255, 255, 255, 0.1); border-radius: 6px; flex-shrink: 0; @@ -32,8 +32,8 @@ .airtable-h2 { margin: 0; - color: #ffffff; - font-size: 18px; + color: var(--white); + font-size: var(--font-size-xl); font-weight: 600; display: inline; line-height: 40px; @@ -48,19 +48,19 @@ .airtable-count { background: rgba(77, 171, 247, 0.2); - color: #4dabf7; + color: var(--c-highlight-blue); padding: 4px 8px; border-radius: 12px; - font-size: 12px; + font-size: var(--font-size-sm); font-weight: 500; } .airtable-permission { background: rgba(255, 255, 255, 0.1); - color: #ffffff; + color: var(--white); padding: 4px 8px; border-radius: 12px; - font-size: 12px; + font-size: var(--font-size-sm); font-weight: 500; text-transform: capitalize; } @@ -76,8 +76,8 @@ p { margin: 0; - color: #ddd; - font-size: 14px; + color: var(--c-neutral-text-light); + font-size: var(--font-size-md); line-height: 1.5; } } diff --git a/packages/modules/airtable/src/lib/components/node-airtable/airtable-table.scss b/packages/modules/airtable/src/lib/components/node-airtable/airtable-table.scss index c87c4126..390d8d76 100644 --- a/packages/modules/airtable/src/lib/components/node-airtable/airtable-table.scss +++ b/packages/modules/airtable/src/lib/components/node-airtable/airtable-table.scss @@ -1,3 +1,42 @@ +:root { + // --- Status Colors (task/kanban) — shared by airtable + notion --- + --c-status-red: #ef4444; + --c-status-orange: #f97316; + --c-status-yellow: #eab308; + --c-status-green: #22c55e; + --c-status-blue: #3b82f6; + --c-status-purple: #a855f7; + --c-status-pink: #ec4899; + --c-status-gray: #6b7280; + --c-status-brown: #a16207; + + // --- Accent / Highlight Colors --- + --c-highlight-blue: #4dabf7; + --c-highlight-green: #51cf66; + --c-highlight-red: #ff6b6b; + --c-highlight-yellow: #ffd43b; + --c-highlight-orange: #ff922b; + --c-highlight-orange-accent: #ff6b35; + --c-highlight-purple: #cc5de8; + --c-highlight-pink: #f783ac; + + // --- UI Neutral Colors (airtable/notion shared) --- + --c-neutral-border: #404040; + --c-neutral-border-light: #444; + --c-neutral-bg-dark: #222; + --c-neutral-bg-darker: #2d2d2d; + --c-neutral-bg-option: #333; + --c-neutral-text-muted: #868e96; + --c-neutral-text-light: #ddd; + --c-neutral-text-dim: #bbb; + --c-neutral-text-dimmer: #adb5bd; + --c-neutral-text-subtle: #999; + --c-neutral-text-faint: #666; + --c-neutral-border-form: #e0e0e0; + --c-neutral-border-input: #e5e7eb; + --c-neutral-select-handle: #aaa; +} + .airtable-table { .airtable-logo { width: 24px; @@ -9,13 +48,13 @@ align-items: center; gap: 12px; padding: 16px; - border-bottom: 1px solid #404040; + border-bottom: 1px solid var(--c-neutral-border); .airtable-h2 { margin: 0; - font-size: 18px; + font-size: var(--font-size-xl); font-weight: 600; - color: #ffffff; + color: var(--white); } .airtable-view-switcher { @@ -24,20 +63,20 @@ gap: 8px; button { - background: #222; - color: #fff; - border: 1px solid #444; + background: var(--c-neutral-bg-dark); + color: var(--white); + border: 1px solid var(--c-neutral-border-light); border-radius: 4px; padding: 4px 12px; cursor: pointer; - font-size: 13px; + font-size: var(--font-size-md); transition: background 0.2s, color 0.2s; &.active, &:hover { - background: #ff6b35; - color: #222; - border-color: #ff6b35; + background: var(--c-highlight-orange-accent); + color: var(--c-neutral-bg-dark); + border-color: var(--c-highlight-orange-accent); } } } @@ -52,20 +91,20 @@ h3 { margin: 0 0 8px 0; - font-size: 16px; - color: #ffffff; + font-size: var(--font-size-lg); + color: var(--white); } p { margin: 0; - color: #ddd; - font-size: 14px; + color: var(--c-neutral-text-light); + font-size: var(--font-size-md); } } .airtable-list-content { .airtable-list-item { - border: 1px solid #404040; + border: 1px solid var(--c-neutral-border); border-radius: 4px; padding: 12px; margin-bottom: 8px; @@ -74,7 +113,7 @@ .airtable-record-id { font-weight: 600; margin-bottom: 8px; - color: #ffffff; + color: var(--white); } .airtable-record-fields { @@ -82,16 +121,16 @@ display: flex; gap: 8px; margin-bottom: 4px; - font-size: 14px; + font-size: var(--font-size-md); .field-name { font-weight: 500; - color: #bbb; + color: var(--c-neutral-text-dim); min-width: 80px; } .field-value { - color: #ffffff; + color: var(--white); } } } @@ -109,32 +148,32 @@ .airtable-kanban-subgroup-selector { padding: 12px 16px; - border-bottom: 1px solid #404040; + border-bottom: 1px solid var(--c-neutral-border); background: rgba(255, 255, 255, 0.05); label { font-weight: 500; margin-right: 8px; - color: #ffffff; + color: var(--white); } .airtable-subgroup-select { padding: 6px 12px; - border: 1px solid #404040; + border: 1px solid var(--c-neutral-border); border-radius: 6px; background: rgba(255, 255, 255, 0.1); - font-size: 14px; - color: #ffffff; + font-size: var(--font-size-md); + color: var(--white); &:focus { outline: none; - border-color: #4dabf7; - box-shadow: 0 0 0 3px rgba(77, 171, 247, 0.1); + border-color: var(--c-highlight-blue); + box-shadow: var(--shadow-focus-blue); } option { - background: #333; - color: #ffffff; + background: var(--c-neutral-bg-option); + color: var(--white); } } } @@ -152,7 +191,7 @@ flex: 1 1 280px; background: rgba(255, 255, 255, 0.1); border-radius: 8px; - border: 1px solid #404040; + border: 1px solid var(--c-neutral-border); display: flex; flex-direction: column; transition: all 0.2s ease; @@ -162,12 +201,12 @@ &:hover { background: rgba(54, 54, 54, 0.2) !important; transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + box-shadow: var(--shadow-card-hover); } &.drag-over { - border-color: #4dabf7; - box-shadow: 0 0 0 3px rgba(77, 171, 247, 0.1); + border-color: var(--c-highlight-blue); + box-shadow: var(--shadow-focus-blue); } &.dragging { @@ -178,7 +217,7 @@ .airtable-kanban-column-header { padding: 12px 16px; - border-bottom: 1px solid #404040; + border-bottom: 1px solid var(--c-neutral-border); background: rgba(255, 255, 255, 0.05); border-radius: 8px 8px 0 0; } @@ -198,16 +237,16 @@ } .airtable-kanban-title { - font-size: 14px; + font-size: var(--font-size-md); font-weight: 600; - color: #ffffff; + color: var(--white); margin: 0; flex: 1; } .airtable-kanban-count { - font-size: 12px; - color: #868e96; + font-size: var(--font-size-sm); + color: var(--c-neutral-text-muted); font-weight: 500; } @@ -222,7 +261,7 @@ margin-bottom: 12px; border-radius: 6px; background: rgba(255, 255, 255, 0.05); - border: 1px solid #404040; + border: 1px solid var(--c-neutral-border); transition: all 0.2s ease; position: relative; overflow: hidden; @@ -230,12 +269,12 @@ &:hover { background: rgba(54, 54, 54, 0.2) !important; transform: translateY(-1px); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); + box-shadow: var(--shadow-card-md-hover); } &.drag-over { - border-color: #4dabf7; - box-shadow: 0 0 0 2px rgba(77, 171, 247, 0.1); + border-color: var(--c-highlight-blue); + box-shadow: var(--shadow-focus-blue-sm); } &:last-child { @@ -245,7 +284,7 @@ .airtable-kanban-subgroup-header { padding: 8px 12px; - border-bottom: 1px solid #404040; + border-bottom: 1px solid var(--c-neutral-border); display: flex; align-items: center; gap: 6px; @@ -261,15 +300,15 @@ } .airtable-kanban-subgroup-title { - font-size: 12px; + font-size: var(--font-size-sm); font-weight: 500; - color: #bbb; + color: var(--c-neutral-text-dim); flex: 1; } .airtable-kanban-subgroup-count { - font-size: 11px; - color: #868e96; + font-size: var(--font-size-xs); + color: var(--c-neutral-text-muted); font-weight: 500; } @@ -280,34 +319,34 @@ // Task status colors - Gallery theme compatible .task-status { &.bg-red { - background-color: #ef4444; + background-color: var(--c-status-red); } &.bg-orange { - background-color: #f97316; + background-color: var(--c-status-orange); } &.bg-yellow { - background-color: #eab308; + background-color: var(--c-status-yellow); } &.bg-green { - background-color: #22c55e; + background-color: var(--c-status-green); } &.bg-blue { - background-color: #3b82f6; + background-color: var(--c-status-blue); } &.bg-purple { - background-color: #a855f7; + background-color: var(--c-status-purple); } &.bg-pink { - background-color: #ec4899; + background-color: var(--c-status-pink); } &.bg-gray { - background-color: #6b7280; + background-color: var(--c-status-gray); } &.bg-brown { - background-color: #a16207; + background-color: var(--c-status-brown); } &.bg-default { - background-color: #6b7280; + background-color: var(--c-status-gray); } } @@ -319,14 +358,14 @@ h3 { margin: 0 0 8px 0; - font-size: 16px; - color: #ffffff; + font-size: var(--font-size-lg); + color: var(--white); } p { margin: 0; - color: #ddd; - font-size: 14px; + color: var(--c-neutral-text-light); + font-size: var(--font-size-md); } } @@ -338,7 +377,7 @@ .airtable-gallery-card { background: rgba(255, 255, 255, 0.1); - border: 1px solid #404040; + border: 1px solid var(--c-neutral-border); border-radius: 8px; padding: 16px; min-height: 120px; @@ -353,13 +392,13 @@ &:hover { background: rgba(54, 54, 54, 0.2) !important; transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + box-shadow: var(--shadow-card-hover); } .gallery-card-header { .gallery-card-title { - color: #ffffff; - font-size: 14px; + color: var(--white); + font-size: var(--font-size-md); font-weight: 600; margin: 0 0 8px 0; line-height: 1.4; @@ -371,22 +410,22 @@ flex-direction: column; gap: 8px; margin-top: auto; - font-size: 12px; - color: #ddd; + font-size: var(--font-size-sm); + color: var(--c-neutral-text-light); .gallery-field { display: flex; justify-content: space-between; align-items: center; gap: 8px; - font-size: 12px; + font-size: var(--font-size-sm); .field-label { - color: #bbb; + color: var(--c-neutral-text-dim); font-weight: 500; min-width: 80px; } .field-value { - color: #fff; + color: var(--white); text-align: right; max-width: 60%; overflow: hidden; @@ -403,7 +442,7 @@ // Enhanced gallery card styles for kanban compatibility - Gallery theme .airtable-gallery-card { background: rgba(255, 255, 255, 0.1); - border: 1px solid #404040; + border: 1px solid var(--c-neutral-border); border-radius: 8px; padding: 16px; min-height: 120px; @@ -418,7 +457,7 @@ &:hover { background: rgba(54, 54, 54, 0.2) !important; transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + box-shadow: var(--shadow-card-hover); } &:last-child { @@ -431,9 +470,9 @@ } .gallery-card-title { - font-size: 14px; + font-size: var(--font-size-md); font-weight: 600; - color: #ffffff; + color: var(--white); margin: 0; line-height: 1.4; } @@ -443,28 +482,28 @@ flex-direction: column; gap: 8px; margin-top: auto; - font-size: 12px; - color: #ddd; + font-size: var(--font-size-sm); + color: var(--c-neutral-text-light); .gallery-field { display: flex; justify-content: space-between; align-items: center; gap: 8px; - font-size: 12px; + font-size: var(--font-size-sm); &:last-child { margin-bottom: 0; } .field-label { - color: #bbb; + color: var(--c-neutral-text-dim); font-weight: 500; min-width: 80px; } .field-value { - color: #fff; + color: var(--white); text-align: right; max-width: 60%; overflow: hidden; @@ -484,14 +523,14 @@ h3 { margin: 0 0 8px 0; - font-size: 16px; - color: #ffffff; + font-size: var(--font-size-lg); + color: var(--white); } p { margin: 0; - color: #ddd; - font-size: 14px; + color: var(--c-neutral-text-light); + font-size: var(--font-size-md); } } @@ -506,7 +545,7 @@ background: rgba(255, 255, 255, 0.1); border-radius: 8px; padding: 16px; - border: 1px solid #404040; + border: 1px solid var(--c-neutral-border); position: relative; overflow: hidden; transition: all 0.2s ease; @@ -514,24 +553,24 @@ &:hover { background: rgba(54, 54, 54, 0.2) !important; transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + box-shadow: var(--shadow-card-hover); } h4 { margin: 0 0 12px 0; - font-size: 14px; + font-size: var(--font-size-md); font-weight: 600; - color: #ffffff; + color: var(--white); } .airtable-kanban-cards { .airtable-kanban-card { background: rgba(255, 255, 255, 0.1); - border: 1px solid #404040; + border: 1px solid var(--c-neutral-border); border-radius: 6px; padding: 12px; margin-bottom: 8px; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); + box-shadow: var(--shadow-sm); position: relative; overflow: hidden; transition: all 0.2s ease; @@ -539,13 +578,13 @@ &:hover { background: rgba(54, 54, 54, 0.2) !important; transform: translateY(-1px); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); + box-shadow: var(--shadow-card-md-hover); } .card-title { font-weight: 600; margin-bottom: 8px; - color: #ffffff; + color: var(--white); } .card-content { @@ -553,14 +592,14 @@ display: flex; justify-content: space-between; margin-bottom: 4px; - font-size: 12px; + font-size: var(--font-size-sm); span:first-child { - color: #bbb; + color: var(--c-neutral-text-dim); } span:last-child { - color: #ffffff; + color: var(--white); font-weight: 500; } } @@ -587,7 +626,7 @@ background: rgba(255, 255, 255, 0.1); border-radius: 8px; padding: 16px; - border: 1px solid #404040; + border: 1px solid var(--c-neutral-border); transition: all 0.2s ease; display: flex; flex-direction: column; @@ -598,11 +637,11 @@ .kanban-column:hover { background: rgba(54, 54, 54, 0.2) !important; transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + box-shadow: var(--shadow-card-hover); } .kanban-column.drag-over { background: rgba(54, 54, 54, 0.3) !important; - border-color: #4dabf7 !important; + border-color: var(--c-highlight-blue) !important; } .kanban-column .column-header { margin-bottom: 16px; @@ -612,14 +651,14 @@ gap: 8px; } .kanban-column .column-header h4 { - color: #ffffff; - font-size: 14px; + color: var(--white); + font-size: var(--font-size-md); font-weight: 600; margin: 0; } .kanban-column .record-count { - color: #868e96; - font-size: 12px; + color: var(--c-neutral-text-muted); + font-size: var(--font-size-sm); margin-left: 20px; } diff --git a/packages/modules/chats/.storybook/preview.ts b/packages/modules/chats/.storybook/preview.ts index 7f2290b1..7f82cf53 100644 --- a/packages/modules/chats/.storybook/preview.ts +++ b/packages/modules/chats/.storybook/preview.ts @@ -18,7 +18,7 @@ const preview: Preview = { values: [ { name: 'dark', - value: 'var(--color-background)', + value: 'var(--color-bg-app)', }, ], }, diff --git a/packages/modules/chats/.storybook/test-runner.js b/packages/modules/chats/.storybook/test-runner.js new file mode 100644 index 00000000..4b458ca7 --- /dev/null +++ b/packages/modules/chats/.storybook/test-runner.js @@ -0,0 +1,108 @@ +const path = require('path'); +const { toMatchImageSnapshot } = require('jest-image-snapshot'); + +const PKG_ROOT = path.resolve(__dirname, '..'); + +const SNAPSHOT_OPTS = { + failureThreshold: 0.002, // 0.2% pixel difference allowed + failureThresholdType: 'percent', + customSnapshotsDir: + process.env['SNAPSHOT_DIR'] || path.join(PKG_ROOT, '__screenshots__'), + customDiffDir: + process.env['DIFF_DIR'] || path.join(PKG_ROOT, '__diff_output__'), +}; + +/** + * Wait for the page to be ready without relying on networkidle + * (which times out when stories load external resources like avatars). + */ +async function waitUntilReady(page) { + await page.waitForLoadState('domcontentloaded'); + await page.waitForLoadState('load'); + await page.evaluate(() => document.fonts.ready); + // Small settle time for React re-renders + await page.waitForTimeout(300); +} + +module.exports = { + setup() { + expect.extend({ toMatchImageSnapshot }); + }, + + async preVisit(page) { + // Block external image requests to prevent navigation context destruction. + // Stories load random avatars from pravatar.cc which cause flaky test failures. + await page.route('**/*pravatar*/**', (route) => route.abort()); + await page.route('**/*.pravatar.*/**', (route) => route.abort()); + }, + + async postVisit(page, context) { + // Disable CSS animations/transitions to avoid flaky diffs + await page.addStyleTag({ + content: ` + *, *::before, *::after { + animation-duration: 0s !important; + animation-delay: 0s !important; + transition-duration: 0s !important; + transition-delay: 0s !important; + } + `, + }); + + // Wait for page to be ready (DOM, resources, fonts — skip networkidle) + await waitUntilReady(page); + + const root = page.locator('#storybook-root'); + + try { + await root.waitFor({ state: 'visible', timeout: 5000 }); + } catch { + // Root not visible — fall back to full page screenshot + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + // Check if a Radix dialog portal is present (renders outside #storybook-root) + const dialogContent = page.locator('[data-radix-portal] [role="dialog"]'); + const hasDialog = await dialogContent.count().then((c) => c > 0); + + if (hasDialog) { + // Screenshot the dialog content instead of the empty root + try { + await dialogContent + .first() + .waitFor({ state: 'visible', timeout: 3000 }); + const screenshot = await dialogContent.first().screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } catch { + // Dialog not visible, fall through to page screenshot + } + } + + // Check if root content is too small (likely clipped by overflow) + const box = await root.boundingBox(); + if (box && (box.width < 10 || box.height < 10)) { + // Component is too small to be meaningful — screenshot full page + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + const screenshot = await root.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + }, +}; diff --git a/packages/modules/chats/__screenshots__/module-chats-components-chat-anchor--closed-default.png b/packages/modules/chats/__screenshots__/module-chats-components-chat-anchor--closed-default.png new file mode 100644 index 00000000..3ac5f03a Binary files /dev/null and b/packages/modules/chats/__screenshots__/module-chats-components-chat-anchor--closed-default.png differ diff --git a/packages/modules/chats/__screenshots__/module-chats-components-chat-anchor--closed-new.png b/packages/modules/chats/__screenshots__/module-chats-components-chat-anchor--closed-new.png new file mode 100644 index 00000000..73d63c7e Binary files /dev/null and b/packages/modules/chats/__screenshots__/module-chats-components-chat-anchor--closed-new.png differ diff --git a/packages/modules/chats/__screenshots__/module-chats-components-chat-anchor--closed-resolved.png b/packages/modules/chats/__screenshots__/module-chats-components-chat-anchor--closed-resolved.png new file mode 100644 index 00000000..3aa9867a Binary files /dev/null and b/packages/modules/chats/__screenshots__/module-chats-components-chat-anchor--closed-resolved.png differ diff --git a/packages/modules/chats/__screenshots__/module-chats-components-chat-anchor--opened-default.png b/packages/modules/chats/__screenshots__/module-chats-components-chat-anchor--opened-default.png new file mode 100644 index 00000000..d160ce81 Binary files /dev/null and b/packages/modules/chats/__screenshots__/module-chats-components-chat-anchor--opened-default.png differ diff --git a/packages/modules/chats/__screenshots__/module-chats-components-chat-anchor--opened-resolved.png b/packages/modules/chats/__screenshots__/module-chats-components-chat-anchor--opened-resolved.png new file mode 100644 index 00000000..d160ce81 Binary files /dev/null and b/packages/modules/chats/__screenshots__/module-chats-components-chat-anchor--opened-resolved.png differ diff --git a/packages/modules/chats/__screenshots__/module-chats-components-chat-box--default.png b/packages/modules/chats/__screenshots__/module-chats-components-chat-box--default.png new file mode 100644 index 00000000..0b93b174 Binary files /dev/null and b/packages/modules/chats/__screenshots__/module-chats-components-chat-box--default.png differ diff --git a/packages/modules/chats/__screenshots__/module-chats-components-chat-box--general.png b/packages/modules/chats/__screenshots__/module-chats-components-chat-box--general.png new file mode 100644 index 00000000..0bd72c54 Binary files /dev/null and b/packages/modules/chats/__screenshots__/module-chats-components-chat-box--general.png differ diff --git a/packages/modules/chats/__screenshots__/module-chats-components-chat-box--new.png b/packages/modules/chats/__screenshots__/module-chats-components-chat-box--new.png new file mode 100644 index 00000000..401d9bfc Binary files /dev/null and b/packages/modules/chats/__screenshots__/module-chats-components-chat-box--new.png differ diff --git a/packages/modules/chats/__screenshots__/module-chats-components-chat-box--resolved.png b/packages/modules/chats/__screenshots__/module-chats-components-chat-box--resolved.png new file mode 100644 index 00000000..36b3bb52 Binary files /dev/null and b/packages/modules/chats/__screenshots__/module-chats-components-chat-box--resolved.png differ diff --git a/packages/modules/chats/__screenshots__/module-chats-components-chat-box--someone-writing.png b/packages/modules/chats/__screenshots__/module-chats-components-chat-box--someone-writing.png new file mode 100644 index 00000000..5e6e5635 Binary files /dev/null and b/packages/modules/chats/__screenshots__/module-chats-components-chat-box--someone-writing.png differ diff --git a/packages/modules/chats/__screenshots__/module-chats-components-chat-box--wide.png b/packages/modules/chats/__screenshots__/module-chats-components-chat-box--wide.png new file mode 100644 index 00000000..447b411b Binary files /dev/null and b/packages/modules/chats/__screenshots__/module-chats-components-chat-box--wide.png differ diff --git a/packages/modules/chats/__screenshots__/module-chats-components-discussion-item--normal.png b/packages/modules/chats/__screenshots__/module-chats-components-discussion-item--normal.png new file mode 100644 index 00000000..e65775e1 Binary files /dev/null and b/packages/modules/chats/__screenshots__/module-chats-components-discussion-item--normal.png differ diff --git a/packages/modules/chats/__screenshots__/module-chats-components-reply-item--normal.png b/packages/modules/chats/__screenshots__/module-chats-components-reply-item--normal.png new file mode 100644 index 00000000..dc499128 Binary files /dev/null and b/packages/modules/chats/__screenshots__/module-chats-components-reply-item--normal.png differ diff --git a/packages/modules/chats/src/lib/components/discussionItem/discussionItem.scss b/packages/modules/chats/src/lib/components/discussionItem/discussionItem.scss index e4f639b7..798f5116 100644 --- a/packages/modules/chats/src/lib/components/discussionItem/discussionItem.scss +++ b/packages/modules/chats/src/lib/components/discussionItem/discussionItem.scss @@ -1,6 +1,6 @@ .discussion-item { - --reply-bt-color: var(--c-pink-2); - --reply-bt-color-hover: var(--c-pink-3); + --reply-bt-color: var(--primary-200); + --reply-bt-color-hover: var(--primary-500); --avatar-width: 25px; display: flex; gap: 6px; @@ -38,12 +38,12 @@ } h4 { - font-size: 14px; + font-size: var(--font-size-md); } p { - font-size: 12px; - color: var(--c-white-1); + font-size: var(--font-size-sm); + color: var(--white); } } } @@ -51,8 +51,8 @@ .discussion-content .content-text { margin: 0; padding: 0; - color: #ffffff; - font-size: 14px; + color: var(--white); + font-size: var(--font-size-md); } } @@ -62,7 +62,7 @@ flex: 0 0 4px; width: 4px; height: 4px; - background: var(--ca-white-2); + background: var(--alpha-white-15); border-radius: 50%; margin: 0 5px; } diff --git a/packages/modules/chats/src/lib/components/discussionItem/discussionItem.stories.ts b/packages/modules/chats/src/lib/components/discussionItem/discussionItem.stories.ts index d7ba88c3..c468aa8d 100644 --- a/packages/modules/chats/src/lib/components/discussionItem/discussionItem.stories.ts +++ b/packages/modules/chats/src/lib/components/discussionItem/discussionItem.stories.ts @@ -20,7 +20,7 @@ export const Normal: Story = { date: new Date(), username: 'local:John', content: 'Lorem ipsum dolor sit am', - color: 'var(--c-orange-2)', + color: 'var(--orange-200)', space: 'space12', id: '89461896', }, diff --git a/packages/modules/chats/src/lib/components/node-chat-anchor/node-chat-anchor.scss b/packages/modules/chats/src/lib/components/node-chat-anchor/node-chat-anchor.scss index 005ef77c..7a8f54d2 100644 --- a/packages/modules/chats/src/lib/components/node-chat-anchor/node-chat-anchor.scss +++ b/packages/modules/chats/src/lib/components/node-chat-anchor/node-chat-anchor.scss @@ -22,8 +22,8 @@ } .new-comment span { - color: var(--c-white-1); - font-size: 10px; + color: var(--white); + font-size: var(--font-size-2xs); } .comment-wrapper { @@ -59,7 +59,7 @@ .side-comment p { margin: 0; padding: 0; - font-size: 9px; + font-size: var(--font-size-2xs); outline: none; } } @@ -69,7 +69,7 @@ .comment-icon { .node-outputs-closed { bottom: 50%; - z-index: -1; + z-index: var(--z-base); .edges-count { top: 0; transform: translate(-50%, -50%); diff --git a/packages/modules/chats/src/lib/components/node-chat-anchor/node-chat-anchor.tsx b/packages/modules/chats/src/lib/components/node-chat-anchor/node-chat-anchor.tsx index e73e0172..24d02684 100644 --- a/packages/modules/chats/src/lib/components/node-chat-anchor/node-chat-anchor.tsx +++ b/packages/modules/chats/src/lib/components/node-chat-anchor/node-chat-anchor.tsx @@ -1,5 +1,8 @@ import { icons } from '@holistix-forge/ui-base'; -import { InputsAndOutputs, useNodeContext } from '@holistix-forge/whiteboard/frontend'; +import { + InputsAndOutputs, + useNodeContext, +} from '@holistix-forge/whiteboard/frontend'; import { useNodeEdges } from '@holistix-forge/core-graph/frontend'; import { TGraphNode } from '@holistix-forge/core-graph'; import { useLocalSharedData } from '@holistix-forge/collab/frontend'; @@ -34,22 +37,24 @@ export const NodeChatAnchor = ({ node }: { node: TGraphNode }) => { // we want to open also the chat node const handleOpen = () => { useNodeValue.open(); - chatNodeId && + if (chatNodeId) { dispatcher.dispatch({ type: 'whiteboard:open-node', nid: chatNodeId, viewId: useNodeValue.viewId, }); + } }; const handleClose = () => { useNodeValue.close(); - chatNodeId && + if (chatNodeId) { dispatcher.dispatch({ type: 'whiteboard:close-node', nid: chatNodeId, viewId: useNodeValue.viewId, }); + } }; let unread = 0; @@ -136,7 +141,7 @@ export const NodeChatAnchorInternal = ({ style={{ border: status === 'new' ? '1px solid var(--color-chat-new)' : 'none', - color: status === 'new' ? 'var(--c-white-1)' : 'var(--c-pink-2)', + color: status === 'new' ? 'var(--white)' : 'var(--primary-200)', }} className={`side-comment ${isOpened ? 'side-comment-open' : ''}`} > diff --git a/packages/modules/chats/src/lib/components/node-chat/chatbox.scss b/packages/modules/chats/src/lib/components/node-chat/chatbox.scss index e11fee12..74d2c48d 100644 --- a/packages/modules/chats/src/lib/components/node-chat/chatbox.scss +++ b/packages/modules/chats/src/lib/components/node-chat/chatbox.scss @@ -1,5 +1,5 @@ .chat-container { - --chat-grey: var(--c-gray-9); + --chat-grey: var(--neutral-9); width: 100%; height: 100%; @@ -52,17 +52,17 @@ border-radius: 10px; } div.last-read-flag { - border-top: solid 4px var(--c-red-4); + border-top: solid 4px var(--red-500); > div { position: relative; top: -4px; - color: var(--c-white-1); - background-color: var(--c-red-4); + color: var(--white); + background-color: var(--red-500); width: fit-content; margin: 0 auto; padding: 3px 10px; border-radius: 0 0 3px 3px; - font-size: 11px; + font-size: var(--font-size-xs); } } } @@ -108,7 +108,7 @@ width: 100%; cursor: text; &::placeholder { - color: var(--c-gray-11); + color: var(--neutral-11); } } } @@ -134,18 +134,18 @@ .id { margin: 0; padding: 0; - color: var(--White, var(--c-white-1)); + color: var(--White, var(--white)); font-weight: 700; - font-size: 14px; + font-size: var(--font-size-md); overflow: hidden; text-overflow: ellipsis; } .badge { padding: 2px 4px; border-radius: 4px; - font-size: 10px; + font-size: var(--font-size-2xs); height: fit-content; - color: var(--color-background); + color: var(--color-bg-app); text-transform: uppercase; } } @@ -160,7 +160,7 @@ align-items: center; gap: 8px; border-radius: var(--border-radius-box); - background-color: var(--ca-black-7); + background-color: var(--alpha-black-17); svg { min-width: 20px; @@ -172,7 +172,7 @@ text-overflow: ellipsis; max-width: 100%; color: var(--chat-grey); - font-size: 11px; + font-size: var(--font-size-xs); } &.writing-users { @@ -229,7 +229,7 @@ height: 100%; .node-inputs-closed { top: 50%; - z-index: -1; + z-index: var(--z-base); .edges-count { top: 50%; transform: translate(-50%, -50%); diff --git a/packages/modules/chats/src/lib/components/node-chat/chatbox.tsx b/packages/modules/chats/src/lib/components/node-chat/chatbox.tsx index 4e2a15ea..57b7ba71 100644 --- a/packages/modules/chats/src/lib/components/node-chat/chatbox.tsx +++ b/packages/modules/chats/src/lib/components/node-chat/chatbox.tsx @@ -9,7 +9,10 @@ import { ButtonIconProps, useAction, } from '@holistix-forge/ui-base'; -import { DisableZoomDragPan, NodeMainToolbar } from '@holistix-forge/whiteboard/frontend'; +import { + DisableZoomDragPan, + NodeMainToolbar, +} from '@holistix-forge/whiteboard/frontend'; import { DiscussionItem, @@ -125,7 +128,6 @@ export const Chatbox = ({ // execute handleScroll for special case when the chat // is almost empty and no scroll bar exist handleScroll(); - // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // @@ -187,7 +189,7 @@ export const Chatbox = ({ { const placeholder = { username: 'Loading...', - color: 'var(--c-gray-9)', + color: 'var(--neutral-9)', picture: null, }; @@ -107,21 +107,23 @@ export const NodeChatbox = ({ node }: { node: TGraphNode }) => { // we want to close the anchor node rather than this node const handleClose = () => { - anchorNodeId && + if (anchorNodeId) { dispatcher.dispatch({ type: 'whiteboard:close-node', nid: anchorNodeId, viewId: useNodeValue.viewId, }); + } }; const handleFilterOut = () => { - anchorNodeId && + if (anchorNodeId) { dispatcher.dispatch({ type: 'whiteboard:filter-out-node', nid: anchorNodeId, viewId: useNodeValue.viewId, }); + } }; const handleDelete = () => { diff --git a/packages/modules/chats/src/lib/components/node-chat/test-data.ts b/packages/modules/chats/src/lib/components/node-chat/test-data.ts index 3efc9191..b1149ef0 100644 --- a/packages/modules/chats/src/lib/components/node-chat/test-data.ts +++ b/packages/modules/chats/src/lib/components/node-chat/test-data.ts @@ -18,7 +18,7 @@ export const useTestChatBox = (_status: ChatboxProps['status']) => { const n: SimpleMessage = { username: 'local:John Doe', content: msg, - color: 'var(--c-orange-2)', + color: 'var(--orange-200)', space: '48946', id: `${messageList.length}`, date: new Date(), @@ -61,17 +61,17 @@ export const useTestChatBox = (_status: ChatboxProps['status']) => { export const testUsers = [ { username: 'github:Alice Dj', - color: 'var(--c-pink-4)', + color: 'var(--primary-600)', picture: null, }, { username: 'local:Mickey Willis', - color: 'var(--c-green-1)', + color: 'var(--green-400)', picture: null, }, { username: 'gitlab:Paul Jean-Claude Junior de la Vega', - color: 'var(--c-blue-3)', + color: 'var(--cyan-300)', picture: null, }, ]; diff --git a/packages/modules/chats/src/lib/components/replyItem/replyItem.scss b/packages/modules/chats/src/lib/components/replyItem/replyItem.scss index abb934a3..eb956efc 100644 --- a/packages/modules/chats/src/lib/components/replyItem/replyItem.scss +++ b/packages/modules/chats/src/lib/components/replyItem/replyItem.scss @@ -34,21 +34,21 @@ .reply-header { h5 { - font-size: 14px; + font-size: var(--font-size-md); } p { - font-size: 10px; - color: var(--c-white-1); + font-size: var(--font-size-2xs); + color: var(--white); } } .reply-wrapper .content-text { - font-size: 11px; + font-size: var(--font-size-xs); max-width: 82%; white-space: nowrap; text-overflow: ellipsis; - color: var(--c-gray-9); + color: var(--neutral-9); overflow: hidden; } } diff --git a/packages/modules/chats/src/lib/components/replyItem/replyItem.stories.ts b/packages/modules/chats/src/lib/components/replyItem/replyItem.stories.ts index 076ac587..99c40100 100644 --- a/packages/modules/chats/src/lib/components/replyItem/replyItem.stories.ts +++ b/packages/modules/chats/src/lib/components/replyItem/replyItem.stories.ts @@ -19,7 +19,7 @@ export const Normal: Story = { args: { username: 'local:John', content: 'Lorem ipsum dolor sit am', - color: 'var(--c-orange-2)', + color: 'var(--orange-200)', space: 'space54', id: '89461896', date: new Date(), @@ -27,7 +27,7 @@ export const Normal: Story = { picture: '', username: 'gitlab:Dave', content: 'Amet consectetur adispicing elit', - color: 'var(--c-pink-3)', + color: 'var(--primary-500)', space: 'space54', id: '61896894', date: new Date(), diff --git a/packages/modules/chats/src/lib/index.scss b/packages/modules/chats/src/lib/index.scss index b5c61c95..fac4f9e2 100644 --- a/packages/modules/chats/src/lib/index.scss +++ b/packages/modules/chats/src/lib/index.scss @@ -1,3 +1,5 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +:root { + // --- Chat domain tokens --- + --color-chat-new: var(--cyan-300); + --color-chat-default: var(--primary-500); +} diff --git a/packages/modules/excalidraw/.storybook/preview.ts b/packages/modules/excalidraw/.storybook/preview.ts index f8b6885b..1346daef 100644 --- a/packages/modules/excalidraw/.storybook/preview.ts +++ b/packages/modules/excalidraw/.storybook/preview.ts @@ -18,7 +18,7 @@ const preview: Preview = { values: [ { name: 'dark', - value: 'var(--color-background)', + value: 'var(--color-bg-app)', }, ], }, diff --git a/packages/modules/excalidraw/.storybook/test-runner.js b/packages/modules/excalidraw/.storybook/test-runner.js new file mode 100644 index 00000000..4b458ca7 --- /dev/null +++ b/packages/modules/excalidraw/.storybook/test-runner.js @@ -0,0 +1,108 @@ +const path = require('path'); +const { toMatchImageSnapshot } = require('jest-image-snapshot'); + +const PKG_ROOT = path.resolve(__dirname, '..'); + +const SNAPSHOT_OPTS = { + failureThreshold: 0.002, // 0.2% pixel difference allowed + failureThresholdType: 'percent', + customSnapshotsDir: + process.env['SNAPSHOT_DIR'] || path.join(PKG_ROOT, '__screenshots__'), + customDiffDir: + process.env['DIFF_DIR'] || path.join(PKG_ROOT, '__diff_output__'), +}; + +/** + * Wait for the page to be ready without relying on networkidle + * (which times out when stories load external resources like avatars). + */ +async function waitUntilReady(page) { + await page.waitForLoadState('domcontentloaded'); + await page.waitForLoadState('load'); + await page.evaluate(() => document.fonts.ready); + // Small settle time for React re-renders + await page.waitForTimeout(300); +} + +module.exports = { + setup() { + expect.extend({ toMatchImageSnapshot }); + }, + + async preVisit(page) { + // Block external image requests to prevent navigation context destruction. + // Stories load random avatars from pravatar.cc which cause flaky test failures. + await page.route('**/*pravatar*/**', (route) => route.abort()); + await page.route('**/*.pravatar.*/**', (route) => route.abort()); + }, + + async postVisit(page, context) { + // Disable CSS animations/transitions to avoid flaky diffs + await page.addStyleTag({ + content: ` + *, *::before, *::after { + animation-duration: 0s !important; + animation-delay: 0s !important; + transition-duration: 0s !important; + transition-delay: 0s !important; + } + `, + }); + + // Wait for page to be ready (DOM, resources, fonts — skip networkidle) + await waitUntilReady(page); + + const root = page.locator('#storybook-root'); + + try { + await root.waitFor({ state: 'visible', timeout: 5000 }); + } catch { + // Root not visible — fall back to full page screenshot + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + // Check if a Radix dialog portal is present (renders outside #storybook-root) + const dialogContent = page.locator('[data-radix-portal] [role="dialog"]'); + const hasDialog = await dialogContent.count().then((c) => c > 0); + + if (hasDialog) { + // Screenshot the dialog content instead of the empty root + try { + await dialogContent + .first() + .waitFor({ state: 'visible', timeout: 3000 }); + const screenshot = await dialogContent.first().screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } catch { + // Dialog not visible, fall through to page screenshot + } + } + + // Check if root content is too small (likely clipped by overflow) + const box = await root.boundingBox(); + if (box && (box.width < 10 || box.height < 10)) { + // Component is too small to be meaningful — screenshot full page + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + const screenshot = await root.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + }, +}; diff --git a/packages/modules/jupyter/.storybook/preview.ts b/packages/modules/jupyter/.storybook/preview.ts index 7f2290b1..7f82cf53 100644 --- a/packages/modules/jupyter/.storybook/preview.ts +++ b/packages/modules/jupyter/.storybook/preview.ts @@ -18,7 +18,7 @@ const preview: Preview = { values: [ { name: 'dark', - value: 'var(--color-background)', + value: 'var(--color-bg-app)', }, ], }, diff --git a/packages/modules/jupyter/.storybook/test-runner.js b/packages/modules/jupyter/.storybook/test-runner.js new file mode 100644 index 00000000..4b458ca7 --- /dev/null +++ b/packages/modules/jupyter/.storybook/test-runner.js @@ -0,0 +1,108 @@ +const path = require('path'); +const { toMatchImageSnapshot } = require('jest-image-snapshot'); + +const PKG_ROOT = path.resolve(__dirname, '..'); + +const SNAPSHOT_OPTS = { + failureThreshold: 0.002, // 0.2% pixel difference allowed + failureThresholdType: 'percent', + customSnapshotsDir: + process.env['SNAPSHOT_DIR'] || path.join(PKG_ROOT, '__screenshots__'), + customDiffDir: + process.env['DIFF_DIR'] || path.join(PKG_ROOT, '__diff_output__'), +}; + +/** + * Wait for the page to be ready without relying on networkidle + * (which times out when stories load external resources like avatars). + */ +async function waitUntilReady(page) { + await page.waitForLoadState('domcontentloaded'); + await page.waitForLoadState('load'); + await page.evaluate(() => document.fonts.ready); + // Small settle time for React re-renders + await page.waitForTimeout(300); +} + +module.exports = { + setup() { + expect.extend({ toMatchImageSnapshot }); + }, + + async preVisit(page) { + // Block external image requests to prevent navigation context destruction. + // Stories load random avatars from pravatar.cc which cause flaky test failures. + await page.route('**/*pravatar*/**', (route) => route.abort()); + await page.route('**/*.pravatar.*/**', (route) => route.abort()); + }, + + async postVisit(page, context) { + // Disable CSS animations/transitions to avoid flaky diffs + await page.addStyleTag({ + content: ` + *, *::before, *::after { + animation-duration: 0s !important; + animation-delay: 0s !important; + transition-duration: 0s !important; + transition-delay: 0s !important; + } + `, + }); + + // Wait for page to be ready (DOM, resources, fonts — skip networkidle) + await waitUntilReady(page); + + const root = page.locator('#storybook-root'); + + try { + await root.waitFor({ state: 'visible', timeout: 5000 }); + } catch { + // Root not visible — fall back to full page screenshot + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + // Check if a Radix dialog portal is present (renders outside #storybook-root) + const dialogContent = page.locator('[data-radix-portal] [role="dialog"]'); + const hasDialog = await dialogContent.count().then((c) => c > 0); + + if (hasDialog) { + // Screenshot the dialog content instead of the empty root + try { + await dialogContent + .first() + .waitFor({ state: 'visible', timeout: 3000 }); + const screenshot = await dialogContent.first().screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } catch { + // Dialog not visible, fall through to page screenshot + } + } + + // Check if root content is too small (likely clipped by overflow) + const box = await root.boundingBox(); + if (box && (box.width < 10 || box.height < 10)) { + // Component is too small to be meaningful — screenshot full page + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + const screenshot = await root.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + }, +}; diff --git a/packages/modules/jupyter/__screenshots__/modules-jupyter-components-card-settings--normal.png b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-card-settings--normal.png new file mode 100644 index 00000000..8224de3b Binary files /dev/null and b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-card-settings--normal.png differ diff --git a/packages/modules/jupyter/__screenshots__/modules-jupyter-components-cells-hive--normal.png b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-cells-hive--normal.png new file mode 100644 index 00000000..9ba12b19 Binary files /dev/null and b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-cells-hive--normal.png differ diff --git a/packages/modules/jupyter/__screenshots__/modules-jupyter-components-code-editor--normal.png b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-code-editor--normal.png new file mode 100644 index 00000000..08d9c036 Binary files /dev/null and b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-code-editor--normal.png differ diff --git a/packages/modules/jupyter/__screenshots__/modules-jupyter-components-control-bar--normal.png b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-control-bar--normal.png new file mode 100644 index 00000000..8c4033f7 Binary files /dev/null and b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-control-bar--normal.png differ diff --git a/packages/modules/jupyter/__screenshots__/modules-jupyter-components-display-menu--normal.png b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-display-menu--normal.png new file mode 100644 index 00000000..dd374f1d Binary files /dev/null and b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-display-menu--normal.png differ diff --git a/packages/modules/jupyter/__screenshots__/modules-jupyter-components-hive-tag--normal.png b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-hive-tag--normal.png new file mode 100644 index 00000000..1cfc1c86 Binary files /dev/null and b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-hive-tag--normal.png differ diff --git a/packages/modules/jupyter/__screenshots__/modules-jupyter-components-menuexpanded--dataset.png b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-menuexpanded--dataset.png new file mode 100644 index 00000000..3828b2da Binary files /dev/null and b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-menuexpanded--dataset.png differ diff --git a/packages/modules/jupyter/__screenshots__/modules-jupyter-components-menuexpanded--scene.png b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-menuexpanded--scene.png new file mode 100644 index 00000000..4dd5d2db Binary files /dev/null and b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-menuexpanded--scene.png differ diff --git a/packages/modules/jupyter/__screenshots__/modules-jupyter-components-menuexpanded--screening.png b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-menuexpanded--screening.png new file mode 100644 index 00000000..b5b474fc Binary files /dev/null and b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-menuexpanded--screening.png differ diff --git a/packages/modules/jupyter/__screenshots__/modules-jupyter-components-menuexpanded--vault.png b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-menuexpanded--vault.png new file mode 100644 index 00000000..94542f9d Binary files /dev/null and b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-menuexpanded--vault.png differ diff --git a/packages/modules/jupyter/__screenshots__/modules-jupyter-components-reduced-cells--error.png b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-reduced-cells--error.png new file mode 100644 index 00000000..6a1af211 Binary files /dev/null and b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-reduced-cells--error.png differ diff --git a/packages/modules/jupyter/__screenshots__/modules-jupyter-components-reduced-cells--normal.png b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-reduced-cells--normal.png new file mode 100644 index 00000000..42e01ea7 Binary files /dev/null and b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-reduced-cells--normal.png differ diff --git a/packages/modules/jupyter/__screenshots__/modules-jupyter-components-reduced-cells--running.png b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-reduced-cells--running.png new file mode 100644 index 00000000..66266ad4 Binary files /dev/null and b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-reduced-cells--running.png differ diff --git a/packages/modules/jupyter/__screenshots__/modules-jupyter-components-reduced-cells--validate.png b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-reduced-cells--validate.png new file mode 100644 index 00000000..59e36711 Binary files /dev/null and b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-reduced-cells--validate.png differ diff --git a/packages/modules/jupyter/__screenshots__/modules-jupyter-components-tag--crowned.png b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-tag--crowned.png new file mode 100644 index 00000000..5e847865 Binary files /dev/null and b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-tag--crowned.png differ diff --git a/packages/modules/jupyter/__screenshots__/modules-jupyter-components-tag--hover.png b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-tag--hover.png new file mode 100644 index 00000000..d9080395 Binary files /dev/null and b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-tag--hover.png differ diff --git a/packages/modules/jupyter/__screenshots__/modules-jupyter-components-tag--normal.png b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-tag--normal.png new file mode 100644 index 00000000..d9080395 Binary files /dev/null and b/packages/modules/jupyter/__screenshots__/modules-jupyter-components-tag--normal.png differ diff --git a/packages/modules/jupyter/src/lib/components/code-cell/cell.scss b/packages/modules/jupyter/src/lib/components/code-cell/cell.scss index 169539ba..f322b8f1 100644 --- a/packages/modules/jupyter/src/lib/components/code-cell/cell.scss +++ b/packages/modules/jupyter/src/lib/components/code-cell/cell.scss @@ -1,7 +1,7 @@ .jupyterlab-code-cell { - --jp-widgets-label-color: var(--color-code-output-text); + --jp-widgets-label-color: var(--white); - background: var(--color-node-background); + background: var(--color-bg-node); width: 100%; height: 100%; position: relative; @@ -20,16 +20,16 @@ */ width: calc(100% - 20px); margin: 0 10px 5px 10px; - background-color: var(--c-gray-12); - color: var(--color-code-output-text); - border: solid 1px var(--c-gray-10); + background-color: var(--neutral-12); + color: var(--white); + border: solid 1px var(--neutral-10); padding: 0 5px; border-radius: 4px; box-sizing: border-box; } &.busy { - border: solid 1.5px var(--color-background); + border: solid 1.5px var(--color-bg-app); &:before, &:after { @@ -48,7 +48,7 @@ background-size: 400%; width: calc(100% + 4px); height: calc(100% + 4px); - z-index: -1; + z-index: var(--z-base); animation: steam 30s linear infinite; filter: blur(3px); @@ -70,12 +70,12 @@ /* python output */ .jp-RenderedText pre { - color: var(--color-code-output-text); + color: var(--white); } .jupyter-output-area-box { border: solid 1px transparent; .jupyter-output-area-box-debug { - font-size: 13px; + font-size: var(--font-size-md); color: var(--color-debug); } } diff --git a/packages/modules/jupyter/src/lib/components/menuExpanded/menuExpanded.scss b/packages/modules/jupyter/src/lib/components/menuExpanded/menuExpanded.scss index 2bf43399..67cf1ee2 100644 --- a/packages/modules/jupyter/src/lib/components/menuExpanded/menuExpanded.scss +++ b/packages/modules/jupyter/src/lib/components/menuExpanded/menuExpanded.scss @@ -1,8 +1,8 @@ .menu-expanded { width: 340px; border-radius: 8px; - background: var(--c-blue-61); - border: 1px solid var(--c-blue-gray-9); + background: var(--surface-900); + border: 1px solid var(--surface-700); .header ul { list-style: none; @@ -22,7 +22,7 @@ .header { padding: 3px 10px; - background: var(--c-blue-gray-1); + background: var(--surface-700); display: flex; justify-content: space-between; border-radius: 8px 8px 0 0; @@ -37,11 +37,11 @@ display: flex; flex-direction: column; justify-content: center; - color: var(--c-white-1); + color: var(--white); gap: 2px; p { - font-size: 8px; + font-size: var(--font-size-2xs); font-weight: 700; margin: 0; } @@ -56,7 +56,7 @@ align-items: flex-start; li { - font-size: 6px; + font-size: var(--font-size-2xs); margin: 0; padding: 0; font-weight: 300; @@ -78,7 +78,7 @@ .badge { padding: 0 4px; border-radius: 3px; - background-color: var(--c-gray-10); + background-color: var(--neutral-10); &.orange { background: var(--color-scene); @@ -89,13 +89,13 @@ } &.blue { - background: var(--color-dataset); + background: var(--cyan-300); } p { text-transform: uppercase; - color: var(--c-white-1); - font-size: 6px; + color: var(--white); + font-size: var(--font-size-2xs); letter-spacing: 0.5px; line-height: 13px; } @@ -109,7 +109,7 @@ } .footer { - background: var(--c-pink-41); + background: var(--primary-600); padding: 8px 10px; border-radius: 0 0 8px 8px; display: flex; diff --git a/packages/modules/jupyter/src/lib/components/node-dataset/node-dataset.stories.tsx b/packages/modules/jupyter/src/lib/components/node-dataset/node-dataset.stories.tsx index a6953590..3242f137 100644 --- a/packages/modules/jupyter/src/lib/components/node-dataset/node-dataset.stories.tsx +++ b/packages/modules/jupyter/src/lib/components/node-dataset/node-dataset.stories.tsx @@ -66,7 +66,7 @@ type Story = StoryObj; export const Closed: Story = { args: { - color: 'var(--c-alt-blue-2)', + color: 'var(--cyan-300)', expanded: false, selected: true, }, @@ -74,7 +74,7 @@ export const Closed: Story = { export const Closed_Hover: Story = { args: { - color: 'var(--c-alt-blue-2)', + color: 'var(--cyan-300)', expanded: false, selected: true, }, @@ -83,7 +83,7 @@ export const Closed_Hover: Story = { export const Open: Story = { args: { - color: 'var(--c-alt-blue-2)', + color: 'var(--cyan-300)', expanded: true, selected: true, }, diff --git a/packages/modules/jupyter/src/lib/components/node-kernel/kernel-state-indicator.scss b/packages/modules/jupyter/src/lib/components/node-kernel/kernel-state-indicator.scss index c94574c9..a748997b 100644 --- a/packages/modules/jupyter/src/lib/components/node-kernel/kernel-state-indicator.scss +++ b/packages/modules/jupyter/src/lib/components/node-kernel/kernel-state-indicator.scss @@ -13,7 +13,7 @@ border: none; overflow: hidden; position: relative; - background-color: var(--c-gray-12); + background-color: var(--neutral-12); span { position: absolute; @@ -21,12 +21,12 @@ left: 0; line-height: var(--height); font-size: var(--height); - color: var(--c-white-1); + color: var(--white); margin-left: 5px; } .progress { - background: var(--ca-white-1); + background: var(--alpha-white-10); height: 100%; transition: width ease-out 1s; } diff --git a/packages/modules/jupyter/src/lib/components/node-kernel/kernel-state-indicator.tsx b/packages/modules/jupyter/src/lib/components/node-kernel/kernel-state-indicator.tsx index f5b6bd9d..66bc9ef1 100644 --- a/packages/modules/jupyter/src/lib/components/node-kernel/kernel-state-indicator.tsx +++ b/packages/modules/jupyter/src/lib/components/node-kernel/kernel-state-indicator.tsx @@ -24,7 +24,7 @@ const stateToColor = (state: number) => { export const KernelStateIndicator = ({ state }: KernelStateIndicatorProps) => { return ( -

+
(); const handleDeleteKernel = useCallback(async () => { - client_id && - (await dispatcher.dispatch({ + if (client_id) { + await dispatcher.dispatch({ type: 'jupyter:delete-kernel-node', nodeId: id, - })); + }); + } }, [dispatcher, kernel_id, client_id]); const buttons = useNodeHeaderButtons({ @@ -89,7 +90,10 @@ export const NodeKernel = ({ {isOpened && (
-
+

kernel {kernel?.kernel_id.substring(0, 8)}{' '} @@ -110,13 +114,32 @@ export const NodeKernel = ({ )}

{kernel.notebooks && kernel.notebooks.length > 0 && ( -
-

Connected Notebooks:

-
    +
    +

    + Connected Notebooks: +

    +
      {kernel.notebooks.map((notebook: any) => ( -
    • +
    • {notebook.name} - + ({notebook.path})
    • diff --git a/packages/modules/jupyter/src/lib/components/node-notebook-component/node-notebook-component.stories.tsx b/packages/modules/jupyter/src/lib/components/node-notebook-component/node-notebook-component.stories.tsx index 2bedb313..a3776af1 100644 --- a/packages/modules/jupyter/src/lib/components/node-notebook-component/node-notebook-component.stories.tsx +++ b/packages/modules/jupyter/src/lib/components/node-notebook-component/node-notebook-component.stories.tsx @@ -87,7 +87,7 @@ type Story = StoryObj; export const ReducedNormal: Story = { args: { - color: 'var(--c-red-4)', + color: 'var(--red-500)', status: 'success', selected: false, expanded: true, @@ -102,7 +102,7 @@ export const ReducedHover: Story = { }, }, args: { - color: 'var(--c-red-4)', + color: 'var(--red-500)', status: 'success', selected: false, expanded: true, @@ -117,7 +117,7 @@ export const ReducedOpen: Story = { }, }, args: { - color: 'var(--c-red-4)', + color: 'var(--red-500)', status: 'success', notebookOpened: true, selected: false, diff --git a/packages/modules/jupyter/src/lib/components/node-notebook-component/node-notebook-component.tsx b/packages/modules/jupyter/src/lib/components/node-notebook-component/node-notebook-component.tsx index fc459376..9fd656fd 100644 --- a/packages/modules/jupyter/src/lib/components/node-notebook-component/node-notebook-component.tsx +++ b/packages/modules/jupyter/src/lib/components/node-notebook-component/node-notebook-component.tsx @@ -77,13 +77,17 @@ export const NodeNotebookComponent = ({ }, [inConOpened, outConOpened]); return ( -
      +
      {/* Menu top left */} {!isExpanded && (
      @@ -92,9 +96,14 @@ export const NodeNotebookComponent = ({ {/* Module right */} {!isExpanded && (
      -

      Node #12345

      +
      +

      + Node #12345 +

      @@ -134,13 +159,23 @@ export const NodeNotebookComponent = ({ <> {/* Output right */}
      {/* Input left */}
      @@ -154,13 +189,22 @@ export const NodeNotebookComponent = ({ style={{ fill: status === 'loading' - ? 'var(--c-green-2)' - : 'var(--c-red-2)', + ? 'var(--green-600)' + : 'var(--red-400)', }} />
      {openNotebook ? ( -
      +
      {/* Input left */}
      -
      +
      @@ -191,41 +243,86 @@ export const NodeNotebookComponent = ({ {/* Output right */}
      setOpenNotebook(false)} />
      ) : (
      setOpenNotebook(true)} - className="absolute left-1/2 group-[.testhover]:!opacity-100 -translate-x-1/2 bottom-0 flex justify-center opacity-0 group-hover/notebook:opacity-100 transition-opacity cursor-pointer z-20" + className="absolute flex justify-center opacity-0 group-hover/notebook:opacity-100 transition-opacity cursor-pointer group-[.testhover]:!opacity-100" + style={{ + left: '50%', + transform: 'translateX(-50%)', + bottom: 0, + zIndex: 20, + }} > - +
      )} -
        +
        • @@ -276,18 +373,27 @@ const HiveLine = ({ {/* Hive tag */}

          {title}

          diff --git a/packages/modules/jupyter/src/lib/components/node-notebook/card-settings.tsx b/packages/modules/jupyter/src/lib/components/node-notebook/card-settings.tsx index 851643db..abbfdfb7 100644 --- a/packages/modules/jupyter/src/lib/components/node-notebook/card-settings.tsx +++ b/packages/modules/jupyter/src/lib/components/node-notebook/card-settings.tsx @@ -20,28 +20,79 @@ export const CardSettings = ({ status }: CardSettingsProps) => { }); return ( -
          -
          +
          +
          -
          +
          - + Notebook -
          +
          -
          -
          +
          +

          Node #12345

          -
          +
          {
          -
          +
          {
          -
          +
          {
          -
          +
          {
          {status === 'success' ? ( -
          +
          ) : status === 'error' ? ( -
          +
          ) : ( -
          +
          )}
          ); diff --git a/packages/modules/jupyter/src/lib/components/node-notebook/cells-hive.tsx b/packages/modules/jupyter/src/lib/components/node-notebook/cells-hive.tsx index a4dacb14..1cee325a 100644 --- a/packages/modules/jupyter/src/lib/components/node-notebook/cells-hive.tsx +++ b/packages/modules/jupyter/src/lib/components/node-notebook/cells-hive.tsx @@ -10,7 +10,6 @@ export interface CellsHiveProps { } export const CellsHive = ({ cells, columnsNumber }: CellsHiveProps) => { - const columns: Array = Array(columnsNumber) .fill(1) .map(() => []); @@ -18,13 +17,15 @@ export const CellsHive = ({ cells, columnsNumber }: CellsHiveProps) => { cells.forEach((c, i) => columns[i % columnsNumber].push(c)); return ( -
          +
          {columns.map((column, i) => (
          {column.map((c) => ( diff --git a/packages/modules/jupyter/src/lib/components/node-notebook/control-bar.tsx b/packages/modules/jupyter/src/lib/components/node-notebook/control-bar.tsx index 75801fc2..af044dec 100644 --- a/packages/modules/jupyter/src/lib/components/node-notebook/control-bar.tsx +++ b/packages/modules/jupyter/src/lib/components/node-notebook/control-bar.tsx @@ -16,103 +16,287 @@ export const ControlBar = () => { }; return ( -
          -
          - -
          +
          +
          + +
          {isMenuOpen && ( -
            +
            • setChildArrowHovered(true)} onMouseLeave={() => setChildArrowHovered(false)} - className={`h-[60px] flex items-center w-full justify-center border-white border-opacity-20 hover:border-opacity-100 border-r transition-all relative group`} + className="flex items-center w-full justify-center relative group transition-all" + style={{ + height: '60px', + borderRight: '1px solid rgba(255, 255, 255, 0.2)', + }} onClick={() => setSelectedArrow('top')} > - -
              + +
            • setChildArrowHovered(true)} onMouseLeave={() => setChildArrowHovered(false)} - className={`h-[60px] flex items-center w-full justify-center border-white border-opacity-20 hover:border-opacity-100 border-r transition-all relative group`} + className="flex items-center w-full justify-center relative group transition-all" + style={{ + height: '60px', + borderRight: '1px solid rgba(255, 255, 255, 0.2)', + }} onClick={() => setSelectedArrow('left')} > - -
              + +
            • setChildArrowHovered(true)} onMouseLeave={() => setChildArrowHovered(false)} - className={`h-[60px] flex items-center w-full justify-center border-white border-opacity-20 hover:border-opacity-100 border-r transition-all relative group`} + className="flex items-center w-full justify-center relative group transition-all" + style={{ + height: '60px', + borderRight: '1px solid rgba(255, 255, 255, 0.2)', + }} onClick={() => setSelectedArrow('bottom')} > - -
              + +
            )}
            {selectedArrow === 'top' ? ( - + ) : selectedArrow === 'left' ? ( - + ) : selectedArrow === 'bottom' ? ( - + ) : ( - + )}
          -
          - -
          +
          + +
          -
          -
          - -
          +
          +
          + +
          -
          -
          - -
          +
          +
          + +
          -
          -
          - -
          +
          +
          + +
          -
          -
          - -
          +
          +
          + +
          -
          -
          - -
          +
          +
          + +
          -
          -
          - -
          +
          +
          + +
          ); diff --git a/packages/modules/jupyter/src/lib/components/node-notebook/display-menu.tsx b/packages/modules/jupyter/src/lib/components/node-notebook/display-menu.tsx index d2924591..42ed9e01 100644 --- a/packages/modules/jupyter/src/lib/components/node-notebook/display-menu.tsx +++ b/packages/modules/jupyter/src/lib/components/node-notebook/display-menu.tsx @@ -18,8 +18,17 @@ export const DisplayMenu = ({ setOutput, }: DisplayMenuProps) => { return ( -
            -
          • +
              +
            • -
            • +
            • -
            • +
            diff --git a/packages/modules/jupyter/src/lib/components/node-notebook/hive-tag.tsx b/packages/modules/jupyter/src/lib/components/node-notebook/hive-tag.tsx index fcc0f442..f143129b 100644 --- a/packages/modules/jupyter/src/lib/components/node-notebook/hive-tag.tsx +++ b/packages/modules/jupyter/src/lib/components/node-notebook/hive-tag.tsx @@ -1,10 +1,24 @@ export const HiveTag = ({ title, color }: { title: string; color: string }) => { return ( -
            +
            -

            +

            {title}

            diff --git a/packages/modules/jupyter/src/lib/components/node-notebook/node-notebook.tsx b/packages/modules/jupyter/src/lib/components/node-notebook/node-notebook.tsx index 867af07a..efce4e82 100644 --- a/packages/modules/jupyter/src/lib/components/node-notebook/node-notebook.tsx +++ b/packages/modules/jupyter/src/lib/components/node-notebook/node-notebook.tsx @@ -80,9 +80,12 @@ export const NodeNotebook = ({ }; return ( -
            +
            {expanded && ( -
            +
            )} -
            +
            {/* input left */} -
            +
            {/* Output right */} -
            +
            {expanded ? ( -
            +
            {/* SpreadCell Top */} -
            +
            {title && ( -
            -
            +
            +

            Cell #1

            @@ -130,10 +176,13 @@ export const NodeNotebook = ({ )} {input && ( -
            +
            {input && !output ? ( -
            -
            +
            +
            {status === 'success' ? ( ) : status === 'error' ? ( @@ -152,19 +201,36 @@ export const NodeNotebook = ({ )} -
            +
            ) : ( <> -
            -

            Entrée [1]

            +
            +

            + Entrée [1] +

            {status === 'success' ? ( @@ -185,8 +251,12 @@ export const NodeNotebook = ({ )}
            @@ -196,10 +266,22 @@ export const NodeNotebook = ({ )} {output && ( -
            +
            {!input && output ? ( -
            -
            +
            +
            {status === 'success' ? ( ) : status === 'error' ? ( @@ -218,16 +300,47 @@ export const NodeNotebook = ({ )} -
            +
            -
            +
            ) : ( <> - + Output [1] -

            +

            Lorem ipsum dolor sit, amet consectetur adipisicing elit. Repudiandae fugiat iure nam eligendi expedita vel odio a molestiae? Nam dolore esse fuga omnis doloribus animi @@ -238,21 +351,28 @@ export const NodeNotebook = ({

            )} -
            +
            { // if just output is displayed, we need to add a spacer !input && !output && ( -
            +
            ) }
            -
            +
            {tag.map((tag, i) => ( addTag('test', false)} - className="flex items-center justify-center h-5 w-5 rounded-[4px] border -border--c-blue-gray-1 cursor-pointer p-0" + className="flex items-center justify-center cursor-pointer" + style={{ + height: '20px', + width: '20px', + borderRadius: '4px', + border: '1px solid var(--surface-800)', + padding: 0, + }} > @@ -273,12 +400,25 @@ export const NodeNotebook = ({
            {/* SpreadCell Bottom */} -
            +
            ) : ( -
            +
            )}
            ); diff --git a/packages/modules/jupyter/src/lib/components/node-notebook/reduced-cell.stories.ts b/packages/modules/jupyter/src/lib/components/node-notebook/reduced-cell.stories.ts index e52b8bc8..f6bda9f1 100644 --- a/packages/modules/jupyter/src/lib/components/node-notebook/reduced-cell.stories.ts +++ b/packages/modules/jupyter/src/lib/components/node-notebook/reduced-cell.stories.ts @@ -1,10 +1,19 @@ import type { Meta, StoryObj } from '@storybook/react'; +import { createElement } from 'react'; import { ReducedCell } from './reduced-cell'; const meta = { title: 'Modules/Jupyter/Components/Reduced Cells', component: ReducedCell, + decorators: [ + (Story) => + createElement( + 'div', + { style: { padding: '20px', minWidth: '40px', minHeight: '40px' } }, + createElement(Story) + ), + ], parameters: { layout: 'centered', }, diff --git a/packages/modules/jupyter/src/lib/components/node-notebook/reduced-cell.tsx b/packages/modules/jupyter/src/lib/components/node-notebook/reduced-cell.tsx index 38e0326a..85b64e0a 100644 --- a/packages/modules/jupyter/src/lib/components/node-notebook/reduced-cell.tsx +++ b/packages/modules/jupyter/src/lib/components/node-notebook/reduced-cell.tsx @@ -18,12 +18,34 @@ export const ReducedCell = ({ type }: ReducedCellProps) => { ) : type === 'selected' ? (
            - +
            ) : type === 'glow' ? (
            -
            - +
            +
            ) : null} diff --git a/packages/modules/jupyter/src/lib/components/node-notebook/tag.tsx b/packages/modules/jupyter/src/lib/components/node-notebook/tag.tsx index 470282ac..78221a70 100644 --- a/packages/modules/jupyter/src/lib/components/node-notebook/tag.tsx +++ b/packages/modules/jupyter/src/lib/components/node-notebook/tag.tsx @@ -17,12 +17,18 @@ export const Tag = ({ }: TagProps) => { return ( - + {text} {crowned ? ( diff --git a/packages/modules/jupyter/src/lib/components/node-python/node-python.stories.tsx b/packages/modules/jupyter/src/lib/components/node-python/node-python.stories.tsx index 59fd4467..efe80b56 100644 --- a/packages/modules/jupyter/src/lib/components/node-python/node-python.stories.tsx +++ b/packages/modules/jupyter/src/lib/components/node-python/node-python.stories.tsx @@ -86,7 +86,7 @@ type Story = StoryObj; export const Closed: Story = { args: { - color: 'var(--c-red-4)', + color: 'var(--red-500)', nodeInfos: true, status: 'success', expanded: false, @@ -96,7 +96,7 @@ export const Closed: Story = { export const Closed_Hover: Story = { args: { - color: 'var(--c-red-4)', + color: 'var(--red-500)', nodeInfos: true, status: 'success', expanded: false, @@ -113,7 +113,7 @@ export const Opened: Story = { }, }, args: { - color: 'var(--c-red-4)', + color: 'var(--red-500)', nodeInfos: true, status: 'success', expanded: true, diff --git a/packages/modules/jupyter/src/lib/components/node-screening/node-screening.stories.tsx b/packages/modules/jupyter/src/lib/components/node-screening/node-screening.stories.tsx index 3d13c047..44c61461 100644 --- a/packages/modules/jupyter/src/lib/components/node-screening/node-screening.stories.tsx +++ b/packages/modules/jupyter/src/lib/components/node-screening/node-screening.stories.tsx @@ -72,7 +72,7 @@ type Story = StoryObj; export const Closed: Story = { args: { - color: 'var(--c-alt-blue-2)', + color: 'var(--cyan-300)', inputs: 4, expanded: false, selected: true, @@ -81,7 +81,7 @@ export const Closed: Story = { export const Closed_Hover: Story = { args: { - color: 'var(--c-alt-blue-2)', + color: 'var(--cyan-300)', inputs: 4, expanded: false, selected: true, @@ -91,7 +91,7 @@ export const Closed_Hover: Story = { export const Opened: Story = { args: { - color: 'var(--c-alt-blue-2)', + color: 'var(--cyan-300)', inputs: 4, expanded: true, selected: true, diff --git a/packages/modules/jupyter/src/lib/components/node-vault/node-vault.stories.tsx b/packages/modules/jupyter/src/lib/components/node-vault/node-vault.stories.tsx index deedeab2..246c837d 100644 --- a/packages/modules/jupyter/src/lib/components/node-vault/node-vault.stories.tsx +++ b/packages/modules/jupyter/src/lib/components/node-vault/node-vault.stories.tsx @@ -76,7 +76,7 @@ type Story = StoryObj; export const Closed: Story = { args: { - color: 'var(--c-red-4)', + color: 'var(--red-500)', inputs: 6, outputs: 7, expanded: false, @@ -86,7 +86,7 @@ export const Closed: Story = { export const Closed_Hover: Story = { args: { - color: 'var(--c-red-4)', + color: 'var(--red-500)', inputs: 6, outputs: 7, expanded: false, @@ -97,7 +97,7 @@ export const Closed_Hover: Story = { export const Opened: Story = { args: { - color: 'var(--c-red-4)', + color: 'var(--red-500)', inputs: 6, outputs: 7, expanded: true, diff --git a/packages/modules/jupyter/src/lib/index.scss b/packages/modules/jupyter/src/lib/index.scss index b5c61c95..b4fdab4a 100644 --- a/packages/modules/jupyter/src/lib/index.scss +++ b/packages/modules/jupyter/src/lib/index.scss @@ -1,3 +1,15 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +:root { + // --- Jupyter domain tokens --- + --color-kernel: var(--primary-600); + --color-kernel-state-1: var(--primary-200); + --color-kernel-state-2: var(--primary-500); + + // JupyterLab collaborator colors + --jp-collaborator-color1: #ffad8e; + --jp-collaborator-color2: #dac83d; + --jp-collaborator-color3: #72dd76; + --jp-collaborator-color4: #00e4d0; + --jp-collaborator-color5: #45d4ff; + --jp-collaborator-color6: #e2b1ff; + --jp-collaborator-color7: #ff9de6; +} diff --git a/packages/modules/notion/.storybook/preview.ts b/packages/modules/notion/.storybook/preview.ts index 78709de6..7a586e21 100644 --- a/packages/modules/notion/.storybook/preview.ts +++ b/packages/modules/notion/.storybook/preview.ts @@ -6,25 +6,25 @@ import '@holistix-forge/whiteboard/style'; // import '../src/lib/index.scss'; const preview: Preview = { - parameters: { - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/, - }, - }, - backgrounds: { - default: 'dark', - values: [ - { - name: 'dark', - value: 'var(--color-background)', - }, - ], + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, + backgrounds: { + default: 'dark', + values: [ + { + name: 'dark', + value: 'var(--color-bg-app)', }, + ], }, - decorators: [GlobalWrapper], - tags: [], + }, + decorators: [GlobalWrapper], + tags: [], }; export default preview; diff --git a/packages/modules/notion/.storybook/test-runner.js b/packages/modules/notion/.storybook/test-runner.js new file mode 100644 index 00000000..4b458ca7 --- /dev/null +++ b/packages/modules/notion/.storybook/test-runner.js @@ -0,0 +1,108 @@ +const path = require('path'); +const { toMatchImageSnapshot } = require('jest-image-snapshot'); + +const PKG_ROOT = path.resolve(__dirname, '..'); + +const SNAPSHOT_OPTS = { + failureThreshold: 0.002, // 0.2% pixel difference allowed + failureThresholdType: 'percent', + customSnapshotsDir: + process.env['SNAPSHOT_DIR'] || path.join(PKG_ROOT, '__screenshots__'), + customDiffDir: + process.env['DIFF_DIR'] || path.join(PKG_ROOT, '__diff_output__'), +}; + +/** + * Wait for the page to be ready without relying on networkidle + * (which times out when stories load external resources like avatars). + */ +async function waitUntilReady(page) { + await page.waitForLoadState('domcontentloaded'); + await page.waitForLoadState('load'); + await page.evaluate(() => document.fonts.ready); + // Small settle time for React re-renders + await page.waitForTimeout(300); +} + +module.exports = { + setup() { + expect.extend({ toMatchImageSnapshot }); + }, + + async preVisit(page) { + // Block external image requests to prevent navigation context destruction. + // Stories load random avatars from pravatar.cc which cause flaky test failures. + await page.route('**/*pravatar*/**', (route) => route.abort()); + await page.route('**/*.pravatar.*/**', (route) => route.abort()); + }, + + async postVisit(page, context) { + // Disable CSS animations/transitions to avoid flaky diffs + await page.addStyleTag({ + content: ` + *, *::before, *::after { + animation-duration: 0s !important; + animation-delay: 0s !important; + transition-duration: 0s !important; + transition-delay: 0s !important; + } + `, + }); + + // Wait for page to be ready (DOM, resources, fonts — skip networkidle) + await waitUntilReady(page); + + const root = page.locator('#storybook-root'); + + try { + await root.waitFor({ state: 'visible', timeout: 5000 }); + } catch { + // Root not visible — fall back to full page screenshot + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + // Check if a Radix dialog portal is present (renders outside #storybook-root) + const dialogContent = page.locator('[data-radix-portal] [role="dialog"]'); + const hasDialog = await dialogContent.count().then((c) => c > 0); + + if (hasDialog) { + // Screenshot the dialog content instead of the empty root + try { + await dialogContent + .first() + .waitFor({ state: 'visible', timeout: 3000 }); + const screenshot = await dialogContent.first().screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } catch { + // Dialog not visible, fall through to page screenshot + } + } + + // Check if root content is too small (likely clipped by overflow) + const box = await root.boundingBox(); + if (box && (box.width < 10 || box.height < 10)) { + // Component is too small to be meaningful — screenshot full page + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + const screenshot = await root.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + }, +}; diff --git a/packages/modules/notion/__screenshots__/modules-notion-components-notiondatabase--default.png b/packages/modules/notion/__screenshots__/modules-notion-components-notiondatabase--default.png new file mode 100644 index 00000000..2ca4661c Binary files /dev/null and b/packages/modules/notion/__screenshots__/modules-notion-components-notiondatabase--default.png differ diff --git a/packages/modules/notion/src/lib/components/forms/new-database.scss b/packages/modules/notion/src/lib/components/forms/new-database.scss index 4d8eb0ba..27842cfa 100644 --- a/packages/modules/notion/src/lib/components/forms/new-database.scss +++ b/packages/modules/notion/src/lib/components/forms/new-database.scss @@ -15,11 +15,11 @@ background-color: var(--success-background, rgba(34, 197, 94, 0.1)); border: 1px solid var(--success-border, rgba(34, 197, 94, 0.3)); border-radius: 0.375rem; - font-size: 0.875rem; + font-size: var(--font-size-sm); color: var(--success-text, #22c55e); .back-button { - font-size: 0.75rem; + font-size: var(--font-size-sm); padding: 0.25rem 0.5rem; } } @@ -38,7 +38,7 @@ .empty-state { text-align: center; padding: 20px; - color: #666; + color: var(--c-neutral-text-faint); } .database-list { @@ -49,7 +49,7 @@ .database-item { padding: 12px; - border: 1px solid #e0e0e0; + border: 1px solid var(--c-neutral-border-form); border-radius: 6px; cursor: pointer; transition: background-color 0.2s ease; @@ -59,7 +59,7 @@ } &.selected { - border-color: #007acc; + border-color: var(--c-brand-highlight-blue); &:hover { background-color: rgba(255, 255, 255, 0.1); @@ -69,18 +69,18 @@ .database-title { font-weight: bold; margin-bottom: 4px; - color: #fff; + color: var(--white); } .database-description { - font-size: 0.9em; - color: #666; + font-size: var(--font-size-sm); + color: var(--c-neutral-text-faint); margin-bottom: 4px; } .database-id { - font-size: 0.8em; - color: #999; + font-size: var(--font-size-sm); + color: var(--c-neutral-text-subtle); font-family: 'Courier New', monospace; } } diff --git a/packages/modules/notion/src/lib/components/node-notion/node-notion-task.scss b/packages/modules/notion/src/lib/components/node-notion/node-notion-task.scss index fbf5b87a..ea0c4ab6 100644 --- a/packages/modules/notion/src/lib/components/node-notion/node-notion-task.scss +++ b/packages/modules/notion/src/lib/components/node-notion/node-notion-task.scss @@ -11,16 +11,16 @@ display: inline-block; padding: 6px 10px; border-radius: 3px; - font-size: 12px; - border: 1px solid #404040; - color: #ffffff; + font-size: var(--font-size-sm); + border: 1px solid var(--c-neutral-border); + color: var(--white); background-color: rgba(54, 54, 54, 0.1); cursor: pointer; margin-bottom: 8px; outline: none; option { - color: #ffffff; + color: var(--white); padding: 8px; } } @@ -39,7 +39,7 @@ gap: 8px; padding: 4px 8px; border-radius: 3px; - font-size: 12px; + font-size: var(--font-size-sm); margin-bottom: 12px; } @@ -61,17 +61,17 @@ &-label { display: block; - color: #adb5bd; + color: var(--c-neutral-text-dimmer); margin-right: 8px; - font-size: 11px; + font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: 0.5px; } &-value { - color: #ffffff; + color: var(--white); font-weight: 500; - font-size: 12px; + font-size: var(--font-size-sm); padding: 0; } } @@ -84,17 +84,17 @@ gap: 4px; &-label { - font-size: 12px; - color: #6b7280; + font-size: var(--font-size-sm); + color: var(--c-status-gray); } } .node-notion-input, .node-notion-select { padding: 4px 8px; - border: 1px solid #e5e7eb; + border: 1px solid var(--c-neutral-border-input); border-radius: 4px; - font-size: 14px; + font-size: var(--font-size-md); &:focus { outline: none; @@ -102,8 +102,8 @@ } .node-notion-relation { - font-size: 14px; - color: #6b7280; + font-size: var(--font-size-md); + color: var(--c-status-gray); padding: 4px 0; } @@ -121,8 +121,8 @@ outline: none; background: linear-gradient( 0deg, - var(--c-pink-4) 0%, - var(--c-pink-3) 100% + var(--primary-600) 0%, + var(--primary-500) 100% ); } } @@ -135,26 +135,26 @@ &:checked { background: linear-gradient( 0deg, - var(--c-pink-4) 0%, - var(--c-pink-3) 100% + var(--primary-600) 0%, + var(--primary-500) 100% ); } &:hover { background: linear-gradient( 0deg, - var(--c-pink-4) 0%, - var(--c-pink-3) 100% + var(--primary-600) 0%, + var(--primary-500) 100% ) rgba(255, 255, 255, 0.25); } &:focus { - background-color: yellow; + background-color: var(--yellow-300); } &:active { - background-color: orange; + background-color: var(--orange-400); } } } diff --git a/packages/modules/notion/src/lib/components/node-notion/notion-database.scss b/packages/modules/notion/src/lib/components/node-notion/notion-database.scss index 7a0ff27f..a268bf9b 100644 --- a/packages/modules/notion/src/lib/components/node-notion/notion-database.scss +++ b/packages/modules/notion/src/lib/components/node-notion/notion-database.scss @@ -14,7 +14,7 @@ flex-direction: column; &-header { - border-bottom: 1px solid #fff; + border-bottom: 1px solid var(--white); margin-bottom: 10px; display: flex; align-items: center; @@ -25,8 +25,8 @@ .notion-h2 { margin: 0; - color: #ffffff; - font-size: 18px; + color: var(--white); + font-size: var(--font-size-xl); font-weight: 600; display: inline; line-height: 40px; @@ -47,18 +47,18 @@ display: flex; gap: 8px; button { - background: #222; - color: #fff; - border: 1px solid #444; + background: var(--c-neutral-bg-dark); + color: var(--white); + border: 1px solid var(--c-neutral-border-light); border-radius: 4px; padding: 4px 12px; cursor: pointer; - font-size: 13px; + font-size: var(--font-size-md); transition: background 0.2s, color 0.2s; &.active, &:hover { - background: #4dabf7; - color: #222; + background: var(--c-highlight-blue); + color: var(--c-neutral-bg-dark); } } } @@ -75,13 +75,13 @@ .task-meta { display: inline-flex; gap: 8px; - font-size: 12px; + font-size: var(--font-size-sm); } .task-status { padding: 2px 6px; border-radius: 3px; - color: #ffffff; + color: var(--white); } } @@ -99,38 +99,38 @@ align-items: center; gap: 8px; padding: 12px 0; - border-bottom: 1px solid #404040; + border-bottom: 1px solid var(--c-neutral-border); flex-shrink: 0; label { - color: #ffffff; - font-size: 14px; + color: var(--white); + font-size: var(--font-size-md); font-weight: 500; } .notion-subgroup-select { background: rgba(54, 54, 54, 0.3); - border: 1px solid #404040; + border: 1px solid var(--c-neutral-border); border-radius: 4px; - color: #ffffff; + color: var(--white); padding: 6px 12px; - font-size: 13px; + font-size: var(--font-size-md); cursor: pointer; transition: border-color 0.2s ease; &:hover { - border-color: #4dabf7; + border-color: var(--c-highlight-blue); } &:focus { outline: none; - border-color: #4dabf7; - box-shadow: 0 0 0 2px rgba(77, 171, 247, 0.2); + border-color: var(--c-highlight-blue); + box-shadow: var(--shadow-focus-blue-sm); } option { - background: #2d2d2d; - color: #ffffff; + background: var(--c-neutral-bg-darker); + color: var(--white); } } } @@ -151,7 +151,7 @@ background: rgba(54, 54, 54, 0.1); border-radius: 8px; padding: 16px; - border: 1px solid #404040; + border: 1px solid var(--c-neutral-border); transition: all 0.2s ease; display: flex; flex-direction: column; @@ -161,7 +161,7 @@ } &.drag-over { background: rgba(54, 54, 54, 0.3) !important; - border-color: #4dabf7 !important; + border-color: var(--c-highlight-blue) !important; } } .notion-kanban-column-header { @@ -180,14 +180,14 @@ border-radius: 50%; } .notion-kanban-title { - color: #ffffff; - font-size: 14px; + color: var(--white); + font-size: var(--font-size-md); font-weight: 600; margin: 0; } .notion-kanban-count { - color: #868e96; - font-size: 12px; + color: var(--c-neutral-text-muted); + font-size: var(--font-size-sm); margin-left: 20px; } } @@ -229,8 +229,8 @@ &.drag-over { background: rgba(77, 171, 247, 0.1) !important; - border-color: #4dabf7 !important; - box-shadow: 0 0 0 2px rgba(77, 171, 247, 0.2); + border-color: var(--c-highlight-blue) !important; + box-shadow: var(--shadow-focus-blue-sm); } .notion-kanban-subgroup-header { @@ -248,15 +248,15 @@ } .notion-kanban-subgroup-title { - color: #ffffff; - font-size: 12px; + color: var(--white); + font-size: var(--font-size-sm); font-weight: 500; flex: 1; } .notion-kanban-subgroup-count { - color: #868e96; - font-size: 11px; + color: var(--c-neutral-text-muted); + font-size: var(--font-size-xs); background: rgba(255, 255, 255, 0.1); padding: 2px 6px; border-radius: 10px; @@ -286,29 +286,29 @@ .notion-kanban, .node-notion-task { .bg-blue { - background: #4dabf7; + background: var(--c-highlight-blue); } .bg-green { - background: #51cf66; + background: var(--c-highlight-green); } .bg-red { - background: #ff6b6b; + background: var(--c-highlight-red); } .bg-yellow { - background: #ffd43b; + background: var(--c-highlight-yellow); } .bg-orange { - background: #ff922b; + background: var(--c-highlight-orange); } .bg-purple { - background: #cc5de8; + background: var(--c-highlight-purple); } .bg-pink { - background: #f783ac; + background: var(--c-highlight-pink); } .bg-gray, .bg-default { - background: #868e96; + background: var(--c-neutral-text-muted); } } @@ -327,7 +327,7 @@ .task-item { background: rgba(255, 255, 255, 0.1); - border: 1px solid #404040; + border: 1px solid var(--c-neutral-border); border-radius: 8px; padding: 16px; cursor: grab; @@ -342,13 +342,13 @@ .task-item-header, .task-item-meta { position: relative; - z-index: 2; + z-index: var(--z-base); } &:hover { background: rgba(54, 54, 54, 0.2) !important; transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + box-shadow: var(--shadow-card-hover); } &.dragging { @@ -358,15 +358,15 @@ .task-item-header { .task-item-title { - color: #ffffff; - font-size: 14px; + color: var(--white); + font-size: var(--font-size-md); font-weight: 600; margin: 0 0 8px 0; line-height: 1.4; } .task-item-description { - color: #ddd; - font-size: 12px; + color: var(--c-neutral-text-light); + font-size: var(--font-size-sm); line-height: 24px; margin: 0 0 12px 0; border-radius: 4px; diff --git a/packages/modules/socials/.storybook/preview.ts b/packages/modules/socials/.storybook/preview.ts index 3e41a12d..c687277a 100644 --- a/packages/modules/socials/.storybook/preview.ts +++ b/packages/modules/socials/.storybook/preview.ts @@ -17,7 +17,7 @@ const preview: Preview = { values: [ { name: 'dark', - value: 'var(--color-background)', + value: 'var(--color-bg-app)', }, ], }, diff --git a/packages/modules/socials/.storybook/test-runner.js b/packages/modules/socials/.storybook/test-runner.js new file mode 100644 index 00000000..4b458ca7 --- /dev/null +++ b/packages/modules/socials/.storybook/test-runner.js @@ -0,0 +1,108 @@ +const path = require('path'); +const { toMatchImageSnapshot } = require('jest-image-snapshot'); + +const PKG_ROOT = path.resolve(__dirname, '..'); + +const SNAPSHOT_OPTS = { + failureThreshold: 0.002, // 0.2% pixel difference allowed + failureThresholdType: 'percent', + customSnapshotsDir: + process.env['SNAPSHOT_DIR'] || path.join(PKG_ROOT, '__screenshots__'), + customDiffDir: + process.env['DIFF_DIR'] || path.join(PKG_ROOT, '__diff_output__'), +}; + +/** + * Wait for the page to be ready without relying on networkidle + * (which times out when stories load external resources like avatars). + */ +async function waitUntilReady(page) { + await page.waitForLoadState('domcontentloaded'); + await page.waitForLoadState('load'); + await page.evaluate(() => document.fonts.ready); + // Small settle time for React re-renders + await page.waitForTimeout(300); +} + +module.exports = { + setup() { + expect.extend({ toMatchImageSnapshot }); + }, + + async preVisit(page) { + // Block external image requests to prevent navigation context destruction. + // Stories load random avatars from pravatar.cc which cause flaky test failures. + await page.route('**/*pravatar*/**', (route) => route.abort()); + await page.route('**/*.pravatar.*/**', (route) => route.abort()); + }, + + async postVisit(page, context) { + // Disable CSS animations/transitions to avoid flaky diffs + await page.addStyleTag({ + content: ` + *, *::before, *::after { + animation-duration: 0s !important; + animation-delay: 0s !important; + transition-duration: 0s !important; + transition-delay: 0s !important; + } + `, + }); + + // Wait for page to be ready (DOM, resources, fonts — skip networkidle) + await waitUntilReady(page); + + const root = page.locator('#storybook-root'); + + try { + await root.waitFor({ state: 'visible', timeout: 5000 }); + } catch { + // Root not visible — fall back to full page screenshot + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + // Check if a Radix dialog portal is present (renders outside #storybook-root) + const dialogContent = page.locator('[data-radix-portal] [role="dialog"]'); + const hasDialog = await dialogContent.count().then((c) => c > 0); + + if (hasDialog) { + // Screenshot the dialog content instead of the empty root + try { + await dialogContent + .first() + .waitFor({ state: 'visible', timeout: 3000 }); + const screenshot = await dialogContent.first().screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } catch { + // Dialog not visible, fall through to page screenshot + } + } + + // Check if root content is too small (likely clipped by overflow) + const box = await root.boundingBox(); + if (box && (box.width < 10 || box.height < 10)) { + // Component is too small to be meaningful — screenshot full page + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + const screenshot = await root.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + }, +}; diff --git a/packages/modules/socials/__screenshots__/modules-socials-forms-newiframe--normal.png b/packages/modules/socials/__screenshots__/modules-socials-forms-newiframe--normal.png new file mode 100644 index 00000000..a9cc7ef9 Binary files /dev/null and b/packages/modules/socials/__screenshots__/modules-socials-forms-newiframe--normal.png differ diff --git a/packages/modules/socials/__screenshots__/modules-socials-forms-newnodeuser--normal.png b/packages/modules/socials/__screenshots__/modules-socials-forms-newnodeuser--normal.png new file mode 100644 index 00000000..5953dad9 Binary files /dev/null and b/packages/modules/socials/__screenshots__/modules-socials-forms-newnodeuser--normal.png differ diff --git a/packages/modules/socials/__screenshots__/modules-socials-forms-newyoutube--normal.png b/packages/modules/socials/__screenshots__/modules-socials-forms-newyoutube--normal.png new file mode 100644 index 00000000..9386a8e0 Binary files /dev/null and b/packages/modules/socials/__screenshots__/modules-socials-forms-newyoutube--normal.png differ diff --git a/packages/modules/socials/__screenshots__/users-idcard--normal.png b/packages/modules/socials/__screenshots__/users-idcard--normal.png new file mode 100644 index 00000000..9be4e79d Binary files /dev/null and b/packages/modules/socials/__screenshots__/users-idcard--normal.png differ diff --git a/packages/modules/socials/src/lib/components/node-id-card.scss b/packages/modules/socials/src/lib/components/node-id-card.scss index cfd7c668..b2876eb8 100644 --- a/packages/modules/socials/src/lib/components/node-id-card.scss +++ b/packages/modules/socials/src/lib/components/node-id-card.scss @@ -14,8 +14,8 @@ justify-content: center; padding-top: 25px; height: 340px; - background-color: var(--c-pink-6); - box-shadow: 0 0 2px 0 rgba(255, 255, 255, 0.5); + background-color: var(--primary-900); + box-shadow: var(--shadow-id-card); } .photo-section { @@ -33,12 +33,12 @@ margin-top: 20px; .username { - background-color: #ffffff; + background-color: var(--white); border-radius: 4px; padding: 6px 10px; display: block; max-width: 100%; - font-size: 22px; + font-size: var(--font-size-2xl); font-weight: 525; //text-shadow: 0 0 7px rgba(255, 255, 255, 1); margin: 0; @@ -54,11 +54,11 @@ .node-reservation { .id-card { - background-color: #0a66c2; + background-color: var(--c-brand-linkedin); } .reservation-label { - color: #ffffff; - font-size: 14px; + color: var(--white); + font-size: var(--font-size-md); font-weight: 525; text-transform: uppercase; letter-spacing: 0.05em; diff --git a/packages/modules/socials/src/lib/components/node-id-card.tsx b/packages/modules/socials/src/lib/components/node-id-card.tsx index a5793895..a78894b0 100644 --- a/packages/modules/socials/src/lib/components/node-id-card.tsx +++ b/packages/modules/socials/src/lib/components/node-id-card.tsx @@ -51,8 +51,8 @@ export const NodeIdCard = ({ node }: { node: TGraphNode }) => { const users = useAwarenessUserList(); const color = user ? users.find((u) => u.username === user.username)?.color || - 'var(--c-gray-9)' - : 'var(--c-gray-9)'; + 'var(--neutral-9)' + : 'var(--neutral-9)'; const { id, selected, open, isOpened } = useNodeContext(); diff --git a/packages/modules/socials/src/lib/components/node-iframe.scss b/packages/modules/socials/src/lib/components/node-iframe.scss index c67dda1f..1ea9651e 100644 --- a/packages/modules/socials/src/lib/components/node-iframe.scss +++ b/packages/modules/socials/src/lib/components/node-iframe.scss @@ -24,7 +24,7 @@ right: 0; padding: 8px; background: rgba(0, 0, 0, 0.5); - color: white; + color: var(--white); text-align: center; opacity: 0; transition: opacity 0.2s ease; diff --git a/packages/modules/socials/src/lib/components/node-reservation.tsx b/packages/modules/socials/src/lib/components/node-reservation.tsx index 16a060c3..020902f8 100644 --- a/packages/modules/socials/src/lib/components/node-reservation.tsx +++ b/packages/modules/socials/src/lib/components/node-reservation.tsx @@ -57,8 +57,8 @@ export const NodeReservation = ({ node }: { node: TGraphNode }) => { const users = useAwarenessUserList(); const color = user ? users.find((u) => u.username === user.username)?.color || - 'var(--c-gray-9)' - : 'var(--c-gray-9)'; + 'var(--neutral-9)' + : 'var(--neutral-9)'; const { id, selected, open, isOpened } = useNodeContext(); diff --git a/packages/modules/socials/src/lib/components/node-video.scss b/packages/modules/socials/src/lib/components/node-video.scss index c2c2b63d..fc242866 100644 --- a/packages/modules/socials/src/lib/components/node-video.scss +++ b/packages/modules/socials/src/lib/components/node-video.scss @@ -1,3 +1,8 @@ +:root { + // --- Socials domain tokens --- + --color-youtube: #f00; +} + .video-node { .select-handle { position: absolute; @@ -6,12 +11,12 @@ transform: translateX(-50%); width: 100px; height: 30px; - background-color: #aaa; + background-color: var(--c-neutral-select-handle); border-radius: 0 0 10px 10px; opacity: 0; transition: opacity 0.3s ease-in-out; - font-size: 14px; - color: #fff; + font-size: var(--font-size-md); + color: var(--white); display: flex; align-items: center; justify-content: center; diff --git a/packages/modules/socials/src/lib/components/text-editor.scss b/packages/modules/socials/src/lib/components/text-editor.scss index 70606b69..9f6ddd90 100644 --- a/packages/modules/socials/src/lib/components/text-editor.scss +++ b/packages/modules/socials/src/lib/components/text-editor.scss @@ -4,10 +4,10 @@ transition: opacity 0.3s ease-in-out; .ql-picker { - color: white; + color: var(--white); &.ql-size { - border: solid 1px #fff; + border: solid 1px var(--white); } &.ql-size .ql-picker-label::before, &.ql-size .ql-picker-item::before { @@ -16,14 +16,14 @@ } .ql-stroke { - stroke: white; + stroke: var(--white); } .ql-picker-options { - background-color: #2a2a2a; + background-color: var(--black-700); .ql-picker-item { - color: white; + color: var(--white); } } @@ -40,9 +40,9 @@ .ql-picker-label.ql-active, .ql-picker-item:hover, .ql-picker-item.ql-selected { - color: #ccc; + color: var(--neutral-7); .ql-stroke { - stroke: #ccc; + stroke: var(--neutral-7); } } } @@ -53,10 +53,10 @@ } .ql-editor * { - color: white; + color: var(--white); } .ql-editor.ql-blank::before { - color: #bbb; + color: var(--neutral-8); } .node-quill:hover { diff --git a/packages/modules/tabs/.storybook/preview.ts b/packages/modules/tabs/.storybook/preview.ts index e62e8e28..f643071d 100644 --- a/packages/modules/tabs/.storybook/preview.ts +++ b/packages/modules/tabs/.storybook/preview.ts @@ -16,7 +16,7 @@ const preview: Preview = { values: [ { name: 'dark', - value: 'var(--color-background)', + value: 'var(--color-bg-app)', }, ], }, diff --git a/packages/modules/tabs/.storybook/test-runner.js b/packages/modules/tabs/.storybook/test-runner.js new file mode 100644 index 00000000..4b458ca7 --- /dev/null +++ b/packages/modules/tabs/.storybook/test-runner.js @@ -0,0 +1,108 @@ +const path = require('path'); +const { toMatchImageSnapshot } = require('jest-image-snapshot'); + +const PKG_ROOT = path.resolve(__dirname, '..'); + +const SNAPSHOT_OPTS = { + failureThreshold: 0.002, // 0.2% pixel difference allowed + failureThresholdType: 'percent', + customSnapshotsDir: + process.env['SNAPSHOT_DIR'] || path.join(PKG_ROOT, '__screenshots__'), + customDiffDir: + process.env['DIFF_DIR'] || path.join(PKG_ROOT, '__diff_output__'), +}; + +/** + * Wait for the page to be ready without relying on networkidle + * (which times out when stories load external resources like avatars). + */ +async function waitUntilReady(page) { + await page.waitForLoadState('domcontentloaded'); + await page.waitForLoadState('load'); + await page.evaluate(() => document.fonts.ready); + // Small settle time for React re-renders + await page.waitForTimeout(300); +} + +module.exports = { + setup() { + expect.extend({ toMatchImageSnapshot }); + }, + + async preVisit(page) { + // Block external image requests to prevent navigation context destruction. + // Stories load random avatars from pravatar.cc which cause flaky test failures. + await page.route('**/*pravatar*/**', (route) => route.abort()); + await page.route('**/*.pravatar.*/**', (route) => route.abort()); + }, + + async postVisit(page, context) { + // Disable CSS animations/transitions to avoid flaky diffs + await page.addStyleTag({ + content: ` + *, *::before, *::after { + animation-duration: 0s !important; + animation-delay: 0s !important; + transition-duration: 0s !important; + transition-delay: 0s !important; + } + `, + }); + + // Wait for page to be ready (DOM, resources, fonts — skip networkidle) + await waitUntilReady(page); + + const root = page.locator('#storybook-root'); + + try { + await root.waitFor({ state: 'visible', timeout: 5000 }); + } catch { + // Root not visible — fall back to full page screenshot + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + // Check if a Radix dialog portal is present (renders outside #storybook-root) + const dialogContent = page.locator('[data-radix-portal] [role="dialog"]'); + const hasDialog = await dialogContent.count().then((c) => c > 0); + + if (hasDialog) { + // Screenshot the dialog content instead of the empty root + try { + await dialogContent + .first() + .waitFor({ state: 'visible', timeout: 3000 }); + const screenshot = await dialogContent.first().screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } catch { + // Dialog not visible, fall through to page screenshot + } + } + + // Check if root content is too small (likely clipped by overflow) + const box = await root.boundingBox(); + if (box && (box.width < 10 || box.height < 10)) { + // Component is too small to be meaningful — screenshot full page + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + const screenshot = await root.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + }, +}; diff --git a/packages/modules/tabs/__screenshots__/modules-tabs-components-tabs--normal.png b/packages/modules/tabs/__screenshots__/modules-tabs-components-tabs--normal.png new file mode 100644 index 00000000..a96d0941 Binary files /dev/null and b/packages/modules/tabs/__screenshots__/modules-tabs-components-tabs--normal.png differ diff --git a/packages/modules/tabs/src/lib/components/tabs-radix.scss b/packages/modules/tabs/src/lib/components/tabs-radix.scss index 2ee0824a..e9882173 100644 --- a/packages/modules/tabs/src/lib/components/tabs-radix.scss +++ b/packages/modules/tabs/src/lib/components/tabs-radix.scss @@ -11,8 +11,8 @@ .TabsList { flex-shrink: 0; display: flex; - border-bottom: 1px solid var(--color-background); - background: var(--c-blue-gray-5); + border-bottom: 1px solid var(--color-bg-app); + background: var(--surface-700); padding: 0 20px 0 40px; gap: 15px; overflow-x: auto; @@ -23,10 +23,10 @@ &::-webkit-scrollbar { height: 5px; width: 5px; - background: var(--c-blue-gray-6); + background: var(--surface-800); } &::-webkit-scrollbar-thumb { - background: var(--c-blue-gray-4); + background: var(--surface-300); border-radius: 5px; } @@ -34,7 +34,7 @@ min-width: 2px; border-radius: 2px; height: 20px; - background: var(--c-blue-gray-4); + background: var(--surface-300); margin-left: 17px; } @@ -42,9 +42,9 @@ flex: 0 0 auto; line-height: 1; user-select: none; - color: var(--c-gray-7); + color: var(--neutral-7); font-weight: 700; - font-size: 12px; + font-size: var(--font-size-sm); cursor: pointer; height: 100%; display: flex; @@ -71,7 +71,7 @@ } .trigger-active { - background: var(--color-background); + background: var(--color-bg-app); &:before { left: -33px; @include left-curve('%23141432'); @@ -82,7 +82,7 @@ } .trigger-terminal { - background: var(--c-pink-3); + background: var(--primary-500); &:before { left: -33px; @include left-curve('%23a35bbb'); @@ -131,7 +131,7 @@ } .icon-add { left: 2px; - background: var(--c-blue-gray-7); + background: var(--surface-700); height: 12px; width: 12px; border-radius: 3px; @@ -152,13 +152,13 @@ span.editing { border-radius: 4px; box-sizing: border-box; - border: solid 1px var(--c-gray-8); + border: solid 1px var(--neutral-8); outline: none; padding: 3px 8px; } .children-count { - background-color: var(--c-pink-3); + background-color: var(--primary-500); margin-left: 10px; border-radius: 50%; height: 14px; @@ -166,7 +166,7 @@ display: flex; align-items: center; justify-content: center; - font-size: 9px; + font-size: var(--font-size-2xs); } } } @@ -186,7 +186,7 @@ .TabsContent { flex-grow: 1; border-bottom: none; - color: gray; + color: var(--neutral-9); outline: none; &[data-state='inactive'] { display: none; /* Hide inactive tab content */ diff --git a/packages/modules/user-containers/.storybook/preview.ts b/packages/modules/user-containers/.storybook/preview.ts index 7f2290b1..7f82cf53 100644 --- a/packages/modules/user-containers/.storybook/preview.ts +++ b/packages/modules/user-containers/.storybook/preview.ts @@ -18,7 +18,7 @@ const preview: Preview = { values: [ { name: 'dark', - value: 'var(--color-background)', + value: 'var(--color-bg-app)', }, ], }, diff --git a/packages/modules/user-containers/.storybook/test-runner.js b/packages/modules/user-containers/.storybook/test-runner.js new file mode 100644 index 00000000..4b458ca7 --- /dev/null +++ b/packages/modules/user-containers/.storybook/test-runner.js @@ -0,0 +1,108 @@ +const path = require('path'); +const { toMatchImageSnapshot } = require('jest-image-snapshot'); + +const PKG_ROOT = path.resolve(__dirname, '..'); + +const SNAPSHOT_OPTS = { + failureThreshold: 0.002, // 0.2% pixel difference allowed + failureThresholdType: 'percent', + customSnapshotsDir: + process.env['SNAPSHOT_DIR'] || path.join(PKG_ROOT, '__screenshots__'), + customDiffDir: + process.env['DIFF_DIR'] || path.join(PKG_ROOT, '__diff_output__'), +}; + +/** + * Wait for the page to be ready without relying on networkidle + * (which times out when stories load external resources like avatars). + */ +async function waitUntilReady(page) { + await page.waitForLoadState('domcontentloaded'); + await page.waitForLoadState('load'); + await page.evaluate(() => document.fonts.ready); + // Small settle time for React re-renders + await page.waitForTimeout(300); +} + +module.exports = { + setup() { + expect.extend({ toMatchImageSnapshot }); + }, + + async preVisit(page) { + // Block external image requests to prevent navigation context destruction. + // Stories load random avatars from pravatar.cc which cause flaky test failures. + await page.route('**/*pravatar*/**', (route) => route.abort()); + await page.route('**/*.pravatar.*/**', (route) => route.abort()); + }, + + async postVisit(page, context) { + // Disable CSS animations/transitions to avoid flaky diffs + await page.addStyleTag({ + content: ` + *, *::before, *::after { + animation-duration: 0s !important; + animation-delay: 0s !important; + transition-duration: 0s !important; + transition-delay: 0s !important; + } + `, + }); + + // Wait for page to be ready (DOM, resources, fonts — skip networkidle) + await waitUntilReady(page); + + const root = page.locator('#storybook-root'); + + try { + await root.waitFor({ state: 'visible', timeout: 5000 }); + } catch { + // Root not visible — fall back to full page screenshot + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + // Check if a Radix dialog portal is present (renders outside #storybook-root) + const dialogContent = page.locator('[data-radix-portal] [role="dialog"]'); + const hasDialog = await dialogContent.count().then((c) => c > 0); + + if (hasDialog) { + // Screenshot the dialog content instead of the empty root + try { + await dialogContent + .first() + .waitFor({ state: 'visible', timeout: 3000 }); + const screenshot = await dialogContent.first().screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } catch { + // Dialog not visible, fall through to page screenshot + } + } + + // Check if root content is too small (likely clipped by overflow) + const box = await root.boundingBox(); + if (box && (box.width < 10 || box.height < 10)) { + // Component is too small to be meaningful — screenshot full page + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + const screenshot = await root.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + }, +}; diff --git a/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-led--notebook-card.png b/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-led--notebook-card.png new file mode 100644 index 00000000..8f069ffb Binary files /dev/null and b/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-led--notebook-card.png differ diff --git a/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-led--resource-bar.png b/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-led--resource-bar.png new file mode 100644 index 00000000..e6b3082b Binary files /dev/null and b/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-led--resource-bar.png differ diff --git a/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-led--server-card.png b/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-led--server-card.png new file mode 100644 index 00000000..2c0ea420 Binary files /dev/null and b/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-led--server-card.png differ diff --git a/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-node-server--off.png b/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-node-server--off.png new file mode 100644 index 00000000..d3f4807c Binary files /dev/null and b/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-node-server--off.png differ diff --git a/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-node-server--running-recent-activity.png b/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-node-server--running-recent-activity.png new file mode 100644 index 00000000..f940441e Binary files /dev/null and b/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-node-server--running-recent-activity.png differ diff --git a/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-node-server--with-services.png b/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-node-server--with-services.png new file mode 100644 index 00000000..9238bb68 Binary files /dev/null and b/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-node-server--with-services.png differ diff --git a/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-server-card--off.png b/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-server-card--off.png new file mode 100644 index 00000000..e3aed659 Binary files /dev/null and b/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-server-card--off.png differ diff --git a/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-server-card--running-recent-activity.png b/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-server-card--running-recent-activity.png new file mode 100644 index 00000000..cf82e66f Binary files /dev/null and b/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-server-card--running-recent-activity.png differ diff --git a/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-server-card--with-services.png b/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-server-card--with-services.png new file mode 100644 index 00000000..b991df2b Binary files /dev/null and b/packages/modules/user-containers/__screenshots__/modules-usercontainers-components-server-card--with-services.png differ diff --git a/packages/modules/user-containers/__screenshots__/modules-usercontainers-forms-docker-options--normal.png b/packages/modules/user-containers/__screenshots__/modules-usercontainers-forms-docker-options--normal.png new file mode 100644 index 00000000..9bc41837 Binary files /dev/null and b/packages/modules/user-containers/__screenshots__/modules-usercontainers-forms-docker-options--normal.png differ diff --git a/packages/modules/user-containers/src/lib/components/server-card.tsx b/packages/modules/user-containers/src/lib/components/server-card.tsx index 61f96b27..2e04f0da 100644 --- a/packages/modules/user-containers/src/lib/components/server-card.tsx +++ b/packages/modules/user-containers/src/lib/components/server-card.tsx @@ -98,8 +98,12 @@ export const UserContainerCardInternal = ({ serviceUrl(container, firstServiceName); // Terminal URL: use serviceUrl helper to construct proper FQDN-based URL - const terminalService = container.httpServices.find(s => s.name === 'terminal'); - const terminalUrl = terminalService ? serviceUrl(container, 'terminal') : null; + const terminalService = container.httpServices.find( + (s) => s.name === 'terminal' + ); + const terminalUrl = terminalService + ? serviceUrl(container, 'terminal') + : null; // @@ -107,20 +111,31 @@ export const UserContainerCardInternal = ({
            { - firstServiceName && onOpenService?.(firstServiceName); + if (firstServiceName) { + onOpenService?.(firstServiceName); + } }} > {alive && container.last_activity && (
            -
            -

            +

            +

            last activity  -

            -

            +

            +

            {container.container_name}

            {image && ( {image.description} @@ -151,7 +180,7 @@ export const UserContainerCardInternal = ({ - + {container.runner.id === 'none' && ( <> -
            +

            Select a runner to start the container

            -
            +
            {Array.from(runners.values()).map((runner, k) => (
            { onSelectRunner(runner.label); }} @@ -227,11 +262,11 @@ export const UserContainerCardInternal = ({ -
            +
            -
            +
            {alive && container.system && }
            @@ -260,11 +295,11 @@ const SystemInfo = ({ return ( - + -
            +
            {cpu && (
            CPU: {cpu.model} ({cpu.count} cores,{' '} diff --git a/packages/modules/user-containers/src/lib/components/status-led.scss b/packages/modules/user-containers/src/lib/components/status-led.scss index 6afd14ab..7dc259f5 100644 --- a/packages/modules/user-containers/src/lib/components/status-led.scss +++ b/packages/modules/user-containers/src/lib/components/status-led.scss @@ -1,28 +1,28 @@ .status-led { &.led-blue { - --led-color: #5aa3f8; - --led-shadow-color: #2b6da0; + --led-color: var(--c-led-blue); + --led-box-shadow: 0px 0px 2px 1px var(--c-led-blue-shadow); } &.led-green { - --led-color: #5bc773; - --led-shadow-color: #2b961d; + --led-color: var(--green-300); + --led-box-shadow: 0px 0px 2px 1px var(--c-led-green-shadow); } &.led-red { - --led-color: #f75252; - --led-shadow-color: #ab3030; + --led-color: var(--c-led-red); + --led-box-shadow: 0px 0px 2px 1px var(--c-led-red-shadow); } &.led-yellow { - --led-color: #ffe921; - --led-shadow-color: #67600e; + --led-color: var(--c-led-yellow); + --led-box-shadow: 0px 0px 2px 1px var(--c-led-yellow-shadow); } > div { background-color: var(--led-color); filter: drop-shadow(0px 0px 4px var(--led-color)); - box-shadow: 0px 0px 2px 1px var(--led-shadow-color); + box-shadow: var(--led-box-shadow); > div { background-color: var(--led-color); diff --git a/packages/modules/user-containers/src/lib/components/status-led.tsx b/packages/modules/user-containers/src/lib/components/status-led.tsx index 2c50dc75..add54fd5 100644 --- a/packages/modules/user-containers/src/lib/components/status-led.tsx +++ b/packages/modules/user-containers/src/lib/components/status-led.tsx @@ -1,13 +1,5 @@ import './status-led.scss'; -// force tailwind class compilation -const dummy = ` - h-[14px] w-[14px] h-[12px] w-[12px] h-[10px] w-[10px] - h-[8px] w-[8px] h-[6px] w-[6px] -`; -// Use dummy to avoid unused variable warning -void dummy; - export type StatusLedProps = { type: 'resource-bar' | 'server-card' | 'notebook-card'; color: 'green' | 'red' | 'yellow' | 'blue'; @@ -24,9 +16,20 @@ export const StatusLed = ({ type, color }: StatusLedProps) => { return (
            -
            +
            diff --git a/packages/modules/user-containers/src/lib/index.scss b/packages/modules/user-containers/src/lib/index.scss index b5c61c95..cf5055c4 100644 --- a/packages/modules/user-containers/src/lib/index.scss +++ b/packages/modules/user-containers/src/lib/index.scss @@ -1,3 +1,10 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +:root { + // --- LED Status domain tokens --- + --c-led-blue: #5aa3f8; + --c-led-blue-shadow: #2b6da0; + --c-led-green-shadow: #2b961d; + --c-led-red: #f75252; + --c-led-red-shadow: #ab3030; + --c-led-yellow: #ffe921; + --c-led-yellow-shadow: #67600e; +} diff --git a/packages/modules/whiteboard/.storybook/preview.ts b/packages/modules/whiteboard/.storybook/preview.ts index d9cd1186..b907c9b6 100644 --- a/packages/modules/whiteboard/.storybook/preview.ts +++ b/packages/modules/whiteboard/.storybook/preview.ts @@ -17,7 +17,7 @@ const preview: Preview = { values: [ { name: 'dark', - value: 'var(--color-background)', + value: 'var(--color-bg-app)', }, ], }, diff --git a/packages/modules/whiteboard/.storybook/test-runner.js b/packages/modules/whiteboard/.storybook/test-runner.js new file mode 100644 index 00000000..4b458ca7 --- /dev/null +++ b/packages/modules/whiteboard/.storybook/test-runner.js @@ -0,0 +1,108 @@ +const path = require('path'); +const { toMatchImageSnapshot } = require('jest-image-snapshot'); + +const PKG_ROOT = path.resolve(__dirname, '..'); + +const SNAPSHOT_OPTS = { + failureThreshold: 0.002, // 0.2% pixel difference allowed + failureThresholdType: 'percent', + customSnapshotsDir: + process.env['SNAPSHOT_DIR'] || path.join(PKG_ROOT, '__screenshots__'), + customDiffDir: + process.env['DIFF_DIR'] || path.join(PKG_ROOT, '__diff_output__'), +}; + +/** + * Wait for the page to be ready without relying on networkidle + * (which times out when stories load external resources like avatars). + */ +async function waitUntilReady(page) { + await page.waitForLoadState('domcontentloaded'); + await page.waitForLoadState('load'); + await page.evaluate(() => document.fonts.ready); + // Small settle time for React re-renders + await page.waitForTimeout(300); +} + +module.exports = { + setup() { + expect.extend({ toMatchImageSnapshot }); + }, + + async preVisit(page) { + // Block external image requests to prevent navigation context destruction. + // Stories load random avatars from pravatar.cc which cause flaky test failures. + await page.route('**/*pravatar*/**', (route) => route.abort()); + await page.route('**/*.pravatar.*/**', (route) => route.abort()); + }, + + async postVisit(page, context) { + // Disable CSS animations/transitions to avoid flaky diffs + await page.addStyleTag({ + content: ` + *, *::before, *::after { + animation-duration: 0s !important; + animation-delay: 0s !important; + transition-duration: 0s !important; + transition-delay: 0s !important; + } + `, + }); + + // Wait for page to be ready (DOM, resources, fonts — skip networkidle) + await waitUntilReady(page); + + const root = page.locator('#storybook-root'); + + try { + await root.waitFor({ state: 'visible', timeout: 5000 }); + } catch { + // Root not visible — fall back to full page screenshot + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + // Check if a Radix dialog portal is present (renders outside #storybook-root) + const dialogContent = page.locator('[data-radix-portal] [role="dialog"]'); + const hasDialog = await dialogContent.count().then((c) => c > 0); + + if (hasDialog) { + // Screenshot the dialog content instead of the empty root + try { + await dialogContent + .first() + .waitFor({ state: 'visible', timeout: 3000 }); + const screenshot = await dialogContent.first().screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } catch { + // Dialog not visible, fall through to page screenshot + } + } + + // Check if root content is too small (likely clipped by overflow) + const box = await root.boundingBox(); + if (box && (box.width < 10 || box.height < 10)) { + // Component is too small to be meaningful — screenshot full page + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + const screenshot = await root.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + }, +}; diff --git a/packages/modules/whiteboard/__screenshots__/modules-space-components-cursor--normal.png b/packages/modules/whiteboard/__screenshots__/modules-space-components-cursor--normal.png new file mode 100644 index 00000000..1b1341cb Binary files /dev/null and b/packages/modules/whiteboard/__screenshots__/modules-space-components-cursor--normal.png differ diff --git a/packages/modules/whiteboard/__screenshots__/modules-space-components-edges--primary.png b/packages/modules/whiteboard/__screenshots__/modules-space-components-edges--primary.png new file mode 100644 index 00000000..59f12120 Binary files /dev/null and b/packages/modules/whiteboard/__screenshots__/modules-space-components-edges--primary.png differ diff --git a/packages/modules/whiteboard/__screenshots__/modules-space-components-inputs-outputs--piano-input.png b/packages/modules/whiteboard/__screenshots__/modules-space-components-inputs-outputs--piano-input.png new file mode 100644 index 00000000..ab698666 Binary files /dev/null and b/packages/modules/whiteboard/__screenshots__/modules-space-components-inputs-outputs--piano-input.png differ diff --git a/packages/modules/whiteboard/__screenshots__/modules-space-components-inputs-outputs--piano-ouput.png b/packages/modules/whiteboard/__screenshots__/modules-space-components-inputs-outputs--piano-ouput.png new file mode 100644 index 00000000..e6868d53 Binary files /dev/null and b/packages/modules/whiteboard/__screenshots__/modules-space-components-inputs-outputs--piano-ouput.png differ diff --git a/packages/modules/whiteboard/__screenshots__/modules-space-components-inputs-outputs--simple-input.png b/packages/modules/whiteboard/__screenshots__/modules-space-components-inputs-outputs--simple-input.png new file mode 100644 index 00000000..f4d7bc64 Binary files /dev/null and b/packages/modules/whiteboard/__screenshots__/modules-space-components-inputs-outputs--simple-input.png differ diff --git a/packages/modules/whiteboard/__screenshots__/modules-space-components-inputs-outputs--simple-ouput.png b/packages/modules/whiteboard/__screenshots__/modules-space-components-inputs-outputs--simple-ouput.png new file mode 100644 index 00000000..f9684148 Binary files /dev/null and b/packages/modules/whiteboard/__screenshots__/modules-space-components-inputs-outputs--simple-ouput.png differ diff --git a/packages/modules/whiteboard/__screenshots__/modules-space-components-node-toolbar--normal.png b/packages/modules/whiteboard/__screenshots__/modules-space-components-node-toolbar--normal.png new file mode 100644 index 00000000..a4b45465 Binary files /dev/null and b/packages/modules/whiteboard/__screenshots__/modules-space-components-node-toolbar--normal.png differ diff --git a/packages/modules/whiteboard/__screenshots__/modules-space-components-pin--normal.png b/packages/modules/whiteboard/__screenshots__/modules-space-components-pin--normal.png new file mode 100644 index 00000000..645546d0 Binary files /dev/null and b/packages/modules/whiteboard/__screenshots__/modules-space-components-pin--normal.png differ diff --git a/packages/modules/whiteboard/src/lib/components/assets/cursor/Cursor.stories.ts b/packages/modules/whiteboard/src/lib/components/assets/cursor/Cursor.stories.ts index 9ee37c46..639df47c 100644 --- a/packages/modules/whiteboard/src/lib/components/assets/cursor/Cursor.stories.ts +++ b/packages/modules/whiteboard/src/lib/components/assets/cursor/Cursor.stories.ts @@ -40,6 +40,6 @@ export const Normal: Story = { username: 'github:JohnManager', firstname: 'John', lastname: 'Manager', - color: 'var(--c-pink-3)', + color: 'var(--primary-500)', }, }; diff --git a/packages/modules/whiteboard/src/lib/components/assets/cursor/Cursor.tsx b/packages/modules/whiteboard/src/lib/components/assets/cursor/Cursor.tsx index b85bab7d..d5a88d27 100644 --- a/packages/modules/whiteboard/src/lib/components/assets/cursor/Cursor.tsx +++ b/packages/modules/whiteboard/src/lib/components/assets/cursor/Cursor.tsx @@ -41,8 +41,8 @@ const CursorIcon = ({ ...props }: React.HTMLAttributes) => ( ); diff --git a/packages/modules/whiteboard/src/lib/components/assets/edges/edge-menu.scss b/packages/modules/whiteboard/src/lib/components/assets/edges/edge-menu.scss index 72131ed1..b05b95b1 100644 --- a/packages/modules/whiteboard/src/lib/components/assets/edges/edge-menu.scss +++ b/packages/modules/whiteboard/src/lib/components/assets/edges/edge-menu.scss @@ -17,7 +17,7 @@ .edge-menu-label { min-width: 70px; font-weight: 600; - font-size: 12px; + font-size: var(--font-size-sm); } // For grouping marker controls @@ -28,7 +28,7 @@ } .Label { - font-size: 12px; + font-size: var(--font-size-sm); width: initial; min-width: 60px; text-align: left; diff --git a/packages/modules/whiteboard/src/lib/components/assets/edges/edge.stories.tsx b/packages/modules/whiteboard/src/lib/components/assets/edges/edge.stories.tsx index 3f0778d6..1b82b04e 100644 --- a/packages/modules/whiteboard/src/lib/components/assets/edges/edge.stories.tsx +++ b/packages/modules/whiteboard/src/lib/components/assets/edges/edge.stories.tsx @@ -96,7 +96,7 @@ const CustomNode = ({ id }: NodeProps) => { width: '50px', height: '50px', borderRadius: '10px', - background: 'var(--c-black-3)', + background: 'var(--black-600)', }} > diff --git a/packages/modules/whiteboard/src/lib/components/assets/inputsOutputs/icons.tsx b/packages/modules/whiteboard/src/lib/components/assets/inputsOutputs/icons.tsx index 27beec82..21a9bd79 100644 --- a/packages/modules/whiteboard/src/lib/components/assets/inputsOutputs/icons.tsx +++ b/packages/modules/whiteboard/src/lib/components/assets/inputsOutputs/icons.tsx @@ -12,7 +12,7 @@ export const icons = { > @@ -29,13 +29,13 @@ export const icons = { > @@ -50,7 +50,7 @@ export const icons = { fill="none" xmlns="http://www.w3.org/2000/svg" > - + @@ -74,7 +74,7 @@ export const icons = { xmlns="http://www.w3.org/2000/svg" > - + @@ -98,7 +98,7 @@ export const icons = { fill="none" xmlns="http://www.w3.org/2000/svg" > - + @@ -121,7 +121,7 @@ export const icons = { fill="none" xmlns="http://www.w3.org/2000/svg" > - + @@ -145,7 +145,7 @@ export const icons = { fill="none" xmlns="http://www.w3.org/2000/svg" > - + - + @@ -180,7 +180,7 @@ export const icons = { fill="none" xmlns="http://www.w3.org/2000/svg" > - + - + diff --git a/packages/modules/whiteboard/src/lib/components/assets/inputsOutputs/inputsOutputs.css b/packages/modules/whiteboard/src/lib/components/assets/inputsOutputs/inputsOutputs.css index d0e56405..a16500d4 100644 --- a/packages/modules/whiteboard/src/lib/components/assets/inputsOutputs/inputsOutputs.css +++ b/packages/modules/whiteboard/src/lib/components/assets/inputsOutputs/inputsOutputs.css @@ -39,13 +39,13 @@ --edges-count-offset-closed: 5px; --edges-count-offset-opened: 20px; border-radius: 50%; - background: var(--c-blue-gray-1); + background: var(--surface-700); display: flex; align-items: center; justify-content: center; - border: 1px solid white; - color: var(--c-white-1); - font-size: 10px; + border: 1px solid var(--white); + color: var(--white); + font-size: var(--font-size-2xs); position: absolute; left: 50%; transform: translate(-50%, 0); diff --git a/packages/modules/whiteboard/src/lib/components/assets/node-header/node-header.scss b/packages/modules/whiteboard/src/lib/components/assets/node-header/node-header.scss index 2fe14250..bd0302cf 100644 --- a/packages/modules/whiteboard/src/lib/components/assets/node-header/node-header.scss +++ b/packages/modules/whiteboard/src/lib/components/assets/node-header/node-header.scss @@ -17,10 +17,10 @@ height: 22px; font-style: normal; font-weight: 500; - font-size: 10px; - background: var(--c-gray-10); + font-size: var(--font-size-2xs); + background: var(--neutral-10); border-radius: 4px; - color: var(--c-white-1); + color: var(--white); line-height: 22px; text-transform: uppercase; vertical-align: top; @@ -34,13 +34,13 @@ background: var(--color-youtube); } &.node-type-terminal { - background: var(--c-black-6); + background: var(--black-900); } &.node-type-server { - background: var(--c-orange-2); + background: var(--orange-200); } &.node-type-volume { - background: var(--color-volume); + background: var(--cyan-600); } } } @@ -50,9 +50,9 @@ min-width: 0; font-style: normal; font-weight: 700; - font-size: 14px; + font-size: var(--font-size-md); line-height: 16px; - color: var(--c-white-1); + color: var(--white); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -60,7 +60,7 @@ } .header-row { - border-top: solid 1px rgba(255, 255, 255, 0.2); + border-top: solid 1px var(--alpha-white-15); margin-top: 6px; padding-top: 4px; } diff --git a/packages/modules/whiteboard/src/lib/components/assets/node-header/node-main-toolbar.scss b/packages/modules/whiteboard/src/lib/components/assets/node-header/node-main-toolbar.scss index 60c84871..d99b1d55 100644 --- a/packages/modules/whiteboard/src/lib/components/assets/node-header/node-main-toolbar.scss +++ b/packages/modules/whiteboard/src/lib/components/assets/node-header/node-main-toolbar.scss @@ -16,9 +16,9 @@ vertical-align: middle; &:hover { - background: var(--c-pink-4); + background: var(--primary-600); &.red { - background: var(--c-red-4); + background: var(--red-500); } } svg { @@ -26,7 +26,7 @@ --icon-height: 14px; path { - fill: var(--c-white-1); + fill: var(--white); } } diff --git a/packages/modules/whiteboard/src/lib/components/assets/slot/Slot.stories.tsx b/packages/modules/whiteboard/src/lib/components/assets/slot/Slot.stories.tsx index fa9cee77..5e519923 100644 --- a/packages/modules/whiteboard/src/lib/components/assets/slot/Slot.stories.tsx +++ b/packages/modules/whiteboard/src/lib/components/assets/slot/Slot.stories.tsx @@ -13,11 +13,11 @@ const SlotStory = (props: SlotProps) => { width: '100px', height: '100px', margin: '100px', - background: 'var(--ca-white-1)', + background: 'var(--alpha-white-10)', borderRadius: '10px', position: 'relative', textAlign: 'center', - color: 'var(--c-gray-8)', + color: 'var(--neutral-8)', padding: '15px 0', boxSizing: 'border-box', }} diff --git a/packages/modules/whiteboard/src/lib/components/assets/slot/slot.css b/packages/modules/whiteboard/src/lib/components/assets/slot/slot.css index 4e567734..40861d41 100644 --- a/packages/modules/whiteboard/src/lib/components/assets/slot/slot.css +++ b/packages/modules/whiteboard/src/lib/components/assets/slot/slot.css @@ -8,15 +8,15 @@ } .handles-bar ul li { - font-size: 8px; - color: var(--c-yellow-1); + font-size: var(--font-size-2xs); + color: var(--yellow-100); white-space: nowrap; margin: 0; padding: 0; width: 8.5px; height: 40px; border-radius: 8px; - border: 1px solid var(--c-yellow-1); + border: 1px solid var(--yellow-100); user-select: none; position: relative; } diff --git a/packages/modules/whiteboard/src/lib/components/css/edges.scss b/packages/modules/whiteboard/src/lib/components/css/edges.scss index f4b2c8d0..61a9e227 100644 --- a/packages/modules/whiteboard/src/lib/components/css/edges.scss +++ b/packages/modules/whiteboard/src/lib/components/css/edges.scss @@ -1,17 +1,24 @@ +:root { + // --- Whiteboard edge domain tokens --- + --color-edge-reference: var(--orange-300); + --color-edge-sequence: var(--primary-500); + --color-scene: var(--orange-500); +} + path.react-flow__edge-path { // transition: color 0.2s; - stroke: var(--c-pink-3); + stroke: var(--primary-500); stroke-width: 1.5px; &._unknown_ { - stroke: #7e004c; /* black */ + stroke: #7e004c; /* charter-exception: unique color */ stroke-width: 3; } &.referenced_by { - stroke: var(--c-alt-blue-2); + stroke: var(--cyan-300); stroke-dasharray: 0; &.chat-anchor { stroke: var(--color-chat-new); @@ -21,42 +28,42 @@ path.react-flow__edge-path { } &.next_in_sequence { - stroke: #00ff00; /* green */ + stroke: var(--green-300); stroke-width: 2; } &.owned_by { - stroke: #0000ff; /* blue */ + stroke: #0000ff; /* charter-exception: unique color (pure blue) */ stroke-width: 2; } &.composed_of { - stroke: #ffff00; /* yellow */ + stroke: var(--yellow-300); stroke-width: 2; } &.satisfied_by { - stroke: #ff00ff; /* magenta */ + stroke: #ff00ff; /* charter-exception: unique color (magenta) */ stroke-width: 2; } &.tested_by { - stroke: #00ffff; /* cyan */ + stroke: #00ffff; /* charter-exception: unique color (cyan) */ stroke-width: 2; } &.wired_to { - stroke: #ffa500; /* orange */ + stroke: var(--orange-400); stroke-width: 2; } &.depends_on { - stroke: #800080; /* purple */ + stroke: #800080; /* charter-exception: unique color (purple) */ stroke-width: 2; } &.easy-connect { - stroke: var(--c-alt-blue-2); + stroke: var(--cyan-300); stroke-width: 3; stroke-dasharray: 5 3; } @@ -64,13 +71,13 @@ path.react-flow__edge-path { &.edges-group { stroke-width: 4px; &.highlighted { - filter: drop-shadow(0 0 3px #fff); + filter: drop-shadow(0 0 3px var(--white)); } } &.highlighted { stroke-width: 4px; - filter: drop-shadow(0 0 3px #fff); + filter: drop-shadow(0 0 3px var(--white)); } } @@ -84,11 +91,11 @@ path.react-flow__edge-path { vertical-align: middle; padding: 0 10px; line-height: 18px; - font-size: 11px; - color: var(--c-white-1); + font-size: var(--font-size-xs); + color: var(--white); border-radius: 4px; - background-color: var(--c-black-5); - // border: solid 1px var(--c-blue-3); + background-color: var(--black-800); + // border: solid 1px var(--cyan-300); .edge-label-icon, .edge-label-icon.red { @@ -96,6 +103,6 @@ path.react-flow__edge-path { width: 22px; height: 22px; background: none; - color: var(--c-white-1); + color: var(--white); } } diff --git a/packages/modules/whiteboard/src/lib/components/css/nodes.scss b/packages/modules/whiteboard/src/lib/components/css/nodes.scss index 552bd671..12881ca3 100644 --- a/packages/modules/whiteboard/src/lib/components/css/nodes.scss +++ b/packages/modules/whiteboard/src/lib/components/css/nodes.scss @@ -5,13 +5,12 @@ border: solid 2px transparent; --handle-offset: 7px; border-radius: var(--node-border-radius); - font-size: 13px; + font-size: var(--font-size-sm); box-sizing: border-box; } .react-flow__node-wrapper > div { - box-shadow: 0 4px 6px -1px var(--ca-black-7), - 0 2px 4px -1px rvar(--ca-black-8); + box-shadow: var(--shadow-md); } /* @@ -36,7 +35,7 @@ position: relative; &.has-selection { - border-color: rgba(255, 255, 255, 0.2); + border-color: var(--alpha-white-15); } } @@ -67,23 +66,23 @@ display: flex; align-items: center; gap: 6px; - border: 1px solid var(--c-blue-gray-3); + border: 1px solid var(--surface-400); padding: 2px 5px; opacity: 0; transition: 0.2s; &:hover, &:focus { - color: var(--c-pink-41); - border: 1px solid var(--c-pink-41); + color: var(--primary-600); + border: 1px solid var(--primary-600); } &:hover, &:hover svg circle, &:hover svg path { - color: var(--c-pink-41); - stroke: var(--c-pink-41); - border: 1px solid var(--c-pink-41); + color: var(--primary-600); + stroke: var(--primary-600); + border: 1px solid var(--primary-600); } } @@ -107,7 +106,7 @@ display: flex; flex-direction: column; gap: 10px; - z-index: 2; + z-index: var(--z-base); } .node-right { @@ -157,7 +156,7 @@ top: -60%; left: 100%; width: 130px; - z-index: 2; + z-index: var(--z-base); transition: 0.2s; } @@ -199,12 +198,12 @@ .content { position: relative; - z-index: 2; + z-index: var(--z-base); } ul { position: absolute; - z-index: 3; + z-index: var(--z-base); left: 0; top: 0; height: 90px; @@ -298,7 +297,7 @@ .content { position: relative; - z-index: 2; + z-index: var(--z-base); } } @@ -374,7 +373,7 @@ background: var(--color-debug); padding: 6px; border-radius: 5px; - font-size: 10px; + font-size: var(--font-size-2xs); font-weight: 500; } diff --git a/packages/modules/whiteboard/src/lib/components/css/react-flow.scss b/packages/modules/whiteboard/src/lib/components/css/react-flow.scss index 48639595..af730e29 100644 --- a/packages/modules/whiteboard/src/lib/components/css/react-flow.scss +++ b/packages/modules/whiteboard/src/lib/components/css/react-flow.scss @@ -8,27 +8,27 @@ a, a:visited { - color: var(--c-white-1); + color: var(--white); } } .react-flow__controls button { - background-color: var(--color-background); - border-bottom: var(--ca-black-6); + background-color: var(--color-bg-app); + border-bottom: var(--alpha-black-25); path { - stroke: var(--c-white-1); - fill: var(--c-white-1); + stroke: var(--white); + fill: var(--white); } } .react-flow__minimap { - background-color: var(--color-background); + background-color: var(--color-bg-app); path.react-flow__minimap-mask { - fill: var(--ca-black-7); + fill: var(--alpha-black-17); } rect.react-flow__minimap-node { - fill: var(--color-node-background); + fill: var(--color-bg-node); } } diff --git a/packages/modules/whiteboard/src/lib/components/group/group.scss b/packages/modules/whiteboard/src/lib/components/group/group.scss index 93baa40a..4a5115bc 100644 --- a/packages/modules/whiteboard/src/lib/components/group/group.scss +++ b/packages/modules/whiteboard/src/lib/components/group/group.scss @@ -6,7 +6,7 @@ .group-border { flex: 1; - border: 1.5px solid var(--group-border-color, var(--c-pink-51)); + border: 1.5px solid var(--group-border-color, var(--primary-600)); width: 100%; border-radius: var(--node-border-radius); background-color: var(--group-fill-color, transparent); diff --git a/packages/modules/whiteboard/src/lib/components/htmlAvatarStore.tsx b/packages/modules/whiteboard/src/lib/components/htmlAvatarStore.tsx index 4af2a99c..2cd095d4 100644 --- a/packages/modules/whiteboard/src/lib/components/htmlAvatarStore.tsx +++ b/packages/modules/whiteboard/src/lib/components/htmlAvatarStore.tsx @@ -132,4 +132,4 @@ export class HtmlAvatarStore extends AvatarStore { // -const INACTIVE = 'var(--c-gray-9)'; +const INACTIVE = 'var(--neutral-9)'; diff --git a/packages/modules/whiteboard/src/lib/components/node-wrappers/node-wrapper.scss b/packages/modules/whiteboard/src/lib/components/node-wrappers/node-wrapper.scss index 0770e441..3d556b12 100644 --- a/packages/modules/whiteboard/src/lib/components/node-wrappers/node-wrapper.scss +++ b/packages/modules/whiteboard/src/lib/components/node-wrappers/node-wrapper.scss @@ -19,7 +19,7 @@ } .move-node-mode { - background-color: rgba(0, 0, 0, 0.1); + background-color: var(--alpha-black-10); backdrop-filter: blur(2px); pointer-events: auto; } @@ -28,7 +28,7 @@ display: flex; align-items: center; justify-content: center; - font-size: 14px; + font-size: var(--font-size-md); opacity: 0; transition: opacity 0.2s ease-in-out; pointer-events: none; @@ -44,7 +44,7 @@ &.easy-connect-active { opacity: 1; - background-color: rgba(103, 42, 164, 0.25); + background-color: rgba(103, 42, 164, 0.25); /* charter-exception: unique color (purple overlay) */ backdrop-filter: blur(4px); pointer-events: auto; } diff --git a/packages/modules/whiteboard/src/lib/components/node-wrappers/selection-awareness.scss b/packages/modules/whiteboard/src/lib/components/node-wrappers/selection-awareness.scss index 050963d5..8e660e01 100644 --- a/packages/modules/whiteboard/src/lib/components/node-wrappers/selection-awareness.scss +++ b/packages/modules/whiteboard/src/lib/components/node-wrappers/selection-awareness.scss @@ -7,7 +7,7 @@ .selections-awareness { position: absolute; left: calc(100% + 10px); - font-size: 10px; + font-size: var(--font-size-2xs); width: 150px; top: 0; } diff --git a/packages/modules/whiteboard/src/lib/components/right-panels.tsx b/packages/modules/whiteboard/src/lib/components/right-panels.tsx index 50ab76f4..548bc2f9 100644 --- a/packages/modules/whiteboard/src/lib/components/right-panels.tsx +++ b/packages/modules/whiteboard/src/lib/components/right-panels.tsx @@ -147,7 +147,7 @@ export const RightPanels: FC = ({
            = ({ right: '10px', width: '20px', height: '20px', - backgroundColor: 'var(--c-pink-4)', + backgroundColor: 'var(--primary-600)', cursor: 'pointer', borderRadius: '3px', display: 'flex', diff --git a/packages/modules/whiteboard/src/lib/components/whiteboard.tsx b/packages/modules/whiteboard/src/lib/components/whiteboard.tsx index 902a4aa8..724cd3fe 100644 --- a/packages/modules/whiteboard/src/lib/components/whiteboard.tsx +++ b/packages/modules/whiteboard/src/lib/components/whiteboard.tsx @@ -376,7 +376,7 @@ const WhiteboardWhiteboard = ({ style={{ flex: '0 0 ' + (showLayersPanel ? '240px' : '24px'), transition: 'flex 120ms ease', - background: 'var(--c-blue-61)', + background: 'var(--surface-900)', border: '1px solid var(--color-border, #e5e7eb)', borderRadius: 6, overflow: 'hidden', @@ -535,7 +535,9 @@ const ReactFlowBaseLayer = ({ useHotkeys( 'shift+z', () => { - active && setMode(mode === 'move-node' ? 'default' : 'move-node'); + if (active) { + setMode(mode === 'move-node' ? 'default' : 'move-node'); + } }, { preventDefault: true, diff --git a/packages/ui-base/.storybook/preview.ts b/packages/ui-base/.storybook/preview.ts index 301660a7..ef0cc83e 100644 --- a/packages/ui-base/.storybook/preview.ts +++ b/packages/ui-base/.storybook/preview.ts @@ -16,7 +16,7 @@ const preview: Preview = { values: [ { name: 'dark', - value: 'var(--color-background)', + value: 'var(--color-bg-app)', }, ], }, diff --git a/packages/ui-base/.storybook/test-runner.js b/packages/ui-base/.storybook/test-runner.js new file mode 100644 index 00000000..4b458ca7 --- /dev/null +++ b/packages/ui-base/.storybook/test-runner.js @@ -0,0 +1,108 @@ +const path = require('path'); +const { toMatchImageSnapshot } = require('jest-image-snapshot'); + +const PKG_ROOT = path.resolve(__dirname, '..'); + +const SNAPSHOT_OPTS = { + failureThreshold: 0.002, // 0.2% pixel difference allowed + failureThresholdType: 'percent', + customSnapshotsDir: + process.env['SNAPSHOT_DIR'] || path.join(PKG_ROOT, '__screenshots__'), + customDiffDir: + process.env['DIFF_DIR'] || path.join(PKG_ROOT, '__diff_output__'), +}; + +/** + * Wait for the page to be ready without relying on networkidle + * (which times out when stories load external resources like avatars). + */ +async function waitUntilReady(page) { + await page.waitForLoadState('domcontentloaded'); + await page.waitForLoadState('load'); + await page.evaluate(() => document.fonts.ready); + // Small settle time for React re-renders + await page.waitForTimeout(300); +} + +module.exports = { + setup() { + expect.extend({ toMatchImageSnapshot }); + }, + + async preVisit(page) { + // Block external image requests to prevent navigation context destruction. + // Stories load random avatars from pravatar.cc which cause flaky test failures. + await page.route('**/*pravatar*/**', (route) => route.abort()); + await page.route('**/*.pravatar.*/**', (route) => route.abort()); + }, + + async postVisit(page, context) { + // Disable CSS animations/transitions to avoid flaky diffs + await page.addStyleTag({ + content: ` + *, *::before, *::after { + animation-duration: 0s !important; + animation-delay: 0s !important; + transition-duration: 0s !important; + transition-delay: 0s !important; + } + `, + }); + + // Wait for page to be ready (DOM, resources, fonts — skip networkidle) + await waitUntilReady(page); + + const root = page.locator('#storybook-root'); + + try { + await root.waitFor({ state: 'visible', timeout: 5000 }); + } catch { + // Root not visible — fall back to full page screenshot + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + // Check if a Radix dialog portal is present (renders outside #storybook-root) + const dialogContent = page.locator('[data-radix-portal] [role="dialog"]'); + const hasDialog = await dialogContent.count().then((c) => c > 0); + + if (hasDialog) { + // Screenshot the dialog content instead of the empty root + try { + await dialogContent + .first() + .waitFor({ state: 'visible', timeout: 3000 }); + const screenshot = await dialogContent.first().screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } catch { + // Dialog not visible, fall through to page screenshot + } + } + + // Check if root content is too small (likely clipped by overflow) + const box = await root.boundingBox(); + if (box && (box.width < 10 || box.height < 10)) { + // Component is too small to be meaningful — screenshot full page + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + const screenshot = await root.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + }, +}; diff --git a/packages/ui-base/__screenshots__/basics-buttons--primary.png b/packages/ui-base/__screenshots__/basics-buttons--primary.png new file mode 100644 index 00000000..460b3c8b Binary files /dev/null and b/packages/ui-base/__screenshots__/basics-buttons--primary.png differ diff --git a/packages/ui-base/__screenshots__/basics-datetime--hover-only.png b/packages/ui-base/__screenshots__/basics-datetime--hover-only.png new file mode 100644 index 00000000..1b166328 Binary files /dev/null and b/packages/ui-base/__screenshots__/basics-datetime--hover-only.png differ diff --git a/packages/ui-base/__screenshots__/basics-datetime--multiple-formats.png b/packages/ui-base/__screenshots__/basics-datetime--multiple-formats.png new file mode 100644 index 00000000..10b92eb1 Binary files /dev/null and b/packages/ui-base/__screenshots__/basics-datetime--multiple-formats.png differ diff --git a/packages/ui-base/__screenshots__/basics-datetime--red.png b/packages/ui-base/__screenshots__/basics-datetime--red.png new file mode 100644 index 00000000..6e9a547d Binary files /dev/null and b/packages/ui-base/__screenshots__/basics-datetime--red.png differ diff --git a/packages/ui-base/__screenshots__/basics-datetime--short-hover-long.png b/packages/ui-base/__screenshots__/basics-datetime--short-hover-long.png new file mode 100644 index 00000000..f39770aa Binary files /dev/null and b/packages/ui-base/__screenshots__/basics-datetime--short-hover-long.png differ diff --git a/packages/ui-base/__screenshots__/basics-datetime--white.png b/packages/ui-base/__screenshots__/basics-datetime--white.png new file mode 100644 index 00000000..bfc246f4 Binary files /dev/null and b/packages/ui-base/__screenshots__/basics-datetime--white.png differ diff --git a/packages/ui-base/__screenshots__/basics-resourcebuttons--primary.png b/packages/ui-base/__screenshots__/basics-resourcebuttons--primary.png new file mode 100644 index 00000000..b06aec18 Binary files /dev/null and b/packages/ui-base/__screenshots__/basics-resourcebuttons--primary.png differ diff --git a/packages/ui-base/__screenshots__/forms-dialogmodal--primary.png b/packages/ui-base/__screenshots__/forms-dialogmodal--primary.png new file mode 100644 index 00000000..251cd1b4 Binary files /dev/null and b/packages/ui-base/__screenshots__/forms-dialogmodal--primary.png differ diff --git a/packages/ui-base/__screenshots__/forms-errors-formerror--primary.png b/packages/ui-base/__screenshots__/forms-errors-formerror--primary.png new file mode 100644 index 00000000..acfe734f Binary files /dev/null and b/packages/ui-base/__screenshots__/forms-errors-formerror--primary.png differ diff --git a/packages/ui-base/__screenshots__/forms-errors-formerrors--primary.png b/packages/ui-base/__screenshots__/forms-errors-formerrors--primary.png new file mode 100644 index 00000000..590f6765 Binary files /dev/null and b/packages/ui-base/__screenshots__/forms-errors-formerrors--primary.png differ diff --git a/packages/ui-base/__screenshots__/forms-fields-select--normal.png b/packages/ui-base/__screenshots__/forms-fields-select--normal.png new file mode 100644 index 00000000..84c7e03c Binary files /dev/null and b/packages/ui-base/__screenshots__/forms-fields-select--normal.png differ diff --git a/packages/ui-base/__screenshots__/forms-fields-switch--active.png b/packages/ui-base/__screenshots__/forms-fields-switch--active.png new file mode 100644 index 00000000..dad9a874 Binary files /dev/null and b/packages/ui-base/__screenshots__/forms-fields-switch--active.png differ diff --git a/packages/ui-base/__screenshots__/forms-fields-switch--inactive.png b/packages/ui-base/__screenshots__/forms-fields-switch--inactive.png new file mode 100644 index 00000000..80dad2eb Binary files /dev/null and b/packages/ui-base/__screenshots__/forms-fields-switch--inactive.png differ diff --git a/packages/ui-base/__screenshots__/forms-fields-text--email.png b/packages/ui-base/__screenshots__/forms-fields-text--email.png new file mode 100644 index 00000000..7014da39 Binary files /dev/null and b/packages/ui-base/__screenshots__/forms-fields-text--email.png differ diff --git a/packages/ui-base/__screenshots__/forms-fields-text--normal.png b/packages/ui-base/__screenshots__/forms-fields-text--normal.png new file mode 100644 index 00000000..6a6d5dad Binary files /dev/null and b/packages/ui-base/__screenshots__/forms-fields-text--normal.png differ diff --git a/packages/ui-base/__screenshots__/forms-fields-text--number.png b/packages/ui-base/__screenshots__/forms-fields-text--number.png new file mode 100644 index 00000000..4f6cd3e6 Binary files /dev/null and b/packages/ui-base/__screenshots__/forms-fields-text--number.png differ diff --git a/packages/ui-base/__screenshots__/forms-fields-text--password.png b/packages/ui-base/__screenshots__/forms-fields-text--password.png new file mode 100644 index 00000000..7e30f9b4 Binary files /dev/null and b/packages/ui-base/__screenshots__/forms-fields-text--password.png differ diff --git a/packages/ui-base/__screenshots__/forms-fields-totp--normal.png b/packages/ui-base/__screenshots__/forms-fields-totp--normal.png new file mode 100644 index 00000000..50d7dc4a Binary files /dev/null and b/packages/ui-base/__screenshots__/forms-fields-totp--normal.png differ diff --git a/packages/ui-base/__screenshots__/icons-all--mosaic.png b/packages/ui-base/__screenshots__/icons-all--mosaic.png new file mode 100644 index 00000000..cea2b900 Binary files /dev/null and b/packages/ui-base/__screenshots__/icons-all--mosaic.png differ diff --git a/packages/ui-base/__screenshots__/icons-background--mosaic.png b/packages/ui-base/__screenshots__/icons-background--mosaic.png new file mode 100644 index 00000000..8bbb059e Binary files /dev/null and b/packages/ui-base/__screenshots__/icons-background--mosaic.png differ diff --git a/packages/ui-base/__screenshots__/internals-accordion--closed.png b/packages/ui-base/__screenshots__/internals-accordion--closed.png new file mode 100644 index 00000000..87999054 Binary files /dev/null and b/packages/ui-base/__screenshots__/internals-accordion--closed.png differ diff --git a/packages/ui-base/__screenshots__/internals-accordion--opened.png b/packages/ui-base/__screenshots__/internals-accordion--opened.png new file mode 100644 index 00000000..ad7b9ba2 Binary files /dev/null and b/packages/ui-base/__screenshots__/internals-accordion--opened.png differ diff --git a/packages/ui-base/__screenshots__/internals-wrappercsscoordinates--primary.png b/packages/ui-base/__screenshots__/internals-wrappercsscoordinates--primary.png new file mode 100644 index 00000000..9d5a978c Binary files /dev/null and b/packages/ui-base/__screenshots__/internals-wrappercsscoordinates--primary.png differ diff --git a/packages/ui-base/__screenshots__/mvp-assets-user-bubble--horizontal-large-live.png b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--horizontal-large-live.png new file mode 100644 index 00000000..b0cffcb4 Binary files /dev/null and b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--horizontal-large-live.png differ diff --git a/packages/ui-base/__screenshots__/mvp-assets-user-bubble--horizontal-large.png b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--horizontal-large.png new file mode 100644 index 00000000..85d502ea Binary files /dev/null and b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--horizontal-large.png differ diff --git a/packages/ui-base/__screenshots__/mvp-assets-user-bubble--horizontal-small-live.png b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--horizontal-small-live.png new file mode 100644 index 00000000..e580282f Binary files /dev/null and b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--horizontal-small-live.png differ diff --git a/packages/ui-base/__screenshots__/mvp-assets-user-bubble--horizontal-small.png b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--horizontal-small.png new file mode 100644 index 00000000..14b41b2b Binary files /dev/null and b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--horizontal-small.png differ diff --git a/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-horizontal-large-live.png b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-horizontal-large-live.png new file mode 100644 index 00000000..627e519c Binary files /dev/null and b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-horizontal-large-live.png differ diff --git a/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-horizontal-large.png b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-horizontal-large.png new file mode 100644 index 00000000..6b5ed387 Binary files /dev/null and b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-horizontal-large.png differ diff --git a/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-horizontal-small-live.png b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-horizontal-small-live.png new file mode 100644 index 00000000..77e70685 Binary files /dev/null and b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-horizontal-small-live.png differ diff --git a/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-horizontal-small.png b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-horizontal-small.png new file mode 100644 index 00000000..289de321 Binary files /dev/null and b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-horizontal-small.png differ diff --git a/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-vertical-large-live.png b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-vertical-large-live.png new file mode 100644 index 00000000..d5be1c50 Binary files /dev/null and b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-vertical-large-live.png differ diff --git a/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-vertical-large.png b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-vertical-large.png new file mode 100644 index 00000000..1c9e6cb4 Binary files /dev/null and b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-vertical-large.png differ diff --git a/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-vertical-small-live.png b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-vertical-small-live.png new file mode 100644 index 00000000..2de1a8b0 Binary files /dev/null and b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-vertical-small-live.png differ diff --git a/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-vertical-small.png b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-vertical-small.png new file mode 100644 index 00000000..479076a1 Binary files /dev/null and b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--many-vertical-small.png differ diff --git a/packages/ui-base/__screenshots__/mvp-assets-user-bubble--vertical-large-live.png b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--vertical-large-live.png new file mode 100644 index 00000000..66141c88 Binary files /dev/null and b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--vertical-large-live.png differ diff --git a/packages/ui-base/__screenshots__/mvp-assets-user-bubble--vertical-large.png b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--vertical-large.png new file mode 100644 index 00000000..4fe7cd2f Binary files /dev/null and b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--vertical-large.png differ diff --git a/packages/ui-base/__screenshots__/mvp-assets-user-bubble--vertical-small-live.png b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--vertical-small-live.png new file mode 100644 index 00000000..4e7c5620 Binary files /dev/null and b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--vertical-small-live.png differ diff --git a/packages/ui-base/__screenshots__/mvp-assets-user-bubble--vertical-small.png b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--vertical-small.png new file mode 100644 index 00000000..ac111d62 Binary files /dev/null and b/packages/ui-base/__screenshots__/mvp-assets-user-bubble--vertical-small.png differ diff --git a/packages/ui-base/__screenshots__/palette-default--default.png b/packages/ui-base/__screenshots__/palette-default--default.png new file mode 100644 index 00000000..f6b895b4 Binary files /dev/null and b/packages/ui-base/__screenshots__/palette-default--default.png differ diff --git a/packages/ui-base/__screenshots__/ui-livespace--normal.png b/packages/ui-base/__screenshots__/ui-livespace--normal.png new file mode 100644 index 00000000..b88568f7 Binary files /dev/null and b/packages/ui-base/__screenshots__/ui-livespace--normal.png differ diff --git a/packages/ui-base/__screenshots__/ui-preview--normal.png b/packages/ui-base/__screenshots__/ui-preview--normal.png new file mode 100644 index 00000000..5422b519 Binary files /dev/null and b/packages/ui-base/__screenshots__/ui-preview--normal.png differ diff --git a/packages/ui-base/__screenshots__/ui-sidebar--normal.png b/packages/ui-base/__screenshots__/ui-sidebar--normal.png new file mode 100644 index 00000000..d9080395 Binary files /dev/null and b/packages/ui-base/__screenshots__/ui-sidebar--normal.png differ diff --git a/packages/ui-base/__screenshots__/users-roleeditor--primary.png b/packages/ui-base/__screenshots__/users-roleeditor--primary.png new file mode 100644 index 00000000..257f5546 Binary files /dev/null and b/packages/ui-base/__screenshots__/users-roleeditor--primary.png differ diff --git a/packages/ui-base/__screenshots__/users-roleeditor--read-only.png b/packages/ui-base/__screenshots__/users-roleeditor--read-only.png new file mode 100644 index 00000000..2bd1a84a Binary files /dev/null and b/packages/ui-base/__screenshots__/users-roleeditor--read-only.png differ diff --git a/packages/ui-base/__screenshots__/users-useravatar--default.png b/packages/ui-base/__screenshots__/users-useravatar--default.png new file mode 100644 index 00000000..29da13d5 Binary files /dev/null and b/packages/ui-base/__screenshots__/users-useravatar--default.png differ diff --git a/packages/ui-base/__screenshots__/users-useravatar--host.png b/packages/ui-base/__screenshots__/users-useravatar--host.png new file mode 100644 index 00000000..9dbf3b48 Binary files /dev/null and b/packages/ui-base/__screenshots__/users-useravatar--host.png differ diff --git a/packages/ui-base/__screenshots__/users-useravatar--live.png b/packages/ui-base/__screenshots__/users-useravatar--live.png new file mode 100644 index 00000000..eb93a39e Binary files /dev/null and b/packages/ui-base/__screenshots__/users-useravatar--live.png differ diff --git a/packages/ui-base/__screenshots__/users-useravatar--small.png b/packages/ui-base/__screenshots__/users-useravatar--small.png new file mode 100644 index 00000000..7b7e2276 Binary files /dev/null and b/packages/ui-base/__screenshots__/users-useravatar--small.png differ diff --git a/packages/ui-base/__screenshots__/users-userusername--discord.png b/packages/ui-base/__screenshots__/users-userusername--discord.png new file mode 100644 index 00000000..48cbc89a Binary files /dev/null and b/packages/ui-base/__screenshots__/users-userusername--discord.png differ diff --git a/packages/ui-base/__screenshots__/users-userusername--github.png b/packages/ui-base/__screenshots__/users-userusername--github.png new file mode 100644 index 00000000..7e5db6d1 Binary files /dev/null and b/packages/ui-base/__screenshots__/users-userusername--github.png differ diff --git a/packages/ui-base/__screenshots__/users-userusername--gitlab.png b/packages/ui-base/__screenshots__/users-userusername--gitlab.png new file mode 100644 index 00000000..6eeae2b8 Binary files /dev/null and b/packages/ui-base/__screenshots__/users-userusername--gitlab.png differ diff --git a/packages/ui-base/__screenshots__/users-userusername--linked-in.png b/packages/ui-base/__screenshots__/users-userusername--linked-in.png new file mode 100644 index 00000000..c3870f07 Binary files /dev/null and b/packages/ui-base/__screenshots__/users-userusername--linked-in.png differ diff --git a/packages/ui-base/__screenshots__/users-userusername--local.png b/packages/ui-base/__screenshots__/users-userusername--local.png new file mode 100644 index 00000000..f66a3eab Binary files /dev/null and b/packages/ui-base/__screenshots__/users-userusername--local.png differ diff --git a/packages/ui-base/src/lib/accordion/accordion.css b/packages/ui-base/src/lib/accordion/accordion.css index e94384f6..d62003fd 100644 --- a/packages/ui-base/src/lib/accordion/accordion.css +++ b/packages/ui-base/src/lib/accordion/accordion.css @@ -9,26 +9,26 @@ align-items: center; gap: 4px; padding: 4px 6px; - background: var(--c-blue-gray-1); + background: var(--surface-700); } .accordion-item .accordion-title p { padding: 0; margin: 0; - color: var(--c-pink-41); - font-size: 6px; + color: var(--primary-600); + font-size: var(--font-size-2xs); } .accordion-item .accordion-title span { padding: 0; margin: 0; - font-size: 6px; - color: var(--c-white-1); + font-size: var(--font-size-2xs); + color: var(--white); } .accordion-content { - color: var(--c-white-1); - font-size: 7px; + color: var(--white); + font-size: var(--font-size-2xs); padding: 5px 10px; - background: var(--c-blue-gray-2); + background: var(--surface-800); } diff --git a/packages/ui-base/src/lib/assets/background.stories.tsx b/packages/ui-base/src/lib/assets/background.stories.tsx index d50320d1..f1fe8dfc 100644 --- a/packages/ui-base/src/lib/assets/background.stories.tsx +++ b/packages/ui-base/src/lib/assets/background.stories.tsx @@ -1,14 +1,21 @@ import type { Meta, StoryObj } from '@storybook/react'; import { CSSProperties } from 'react'; +import nodePythonBg from './node-python-bg.svg'; +import nodeVaultBg from './node-vault-bg.svg'; +import nodeDatasetBg from './node-dataset-bg.svg'; +import bgToolbar from './bg-toolbar.svg'; +import bgToolbarSecondary from './bg-toolbar-secondary.svg'; +import bgToolbarTertiary from './bg-toolbar-tertiary.svg'; + const s1 = { width: '120px', height: '120px', - border: 'solid 1px var(--c-gray-9)', + border: 'solid 1px var(--neutral-9)', position: 'relative', backgroundRepeat: 'no-repeat', backgroundPosition: '50% 50%', - backgroundColor: 'var(--ca-white-1)', + backgroundColor: 'var(--alpha-white-10)', } as CSSProperties; const s3 = { @@ -16,16 +23,16 @@ const s3 = { bottom: '-9px', left: 0, fontSize: '9px', - color: 'var(--c-white-1)', + color: 'var(--white)', } as CSSProperties; -const bgs = [ - 'node-python-bg.svg', - 'node-vault-bg.svg', - 'node-dataset-bg.svg', - 'bg-toolbar.svg', - 'bg-toolbar-secondary.svg', - 'bg-toolbar-tertiary.svg', +const bgs: { name: string; url: string }[] = [ + { name: 'node-python-bg.svg', url: nodePythonBg }, + { name: 'node-vault-bg.svg', url: nodeVaultBg }, + { name: 'node-dataset-bg.svg', url: nodeDatasetBg }, + { name: 'bg-toolbar.svg', url: bgToolbar }, + { name: 'bg-toolbar-secondary.svg', url: bgToolbarSecondary }, + { name: 'bg-toolbar-tertiary.svg', url: bgToolbarTertiary }, ]; const AllIcon = () => { @@ -38,16 +45,16 @@ const AllIcon = () => { gap: '15px', }} > - {bgs.map((k) => { + {bgs.map((bg) => { return (
            - {k} + {bg.name}
            ); })} diff --git a/packages/ui-base/src/lib/assets/css/button.scss b/packages/ui-base/src/lib/assets/css/button.scss index a63ecf89..2e7f17e9 100644 --- a/packages/ui-base/src/lib/assets/css/button.scss +++ b/packages/ui-base/src/lib/assets/css/button.scss @@ -9,20 +9,20 @@ span.button-root { height: var(--form-input-height); padding: 0 15px; // - font-size: 15px; + font-size: var(--font-size-lg); line-height: 1; font-weight: 500; // cursor: pointer; // - color: var(--c-white-1); - border: solid 1px var(--c-gray-9); + color: var(--white); + border: solid 1px var(--neutral-9); &:hover, &.testhover { // border: solid 1.5px var(--color-button-blue); color: var(--form-input-color); - background-color: var(--ca-white-1); + background-color: var(--alpha-white-10); } &.icon { @@ -36,40 +36,40 @@ span.button-root { &.small { padding: 0 8px; - font-size: 11px; + font-size: var(--font-size-xs); height: 28px; } &.submit { - background-color: var(--c-pink-2); - border-color: var(--c-pink-2); - color: var(--c-white-1); - box-shadow: 0 2px 10px var(--ca-black-7); + background-color: var(--primary-200); + border-color: var(--primary-200); + color: var(--white); + box-shadow: var(--shadow-md); &:hover, &.testhover { - background-color: var(--c-pink-1); + background-color: var(--primary-100); } } &.red { - color: var(--c-red-1); - border-color: var(--c-red-1); - //background-color: var(--c-white-1); + color: var(--red-300); + border-color: var(--red-300); + //background-color: var(--white); &:hover, &.testhover { - color: var(--c-white-1); - background-color: var(--c-red-1); + color: var(--white); + background-color: var(--red-300); } } &.blue { color: var(--color-button-blue); border-color: var(--color-button-blue); - //background-color: var(--c-white-1); + //background-color: var(--white); &:hover, &.testhover { - color: var(--c-white-1); + color: var(--white); background-color: var(--color-button-blue); } } @@ -86,7 +86,7 @@ span.button-root { height: 46px; min-height: 46px; border-radius: 8px; - border: solid 1px #9542c6; + border: solid 1px #9542c6; /* charter-exception: unique color */ --icon-width: 22px; --icon-height: 22px; @@ -131,9 +131,9 @@ span.button-root { &.cloud, &.share { width: 49px; - font-size: 12px; - color: white; - fill: white; + font-size: var(--font-size-sm); + color: var(--white); + fill: var(--white); } &.docker { @@ -191,7 +191,7 @@ span.button-root { &:hover, &.testhover { - box-shadow: 0px 0px 3px #987bbd inset; + box-shadow: var(--shadow-btn-purple-inset); filter: drop-shadow(0px 0px 3px #987bbd); --tw-text-opacity: 1; color: rgb(152 123 189 / var(--tw-text-opacity)); @@ -215,7 +215,7 @@ span.button-root { &:hover, &.testhover { - box-shadow: 0px 0px 3px #0550b3 inset; + box-shadow: var(--shadow-btn-blue-inset); filter: drop-shadow(0px 0px 3px #0550b3); --tw-text-opacity: 1; color: rgb(90 163 248 / var(--tw-text-opacity)); @@ -223,12 +223,12 @@ span.button-root { } &.enter { - border: solid 1px #a998d6; + border: solid 1px var(--surface-300); } - background-color: var(--c-blue-6); + background-color: var(--surface-900); &.stop { - background-color: #29293e; + background-color: var(--surface-700); } } @@ -257,25 +257,23 @@ span.button-root { .TooltipContent { border-radius: 4px; padding: 10px 15px; - font-size: 13px; + font-size: var(--font-size-md); line-height: 1; - box-shadow: - hsl(206 22% 7% / 35%) 0px 10px 38px -10px, - hsl(206 22% 7% / 20%) 0px 10px 20px -15px; + box-shadow: var(--shadow-xl); user-select: none; animation-duration: 400ms; animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); will-change: transform, opacity; &.errors { - background-color: var(--c-red-1); + background-color: var(--red-300); max-width: 200px; } &.success { - background-color: var(--c-green-3); + background-color: var(--green-300); } &.tooltip { - background-color: var(--c-alt-blue-2); + background-color: var(--cyan-300); } svg { @@ -297,13 +295,13 @@ span.button-root { .TooltipArrow { &.errors { - fill: var(--c-red-1); + fill: var(--red-300); } &.success { - fill: var(--c-green-3); + fill: var(--green-300); } &.tooltip { - fill: var(--c-alt-blue-2); + fill: var(--cyan-300); } } diff --git a/packages/ui-base/src/lib/assets/css/global.scss b/packages/ui-base/src/lib/assets/css/global.scss index b6aafe20..47f95af7 100644 --- a/packages/ui-base/src/lib/assets/css/global.scss +++ b/packages/ui-base/src/lib/assets/css/global.scss @@ -36,8 +36,8 @@ width: 18px; height: 18px; border-radius: 50%; - border: 1px solid var(--c-white-1); - background: var(--c-blue-62); + border: 1px solid var(--white); + background: var(--surface-800); display: flex; align-items: center; justify-content: center; @@ -46,8 +46,8 @@ .number-field-input .number p, .number-field-output .number p { margin: 0; - font-size: 10px; - color: var(--c-white-1); + font-size: var(--font-size-2xs); + color: var(--white); } .node-io-grid-output { @@ -80,7 +80,7 @@ ); height: 9px; width: 9px; - background: var(--c-alt-blue-1); + background: var(--cyan-100); } } } @@ -115,7 +115,7 @@ ); height: 9px; width: 9px; - background: var(--c-alt-blue-4); + background: var(--cyan-400); } } } @@ -123,7 +123,7 @@ /* Mains infos text */ .main-infos { - color: var(--c-white-1); + color: var(--white); } .main-infos p { @@ -142,8 +142,8 @@ .main-infos ul li { white-space: nowrap; - font-size: 10px; - color: var(--c-white-1); + font-size: var(--font-size-2xs); + color: var(--white); font-weight: 300; } @@ -164,7 +164,7 @@ opacity: 1; } .node-menu { - z-index: 5; + z-index: var(--z-base); } } @@ -178,7 +178,7 @@ .expanded-menu { opacity: 1 !important; - z-index: 3; + z-index: var(--z-base); } .gradient-conic-pink { @@ -225,38 +225,38 @@ &:hover { --gradient-opacity: 0.8; background-color: rgba(20, 20, 50, 0.2); - border: 2px solid #9744a3; - box-shadow: 0px 0px 90px -10px #92329f; + border: 2px solid #9744a3; /* charter-exception: unique color */ + box-shadow: var(--shadow-glow-pink-hover); } &:active:not(:has(button:active)):not(:has(.tags-container:active)) { --gradient-opacity: 0.2; - border: 2px solid #8e60f4; - box-shadow: 0px 0px 90px -10px #b562c0; + border: 2px solid #8e60f4; /* charter-exception: unique color */ + box-shadow: var(--shadow-glow-pink-active); } } .node-background { - background: var(--color-node-background); - box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); + background: var(--color-bg-node); + box-shadow: var(--shadow-lg); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.1); + border: 1px solid var(--alpha-white-10); &:hover { - background: rgba(255, 255, 255, 0.15); + background: var(--alpha-white-15); } } .node-header-background { - background: var(--c-pink-6); - box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); + background: var(--primary-900); + box-shadow: var(--shadow-lg); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); - border: 1px solid var(--c-pink-4); + border: 1px solid var(--primary-600); &:hover { - background: var(--c-pink-5); + background: var(--primary-700); } } @@ -303,7 +303,7 @@ .conic-gradient-live { background: radial-gradient(circle, #f7c8de 0%, #f72686 50%); - box-shadow: 0px 0px 10px 1px #f72686; + box-shadow: var(--shadow-glow-live); } .text-shadow-pink { @@ -318,7 +318,7 @@ * { scrollbar-width: none; /* Options: auto, thin, none */ - scrollbar-color: green orange; /* Thumb and track colors */ + scrollbar-color: var(--green-600) var(--orange-300); /* Thumb and track colors */ } .opacity-on-hover { diff --git a/packages/ui-base/src/lib/assets/css/index.scss b/packages/ui-base/src/lib/assets/css/index.scss index 72255653..83f14473 100644 --- a/packages/ui-base/src/lib/assets/css/index.scss +++ b/packages/ui-base/src/lib/assets/css/index.scss @@ -1,5 +1,5 @@ -@use './tailwind.scss'; @use './variables'; +@use './utilities'; @use './reset'; @use './template'; @use './global'; diff --git a/packages/ui-base/src/lib/assets/css/radix.scss b/packages/ui-base/src/lib/assets/css/radix.scss index bd6b3b0a..cad355df 100644 --- a/packages/ui-base/src/lib/assets/css/radix.scss +++ b/packages/ui-base/src/lib/assets/css/radix.scss @@ -5,21 +5,20 @@ .ContextMenuContent, .ContextMenuSubContent { min-width: 170px; - background-color: var(--c-white-1); + background-color: var(--white); border-radius: var(--border-radius-box); overflow: hidden; padding: 5px; - box-shadow: 0px 10px 38px -10px var(--ca-black-6), - 0px 10px 20px -15px var(--ca-black-8); + box-shadow: var(--shadow-lg); } .ContextMenuItem, .ContextMenuCheckboxItem, .ContextMenuRadioItem, .ContextMenuSubTrigger { - font-size: 13px; + font-size: var(--font-size-md); line-height: 1; - color: var(--c-gray-12); + color: var(--neutral-12); border-radius: var(--form-input-border-radius); display: flex; align-items: center; @@ -31,34 +30,34 @@ outline: none; } .ContextMenuSubTrigger[data-state='open'] { - background-color: var(--c-pink-2); - color: var(--c-white-1); + background-color: var(--primary-200); + color: var(--white); } .ContextMenuItem[data-disabled], .ContextMenuCheckboxItem[data-disabled], .ContextMenuRadioItem[data-disabled], .ContextMenuSubTrigger[data-disabled] { - color: var(--c-gray-6); + color: var(--neutral-6); pointer-events: 'none'; } .ContextMenuItem[data-highlighted], .ContextMenuCheckboxItem[data-highlighted], .ContextMenuRadioItem[data-highlighted], .ContextMenuSubTrigger[data-highlighted] { - background-color: var(--c-pink-1); - color: var(--c-white-1); + background-color: var(--primary-100); + color: var(--white); } .ContextMenuLabel { padding-left: 25px; - font-size: 12px; + font-size: var(--font-size-sm); line-height: 25px; - color: var(--c-gray-11); + color: var(--neutral-11); } .ContextMenuSeparator { height: 1px; - background-color: var(--c-pink-2); + background-color: var(--primary-200); margin: 5px; } @@ -74,13 +73,13 @@ .RightSlot { margin-left: auto; padding-left: 20px; - color: var(--c-gray-11); + color: var(--neutral-11); } [data-highlighted] > .RightSlot { - color: var(--c-white-1); + color: var(--white); } [data-disabled] .RightSlot { - color: var(--c-gray-8); + color: var(--neutral-8); } /** @@ -88,18 +87,17 @@ */ .DialogOverlay { - background-color: var(--ca-black-9); + background-color: var(--alpha-black-5); position: fixed; inset: 0; animation: overlayShow 150ms cubic-bezier(0.16, 1, 0.3, 1); } .DialogContent { - background-color: rgba(43, 43, 68, 0.92); + background-color: rgba(43, 43, 68, 0.92); /* charter-exception: unique color */ backdrop-filter: blur(2px); border-radius: var(--border-radius-box); - box-shadow: hsl(206 22% 7% / 35%) 0px 10px 38px -10px, - hsl(206 22% 7% / 20%) 0px 10px 20px -15px; + box-shadow: var(--shadow-xl); position: fixed; top: 50%; left: 50%; @@ -117,14 +115,14 @@ .DialogTitle { margin: 0; font-weight: 500; - color: var(--ca-white-9); - font-size: 17px; + color: var(--alpha-white-90); + font-size: var(--font-size-xl); } .DialogDescription { margin: 10px 0 20px; - color: var(--c-gray-11); - font-size: 15px; + color: var(--neutral-11); + font-size: var(--font-size-lg); line-height: 1.5; } @@ -161,16 +159,16 @@ display: inline-flex; align-items: center; justify-content: center; - color: var(--c-gray-11); + color: var(--neutral-11); position: absolute; top: 10px; right: 10px; } .IconButton:hover { - background-color: var(--c-gray-4); + background-color: var(--neutral-4); } .IconButton:focus { - box-shadow: 0 0 0 2px var(--c-gray-7); + box-shadow: var(--shadow-focus-ring-gray); } .Fieldset { @@ -186,7 +184,7 @@ } .Label { - font-size: 15px; + font-size: var(--font-size-lg); color: var(--form-label-color); width: 150px; min-width: 150px; @@ -214,20 +212,13 @@ padding: 0 var(--form-input-padding-x); font-size: var(--form-input-font-size); color: var(--form-input-color); - box-shadow: var(--form-input-bs-x, 0) var(--form-input-bs-y, 0) - var(--form-input-bs-blur) var(--form-input-bs-scale, 0) - var(--form-input-bs-color, transparent); + box-shadow: var(--shadow-form-input); } .Input:focus, .SelectTrigger:focus, .Button.submit:focus { outline: none; - box-shadow: var(--form-input-bs-x, 0) var(--form-input-bs-y, 0) - var(--form-input-bs-blur) var(--form-input-bs-scale, 0) - var(--form-input-bs-color, transparent), - var(--form-input-focus-bs-x, 0) var(--form-input-focus-bs-y, 0) - var(--form-input-focus-bs-blur) var(--form-input-focus-bs-scale, 0) - var(--form-input-focus-bs-color, transparent); + box-shadow: var(--shadow-form-input-focus); } .Fieldset.text.with-copy-button { @@ -272,7 +263,7 @@ } &.integrated { - background: red; + background: var(--red-500); border: none; cursor: pointer; outline: none; @@ -283,12 +274,12 @@ } &.small { - font-size: 9px; - color: var(--c-white-1); + font-size: var(--font-size-2xs); + color: var(--white); background: none; .SelectIcon { - color: var(--c-white-1); + color: var(--white); svg { width: 11px; height: 11px; @@ -302,14 +293,14 @@ } .SelectIcon { - color: Var(--c-gray-11); + color: var(--neutral-11); } .SelectContent { overflow: hidden; - background-color: var(--c-blue-gray-2); + background-color: var(--surface-800); border-radius: var(--border-radius-box); - box-shadow: 0px 0px 5px 0px var(--form-input-bs-color); + box-shadow: var(--shadow-select-glow); } .SelectViewport { @@ -329,25 +320,25 @@ user-select: none; } .SelectItem[data-disabled] { - color: var(--c-gray-8); + color: var(--neutral-8); pointer-events: none; } .SelectItem[data-highlighted] { outline: none; - background-color: var(--c-pink-1); - color: var(--c-white-1); + background-color: var(--primary-100); + color: var(--white); } .SelectLabel { padding: 0 25px; - font-size: 12px; + font-size: var(--font-size-sm); line-height: 25px; color: var(--form-label-color); } .SelectSeparator { height: 1px; - background-color: var(--c-pink-2); + background-color: var(--primary-200); margin: 5px; } @@ -365,13 +356,13 @@ align-items: center; justify-content: center; height: 25px; - background-color: var(--c-blue-gray-2); - color: var(--c-pink-6); + background-color: var(--surface-800); + color: var(--primary-900); cursor: default; svg { - fill: white; - stroke: white; + fill: var(--white); + stroke: var(--white); } } @@ -388,14 +379,14 @@ .SliderTrack { position: relative; flex-grow: 1; - background-color: var(--c-gray-7); + background-color: var(--neutral-7); height: 4px; border-radius: 9999px; } .SliderRange { position: absolute; - background-color: var(--c-pink-3); + background-color: var(--primary-500); border-radius: 9999px; height: 100%; } @@ -404,9 +395,9 @@ display: block; width: 17px; height: 17px; - background-color: var(--c-pink-2); + background-color: var(--primary-200); border-radius: 50%; - box-shadow: 0 2px 10px var(--c-pink-2); + box-shadow: var(--shadow-slider-pink); cursor: pointer; } @@ -424,17 +415,15 @@ border-radius: 4px; padding: 20px; width: fit-content; - background-color: var(--c-white-1); - box-shadow: hsl(206 22% 7% / 35%) 0px 10px 38px -10px, - hsl(206 22% 7% / 20%) 0px 10px 20px -15px; + background-color: var(--white); + box-shadow: var(--shadow-xl); animation-duration: 400ms; animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); will-change: transform, opacity; outline: none; } .PopoverContent:focus { - box-shadow: hsl(206 22% 7% / 35%) 0px 10px 38px -10px, - hsl(206 22% 7% / 20%) 0px 10px 20px -15px, 0 0 0 2px var(--c-pink-2); + box-shadow: var(--shadow-popover-focus); } .PopoverContent[data-state='open'][data-side='top'] { animation-name: slideDownAndFade; @@ -450,7 +439,7 @@ } .PopoverArrow { - fill: var(--c-white-1); + fill: var(--white); } .PopoverClose { @@ -461,7 +450,7 @@ display: inline-flex; align-items: center; justify-content: center; - color: var(--c-pink-6); + color: var(--primary-900); position: absolute; top: 5px; right: 5px; @@ -517,25 +506,25 @@ button.CheckboxRoot { all: unset; - background-color: var(--c-white-1); + background-color: var(--white); width: 20px; height: 20px; border-radius: 4px; display: flex; align-items: center; justify-content: center; - box-shadow: 0 2px 10px var(--ca-black-7); + box-shadow: var(--shadow-md); cursor: pointer; } .CheckboxRoot:hover { - background-color: var(--c-pink-1); + background-color: var(--primary-100); } .CheckboxRoot:focus { - box-shadow: 0 0 0 1.5px var(--c-pink-1); + box-shadow: var(--shadow-focus-ring-pink); } .CheckboxIndicator { - color: var(--c-pink-6); + color: var(--primary-900); } /** @@ -544,10 +533,10 @@ button.CheckboxRoot { .MenubarRoot { display: flex; - background-color: white; + background-color: var(--white); padding: 3px; border-radius: 6px; - box-shadow: 0 2px 10px var(--black-a7); + box-shadow: var(--shadow-md); &.integrated { padding: 0; @@ -563,7 +552,7 @@ button.CheckboxRoot { font-weight: 500; line-height: 1; border-radius: 4px; - font-size: 13px; + font-size: var(--font-size-md); display: flex; align-items: center; justify-content: space-between; @@ -572,20 +561,19 @@ button.CheckboxRoot { .MenubarTrigger[data-highlighted], .MenubarTrigger[data-state='open'] { - background-color: var(--c-pink-1); + background-color: var(--primary-100); } .MenubarContent, .MenubarSubContent { min-width: 160px; - background-color: var(--c-blue-6); + background-color: var(--surface-900); border-radius: 6px; padding: 5px; animation-duration: 400ms; animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); will-change: transform, opacity; - box-shadow: hsl(206 22% 7% / 35%) 0px 10px 38px -10px, - hsl(206 22% 7% / 20%) 0px 10px 20px -15px; + box-shadow: var(--shadow-xl); } .MenubarItem, @@ -593,9 +581,9 @@ button.CheckboxRoot { .MenubarCheckboxItem, .MenubarRadioItem { all: unset; - font-size: 13px; + font-size: var(--font-size-md); line-height: 1; - color: var(--mauve-9); + color: var(--neutral-9); border-radius: 4px; display: flex; align-items: center; @@ -607,9 +595,9 @@ button.CheckboxRoot { cursor: pointer; &.red { - color: var(--c-red-1); - fill: var(--c-red-1); - stroke: var(--c-red-1); + color: var(--red-300); + fill: var(--red-300); + stroke: var(--red-300); } } @@ -622,8 +610,8 @@ button.CheckboxRoot { .MenubarItem[data-state='open'], .MenubarSubTrigger[data-state='open'] { - background-color: var(--c-pink-3); - color: white; + background-color: var(--primary-500); + color: var(--white); } .MenubarItem[data-highlighted], @@ -632,18 +620,18 @@ button.CheckboxRoot { .MenubarRadioItem[data-highlighted] { background-image: linear-gradient( 135deg, - var(--c-pink-3) 0%, - var(--c-pink-51) 100% + var(--primary-500) 0%, + var(--primary-600) 100% ); - color: white; - fill: white; - stroke: white; + color: var(--white); + fill: var(--white); + stroke: var(--white); &.red { background-image: linear-gradient( 135deg, - var(--c-red-1) 0%, - var(--c-red-3) 100% + var(--red-300) 0%, + var(--red-400) 100% ); } } @@ -652,7 +640,7 @@ button.CheckboxRoot { .MenubarSubTrigger[data-disabled], .MenubarCheckboxItem[data-disabled], .MenubarRadioItem[data-disabled] { - color: var(--mauve-8); + color: var(--neutral-8); pointer-events: none; } @@ -667,20 +655,20 @@ button.CheckboxRoot { .MenubarSeparator { height: 1px; - background-color: var(--c-pink-1); + background-color: var(--primary-100); margin: 5px; } .RightSlot { margin-left: auto; padding-left: 20px; - color: var(--mauve-9); + color: var(--neutral-9); } [data-highlighted] > .RightSlot { - color: white; + color: var(--white); } [data-disabled] > .RightSlot { - color: var(--mauve-8); + color: var(--neutral-8); } diff --git a/packages/ui-base/src/lib/assets/css/reset.scss b/packages/ui-base/src/lib/assets/css/reset.scss index 208d903b..0aaa4824 100644 --- a/packages/ui-base/src/lib/assets/css/reset.scss +++ b/packages/ui-base/src/lib/assets/css/reset.scss @@ -10,8 +10,8 @@ html body, .root { - color: var(--c-white-1); - background-color: var(--color-background); + color: var(--white); + background-color: var(--color-bg-app); margin: 0; padding: 0; @@ -26,21 +26,21 @@ html body, } a { - color: var(--color-a-not-visited); - text-decoration-color: var(--color-a-not-visited); + color: var(--color-link); + text-decoration-color: var(--color-link); text-decoration: none; } a:visited { - color: var(--color-a-visited); - text-decoration-color: var(--color-a-visited); + color: var(--color-link); + text-decoration-color: var(--color-link); } a:hover { - color: var(--color-a-hover); - text-decoration-color: var(--color-a-hover); + color: var(--color-link); + text-decoration-color: var(--color-link); } h1 { - font-size: 30px; + font-size: var(--font-size-4xl); margin-bottom: 20px; } } diff --git a/packages/ui-base/src/lib/assets/css/tailwind.scss b/packages/ui-base/src/lib/assets/css/tailwind.scss deleted file mode 100644 index b5c61c95..00000000 --- a/packages/ui-base/src/lib/assets/css/tailwind.scss +++ /dev/null @@ -1,3 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; diff --git a/packages/ui-base/src/lib/assets/css/utilities.scss b/packages/ui-base/src/lib/assets/css/utilities.scss new file mode 100644 index 00000000..48f3f84c --- /dev/null +++ b/packages/ui-base/src/lib/assets/css/utilities.scss @@ -0,0 +1,246 @@ +// ============================================================================ +// UTILITY CLASSES +// Minimal reusable layout patterns — only for patterns used in 5+ places. +// Replaces common Tailwind utility classes with SCSS equivalents. +// ============================================================================ + +// --- Flexbox Layout --- +.flex { + display: flex; +} + +.flex-col { + display: flex; + flex-direction: column; +} + +.flex-row { + display: flex; + flex-direction: row; +} + +.flex-center { + display: flex; + align-items: center; + justify-content: center; +} + +.flex-between { + display: flex; + align-items: center; + justify-content: space-between; +} + +.items-center { + align-items: center; +} + +.items-start { + align-items: flex-start; +} + +.items-end { + align-items: flex-end; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.justify-end { + justify-content: flex-end; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.flex-1 { + flex: 1; +} + +.flex-shrink-0 { + flex-shrink: 0; +} + +.flex-grow { + flex-grow: 1; +} + +// --- Sizing --- +.w-full { + width: 100%; +} + +.h-full { + height: 100%; +} + +.w-fit { + width: fit-content; +} + +.min-w-0 { + min-width: 0; +} + +// --- Overflow --- +.overflow-hidden { + overflow: hidden; +} + +.overflow-auto { + overflow: auto; +} + +.overflow-y-auto { + overflow-y: auto; +} + +// --- Position --- +.relative { + position: relative; +} + +.absolute { + position: absolute; +} + +// --- Interaction --- +.cursor-pointer { + cursor: pointer; +} + +.select-none { + user-select: none; +} + +.pointer-events-none { + pointer-events: none; +} + +// --- Text --- +.text-center { + text-align: center; +} + +.text-left { + text-align: left; +} + +.text-right { + text-align: right; +} + +.truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.whitespace-nowrap { + white-space: nowrap; +} + +.font-bold { + font-weight: var(--font-weight-bold); +} + +.font-medium { + font-weight: var(--font-weight-medium); +} + +// --- Grid --- +.grid { + display: grid; +} + +// --- Visibility --- +.hidden { + display: none; +} + +.opacity-0 { + opacity: 0; +} + +.opacity-50 { + opacity: 0.5; +} + +// --- Fill Utilities (SVG) --- +.fill-cyan { + fill: var(--cyan-300); +} +.fill-cyan-muted { + fill: var(--cyan-400); +} +.fill-cyan-accent { + fill: var(--cyan-100); +} +.fill-red { + fill: var(--red-400); +} +.fill-orange { + fill: var(--orange-300); +} +.fill-orange-muted { + fill: var(--orange-200); +} +.fill-neutral-light { + fill: var(--neutral-2); +} +.fill-neutral { + fill: var(--neutral-8); +} +.fill-neutral-dark { + fill: var(--neutral-11); +} +.fill-surface-dark { + fill: var(--surface-800); +} +.fill-primary { + fill: var(--primary-500); +} +.fill-green { + fill: var(--green-300); +} +.fill-bg-app { + fill: var(--surface-900); +} +.fill-red-muted { + fill: var(--red-300); +} +.fill-white { + fill: var(--white); +} + +// --- Stroke Utilities (SVG) --- +.stroke-yellow { + stroke: var(--yellow-300); +} +.stroke-orange { + stroke: var(--orange-300); +} +.stroke-cyan { + stroke: var(--cyan-300); +} +.stroke-red { + stroke: var(--red-400); +} +.stroke-green { + stroke: var(--green-400); +} +.stroke-neutral-light { + stroke: var(--neutral-2); +} +.stroke-neutral { + stroke: var(--neutral-8); +} +.stroke-white { + stroke: var(--white); +} diff --git a/packages/ui-base/src/lib/assets/css/variables.scss b/packages/ui-base/src/lib/assets/css/variables.scss index b2be6a4a..f5aea037 100644 --- a/packages/ui-base/src/lib/assets/css/variables.scss +++ b/packages/ui-base/src/lib/assets/css/variables.scss @@ -1,314 +1,357 @@ :root { - // copy from jupyterlab\packages\theme-light-extension\style\variables.css - --jp-collaborator-color1: #ffad8e; - --jp-collaborator-color2: #dac83d; - --jp-collaborator-color3: #72dd76; - --jp-collaborator-color4: #00e4d0; - --jp-collaborator-color5: #45d4ff; - --jp-collaborator-color6: #e2b1ff; - --jp-collaborator-color7: #ff9de6; - - --mauve-1: #fdfcfd; - --mauve-2: #faf9fb; - --mauve-3: #f2eff3; - --mauve-4: #eae7ec; - --mauve-5: #e3dfe6; - --mauve-6: #dbd8e0; - --mauve-7: #d0cdd7; - --mauve-8: #bcbac7; - --mauve-9: #8e8c99; - --mauve-10: #84828e; - --mauve-11: #65636d; - --mauve-12: #211f26; - - --c-gray-1: var(--mauve-1); - --c-gray-2: var(--mauve-2); - --c-gray-3: var(--mauve-3); - --c-gray-4: var(--mauve-4); - --c-gray-5: var(--mauve-5); - --c-gray-6: var(--mauve-6); - --c-gray-7: var(--mauve-7); - --c-gray-8: var(--mauve-8); - --c-gray-9: var(--mauve-9); - --c-gray-10: var(--mauve-10); - --c-gray-11: var(--mauve-11); - --c-gray-12: var(--mauve-12); - - /*-----*/ - - --c-blue-6: #141432; - --c-blue-61: #161230; - --c-blue-62: #1b1a43; - --c-blue-4: #2c6e8a; - --c-blue-3: #288eb9; - --c-blue-1: #81f6ef; - /* - --c-blue-5: #11375b; - --c-blue-4: #005e80; - --c-blue-3: #00869d; - --c-blue-2: #31b0b0; - --c-blue-1: #72d9bb; - */ + // ============================================================================ + // DESIGN TOKENS + // ============================================================================ // - --c-blue-gray-0: #2b2b46; - --c-blue-gray-1: #292543; - --c-blue-gray-2: #1c1c39; - --c-blue-gray-3: #6e678c; - --c-blue-gray-4: #a998da; - --c-blue-gray-5: #352c58; - --c-blue-gray-6: #221c3d; - --c-blue-gray-7: #352c58; - --c-blue-gray-8: #2c2c47; - --c-blue-gray-9: #2e2b43; + // Font Size Scale + // --font-size-2xs: 9px (labels, fine print) + // --font-size-xs: 11px (captions, badges) + // --font-size-sm: 12px (secondary text, metadata) + // --font-size-md: 14px (body text — default) + // --font-size-lg: 16px (subheadings, emphasis) + // --font-size-xl: 18px (section headers) + // --font-size-2xl: 21px (page titles) + // --font-size-3xl: 25px (hero text) + // --font-size-4xl: 30px (display text) // - --color-chat-new: var(--c-blue-1); - --color-volume: var(--c-blue-4); - --color-terminal: var(--c-blue-4); - --color-button-blue: var(--c-blue-3); - --color-background: var(--c-blue-6); - --color-node-background: rgba(255, 255, 255, 0.1); - - /*-----*/ - - --c-alt-blue-4: #6077c9; - --c-alt-blue-3: #6563ff; - --c-alt-blue-2: #52acff; - --c-alt-blue-1: #60c9c9; + // Line Height + // --line-height-tight: 1.2 (headings) + // --line-height-normal: 1.5 (body text) + // --line-height-relaxed: 1.75 (reading-heavy content) // - --color-selection: var(--c-alt-blue-3); - --color-dataset: var(--c-alt-blue-2); - - /*-----*/ - - /* - --c-violet-6: #280025; - --c-violet-5: #592c54; - --c-violet-4: #7f4f79; - --c-violet-3: #a7749f; - --c-violet-2: #d09bc8; - --c-violet-1: #fbc4f2; - */ - - /*-----*/ - - --c-unknown-3: #dc745f; - - /*-----*/ - - --c-orange-4: #fa8100; - --c-orange-3: #ca922e; - --c-orange-2: #fbb25b; - --c-orange-1: #f2994a; - --ca-orange-45: #fa810080; - // - --color-edge-reference: var(--c-orange-2); - --color-scene: var(--c-orange-3); - --color-yjs-awareness-default: var(--c-orange-4); - - /*-----*/ - - --c-yellow-3: #ffcc00; - --c-yellow-2: #f9f871; - --c-yellow-1: #f8f1cb; - --color-debug: var(--c-yellow-3); - - /*-----*/ - - --c-green-4: #397833; - --c-green-3: #5bc673; - --c-green-2: #569047; - --c-green-1: #00c897; + // Font Weight + // --font-weight-light: 300 + // --font-weight-normal: 400 + // --font-weight-medium: 500 + // --font-weight-bold: 700 // - --color-good: var(--c-green-1); - - /*-----*/ - - --c-white-1: #ffffff; + // Spacing Scale (power-of-2) + // --spacing-0: 0px + // --spacing-1: 2px + // --spacing-2: 4px + // --spacing-3: 6px + // --spacing-4: 8px + // --spacing-5: 12px + // --spacing-6: 16px + // --spacing-7: 20px + // --spacing-8: 24px + // --spacing-9: 28px + // --spacing-10: 32px + // --spacing-11: 36px + // --spacing-12: 40px + // --spacing-14: 48px + // --spacing-16: 64px // - --ca-white-9: rgba(255, 255, 255, 0.9); - --ca-white-4: rgba(255, 255, 255, 0.35); - --ca-white-3: rgba(255, 255, 255, 0.25); - --ca-white-2: rgba(255, 255, 255, 0.15); - --ca-white-1: rgba(255, 255, 255, 0.1); - + // Shadow Set (dark-theme tuned) + // --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4) + // --shadow-md: 0 2px 6px rgba(0, 0, 0, 0.5) + // --shadow-lg: 0 4px 12px rgba(0, 0, 0, 0.6) + // --shadow-xl: 0 8px 24px rgba(0, 0, 0, 0.7) // - --color-code-output-text: var(--c-white-1); - --color-a-not-visited: var(--c-white-1); - --color-a-visited: var(--color-a-not-visited); - --color-a-hover: var(--color-a-not-visited); - - /*-----*/ - - --c-red-4: #be0000; - --c-red-3: #c31c4f; - --c-red-2: #b34444; - --c-red-1: #c43838; - + // Z-Index Layers + // --z-base: 0 + // --z-dropdown: 100 + // --z-sticky: 200 + // --z-modal: 300 + // --z-tooltip: 400 + // --z-toast: 500 // - --color-error: var(--c-red-4); - --color-youtube: #f00; - --color-vault: var(--c-red-3); - - /*-----*/ - - --c-taupe-4: #a14862; - - /*-----*/ - - /* - --c-pink-1: #760b9b; - --c-pink-2: #935bd9; - --c-pink-3: #d37dd4; - */ + // ============================================================================ + + // --- Font Size --- + --font-size-2xs: 9px; + --font-size-xs: 11px; + --font-size-sm: 12px; + --font-size-md: 14px; + --font-size-lg: 16px; + --font-size-xl: 18px; + --font-size-2xl: 21px; + --font-size-3xl: 25px; + --font-size-4xl: 30px; + + // --- Line Height --- + --line-height-tight: 1.2; + --line-height-normal: 1.5; + --line-height-relaxed: 1.75; + + // --- Font Weight --- + --font-weight-light: 300; + --font-weight-normal: 400; + --font-weight-medium: 500; + --font-weight-bold: 700; + + // --- Spacing --- + --spacing-0: 0px; + --spacing-1: 2px; + --spacing-2: 4px; + --spacing-3: 6px; + --spacing-4: 8px; + --spacing-5: 12px; + --spacing-6: 16px; + --spacing-7: 20px; + --spacing-8: 24px; + --spacing-9: 28px; + --spacing-10: 32px; + --spacing-11: 36px; + --spacing-12: 40px; + --spacing-14: 48px; + --spacing-16: 64px; + + // --- Shadow (dark-theme tuned) --- + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4); + --shadow-md: 0 2px 6px rgba(0, 0, 0, 0.5); + --shadow-lg: 0 4px 12px rgba(0, 0, 0, 0.6); + --shadow-xl: 0 8px 24px rgba(0, 0, 0, 0.7); + --shadow-node-editor: 0 0 6px -1px rgba(0, 0, 0, 0.25), 0 2px 4px -1px rgba(0, 0, 0, 0.1); + --shadow-form-glow: 0 0 2px 0 rgba(255, 255, 255, 0.25); + + // --- Shadow (component-specific) --- + --shadow-btn-purple-inset: 0px 0px 3px #987bbd inset; + --shadow-btn-blue-inset: 0px 0px 3px #0550b3 inset; + --shadow-glow-pink-hover: 0px 0px 90px -10px #92329f; + --shadow-glow-pink-active: 0px 0px 90px -10px #b562c0; + --shadow-glow-live: 0px 0px 10px 1px #f72686; + --shadow-focus-ring-gray: 0 0 0 2px var(--neutral-7); + --shadow-focus-ring-pink: 0 0 0 1.5px var(--primary-100); + --shadow-focus-ring-accent: 0 0 0 2px var(--primary-600, rgba(168, 85, 247, 0.2)); + --shadow-slider-pink: 0 2px 10px var(--primary-200); + --shadow-popover-focus: var(--shadow-xl), 0 0 0 2px var(--primary-200); + --shadow-select-glow: 0px 0px 5px 0px var(--form-input-bs-color); + --shadow-form-error-glow: 0 0 10px -5px var(--color-error); + --shadow-form-input: var(--form-input-bs-x, 0) var(--form-input-bs-y, 0) + var(--form-input-bs-blur) var(--form-input-bs-scale, 0) + var(--form-input-bs-color, transparent); + --shadow-form-input-focus: var(--form-input-bs-x, 0) var(--form-input-bs-y, 0) + var(--form-input-bs-blur) var(--form-input-bs-scale, 0) + var(--form-input-bs-color, transparent), + var(--form-input-focus-bs-x, 0) var(--form-input-focus-bs-y, 0) + var(--form-input-focus-bs-blur) var(--form-input-focus-bs-scale, 0) + var(--form-input-focus-bs-color, transparent); + + // --- Shadow (card-level) --- + --shadow-card-hover: 0 4px 12px rgba(0, 0, 0, 0.3); + --shadow-card-md-hover: 0 2px 8px rgba(0, 0, 0, 0.2); + --shadow-focus-blue: 0 0 0 3px rgba(77, 171, 247, 0.1); + --shadow-focus-blue-sm: 0 0 0 2px rgba(77, 171, 247, 0.2); + --shadow-id-card: 0 0 2px 0 rgba(255, 255, 255, 0.5); + + // --- Z-Index Layers --- + --z-base: 0; + --z-behind: -1; + --z-dropdown: 100; + --z-sticky: 200; + --z-modal: 300; + --z-tooltip: 400; + --z-toast: 500; + + // ============================================================================ + // LAYER 1 — PRIMITIVE COLOR TOKENS (OKLCH-derived) + // ============================================================================ // - - --c-pink-6: #41005a; - --c-pink-5: #4b0164; - --c-pink-51: #672aa4; - --c-pink-4: #76318e; - --c-pink-41: #742f94; - --c-pink-3: #a35bbb; - --c-pink-2: #d186e9; - --c-pink-1: #ffb3ff; + // Generated from OKLCH color space with consistent lightness/chroma curves. + // Components should rarely reference these directly — use semantic tokens. // - --color-chat-default: var(--c-pink-3); - --color-kernel: var(--c-pink-4); - --color-edge-sequence: var(--c-pink-3); - --color-kernel-state-1: var(--c-pink-2); - --color-kernel-state-2: var(--c-pink-3); - --color-form-border: var(--c-pink-1); - - /*-----*/ - - --c-black-6: #000000; - --c-black-5: #111111; - --c-black-4: #222222; - --c-black-3: #333333; + // Shade L C Role + // 100 0.93 0.04 lightest, near-white tint + // 200 0.82 0.08 light + // 300 0.72 0.14 light accent + // 400 0.62 0.18 medium + // 500 0.52 0.20 base (most saturated) + // 600 0.42 0.16 dark accent + // 700 0.32 0.12 dark + // 800 0.22 0.07 very dark + // 900 0.13 0.04 darkest + // ============================================================================ + + // --- Primary (H=300, brand purple/magenta) --- + --primary-100: #ece2ff; + --primary-200: #ccb9f1; + --primary-300: #b28fef; + --primary-400: #9867e1; + --primary-500: #7d40c8; + --primary-600: #5c2f95; + --primary-700: #3e1e65; + --primary-800: #201136; + --primary-900: #0a0415; + + // --- Surface (H=280, dark-theme surfaces) --- + --surface-100: #e2e6ff; + --surface-200: #b9bff8; + --surface-300: #9499fa; + --surface-400: #7474ef; + --surface-500: #594fd7; + --surface-600: #413aa0; + --surface-700: #2a266d; + --surface-800: #15153a; + --surface-900: #050517; + + // --- Cyan (H=230, data/interactive accent) --- + --cyan-100: #ceeefe; + --cyan-200: #8dceee; + --cyan-300: #16b3eb; + --cyan-400: #0095dc; + --cyan-500: #0076c4; + --cyan-600: #005792; + --cyan-700: #003a63; + --cyan-800: #001f34; + --cyan-900: #000914; + + // --- Red (H=25, error/danger) --- + --red-100: #ffdedb; + --red-200: #f4b0aa; + --red-300: #f07f77; + --red-400: #de4e4b; + --red-500: #c21725; + --red-600: #90101a; + --red-700: #62090f; + --red-800: #340909; + --red-900: #140202; + + // --- Orange (H=50, warning) --- + --orange-100: #ffe1d1; + --orange-200: #efb696; + --orange-300: #e9884d; + --orange-400: #d75c00; + --orange-500: #bc3000; + --orange-600: #8c2300; + --orange-700: #5f1600; + --orange-800: #330d00; + --orange-900: #130300; + + // --- Yellow (H=85, debug/info) --- + --yellow-100: #f4e6ca; + --yellow-200: #dcc188; + --yellow-300: #cd9c1f; + --yellow-400: #b77900; + --yellow-500: #9c5700; + --yellow-600: #744000; + --yellow-700: #4e2a00; + --yellow-800: #291600; + --yellow-900: #0e0600; + + // --- Green (H=160, success) --- + --green-100: #d2f1df; + --green-200: #96d5b2; + --green-300: #3fbf86; + --green-400: #00a55e; + --green-500: #00883c; + --green-600: #00652b; + --green-700: #00431b; + --green-800: #00240e; + --green-900: #000c03; + + // --- Neutrals (Radix mauve scale, perceptually uniform) --- + --neutral-1: #fdfcfd; + --neutral-2: #faf9fb; + --neutral-3: #f2eff3; + --neutral-4: #eae7ec; + --neutral-5: #e3dfe6; + --neutral-6: #dbd8e0; + --neutral-7: #d0cdd7; + --neutral-8: #bcbac7; + --neutral-9: #8e8c99; + --neutral-10: #84828e; + --neutral-11: #65636d; + --neutral-12: #211f26; + + // --- Alpha (white) --- + --alpha-white-90: rgba(255, 255, 255, 0.9); + --alpha-white-35: rgba(255, 255, 255, 0.35); + --alpha-white-25: rgba(255, 255, 255, 0.25); + --alpha-white-15: rgba(255, 255, 255, 0.15); + --alpha-white-10: rgba(255, 255, 255, 0.1); + + // --- Alpha (black) --- + --alpha-black-75: rgba(0, 0, 0, 0.75); + --alpha-black-65: rgba(0, 0, 0, 0.65); + --alpha-black-55: rgba(0, 0, 0, 0.55); + --alpha-black-45: rgba(0, 0, 0, 0.45); + --alpha-black-25: rgba(0, 0, 0, 0.25); + --alpha-black-17: rgba(0, 0, 0, 0.17); + --alpha-black-10: rgba(0, 0, 0, 0.1); + --alpha-black-5: rgba(0, 0, 0, 0.05); + + // --- Solids --- + --white: #ffffff; + --black-900: #000000; + --black-800: #111111; + --black-700: #222222; + --black-600: #333333; + + // ============================================================================ + // LAYER 2 — SEMANTIC TOKENS (theme-switchable) + // ============================================================================ // - --ca-black-2: rgba(0, 0, 0, 0.75); - --ca-black-3: rgba(0, 0, 0, 0.65); - --ca-black-4: rgba(0, 0, 0, 0.55); - --ca-black-5: rgba(0, 0, 0, 0.45); - --ca-black-6: rgba(0, 0, 0, 0.25); - --ca-black-7: rgba(0, 0, 0, 0.17); - --ca-black-8: rgba(0, 0, 0, 0.1); - --ca-black-9: rgba(0, 0, 0, 0.05); - - /*-----*/ - - --c-random-1: rgb(91, 145, 243); - --c-random-2: rgb(182, 116, 150); - --c-random-3: rgb(105, 85, 132); - --c-random-4: rgb(248, 144, 96); - --c-random-5: rgb(0, 163, 163); - - /* - * - * - */ + // Purpose-based names. Components use these for standard UI. + // To add a light theme: override these in [data-theme="light"] { } + // ============================================================================ + + // --- Backgrounds --- + --color-bg-app: var(--surface-900); + --color-bg-surface: var(--surface-800); + --color-bg-elevated: var(--surface-700); + --color-bg-input: var(--surface-800); + --color-bg-hover: var(--alpha-white-10); + --color-bg-node: var(--alpha-white-10); + + // --- Text --- + --color-text: var(--neutral-8); + --color-text-muted: var(--neutral-9); + --color-text-faint: var(--neutral-11); + --color-text-on-color: var(--white); + --color-text-placeholder: var(--alpha-white-35); + + // --- Borders --- + --color-border: var(--surface-600); + --color-border-muted: var(--surface-700); + --color-border-focus: var(--primary-300); + + // --- Accent --- + --color-accent: var(--primary-500); + --color-accent-hover: var(--primary-400); + --color-accent-muted: var(--primary-600); + --color-accent-strong: var(--primary-300); + + // --- Status --- + --color-success: var(--green-500); + --color-error: var(--red-500); + --color-warning: var(--orange-500); + --color-info: var(--cyan-500); + + // --- Interactive --- + --color-selection: var(--cyan-500); + --color-link: var(--white); + + // --- Shared UI aliases --- + --color-debug: var(--yellow-300); + --color-button-blue: var(--cyan-300); + --color-vault: var(--red-400); + + // ============================================================================ + // LAYOUT TOKENS + // ============================================================================ --border-radius-box: 8px; --node-border-radius: var(--border-radius-box); - // form - --form-label-color: var(--c-gray-8); + // --- Form System --- + --form-label-color: var(--neutral-8); --form-input-height: 35px; --form-input-border-radius: 4px; --form-input-padding-x: 15px; - --form-input-color: var(--c-gray-8); - --form-input-placeholder-color: var(--ca-white-4); + --form-input-color: var(--neutral-8); + --form-input-placeholder-color: var(--alpha-white-35); --form-input-font-size: 15px; --form-input-bs-x: 0; --form-input-bs-y: 0; --form-input-bs-blur: 0; --form-input-bs-scale: 1.5px; - --form-input-bs-color: var(--color-form-border); + --form-input-bs-color: var(--color-border-focus); --form-input-focus-bs-x: 0; --form-input-focus-bs-y: 0; --form-input-focus-bs-blur: 10px; --form-input-focus-bs-scale: -3px; - --form-input-focus-bs-color: var(--color-form-border); - - // - - .fill--c-alt-blue-2 { - fill: var(--c-alt-blue-2); - } - .fill--c-red-3 { - fill: var(--c-red-3); - } - .fill--c-orange-2 { - fill: var(--c-orange-2); - } - .fill--c-orange-3 { - fill: var(--c-orange-3); - } - .fill--c-alt-blue-2 { - fill: var(--c-alt-blue-2); - } - .fill--c-alt-blue-1 { - fill: var(--c-alt-blue-1); - } - .fill--c-alt-blue-4 { - fill: var(--c-alt-blue-4); - } - .fill--c-gray-2 { - fill: var(--c-gray-2); - } - .fill--c-gray-8 { - fill: var(--c-gray-8); - } - .fill--c-gray-11 { - fill: var(--c-gray-11); - } - .fill--c-blue-gray-6 { - fill: var(--c-blue-gray-6); - } - .fill--c-pink-3 { - fill: var(--c-pink-3); - } - .fill--c-orange-1 { - fill: var(--c-orange-1); - } - .fill--c-green-3 { - fill: var(--c-green-3); - } - .fill--c-blue-6 { - fill: var(--c-blue-6); - } - .fill--c-red-1 { - fill: var(--c-red-1); - } - .fill--c-white-1 { - fill: var(--c-white-1); - } - - // - - .stroke--c-yellow-3 { - stroke: var(--c-yellow-3); - } - .stroke--c-orange-2 { - stroke: var(--c-orange-2); - } - .stroke--c-alt-blue-2 { - stroke: var(--c-alt-blue-2); - } - .stroke--c-red-3 { - stroke: var(--c-red-3); - } - .stroke--c-green-1 { - stroke: var(--c-green-1); - } - .stroke--c-gray-2 { - stroke: var(--c-gray-2); - } - .stroke--c-gray-8 { - stroke: var(--c-gray-8); - } - .stroke--c-white-1 { - stroke: var(--c-white-1); - } + --form-input-focus-bs-color: var(--color-border-focus); } diff --git a/packages/ui-base/src/lib/assets/icons.stories.tsx b/packages/ui-base/src/lib/assets/icons.stories.tsx index f20a0aef..9e3e2d7d 100644 --- a/packages/ui-base/src/lib/assets/icons.stories.tsx +++ b/packages/ui-base/src/lib/assets/icons.stories.tsx @@ -6,12 +6,12 @@ import { CSSProperties, FC, HTMLAttributes } from 'react'; const s1 = { width: '120px', height: '120px', - border: 'solid 1px var(--c-gray-9)', + border: 'solid 1px var(--neutral-9)', display: 'flex' /* Use flexbox for centering */, justifyContent: 'center' /* Center horizontally */, alignItems: 'center' /* Center vertically */, position: 'relative', - backgroundColor: 'var(--ca-white-1)', + backgroundColor: 'var(--alpha-white-10)', } as CSSProperties; const s3 = { @@ -19,7 +19,7 @@ const s3 = { bottom: '-3px', left: 0, fontSize: '11px', - color: 'var(--c-white-1)', + color: 'var(--white)', } as CSSProperties; const AllIcon = () => { @@ -41,9 +41,7 @@ const AllIcon = () => { @@ -30,7 +30,7 @@ export const icons = { > @@ -61,11 +61,11 @@ export const icons = { > ), @@ -112,13 +112,13 @@ export const icons = { fillRule="evenodd" clipRule="evenodd" d="M8.99999 0.899902C13.4735 0.899902 17.1 4.5264 17.1 8.9999C17.1 13.4734 13.4735 17.0999 8.99999 17.0999C4.52649 17.0999 0.899994 13.4734 0.899994 8.9999C0.899994 4.5264 4.52649 0.899902 8.99999 0.899902Z" - className="stroke--c-green-1" + className="stroke-green" strokeLinecap="round" strokeLinejoin="round" /> - + @@ -142,7 +142,7 @@ export const icons = { > @@ -163,7 +163,7 @@ export const icons = { height="8" rx="0.666667" transform="rotate(-45 2.35703 3.2998)" - className="fill--c-gray-8" + className="fill-neutral" /> ), @@ -185,14 +185,14 @@ export const icons = { fill="none" xmlns="http://www.w3.org/2000/svg" > - + ), @@ -208,12 +208,12 @@ export const icons = { @@ -438,12 +438,12 @@ export const icons = { fillRule="evenodd" clipRule="evenodd" d="M3.26959 3.37874L9.07057 1.01411L14.6447 3.37364L17.1227 9.0871L14.6567 14.8801L9.07135 17.1369L3.26959 14.7719L1.01365 9.07535L3.26959 3.37874ZM7.13364 12.7871C6.42765 12.6101 5.48633 11.3122 5.48633 11.0762C5.48633 10.9976 5.67665 10.7663 5.93845 10.4862C6.00346 10.2895 6.13349 9.82542 6.13349 9.54222C6.13349 9.41427 6.04775 9.14689 5.94255 8.81884L5.94255 8.81883L5.94255 8.81882C5.75672 8.23932 5.51018 7.47048 5.56825 6.9463C5.68591 5.88434 6.58454 5.09769 7.13364 4.64538L6.8983 4.40938H11.0166L10.7813 4.64538C9.99683 5.39269 8.87508 7.34749 10.6636 9.18824C12.4521 11.029 12.5855 11.2138 12.4286 11.0762C12.154 11.5678 11.4637 12.5983 10.8989 12.7871C10.3341 12.9759 9.36928 13.0231 8.95746 13.0231C8.58485 13.0035 7.69844 12.9287 7.13364 12.7871ZM6.37597 8.53685C5.44278 6.52698 6.77752 5.45624 7.58508 5.12725C9.02064 6.77821 7.82437 8.74621 7.04679 9.52384C6.66157 9.13857 6.47665 8.74852 6.39908 8.5849L6.39906 8.58487C6.38993 8.56559 6.38228 8.54946 6.37597 8.53685Z" - className="fill--c-white-1" + className="fill-white" fillOpacity="0.06" /> @@ -484,15 +484,15 @@ export const icons = { > - + - + - + - + - + @@ -617,12 +617,12 @@ export const icons = { width="5.34787" height="0.894305" rx="0.447152" - className="stroke--c-orange-2" + className="stroke-orange" strokeWidth="0.2" /> @@ -668,104 +668,89 @@ export const icons = { width="26.4233" height="15.1314" rx="1" - fill="var(--color-background)" - className="stroke--c-alt-blue-2" + fill="var(--color-bg-app)" + className="stroke-cyan" /> - - - - - + + + + + ), @@ -780,11 +765,11 @@ export const icons = { > @@ -802,11 +787,11 @@ export const icons = { fillRule="evenodd" clipRule="evenodd" d="M0.666824 25.4334C0.666824 26.3101 4.16384 31.132 6.7866 31.7895C8.88481 32.3156 12.1778 32.5932 13.5621 32.6663C15.092 32.6663 18.6764 32.4909 20.7746 31.7895C22.8728 31.0882 25.4373 27.2599 26.4573 25.4334C27.0401 25.9448 26.5447 25.258 19.9004 18.4197C13.256 11.5813 17.4233 4.31923 20.3375 1.54297L21.2118 0.66626H5.91232L6.7866 1.54297C4.74668 3.22334 1.40828 6.14571 0.971186 10.0909C0.755438 12.0382 1.67134 14.8945 2.36169 17.0473C2.7525 18.2661 3.07102 19.2594 3.07102 19.7348C3.07102 20.7868 2.58797 22.511 2.34645 23.2416C1.37387 24.2822 0.666824 25.1414 0.666824 25.4334ZM8.46335 3.33293C5.46329 4.55515 0.504715 8.53293 3.97151 15.9996C3.99496 16.0465 4.02339 16.1064 4.05737 16.1781C4.34554 16.7859 5.03253 18.235 6.46361 19.6663C9.35232 16.7774 13.7965 9.46626 8.46335 3.33293Z" - className="fill--c-blue-gray-6" + className="fill-surface-dark" /> ), @@ -821,7 +806,7 @@ export const icons = { > ), @@ -836,7 +821,7 @@ export const icons = { > ), @@ -851,7 +836,7 @@ export const icons = { > ), @@ -866,14 +851,14 @@ export const icons = { > ), @@ -888,22 +873,22 @@ export const icons = { > @@ -919,22 +904,22 @@ export const icons = { > @@ -989,7 +974,7 @@ export const icons = { > ), @@ -1004,7 +989,7 @@ export const icons = { > ), @@ -1022,13 +1007,13 @@ export const icons = { fillRule="evenodd" clipRule="evenodd" d="M11 1.09998C16.4676 1.09998 20.9 5.53236 20.9 11C20.9 16.4676 16.4676 20.9 11 20.9C5.53236 20.9 1.09998 16.4676 1.09998 11C1.09998 5.53236 5.53236 1.09998 11 1.09998Z" - className="stroke--c-green-1" + className="stroke-green" strokeLinecap="round" strokeLinejoin="round" /> - + @@ -1052,13 +1037,13 @@ export const icons = { > @@ -1066,7 +1051,7 @@ export const icons = { fillRule="evenodd" clipRule="evenodd" d="M14.4004 1C19.868 1 24.3004 5.43238 24.3004 10.9C24.3004 16.3676 16.3676 20.7994 10.9 20.7994C5.43238 20.7994 1 16.3671 1 10.8994C1 5.43182 8.93277 1 14.4004 1Z" - className="stroke--c-red-3" + className="stroke-red" strokeLinecap="round" strokeLinejoin="round" /> @@ -1083,28 +1068,28 @@ export const icons = { > - + - + - + - + ), @@ -1234,7 +1219,7 @@ export const icons = { > @@ -1265,7 +1250,7 @@ export const icons = { fill="none" xmlns="http://www.w3.org/2000/svg" > - + ), @@ -1297,17 +1282,17 @@ export const icons = { > ), @@ -1324,11 +1309,11 @@ export const icons = { fillRule="evenodd" clipRule="evenodd" d="M25.4771 12.231C26.6343 13.1264 26.6343 14.8735 25.4771 15.7688C21.9799 18.4748 18.0749 20.6077 13.9081 22.0876L13.1469 22.3579C11.6905 22.8751 10.1523 21.8898 9.95513 20.3865C9.40428 16.1863 9.40428 11.8136 9.95513 7.61336C10.1523 6.11006 11.6906 5.12474 13.1469 5.64197L13.9081 5.91232C18.0749 7.39222 21.9799 9.52506 25.4771 12.231ZM24.4062 14.3848C24.6579 14.19 24.6579 13.8099 24.4062 13.6151C21.0557 11.0226 17.3144 8.97923 13.3224 7.5614L12.5612 7.29105C12.1821 7.1564 11.7464 7.41302 11.6903 7.84092C11.1592 11.8901 11.1592 16.1098 11.6903 20.159C11.7464 20.5869 12.1821 20.8435 12.5612 20.7088L13.3224 20.4385C17.3144 19.0207 21.0557 16.9773 24.4062 14.3848Z" - className="fill--c-white-1" + className="fill-white" /> ), @@ -1343,7 +1328,7 @@ export const icons = { > ), @@ -1358,7 +1343,7 @@ export const icons = { > ), @@ -1377,7 +1362,7 @@ export const icons = { width="16" height="16" rx="3" - className="stroke--c-white-1" + className="stroke-white" strokeWidth="2" /> @@ -1394,24 +1379,24 @@ export const icons = { - + @@ -1427,7 +1412,7 @@ export const icons = { > ), @@ -1444,7 +1429,7 @@ export const icons = { fillRule="evenodd" clipRule="evenodd" d="M14.3998 1.8C14.3998 0.805886 13.5939 0 12.5998 0H5.21813L1.19981 2.67889V13.8C1.19981 14.7941 2.00573 15.6 2.99981 15.6H3.59981V16.2C3.59981 17.1941 4.40573 18 5.39981 18H14.9998C15.9939 18 16.7998 17.1941 16.7998 16.2V4.2C16.7998 3.20588 15.9939 2.4 14.9998 2.4H14.3998V1.8ZM14.3998 3.6H14.9998C15.3312 3.6 15.5998 3.86863 15.5998 4.2V16.2C15.5998 16.5313 15.3312 16.8 14.9998 16.8H5.39981C5.06849 16.8 4.79981 16.5313 4.79981 16.2V15.6H12.5998C13.5939 15.6 14.3998 14.7941 14.3998 13.8V3.6Z" - className="fill--c-white-1" + className="fill-white" /> ), @@ -1459,12 +1444,12 @@ export const icons = { > @@ -1569,7 +1554,7 @@ export const icons = { > ), @@ -1582,40 +1567,12 @@ export const icons = { fill="none" xmlns="http://www.w3.org/2000/svg" > - - - - - - + + + + + + ), Grow: ({ ...props }: IconProps) => ( @@ -1627,10 +1584,10 @@ export const icons = { fill="none" xmlns="http://www.w3.org/2000/svg" > - + ), @@ -1646,7 +1603,7 @@ export const icons = { @@ -1688,13 +1645,13 @@ export const icons = { @@ -1759,15 +1716,15 @@ export const icons = { - + @@ -1809,13 +1766,13 @@ export const icons = { @@ -2221,7 +2178,7 @@ export const icons = { @@ -2279,7 +2236,7 @@ export const icons = { > ), diff --git a/packages/ui-base/src/lib/credentials/credentials.scss b/packages/ui-base/src/lib/credentials/credentials.scss index 29f2ab56..265f6e0b 100644 --- a/packages/ui-base/src/lib/credentials/credentials.scss +++ b/packages/ui-base/src/lib/credentials/credentials.scss @@ -16,7 +16,7 @@ h2 { margin: 0; - font-size: 1.25rem; + font-size: var(--font-size-2xl); font-weight: 600; color: var(--c-text-primary, #fff); } @@ -97,7 +97,7 @@ h3 { margin: 0; - font-size: 0.95rem; + font-size: var(--font-size-lg); font-weight: 500; color: var(--c-text-primary, #fff); white-space: nowrap; @@ -112,12 +112,12 @@ margin-top: 4px; span { - font-size: 0.8rem; + font-size: var(--font-size-sm); color: var(--c-text-muted, rgba(255, 255, 255, 0.5)); } .badge { - font-size: 0.7rem; + font-size: var(--font-size-xs); padding: 2px 6px; border-radius: var(--radius-xs, 4px); background: var(--c-accent-muted, rgba(168, 85, 247, 0.2)); @@ -175,14 +175,14 @@ &__header { h3 { margin: 0 0 var(--spacing-xs, 0.25rem); - font-size: 1.1rem; + font-size: var(--font-size-xl); font-weight: 600; color: var(--c-text-primary, #fff); } p { margin: 0; - font-size: 0.85rem; + font-size: var(--font-size-md); color: var(--c-text-muted, rgba(255, 255, 255, 0.6)); } } @@ -202,7 +202,7 @@ button { padding: var(--spacing-sm, 0.5rem) var(--spacing-md, 1rem); - font-size: 0.9rem; + font-size: var(--font-size-md); font-weight: 500; border-radius: var(--radius-sm, 6px); cursor: pointer; @@ -222,7 +222,7 @@ .submit-btn { background: var(--c-accent, #a855f7); border: none; - color: #fff; + color: var(--white); &:hover { background: var(--c-accent-hover, #9333ea); @@ -268,12 +268,12 @@ p { margin: 0; - font-size: 0.9rem; + font-size: var(--font-size-md); color: var(--c-text-secondary, rgba(255, 255, 255, 0.7)); } span { - font-size: 0.8rem; + font-size: var(--font-size-sm); color: var(--c-text-muted, rgba(255, 255, 255, 0.5)); } } @@ -307,7 +307,7 @@ } span { - font-size: 0.8rem; + font-size: var(--font-size-sm); color: var(--c-text-secondary, rgba(255, 255, 255, 0.8)); text-align: center; } @@ -325,7 +325,7 @@ display: flex; align-items: center; justify-content: center; - z-index: 1000; + z-index: var(--z-toast); padding: var(--spacing-md, 1rem); } @@ -358,7 +358,7 @@ h3 { margin: 0; - font-size: 1.1rem; + font-size: var(--font-size-xl); font-weight: 600; color: var(--c-text-primary, #fff); } @@ -392,7 +392,7 @@ &__description { padding: var(--spacing-md, 1rem) var(--spacing-lg, 1.5rem); margin: 0; - font-size: 0.9rem; + font-size: var(--font-size-md); color: var(--c-text-secondary, rgba(255, 255, 255, 0.7)); line-height: 1.5; @@ -406,7 +406,7 @@ h4 { margin: 0 0 var(--spacing-sm, 0.5rem); - font-size: 0.85rem; + font-size: var(--font-size-md); font-weight: 500; color: var(--c-text-muted, rgba(255, 255, 255, 0.5)); text-transform: uppercase; @@ -420,7 +420,7 @@ h4 { margin: 0 0 var(--spacing-md, 1rem); - font-size: 0.85rem; + font-size: var(--font-size-md); font-weight: 500; color: var(--c-text-muted, rgba(255, 255, 255, 0.5)); text-transform: uppercase; @@ -447,7 +447,7 @@ background: transparent; border-radius: var(--radius-xs, 4px); color: var(--c-text-secondary, rgba(255, 255, 255, 0.6)); - font-size: 0.85rem; + font-size: var(--font-size-md); font-weight: 500; cursor: pointer; transition: all 0.15s ease; @@ -463,7 +463,7 @@ &--active { background: var(--c-accent, #a855f7); - color: #fff; + color: var(--white); } } } @@ -479,7 +479,7 @@ border: 1px solid var(--c-border, rgba(255, 255, 255, 0.1)); border-radius: var(--radius-sm, 6px); color: var(--c-text-primary, #fff); - font-size: 0.9rem; + font-size: var(--font-size-md); cursor: pointer; transition: all 0.15s ease; @@ -490,7 +490,7 @@ &:focus { outline: none; border-color: var(--c-accent, #a855f7); - box-shadow: 0 0 0 2px var(--c-accent-muted, rgba(168, 85, 247, 0.2)); + box-shadow: var(--shadow-focus-ring-accent); } option { @@ -505,7 +505,7 @@ background: var(--c-surface-secondary, rgba(255, 255, 255, 0.03)); border: 1px dashed var(--c-border, rgba(255, 255, 255, 0.1)); border-radius: var(--radius-sm, 6px); - font-size: 0.85rem; + font-size: var(--font-size-md); color: var(--c-text-muted, rgba(255, 255, 255, 0.5)); text-align: center; } @@ -518,7 +518,7 @@ button { padding: var(--spacing-sm, 0.5rem) var(--spacing-md, 1rem); - font-size: 0.9rem; + font-size: var(--font-size-md); font-weight: 500; border-radius: var(--radius-sm, 6px); cursor: pointer; @@ -538,7 +538,7 @@ .submit-btn { background: var(--c-accent, #a855f7); border: none; - color: #fff; + color: var(--white); &:hover { background: var(--c-accent-hover, #9333ea); @@ -594,7 +594,7 @@ } &__name { - font-size: 0.9rem; + font-size: var(--font-size-md); font-weight: 500; color: var(--c-text-primary, #fff); white-space: nowrap; @@ -603,7 +603,7 @@ } &__type { - font-size: 0.75rem; + font-size: var(--font-size-sm); color: var(--c-text-muted, rgba(255, 255, 255, 0.5)); } diff --git a/packages/ui-base/src/lib/css-utils/wrapper-css-coordinates.stories.tsx b/packages/ui-base/src/lib/css-utils/wrapper-css-coordinates.stories.tsx index 70d1367b..9ef05ae7 100644 --- a/packages/ui-base/src/lib/css-utils/wrapper-css-coordinates.stories.tsx +++ b/packages/ui-base/src/lib/css-utils/wrapper-css-coordinates.stories.tsx @@ -18,7 +18,7 @@ export const Primary: Story = { style={{ width: '300px', height: '300px', - background: `linear-gradient(45deg, var(--c-orange-2), var(--c-green-1))`, + background: `linear-gradient(45deg, var(--orange-200), var(--green-400))`, backgroundPosition: 'var(--x) var(--y)', backgroundSize: '400%', }} @@ -31,7 +31,7 @@ export const Primary: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); expect( - canvas.getByText(/Welcome to WrapperCssCoordinates!/gi), + canvas.getByText(/Welcome to WrapperCssCoordinates!/gi) ).toBeTruthy(); }, }; diff --git a/packages/ui-base/src/lib/datetime/datetime.scss b/packages/ui-base/src/lib/datetime/datetime.scss index 150eaa9c..57b9a2da 100644 --- a/packages/ui-base/src/lib/datetime/datetime.scss +++ b/packages/ui-base/src/lib/datetime/datetime.scss @@ -12,8 +12,8 @@ margin-right: 5px; display: inline; path { - stroke: var(--c-gray-8); - fill: var(--c-gray-8); + stroke: var(--neutral-8); + fill: var(--neutral-8); } } @@ -24,9 +24,9 @@ .datetime-hover { position: absolute; - background-color: var(--c-black-5); - border: solid 1px var(--c-white-1); - color: var(--c-white-1); + background-color: var(--black-800); + border: solid 1px var(--white); + color: var(--white); padding: 0px 7px; border-radius: 9999px; white-space: nowrap; diff --git a/packages/ui-base/src/lib/form/form-errors/form-errors.scss b/packages/ui-base/src/lib/form/form-errors/form-errors.scss index 3fcb3756..21c93b9e 100644 --- a/packages/ui-base/src/lib/form/form-errors/form-errors.scss +++ b/packages/ui-base/src/lib/form/form-errors/form-errors.scss @@ -4,14 +4,14 @@ .form-error { color: var(--color-error); - font-size: 10px; + font-size: var(--font-size-2xs); text-align: center; } .form-errors { padding: 10px; border-radius: var(--border-radius-box); - box-shadow: 0 0 10px -5px var(--color-error); + box-shadow: var(--shadow-form-error-glow); ul { list-style-type: none; diff --git a/packages/ui-base/src/lib/form/form-fields/color-picker.scss b/packages/ui-base/src/lib/form/form-fields/color-picker.scss index ee0742fb..e93a243f 100644 --- a/packages/ui-base/src/lib/form/form-fields/color-picker.scss +++ b/packages/ui-base/src/lib/form/form-fields/color-picker.scss @@ -2,7 +2,7 @@ width: 20px; height: 20px; border-radius: 4px; - border: 1px solid #ccc; + border: 1px solid var(--neutral-7); cursor: pointer; display: flex; align-items: center; @@ -11,16 +11,16 @@ background: none; &:hover { - border-color: #ddd; + border-color: var(--neutral-6); } } .color-popover { - background-color: var(--c-blue-gray-8); + background-color: var(--surface-700); border-radius: 6px; padding: 12px; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); - z-index: 1000; + box-shadow: var(--shadow-md); + z-index: var(--z-toast); .color-grid { display: grid; @@ -33,7 +33,7 @@ height: 24px; border-radius: 4px; cursor: pointer; - border: 1px solid var(--c-gray-30); + border: 1px solid var(--neutral-3); transition: transform 0.2s ease; position: relative; @@ -46,8 +46,8 @@ display: flex; align-items: center; justify-content: center; - font-size: 16px; - color: white; + font-size: var(--font-size-lg); + color: var(--white); input[type='color'] { position: absolute; diff --git a/packages/ui-base/src/lib/form/form-fields/switch-fieldset.scss b/packages/ui-base/src/lib/form/form-fields/switch-fieldset.scss index 7e5e11f3..2c5c9e0a 100644 --- a/packages/ui-base/src/lib/form/form-fields/switch-fieldset.scss +++ b/packages/ui-base/src/lib/form/form-fields/switch-fieldset.scss @@ -23,13 +23,13 @@ margin: 0; margin-left: 15px; padding: 0; - color: var(--c-white-1); - font-size: 9px; + color: var(--white); + font-size: var(--font-size-2xs); font-weight: 500; transition: 0.2s; &.switch-active { - color: var(--c-green-1); + color: var(--green-400); } } } @@ -38,12 +38,12 @@ display: inline-block; width: var(--button-width); height: var(--button-height); - background-color: var(--color-node-background); + background-color: var(--color-bg-node); border-radius: calc(var(--button-height) / 1.8); position: relative; transition: 0.3s all ease-in-out; cursor: pointer; - border: 1px solid var(--c-gray-7); + border: 1px solid var(--neutral-7); } span::after { @@ -51,23 +51,23 @@ display: inline-block; width: var(--toggle-diameter); height: var(--toggle-diameter); - background-color: var(--c-gray-7); + background-color: var(--neutral-7); border-radius: calc(var(--toggle-diameter) / 2); position: absolute; top: var(--button-toggle-offset); left: 0; transform: translateX(var(--button-toggle-offset)); box-shadow: var(--toggle-shadow-offset) 0 - calc(var(--toggle-shadow-offset) * 4) var(--ca-black-8); + calc(var(--toggle-shadow-offset) * 4) var(--alpha-black-10); transition: 0.3s all ease-in-out; } input[type='checkbox']:checked + span { - background-color: var(--c-green-1); + background-color: var(--green-400); } input[type='checkbox']:checked + span::after { - background-color: var(--c-white-1); + background-color: var(--white); transform: translateX( calc( var(--button-width) - var(--toggle-diameter) - @@ -75,7 +75,7 @@ ) ); box-shadow: calc(var(--toggle-shadow-offset) * -1) 0 - calc(var(--toggle-shadow-offset) * 4) var(--ca-black-8); + calc(var(--toggle-shadow-offset) * 4) var(--alpha-black-10); } input[type='checkbox'] { diff --git a/packages/ui-base/src/lib/form/form-fields/text-fieldset.tsx b/packages/ui-base/src/lib/form/form-fields/text-fieldset.tsx index d34b71c6..0b3a0fb2 100644 --- a/packages/ui-base/src/lib/form/form-fields/text-fieldset.tsx +++ b/packages/ui-base/src/lib/form/form-fields/text-fieldset.tsx @@ -72,7 +72,7 @@ export const TextFieldset = ({ {copyButton && ( )} diff --git a/packages/ui-base/src/lib/form/form-fields/totp-fieldset.scss b/packages/ui-base/src/lib/form/form-fields/totp-fieldset.scss index 16a79b20..8925c049 100644 --- a/packages/ui-base/src/lib/form/form-fields/totp-fieldset.scss +++ b/packages/ui-base/src/lib/form/form-fields/totp-fieldset.scss @@ -14,14 +14,14 @@ width: 40px; box-shadow: var(--form-input-bs-x, 0) var(--form-input-bs-y, 0) var(--form-input-bs-blur) var(--form-input-bs-scale, 0) - var(--ca-white-4, transparent); + var(--alpha-white-35, transparent); padding: 0; - background-color: var(--color-background); + background-color: var(--color-bg-app); svg { height: 75%; width: 75%; - color: var(--c-white-1); + color: var(--white); } } } diff --git a/packages/ui-base/src/lib/liveSpace/liveSpace.css b/packages/ui-base/src/lib/liveSpace/liveSpace.css index 522ab877..678a4e2c 100644 --- a/packages/ui-base/src/lib/liveSpace/liveSpace.css +++ b/packages/ui-base/src/lib/liveSpace/liveSpace.css @@ -15,8 +15,8 @@ position: absolute; top: -50px; left: 40px; - color: var(--c-white-1); - font-size: 14px; + color: var(--white); + font-size: var(--font-size-md); font-weight: bold; } @@ -55,16 +55,16 @@ display: flex; align-items: center; justify-content: center; - background: var(--c-pink-51); + background: var(--primary-600); } .new-comment span { - color: var(--c-white-1); - font-size: 10px; + color: var(--white); + font-size: var(--font-size-2xs); } .resizable-div { - border: 3px dashed var(--c-black-6); + border: 3px dashed var(--black-900); overflow: hidden; position: relative; border-radius: 8px; @@ -79,13 +79,13 @@ .anchor { width: 20px; height: 20px; - background-color: var(--c-gray-7); + background-color: var(--neutral-7); opacity: 0.2; border-radius: 50%; position: absolute; bottom: -8px; right: -8px; - z-index: 10; + z-index: var(--z-base); cursor: nwse-resize; transition: 0.2s; } diff --git a/packages/ui-base/src/lib/liveSpace/liveSpace.stories.ts b/packages/ui-base/src/lib/liveSpace/liveSpace.stories.ts index bf92b508..6bc32b19 100644 --- a/packages/ui-base/src/lib/liveSpace/liveSpace.stories.ts +++ b/packages/ui-base/src/lib/liveSpace/liveSpace.stories.ts @@ -1,10 +1,19 @@ import type { Meta, StoryObj } from '@storybook/react'; +import { createElement } from 'react'; import { LiveSpace } from './liveSpace'; const meta: Meta = { title: 'UI/LiveSpace', component: LiveSpace, + decorators: [ + (Story) => + createElement( + 'div', + { style: { padding: '60px 50px' } }, + createElement(Story) + ), + ], parameters: { layout: 'centered', }, @@ -27,7 +36,7 @@ type Story = StoryObj; export const Normal: Story = { args: { - color: 'var(--c-green-1)', + color: 'var(--green-400)', status: 'default', }, }; diff --git a/packages/ui-base/src/lib/palette/palette-viewer.css b/packages/ui-base/src/lib/palette/palette-viewer.css index 360731da..1c208772 100644 --- a/packages/ui-base/src/lib/palette/palette-viewer.css +++ b/packages/ui-base/src/lib/palette/palette-viewer.css @@ -1,7 +1,7 @@ h1, h2, h3 { - color: var(--c-white-1); + color: var(--white); } .palette-viewer { @@ -17,19 +17,19 @@ h3 { } .color-box { - border: solid 1px var(--c-gray-9); + border: solid 1px var(--neutral-9); height: 100px; padding: 20px 15px; border-radius: 5px; box-sizing: border-box; - color: var(--c-white-1); + color: var(--white); font-weight: bold; cursor: pointer; } .color-box:hover { - color: var(--c-black-4); + color: var(--black-700); } .color-label { - font-size: 14px; + font-size: var(--font-size-md); } diff --git a/packages/ui-base/src/lib/palette/palette.stories.tsx b/packages/ui-base/src/lib/palette/palette.stories.tsx index 6e3fe9a6..c12c86ea 100644 --- a/packages/ui-base/src/lib/palette/palette.stories.tsx +++ b/packages/ui-base/src/lib/palette/palette.stories.tsx @@ -14,7 +14,7 @@ const StoryWrapper = (props: Record) => { const [copied, setCopied] = useState(''); useEffect(() => { - const palette = getCssProperties('--c-'); + const palette = getCssProperties('--'); const groups: PaletteGroups = {}; Object.keys(palette).forEach((property) => { const group = property.replace(/\d/g, ''); diff --git a/packages/ui-base/src/lib/permissions-page/permissions-page.scss b/packages/ui-base/src/lib/permissions-page/permissions-page.scss index e34fb5e3..03ef5230 100644 --- a/packages/ui-base/src/lib/permissions-page/permissions-page.scss +++ b/packages/ui-base/src/lib/permissions-page/permissions-page.scss @@ -12,7 +12,7 @@ margin-bottom: 2rem; h1 { - font-size: 2rem; + font-size: var(--font-size-4xl); font-weight: 600; margin: 0 0 0.5rem 0; color: var(--color-text-primary, #ffffff); @@ -21,7 +21,7 @@ .subtitle { margin: 0; color: var(--color-text-secondary, #888888); - font-size: 0.95rem; + font-size: var(--font-size-lg); } } @@ -44,7 +44,7 @@ border: none; color: var(--color-text-secondary, #888888); cursor: pointer; - font-size: 0.95rem; + font-size: var(--font-size-lg); font-weight: 500; transition: all 0.2s; border-bottom: 2px solid transparent; @@ -96,13 +96,13 @@ h3 { margin: 0 0 0.25rem 0; - font-size: 1.1rem; + font-size: var(--font-size-xl); font-weight: 600; } .member-count { margin: 0; - font-size: 0.85rem; + font-size: var(--font-size-md); color: var(--color-text-secondary, #888888); } } @@ -129,7 +129,7 @@ border: 1px solid var(--color-border, #333333); border-radius: 6px; color: var(--color-text-primary, #e0e0e0); - font-size: 0.9rem; + font-size: var(--font-size-md); &::placeholder { color: var(--color-text-secondary, #666666); @@ -198,7 +198,7 @@ &.selected { background: var(--color-accent-primary, #3b82f6); - color: #ffffff; + color: var(--white); .email, .no-roles, @@ -219,12 +219,12 @@ height: 36px; border-radius: 50%; background: var(--color-accent-secondary, #6366f1); - color: #ffffff; + color: var(--white); display: flex; align-items: center; justify-content: center; font-weight: 600; - font-size: 0.9rem; + font-size: var(--font-size-md); flex-shrink: 0; } @@ -234,14 +234,14 @@ .username { font-weight: 500; - font-size: 0.95rem; + font-size: var(--font-size-lg); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .email { - font-size: 0.8rem; + font-size: var(--font-size-sm); color: var(--color-text-secondary, #888888); white-space: nowrap; overflow: hidden; @@ -252,7 +252,7 @@ .roles-preview { flex-shrink: 0; - font-size: 0.85rem; + font-size: var(--font-size-md); .no-roles { color: var(--color-text-secondary, #666666); @@ -279,7 +279,7 @@ justify-content: center; height: 100%; color: var(--color-text-secondary, #888888); - font-size: 0.95rem; + font-size: var(--font-size-lg); } } } @@ -301,13 +301,13 @@ header { h3 { margin: 0 0 0.25rem 0; - font-size: 1.25rem; + font-size: var(--font-size-2xl); font-weight: 600; } .user-id-hint { margin: 0; - font-size: 0.8rem; + font-size: var(--font-size-sm); color: var(--color-text-secondary, #666666); font-family: 'Courier New', monospace; } @@ -316,7 +316,7 @@ section { h4 { margin: 0 0 0.75rem 0; - font-size: 0.95rem; + font-size: var(--font-size-lg); font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; @@ -351,7 +351,7 @@ border: 1px solid var(--color-border, #333333); border-radius: 6px; color: var(--color-text-primary, #e0e0e0); - font-size: 0.9rem; + font-size: var(--font-size-md); &:focus { outline: none; @@ -373,7 +373,7 @@ border: 1px solid rgba(59, 130, 246, 0.3); border-radius: 6px; color: var(--color-accent-primary, #3b82f6); - font-size: 0.9rem; + font-size: var(--font-size-md); } } @@ -384,19 +384,19 @@ gap: 0.5rem; padding: 0.5rem 0.75rem; border-radius: 6px; - font-size: 0.85rem; + font-size: var(--font-size-md); font-weight: 500; &.system { background: rgba(139, 92, 246, 0.15); border: 1px solid rgba(139, 92, 246, 0.4); - color: #a78bfa; + color: var(--surface-300); /* charter-exception: unique purple */ } &.custom { background: rgba(59, 130, 246, 0.15); border: 1px solid rgba(59, 130, 246, 0.4); - color: #60a5fa; + color: var(--c-role-custom); } .role-name { @@ -427,7 +427,7 @@ } .remove-btn:hover { - color: #ef4444; + color: var(--red-300); } } @@ -438,8 +438,8 @@ background: var(--color-bg-secondary, #1a1a1a); border: 1px solid var(--color-border, #333333); border-radius: 8px; - box-shadow: 0 10px 38px -10px rgba(0, 0, 0, 0.5), 0 10px 20px -15px rgba(0, 0, 0, 0.4); - z-index: 1000; + box-shadow: var(--shadow-xl); + z-index: var(--z-toast); overflow: hidden; .popover-header { @@ -448,13 +448,13 @@ h4 { margin: 0 0 0.25rem 0; - font-size: 1rem; + font-size: var(--font-size-lg); font-weight: 600; } .description { margin: 0; - font-size: 0.85rem; + font-size: var(--font-size-md); color: var(--color-text-secondary, #888888); } } @@ -464,7 +464,7 @@ h5 { margin: 0 0 0.75rem 0; - font-size: 0.85rem; + font-size: var(--font-size-md); font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; @@ -501,20 +501,20 @@ align-items: start; gap: 0.5rem; padding: 0.4rem 0; - font-size: 0.85rem; + font-size: var(--font-size-md); .check-icon { flex-shrink: 0; width: 14px; height: 14px; margin-top: 2px; - color: #10b981; + color: var(--green-400); } code { flex: 1; font-family: 'Courier New', monospace; - font-size: 0.8rem; + font-size: var(--font-size-sm); color: var(--color-text-primary, #e0e0e0); word-break: break-all; } diff --git a/packages/ui-base/src/lib/preview/preview.css b/packages/ui-base/src/lib/preview/preview.css index fde826f0..aee7f1a3 100644 --- a/packages/ui-base/src/lib/preview/preview.css +++ b/packages/ui-base/src/lib/preview/preview.css @@ -1,12 +1,12 @@ .preview { - border: 1px solid var(--c-pink-4); + border: 1px solid var(--primary-600); width: 200px; border-radius: 3px; } .preview-header { padding: 2px 6px; - background: var(--c-blue-gray-1); + background: var(--surface-700); display: flex; align-items: center; justify-content: space-between; @@ -43,16 +43,16 @@ .preview-header .info p { margin: 0; padding: 0; - font-size: 8px; - color: var(--c-pink-2); + font-size: var(--font-size-2xs); + color: var(--primary-200); } .preview-header .info span { - background: var(--c-green-4); + background: var(--green-600); border-radius: 1.5px; padding: 2px 4px; - font-size: 5px; - color: var(--c-white-1); + font-size: var(--font-size-2xs); + color: var(--white); } .preview textarea { @@ -62,9 +62,9 @@ background: none; border: none; outline: none; - color: var(--c-white-1); + color: var(--white); padding: 5px 10px; - font-size: 7px; + font-size: var(--font-size-2xs); } .preview .accordion { @@ -77,5 +77,5 @@ .preview hr { margin: 0; padding: 0; - border-color: var(--c-pink-4); + border-color: var(--primary-600); } diff --git a/packages/ui-base/src/lib/role-editor/role-editor.scss b/packages/ui-base/src/lib/role-editor/role-editor.scss index 3e263587..cf925910 100644 --- a/packages/ui-base/src/lib/role-editor/role-editor.scss +++ b/packages/ui-base/src/lib/role-editor/role-editor.scss @@ -2,7 +2,7 @@ display: flex; height: 100%; gap: 20px; - background: var(--c-black-2); + background: var(--surface-900); border-radius: 8px; overflow: hidden; @@ -19,7 +19,7 @@ justify-content: space-between; align-items: center; padding: 15px 20px; - border-bottom: 1px solid var(--c-black-3); + border-bottom: 1px solid var(--black-600); & > div { display: flex; @@ -28,14 +28,14 @@ } .panel-title { - font-size: 16px; + font-size: var(--font-size-lg); font-weight: 600; - color: var(--c-white-1); + color: var(--white); } .separator { width: 1px; - background: var(--c-black-3); + background: var(--black-600); } .ScrollAreaRoot { @@ -60,24 +60,24 @@ justify-content: space-between; align-items: center; padding: 12px 15px; - background: var(--c-black-1); - border: 1px solid var(--c-black-3); + background: var(--surface-800); + border: 1px solid var(--black-600); border-radius: 6px; cursor: pointer; transition: all 0.2s; &:hover { - background: var(--c-black-3); - border-color: var(--c-blue-1); + background: var(--black-600); + border-color: var(--cyan-100); } &.system-role { - background: var(--c-black-0); + background: var(--surface-700); cursor: default; &:hover { - background: var(--c-black-0); - border-color: var(--c-black-3); + background: var(--surface-700); + border-color: var(--black-600); } } } @@ -87,9 +87,9 @@ } .role-name { - font-size: 14px; + font-size: var(--font-size-md); font-weight: 600; - color: var(--c-white-1); + color: var(--white); margin-bottom: 4px; display: flex; align-items: center; @@ -97,24 +97,24 @@ } .badge { - font-size: 10px; + font-size: var(--font-size-2xs); padding: 2px 6px; - background: var(--c-blue-1); - color: var(--c-white-1); + background: var(--cyan-100); + color: var(--white); border-radius: 3px; font-weight: 500; } .role-scope { - font-size: 11px; - color: var(--c-white-3); + font-size: var(--font-size-xs); + color: var(--color-text-faint); text-transform: uppercase; margin-bottom: 2px; } .role-permissions-count { - font-size: 12px; - color: var(--c-white-2); + font-size: var(--font-size-sm); + color: var(--color-text-muted); } .role-actions { @@ -134,23 +134,23 @@ gap: 8px; label { - font-size: 13px; + font-size: var(--font-size-md); font-weight: 600; - color: var(--c-white-2); + color: var(--color-text-muted); } input, select { padding: 8px 12px; - background: var(--c-black-1); - border: 1px solid var(--c-black-3); + background: var(--surface-800); + border: 1px solid var(--black-600); border-radius: 4px; - color: var(--c-white-1); - font-size: 13px; + color: var(--white); + font-size: var(--font-size-md); &:focus { outline: none; - border-color: var(--c-blue-1); + border-color: var(--cyan-100); } } @@ -166,16 +166,16 @@ input { flex: 1; padding: 8px 12px; - background: var(--c-black-1); - border: 1px solid var(--c-black-3); + background: var(--surface-800); + border: 1px solid var(--black-600); border-radius: 4px; - color: var(--c-white-1); - font-size: 13px; + color: var(--white); + font-size: var(--font-size-md); font-family: 'Courier New', monospace; &:focus { outline: none; - border-color: var(--c-blue-1); + border-color: var(--cyan-100); } } } @@ -192,22 +192,22 @@ align-items: center; gap: 6px; padding: 6px 10px; - background: var(--c-black-1); - border: 1px solid var(--c-black-3); + background: var(--surface-800); + border: 1px solid var(--black-600); border-radius: 4px; - font-size: 12px; + font-size: var(--font-size-sm); code { - color: var(--c-blue-2); + color: var(--cyan-300); font-family: 'Courier New', monospace; } .remove-btn { background: transparent; border: none; - color: var(--c-red-1); + color: var(--red-300); cursor: pointer; - font-size: 18px; + font-size: var(--font-size-xl); line-height: 1; padding: 0; width: 16px; @@ -217,7 +217,7 @@ justify-content: center; &:hover { - color: var(--c-red-2); + color: var(--red-400); } } } diff --git a/packages/ui-base/src/lib/sidebar/sidebar.css b/packages/ui-base/src/lib/sidebar/sidebar.css index f5bfb89e..ea82a212 100644 --- a/packages/ui-base/src/lib/sidebar/sidebar.css +++ b/packages/ui-base/src/lib/sidebar/sidebar.css @@ -1,5 +1,5 @@ aside { - background: var(--c-blue-gray-0); + background: var(--surface-700); border-radius: 8px; width: fit-content; height: auto; @@ -32,7 +32,7 @@ aside ul:before { position: absolute; right: 0; top: 0; - background: var(--c-white-1); + background: var(--white); width: 2px; height: 24px; border-radius: 8px; diff --git a/packages/ui-base/src/lib/tags/tags.scss b/packages/ui-base/src/lib/tags/tags.scss new file mode 100644 index 00000000..e5bf37d1 --- /dev/null +++ b/packages/ui-base/src/lib/tags/tags.scss @@ -0,0 +1,133 @@ +// Tags component styles + +.tag-input { + text-transform: uppercase; + background-color: var(--surface-700); + border-radius: 4px; + padding: 4px 8px; + font-size: var(--font-size-2xs); + font-weight: var(--font-weight-medium); + line-height: 14px; + height: 22px; + outline: none; + border: 1px solid var(--surface-300); +} + +.tag-label { + text-transform: uppercase; + background-color: var(--surface-700); + border-radius: 4px; + padding: 4px 8px; + font-size: var(--font-size-2xs); + font-weight: var(--font-weight-medium); + line-height: 14px; + height: 22px; + display: flex; + align-items: center; + + &.tag-editable { + cursor: text; + transition: background-color 0.15s; + + &:hover { + background-color: var(--surface-700); + } + } +} + +.tags-container { + position: relative; + z-index: var(--z-base); + width: 80%; + display: flex; + flex-wrap: wrap; + align-items: center; + margin-top: 16px; + gap: 4px; +} + +.tag-appear { + transition: opacity 0.3s; + opacity: 1; +} + +.tag-toggle-btn { + height: 20px; + width: 20px; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + transition: all 0.15s; + cursor: pointer; + + &:hover { + background-color: var(--alpha-white-10); + } + + &.has-others { + border: 1px solid var(--surface-300); + + .tag-toggle-text { + color: var(--surface-300); + } + + &.is-opened { + border-color: var(--primary-600); + } + } + + &.no-others { + border: 1px solid var(--surface-400); + + .tag-toggle-text { + color: var(--surface-400); + } + } +} + +.tag-toggle-text { + margin-left: 0.5px; + margin-top: 0.5px; + line-height: 0%; + + &.collapsed { + margin-bottom: 8px; + } +} + +.tags-dropdown { + position: absolute; + padding: 8px; + flex-wrap: wrap; + top: 100%; + max-height: 200px; + overflow-y: auto; + display: flex; + right: 0; + width: 100%; + background-color: var(--surface-700); + z-index: var(--z-base); + border-radius: 4px; + border: 1px solid var(--primary-600); + gap: 5px; +} + +.tag-add-btn { + border: 1px solid var(--surface-400); + height: 20px; + width: 20px; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + color: var(--surface-400); + text-align: center; + transition: all 0.15s; + cursor: pointer; + + &:hover { + background-color: var(--alpha-white-10); + } +} diff --git a/packages/ui-base/src/lib/tags/tags.tsx b/packages/ui-base/src/lib/tags/tags.tsx index 6dbbcf85..22e1ef6e 100644 --- a/packages/ui-base/src/lib/tags/tags.tsx +++ b/packages/ui-base/src/lib/tags/tags.tsx @@ -1,6 +1,8 @@ import { useEffect, useRef, useState } from 'react'; import { randomColor } from '../css-utils/css-utils'; +import './tags.scss'; + export type Tag = { text: string; color: string }; export const Tags = ({ @@ -54,7 +56,7 @@ export const Tags = ({ onChange={(e) => setEditedText(e.target.value)} onBlur={handleBlur} onKeyDown={handleKeyDown} - className="uppercase bg-[#252546] rounded-[4px] px-2 py-1 text-[10px] font-medium leading-[14px] h-[22px] outline-none border border-[#A998DA]" + className="tag-input" style={{ color }} /> ); @@ -62,9 +64,7 @@ export const Tags = ({ return ( onEdit && setIsEditing(true)} > @@ -129,17 +129,11 @@ export const TagsBar = ({ tags = [], addTag, editTag }: TagsBarProps) => { return ( <> -
            +
            {visibleTags.map((tag: Tag, visibleIndex: number) => { const originalIndex = tags.findIndex((t) => t === tag); return ( - + { })}
            {otherTagsOpened && ( -
            +
            {otherTags.map((tag: Tag, otherIndex: number) => { const originalIndex = tags.findIndex((t) => t === tag); return ( @@ -198,7 +190,7 @@ export const TagsBar = ({ tags = [], addTag, editTag }: TagsBarProps) => { onClick={(e) => { addTag?.({ text: `tag-${tags.length}`, color: randomColor() }); }} - className={`border h-5 w-5 rounded-[4px] flex items-center justify-center border-[#50506C] text-[#50506C] text-center hover:bg-white/10 transition-all cursor-pointer`} + className="tag-add-btn" > + diff --git a/packages/ui-base/src/lib/users/user-bubble.scss b/packages/ui-base/src/lib/users/user-bubble.scss new file mode 100644 index 00000000..ededcf1a --- /dev/null +++ b/packages/ui-base/src/lib/users/user-bubble.scss @@ -0,0 +1,152 @@ +// UserBubble component styles + +.user-bubble-root { + position: relative; + display: flex; + + &.horizontal { + align-items: center; + } + + &.vertical { + flex-direction: column; + align-items: flex-start; + } +} + +.live-indicator { + position: absolute; + display: flex; + align-items: center; + height: fit-content; + line-height: 10px; + font-size: var(--font-size-sm); + color: var(--c-live-indicator); + width: fit-content; + gap: 2px; + z-index: var(--z-dropdown); + + &.live-small { + bottom: 2px; + } + + &.live-large { + bottom: 4px; + } +} + +.live-dot { + border-radius: 9999px; + height: 8px; + width: 8px; + margin-bottom: 2px; +} + +.bubble-avatar-wrapper { + position: absolute; + border-radius: 9999px; +} + +.plus-n-wrapper { + position: absolute; + border-radius: 9999px; + display: flex; + align-items: center; + justify-content: center; + + &.horizontal { + left: 0; + } + + &.vertical { + top: 0; + } +} + +.plus-n-ping { + position: absolute; + z-index: var(--z-behind); + height: 65%; + width: 65%; + background-color: var(--c-live-ping); + transform-origin: center; + border-radius: 9999px; + animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite; +} + +.plus-n-circle { + position: relative; + display: flex; + align-items: center; + justify-content: center; + z-index: var(--z-base); + height: 100%; + width: 100%; + border-radius: 9999px; + background-color: var(--c-plus-n-bg); + border: 1px solid var(--c-plus-n-border); + + &.live-border { + border-color: var(--c-live-ping); + filter: drop-shadow(0px 0px 4px var(--c-live-ping)); + } + + &.size-small { + max-height: 38px; + max-width: 38px; + min-height: 38px; + min-width: 38px; + } + + &.size-large { + max-height: 48px; + max-width: 48px; + min-height: 48px; + min-width: 48px; + } +} + +// Avatar styles for users.tsx +.avatar-live-ping { + position: absolute; + z-index: var(--z-behind); + height: 31px; + width: 31px; + transform-origin: center; + border-radius: 9999px; + animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite; + + &.ping-live { + background-color: var(--c-live-ping); + } + + &.ping-host { + background-color: var(--c-host-ping); + } +} + +.avatar-host-label { + position: absolute; + opacity: 0; + transition: all 0.15s; + left: 50%; + transform: translateX(-50%); + font-size: var(--font-size-lg); + color: var(--c-host-label); + z-index: var(--z-behind); + padding: 2px; + border-radius: 4px; + border: 1px solid var(--c-host-ping); +} + +.avatar-host-text { + font-weight: var(--font-weight-bold); +} + +@keyframes ping { + 75%, + 100% { + transform: scale(2); + opacity: 0; + } +} diff --git a/packages/ui-base/src/lib/users/user-bubble.tsx b/packages/ui-base/src/lib/users/user-bubble.tsx index 2269d162..f2daa18c 100644 --- a/packages/ui-base/src/lib/users/user-bubble.tsx +++ b/packages/ui-base/src/lib/users/user-bubble.tsx @@ -1,6 +1,8 @@ import { TF_User } from '@holistix-forge/types'; import { UserAvatar } from './users'; +import './user-bubble.scss'; + export interface UserBubbleProps { direction: 'horizontal' | 'vertical'; size: 'small' | 'large'; @@ -29,25 +31,28 @@ export const UserBubble = ({ return (
            {live && !displayPlusN && (
            -
            +
            Live
            )} @@ -55,7 +60,7 @@ export const UserBubble = ({ {displayedUsers.map((u, index: number) => (
            - {live && ( -
            - )} + {live &&
            }
            +{users.length - 2} {live && (
            -
            +
            Live
            )} diff --git a/packages/ui-base/src/lib/users/users.scss b/packages/ui-base/src/lib/users/users.scss index db304f73..c81188b3 100644 --- a/packages/ui-base/src/lib/users/users.scss +++ b/packages/ui-base/src/lib/users/users.scss @@ -1,3 +1,20 @@ +:root { + // --- User Bubble / Avatar domain tokens --- + --c-live-indicator: #f7c8de; + --c-live-ping: #f72585; + --c-plus-n-bg: #3c2986; + --c-plus-n-border: #451d5f; + --c-host-ping: #8498ff; + --c-host-label: #1032e3; + + // --- Role Badge Colors --- + --c-role-custom: #60a5fa; + + // --- Brand Colors --- + --c-brand-linkedin: #0a66c2; + --c-brand-highlight-blue: #007acc; +} + .AvatarRoot { display: inline-flex; align-items: center; @@ -10,6 +27,11 @@ border-radius: 100%; margin-right: 10px; + &:hover .avatar-host-label { + transform: translateX(-50%) translateY(-190%); + opacity: 1; + } + .AvatarImage { width: 100%; height: 100%; @@ -24,9 +46,9 @@ border-radius: 100%; align-items: center; justify-content: center; - background-color: var(--c-gray-4); - color: var(--c-gray-8); - font-size: 15px; + background-color: var(--neutral-4); + color: var(--neutral-8); + font-size: var(--font-size-lg); line-height: 1; font-weight: 500; text-transform: uppercase; @@ -46,7 +68,7 @@ &.gitlab { background-color: var(--user-color); border-radius: 50%; - color: var(--color-background); + color: var(--color-bg-app); } &.linkedin { diff --git a/packages/ui-base/src/lib/users/users.tsx b/packages/ui-base/src/lib/users/users.tsx index 3ebbe89c..3f38b860 100644 --- a/packages/ui-base/src/lib/users/users.tsx +++ b/packages/ui-base/src/lib/users/users.tsx @@ -5,6 +5,7 @@ import { icons } from '../assets/icons'; import { CSSProperties } from 'react'; import './users.scss'; +import './user-bubble.scss'; /** * split the username to separate provider and id @@ -46,29 +47,43 @@ export const UserAvatar = ({ size === 'small' ? '38px' : size === 'large' ? '48px' : undefined, }; - const className = ` - ${removeMarginRight && '!mr-0'} - AvatarRoot - ${host ? 'border border-[#8498FF] drop-shadow-[0px_0px_4px_#0550B3]' : ''} - ${live ? 'border border-[#F72585] drop-shadow-[0px_0px_4px_#F72585]' : ''} - rounded-full group/avatar bg-black relative`; + const hostStyle = host + ? { + border: '1px solid #8498FF', + filter: 'drop-shadow(0px 0px 4px #0550B3)', + } + : {}; + const liveStyle = live + ? { + border: '1px solid #F72585', + filter: 'drop-shadow(0px 0px 4px #F72585)', + } + : {}; + const mrStyle = removeMarginRight ? { marginRight: 0 } : {}; + + const avatarStyle = { + ...hostStyle, + ...liveStyle, + ...mrStyle, + borderRadius: '9999px', + backgroundColor: 'var(--surface-700)', + position: 'relative' as const, + }; + + const className = 'AvatarRoot'; return ( - {live && ( -
            - )} - {host && ( -
            - )} + {live &&
            } + {host &&
            } {host && ( - - host + + host )} diff --git a/packages/ui-views/.storybook/preview.ts b/packages/ui-views/.storybook/preview.ts index f2842c8a..b9ec5fd6 100644 --- a/packages/ui-views/.storybook/preview.ts +++ b/packages/ui-views/.storybook/preview.ts @@ -18,7 +18,7 @@ const preview: Preview = { values: [ { name: 'dark', - value: 'var(--color-background)', + value: 'var(--color-bg-app)', }, ], }, diff --git a/packages/ui-views/.storybook/test-runner.js b/packages/ui-views/.storybook/test-runner.js new file mode 100644 index 00000000..4b458ca7 --- /dev/null +++ b/packages/ui-views/.storybook/test-runner.js @@ -0,0 +1,108 @@ +const path = require('path'); +const { toMatchImageSnapshot } = require('jest-image-snapshot'); + +const PKG_ROOT = path.resolve(__dirname, '..'); + +const SNAPSHOT_OPTS = { + failureThreshold: 0.002, // 0.2% pixel difference allowed + failureThresholdType: 'percent', + customSnapshotsDir: + process.env['SNAPSHOT_DIR'] || path.join(PKG_ROOT, '__screenshots__'), + customDiffDir: + process.env['DIFF_DIR'] || path.join(PKG_ROOT, '__diff_output__'), +}; + +/** + * Wait for the page to be ready without relying on networkidle + * (which times out when stories load external resources like avatars). + */ +async function waitUntilReady(page) { + await page.waitForLoadState('domcontentloaded'); + await page.waitForLoadState('load'); + await page.evaluate(() => document.fonts.ready); + // Small settle time for React re-renders + await page.waitForTimeout(300); +} + +module.exports = { + setup() { + expect.extend({ toMatchImageSnapshot }); + }, + + async preVisit(page) { + // Block external image requests to prevent navigation context destruction. + // Stories load random avatars from pravatar.cc which cause flaky test failures. + await page.route('**/*pravatar*/**', (route) => route.abort()); + await page.route('**/*.pravatar.*/**', (route) => route.abort()); + }, + + async postVisit(page, context) { + // Disable CSS animations/transitions to avoid flaky diffs + await page.addStyleTag({ + content: ` + *, *::before, *::after { + animation-duration: 0s !important; + animation-delay: 0s !important; + transition-duration: 0s !important; + transition-delay: 0s !important; + } + `, + }); + + // Wait for page to be ready (DOM, resources, fonts — skip networkidle) + await waitUntilReady(page); + + const root = page.locator('#storybook-root'); + + try { + await root.waitFor({ state: 'visible', timeout: 5000 }); + } catch { + // Root not visible — fall back to full page screenshot + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + // Check if a Radix dialog portal is present (renders outside #storybook-root) + const dialogContent = page.locator('[data-radix-portal] [role="dialog"]'); + const hasDialog = await dialogContent.count().then((c) => c > 0); + + if (hasDialog) { + // Screenshot the dialog content instead of the empty root + try { + await dialogContent + .first() + .waitFor({ state: 'visible', timeout: 3000 }); + const screenshot = await dialogContent.first().screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } catch { + // Dialog not visible, fall through to page screenshot + } + } + + // Check if root content is too small (likely clipped by overflow) + const box = await root.boundingBox(); + if (box && (box.width < 10 || box.height < 10)) { + // Component is too small to be meaningful — screenshot full page + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + return; + } + + const screenshot = await root.screenshot(); + expect(screenshot).toMatchImageSnapshot({ + ...SNAPSHOT_OPTS, + customSnapshotIdentifier: context.id, + }); + }, +}; diff --git a/packages/ui-views/__screenshots__/forms-account-login--normal.png b/packages/ui-views/__screenshots__/forms-account-login--normal.png new file mode 100644 index 00000000..4e40b20f Binary files /dev/null and b/packages/ui-views/__screenshots__/forms-account-login--normal.png differ diff --git a/packages/ui-views/__screenshots__/forms-account-magiclink--reset-password.png b/packages/ui-views/__screenshots__/forms-account-magiclink--reset-password.png new file mode 100644 index 00000000..ef4583d6 Binary files /dev/null and b/packages/ui-views/__screenshots__/forms-account-magiclink--reset-password.png differ diff --git a/packages/ui-views/__screenshots__/forms-account-magiclink--validate-email.png b/packages/ui-views/__screenshots__/forms-account-magiclink--validate-email.png new file mode 100644 index 00000000..34053fd8 Binary files /dev/null and b/packages/ui-views/__screenshots__/forms-account-magiclink--validate-email.png differ diff --git a/packages/ui-views/__screenshots__/forms-account-newpassword--normal.png b/packages/ui-views/__screenshots__/forms-account-newpassword--normal.png new file mode 100644 index 00000000..d7da921c Binary files /dev/null and b/packages/ui-views/__screenshots__/forms-account-newpassword--normal.png differ diff --git a/packages/ui-views/__screenshots__/forms-account-signup--normal.png b/packages/ui-views/__screenshots__/forms-account-signup--normal.png new file mode 100644 index 00000000..1339f24d Binary files /dev/null and b/packages/ui-views/__screenshots__/forms-account-signup--normal.png differ diff --git a/packages/ui-views/__screenshots__/forms-account-totp--active.png b/packages/ui-views/__screenshots__/forms-account-totp--active.png new file mode 100644 index 00000000..2ebb78f6 Binary files /dev/null and b/packages/ui-views/__screenshots__/forms-account-totp--active.png differ diff --git a/packages/ui-views/__screenshots__/forms-account-totp--inactive.png b/packages/ui-views/__screenshots__/forms-account-totp--inactive.png new file mode 100644 index 00000000..5a8f5563 Binary files /dev/null and b/packages/ui-views/__screenshots__/forms-account-totp--inactive.png differ diff --git a/packages/ui-views/__screenshots__/forms-account-totplogin--normal.png b/packages/ui-views/__screenshots__/forms-account-totplogin--normal.png new file mode 100644 index 00000000..0719ae1f Binary files /dev/null and b/packages/ui-views/__screenshots__/forms-account-totplogin--normal.png differ diff --git a/packages/ui-views/__screenshots__/forms-mountvolume--normal.png b/packages/ui-views/__screenshots__/forms-mountvolume--normal.png new file mode 100644 index 00000000..4edcd3ff Binary files /dev/null and b/packages/ui-views/__screenshots__/forms-mountvolume--normal.png differ diff --git a/packages/ui-views/__screenshots__/forms-newproject--normal.png b/packages/ui-views/__screenshots__/forms-newproject--normal.png new file mode 100644 index 00000000..b72f12a2 Binary files /dev/null and b/packages/ui-views/__screenshots__/forms-newproject--normal.png differ diff --git a/packages/ui-views/__screenshots__/mvp-assets-resource-selection--closed.png b/packages/ui-views/__screenshots__/mvp-assets-resource-selection--closed.png new file mode 100644 index 00000000..4a3149a3 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-assets-resource-selection--closed.png differ diff --git a/packages/ui-views/__screenshots__/mvp-assets-resource-selection--open.png b/packages/ui-views/__screenshots__/mvp-assets-resource-selection--open.png new file mode 100644 index 00000000..329c55b8 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-assets-resource-selection--open.png differ diff --git a/packages/ui-views/__screenshots__/mvp-assets-user-display--default.png b/packages/ui-views/__screenshots__/mvp-assets-user-display--default.png new file mode 100644 index 00000000..b7fc601d Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-assets-user-display--default.png differ diff --git a/packages/ui-views/__screenshots__/mvp-assets-user-display-item--default.png b/packages/ui-views/__screenshots__/mvp-assets-user-display-item--default.png new file mode 100644 index 00000000..42d6022c Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-assets-user-display-item--default.png differ diff --git a/packages/ui-views/__screenshots__/mvp-assets-wrapper--default.png b/packages/ui-views/__screenshots__/mvp-assets-wrapper--default.png new file mode 100644 index 00000000..518cce0a Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-assets-wrapper--default.png differ diff --git a/packages/ui-views/__screenshots__/mvp-assets-wrapper--optionnal-tag.png b/packages/ui-views/__screenshots__/mvp-assets-wrapper--optionnal-tag.png new file mode 100644 index 00000000..b43b6564 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-assets-wrapper--optionnal-tag.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-filter-box--group.png b/packages/ui-views/__screenshots__/mvp-components-filter-box--group.png new file mode 100644 index 00000000..b03a6a6b Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-filter-box--group.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-filter-box--role.png b/packages/ui-views/__screenshots__/mvp-components-filter-box--role.png new file mode 100644 index 00000000..b0918634 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-filter-box--role.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-header--logged-in.png b/packages/ui-views/__screenshots__/mvp-components-header--logged-in.png new file mode 100644 index 00000000..f8b4c2c4 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-header--logged-in.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-header--logout.png b/packages/ui-views/__screenshots__/mvp-components-header--logout.png new file mode 100644 index 00000000..4db694d9 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-header--logout.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-header--one-other-user.png b/packages/ui-views/__screenshots__/mvp-components-header--one-other-user.png new file mode 100644 index 00000000..075b16d8 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-header--one-other-user.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-notebook-card--default.png b/packages/ui-views/__screenshots__/mvp-components-notebook-card--default.png new file mode 100644 index 00000000..ebc9d05f Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-notebook-card--default.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-notebook-card--grouped.png b/packages/ui-views/__screenshots__/mvp-components-notebook-card--grouped.png new file mode 100644 index 00000000..f819ce34 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-notebook-card--grouped.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-notebook-card--running-hosted-live.png b/packages/ui-views/__screenshots__/mvp-components-notebook-card--running-hosted-live.png new file mode 100644 index 00000000..d2f89768 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-notebook-card--running-hosted-live.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-notebook-card--running-hosted.png b/packages/ui-views/__screenshots__/mvp-components-notebook-card--running-hosted.png new file mode 100644 index 00000000..49330619 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-notebook-card--running-hosted.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-notebook-card--running-live.png b/packages/ui-views/__screenshots__/mvp-components-notebook-card--running-live.png new file mode 100644 index 00000000..852f787c Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-notebook-card--running-live.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-notebook-card--stopped.png b/packages/ui-views/__screenshots__/mvp-components-notebook-card--stopped.png new file mode 100644 index 00000000..5107f05a Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-notebook-card--stopped.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-notebook-card--tags.png b/packages/ui-views/__screenshots__/mvp-components-notebook-card--tags.png new file mode 100644 index 00000000..aabcbef4 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-notebook-card--tags.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-resource-bar--default.png b/packages/ui-views/__screenshots__/mvp-components-resource-bar--default.png new file mode 100644 index 00000000..d6b3e629 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-resource-bar--default.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-resource-description--default.png b/packages/ui-views/__screenshots__/mvp-components-resource-description--default.png new file mode 100644 index 00000000..14acf25c Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-resource-description--default.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-resource-description--editing.png b/packages/ui-views/__screenshots__/mvp-components-resource-description--editing.png new file mode 100644 index 00000000..a80b1ba2 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-resource-description--editing.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-rules--group.png b/packages/ui-views/__screenshots__/mvp-components-rules--group.png new file mode 100644 index 00000000..6ff63455 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-rules--group.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-rules--role.png b/packages/ui-views/__screenshots__/mvp-components-rules--role.png new file mode 100644 index 00000000..6ff63455 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-rules--role.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-summary-accesses--groups.png b/packages/ui-views/__screenshots__/mvp-components-summary-accesses--groups.png new file mode 100644 index 00000000..e32179dc Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-summary-accesses--groups.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-summary-accesses--roles.png b/packages/ui-views/__screenshots__/mvp-components-summary-accesses--roles.png new file mode 100644 index 00000000..0524c0b5 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-summary-accesses--roles.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-summary-accesses--users.png b/packages/ui-views/__screenshots__/mvp-components-summary-accesses--users.png new file mode 100644 index 00000000..a576abef Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-summary-accesses--users.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-tabs--default.png b/packages/ui-views/__screenshots__/mvp-components-tabs--default.png new file mode 100644 index 00000000..fc9807b9 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-tabs--default.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-user-informations--default.png b/packages/ui-views/__screenshots__/mvp-components-user-informations--default.png new file mode 100644 index 00000000..dd6761c5 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-user-informations--default.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-user-informations--edition.png b/packages/ui-views/__screenshots__/mvp-components-user-informations--edition.png new file mode 100644 index 00000000..d69e0dc5 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-user-informations--edition.png differ diff --git a/packages/ui-views/__screenshots__/mvp-components-user-list--default.png b/packages/ui-views/__screenshots__/mvp-components-user-list--default.png new file mode 100644 index 00000000..cda2686f Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-components-user-list--default.png differ diff --git a/packages/ui-views/__screenshots__/mvp-resource-notebook-notebook-view--host.png b/packages/ui-views/__screenshots__/mvp-resource-notebook-notebook-view--host.png new file mode 100644 index 00000000..3618b0db Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-resource-notebook-notebook-view--host.png differ diff --git a/packages/ui-views/__screenshots__/mvp-resource-notebook-notebook-view--load.png b/packages/ui-views/__screenshots__/mvp-resource-notebook-notebook-view--load.png new file mode 100644 index 00000000..3618b0db Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-resource-notebook-notebook-view--load.png differ diff --git a/packages/ui-views/__screenshots__/mvp-resource-notebook-notebook-view--running.png b/packages/ui-views/__screenshots__/mvp-resource-notebook-notebook-view--running.png new file mode 100644 index 00000000..3618b0db Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-resource-notebook-notebook-view--running.png differ diff --git a/packages/ui-views/__screenshots__/mvp-resource-notebook-notebook-view--stop.png b/packages/ui-views/__screenshots__/mvp-resource-notebook-notebook-view--stop.png new file mode 100644 index 00000000..8de74cbb Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-resource-notebook-notebook-view--stop.png differ diff --git a/packages/ui-views/__screenshots__/mvp-view-accesses--normal.png b/packages/ui-views/__screenshots__/mvp-view-accesses--normal.png new file mode 100644 index 00000000..cff9720f Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-view-accesses--normal.png differ diff --git a/packages/ui-views/__screenshots__/mvp-view-notebook-view--normal.png b/packages/ui-views/__screenshots__/mvp-view-notebook-view--normal.png new file mode 100644 index 00000000..395a7af8 Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-view-notebook-view--normal.png differ diff --git a/packages/ui-views/__screenshots__/mvp-view-notebook-view--update-description.png b/packages/ui-views/__screenshots__/mvp-view-notebook-view--update-description.png new file mode 100644 index 00000000..04c24d4f Binary files /dev/null and b/packages/ui-views/__screenshots__/mvp-view-notebook-view--update-description.png differ diff --git a/packages/ui-views/src/lib/form/form-login/login.scss b/packages/ui-views/src/lib/form/form-login/login.scss index 9f4055a4..c1564992 100644 --- a/packages/ui-views/src/lib/form/form-login/login.scss +++ b/packages/ui-views/src/lib/form/form-login/login.scss @@ -14,7 +14,7 @@ } span { - font-size: 12px; + font-size: var(--font-size-sm); } .provider-logo { @@ -23,13 +23,13 @@ margin: auto; display: inline; vertical-align: middle; - fill: white; + fill: var(--white); &.gitlab { - background-color: var(--c-white-1); + background-color: var(--white); border-radius: 50%; - color: var(--color-background); - fill: var(--color-background); + color: var(--color-bg-app); + fill: var(--color-bg-app); } } } @@ -39,6 +39,6 @@ .providers { padding: 15px; margin: 25px; - box-shadow: 0 0 2px 0 var(--ca-white-3); + box-shadow: var(--shadow-form-glow); border-radius: var(--form-input-border-radius); } diff --git a/packages/ui-views/src/lib/index.scss b/packages/ui-views/src/lib/index.scss index d5213950..fce9114c 100644 --- a/packages/ui-views/src/lib/index.scss +++ b/packages/ui-views/src/lib/index.scss @@ -1,9 +1,5 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - .app-header { .AvatarRoot { - border: solid 1.5px var(--c-pink-2); + border: solid 1.5px var(--primary-200); } } diff --git a/packages/ui-views/src/lib/mvp-ui-view/assets/resource-selection.stories.tsx b/packages/ui-views/src/lib/mvp-ui-view/assets/resource-selection.stories.tsx index 3d9ecc32..4249cb9c 100644 --- a/packages/ui-views/src/lib/mvp-ui-view/assets/resource-selection.stories.tsx +++ b/packages/ui-views/src/lib/mvp-ui-view/assets/resource-selection.stories.tsx @@ -5,6 +5,13 @@ import { ResourceSelection } from './resource-selection'; const meta = { title: 'Mvp/Assets/resource-selection', component: ResourceSelection, + decorators: [ + (Story) => ( +
            + +
            + ), + ], parameters: { layout: 'centered', }, diff --git a/packages/ui-views/src/lib/mvp-ui-view/assets/resource-selection.tsx b/packages/ui-views/src/lib/mvp-ui-view/assets/resource-selection.tsx index 0ec908c3..43aa774a 100644 --- a/packages/ui-views/src/lib/mvp-ui-view/assets/resource-selection.tsx +++ b/packages/ui-views/src/lib/mvp-ui-view/assets/resource-selection.tsx @@ -68,23 +68,46 @@ export const ResourceSelection = ({ isOpen }: ResourceButtonsProps) => { return (
            setIsMenuOpen(!isMenuOpen)} - style={{ backgroundColor: ressources[selected].color }} - className={`cursor-pointer flex flex-row space-x-4 text-white h-[72px] min-w-[388px] ${ - isMenuOpen ? 'rounded-b-none' : '' - } rounded-lg pl-[38px] uppercase relative text-[20px] py-[10px] items-center justify-between`} + style={{ + backgroundColor: ressources[selected].color, + height: '72px', + minWidth: '388px', + borderRadius: isMenuOpen ? '8px 8px 0 0' : '8px', + paddingLeft: '38px', + textTransform: 'uppercase', + fontSize: '20px', + padding: '10px 0 10px 38px', + color: 'white', + display: 'flex', + flexDirection: 'row', + gap: '16px', + alignItems: 'center', + justifyContent: 'space-between', + cursor: 'pointer', + position: 'relative', + }} > {ressources[selected].name}
            -
            +
            {isMenuOpen && (
            { setIsMenuOpen(false); }} @@ -95,8 +118,18 @@ export const ResourceSelection = ({ isOpen }: ResourceButtonsProps) => {
            setSelected(ressource.id)} - className={`cursor-pointer hover:brightness-75 transition-all flex text-white h-[72px] w-full pl-[38px] uppercase text-[20px] py-[10px] items-center justify-between`} - style={{ backgroundColor: ressource.color }} + className="cursor-pointer flex items-center justify-between" + style={{ + transition: 'all 0.2s', + color: 'white', + height: '72px', + width: '100%', + paddingLeft: '38px', + textTransform: 'uppercase', + fontSize: '20px', + padding: '10px 0 10px 38px', + backgroundColor: ressource.color, + }} > {ressource.name}
            diff --git a/packages/ui-views/src/lib/mvp-ui-view/assets/user-display-item.tsx b/packages/ui-views/src/lib/mvp-ui-view/assets/user-display-item.tsx index afcf6306..bad5b820 100644 --- a/packages/ui-views/src/lib/mvp-ui-view/assets/user-display-item.tsx +++ b/packages/ui-views/src/lib/mvp-ui-view/assets/user-display-item.tsx @@ -26,16 +26,24 @@ export const UserDisplayItem = ({ removeUser, }: UserDisplayItemProps) => { return ( -
            +

            @@ -43,43 +51,54 @@ export const UserDisplayItem = ({

            {role}
            -

            {mail ? mail : null}

            +

            + {mail ? mail : null} +

            {buttons?.settings && (
            - +
            )} {buttons?.remove && (
            - +
            )} {buttons?.filter && (
            - +
            )} {buttons?.delete && (
            - +
            )}
            diff --git a/packages/ui-views/src/lib/mvp-ui-view/assets/user-display.tsx b/packages/ui-views/src/lib/mvp-ui-view/assets/user-display.tsx index b8096e9f..50ac4d64 100644 --- a/packages/ui-views/src/lib/mvp-ui-view/assets/user-display.tsx +++ b/packages/ui-views/src/lib/mvp-ui-view/assets/user-display.tsx @@ -8,7 +8,7 @@ type UserDisplayProps = Record; export const UserDisplay = (_props: UserDisplayProps) => { return ( -
            +
            ( +
            + +
            + ), + ], parameters: { layout: 'centered', }, diff --git a/packages/ui-views/src/lib/mvp-ui-view/assets/wrapper.tsx b/packages/ui-views/src/lib/mvp-ui-view/assets/wrapper.tsx index 6ecec54d..00af0eff 100644 --- a/packages/ui-views/src/lib/mvp-ui-view/assets/wrapper.tsx +++ b/packages/ui-views/src/lib/mvp-ui-view/assets/wrapper.tsx @@ -31,12 +31,21 @@ export const Wrapper = ({ const [displayAll, setDisplayAll] = useState(false); return ( -
            +
            -
            +
            @@ -44,8 +53,15 @@ export const Wrapper = ({
            {tagSecondary && (
            @@ -55,72 +71,163 @@ export const Wrapper = ({
            -
            -
            +
            +
            France
            -
            +
            France
            -
            +
            France
            -
            +
            France
            -
            +
            France
            {user && ( -
            -
            +
            +
            -

            +

            -
            +
            -

            +

            setDisplayAll(!displayAll)} > {displayAll ? '-' : '+ 10'}
            {displayAll && ( <> -
            +
            -

            +

            -
            +
            -

            +

            -
            +
            -

            +

            -
            +
            -

            +

            @@ -131,21 +238,26 @@ export const Wrapper = ({
            {displaySettings && (
            - +
            )} {displayRemove && (
            - +
            )} {displayDelete && (
            - +
            )}
            diff --git a/packages/ui-views/src/lib/mvp-ui-view/components/filter-box.tsx b/packages/ui-views/src/lib/mvp-ui-view/components/filter-box.tsx index 6132ebec..33090765 100644 --- a/packages/ui-views/src/lib/mvp-ui-view/components/filter-box.tsx +++ b/packages/ui-views/src/lib/mvp-ui-view/components/filter-box.tsx @@ -40,24 +40,40 @@ export const FilterBox = ({ mode, name, user }: FilterBoxProps) => { }; return ( -
            -
            +
            +
            - +
            { if (mode === 'Tags') { addTag(`tag-${tags.length}`, '#ff0000'); @@ -66,12 +82,12 @@ export const FilterBox = ({ mode, name, user }: FilterBoxProps) => { } }} > -

            {name}

            +

            {name}

            {mode === 'Tags' ? ( -
            +
            {tags.map((tag: any, index: number) => (
            @@ -82,7 +98,10 @@ export const FilterBox = ({ mode, name, user }: FilterBoxProps) => { ))}
            ) : ( -
            +
            {Array.from({ length: addFilter }).map((_, index) => ( { export const Tag = ({ text, color }: { text: string; color?: string }) => { return ( {text} diff --git a/packages/ui-views/src/lib/mvp-ui-view/components/header.scss b/packages/ui-views/src/lib/mvp-ui-view/components/header.scss index 893868c6..a638d9d8 100644 --- a/packages/ui-views/src/lib/mvp-ui-view/components/header.scss +++ b/packages/ui-views/src/lib/mvp-ui-view/components/header.scss @@ -18,7 +18,7 @@ .app-header { height: 0 !important; position: absolute; - z-index: 1000000; + z-index: var(--z-toast); pointer-events: none; background: none; } diff --git a/packages/ui-views/src/lib/mvp-ui-view/components/header.tsx b/packages/ui-views/src/lib/mvp-ui-view/components/header.tsx index d025f2d1..67614af8 100644 --- a/packages/ui-views/src/lib/mvp-ui-view/components/header.tsx +++ b/packages/ui-views/src/lib/mvp-ui-view/components/header.tsx @@ -40,53 +40,73 @@ export const Header = ({ const NiAction = useNotImplemented(); return ( -