@btst/stack 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/README.md +85 -37
  2. package/dist/node_modules/.pnpm/{@radix-ui_react-alert-dialog@1.1.15_@types_react-dom@19.2.2_@types_react@19.2.2__@types_ec789942cd38340bd7362c09e7d34384 → @radix-ui_react-alert-dialog@1.1.15_@types_react-dom@19.2.3_@types_react@19.2.6__@types_4f58c757aa677e233cf96d60fda2f1da}/node_modules/@radix-ui/react-alert-dialog/dist/index.cjs +2 -2
  3. package/dist/node_modules/.pnpm/{@radix-ui_react-alert-dialog@1.1.15_@types_react-dom@19.2.2_@types_react@19.2.2__@types_ec789942cd38340bd7362c09e7d34384 → @radix-ui_react-alert-dialog@1.1.15_@types_react-dom@19.2.3_@types_react@19.2.6__@types_4f58c757aa677e233cf96d60fda2f1da}/node_modules/@radix-ui/react-alert-dialog/dist/index.mjs +2 -2
  4. package/dist/node_modules/.pnpm/{@radix-ui_react-arrow@1.1.7_@types_react-dom@19.2.2_@types_react@19.2.2__@types_react@1_9e04309f365863673e44407648bb0cb6 → @radix-ui_react-arrow@1.1.7_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react@1_35df44f6d87656c1401686c91d770dbb}/node_modules/@radix-ui/react-arrow/dist/index.cjs +1 -1
  5. package/dist/node_modules/.pnpm/{@radix-ui_react-arrow@1.1.7_@types_react-dom@19.2.2_@types_react@19.2.2__@types_react@1_9e04309f365863673e44407648bb0cb6 → @radix-ui_react-arrow@1.1.7_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react@1_35df44f6d87656c1401686c91d770dbb}/node_modules/@radix-ui/react-arrow/dist/index.mjs +1 -1
  6. package/dist/node_modules/.pnpm/{@radix-ui_react-collection@1.1.7_@types_react-dom@19.2.2_@types_react@19.2.2__@types_re_6d7c277722b3619c9ee7e64e9a822c45 → @radix-ui_react-collection@1.1.7_@types_react-dom@19.2.3_@types_react@19.2.6__@types_re_59aa5e150e70a3e7bfb1567148b021b5}/node_modules/@radix-ui/react-collection/dist/index.cjs +2 -2
  7. package/dist/node_modules/.pnpm/{@radix-ui_react-collection@1.1.7_@types_react-dom@19.2.2_@types_react@19.2.2__@types_re_6d7c277722b3619c9ee7e64e9a822c45 → @radix-ui_react-collection@1.1.7_@types_react-dom@19.2.3_@types_react@19.2.6__@types_re_59aa5e150e70a3e7bfb1567148b021b5}/node_modules/@radix-ui/react-collection/dist/index.mjs +2 -2
  8. package/dist/node_modules/.pnpm/{@radix-ui_react-dismissable-layer@1.1.11_@types_react-dom@19.2.2_@types_react@19.2.2__@_ca5522e5d45d4722cb9eb5ce53defa61 → @radix-ui_react-dismissable-layer@1.1.11_@types_react-dom@19.2.3_@types_react@19.2.6__@_9ee1db7daf927866cf505b31d40047ad}/node_modules/@radix-ui/react-dismissable-layer/dist/index.cjs +4 -4
  9. package/dist/node_modules/.pnpm/{@radix-ui_react-dismissable-layer@1.1.11_@types_react-dom@19.2.2_@types_react@19.2.2__@_ca5522e5d45d4722cb9eb5ce53defa61 → @radix-ui_react-dismissable-layer@1.1.11_@types_react-dom@19.2.3_@types_react@19.2.6__@_9ee1db7daf927866cf505b31d40047ad}/node_modules/@radix-ui/react-dismissable-layer/dist/index.mjs +4 -4
  10. package/dist/node_modules/.pnpm/@radix-ui_react-dropdown-menu@2.1.16_@types_react-dom@19.2.3_@types_react@19.2.6__@type_a50051c7210b6fbd5be09388bda08578/node_modules/@radix-ui/react-dropdown-menu/dist/index.cjs +282 -0
  11. package/dist/node_modules/.pnpm/@radix-ui_react-dropdown-menu@2.1.16_@types_react-dom@19.2.3_@types_react@19.2.6__@type_a50051c7210b6fbd5be09388bda08578/node_modules/@radix-ui/react-dropdown-menu/dist/index.mjs +247 -0
  12. package/dist/node_modules/.pnpm/{@radix-ui_react-focus-scope@1.1.7_@types_react-dom@19.2.2_@types_react@19.2.2__@types_r_93de389b3f622f9f764acc8e59ec80c0 → @radix-ui_react-focus-scope@1.1.7_@types_react-dom@19.2.3_@types_react@19.2.6__@types_r_0a31b7f987af9482d13505312e1a1be9}/node_modules/@radix-ui/react-focus-scope/dist/index.cjs +3 -3
  13. package/dist/node_modules/.pnpm/{@radix-ui_react-focus-scope@1.1.7_@types_react-dom@19.2.2_@types_react@19.2.2__@types_r_93de389b3f622f9f764acc8e59ec80c0 → @radix-ui_react-focus-scope@1.1.7_@types_react-dom@19.2.3_@types_react@19.2.6__@types_r_0a31b7f987af9482d13505312e1a1be9}/node_modules/@radix-ui/react-focus-scope/dist/index.mjs +3 -3
  14. package/dist/node_modules/.pnpm/{@radix-ui_react-id@1.1.1_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-id@1.1.1_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-id/dist/index.cjs +1 -1
  15. package/dist/node_modules/.pnpm/{@radix-ui_react-id@1.1.1_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-id@1.1.1_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-id/dist/index.mjs +1 -1
  16. package/dist/node_modules/.pnpm/@radix-ui_react-menu@2.1.16_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react@1_82ed1fcd848b6984d9291a784d477cf4/node_modules/@radix-ui/react-menu/dist/index.cjs +845 -0
  17. package/dist/node_modules/.pnpm/@radix-ui_react-menu@2.1.16_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react@1_82ed1fcd848b6984d9291a784d477cf4/node_modules/@radix-ui/react-menu/dist/index.mjs +799 -0
  18. package/dist/node_modules/.pnpm/{@radix-ui_react-popper@1.2.8_@types_react-dom@19.2.2_@types_react@19.2.2__@types_react@_d6285b8269ea5d6b59b300f5be279a0c → @radix-ui_react-popper@1.2.8_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react@_7ac2caae1e39f9ba93d5015614525161}/node_modules/@radix-ui/react-popper/dist/index.cjs +7 -7
  19. package/dist/node_modules/.pnpm/{@radix-ui_react-popper@1.2.8_@types_react-dom@19.2.2_@types_react@19.2.2__@types_react@_d6285b8269ea5d6b59b300f5be279a0c → @radix-ui_react-popper@1.2.8_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react@_7ac2caae1e39f9ba93d5015614525161}/node_modules/@radix-ui/react-popper/dist/index.mjs +7 -7
  20. package/dist/node_modules/.pnpm/{@radix-ui_react-portal@1.1.9_@types_react-dom@19.2.2_@types_react@19.2.2__@types_react@_478b3d5dd4afab1a3dcce7ed1748cb95 → @radix-ui_react-portal@1.1.9_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react@_1bb4e0f97f86496802d28a2e74e2a8b9}/node_modules/@radix-ui/react-portal/dist/index.cjs +2 -2
  21. package/dist/node_modules/.pnpm/{@radix-ui_react-portal@1.1.9_@types_react-dom@19.2.2_@types_react@19.2.2__@types_react@_478b3d5dd4afab1a3dcce7ed1748cb95 → @radix-ui_react-portal@1.1.9_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react@_1bb4e0f97f86496802d28a2e74e2a8b9}/node_modules/@radix-ui/react-portal/dist/index.mjs +2 -2
  22. package/dist/node_modules/.pnpm/@radix-ui_react-presence@1.1.5_@types_react-dom@19.2.3_@types_react@19.2.6__@types_reac_90f8e5c12233caef3399d5fd66452a13/node_modules/@radix-ui/react-presence/dist/index.cjs +147 -0
  23. package/dist/node_modules/.pnpm/@radix-ui_react-presence@1.1.5_@types_react-dom@19.2.3_@types_react@19.2.6__@types_reac_90f8e5c12233caef3399d5fd66452a13/node_modules/@radix-ui/react-presence/dist/index.mjs +131 -0
  24. package/dist/node_modules/.pnpm/@radix-ui_react-roving-focus@1.1.11_@types_react-dom@19.2.3_@types_react@19.2.6__@types_fe1151d1f393bbc1072b24a86dff3a23/node_modules/@radix-ui/react-roving-focus/dist/index.cjs +244 -0
  25. package/dist/node_modules/.pnpm/@radix-ui_react-roving-focus@1.1.11_@types_react-dom@19.2.3_@types_react@19.2.6__@types_fe1151d1f393bbc1072b24a86dff3a23/node_modules/@radix-ui/react-roving-focus/dist/index.mjs +224 -0
  26. package/dist/node_modules/.pnpm/@radix-ui_react-scroll-area@1.2.10_@types_react-dom@19.2.3_@types_react@19.2.6__@types__e3f7735e9b444a10b3bbfd9fe97d44d0/node_modules/@radix-ui/react-scroll-area/dist/index.cjs +742 -0
  27. package/dist/node_modules/.pnpm/@radix-ui_react-scroll-area@1.2.10_@types_react-dom@19.2.3_@types_react@19.2.6__@types__e3f7735e9b444a10b3bbfd9fe97d44d0/node_modules/@radix-ui/react-scroll-area/dist/index.mjs +719 -0
  28. package/dist/node_modules/.pnpm/{@radix-ui_react-select@2.2.6_@types_react-dom@19.2.2_@types_react@19.2.2__@types_react@_802c3d414c85063bee785fcc98a39c07 → @radix-ui_react-select@2.2.6_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react@_38dc681bb1f2bcfeb5249d8ca2bc01f5}/node_modules/@radix-ui/react-select/dist/index.cjs +17 -17
  29. package/dist/node_modules/.pnpm/{@radix-ui_react-select@2.2.6_@types_react-dom@19.2.2_@types_react@19.2.2__@types_react@_802c3d414c85063bee785fcc98a39c07 → @radix-ui_react-select@2.2.6_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react@_38dc681bb1f2bcfeb5249d8ca2bc01f5}/node_modules/@radix-ui/react-select/dist/index.mjs +17 -17
  30. package/dist/node_modules/.pnpm/{@radix-ui_react-use-controllable-state@1.2.2_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-use-controllable-state@1.2.2_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-use-controllable-state/dist/index.cjs +1 -1
  31. package/dist/node_modules/.pnpm/{@radix-ui_react-use-controllable-state@1.2.2_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-use-controllable-state@1.2.2_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-use-controllable-state/dist/index.mjs +1 -1
  32. package/dist/node_modules/.pnpm/{@radix-ui_react-use-escape-keydown@1.1.1_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-use-escape-keydown@1.1.1_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-use-escape-keydown/dist/index.cjs +1 -1
  33. package/dist/node_modules/.pnpm/{@radix-ui_react-use-escape-keydown@1.1.1_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-use-escape-keydown@1.1.1_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-use-escape-keydown/dist/index.mjs +1 -1
  34. package/dist/node_modules/.pnpm/{@radix-ui_react-use-size@1.1.1_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-use-size@1.1.1_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-use-size/dist/index.cjs +1 -1
  35. package/dist/node_modules/.pnpm/{@radix-ui_react-use-size@1.1.1_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-use-size@1.1.1_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-use-size/dist/index.mjs +1 -1
  36. package/dist/node_modules/.pnpm/{@radix-ui_react-visually-hidden@1.2.3_@types_react-dom@19.2.2_@types_react@19.2.2__@typ_a84e98a44624c31e835a98d4b8b0c30d → @radix-ui_react-visually-hidden@1.2.3_@types_react-dom@19.2.3_@types_react@19.2.6__@typ_f453379bbd5a4932ec515167f81be42a}/node_modules/@radix-ui/react-visually-hidden/dist/index.cjs +1 -1
  37. package/dist/node_modules/.pnpm/{@radix-ui_react-visually-hidden@1.2.3_@types_react-dom@19.2.2_@types_react@19.2.2__@typ_a84e98a44624c31e835a98d4b8b0c30d → @radix-ui_react-visually-hidden@1.2.3_@types_react-dom@19.2.3_@types_react@19.2.6__@typ_f453379bbd5a4932ec515167f81be42a}/node_modules/@radix-ui/react-visually-hidden/dist/index.mjs +1 -1
  38. package/dist/node_modules/.pnpm/{react-remove-scroll-bar@2.3.8_@types_react@19.2.2_react@19.2.0 → react-remove-scroll-bar@2.3.8_@types_react@19.2.6_react@19.2.0}/node_modules/react-remove-scroll-bar/dist/es2015/component.cjs +1 -1
  39. package/dist/node_modules/.pnpm/{react-remove-scroll-bar@2.3.8_@types_react@19.2.2_react@19.2.0 → react-remove-scroll-bar@2.3.8_@types_react@19.2.6_react@19.2.0}/node_modules/react-remove-scroll-bar/dist/es2015/component.mjs +1 -1
  40. package/dist/node_modules/.pnpm/{react-remove-scroll@2.7.1_@types_react@19.2.2_react@19.2.0 → react-remove-scroll@2.7.1_@types_react@19.2.6_react@19.2.0}/node_modules/react-remove-scroll/dist/es2015/SideEffect.cjs +2 -2
  41. package/dist/node_modules/.pnpm/{react-remove-scroll@2.7.1_@types_react@19.2.2_react@19.2.0 → react-remove-scroll@2.7.1_@types_react@19.2.6_react@19.2.0}/node_modules/react-remove-scroll/dist/es2015/SideEffect.mjs +2 -2
  42. package/dist/node_modules/.pnpm/{react-remove-scroll@2.7.1_@types_react@19.2.2_react@19.2.0 → react-remove-scroll@2.7.1_@types_react@19.2.6_react@19.2.0}/node_modules/react-remove-scroll/dist/es2015/UI.cjs +2 -2
  43. package/dist/node_modules/.pnpm/{react-remove-scroll@2.7.1_@types_react@19.2.2_react@19.2.0 → react-remove-scroll@2.7.1_@types_react@19.2.6_react@19.2.0}/node_modules/react-remove-scroll/dist/es2015/UI.mjs +2 -2
  44. package/dist/node_modules/.pnpm/{react-remove-scroll@2.7.1_@types_react@19.2.2_react@19.2.0 → react-remove-scroll@2.7.1_@types_react@19.2.6_react@19.2.0}/node_modules/react-remove-scroll/dist/es2015/medium.cjs +1 -1
  45. package/dist/node_modules/.pnpm/{react-remove-scroll@2.7.1_@types_react@19.2.2_react@19.2.0 → react-remove-scroll@2.7.1_@types_react@19.2.6_react@19.2.0}/node_modules/react-remove-scroll/dist/es2015/medium.mjs +1 -1
  46. package/dist/node_modules/.pnpm/react-remove-scroll@2.7.1_@types_react@19.2.6_react@19.2.0/node_modules/react-remove-scroll/dist/es2015/sidecar.cjs +9 -0
  47. package/dist/node_modules/.pnpm/{react-remove-scroll@2.7.1_@types_react@19.2.2_react@19.2.0 → react-remove-scroll@2.7.1_@types_react@19.2.6_react@19.2.0}/node_modules/react-remove-scroll/dist/es2015/sidecar.mjs +1 -1
  48. package/dist/packages/better-stack/src/plugins/ai-chat/api/plugin.cjs +610 -0
  49. package/dist/packages/better-stack/src/plugins/ai-chat/api/plugin.mjs +608 -0
  50. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/chat-input.cjs +221 -0
  51. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/chat-input.mjs +219 -0
  52. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/chat-interface.cjs +315 -0
  53. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/chat-interface.mjs +313 -0
  54. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/chat-layout.cjs +122 -0
  55. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/chat-layout.mjs +120 -0
  56. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/chat-message.cjs +397 -0
  57. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/chat-message.mjs +391 -0
  58. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/chat-sidebar.cjs +227 -0
  59. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/chat-sidebar.mjs +225 -0
  60. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/loading/chat-page-skeleton.cjs +31 -0
  61. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/loading/chat-page-skeleton.mjs +29 -0
  62. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/loading/index.cjs +11 -0
  63. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/loading/index.mjs +8 -0
  64. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/pages/404-page.cjs +18 -0
  65. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/pages/404-page.mjs +16 -0
  66. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/pages/chat-page.cjs +39 -0
  67. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/pages/chat-page.internal.cjs +22 -0
  68. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/pages/chat-page.internal.mjs +20 -0
  69. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/pages/chat-page.mjs +37 -0
  70. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/shared/default-error.cjs +18 -0
  71. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/shared/default-error.mjs +16 -0
  72. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/shared/error-placeholder.cjs +26 -0
  73. package/dist/packages/better-stack/src/plugins/ai-chat/client/components/shared/error-placeholder.mjs +24 -0
  74. package/dist/packages/better-stack/src/plugins/ai-chat/client/hooks/chat-hooks.cjs +199 -0
  75. package/dist/packages/better-stack/src/plugins/ai-chat/client/hooks/chat-hooks.mjs +191 -0
  76. package/dist/packages/better-stack/src/plugins/ai-chat/client/localization/index.cjs +63 -0
  77. package/dist/packages/better-stack/src/plugins/ai-chat/client/localization/index.mjs +61 -0
  78. package/dist/packages/better-stack/src/plugins/ai-chat/client/overrides.cjs +14 -0
  79. package/dist/packages/better-stack/src/plugins/ai-chat/client/overrides.mjs +11 -0
  80. package/dist/packages/better-stack/src/plugins/ai-chat/client/plugin.cjs +241 -0
  81. package/dist/packages/better-stack/src/plugins/ai-chat/client/plugin.mjs +239 -0
  82. package/dist/packages/better-stack/src/plugins/ai-chat/db.cjs +65 -0
  83. package/dist/packages/better-stack/src/plugins/ai-chat/db.mjs +63 -0
  84. package/dist/packages/better-stack/src/plugins/ai-chat/schemas.cjs +42 -0
  85. package/dist/packages/better-stack/src/plugins/ai-chat/schemas.mjs +38 -0
  86. package/dist/packages/better-stack/src/plugins/blog/api/plugin.cjs +8 -2
  87. package/dist/packages/better-stack/src/plugins/blog/api/plugin.mjs +8 -2
  88. package/dist/packages/better-stack/src/plugins/blog/client/components/pages/post-page.internal.cjs +1 -1
  89. package/dist/packages/better-stack/src/plugins/blog/client/components/pages/post-page.internal.mjs +1 -1
  90. package/dist/packages/better-stack/src/plugins/blog/client/components/shared/markdown-content.cjs +12 -309
  91. package/dist/packages/better-stack/src/plugins/blog/client/components/shared/markdown-content.mjs +13 -303
  92. package/dist/packages/better-stack/src/plugins/blog/client/components/shared/on-this-page.cjs +1 -1
  93. package/dist/packages/better-stack/src/plugins/blog/client/components/shared/on-this-page.mjs +1 -1
  94. package/dist/packages/better-stack/src/plugins/blog/client/components/shared/page-wrapper.cjs +2 -2
  95. package/dist/packages/better-stack/src/plugins/blog/client/components/shared/page-wrapper.mjs +2 -2
  96. package/dist/packages/better-stack/src/plugins/blog/client/components/shared/recent-posts-carousel.cjs +2 -2
  97. package/dist/packages/better-stack/src/plugins/blog/client/components/shared/recent-posts-carousel.mjs +2 -2
  98. package/dist/packages/ui/src/components/alert-dialog.cjs +1 -1
  99. package/dist/packages/ui/src/components/alert-dialog.mjs +1 -1
  100. package/dist/packages/{better-stack/src/plugins/blog/client/components/shared/better-blog-attribution.cjs → ui/src/components/better-stack-attribution.cjs} +3 -3
  101. package/dist/packages/{better-stack/src/plugins/blog/client/components/shared/better-blog-attribution.mjs → ui/src/components/better-stack-attribution.mjs} +3 -3
  102. package/dist/packages/ui/src/components/dialog.cjs +14 -0
  103. package/dist/packages/ui/src/components/dialog.mjs +14 -1
  104. package/dist/packages/ui/src/components/dropdown-menu.cjs +67 -0
  105. package/dist/packages/ui/src/components/dropdown-menu.mjs +62 -0
  106. package/dist/packages/ui/src/components/markdown-content.cjs +306 -0
  107. package/dist/packages/ui/src/components/markdown-content.mjs +297 -0
  108. package/dist/packages/ui/src/components/scroll-area.cjs +63 -0
  109. package/dist/packages/ui/src/components/scroll-area.mjs +60 -0
  110. package/dist/packages/ui/src/components/select.cjs +1 -1
  111. package/dist/packages/ui/src/components/select.mjs +1 -1
  112. package/dist/packages/ui/src/components/sheet.cjs +87 -0
  113. package/dist/packages/ui/src/components/sheet.mjs +69 -0
  114. package/dist/plugins/ai-chat/api/index.cjs +9 -0
  115. package/dist/plugins/ai-chat/api/index.d.cts +9 -0
  116. package/dist/plugins/ai-chat/api/index.d.mts +9 -0
  117. package/dist/plugins/ai-chat/api/index.d.ts +9 -0
  118. package/dist/plugins/ai-chat/api/index.mjs +2 -0
  119. package/dist/plugins/ai-chat/client/components/index.cjs +27 -0
  120. package/dist/plugins/ai-chat/client/components/index.d.cts +30 -0
  121. package/dist/plugins/ai-chat/client/components/index.d.mts +30 -0
  122. package/dist/plugins/ai-chat/client/components/index.d.ts +30 -0
  123. package/dist/plugins/ai-chat/client/components/index.mjs +11 -0
  124. package/dist/plugins/ai-chat/client/hooks/index.cjs +13 -0
  125. package/dist/plugins/ai-chat/client/hooks/index.d.cts +98 -0
  126. package/dist/plugins/ai-chat/client/hooks/index.d.mts +98 -0
  127. package/dist/plugins/ai-chat/client/hooks/index.d.ts +98 -0
  128. package/dist/plugins/ai-chat/client/hooks/index.mjs +1 -0
  129. package/dist/plugins/ai-chat/client/index.cjs +19 -0
  130. package/dist/plugins/ai-chat/client/index.d.cts +327 -0
  131. package/dist/plugins/ai-chat/client/index.d.mts +327 -0
  132. package/dist/plugins/ai-chat/client/index.d.ts +327 -0
  133. package/dist/plugins/ai-chat/client/index.mjs +7 -0
  134. package/dist/plugins/ai-chat/client.css +6 -0
  135. package/dist/plugins/ai-chat/query-keys.cjs +60 -0
  136. package/dist/plugins/ai-chat/query-keys.d.cts +478 -0
  137. package/dist/plugins/ai-chat/query-keys.d.mts +478 -0
  138. package/dist/plugins/ai-chat/query-keys.d.ts +478 -0
  139. package/dist/plugins/ai-chat/query-keys.mjs +58 -0
  140. package/dist/plugins/ai-chat/style.css +19 -0
  141. package/dist/plugins/blog/api/index.d.cts +1 -1
  142. package/dist/plugins/blog/api/index.d.mts +1 -1
  143. package/dist/plugins/blog/api/index.d.ts +1 -1
  144. package/dist/plugins/blog/client/components/shared/markdown-content-styles.css +85 -62
  145. package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
  146. package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
  147. package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
  148. package/dist/plugins/blog/client/index.d.cts +1 -1
  149. package/dist/plugins/blog/client/index.d.mts +1 -1
  150. package/dist/plugins/blog/client/index.d.ts +1 -1
  151. package/dist/plugins/blog/query-keys.d.cts +2 -2
  152. package/dist/plugins/blog/query-keys.d.mts +2 -2
  153. package/dist/plugins/blog/query-keys.d.ts +2 -2
  154. package/dist/shared/stack.Be1QIHEn.d.cts +23 -0
  155. package/dist/shared/stack.Be1QIHEn.d.mts +23 -0
  156. package/dist/shared/stack.Be1QIHEn.d.ts +23 -0
  157. package/dist/shared/stack.DorMi9CZ.d.cts +80 -0
  158. package/dist/shared/stack.DorMi9CZ.d.mts +80 -0
  159. package/dist/shared/stack.DorMi9CZ.d.ts +80 -0
  160. package/package.json +61 -3
  161. package/src/plugins/ai-chat/api/index.ts +2 -0
  162. package/src/plugins/ai-chat/api/plugin.ts +1083 -0
  163. package/src/plugins/ai-chat/client/components/chat-input.tsx +295 -0
  164. package/src/plugins/ai-chat/client/components/chat-interface.tsx +455 -0
  165. package/src/plugins/ai-chat/client/components/chat-layout.tsx +160 -0
  166. package/src/plugins/ai-chat/client/components/chat-message.tsx +505 -0
  167. package/src/plugins/ai-chat/client/components/chat-sidebar.tsx +296 -0
  168. package/src/plugins/ai-chat/client/components/index.ts +16 -0
  169. package/src/plugins/ai-chat/client/components/loading/chat-page-skeleton.tsx +57 -0
  170. package/src/plugins/ai-chat/client/components/loading/index.tsx +11 -0
  171. package/src/plugins/ai-chat/client/components/pages/404-page.tsx +27 -0
  172. package/src/plugins/ai-chat/client/components/pages/chat-page.internal.tsx +31 -0
  173. package/src/plugins/ai-chat/client/components/pages/chat-page.tsx +46 -0
  174. package/src/plugins/ai-chat/client/components/shared/default-error.tsx +28 -0
  175. package/src/plugins/ai-chat/client/components/shared/error-placeholder.tsx +22 -0
  176. package/src/plugins/ai-chat/client/hooks/chat-hooks.tsx +349 -0
  177. package/src/plugins/ai-chat/client/hooks/index.tsx +1 -0
  178. package/src/plugins/ai-chat/client/index.ts +14 -0
  179. package/src/plugins/ai-chat/client/localization/index.ts +156 -0
  180. package/src/plugins/ai-chat/client/overrides.ts +170 -0
  181. package/src/plugins/ai-chat/client/plugin.tsx +449 -0
  182. package/src/plugins/ai-chat/client.css +6 -0
  183. package/src/plugins/ai-chat/db.ts +65 -0
  184. package/src/plugins/ai-chat/query-keys.ts +87 -0
  185. package/src/plugins/ai-chat/schemas.ts +40 -0
  186. package/src/plugins/ai-chat/style.css +19 -0
  187. package/src/plugins/ai-chat/types.ts +29 -0
  188. package/src/plugins/blog/api/plugin.ts +10 -4
  189. package/src/plugins/blog/client/components/pages/post-page.internal.tsx +1 -1
  190. package/src/plugins/blog/client/components/shared/markdown-content-styles.css +85 -62
  191. package/src/plugins/blog/client/components/shared/markdown-content.tsx +19 -427
  192. package/src/plugins/blog/client/components/shared/on-this-page.tsx +1 -1
  193. package/src/plugins/blog/client/components/shared/page-wrapper.tsx +2 -2
  194. package/src/plugins/blog/client/components/shared/recent-posts-carousel.tsx +2 -2
  195. package/dist/node_modules/.pnpm/react-remove-scroll@2.7.1_@types_react@19.2.2_react@19.2.0/node_modules/react-remove-scroll/dist/es2015/sidecar.cjs +0 -9
  196. package/src/plugins/blog/client/components/shared/better-blog-attribution.tsx +0 -19
  197. package/dist/node_modules/.pnpm/{@radix-ui_react-compose-refs@1.1.2_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-compose-refs@1.1.2_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-compose-refs/dist/index.cjs +0 -0
  198. package/dist/node_modules/.pnpm/{@radix-ui_react-compose-refs@1.1.2_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-compose-refs@1.1.2_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-compose-refs/dist/index.mjs +0 -0
  199. package/dist/node_modules/.pnpm/{@radix-ui_react-context@1.1.2_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-context@1.1.2_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-context/dist/index.cjs +0 -0
  200. package/dist/node_modules/.pnpm/{@radix-ui_react-context@1.1.2_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-context@1.1.2_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-context/dist/index.mjs +0 -0
  201. package/dist/node_modules/.pnpm/{@radix-ui_react-direction@1.1.1_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-direction@1.1.1_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-direction/dist/index.cjs +0 -0
  202. package/dist/node_modules/.pnpm/{@radix-ui_react-direction@1.1.1_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-direction@1.1.1_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-direction/dist/index.mjs +0 -0
  203. package/dist/node_modules/.pnpm/{@radix-ui_react-focus-guards@1.1.3_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-focus-guards@1.1.3_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-focus-guards/dist/index.cjs +0 -0
  204. package/dist/node_modules/.pnpm/{@radix-ui_react-focus-guards@1.1.3_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-focus-guards@1.1.3_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-focus-guards/dist/index.mjs +0 -0
  205. package/dist/node_modules/.pnpm/{@radix-ui_react-primitive@2.1.3_@types_react-dom@19.2.2_@types_react@19.2.2__@types_rea_bdc15f10281778271ffcbe8dd3cd491e → @radix-ui_react-primitive@2.1.3_@types_react-dom@19.2.3_@types_react@19.2.6__@types_rea_a92a69cb1cb39305138539e4fa72f596}/node_modules/@radix-ui/react-primitive/dist/index.cjs +0 -0
  206. package/dist/node_modules/.pnpm/{@radix-ui_react-primitive@2.1.3_@types_react-dom@19.2.2_@types_react@19.2.2__@types_rea_bdc15f10281778271ffcbe8dd3cd491e → @radix-ui_react-primitive@2.1.3_@types_react-dom@19.2.3_@types_react@19.2.6__@types_rea_a92a69cb1cb39305138539e4fa72f596}/node_modules/@radix-ui/react-primitive/dist/index.mjs +0 -0
  207. package/dist/node_modules/.pnpm/{@radix-ui_react-use-callback-ref@1.1.1_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-use-callback-ref@1.1.1_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-use-callback-ref/dist/index.cjs +0 -0
  208. package/dist/node_modules/.pnpm/{@radix-ui_react-use-callback-ref@1.1.1_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-use-callback-ref@1.1.1_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-use-callback-ref/dist/index.mjs +0 -0
  209. package/dist/node_modules/.pnpm/{@radix-ui_react-use-layout-effect@1.1.1_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-use-layout-effect@1.1.1_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-use-layout-effect/dist/index.cjs +0 -0
  210. package/dist/node_modules/.pnpm/{@radix-ui_react-use-layout-effect@1.1.1_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-use-layout-effect@1.1.1_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-use-layout-effect/dist/index.mjs +0 -0
  211. package/dist/node_modules/.pnpm/{@radix-ui_react-use-previous@1.1.1_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-use-previous@1.1.1_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-use-previous/dist/index.cjs +0 -0
  212. package/dist/node_modules/.pnpm/{@radix-ui_react-use-previous@1.1.1_@types_react@19.2.2_react@19.2.0 → @radix-ui_react-use-previous@1.1.1_@types_react@19.2.6_react@19.2.0}/node_modules/@radix-ui/react-use-previous/dist/index.mjs +0 -0
  213. package/dist/node_modules/.pnpm/{react-remove-scroll-bar@2.3.8_@types_react@19.2.2_react@19.2.0 → react-remove-scroll-bar@2.3.8_@types_react@19.2.6_react@19.2.0}/node_modules/react-remove-scroll-bar/dist/es2015/constants.cjs +0 -0
  214. package/dist/node_modules/.pnpm/{react-remove-scroll-bar@2.3.8_@types_react@19.2.2_react@19.2.0 → react-remove-scroll-bar@2.3.8_@types_react@19.2.6_react@19.2.0}/node_modules/react-remove-scroll-bar/dist/es2015/constants.mjs +0 -0
  215. package/dist/node_modules/.pnpm/{react-remove-scroll-bar@2.3.8_@types_react@19.2.2_react@19.2.0 → react-remove-scroll-bar@2.3.8_@types_react@19.2.6_react@19.2.0}/node_modules/react-remove-scroll-bar/dist/es2015/utils.cjs +0 -0
  216. package/dist/node_modules/.pnpm/{react-remove-scroll-bar@2.3.8_@types_react@19.2.2_react@19.2.0 → react-remove-scroll-bar@2.3.8_@types_react@19.2.6_react@19.2.0}/node_modules/react-remove-scroll-bar/dist/es2015/utils.mjs +0 -0
  217. package/dist/node_modules/.pnpm/{react-remove-scroll@2.7.1_@types_react@19.2.2_react@19.2.0 → react-remove-scroll@2.7.1_@types_react@19.2.6_react@19.2.0}/node_modules/react-remove-scroll/dist/es2015/Combination.cjs +0 -0
  218. package/dist/node_modules/.pnpm/{react-remove-scroll@2.7.1_@types_react@19.2.2_react@19.2.0 → react-remove-scroll@2.7.1_@types_react@19.2.6_react@19.2.0}/node_modules/react-remove-scroll/dist/es2015/Combination.mjs +0 -0
  219. package/dist/node_modules/.pnpm/{react-remove-scroll@2.7.1_@types_react@19.2.2_react@19.2.0 → react-remove-scroll@2.7.1_@types_react@19.2.6_react@19.2.0}/node_modules/react-remove-scroll/dist/es2015/aggresiveCapture.cjs +0 -0
  220. package/dist/node_modules/.pnpm/{react-remove-scroll@2.7.1_@types_react@19.2.2_react@19.2.0 → react-remove-scroll@2.7.1_@types_react@19.2.6_react@19.2.0}/node_modules/react-remove-scroll/dist/es2015/aggresiveCapture.mjs +0 -0
  221. package/dist/node_modules/.pnpm/{react-remove-scroll@2.7.1_@types_react@19.2.2_react@19.2.0 → react-remove-scroll@2.7.1_@types_react@19.2.6_react@19.2.0}/node_modules/react-remove-scroll/dist/es2015/handleScroll.cjs +0 -0
  222. package/dist/node_modules/.pnpm/{react-remove-scroll@2.7.1_@types_react@19.2.2_react@19.2.0 → react-remove-scroll@2.7.1_@types_react@19.2.6_react@19.2.0}/node_modules/react-remove-scroll/dist/es2015/handleScroll.mjs +0 -0
  223. package/dist/node_modules/.pnpm/{react-style-singleton@2.2.3_@types_react@19.2.2_react@19.2.0 → react-style-singleton@2.2.3_@types_react@19.2.6_react@19.2.0}/node_modules/react-style-singleton/dist/es2015/component.cjs +0 -0
  224. package/dist/node_modules/.pnpm/{react-style-singleton@2.2.3_@types_react@19.2.2_react@19.2.0 → react-style-singleton@2.2.3_@types_react@19.2.6_react@19.2.0}/node_modules/react-style-singleton/dist/es2015/component.mjs +0 -0
  225. package/dist/node_modules/.pnpm/{react-style-singleton@2.2.3_@types_react@19.2.2_react@19.2.0 → react-style-singleton@2.2.3_@types_react@19.2.6_react@19.2.0}/node_modules/react-style-singleton/dist/es2015/hook.cjs +0 -0
  226. package/dist/node_modules/.pnpm/{react-style-singleton@2.2.3_@types_react@19.2.2_react@19.2.0 → react-style-singleton@2.2.3_@types_react@19.2.6_react@19.2.0}/node_modules/react-style-singleton/dist/es2015/hook.mjs +0 -0
  227. package/dist/node_modules/.pnpm/{react-style-singleton@2.2.3_@types_react@19.2.2_react@19.2.0 → react-style-singleton@2.2.3_@types_react@19.2.6_react@19.2.0}/node_modules/react-style-singleton/dist/es2015/singleton.cjs +0 -0
  228. package/dist/node_modules/.pnpm/{react-style-singleton@2.2.3_@types_react@19.2.2_react@19.2.0 → react-style-singleton@2.2.3_@types_react@19.2.6_react@19.2.0}/node_modules/react-style-singleton/dist/es2015/singleton.mjs +0 -0
  229. package/dist/node_modules/.pnpm/{use-callback-ref@1.3.3_@types_react@19.2.2_react@19.2.0 → use-callback-ref@1.3.3_@types_react@19.2.6_react@19.2.0}/node_modules/use-callback-ref/dist/es2015/assignRef.cjs +0 -0
  230. package/dist/node_modules/.pnpm/{use-callback-ref@1.3.3_@types_react@19.2.2_react@19.2.0 → use-callback-ref@1.3.3_@types_react@19.2.6_react@19.2.0}/node_modules/use-callback-ref/dist/es2015/assignRef.mjs +0 -0
  231. package/dist/node_modules/.pnpm/{use-callback-ref@1.3.3_@types_react@19.2.2_react@19.2.0 → use-callback-ref@1.3.3_@types_react@19.2.6_react@19.2.0}/node_modules/use-callback-ref/dist/es2015/useMergeRef.cjs +0 -0
  232. package/dist/node_modules/.pnpm/{use-callback-ref@1.3.3_@types_react@19.2.2_react@19.2.0 → use-callback-ref@1.3.3_@types_react@19.2.6_react@19.2.0}/node_modules/use-callback-ref/dist/es2015/useMergeRef.mjs +0 -0
  233. package/dist/node_modules/.pnpm/{use-callback-ref@1.3.3_@types_react@19.2.2_react@19.2.0 → use-callback-ref@1.3.3_@types_react@19.2.6_react@19.2.0}/node_modules/use-callback-ref/dist/es2015/useRef.cjs +0 -0
  234. package/dist/node_modules/.pnpm/{use-callback-ref@1.3.3_@types_react@19.2.2_react@19.2.0 → use-callback-ref@1.3.3_@types_react@19.2.6_react@19.2.0}/node_modules/use-callback-ref/dist/es2015/useRef.mjs +0 -0
  235. package/dist/node_modules/.pnpm/{use-sidecar@1.1.3_@types_react@19.2.2_react@19.2.0 → use-sidecar@1.1.3_@types_react@19.2.6_react@19.2.0}/node_modules/use-sidecar/dist/es2015/exports.cjs +0 -0
  236. package/dist/node_modules/.pnpm/{use-sidecar@1.1.3_@types_react@19.2.2_react@19.2.0 → use-sidecar@1.1.3_@types_react@19.2.6_react@19.2.0}/node_modules/use-sidecar/dist/es2015/exports.mjs +0 -0
  237. package/dist/node_modules/.pnpm/{use-sidecar@1.1.3_@types_react@19.2.2_react@19.2.0 → use-sidecar@1.1.3_@types_react@19.2.6_react@19.2.0}/node_modules/use-sidecar/dist/es2015/medium.cjs +0 -0
  238. package/dist/node_modules/.pnpm/{use-sidecar@1.1.3_@types_react@19.2.2_react@19.2.0 → use-sidecar@1.1.3_@types_react@19.2.6_react@19.2.0}/node_modules/use-sidecar/dist/es2015/medium.mjs +0 -0
  239. package/dist/shared/{stack.DLhzx1-D.d.ts → stack.CcI4sYJP.d.cts} +1 -1
  240. package/dist/shared/{stack.DLhzx1-D.d.cts → stack.CcI4sYJP.d.mts} +1 -1
  241. package/dist/shared/{stack.DLhzx1-D.d.mts → stack.CcI4sYJP.d.ts} +1 -1
@@ -0,0 +1,449 @@
1
+ import {
2
+ defineClientPlugin,
3
+ createApiClient,
4
+ } from "@btst/stack/plugins/client";
5
+ import { createRoute } from "@btst/yar";
6
+ import type { QueryClient } from "@tanstack/react-query";
7
+ import type { AiChatApiRouter } from "../api";
8
+ import { createAiChatQueryKeys } from "../query-keys";
9
+ import type { SerializedConversation, SerializedMessage } from "../types";
10
+ import { ChatLayout } from "./components/chat-layout";
11
+ import type { AiChatMode } from "./overrides";
12
+
13
+ /**
14
+ * Context passed to route hooks
15
+ */
16
+ export interface RouteContext {
17
+ /** Current route path */
18
+ path: string;
19
+ /** Route parameters (e.g., { id: "abc123" }) */
20
+ params?: Record<string, string>;
21
+ /** Whether rendering on server (true) or client (false) */
22
+ isSSR: boolean;
23
+ /** Additional context properties */
24
+ [key: string]: any;
25
+ }
26
+
27
+ /**
28
+ * Context passed to loader hooks
29
+ */
30
+ export interface LoaderContext {
31
+ /** Current route path */
32
+ path: string;
33
+ /** Route parameters (e.g., { id: "abc123" }) */
34
+ params?: Record<string, string>;
35
+ /** Whether rendering on server (true) or client (false) */
36
+ isSSR: boolean;
37
+ /** Base URL for API calls */
38
+ apiBaseURL: string;
39
+ /** Path where the API is mounted */
40
+ apiBasePath: string;
41
+ /** Optional headers for the request */
42
+ headers?: Headers;
43
+ /** Additional context properties */
44
+ [key: string]: any;
45
+ }
46
+
47
+ /**
48
+ * Configuration for AI Chat client plugin
49
+ */
50
+ export interface AiChatClientConfig {
51
+ /** Base URL for API calls (e.g., "http://localhost:3000") */
52
+ apiBaseURL: string;
53
+ /** Path where the API is mounted (e.g., "/api/data") */
54
+ apiBasePath: string;
55
+ /** Base URL of your site for SEO meta tags */
56
+ siteBaseURL: string;
57
+ /** Path where pages are mounted (e.g., "/pages") */
58
+ siteBasePath: string;
59
+ /** React Query client instance for caching */
60
+ queryClient: QueryClient;
61
+
62
+ /**
63
+ * Plugin mode - should match backend config
64
+ * - 'authenticated': Full chat with conversation history (default)
65
+ * - 'public': Simple widget mode, no persistence, no sidebar
66
+ * @default 'authenticated'
67
+ */
68
+ mode?: AiChatMode;
69
+
70
+ /** Optional SEO configuration for meta tags */
71
+ seo?: {
72
+ /** Site name for Open Graph tags */
73
+ siteName?: string;
74
+ /** Default description */
75
+ description?: string;
76
+ /** Locale for Open Graph (e.g., "en_US") */
77
+ locale?: string;
78
+ /** Default image URL for social sharing */
79
+ defaultImage?: string;
80
+ };
81
+
82
+ /** Optional hooks for customizing behavior */
83
+ hooks?: AiChatClientHooks;
84
+
85
+ /** Optional headers for SSR (e.g., forwarding cookies) */
86
+ headers?: Headers;
87
+ }
88
+
89
+ /**
90
+ * Hooks for AI Chat client plugin
91
+ * All hooks are optional and allow consumers to customize behavior
92
+ */
93
+ export interface AiChatClientHooks {
94
+ /**
95
+ * Called before loading conversations list. Return false to cancel loading.
96
+ * @param context - Loader context with path, params, etc.
97
+ */
98
+ beforeLoadConversations?: (
99
+ context: LoaderContext,
100
+ ) => Promise<boolean> | boolean;
101
+
102
+ /**
103
+ * Called after conversations are loaded. Return false to cancel further processing.
104
+ * @param conversations - Array of loaded conversations or null
105
+ * @param context - Loader context
106
+ */
107
+ afterLoadConversations?: (
108
+ conversations: SerializedConversation[] | null,
109
+ context: LoaderContext,
110
+ ) => Promise<boolean> | boolean;
111
+
112
+ /**
113
+ * Called before loading a single conversation. Return false to cancel loading.
114
+ * @param id - Conversation ID being loaded
115
+ * @param context - Loader context
116
+ */
117
+ beforeLoadConversation?: (
118
+ id: string,
119
+ context: LoaderContext,
120
+ ) => Promise<boolean> | boolean;
121
+
122
+ /**
123
+ * Called after a conversation is loaded. Return false to cancel further processing.
124
+ * @param conversation - Loaded conversation or null if not found
125
+ * @param id - Conversation ID that was requested
126
+ * @param context - Loader context
127
+ */
128
+ afterLoadConversation?: (
129
+ conversation:
130
+ | (SerializedConversation & { messages: SerializedMessage[] })
131
+ | null,
132
+ id: string,
133
+ context: LoaderContext,
134
+ ) => Promise<boolean> | boolean;
135
+
136
+ /**
137
+ * Called when a loading error occurs
138
+ * @param error - The error that occurred
139
+ * @param context - Loader context
140
+ */
141
+ onLoadError?: (error: Error, context: LoaderContext) => Promise<void> | void;
142
+ }
143
+
144
+ // Loader for chat home page (list conversations)
145
+ function createConversationsLoader(config: AiChatClientConfig) {
146
+ return async () => {
147
+ // Skip loading in public mode - no persistence
148
+ if (config.mode === "public") {
149
+ return;
150
+ }
151
+
152
+ if (typeof window === "undefined") {
153
+ const { queryClient, apiBasePath, apiBaseURL, hooks, headers } = config;
154
+
155
+ const context: LoaderContext = {
156
+ path: "/chat",
157
+ isSSR: true,
158
+ apiBaseURL,
159
+ apiBasePath,
160
+ headers,
161
+ };
162
+
163
+ try {
164
+ // Before hook
165
+ if (hooks?.beforeLoadConversations) {
166
+ const canLoad = await hooks.beforeLoadConversations(context);
167
+ if (!canLoad) {
168
+ throw new Error("Load prevented by beforeLoadConversations hook");
169
+ }
170
+ }
171
+
172
+ const client = createApiClient<AiChatApiRouter>({
173
+ baseURL: apiBaseURL,
174
+ basePath: apiBasePath,
175
+ });
176
+
177
+ const queries = createAiChatQueryKeys(client, headers);
178
+ const listQuery = queries.conversations.list();
179
+
180
+ await queryClient.prefetchQuery(listQuery);
181
+
182
+ // After hook
183
+ if (hooks?.afterLoadConversations) {
184
+ const conversations =
185
+ queryClient.getQueryData<SerializedConversation[]>(
186
+ listQuery.queryKey,
187
+ ) || null;
188
+ const canContinue = await hooks.afterLoadConversations(
189
+ conversations,
190
+ context,
191
+ );
192
+ if (canContinue === false) {
193
+ throw new Error("Load prevented by afterLoadConversations hook");
194
+ }
195
+ }
196
+
197
+ // Check for errors
198
+ const queryState = queryClient.getQueryState(listQuery.queryKey);
199
+ if (queryState?.error && hooks?.onLoadError) {
200
+ const error =
201
+ queryState.error instanceof Error
202
+ ? queryState.error
203
+ : new Error(String(queryState.error));
204
+ await hooks.onLoadError(error, context);
205
+ }
206
+ } catch (error) {
207
+ if (hooks?.onLoadError) {
208
+ await hooks.onLoadError(error as Error, context);
209
+ }
210
+ }
211
+ }
212
+ };
213
+ }
214
+
215
+ // Loader for single conversation page
216
+ function createConversationLoader(id: string, config: AiChatClientConfig) {
217
+ return async () => {
218
+ if (typeof window === "undefined") {
219
+ const { queryClient, apiBasePath, apiBaseURL, hooks, headers } = config;
220
+
221
+ const context: LoaderContext = {
222
+ path: `/chat/${id}`,
223
+ params: { id },
224
+ isSSR: true,
225
+ apiBaseURL,
226
+ apiBasePath,
227
+ headers,
228
+ };
229
+
230
+ try {
231
+ // Before hook
232
+ if (hooks?.beforeLoadConversation) {
233
+ const canLoad = await hooks.beforeLoadConversation(id, context);
234
+ if (!canLoad) {
235
+ throw new Error("Load prevented by beforeLoadConversation hook");
236
+ }
237
+ }
238
+
239
+ const client = createApiClient<AiChatApiRouter>({
240
+ baseURL: apiBaseURL,
241
+ basePath: apiBasePath,
242
+ });
243
+
244
+ const queries = createAiChatQueryKeys(client, headers);
245
+
246
+ // Prefetch both the conversation and the conversations list
247
+ const conversationQuery = queries.conversations.detail(id);
248
+ const listQuery = queries.conversations.list();
249
+
250
+ await Promise.all([
251
+ queryClient.prefetchQuery(conversationQuery),
252
+ queryClient.prefetchQuery(listQuery),
253
+ ]);
254
+
255
+ // After hook
256
+ if (hooks?.afterLoadConversation) {
257
+ const conversation =
258
+ queryClient.getQueryData<
259
+ SerializedConversation & { messages: SerializedMessage[] }
260
+ >(conversationQuery.queryKey) || null;
261
+ const canContinue = await hooks.afterLoadConversation(
262
+ conversation,
263
+ id,
264
+ context,
265
+ );
266
+ if (canContinue === false) {
267
+ throw new Error("Load prevented by afterLoadConversation hook");
268
+ }
269
+ }
270
+
271
+ // Check for errors
272
+ const queryState = queryClient.getQueryState(
273
+ conversationQuery.queryKey,
274
+ );
275
+ if (queryState?.error && hooks?.onLoadError) {
276
+ const error =
277
+ queryState.error instanceof Error
278
+ ? queryState.error
279
+ : new Error(String(queryState.error));
280
+ await hooks.onLoadError(error, context);
281
+ }
282
+ } catch (error) {
283
+ if (hooks?.onLoadError) {
284
+ await hooks.onLoadError(error as Error, context);
285
+ }
286
+ }
287
+ }
288
+ };
289
+ }
290
+
291
+ // Meta generator for chat home page
292
+ function createChatHomeMeta(config: AiChatClientConfig) {
293
+ return () => {
294
+ const { siteBaseURL, siteBasePath, seo } = config;
295
+ const fullUrl = `${siteBaseURL}${siteBasePath}/chat`;
296
+ const title = "Chat";
297
+ const description = seo?.description || "Start a conversation with AI";
298
+
299
+ return [
300
+ { title },
301
+ { name: "title", content: title },
302
+ { name: "description", content: description },
303
+ { name: "robots", content: "noindex, nofollow" }, // Chat pages typically shouldn't be indexed
304
+
305
+ // Open Graph
306
+ { property: "og:type", content: "website" },
307
+ { property: "og:title", content: title },
308
+ { property: "og:description", content: description },
309
+ { property: "og:url", content: fullUrl },
310
+ ...(seo?.siteName
311
+ ? [{ property: "og:site_name", content: seo.siteName }]
312
+ : []),
313
+ ...(seo?.locale ? [{ property: "og:locale", content: seo.locale }] : []),
314
+ ...(seo?.defaultImage
315
+ ? [{ property: "og:image", content: seo.defaultImage }]
316
+ : []),
317
+
318
+ // Twitter
319
+ { name: "twitter:card", content: "summary" },
320
+ { name: "twitter:title", content: title },
321
+ { name: "twitter:description", content: description },
322
+ ];
323
+ };
324
+ }
325
+
326
+ // Meta generator for single conversation page
327
+ function createConversationMeta(id: string, config: AiChatClientConfig) {
328
+ return () => {
329
+ const {
330
+ queryClient,
331
+ apiBaseURL,
332
+ apiBasePath,
333
+ siteBaseURL,
334
+ siteBasePath,
335
+ seo,
336
+ } = config;
337
+ const queries = createAiChatQueryKeys(
338
+ createApiClient<AiChatApiRouter>({
339
+ baseURL: apiBaseURL,
340
+ basePath: apiBasePath,
341
+ }),
342
+ );
343
+
344
+ const conversation = queryClient.getQueryData<
345
+ SerializedConversation & { messages: SerializedMessage[] }
346
+ >(queries.conversations.detail(id).queryKey);
347
+
348
+ const fullUrl = `${siteBaseURL}${siteBasePath}/chat/${id}`;
349
+ const title = conversation?.title || "Chat";
350
+ const description = seo?.description || "AI conversation";
351
+
352
+ return [
353
+ { title },
354
+ { name: "title", content: title },
355
+ { name: "description", content: description },
356
+ { name: "robots", content: "noindex, nofollow" },
357
+
358
+ // Open Graph
359
+ { property: "og:type", content: "website" },
360
+ { property: "og:title", content: title },
361
+ { property: "og:description", content: description },
362
+ { property: "og:url", content: fullUrl },
363
+ ...(seo?.siteName
364
+ ? [{ property: "og:site_name", content: seo.siteName }]
365
+ : []),
366
+
367
+ // Twitter
368
+ { name: "twitter:card", content: "summary" },
369
+ { name: "twitter:title", content: title },
370
+ ];
371
+ };
372
+ }
373
+
374
+ /**
375
+ * AI Chat client plugin
376
+ * Provides routes, components, and React Query hooks for AI chat
377
+ *
378
+ * @param config - Configuration including queryClient, baseURL, and optional hooks
379
+ */
380
+ export const aiChatClientPlugin = (config: AiChatClientConfig) => {
381
+ const isPublicMode = config.mode === "public";
382
+
383
+ // Define routes based on mode
384
+ // In public mode, only the base chat route is available
385
+ // In authenticated mode, conversation routes are also available
386
+ if (isPublicMode) {
387
+ return defineClientPlugin({
388
+ name: "ai-chat",
389
+
390
+ routes: () => ({
391
+ // Chat home - simple chat interface without history
392
+ chat: createRoute("/chat", () => ({
393
+ PageComponent: () => (
394
+ <ChatLayout
395
+ apiBaseURL={config.apiBaseURL}
396
+ apiBasePath={config.apiBasePath}
397
+ showSidebar={false}
398
+ />
399
+ ),
400
+ loader: createConversationsLoader(config),
401
+ meta: createChatHomeMeta(config),
402
+ })),
403
+ }),
404
+
405
+ sitemap: async () => [],
406
+ });
407
+ }
408
+
409
+ // Authenticated mode - full chat with conversation history
410
+ return defineClientPlugin({
411
+ name: "ai-chat",
412
+
413
+ routes: () => ({
414
+ // Chat home - new conversation or list
415
+ chat: createRoute("/chat", () => ({
416
+ PageComponent: () => (
417
+ <ChatLayout
418
+ apiBaseURL={config.apiBaseURL}
419
+ apiBasePath={config.apiBasePath}
420
+ />
421
+ ),
422
+ loader: createConversationsLoader(config),
423
+ meta: createChatHomeMeta(config),
424
+ })),
425
+
426
+ // Existing conversation
427
+ chatConversation: createRoute("/chat/:id", ({ params }) => ({
428
+ PageComponent: () => (
429
+ <ChatLayout
430
+ apiBaseURL={config.apiBaseURL}
431
+ apiBasePath={config.apiBasePath}
432
+ conversationId={params.id}
433
+ />
434
+ ),
435
+ loader: createConversationLoader(params.id, config),
436
+ meta: createConversationMeta(params.id, config),
437
+ })),
438
+ }),
439
+
440
+ // Chat pages typically shouldn't be in sitemap, but we provide the option
441
+ sitemap: async () => {
442
+ // Return empty array - chat conversations are private and shouldn't be indexed
443
+ return [];
444
+ },
445
+ });
446
+ };
447
+
448
+ export type { SerializedConversation, SerializedMessage } from "../types";
449
+ export type { AiChatMode } from "./overrides";
@@ -0,0 +1,6 @@
1
+ /* AI Chat Plugin Client Styles */
2
+ /* NOTE:
3
+ * Markdown + syntax highlighting styles are imported from code (TS/TSX),
4
+ * matching the blog plugin approach. This avoids brittle nested @import
5
+ * resolution when consumers import `@btst/stack/plugins/ai-chat/css`.
6
+ */
@@ -0,0 +1,65 @@
1
+ import { createDbPlugin } from "@btst/db";
2
+
3
+ /**
4
+ * AI Chat plugin schema
5
+ * Defines the database tables for conversations and messages
6
+ */
7
+ export const aiChatSchema = createDbPlugin("aiChat", {
8
+ conversation: {
9
+ modelName: "conversation",
10
+ fields: {
11
+ id: {
12
+ type: "string",
13
+ required: true,
14
+ unique: true,
15
+ },
16
+ userId: {
17
+ type: "string",
18
+ required: false,
19
+ },
20
+ title: {
21
+ type: "string",
22
+ required: true,
23
+ },
24
+ createdAt: {
25
+ type: "date",
26
+ defaultValue: () => new Date(),
27
+ },
28
+ updatedAt: {
29
+ type: "date",
30
+ defaultValue: () => new Date(),
31
+ },
32
+ },
33
+ },
34
+ message: {
35
+ modelName: "message",
36
+ fields: {
37
+ id: {
38
+ type: "string",
39
+ required: true,
40
+ unique: true,
41
+ },
42
+ conversationId: {
43
+ type: "string",
44
+ required: true,
45
+ references: {
46
+ model: "conversation",
47
+ field: "id",
48
+ onDelete: "cascade",
49
+ },
50
+ },
51
+ role: {
52
+ type: "string",
53
+ required: true,
54
+ },
55
+ content: {
56
+ type: "string",
57
+ required: true,
58
+ },
59
+ createdAt: {
60
+ type: "date",
61
+ defaultValue: () => new Date(),
62
+ },
63
+ },
64
+ },
65
+ });
@@ -0,0 +1,87 @@
1
+ import {
2
+ mergeQueryKeys,
3
+ createQueryKeys,
4
+ } from "@lukemorales/query-key-factory";
5
+ import type { AiChatApiRouter } from "./api";
6
+ import { createApiClient } from "@btst/stack/plugins/client";
7
+ import type { SerializedConversation, SerializedMessage } from "./types";
8
+
9
+ // Helper to check and handle error responses
10
+ function handleResponse<T>(response: unknown): T {
11
+ if (
12
+ typeof response === "object" &&
13
+ response !== null &&
14
+ "error" in response
15
+ ) {
16
+ const errorResponse = response as { error?: unknown };
17
+ if (errorResponse.error !== null && errorResponse.error !== undefined) {
18
+ const error = errorResponse.error;
19
+ if (error instanceof Error) {
20
+ throw error;
21
+ }
22
+ if (typeof error === "object" && error !== null) {
23
+ const errorObj = error as Record<string, unknown>;
24
+ const message =
25
+ (typeof errorObj.message === "string" ? errorObj.message : null) ||
26
+ (typeof errorObj.error === "string" ? errorObj.error : null) ||
27
+ JSON.stringify(error);
28
+ throw new Error(message);
29
+ }
30
+ throw new Error(String(error));
31
+ }
32
+ }
33
+ return (response as { data?: T }).data as T;
34
+ }
35
+
36
+ export type ConversationWithMessages = SerializedConversation & {
37
+ messages: SerializedMessage[];
38
+ };
39
+
40
+ export function createAiChatQueryKeys(
41
+ client: ReturnType<typeof createApiClient<AiChatApiRouter>>,
42
+ headers?: HeadersInit,
43
+ ) {
44
+ const conversations = createConversationsQueries(client, headers);
45
+
46
+ return mergeQueryKeys(conversations);
47
+ }
48
+
49
+ function createConversationsQueries(
50
+ client: ReturnType<typeof createApiClient<AiChatApiRouter>>,
51
+ headers?: HeadersInit,
52
+ ) {
53
+ return createQueryKeys("conversations", {
54
+ // List all conversations
55
+ list: () => ({
56
+ // NOTE: query-key-factory already namespaces as:
57
+ // ["conversations", "list", ...queryKey]
58
+ // so we use a stable marker (not "list") to avoid ["...","list","list"].
59
+ queryKey: ["all"],
60
+ queryFn: async () => {
61
+ const response = await client("/chat/conversations", {
62
+ method: "GET",
63
+ headers,
64
+ });
65
+
66
+ const data = handleResponse<SerializedConversation[] | null>(response);
67
+ return data ?? [];
68
+ },
69
+ }),
70
+
71
+ // Get single conversation with messages
72
+ detail: (id: string) => ({
73
+ queryKey: [id],
74
+ queryFn: async () => {
75
+ if (!id) return null;
76
+
77
+ const response = await client("/chat/conversations/:id", {
78
+ method: "GET",
79
+ params: { id },
80
+ headers,
81
+ });
82
+
83
+ return handleResponse<ConversationWithMessages | null>(response);
84
+ },
85
+ }),
86
+ });
87
+ }
@@ -0,0 +1,40 @@
1
+ import { z } from "zod";
2
+
3
+ export const createConversationSchema = z.object({
4
+ id: z.string().optional(),
5
+ title: z.string().optional(),
6
+ });
7
+
8
+ export const updateConversationSchema = z.object({
9
+ title: z.string().optional(),
10
+ });
11
+
12
+ export const chatRequestSchema = z.object({
13
+ messages: z.array(
14
+ z.union([
15
+ // Format with content string
16
+ z.object({
17
+ role: z.enum(["system", "user", "assistant", "data"]),
18
+ content: z.string(),
19
+ id: z.string().optional(),
20
+ }),
21
+ // Format with parts array (from UIMessage)
22
+ z.object({
23
+ role: z.enum(["system", "user", "assistant", "data"]),
24
+ parts: z.array(
25
+ z
26
+ .object({
27
+ type: z.string(),
28
+ text: z.string().optional(),
29
+ // Allow other properties that might be present
30
+ })
31
+ .loose(),
32
+ ),
33
+ id: z.string().optional(),
34
+ metadata: z.any().optional(),
35
+ }),
36
+ ]),
37
+ ),
38
+ conversationId: z.string().optional(),
39
+ model: z.string().optional(),
40
+ });
@@ -0,0 +1,19 @@
1
+ @import "./client.css";
2
+
3
+ /*
4
+ * AI Chat Plugin CSS - Includes Tailwind class scanning
5
+ *
6
+ * When consumed from npm, Tailwind v4 will automatically scan this package's
7
+ * source files for Tailwind classes. Consumers only need:
8
+ * @import "@btst/stack/plugins/ai-chat/css";
9
+ */
10
+
11
+ /* Scan this package's source files for Tailwind classes */
12
+ @source "../../../src/**/*.{ts,tsx}";
13
+
14
+ /* Scan UI package components (when installed as npm package the UI package will be in this dir) */
15
+ @source "../../packages/ui/src";
16
+
17
+ /*
18
+ * alternatively consumer can use @source "../node_modules/@btst/stack/src/**\/*.{ts,tsx}";
19
+ */
@@ -0,0 +1,29 @@
1
+ export type Conversation = {
2
+ id: string;
3
+ userId?: string;
4
+ title: string;
5
+ createdAt: Date;
6
+ updatedAt: Date;
7
+ };
8
+
9
+ export type ConversationWithMessages = Conversation & {
10
+ message?: Message[];
11
+ };
12
+
13
+ export type Message = {
14
+ id: string;
15
+ conversationId: string;
16
+ role: "system" | "user" | "assistant" | "data";
17
+ content: string;
18
+ createdAt: Date;
19
+ };
20
+
21
+ export interface SerializedConversation
22
+ extends Omit<Conversation, "createdAt" | "updatedAt"> {
23
+ createdAt: string;
24
+ updatedAt: string;
25
+ }
26
+
27
+ export interface SerializedMessage extends Omit<Message, "createdAt"> {
28
+ createdAt: string;
29
+ }