diff --git a/doc/DEVELOPING.md b/doc/DEVELOPING.md index 099c6359ac8..2611a0eb397 100644 --- a/doc/DEVELOPING.md +++ b/doc/DEVELOPING.md @@ -43,6 +43,17 @@ This starts: `pnpm dev` and `pnpm dev:once` are now idempotent for the current repo and instance: if the matching Paperclip dev runner is already alive, Paperclip reports the existing process instead of starting a duplicate. +## Storybook + +The board UI Storybook keeps stories and Storybook config under `ui/storybook/` so component review files stay out of the app source routes. + +```sh +pnpm storybook +pnpm build-storybook +``` + +These run the `@paperclipai/ui` Storybook on port `6006` and build the static output to `ui/storybook-static/`. + Inspect or stop the current repo's managed dev runner: ```sh diff --git a/package.json b/package.json index ce666bfaf38..21894e98a21 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ "dev:stop": "pnpm --filter @paperclipai/server exec tsx ../scripts/dev-service.ts stop", "dev:server": "pnpm --filter @paperclipai/server dev", "dev:ui": "pnpm --filter @paperclipai/ui dev", + "storybook": "pnpm --filter @paperclipai/ui storybook", + "build-storybook": "pnpm --filter @paperclipai/ui build-storybook", "build": "pnpm run preflight:workspace-links && pnpm -r build", "typecheck": "pnpm run preflight:workspace-links && pnpm -r typecheck", "test": "pnpm run test:run", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d0920d05ed4..67b5a2a060e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,7 +30,7 @@ importers: version: 5.9.3 vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0) cli: dependencies: @@ -252,7 +252,7 @@ importers: version: 5.9.3 vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0) packages/mcp-server: dependencies: @@ -274,7 +274,7 @@ importers: version: 5.9.3 vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0) packages/plugins/create-paperclip-plugin: dependencies: @@ -324,7 +324,7 @@ importers: version: 5.9.3 vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0) packages/plugins/examples/plugin-file-browser-example: dependencies: @@ -600,7 +600,7 @@ importers: version: 6.4.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0) ui: dependencies: @@ -701,6 +701,15 @@ importers: specifier: ^3.4.1 version: 3.4.1 devDependencies: + '@storybook/addon-a11y': + specifier: 10.3.5 + version: 10.3.5(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) + '@storybook/addon-docs': + specifier: 10.3.5 + version: 10.3.5(@types/react@19.2.14)(esbuild@0.27.3)(rollup@4.60.1)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + '@storybook/react-vite': + specifier: 10.3.5 + version: 10.3.5(esbuild@0.27.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.60.1)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) '@tailwindcss/vite': specifier: ^4.0.7 version: 4.1.18(vite@6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) @@ -716,6 +725,9 @@ importers: '@vitejs/plugin-react': specifier: ^4.3.4 version: 4.7.0(vite@6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + storybook: + specifier: 10.3.5 + version: 10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) tailwindcss: specifier: ^4.0.7 version: 4.1.18 @@ -727,13 +739,16 @@ importers: version: 6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0) packages: '@acemir/cssom@0.9.31': resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==} + '@adobe/css-tools@4.4.4': + resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} + '@antfu/install-pkg@1.1.0': resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} @@ -1957,6 +1972,15 @@ packages: cpu: [x64] os: [win32] + '@joshwooding/vite-plugin-react-docgen-typescript@0.7.0': + resolution: {integrity: sha512-qvsTEwEFefhdirGOPnu9Wp6ChfIwy2dBCRuETU3uE+4cC+PFoxMSiiEhxk4lOluA34eARHA0OxqsEUYDqRMgeQ==} + peerDependencies: + typescript: '>= 4.3.x' + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + typescript: + optional: true + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -2098,6 +2122,12 @@ packages: '@marijn/find-cluster-break@1.0.2': resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} + '@mdx-js/react@3.1.1': + resolution: {integrity: sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==} + peerDependencies: + '@types/react': '>=16' + react: '>=16' + '@mdxeditor/editor@3.52.4': resolution: {integrity: sha512-Tr/QKR7pVrle9xF3ZCsUESOlLY8UZ0N/8RZcyyRWvnuEvePi4EAcbthwnyDkVpnwGppkUxPNrFTAnL7Y0R1Hwg==} engines: {node: '>=16'} @@ -3274,6 +3304,75 @@ packages: '@stitches/core@1.2.8': resolution: {integrity: sha512-Gfkvwk9o9kE9r9XNBmJRfV8zONvXThnm1tcuojL04Uy5uRyqg93DC83lDebl0rocZCfKSjUv+fWYtMQmEDJldg==} + '@storybook/addon-a11y@10.3.5': + resolution: {integrity: sha512-5k6lpgfIeLxvNhE8v3wEzdiu73ONKjF4gmH1AHvfqYd8kIVzQJai0KCDxgvqNncXHQhIWkaf1fg6+9hKaYJyaw==} + peerDependencies: + storybook: ^10.3.5 + + '@storybook/addon-docs@10.3.5': + resolution: {integrity: sha512-WuHbxia/o5TX4Rg/IFD0641K5qId/Nk0dxhmAUNoFs5L0+yfZUwh65XOBbzXqrkYmYmcVID4v7cgDRmzstQNkA==} + peerDependencies: + storybook: ^10.3.5 + + '@storybook/builder-vite@10.3.5': + resolution: {integrity: sha512-i4KwCOKbhtlbQIbhm53+Kk7bMnxa0cwTn1pxmtA/x5wm1Qu7FrrBQV0V0DNjkUqzcSKo1CjspASJV/HlY0zYlw==} + peerDependencies: + storybook: ^10.3.5 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@storybook/csf-plugin@10.3.5': + resolution: {integrity: sha512-qlEzNKxOjq86pvrbuMwiGD/bylnsXk1dg7ve0j77YFjEEchqtl7qTlrXvFdNaLA89GhW6D/EV6eOCu/eobPDgw==} + peerDependencies: + esbuild: '*' + rollup: '>=4.59.0' + storybook: ^10.3.5 + vite: '*' + webpack: '*' + peerDependenciesMeta: + esbuild: + optional: true + rollup: + optional: true + vite: + optional: true + webpack: + optional: true + + '@storybook/global@5.0.0': + resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} + + '@storybook/icons@2.0.1': + resolution: {integrity: sha512-/smVjw88yK3CKsiuR71vNgWQ9+NuY2L+e8X7IMrFjexjm6ZR8ULrV2DRkTA61aV6ryefslzHEGDInGpnNeIocg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@storybook/react-dom-shim@10.3.5': + resolution: {integrity: sha512-Gw8R7XZm0zSUH0XAuxlQJhmizsLzyD6x00KOlP6l7oW9eQHXGfxg3seNDG3WrSAcW07iP1/P422kuiriQlOv7g==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + storybook: ^10.3.5 + + '@storybook/react-vite@10.3.5': + resolution: {integrity: sha512-UB5sJHeh26bfd8sNMx2YPGYRYmErIdTRaLOT28m4bykQIa1l9IgVktsYg/geW7KsJU0lXd3oTbnUjLD+enpi3w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + storybook: ^10.3.5 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@storybook/react@10.3.5': + resolution: {integrity: sha512-tpLTLaVGoA6fLK3ReyGzZUricq7lyPaV2hLPpj5wqdXLV/LpRtAHClUpNoPDYSBjlnSjL81hMZijbkGC3mA+gw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + storybook: ^10.3.5 + typescript: '>= 4.9.x' + peerDependenciesMeta: + typescript: + optional: true + '@tailwindcss/node@4.1.18': resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==} @@ -3377,6 +3476,23 @@ packages: peerDependencies: react: ^18 || ^19 + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + + '@testing-library/jest-dom@6.9.1': + resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + + '@testing-library/user-event@14.6.1': + resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -3500,6 +3616,9 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/doctrine@0.0.9': + resolution: {integrity: sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==} + '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} @@ -3527,6 +3646,9 @@ packages: '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + '@types/mdx@2.0.13': + resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} + '@types/methods@1.1.4': resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} @@ -3631,6 +3753,9 @@ packages: '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + '@webcontainer/env@1.1.1': + resolution: {integrity: sha512-6aN99yL695Hi9SuIk1oC88l9o0gmxL1nGWWQ/kNy81HigJ0FoaoTXpytCj6ItzgyCEwA9kF1wixsTuv5cjsgng==} + accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -3667,6 +3792,14 @@ packages: anser@2.3.5: resolution: {integrity: sha512-vcZjxvvVoxTeR5XBNJB38oTu/7eDCZlwdz32N1eNgpyPF7j/Z7Idf+CUwQOkKKpJ7RJyjxgLHCM7vdIK0iCNMQ==} + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + append-field@1.0.0: resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} @@ -3677,6 +3810,13 @@ packages: resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} engines: {node: '>=10'} + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} @@ -3690,6 +3830,10 @@ packages: assistant-stream@0.3.10: resolution: {integrity: sha512-lgyMePFFzU50NM4x4zdDnGLmZ31smljVz7W9t3ngMlxTrL4XF8VhodxSn5dHSuR6W6zBHf29U7EZSVyu8MzJWw==} + ast-types@0.16.1: + resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} + engines: {node: '>=4'} + async-exit-hook@2.0.1: resolution: {integrity: sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==} engines: {node: '>=0.12.0'} @@ -3701,9 +3845,17 @@ packages: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} + axe-core@4.11.3: + resolution: {integrity: sha512-zBQouZixDTbo3jMGqHKyePxYxr1e5W8UdTmBQ7sNtaA9M2bE32daxxPLS/jojhKOHxQ7LWwPjfiwf/fhaJWzlg==} + engines: {node: '>=4'} + bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -3791,6 +3943,10 @@ packages: bowser@2.14.1: resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + brace-expansion@5.0.5: + resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} + engines: {node: 18 || 20 || >=22} + browserslist@4.28.1: resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -3981,6 +4137,9 @@ packages: resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -4238,6 +4397,16 @@ packages: resolution: {integrity: sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==} engines: {node: '>=0.3.1'} + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dompurify@3.3.2: resolution: {integrity: sha512-6obghkliLdmKa56xdbLOpUZ43pAR6xFy1uOrxBaIDjT+yaRuuybLjGS9eVBoSR/UPU5fq3OXClEHLJNGvbxKpQ==} engines: {node: '>=20'} @@ -4365,6 +4534,10 @@ packages: resolution: {integrity: sha512-TDp7Ld0h84x5fzIIZFyreYWqZrxUNjuXB6OxqJCmV6PodB2vzQ+1hlL6n4uK1de7bIAxYt5OkDykwcuIONQdQg==} engines: {node: '>=16'} + empathic@2.0.0: + resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} + engines: {node: '>=14'} + encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} @@ -4448,6 +4621,11 @@ packages: resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} engines: {node: '>=0.10'} + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + estree-util-is-identifier-name@3.0.0: resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} @@ -4460,6 +4638,10 @@ packages: estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + etag@1.8.1: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} @@ -4583,6 +4765,10 @@ packages: get-tsconfig@4.13.6: resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -4652,6 +4838,10 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -5137,9 +5327,21 @@ packages: engines: {node: '>=4.0.0'} hasBin: true + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + mlly@1.8.1: resolution: {integrity: sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ==} @@ -5197,6 +5399,10 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + open@10.2.0: + resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} + engines: {node: '>=18'} + open@11.0.0: resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==} engines: {node: '>=20'} @@ -5230,6 +5436,10 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + path-to-regexp@8.3.0: resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} @@ -5356,6 +5566,10 @@ packages: resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==} engines: {node: '>=20'} + 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} + prismjs@1.30.0: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} @@ -5411,6 +5625,15 @@ packages: react-devtools-inline@4.4.0: resolution: {integrity: sha512-ES0GolSrKO8wsKbsEkVeiR/ZAaHQTY4zDh1UW8DImVmm8oaGLl3ijJDvSGe+qDRKPZdPRnDtWWnSvvrgxXdThQ==} + react-docgen-typescript@2.4.0: + resolution: {integrity: sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg==} + peerDependencies: + typescript: '>= 4.3.x' + + react-docgen@8.0.3: + resolution: {integrity: sha512-aEZ9qP+/M+58x2qgfSFEWH1BxLyHe5+qkLNJOZQb5iGS017jpbRnoKhNRrXPeA6RfBrZO5wZrT9DMC1UqE1f1w==} + engines: {node: ^20.9.0 || >=22} + react-dom@19.2.4: resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} peerDependencies: @@ -5513,6 +5736,14 @@ packages: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} + recast@0.23.11: + resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} + engines: {node: '>= 4'} + + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + remark-gfm@4.0.1: resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} @@ -5677,6 +5908,15 @@ packages: std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + storybook@10.3.5: + resolution: {integrity: sha512-uBSZu/GZa9aEIW3QMGvdQPMZWhGxSe4dyRWU8B3/Vd47Gy/XLC7tsBxRr13txmmPOEDHZR94uLuq0H50fvuqBw==} + hasBin: true + peerDependencies: + prettier: ^2 || ^3 + peerDependenciesMeta: + prettier: + optional: true + streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -5690,6 +5930,18 @@ packages: stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + strip-indent@4.1.1: + resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} + engines: {node: '>=12'} + strip-json-comments@5.0.3: resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} engines: {node: '>=14.16'} @@ -5743,6 +5995,9 @@ packages: thread-stream@3.1.0: resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -5798,6 +6053,10 @@ packages: resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} engines: {node: '>=6.10'} + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -5869,6 +6128,10 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} + unplugin@2.3.11: + resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} + engines: {node: '>=18.12.0'} + update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -6098,6 +6361,9 @@ packages: resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} engines: {node: '>=20'} + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + whatwg-mimetype@5.0.0: resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} engines: {node: '>=20'} @@ -6131,6 +6397,10 @@ packages: utf-8-validate: optional: true + wsl-utils@0.1.0: + resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} + engines: {node: '>=18'} + wsl-utils@0.3.1: resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==} engines: {node: '>=20'} @@ -6189,6 +6459,8 @@ snapshots: '@acemir/cssom@0.9.31': {} + '@adobe/css-tools@4.4.4': {} + '@antfu/install-pkg@1.1.0': dependencies: package-manager-detector: 1.6.0 @@ -7674,6 +7946,14 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true + '@joshwooding/vite-plugin-react-docgen-typescript@0.7.0(typescript@5.9.3)(vite@6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': + dependencies: + glob: 13.0.6 + react-docgen-typescript: 2.4.0(typescript@5.9.3) + vite: 6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + optionalDependencies: + typescript: 5.9.3 + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -7934,6 +8214,12 @@ snapshots: '@marijn/find-cluster-break@1.0.2': {} + '@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4)': + dependencies: + '@types/mdx': 2.0.13 + '@types/react': 19.2.14 + react: 19.2.4 + '@mdxeditor/editor@3.52.4(@codemirror/language@6.12.1)(@lezer/highlight@1.2.3)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(yjs@13.6.29)': dependencies: '@codemirror/commands': 6.10.2 @@ -9284,6 +9570,98 @@ snapshots: '@stitches/core@1.2.8': {} + '@storybook/addon-a11y@10.3.5(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))': + dependencies: + '@storybook/global': 5.0.0 + axe-core: 4.11.3 + storybook: 10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + + '@storybook/addon-docs@10.3.5(@types/react@19.2.14)(esbuild@0.27.3)(rollup@4.60.1)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': + dependencies: + '@mdx-js/react': 3.1.1(@types/react@19.2.14)(react@19.2.4) + '@storybook/csf-plugin': 10.3.5(esbuild@0.27.3)(rollup@4.60.1)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + '@storybook/icons': 2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@storybook/react-dom-shim': 10.3.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + storybook: 10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + ts-dedent: 2.2.0 + transitivePeerDependencies: + - '@types/react' + - esbuild + - rollup + - vite + - webpack + + '@storybook/builder-vite@10.3.5(esbuild@0.27.3)(rollup@4.60.1)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': + dependencies: + '@storybook/csf-plugin': 10.3.5(esbuild@0.27.3)(rollup@4.60.1)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + storybook: 10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + ts-dedent: 2.2.0 + vite: 6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + transitivePeerDependencies: + - esbuild + - rollup + - webpack + + '@storybook/csf-plugin@10.3.5(esbuild@0.27.3)(rollup@4.60.1)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': + dependencies: + storybook: 10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + unplugin: 2.3.11 + optionalDependencies: + esbuild: 0.27.3 + rollup: 4.60.1 + vite: 6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + + '@storybook/global@5.0.0': {} + + '@storybook/icons@2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + '@storybook/react-dom-shim@10.3.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))': + dependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + storybook: 10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + + '@storybook/react-vite@10.3.5(esbuild@0.27.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(rollup@4.60.1)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': + dependencies: + '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(typescript@5.9.3)(vite@6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + '@rollup/pluginutils': 5.3.0(rollup@4.60.1) + '@storybook/builder-vite': 10.3.5(esbuild@0.27.3)(rollup@4.60.1)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + '@storybook/react': 10.3.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) + empathic: 2.0.0 + magic-string: 0.30.21 + react: 19.2.4 + react-docgen: 8.0.3 + react-dom: 19.2.4(react@19.2.4) + resolve: 1.22.11 + storybook: 10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + tsconfig-paths: 4.2.0 + vite: 6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + transitivePeerDependencies: + - esbuild + - rollup + - supports-color + - typescript + - webpack + + '@storybook/react@10.3.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)': + dependencies: + '@storybook/global': 5.0.0 + '@storybook/react-dom-shim': 10.3.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) + react: 19.2.4 + react-docgen: 8.0.3 + react-docgen-typescript: 2.4.0(typescript@5.9.3) + react-dom: 19.2.4(react@19.2.4) + storybook: 10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@tailwindcss/node@4.1.18': dependencies: '@jridgewell/remapping': 2.3.5 @@ -9364,6 +9742,32 @@ snapshots: '@tanstack/query-core': 5.90.20 react: 19.2.4 + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/runtime': 7.28.6 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@6.9.1': + dependencies: + '@adobe/css-tools': 4.4.4 + aria-query: 5.3.2 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + picocolors: 1.1.1 + redent: 3.0.0 + + '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': + dependencies: + '@testing-library/dom': 10.4.1 + + '@types/aria-query@5.0.4': {} + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.29.0 @@ -9524,6 +9928,8 @@ snapshots: '@types/deep-eql@4.0.2': {} + '@types/doctrine@0.0.9': {} + '@types/estree-jsx@1.0.5': dependencies: '@types/estree': 1.0.8 @@ -9562,6 +9968,8 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/mdx@2.0.13': {} + '@types/methods@1.1.4': {} '@types/ms@2.1.0': {} @@ -9698,6 +10106,8 @@ snapshots: loupe: 3.2.1 tinyrainbow: 2.0.0 + '@webcontainer/env@1.1.1': {} + accepts@2.0.0: dependencies: mime-types: 3.0.2 @@ -9726,6 +10136,10 @@ snapshots: anser@2.3.5: {} + ansi-regex@5.0.1: {} + + ansi-styles@5.2.0: {} + append-field@1.0.0: {} argparse@2.0.1: {} @@ -9734,6 +10148,12 @@ snapshots: dependencies: tslib: 2.8.1 + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + aria-query@5.3.2: {} + asap@2.0.6: {} assertion-error@2.0.1: {} @@ -9748,14 +10168,22 @@ snapshots: nanoid: 5.1.7 secure-json-parse: 4.1.0 + ast-types@0.16.1: + dependencies: + tslib: 2.8.1 + async-exit-hook@2.0.1: {} asynckit@0.4.0: {} atomic-sleep@1.0.0: {} + axe-core@4.11.3: {} + bail@2.0.2: {} + balanced-match@4.0.4: {} + base64-js@1.5.1: {} baseline-browser-mapping@2.9.19: {} @@ -9811,6 +10239,10 @@ snapshots: bowser@2.14.1: {} + brace-expansion@5.0.5: + dependencies: + balanced-match: 4.0.4 + browserslist@4.28.1: dependencies: baseline-browser-mapping: 2.9.19 @@ -9999,6 +10431,8 @@ snapshots: mdn-data: 2.27.1 source-map-js: 1.2.1 + css.escape@1.5.1: {} + cssesc@3.0.0: {} cssstyle@6.2.0: @@ -10264,6 +10698,14 @@ snapshots: diff@5.2.2: {} + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dom-accessibility-api@0.5.16: {} + + dom-accessibility-api@0.6.3: {} + dompurify@3.3.2: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -10325,6 +10767,8 @@ snapshots: transitivePeerDependencies: - pg-native + empathic@2.0.0: {} + encodeurl@2.0.0: {} end-of-stream@1.4.5: @@ -10478,6 +10922,8 @@ snapshots: event-emitter: 0.3.5 type: 2.7.3 + esprima@4.0.1: {} + estree-util-is-identifier-name@3.0.0: {} estree-util-visit@2.0.0: @@ -10491,6 +10937,8 @@ snapshots: dependencies: '@types/estree': 1.0.8 + esutils@2.0.3: {} + etag@1.8.1: {} event-emitter@0.3.5: @@ -10637,6 +11085,12 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + glob@13.0.6: + dependencies: + minimatch: 10.2.5 + minipass: 7.1.3 + path-scurry: 2.0.2 + gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -10726,6 +11180,8 @@ snapshots: ieee754@1.2.1: {} + indent-string@4.0.0: {} + inherits@2.0.4: {} inline-style-parser@0.2.7: {} @@ -11466,8 +11922,16 @@ snapshots: mime@2.6.0: {} + min-indent@1.0.1: {} + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.5 + minimist@1.2.8: {} + minipass@7.1.3: {} + mlly@1.8.1: dependencies: acorn: 8.16.0 @@ -11512,6 +11976,13 @@ snapshots: dependencies: wrappy: 1.0.2 + open@10.2.0: + dependencies: + default-browser: 5.5.0 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + wsl-utils: 0.1.0 + open@11.0.0: dependencies: default-browser: 5.5.0 @@ -11551,6 +12022,11 @@ snapshots: path-parse@1.0.7: {} + path-scurry@2.0.2: + dependencies: + lru-cache: 11.2.7 + minipass: 7.1.3 + path-to-regexp@8.3.0: {} pathe@2.0.3: {} @@ -11691,6 +12167,12 @@ snapshots: powershell-utils@0.1.0: {} + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + prismjs@1.30.0: {} process-warning@5.0.0: {} @@ -11797,6 +12279,25 @@ snapshots: dependencies: es6-symbol: 3.1.4 + react-docgen-typescript@2.4.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + react-docgen@8.0.3: + dependencies: + '@babel/core': 7.29.0 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.28.0 + '@types/doctrine': 0.0.9 + '@types/resolve': 1.20.2 + doctrine: 3.0.0 + resolve: 1.22.11 + strip-indent: 4.1.1 + transitivePeerDependencies: + - supports-color + react-dom@19.2.4(react@19.2.4): dependencies: react: 19.2.4 @@ -11897,6 +12398,19 @@ snapshots: real-require@0.2.0: {} + recast@0.23.11: + dependencies: + ast-types: 0.16.1 + esprima: 4.0.1 + source-map: 0.6.1 + tiny-invariant: 1.3.3 + tslib: 2.8.1 + + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + remark-gfm@4.0.1: dependencies: '@types/mdast': 4.0.4 @@ -12147,6 +12661,28 @@ snapshots: std-env@3.10.0: {} + storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + '@storybook/global': 5.0.0 + '@storybook/icons': 2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@testing-library/jest-dom': 6.9.1 + '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) + '@vitest/expect': 3.2.4 + '@vitest/spy': 3.2.4 + '@webcontainer/env': 1.1.1 + esbuild: 0.27.3 + open: 10.2.0 + recast: 0.23.11 + semver: 7.7.4 + use-sync-external-store: 1.6.0(react@19.2.4) + ws: 8.19.0 + transitivePeerDependencies: + - '@testing-library/dom' + - bufferutil + - react + - react-dom + - utf-8-validate + streamsearch@1.1.0: {} strict-event-emitter@0.4.6: {} @@ -12160,6 +12696,14 @@ snapshots: character-entities-html4: 2.1.0 character-entities-legacy: 3.0.0 + strip-bom@3.0.0: {} + + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + strip-indent@4.1.1: {} + strip-json-comments@5.0.3: {} strip-literal@3.1.0: @@ -12218,6 +12762,8 @@ snapshots: dependencies: real-require: 0.2.0 + tiny-invariant@1.3.3: {} + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -12257,6 +12803,12 @@ snapshots: ts-dedent@2.2.0: {} + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + tslib@2.8.1: {} tsx@4.21.0: @@ -12336,6 +12888,13 @@ snapshots: unpipe@1.0.0: {} + unplugin@2.3.11: + dependencies: + '@jridgewell/remapping': 2.3.5 + acorn: 8.16.0 + picomatch: 4.0.3 + webpack-virtual-modules: 0.6.2 + update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: browserslist: 4.28.1 @@ -12551,8 +13110,52 @@ snapshots: - terser - tsx - yaml + optional: true - vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0): + dependencies: + '@types/chai': 5.2.3 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.3.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + vite-node: 3.2.4(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 24.12.0 + jsdom: 28.1.0(@noble/hashes@2.0.1) + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 @@ -12620,6 +13223,8 @@ snapshots: webidl-conversions@8.0.1: {} + webpack-virtual-modules@0.6.2: {} + whatwg-mimetype@5.0.0: {} whatwg-url@16.0.1(@noble/hashes@2.0.1): @@ -12643,6 +13248,10 @@ snapshots: ws@8.19.0: {} + wsl-utils@0.1.0: + dependencies: + is-wsl: 3.1.1 + wsl-utils@0.3.1: dependencies: is-wsl: 3.1.1 diff --git a/ui/README.md b/ui/README.md index 0e688669e30..b33021d1113 100644 --- a/ui/README.md +++ b/ui/README.md @@ -6,6 +6,15 @@ Published static assets for the Paperclip board UI. The npm package contains the production build under `dist/`. It does not ship the UI source tree or workspace-only dependencies. +## Storybook + +Storybook config, stories, and fixtures live under `ui/storybook/`. + +```sh +pnpm --filter @paperclipai/ui storybook +pnpm --filter @paperclipai/ui build-storybook +``` + ## Typical use Install the package, then serve or copy the built files from `node_modules/@paperclipai/ui/dist`. diff --git a/ui/package.json b/ui/package.json index 020e2ec2f8a..dda6e7154b6 100644 --- a/ui/package.json +++ b/ui/package.json @@ -16,9 +16,11 @@ "scripts": { "dev": "vite", "build": "tsc -b && vite build", + "storybook": "storybook dev -p 6006 -c storybook/.storybook", + "build-storybook": "storybook build -c storybook/.storybook -o storybook-static", "preview": "vite preview", "typecheck": "tsc -b", - "clean": "rm -rf dist tsconfig.tsbuildinfo", + "clean": "rm -rf dist storybook-static tsconfig.tsbuildinfo", "prepack": "rm -f package.dev.json && cp package.json package.dev.json && node ../scripts/generate-ui-package-json.mjs", "postpack": "if [ -f package.dev.json ]; then mv package.dev.json package.json; fi" }, @@ -61,12 +63,16 @@ }, "devDependencies": { "@tailwindcss/vite": "^4.0.7", + "@storybook/addon-a11y": "10.3.5", + "@storybook/addon-docs": "10.3.5", + "@storybook/react-vite": "10.3.5", "@types/node": "^25.2.3", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", "@vitejs/plugin-react": "^4.3.4", "tailwindcss": "^4.0.7", "typescript": "^5.7.3", + "storybook": "10.3.5", "vite": "^6.1.0", "vitest": "^3.0.5" } diff --git a/ui/src/App.tsx b/ui/src/App.tsx index 1a82d1a613e..f495dc0e4d0 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -40,9 +40,6 @@ import { PluginManager } from "./pages/PluginManager"; import { PluginSettings } from "./pages/PluginSettings"; import { AdapterManager } from "./pages/AdapterManager"; import { PluginPage } from "./pages/PluginPage"; -import { IssueChatUxLab } from "./pages/IssueChatUxLab"; -import { InviteUxLab } from "./pages/InviteUxLab"; -import { RunTranscriptUxLab } from "./pages/RunTranscriptUxLab"; import { OrgChart } from "./pages/OrgChart"; import { NewAgent } from "./pages/NewAgent"; import { AuthPage } from "./pages/Auth"; @@ -122,9 +119,6 @@ function boardRoutes() { } /> } /> } /> - } /> - } /> - } /> } /> } /> } /> @@ -303,8 +297,6 @@ export function App() { } /> } /> } /> - } /> - } /> }> {boardRoutes()} diff --git a/ui/storybook/.storybook/main.ts b/ui/storybook/.storybook/main.ts new file mode 100644 index 00000000000..e7e2dadaf0c --- /dev/null +++ b/ui/storybook/.storybook/main.ts @@ -0,0 +1,32 @@ +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import type { StorybookConfig } from "@storybook/react-vite"; +import tailwindcss from "@tailwindcss/vite"; +import { mergeConfig } from "vite"; + +const storybookConfigDir = path.dirname(fileURLToPath(import.meta.url)); + +const config: StorybookConfig = { + stories: ["../stories/**/*.stories.@(ts|tsx|mdx)"], + staticDirs: ["../../public"], + addons: ["@storybook/addon-docs", "@storybook/addon-a11y"], + framework: { + name: "@storybook/react-vite", + options: {}, + }, + docs: { + autodocs: true, + }, + viteFinal: async (baseConfig) => + mergeConfig(baseConfig, { + plugins: [tailwindcss()], + resolve: { + alias: { + "@": path.resolve(storybookConfigDir, "../../src"), + lexical: path.resolve(storybookConfigDir, "../../node_modules/lexical/Lexical.mjs"), + }, + }, + }), +}; + +export default config; diff --git a/ui/storybook/.storybook/preview.tsx b/ui/storybook/.storybook/preview.tsx new file mode 100644 index 00000000000..cd13c504514 --- /dev/null +++ b/ui/storybook/.storybook/preview.tsx @@ -0,0 +1,189 @@ +import { useState, type ReactNode } from "react"; +import type { Preview } from "@storybook/react-vite"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { MemoryRouter } from "@/lib/router"; +import { BreadcrumbProvider } from "@/context/BreadcrumbContext"; +import { CompanyProvider } from "@/context/CompanyContext"; +import { DialogProvider } from "@/context/DialogContext"; +import { EditorAutocompleteProvider } from "@/context/EditorAutocompleteContext"; +import { PanelProvider } from "@/context/PanelContext"; +import { SidebarProvider } from "@/context/SidebarContext"; +import { ThemeProvider } from "@/context/ThemeContext"; +import { ToastProvider } from "@/context/ToastContext"; +import { TooltipProvider } from "@/components/ui/tooltip"; +import "@mdxeditor/editor/style.css"; +import "@/index.css"; +import "./styles.css"; + +const storybookCompany = { + id: "company-storybook", + name: "Paperclip Storybook", + description: "Fixture company for isolated UI review.", + status: "active", + pauseReason: null, + pausedAt: null, + issuePrefix: "PAP", + issueCounter: 1641, + budgetMonthlyCents: 250_000, + spentMonthlyCents: 67_500, + requireBoardApprovalForNewAgents: true, + feedbackDataSharingEnabled: true, + feedbackDataSharingConsentAt: null, + feedbackDataSharingConsentByUserId: null, + feedbackDataSharingTermsVersion: null, + brandColor: "#0f766e", + logoAssetId: null, + logoUrl: null, + createdAt: new Date("2026-04-01T09:00:00.000Z"), + updatedAt: new Date("2026-04-20T12:00:00.000Z"), +}; + +function installStorybookApiFixtures() { + if (typeof window === "undefined") return; + const currentWindow = window as typeof window & { + __paperclipStorybookFetchInstalled?: boolean; + }; + if (currentWindow.__paperclipStorybookFetchInstalled) return; + + const originalFetch = window.fetch.bind(window); + currentWindow.__paperclipStorybookFetchInstalled = true; + + window.fetch = async (input: RequestInfo | URL, init?: RequestInit) => { + const rawUrl = + typeof input === "string" + ? input + : input instanceof URL + ? input.href + : input.url; + const url = new URL(rawUrl, window.location.origin); + + if (url.pathname === "/api/companies") { + return Response.json([storybookCompany]); + } + + if (url.pathname.startsWith("/api/invites/") && url.pathname.endsWith("/logo")) { + return new Response(null, { status: 204 }); + } + + return originalFetch(input, init); + }; +} + +function applyStorybookTheme(theme: "light" | "dark") { + if (typeof document === "undefined") return; + document.documentElement.classList.toggle("dark", theme === "dark"); + document.documentElement.style.colorScheme = theme; +} + +function StorybookProviders({ + children, + theme, +}: { + children: ReactNode; + theme: "light" | "dark"; +}) { + const [queryClient] = useState( + () => + new QueryClient({ + defaultOptions: { + queries: { + retry: false, + staleTime: Number.POSITIVE_INFINITY, + }, + }, + }), + ); + + applyStorybookTheme(theme); + installStorybookApiFixtures(); + + return ( + + + + + + + + + + + {children} + + + + + + + + + + + ); +} + +const preview: Preview = { + decorators: [ + (Story, context) => { + const theme = context.globals.theme === "light" ? "light" : "dark"; + return ( + + + + ); + }, + ], + globalTypes: { + theme: { + description: "Paperclip color mode", + defaultValue: "dark", + toolbar: { + title: "Theme", + icon: "mirror", + items: [ + { value: "dark", title: "Dark" }, + { value: "light", title: "Light" }, + ], + dynamicTitle: true, + }, + }, + }, + parameters: { + actions: { argTypesRegex: "^on[A-Z].*" }, + a11y: { + test: "todo", + }, + backgrounds: { + disable: true, + }, + controls: { + expanded: true, + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + docs: { + toc: true, + }, + layout: "fullscreen", + viewport: { + viewports: { + mobile: { + name: "Mobile", + styles: { width: "390px", height: "844px" }, + }, + tablet: { + name: "Tablet", + styles: { width: "834px", height: "1112px" }, + }, + desktop: { + name: "Desktop", + styles: { width: "1440px", height: "960px" }, + }, + }, + }, + }, +}; + +export default preview; diff --git a/ui/storybook/.storybook/styles.css b/ui/storybook/.storybook/styles.css new file mode 100644 index 00000000000..c3bfb713146 --- /dev/null +++ b/ui/storybook/.storybook/styles.css @@ -0,0 +1,49 @@ +html, +body, +#storybook-root { + min-height: 100%; +} + +body { + overflow: auto; + background: var(--background); +} + +.sb-show-main { + background: var(--background); + color: var(--foreground); +} + +.paperclip-story { + min-height: 100vh; + background: + linear-gradient(180deg, color-mix(in oklab, var(--muted) 28%, transparent), transparent 340px), + var(--background); + color: var(--foreground); + padding: 24px; +} + +.paperclip-story__inner { + margin: 0 auto; + max-width: 1180px; +} + +.paperclip-story__frame { + border: 1px solid var(--border); + background: color-mix(in oklab, var(--card) 86%, transparent); + box-shadow: 0 24px 90px color-mix(in oklab, black 16%, transparent); +} + +.paperclip-story__label { + color: var(--muted-foreground); + font-size: 11px; + font-weight: 700; + letter-spacing: 0.18em; + text-transform: uppercase; +} + +@media (max-width: 640px) { + .paperclip-story { + padding: 16px; + } +} diff --git a/ui/storybook/fixtures/paperclipData.ts b/ui/storybook/fixtures/paperclipData.ts new file mode 100644 index 00000000000..01f1e950341 --- /dev/null +++ b/ui/storybook/fixtures/paperclipData.ts @@ -0,0 +1,389 @@ +import type { + ActivityEvent, + Agent, + Approval, + BudgetPolicySummary, + Issue, +} from "@paperclipai/shared"; + +const now = new Date("2026-04-20T12:00:00.000Z"); +const recent = (minutesAgo: number) => new Date(now.getTime() - minutesAgo * 60_000); + +export const storybookAgents: Agent[] = [ + { + id: "agent-codex", + companyId: "company-storybook", + name: "CodexCoder", + urlKey: "codexcoder", + role: "engineer", + title: "Senior Product Engineer", + icon: "code", + status: "running", + reportsTo: "agent-cto", + capabilities: "Ships full-stack Paperclip product tasks, Storybook coverage, and verification.", + adapterType: "codex_local", + adapterConfig: {}, + runtimeConfig: {}, + budgetMonthlyCents: 125_000, + spentMonthlyCents: 43_200, + pauseReason: null, + pausedAt: null, + permissions: { canCreateAgents: false }, + lastHeartbeatAt: recent(3), + metadata: null, + createdAt: recent(12_000), + updatedAt: recent(3), + }, + { + id: "agent-qa", + companyId: "company-storybook", + name: "QAChecker", + urlKey: "qachecker", + role: "qa", + title: "QA Engineer", + icon: "shield", + status: "idle", + reportsTo: "agent-cto", + capabilities: "Validates browser flows, acceptance criteria, and release smoke tests.", + adapterType: "claude_local", + adapterConfig: {}, + runtimeConfig: {}, + budgetMonthlyCents: 80_000, + spentMonthlyCents: 18_900, + pauseReason: null, + pausedAt: null, + permissions: { canCreateAgents: false }, + lastHeartbeatAt: recent(24), + metadata: null, + createdAt: recent(11_000), + updatedAt: recent(24), + }, + { + id: "agent-cto", + companyId: "company-storybook", + name: "CTO", + urlKey: "cto", + role: "cto", + title: "CTO", + icon: "crown", + status: "active", + reportsTo: null, + capabilities: "Reviews architecture, quality gates, and engineering priority tradeoffs.", + adapterType: "codex_local", + adapterConfig: {}, + runtimeConfig: {}, + budgetMonthlyCents: 200_000, + spentMonthlyCents: 54_000, + pauseReason: null, + pausedAt: null, + permissions: { canCreateAgents: true }, + lastHeartbeatAt: recent(41), + metadata: null, + createdAt: recent(14_000), + updatedAt: recent(41), + }, +]; + +export const storybookAgentMap = new Map(storybookAgents.map((agent) => [agent.id, agent])); + +export function createIssue(overrides: Partial = {}): Issue { + return { + id: "issue-storybook-1", + companyId: "company-storybook", + projectId: "project-board-ui", + projectWorkspaceId: "workspace-board-ui", + goalId: "goal-company", + parentId: null, + title: "Create super-detailed storybooks for the project", + description: "Set up Storybook and move UX review surfaces into stories.", + status: "in_progress", + priority: "high", + assigneeAgentId: "agent-codex", + assigneeUserId: null, + checkoutRunId: "run-storybook", + executionRunId: "run-storybook", + executionAgentNameKey: "codexcoder", + executionLockedAt: recent(28), + createdByAgentId: null, + createdByUserId: "user-board", + issueNumber: 1641, + identifier: "PAP-1641", + requestDepth: 0, + billingCode: "product", + assigneeAdapterOverrides: null, + executionWorkspaceId: "execution-workspace-storybook", + executionWorkspacePreference: "project", + executionWorkspaceSettings: null, + startedAt: recent(28), + completedAt: null, + cancelledAt: null, + hiddenAt: null, + labelIds: ["label-ui", "label-docs"], + labels: [ + { + id: "label-ui", + companyId: "company-storybook", + name: "UI", + color: "#0f766e", + createdAt: recent(20_000), + updatedAt: recent(20_000), + }, + { + id: "label-docs", + companyId: "company-storybook", + name: "Design system", + color: "#f59e0b", + createdAt: recent(20_000), + updatedAt: recent(20_000), + }, + ], + blockedBy: [], + blocks: [], + planDocument: null, + documentSummaries: [], + legacyPlanDocument: null, + project: null, + goal: null, + currentExecutionWorkspace: null, + workProducts: [], + mentionedProjects: [], + myLastTouchAt: recent(8), + lastExternalCommentAt: recent(70), + lastActivityAt: recent(3), + isUnreadForMe: true, + createdAt: recent(90), + updatedAt: recent(3), + ...overrides, + }; +} + +export const storybookIssues: Issue[] = [ + createIssue(), + createIssue({ + id: "issue-storybook-2", + title: "Add budget hard-stop incident review", + status: "blocked", + priority: "critical", + assigneeAgentId: "agent-qa", + checkoutRunId: null, + executionRunId: null, + executionLockedAt: null, + startedAt: null, + identifier: "PAP-1528", + issueNumber: 1528, + lastActivityAt: recent(18), + }), + createIssue({ + id: "issue-storybook-3", + title: "QA invite flow on authenticated private mode", + status: "in_review", + priority: "medium", + assigneeAgentId: null, + assigneeUserId: "user-board", + checkoutRunId: null, + executionRunId: null, + executionLockedAt: null, + identifier: "PAP-1602", + issueNumber: 1602, + completedAt: null, + lastActivityAt: recent(49), + isUnreadForMe: false, + }), +]; + +export const storybookApprovals: Approval[] = [ + { + id: "approval-hire-designer", + companyId: "company-storybook", + type: "hire_agent", + requestedByAgentId: "agent-cto", + requestedByUserId: null, + status: "pending", + payload: { + name: "DesignSystemCoder", + role: "designer", + title: "Design System Engineer", + icon: "sparkles", + adapterType: "codex_local", + capabilities: "Owns component quality, visual regression stories, and design-system consolidation.", + desiredSkills: ["frontend-design", "web-design-guidelines"], + }, + decisionNote: null, + decidedByUserId: null, + decidedAt: null, + createdAt: recent(42), + updatedAt: recent(42), + }, + { + id: "approval-budget", + companyId: "company-storybook", + type: "budget_override_required", + requestedByAgentId: "agent-codex", + requestedByUserId: null, + status: "revision_requested", + payload: { + scopeName: "Paperclip App", + scopeType: "project", + windowKind: "calendar_month_utc", + metric: "billed_cents", + budgetAmount: 120_000, + observedAmount: 131_400, + guidance: "Raise the project budget only after current release smoke checks are green.", + }, + decisionNote: "Need a tighter verification list before approving more spend.", + decidedByUserId: "user-board", + decidedAt: recent(12), + createdAt: recent(75), + updatedAt: recent(12), + }, + { + id: "approval-board", + companyId: "company-storybook", + type: "request_board_approval", + requestedByAgentId: "agent-cto", + requestedByUserId: null, + status: "approved", + payload: { + title: "Publish the Storybook preview for design review", + summary: "Build the static Storybook and attach the generated URL to the release issue.", + recommendedAction: "Approve publishing the preview for internal board review.", + nextActionOnApproval: "Run build-storybook, upload the static artifact, and request QA visual review.", + risks: [ + "Stories may expose stale fixture copy if not maintained with UI changes.", + "Preview screenshots can hide responsive regressions unless mobile viewports are checked.", + ], + }, + decisionNote: "Approved for internal-only review.", + decidedByUserId: "user-board", + decidedAt: recent(4), + createdAt: recent(55), + updatedAt: recent(4), + }, +]; + +export const storybookBudgetSummaries: BudgetPolicySummary[] = [ + { + policyId: "budget-company-ok", + companyId: "company-storybook", + scopeType: "company", + scopeId: "company-storybook", + scopeName: "Paperclip Storybook", + metric: "billed_cents", + windowKind: "calendar_month_utc", + amount: 250_000, + observedAmount: 67_500, + remainingAmount: 182_500, + utilizationPercent: 27, + warnPercent: 80, + hardStopEnabled: true, + notifyEnabled: true, + isActive: true, + status: "ok", + paused: false, + pauseReason: null, + windowStart: new Date("2026-04-01T00:00:00.000Z"), + windowEnd: new Date("2026-05-01T00:00:00.000Z"), + }, + { + policyId: "budget-project-warning", + companyId: "company-storybook", + scopeType: "project", + scopeId: "project-board-ui", + scopeName: "Paperclip App", + metric: "billed_cents", + windowKind: "calendar_month_utc", + amount: 120_000, + observedAmount: 103_100, + remainingAmount: 16_900, + utilizationPercent: 86, + warnPercent: 80, + hardStopEnabled: true, + notifyEnabled: true, + isActive: true, + status: "warning", + paused: false, + pauseReason: null, + windowStart: new Date("2026-04-01T00:00:00.000Z"), + windowEnd: new Date("2026-05-01T00:00:00.000Z"), + }, + { + policyId: "budget-agent-hard-stop", + companyId: "company-storybook", + scopeType: "agent", + scopeId: "agent-codex", + scopeName: "CodexCoder", + metric: "billed_cents", + windowKind: "calendar_month_utc", + amount: 40_000, + observedAmount: 43_200, + remainingAmount: 0, + utilizationPercent: 108, + warnPercent: 80, + hardStopEnabled: true, + notifyEnabled: true, + isActive: true, + status: "hard_stop", + paused: true, + pauseReason: "budget", + windowStart: new Date("2026-04-01T00:00:00.000Z"), + windowEnd: new Date("2026-05-01T00:00:00.000Z"), + }, +]; + +export const storybookActivityEvents: ActivityEvent[] = [ + { + id: "activity-1", + companyId: "company-storybook", + actorType: "agent", + actorId: "agent-codex", + action: "issue.status_changed", + entityType: "issue", + entityId: "issue-storybook-1", + agentId: "agent-codex", + runId: "run-storybook", + details: { from: "todo", to: "in_progress" }, + createdAt: recent(28), + }, + { + id: "activity-2", + companyId: "company-storybook", + actorType: "user", + actorId: "user-board", + action: "approval.revision_requested", + entityType: "approval", + entityId: "approval-budget", + agentId: null, + runId: null, + details: { type: "budget_override_required" }, + createdAt: recent(12), + }, + { + id: "activity-3", + companyId: "company-storybook", + actorType: "system", + actorId: "system", + action: "budget.hard_stop", + entityType: "agent", + entityId: "agent-codex", + agentId: "agent-codex", + runId: null, + details: { observedAmount: 43_200, budgetAmount: 40_000 }, + createdAt: recent(7), + }, +]; + +export const storybookEntityNameMap = new Map([ + ["issue:issue-storybook-1", "PAP-1641"], + ["issue:issue-storybook-2", "PAP-1528"], + ["issue:issue-storybook-3", "PAP-1602"], + ["approval:approval-budget", "Budget override"], + ["agent:agent-codex", "CodexCoder"], + ["agent:agent-qa", "QAChecker"], + ["agent:agent-cto", "CTO"], +]); + +export const storybookEntityTitleMap = new Map([ + ["issue:issue-storybook-1", "Create super-detailed storybooks for the project"], + ["issue:issue-storybook-2", "Add budget hard-stop incident review"], + ["issue:issue-storybook-3", "QA invite flow on authenticated private mode"], +]); diff --git a/ui/storybook/stories/control-plane-surfaces.stories.tsx b/ui/storybook/stories/control-plane-surfaces.stories.tsx new file mode 100644 index 00000000000..98e4a1fc5cb --- /dev/null +++ b/ui/storybook/stories/control-plane-surfaces.stories.tsx @@ -0,0 +1,266 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { AlertTriangle, CheckCircle2, Clock3, Eye, GitPullRequest, Inbox, WalletCards } from "lucide-react"; +import { ActivityRow } from "@/components/ActivityRow"; +import { ApprovalCard } from "@/components/ApprovalCard"; +import { BudgetPolicyCard } from "@/components/BudgetPolicyCard"; +import { Identity } from "@/components/Identity"; +import { IssueRow } from "@/components/IssueRow"; +import { PriorityIcon } from "@/components/PriorityIcon"; +import { StatusBadge } from "@/components/StatusBadge"; +import { Badge } from "@/components/ui/badge"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { + storybookActivityEvents, + storybookAgentMap, + storybookAgents, + storybookApprovals, + storybookBudgetSummaries, + storybookEntityNameMap, + storybookEntityTitleMap, + storybookIssues, +} from "../fixtures/paperclipData"; + +function Section({ + eyebrow, + title, + children, +}: { + eyebrow: string; + title: string; + children: React.ReactNode; +}) { + return ( +
+
+
{eyebrow}
+

