From a3165d056ae26fc335e74f50b511aadefa62f80d Mon Sep 17 00:00:00 2001 From: Alex Martin Date: Tue, 9 Jun 2026 14:23:13 -0700 Subject: [PATCH 1/2] chore(linting): switch prettier to oxfmt --- .oxfmtrc.json | 18 ++ .prettierignore | 2 - .prettierrc.json | 7 - eslint.config.mjs | 62 ++++--- package.json | 9 +- pnpm-lock.yaml | 435 +++++++++++++++++++++++++++------------------- 6 files changed, 311 insertions(+), 222 deletions(-) create mode 100644 .oxfmtrc.json delete mode 100644 .prettierignore delete mode 100644 .prettierrc.json diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 00000000..39717ba0 --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,18 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "printWidth": 80, + "experimentalSortPackageJson": false, + "ignorePatterns": ["/src/components/Icon/names.ts", "/types/utilities.d.ts"], + "overrides": [ + { + "files": ["src/**"], + "options": { + "sortTailwindcss": { + "stylesheet": "./src/global.css", + "functions": ["clsx", "cn"], + "preserveWhitespace": true + } + } + } + ] +} diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index ad2617fb..00000000 --- a/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -src/components/Icon/names.ts -types/utilities.d.ts diff --git a/.prettierrc.json b/.prettierrc.json deleted file mode 100644 index 8c13594e..00000000 --- a/.prettierrc.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "trailingComma": "es5", - "tabWidth": 2, - "semi": false, - "singleQuote": true, - "plugins": ["prettier-plugin-tailwindcss"] -} diff --git a/eslint.config.mjs b/eslint.config.mjs index 8321370e..faf3a663 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,28 +1,27 @@ // For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format -import storybook from 'eslint-plugin-storybook' +import storybook from "eslint-plugin-storybook"; // @ts-check -import eslint from '@eslint/js' -import tseslint from 'typescript-eslint' -import { includeIgnoreFile } from '@eslint/compat' -import path from 'node:path' -import { fileURLToPath } from 'node:url' -import prettier from 'eslint-plugin-prettier' -import reactRefresh from 'eslint-plugin-react-refresh' -import unusedImports from 'eslint-plugin-unused-imports' +import eslint from "@eslint/js"; +import tseslint from "typescript-eslint"; +import { includeIgnoreFile } from "@eslint/compat"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import reactRefresh from "eslint-plugin-react-refresh"; +import unusedImports from "eslint-plugin-unused-imports"; -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) -const gitignorePath = path.resolve(__dirname, '.gitignore') +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const gitignorePath = path.resolve(__dirname, ".gitignore"); export default tseslint.config( eslint.configs.recommended, ...tseslint.configs.recommended, includeIgnoreFile(gitignorePath), - ...storybook.configs['flat/recommended'], + ...storybook.configs["flat/recommended"], { - ignores: ['scripts/generate-utility-docs.js'], + ignores: ["scripts/generate-utility-docs.js"], }, { languageOptions: { @@ -33,34 +32,31 @@ export default tseslint.config( }, { plugins: { - prettier, - 'react-refresh': reactRefresh, - 'unused-imports': unusedImports, + "react-refresh": reactRefresh, + "unused-imports": unusedImports, }, rules: { - 'prettier/prettier': ['error', {}, { usePrettierrc: true }], // Enable Prettier and ensure it uses .prettierrc - // Ensure only components are exported in a component file // so that HMR doesn't break - 'react-refresh/only-export-components': [ - 'error', - { extraHOCs: ['assign', 'forwardRefWithGenerics'] }, + "react-refresh/only-export-components": [ + "error", + { extraHOCs: ["assign", "forwardRefWithGenerics"] }, ], - 'no-unused-vars': 'off', // or "@typescript-eslint/no-unused-vars": "off", - 'unused-imports/no-unused-imports': 'error', + "no-unused-vars": "off", // or "@typescript-eslint/no-unused-vars": "off", + "unused-imports/no-unused-imports": "error", - 'storybook/no-redundant-story-name': 'off', + "storybook/no-redundant-story-name": "off", - 'unused-imports/no-unused-vars': [ - 'warn', + "unused-imports/no-unused-vars": [ + "warn", { - vars: 'all', - varsIgnorePattern: '^_', - args: 'after-used', - argsIgnorePattern: '^_', + vars: "all", + varsIgnorePattern: "^_", + args: "after-used", + argsIgnorePattern: "^_", }, ], }, - } -) + }, +); diff --git a/package.json b/package.json index bcf3b30f..ea1d79c1 100644 --- a/package.json +++ b/package.json @@ -40,8 +40,10 @@ "prepack": "pnpm build", "storybook": "storybook dev -p 7006", "build-storybook": "storybook build", - "lint": "eslint", + "format": "oxfmt -c .oxfmtrc.json", + "lint": "pnpm lint:format && eslint", "lint:fix": "eslint --fix", + "lint:format": "oxfmt -c .oxfmtrc.json --check .", "test": "vitest", "analyze-bundle": "npx vite-bundle-visualizer", "generate:docs": "node scripts/generate-utility-docs.js", @@ -72,15 +74,12 @@ "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.2", "eslint": "^10.4.1", - "eslint-config-prettier": "^10.1.8", - "eslint-plugin-prettier": "^5.5.6", "eslint-plugin-react-refresh": "^0.5.2", "eslint-plugin-storybook": "10.4.1", "eslint-plugin-unused-imports": "^4.4.1", "jsdom": "^29.1.1", + "oxfmt": "^0.53.0", "postcss": "^8.4.47", - "prettier": "^3.3.3", - "prettier-plugin-tailwindcss": "^0.8.0", "rimraf": "^6.0.1", "semantic-release": "^25.0.3", "storybook": "^10.4.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6c21ac98..3dcab0be 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -119,22 +119,22 @@ importers: version: 14.1.1(semantic-release@25.0.3(typescript@5.9.3)) '@storybook/addon-docs': specifier: ^10.4.1 - version: 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0)) + version: 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0)) '@storybook/addon-links': specifier: ^10.4.1 - version: 10.4.1(@types/react@19.2.17)(react@19.2.3)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) + version: 10.4.1(@types/react@19.2.17)(react@19.2.3)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) '@storybook/addon-mcp': specifier: ^0.6.0 - version: 0.6.0(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3) + version: 0.6.0(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3) '@storybook/addon-themes': specifier: ^10.4.1 - version: 10.4.1(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) + version: 10.4.1(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) '@storybook/builder-vite': specifier: ^10.4.1 - version: 10.4.1(esbuild@0.27.2)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0)) + version: 10.4.1(esbuild@0.27.2)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0)) '@storybook/react-vite': specifier: ^10.4.1 - version: 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0)) + version: 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0)) '@tailwindcss/typography': specifier: ^0.5.19 version: 0.5.19(tailwindcss@4.3.0) @@ -165,33 +165,24 @@ importers: eslint: specifier: ^10.4.1 version: 10.4.1(jiti@2.7.0) - eslint-config-prettier: - specifier: ^10.1.8 - version: 10.1.8(eslint@10.4.1(jiti@2.7.0)) - eslint-plugin-prettier: - specifier: ^5.5.6 - version: 5.5.6(eslint-config-prettier@10.1.8(eslint@10.4.1(jiti@2.7.0)))(eslint@10.4.1(jiti@2.7.0))(prettier@3.7.4) eslint-plugin-react-refresh: specifier: ^0.5.2 version: 0.5.2(eslint@10.4.1(jiti@2.7.0)) eslint-plugin-storybook: specifier: 10.4.1 - version: 10.4.1(eslint@10.4.1(jiti@2.7.0))(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3) + version: 10.4.1(eslint@10.4.1(jiti@2.7.0))(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3) eslint-plugin-unused-imports: specifier: ^4.4.1 version: 4.4.1(@typescript-eslint/eslint-plugin@8.60.1(@typescript-eslint/parser@8.60.1(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3))(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3))(eslint@10.4.1(jiti@2.7.0)) jsdom: specifier: ^29.1.1 version: 29.1.1 + oxfmt: + specifier: ^0.53.0 + version: 0.53.0 postcss: specifier: ^8.4.47 version: 8.5.6 - prettier: - specifier: ^3.3.3 - version: 3.7.4 - prettier-plugin-tailwindcss: - specifier: ^0.8.0 - version: 0.8.0(prettier@3.7.4) rimraf: specifier: ^6.0.1 version: 6.1.2 @@ -200,7 +191,7 @@ importers: version: 25.0.3(typescript@5.9.3) storybook: specifier: ^10.4.1 - version: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + version: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) svgo: specifier: ^3.3.2 version: 3.3.2 @@ -227,7 +218,7 @@ importers: version: 5.2.0(typescript@5.9.3)(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0)) vitest: specifier: ^4.1.7 - version: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@24.13.1)(jsdom@29.1.1)(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0)) + version: 4.1.7(@types/node@24.13.1)(jsdom@29.1.1)(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0)) packages: @@ -1045,9 +1036,127 @@ packages: cpu: [x64] os: [win32] - '@pkgr/core@0.3.6': - resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==} - engines: {node: ^14.18.0 || >=16.0.0} + '@oxfmt/binding-android-arm-eabi@0.53.0': + resolution: {integrity: sha512-XfVM8AmIovBTKXCt14Op5wbfcoM8418nttd+nhMgM3RAVaJg1MtJc73FyWfUt0oxLyBGVwfniNVUsbV/b3VmPg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxfmt/binding-android-arm64@0.53.0': + resolution: {integrity: sha512-btHDfXckwdf9zgyAVznfZkf+GVyB0I1m1hlvaOMRx2xoyz3hphfPX97s89J3wfCN8QBETLtk4lQUaeOkrMuQOg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxfmt/binding-darwin-arm64@0.53.0': + resolution: {integrity: sha512-k2RjMcSTkHjoOlsVGbL35JVzXL+oQco3GHPl/5kjebVF4oHNfE24In8F5isqBh9LBJucycWHKDXdGrCchdWcHQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxfmt/binding-darwin-x64@0.53.0': + resolution: {integrity: sha512-65jIBE2H1l5SSs16fmv6/7b6sAx/WpvnsgDhVWK9qSjNFDUro7MPQ6q5UhpY7kl46yltfR046iAnxy/Bzqbiew==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxfmt/binding-freebsd-x64@0.53.0': + resolution: {integrity: sha512-oYe1gkz7U49PCYrS9147d2fJZj8mDI4Di6AvlsU5fu9p+Tq8S7qqOMSZjUiVTLX8bXuSA9Lk/tIxuegVjkNYRA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxfmt/binding-linux-arm-gnueabihf@0.53.0': + resolution: {integrity: sha512-ailB2vLzGi629tymdAb2VYJyEHref7oqGxP+tRBrtRBxQrb6NV55JMT7xtGZ8uTeG2+Y9zojqW4LhJYxQnz9Pg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm-musleabihf@0.53.0': + resolution: {integrity: sha512-abh4mWBvOvD966sobqF7r103y2yYx7Rb4WGHLOS4+5igGqLbbPxS9aK5+45D6iUY7dWMsk3Muz9a8gUtufvqJA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm64-gnu@0.53.0': + resolution: {integrity: sha512-z73PvuhJ8qA+cDbaiqbtopHglA91U4+y5wn2sTJJrnpB957d5P33FEuyP3DQIFd7ofljmDmfVT4G0CVGHZaJWg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-arm64-musl@0.53.0': + resolution: {integrity: sha512-I6bhOTroqc3ThrwZ89l2k3ivKuELhdPLbAcJhRNyjWvlgwb0vjRgEnVL1XLx5Jud04/ypNRZBykAWrSk6l/D+g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-ppc64-gnu@0.53.0': + resolution: {integrity: sha512-w0p3JzB/PkkQjXALMJMqP9YfP3yq4w6zGsu5kezQmUnxRkN3b/Theg2l/nDgBsOcczxS3gL6Gam5XNAVrO6QJQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-gnu@0.53.0': + resolution: {integrity: sha512-mzBhF6k1Yq1K/dqDmVe/AAafnlJfEpx7yfUiksyeWXJk5iSzZqBSxcsa02zIytYgQFRZ7h6WPZfwHg/DoOE1Kw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-musl@0.53.0': + resolution: {integrity: sha512-AlFCpnRQhogQFzZXWbO6xB6/Udy745L+eQNmDPGg7G/OeWsYmJc4jZYfUN5pQg0reOPWSED2mOQqKZOJM1U8cA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-s390x-gnu@0.53.0': + resolution: {integrity: sha512-XD4ulY4f1DWbuuZXAqxhVn+gdPmrhnmojWtFN78ctVoupmS845fGhsUrk1HZXKQI+iymbaiz9vAjPsghHNQ7Ag==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-gnu@0.53.0': + resolution: {integrity: sha512-xg8KWX0QnxmYWRe60CgHYWXI0ZOtBbqTsXvWiWrcl2XUHJ3fht2QerOk2iWvylzX3zNT2GpvBRxGoR4d3sxPRQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-musl@0.53.0': + resolution: {integrity: sha512-MWExpYBGvl+pIvVB/gj/CcWlN2al8AizT7rUbtaYaWNoQkhWARM6W3qpgoCr72CYSN9PborzPmM5MIRe2BrNdA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-openharmony-arm64@0.53.0': + resolution: {integrity: sha512-u4sajgO4nxgmJIgc/y2AqPhkdbOkQH8WugXpA1+pW0ESQhvGZ1oGq61Q4xMbJHJU1hFgtO18QNrcFYDPYH0gwQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxfmt/binding-win32-arm64-msvc@0.53.0': + resolution: {integrity: sha512-Yq9sOZoIOJ5xPjO0qOyHJS4CiPuTkB2en9auxZz7Ar2p5RaC7BzLyVVmAA7zz9/L9YnjjY1DwNxN+ivKXimN/A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxfmt/binding-win32-ia32-msvc@0.53.0': + resolution: {integrity: sha512-es1fVNZEkBqEcQtBpn19SYFgZF7FawlkCjkT/iImfEAus4gun8fBwB1E9hpV5LcR9B0DBNvRIXhW8BQk3JaE+Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxfmt/binding-win32-x64-msvc@0.53.0': + resolution: {integrity: sha512-QFmJs2bEu9AO4O6qsmEaZNGi6dFq8N+rT8EHAAnZIq/B9SeJDUbc4DzVxQ48MfDsL7D3sCZzo37zuTuspcURgg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] '@pnpm/config.env-replace@1.1.0': resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} @@ -2716,26 +2825,6 @@ packages: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} - eslint-config-prettier@10.1.8: - resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' - - eslint-plugin-prettier@5.5.6: - resolution: {integrity: sha512-ifetmTcxWfz+4qRW3pH/ujdTq2jQIj59AxJMIN26K5avYgU8dxycUETQonWiW+wPrYXA0j3Try0l1CnwVQtDqQ==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - '@types/eslint': '>=8.0.0' - eslint: '>=8.0.0' - eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' - prettier: '>=3.0.0' - peerDependenciesMeta: - '@types/eslint': - optional: true - eslint-config-prettier: - optional: true - eslint-plugin-react-refresh@0.5.2: resolution: {integrity: sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==} peerDependencies: @@ -2839,9 +2928,6 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} @@ -3792,6 +3878,19 @@ packages: oxc-resolver@11.19.1: resolution: {integrity: sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg==} + oxfmt@0.53.0: + resolution: {integrity: sha512-9cB5glS3Ip6NMuZ+6NYTao9FCWkDhRtPYCtR3QBu/NxHoFbgzzTvi41N4jxz/GqGfuLKspui1qb/LlSu2IbMcw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + svelte: ^5.0.0 + vite-plus: '*' + peerDependenciesMeta: + svelte: + optional: true + vite-plus: + optional: true + p-each-series@3.0.0: resolution: {integrity: sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==} engines: {node: '>=12'} @@ -3963,70 +4062,6 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier-linter-helpers@1.0.1: - resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} - engines: {node: '>=6.0.0'} - - prettier-plugin-tailwindcss@0.8.0: - resolution: {integrity: sha512-V8ITGH87yuBDF6JpEZTOVlUz/saAwqb8f3HRgUj8Lh+tGCcrmorhsLpYqzygwFwK0PE2Ib6Mv3M7T/uE2tZV1g==} - engines: {node: '>=20.19'} - peerDependencies: - '@ianvs/prettier-plugin-sort-imports': '*' - '@prettier/plugin-hermes': '*' - '@prettier/plugin-oxc': '*' - '@prettier/plugin-pug': '*' - '@shopify/prettier-plugin-liquid': '*' - '@trivago/prettier-plugin-sort-imports': '*' - '@zackad/prettier-plugin-twig': '*' - prettier: ^3.0 - prettier-plugin-astro: '*' - prettier-plugin-css-order: '*' - prettier-plugin-jsdoc: '*' - prettier-plugin-marko: '*' - prettier-plugin-multiline-arrays: '*' - prettier-plugin-organize-attributes: '*' - prettier-plugin-organize-imports: '*' - prettier-plugin-sort-imports: '*' - prettier-plugin-svelte: '*' - peerDependenciesMeta: - '@ianvs/prettier-plugin-sort-imports': - optional: true - '@prettier/plugin-hermes': - optional: true - '@prettier/plugin-oxc': - optional: true - '@prettier/plugin-pug': - optional: true - '@shopify/prettier-plugin-liquid': - optional: true - '@trivago/prettier-plugin-sort-imports': - optional: true - '@zackad/prettier-plugin-twig': - optional: true - prettier-plugin-astro: - optional: true - prettier-plugin-css-order: - optional: true - prettier-plugin-jsdoc: - optional: true - prettier-plugin-marko: - optional: true - prettier-plugin-multiline-arrays: - optional: true - prettier-plugin-organize-attributes: - optional: true - prettier-plugin-organize-imports: - optional: true - prettier-plugin-sort-imports: - optional: true - prettier-plugin-svelte: - optional: true - - prettier@3.7.4: - resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} - engines: {node: '>=14'} - hasBin: true - pretty-format@27.5.1: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -4433,10 +4468,6 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - synckit@0.11.13: - resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==} - engines: {node: ^14.18.0 || >=16.0.0} - tagged-tag@1.0.0: resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} engines: {node: '>=20'} @@ -4495,6 +4526,10 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} + tinypool@2.1.0: + resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} + engines: {node: ^20.0.0 || >=22.0.0} + tinyrainbow@2.0.0: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} @@ -5655,7 +5690,62 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.19.1': optional: true - '@pkgr/core@0.3.6': {} + '@oxfmt/binding-android-arm-eabi@0.53.0': + optional: true + + '@oxfmt/binding-android-arm64@0.53.0': + optional: true + + '@oxfmt/binding-darwin-arm64@0.53.0': + optional: true + + '@oxfmt/binding-darwin-x64@0.53.0': + optional: true + + '@oxfmt/binding-freebsd-x64@0.53.0': + optional: true + + '@oxfmt/binding-linux-arm-gnueabihf@0.53.0': + optional: true + + '@oxfmt/binding-linux-arm-musleabihf@0.53.0': + optional: true + + '@oxfmt/binding-linux-arm64-gnu@0.53.0': + optional: true + + '@oxfmt/binding-linux-arm64-musl@0.53.0': + optional: true + + '@oxfmt/binding-linux-ppc64-gnu@0.53.0': + optional: true + + '@oxfmt/binding-linux-riscv64-gnu@0.53.0': + optional: true + + '@oxfmt/binding-linux-riscv64-musl@0.53.0': + optional: true + + '@oxfmt/binding-linux-s390x-gnu@0.53.0': + optional: true + + '@oxfmt/binding-linux-x64-gnu@0.53.0': + optional: true + + '@oxfmt/binding-linux-x64-musl@0.53.0': + optional: true + + '@oxfmt/binding-openharmony-arm64@0.53.0': + optional: true + + '@oxfmt/binding-win32-arm64-msvc@0.53.0': + optional: true + + '@oxfmt/binding-win32-ia32-msvc@0.53.0': + optional: true + + '@oxfmt/binding-win32-x64-msvc@0.53.0': + optional: true '@pnpm/config.env-replace@1.1.0': {} @@ -6279,15 +6369,15 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@storybook/addon-docs@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0))': + '@storybook/addon-docs@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0))': dependencies: '@mdx-js/react': 3.1.1(@types/react@19.2.17)(react@19.2.3) - '@storybook/csf-plugin': 10.4.1(esbuild@0.27.2)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0)) + '@storybook/csf-plugin': 10.4.1(esbuild@0.27.2)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0)) '@storybook/icons': 2.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@storybook/react-dom-shim': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) + '@storybook/react-dom-shim': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) ts-dedent: 2.2.0 optionalDependencies: '@types/react': 19.2.17 @@ -6298,36 +6388,36 @@ snapshots: - vite - webpack - '@storybook/addon-links@10.4.1(@types/react@19.2.17)(react@19.2.3)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))': + '@storybook/addon-links@10.4.1(@types/react@19.2.17)(react@19.2.3)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))': dependencies: '@storybook/global': 5.0.0 - storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) optionalDependencies: '@types/react': 19.2.17 react: 19.2.3 - '@storybook/addon-mcp@0.6.0(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)': + '@storybook/addon-mcp@0.6.0(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)': dependencies: '@storybook/mcp': 0.7.0(typescript@5.9.3) '@tmcp/adapter-valibot': 0.1.6(tmcp@1.19.4(typescript@5.9.3))(valibot@1.2.0(typescript@5.9.3)) '@tmcp/transport-http': 0.8.6(tmcp@1.19.4(typescript@5.9.3)) picoquery: 2.5.0 - storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) tmcp: 1.19.4(typescript@5.9.3) valibot: 1.2.0(typescript@5.9.3) transitivePeerDependencies: - '@tmcp/auth' - typescript - '@storybook/addon-themes@10.4.1(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))': + '@storybook/addon-themes@10.4.1(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))': dependencies: - storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) ts-dedent: 2.2.0 - '@storybook/builder-vite@10.4.1(esbuild@0.27.2)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0))': + '@storybook/builder-vite@10.4.1(esbuild@0.27.2)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0))': dependencies: - '@storybook/csf-plugin': 10.4.1(esbuild@0.27.2)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0)) - storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@storybook/csf-plugin': 10.4.1(esbuild@0.27.2)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0)) + storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) ts-dedent: 2.2.0 vite: 8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0) transitivePeerDependencies: @@ -6335,9 +6425,9 @@ snapshots: - rollup - webpack - '@storybook/csf-plugin@10.4.1(esbuild@0.27.2)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0))': + '@storybook/csf-plugin@10.4.1(esbuild@0.27.2)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0))': dependencies: - storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) unplugin: 2.3.11 optionalDependencies: esbuild: 0.27.2 @@ -6360,28 +6450,28 @@ snapshots: - '@tmcp/auth' - typescript - '@storybook/react-dom-shim@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))': + '@storybook/react-dom-shim@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))': dependencies: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@storybook/react-vite@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0))': + '@storybook/react-vite@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0))': dependencies: '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(typescript@5.9.3)(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0)) '@rollup/pluginutils': 5.3.0 - '@storybook/builder-vite': 10.4.1(esbuild@0.27.2)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0)) - '@storybook/react': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3) + '@storybook/builder-vite': 10.4.1(esbuild@0.27.2)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0)) + '@storybook/react': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3) empathic: 2.0.1 magic-string: 0.30.21 react: 19.2.3 react-docgen: 8.0.2 react-dom: 19.2.3(react@19.2.3) resolve: 1.22.11 - storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) tsconfig-paths: 4.2.0 vite: 8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0) transitivePeerDependencies: @@ -6393,15 +6483,15 @@ snapshots: - typescript - webpack - '@storybook/react@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)': + '@storybook/react@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)': dependencies: '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) + '@storybook/react-dom-shim': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) react: 19.2.3 react-docgen: 8.0.2 react-docgen-typescript: 2.4.0(typescript@5.9.3) react-dom: 19.2.3(react@19.2.3) - storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) @@ -7361,28 +7451,15 @@ snapshots: escape-string-regexp@5.0.0: {} - eslint-config-prettier@10.1.8(eslint@10.4.1(jiti@2.7.0)): - dependencies: - eslint: 10.4.1(jiti@2.7.0) - - eslint-plugin-prettier@5.5.6(eslint-config-prettier@10.1.8(eslint@10.4.1(jiti@2.7.0)))(eslint@10.4.1(jiti@2.7.0))(prettier@3.7.4): - dependencies: - eslint: 10.4.1(jiti@2.7.0) - prettier: 3.7.4 - prettier-linter-helpers: 1.0.1 - synckit: 0.11.13 - optionalDependencies: - eslint-config-prettier: 10.1.8(eslint@10.4.1(jiti@2.7.0)) - eslint-plugin-react-refresh@0.5.2(eslint@10.4.1(jiti@2.7.0)): dependencies: eslint: 10.4.1(jiti@2.7.0) - eslint-plugin-storybook@10.4.1(eslint@10.4.1(jiti@2.7.0))(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3): + eslint-plugin-storybook@10.4.1(eslint@10.4.1(jiti@2.7.0))(storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3): dependencies: '@typescript-eslint/utils': 8.60.1(eslint@10.4.1(jiti@2.7.0))(typescript@5.9.3) eslint: 10.4.1(jiti@2.7.0) - storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + storybook: 10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) transitivePeerDependencies: - supports-color - typescript @@ -7508,8 +7585,6 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-diff@1.3.0: {} - fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -8596,6 +8671,30 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' + oxfmt@0.53.0: + dependencies: + tinypool: 2.1.0 + optionalDependencies: + '@oxfmt/binding-android-arm-eabi': 0.53.0 + '@oxfmt/binding-android-arm64': 0.53.0 + '@oxfmt/binding-darwin-arm64': 0.53.0 + '@oxfmt/binding-darwin-x64': 0.53.0 + '@oxfmt/binding-freebsd-x64': 0.53.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.53.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.53.0 + '@oxfmt/binding-linux-arm64-gnu': 0.53.0 + '@oxfmt/binding-linux-arm64-musl': 0.53.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.53.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.53.0 + '@oxfmt/binding-linux-riscv64-musl': 0.53.0 + '@oxfmt/binding-linux-s390x-gnu': 0.53.0 + '@oxfmt/binding-linux-x64-gnu': 0.53.0 + '@oxfmt/binding-linux-x64-musl': 0.53.0 + '@oxfmt/binding-openharmony-arm64': 0.53.0 + '@oxfmt/binding-win32-arm64-msvc': 0.53.0 + '@oxfmt/binding-win32-ia32-msvc': 0.53.0 + '@oxfmt/binding-win32-x64-msvc': 0.53.0 + p-each-series@3.0.0: {} p-event@6.0.1: @@ -8754,16 +8853,6 @@ snapshots: prelude-ls@1.2.1: {} - prettier-linter-helpers@1.0.1: - dependencies: - fast-diff: 1.3.0 - - prettier-plugin-tailwindcss@0.8.0(prettier@3.7.4): - dependencies: - prettier: 3.7.4 - - prettier@3.7.4: {} - pretty-format@27.5.1: dependencies: ansi-regex: 5.0.1 @@ -9146,7 +9235,7 @@ snapshots: std-env@4.1.0: {} - storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.7.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + storybook@10.4.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@testing-library/dom@10.4.1)(@types/react@19.2.17)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: '@storybook/global': 5.0.0 '@storybook/icons': 2.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -9165,7 +9254,6 @@ snapshots: ws: 8.19.0 optionalDependencies: '@types/react': 19.2.17 - prettier: 3.7.4 transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -9272,10 +9360,6 @@ snapshots: symbol-tree@3.2.4: {} - synckit@0.11.13: - dependencies: - '@pkgr/core': 0.3.6 - tagged-tag@1.0.0: {} tailwind-merge@3.5.0: {} @@ -9328,6 +9412,8 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 + tinypool@2.1.0: {} + tinyrainbow@2.0.0: {} tinyrainbow@3.1.0: {} @@ -9605,7 +9691,7 @@ snapshots: jiti: 2.7.0 tsx: 4.21.0 - vitest@4.1.7(@opentelemetry/api@1.9.0)(@types/node@24.13.1)(jsdom@29.1.1)(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0)): + vitest@4.1.7(@types/node@24.13.1)(jsdom@29.1.1)(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0)): dependencies: '@vitest/expect': 4.1.7 '@vitest/mocker': 4.1.7(vite@8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0)) @@ -9628,7 +9714,6 @@ snapshots: vite: 8.0.14(@types/node@24.13.1)(esbuild@0.27.2)(jiti@2.7.0)(tsx@4.21.0) why-is-node-running: 2.3.0 optionalDependencies: - '@opentelemetry/api': 1.9.0 '@types/node': 24.13.1 jsdom: 29.1.1 transitivePeerDependencies: From 0097d604d43233e8b89c6a1742f8edcab78fe436 Mon Sep 17 00:00:00 2001 From: Alex Martin Date: Tue, 9 Jun 2026 14:23:31 -0700 Subject: [PATCH 2/2] style: format with oxfmt --- .github/workflows/lint.yml | 2 +- .github/workflows/publish.yml | 2 +- .github/workflows/published-test.yml | 4 +- .github/workflows/test.yml | 2 +- .github/workflows/typecheck.yml | 2 +- .storybook/fonts.css | 2 +- .storybook/main.ts | 38 +- .storybook/manager.ts | 40 +- .storybook/modes.ts | 28 +- .storybook/preview.tsx | 38 +- .storybook/themedDocsContainer.tsx | 46 +- .vscode/extensions.json | 2 +- .vscode/settings.json | 2 +- CLAUDE.md | 103 ++-- README.md | 40 +- components.json | 2 +- cubic.yaml | 2 +- integration/integration.test.tsx | 150 ++--- integration/setup.js | 12 +- integration/tsconfig.json | 8 +- integration/vite.config.mts | 18 +- postcss.config.mjs | 2 +- scripts/generate-utility-docs.js | 288 ++++----- src/base.css | 552 +++++++++--------- .../AIChat/AIChatContainer.stories.tsx | 448 +++++++------- src/components/AIChat/AIChatContainer.tsx | 42 +- src/components/AIChat/AIChatMessage.tsx | 72 +-- .../AIChat/AIChatMessageComposer.tsx | 122 ++-- src/components/AIChat/AIChatMessageList.tsx | 24 +- src/components/AIChat/AIChatModelSelector.tsx | 94 +-- src/components/AIChat/componentsTypes.ts | 32 +- src/components/AIChat/context.ts | 14 +- src/components/AIChat/index.ts | 24 +- .../AIChat/parts/AIChatMessageFilePart.tsx | 38 +- .../parts/AIChatMessageReasoningPart.tsx | 12 +- .../AIChat/parts/AIChatMessageSourcePart.tsx | 20 +- .../AIChat/parts/AIChatMessageTextPart.tsx | 32 +- .../parts/AIChatMessageToolInvocationPart.tsx | 18 +- .../parts/AIChatMessageToolPart.stories.tsx | 146 ++--- .../AIChat/parts/AIChatMessageToolPart.tsx | 204 +++---- .../parts/AIChatMessageToolResultPart.tsx | 16 +- src/components/AIChat/toolCallApproval.ts | 52 +- src/components/AIChat/types.ts | 118 ++-- src/components/ActionBar/index.stories.tsx | 22 +- src/components/ActionBar/index.tsx | 108 ++-- src/components/Alert/index.stories.tsx | 80 +-- src/components/Alert/index.tsx | 98 ++-- src/components/Alert/types.ts | 20 +- src/components/AppLayout/context.tsx | 20 +- src/components/AppLayout/index.stories.tsx | 198 +++---- src/components/AppLayout/index.tsx | 394 ++++++------- src/components/AppLayout/provider.tsx | 26 +- src/components/AppLayout/useAppLayoutKeys.ts | 22 +- src/components/Badge/index.stories.tsx | 64 +- src/components/Badge/index.test.tsx | 78 +-- src/components/Badge/index.tsx | 196 +++---- src/components/Button/index.stories.tsx | 192 +++--- src/components/Button/index.test.tsx | 34 +- src/components/Button/index.tsx | 436 +++++++------- src/components/Card/index.stories.tsx | 90 +-- src/components/Card/index.test.tsx | 36 +- src/components/Card/index.tsx | 148 ++--- .../CodeEditorLayout/index.stories.tsx | 100 ++-- src/components/CodeEditorLayout/index.tsx | 244 ++++---- .../CodeEditorLayout/styles.module.css | 8 +- src/components/CodeHighlight/Pre.tsx | 40 +- .../CodePlayground/index.stories.tsx | 68 +-- src/components/CodePlayground/index.tsx | 202 +++---- src/components/CodeSnippet/codeSnippet.css | 11 +- src/components/CodeSnippet/index.stories.tsx | 122 ++-- src/components/CodeSnippet/index.tsx | 140 ++--- src/components/Combobox/index.stories.tsx | 136 ++--- src/components/Combobox/index.tsx | 140 ++--- src/components/Command/index.tsx | 80 +-- src/components/Container/index.stories.tsx | 16 +- src/components/Container/index.tsx | 24 +- .../ContextDropdown/index.stories.tsx | 70 +-- src/components/ContextDropdown/index.tsx | 74 +-- src/components/Dialog/index.stories.tsx | 26 +- src/components/Dialog/index.tsx | 68 +-- src/components/DragNDrop/DragNDropArea.tsx | 16 +- src/components/DragNDrop/DragOverlay.tsx | 4 +- src/components/DragNDrop/Draggable.tsx | 38 +- src/components/DragNDrop/Droppable.tsx | 28 +- src/components/DragNDrop/index.stories.tsx | 74 +-- src/components/Dropdown/index.stories.tsx | 22 +- src/components/Dropdown/index.tsx | 108 ++-- src/components/ExternalPill/index.stories.tsx | 42 +- src/components/ExternalPill/index.tsx | 52 +- src/components/Facepile/index.stories.tsx | 174 +++--- src/components/Facepile/index.tsx | 184 +++--- .../GradientCircle/gradientCircle.css | 4 +- .../GradientCircle/index.stories.tsx | 60 +- src/components/GradientCircle/index.tsx | 126 ++-- src/components/Grid/index.stories.tsx | 42 +- src/components/Grid/index.tsx | 60 +- src/components/Heading/index.stories.tsx | 50 +- src/components/Heading/index.tsx | 52 +- .../HighlightedText/index.stories.tsx | 40 +- src/components/HighlightedText/index.tsx | 130 +++-- .../customIcons/createCustomLucideIcon.ts | 20 +- src/components/Icon/customIcons/gems.ts | 28 +- src/components/Icon/customIcons/go.ts | 28 +- src/components/Icon/customIcons/index.ts | 18 +- src/components/Icon/customIcons/maven.ts | 22 +- src/components/Icon/customIcons/npm.ts | 22 +- src/components/Icon/customIcons/nuget.ts | 22 +- src/components/Icon/customIcons/packagist.ts | 130 ++--- src/components/Icon/customIcons/pypi.ts | 20 +- src/components/Icon/index.stories.tsx | 64 +- src/components/Icon/index.tsx | 76 +-- src/components/Icon/isIconName.ts | 8 +- src/components/IconButton/index.stories.tsx | 102 ++-- src/components/IconButton/index.tsx | 64 +- src/components/Input/index.stories.tsx | 26 +- src/components/Input/index.tsx | 60 +- src/components/KeyHint/index.stories.tsx | 48 +- src/components/KeyHint/index.tsx | 74 +-- .../LanguageIndicator/index.stories.tsx | 66 +-- src/components/LanguageIndicator/index.tsx | 80 +-- src/components/Link/index.stories.tsx | 188 +++--- src/components/Link/index.tsx | 110 ++-- .../LoggedInUserMenu/index.stories.tsx | 56 +- src/components/LoggedInUserMenu/index.tsx | 66 +-- src/components/Logo/Animated.stories.tsx | 54 +- src/components/Logo/Animated.tsx | 108 ++-- src/components/Logo/index.stories.tsx | 42 +- src/components/Logo/index.tsx | 16 +- src/components/Logo/svgs/index.tsx | 18 +- src/components/Modal/index.stories.tsx | 104 ++-- src/components/Modal/index.tsx | 52 +- src/components/PageHeader/index.stories.tsx | 24 +- src/components/PageHeader/index.tsx | 142 ++--- src/components/PageHeader/styles.module.css | 22 +- src/components/Popover/index.stories.tsx | 16 +- src/components/Popover/index.tsx | 36 +- src/components/PromptInput/index.stories.tsx | 122 ++-- src/components/PromptInput/index.tsx | 228 ++++---- .../PullRequestLink/index.stories.tsx | 22 +- src/components/PullRequestLink/index.tsx | 56 +- .../ResizablePanel/index.stories.tsx | 62 +- src/components/ResizablePanel/index.tsx | 80 +-- src/components/Score/index.module.css | 2 +- src/components/Score/index.stories.tsx | 66 +-- src/components/Score/index.tsx | 170 +++--- src/components/ScrollArea/index.tsx | 36 +- .../SegmentedButton/index.module.css | 1 - .../SegmentedButton/index.stories.tsx | 58 +- src/components/SegmentedButton/index.tsx | 79 +-- src/components/Select/index.tsx | 82 +-- src/components/Separator/index.stories.tsx | 24 +- src/components/Separator/index.tsx | 20 +- src/components/Skeleton/index.stories.tsx | 26 +- src/components/Skeleton/index.tsx | 22 +- src/components/Skeleton/skeleton.css | 2 +- src/components/Stack/index.stories.tsx | 392 ++++++------- src/components/Stack/index.test.tsx | 20 +- src/components/Stack/index.tsx | 136 ++--- src/components/Subnav/index.stories.tsx | 30 +- src/components/Subnav/index.tsx | 220 +++---- src/components/Switch/index.stories.tsx | 16 +- src/components/Switch/index.tsx | 20 +- src/components/Table/context/context.tsx | 16 +- .../Table/context/tableProvider.tsx | 30 +- src/components/Table/index.stories.tsx | 194 +++--- src/components/Table/index.test.tsx | 180 +++--- src/components/Table/index.tsx | 542 ++++++++--------- src/components/Table/sorting.test.ts | 211 +++---- src/components/Table/sorting.ts | 68 +-- src/components/Table/styles.module.css | 6 +- src/components/Tabs/index.stories.tsx | 18 +- src/components/Tabs/index.tsx | 66 +-- .../TargetLanguageIcon/index.stories.tsx | 38 +- src/components/TargetLanguageIcon/index.tsx | 56 +- src/components/Text/index.stories.tsx | 36 +- src/components/Text/index.test.tsx | 18 +- src/components/Text/index.tsx | 58 +- .../ThemeSwitcher/index.stories.tsx | 20 +- src/components/ThemeSwitcher/index.tsx | 80 +-- src/components/Timeline/Timeline.test.tsx | 350 +++++------ src/components/Timeline/index.stories.tsx | 48 +- src/components/Timeline/index.tsx | 170 +++--- src/components/Tooltip/index.stories.tsx | 54 +- src/components/Tooltip/index.tsx | 42 +- src/components/UserAvatar/index.stories.tsx | 64 +- src/components/UserAvatar/index.tsx | 66 +-- src/components/UserAvatar/sizeMap.ts | 8 +- src/components/Wizard/index.stories.tsx | 140 ++--- src/components/Wizard/index.tsx | 128 ++-- src/components/Wizard/types.ts | 22 +- .../WorkspaceSelector/CreateOrg.tsx | 36 +- .../WorkspaceSelector/CreateWorkspace.tsx | 98 ++-- src/components/WorkspaceSelector/OrgList.tsx | 72 +-- .../WorkspaceSelector/OrgSelector.tsx | 162 ++--- .../WorkspaceSelector/RecentWorkspaces.tsx | 56 +- .../WorkspaceSelector/ScrollingList.tsx | 46 +- .../WorkspaceSelector/SearchBox.tsx | 22 +- .../WorkspaceSelector/WorkspaceItem.tsx | 34 +- .../WorkspaceSelector/WorkspaceList.tsx | 64 +- .../WorkspaceSelector/index.stories.tsx | 358 ++++++------ src/components/WorkspaceSelector/index.tsx | 300 +++++----- .../__beta__/CLIWizard/index.stories.tsx | 100 ++-- src/components/__beta__/CLIWizard/index.tsx | 136 ++--- .../__beta__/CLIWizard/terminal-command.tsx | 66 +-- .../__beta__/CLIWizard/terminal.tsx | 40 +- src/components/index.mdx | 11 +- src/context/ConfigContext.tsx | 16 +- src/context/ModalContext.tsx | 106 ++-- src/context/theme.ts | 2 +- src/global.css | 24 +- src/hooks/useAppLayout.ts | 12 +- src/hooks/useConfig.ts | 10 +- src/hooks/useIsMounted.ts | 14 +- src/hooks/useModal.tsx | 10 +- src/hooks/useTailwindBreakpoint.ts | 48 +- src/hooks/useTheme.ts | 8 +- src/index.ts | 161 ++--- src/lib/assert.ts | 4 +- src/lib/codeUtils.test.ts | 230 ++++---- src/lib/codeUtils.ts | 174 +++--- src/lib/debounce.ts | 8 +- src/lib/events.ts | 2 +- src/lib/responsiveMappers.ts | 82 +-- src/lib/responsiveUtils.test.ts | 84 +-- src/lib/responsiveUtils.ts | 20 +- src/lib/storybookUtils.tsx | 10 +- src/lib/typeUtils.ts | 66 +-- src/lib/utils.test.ts | 50 +- src/lib/utils.ts | 52 +- src/types.ts | 252 ++++---- src/utilities.css | 1 - src/vite-env.d.ts | 6 +- tests/setup.js | 12 +- tsconfig.json | 8 +- tsconfig.typecheck.json | 4 +- type-tests/table-sort.ts | 28 +- type-tests/tooltip-props.ts | 14 +- vite.config.mts | 82 +-- 238 files changed, 9030 insertions(+), 8974 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index bbf5853b..f3686733 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -16,7 +16,7 @@ jobs: - uses: actions/setup-node@v6 with: node-version: 24 - cache: 'pnpm' + cache: "pnpm" - run: pnpm install diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 87d63d3c..945abae5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -24,7 +24,7 @@ jobs: uses: actions/setup-node@v6 with: node-version: 24 - cache: 'pnpm' + cache: "pnpm" - name: Install dependencies run: pnpm install - name: Semantic Release diff --git a/.github/workflows/published-test.yml b/.github/workflows/published-test.yml index cf3c4367..8a9112ff 100644 --- a/.github/workflows/published-test.yml +++ b/.github/workflows/published-test.yml @@ -5,7 +5,6 @@ on: branches: - main - jobs: test: runs-on: ubuntu-latest @@ -17,7 +16,7 @@ jobs: - uses: actions/setup-node@v6 with: node-version: 24 - cache: 'pnpm' + cache: "pnpm" - run: pnpm install - run: pnpm pack @@ -28,6 +27,5 @@ jobs: - run: pnpm install --no-frozen-lockfile working-directory: integration - - run: pnpm test working-directory: integration diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c550ebe7..e1c4ecde 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: - uses: actions/setup-node@v6 with: node-version: 24 - cache: 'pnpm' + cache: "pnpm" - run: pnpm install diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 845ccd32..8c1cbf63 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -16,7 +16,7 @@ jobs: - uses: actions/setup-node@v6 with: node-version: 24 - cache: 'pnpm' + cache: "pnpm" - run: pnpm install diff --git a/.storybook/fonts.css b/.storybook/fonts.css index 8cd6127a..72923cfb 100644 --- a/.storybook/fonts.css +++ b/.storybook/fonts.css @@ -44,4 +44,4 @@ font-weight: 100; font-style: normal; font-display: block; -} \ No newline at end of file +} diff --git a/.storybook/main.ts b/.storybook/main.ts index 88890af4..ab90ca05 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -1,46 +1,46 @@ -import type { StorybookConfig } from '@storybook/react-vite' -import { resolve } from 'path' +import type { StorybookConfig } from "@storybook/react-vite"; +import { resolve } from "path"; const config: StorybookConfig = { - stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], addons: [ - '@storybook/addon-links', - '@storybook/addon-themes', - '@storybook/addon-docs', - '@storybook/addon-mcp', + "@storybook/addon-links", + "@storybook/addon-themes", + "@storybook/addon-docs", + "@storybook/addon-mcp", ], framework: { - name: '@storybook/react-vite', + name: "@storybook/react-vite", options: {}, }, features: { sidebarOnboardingChecklist: false, }, - staticDirs: ['./public'], + staticDirs: ["./public"], async viteFinal(config) { - const { mergeConfig } = await import('vite') + const { mergeConfig } = await import("vite"); return mergeConfig(config, { plugins: [ { // MDX compiler generates file:// imports for pnpm virtual store paths // that Vite can't resolve. Normalize them to absolute paths. - name: 'resolve-file-protocol-imports', + name: "resolve-file-protocol-imports", resolveId(id) { - if (!id.startsWith('file://')) return - const path = id.slice('file://'.length) - return path.startsWith('/') ? path : resolve(process.cwd(), path) + if (!id.startsWith("file://")) return; + const path = id.slice("file://".length); + return path.startsWith("/") ? path : resolve(process.cwd(), path); }, }, ], optimizeDeps: { // https://github.com/storybookjs/storybook/issues/28542#issuecomment-2268031095 exclude: [ - 'node_modules/.cache/sb-vite', - 'node_modules/.cache/storybook', + "node_modules/.cache/sb-vite", + "node_modules/.cache/storybook", ], }, - }) + }); }, -} -export default config +}; +export default config; diff --git a/.storybook/manager.ts b/.storybook/manager.ts index bb0f6692..c01b193c 100644 --- a/.storybook/manager.ts +++ b/.storybook/manager.ts @@ -1,40 +1,40 @@ -import { addons } from 'storybook/manager-api' -import { create } from 'storybook/theming' +import { addons } from "storybook/manager-api"; +import { create } from "storybook/theming"; export const speakeasyTheme = create({ - base: 'light', + base: "light", // Speakeasy's brand colors - colorPrimary: 'hsl(53, 96%, 40%)', // Primary brand color - colorSecondary: 'hsl(53, 96%, 40%)', // Accent color + colorPrimary: "hsl(53, 96%, 40%)", // Primary brand color + colorSecondary: "hsl(53, 96%, 40%)", // Accent color // UI elements - appBg: 'hsl(0, 0%, 100%)', + appBg: "hsl(0, 0%, 100%)", // Docs bg - appContentBg: 'hsl(0, 0%, 100%)', - appBorderColor: 'hsl(0, 0%, 20%)', + appContentBg: "hsl(0, 0%, 100%)", + appBorderColor: "hsl(0, 0%, 20%)", appBorderRadius: 4, // Text colors - textColor: 'hsl(0, 0%, 10%)', - textInverseColor: 'hsl(0, 0%, 100%)', + textColor: "hsl(0, 0%, 10%)", + textInverseColor: "hsl(0, 0%, 100%)", // Toolbar default and active colors - barTextColor: 'hsl(0, 0%, 10%)', - barSelectedColor: 'hsl(0, 0%, 10%)', - barBg: 'hsl(0, 0%, 100%)', + barTextColor: "hsl(0, 0%, 10%)", + barSelectedColor: "hsl(0, 0%, 10%)", + barBg: "hsl(0, 0%, 100%)", // Form colors - inputBg: 'hsl(0, 0%, 100%)', - inputBorder: 'hsl(0, 0%, 20%)', - inputTextColor: 'hsl(0, 0%, 10%)', + inputBg: "hsl(0, 0%, 100%)", + inputBorder: "hsl(0, 0%, 20%)", + inputTextColor: "hsl(0, 0%, 10%)", inputBorderRadius: 4, - brandTitle: 'Speakeasy Moonshine', - brandUrl: 'https://speakeasy.com', -}) + brandTitle: "Speakeasy Moonshine", + brandUrl: "https://speakeasy.com", +}); addons.setConfig({ theme: speakeasyTheme, -}) +}); diff --git a/.storybook/modes.ts b/.storybook/modes.ts index 7d63a4c2..abc1d5dc 100644 --- a/.storybook/modes.ts +++ b/.storybook/modes.ts @@ -1,23 +1,23 @@ export const allModes = { desktop: { - viewport: 'large', + viewport: "large", }, dark: { - backgrounds: { value: 'hsl(0, 0%, 7%)' }, - theme: 'dark', + backgrounds: { value: "hsl(0, 0%, 7%)" }, + theme: "dark", }, light: { - backgrounds: { value: '#fff' }, - theme: 'light', + backgrounds: { value: "#fff" }, + theme: "light", }, - 'dark desktop': { - backgrounds: { value: 'hsl(0, 0%, 7%)' }, - theme: 'dark', - viewport: 'large', + "dark desktop": { + backgrounds: { value: "hsl(0, 0%, 7%)" }, + theme: "dark", + viewport: "large", }, - 'light desktop': { - backgrounds: { value: '#fff' }, - theme: 'light', - viewport: 'large', + "light desktop": { + backgrounds: { value: "#fff" }, + theme: "light", + viewport: "large", }, -} +}; diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 48877741..9dc4a2b1 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,43 +1,43 @@ -import type { Preview, Decorator } from '@storybook/react-vite' -import '../src/global.css' -import './fonts.css' -import React from 'react' -import { withThemeByClassName } from '@storybook/addon-themes' -import { ThemedDocsContainer } from './themedDocsContainer' +import type { Preview, Decorator } from "@storybook/react-vite"; +import "../src/global.css"; +import "./fonts.css"; +import React from "react"; +import { withThemeByClassName } from "@storybook/addon-themes"; +import { ThemedDocsContainer } from "./themedDocsContainer"; -import { MoonshineConfigProvider } from '../src/context/ConfigContext' +import { MoonshineConfigProvider } from "../src/context/ConfigContext"; const moonshineConfigProviderDecorator: Decorator = (story, context) => { return ( {}}> {story()} - ) -} + ); +}; export const decorators: Decorator[] = [ withThemeByClassName({ - themes: { light: 'light', dark: 'dark' }, - defaultTheme: 'light', + themes: { light: "light", dark: "dark" }, + defaultTheme: "light", }), moonshineConfigProviderDecorator, -] +]; const preview: Preview = { parameters: { viewport: { options: { - small: { name: 'Small', styles: { width: '640px', height: '800px' } }, + small: { name: "Small", styles: { width: "640px", height: "800px" } }, large: { - name: 'Large', - styles: { width: '1024px', height: '1000px' }, + name: "Large", + styles: { width: "1024px", height: "1000px" }, }, }, }, backgrounds: { options: { - light: { name: 'light', value: '#fff' }, - dark: { name: 'dark', value: 'hsl(0, 0%, 7%)' }, + light: { name: "light", value: "#fff" }, + dark: { name: "dark", value: "hsl(0, 0%, 7%)" }, }, }, @@ -51,6 +51,6 @@ const preview: Preview = { }, }, }, -} +}; -export default preview +export default preview; diff --git a/.storybook/themedDocsContainer.tsx b/.storybook/themedDocsContainer.tsx index 9c382368..0e5744a4 100644 --- a/.storybook/themedDocsContainer.tsx +++ b/.storybook/themedDocsContainer.tsx @@ -1,46 +1,46 @@ -import { DocsContainer } from '@storybook/addon-docs/blocks' -import React, { useEffect, useState } from 'react' -import { themes } from 'storybook/theming' -import { speakeasyTheme } from './manager' +import { DocsContainer } from "@storybook/addon-docs/blocks"; +import React, { useEffect, useState } from "react"; +import { themes } from "storybook/theming"; +import { speakeasyTheme } from "./manager"; -import { addons } from 'storybook/preview-api' +import { addons } from "storybook/preview-api"; -const DARK_MODE_EVENT_NAME = 'DARK_MODE' +const DARK_MODE_EVENT_NAME = "DARK_MODE"; function useDocumentClassListObserver( - callback: (classList: DOMTokenList) => void + callback: (classList: DOMTokenList) => void, ) { useEffect(() => { // Define a MutationObserver that listens to changes in classList const observer = new MutationObserver((mutationsList) => { for (const mutation of mutationsList) { - if (mutation.attributeName === 'class') { - callback(document.documentElement.classList) + if (mutation.attributeName === "class") { + callback(document.documentElement.classList); } } - }) + }); // Start observing the documentElement for attribute changes - observer.observe(document.documentElement, { attributes: true }) + observer.observe(document.documentElement, { attributes: true }); // Clean up on component unmount - return () => observer.disconnect() - }, [callback]) + return () => observer.disconnect(); + }, [callback]); } export const ThemedDocsContainer = ({ children, context }) => { const [isDark, setIsDark] = useState( - document.documentElement.classList.contains('dark') - ) + document.documentElement.classList.contains("dark"), + ); useDocumentClassListObserver((classList) => { - setIsDark(classList.contains('dark')) - }) + setIsDark(classList.contains("dark")); + }); useEffect(() => { - const chan = addons.getChannel() - chan.on(DARK_MODE_EVENT_NAME, setIsDark) - return () => chan.off(DARK_MODE_EVENT_NAME, setIsDark) - }, []) + const chan = addons.getChannel(); + chan.on(DARK_MODE_EVENT_NAME, setIsDark); + return () => chan.off(DARK_MODE_EVENT_NAME, setIsDark); + }, []); return ( { > {children} - ) -} + ); +}; diff --git a/.vscode/extensions.json b/.vscode/extensions.json index e34b53e5..f31c27cd 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,4 +3,4 @@ "bradlc.vscode-tailwindcss", // Tailwind CSS IntelliSense "dbaeumer.vscode-eslint" // ESLint extension ] -} \ No newline at end of file +} diff --git a/.vscode/settings.json b/.vscode/settings.json index f77351e1..667629c3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,4 +11,4 @@ "files.associations": { "global.css": "tailwindcss" } -} \ No newline at end of file +} diff --git a/CLAUDE.md b/CLAUDE.md index 9e816a1a..41200128 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -22,21 +22,24 @@ src/ This file contains four main sections: 1. **Primitive Tokens** - Raw design values (should not be used directly) + ```css --color-neutral-200: hsl(0, 0%, 92%); - --font-diatype: 'Diatype', -apple-system, ...; + --font-diatype: "Diatype", -apple-system, ...; ``` 2. **Semantic/Utility Tokens** - Theme-aware variables that map to utilities + ```css /* Light mode */ --text-warning: var(--color-feedback-orange-700); --bg-warning: var(--color-feedback-orange-100); - + /* Automatically switches in dark mode */ ``` 3. **Component Tokens** - Higher-level semantic tokens + ```css --radius: 0.625rem; --shadow: hsl(0 0% 50%); @@ -67,6 +70,7 @@ Handles Tailwind configuration and imports: ### Why Custom Typography Utilities? Instead of allowing arbitrary combinations like: + ```css /* ❌ Bad - leads to inconsistency */ .heading { @@ -75,6 +79,7 @@ Instead of allowing arbitrary combinations like: ``` We provide semantic utilities: + ```css /* ✅ Good - enforces design system */ .heading { @@ -98,6 +103,7 @@ The "utility tokens" pattern in base.css enables automatic theme switching: ### Preventing Design System Escape Hatches We intentionally: + - Don't expose raw color values as utilities - Provide complete typography utilities (not individual properties) - Use semantic naming to guide correct usage @@ -119,6 +125,7 @@ We intentionally: ### Extending Responsive Utilities Add new `@source` declarations in global.css: + ```css @source inline("{,sm:,md:,lg:,xl:,2xl:}your-utility-{value1,value2}"); ``` @@ -738,7 +745,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 color: var(--color-neutral-900); /* dark variant */ - color: var(--color-neutral-100); + color: var(--color-neutral-100); } ``` @@ -749,7 +756,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 color: var(--color-base-black); /* dark variant */ - color: var(--color-base-white); + color: var(--color-base-white); } ``` @@ -760,7 +767,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 color: var(--color-base-black); /* dark variant */ - color: var(--color-base-white); + color: var(--color-base-white); } ``` @@ -772,8 +779,8 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 opacity: 0.4; /* dark variant */ - color: var(--color-neutral-100); - opacity: 0.4; + color: var(--color-neutral-100); + opacity: 0.4; } ``` @@ -784,7 +791,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 color: var(--color-neutral-100); /* dark variant */ - color: var(--color-neutral-900); + color: var(--color-neutral-900); } ``` @@ -795,7 +802,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 color: var(--color-base-white); /* dark variant */ - color: var(--color-base-black); + color: var(--color-base-black); } ``` @@ -806,7 +813,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 color: var(--color-base-white); /* dark variant */ - color: var(--color-base-black); + color: var(--color-base-black); } ``` @@ -818,8 +825,8 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 opacity: 0.4; /* dark variant */ - color: var(--color-neutral-900); - opacity: 0.4; + color: var(--color-neutral-900); + opacity: 0.4; } ``` @@ -830,7 +837,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 color: var(--color-neutral-900); /* dark variant */ - color: var(--color-neutral-100); + color: var(--color-neutral-100); } ``` @@ -841,7 +848,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 color: var(--color-base-black); /* dark variant */ - color: var(--color-base-white); + color: var(--color-base-white); } ``` @@ -852,7 +859,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 color: var(--color-base-black); /* dark variant */ - color: var(--color-base-white); + color: var(--color-base-white); } ``` @@ -864,8 +871,8 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 opacity: 0.4; /* dark variant */ - color: var(--color-neutral-100); - opacity: 0.4; + color: var(--color-neutral-100); + opacity: 0.4; } ``` @@ -876,7 +883,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 color: var(--color-neutral-900); /* dark variant */ - color: var(--color-neutral-100); + color: var(--color-neutral-100); } ``` @@ -887,7 +894,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 color: var(--color-base-black); /* dark variant */ - color: var(--color-base-white); + color: var(--color-base-white); } ``` @@ -898,7 +905,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 color: var(--color-base-black); /* dark variant */ - color: var(--color-base-white); + color: var(--color-base-white); } ``` @@ -910,8 +917,8 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 opacity: 0.4; /* dark variant */ - color: var(--color-neutral-100); - opacity: 0.4; + color: var(--color-neutral-100); + opacity: 0.4; } ``` @@ -955,7 +962,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 color: var(--color-feedback-red-600); /* dark variant */ - color: var(--color-feedback-red-400); + color: var(--color-feedback-red-400); } ``` @@ -966,7 +973,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 color: var(--color-feedback-red-700); /* dark variant */ - color: var(--color-feedback-red-500); + color: var(--color-feedback-red-500); } ``` @@ -977,7 +984,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 color: var(--color-feedback-red-800); /* dark variant */ - color: var(--color-feedback-red-600); + color: var(--color-feedback-red-600); } ``` @@ -989,8 +996,8 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 opacity: 0.4; /* dark variant */ - color: var(--color-feedback-red-400); - opacity: 0.4; + color: var(--color-feedback-red-400); + opacity: 0.4; } ``` @@ -1049,7 +1056,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 background-color: var(--color-base-white); /* dark variant */ - background-color: var(--color-base-black); + background-color: var(--color-base-black); } ``` @@ -1060,7 +1067,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 background-color: var(--color-neutral-100); /* dark variant */ - background-color: var(--color-neutral-900); + background-color: var(--color-neutral-900); } ``` @@ -1079,7 +1086,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 background-color: var(--color-base-white); /* dark variant */ - background-color: var(--color-base-black); + background-color: var(--color-base-black); } ``` @@ -1090,7 +1097,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 background-color: var(--color-neutral-100); /* dark variant */ - background-color: var(--color-neutral-900); + background-color: var(--color-neutral-900); } ``` @@ -1101,7 +1108,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 background-color: var(--color-neutral-200); /* dark variant */ - background-color: var(--color-neutral-800); + background-color: var(--color-neutral-800); } ``` @@ -1114,7 +1121,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 color: var(--text-muted); /* dark variant */ - background-color: var(--color-base-black); + background-color: var(--color-base-black); } ``` @@ -1125,7 +1132,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 background-color: var(--color-neutral-800); /* dark variant */ - background-color: var(--color-neutral-200); + background-color: var(--color-neutral-200); } ``` @@ -1136,7 +1143,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 background-color: var(--color-neutral-900); /* dark variant */ - background-color: var(--color-neutral-100); + background-color: var(--color-neutral-100); } ``` @@ -1147,7 +1154,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 background-color: var(--color-base-black); /* dark variant */ - background-color: var(--color-base-white); + background-color: var(--color-base-white); } ``` @@ -1159,8 +1166,8 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 opacity: 0.4; /* dark variant */ - background-color: var(--color-neutral-200); - opacity: 0.4; + background-color: var(--color-neutral-200); + opacity: 0.4; } ``` @@ -1171,7 +1178,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 background-color: var(--color-neutral-200); /* dark variant */ - background-color: var(--color-neutral-800); + background-color: var(--color-neutral-800); } ``` @@ -1182,7 +1189,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 background-color: var(--color-neutral-300); /* dark variant */ - background-color: var(--color-neutral-700); + background-color: var(--color-neutral-700); } ``` @@ -1193,7 +1200,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 background-color: var(--color-neutral-400); /* dark variant */ - background-color: var(--color-neutral-600); + background-color: var(--color-neutral-600); } ``` @@ -1205,8 +1212,8 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 opacity: 0.4; /* dark variant */ - background-color: var(--color-neutral-800); - opacity: 0.4; + background-color: var(--color-neutral-800); + opacity: 0.4; } ``` @@ -1217,7 +1224,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 background-color: var(--color-feedback-red-600); /* dark variant */ - background-color: var(--color-feedback-red-400); + background-color: var(--color-feedback-red-400); } ``` @@ -1228,7 +1235,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 background-color: var(--color-feedback-red-700); /* dark variant */ - background-color: var(--color-feedback-red-500); + background-color: var(--color-feedback-red-500); } ``` @@ -1239,7 +1246,7 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 background-color: var(--color-feedback-red-800); /* dark variant */ - background-color: var(--color-feedback-red-600); + background-color: var(--color-feedback-red-600); } ``` @@ -1251,8 +1258,8 @@ This section is auto-generated from the CSS files. Last updated: 2026-05-19T16:2 opacity: 0.4; /* dark variant */ - background-color: var(--color-feedback-red-400); - opacity: 0.4; + background-color: var(--color-feedback-red-400); + opacity: 0.4; } ``` diff --git a/README.md b/README.md index ae0442ce..ed7e27f1 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Add this to the top of your project's CSS file where you configure Tailwind: @reference "../node_modules/@speakeasy-api/moonshine/src/global.css"; /* Your Tailwind setup */ -@import 'tailwindcss'; +@import "tailwindcss"; ``` **Note:** The `@reference` directive is required for Tailwind v4 to recognize Moonshine's custom utilities and make them available in your project. @@ -38,7 +38,7 @@ Add this to the top of your project's CSS file where you configure Tailwind: In your main app file (or root layout): ```ts -import '@speakeasy-api/moonshine/moonshine.css' +import "@speakeasy-api/moonshine/moonshine.css"; ``` ### 4. Set up the Provider @@ -46,10 +46,10 @@ import '@speakeasy-api/moonshine/moonshine.css' Wrap your application in the `MoonshineConfigProvider` component: ```tsx -import { MoonshineConfigProvider } from '@speakeasy-api/moonshine' -; +import { MoonshineConfigProvider } from "@speakeasy-api/moonshine"; + - +; ``` ### 5. Configure Custom Fonts (Optional) @@ -59,16 +59,16 @@ Moonshine uses custom fonts (Diatype, Tobias). If you have licenses for these fo ```css /* In your global CSS */ @font-face { - font-family: 'Diatype'; - src: url('/fonts/diatype/ABCDiatype-Regular.woff2') format('woff2'); + font-family: "Diatype"; + src: url("/fonts/diatype/ABCDiatype-Regular.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: block; } @font-face { - font-family: 'Diatype'; - src: url('/fonts/diatype/ABCDiatype-Light.woff2') format('woff2'); + font-family: "Diatype"; + src: url("/fonts/diatype/ABCDiatype-Light.woff2") format("woff2"); font-weight: 300; font-style: normal; font-display: block; @@ -82,10 +82,10 @@ If you don't have these fonts, the design system will fall back to system fonts. ### 6. Use Components and Utilities ```tsx -import { Grid } from '@speakeasy-api/moonshine' +import { Grid } from "@speakeasy-api/moonshine"; // Use semantic utility classes -;
Hello Moonshine!
+
Hello Moonshine!
; ``` ### TypeScript Support for Utility Classes @@ -93,15 +93,15 @@ import { Grid } from '@speakeasy-api/moonshine' Moonshine provides TypeScript types for all available utility classes to improve your development experience: ```tsx -import type { MoonshineClasses } from '@speakeasy-api/moonshine/types/utilities' +import type { MoonshineClasses } from "@speakeasy-api/moonshine/types/utilities"; // Use for type-safe className props interface MyComponentProps { - className?: MoonshineClasses + className?: MoonshineClasses; } // Get autocomplete for all available utilities -const styles: MoonshineClasses = 'text-heading-lg' // ✅ Autocompletes! +const styles: MoonshineClasses = "text-heading-lg"; // ✅ Autocompletes! ``` The types are automatically generated during the build process and include: @@ -119,15 +119,15 @@ The package is built with [vite](https://vitejs.dev/), and is distributed in bot Moonshine exports a custom wrapper for Tailwind Merge to avoid unexpected clashes between semantic class names. This version should be used in favour of using tailwind merge directly. ```tsx -import { cn } from '@speakeasy-api/moonshine' +import { cn } from "@speakeasy-api/moonshine"; return ( Lorem Ipsum -) +); ``` ## Design System Architecture @@ -139,13 +139,11 @@ Moonshine is a utility-first design system built on top of [Tailwind CSS v4](htt Our CSS is organized into three main files: 1. **`base.css`** - Primitive design tokens (colors, fonts, spacing scales) - - Contains raw values that should **not** be used directly in components - Defines theme-aware semantic tokens that adapt to light/dark mode - Houses base element styles and resets 2. **`utilities.css`** - The public API of our design system - - Exposes carefully crafted utility classes like `text-heading-xl`, `bg-surface-primary` - Enforces typography combinations to prevent arbitrary text styling - Provides semantic color utilities that automatically handle theming @@ -197,7 +195,7 @@ Ensure the `themeElement` prop in `MoonshineConfigProvider` points to the elemen Make sure you're importing from the correct path: ```tsx -import type { MoonshineClasses } from '@speakeasy-api/moonshine/types/utilities' +import type { MoonshineClasses } from "@speakeasy-api/moonshine/types/utilities"; ``` ## Contributing @@ -275,7 +273,7 @@ pnpm link ../path/to/moonshine The lockfile file within your app should referenced the linked copy: ```yaml -'@speakeasy-api/moonshine': +"@speakeasy-api/moonshine": specifier: ^0.43.1 version: link:../../../../moonshine ``` diff --git a/components.json b/components.json index 6d4ed3d3..3df5523a 100644 --- a/components.json +++ b/components.json @@ -17,4 +17,4 @@ "lib": "@/lib", "hooks": "@/hooks" } -} \ No newline at end of file +} diff --git a/cubic.yaml b/cubic.yaml index 0e8cc2e4..6cb59ccb 100644 --- a/cubic.yaml +++ b/cubic.yaml @@ -20,7 +20,7 @@ reviews: auto_approve: disabled ignore: files: - - '**/*.svg' + - "**/*.svg" - docs/ - tests/visual/storybook.visual.spec.ts-snapshots/ pr_labels: diff --git a/integration/integration.test.tsx b/integration/integration.test.tsx index 85f8f97d..b33ccc3c 100644 --- a/integration/integration.test.tsx +++ b/integration/integration.test.tsx @@ -1,4 +1,4 @@ -import { render, screen } from '@testing-library/react' +import { render, screen } from "@testing-library/react"; import { Card, Grid, @@ -13,106 +13,106 @@ import { Score, Container, Combobox, -} from '@speakeasy-api/moonshine' -import { describe, it, expect } from 'vitest' +} from "@speakeasy-api/moonshine"; +import { describe, it, expect } from "vitest"; // This is a sense check that the components in the **PUBLISHED** package are working -describe('Card', () => { - it('renders', () => { +describe("Card", () => { + it("renders", () => { render( Header Content - - - ) + + , + ); - expect(screen.getByText('Header')).toBeInTheDocument() - expect(screen.getByText('Content')).toBeInTheDocument() - expect(screen.getByText('Footer')).toBeInTheDocument() - }) -}) + expect(screen.getByText("Header")).toBeInTheDocument(); + expect(screen.getByText("Content")).toBeInTheDocument(); + expect(screen.getByText("Footer")).toBeInTheDocument(); + }); +}); -describe('Grid', () => { - it('renders', () => { +describe("Grid", () => { + it("renders", () => { render(
1
2
3
-
- ) - }) -}) + , + ); + }); +}); -describe('Separator', () => { - it('renders', () => { - render() - }) -}) +describe("Separator", () => { + it("renders", () => { + render(); + }); +}); -describe('Button', () => { - it('renders', () => { - render() - }) -}) +describe("Button", () => { + it("renders", () => { + render(); + }); +}); -describe('Icon', () => { - it('renders', () => { - render() - }) -}) +describe("Icon", () => { + it("renders", () => { + render(); + }); +}); -describe('Stack', () => { - it('renders', () => { +describe("Stack", () => { + it("renders", () => { render(
1
2
-
- ) - }) -}) + , + ); + }); +}); -describe('Badge', () => { - it('renders', () => { - render(Default) - }) -}) +describe("Badge", () => { + it("renders", () => { + render(Default); + }); +}); -describe('Heading', () => { - it('renders', () => { - render(Heading) - }) -}) +describe("Heading", () => { + it("renders", () => { + render(Heading); + }); +}); -describe('Score', () => { - it('renders', () => { - render() - }) -}) +describe("Score", () => { + it("renders", () => { + render(); + }); +}); -describe('Text', () => { - it('renders', () => { - render(Text) - }) -}) +describe("Text", () => { + it("renders", () => { + render(Text); + }); +}); -describe('Logo', () => { - it('renders', () => { - render() - }) -}) +describe("Logo", () => { + it("renders", () => { + render(); + }); +}); -describe('Container', () => { - it('renders', () => { - render(Container) - }) -}) +describe("Container", () => { + it("renders", () => { + render(Container); + }); +}); -describe('Combobox', () => { - it('renders', () => { - render() - }) -}) +describe("Combobox", () => { + it("renders", () => { + render(); + }); +}); diff --git a/integration/setup.js b/integration/setup.js index f533ae70..971941d9 100644 --- a/integration/setup.js +++ b/integration/setup.js @@ -1,9 +1,9 @@ -import { expect, afterEach } from 'vitest' -import { cleanup } from '@testing-library/react' -import * as matchers from '@testing-library/jest-dom/matchers' +import { expect, afterEach } from "vitest"; +import { cleanup } from "@testing-library/react"; +import * as matchers from "@testing-library/jest-dom/matchers"; -expect.extend(matchers) +expect.extend(matchers); afterEach(() => { - cleanup() -}) + cleanup(); +}); diff --git a/integration/tsconfig.json b/integration/tsconfig.json index 2bf0ba5d..0168b6b0 100644 --- a/integration/tsconfig.json +++ b/integration/tsconfig.json @@ -13,8 +13,6 @@ "forceConsistentCasingInFileNames": true, "skipLibCheck": true, "baseUrl": ".", - "types": [ - "@testing-library/jest-dom" - ], - }, -} \ No newline at end of file + "types": ["@testing-library/jest-dom"] + } +} diff --git a/integration/vite.config.mts b/integration/vite.config.mts index 1b6241ce..b30f64e8 100644 --- a/integration/vite.config.mts +++ b/integration/vite.config.mts @@ -1,19 +1,19 @@ -import { defineConfig } from 'vite' -import path, { dirname } from 'path' -import { fileURLToPath } from 'url' +import { defineConfig } from "vite"; +import path, { dirname } from "path"; +import { fileURLToPath } from "url"; -const __dirname = dirname(fileURLToPath(import.meta.url)) +const __dirname = dirname(fileURLToPath(import.meta.url)); export default defineConfig({ test: { globals: true, - environment: 'jsdom', - setupFiles: './setup.js', + environment: "jsdom", + setupFiles: "./setup.js", }, - define: process.env.VITEST ? {} : { global: 'window' }, + define: process.env.VITEST ? {} : { global: "window" }, resolve: { alias: { - react: path.resolve(__dirname, 'node_modules/react'), + react: path.resolve(__dirname, "node_modules/react"), }, }, -}) +}); diff --git a/postcss.config.mjs b/postcss.config.mjs index 2b4c7ed9..3e0d24c4 100644 --- a/postcss.config.mjs +++ b/postcss.config.mjs @@ -1,3 +1,3 @@ export default { plugins: {}, -} +}; diff --git a/scripts/generate-utility-docs.js b/scripts/generate-utility-docs.js index 1d75be52..15c4581e 100644 --- a/scripts/generate-utility-docs.js +++ b/scripts/generate-utility-docs.js @@ -1,112 +1,112 @@ #!/usr/bin/env node -const fs = require('fs') -const path = require('path') -const postcss = require('postcss') +const fs = require("fs"); +const path = require("path"); +const postcss = require("postcss"); // Paths -const UTILITIES_CSS = path.join(__dirname, '../src/utilities.css') -const BASE_CSS = path.join(__dirname, '../src/base.css') -const CLAUDE_MD = path.join(__dirname, '../CLAUDE.md') -const TYPES_DIR = path.join(__dirname, '../types') -const UTILITIES_TYPES = path.join(TYPES_DIR, 'utilities.d.ts') +const UTILITIES_CSS = path.join(__dirname, "../src/utilities.css"); +const BASE_CSS = path.join(__dirname, "../src/base.css"); +const CLAUDE_MD = path.join(__dirname, "../CLAUDE.md"); +const TYPES_DIR = path.join(__dirname, "../types"); +const UTILITIES_TYPES = path.join(TYPES_DIR, "utilities.d.ts"); // Ensure types directory exists if (!fs.existsSync(TYPES_DIR)) { - fs.mkdirSync(TYPES_DIR) + fs.mkdirSync(TYPES_DIR); } // Parse CSS and extract utilities async function extractUtilities(cssPath) { if (!fs.existsSync(cssPath)) { - console.error(`CSS file not found: ${cssPath}`) - return [] + console.error(`CSS file not found: ${cssPath}`); + return []; } - const css = fs.readFileSync(cssPath, 'utf8') + const css = fs.readFileSync(cssPath, "utf8"); - let ast + let ast; try { - ast = postcss.parse(css) + ast = postcss.parse(css); } catch (error) { - console.error(`Error parsing CSS file ${cssPath}:`, error.message) - return [] + console.error(`Error parsing CSS file ${cssPath}:`, error.message); + return []; } - const utilities = [] + const utilities = []; - ast.walkAtRules('utility', (rule) => { - const name = rule.params.trim() + ast.walkAtRules("utility", (rule) => { + const name = rule.params.trim(); // Extract the CSS properties - const properties = [] + const properties = []; rule.walkDecls((decl) => { // Skip nested rules (like @variant) if (decl.parent === rule) { - properties.push(` ${decl.prop}: ${decl.value};`) + properties.push(` ${decl.prop}: ${decl.value};`); } - }) + }); // Check for variant rules (like dark mode) - const variants = [] - rule.walkAtRules('variant', (variantRule) => { - const variantProps = [] + const variants = []; + rule.walkAtRules("variant", (variantRule) => { + const variantProps = []; variantRule.walkDecls((decl) => { - variantProps.push(` ${decl.prop}: ${decl.value};`) - }) + variantProps.push(` ${decl.prop}: ${decl.value};`); + }); variants.push({ name: variantRule.params.trim(), properties: variantProps, - }) - }) + }); + }); utilities.push({ name, properties, variants, raw: rule.toString(), - }) - }) + }); + }); - return utilities + return utilities; } // Extract theme colors that are exposed as utilities async function extractThemeColors(cssPath) { - const css = fs.readFileSync(cssPath, 'utf8') - const ast = postcss.parse(css) - const colors = new Set() + const css = fs.readFileSync(cssPath, "utf8"); + const ast = postcss.parse(css); + const colors = new Set(); - ast.walkAtRules('theme', (rule) => { + ast.walkAtRules("theme", (rule) => { rule.walkDecls((decl) => { - if (decl.prop.startsWith('--color-') && !decl.prop.includes('*')) { - const colorName = decl.prop.replace('--color-', '') + if (decl.prop.startsWith("--color-") && !decl.prop.includes("*")) { + const colorName = decl.prop.replace("--color-", ""); // Only include semantic colors, not raw values if ( !colorName.match( - /^(brand|neutral|destructive|warning|success|info)-\d+$/ + /^(brand|neutral|destructive|warning|success|info)-\d+$/, ) ) { - colors.add(colorName) + colors.add(colorName); } } - }) - }) + }); + }); - return Array.from(colors) + return Array.from(colors); } // Generate TypeScript definitions function generateTypeDefinitions(utilities, themeColors) { - const utilityTypes = utilities.map((u) => ` | '${u.name}'`).join('\n') + const utilityTypes = utilities.map((u) => ` | '${u.name}'`).join("\n"); // Generate color utility types - const colorPrefixes = ['bg', 'text', 'border', 'ring', 'divide'] + const colorPrefixes = ["bg", "text", "border", "ring", "divide"]; const colorUtilities = themeColors .flatMap((color) => - colorPrefixes.map((prefix) => ` | '${prefix}-${color}'`) + colorPrefixes.map((prefix) => ` | '${prefix}-${color}'`), ) - .join('\n') + .join("\n"); return `/** * Auto-generated TypeScript definitions for Moonshine utilities @@ -138,187 +138,187 @@ export type MoonshineClasses = MoonshineUtilities | MoonshineColorUtilities; * Helper type for className props */ export type MoonshineClassName = MoonshineClasses | MoonshineClasses[] | undefined | null | false; -` +`; } // Generate documentation section for CLAUDE.md function generateDocSection(utilities) { - const sections = [] + const sections = []; // Group utilities by type const typography = utilities.filter( - (u) => u.name.startsWith('text-') || u.name.startsWith('typography-') - ) - const backgrounds = utilities.filter((u) => u.name.startsWith('bg-')) - const borders = utilities.filter((u) => u.name.startsWith('border-')) + (u) => u.name.startsWith("text-") || u.name.startsWith("typography-"), + ); + const backgrounds = utilities.filter((u) => u.name.startsWith("bg-")); + const borders = utilities.filter((u) => u.name.startsWith("border-")); const other = utilities.filter( (u) => - !u.name.startsWith('text-') && - !u.name.startsWith('typography-') && - !u.name.startsWith('bg-') && - !u.name.startsWith('border-') - ) + !u.name.startsWith("text-") && + !u.name.startsWith("typography-") && + !u.name.startsWith("bg-") && + !u.name.startsWith("border-"), + ); - sections.push('## Available Utility Classes\n') + sections.push("## Available Utility Classes\n"); sections.push( - 'This section is auto-generated from the CSS files. Last updated: ' + + "This section is auto-generated from the CSS files. Last updated: " + new Date().toISOString() + - '\n' - ) + "\n", + ); // Typography if (typography.length > 0) { - sections.push('### Typography Utilities\n') + sections.push("### Typography Utilities\n"); typography.forEach((u) => { - sections.push(`#### \`${u.name}\`\n`) - sections.push('```css') - sections.push(`.${u.name} {`) - sections.push(u.properties.join('\n')) + sections.push(`#### \`${u.name}\`\n`); + sections.push("```css"); + sections.push(`.${u.name} {`); + sections.push(u.properties.join("\n")); if (u.variants.length > 0) { u.variants.forEach((v) => { - sections.push(`\n /* ${v.name} variant */`) - sections.push(v.properties.join('\n')) - }) + sections.push(`\n /* ${v.name} variant */`); + sections.push(v.properties.join("\n")); + }); } - sections.push('}') - sections.push('```\n') - }) + sections.push("}"); + sections.push("```\n"); + }); } // Backgrounds if (backgrounds.length > 0) { - sections.push('### Background Utilities\n') + sections.push("### Background Utilities\n"); backgrounds.forEach((u) => { - sections.push(`#### \`${u.name}\`\n`) - sections.push('```css') - sections.push(`.${u.name} {`) - sections.push(u.properties.join('\n')) + sections.push(`#### \`${u.name}\`\n`); + sections.push("```css"); + sections.push(`.${u.name} {`); + sections.push(u.properties.join("\n")); if (u.variants.length > 0) { u.variants.forEach((v) => { - sections.push(`\n /* ${v.name} variant */`) - sections.push(v.properties.join('\n')) - }) + sections.push(`\n /* ${v.name} variant */`); + sections.push(v.properties.join("\n")); + }); } - sections.push('}') - sections.push('```\n') - }) + sections.push("}"); + sections.push("```\n"); + }); } // Borders if (borders.length > 0) { - sections.push('### Border Utilities\n') + sections.push("### Border Utilities\n"); borders.forEach((u) => { - sections.push(`#### \`${u.name}\`\n`) - sections.push('```css') - sections.push(`.${u.name} {`) - sections.push(u.properties.join('\n')) - sections.push('}') - sections.push('```\n') - }) + sections.push(`#### \`${u.name}\`\n`); + sections.push("```css"); + sections.push(`.${u.name} {`); + sections.push(u.properties.join("\n")); + sections.push("}"); + sections.push("```\n"); + }); } // Other if (other.length > 0) { - sections.push('### Other Utilities\n') + sections.push("### Other Utilities\n"); other.forEach((u) => { - sections.push(`#### \`${u.name}\`\n`) - sections.push('```css') - sections.push(`.${u.name} {`) - sections.push(u.properties.join('\n')) - sections.push('}') - sections.push('```\n') - }) + sections.push(`#### \`${u.name}\`\n`); + sections.push("```css"); + sections.push(`.${u.name} {`); + sections.push(u.properties.join("\n")); + sections.push("}"); + sections.push("```\n"); + }); } - return sections.join('\n') + return sections.join("\n"); } // Update CLAUDE.md with generated content function updateClaudeMd(newContent) { - const claudeMd = fs.readFileSync(CLAUDE_MD, 'utf8') + const claudeMd = fs.readFileSync(CLAUDE_MD, "utf8"); // Find the section to replace - const startMarker = '## Available Utility Classes' - const endMarker = '## Migration Notes' + const startMarker = "## Available Utility Classes"; + const endMarker = "## Migration Notes"; - const startIndex = claudeMd.indexOf(startMarker) - let endIndex = claudeMd.indexOf(endMarker) + const startIndex = claudeMd.indexOf(startMarker); + let endIndex = claudeMd.indexOf(endMarker); // If end marker not found, look for the next ## heading if (endIndex === -1) { - const afterStart = claudeMd.substring(startIndex + startMarker.length) - const nextHeadingMatch = afterStart.match(/\n## /) + const afterStart = claudeMd.substring(startIndex + startMarker.length); + const nextHeadingMatch = afterStart.match(/\n## /); if (nextHeadingMatch) { - endIndex = startIndex + startMarker.length + nextHeadingMatch.index + 1 + endIndex = startIndex + startMarker.length + nextHeadingMatch.index + 1; } } if (startIndex === -1) { console.error( - 'Could not find "## Available Utility Classes" section in CLAUDE.md' - ) + 'Could not find "## Available Utility Classes" section in CLAUDE.md', + ); console.error( - 'Please ensure CLAUDE.md contains this section for auto-generation to work' - ) - return + "Please ensure CLAUDE.md contains this section for auto-generation to work", + ); + return; } if (endIndex === -1 || endIndex <= startIndex) { // No end marker found, append to end of file - const updated = claudeMd + '\n\n' + newContent - fs.writeFileSync(CLAUDE_MD, updated, 'utf8') - console.log(' Appended to end of CLAUDE.md (no end marker found)') + const updated = claudeMd + "\n\n" + newContent; + fs.writeFileSync(CLAUDE_MD, updated, "utf8"); + console.log(" Appended to end of CLAUDE.md (no end marker found)"); } else { - const before = claudeMd.substring(0, startIndex) - const after = claudeMd.substring(endIndex) - const updated = before + newContent + '\n' + after - fs.writeFileSync(CLAUDE_MD, updated, 'utf8') + const before = claudeMd.substring(0, startIndex); + const after = claudeMd.substring(endIndex); + const updated = before + newContent + "\n" + after; + fs.writeFileSync(CLAUDE_MD, updated, "utf8"); } } // Main function async function main() { - console.log('🎨 Generating Moonshine utility documentation...\n') + console.log("🎨 Generating Moonshine utility documentation...\n"); try { // Extract utilities - console.log('📖 Reading utilities.css...') - const utilities = await extractUtilities(UTILITIES_CSS) - console.log(` Found ${utilities.length} utility classes`) + console.log("📖 Reading utilities.css..."); + const utilities = await extractUtilities(UTILITIES_CSS); + console.log(` Found ${utilities.length} utility classes`); // Extract theme colors - console.log('🎨 Reading theme colors from utilities.css...') - const themeColors = await extractThemeColors(UTILITIES_CSS) - console.log(` Found ${themeColors.length} theme colors`) + console.log("🎨 Reading theme colors from utilities.css..."); + const themeColors = await extractThemeColors(UTILITIES_CSS); + console.log(` Found ${themeColors.length} theme colors`); // Generate TypeScript definitions - console.log('\n📝 Generating TypeScript definitions...') - const typeDefs = generateTypeDefinitions(utilities, themeColors) - fs.writeFileSync(UTILITIES_TYPES, typeDefs, 'utf8') + console.log("\n📝 Generating TypeScript definitions..."); + const typeDefs = generateTypeDefinitions(utilities, themeColors); + fs.writeFileSync(UTILITIES_TYPES, typeDefs, "utf8"); console.log( - ` Written to ${path.relative(process.cwd(), UTILITIES_TYPES)}` - ) + ` Written to ${path.relative(process.cwd(), UTILITIES_TYPES)}`, + ); // Generate documentation - console.log('\n📚 Generating documentation...') - const docSection = generateDocSection(utilities) - updateClaudeMd(docSection) - console.log(` Updated ${path.relative(process.cwd(), CLAUDE_MD)}`) + console.log("\n📚 Generating documentation..."); + const docSection = generateDocSection(utilities); + updateClaudeMd(docSection); + console.log(` Updated ${path.relative(process.cwd(), CLAUDE_MD)}`); - console.log('\n✅ Documentation generation complete!') - console.log('\n💡 Tip: Import the types in your components:') + console.log("\n✅ Documentation generation complete!"); + console.log("\n💡 Tip: Import the types in your components:"); console.log( - ' import type { MoonshineClasses } from "@speakeasy-api/moonshine/types/utilities"' - ) + ' import type { MoonshineClasses } from "@speakeasy-api/moonshine/types/utilities"', + ); } catch (error) { - console.error('\n❌ Error generating documentation:', error) - process.exit(1) + console.error("\n❌ Error generating documentation:", error); + process.exit(1); } } // Run the script -main() +main(); diff --git a/src/base.css b/src/base.css index 036b8b61..4b5a0c2e 100644 --- a/src/base.css +++ b/src/base.css @@ -20,12 +20,14 @@ * ============================================ */ /* Font Families */ - --font-diatype: 'Diatype', -apple-system, BlinkMacSystemFont, 'Segoe UI', - Roboto, Helvetica, Arial, sans-serif; - --font-diatype-mono: 'Diatype Mono', SFMono-Regular, Menlo, Monaco, Consolas, - 'Liberation Mono', 'Courier New', monospace; - --font-tobias: 'Tobias', 'Cormorant', Georgia, Cambria, 'Times New Roman', - Times, serif; + --font-diatype: + "Diatype", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, + Arial, sans-serif; + --font-diatype-mono: + "Diatype Mono", SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", + "Courier New", monospace; + --font-tobias: + "Tobias", "Cormorant", Georgia, Cambria, "Times New Roman", Times, serif; --color-base-white: hsl(0 0% 100%); --color-base-black: hsl(0 0% 0%); @@ -142,11 +144,12 @@ --color-brand-ruby: hsl(23, 96%, 62%); --color-brand-postman: hsl(0, 0%, 83%); - --gradient-brand-primary-colors: var(--color-brand-php) 0%, - var(--color-brand-swift) 12.56%, var(--color-brand-ruby) 25.06%, - var(--color-brand-unity) 37.56%, var(--color-brand-terraform) 50.06%, - var(--color-brand-c) 62.06%, var(--color-brand-go) 74.06%, - var(--color-brand-java) 86.06%, var(--color-brand-python) 97.06%; + --gradient-brand-primary-colors: + var(--color-brand-php) 0%, var(--color-brand-swift) 12.56%, + var(--color-brand-ruby) 25.06%, var(--color-brand-unity) 37.56%, + var(--color-brand-terraform) 50.06%, var(--color-brand-c) 62.06%, + var(--color-brand-go) 74.06%, var(--color-brand-java) 86.06%, + var(--color-brand-python) 97.06%; --gradient-brand-primary: linear-gradient( 90deg, @@ -171,10 +174,15 @@ --color-feedback-red-800: hsl(1, 63%, 24%); --color-feedback-red-900: hsl(0, 62%, 17%); - --color-feedback-red-400-56: hsl(from var(--color-feedback-red-400) h s l / 56%); - --color-feedback-red-500-56: hsl(from var(--color-feedback-red-500) h s l / 56%); - --color-feedback-red-600-56: hsl(from var(--color-feedback-red-600) h s l / 56%); - + --color-feedback-red-400-56: hsl( + from var(--color-feedback-red-400) h s l / 56% + ); + --color-feedback-red-500-56: hsl( + from var(--color-feedback-red-500) h s l / 56% + ); + --color-feedback-red-600-56: hsl( + from var(--color-feedback-red-600) h s l / 56% + ); /* Orange */ --color-feedback-orange-100: hsl(29, 100%, 95%); @@ -187,8 +195,12 @@ --color-feedback-orange-800: hsl(19, 66%, 33%); --color-feedback-orange-900: hsl(18, 69%, 24%); - --color-feedback-orange-400-56: hsl(from var(--color-feedback-orange-400) h s l / 56%); - --color-feedback-orange-500-56: hsl(from var(--color-feedback-orange-500) h s l / 56%); + --color-feedback-orange-400-56: hsl( + from var(--color-feedback-orange-400) h s l / 56% + ); + --color-feedback-orange-500-56: hsl( + from var(--color-feedback-orange-500) h s l / 56% + ); /* Green */ --color-feedback-green-100: hsl(102, 35%, 93%); @@ -201,8 +213,12 @@ --color-feedback-green-800: hsl(104, 24%, 20%); --color-feedback-green-900: hsl(105, 24%, 13%); - --color-feedback-green-400-56: hsl(from var(--color-feedback-green-400) h s l / 56%); - --color-feedback-green-500-56: hsl(from var(--color-feedback-green-500) h s l / 56%); + --color-feedback-green-400-56: hsl( + from var(--color-feedback-green-400) h s l / 56% + ); + --color-feedback-green-500-56: hsl( + from var(--color-feedback-green-500) h s l / 56% + ); /* Blue */ --color-feedback-blue-100: hsl(212, 100%, 95%); @@ -215,12 +231,16 @@ --color-feedback-blue-800: hsl(217, 73%, 23%); --color-feedback-blue-900: hsl(218, 73%, 15%); - --color-feedback-blue-400-56: hsl(from var(--color-feedback-blue-400) h s l / 56%); - --color-feedback-blue-500-56: hsl(from var(--color-feedback-blue-500) h s l / 56%); + --color-feedback-blue-400-56: hsl( + from var(--color-feedback-blue-400) h s l / 56% + ); + --color-feedback-blue-500-56: hsl( + from var(--color-feedback-blue-500) h s l / 56% + ); /* Violet */ --color-feedback-violet-300: hsl(265, 77%, 65%); - --color-feedback-violet-600: hsl(268, 75%, 31%); + --color-feedback-violet-600: hsl(268, 75%, 31%); --color-link-default: var(--color-brand-blue-600); --color-link-visited-primary: var(--color-feedback-violet-600); @@ -280,7 +300,6 @@ --text-default-warning: var(--color-feedback-orange-700); --text-link-warning: var(--color-feedback-orange-900); - --text-body: var(--color-neutral-900); --text-body-muted: var(--color-neutral-400); @@ -289,13 +308,11 @@ --text-warning: var(--color-feedback-orange-700); --border-warning: var(--color-feedback-orange-300); - - /* Link utilities */ + /* Link utilities */ --underline-link-primary: var(--color-brand-blue-600); --underline-link-secondary: var(--color-base-black); --underline-link-visited: var(--color-feedback-violet-600); - /* ============================================ * BORDER TOKENS * ============================================ */ @@ -314,7 +331,6 @@ 0.2 ); /* Black with 20% opacity for light mode */ - /* Border Destructive utilities */ --border-destructive-highlight: var(--color-feedback-red-600); --border-destructive-default: var(--color-feedback-red-500); @@ -342,160 +358,156 @@ /* Border Focus utilities */ --border-focus: var(--color-brand-blue-600); - /* ============================================ * FILLS * ============================================ */ - /* Neutrals */ - --fill-neutral-highlight: var(--color-base-black); - --fill-neutral-active: var(--color-neutral-900); - --fill-neutral-default: var(--color-neutral-800); - --fill-neutral-muted: var(--color-neutral-800-56); - - /* Fixed dark fill */ - --fill-neutral-highlight-fixed-dark: var(--color-base-black); - --fill-neutral-default-fixed-dark: var(--color-neutral-800); - --fill-neutral-muted-fixed-dark: var(--color-neutral-900-56); - - /* Fixed light fill */ - --fill-neutral-highlight-fixed-light: var(--color-base-white); - --fill-neutral-default-fixed-light: var(--color-neutral-100); - --fill-neutral-muted-fixed-light: var(--color-neutral-100-56); - - /* Inverse fill */ - --fill-neutral-highlight-inverse: var(--color-base-white); - --fill-neutral-default-inverse: var(--color-neutral-100); - --fill-neutral-muted-inverse: var(--color-neutral-100-56); - - /* Links */ - --fill-link-primary: var(--color-brand-blue-600); - --fill-link-secondary: var(--color-base-black); - --fill-link-visited: var(--color-feedback-violet-600); - - /* Destructive */ - --fill-destructive-highlight: var(--color-feedback-red-600); - --fill-destructive-default: var(--color-feedback-red-500); - --fill-destructive-muted: var(--color-feedback-red-600-56); - - /* Information */ - --fill-information-highlight: var(--color-feedback-blue-600); - --fill-information-default: var(--color-feedback-blue-500); - --fill-information-muted: var(--color-feedback-blue-500-56); - - /* Success */ - --fill-success-highlight: var(--color-feedback-green-600); - --fill-success-default: var(--color-feedback-green-500); - --fill-success-muted: var(--color-feedback-green-500-56); - - /* Warning */ - --fill-warning-highlight: var(--color-feedback-orange-600); - --fill-warning-default: var(--color-feedback-orange-500); - --fill-warning-muted: var(--color-feedback-orange-500-56); - - /* ============================================ + /* Neutrals */ + --fill-neutral-highlight: var(--color-base-black); + --fill-neutral-active: var(--color-neutral-900); + --fill-neutral-default: var(--color-neutral-800); + --fill-neutral-muted: var(--color-neutral-800-56); + + /* Fixed dark fill */ + --fill-neutral-highlight-fixed-dark: var(--color-base-black); + --fill-neutral-default-fixed-dark: var(--color-neutral-800); + --fill-neutral-muted-fixed-dark: var(--color-neutral-900-56); + + /* Fixed light fill */ + --fill-neutral-highlight-fixed-light: var(--color-base-white); + --fill-neutral-default-fixed-light: var(--color-neutral-100); + --fill-neutral-muted-fixed-light: var(--color-neutral-100-56); + + /* Inverse fill */ + --fill-neutral-highlight-inverse: var(--color-base-white); + --fill-neutral-default-inverse: var(--color-neutral-100); + --fill-neutral-muted-inverse: var(--color-neutral-100-56); + + /* Links */ + --fill-link-primary: var(--color-brand-blue-600); + --fill-link-secondary: var(--color-base-black); + --fill-link-visited: var(--color-feedback-violet-600); + + /* Destructive */ + --fill-destructive-highlight: var(--color-feedback-red-600); + --fill-destructive-default: var(--color-feedback-red-500); + --fill-destructive-muted: var(--color-feedback-red-600-56); + + /* Information */ + --fill-information-highlight: var(--color-feedback-blue-600); + --fill-information-default: var(--color-feedback-blue-500); + --fill-information-muted: var(--color-feedback-blue-500-56); + + /* Success */ + --fill-success-highlight: var(--color-feedback-green-600); + --fill-success-default: var(--color-feedback-green-500); + --fill-success-muted: var(--color-feedback-green-500-56); + + /* Warning */ + --fill-warning-highlight: var(--color-feedback-orange-600); + --fill-warning-default: var(--color-feedback-orange-500); + --fill-warning-muted: var(--color-feedback-orange-500-56); + + /* ============================================ * Strokes * ============================================ */ - /* Neutrals */ - --stroke-neutral-highlight: var(--color-base-black); - --stroke-neutral-active: var(--color-neutral-900); - --stroke-neutral-default: var(--color-neutral-800); - --stroke-neutral-muted: var(--color-neutral-800-56); - - /* Fixed dark stroke */ - --stroke-neutral-highlight-fixed-dark: var(--color-base-black); - --stroke-neutral-default-fixed-dark: var(--color-neutral-800); - --stroke-neutral-muted-fixed-dark: var(--color-neutral-900-56); - - /* Fixed light stroke */ - --stroke-neutral-highlight-fixed-light: var(--color-base-white); - --stroke-neutral-default-fixed-light: var(--color-neutral-100); - --stroke-neutral-muted-fixed-light: var(--color-neutral-100-56); - - /* Inverse stroke */ - --stroke-neutral-highlight-inverse: var(--color-base-white); - --stroke-neutral-default-inverse: var(--color-neutral-100); - --stroke-neutral-muted-inverse: var(--color-neutral-100-56); - - /* Links */ - --stroke-link-primary: var(--color-brand-blue-600); - --stroke-link-secondary: var(--color-base-black); - --stroke-link-visited: var(--color-feedback-violet-600); - - /* Destructive */ - --stroke-destructive-highlight: var(--color-feedback-red-600); - --stroke-destructive-default: var(--color-feedback-red-500); - --stroke-destructive-muted: var(--color-feedback-red-600-56); - - /* Information */ - --stroke-information-highlight: var(--color-feedback-blue-600); - --stroke-information-default: var(--color-feedback-blue-500); - --stroke-information-muted: var(--color-feedback-blue-500-56); - - /* Success */ - --stroke-success-highlight: var(--color-feedback-green-600); - --stroke-success-default: var(--color-feedback-green-500); - --stroke-success-muted: var(--color-feedback-green-500-56); - - /* Warning */ - --stroke-warning-highlight: var(--color-feedback-orange-600); - --stroke-warning-default: var(--color-feedback-orange-500); - --stroke-warning-muted: var(--color-feedback-orange-500-56); + /* Neutrals */ + --stroke-neutral-highlight: var(--color-base-black); + --stroke-neutral-active: var(--color-neutral-900); + --stroke-neutral-default: var(--color-neutral-800); + --stroke-neutral-muted: var(--color-neutral-800-56); + + /* Fixed dark stroke */ + --stroke-neutral-highlight-fixed-dark: var(--color-base-black); + --stroke-neutral-default-fixed-dark: var(--color-neutral-800); + --stroke-neutral-muted-fixed-dark: var(--color-neutral-900-56); + + /* Fixed light stroke */ + --stroke-neutral-highlight-fixed-light: var(--color-base-white); + --stroke-neutral-default-fixed-light: var(--color-neutral-100); + --stroke-neutral-muted-fixed-light: var(--color-neutral-100-56); + + /* Inverse stroke */ + --stroke-neutral-highlight-inverse: var(--color-base-white); + --stroke-neutral-default-inverse: var(--color-neutral-100); + --stroke-neutral-muted-inverse: var(--color-neutral-100-56); + + /* Links */ + --stroke-link-primary: var(--color-brand-blue-600); + --stroke-link-secondary: var(--color-base-black); + --stroke-link-visited: var(--color-feedback-violet-600); + + /* Destructive */ + --stroke-destructive-highlight: var(--color-feedback-red-600); + --stroke-destructive-default: var(--color-feedback-red-500); + --stroke-destructive-muted: var(--color-feedback-red-600-56); + + /* Information */ + --stroke-information-highlight: var(--color-feedback-blue-600); + --stroke-information-default: var(--color-feedback-blue-500); + --stroke-information-muted: var(--color-feedback-blue-500-56); + + /* Success */ + --stroke-success-highlight: var(--color-feedback-green-600); + --stroke-success-default: var(--color-feedback-green-500); + --stroke-success-muted: var(--color-feedback-green-500-56); + + /* Warning */ + --stroke-warning-highlight: var(--color-feedback-orange-600); + --stroke-warning-default: var(--color-feedback-orange-500); + --stroke-warning-muted: var(--color-feedback-orange-500-56); /* ============================================ * BACKGROUNDS * ============================================ */ - --bg-surface-primary-default: var(--color-base-white); - --bg-surface-secondary-default: var(--color-neutral-100); - --bg-surface-tertiary-default: var(--color-neutral-200); - --bg-surface-primary-inverse: var(--color-base-black); - --bg-surface-secondary-inverse: var(--color-neutral-900); - --bg-surface-tertiary-inverse: var(--color-neutral-800); - - /* State */ - --bg-highlight: var(--color-neutral-300); - --bg-active: var(--color-neutral-200); - --bg-default: var(--color-neutral-100); - --bg-muted: var(--color-neutral-100-56); - --bg-inset: var(--color-base-white); - - /* Light background in dark mode */ - --bg-surface-primary-fixed-light: var(--color-base-white); - --bg-surface-secondary-fixed-light: var(--color-neutral-100); - --bg-surface-tertiary-fixed-light: var(--color-neutral-200); - - /* Dark background in light mode */ - --bg-surface-primary-fixed-dark: var(--color-base-black); - --bg-surface-secondary-fixed-dark: var(--color-neutral-900); - --bg-surface-tertiary-fixed-dark: var(--color-neutral-800); - - /* Destructive */ - --bg-destructive-highlight: var(--color-feedback-red-600); - --bg-destructive-default: var(--color-feedback-red-500); - --bg-destructive-muted: var(--color-feedback-red-500-56); - --bg-destructive-softest: var(--color-feedback-red-100); - - /* Information */ - --bg-information-highlight: var(--color-feedback-blue-600); - --bg-information-default: var(--color-feedback-blue-500); - --bg-information-muted: var(--color-feedback-blue-500-56); - --bg-information-softest: var(--color-feedback-blue-100); - - /* Success */ - --bg-success-highlight: var(--color-feedback-green-600); - --bg-success-default: var(--color-feedback-green-500); - --bg-success-muted: var(--color-feedback-green-500-56); - --bg-success-softest: var(--color-feedback-green-100); - - /* Warning */ - --bg-warning-highlight: var(--color-feedback-orange-600); - --bg-warning-default: var(--color-feedback-orange-500); - --bg-warning-muted: var(--color-feedback-orange-500-56); - --bg-warning-softest: var(--color-feedback-orange-100); - - - + --bg-surface-primary-default: var(--color-base-white); + --bg-surface-secondary-default: var(--color-neutral-100); + --bg-surface-tertiary-default: var(--color-neutral-200); + --bg-surface-primary-inverse: var(--color-base-black); + --bg-surface-secondary-inverse: var(--color-neutral-900); + --bg-surface-tertiary-inverse: var(--color-neutral-800); + + /* State */ + --bg-highlight: var(--color-neutral-300); + --bg-active: var(--color-neutral-200); + --bg-default: var(--color-neutral-100); + --bg-muted: var(--color-neutral-100-56); + --bg-inset: var(--color-base-white); + + /* Light background in dark mode */ + --bg-surface-primary-fixed-light: var(--color-base-white); + --bg-surface-secondary-fixed-light: var(--color-neutral-100); + --bg-surface-tertiary-fixed-light: var(--color-neutral-200); + + /* Dark background in light mode */ + --bg-surface-primary-fixed-dark: var(--color-base-black); + --bg-surface-secondary-fixed-dark: var(--color-neutral-900); + --bg-surface-tertiary-fixed-dark: var(--color-neutral-800); + + /* Destructive */ + --bg-destructive-highlight: var(--color-feedback-red-600); + --bg-destructive-default: var(--color-feedback-red-500); + --bg-destructive-muted: var(--color-feedback-red-500-56); + --bg-destructive-softest: var(--color-feedback-red-100); + + /* Information */ + --bg-information-highlight: var(--color-feedback-blue-600); + --bg-information-default: var(--color-feedback-blue-500); + --bg-information-muted: var(--color-feedback-blue-500-56); + --bg-information-softest: var(--color-feedback-blue-100); + + /* Success */ + --bg-success-highlight: var(--color-feedback-green-600); + --bg-success-default: var(--color-feedback-green-500); + --bg-success-muted: var(--color-feedback-green-500-56); + --bg-success-softest: var(--color-feedback-green-100); + + /* Warning */ + --bg-warning-highlight: var(--color-feedback-orange-600); + --bg-warning-default: var(--color-feedback-orange-500); + --bg-warning-muted: var(--color-feedback-orange-500-56); + --bg-warning-softest: var(--color-feedback-orange-100); /* ============================================ * 3. COMPONENT TOKENS @@ -616,7 +628,7 @@ } button:not(:disabled), - [role='button']:not(:disabled) { + [role="button"]:not(:disabled) { cursor: pointer; } @@ -684,7 +696,7 @@ --text-warning: var(--color-feedback-orange-300); --border-warning: var(--color-feedback-orange-700); - /* Link utilities (dark mode) */ + /* Link utilities (dark mode) */ --underline-link-primary: var(--color-brand-blue-300); --underline-link-secondary: var(--color-base-white); --underline-link-visited: var(--color-feedback-violet-300); @@ -731,15 +743,14 @@ --border-warning-muted: var(--color-feedback-orange-500-56); --border-warning-softest: var(--color-feedback-orange-700); - /* Border Focus utilities */ + /* Border Focus utilities */ --border-focus: var(--color-brand-blue-300); - - /* ============================================ + /* ============================================ * FILLS * ============================================ */ - /* Neutrals */ + /* Neutrals */ --fill-neutral-highlight: var(--color-base-white); --fill-neutral-active: var(--color-neutral-100); --fill-neutral-default: var(--color-neutral-200); @@ -747,43 +758,43 @@ /* Fixed dark fill */ - /* Fixed light fill */ + /* Fixed light fill */ - /* Inverse fill */ - --fill-neutral-highlight-inverse: var(--color-base-black); - --fill-neutral-default-inverse: var(--color-neutral-900); - --fill-neutral-muted-inverse: var(--color-neutral-900-56); + /* Inverse fill */ + --fill-neutral-highlight-inverse: var(--color-base-black); + --fill-neutral-default-inverse: var(--color-neutral-900); + --fill-neutral-muted-inverse: var(--color-neutral-900-56); - /* Links */ - --fill-link-primary: var(--color-brand-blue-300); - --fill-link-secondary: var(--color-base-white); - --fill-link-visited: var(--color-feedback-violet-300); + /* Links */ + --fill-link-primary: var(--color-brand-blue-300); + --fill-link-secondary: var(--color-base-white); + --fill-link-visited: var(--color-feedback-violet-300); - /* Destructive */ - --fill-destructive-highlight: var(--color-feedback-red-300); - --fill-destructive-default: var(--color-feedback-red-400); - --fill-destructive-muted: var(--color-feedback-red-400-56); + /* Destructive */ + --fill-destructive-highlight: var(--color-feedback-red-300); + --fill-destructive-default: var(--color-feedback-red-400); + --fill-destructive-muted: var(--color-feedback-red-400-56); - /* Information */ - --fill-information-highlight: var(--color-feedback-blue-300); - --fill-information-default: var(--color-feedback-blue-400); - --fill-information-muted: var(--color-feedback-blue-400-56); + /* Information */ + --fill-information-highlight: var(--color-feedback-blue-300); + --fill-information-default: var(--color-feedback-blue-400); + --fill-information-muted: var(--color-feedback-blue-400-56); - /* Success */ - --fill-success-highlight: var(--color-feedback-green-300); - --fill-success-default: var(--color-feedback-green-400); - --fill-success-muted: var(--color-feedback-green-400-56); + /* Success */ + --fill-success-highlight: var(--color-feedback-green-300); + --fill-success-default: var(--color-feedback-green-400); + --fill-success-muted: var(--color-feedback-green-400-56); - /* Warning */ - --fill-warning-highlight: var(--color-feedback-orange-300); - --fill-warning-default: var(--color-feedback-orange-400); - --fill-warning-muted: var(--color-feedback-orange-400-56); + /* Warning */ + --fill-warning-highlight: var(--color-feedback-orange-300); + --fill-warning-default: var(--color-feedback-orange-400); + --fill-warning-muted: var(--color-feedback-orange-400-56); - /* ============================================ + /* ============================================ * Strokes * ============================================ */ - /* Neutrals */ + /* Neutrals */ --stroke-neutral-highlight: var(--color-base-white); --stroke-neutral-active: var(--color-neutral-100); --stroke-neutral-default: var(--color-neutral-200); @@ -791,83 +802,82 @@ /* Fixed dark stroke*/ - /* Fixed light stroke */ + /* Fixed light stroke */ - /* Inverse stroke */ - --stroke-neutral-highlight-inverse: var(--color-base-black); - --stroke-neutral-default-inverse: var(--color-neutral-900); - --stroke-neutral-muted-inverse: var(--color-neutral-900-56); + /* Inverse stroke */ + --stroke-neutral-highlight-inverse: var(--color-base-black); + --stroke-neutral-default-inverse: var(--color-neutral-900); + --stroke-neutral-muted-inverse: var(--color-neutral-900-56); - /* Links */ - --stroke-link-primary: var(--color-brand-blue-300); - --stroke-link-secondary: var(--color-base-white); - --stroke-link-visited: var(--color-feedback-violet-300); + /* Links */ + --stroke-link-primary: var(--color-brand-blue-300); + --stroke-link-secondary: var(--color-base-white); + --stroke-link-visited: var(--color-feedback-violet-300); - /* Destructive */ - --stroke-destructive-highlight: var(--color-feedback-red-300); - --stroke-destructive-default: var(--color-feedback-red-400); - --stroke-destructive-muted: var(--color-feedback-red-400-56); + /* Destructive */ + --stroke-destructive-highlight: var(--color-feedback-red-300); + --stroke-destructive-default: var(--color-feedback-red-400); + --stroke-destructive-muted: var(--color-feedback-red-400-56); - /* Information */ - --stroke-information-highlight: var(--color-feedback-blue-300); - --stroke-information-default: var(--color-feedback-blue-400); - --stroke-information-muted: var(--color-feedback-blue-400-56); + /* Information */ + --stroke-information-highlight: var(--color-feedback-blue-300); + --stroke-information-default: var(--color-feedback-blue-400); + --stroke-information-muted: var(--color-feedback-blue-400-56); - /* Success */ - --stroke-success-highlight: var(--color-feedback-green-300); - --stroke-success-default: var(--color-feedback-green-400); - --stroke-success-muted: var(--color-feedback-green-400-56); + /* Success */ + --stroke-success-highlight: var(--color-feedback-green-300); + --stroke-success-default: var(--color-feedback-green-400); + --stroke-success-muted: var(--color-feedback-green-400-56); - /* Warning */ - --stroke-warning-highlight: var(--color-feedback-orange-300); - --stroke-warning-default: var(--color-feedback-orange-400); - --stroke-warning-muted: var(--color-feedback-orange-400-56); + /* Warning */ + --stroke-warning-highlight: var(--color-feedback-orange-300); + --stroke-warning-default: var(--color-feedback-orange-400); + --stroke-warning-muted: var(--color-feedback-orange-400-56); - /* ============================================ + /* ============================================ * BACKGROUNDS * ============================================ */ - --bg-surface-primary-default: var(--color-base-black); - --bg-surface-secondary-default: var(--color-neutral-900); - --bg-surface-tertiary-default: var(--color-neutral-800); - --bg-surface-primary-inverse: var(--color-base-white); - --bg-surface-secondary-inverse: var(--color-neutral-100); - --bg-surface-tertiary-inverse: var(--color-neutral-200); - - /* State */ - --bg-highlight: var(--color-neutral-700); - --bg-active: var(--color-neutral-800); - --bg-default: var(--color-neutral-900); - --bg-muted: var(--color-neutral-900-56); - --bg-inset: var(--color-base-black); - - /* Light background in dark mode */ - - /* Dark background in light mode */ - - /* Destructive */ - --bg-destructive-highlight: var(--color-feedback-red-400); - --bg-destructive-default: var(--color-feedback-red-500); - --bg-destructive-muted: var(--color-feedback-red-500-56); - --bg-destructive-softest: var(--color-feedback-red-900); - - /* Information */ - --bg-information-highlight: var(--color-feedback-blue-400); - --bg-information-default: var(--color-feedback-blue-500); - --bg-information-muted: var(--color-feedback-blue-500-56); - --bg-information-softest: var(--color-feedback-blue-900); - - /* Success */ - --bg-success-highlight: var(--color-feedback-green-400); - --bg-success-default: var(--color-feedback-green-500); - --bg-success-muted: var(--color-feedback-green-500-56); - --bg-success-softest: var(--color-feedback-green-900); - - /* Warning */ - --bg-warning-highlight: var(--color-feedback-orange-400); - --bg-warning-default: var(--color-feedback-orange-500); - --bg-warning-muted: var(--color-feedback-orange-500-56); - --bg-warning-softest: var(--color-feedback-orange-900); - + --bg-surface-primary-default: var(--color-base-black); + --bg-surface-secondary-default: var(--color-neutral-900); + --bg-surface-tertiary-default: var(--color-neutral-800); + --bg-surface-primary-inverse: var(--color-base-white); + --bg-surface-secondary-inverse: var(--color-neutral-100); + --bg-surface-tertiary-inverse: var(--color-neutral-200); + + /* State */ + --bg-highlight: var(--color-neutral-700); + --bg-active: var(--color-neutral-800); + --bg-default: var(--color-neutral-900); + --bg-muted: var(--color-neutral-900-56); + --bg-inset: var(--color-base-black); + + /* Light background in dark mode */ + + /* Dark background in light mode */ + + /* Destructive */ + --bg-destructive-highlight: var(--color-feedback-red-400); + --bg-destructive-default: var(--color-feedback-red-500); + --bg-destructive-muted: var(--color-feedback-red-500-56); + --bg-destructive-softest: var(--color-feedback-red-900); + + /* Information */ + --bg-information-highlight: var(--color-feedback-blue-400); + --bg-information-default: var(--color-feedback-blue-500); + --bg-information-muted: var(--color-feedback-blue-500-56); + --bg-information-softest: var(--color-feedback-blue-900); + + /* Success */ + --bg-success-highlight: var(--color-feedback-green-400); + --bg-success-default: var(--color-feedback-green-500); + --bg-success-muted: var(--color-feedback-green-500-56); + --bg-success-softest: var(--color-feedback-green-900); + + /* Warning */ + --bg-warning-highlight: var(--color-feedback-orange-400); + --bg-warning-default: var(--color-feedback-orange-500); + --bg-warning-muted: var(--color-feedback-orange-500-56); + --bg-warning-softest: var(--color-feedback-orange-900); /* ============================================ * 3. COMPONENT TOKENS (Dark Mode) diff --git a/src/components/AIChat/AIChatContainer.stories.tsx b/src/components/AIChat/AIChatContainer.stories.tsx index 88dbc25f..76d9c80d 100644 --- a/src/components/AIChat/AIChatContainer.stories.tsx +++ b/src/components/AIChat/AIChatContainer.stories.tsx @@ -1,17 +1,17 @@ -import type { Meta, StoryObj } from '@storybook/react-vite' -import { Button } from '../Button' -import { AIChatContainer } from './AIChatContainer' -import { useToolCallApproval } from './toolCallApproval' -import type { ChatMessage, ToolInvocation, ToolResult } from './types' -import { useState, useEffect, useRef } from 'react' +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { Button } from "../Button"; +import { AIChatContainer } from "./AIChatContainer"; +import { useToolCallApproval } from "./toolCallApproval"; +import type { ChatMessage, ToolInvocation, ToolResult } from "./types"; +import { useState, useEffect, useRef } from "react"; const meta: Meta = { component: AIChatContainer, - tags: ['autodocs'], + tags: ["autodocs"], parameters: { - layout: 'fullscreen', + layout: "fullscreen", backgrounds: { - default: 'dark', + default: "dark", }, }, decorators: [ @@ -21,46 +21,46 @@ const meta: Meta = { ), ], -} +}; -export default meta +export default meta; -type Story = StoryObj +type Story = StoryObj; const openApiEditingMessages: ChatMessage[] = [ { - id: '1', - role: 'user', + id: "1", + role: "user", parts: [ { - type: 'text', - text: 'Can you help me add a new endpoint to my OpenAPI spec for creating a user subscription?', + type: "text", + text: "Can you help me add a new endpoint to my OpenAPI spec for creating a user subscription?", }, ], }, { - id: '2', - role: 'assistant', + id: "2", + role: "assistant", parts: [ { - type: 'text', + type: "text", text: "I'll help you add a new endpoint for user subscriptions. First, let me check the current OpenAPI spec.", }, { - type: 'tool-invocation', + type: "tool-invocation", toolInvocation: { - toolName: 'read_file', - toolCallId: 'read-1', - state: 'call', + toolName: "read_file", + toolCallId: "read-1", + state: "call", args: { - target_file: 'openapi.yaml', + target_file: "openapi.yaml", should_read_entire_file: true, }, }, }, { - type: 'tool-result', - toolCallId: 'read-1', + type: "tool-result", + toolCallId: "read-1", content: `openapi: 3.0.0 info: title: User Management API @@ -73,19 +73,19 @@ paths: summary: Create user`, }, { - type: 'text', + type: "text", text: "I'll add a new POST endpoint for creating subscriptions with proper request/response schemas.", }, { - type: 'tool-invocation', + type: "tool-invocation", toolInvocation: { - toolName: 'edit_file', - toolCallId: 'edit-1', - state: 'call', + toolName: "edit_file", + toolCallId: "edit-1", + state: "call", args: { - target_file: 'openapi.yaml', + target_file: "openapi.yaml", instructions: - 'Add POST /subscriptions endpoint with request/response schemas', + "Add POST /subscriptions endpoint with request/response schemas", code_edit: `openapi: 3.0.0 info: title: User Management API @@ -139,44 +139,44 @@ paths: }, }, { - type: 'tool-result', - toolCallId: 'edit-1', + type: "tool-result", + toolCallId: "edit-1", content: - 'Successfully updated openapi.yaml with new /subscriptions endpoint', + "Successfully updated openapi.yaml with new /subscriptions endpoint", }, { - type: 'text', + type: "text", text: "I've added the new POST /subscriptions endpoint. Would you like me to add any additional operations for managing subscriptions, such as GET or PUT endpoints?", }, ], }, { - id: '3', - role: 'user', + id: "3", + role: "user", parts: [ { - type: 'text', - text: 'Yes, please add a GET endpoint to fetch a subscription by ID', + type: "text", + text: "Yes, please add a GET endpoint to fetch a subscription by ID", }, ], }, { - id: '4', - role: 'assistant', + id: "4", + role: "assistant", parts: [ { - type: 'text', + type: "text", text: "I'll add a GET endpoint for retrieving individual subscriptions.", }, { - type: 'tool-invocation', + type: "tool-invocation", toolInvocation: { - toolName: 'edit_file', - toolCallId: 'edit-2', - state: 'call', + toolName: "edit_file", + toolCallId: "edit-2", + state: "call", args: { - target_file: 'openapi.yaml', - instructions: 'Add GET /subscriptions/{id} endpoint', + target_file: "openapi.yaml", + instructions: "Add GET /subscriptions/{id} endpoint", code_edit: `// ... existing code ... /subscriptions/{id}: get: @@ -222,38 +222,38 @@ paths: }, }, { - type: 'tool-result', - toolCallId: 'edit-2', - content: 'Successfully added GET /subscriptions/{id} endpoint', + type: "tool-result", + toolCallId: "edit-2", + content: "Successfully added GET /subscriptions/{id} endpoint", }, { - type: 'text', + type: "text", text: "I've added the GET endpoint with detailed response schemas and error handling. Would you like me to add any other endpoints or make any adjustments to the existing ones?", }, ], }, -] +]; export const OpenAPIEditing: Story = { args: { messages: openApiEditingMessages, isLoading: false, - onSendMessage: (message) => console.log('Sending message:', message), + onSendMessage: (message) => console.log("Sending message:", message), }, -} +}; export const Loading: Story = { args: { messages: openApiEditingMessages.slice(0, 2), isLoading: true, - onSendMessage: (message) => console.log('Sending message:', message), + onSendMessage: (message) => console.log("Sending message:", message), }, -} +}; export const Customized: Story = { args: { messages: openApiEditingMessages.slice(0, 3), - onSendMessage: (message) => console.log('Sending message:', message), + onSendMessage: (message) => console.log("Sending message:", message), components: { composer: { submitButton: ({ disabled, type }) => ( @@ -265,97 +265,97 @@ export const Customized: Story = { }, message: { toolCall: { - toolName: 'bg-red-500', - input: 'bg-blue-500', - result: 'bg-green-500', + toolName: "bg-red-500", + input: "bg-blue-500", + result: "bg-green-500", }, }, }, }, -} +}; const ModelSelectorDemoComponent = () => { const [messages, setMessages] = useState([ { - id: '1', - role: 'user', + id: "1", + role: "user", parts: [ { - type: 'text', - text: 'What are the main differences between GPT-4 and Claude 3 Opus?', + type: "text", + text: "What are the main differences between GPT-4 and Claude 3 Opus?", }, ], }, { - id: '2', - role: 'assistant', + id: "2", + role: "assistant", parts: [ { - type: 'text', - text: 'GPT-4 and Claude 3 Opus are both advanced language models. GPT-4 is known for its reasoning and code generation, while Claude 3 Opus excels at summarization and safety.', + type: "text", + text: "GPT-4 and Claude 3 Opus are both advanced language models. GPT-4 is known for its reasoning and code generation, while Claude 3 Opus excels at summarization and safety.", }, ], }, { - id: '3', - role: 'user', + id: "3", + role: "user", parts: [ { - type: 'text', - text: 'Can you summarize this text using Claude 3 Sonnet?', + type: "text", + text: "Can you summarize this text using Claude 3 Sonnet?", }, ], }, { - id: '4', - role: 'assistant', + id: "4", + role: "assistant", parts: [ { - type: 'text', - text: 'Certainly! Here is a concise summary using Claude 3 Sonnet.', + type: "text", + text: "Certainly! Here is a concise summary using Claude 3 Sonnet.", }, ], }, - ]) - const [isLoading, setIsLoading] = useState(false) - const [model, setModel] = useState('gpt-4') + ]); + const [isLoading, setIsLoading] = useState(false); + const [model, setModel] = useState("gpt-4"); const availableModels = [ - { label: 'GPT-4', value: 'gpt-4' }, - { label: 'GPT-3.5 Turbo', value: 'gpt-3.5-turbo' }, - { label: 'Claude 3 Opus', value: 'claude-3-opus' }, - { label: 'Claude 3 Sonnet', value: 'claude-3-sonnet' }, - ] + { label: "GPT-4", value: "gpt-4" }, + { label: "GPT-3.5 Turbo", value: "gpt-3.5-turbo" }, + { label: "Claude 3 Opus", value: "claude-3-opus" }, + { label: "Claude 3 Sonnet", value: "claude-3-sonnet" }, + ]; const handleSendMessage = async (message: string) => { - setIsLoading(true) + setIsLoading(true); setMessages((prev) => [ ...prev, { id: Date.now().toString(), - role: 'user', - parts: [{ type: 'text', text: message }], + role: "user", + parts: [{ type: "text", text: message }], }, - ]) + ]); // Simulate AI response - await new Promise((resolve) => setTimeout(resolve, 1000)) + await new Promise((resolve) => setTimeout(resolve, 1000)); setMessages((prev) => [ ...prev, { id: (Date.now() + 1).toString(), - role: 'assistant', + role: "assistant", parts: [ { - type: 'text', + type: "text", text: `This is a simulated response from ${model}. The actual model would generate a more sophisticated response.`, }, ], }, - ]) - setIsLoading(false) - } + ]); + setIsLoading(false); + }; return ( { availableModels, }} /> - ) -} + ); +}; export const WithModelSelector: Story = { render: () => , -} +}; const ToolCallApprovalDemoComponent = () => { const toolCallApproval = useToolCallApproval({ executeToolCall: async () => { - alert('Tool call approved') - return 'Tool call executed successfully' + alert("Tool call approved"); + return "Tool call executed successfully"; }, requiresApproval: () => { - return 'Allow this file to be read?' + return "Allow this file to be read?"; }, - }) + }); useEffect(() => { toolCallApproval.toolCallFn({ // eslint-disable-next-line @typescript-eslint/no-explicit-any toolCall: (toolCallStates[1].parts[1] as any).toolInvocation, - }) - }, []) + }); + }, []); return ( - ) -} + ); +}; export const WithToolCallApproval: Story = { render: () => , -} +}; export const Empty: Story = { args: { messages: [], isLoading: false, - onSendMessage: (message) => console.log('Sending message:', message), + onSendMessage: (message) => console.log("Sending message:", message), }, -} +}; // Tool call states demonstration const toolCallStates: ChatMessage[] = [ { - id: '1', - role: 'user', + id: "1", + role: "user", parts: [ { - type: 'text', - text: 'Can you help me with a tool call demonstration?', + type: "text", + text: "Can you help me with a tool call demonstration?", }, ], }, { - id: '2', - role: 'assistant', + id: "2", + role: "assistant", parts: [ { - type: 'text', + type: "text", text: "I'll demonstrate the different states of a tool call. Let's start with a partial call.", }, { - type: 'tool-invocation', + type: "tool-invocation", toolInvocation: { - toolName: 'read_file', - toolCallId: 'read-1', - state: 'partial-call', + toolName: "read_file", + toolCallId: "read-1", + state: "partial-call", args: { - target_file: 'example.txt', + target_file: "example.txt", should_read_entire_file: true, }, }, }, ], }, -] +]; // Create a component that cycles through tool call states const ToolCallStateDemoComponent = () => { const [currentState, setCurrentState] = useState< - 'partial-call' | 'call' | 'result' - >('partial-call') - const [messages, setMessages] = useState(toolCallStates) + "partial-call" | "call" | "result" + >("partial-call"); + const [messages, setMessages] = useState(toolCallStates); useEffect(() => { const interval = setInterval(() => { setCurrentState((prevState) => { - if (prevState === 'partial-call') return 'call' - if (prevState === 'call') return 'result' - return 'partial-call' - }) - }, 1000) // Change state every 1 second + if (prevState === "partial-call") return "call"; + if (prevState === "call") return "result"; + return "partial-call"; + }); + }, 1000); // Change state every 1 second - return () => clearInterval(interval) - }, []) + return () => clearInterval(interval); + }, []); useEffect(() => { // Update the tool call state in the messages setMessages((prevMessages) => { - const newMessages = [...prevMessages] - const assistantMessage = newMessages[1] + const newMessages = [...prevMessages]; + const assistantMessage = newMessages[1]; // Find the tool invocation part const toolInvocationIndex = assistantMessage.parts.findIndex( - (part) => part.type === 'tool-invocation' - ) + (part) => part.type === "tool-invocation", + ); if (toolInvocationIndex !== -1) { const toolPart = assistantMessage.parts[ toolInvocationIndex - ] as ToolInvocation + ] as ToolInvocation; // Update the tool invocation state - toolPart.toolInvocation.state = currentState + toolPart.toolInvocation.state = currentState; // Handle the result state - if (currentState === 'result') { + if (currentState === "result") { // Check if we already have a result part const hasResultPart = assistantMessage.parts.some( (part) => - part.type === 'tool-result' && - part.toolCallId === toolPart.toolInvocation.toolCallId - ) + part.type === "tool-result" && + part.toolCallId === toolPart.toolInvocation.toolCallId, + ); // Add a result part if we don't have one if (!hasResultPart) { const resultPart: ToolResult = { - type: 'tool-result', + type: "tool-result", toolCallId: toolPart.toolInvocation.toolCallId, content: - 'This is the result of the tool call. It contains some example data that would normally be returned from an API.', - } + "This is the result of the tool call. It contains some example data that would normally be returned from an API.", + }; // Insert the result part after the tool invocation assistantMessage.parts.splice( toolInvocationIndex + 1, 0, - resultPart - ) + resultPart, + ); } } else { // Remove any result parts when not in result state assistantMessage.parts = assistantMessage.parts.filter( (part) => !( - part.type === 'tool-result' && + part.type === "tool-result" && part.toolCallId === toolPart.toolInvocation.toolCallId - ) - ) + ), + ); } } - return newMessages - }) - }, [currentState]) + return newMessages; + }); + }, [currentState]); return ( console.log('Sending message:', message)} + onSendMessage={(message) => console.log("Sending message:", message)} /> - ) -} + ); +}; export const ToolCallStateDemo: Story = { render: () => , -} +}; // Markdown rendering demonstration const markdownRenderingMessages: ChatMessage[] = [ { - id: '1', - role: 'user', + id: "1", + role: "user", parts: [ { - type: 'text', - text: 'Can you show me how markdown rendering works in the chat?', + type: "text", + text: "Can you show me how markdown rendering works in the chat?", }, ], }, { - id: '2', - role: 'assistant', + id: "2", + role: "assistant", parts: [ { - type: 'text', + type: "text", text: `This message demonstrates the various markdown features supported by our enhanced chat component: ## Text Formatting @@ -626,117 +626,117 @@ That's all for this markdown demonstration!`, }, ], }, -] +]; export const MarkdownRendering: Story = { args: { messages: markdownRenderingMessages, isLoading: false, - onSendMessage: (message) => console.log('Sending message:', message), + onSendMessage: (message) => console.log("Sending message:", message), }, -} +}; // Auto-scrolling demonstration const AutoScrollDemoComponent = () => { const initialMessages: ChatMessage[] = [ - { id: '1', role: 'user', parts: [{ type: 'text', text: 'Hello!' }] }, + { id: "1", role: "user", parts: [{ type: "text", text: "Hello!" }] }, { - id: '2', - role: 'assistant', - parts: [{ type: 'text', text: 'Hi there! How can I help you today?' }], + id: "2", + role: "assistant", + parts: [{ type: "text", text: "Hi there! How can I help you today?" }], }, { - id: '3', - role: 'user', - parts: [{ type: 'text', text: "I'm just testing the scroll behavior." }], + id: "3", + role: "user", + parts: [{ type: "text", text: "I'm just testing the scroll behavior." }], }, { - id: '4', - role: 'assistant', + id: "4", + role: "assistant", parts: [ { - type: 'text', - text: 'Understood. This list should be long enough to require scrolling.', + type: "text", + text: "Understood. This list should be long enough to require scrolling.", }, ], }, { - id: '5', - role: 'user', - parts: [{ type: 'text', text: "Let's add a few more to be sure." }], + id: "5", + role: "user", + parts: [{ type: "text", text: "Let's add a few more to be sure." }], }, { - id: '6', - role: 'assistant', - parts: [{ type: 'text', text: 'Message number six.' }], + id: "6", + role: "assistant", + parts: [{ type: "text", text: "Message number six." }], }, { - id: '7', - role: 'user', - parts: [{ type: 'text', text: 'Message number seven.' }], + id: "7", + role: "user", + parts: [{ type: "text", text: "Message number seven." }], }, { - id: '8', - role: 'assistant', + id: "8", + role: "assistant", parts: [ { - type: 'text', - text: 'Message number eight, this should definitely be scrollable now.', + type: "text", + text: "Message number eight, this should definitely be scrollable now.", }, ], }, { - id: '9', - role: 'user', + id: "9", + role: "user", parts: [ { - type: 'text', - text: 'Okay, I think that is enough initial messages.', + type: "text", + text: "Okay, I think that is enough initial messages.", }, ], }, { - id: '10', - role: 'assistant', + id: "10", + role: "assistant", parts: [ - { type: 'text', text: 'Agreed. Ready for the new message test?' }, + { type: "text", text: "Agreed. Ready for the new message test?" }, ], }, - ] + ]; - const [messages, setMessages] = useState(initialMessages) - const [isLoading, setIsLoading] = useState(false) - const messageCounter = useRef(initialMessages.length) + const [messages, setMessages] = useState(initialMessages); + const [isLoading, setIsLoading] = useState(false); + const messageCounter = useRef(initialMessages.length); const handleAddMessage = async () => { - setIsLoading(true) - messageCounter.current += 1 + setIsLoading(true); + messageCounter.current += 1; const newMessage: ChatMessage = { id: `msg-${messageCounter.current}`, - role: messageCounter.current % 2 === 0 ? 'assistant' : 'user', + role: messageCounter.current % 2 === 0 ? "assistant" : "user", parts: [ { - type: 'text', + type: "text", text: `This is new message number ${messageCounter.current}.`, }, ], - } + }; // Simulate some delay - await new Promise((resolve) => setTimeout(resolve, 300)) + await new Promise((resolve) => setTimeout(resolve, 300)); - setMessages((prevMessages) => [...prevMessages, newMessage]) - setIsLoading(false) - } + setMessages((prevMessages) => [...prevMessages, newMessage]); + setIsLoading(false); + }; return (
{
- ) -} + ); +}; export const AutoScrollDemo: Story = { render: () => , -} +}; diff --git a/src/components/AIChat/AIChatContainer.tsx b/src/components/AIChat/AIChatContainer.tsx index 07f1ac51..2408144b 100644 --- a/src/components/AIChat/AIChatContainer.tsx +++ b/src/components/AIChat/AIChatContainer.tsx @@ -1,35 +1,35 @@ -import { ReactNode } from 'react' -import { cn } from '../../lib/utils' -import { AIChatMessageComponents } from './AIChatMessage' +import { ReactNode } from "react"; +import { cn } from "../../lib/utils"; +import { AIChatMessageComponents } from "./AIChatMessage"; import { AIChatMessageComposer, AIChatMessageComposerComponents, -} from './AIChatMessageComposer' -import { AIChatMessageList } from './AIChatMessageList' -import { AIChatContext } from './context' -import type { AIChatContextValue, ToolCallApprovalProps } from './types' +} from "./AIChatMessageComposer"; +import { AIChatMessageList } from "./AIChatMessageList"; +import { AIChatContext } from "./context"; +import type { AIChatContextValue, ToolCallApprovalProps } from "./types"; export interface ModelSelectorConfig { - model: string - onModelChange: (v: string) => void - availableModels: { label: string; value: string }[] + model: string; + onModelChange: (v: string) => void; + availableModels: { label: string; value: string }[]; } export interface AIChatContainerProps extends Omit< AIChatContextValue, - 'model' | 'onModelChange' | 'availableModels' | 'toolCallApproval' + "model" | "onModelChange" | "availableModels" | "toolCallApproval" > { - modelSelector?: ModelSelectorConfig - className?: string - components?: Partial - toolCallApproval?: ToolCallApprovalProps - children?: ReactNode - initialInput?: string + modelSelector?: ModelSelectorConfig; + className?: string; + components?: Partial; + toolCallApproval?: ToolCallApprovalProps; + children?: ReactNode; + initialInput?: string; } interface AIChatComponents { - composer: Partial - message: Partial + composer: Partial; + message: Partial; } export function AIChatContainer({ @@ -56,7 +56,7 @@ export function AIChatContainer({ initialInput, }} > -
+
@@ -66,5 +66,5 @@ export function AIChatContainer({ {children}
- ) + ); } diff --git a/src/components/AIChat/AIChatMessage.tsx b/src/components/AIChat/AIChatMessage.tsx index 168ff0a2..adee2056 100644 --- a/src/components/AIChat/AIChatMessage.tsx +++ b/src/components/AIChat/AIChatMessage.tsx @@ -1,35 +1,35 @@ -import { cn } from '../../lib/utils' +import { cn } from "../../lib/utils"; import { BaseComponents, DefaultComponents, FcOrClassName, renderComponent, -} from './componentsTypes' -import { AIChatMessageFilePart } from './parts/AIChatMessageFilePart' -import { AIChatMessageReasoningPart } from './parts/AIChatMessageReasoningPart' -import { AIChatMessageSourcePart } from './parts/AIChatMessageSourcePart' -import { AIChatMessageTextPart } from './parts/AIChatMessageTextPart' +} from "./componentsTypes"; +import { AIChatMessageFilePart } from "./parts/AIChatMessageFilePart"; +import { AIChatMessageReasoningPart } from "./parts/AIChatMessageReasoningPart"; +import { AIChatMessageSourcePart } from "./parts/AIChatMessageSourcePart"; +import { AIChatMessageTextPart } from "./parts/AIChatMessageTextPart"; import { AIChatMessageToolPart, AIChatMessageToolPartComponents, -} from './parts/AIChatMessageToolPart' -import type { ChatMessage } from './types' +} from "./parts/AIChatMessageToolPart"; +import type { ChatMessage } from "./types"; export interface AIChatMessageProps { - message: ChatMessage - className?: string - components?: Partial + message: ChatMessage; + className?: string; + components?: Partial; } interface AvatarComponents extends BaseComponents { - user: FcOrClassName - assistant: FcOrClassName - system: FcOrClassName + user: FcOrClassName; + assistant: FcOrClassName; + system: FcOrClassName; } export interface AIChatMessageComponents { - toolCall: Partial - avatar: Partial + toolCall: Partial; + avatar: Partial; } const defaultAvatars: DefaultComponents = { @@ -39,8 +39,8 @@ const defaultAvatars: DefaultComponents = { role="img" aria-label="User" className={cn( - 'bg-muted text-body-muted h-6 w-6 rounded-full ring-1 ring-inset', - className + "h-6 w-6 rounded-full bg-muted text-body-muted ring-1 ring-inset", + className, )} > @@ -67,8 +67,8 @@ const defaultAvatars: DefaultComponents = { assistant: ({ className }) => (
AI @@ -77,7 +77,7 @@ const defaultAvatars: DefaultComponents = { system: ({ className }) => ( = { /> ), -} +}; export function AIChatMessage({ message, @@ -98,37 +98,37 @@ export function AIChatMessage({ components, }: AIChatMessageProps) { return ( -
  • +
  • {renderComponent( defaultAvatars, components?.avatar, message.role, - {} + {}, )}
    {message.parts.map((part, index) => { switch (part.type) { - case 'text': - return - case 'reasoning': + case "text": + return ; + case "reasoning": return ( - ) - case 'tool-invocation': + ); + case "tool-invocation": return ( - ) - case 'file': + ); + case "file": return ( - ) - case 'source': + ); + case "source": return ( - ) + ); default: - return null + return null; } })}
  • - ) + ); } diff --git a/src/components/AIChat/AIChatMessageComposer.tsx b/src/components/AIChat/AIChatMessageComposer.tsx index 8f3c3f71..5ed1ca9d 100644 --- a/src/components/AIChat/AIChatMessageComposer.tsx +++ b/src/components/AIChat/AIChatMessageComposer.tsx @@ -1,32 +1,32 @@ -import React, { ReactNode, useEffect, useRef, useState } from 'react' -import { cn } from '../../lib/utils' -import { IconButton } from '../IconButton' -import { Icon } from '../Icon' -import { AIChatModelSelector } from './AIChatModelSelector' +import React, { ReactNode, useEffect, useRef, useState } from "react"; +import { cn } from "../../lib/utils"; +import { IconButton } from "../IconButton"; +import { Icon } from "../Icon"; +import { AIChatModelSelector } from "./AIChatModelSelector"; import { DefaultComponents, FcOrClassName, renderComponent, -} from './componentsTypes' -import { useAIChat } from './context' +} from "./componentsTypes"; +import { useAIChat } from "./context"; export interface AIChatMessageComposerProps { - className?: string - components?: Partial + className?: string; + components?: Partial; } export type AIChatMessageComposerComponents = { submitButton: FcOrClassName<{ - disabled?: boolean - type: 'submit' - }> + disabled?: boolean; + type: "submit"; + }>; modelSelector: FcOrClassName<{ - model: string - onModelChange: (model: string) => void - availableModels: { label: string; value: string }[] - }> - additionalActions: ReactNode -} + model: string; + onModelChange: (model: string) => void; + availableModels: { label: string; value: string }[]; + }>; + additionalActions: ReactNode; +}; const defaultComponents: DefaultComponents = { submitButton: ({ @@ -34,9 +34,9 @@ const defaultComponents: DefaultComponents = { type, className, }: { - disabled?: boolean - type: 'submit' - className: string + disabled?: boolean; + type: "submit"; + className: string; }) => ( = { variant="secondary" icon={} aria-label="Send message" - className={cn('h-8 w-8 rounded-[7px]', className)} + className={cn("h-8 w-8 rounded-[7px]", className)} /> ), modelSelector: ({ @@ -53,19 +53,19 @@ const defaultComponents: DefaultComponents = { availableModels, className, }: { - model: string - onModelChange: (model: string) => void - availableModels: { label: string; value: string }[] - className: string + model: string; + onModelChange: (model: string) => void; + availableModels: { label: string; value: string }[]; + className: string; }) => ( ), additionalActions: null, -} +}; export function AIChatMessageComposer({ className, @@ -78,55 +78,55 @@ export function AIChatMessageComposer({ onModelChange, availableModels, initialInput, - } = useAIChat() - const [message, setMessage] = useState(initialInput || '') - const [isFocused, setIsFocused] = useState(false) - const textareaRef = useRef(null) + } = useAIChat(); + const [message, setMessage] = useState(initialInput || ""); + const [isFocused, setIsFocused] = useState(false); + const textareaRef = useRef(null); const adjustHeight = () => { - const textarea = textareaRef.current - if (!textarea) return - textarea.style.height = 'auto' - textarea.style.height = `${textarea.scrollHeight}px` - } + const textarea = textareaRef.current; + if (!textarea) return; + textarea.style.height = "auto"; + textarea.style.height = `${textarea.scrollHeight}px`; + }; // Initialize height on mount and adjust on content change useEffect(() => { - adjustHeight() - }, [message]) + adjustHeight(); + }, [message]); // Set initial input only once when component mounts with an initialInput useEffect(() => { if (initialInput) { - setMessage(initialInput) + setMessage(initialInput); } - }, []) + }, []); const handleSubmit = (e?: React.FormEvent) => { - e?.preventDefault() + e?.preventDefault(); if (message.trim() && onSendMessage && !isLoading) { - onSendMessage(message) - setMessage('') + onSendMessage(message); + setMessage(""); if (textareaRef.current) { - textareaRef.current.style.height = 'auto' + textareaRef.current.style.height = "auto"; } } - } + }; const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault() - handleSubmit() + if (e.key === "Enter" && !e.shiftKey) { + e.preventDefault(); + handleSubmit(); } - } + }; return ( -
    +