From ff92d26f046543d39ff781a720852363c5d44941 Mon Sep 17 00:00:00 2001 From: Rasmus Viloma Date: Mon, 3 Aug 2026 18:48:36 +0300 Subject: [PATCH 1/4] build(chat): emit unbundled per-module output with tsc --- bun.lock | 137 ------------------ packages/chat/bunup.config.ts | 41 ------ packages/chat/package.json | 47 +++--- packages/chat/scripts/add-dist-extensions.mjs | 66 +++++++++ packages/chat/tsconfig.build.json | 23 +++ 5 files changed, 112 insertions(+), 202 deletions(-) delete mode 100644 packages/chat/bunup.config.ts create mode 100644 packages/chat/scripts/add-dist-extensions.mjs create mode 100644 packages/chat/tsconfig.build.json diff --git a/bun.lock b/bun.lock index 23e5859..040badb 100644 --- a/bun.lock +++ b/bun.lock @@ -56,7 +56,6 @@ "@types/react": "^19", "@types/react-dom": "^19", "axe-core": "^4.12.1", - "bunup": "^0.16.32", "typescript": "^5", }, "peerDependencies": { @@ -82,16 +81,10 @@ "@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - "@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/runtime": ["@babel/runtime@7.29.7", "", {}, "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw=="], - "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@base-ui/react": ["@base-ui/react@1.6.0", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@base-ui/utils": "0.3.1", "@floating-ui/react-dom": "^2.1.8", "@floating-ui/utils": "^0.2.11", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@date-fns/tz": "^1.2.0", "@types/react": "^17 || ^18 || ^19", "date-fns": "^4.0.0", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@date-fns/tz", "@types/react", "date-fns"] }, "sha512-/jzjTWJYXhRFO45Bev9lc3cHbmjzCMpUqbMZ2AgKy/z25mY9B6shGSNcXcjQar9n5doM0KYW1W8fcFv2jZBuMw=="], "@base-ui/utils": ["@base-ui/utils@0.3.1", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@floating-ui/utils": "^0.2.11", "reselect": "^5.2.0", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-gFFiltORVmW/N6IILTGxizP3PBpVpysqML1ALY5Vk0mH+7faVkCknOU31goYHN5Aoek2dkjxva1XOD2Ce9WuIg=="], @@ -116,10 +109,6 @@ "@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.2", "", {}, "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA=="], - "@bunup/dts": ["@bunup/dts@0.14.53", "", { "dependencies": { "@babel/parser": "^7.28.6", "coffi": "^0.1.37", "oxc-minify": "^0.93.0", "oxc-resolver": "^11.16.2", "oxc-transform": "^0.93.0", "picocolors": "^1.1.1", "std-env": "^3.10.0", "ts-import-resolver": "^0.1.23" }, "peerDependencies": { "typescript": ">=4.5.0" }, "optionalPeers": ["typescript"] }, "sha512-qyHgYEYxcghoChICEhuFKfA/EqqRA9WHOHLXcIkR9c70DgLAEXcOlkYhfBPmGxfMKJYL0X4VtQwst0/AMk9D3g=="], - - "@bunup/shared": ["@bunup/shared@0.16.31", "", { "peerDependencies": { "typescript": ">=4.5.0" }, "optionalPeers": ["typescript"] }, "sha512-h1dw669Uk68NCt1WvgqNfQoCcaK1RxLVAs3yQRCRaFxJiI9cqhU0EquVoGFM1g1BxzujGa7+bA9gt5JR0ZVLxA=="], - "@changesets/apply-release-plan": ["@changesets/apply-release-plan@7.1.1", "", { "dependencies": { "@changesets/config": "^3.1.4", "@changesets/get-version-range-type": "^0.4.0", "@changesets/git": "^3.0.4", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "detect-indent": "^6.0.0", "fs-extra": "^7.0.1", "lodash.startcase": "^4.4.0", "outdent": "^0.5.0", "prettier": "^2.7.1", "resolve-from": "^5.0.0", "semver": "^7.5.3" } }, "sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA=="], "@changesets/assemble-release-plan": ["@changesets/assemble-release-plan@6.0.10", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.4", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "semver": "^7.5.3" } }, "sha512-rSDcqdJ9KbVyjpBIuCidhvZNIiVt1XaIYp73ycVQRIA5n/j6wQaEk0ChRLMUQ1vkxZe51PTQ9OIhbg6HQMW45A=="], @@ -160,12 +149,8 @@ "@chevrotain/types": ["@chevrotain/types@11.1.2", "", {}, "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw=="], - "@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="], - "@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="], - "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="], "@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="], @@ -304,8 +289,6 @@ "@mermaid-js/parser": ["@mermaid-js/parser@1.2.0", "", { "dependencies": { "@chevrotain/types": "~11.1.2" } }, "sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA=="], - "@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=="], - "@next/env": ["@next/env@16.1.6", "", {}, "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ=="], "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.1.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw=="], @@ -334,104 +317,6 @@ "@orama/orama": ["@orama/orama@3.1.18", "", {}, "sha512-a61ljmRVVyG5MC/698C8/FfFDw5a8LOIvyOLW5fztgUXqUpc1jOfQzOitSCbge657OgXXThmY3Tk8fpiDb4UcA=="], - "@oxc-minify/binding-android-arm64": ["@oxc-minify/binding-android-arm64@0.93.0", "", { "os": "android", "cpu": "arm64" }, "sha512-N3j/JoK4hXwQbnyOJoEltM8MEkddWV3XtfYimO6jsMjr5R6QdauKaSVeQHO/lSezB7SFkrMPqr6X7tBfghHiXA=="], - - "@oxc-minify/binding-darwin-arm64": ["@oxc-minify/binding-darwin-arm64@0.93.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-kLJJe7uBE+a9ql6eLGAtJ1g1LuEXi4aHbsiu342wGe+wRieSPi/Cx0aeDsQjdetwK5mqJWjWS2FO/n03jiw+IQ=="], - - "@oxc-minify/binding-darwin-x64": ["@oxc-minify/binding-darwin-x64@0.93.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-0g6sVLaatgJpD28et/ykZCr5MHn7SWOblRCpXS47vcVmRyBFnUt21oiv3RqKacl1LKgk3czDHHribEfRyygiVw=="], - - "@oxc-minify/binding-freebsd-x64": ["@oxc-minify/binding-freebsd-x64@0.93.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-h8HUtllzBHIx31Uxx1maxd7ur4lMi0qoz0OzoqiohZ1/Ty5GZDe3F/yd6mN4M913e4xrNlcGLFaN8tI9W+Fstw=="], - - "@oxc-minify/binding-linux-arm-gnueabihf": ["@oxc-minify/binding-linux-arm-gnueabihf@0.93.0", "", { "os": "linux", "cpu": "arm" }, "sha512-CIElV4OLqkt5PJtirJ1ZRNvKkELhPkDWZmYpiDRI43mQl6Hkt0YsoBAeFKqrwx3AfwAYWvbFjHII++S+Vvnc4A=="], - - "@oxc-minify/binding-linux-arm-musleabihf": ["@oxc-minify/binding-linux-arm-musleabihf@0.93.0", "", { "os": "linux", "cpu": "arm" }, "sha512-AOvZfiwnNULzSHxtW0BO0VXrFzDDFcknLZzQLZF8z9i070un+0S2Q3oZtA13gx7Z7LFJ87+/gKmgSKjHUI1Mzg=="], - - "@oxc-minify/binding-linux-arm64-gnu": ["@oxc-minify/binding-linux-arm64-gnu@0.93.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-RNBTRvQ3XiVwoDU12y92HCwkznRb5N5ybGqC1Jt/eyWuRDI2838rzcoTrHQ/oke9/u4vHZ1lZwabR8z+VALE1g=="], - - "@oxc-minify/binding-linux-arm64-musl": ["@oxc-minify/binding-linux-arm64-musl@0.93.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-qtE6qL7HC61rLKQBCvgsNJ9Bvvhe4U6V9VBPmvFw1SKRhoUp3PAzkWGnrvAgqQQnFaP34sUq/P4YmXmWL9m7bw=="], - - "@oxc-minify/binding-linux-riscv64-gnu": ["@oxc-minify/binding-linux-riscv64-gnu@0.93.0", "", { "os": "linux", "cpu": "none" }, "sha512-2/8Y2lY8Klns2xrOufR26SR6Ci79NbOR89vuQJCjQwVS7Veb8Gk3lJWn6XRna2QxdvjMQ+6+kmW2/CajJED1rg=="], - - "@oxc-minify/binding-linux-s390x-gnu": ["@oxc-minify/binding-linux-s390x-gnu@0.93.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-0TyiFgN1NPBQcKbdOh4VDt16kaFknKRXvmVshblEqp0JdwZeHFDCWVsTdpFH+NCbnUuzeY932IeP18dUnT/BVw=="], - - "@oxc-minify/binding-linux-x64-gnu": ["@oxc-minify/binding-linux-x64-gnu@0.93.0", "", { "os": "linux", "cpu": "x64" }, "sha512-2vc22dp1RtbfKUM3DbkOvEwGa11JsjAdLHXBAAAkNAqD/ux+ERwLscoP2O71I5jXMTbeHe6eDem6gldyiWr76Q=="], - - "@oxc-minify/binding-linux-x64-musl": ["@oxc-minify/binding-linux-x64-musl@0.93.0", "", { "os": "linux", "cpu": "x64" }, "sha512-o7vVnDnF5k3xlgINjL7D2R9v47jczG5Jk+4xeZ+O9c9XxSx4jvGTqtBWf1Ka95fupJEm2QpSrO+wlA14exF3kA=="], - - "@oxc-minify/binding-wasm32-wasi": ["@oxc-minify/binding-wasm32-wasi@0.93.0", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.0.5" }, "cpu": "none" }, "sha512-cW338ha926sD39uRDOrD/OMBgzosVtVPthXj1cl5rW9OQxuduXNqWbn/rJCA4X16T3IUcUnIbP2oCR8NI9D9xw=="], - - "@oxc-minify/binding-win32-arm64-msvc": ["@oxc-minify/binding-win32-arm64-msvc@0.93.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-f/STdah7v3L6hZDcGUlk2NVcXtwlrzFUiQHI+TcE21uMzHItCEv34gkDtrAq04sAsDF+Aykuw3LnsWwg0pv7RA=="], - - "@oxc-minify/binding-win32-x64-msvc": ["@oxc-minify/binding-win32-x64-msvc@0.93.0", "", { "os": "win32", "cpu": "x64" }, "sha512-FLnVjLRW3ifwHR6/87q/OQCukw+KTCR1SpcawwcykG0F62BQZjiwXpEHwNWgrV41M9Mdd52ND8/+HrMqcFlZMw=="], - - "@oxc-resolver/binding-android-arm-eabi": ["@oxc-resolver/binding-android-arm-eabi@11.23.0", "", { "os": "android", "cpu": "arm" }, "sha512-8IJyWRLVAyhTfe9/TIEbQqSQnl5rUqYJrUOS6Dkr+Mq9FGHMxDGeiEmwkBqCvDP5KckpPh/GYSgbag66O6JsCw=="], - - "@oxc-resolver/binding-android-arm64": ["@oxc-resolver/binding-android-arm64@11.23.0", "", { "os": "android", "cpu": "arm64" }, "sha512-pprVojnNhHxupwTT2gdeUlkxll6XEvWWBk3oVicOSNVWQC99OBnDhMQDoirqnzrE1bScQSMS2JgPpqdlrhz/Fg=="], - - "@oxc-resolver/binding-darwin-arm64": ["@oxc-resolver/binding-darwin-arm64@11.23.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-mbIrWIMAJeytyee36OyUP5XH92TP7FaKaQ2m5AjokKy7STgjrhRt7SMXqpqLjhGm6Xn721Xmsg6H3Rtd9YQETw=="], - - "@oxc-resolver/binding-darwin-x64": ["@oxc-resolver/binding-darwin-x64@11.23.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-UnIphmZ1LazUCr9DXWaKYWtKDefPMbgLsywaoYxRqVCNHhq4MM6d2q1Nz1i9Vzxt5i+cE2nRUYpAUHr/lijNYA=="], - - "@oxc-resolver/binding-freebsd-x64": ["@oxc-resolver/binding-freebsd-x64@11.23.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-aaZ/cSEYFkSxgS2hOrobT6RQcsWNviOX8dW6CEkVx2/UYkAf9MeHbjl3W0usWV53rVV//ndBdn2nb1y7jsu4lw=="], - - "@oxc-resolver/binding-linux-arm-gnueabihf": ["@oxc-resolver/binding-linux-arm-gnueabihf@11.23.0", "", { "os": "linux", "cpu": "arm" }, "sha512-IoJLvO5SjLSVMaq83BNTrPCb1FppvoJc1IhZ5CoUVl3PykUBku7D+LK1j0GSurhJcIc6zfjghsvaZNpq5ev6Mg=="], - - "@oxc-resolver/binding-linux-arm-musleabihf": ["@oxc-resolver/binding-linux-arm-musleabihf@11.23.0", "", { "os": "linux", "cpu": "arm" }, "sha512-vskFpwg44T/LFsfjSCnVZ5ygcuqzPC1yUzVEiKa8BgHAQz0+QLQQW3EGWLPVi8EXFghzjR4EtgPBtOhCjU4jdw=="], - - "@oxc-resolver/binding-linux-arm64-gnu": ["@oxc-resolver/binding-linux-arm64-gnu@11.23.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-//TcHVhrChyw5RYtgts6WO7KcWq9387c1Z5Zvhqpk/ktAbyaRYgBZrpSY1GDCFq50ASt6B6jhh+JxB1rB45IAg=="], - - "@oxc-resolver/binding-linux-arm64-musl": ["@oxc-resolver/binding-linux-arm64-musl@11.23.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZFqlwiTf7CXLLSGyAR9tYiO33LiaeIEXW+xm42d8mnUGpDgPltyrCGYtQezyMMEXvjhOgCz1X+i7sbDTJEx+bg=="], - - "@oxc-resolver/binding-linux-ppc64-gnu": ["@oxc-resolver/binding-linux-ppc64-gnu@11.23.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-oZ5LeN5+H1R19dRjTAxKrxQguH+AsemHcnthEfFxf4OjmBSty2doHLeSmMunKy3zpTHJQ3lh3Af+dNS+W6dYeA=="], - - "@oxc-resolver/binding-linux-riscv64-gnu": ["@oxc-resolver/binding-linux-riscv64-gnu@11.23.0", "", { "os": "linux", "cpu": "none" }, "sha512-O4ciFDyX5ebQd0qkb1bjAIg8IEfiLT03GbSeylwlwlUMK9KwBWaALwrxSbc0Msaz4U6iPj+T9eRXpD5mxBfmvA=="], - - "@oxc-resolver/binding-linux-riscv64-musl": ["@oxc-resolver/binding-linux-riscv64-musl@11.23.0", "", { "os": "linux", "cpu": "none" }, "sha512-P3o8Y9kISYjcxadmbO+94ThRwLhwGuDAbA7dcdd4+YLpfeF+mmobz8fXf4NmSdfSqjyRSkceJDBRZha9NVYkiQ=="], - - "@oxc-resolver/binding-linux-s390x-gnu": ["@oxc-resolver/binding-linux-s390x-gnu@11.23.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-oj03m1E3RmTFczKhcKJDzHaEDKJnPIsDcQFVxBJsSdXGSuIPdt5TvcM332FfMQgzI6yDJqyl4InrnFfXrmUTKQ=="], - - "@oxc-resolver/binding-linux-x64-gnu": ["@oxc-resolver/binding-linux-x64-gnu@11.23.0", "", { "os": "linux", "cpu": "x64" }, "sha512-BqJxbSC8FdP7mSuSpRePTGHm0hXWV+dfz//f7SjsteZncLaBgWTBmi/OZNv7sX6CyG/Pt/eJkPorP+DkMOhMwQ=="], - - "@oxc-resolver/binding-linux-x64-musl": ["@oxc-resolver/binding-linux-x64-musl@11.23.0", "", { "os": "linux", "cpu": "x64" }, "sha512-utmw+VmUrW4K8LI5/6jhg4aGYKJHOIjQ9syYOOA6pF3w7haKu4r4enTe2U0C04/HbUvkq/Zif43xFsKW1Pnq9w=="], - - "@oxc-resolver/binding-openharmony-arm64": ["@oxc-resolver/binding-openharmony-arm64@11.23.0", "", { "os": "none", "cpu": "arm64" }, "sha512-V6lbRrthHa4TbvsLjPtg+EkXT1tRY+s4I8rYLXUfiHlZzGx3sLv1EH9CEOOevjvUYHLsbe/gqCIc73XnQfPb9A=="], - - "@oxc-resolver/binding-wasm32-wasi": ["@oxc-resolver/binding-wasm32-wasi@11.23.0", "", { "dependencies": { "@emnapi/core": "1.11.1", "@emnapi/runtime": "1.11.1", "@napi-rs/wasm-runtime": "^1.1.6" }, "cpu": "none" }, "sha512-gRoOxQPdnAmIAjxcuQNBxfihvx+wjTaQM/9/eP12xwnGNawOG/+Zz9RHN4WNSxT45b5CrscK4NB8aPh+oZQXAQ=="], - - "@oxc-resolver/binding-win32-arm64-msvc": ["@oxc-resolver/binding-win32-arm64-msvc@11.23.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-CgTGMYsJVe1eUiCdJTpGw21svXw79ITsemN1h0hcNkiswasDbN5MoibSLY+gRMWP5syfEz5iffrjZnwEP8xeUA=="], - - "@oxc-resolver/binding-win32-x64-msvc": ["@oxc-resolver/binding-win32-x64-msvc@11.23.0", "", { "os": "win32", "cpu": "x64" }, "sha512-gUGJpr+Rn6zMxm5juApV0K3U845i8t47o8k+rbO0BHbi4PoJIfSPeQmrE2dgohQm2g5k6iviNFyXCGqvmaYUpw=="], - - "@oxc-transform/binding-android-arm64": ["@oxc-transform/binding-android-arm64@0.93.0", "", { "os": "android", "cpu": "arm64" }, "sha512-sjmbt7SbsIgHC9luOLgwoFTI2zbTDesZlfiSFrSYNZv6S6o4zfR2Q/OLhRQqmar15JtxP8NVPuiPyqyx0mqHyg=="], - - "@oxc-transform/binding-darwin-arm64": ["@oxc-transform/binding-darwin-arm64@0.93.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XhYYdSU1Oz1UFeMm8fbfdPrODDQkLz2USDjKmfGuoOQRFKXlq0YTktfzF6z1bxn+T8pc9jIlBDr7f+cyy2CCjg=="], - - "@oxc-transform/binding-darwin-x64": ["@oxc-transform/binding-darwin-x64@0.93.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-cRQE9cWmP1cLPqGKqbr453nio1uIgv2LAfIfdx1fQSClG6PvzfTWTqujM0bJpKquodkm4k07ug35+tp0aIkl0w=="], - - "@oxc-transform/binding-freebsd-x64": ["@oxc-transform/binding-freebsd-x64@0.93.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-m2vojbIELwBwX4ehbEs+7gXx4ooBn2mpR8MkxjZdhucMTj7P38W+jSdW+04pX+/bf2vYxP2madTEZXSX6mseLg=="], - - "@oxc-transform/binding-linux-arm-gnueabihf": ["@oxc-transform/binding-linux-arm-gnueabihf@0.93.0", "", { "os": "linux", "cpu": "arm" }, "sha512-NEoI0t9b8NHzvXuBIADYubKUbfsuDsY9g/uOTiVNoP+r16vpOdiY3avoqP2x2WPSiuprYVFM3Olq3WVngSg+IA=="], - - "@oxc-transform/binding-linux-arm-musleabihf": ["@oxc-transform/binding-linux-arm-musleabihf@0.93.0", "", { "os": "linux", "cpu": "arm" }, "sha512-gzhgsb/o+V2PBElu2aMD7H1ZYOntr4lzuXDyVq/RbwwzF3G3jjFMB5hddbcjky8rdtmVzEaqqESI2h5kWkZUAw=="], - - "@oxc-transform/binding-linux-arm64-gnu": ["@oxc-transform/binding-linux-arm64-gnu@0.93.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-4PdTqvMzLeMLbkwpHvj2ovQoIKaK8i1OnUGW7XzhZKPBGhkcdt/H3oa5FhZ2uoqSIM1KnKKP80MSC1OYqK+w0Q=="], - - "@oxc-transform/binding-linux-arm64-musl": ["@oxc-transform/binding-linux-arm64-musl@0.93.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-c+CrpmFv32Z1WfR2V8sEKPI4XLewK9hQUq57RUDXj3P99IZ9eA0qIq/2Azle4YGbHdeEywAvqEDlbGa7o3ZFNQ=="], - - "@oxc-transform/binding-linux-riscv64-gnu": ["@oxc-transform/binding-linux-riscv64-gnu@0.93.0", "", { "os": "linux", "cpu": "none" }, "sha512-9rkciYe67g/uuVU4bFst96c7Xc2rk2fhzWTsBySUjTvxpgEeBXPsx78OLNwFVZoGf0lGNMXU/oSxr8OImEgvcw=="], - - "@oxc-transform/binding-linux-s390x-gnu": ["@oxc-transform/binding-linux-s390x-gnu@0.93.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-b/3he7qO4It9bTZbKWNvYMVNyoNldgWVDsTleWiRSskDZPTP6CggpcxVolltn8Eegiq4GycKSN1riInTngR6+w=="], - - "@oxc-transform/binding-linux-x64-gnu": ["@oxc-transform/binding-linux-x64-gnu@0.93.0", "", { "os": "linux", "cpu": "x64" }, "sha512-PeKWwubXPza6JGYjZGRt3sleTAaxTac4SG3Nd/VF9WGCY7ljAb6Q0t/gIuyjLm7tgB2E4luFezJogqkAW/b1ng=="], - - "@oxc-transform/binding-linux-x64-musl": ["@oxc-transform/binding-linux-x64-musl@0.93.0", "", { "os": "linux", "cpu": "x64" }, "sha512-UjeqejYo3ynOimHKKPdqtryD0iCWiYHRnNNl5sxzK4GPA/JcxNnRGejAbLH6gkPOFtAi2k4Y5ujc2nU8cX8LSw=="], - - "@oxc-transform/binding-wasm32-wasi": ["@oxc-transform/binding-wasm32-wasi@0.93.0", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.0.5" }, "cpu": "none" }, "sha512-pMUgg0Mm5ASd8oEPf/yiZmHCqH5WMC0mjCK3CccEvfpUf+WC8WYiKiLkPz+0e7AyPW/Kb+MDI9FaYDKQ5QgyoQ=="], - - "@oxc-transform/binding-win32-arm64-msvc": ["@oxc-transform/binding-win32-arm64-msvc@0.93.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-RR30xNVMIEe5PWSD26uGEZp6yH7fkLKznvPSebVOVl2IWW8Sjnd59i6Ws08FmBKH9QP3jW30MypL6ESdlE5yWw=="], - - "@oxc-transform/binding-win32-x64-msvc": ["@oxc-transform/binding-win32-x64-msvc@0.93.0", "", { "os": "win32", "cpu": "x64" }, "sha512-6QN3DEaEw3eWioWEFRgNsTvYq8czYSnpkjB2za+/WdLN0g5FzOl2ZEfNiPrBWIPnSmjUmDWtWVWcSjwY7fX5/Q=="], - "@shikijs/core": ["@shikijs/core@4.3.0", "", { "dependencies": { "@shikijs/primitive": "4.3.0", "@shikijs/types": "4.3.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-EooU3i9F6IAE8kEu+AnGf9DFZWkQBZ+hJn3tLVbsH+61mtQiva5biai66fAA6nvFPXkLgvrh7BrR7YcJU83xQQ=="], "@shikijs/engine-javascript": ["@shikijs/engine-javascript@4.3.0", "", { "dependencies": { "@shikijs/types": "4.3.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.6" } }, "sha512-hTv/KiFf2tpiqlACPiztGGurEARWIutB8YUhcrA1pUC7VzzwKO+g5crUocrLztrZ5ro5Z4hbXg7bYclETn3gSQ=="], @@ -486,8 +371,6 @@ "@testing-library/react": ["@testing-library/react@16.3.2", "", { "dependencies": { "@babel/runtime": "^7.12.5" }, "peerDependencies": { "@testing-library/dom": "^10.0.0", "@types/react": "^18.0.0 || ^19.0.0", "@types/react-dom": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g=="], - "@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/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], @@ -624,8 +507,6 @@ "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], - "bunup": ["bunup@0.16.32", "", { "dependencies": { "@bunup/dts": "^0.14.53", "@bunup/shared": "0.16.31", "chokidar": "^5.0.0", "coffi": "^0.1.37", "lightningcss": "^1.32.0", "picocolors": "^1.1.1", "tinyexec": "^1.2.4", "tree-kill": "^1.2.2", "zlye": "^0.4.4" }, "peerDependencies": { "typescript": ">=4.5.0" }, "optionalPeers": ["typescript"], "bin": { "bunup": "dist/cli/index.js" } }, "sha512-w7RpjGl/AlJMRs0EovFJ0ddF5BOnxZlgDt5Q5xamz/WPd74BqJSetaeCQPDBzvSlOe0gZY90q6lFmjip/aKv2w=="], - "caniuse-lite": ["caniuse-lite@1.0.30001800", "", {}, "sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA=="], "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], @@ -648,8 +529,6 @@ "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], - "coffi": ["coffi@0.1.37", "", { "dependencies": { "strip-json-comments": "^5.0.3" } }, "sha512-ewO5Xis7sw7g54yI/3lJ/nNV90Er4ZnENeDORZjrs58T70MmwKFLZgevraNCz+RmB4KDKsYT1ui1wDB36iPWqQ=="], - "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="], "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], @@ -1082,12 +961,6 @@ "outdent": ["outdent@0.5.0", "", {}, "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q=="], - "oxc-minify": ["oxc-minify@0.93.0", "", { "optionalDependencies": { "@oxc-minify/binding-android-arm64": "0.93.0", "@oxc-minify/binding-darwin-arm64": "0.93.0", "@oxc-minify/binding-darwin-x64": "0.93.0", "@oxc-minify/binding-freebsd-x64": "0.93.0", "@oxc-minify/binding-linux-arm-gnueabihf": "0.93.0", "@oxc-minify/binding-linux-arm-musleabihf": "0.93.0", "@oxc-minify/binding-linux-arm64-gnu": "0.93.0", "@oxc-minify/binding-linux-arm64-musl": "0.93.0", "@oxc-minify/binding-linux-riscv64-gnu": "0.93.0", "@oxc-minify/binding-linux-s390x-gnu": "0.93.0", "@oxc-minify/binding-linux-x64-gnu": "0.93.0", "@oxc-minify/binding-linux-x64-musl": "0.93.0", "@oxc-minify/binding-wasm32-wasi": "0.93.0", "@oxc-minify/binding-win32-arm64-msvc": "0.93.0", "@oxc-minify/binding-win32-x64-msvc": "0.93.0" } }, "sha512-pwMjOGN/I+cfLVkSmECcVHROKwECNVAXCT5h/29S4f0aArIUh3CQnix1yYy7MTQ3yThNuGANjjE9jWJyT43Vbw=="], - - "oxc-resolver": ["oxc-resolver@11.23.0", "", { "optionalDependencies": { "@oxc-resolver/binding-android-arm-eabi": "11.23.0", "@oxc-resolver/binding-android-arm64": "11.23.0", "@oxc-resolver/binding-darwin-arm64": "11.23.0", "@oxc-resolver/binding-darwin-x64": "11.23.0", "@oxc-resolver/binding-freebsd-x64": "11.23.0", "@oxc-resolver/binding-linux-arm-gnueabihf": "11.23.0", "@oxc-resolver/binding-linux-arm-musleabihf": "11.23.0", "@oxc-resolver/binding-linux-arm64-gnu": "11.23.0", "@oxc-resolver/binding-linux-arm64-musl": "11.23.0", "@oxc-resolver/binding-linux-ppc64-gnu": "11.23.0", "@oxc-resolver/binding-linux-riscv64-gnu": "11.23.0", "@oxc-resolver/binding-linux-riscv64-musl": "11.23.0", "@oxc-resolver/binding-linux-s390x-gnu": "11.23.0", "@oxc-resolver/binding-linux-x64-gnu": "11.23.0", "@oxc-resolver/binding-linux-x64-musl": "11.23.0", "@oxc-resolver/binding-openharmony-arm64": "11.23.0", "@oxc-resolver/binding-wasm32-wasi": "11.23.0", "@oxc-resolver/binding-win32-arm64-msvc": "11.23.0", "@oxc-resolver/binding-win32-x64-msvc": "11.23.0" } }, "sha512-f0+l598CJMOLnYPXsXxttJALH0ljtivdRMKtvHhxRuWa5FYmw5+qODARl8oYjMC/brpzKcrpdORsOBrTqhBZ9A=="], - - "oxc-transform": ["oxc-transform@0.93.0", "", { "optionalDependencies": { "@oxc-transform/binding-android-arm64": "0.93.0", "@oxc-transform/binding-darwin-arm64": "0.93.0", "@oxc-transform/binding-darwin-x64": "0.93.0", "@oxc-transform/binding-freebsd-x64": "0.93.0", "@oxc-transform/binding-linux-arm-gnueabihf": "0.93.0", "@oxc-transform/binding-linux-arm-musleabihf": "0.93.0", "@oxc-transform/binding-linux-arm64-gnu": "0.93.0", "@oxc-transform/binding-linux-arm64-musl": "0.93.0", "@oxc-transform/binding-linux-riscv64-gnu": "0.93.0", "@oxc-transform/binding-linux-s390x-gnu": "0.93.0", "@oxc-transform/binding-linux-x64-gnu": "0.93.0", "@oxc-transform/binding-linux-x64-musl": "0.93.0", "@oxc-transform/binding-wasm32-wasi": "0.93.0", "@oxc-transform/binding-win32-arm64-msvc": "0.93.0", "@oxc-transform/binding-win32-x64-msvc": "0.93.0" } }, "sha512-QCwM2nMAWf4hEBehLVA2apllxdmmWLb5M0in9HwC2boaaFbP0QntbLy4hfRZGur2KKyEBErZbH9S5NYX8eHslg=="], - "p-filter": ["p-filter@2.1.0", "", { "dependencies": { "p-map": "^2.0.0" } }, "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw=="], "p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], @@ -1226,8 +1099,6 @@ "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], - "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], - "streamdown": ["streamdown@2.5.0", "", { "dependencies": { "clsx": "^2.1.1", "hast-util-to-jsx-runtime": "^2.3.6", "html-url-attributes": "^3.0.1", "marked": "^17.0.1", "mermaid": "^11.12.2", "rehype-harden": "^1.1.8", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remend": "1.3.0", "tailwind-merge": "^3.4.0", "unified": "^11.0.5", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-/tTnURfIOxZK/pqJAxsfCvETG/XCJHoWnk3jq9xLcuz6CSpnjjuxSRBTTL4PKGhxiZQf0lqPxGhImdpwcZ2XwA=="], "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], @@ -1236,8 +1107,6 @@ "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], - "strip-json-comments": ["strip-json-comments@5.0.3", "", {}, "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw=="], - "style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="], "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="], @@ -1266,16 +1135,12 @@ "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], - "tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="], - "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], "ts-dedent": ["ts-dedent@2.3.0", "", {}, "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg=="], - "ts-import-resolver": ["ts-import-resolver@0.1.23", "", { "peerDependencies": { "typescript": ">=4.5.0" }, "optionalPeers": ["typescript"] }, "sha512-282pgr6j6aOvP3P2I6XugDxdBobkpdMmdbWjRjGl5gjPI1p0+oTNGDh1t924t75kRlyIkF65DiwhSIUysmyHQA=="], - "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], @@ -1324,8 +1189,6 @@ "ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], - "zlye": ["zlye@0.4.4", "", { "dependencies": { "picocolors": "^1.1.1" }, "peerDependencies": { "typescript": ">=4.5.0" }, "optionalPeers": ["typescript"] }, "sha512-fwpeC841X3ElOLYRMKXbwX29pitNrsm6nRNvEhDMrRXDl3BhR2i03Bkr0GNrpyYgZJuEzUsBylXAYzgGPXXOCQ=="], - "zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], "zustand": ["zustand@5.0.14", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g=="], diff --git a/packages/chat/bunup.config.ts b/packages/chat/bunup.config.ts deleted file mode 100644 index 6a161ba..0000000 --- a/packages/chat/bunup.config.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { defineConfig } from "bunup"; - -// One self-contained output file per public entry point (subpath exports), -// ESM only, with declaration files. Splitting is OFF: it hoists a re-export-only -// entry's body into a shared chunk, which strips the entry's own "use client" -// directive and moves the RSC boundary off the module the consumer imports. The -// cost is duplicating the small internal/ helpers across entries — cheap next to -// a correct client boundary. Dependencies and peers are externalized from -// package.json automatically. -// -// dts.inferTypes is REQUIRED, not an optimization. bunup's default declaration -// emit uses TypeScript's isolated-declarations mode, which cannot infer the type -// of an `Object.assign(Root, {...})` compound — it warns TS9010 and emits -// `declare const Thread: unknown`. That erased every compound component in the -// public API while still producing .d.ts files that publint happily accepted. -// inferTypes routes emit through tsc so the compound types are inferred. -// -// The build script pins NODE_ENV=production, and that is load-bearing: with it -// unset the transpiler emits jsxDEV from react/jsx-dev-runtime, which throws -// "jsxDEV is not a function" in any consumer's production build. The `jsx: -// { development: false }` option looks like the declarative fix but bunup -// 0.16.32 ignores it — verified — so the env var is the only lever. -export default defineConfig({ - entry: [ - "src/types.ts", - "src/composer/index.tsx", - "src/chip.tsx", - "src/thread/index.tsx", - "src/reasoning.tsx", - "src/steps.tsx", - "src/attachments.tsx", - "src/ask-user.tsx", - "src/message.tsx", - "src/message-utils.ts", - "src/chip-markdown.ts", - ], - format: ["esm"], - dts: { inferTypes: true }, - splitting: false, - clean: true, -}); diff --git a/packages/chat/package.json b/packages/chat/package.json index 6835a0c..c212000 100644 --- a/packages/chat/package.json +++ b/packages/chat/package.json @@ -44,7 +44,7 @@ "./chip-markdown": "./src/chip-markdown.ts" }, "scripts": { - "build": "NODE_ENV=production bunup", + "build": "rm -rf dist && tsc -p tsconfig.build.json && node scripts/add-dist-extensions.mjs", "test": "bun test", "typecheck": "tsc --noEmit", "prepack": "bun run build && node scripts/swap-exports.mjs pre", @@ -65,55 +65,54 @@ "@types/react": "^19", "@types/react-dom": "^19", "axe-core": "^4.12.1", - "bunup": "^0.16.32", "typescript": "^5" }, "publishConfig": { "access": "public", "exports": { "./types": { - "types": "./dist/src/types.d.ts", - "default": "./dist/src/types.js" + "types": "./dist/types.d.ts", + "default": "./dist/types.js" }, "./composer": { - "types": "./dist/src/composer/index.d.ts", - "default": "./dist/src/composer/index.js" + "types": "./dist/composer/index.d.ts", + "default": "./dist/composer/index.js" }, "./chip": { - "types": "./dist/src/chip.d.ts", - "default": "./dist/src/chip.js" + "types": "./dist/chip.d.ts", + "default": "./dist/chip.js" }, "./thread": { - "types": "./dist/src/thread/index.d.ts", - "default": "./dist/src/thread/index.js" + "types": "./dist/thread/index.d.ts", + "default": "./dist/thread/index.js" }, "./reasoning": { - "types": "./dist/src/reasoning.d.ts", - "default": "./dist/src/reasoning.js" + "types": "./dist/reasoning.d.ts", + "default": "./dist/reasoning.js" }, "./steps": { - "types": "./dist/src/steps.d.ts", - "default": "./dist/src/steps.js" + "types": "./dist/steps.d.ts", + "default": "./dist/steps.js" }, "./attachments": { - "types": "./dist/src/attachments.d.ts", - "default": "./dist/src/attachments.js" + "types": "./dist/attachments.d.ts", + "default": "./dist/attachments.js" }, "./ask-user": { - "types": "./dist/src/ask-user.d.ts", - "default": "./dist/src/ask-user.js" + "types": "./dist/ask-user.d.ts", + "default": "./dist/ask-user.js" }, "./message": { - "types": "./dist/src/message.d.ts", - "default": "./dist/src/message.js" + "types": "./dist/message.d.ts", + "default": "./dist/message.js" }, "./message-utils": { - "types": "./dist/src/message-utils.d.ts", - "default": "./dist/src/message-utils.js" + "types": "./dist/message-utils.d.ts", + "default": "./dist/message-utils.js" }, "./chip-markdown": { - "types": "./dist/src/chip-markdown.d.ts", - "default": "./dist/src/chip-markdown.js" + "types": "./dist/chip-markdown.d.ts", + "default": "./dist/chip-markdown.js" } } } diff --git a/packages/chat/scripts/add-dist-extensions.mjs b/packages/chat/scripts/add-dist-extensions.mjs new file mode 100644 index 0000000..51c5fab --- /dev/null +++ b/packages/chat/scripts/add-dist-extensions.mjs @@ -0,0 +1,66 @@ +// Add explicit .js extensions to relative specifiers in the emitted output. +// +// Why this exists rather than writing extensions in src: the app consumes the +// package's *source* through Next's transpilePackages, and Turbopack does not +// resolve "./actions.js" against actions.tsx — it has no extensionAlias +// equivalent. Extensions in src would therefore break the dev loop. tsc has no +// option to add them on emit either (rewriteRelativeImportExtensions only +// rewrites specifiers that already carry one), so the published artifact gets +// them here instead. +// +// Source stays bundler-friendly; dist stays valid ESM. + +import { readdirSync, readFileSync, statSync, writeFileSync } from "node:fs"; +import { dirname, join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const distDir = fileURLToPath(new URL("../dist", import.meta.url)); + +const walk = (dir) => + readdirSync(dir, { withFileTypes: true }).flatMap((entry) => + entry.isDirectory() ? walk(join(dir, entry.name)) : [join(dir, entry.name)], + ); + +const isFile = (path) => { + try { + return statSync(path).isFile(); + } catch { + return false; + } +}; + +// A specifier resolves either to a sibling module or to a directory's index. +const withExtension = (fromFile, specifier) => { + const target = resolve(dirname(fromFile), specifier); + if (isFile(`${target}.js`)) return `${specifier}.js`; + if (isFile(join(target, "index.js"))) return `${specifier}/index.js`; + return null; +}; + +// `from "x"`, bare `import "x"`, and dynamic `import("x")`. +const SPECIFIER = /(\bfrom\s*"|\bimport\s*"|\bimport\s*\(\s*")(\.\.?\/[^"]*)(")/g; + +let rewritten = 0; +const unresolved = []; + +for (const file of walk(distDir)) { + if (!/\.(js|d\.ts)$/.test(file)) continue; + const original = readFileSync(file, "utf8"); + const updated = original.replace(SPECIFIER, (match, prefix, specifier, suffix) => { + if (/\.(js|json|css)$/.test(specifier)) return match; + const next = withExtension(file, specifier); + if (!next) { + unresolved.push(`${file}: ${specifier}`); + return match; + } + rewritten++; + return prefix + next + suffix; + }); + if (updated !== original) writeFileSync(file, updated); +} + +if (unresolved.length > 0) { + throw new Error(`Unresolved relative specifiers in dist:\n ${unresolved.join("\n ")}`); +} + +console.log(`add-dist-extensions: rewrote ${rewritten} specifiers`); diff --git a/packages/chat/tsconfig.build.json b/packages/chat/tsconfig.build.json new file mode 100644 index 0000000..33b991d --- /dev/null +++ b/packages/chat/tsconfig.build.json @@ -0,0 +1,23 @@ +{ + // Publish build. tsc rather than a bundler, deliberately: the output must + // preserve the module graph 1:1 so each part module keeps its own "use client" + // directive. Bundling collapses the entries into one file, and one file gets + // one top-level directive — which makes every compound sub-component + // unreachable from a React Server Component. + // + // Emitting per-module also means tsc infers declarations natively (no + // isolated-declarations erasure of Object.assign compounds) and always emits + // the production react/jsx-runtime, so neither of those needs a workaround. + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "declaration": true, + // No source maps: they would reference ../src, which `files` doesn't ship. + // Unbundled tsc output is a near 1:1 readable transform of the source, so + // the maps buy little — and a map pointing at a missing file is worse than + // no map. + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src"] +} From cd02877867d52eb3d12cb05374f850d747083d9a Mon Sep 17 00:00:00 2001 From: Rasmus Viloma Date: Mon, 3 Aug 2026 18:53:32 +0300 Subject: [PATCH 2/4] refactor(chip)!: expose parts via namespace export --- components/ai/chip.tsx | 4 ++-- packages/chat/package.json | 6 +++--- packages/chat/src/{ => chip}/chip.tsx | 18 ++++++------------ packages/chat/src/chip/index.parts.ts | 10 ++++++++++ packages/chat/src/chip/index.ts | 4 ++++ .../chat/src/composer/use-composer-editor.tsx | 6 +++--- packages/chat/tests/a11y/harness.a11y.test.tsx | 4 ++-- 7 files changed, 30 insertions(+), 22 deletions(-) rename packages/chat/src/{ => chip}/chip.tsx (81%) create mode 100644 packages/chat/src/chip/index.parts.ts create mode 100644 packages/chat/src/chip/index.ts diff --git a/components/ai/chip.tsx b/components/ai/chip.tsx index f407d9d..86333e9 100644 --- a/components/ai/chip.tsx +++ b/components/ai/chip.tsx @@ -34,12 +34,12 @@ export type ChipVariant = NonNullable["variant const CHIP_ICON_WRAPPER_CLASSES = "mr-0.5 inline-block size-3.5 align-[-0.15em] opacity-70 [&>svg]:block [&>svg]:size-3.5"; -type ChipRootProps = Omit, "variant"> & { +type ChipRootProps = Omit, "variant"> & { variant?: ChipVariant; }; const ChipRoot = ({ variant, className, ...props }: ChipRootProps) => ( - ( diff --git a/packages/chat/package.json b/packages/chat/package.json index c212000..4c74269 100644 --- a/packages/chat/package.json +++ b/packages/chat/package.json @@ -33,7 +33,7 @@ "exports": { "./types": "./src/types.ts", "./composer": "./src/composer/index.tsx", - "./chip": "./src/chip.tsx", + "./chip": "./src/chip/index.ts", "./thread": "./src/thread/index.tsx", "./reasoning": "./src/reasoning.tsx", "./steps": "./src/steps.tsx", @@ -79,8 +79,8 @@ "default": "./dist/composer/index.js" }, "./chip": { - "types": "./dist/chip.d.ts", - "default": "./dist/chip.js" + "types": "./dist/chip/index.d.ts", + "default": "./dist/chip/index.js" }, "./thread": { "types": "./dist/thread/index.d.ts", diff --git a/packages/chat/src/chip.tsx b/packages/chat/src/chip/chip.tsx similarity index 81% rename from packages/chat/src/chip.tsx rename to packages/chat/src/chip/chip.tsx index 2d3fab4..b2e9a6f 100644 --- a/packages/chat/src/chip.tsx +++ b/packages/chat/src/chip/chip.tsx @@ -7,14 +7,14 @@ // Every part supports the Base UI render prop. import { Children, isValidElement, type ReactElement, type ReactNode } from "react"; -import type { PrimitiveProps } from "./internal/primitive-props"; -import { useRenderElement } from "./internal/render/useRenderElement"; +import type { PrimitiveProps } from "../internal/primitive-props"; +import { useRenderElement } from "../internal/render/useRenderElement"; type ChipPreviewProps = { children: ReactNode }; // Marker component — never renders directly. ChipRoot inspects its children // and routes ChipPreview's content into `renderWithPreview`. -const ChipPreview = (_props: ChipPreviewProps): ReactNode => null; +export const ChipPreview = (_props: ChipPreviewProps): ReactNode => null; export type ChipState = { /** Opaque styling hook, surfaced as data-variant. */ @@ -31,7 +31,7 @@ export type ChipRootProps = PrimitiveProps<"span", ChipState> & { renderWithPreview?: (badge: ReactElement, preview: ReactNode) => ReactNode; }; -const ChipRoot = ({ +export const ChipRoot = ({ variant, renderWithPreview, className, @@ -64,7 +64,7 @@ const ChipRoot = ({ export type ChipIconProps = PrimitiveProps<"span">; -const ChipIcon = ({ className, render, style, ...elementProps }: ChipIconProps) => +export const ChipIcon = ({ className, render, style, ...elementProps }: ChipIconProps) => useRenderElement( "span", { className, render, style }, @@ -73,15 +73,9 @@ const ChipIcon = ({ className, render, style, ...elementProps }: ChipIconProps) export type ChipLabelProps = PrimitiveProps<"span">; -const ChipLabel = ({ className, render, style, ...elementProps }: ChipLabelProps) => +export const ChipLabel = ({ className, render, style, ...elementProps }: ChipLabelProps) => useRenderElement( "span", { className, render, style }, { props: [{ "data-chip-label": "" }, elementProps] }, ); - -export const Chip = Object.assign(ChipRoot, { - Icon: ChipIcon, - Label: ChipLabel, - Preview: ChipPreview, -}); diff --git a/packages/chat/src/chip/index.parts.ts b/packages/chat/src/chip/index.parts.ts new file mode 100644 index 0000000..30c9646 --- /dev/null +++ b/packages/chat/src/chip/index.parts.ts @@ -0,0 +1,10 @@ +// Part names for the `Chip` namespace. Deliberately carries no "use client": +// this module and index.ts must stay server-resolvable so a React Server +// Component can reach Chip.Root / Chip.Label through them. The directive lives +// one level down, on chip.tsx. +export { + ChipIcon as Icon, + ChipLabel as Label, + ChipPreview as Preview, + ChipRoot as Root, +} from "./chip"; diff --git a/packages/chat/src/chip/index.ts b/packages/chat/src/chip/index.ts new file mode 100644 index 0000000..f9e72a8 --- /dev/null +++ b/packages/chat/src/chip/index.ts @@ -0,0 +1,4 @@ +// Public surface of @intentface/chat/chip. No "use client" — see index.parts.ts. + +export type { ChipIconProps, ChipLabelProps, ChipRootProps, ChipState } from "./chip"; +export * as Chip from "./index.parts"; diff --git a/packages/chat/src/composer/use-composer-editor.tsx b/packages/chat/src/composer/use-composer-editor.tsx index 6a87abc..e021e08 100644 --- a/packages/chat/src/composer/use-composer-editor.tsx +++ b/packages/chat/src/composer/use-composer-editor.tsx @@ -23,7 +23,7 @@ import { useSyncExternalStore, } from "react"; import { createPortal } from "react-dom"; -import { Chip } from "../chip"; +import { Chip } from "../chip/index"; import { type ChipData, parseChipSegments } from "../chip-markdown"; import { useRefWithInit } from "../internal/render/useRefWithInit"; import { @@ -795,9 +795,9 @@ export const useComposerEditor = (options: UseComposerEditorOptions): UseCompose return [ createPortal( options.renderChip?.(segment.chip) ?? ( - + {segment.chip.label} - + ), element, segment.id, diff --git a/packages/chat/tests/a11y/harness.a11y.test.tsx b/packages/chat/tests/a11y/harness.a11y.test.tsx index e6cb088..6f87409 100644 --- a/packages/chat/tests/a11y/harness.a11y.test.tsx +++ b/packages/chat/tests/a11y/harness.a11y.test.tsx @@ -11,9 +11,9 @@ import { expectNoAxeViolations } from "./axe"; describe("a11y harness", () => { test("renders a primitive and passes structural axe", async () => { const { container } = render( - + PRD.md - , + , ); await expectNoAxeViolations(container); cleanup(); From f6260da36439a5b321bce5b03e8e8321c5de8e43 Mon Sep 17 00:00:00 2001 From: Rasmus Viloma Date: Mon, 3 Aug 2026 19:06:14 +0300 Subject: [PATCH 3/4] refactor(chat)!: expose all primitives via namespace exports --- components/ai/ask-user.tsx | 4 +- components/ai/composer.tsx | 4 +- components/ai/message.tsx | 4 +- components/ai/reasoning.tsx | 4 +- components/ai/steps.tsx | 4 +- components/ai/thread.tsx | 4 +- packages/chat/package.json | 34 ++++----- packages/chat/src/{ => ask-user}/ask-user.tsx | 73 ++++++++++--------- packages/chat/src/ask-user/index.parts.ts | 21 ++++++ packages/chat/src/ask-user/index.ts | 24 ++++++ .../src/{ => attachments}/attachments.tsx | 39 ++++++---- packages/chat/src/attachments/index.parts.ts | 12 +++ packages/chat/src/attachments/index.ts | 20 +++++ packages/chat/src/composer/index.parts.ts | 34 +++++++++ .../chat/src/composer/{index.tsx => index.ts} | 57 +-------------- packages/chat/src/message/index.parts.ts | 9 +++ packages/chat/src/message/index.ts | 16 ++++ packages/chat/src/{ => message}/message.tsx | 17 ++--- packages/chat/src/reasoning/index.parts.ts | 9 +++ packages/chat/src/reasoning/index.ts | 9 +++ .../chat/src/{ => reasoning}/reasoning.tsx | 13 +--- packages/chat/src/steps/index.parts.ts | 13 ++++ packages/chat/src/steps/index.ts | 13 ++++ packages/chat/src/{ => steps}/steps.tsx | 43 ++++++----- packages/chat/src/thread/index.parts.ts | 12 +++ packages/chat/src/thread/index.ts | 16 ++++ .../chat/src/thread/{index.tsx => thread.tsx} | 44 ++++++----- .../chat/tests/a11y/ask-user.a11y.test.tsx | 20 ++--- .../chat/tests/a11y/combobox.a11y.test.tsx | 12 +-- .../chat/tests/a11y/display.a11y.test.tsx | 12 +-- packages/chat/tests/a11y/names.a11y.test.tsx | 12 +-- 31 files changed, 390 insertions(+), 218 deletions(-) rename packages/chat/src/{ => ask-user}/ask-user.tsx (90%) create mode 100644 packages/chat/src/ask-user/index.parts.ts create mode 100644 packages/chat/src/ask-user/index.ts rename packages/chat/src/{ => attachments}/attachments.tsx (89%) create mode 100644 packages/chat/src/attachments/index.parts.ts create mode 100644 packages/chat/src/attachments/index.ts create mode 100644 packages/chat/src/composer/index.parts.ts rename packages/chat/src/composer/{index.tsx => index.ts} (52%) create mode 100644 packages/chat/src/message/index.parts.ts create mode 100644 packages/chat/src/message/index.ts rename packages/chat/src/{ => message}/message.tsx (94%) create mode 100644 packages/chat/src/reasoning/index.parts.ts create mode 100644 packages/chat/src/reasoning/index.ts rename packages/chat/src/{ => reasoning}/reasoning.tsx (90%) create mode 100644 packages/chat/src/steps/index.parts.ts create mode 100644 packages/chat/src/steps/index.ts rename packages/chat/src/{ => steps}/steps.tsx (86%) create mode 100644 packages/chat/src/thread/index.parts.ts create mode 100644 packages/chat/src/thread/index.ts rename packages/chat/src/thread/{index.tsx => thread.tsx} (97%) diff --git a/components/ai/ask-user.tsx b/components/ai/ask-user.tsx index fdd70ae..5ab4aec 100644 --- a/components/ai/ask-user.tsx +++ b/components/ai/ask-user.tsx @@ -19,10 +19,10 @@ import { cn } from "@/lib/utils"; export type { AskUserOptionsHandle }; /** AskUser root container. Stateless — consumers manage all state externally. */ -type AskUserRootProps = ComponentProps; +type AskUserRootProps = ComponentProps; const AskUserRoot = ({ className, ...props }: AskUserRootProps) => ( - + ); /** Question heading text. */ diff --git a/components/ai/composer.tsx b/components/ai/composer.tsx index fcf88da..c308342 100644 --- a/components/ai/composer.tsx +++ b/components/ai/composer.tsx @@ -66,10 +66,10 @@ export type { // Root // --------------------------------------------------------------------------- -export type ComposerRootProps = ComponentProps; +export type ComposerRootProps = ComponentProps; const ComposerRoot = ({ className, ...props }: ComposerRootProps) => ( - + ); // --------------------------------------------------------------------------- diff --git a/components/ai/message.tsx b/components/ai/message.tsx index 00e6ecb..1058016 100644 --- a/components/ai/message.tsx +++ b/components/ai/message.tsx @@ -27,12 +27,12 @@ import { CopyIcon } from "../icons/copy"; import { FileBendIcon } from "../icons/file-bend"; import { StopIcon } from "../icons/stop"; -type MessageRootProps = ComponentProps; +type MessageRootProps = ComponentProps; // Message wrapper with entrance animation: the primitive owns the state/data // attributes and merges them onto the motion element via the render prop. const MessageRoot = ({ className, ...props }: MessageRootProps) => ( - ; +export type ReasoningRootProps = ComponentProps; const ReasoningRoot = memo(({ className, ...props }: ReasoningRootProps) => ( - + )); ReasoningRoot.displayName = "Reasoning"; diff --git a/components/ai/steps.tsx b/components/ai/steps.tsx index 6b598ff..4064f3b 100644 --- a/components/ai/steps.tsx +++ b/components/ai/steps.tsx @@ -13,10 +13,10 @@ import { cn } from "@/lib/utils"; // Root // --------------------------------------------------------------------------- -type StepsRootProps = ComponentProps; +type StepsRootProps = ComponentProps; const StepsRoot = ({ className, ...props }: StepsRootProps) => ( - + ); StepsRoot.displayName = "Steps"; diff --git a/components/ai/thread.tsx b/components/ai/thread.tsx index e602da1..a5915b8 100644 --- a/components/ai/thread.tsx +++ b/components/ai/thread.tsx @@ -22,10 +22,10 @@ export type { ThreadAutoScrollMode, ThreadVisibilityState }; // Root // --------------------------------------------------------------------------- -export type ThreadRootProps = ComponentProps; +export type ThreadRootProps = ComponentProps; const ThreadRoot = ({ className, ...props }: ThreadRootProps) => ( - ({ labelId: "", s /** AskUser root container. Stateless — consumers manage all state externally. */ export type AskUserRootProps = PrimitiveProps<"div">; -const AskUserRoot = ({ className, render, style, ...elementProps }: AskUserRootProps) => { +export const AskUserRoot = ({ className, render, style, ...elementProps }: AskUserRootProps) => { const labelId = useId(); const stepLabelId = useId(); const element = useRenderElement( @@ -46,7 +46,7 @@ const AskUserRoot = ({ className, render, style, ...elementProps }: AskUserRootP /** Question heading text. Carries the id `Options` uses as its accessible name. */ export type AskUserLabelProps = PrimitiveProps<"p">; -const AskUserLabel = ({ className, render, style, ...elementProps }: AskUserLabelProps) => { +export const AskUserLabel = ({ className, render, style, ...elementProps }: AskUserLabelProps) => { const { labelId } = use(AskUserIdsContext); return useRenderElement( "p", @@ -58,7 +58,7 @@ const AskUserLabel = ({ className, render, style, ...elementProps }: AskUserLabe /** Row container for `Label` and optional `Navigation`. */ export type AskUserHeaderProps = PrimitiveProps<"div">; -const AskUserHeader = ({ className, render, style, ...elementProps }: AskUserHeaderProps) => +export const AskUserHeader = ({ className, render, style, ...elementProps }: AskUserHeaderProps) => useRenderElement( "div", { className, render, style }, @@ -68,7 +68,12 @@ const AskUserHeader = ({ className, render, style, ...elementProps }: AskUserHea /** Row container for `Previous`, `StepLabel`, and `Next`. */ export type AskUserNavigationProps = PrimitiveProps<"div">; -const AskUserNavigation = ({ className, render, style, ...elementProps }: AskUserNavigationProps) => +export const AskUserNavigation = ({ + className, + render, + style, + ...elementProps +}: AskUserNavigationProps) => useRenderElement( "div", { className, render, style }, @@ -78,7 +83,12 @@ const AskUserNavigation = ({ className, render, style, ...elementProps }: AskUse /** Navigate to the previous step. Default accessible name; override via aria-label. */ export type AskUserPreviousProps = PrimitiveProps<"button">; -const AskUserPrevious = ({ className, render, style, ...elementProps }: AskUserPreviousProps) => +export const AskUserPrevious = ({ + className, + render, + style, + ...elementProps +}: AskUserPreviousProps) => useRenderElement( "button", { className, render, style }, @@ -88,7 +98,7 @@ const AskUserPrevious = ({ className, render, style, ...elementProps }: AskUserP /** Navigate to the next step. Default accessible name; override via aria-label. */ export type AskUserNextProps = PrimitiveProps<"button">; -const AskUserNext = ({ className, render, style, ...elementProps }: AskUserNextProps) => +export const AskUserNext = ({ className, render, style, ...elementProps }: AskUserNextProps) => useRenderElement( "button", { className, render, style }, @@ -104,7 +114,7 @@ export type AskUserStepLabelProps = Omit, "children"> & { children?: ReactNode | ((state: AskUserStepLabelState) => ReactNode); }; -const AskUserStepLabel = ({ +export const AskUserStepLabel = ({ children, className, render, @@ -172,7 +182,7 @@ export type AskUserOptionsProps = Omit, "value" | "re ref?: RefObject; }; -const AskUserOptions = ({ +export const AskUserOptions = ({ multiSelect = false, groupName = "", ref, @@ -343,7 +353,7 @@ export type AskUserOptionProps = PrimitiveProps<"label", AskUserOptionState> & { onSelect?: () => void; }; -const AskUserOption = ({ +export const AskUserOption = ({ value = "", selected = false, onSelect, @@ -420,7 +430,7 @@ const useItemRegistration = ( /** Flex column wrapper for `OptionLabel` and `OptionDescription`. */ export type AskUserOptionContentProps = PrimitiveProps<"span">; -const AskUserOptionContent = ({ +export const AskUserOptionContent = ({ className, render, style, @@ -435,7 +445,7 @@ const AskUserOptionContent = ({ /** Option title text. */ export type AskUserOptionLabelProps = PrimitiveProps<"span">; -const AskUserOptionLabel = ({ +export const AskUserOptionLabel = ({ className, render, style, @@ -450,7 +460,7 @@ const AskUserOptionLabel = ({ /** Option subtitle/description text. */ export type AskUserOptionDescriptionProps = PrimitiveProps<"span">; -const AskUserOptionDescription = ({ +export const AskUserOptionDescription = ({ className, render, style, @@ -465,7 +475,7 @@ const AskUserOptionDescription = ({ /** Keyboard shortcut hints displayed below the ask-user options. */ export type AskUserHintsProps = PrimitiveProps<"div">; -const AskUserHints = ({ className, render, style, ...elementProps }: AskUserHintsProps) => +export const AskUserHints = ({ className, render, style, ...elementProps }: AskUserHintsProps) => useRenderElement( "div", { className, render, style }, @@ -475,7 +485,12 @@ const AskUserHints = ({ className, render, style, ...elementProps }: AskUserHint /** Dismiss/skip button. Stateless — wire `onClick` to your dismiss handler. */ export type AskUserDismissProps = PrimitiveProps<"button">; -const AskUserDismiss = ({ className, render, style, ...elementProps }: AskUserDismissProps) => +export const AskUserDismiss = ({ + className, + render, + style, + ...elementProps +}: AskUserDismissProps) => useRenderElement( "button", { className, render, style }, @@ -485,26 +500,14 @@ const AskUserDismiss = ({ className, render, style, ...elementProps }: AskUserDi /** Continue/submit button — `type=submit` so the enclosing form drives it. */ export type AskUserContinueProps = PrimitiveProps<"button">; -const AskUserContinue = ({ className, render, style, ...elementProps }: AskUserContinueProps) => +export const AskUserContinue = ({ + className, + render, + style, + ...elementProps +}: AskUserContinueProps) => useRenderElement( "button", { className, render, style }, { props: [{ type: "submit" as const, "data-ask-user-continue": "" }, elementProps] }, ); - -export const AskUser = Object.assign(AskUserRoot, { - Header: AskUserHeader, - Label: AskUserLabel, - Navigation: AskUserNavigation, - Previous: AskUserPrevious, - Next: AskUserNext, - StepLabel: AskUserStepLabel, - Options: AskUserOptions, - Option: AskUserOption, - OptionContent: AskUserOptionContent, - OptionLabel: AskUserOptionLabel, - OptionDescription: AskUserOptionDescription, - Hints: AskUserHints, - Dismiss: AskUserDismiss, - Continue: AskUserContinue, -}); diff --git a/packages/chat/src/ask-user/index.parts.ts b/packages/chat/src/ask-user/index.parts.ts new file mode 100644 index 0000000..e9f52ab --- /dev/null +++ b/packages/chat/src/ask-user/index.parts.ts @@ -0,0 +1,21 @@ +// Part names for the `AskUser` namespace. Deliberately carries no +// "use client" directive: this module and index.ts must stay server-resolvable +// so a React Server Component can reach AskUser.Root and its siblings +// through them. The directive lives one level down, on ask-user.tsx. +export { + AskUserContinue as Continue, + AskUserDismiss as Dismiss, + AskUserHeader as Header, + AskUserHints as Hints, + AskUserLabel as Label, + AskUserNavigation as Navigation, + AskUserNext as Next, + AskUserOption as Option, + AskUserOptionContent as OptionContent, + AskUserOptionDescription as OptionDescription, + AskUserOptionLabel as OptionLabel, + AskUserOptions as Options, + AskUserPrevious as Previous, + AskUserRoot as Root, + AskUserStepLabel as StepLabel, +} from "./ask-user"; diff --git a/packages/chat/src/ask-user/index.ts b/packages/chat/src/ask-user/index.ts new file mode 100644 index 0000000..c52d674 --- /dev/null +++ b/packages/chat/src/ask-user/index.ts @@ -0,0 +1,24 @@ +// Public surface of @intentface/chat/ask-user. No directive — see index.parts.ts. + +export type { + AskUserContinueProps, + AskUserDismissProps, + AskUserHeaderProps, + AskUserHintsProps, + AskUserLabelProps, + AskUserNavigationProps, + AskUserNextProps, + AskUserOptionContentProps, + AskUserOptionDescriptionProps, + AskUserOptionLabelProps, + AskUserOptionProps, + AskUserOptionState, + AskUserOptionsHandle, + AskUserOptionsProps, + AskUserPreviousProps, + AskUserRootProps, + AskUserStepLabelProps, + AskUserStepLabelState, +} from "./ask-user"; +export { useAskUserOption, useAskUserOptions } from "./ask-user"; +export * as AskUser from "./index.parts"; diff --git a/packages/chat/src/attachments.tsx b/packages/chat/src/attachments/attachments.tsx similarity index 89% rename from packages/chat/src/attachments.tsx rename to packages/chat/src/attachments/attachments.tsx index 9a815ac..aad5f9b 100644 --- a/packages/chat/src/attachments.tsx +++ b/packages/chat/src/attachments/attachments.tsx @@ -10,8 +10,8 @@ import { nanoid } from "nanoid"; import { type ReactNode, useEffect, useState } from "react"; import { createPortal } from "react-dom"; -import type { PrimitiveProps } from "./internal/primitive-props"; -import { useRenderElement } from "./internal/render/useRenderElement"; +import type { PrimitiveProps } from "../internal/primitive-props"; +import { useRenderElement } from "../internal/render/useRenderElement"; // --------------------------------------------------------------------------- // Types @@ -86,7 +86,12 @@ export const matchesAccept = (file: File, accept: string): boolean => { export type AttachmentsRootProps = PrimitiveProps<"div">; -const AttachmentsRoot = ({ className, render, style, ...elementProps }: AttachmentsRootProps) => +export const AttachmentsRoot = ({ + className, + render, + style, + ...elementProps +}: AttachmentsRootProps) => useRenderElement( "div", { className, render, style }, @@ -98,7 +103,12 @@ export type AttachmentsItemProps = PrimitiveProps<"div">; // Generic structural slot for one attachment. It carries no media taxonomy — // categorization (image/pdf/file, icons, thumbnails) belongs to the styled // layer, which reads the item's raw mediaType however it wants. -const AttachmentsItem = ({ className, render, style, ...elementProps }: AttachmentsItemProps) => +export const AttachmentsItem = ({ + className, + render, + style, + ...elementProps +}: AttachmentsItemProps) => useRenderElement( "div", { className, render, style }, @@ -111,7 +121,7 @@ export type AttachmentsRemoveProps = PrimitiveProps<"button"> & { filename?: string; }; -const AttachmentsRemove = ({ +export const AttachmentsRemove = ({ onRemove, filename, className, @@ -148,7 +158,7 @@ export type AttachmentsDropzoneProps = PrimitiveProps<"div", AttachmentsDropzone children?: ReactNode; }; -const AttachmentsDropzone = ({ +export const AttachmentsDropzone = ({ visible = false, keepMounted = false, portalSelector, @@ -184,7 +194,12 @@ export type AttachmentsErrorProps = PrimitiveProps<"span">; // role="alert" (assertive): a rejected pick/drop is a user-action failure that // should announce immediately. The copy inside stays the consumer's. -const AttachmentsError = ({ className, render, style, ...elementProps }: AttachmentsErrorProps) => +export const AttachmentsError = ({ + className, + render, + style, + ...elementProps +}: AttachmentsErrorProps) => useRenderElement( "span", { className, render, style }, @@ -193,7 +208,7 @@ const AttachmentsError = ({ className, render, style, ...elementProps }: Attachm export type AttachmentsTriggerProps = PrimitiveProps<"button">; -const AttachmentsTrigger = ({ +export const AttachmentsTrigger = ({ className, render, style, @@ -207,11 +222,3 @@ const AttachmentsTrigger = ({ props: [{ "aria-label": "Add attachment", "data-attachments-trigger": "" }, elementProps], }, ); - -export const Attachments = Object.assign(AttachmentsRoot, { - Dropzone: AttachmentsDropzone, - Item: AttachmentsItem, - Remove: AttachmentsRemove, - Error: AttachmentsError, - Trigger: AttachmentsTrigger, -}); diff --git a/packages/chat/src/attachments/index.parts.ts b/packages/chat/src/attachments/index.parts.ts new file mode 100644 index 0000000..334020c --- /dev/null +++ b/packages/chat/src/attachments/index.parts.ts @@ -0,0 +1,12 @@ +// Part names for the `Attachments` namespace. Deliberately carries no +// "use client" directive: this module and index.ts must stay server-resolvable +// so a React Server Component can reach Attachments.Root and its siblings +// through them. The directive lives one level down, on attachments.tsx. +export { + AttachmentsDropzone as Dropzone, + AttachmentsError as Error, + AttachmentsItem as Item, + AttachmentsRemove as Remove, + AttachmentsRoot as Root, + AttachmentsTrigger as Trigger, +} from "./attachments"; diff --git a/packages/chat/src/attachments/index.ts b/packages/chat/src/attachments/index.ts new file mode 100644 index 0000000..59cc00f --- /dev/null +++ b/packages/chat/src/attachments/index.ts @@ -0,0 +1,20 @@ +// Public surface of @intentface/chat/attachments. No directive — see index.parts.ts. + +export type { + AttachmentErrorCode, + AttachmentItem, + AttachmentsDropzoneProps, + AttachmentsDropzoneState, + AttachmentsErrorProps, + AttachmentsItemProps, + AttachmentsRemoveProps, + AttachmentsRootProps, + AttachmentsTriggerProps, +} from "./attachments"; +export { + matchesAccept, + revokeAllAttachmentUrls, + revokeAttachmentUrl, + toAttachmentItem, +} from "./attachments"; +export * as Attachments from "./index.parts"; diff --git a/packages/chat/src/composer/index.parts.ts b/packages/chat/src/composer/index.parts.ts new file mode 100644 index 0000000..bde4fd1 --- /dev/null +++ b/packages/chat/src/composer/index.parts.ts @@ -0,0 +1,34 @@ +// Part names for the `Composer` namespace. Deliberately carries no +// "use client" directive: this module and index.ts must stay server-resolvable +// so a React Server Component can reach Composer.Root and its siblings through +// them. Each part module carries the directive itself. +export { + ComposerActions as Actions, + ComposerContextWindow as ContextWindow, + ComposerSubmit as Submit, +} from "./actions"; +export { + ComposerAttachments as Attachments, + ComposerAttachmentTrigger as AttachmentTrigger, +} from "./attachments"; +export { + ComposerCommand as Command, + ComposerCommandDismiss as CommandDismiss, + ComposerCommandEmpty as CommandEmpty, + ComposerCommandGroup as CommandGroup, + ComposerCommandGroupLabel as CommandGroupLabel, + ComposerCommandItem as CommandItem, + ComposerCommandItemDescription as CommandItemDescription, + ComposerCommandItemIcon as CommandItemIcon, + ComposerCommandItemLabel as CommandItemLabel, + ComposerCommandList as CommandList, + ComposerCommandLoading as CommandLoading, +} from "./command-list"; +export { ComposerContainer as Container } from "./container"; +export { ComposerPanel as Panel } from "./panel"; +export { ComposerPlaceholder as Placeholder } from "./placeholder"; +export { ComposerPopover as Popover } from "./popover"; +export { ComposerRoot as Root } from "./root"; +/** Create a standalone store handle: `` + `useComposerStore(store, selector)` + `store.controller` for imperative access. */ +export { createComposerStore as createStore } from "./store"; +export { ComposerTextarea as Textarea } from "./textarea"; diff --git a/packages/chat/src/composer/index.tsx b/packages/chat/src/composer/index.ts similarity index 52% rename from packages/chat/src/composer/index.tsx rename to packages/chat/src/composer/index.ts index aa13d23..4b3dae8 100644 --- a/packages/chat/src/composer/index.tsx +++ b/packages/chat/src/composer/index.ts @@ -1,56 +1,6 @@ -"use client"; - -// Public surface of @intentface/chat/composer: the compound component plus -// the store/controller/hook building blocks. - -import { ComposerActions, ComposerContextWindow, ComposerSubmit } from "./actions"; -import { ComposerAttachments, ComposerAttachmentTrigger } from "./attachments"; -import { - ComposerCommand, - ComposerCommandDismiss, - ComposerCommandEmpty, - ComposerCommandGroup, - ComposerCommandGroupLabel, - ComposerCommandItem, - ComposerCommandItemDescription, - ComposerCommandItemIcon, - ComposerCommandItemLabel, - ComposerCommandList, - ComposerCommandLoading, -} from "./command-list"; -import { ComposerContainer } from "./container"; -import { ComposerPanel } from "./panel"; -import { ComposerPlaceholder } from "./placeholder"; -import { ComposerPopover } from "./popover"; -import { ComposerRoot } from "./root"; -import { createComposerStore } from "./store"; -import { ComposerTextarea } from "./textarea"; - -export const Composer = Object.assign(ComposerRoot, { - /** Create a standalone store handle: + useComposerStore(store, selector) + store.controller for imperative access. */ - createStore: createComposerStore, - Container: ComposerContainer, - Attachments: ComposerAttachments, - AttachmentTrigger: ComposerAttachmentTrigger, - ContextWindow: ComposerContextWindow, - Actions: ComposerActions, - Placeholder: ComposerPlaceholder, - Submit: ComposerSubmit, - Panel: ComposerPanel, - Popover: ComposerPopover, - Textarea: ComposerTextarea, - Command: ComposerCommand, - CommandList: ComposerCommandList, - CommandLoading: ComposerCommandLoading, - CommandEmpty: ComposerCommandEmpty, - CommandDismiss: ComposerCommandDismiss, - CommandItem: ComposerCommandItem, - CommandItemIcon: ComposerCommandItemIcon, - CommandItemLabel: ComposerCommandItemLabel, - CommandItemDescription: ComposerCommandItemDescription, - CommandGroup: ComposerCommandGroup, - CommandGroupLabel: ComposerCommandGroupLabel, -}); +// Public surface of @intentface/chat/composer: the `Composer` namespace plus +// the store/controller/hook building blocks. No "use client" directive here — +// see index.parts.ts. export type { ComposerSubmitState, UseComposerSubmitOptions } from "./actions"; export { useComposerSubmit } from "./actions"; @@ -60,6 +10,7 @@ export { useCommandListItems } from "./command-list"; export type { ComposerContainerProps } from "./container"; export { useComposerController } from "./controller"; export { filterArrayItems, fuzzyScore, suggestionRemainder } from "./fuzzy"; +export * as Composer from "./index.parts"; export type { ComposerSubmitOn, EditorKeyAction, EditorKeyContext } from "./keyboard"; export { interpretAskUserKey, interpretEditorKey } from "./keyboard"; export type { ComposerPanelProps } from "./panel"; diff --git a/packages/chat/src/message/index.parts.ts b/packages/chat/src/message/index.parts.ts new file mode 100644 index 0000000..8f85ff5 --- /dev/null +++ b/packages/chat/src/message/index.parts.ts @@ -0,0 +1,9 @@ +// Part names for the `Message` namespace. Deliberately carries no +// "use client" directive: this module and index.ts must stay server-resolvable +// so a React Server Component can reach Message.Root and its siblings +// through them. The directive lives one level down, on message.tsx. +export { + MessageRoot as Root, + MessageText as Text, + MessageTurn as Turn, +} from "./message"; diff --git a/packages/chat/src/message/index.ts b/packages/chat/src/message/index.ts new file mode 100644 index 0000000..e744e49 --- /dev/null +++ b/packages/chat/src/message/index.ts @@ -0,0 +1,16 @@ +// Public surface of @intentface/chat/message. No directive — see index.parts.ts. + +export * as Message from "./index.parts"; +export type { + MessageChipSegment, + MessageRootProps, + MessageSelection, + MessageState, + MessageTextProps, + MessageTurnProps, +} from "./message"; +export { + readMessageSelection, + useMessageSelection, + useMessageSelectionScope, +} from "./message"; diff --git a/packages/chat/src/message.tsx b/packages/chat/src/message/message.tsx similarity index 94% rename from packages/chat/src/message.tsx rename to packages/chat/src/message/message.tsx index ff626bc..8ed4cb0 100644 --- a/packages/chat/src/message.tsx +++ b/packages/chat/src/message/message.tsx @@ -12,9 +12,9 @@ // CSS consumers see the same state. import { Fragment, type ReactNode, useCallback, useEffect, useMemo, useState } from "react"; -import { type ChipSegment, parseChipSegments } from "./chip-markdown"; -import type { PrimitiveProps } from "./internal/primitive-props"; -import { useRenderElement } from "./internal/render/useRenderElement"; +import { type ChipSegment, parseChipSegments } from "../chip-markdown"; +import type { PrimitiveProps } from "../internal/primitive-props"; +import { useRenderElement } from "../internal/render/useRenderElement"; // --------------------------------------------------------------------------- // Root — data-attribute contract: data-message="", data-role, data-error, @@ -37,7 +37,7 @@ export type MessageRootProps = Omit, "role"> isError?: boolean; }; -const MessageRoot = ({ +export const MessageRoot = ({ role, isLast = false, isError = false, @@ -64,7 +64,7 @@ const MessageRoot = ({ export type MessageTurnProps = PrimitiveProps<"div">; -const MessageTurn = ({ className, render, style, ...elementProps }: MessageTurnProps) => +export const MessageTurn = ({ className, render, style, ...elementProps }: MessageTurnProps) => useRenderElement( "div", { className, render, style }, @@ -90,7 +90,7 @@ export type MessageTextProps = Omit, "children"> & { renderChip?: (chip: MessageChipSegment, index: number) => ReactNode; }; -const MessageText = ({ +export const MessageText = ({ children, className, render, @@ -196,8 +196,3 @@ export const useMessageSelectionScope = () => { // --------------------------------------------------------------------------- // Compound export // --------------------------------------------------------------------------- - -export const Message = Object.assign(MessageRoot, { - Turn: MessageTurn, - Text: MessageText, -}); diff --git a/packages/chat/src/reasoning/index.parts.ts b/packages/chat/src/reasoning/index.parts.ts new file mode 100644 index 0000000..97f02f3 --- /dev/null +++ b/packages/chat/src/reasoning/index.parts.ts @@ -0,0 +1,9 @@ +// Part names for the `Reasoning` namespace. Deliberately carries no +// "use client" directive: this module and index.ts must stay server-resolvable +// so a React Server Component can reach Reasoning.Root and its siblings +// through them. The directive lives one level down, on reasoning.tsx. +export { + ReasoningContent as Content, + ReasoningRoot as Root, + ReasoningTrigger as Trigger, +} from "./reasoning"; diff --git a/packages/chat/src/reasoning/index.ts b/packages/chat/src/reasoning/index.ts new file mode 100644 index 0000000..de20978 --- /dev/null +++ b/packages/chat/src/reasoning/index.ts @@ -0,0 +1,9 @@ +// Public surface of @intentface/chat/reasoning. No directive — see index.parts.ts. + +export * as Reasoning from "./index.parts"; +export type { + ReasoningContentProps, + ReasoningRootProps, + ReasoningTriggerProps, +} from "./reasoning"; +export { useReasoning } from "./reasoning"; diff --git a/packages/chat/src/reasoning.tsx b/packages/chat/src/reasoning/reasoning.tsx similarity index 90% rename from packages/chat/src/reasoning.tsx rename to packages/chat/src/reasoning/reasoning.tsx index 40999d7..6d1970b 100644 --- a/packages/chat/src/reasoning.tsx +++ b/packages/chat/src/reasoning/reasoning.tsx @@ -15,7 +15,7 @@ import { useRef, useState, } from "react"; -import { Collapsible } from "./internal/collapsible"; +import { Collapsible } from "../internal/collapsible"; const MS_IN_S = 1000; @@ -42,7 +42,7 @@ export type ReasoningRootProps = Omit, "onOpe onOpenChange?: (open: boolean) => void; }; -const ReasoningRoot = memo( +export const ReasoningRoot = memo( ({ isStreaming = false, open: controlledOpen, @@ -111,17 +111,12 @@ ReasoningRoot.displayName = "Reasoning"; export type ReasoningTriggerProps = ComponentProps; -const ReasoningTrigger = (props: ReasoningTriggerProps) => ( +export const ReasoningTrigger = (props: ReasoningTriggerProps) => ( ); export type ReasoningContentProps = ComponentProps; -const ReasoningContent = (props: ReasoningContentProps) => ( +export const ReasoningContent = (props: ReasoningContentProps) => ( ); - -export const Reasoning = Object.assign(ReasoningRoot, { - Trigger: ReasoningTrigger, - Content: ReasoningContent, -}); diff --git a/packages/chat/src/steps/index.parts.ts b/packages/chat/src/steps/index.parts.ts new file mode 100644 index 0000000..b6809e7 --- /dev/null +++ b/packages/chat/src/steps/index.parts.ts @@ -0,0 +1,13 @@ +// Part names for the `Steps` namespace. Deliberately carries no +// "use client" directive: this module and index.ts must stay server-resolvable +// so a React Server Component can reach Steps.Root and its siblings +// through them. The directive lives one level down, on steps.tsx. +export { + StepsIcon as Icon, + StepsItem as Item, + StepsLabel as Label, + StepsPanel as Panel, + StepsRoot as Root, + StepsStatus as Status, + StepsTrigger as Trigger, +} from "./steps"; diff --git a/packages/chat/src/steps/index.ts b/packages/chat/src/steps/index.ts new file mode 100644 index 0000000..a082b7a --- /dev/null +++ b/packages/chat/src/steps/index.ts @@ -0,0 +1,13 @@ +// Public surface of @intentface/chat/steps. No directive — see index.parts.ts. + +export * as Steps from "./index.parts"; +export type { + StepStatus, + StepsIconProps, + StepsItemProps, + StepsLabelProps, + StepsPanelProps, + StepsRootProps, + StepsStatusProps, + StepsTriggerProps, +} from "./steps"; diff --git a/packages/chat/src/steps.tsx b/packages/chat/src/steps/steps.tsx similarity index 86% rename from packages/chat/src/steps.tsx rename to packages/chat/src/steps/steps.tsx index d184416..4c8a269 100644 --- a/packages/chat/src/steps.tsx +++ b/packages/chat/src/steps/steps.tsx @@ -10,9 +10,9 @@ // belong to the styled layer; tool derivation belongs to the app layer. import { type ComponentProps, type CSSProperties, createContext, use } from "react"; -import { Collapsible } from "./internal/collapsible"; -import type { PrimitiveProps } from "./internal/primitive-props"; -import { useRenderElement } from "./internal/render/useRenderElement"; +import { Collapsible } from "../internal/collapsible"; +import type { PrimitiveProps } from "../internal/primitive-props"; +import { useRenderElement } from "../internal/render/useRenderElement"; // Visually-hidden-but-announced (screen-reader-only) default for Steps.Status. const visuallyHiddenStyle: CSSProperties = { @@ -34,7 +34,7 @@ const visuallyHiddenStyle: CSSProperties = { export type StepsRootProps = PrimitiveProps<"div">; -const StepsRoot = ({ className, render, style, ...elementProps }: StepsRootProps) => +export const StepsRoot = ({ className, render, style, ...elementProps }: StepsRootProps) => useRenderElement( "div", { className, render, style }, @@ -67,7 +67,7 @@ export type StepsItemProps = ComponentProps & { status?: StepStatus; }; -const StepsItem = ({ status = "complete", defaultOpen, ...props }: StepsItemProps) => { +export const StepsItem = ({ status = "complete", defaultOpen, ...props }: StepsItemProps) => { const isNested = use(NestedContext); return ( @@ -92,7 +92,7 @@ StepsItem.displayName = "StepsItem"; export type StepsTriggerProps = ComponentProps; -const StepsTrigger = (props: StepsTriggerProps) => ( +export const StepsTrigger = (props: StepsTriggerProps) => ( ); @@ -100,7 +100,9 @@ StepsTrigger.displayName = "StepsTrigger"; export type StepsPanelProps = ComponentProps; -const StepsPanel = (props: StepsPanelProps) => ; +export const StepsPanel = (props: StepsPanelProps) => ( + +); StepsPanel.displayName = "StepsPanel"; @@ -113,7 +115,13 @@ export type StepsIconProps = PrimitiveProps<"span", { status: StepStatus }> & { status?: StepStatus; }; -const StepsIcon = ({ status, className, render, style, ...elementProps }: StepsIconProps) => { +export const StepsIcon = ({ + status, + className, + render, + style, + ...elementProps +}: StepsIconProps) => { const resolvedStatus = useStepStatus(status); return useRenderElement( @@ -132,7 +140,13 @@ export type StepsLabelProps = PrimitiveProps<"span", { status: StepStatus }> & { status?: StepStatus; }; -const StepsLabel = ({ status, className, render, style, ...elementProps }: StepsLabelProps) => { +export const StepsLabel = ({ + status, + className, + render, + style, + ...elementProps +}: StepsLabelProps) => { const resolvedStatus = useStepStatus(status); return useRenderElement( @@ -157,7 +171,7 @@ export type StepsStatusProps = PrimitiveProps<"span", { status: StepStatus }> & status?: StepStatus; }; -const StepsStatus = ({ +export const StepsStatus = ({ status, children, className, @@ -189,12 +203,3 @@ StepsStatus.displayName = "StepsStatus"; // --------------------------------------------------------------------------- // Compound export // --------------------------------------------------------------------------- - -export const Steps = Object.assign(StepsRoot, { - Item: StepsItem, - Trigger: StepsTrigger, - Panel: StepsPanel, - Icon: StepsIcon, - Label: StepsLabel, - Status: StepsStatus, -}); diff --git a/packages/chat/src/thread/index.parts.ts b/packages/chat/src/thread/index.parts.ts new file mode 100644 index 0000000..abf05c8 --- /dev/null +++ b/packages/chat/src/thread/index.parts.ts @@ -0,0 +1,12 @@ +// Part names for the `Thread` namespace. Deliberately carries no +// "use client" directive: this module and index.ts must stay server-resolvable +// so a React Server Component can reach Thread.Root and its siblings +// through them. The directive lives one level down, on thread.tsx. +export { + ThreadComposer as Composer, + ThreadContent as Content, + ThreadOverlay as Overlay, + ThreadPlaceholder as Placeholder, + ThreadRoot as Root, + ThreadViewport as Viewport, +} from "./thread"; diff --git a/packages/chat/src/thread/index.ts b/packages/chat/src/thread/index.ts new file mode 100644 index 0000000..ae280ab --- /dev/null +++ b/packages/chat/src/thread/index.ts @@ -0,0 +1,16 @@ +// Public surface of @intentface/chat/thread. No directive — see index.parts.ts. + +export * as Thread from "./index.parts"; +export type { ThreadVisibilityState } from "./stores"; +export type { + ThreadAutoScrollMode, + ThreadComposerProps, + ThreadContentProps, + ThreadOverlayProps, + ThreadOverlayState, + ThreadPlaceholderProps, + ThreadRootProps, + ThreadScrollToMessageOptions, + ThreadViewportProps, +} from "./thread"; +export { useThread, useThreadVisibility } from "./thread"; diff --git a/packages/chat/src/thread/index.tsx b/packages/chat/src/thread/thread.tsx similarity index 97% rename from packages/chat/src/thread/index.tsx rename to packages/chat/src/thread/thread.tsx index b2cd19b..7899a15 100644 --- a/packages/chat/src/thread/index.tsx +++ b/packages/chat/src/thread/thread.tsx @@ -740,7 +740,7 @@ export type ThreadRootProps = PrimitiveProps<"div"> & { dockSelector?: string; }; -const ThreadRoot = ({ +export const ThreadRoot = ({ autoScroll = "follow", preserveScrollOnPrepend = false, dockSelector = DEFAULT_DOCK_SELECTOR, @@ -791,7 +791,7 @@ export type ThreadOverlayProps = PrimitiveProps<"div", ThreadOverlayState> & { direction: "top" | "bottom"; }; -const ThreadOverlay = memo( +export const ThreadOverlay = memo( ({ direction, className, render, style, ...elementProps }: ThreadOverlayProps) => useRenderElement( "div", @@ -825,7 +825,12 @@ ThreadOverlay.displayName = "ThreadOverlay"; export type ThreadViewportProps = PrimitiveProps<"div">; -const ThreadViewport = ({ className, render, style, ...elementProps }: ThreadViewportProps) => { +export const ThreadViewport = ({ + className, + render, + style, + ...elementProps +}: ThreadViewportProps) => { const { scrollRef, releaseFollow } = useThreadContext(); const lastTouchYRef = useRef(0); @@ -892,7 +897,12 @@ const ThreadViewport = ({ className, render, style, ...elementProps }: ThreadVie export type ThreadContentProps = PrimitiveProps<"div">; -const ThreadContent = ({ className, render, style, ...elementProps }: ThreadContentProps) => { +export const ThreadContent = ({ + className, + render, + style, + ...elementProps +}: ThreadContentProps) => { const { contentRef, topSentinelRef, bottomSentinelRef } = useThreadContext(); const element = useRenderElement( @@ -938,7 +948,12 @@ const ThreadContent = ({ className, render, style, ...elementProps }: ThreadCont export type ThreadComposerProps = PrimitiveProps<"div">; -const ThreadComposer = ({ className, render, style, ...elementProps }: ThreadComposerProps) => +export const ThreadComposer = ({ + className, + render, + style, + ...elementProps +}: ThreadComposerProps) => useRenderElement( "div", { className, render, style }, @@ -947,21 +962,14 @@ const ThreadComposer = ({ className, render, style, ...elementProps }: ThreadCom export type ThreadPlaceholderProps = PrimitiveProps<"div">; -const ThreadPlaceholder = ({ className, render, style, ...elementProps }: ThreadPlaceholderProps) => +export const ThreadPlaceholder = ({ + className, + render, + style, + ...elementProps +}: ThreadPlaceholderProps) => useRenderElement( "div", { className, render, style }, { props: [{ "data-thread-placeholder": "" }, elementProps] }, ); - -// --------------------------------------------------------------------------- -// Compound export -// --------------------------------------------------------------------------- - -export const Thread = Object.assign(ThreadRoot, { - Overlay: ThreadOverlay, - Viewport: ThreadViewport, - Content: ThreadContent, - Composer: ThreadComposer, - Placeholder: ThreadPlaceholder, -}); diff --git a/packages/chat/tests/a11y/ask-user.a11y.test.tsx b/packages/chat/tests/a11y/ask-user.a11y.test.tsx index d27e00b..2a76e8e 100644 --- a/packages/chat/tests/a11y/ask-user.a11y.test.tsx +++ b/packages/chat/tests/a11y/ask-user.a11y.test.tsx @@ -35,7 +35,7 @@ const AskUserHarness = () => { const selected = entry?.selected ?? new Set(); return ( - + {question.question} @@ -51,7 +51,7 @@ const AskUserHarness = () => { ))} - + ); }; @@ -69,14 +69,14 @@ describe("ask-user a11y", () => { test("focus lands in a labelled radiogroup, arrows rove, and the question is answerable by keyboard alone", async () => { const submitted: ComposerSubmitData[] = []; const { container, unmount } = render( - { submitted.push(data); }} > - , + , ); await flushFrames(); @@ -114,9 +114,9 @@ describe("ask-user a11y", () => { test("multi-select checkboxes toggle aria-checked in place with Enter and Space", async () => { const { container, unmount } = render( - {}}> + {}}> - , + , ); await flushFrames(); @@ -144,9 +144,9 @@ describe("ask-user a11y", () => { test("arrows still navigate after a chrome click drops focus to body", async () => { const { unmount } = render( - {}}> + {}}> - , + , ); await flushFrames(); @@ -168,9 +168,9 @@ describe("ask-user a11y", () => { test("navigating past the list boundary keeps focus instead of dropping to body", async () => { const { unmount } = render( - {}}> + {}}> - , + , ); await flushFrames(); diff --git a/packages/chat/tests/a11y/combobox.a11y.test.tsx b/packages/chat/tests/a11y/combobox.a11y.test.tsx index 49764fa..8fb217f 100644 --- a/packages/chat/tests/a11y/combobox.a11y.test.tsx +++ b/packages/chat/tests/a11y/combobox.a11y.test.tsx @@ -40,10 +40,10 @@ describe("composer combobox a11y", () => { test("editor wires expanded/controls/activedescendant to a listbox of options", async () => { const store = Composer.createStore(); const { container } = render( - {}} commands={COMMANDS}> + {}} commands={COMMANDS}> - , + , ); const textbox = screen.getByRole("textbox", { name: "Message" }); @@ -100,14 +100,14 @@ describe("composer combobox a11y", () => { { value: "ville", label: "Ville" }, ]; const { container } = render( - {}} commands={{ "@": { kind: "insert", trigger: "word-boundary", items: mixed } }} > - , + , ); act(() => { @@ -154,11 +154,11 @@ describe("composer combobox a11y", () => { test("two mounted commands do not fight over aria-activedescendant", () => { const store = Composer.createStore(); render( - {}} commands={COMMANDS}> + {}} commands={COMMANDS}> - , + , ); act(() => { diff --git a/packages/chat/tests/a11y/display.a11y.test.tsx b/packages/chat/tests/a11y/display.a11y.test.tsx index ead1cfa..e731377 100644 --- a/packages/chat/tests/a11y/display.a11y.test.tsx +++ b/packages/chat/tests/a11y/display.a11y.test.tsx @@ -11,7 +11,7 @@ import { expectNoAxeViolations } from "./axe"; describe("display primitives a11y", () => { test("steps: active item is aria-current and Status announces", async () => { const { container } = render( - + @@ -28,7 +28,7 @@ describe("display primitives a11y", () => { Details - , + , ); const items = container.querySelectorAll("[data-steps-item]"); @@ -54,20 +54,20 @@ describe("display primitives a11y", () => { test("reasoning: root is aria-busy while streaming", async () => { const { container, rerender } = render( - + Reasoning Thinking about it… - , + , ); const root = container.querySelector("[data-reasoning]"); expect(root?.getAttribute("aria-busy")).toBe("true"); rerender( - + Reasoning Thinking about it… - , + , ); expect(root?.getAttribute("aria-busy")).toBeNull(); diff --git a/packages/chat/tests/a11y/names.a11y.test.tsx b/packages/chat/tests/a11y/names.a11y.test.tsx index 96d905d..b0a09e9 100644 --- a/packages/chat/tests/a11y/names.a11y.test.tsx +++ b/packages/chat/tests/a11y/names.a11y.test.tsx @@ -10,7 +10,7 @@ import { expectNoAxeViolations } from "./axe"; describe("composer names a11y", () => { test("controls carry default names; the container is not a control", async () => { const { container } = render( - {}}> + {}}> @@ -20,7 +20,7 @@ describe("composer names a11y", () => { - , + , ); expect(screen.getByRole("button", { name: "Send message" })).toBeTruthy(); @@ -37,9 +37,9 @@ describe("composer names a11y", () => { test("generating submit announces as stop", () => { render( - {}}> + {}}> {}} /> - , + , ); expect(screen.getByRole("button", { name: "Stop generating" })).toBeTruthy(); cleanup(); @@ -47,13 +47,13 @@ describe("composer names a11y", () => { test("remove buttons announce per item and errors announce immediately", async () => { const { container } = render( - + {}} /> Too many files. - , + , ); expect(screen.getByRole("button", { name: "Remove report.pdf" })).toBeTruthy(); From f80dae7dc0494de87e538e572dcd92d6ccc8f977 Mon Sep 17 00:00:00 2001 From: Rasmus Viloma Date: Mon, 3 Aug 2026 19:10:15 +0300 Subject: [PATCH 4/4] docs(chat)!: document the namespace-export convention --- .changeset/rsc-namespace-exports.md | 17 +++++++++ AGENTS.md | 32 ++++++++++++++--- CHAT_ARCHITECTURE.md | 48 ++++++++++++------------- content/docs/headless/state.mdx | 10 +++--- content/docs/primitives/attachments.mdx | 10 +++--- content/docs/primitives/chip.mdx | 10 +++--- content/docs/primitives/composer.mdx | 12 +++---- content/docs/primitives/message.mdx | 12 +++---- content/docs/primitives/reasoning.mdx | 12 +++---- content/docs/primitives/steps.mdx | 6 ++-- content/docs/primitives/thread.mdx | 14 ++++---- packages/chat/README.md | 46 +++++++++++++----------- 12 files changed, 137 insertions(+), 92 deletions(-) create mode 100644 .changeset/rsc-namespace-exports.md diff --git a/.changeset/rsc-namespace-exports.md b/.changeset/rsc-namespace-exports.md new file mode 100644 index 0000000..fd906a7 --- /dev/null +++ b/.changeset/rsc-namespace-exports.md @@ -0,0 +1,17 @@ +--- +"@intentface/chat": minor +--- + +**Breaking:** primitives are now namespace exports, and the root part is explicit. `` becomes ``, `` becomes ``, and likewise for `Thread`, `Steps`, `Reasoning`, `Chip`, `Attachments` and `AskUser`. Sub-component names (`Composer.Container`, `Message.Text`, …), hooks, and type names are unchanged, so the migration is mechanical: + +```diff +- ++ + {text} +- ++ +``` + +This makes the primitives usable from React Server Components. Previously every sub-component resolved to `undefined` in a server component, failing the render with `Element type is invalid… but got: undefined` — a server component importing a `"use client"` module receives a proxy of its *named exports* and cannot read properties off an exported value, which is where `Object.assign` put them. Rendering a static transcript from a server component now works; the parts remain client components, so interactive props and hooks behave exactly as before. + +The package also now ships unbundled, one module per source file, because a bundle can carry only one top-level `"use client"` directive and that collapses the per-part boundaries the above depends on. Two incidental wins from building with tsc: declarations are inferred properly (the previous isolated-declarations emit erased every compound component to `unknown`) and the production `react/jsx-runtime` is always used (the previous build emitted the development runtime, which throws `jsxDEV is not a function` in a consumer's production build). diff --git a/AGENTS.md b/AGENTS.md index e9b9a38..0bbfb8e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,7 +17,11 @@ This file provides guidance to coding agents when working with code in this repo ### Component Architecture -**Every component in `components/ai/` and `components/ui/` must follow the compound component pattern.** Components expose sub-components as static properties via `Object.assign`, giving consumers full control over composition and layout. +Two layers, two different mechanisms for the same compound-component shape. Which one applies depends on where the component lives. + +#### App layer — `components/ai/`, `components/ui/` + +**Every component here must follow the compound component pattern**, exposing sub-components as static properties via `Object.assign`. These are single-file, copy-pasteable shadcn-style components, and one file to copy is worth more than server-component reach. They are the interactive layer; consumers render them from client components. ```tsx // Usage — consumer composes the pieces @@ -55,7 +59,25 @@ Rules for compound components: - **Accept `className`** on every sub-component for style overrides - **Convenience wrappers are fine** — a higher-level component can compose the primitives with default behavior (e.g. `Composer.Attachments` composes `Attachments`, `Attachments.Item`, `Attachments.Remove`) -This pattern is used throughout: `Message`, `Composer`, `Attachments`, `Tooltip`, `Conversation`, `Thread`, etc. +#### Package layer — `packages/chat/` + +Published primitives use **namespace exports**, not `Object.assign`, and the root is explicit: ``, never ``. Three modules per primitive: + +``` +src/message/message.tsx "use client" — MessageRoot, MessageText, … +src/message/index.parts.ts no directive — export { MessageRoot as Root, … } from "./message" +src/message/index.ts no directive — export * as Message from "./index.parts" + + flat type / hook re-exports +``` + +`Object.assign` puts sub-components on an exported *value*. A server component importing a `"use client"` module receives a proxy of its **named exports** and cannot read properties off a value, so `Message.Text` resolves to `undefined` and React throws `Element type is invalid… but got: undefined`. Named exports cross the boundary; property access does not. Namespace re-export through a directive-free layer keeps `Message.Text` statically resolvable. + +Two constraints follow, and both are load-bearing: + +- **`index.ts` and `index.parts.ts` must never carry `"use client"`.** The directive belongs on the component module one level down. Adding it to either barrel silently reintroduces the bug. +- **The build must not bundle.** One file gets one top-level directive, so bundling collapses the boundary. `tsconfig.build.json` emits per-module via tsc for exactly this reason — see the comment there before changing it. + +This pattern is used throughout: `Message`, `Composer`, `Attachments`, `Chip`, `Thread`, `Steps`, `Reasoning`, `AskUser`. ### AI Integration @@ -74,15 +96,15 @@ The chat API follows Vercel AI SDK conventions 2. Avoid `useEffect` for syncing/deriving state. Use it only for true side effects (subscriptions, DOM integrations). 3. Use standard size naming: `xs`, `sm`, `md`, `lg`, `xl`. 4. Organize CVA base classes with arrays/comments when classes are long. -5. Every component in `components/ai/` and `components/ui/` must use the compound component pattern (see Component Architecture above). +5. Every component must use the compound component pattern — via `Object.assign` in `components/ai/` and `components/ui/`, via namespace exports in `packages/chat/` (see Component Architecture above). 6. Use `cn()` from `lib/utils.ts` for className merging. 7. Follow Biome rules and formatting. 8. Use data attributes for styling and state selectors: app components (`components/ai`, `components/ui`) stamp `data-slot` / `data-role`; package primitives (`packages/chat`) emit bespoke part attributes instead (`data-composer-editor`, `data-command-badge`) — `data-slot` belongs to the consumer layer. 9. Leverage Motion for entrance/exit animations. 10. Rich text editing goes through `Composer` from `@intentface/chat/composer` — no editor framework; don't add one. 11. Follow AI SDK patterns (`useChat()`, `streamText()`, `toUIMessageStreamResponse()`). -12. No monolithic components — always decompose into composable sub-components with `Object.assign`. Consumers compose the pieces; components never hardcode their own layout. -13. Do not use index/barrel files (`index.ts` that re-exports from other files). Import directly from the specific module instead. +12. No monolithic components — always decompose into composable sub-components. Consumers compose the pieces; components never hardcode their own layout. +13. Do not use index/barrel files (`index.ts` that re-exports from other files). Import directly from the specific module instead. **Exception:** each `packages/chat/src//` has exactly two barrels — `index.parts.ts` and `index.ts` — which are required for server-component reach and must stay directive-free. ## Environment Variables diff --git a/CHAT_ARCHITECTURE.md b/CHAT_ARCHITECTURE.md index 140ccbe..4bd3c7b 100644 --- a/CHAT_ARCHITECTURE.md +++ b/CHAT_ARCHITECTURE.md @@ -36,7 +36,7 @@ useChat() aggregates parts → messages: AppUIMessage[] ↓ Chat.onFinish → useChatStore.setMessages(chatId, …) [localStorage] ↓ - renders grouped s; the panel reflects derived state + renders grouped s; the panel reflects derived state ``` ## UIMessage parts @@ -74,7 +74,7 @@ export const Composer = Object.assign(ComposerRoot, { The nesting hierarchy at a glance: ```tsx - + @@ -106,12 +106,12 @@ The nesting hierarchy at a glance: - + ``` -### Root — `` +### Root — `` -Owns the editor, attachment state, command/mention popovers, and the ask-user (questionnaire) state machine. `` is itself the provider: each mount creates and owns a store, and parts resolve it from context via `useComposer()` — there is **no `ref`**. To drive a composer from outside its tree (toolbars, shortcut handlers), create the store yourself with `Composer.createStore()`, pass it as the `store` prop, and read it with `useComposerStore(store, selector)` or imperatively through `store.controller`. +Owns the editor, attachment state, command/mention popovers, and the ask-user (questionnaire) state machine. `` is itself the provider: each mount creates and owns a store, and parts resolve it from context via `useComposer()` — there is **no `ref`**. To drive a composer from outside its tree (toolbars, shortcut handlers), create the store yourself with `Composer.createStore()`, pass it as the `store` prop, and read it with `useComposerStore(store, selector)` or imperatively through `store.controller`. ```tsx export type ComposerRootProps = Omit, "onSubmit" | "ref"> & { @@ -208,7 +208,7 @@ When in ask-user mode, swap the `Actions` row from the standard layout to `` into each. +Chips are atomic inline `contenteditable=false` spans carrying `prefix` / `label` / `value` / `icon`; a React portal renders a `` into each. #### Chip wire format @@ -286,7 +286,7 @@ export const Thread = Object.assign(ThreadRoot, { ### Skeleton ```tsx - + @@ -294,10 +294,10 @@ export const Thread = Object.assign(ThreadRoot, { - {/* goes here */} + {/* goes here */} - + ``` | Primitive | Role | @@ -330,16 +330,16 @@ export const Message = Object.assign(MessageRoot, { ```tsx - + - + - + @@ -355,7 +355,7 @@ export const Message = Object.assign(MessageRoot, { - + ``` @@ -387,10 +387,10 @@ The renderer walks a message's parts: user `text` → `Message.Text`, assistant Collapsible block for `reasoning` parts. ```tsx - + {texts} - + ``` | Piece | Notes | @@ -414,7 +414,7 @@ export const Steps = Object.assign(StepsRoot, { ### Skeleton ```tsx - + @@ -427,7 +427,7 @@ export const Steps = Object.assign(StepsRoot, { - + ``` `Steps` is a `Collapsible` that renders a chronological list of in-flight or completed work items. `Steps.Step` takes a `label` + `status` (`"complete" | "active" | "pending"`) and is a static row when it has no children, a nested collapsible when it does. @@ -560,22 +560,22 @@ const ChatSurface = ({ chatId }: { chatId: string }) => { const [tools, setTools] = useState({ webSearch: false, thinking: false }); return ( - + {groupTurns(messages).map((turn) => ( {turn.messages.map((m, i) => ( - + {/* render m.parts → Message.Text / Markdown / Steps / Sources / … */} - + ))} ))} - { if (data.kind === "answers") { if (panelState.type !== "ask-user") return; @@ -617,10 +617,10 @@ const ChatSurface = ({ chatId }: { chatId: string }) => { )} - + - + ); }; ``` diff --git a/content/docs/headless/state.mdx b/content/docs/headless/state.mdx index b1bcc85..5ab8cde 100644 --- a/content/docs/headless/state.mdx +++ b/content/docs/headless/state.mdx @@ -4,7 +4,7 @@ description: One isolated store per Composer, with explicit createStore handles --- The composer's state — editor content, command lists, ask-user flow, attachments -— lives in a store. Every `` creates its own isolated instance, so a +— lives in a store. Every `` creates its own isolated instance, so a single composer works with zero setup and several composers coexist on one page without any wiring. @@ -17,19 +17,19 @@ without any wiring. ```tsx // Zero config — an isolated instance. Two of these never interact. -{/* … */} +{/* … */} // Explicit — a handle you own, for anything that must reach the // composer from outside its tree. const composerStore = Composer.createStore(); -{/* … */} +{/* … */} ``` ## Reading state inside the tree `useComposer(selector)` subscribes to a slice of the nearest composer's store. -It throws outside a `` — inside the tree, no store is ever named: +It throws outside a `` — inside the tree, no store is ever named: ```ts const isSubmitting = useComposer((state) => state.isSubmitting); @@ -67,6 +67,6 @@ export const useChatComposer = (selector: (state: ComposerState) => T) => ``` The rule is deliberate: **explicit at the boundary, implicit within it**. Inside -``, hooks resolve by context and nothing is named; outside it, +``, hooks resolve by context and nothing is named; outside it, someone must say which composer they mean. There is no global fallback — a composer's state can never be read or driven by accident. diff --git a/content/docs/primitives/attachments.mdx b/content/docs/primitives/attachments.mdx index 9726786..ccb957f 100644 --- a/content/docs/primitives/attachments.mdx +++ b/content/docs/primitives/attachments.mdx @@ -1,7 +1,7 @@ --- title: Attachments description: The file-attachment tray — item chips, a remove affordance, and inline or global drop zones. -source: attachments.tsx +source: attachments --- @@ -17,13 +17,13 @@ source: attachments.tsx ## Anatomy ```tsx - 0}> + 0}> {items.map((item) => ( remove(item.id)} /> ))} - + ``` With a drop zone and a picker trigger: @@ -31,13 +31,13 @@ With a drop zone and a picker trigger: ```tsx <> - 0}> + 0}> {items.map((item) => ( remove(item.id)} /> ))} - + ``` diff --git a/content/docs/primitives/chip.mdx b/content/docs/primitives/chip.mdx index b6b8177..b24352e 100644 --- a/content/docs/primitives/chip.mdx +++ b/content/docs/primitives/chip.mdx @@ -1,7 +1,7 @@ --- title: Chip description: An inline, text-flowing token — used for mentions in the composer and reconstructed chips in messages. -source: chip.tsx +source: chip --- @@ -17,22 +17,22 @@ source: chip.tsx ## Anatomy ```tsx - + {icon} {label} - + ``` With a hover preview — the `Chip.Preview` child is lifted into a hover card and never rendered inline: ```tsx - + {label} - + ``` ## Accessibility diff --git a/content/docs/primitives/composer.mdx b/content/docs/primitives/composer.mdx index 33f104b..616f577 100644 --- a/content/docs/primitives/composer.mdx +++ b/content/docs/primitives/composer.mdx @@ -19,7 +19,7 @@ source: composer The bare nesting — every part is optional except `Composer` and `Container`: ```tsx - + {(composer) => composer.commands.active && ( @@ -45,7 +45,7 @@ The bare nesting — every part is optional except `Composer` and `Container`: - + ``` `Composer.Panel` takes plain children or a callback receiving the composer @@ -110,7 +110,7 @@ drive the composer from anywhere — a toolbar, a shortcut — through ## Multiple instances -Every `` creates its own isolated store, so several composers can +Every `` creates its own isolated store, so several composers can live on one page with no wiring. To reach a composer from outside its tree — toolbars, keyboard shortcuts, status bars — create an explicit handle with `Composer.createStore()`, pass it via the `store` prop, and use @@ -137,7 +137,7 @@ const ChatInput = () => { }; const ChatInputInner = memo(({ panelState, status }: ChatInputInnerProps) => ( - + )); ``` @@ -422,7 +422,7 @@ part-specific props. A surface region above the field. `children` is either plain nodes or a callback `(composer) => ReactNode` receiving the composer state, so you pick what to show -by priority (`commands.active ? : askUser.active ? : +by priority (`commands.active ? : askUser.active ? : null`). By default (`anchor`) it renders as a **collision-aware, portaled overlay** anchored to the `Container` — flipping / shifting / sizing to stay on screen (via `@floating-ui/dom`); anchor a ref/element elsewhere, or pass `anchor={false}` for an @@ -592,7 +592,7 @@ to switch continue/submit wording. ### Hooks export const hooks = [ - { name: "useComposer", type: "(selector?) => Selected", description: "Subscribe to a slice of the nearest composer's store. Throws outside ." }, + { name: "useComposer", type: "(selector?) => Selected", description: "Subscribe to a slice of the nearest composer's store. Throws outside ." }, { name: "useComposerStore", type: "(store, selector?) => Selected", description: "Same, for an explicit createStore() handle — the outside-the-tree twin." }, { name: "useComposerController", type: "() => ComposerEditorState", description: "The nearest composer's imperative editor controls (focus, insert, clear, …)." }, { name: "useComposerSubmit", type: "(options) => ComposerSubmitState", description: "Computes Submit's type/disabled/generating; auto-disables while empty or submitting; aborts on Escape." }, diff --git a/content/docs/primitives/message.mdx b/content/docs/primitives/message.mdx index c2218bc..68874c3 100644 --- a/content/docs/primitives/message.mdx +++ b/content/docs/primitives/message.mdx @@ -1,7 +1,7 @@ --- title: Message description: Renders a single chat message — role-aware bubble, markdown, chips, attachments, sources, and actions. -source: message.tsx +source: message --- @@ -19,21 +19,21 @@ source: message.tsx The bare nesting — `Message` and `Message.Content` carry the structure: ```tsx - + {text} - + ``` A realistic assistant turn with markdown, sources, and actions: ```tsx - + {content} @@ -48,7 +48,7 @@ A realistic assistant turn with markdown, sources, and actions: - + ``` @@ -64,7 +64,7 @@ the message object and derives everything inside: const ChatMessageItem = memo(({ message, isLast, isStreaming }: ChatMessageItemProps) => { const { parts } = message; // segmentation, part mapping, actions — all derived in here - return {/* … */}; + return {/* … */}; }); {messages.map((message) => ( diff --git a/content/docs/primitives/reasoning.mdx b/content/docs/primitives/reasoning.mdx index 27dd657..3ae1b73 100644 --- a/content/docs/primitives/reasoning.mdx +++ b/content/docs/primitives/reasoning.mdx @@ -1,7 +1,7 @@ --- title: Reasoning description: A collapsible "chain of thought" disclosure that tracks streaming duration and splits text into labelled sections. -source: reasoning.tsx +source: reasoning --- @@ -17,24 +17,24 @@ source: reasoning.tsx ## Anatomy ```tsx - + {reasoningText} - + ``` Driven from a streaming message, passing the reasoning parts' text: ```tsx - + {texts} - + ``` ## useReasoning -Read the disclosure state from anywhere inside ``: +Read the disclosure state from anywhere inside ``: export const hookMembers = [ { name: "isStreaming", type: "boolean", description: "Whether reasoning is still streaming in." }, diff --git a/content/docs/primitives/steps.mdx b/content/docs/primitives/steps.mdx index 52235ff..b223025 100644 --- a/content/docs/primitives/steps.mdx +++ b/content/docs/primitives/steps.mdx @@ -1,7 +1,7 @@ --- title: Steps description: A collapsible timeline of a run — reasoning, tool calls, and answered questions as chronological steps. -source: steps.tsx +source: steps --- @@ -20,7 +20,7 @@ A timeline is a top-level item whose panel holds rows; a row is an `Icon` + `Label`, and a row that expands is itself a nested `Steps.Item`: ```tsx - + Worked for 3 seconds @@ -48,7 +48,7 @@ A timeline is a top-level item whose panel holds rows; a row is an `Icon` + - + ``` ## Status diff --git a/content/docs/primitives/thread.mdx b/content/docs/primitives/thread.mdx index e470dfd..62833f8 100644 --- a/content/docs/primitives/thread.mdx +++ b/content/docs/primitives/thread.mdx @@ -1,7 +1,7 @@ --- title: Thread description: The scrolling message viewport with auto-follow, a docked composer, overlays, and a scroll-to-bottom button. -source: thread.tsx +source: thread --- @@ -19,17 +19,17 @@ source: thread.tsx The bare nesting — `Thread` provides the scroll context its parts read: ```tsx - + {/* messages */} {/* composer */} - + ``` A realistic surface with overlays and a message list: ```tsx - + {turns.map((turn) => ( @@ -37,11 +37,11 @@ A realistic surface with overlays and a message list: ))} - {/* … */} + {/* … */} - + ``` ## Auto-scroll @@ -76,7 +76,7 @@ to the bottom. ## useThread -Read the scroll state and issue commands from anywhere inside ``: +Read the scroll state and issue commands from anywhere inside ``: export const useThreadMembers = [ { name: "isAtTop", type: "boolean", description: "Whether the top sentinel is in view (start of the transcript) — pairs with load-older-history. Subscribes the caller; independent of isAtBottom." }, diff --git a/packages/chat/README.md b/packages/chat/README.md index 02e3579..9567edf 100644 --- a/packages/chat/README.md +++ b/packages/chat/README.md @@ -50,39 +50,45 @@ const turns = groupTurns(messages); // messages: UIMessage[] ## React Server Components -Every component entry point is a client module. Render the components from a -client component — a file with `"use client"` at the top: +Each primitive is a namespace, and the root is explicit — `Message.Root`, not +`Message`. Server components can reach every part: + +```tsx +// A server component — no "use client" needed. +import { Message } from "@intentface/chat/message"; + +export const Transcript = ({ messages }: { messages: ChatMessage[] }) => ( + <> + {messages.map((message) => ( + + {text(message)} + + ))} + +); +``` + +The parts are still client components — they carry their own `"use client"` — so +interactive props behave the way React requires. Pass an event handler from a +server component and you'll get React's usual error; move that piece into a +client component: ```tsx "use client"; import { Composer } from "@intentface/chat/composer"; export const Chat = () => ( - + send(data)}> - + ); ``` -Reaching a sub-component **from a server component** does not work: - -```tsx -// ❌ Server component — Composer.Container is undefined at runtime: -// "Element type is invalid… but got: undefined" - - - -``` - -This is a React limitation rather than a bug here. A server component importing -a client module receives a proxy of that module's *named exports*; it cannot -read properties off an exported value, and the compound sub-components live on -the `Composer` object itself. - +Hooks (`useComposer`, `useThread`, `useReasoning`, …) are client-only, as usual. The pure modules — `/types`, `/message-utils`, `/chip-markdown` — carry no -`"use client"` and import fine anywhere, server components included. +directive and import fine anywhere. ## Docs