{title}

+
+
{children}
+
+ ); +} + +function ControlPlaneSurfaces() { + return ( +
+
+
+
+
+
Product surfaces
+

Control-plane boards and cards

+

+ Paperclip's common board surfaces are deliberately dense: task rows, approvals, budget policy cards, + and audit rows all need to scan quickly while preserving enough state to make autonomous work governable. +

+
+
+ company scoped + single assignee + auditable +
+
+
+ +
+
+ {storybookIssues.map((issue, index) => ( + undefined} + onArchive={() => undefined} + desktopTrailing={ + + + {issue.assigneeAgentId ? ( + + ) : ( + Board + )} + + } + trailingMeta={index === 0 ? "3m ago" : index === 1 ? "blocked by budget" : "review requested"} + mobileMeta={} + titleSuffix={ + index === 0 ? ( + + Storybook + + ) : null + } + /> + ))} +
+
+ +
+
+ {storybookApprovals.map((approval) => ( + undefined : undefined} + onReject={approval.status === "pending" ? () => undefined : undefined} + detailLink={`/approvals/${approval.id}`} + /> + ))} +
+
+ +
+
+ {storybookBudgetSummaries.map((summary, index) => ( + undefined : undefined} + /> + ))} +
+
+ undefined} + /> +
+
+ +
+
+
+ {storybookActivityEvents.map((event) => ( + + ))} +
+ + + + Run summary card language + Compact status treatments used around live work. + + + {[ + { icon: Clock3, label: "Running", detail: "CodexCoder is editing Storybook fixtures", tone: "text-cyan-600" }, + { icon: GitPullRequest, label: "Review", detail: "QAChecker requested browser screenshots", tone: "text-amber-600" }, + { icon: CheckCircle2, label: "Verified", detail: "Vitest and static Storybook build passed", tone: "text-emerald-600" }, + { icon: AlertTriangle, label: "Blocked", detail: "Budget hard stop paused a run", tone: "text-red-600" }, + ].map((item) => { + const Icon = item.icon; + return ( +
+ +
+
{item.label}
+
{item.detail}
+
+
+ ); + })} +
+
+
+
+ +
+
+ {storybookAgents.map((agent) => ( + + +
+ + +
+ {agent.title} +
+ +

{agent.capabilities}

+
+ {agent.role} + {agent.adapterType} + + + ${(agent.spentMonthlyCents / 100).toFixed(0)} spent + +
+
+
+ ))} +
+
+ +
+
+ + + + + Inbox slice + + Small panels should keep controls reachable without nested cards. + + +
+ Unread + 7 +
+
+ Needs review + 3 +
+
+ Blocked + 1 +
+
+
+
+
+ + Review target +
+ undefined} + desktopTrailing={} + trailingMeta="active run" + /> +
+
+
+
+
+ ); +} + +const meta = { + title: "Product/Control Plane Surfaces", + component: ControlPlaneSurfaces, + parameters: { + docs: { + description: { + component: + "Product-surface stories exercise the board UI components that carry Paperclip's task, approval, budget, activity, and agent governance workflows.", + }, + }, + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const BoardStateMatrix: Story = {}; diff --git a/ui/storybook/stories/foundations.stories.tsx b/ui/storybook/stories/foundations.stories.tsx new file mode 100644 index 00000000000..94c1a6874e4 --- /dev/null +++ b/ui/storybook/stories/foundations.stories.tsx @@ -0,0 +1,300 @@ +import { useState } from "react"; +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { AlertTriangle, ArrowRight, Check, Copy, Play, Plus, Save, Search, Settings } from "lucide-react"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Checkbox } from "@/components/ui/checkbox"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Separator } from "@/components/ui/separator"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Textarea } from "@/components/ui/textarea"; +import { ToggleSwitch } from "@/components/ui/toggle-switch"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; + +const buttonVariants = ["default", "secondary", "outline", "ghost", "destructive", "link"] as const; +const buttonSizes = ["xs", "sm", "default", "lg", "icon", "icon-sm"] as const; +const badgeVariants = ["default", "secondary", "outline", "destructive", "ghost", "link"] as const; + +function Section({ + eyebrow, + title, + children, +}: { + eyebrow: string; + title: string; + children: React.ReactNode; +}) { + return ( +
+
+
{eyebrow}
+

{title}

+
+
{children}
+
+ ); +} + +function FoundationsMatrix() { + const [autoMode, setAutoMode] = useState(true); + const [boardApproval, setBoardApproval] = useState(true); + + return ( +
+
+
+
Foundations
+

Primitives and interaction states

+

+ A dense pass over the base controls that Paperclip pages use for operational actions, filtering, + approvals, and settings. +

+
+ +
+
+
+ {buttonVariants.map((variant) => ( +
+
{variant}
+
+ + +
+
+ ))} +
+
+
Sizes and icon-only actions
+
+ {buttonSizes.map((size) => ( + + ))} +
+
+
+
+ +
+
+ {badgeVariants.map((variant) => ( + + {variant === "destructive" ? : variant === "default" ? : null} + {variant} + + ))} + + PAP-1641 + + + + in review + +
+
+ +
+
+
+
+ + +
+
+ +