diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 84492b8f4..5528fe018 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -363,6 +363,33 @@ jobs: retention-days: 7 path: packages/ui/.artifacts/unit/junit.xml + storybook-smoke: + needs: changes + if: needs.changes.outputs.docs_only != 'true' + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # step-security/harden-runner@v2.19.4 + with: + egress-policy: audit + + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # actions/checkout@v6 + with: + persist-credentials: false + + - uses: ./.github/actions/setup + + - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 + with: + path: .turbo/cache + key: turbo-${{ runner.os }}-build-storybook-${{ hashFiles('turbo.json', '**/package.json', 'bun.lock') }}-${{ github.sha }} + restore-keys: | + turbo-${{ runner.os }}-build-storybook-${{ hashFiles('turbo.json', '**/package.json', 'bun.lock') }}- + turbo-${{ runner.os }}-build-storybook- + + - name: build-storybook + run: bun turbo build-storybook --filter=@opencode-ai/ui + unit-opencode: needs: changes if: needs.changes.outputs.docs_only != 'true' @@ -541,6 +568,7 @@ jobs: - typecheck - frontend-architecture - unit-ui + - storybook-smoke - unit-app - unit-opencode - unit-desktop @@ -553,6 +581,7 @@ jobs: TYPECHECK_RESULT: ${{ needs.typecheck.result }} FRONTEND_ARCHITECTURE_RESULT: ${{ needs['frontend-architecture'].result }} UNIT_UI_RESULT: ${{ needs['unit-ui'].result }} + STORYBOOK_SMOKE_RESULT: ${{ needs['storybook-smoke'].result }} UNIT_APP_RESULT: ${{ needs['unit-app'].result }} UNIT_OPENCODE_RESULT: ${{ needs['unit-opencode'].result }} UNIT_DESKTOP_RESULT: ${{ needs['unit-desktop'].result }} @@ -568,6 +597,7 @@ jobs: if [ "$TYPECHECK_RESULT" != "success" ] || [ "$FRONTEND_ARCHITECTURE_RESULT" != "success" ] || [ "$UNIT_UI_RESULT" != "success" ] || + [ "$STORYBOOK_SMOKE_RESULT" != "success" ] || [ "$UNIT_APP_RESULT" != "success" ] || [ "$UNIT_OPENCODE_RESULT" != "success" ] || [ "$UNIT_DESKTOP_RESULT" != "success" ] || @@ -575,6 +605,7 @@ jobs: echo "typecheck=$TYPECHECK_RESULT" echo "frontend-architecture=$FRONTEND_ARCHITECTURE_RESULT" echo "unit-ui=$UNIT_UI_RESULT" + echo "storybook-smoke=$STORYBOOK_SMOKE_RESULT" echo "unit-app=$UNIT_APP_RESULT" echo "unit-opencode=$UNIT_OPENCODE_RESULT" echo "unit-desktop=$UNIT_DESKTOP_RESULT" diff --git a/bun.lock b/bun.lock index cb6a3bc2e..3b18b0c30 100644 --- a/bun.lock +++ b/bun.lock @@ -385,6 +385,8 @@ "@types/katex": "0.16.7", "@types/luxon": "catalog:", "@typescript/native-preview": "catalog:", + "storybook": "10.4.6", + "storybook-solidjs-vite": "10.5.2", "tailwindcss": "catalog:", "typescript": "catalog:", "vite": "catalog:", @@ -708,8 +710,12 @@ "@electron/windows-sign": ["@electron/windows-sign@1.2.2", "", { "dependencies": { "cross-dirname": "^0.1.0", "debug": "^4.3.4", "fs-extra": "^11.1.1", "minimist": "^1.2.8", "postject": "^1.0.0-alpha.6" }, "bin": { "electron-windows-sign": "bin/electron-windows-sign.js" } }, "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ=="], + "@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="], + "@emnapi/runtime": ["@emnapi/runtime@1.9.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="], + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + "@emotion/is-prop-valid": ["@emotion/is-prop-valid@0.8.8", "", { "dependencies": { "@emotion/memoize": "0.7.4" } }, "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA=="], "@emotion/memoize": ["@emotion/memoize@0.7.4", "", {}, "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw=="], @@ -990,6 +996,8 @@ "@napi-rs/canvas-win32-x64-msvc": ["@napi-rs/canvas-win32-x64-msvc@0.1.97", "", { "os": "win32", "cpu": "x64" }, "sha512-sWtD2EE3fV0IzN+iiQUqr/Q1SwqWhs2O1FKItFlxtdDkikpEj5g7DKQpY3x55H/MAOnL8iomnlk3mcEeGiUMoQ=="], + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.6", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg=="], + "@nodable/entities": ["@nodable/entities@2.1.1", "", {}, "sha512-Pig3HxDIoMgjdEH8OCf/dkcTmLFjJRjWuq8jSnklu284/TKOPibSRERmOykiwmyXTtv61mP+44f3GMx0tLAyjg=="], "@npm/types": ["@npm/types@1.0.2", "", {}, "sha512-KXZccTDEnWqNrrx6JjpJKU/wJvNeg9BDgjS0XhmlZab7br921HtyVbsYzJr4L+xIvjdJ20Wh9dgxgCI2a5CEQw=="], @@ -1108,6 +1116,86 @@ "@oslojs/jwt": ["@oslojs/jwt@0.2.0", "", { "dependencies": { "@oslojs/encoding": "0.4.1" } }, "sha512-bLE7BtHrURedCn4Mco3ma9L4Y1GR2SMBuIvjWr7rmQ4/W/4Jy70TIAgZ+0nIlk0xHz1vNP8x8DCns45Sb2XRbg=="], + "@oxc-parser/binding-android-arm-eabi": ["@oxc-parser/binding-android-arm-eabi@0.127.0", "", { "os": "android", "cpu": "arm" }, "sha512-0LC7ye4hvqbIKxAzThzvswgHLFu2AURKzYLeSVvLdu2TBOYWQDmHnTqPLeA597BcUCxiLqLsS4CJ5uoI5WYWCQ=="], + + "@oxc-parser/binding-android-arm64": ["@oxc-parser/binding-android-arm64@0.127.0", "", { "os": "android", "cpu": "arm64" }, "sha512-b5jtVTH6AU5CJXHNdj7Jj9IEiR9yVjjnwHzPJhGyHGPdcsZSzBCkS9GBbV33niRMvKthDwQRFRJfI4a+k4PvYg=="], + + "@oxc-parser/binding-darwin-arm64": ["@oxc-parser/binding-darwin-arm64@0.127.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-obCE8B7ISKkJidjlhv9xRGJPOSDG2Yu6PRga9Ruaz35uintHxbp1Ki/Yc71wx4rj3Edrm0a1kzG1TAwit0wFpg=="], + + "@oxc-parser/binding-darwin-x64": ["@oxc-parser/binding-darwin-x64@0.127.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-JL6Xb5IwPQT8rUzlpsX7E+AgfcdNklXNPFp8pjCQQ5MQOQo5rtEB2ui+3Hgg9Sn7Y9Egj6YOLLiHhLpdAe12Aw=="], + + "@oxc-parser/binding-freebsd-x64": ["@oxc-parser/binding-freebsd-x64@0.127.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-SDQ/3MQFw58fqQz3Z1PhSKFF3JoCF4gmlNjziDm8X02tTahCw0qJbd7FGPDKw1i4VTBZene9JPyC3mHtSvi+wA=="], + + "@oxc-parser/binding-linux-arm-gnueabihf": ["@oxc-parser/binding-linux-arm-gnueabihf@0.127.0", "", { "os": "linux", "cpu": "arm" }, "sha512-Av+D1MIqzV0YMGPT9we2SIZaMKD7Cxs4CvXSx/yxaWHewZjYEjScpOf5igc8IILASViw4WTnjlwUdI1KzVtDHQ=="], + + "@oxc-parser/binding-linux-arm-musleabihf": ["@oxc-parser/binding-linux-arm-musleabihf@0.127.0", "", { "os": "linux", "cpu": "arm" }, "sha512-Cs2fdJ8cPpFdeebj6p4dag8A4+56hPvZ0AhQQzlaLswGz1tz7bXt1nETLeorrM9+AMcWFFkqxcXwDGfTVidY8g=="], + + "@oxc-parser/binding-linux-arm64-gnu": ["@oxc-parser/binding-linux-arm64-gnu@0.127.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-qdOfTcT6SY8gsJrrV92uyEUyjqMGPpIB5JZUG6QN5dukYd+7/j0kX6MwK1DgQj39jtUYixxPiaRUiEN1+0CXgQ=="], + + "@oxc-parser/binding-linux-arm64-musl": ["@oxc-parser/binding-linux-arm64-musl@0.127.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-EoTCZneNFU/P2qrpEM+RHmQwt+CvDkyGESG6qhr7KaegXLZwePfbrkCDfAk8/rhxbDUVGsZILX+2tqPzFtoFWA=="], + + "@oxc-parser/binding-linux-ppc64-gnu": ["@oxc-parser/binding-linux-ppc64-gnu@0.127.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-zALjmZYgxFLHjXeudcDF0xFGNydTAtkAeXAr2EuC17ywCyFxcmQra4w0BMde0Yi/re4Bi4iwEoEXtYN7l6eBLQ=="], + + "@oxc-parser/binding-linux-riscv64-gnu": ["@oxc-parser/binding-linux-riscv64-gnu@0.127.0", "", { "os": "linux", "cpu": "none" }, "sha512-fPP8M6zQLS7Jz7o9d5ArUSuAuSK3e+WCYVrCpdzeCOejidtZExJ9tjhDrAd3HEPqARBCPmdpqxESPFqy44vkBQ=="], + + "@oxc-parser/binding-linux-riscv64-musl": ["@oxc-parser/binding-linux-riscv64-musl@0.127.0", "", { "os": "linux", "cpu": "none" }, "sha512-7IcC4Ao02oGpfnjt+X/oF4U2mllo2qoSkw5xxiXNKL9MCTsTiAC6616beOuehdxGcnz1bRoPC1RQ2f1GQDdN+g=="], + + "@oxc-parser/binding-linux-s390x-gnu": ["@oxc-parser/binding-linux-s390x-gnu@0.127.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-pbXIhiNFHoqWeqDNLiJ9JkpHz1IM9k4DXa66x+1GTWMG7iLxtkXgE53iiuKSXwmk3zIYmaPVfBvgcAhS583K4Q=="], + + "@oxc-parser/binding-linux-x64-gnu": ["@oxc-parser/binding-linux-x64-gnu@0.127.0", "", { "os": "linux", "cpu": "x64" }, "sha512-MYCguB9RvBvlSd6gbuNI7QwiLoCCAlGnlRJFPrzLI6U1/9wkC/WK6LtBAUln55H1Ctqw45PWmqrobKoMhsYQzQ=="], + + "@oxc-parser/binding-linux-x64-musl": ["@oxc-parser/binding-linux-x64-musl@0.127.0", "", { "os": "linux", "cpu": "x64" }, "sha512-5eY0B/bxf1xIUxb4NOTvOI3KWtBQfPWYyKAzgcrCt0mDibSZygVpO1Pz8bkeiSZ5Jj9+M09dkggG3H8I5d0Uyg=="], + + "@oxc-parser/binding-openharmony-arm64": ["@oxc-parser/binding-openharmony-arm64@0.127.0", "", { "os": "none", "cpu": "arm64" }, "sha512-Gld0ajrFTUXNtdw20fVBuTQx66FA75nIVg+//pPfR3sXkuABB4mTBhl3r9JNzrJpgW//qiwxf0nWXUWGJSL3UQ=="], + + "@oxc-parser/binding-wasm32-wasi": ["@oxc-parser/binding-wasm32-wasi@0.127.0", "", { "dependencies": { "@emnapi/core": "1.9.2", "@emnapi/runtime": "1.9.2", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-T6KVD7rhLzFlwGRXMnxUFfkCZD8FHnb968wVXW1mXzgRFc5RNXOBY2mPPDZ77x5Ln76ltLMgtPg0cOkU1NSrEQ=="], + + "@oxc-parser/binding-win32-arm64-msvc": ["@oxc-parser/binding-win32-arm64-msvc@0.127.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Ujvw4X+LD1CCGULcsQcvb4YNVoBGqt+JHgNNzGGaCImELiZLk477ifUH53gIbE7EKd933NdTi25JWEr9K2HwXw=="], + + "@oxc-parser/binding-win32-ia32-msvc": ["@oxc-parser/binding-win32-ia32-msvc@0.127.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-0cwxKO7KHQQQfo4Uf4B2SQrhgm+cJaP9OvFFhx52Tkg4bezsacu83GB2/In5bC415Ueeym+kXdnge/57rbSfTw=="], + + "@oxc-parser/binding-win32-x64-msvc": ["@oxc-parser/binding-win32-x64-msvc@0.127.0", "", { "os": "win32", "cpu": "x64" }, "sha512-rOrnSQSCbhI2kowr9XxE7m9a8oQXnBHjnS6j95LxxAnEZ0+Fz20WlRXG4ondQb+ejjt2KOsa65sE6++L6kUd+w=="], + + "@oxc-project/types": ["@oxc-project/types@0.127.0", "", {}, "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ=="], + + "@oxc-resolver/binding-android-arm-eabi": ["@oxc-resolver/binding-android-arm-eabi@11.22.0", "", { "os": "android", "cpu": "arm" }, "sha512-il+0FB7BBUfuQaE0Lgd9zlgSjzu88ErN8vr4hintuTt1qRDcPtmzLyurail1gJZpJ1ljo7zA0cid/a/PaWMyZg=="], + + "@oxc-resolver/binding-android-arm64": ["@oxc-resolver/binding-android-arm64@11.22.0", "", { "os": "android", "cpu": "arm64" }, "sha512-rWCyvcoiMxb5JRsGMXI22DFAlsddZHOTBWp/zz48E85Yh/KWQRFko18Gf5xsRdcN0pz65VFnJoisz/B2LWoaGg=="], + + "@oxc-resolver/binding-darwin-arm64": ["@oxc-resolver/binding-darwin-arm64@11.22.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eDx1up8xhb6OH58RfcADHAKWQY3yatNAbrOF2QEqDN2ml0DsOlHBNgj7E5NB3kU62yam2VEYnFTMO8meOYEe1w=="], + + "@oxc-resolver/binding-darwin-x64": ["@oxc-resolver/binding-darwin-x64@11.22.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-4YUMAsVqqQGzkq7eDWEZXUvzm1L7eZFd4jghnoDv76fPF2IisedcBjkJY3iwcAlWQNtZLgc5Od/cL0Z2ogEwaQ=="], + + "@oxc-resolver/binding-freebsd-x64": ["@oxc-resolver/binding-freebsd-x64@11.22.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-I7qjjmCzrqPme94B9b9deHID6YiggKQRy3s9mTjnUuYlpgDx5YgC1G00W2S/Cchrjz5I8VOik17/3uO4joPULw=="], + + "@oxc-resolver/binding-linux-arm-gnueabihf": ["@oxc-resolver/binding-linux-arm-gnueabihf@11.22.0", "", { "os": "linux", "cpu": "arm" }, "sha512-VsI6Vnsyg9O5jLv+bkYP15yHv924i63fLbROZAZfwAAUJ611FF8OE4aCX2KzsG70yRlcn4n7Zh0fyHT5L4myGA=="], + + "@oxc-resolver/binding-linux-arm-musleabihf": ["@oxc-resolver/binding-linux-arm-musleabihf@11.22.0", "", { "os": "linux", "cpu": "arm" }, "sha512-Imedx3sbderR0w8HHZ+vH7PqrY7eL3H7cj666Yrg+erelaRCVzXlJjQD5w0vNk+RtGDqhmnP5R18WIowFCI+uA=="], + + "@oxc-resolver/binding-linux-arm64-gnu": ["@oxc-resolver/binding-linux-arm64-gnu@11.22.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-a9L5IxPBpiSXcvEPGNWOpD5pKbcE0VgC5smcaYn0t/oMaJxHwejrJ1qRoXZLj767HAo+nq9FNEpZ9WFW91lP8g=="], + + "@oxc-resolver/binding-linux-arm64-musl": ["@oxc-resolver/binding-linux-arm64-musl@11.22.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-4AFo9hX0AbA/o7qWrsrAHbRGsZpthcUEZiuMHlxqZsR4JNgvFmeuLtMXUV+KPHWo+gfefFmiQ0UdUx8GPBCrXQ=="], + + "@oxc-resolver/binding-linux-ppc64-gnu": ["@oxc-resolver/binding-linux-ppc64-gnu@11.22.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-QVPpxFDkLxWAnfAqN0DA1TH4agOPL1bxg7dwUZ7goQKU5IfaPoL/ZcPClMol4+Dwb30g2nPNxbr0BPyFmcVV3A=="], + + "@oxc-resolver/binding-linux-riscv64-gnu": ["@oxc-resolver/binding-linux-riscv64-gnu@11.22.0", "", { "os": "linux", "cpu": "none" }, "sha512-saQJeKGMCrtW5DH8uY9N9pPE4/8Hs+DpZ4hJg1+SzvISSKhTf3V6/jOROxluU14ftz5KNd8G/NXRgj9vTS0Emg=="], + + "@oxc-resolver/binding-linux-riscv64-musl": ["@oxc-resolver/binding-linux-riscv64-musl@11.22.0", "", { "os": "linux", "cpu": "none" }, "sha512-/p6aCGRKot+Je44l+WoL+zkizRXY4ApQcvRXlLw8lRM305tmmEqNtAyekDLMCzn8DUt81lS3ZsiUpdn0bL533A=="], + + "@oxc-resolver/binding-linux-s390x-gnu": ["@oxc-resolver/binding-linux-s390x-gnu@11.22.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-ZYfI5CG/W1C+HXDWkJ5+JPjiuwVQw6HBD1jUTneAzJVWImRDjstQPKmixCa1fTkthCM1OCkn2D8fdX+q37kMXQ=="], + + "@oxc-resolver/binding-linux-x64-gnu": ["@oxc-resolver/binding-linux-x64-gnu@11.22.0", "", { "os": "linux", "cpu": "x64" }, "sha512-IR2juRKWbR6TmFZTn6plHFm5iXWD8Szw/fGeKhaGWzwTPN/Oq4CCV6ZVp8Bq8ih2easVh7Mwom2A5CGkB+QVxg=="], + + "@oxc-resolver/binding-linux-x64-musl": ["@oxc-resolver/binding-linux-x64-musl@11.22.0", "", { "os": "linux", "cpu": "x64" }, "sha512-TCE/wgDr3EaxQrwQrU9MbRK35cFsYAVwMT2Du0lbyjmlaXV03uPLnCKIDDmxUPyQUdPgZirM+k26GDR3LNs+hw=="], + + "@oxc-resolver/binding-openharmony-arm64": ["@oxc-resolver/binding-openharmony-arm64@11.22.0", "", { "os": "none", "cpu": "arm64" }, "sha512-uhNpwQzWnYVBZ6ZZomIQN2X/jUDLp3HYjLSVbdsZqA4hNpYSFENSF8JV6I6gdvvV9TLQr1rC/viDsxhvE/5/Ww=="], + + "@oxc-resolver/binding-wasm32-wasi": ["@oxc-resolver/binding-wasm32-wasi@11.22.0", "", { "dependencies": { "@emnapi/core": "1.11.1", "@emnapi/runtime": "1.11.1", "@napi-rs/wasm-runtime": "^1.1.6" }, "cpu": "none" }, "sha512-nn8NCiQhh3rgwrGMf8msky7MjPd0W8Vwmo7oZr5cs7TOJby2IRMZYPPgb+NTz8vVG6VjjjcLPriIMxHE1aAx1A=="], + + "@oxc-resolver/binding-win32-arm64-msvc": ["@oxc-resolver/binding-win32-arm64-msvc@11.22.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-kHx9KiAKQKeSW/yVmt5tUa7oAHUd9OsoB5fdpQjSBDaKtHSQpPIXxNJ1E9q8cnaxS9NNWANR6w3osEHBD+5lSg=="], + + "@oxc-resolver/binding-win32-x64-msvc": ["@oxc-resolver/binding-win32-x64-msvc@11.22.0", "", { "os": "win32", "cpu": "x64" }, "sha512-gZKq7BTQBdAmvFYGQfqkuz3zeoytmAkluFjajToSkWUTed0DsJ3GdCoXpPdpGSElBLLIeAgKM3ju6XdPvKOr0A=="], + "@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="], "@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.1", "", { "os": "android", "cpu": "arm64" }, "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA=="], @@ -1300,6 +1388,14 @@ "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], + "@storybook/builder-vite": ["@storybook/builder-vite@10.4.4", "", { "dependencies": { "@storybook/csf-plugin": "10.4.4", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^10.4.4", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-VyuZ4mEvhhVXjJa1qXMWKH8ohnas0rgEuJDf6u4aJ54XeENFebPUEAHde1Qo2PflJ4rUdVdXieOZzKbYwP5RAQ=="], + + "@storybook/csf-plugin": ["@storybook/csf-plugin@10.4.4", "", { "dependencies": { "unplugin": "^2.3.5" }, "peerDependencies": { "esbuild": "*", "rollup": "*", "storybook": "^10.4.4", "vite": "*", "webpack": "*" }, "optionalPeers": ["esbuild", "rollup", "vite", "webpack"] }, "sha512-1mzZyAwVUmAcw4WEUsJDVdSupkJf+Kf/f5uNAs4RzlBXA75P8YRkDKAb2EoMwsB5URiXFi9XoeAN/vWke0G6+w=="], + + "@storybook/global": ["@storybook/global@5.0.0", "", {}, "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ=="], + + "@storybook/icons": ["@storybook/icons@2.1.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Fxh9vYpX9bQqFeHRiY8h2ApeRGDzRSMLwJwNZ/AIRqnyOKHxRKL+yFe+ctEkVJmuptRE9u1Hrn8ZZNHyfDKKNg=="], + "@swc/helpers": ["@swc/helpers@0.5.20", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-2egEBHUMasdypIzrprsu8g+OEVd7Vp2MM3a2eVlM/cyFYto0nGz5BX5BTgh/ShZZI9ed+ozEq+Ngt+rgmUs8tw=="], "@szmarczak/http-timer": ["@szmarczak/http-timer@4.0.6", "", { "dependencies": { "defer-to-connect": "^2.0.0" } }, "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w=="], @@ -1344,8 +1440,12 @@ "@tediousjs/connection-string": ["@tediousjs/connection-string@0.5.0", "", {}, "sha512-7qSgZbincDDDFyRweCIEvZULFAw5iz/DeunhvuxpL31nfntX3P4Yd4HkHBRg9H8CdqY1e5WFN1PZIz/REL9MVQ=="], + "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], + "@testing-library/jest-dom": ["@testing-library/jest-dom@6.9.1", "", { "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", "picocolors": "^1.1.1", "redent": "^3.0.0" } }, "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA=="], + "@testing-library/user-event": ["@testing-library/user-event@14.6.1", "", { "peerDependencies": { "@testing-library/dom": ">=7.21.4" } }, "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw=="], + "@thisbeyond/solid-dnd": ["@thisbeyond/solid-dnd@0.7.5", "", { "peerDependencies": { "solid-js": "^1.5" } }, "sha512-DfI5ff+yYGpK9M21LhYwIPlbP2msKxN2ARwuu6GF8tT1GgNVDTI8VCQvH4TJFoVApP9d44izmAcTh/iTCH2UUw=="], "@tsconfig/bun": ["@tsconfig/bun@1.0.9", "", {}, "sha512-4M0/Ivfwcpz325z6CwSifOBZYji3DFOEpY6zEUt0+Xi2qRhzwvmqQN9XAHJh3OVvRJuAqVTLU2abdCplvp6mwQ=="], @@ -1356,6 +1456,10 @@ "@tufjs/models": ["@tufjs/models@4.1.0", "", { "dependencies": { "@tufjs/canonical-json": "2.0.0", "minimatch": "^10.1.1" } }, "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww=="], + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="], + + "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], + "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], @@ -1370,10 +1474,14 @@ "@types/cacheable-request": ["@types/cacheable-request@6.0.3", "", { "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "^3.1.4", "@types/node": "*", "@types/responselike": "^1.0.0" } }, "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw=="], + "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], + "@types/cross-spawn": ["@types/cross-spawn@6.0.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA=="], "@types/debug": ["@types/debug@4.1.13", "", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="], + "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], "@types/fs-extra": ["@types/fs-extra@9.0.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA=="], @@ -1418,6 +1526,8 @@ "@types/qrcode": ["@types/qrcode@1.5.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg=="], + "@types/react": ["@types/react@19.2.17", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw=="], + "@types/readable-stream": ["@types/readable-stream@4.0.23", "", { "dependencies": { "@types/node": "*" } }, "sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig=="], "@types/responselike": ["@types/responselike@1.0.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw=="], @@ -1490,6 +1600,22 @@ "@vercel/oidc": ["@vercel/oidc@3.1.0", "", {}, "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w=="], + "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], + + "@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], + + "@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], + + "@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], + + "@volar/language-core": ["@volar/language-core@2.4.28", "", { "dependencies": { "@volar/source-map": "2.4.28" } }, "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ=="], + + "@volar/source-map": ["@volar/source-map@2.4.28", "", {}, "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ=="], + + "@volar/typescript": ["@volar/typescript@2.4.28", "", { "dependencies": { "@volar/language-core": "2.4.28", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw=="], + + "@webcontainer/env": ["@webcontainer/env@1.1.1", "", {}, "sha512-6aN99yL695Hi9SuIk1oC88l9o0gmxL1nGWWQ/kNy81HigJ0FoaoTXpytCj6ItzgyCEwA9kF1wixsTuv5cjsgng=="], + "@xmldom/xmldom": ["@xmldom/xmldom@0.8.13", "", {}, "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw=="], "@zip.js/zip.js": ["@zip.js/zip.js@2.7.62", "", {}, "sha512-OaLvZ8j4gCkLn048ypkZu29KX30r8/OfFF2w4Jo5WXFr+J04J+lzJ5TKZBVgFXhlvSkqNFQdfnY1Q8TMTCyBVA=="], @@ -1536,6 +1662,10 @@ "assert-plus": ["assert-plus@1.0.0", "", {}, "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="], + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + + "ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="], + "astral-regex": ["astral-regex@2.0.0", "", {}, "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ=="], "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], @@ -1630,12 +1760,16 @@ "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], + "check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="], + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], @@ -1730,6 +1864,8 @@ "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], + "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], "default-browser": ["default-browser@5.5.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="], @@ -2304,6 +2440,8 @@ "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], + "lowercase-keys": ["lowercase-keys@2.0.0", "", {}, "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="], "lru-cache": ["lru-cache@11.2.7", "", {}, "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA=="], @@ -2314,6 +2452,8 @@ "luxon": ["luxon@3.6.1", "", {}, "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ=="], + "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], "make-fetch-happen": ["make-fetch-happen@15.0.5", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/agent": "^4.0.0", "@npmcli/redact": "^4.0.0", "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", "minipass-fetch": "^5.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^1.0.0", "proc-log": "^6.0.0", "ssri": "^13.0.0" } }, "sha512-uCbIa8jWWmQZt4dSnEStkVC6gdakiinAm4PiGsywIkguF0eWMdcjDz0ECYhUolFU3pFLOev9VNPCEygydXnddg=="], @@ -2504,6 +2644,10 @@ "ora": ["ora@5.4.1", "", { "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-spinners": "^2.5.0", "is-interactive": "^1.0.0", "is-unicode-supported": "^0.1.0", "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" } }, "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ=="], + "oxc-parser": ["oxc-parser@0.127.0", "", { "dependencies": { "@oxc-project/types": "^0.127.0" }, "optionalDependencies": { "@oxc-parser/binding-android-arm-eabi": "0.127.0", "@oxc-parser/binding-android-arm64": "0.127.0", "@oxc-parser/binding-darwin-arm64": "0.127.0", "@oxc-parser/binding-darwin-x64": "0.127.0", "@oxc-parser/binding-freebsd-x64": "0.127.0", "@oxc-parser/binding-linux-arm-gnueabihf": "0.127.0", "@oxc-parser/binding-linux-arm-musleabihf": "0.127.0", "@oxc-parser/binding-linux-arm64-gnu": "0.127.0", "@oxc-parser/binding-linux-arm64-musl": "0.127.0", "@oxc-parser/binding-linux-ppc64-gnu": "0.127.0", "@oxc-parser/binding-linux-riscv64-gnu": "0.127.0", "@oxc-parser/binding-linux-riscv64-musl": "0.127.0", "@oxc-parser/binding-linux-s390x-gnu": "0.127.0", "@oxc-parser/binding-linux-x64-gnu": "0.127.0", "@oxc-parser/binding-linux-x64-musl": "0.127.0", "@oxc-parser/binding-openharmony-arm64": "0.127.0", "@oxc-parser/binding-wasm32-wasi": "0.127.0", "@oxc-parser/binding-win32-arm64-msvc": "0.127.0", "@oxc-parser/binding-win32-ia32-msvc": "0.127.0", "@oxc-parser/binding-win32-x64-msvc": "0.127.0" } }, "sha512-bkgD4qHlN7WxLdX8bLXdaU54TtQtAIg/ZBAfm0aje/mo3MRDo3P0hZSgr4U7O3xfX+fQmR5AP04JS/TGcZLcFA=="], + + "oxc-resolver": ["oxc-resolver@11.22.0", "", { "optionalDependencies": { "@oxc-resolver/binding-android-arm-eabi": "11.22.0", "@oxc-resolver/binding-android-arm64": "11.22.0", "@oxc-resolver/binding-darwin-arm64": "11.22.0", "@oxc-resolver/binding-darwin-x64": "11.22.0", "@oxc-resolver/binding-freebsd-x64": "11.22.0", "@oxc-resolver/binding-linux-arm-gnueabihf": "11.22.0", "@oxc-resolver/binding-linux-arm-musleabihf": "11.22.0", "@oxc-resolver/binding-linux-arm64-gnu": "11.22.0", "@oxc-resolver/binding-linux-arm64-musl": "11.22.0", "@oxc-resolver/binding-linux-ppc64-gnu": "11.22.0", "@oxc-resolver/binding-linux-riscv64-gnu": "11.22.0", "@oxc-resolver/binding-linux-riscv64-musl": "11.22.0", "@oxc-resolver/binding-linux-s390x-gnu": "11.22.0", "@oxc-resolver/binding-linux-x64-gnu": "11.22.0", "@oxc-resolver/binding-linux-x64-musl": "11.22.0", "@oxc-resolver/binding-openharmony-arm64": "11.22.0", "@oxc-resolver/binding-wasm32-wasi": "11.22.0", "@oxc-resolver/binding-win32-arm64-msvc": "11.22.0", "@oxc-resolver/binding-win32-x64-msvc": "11.22.0" } }, "sha512-F3VuQRlu5uaMN9ffo2ufEa8D/SKykx1a2KaLtBa2JEmbtulRaTZKwQrrLsNLGfvBeLW8M/J0CE47KN3s0VdcmQ=="], + "p-cancelable": ["p-cancelable@2.1.1", "", {}, "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="], "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], @@ -2530,6 +2674,8 @@ "partial-json": ["partial-json@0.1.7", "", {}, "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA=="], + "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], "path-expression-matcher": ["path-expression-matcher@1.5.0", "", {}, "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ=="], @@ -2544,6 +2690,8 @@ "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], + "pdf-lib": ["pdf-lib@1.17.1", "", { "dependencies": { "@pdf-lib/standard-fonts": "^1.0.0", "@pdf-lib/upng": "^1.0.1", "pako": "^1.0.11", "tslib": "^1.11.1" } }, "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw=="], "pdfjs-dist": ["pdfjs-dist@5.6.205", "", { "optionalDependencies": { "@napi-rs/canvas": "^0.1.96", "node-readable-to-web-readable-stream": "^0.4.2" } }, "sha512-tlUj+2IDa7G1SbvBNN74UHRLJybZDWYom+k6p5KIZl7huBvsA4APi6mKL+zCxd3tLjN5hOOEE9Tv7VdzO88pfg=="], @@ -2590,6 +2738,8 @@ "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], + "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], + "proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], @@ -2640,6 +2790,8 @@ "react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], + "read-binary-file-arch": ["read-binary-file-arch@1.0.6", "", { "dependencies": { "debug": "^4.3.4" }, "bin": { "read-binary-file-arch": "cli.js" } }, "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg=="], "read-cmd-shim": ["read-cmd-shim@6.0.0", "", {}, "sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A=="], @@ -2650,6 +2802,8 @@ "real-require": ["real-require@0.2.0", "", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="], + "recast": ["recast@0.23.12", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-dEWRjcINDu/F4l2dYx57ugBtD7HV9KXESyxhzw/MqWLeglJrsjJKqACPyUPg+6AF8mIgm+Zi0dZ3ACoIg+QtpA=="], + "redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="], "redis-errors": ["redis-errors@1.2.0", "", {}, "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w=="], @@ -2842,6 +2996,10 @@ "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + "storybook": ["storybook@10.4.6", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.2", "@testing-library/jest-dom": "^6.9.1", "@testing-library/user-event": "^14.6.1", "@vitest/expect": "3.2.4", "@vitest/spy": "3.2.4", "@webcontainer/env": "^1.1.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0 || ^0.28.0", "open": "^10.2.0", "oxc-parser": "^0.127.0", "oxc-resolver": "^11.19.1", "recast": "^0.23.5", "semver": "^7.7.3", "use-sync-external-store": "^1.5.0", "ws": "^8.18.0" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "prettier": "^2 || ^3", "vite-plus": "^0.1.15" }, "optionalPeers": ["@types/react", "prettier", "vite-plus"], "bin": "./dist/bin/dispatcher.js" }, "sha512-6wkA6LxfDSSilloITsrFOJfsnw0mDUP2h8Ls+lRt8oRsudtz2RWFhLv+Toiwg6NW7hUpdTDc2hzR7DztJid6+A=="], + + "storybook-solidjs-vite": ["storybook-solidjs-vite@10.5.2", "", { "dependencies": { "@storybook/builder-vite": "10.4.4", "@storybook/global": "5.0.0", "@volar/language-core": "2.4.28", "@volar/typescript": "2.4.28", "semver": "7.8.1" }, "peerDependencies": { "@solidjs/web": "^2.0.0-0", "solid-js": "^1.8.0-0 || ^2.0.0-0", "storybook": "^0.0.0-0 || ^10.0.0", "typescript": "^4.0.0 || ^5.0.0 || ^6.0.0", "vite": "7.1.11", "vite-plugin-solid": "2.11.12" }, "optionalPeers": ["@solidjs/web", "typescript"] }, "sha512-u+XSoTxE8JoVZ4IdI+gJRuNMQL00PCLA1BinBPdwHRKSSSVczf+JQhcfBJ2xxODs430RJp+JjAajKZeaSIHj9Q=="], + "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -2898,12 +3056,18 @@ "tiny-async-pool": ["tiny-async-pool@1.3.0", "", { "dependencies": { "semver": "^5.5.0" } }, "sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA=="], + "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], + "tiny-typed-emitter": ["tiny-typed-emitter@2.1.0", "", {}, "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA=="], "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], + + "tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], + "tmp": ["tmp@0.2.7", "", {}, "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw=="], "tmp-promise": ["tmp-promise@3.0.3", "", { "dependencies": { "tmp": "^0.2.0" } }, "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ=="], @@ -2932,6 +3096,8 @@ "ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="], + "ts-dedent": ["ts-dedent@2.3.0", "", {}, "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg=="], + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "tuf-js": ["tuf-js@4.1.0", "", { "dependencies": { "@tufjs/models": "4.1.0", "debug": "^4.4.3", "make-fetch-happen": "^15.0.1" } }, "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ=="], @@ -2994,12 +3160,16 @@ "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + "unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="], + "unused-filename": ["unused-filename@4.0.1", "", { "dependencies": { "escape-string-regexp": "^5.0.0", "path-exists": "^5.0.0" } }, "sha512-ZX6U1J04K1FoSUeoX1OicAhw4d0aro2qo+L8RhJkiGTNtBNkd/Fi1Wxoc9HzcVu6HfOzm0si/N15JjxFmD1z6A=="], "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], + "utf8-byte-length": ["utf8-byte-length@1.0.5", "", {}, "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA=="], "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], @@ -3034,6 +3204,8 @@ "vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="], + "vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="], + "walk-up-path": ["walk-up-path@4.0.0", "", {}, "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A=="], "wcwidth": ["wcwidth@1.0.1", "", { "dependencies": { "defaults": "^1.0.3" } }, "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg=="], @@ -3044,6 +3216,8 @@ "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], + "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], + "whatwg-mimetype": ["whatwg-mimetype@3.0.0", "", {}, "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="], "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], @@ -3266,14 +3440,24 @@ "@openauthjs/openauth/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="], + "@opencode-ai/desktop-electron/semver": ["semver@7.8.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg=="], + "@opencode-ai/desktop-electron/typescript": ["typescript@5.6.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw=="], + "@opencode-ai/script/semver": ["semver@7.8.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg=="], + "@opencode-ai/ui/@solid-primitives/resize-observer": ["@solid-primitives/resize-observer@2.1.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/rootless": "^1.5.2", "@solid-primitives/static-store": "^0.1.2", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-zBLje5E06TgOg93S7rGPldmhDnouNGhvfZVKOp+oG2XU8snA+GoCSSCz1M+jpNAg5Ek2EakU5UVQqL152WmdXQ=="], "@opencode-ai/ui/marked": ["marked@17.0.1", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg=="], "@oslojs/jwt/@oslojs/encoding": ["@oslojs/encoding@0.4.1", "", {}, "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q=="], + "@oxc-parser/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="], + + "@oxc-resolver/binding-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="], + + "@oxc-resolver/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="], + "@parcel/watcher/detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="], "@pierre/diffs/@shikijs/transformers": ["@shikijs/transformers@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/types": "3.20.0" } }, "sha512-PrHHMRr3Q5W1qB/42kJW6laqFyWdhrPF2hNR9qjOm1xcSiAO3hAHo7HaVyHE6pMyevmy3i51O8kuGGXC78uK3g=="], @@ -3308,6 +3492,10 @@ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "@testing-library/dom/aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], + + "@testing-library/dom/dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], + "@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="], "ai/@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.95", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZmUNNbZl3V42xwQzPaNUi+s8eqR2lnrxf0bvB6YbLXpLjHYv0k2Y78t12cNOfY0bxGeuVVTLyk856uLuQIuXEQ=="], @@ -3416,6 +3604,8 @@ "opencode/minimatch": ["minimatch@10.2.3", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg=="], + "opencode/semver": ["semver@7.8.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg=="], + "opencode-gitlab-auth/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="], "openid-client/jose": ["jose@4.15.9", "", {}, "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA=="], @@ -3434,6 +3624,10 @@ "postject/commander": ["commander@9.5.0", "", {}, "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ=="], + "pretty-format/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "proper-lockfile/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], "proxy-addr/ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], @@ -3460,6 +3654,12 @@ "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], + "storybook/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="], + + "storybook/semver": ["semver@7.8.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg=="], + + "storybook-solidjs-vite/semver": ["semver@7.8.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg=="], + "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], @@ -3594,6 +3794,8 @@ "@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], + "@oxc-resolver/binding-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="], + "@pierre/diffs/@shikijs/transformers/@shikijs/core": ["@shikijs/core@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-f2ED7HYV4JEk827mtMDwe/yQ25pRiXZmtHjWF8uzZKuKiEsJR7Ce1nuQ+HhV9FzDcbIo4ObBCD9GPTzNuy9S1g=="], "@pierre/diffs/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="], diff --git a/packages/opencode/test/github/ci-workflow.test.ts b/packages/opencode/test/github/ci-workflow.test.ts index df7937d2f..21adf0996 100644 --- a/packages/opencode/test/github/ci-workflow.test.ts +++ b/packages/opencode/test/github/ci-workflow.test.ts @@ -36,6 +36,7 @@ const hardenRunnerJobs = [ lintJobName, frontendArchitectureJobName, "unit-ui", + "storybook-smoke", "unit-app", "unit-opencode", "unit-desktop", @@ -350,6 +351,7 @@ describe("ci workflow", () => { "typecheck", lintJobName, frontendArchitectureJobName, + "storybook-smoke", ...linuxUnitJobs.map((item) => item.jobName), ] @@ -833,6 +835,7 @@ describe("ci workflow", () => { "typecheck", frontendArchitectureJobName, "unit-ui", + "storybook-smoke", "unit-app", "unit-opencode", "unit-desktop", @@ -850,12 +853,14 @@ describe("ci workflow", () => { expect(validate?.env?.TYPECHECK_RESULT).toBe("${{ needs.typecheck.result }}") expect(validate?.env?.FRONTEND_ARCHITECTURE_RESULT).toBe("${{ needs['frontend-architecture'].result }}") expect(validate?.env?.UNIT_UI_RESULT).toBe("${{ needs['unit-ui'].result }}") + expect(validate?.env?.STORYBOOK_SMOKE_RESULT).toBe("${{ needs['storybook-smoke'].result }}") expect(validate?.env?.UNIT_APP_RESULT).toBe("${{ needs['unit-app'].result }}") expect(validate?.env?.UNIT_OPENCODE_RESULT).toBe("${{ needs['unit-opencode'].result }}") expect(validate?.env?.UNIT_DESKTOP_RESULT).toBe("${{ needs['unit-desktop'].result }}") expect(validate?.env?.UNIT_REMOTE_BRIDGE_RESULT).toBe("${{ needs['unit-remote-bridge'].result }}") expect(validate?.run).toContain("Docs-only change, daily CI skipped.") expect(validate?.run).toContain("FRONTEND_ARCHITECTURE_RESULT") + expect(validate?.run).toContain("STORYBOOK_SMOKE_RESULT") expect(validate?.run).toContain("UNIT_UI_RESULT") expect(validate?.run).toContain("UNIT_APP_RESULT") expect(validate?.run).toContain("UNIT_OPENCODE_RESULT") diff --git a/packages/ui/.gitignore b/packages/ui/.gitignore index a515fd5d2..27a513633 100644 --- a/packages/ui/.gitignore +++ b/packages/ui/.gitignore @@ -3,6 +3,7 @@ dist .output .vercel .netlify +storybook-static # Environment .env diff --git a/packages/ui/.storybook/main.ts b/packages/ui/.storybook/main.ts new file mode 100644 index 000000000..8c5fa7971 --- /dev/null +++ b/packages/ui/.storybook/main.ts @@ -0,0 +1,25 @@ +import type { StorybookConfig } from "storybook-solidjs-vite" +import tailwindcss from "@tailwindcss/vite" + +const config: StorybookConfig = { + framework: "storybook-solidjs-vite", + // App-level components (message-part, session-turn, timeline-playground) that + // need runtime data (sdk messages, tool registry, server context) are not + // previewable in isolation; their fork stories were removed rather than kept + // as dead .skip files. + stories: ["../src/components/*.stories.tsx"], + viteFinal: async (viteConfig) => { + const drop = (name?: string) => + name?.startsWith("icon-spritesheet-generator") || name === "provider-icons-plugin" + const kept = (viteConfig.plugins ?? []).flatMap(function walk(p: unknown): unknown[] { + if (Array.isArray(p)) return p.flatMap(walk) + if (p === false || p == null) return [] + const name = (p as { name?: string })?.name + return drop(name) ? [] : [p] + }) + viteConfig.plugins = [tailwindcss(), ...kept] + return viteConfig + }, +} + +export default config diff --git a/packages/ui/.storybook/preview.tsx b/packages/ui/.storybook/preview.tsx new file mode 100644 index 000000000..be84292f4 --- /dev/null +++ b/packages/ui/.storybook/preview.tsx @@ -0,0 +1,29 @@ +import { createJSXDecorator, type Preview } from "storybook-solidjs-vite" +import { MetaProvider } from "@solidjs/meta" +import { ThemeProvider } from "../src/theme/context" +import { DialogProvider } from "../src/context/dialog" +import { MarkedProvider } from "../src/context/marked" +import "../src/styles/tailwind/index.css" + +// Mirrors the app nesting order (Meta > Theme > Dialog > Marked) so components +// relying on useDialog inside markdown-rendered content resolve the same +// context as in production. FileComponentProvider is omitted: no standalone UI +// story renders file content, and wiring it would require an app-level File +// component that does not belong in the ui package. +const preview: Preview = { + decorators: [ + createJSXDecorator((Story) => ( + + + + + + + + + + )), + ], +} + +export default preview \ No newline at end of file diff --git a/packages/ui/package.json b/packages/ui/package.json index 82fa8634b..a098e95a2 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -32,7 +32,9 @@ "test": "bun test", "test:ci": "mkdir -p .artifacts/unit && bun test --reporter=junit --reporter-outfile=.artifacts/unit/junit.xml", "dev": "vite", - "generate:tailwind": "bun run script/tailwind.ts" + "generate:tailwind": "bun run script/tailwind.ts", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build" }, "devDependencies": { "@happy-dom/global-registrator": "20.0.11", @@ -46,7 +48,9 @@ "typescript": "catalog:", "vite": "catalog:", "vite-plugin-icons-spritesheet": "3.0.1", - "vite-plugin-solid": "catalog:" + "vite-plugin-solid": "catalog:", + "storybook": "10.4.6", + "storybook-solidjs-vite": "10.5.2" }, "dependencies": { "@kobalte/core": "catalog:", diff --git a/packages/ui/src/components/message-part.stories.tsx b/packages/ui/src/components/message-part.stories.tsx deleted file mode 100644 index 28489dc7b..000000000 --- a/packages/ui/src/components/message-part.stories.tsx +++ /dev/null @@ -1,7 +0,0 @@ -// @ts-nocheck -import * as mod from "./message-part" -import { create } from "../storybook/scaffold" - -const story = create({ title: "UI/MessagePart", mod }) -export default { title: "UI/MessagePart", id: "components-message-part", component: story.meta.component } -export const Basic = story.Basic diff --git a/packages/ui/src/components/session-turn.stories.tsx b/packages/ui/src/components/session-turn.stories.tsx deleted file mode 100644 index 927402c8d..000000000 --- a/packages/ui/src/components/session-turn.stories.tsx +++ /dev/null @@ -1,7 +0,0 @@ -// @ts-nocheck -import * as mod from "./session-turn" -import { create } from "../storybook/scaffold" - -const story = create({ title: "UI/SessionTurn", mod }) -export default { title: "UI/SessionTurn", id: "components-session-turn", component: story.meta.component } -export const Basic = story.Basic diff --git a/packages/ui/src/components/timeline-playground.stories.tsx b/packages/ui/src/components/timeline-playground.stories.tsx deleted file mode 100644 index 543858691..000000000 --- a/packages/ui/src/components/timeline-playground.stories.tsx +++ /dev/null @@ -1,2024 +0,0 @@ -// @ts-nocheck -import { createSignal, createMemo, createEffect, on, For, Show, batch } from "solid-js" -import { createStore, produce } from "solid-js/store" -import type { - Message, - UserMessage, - AssistantMessage, - Part, - TextPart, - ReasoningPart, - ToolPart, - FilePart, - AgentPart, -} from "@opencode-ai/sdk/v2" -import { DataProvider } from "../context/data" -import { FileComponentProvider } from "../context/file" -import { SessionTurn } from "./session-turn" - -// --------------------------------------------------------------------------- -// ID helpers -// --------------------------------------------------------------------------- -let seq = 0 -const uid = () => `pg-${++seq}-${Date.now().toString(36)}` - -// --------------------------------------------------------------------------- -// Lorem ipsum content -// --------------------------------------------------------------------------- -const LOREM = [ - "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", - "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.", - "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.", - "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", - "Cras justo odio, dapibus ut facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper.", -] - -// --------------------------------------------------------------------------- -// User message variants -// --------------------------------------------------------------------------- -const USER_VARIANTS = { - short: { - label: "short", - text: "Fix the bug in the login form", - parts: [] as Part[], - }, - medium: { - label: "medium", - text: "Can you update the session timeline component to support lazy loading? The current implementation loads everything eagerly which causes jank on large sessions.", - parts: [] as Part[], - }, - long: { - label: "long", - text: `I need you to refactor the message rendering pipeline. Currently the timeline renders all messages synchronously which blocks first paint. Here's what I want: - -1. Implement virtual scrolling for the message list -2. Defer-mount older messages using requestAnimationFrame batching -3. Add content-visibility: auto to each turn container -4. Make sure the scroll-to-bottom behavior still works correctly after these changes - -Please also add appropriate CSS containment hints and make sure we don't break the sticky header behavior for the session title.`, - parts: [] as Part[], - }, - "with @file": { - label: "with @file", - text: "Update @src/components/session-turn.tsx to fix the spacing issue between parts", - parts: (() => { - const id = `static-file-${Date.now()}` - return [ - { - id, - type: "file", - mime: "text/plain", - filename: "session-turn.tsx", - url: "src/components/session-turn.tsx", - source: { - type: "file", - path: "src/components/session-turn.tsx", - text: { - value: "@src/components/session-turn.tsx", - start: 7, - end: 38, - }, - }, - } as FilePart, - ] - })(), - }, - "with @agent": { - label: "with @agent", - text: "Use @explore to find all CSS files related to the timeline, then fix the spacing", - parts: (() => { - return [ - { - id: `static-agent-${Date.now()}`, - type: "agent", - name: "explore", - source: { start: 4, end: 12 }, - } as AgentPart, - ] - })(), - }, - "with image": { - label: "with image", - text: "Here's a screenshot of the bug I'm seeing", - parts: (() => { - // 1x1 blue pixel PNG as data URI for a realistic attachment - const pixel = - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" - return [ - { - id: `static-img-${Date.now()}`, - type: "file", - mime: "image/png", - filename: "screenshot.png", - url: pixel, - } as FilePart, - ] - })(), - }, - "with file attachment": { - label: "with file attachment", - text: "Check this config file for issues", - parts: (() => { - return [ - { - id: `static-attach-${Date.now()}`, - type: "file", - mime: "application/json", - filename: "tsconfig.json", - url: "data:application/json;base64,e30=", - } as FilePart, - ] - })(), - }, - "multi attachment": { - label: "multi attachment", - text: "Look at these files and the screenshot, then fix the layout", - parts: (() => { - const pixel = - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" - return [ - { - id: `static-multi-img-${Date.now()}`, - type: "file", - mime: "image/png", - filename: "layout-bug.png", - url: pixel, - } as FilePart, - { - id: `static-multi-file-${Date.now()}`, - type: "file", - mime: "text/css", - filename: "session-turn.css", - url: "data:text/css;base64,LyogZW1wdHkgKi8=", - } as FilePart, - { - id: `static-multi-ref-${Date.now()}`, - type: "file", - mime: "text/plain", - filename: "session-turn.tsx", - url: "src/components/session-turn.tsx", - source: { - type: "file", - path: "src/components/session-turn.tsx", - text: { value: "@src/components/session-turn.tsx", start: 0, end: 0 }, - }, - } as FilePart, - ] - })(), - }, -} satisfies Record - -const MARKDOWN_SAMPLES = { - headings: `# Heading 1 -## Heading 2 -### Heading 3 -#### Heading 4 - -Some paragraph text after headings.`, - - lists: `Here's a list of changes: - -- First item with some explanation -- Second item that is a bit longer and wraps to the next line when the viewport is narrow -- Third item - - Nested item A - - Nested item B - -1. Numbered first -2. Numbered second -3. Numbered third`, - - code: `Here's an inline \`variable\` reference and a code block: - -\`\`\`typescript -export function sum(values: number[]) { - return values.reduce((total, value) => total + value, 0) -} - -export function average(values: number[]) { - if (values.length === 0) return 0 - return sum(values) / values.length -} -\`\`\` - -And some text after the code block.`, - - mixed: `## Implementation Plan - -I'll make the following changes: - -1. **Update the schema** - Add new fields to the database model -2. **Create the API endpoint** - Handle validation and persistence -3. **Add frontend components** - Build the form and display views - -Here's the key change: - -\`\`\`typescript -const table = sqliteTable("session", { - id: text().primaryKey(), - project_id: text().notNull(), - created_at: integer().notNull(), -}) -\`\`\` - -> Note: This is a breaking change that requires a migration. - -The migration will handle existing data by setting \`project_id\` to the default workspace. - ---- - -For more details, see the [documentation](https://example.com/docs).`, - - table: `## Comparison - -| Feature | Before | After | -|---------|--------|-------| -| Speed | 120ms | 45ms | -| Memory | 256MB | 128MB | -| Bundle | 1.2MB | 890KB | - -The improvements are significant across all metrics.`, - - blockquote: `## Summary - -> This is a blockquote that contains important information about the implementation approach. -> -> It spans multiple lines and contains **bold** and \`code\` elements. - -The approach above was chosen for its simplicity.`, - - links: `Check out these resources: - -- [SolidJS docs](https://solidjs.com) -- [TypeScript handbook](https://www.typescriptlang.org/docs/handbook) -- The API is at \`https://api.example.com/v2\` - -You can also visit https://example.com/docs for more info.`, - - images: `## Screenshot - -Here's what the output looks like: - -![Alt text](https://via.placeholder.com/400x200) - -And below is the final result.`, -} - -const REASONING_SAMPLES = [ - `**Analyzing the request** - -The user wants to add a new feature to the session timeline. I need to understand the existing component structure first. - -Let me look at the key files involved: -- \`session-turn.tsx\` handles individual turns -- \`message-part.tsx\` renders different part types -- The data flows through the \`DataProvider\` context`, - - `**Considering approaches** - -I could either modify the existing SessionTurn component or create a wrapper. The wrapper approach is cleaner because it doesn't touch the core rendering logic. - -The trade-off is that we'd need to pass additional props through, but that's acceptable for this use case.`, - - `**Planning the implementation** - -I'll need to: -1. Create the data generators -2. Wire up the context providers -3. Add CSS variable controls -4. Implement the export functionality - -This should be straightforward given the existing component architecture.`, -] - -const TOOL_SAMPLES = { - read: { - tool: "read", - input: { filePath: "src/components/session-turn.tsx", offset: 1, limit: 50 }, - output: "export function SessionTurn(props) {\n // component implementation\n return
...
\n}", - title: "Read src/components/session-turn.tsx", - metadata: {}, - }, - glob: { - tool: "glob", - input: { pattern: "**/*.tsx", path: "src/components" }, - output: "src/components/button.tsx\nsrc/components/card.tsx\nsrc/components/session-turn.tsx", - title: "Found 3 files", - metadata: {}, - }, - grep: { - tool: "grep", - input: { pattern: "SessionTurn", path: "src", include: "*.tsx" }, - output: "src/components/session-turn.tsx:141\nsrc/pages/session/timeline.tsx:987", - title: "Found 2 matches", - metadata: {}, - }, - bash: { - tool: "bash", - input: { command: "bun test --filter session", description: "Run session tests" }, - output: - "bun test v1.3.13\n\n✓ session-turn.test.tsx (3 tests) 45ms\n✓ message-part.test.tsx (7 tests) 120ms\n\nTest Suites: 2 passed, 2 total\nTests: 10 passed, 10 total\nTime: 0.89s", - title: "Run session tests", - metadata: { command: "bun test --filter session" }, - }, - edit: { - tool: "edit", - input: { - filePath: "src/components/session-turn.tsx", - oldString: "gap: 12px", - newString: "gap: 18px", - }, - output: "File edited successfully", - title: "Edit src/components/session-turn.tsx", - metadata: { - filediff: { - file: "src/components/session-turn.tsx", - before: " gap: 12px;\n display: flex;", - after: " gap: 18px;\n display: flex;", - additions: 1, - deletions: 1, - }, - }, - }, - write: { - tool: "write", - input: { - filePath: "src/utils/helpers.ts", - content: - "export function clamp(value: number, min: number, max: number) {\n return Math.min(Math.max(value, min), max)\n}\n", - }, - output: "File written successfully", - title: "Write src/utils/helpers.ts", - metadata: {}, - }, - agent: { - tool: "agent", - input: { description: "Explore components", subagent_type: "explore", prompt: "Find all session components" }, - output: "Found 12 session-related components across 3 directories.", - title: "Agent (Explore)", - metadata: { sessionId: "sub-session-1" }, - }, - webfetch: { - tool: "webfetch", - input: { url: "https://solidjs.com/docs/latest/api" }, - output: "# SolidJS API Reference\n\nCore primitives for building reactive applications...", - title: "Fetch https://solidjs.com/docs/latest/api", - metadata: {}, - }, - websearch: { - tool: "websearch", - input: { query: "SolidJS createStore performance" }, - output: - "https://solidjs.com/docs/latest/api#createstore\nhttps://dev.to/solidjs/understanding-solid-reactivity\nhttps://github.com/solidjs/solid/discussions/1234", - title: "Search: SolidJS createStore performance", - metadata: {}, - }, - question: { - tool: "question", - input: { - questions: [ - { - question: "Which approach do you prefer?", - header: "Approach", - options: [ - { label: "Wrapper component", description: "Create a new wrapper around SessionTurn" }, - { label: "Direct modification", description: "Modify SessionTurn directly" }, - ], - }, - ], - }, - output: "", - title: "Question", - metadata: { answers: [["Wrapper component"]] }, - }, - skill: { - tool: "skill", - input: { name: "playwriter" }, - output: "Skill loaded successfully", - title: "playwriter", - metadata: {}, - }, - todowrite: { - tool: "todowrite", - input: { - todos: [ - { content: "Create data generators", status: "completed", priority: "high" }, - { content: "Build UI controls", status: "in_progress", priority: "high" }, - { content: "Add CSS export", status: "pending", priority: "medium" }, - ], - }, - output: "", - title: "Todos", - metadata: { - todos: [ - { content: "Create data generators", status: "completed", priority: "high" }, - { content: "Build UI controls", status: "in_progress", priority: "high" }, - { content: "Add CSS export", status: "pending", priority: "medium" }, - ], - }, - }, -} - -// --------------------------------------------------------------------------- -// Fake data generators -// --------------------------------------------------------------------------- -const SESSION_ID = "playground-session" -const DEFAULT_SESSION = { id: SESSION_ID, title: "Timeline Playground" } - -function record(value: unknown): value is Record { - return !!value && typeof value === "object" && !Array.isArray(value) -} - -function normalize(raw: unknown) { - if (Array.isArray(raw)) { - const info = raw.find((row) => record(row) && row.type === "session" && record(row.data))?.data - if (!record(info) || typeof info.id !== "string") { - throw new Error("No session found in JSON") - } - - const part = new Map() - const messages = raw.flatMap((row) => { - if (!record(row) || !record(row.data)) return [] - if (row.type === "part" && typeof row.data.messageID === "string") { - const list = part.get(row.data.messageID) ?? [] - list.push(row.data as Part) - part.set(row.data.messageID, list) - return [] - } - if (row.type !== "message" || typeof row.data.id !== "string") return [] - return [{ info: row.data as Message, parts: [] as Part[] }] - }) - - return { - info, - messages: messages.map((msg) => ({ - info: msg.info, - parts: part.get(msg.info.id) ?? [], - })), - } - } - - if (!record(raw) || !record(raw.info) || typeof raw.info.id !== "string" || !Array.isArray(raw.messages)) { - throw new Error("Expected an `opencode export` JSON file") - } - - return { - info: raw.info, - messages: raw.messages.flatMap((row) => { - if (!record(row) || !record(row.info) || typeof row.info.id !== "string") return [] - return [{ info: row.info as Message, parts: Array.isArray(row.parts) ? (row.parts as Part[]) : [] }] - }), - } -} - -function mkUser(text: string, extra: Part[] = [], sessionID = SESSION_ID): { message: UserMessage; parts: Part[] } { - const id = uid() - return { - message: { - id, - sessionID, - role: "user", - time: { created: Date.now() }, - agent: "code", - model: { providerID: "anthropic", modelID: "claude-sonnet-4-20250514" }, - } as UserMessage, - parts: [ - { id: uid(), type: "text", text, time: { created: Date.now() } } as TextPart, - // Clone extra parts with fresh ids so each user message owns unique part instances - ...extra.map((p) => ({ ...p, id: uid() })), - ], - } -} - -function mkAssistant(parentID: string, sessionID = SESSION_ID): AssistantMessage { - return { - id: uid(), - sessionID, - role: "assistant", - time: { created: Date.now(), completed: Date.now() + 3000 }, - parentID, - modelID: "claude-sonnet-4-20250514", - providerID: "anthropic", - mode: "default", - agent: "code", - path: { cwd: "/project", root: "/project" }, - cost: 0.003, - tokens: { input: 1200, output: 800, reasoning: 200, cache: { read: 0, write: 0 } }, - } as AssistantMessage -} - -function textPart(text: string): TextPart { - return { id: uid(), type: "text", text, time: { created: Date.now() } } as TextPart -} - -function reasoningPart(text: string): ReasoningPart { - return { id: uid(), type: "reasoning", text, time: { start: Date.now(), end: Date.now() + 500 } } as ReasoningPart -} - -function toolPart(sample: (typeof TOOL_SAMPLES)[keyof typeof TOOL_SAMPLES], status = "completed"): ToolPart { - const base = { - id: uid(), - type: "tool" as const, - callID: uid(), - tool: sample.tool, - } - if (status === "completed") { - return { - ...base, - state: { - status: "completed", - input: sample.input, - output: sample.output, - title: sample.title, - metadata: sample.metadata ?? {}, - time: { start: Date.now(), end: Date.now() + 1000 }, - }, - } as ToolPart - } - if (status === "running") { - return { - ...base, - state: { - status: "running", - input: sample.input, - title: sample.title, - metadata: sample.metadata ?? {}, - time: { start: Date.now() }, - }, - } as ToolPart - } - return { - ...base, - state: { status: "pending", input: sample.input, raw: "" }, - } as ToolPart -} - -// --------------------------------------------------------------------------- -// CSS Controls definition -// --------------------------------------------------------------------------- - -// Source file basenames inside packages/ui/src/components/ -const MD = "markdown.css" -const MP = "message-part.css" -const ST = "session-turn.css" -const CL = "collapsible.css" -const BT = "basic-tool.css" - -/** - * Source mapping for a CSS control. - * - `anchor`: immutable text near the property (comment, selector, etc.) that - * won't change when values change — used to locate the right rule block. - * - `prop`: the CSS property name whose value gets replaced. - * - `format`: turns the slider number into a CSS value string. - */ -type CSSSource = { - file: string - anchor: string - prop: string - format: (v: string) => string -} - -type CSSControl = { - key: string - label: string - group: string - type: "range" | "color" | "select" - initial: string - selector: string - property: string - min?: string - max?: string - step?: string - options?: string[] - unit?: string - source?: CSSSource -} - -const px = (v: string) => `${v}px` -const pxZero = (v: string) => `${v}px 0` -const pct = (v: string) => `${v}%` - -const CSS_CONTROLS: CSSControl[] = [ - // --- Timeline spacing --- - { - key: "turn-gap", - label: "Above user messages", - group: "Timeline Spacing", - type: "range", - initial: "32", - selector: '[data-slot="session-turn-list"]', - property: "gap", - min: "0", - max: "80", - step: "1", - unit: "px", - source: { file: ST, anchor: '[data-slot="session-turn-list"]', prop: "gap", format: px }, - }, - { - key: "container-gap", - label: "Below user messages", - group: "Timeline Spacing", - type: "range", - initial: "0", - selector: '[data-slot="session-turn-message-container"]', - property: "gap", - min: "0", - max: "60", - step: "1", - unit: "px", - source: { file: ST, anchor: '[data-slot="session-turn-message-container"]', prop: "gap", format: px }, - }, - { - key: "assistant-gap", - label: "Assistant parts gap", - group: "Timeline Spacing", - type: "range", - initial: "12", - selector: '[data-slot="session-turn-assistant-content"]', - property: "gap", - min: "0", - max: "40", - step: "1", - unit: "px", - source: { file: ST, anchor: '[data-slot="session-turn-assistant-content"]', prop: "gap", format: px }, - }, - { - key: "text-part-margin", - label: "Text part margin-top", - group: "Timeline Spacing", - type: "range", - initial: "24", - selector: '[data-component="text-part"]', - property: "margin-top", - min: "0", - max: "60", - step: "1", - unit: "px", - source: { file: MP, anchor: '[data-component="text-part"]', prop: "margin-top", format: px }, - }, - - // --- Markdown typography --- - { - key: "md-font-size", - label: "Font size", - group: "Markdown Typography", - type: "range", - initial: "14", - selector: '[data-component="markdown"]', - property: "font-size", - min: "10", - max: "22", - step: "1", - unit: "px", - source: { file: MD, anchor: "/* Reset & Base Typography */", prop: "font-size", format: px }, - }, - { - key: "md-line-height", - label: "Line height", - group: "Markdown Typography", - type: "range", - initial: "180", - selector: '[data-component="markdown"]', - property: "line-height", - min: "100", - max: "300", - step: "5", - unit: "%", - source: { file: MD, anchor: "/* Reset & Base Typography */", prop: "line-height", format: pct }, - }, - - // --- Markdown headings --- - { - key: "md-heading-margin-top", - label: "Heading margin-top", - group: "Markdown Headings", - type: "range", - initial: "32", - selector: '[data-component="markdown"] :is(h1,h2,h3,h4,h5,h6)', - property: "margin-top", - min: "0", - max: "60", - step: "1", - unit: "px", - source: { file: MD, anchor: "/* Headings:", prop: "margin-top", format: px }, - }, - { - key: "md-heading-margin-bottom", - label: "Heading margin-bottom", - group: "Markdown Headings", - type: "range", - initial: "12", - selector: '[data-component="markdown"] :is(h1,h2,h3,h4,h5,h6)', - property: "margin-bottom", - min: "0", - max: "40", - step: "1", - unit: "px", - source: { file: MD, anchor: "/* Headings:", prop: "margin-bottom", format: px }, - }, - { - key: "md-heading-font-size", - label: "Heading font size", - group: "Markdown Headings", - type: "range", - initial: "14", - selector: '[data-component="markdown"] :is(h1,h2,h3,h4,h5,h6)', - property: "font-size", - min: "12", - max: "28", - step: "1", - unit: "px", - source: { file: MD, anchor: "/* Headings:", prop: "font-size", format: px }, - }, - - // --- Markdown paragraphs --- - { - key: "md-p-margin-bottom", - label: "Paragraph margin-bottom", - group: "Markdown Paragraphs", - type: "range", - initial: "16", - selector: '[data-component="markdown"] p', - property: "margin-bottom", - min: "0", - max: "40", - step: "1", - unit: "px", - source: { file: MD, anchor: "/* Paragraphs */", prop: "margin-bottom", format: px }, - }, - - // --- Markdown lists --- - { - key: "md-list-margin-top", - label: "List margin-top", - group: "Markdown Lists", - type: "range", - initial: "8", - selector: '[data-component="markdown"] :is(ul,ol)', - property: "margin-top", - min: "0", - max: "40", - step: "1", - unit: "px", - source: { file: MD, anchor: "/* Lists */", prop: "margin-top", format: px }, - }, - { - key: "md-list-margin-bottom", - label: "List margin-bottom", - group: "Markdown Lists", - type: "range", - initial: "16", - selector: '[data-component="markdown"] :is(ul,ol)', - property: "margin-bottom", - min: "0", - max: "40", - step: "1", - unit: "px", - source: { file: MD, anchor: "/* Lists */", prop: "margin-bottom", format: px }, - }, - { - key: "md-list-padding-left", - label: "List padding-left", - group: "Markdown Lists", - type: "range", - initial: "24", - selector: '[data-component="markdown"] :is(ul,ol)', - property: "padding-left", - min: "0", - max: "60", - step: "1", - unit: "px", - source: { file: MD, anchor: "/* Lists */", prop: "padding-left", format: px }, - }, - { - key: "md-li-margin-bottom", - label: "List item margin-bottom", - group: "Markdown Lists", - type: "range", - initial: "8", - selector: '[data-component="markdown"] li', - property: "margin-bottom", - min: "0", - max: "20", - step: "1", - unit: "px", - // Anchor on `li {` to skip the `ul,ol` margin-bottom above - source: { file: MD, anchor: "\n li {", prop: "margin-bottom", format: px }, - }, - - // --- Markdown code blocks --- - { - key: "md-pre-margin-top", - label: "Code block margin-top", - group: "Markdown Code", - type: "range", - initial: "32", - selector: '[data-component="markdown"] pre', - property: "margin-top", - min: "0", - max: "60", - step: "1", - unit: "px", - source: { file: MD, anchor: "\n pre {", prop: "margin-top", format: px }, - }, - { - key: "md-pre-margin-bottom", - label: "Code block margin-bottom", - group: "Markdown Code", - type: "range", - initial: "32", - selector: '[data-component="markdown"] pre', - property: "margin-bottom", - min: "0", - max: "60", - step: "1", - unit: "px", - source: { file: MD, anchor: "\n pre {", prop: "margin-bottom", format: px }, - }, - { - key: "md-shiki-font-size", - label: "Code font size", - group: "Markdown Code", - type: "range", - initial: "13", - selector: '[data-component="markdown"] .shiki', - property: "font-size", - min: "10", - max: "20", - step: "1", - unit: "px", - source: { file: MD, anchor: ".shiki {", prop: "font-size", format: px }, - }, - { - key: "md-shiki-padding", - label: "Code padding", - group: "Markdown Code", - type: "range", - initial: "12", - selector: '[data-component="markdown"] .shiki', - property: "padding", - min: "0", - max: "32", - step: "1", - unit: "px", - source: { file: MD, anchor: ".shiki {", prop: "padding", format: px }, - }, - { - key: "md-shiki-radius", - label: "Code border-radius", - group: "Markdown Code", - type: "range", - initial: "6", - selector: '[data-component="markdown"] .shiki', - property: "border-radius", - min: "0", - max: "16", - step: "1", - unit: "px", - source: { file: MD, anchor: ".shiki {", prop: "border-radius", format: px }, - }, - - // --- Markdown blockquotes --- - { - key: "md-blockquote-margin", - label: "Blockquote margin", - group: "Markdown Blockquotes", - type: "range", - initial: "24", - selector: '[data-component="markdown"] blockquote', - property: "margin-block", - min: "0", - max: "60", - step: "1", - unit: "px", - source: { file: MD, anchor: "/* Blockquotes */", prop: "margin", format: pxZero }, - }, - { - key: "md-blockquote-padding-left", - label: "Blockquote padding-left", - group: "Markdown Blockquotes", - type: "range", - initial: "8", - selector: '[data-component="markdown"] blockquote', - property: "padding-left", - min: "0", - max: "40", - step: "1", - unit: "px", - source: { file: MD, anchor: "/* Blockquotes */", prop: "padding-left", format: px }, - }, - { - key: "md-blockquote-border-width", - label: "Blockquote border width", - group: "Markdown Blockquotes", - type: "range", - initial: "2", - selector: '[data-component="markdown"] blockquote', - property: "border-left-width", - min: "0", - max: "8", - step: "1", - unit: "px", - source: { - file: MD, - anchor: "/* Blockquotes */", - prop: "border-left", - format: (v) => `${v}px solid var(--border-weak)`, - }, - }, - - // --- Markdown tables --- - { - key: "md-table-margin", - label: "Table margin", - group: "Markdown Tables", - type: "range", - initial: "24", - selector: '[data-component="markdown"] table', - property: "margin-block", - min: "0", - max: "60", - step: "1", - unit: "px", - source: { file: MD, anchor: "/* Tables */", prop: "margin", format: pxZero }, - }, - { - key: "md-td-padding", - label: "Cell padding", - group: "Markdown Tables", - type: "range", - initial: "12", - selector: '[data-component="markdown"] :is(th,td)', - property: "padding", - min: "0", - max: "24", - step: "1", - unit: "px", - // Anchor on td selector to skip other padding rules - source: { file: MD, anchor: "th,\n td {", prop: "padding", format: px }, - }, - - // --- Markdown HR --- - { - key: "md-hr-margin", - label: "HR margin", - group: "Markdown HR", - type: "range", - initial: "40", - selector: '[data-component="markdown"] hr', - property: "margin-block", - min: "0", - max: "80", - step: "1", - unit: "px", - source: { file: MD, anchor: "/* Horizontal Rule", prop: "margin", format: pxZero }, - }, - - // --- Reasoning part --- - { - key: "reasoning-md-font-size", - label: "Reasoning font size", - group: "Reasoning Part", - type: "range", - initial: "14", - selector: '[data-component="reasoning-body"] [data-component="markdown"]', - property: "font-size", - min: "10", - max: "22", - step: "1", - unit: "px", - source: { file: MP, anchor: '[data-component="reasoning-body"] [data-component="markdown"]', prop: "font-size", format: px }, - }, - - // --- User message --- - { - key: "user-msg-padding", - label: "User bubble padding", - group: "User Message", - type: "range", - initial: "12", - selector: '[data-slot="user-message-text"]', - property: "padding", - min: "0", - max: "32", - step: "1", - unit: "px", - source: { file: MP, anchor: '[data-slot="user-message-text"]', prop: "padding", format: px }, - }, - { - key: "user-msg-radius", - label: "User bubble border-radius", - group: "User Message", - type: "range", - initial: "6", - selector: '[data-slot="user-message-text"]', - property: "border-radius", - min: "0", - max: "24", - step: "1", - unit: "px", - source: { file: MP, anchor: '[data-slot="user-message-text"]', prop: "border-radius", format: px }, - }, - - // --- Tool parts --- - { - key: "tool-subtitle-font-size", - label: "Subtitle font size", - group: "Tool Parts", - type: "range", - initial: "14", - selector: '[data-slot="basic-tool-tool-subtitle"]', - property: "font-size", - min: "10", - max: "22", - step: "1", - unit: "px", - source: { file: BT, anchor: '[data-slot="basic-tool-tool-subtitle"]', prop: "font-size", format: px }, - }, - { - key: "exa-output-font-size", - label: "Search output font size", - group: "Tool Parts", - type: "range", - initial: "14", - selector: '[data-component="exa-tool-output"]', - property: "font-size", - min: "10", - max: "22", - step: "1", - unit: "px", - source: { file: MP, anchor: '[data-component="exa-tool-output"]', prop: "font-size", format: px }, - }, - { - key: "tool-content-gap", - label: "Trigger/content gap", - group: "Tool Parts", - type: "range", - initial: "4", - selector: '[data-component="collapsible"].tool-collapsible', - property: "--tool-content-gap", - min: "0", - max: "24", - step: "1", - unit: "px", - source: { file: CL, anchor: "&.tool-collapsible {", prop: "--tool-content-gap", format: px }, - }, - { - key: "context-tool-gap", - label: "Explored tool gap", - group: "Explored Group", - type: "range", - initial: "4", - selector: '[data-component="context-tool-group-list"]', - property: "gap", - min: "0", - max: "40", - step: "1", - unit: "px", - source: { file: MP, anchor: '[data-component="context-tool-group-list"]', prop: "gap", format: px }, - }, - { - key: "context-tool-indent", - label: "Explored indent", - group: "Explored Group", - type: "range", - initial: "0", - selector: '[data-component="context-tool-group-list"]', - property: "padding-left", - min: "0", - max: "48", - step: "1", - unit: "px", - source: { file: MP, anchor: '[data-component="context-tool-group-list"]', prop: "padding-left", format: px }, - }, - { - key: "bash-max-height", - label: "Shell output max-height", - group: "Tool Parts", - type: "range", - initial: "240", - selector: '[data-slot="bash-scroll"]', - property: "max-height", - min: "100", - max: "600", - step: "10", - unit: "px", - source: { file: MP, anchor: '[data-slot="bash-scroll"]', prop: "max-height", format: px }, - }, -] - -// --------------------------------------------------------------------------- -// Playground component -// --------------------------------------------------------------------------- -function FileStub() { - return
File viewer stub
-} - -function Playground() { - // ---- Messages & parts state ---- - const [state, setState] = createStore<{ - messages: Message[] - parts: Record - }>({ - messages: [], - parts: {}, - }) - const [session, setSession] = createSignal({ ...DEFAULT_SESSION }) - const [loaded, setLoaded] = createSignal("") - const [issue, setIssue] = createSignal("") - - // ---- CSS overrides ---- - const [css, setCss] = createStore>({}) - const [defaults, setDefaults] = createStore>({}) - let styleEl: HTMLStyleElement | undefined - let previewRef: HTMLDivElement | undefined - let pick: HTMLInputElement | undefined - - const sample = (ctrl: CSSControl) => { - if (!ctrl.group.startsWith("Markdown")) return ctrl.selector - return ctrl.selector.replace( - '[data-component="markdown"]', - '[data-component="text-part"] [data-component="markdown"]', - ) - } - - /** Read computed styles from the DOM to seed slider defaults */ - const readDefaults = () => { - const root = previewRef - if (!root) return - const next: Record = {} - for (const ctrl of CSS_CONTROLS) { - const el = (root.querySelector(sample(ctrl)) ?? root.querySelector(ctrl.selector)) as HTMLElement | null - if (!el) continue - const styles = getComputedStyle(el) - const raw = ctrl.property.startsWith("--") - ? styles.getPropertyValue(ctrl.property).trim() - : ((styles as any)[ctrl.property] as string) - if (!raw) continue - // Shorthands may return "24px 0px" — take the first value - const num = parseFloat(raw.split(" ")[0]) - if (!Number.isFinite(num)) continue - // line-height returns px — convert back to % relative to font-size - if (ctrl.unit === "%") { - const fs = parseFloat(styles.fontSize) - if (fs > 0) { - next[ctrl.key] = String(Math.round((num / fs) * 100)) - continue - } - } - next[ctrl.key] = String(Math.round(num)) - } - setDefaults(next) - } - - const updateStyle = () => { - const rules: string[] = [] - for (const ctrl of CSS_CONTROLS) { - const val = css[ctrl.key] - if (val === undefined) continue - const value = ctrl.unit ? `${val}${ctrl.unit}` : val - rules.push(`${ctrl.selector} { ${ctrl.property}: ${value} !important; }`) - } - if (styleEl) styleEl.textContent = rules.join("\n") - } - - const setCssValue = (key: string, value: string) => { - setCss(key, value) - updateStyle() - } - - const resetCss = () => { - batch(() => { - for (const ctrl of CSS_CONTROLS) { - setCss(ctrl.key, undefined as any) - } - }) - if (styleEl) styleEl.textContent = "" - } - - // ---- Derived ---- - const userMessages = createMemo(() => state.messages.filter((m): m is UserMessage => m.role === "user")) - - const data = createMemo(() => ({ - session: [session()], - session_status: {}, - turn_change_aggregate: {}, - message: { [session().id]: state.messages }, - part: state.parts, - provider: { - all: [{ id: "anthropic", models: { "claude-sonnet-4-20250514": { name: "Claude Sonnet" } } }], - }, - })) - - // Read computed defaults once DOM has turn elements to query - createEffect( - on( - () => userMessages().length, - (len) => { - if (len === 0) return - // Wait a frame for the DOM to settle after render - requestAnimationFrame(readDefaults) - }, - ), - ) - - // ---- Find or create the last assistant message to append parts to ---- - const lastAssistantID = createMemo(() => { - for (let i = state.messages.length - 1; i >= 0; i--) { - if (state.messages[i].role === "assistant") return state.messages[i].id - } - return undefined - }) - - /** Ensure a turn (user + assistant) exists and return the assistant message id */ - const ensureTurn = (): string => { - const id = lastAssistantID() - if (id) return id - // Create a minimal placeholder turn - const user = mkUser("...", [], session().id) - const asst = mkAssistant(user.message.id, session().id) - setState( - produce((draft) => { - draft.messages.push(user.message) - draft.messages.push(asst) - draft.parts[user.message.id] = user.parts - draft.parts[asst.id] = [] - }), - ) - return asst.id - } - - /** Append parts to the last assistant message */ - const appendParts = (parts: Part[]) => { - const id = ensureTurn() - setState( - produce((draft) => { - const existing = draft.parts[id] ?? [] - draft.parts[id] = [...existing, ...parts] - }), - ) - } - - // ---- User message helpers ---- - const addUser = (variant: keyof typeof USER_VARIANTS) => { - const v = USER_VARIANTS[variant] - const user = mkUser(v.text, v.parts, session().id) - const asst = mkAssistant(user.message.id, session().id) - setState( - produce((draft) => { - draft.messages.push(user.message) - draft.messages.push(asst) - draft.parts[user.message.id] = user.parts - draft.parts[asst.id] = [] - }), - ) - } - - // ---- Part helpers (append to last turn) ---- - const addText = (variant: keyof typeof MARKDOWN_SAMPLES) => { - appendParts([textPart(MARKDOWN_SAMPLES[variant])]) - } - - const addReasoning = () => { - const idx = Math.floor(Math.random() * REASONING_SAMPLES.length) - appendParts([reasoningPart(REASONING_SAMPLES[idx])]) - } - - const addTool = (name: keyof typeof TOOL_SAMPLES) => { - appendParts([toolPart(TOOL_SAMPLES[name])]) - } - - // ---- Composite helpers (create full turns with user + assistant) ---- - const addFullTurn = (userText: string, parts: Part[]) => { - const user = mkUser(userText, [], session().id) - const asst = mkAssistant(user.message.id, session().id) - setState( - produce((draft) => { - draft.messages.push(user.message) - draft.messages.push(asst) - draft.parts[user.message.id] = user.parts - draft.parts[asst.id] = parts - }), - ) - } - - const addContextGroupTurn = () => { - addFullTurn("Read some files", [ - toolPart(TOOL_SAMPLES.read), - toolPart(TOOL_SAMPLES.glob), - toolPart(TOOL_SAMPLES.grep), - textPart("After gathering context, here's what I found:\n\n" + LOREM[2]), - ]) - } - - const addReasoningFullTurn = () => { - addFullTurn("Make the changes described above", [ - reasoningPart(REASONING_SAMPLES[0]), - toolPart(TOOL_SAMPLES.read), - toolPart(TOOL_SAMPLES.glob), - toolPart(TOOL_SAMPLES.grep), - toolPart(TOOL_SAMPLES.edit), - toolPart(TOOL_SAMPLES.bash), - textPart(MARKDOWN_SAMPLES.mixed), - ]) - } - - const addKitchenSink = () => { - // User message variants - addUser("short") - appendParts([textPart(MARKDOWN_SAMPLES.headings)]) - addUser("medium") - appendParts([textPart(MARKDOWN_SAMPLES.lists)]) - addUser("long") - appendParts([textPart(MARKDOWN_SAMPLES.code)]) - addUser("with @file") - appendParts([textPart(MARKDOWN_SAMPLES.mixed)]) - addUser("with image") - appendParts([reasoningPart(REASONING_SAMPLES[0]), textPart(MARKDOWN_SAMPLES.table)]) - addUser("multi attachment") - appendParts([ - toolPart(TOOL_SAMPLES.read), - toolPart(TOOL_SAMPLES.glob), - toolPart(TOOL_SAMPLES.grep), - toolPart(TOOL_SAMPLES.edit), - toolPart(TOOL_SAMPLES.bash), - textPart(MARKDOWN_SAMPLES.blockquote), - ]) - addContextGroupTurn() - addReasoningFullTurn() - } - - const interrupt = () => { - const user = userMessages().at(-1) - if (!user) return - const now = Date.now() - - setState( - produce((draft) => { - const msg = draft.messages.findLast( - (item): item is AssistantMessage => item.role === "assistant" && item.parentID === user.id, - ) - - if (msg) { - const time = msg.time ?? { created: now } - msg.time = { ...time, completed: time.completed ?? now } - msg.error = { name: "MessageAbortedError", message: "Interrupted" } - return - } - - const asst = mkAssistant(user.id, session().id) - asst.time = { created: now, completed: now } - asst.error = { name: "MessageAbortedError", message: "Interrupted" } - draft.messages.push(asst) - draft.parts[asst.id] = [] - }), - ) - } - - const load = (raw: unknown, name: string) => { - const next = normalize(raw) - const id = typeof next.info.id === "string" && next.info.id ? next.info.id : SESSION_ID - const messages = next.messages.map((msg) => ({ - ...msg.info, - sessionID: typeof msg.info.sessionID === "string" ? msg.info.sessionID : id, - })) - const parts = Object.fromEntries( - next.messages.map((msg, idx) => { - const info = messages[idx] - return [ - info.id, - msg.parts.map((part) => ({ - ...part, - messageID: typeof part.messageID === "string" ? part.messageID : info.id, - sessionID: typeof part.sessionID === "string" ? part.sessionID : info.sessionID, - })), - ] - }), - ) - - batch(() => { - setSession({ - ...DEFAULT_SESSION, - ...next.info, - id, - title: typeof next.info.title === "string" && next.info.title ? next.info.title : name, - }) - setState({ messages, parts }) - setLoaded(name) - setIssue("") - }) - } - - const importFile = async (event: Event) => { - const input = event.currentTarget as HTMLInputElement - const file = input.files?.[0] - if (!file) return - - setIssue("") - - try { - load(JSON.parse(await file.text()), file.name) - } catch (err) { - setIssue(err instanceof Error ? err.message : String(err)) - } finally { - input.value = "" - } - } - - const clearAll = () => { - batch(() => { - setState({ messages: [], parts: {} }) - setSession({ ...DEFAULT_SESSION }) - setLoaded("") - setIssue("") - seq = 0 - }) - } - - // ---- CSS export ---- - const exportCss = () => { - const lines: string[] = ["/* Timeline Playground CSS Overrides */", ""] - const groups = new Map() - - for (const ctrl of CSS_CONTROLS) { - const val = css[ctrl.key] - if (val === undefined) continue - const value = ctrl.unit ? `${val}${ctrl.unit}` : val - const group = ctrl.group - if (!groups.has(group)) groups.set(group, []) - groups.get(group)!.push(`/* ${ctrl.label}: ${value} */`) - groups.get(group)!.push(`${ctrl.selector} { ${ctrl.property}: ${value}; }`) - } - - if (groups.size === 0) { - lines.push("/* No overrides applied */") - } else { - for (const [group, rules] of groups) { - lines.push(`/* --- ${group} --- */`) - lines.push(...rules) - lines.push("") - } - } - - const text = lines.join("\n") - navigator.clipboard.writeText(text).catch(() => {}) - return text - } - - const [exported, setExported] = createSignal("") - - // ---- Apply to source files ---- - const [applying, setApplying] = createSignal(false) - const [applyResult, setApplyResult] = createSignal("") - - const changedControls = createMemo(() => CSS_CONTROLS.filter((ctrl) => css[ctrl.key] !== undefined && ctrl.source)) - - const applyToSource = async () => { - const controls = changedControls() - if (controls.length === 0) return - - setApplying(true) - setApplyResult("") - - const edits = controls.map((ctrl) => { - const src = ctrl.source! - return { file: src.file, anchor: src.anchor, prop: src.prop, value: src.format(css[ctrl.key]!) } - }) - - try { - const resp = await fetch("/__playground/apply-css", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ edits }), - }) - const data = await resp.json() - const ok = data.results?.filter((r: any) => r.ok).length ?? 0 - const fail = data.results?.filter((r: any) => !r.ok) ?? [] - const lines = [`Applied ${ok}/${edits.length} edits`] - for (const f of fail) { - lines.push(` FAIL ${f.file} ${f.prop}: ${f.error}`) - } - setApplyResult(lines.join("\n")) - - if (ok === edits.length) { - batch(() => { - for (const ctrl of controls) { - setDefaults(ctrl.key, css[ctrl.key]!) - setCss(ctrl.key, undefined as any) - } - }) - updateStyle() - // Wait for Vite HMR then re-read computed defaults - setTimeout(readDefaults, 500) - } - } catch (err) { - setApplyResult(`Error: ${err}`) - } finally { - setApplying(false) - } - } - - // ---- Panel collapse state ---- - const [panels, setPanels] = createStore({ - generators: true, - css: true, - export: false, - }) - - // ---- Group collapse state for CSS ---- - const [collapsed, setCollapsed] = createStore>({}) - const groups = createMemo(() => { - const result = new Map() - for (const ctrl of CSS_CONTROLS) { - if (!result.has(ctrl.group)) result.set(ctrl.group, []) - result.get(ctrl.group)!.push(ctrl) - } - return result - }) - - // ---- Shared button styles ---- - const sectionLabel = { - "font-size": "11px", - color: "var(--fg-weak)", - "margin-bottom": "4px", - "text-transform": "uppercase", - "letter-spacing": "0.5px", - } as const - const btnStyle = { - padding: "4px 8px", - "border-radius": "4px", - border: "1px solid var(--border-weak)", - background: "var(--surface-base)", - cursor: "pointer", - "font-size": "12px", - color: "var(--fg-base)", - } as const - const btnAccent = { - ...btnStyle, - border: "1px solid var(--brand-primary)", - background: "var(--surface-interactive-base)", - "font-weight": "500", - color: "var(--brand-primary)", - } as const - const btnDanger = { - ...btnStyle, - border: "1px solid var(--error)", - background: "transparent", - color: "var(--error-text)", - } as const - - return ( -
- {/* Inject dynamic style element */} -