@btst/stack 2.5.5 → 2.6.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 (101) hide show
  1. package/README.md +23 -0
  2. package/dist/client/components/index.d.cts +9 -9
  3. package/dist/client/components/index.d.mts +9 -9
  4. package/dist/client/components/index.d.ts +9 -9
  5. package/dist/packages/stack/src/plugins/ai-chat/client/components/shared/default-error.cjs +1 -1
  6. package/dist/packages/stack/src/plugins/ai-chat/client/components/shared/default-error.mjs +1 -1
  7. package/dist/packages/stack/src/plugins/ai-chat/client/plugin.cjs +44 -35
  8. package/dist/packages/stack/src/plugins/ai-chat/client/plugin.mjs +44 -35
  9. package/dist/packages/stack/src/plugins/blog/client/components/shared/default-error.cjs +1 -1
  10. package/dist/packages/stack/src/plugins/blog/client/components/shared/default-error.mjs +1 -1
  11. package/dist/packages/stack/src/plugins/blog/client/hooks/use-debounce.cjs +22 -0
  12. package/dist/packages/stack/src/plugins/blog/client/hooks/use-debounce.mjs +23 -2
  13. package/dist/packages/stack/src/plugins/blog/client/plugin.cjs +12 -6
  14. package/dist/packages/stack/src/plugins/blog/client/plugin.mjs +12 -6
  15. package/dist/packages/stack/src/plugins/cms/client/components/shared/default-error.cjs +1 -1
  16. package/dist/packages/stack/src/plugins/cms/client/components/shared/default-error.mjs +1 -1
  17. package/dist/packages/stack/src/plugins/cms/client/plugin.cjs +38 -26
  18. package/dist/packages/stack/src/plugins/cms/client/plugin.mjs +38 -26
  19. package/dist/packages/stack/src/plugins/form-builder/client/components/shared/default-error.cjs +1 -1
  20. package/dist/packages/stack/src/plugins/form-builder/client/components/shared/default-error.mjs +1 -1
  21. package/dist/packages/stack/src/plugins/form-builder/client/plugin.cjs +32 -20
  22. package/dist/packages/stack/src/plugins/form-builder/client/plugin.mjs +32 -20
  23. package/dist/packages/stack/src/plugins/kanban/client/components/shared/default-error.cjs +1 -1
  24. package/dist/packages/stack/src/plugins/kanban/client/components/shared/default-error.mjs +1 -1
  25. package/dist/packages/stack/src/plugins/kanban/client/plugin.cjs +6 -3
  26. package/dist/packages/stack/src/plugins/kanban/client/plugin.mjs +6 -3
  27. package/dist/packages/stack/src/plugins/ui-builder/client/components/page-renderer.cjs +1 -1
  28. package/dist/packages/stack/src/plugins/ui-builder/client/components/page-renderer.mjs +1 -1
  29. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-builder-page.internal.cjs +3 -1
  30. package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-builder-page.internal.mjs +3 -1
  31. package/dist/packages/stack/src/plugins/ui-builder/client/components/shared/default-error.cjs +1 -1
  32. package/dist/packages/stack/src/plugins/ui-builder/client/components/shared/default-error.mjs +1 -1
  33. package/dist/packages/stack/src/plugins/ui-builder/client/plugin.cjs +24 -15
  34. package/dist/packages/stack/src/plugins/ui-builder/client/plugin.mjs +24 -15
  35. package/dist/packages/ui/src/components/search-select.cjs +13 -3
  36. package/dist/packages/ui/src/components/search-select.mjs +14 -4
  37. package/dist/plugins/ai-chat/client/index.d.cts +17 -4
  38. package/dist/plugins/ai-chat/client/index.d.mts +17 -4
  39. package/dist/plugins/ai-chat/client/index.d.ts +17 -4
  40. package/dist/plugins/blog/client/hooks/index.cjs +3 -0
  41. package/dist/plugins/blog/client/hooks/index.d.cts +7 -226
  42. package/dist/plugins/blog/client/hooks/index.d.mts +7 -226
  43. package/dist/plugins/blog/client/hooks/index.d.ts +7 -226
  44. package/dist/plugins/blog/client/hooks/index.mjs +1 -0
  45. package/dist/plugins/blog/client/index.d.cts +45 -21
  46. package/dist/plugins/blog/client/index.d.mts +45 -21
  47. package/dist/plugins/blog/client/index.d.ts +45 -21
  48. package/dist/plugins/cms/client/index.d.cts +35 -14
  49. package/dist/plugins/cms/client/index.d.mts +35 -14
  50. package/dist/plugins/cms/client/index.d.ts +35 -14
  51. package/dist/plugins/form-builder/client/components/index.d.cts +1 -1
  52. package/dist/plugins/form-builder/client/components/index.d.mts +1 -1
  53. package/dist/plugins/form-builder/client/components/index.d.ts +1 -1
  54. package/dist/plugins/form-builder/client/index.d.cts +32 -14
  55. package/dist/plugins/form-builder/client/index.d.mts +32 -14
  56. package/dist/plugins/form-builder/client/index.d.ts +32 -14
  57. package/dist/plugins/kanban/api/index.d.cts +1 -1
  58. package/dist/plugins/kanban/api/index.d.mts +1 -1
  59. package/dist/plugins/kanban/api/index.d.ts +1 -1
  60. package/dist/plugins/kanban/client/components/index.d.cts +5 -5
  61. package/dist/plugins/kanban/client/components/index.d.mts +5 -5
  62. package/dist/plugins/kanban/client/components/index.d.ts +5 -5
  63. package/dist/plugins/kanban/client/index.d.cts +25 -10
  64. package/dist/plugins/kanban/client/index.d.mts +25 -10
  65. package/dist/plugins/kanban/client/index.d.ts +25 -10
  66. package/dist/plugins/kanban/query-keys.d.cts +1 -1
  67. package/dist/plugins/kanban/query-keys.d.mts +1 -1
  68. package/dist/plugins/kanban/query-keys.d.ts +1 -1
  69. package/dist/plugins/route-docs/client/index.d.cts +4 -4
  70. package/dist/plugins/route-docs/client/index.d.mts +4 -4
  71. package/dist/plugins/route-docs/client/index.d.ts +4 -4
  72. package/dist/plugins/ui-builder/client/components/index.d.cts +1 -1
  73. package/dist/plugins/ui-builder/client/components/index.d.mts +1 -1
  74. package/dist/plugins/ui-builder/client/components/index.d.ts +1 -1
  75. package/dist/plugins/ui-builder/client/index.d.cts +35 -16
  76. package/dist/plugins/ui-builder/client/index.d.mts +35 -16
  77. package/dist/plugins/ui-builder/client/index.d.ts +35 -16
  78. package/dist/shared/stack.CNLHlv7r.d.mts +228 -0
  79. package/dist/shared/stack.CQAZwXhV.d.cts +228 -0
  80. package/dist/shared/stack.D3BsrpAz.d.ts +228 -0
  81. package/package.json +19 -2
  82. package/src/__tests__/page-component-overrides.test.tsx +147 -0
  83. package/src/plugins/ai-chat/client/components/shared/default-error.tsx +1 -1
  84. package/src/plugins/ai-chat/client/plugin.tsx +60 -32
  85. package/src/plugins/blog/client/components/shared/default-error.tsx +2 -1
  86. package/src/plugins/blog/client/hooks/index.tsx +1 -0
  87. package/src/plugins/blog/client/plugin.tsx +41 -6
  88. package/src/plugins/cms/client/components/shared/default-error.tsx +3 -2
  89. package/src/plugins/cms/client/plugin.tsx +65 -32
  90. package/src/plugins/form-builder/client/components/shared/default-error.tsx +3 -2
  91. package/src/plugins/form-builder/client/plugin.tsx +56 -23
  92. package/src/plugins/kanban/client/components/shared/default-error.tsx +3 -2
  93. package/src/plugins/kanban/client/plugin.tsx +23 -3
  94. package/src/plugins/ui-builder/client/components/page-renderer.tsx +5 -3
  95. package/src/plugins/ui-builder/client/components/pages/page-builder-page.internal.tsx +2 -0
  96. package/src/plugins/ui-builder/client/components/shared/default-error.tsx +3 -2
  97. package/src/plugins/ui-builder/client/overrides.ts +10 -1
  98. package/src/plugins/ui-builder/client/plugin.tsx +41 -15
  99. package/dist/shared/{stack.CxNeGV2z.d.mts → stack.Ba_Ks8qi.d.mts} +9 -9
  100. package/dist/shared/{stack.DSxTDZBQ.d.cts → stack.CFqqZUes.d.cts} +9 -9
  101. package/dist/shared/{stack.BFcg0tDz.d.ts → stack.DMobugrZ.d.ts} +9 -9
package/README.md CHANGED
@@ -115,6 +115,22 @@ Supports Prisma, Drizzle, MongoDB and Kysely SQL dialects.
115
115
 
116
116
  ---
117
117
 
118
+ ## Shadcn Registry
119
+
120
+ Each plugin's UI layer is available as a [shadcn registry](https://ui.shadcn.com/docs/registry) block. Use it to **eject and fully customize** the page components while keeping all data-fetching and API logic from `@btst/stack`:
121
+
122
+ ```bash
123
+ # Install a single plugin's UI
124
+ npx shadcn@latest add https://github.com/better-stack-ai/better-stack/blob/main/packages/stack/registry/btst-blog.json
125
+
126
+ # Or install the full collection
127
+ npx shadcn@latest add https://github.com/better-stack-ai/better-stack/blob/main/packages/stack/registry/registry.json
128
+ ```
129
+
130
+ Components are copied into `src/components/btst/{plugin}/client/` — all relative imports remain valid and you can edit them freely.
131
+
132
+ ---
133
+
118
134
  ## Examples
119
135
 
120
136
  * [Next.js App Router](./examples/nextjs)
@@ -130,6 +146,13 @@ Full documentation, guides, and plugin development:
130
146
 
131
147
  ---
132
148
 
149
+ ## Contributing
150
+
151
+ Bug reports, plugin PRs, and documentation improvements are welcome.
152
+ See **[CONTRIBUTING.md](./CONTRIBUTING.md)** for the plugin development guide, testing instructions, and submission checklist.
153
+
154
+ ---
155
+
133
156
  If this saves you time, a ⭐ helps others find it.
134
157
 
135
158
  MIT © [olliethedev](https://github.com/olliethedev)
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import react__default, { ErrorInfo } from 'react';
2
+ import React__default, { ErrorInfo } from 'react';
3
3
  import { FallbackProps } from 'react-error-boundary';
4
4
  export { FallbackProps } from 'react-error-boundary';
5
5
  import { createRouter } from '@btst/yar';
@@ -8,9 +8,9 @@ import { createRouter } from '@btst/yar';
8
8
  * Route type with optional components
9
9
  */
10
10
  type RouteWithComponents = {
11
- PageComponent?: react__default.ComponentType;
12
- ErrorComponent?: react__default.ComponentType<FallbackProps>;
13
- LoadingComponent?: react__default.ComponentType;
11
+ PageComponent?: React__default.ComponentType;
12
+ ErrorComponent?: React__default.ComponentType<FallbackProps>;
13
+ LoadingComponent?: React__default.ComponentType;
14
14
  } | null | undefined;
15
15
  /**
16
16
  * Composes the route content with Suspense and Error Boundary
@@ -21,7 +21,7 @@ type RouteWithComponents = {
21
21
  declare function RouteRenderer({ router, path, NotFoundComponent, onNotFound, onError, props, }: {
22
22
  router: ReturnType<typeof createRouter>;
23
23
  path: string;
24
- NotFoundComponent?: react__default.ComponentType<{
24
+ NotFoundComponent?: React__default.ComponentType<{
25
25
  message: string;
26
26
  }>;
27
27
  onNotFound?: () => never;
@@ -43,11 +43,11 @@ declare function RouteRenderer({ router, path, NotFoundComponent, onNotFound, on
43
43
  */
44
44
  declare function ComposedRoute({ path, PageComponent, ErrorComponent, LoadingComponent, onNotFound, NotFoundComponent, props, onError, }: {
45
45
  path: string;
46
- PageComponent: react__default.ComponentType<any>;
47
- ErrorComponent?: react__default.ComponentType<FallbackProps>;
48
- LoadingComponent: react__default.ComponentType;
46
+ PageComponent: React__default.ComponentType<any>;
47
+ ErrorComponent?: React__default.ComponentType<FallbackProps>;
48
+ LoadingComponent: React__default.ComponentType;
49
49
  onNotFound?: () => never;
50
- NotFoundComponent?: react__default.ComponentType<{
50
+ NotFoundComponent?: React__default.ComponentType<{
51
51
  message: string;
52
52
  }>;
53
53
  props?: any;
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import react__default, { ErrorInfo } from 'react';
2
+ import React__default, { ErrorInfo } from 'react';
3
3
  import { FallbackProps } from 'react-error-boundary';
4
4
  export { FallbackProps } from 'react-error-boundary';
5
5
  import { createRouter } from '@btst/yar';
@@ -8,9 +8,9 @@ import { createRouter } from '@btst/yar';
8
8
  * Route type with optional components
9
9
  */
10
10
  type RouteWithComponents = {
11
- PageComponent?: react__default.ComponentType;
12
- ErrorComponent?: react__default.ComponentType<FallbackProps>;
13
- LoadingComponent?: react__default.ComponentType;
11
+ PageComponent?: React__default.ComponentType;
12
+ ErrorComponent?: React__default.ComponentType<FallbackProps>;
13
+ LoadingComponent?: React__default.ComponentType;
14
14
  } | null | undefined;
15
15
  /**
16
16
  * Composes the route content with Suspense and Error Boundary
@@ -21,7 +21,7 @@ type RouteWithComponents = {
21
21
  declare function RouteRenderer({ router, path, NotFoundComponent, onNotFound, onError, props, }: {
22
22
  router: ReturnType<typeof createRouter>;
23
23
  path: string;
24
- NotFoundComponent?: react__default.ComponentType<{
24
+ NotFoundComponent?: React__default.ComponentType<{
25
25
  message: string;
26
26
  }>;
27
27
  onNotFound?: () => never;
@@ -43,11 +43,11 @@ declare function RouteRenderer({ router, path, NotFoundComponent, onNotFound, on
43
43
  */
44
44
  declare function ComposedRoute({ path, PageComponent, ErrorComponent, LoadingComponent, onNotFound, NotFoundComponent, props, onError, }: {
45
45
  path: string;
46
- PageComponent: react__default.ComponentType<any>;
47
- ErrorComponent?: react__default.ComponentType<FallbackProps>;
48
- LoadingComponent: react__default.ComponentType;
46
+ PageComponent: React__default.ComponentType<any>;
47
+ ErrorComponent?: React__default.ComponentType<FallbackProps>;
48
+ LoadingComponent: React__default.ComponentType;
49
49
  onNotFound?: () => never;
50
- NotFoundComponent?: react__default.ComponentType<{
50
+ NotFoundComponent?: React__default.ComponentType<{
51
51
  message: string;
52
52
  }>;
53
53
  props?: any;
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import react__default, { ErrorInfo } from 'react';
2
+ import React__default, { ErrorInfo } from 'react';
3
3
  import { FallbackProps } from 'react-error-boundary';
4
4
  export { FallbackProps } from 'react-error-boundary';
5
5
  import { createRouter } from '@btst/yar';
@@ -8,9 +8,9 @@ import { createRouter } from '@btst/yar';
8
8
  * Route type with optional components
9
9
  */
10
10
  type RouteWithComponents = {
11
- PageComponent?: react__default.ComponentType;
12
- ErrorComponent?: react__default.ComponentType<FallbackProps>;
13
- LoadingComponent?: react__default.ComponentType;
11
+ PageComponent?: React__default.ComponentType;
12
+ ErrorComponent?: React__default.ComponentType<FallbackProps>;
13
+ LoadingComponent?: React__default.ComponentType;
14
14
  } | null | undefined;
15
15
  /**
16
16
  * Composes the route content with Suspense and Error Boundary
@@ -21,7 +21,7 @@ type RouteWithComponents = {
21
21
  declare function RouteRenderer({ router, path, NotFoundComponent, onNotFound, onError, props, }: {
22
22
  router: ReturnType<typeof createRouter>;
23
23
  path: string;
24
- NotFoundComponent?: react__default.ComponentType<{
24
+ NotFoundComponent?: React__default.ComponentType<{
25
25
  message: string;
26
26
  }>;
27
27
  onNotFound?: () => never;
@@ -43,11 +43,11 @@ declare function RouteRenderer({ router, path, NotFoundComponent, onNotFound, on
43
43
  */
44
44
  declare function ComposedRoute({ path, PageComponent, ErrorComponent, LoadingComponent, onNotFound, NotFoundComponent, props, onError, }: {
45
45
  path: string;
46
- PageComponent: react__default.ComponentType<any>;
47
- ErrorComponent?: react__default.ComponentType<FallbackProps>;
48
- LoadingComponent: react__default.ComponentType;
46
+ PageComponent: React__default.ComponentType<any>;
47
+ ErrorComponent?: React__default.ComponentType<FallbackProps>;
48
+ LoadingComponent: React__default.ComponentType;
49
49
  onNotFound?: () => never;
50
- NotFoundComponent?: react__default.ComponentType<{
50
+ NotFoundComponent?: React__default.ComponentType<{
51
51
  message: string;
52
52
  }>;
53
53
  props?: any;
@@ -11,7 +11,7 @@ function DefaultError({ error }) {
11
11
  localization: index.AI_CHAT_LOCALIZATION
12
12
  });
13
13
  const title = localization?.CHAT_GENERIC_ERROR_TITLE ?? index.AI_CHAT_LOCALIZATION.CHAT_GENERIC_ERROR_TITLE;
14
- const message = process.env.NODE_ENV === "production" ? localization?.CHAT_GENERIC_ERROR_MESSAGE ?? index.AI_CHAT_LOCALIZATION.CHAT_GENERIC_ERROR_MESSAGE : error?.message ?? localization?.CHAT_GENERIC_ERROR_MESSAGE ?? index.AI_CHAT_LOCALIZATION.CHAT_GENERIC_ERROR_MESSAGE;
14
+ const message = process.env.NODE_ENV === "production" ? localization?.CHAT_GENERIC_ERROR_MESSAGE ?? index.AI_CHAT_LOCALIZATION.CHAT_GENERIC_ERROR_MESSAGE : (error instanceof Error ? error.message : void 0) ?? localization?.CHAT_GENERIC_ERROR_MESSAGE ?? index.AI_CHAT_LOCALIZATION.CHAT_GENERIC_ERROR_MESSAGE;
15
15
  return /* @__PURE__ */ jsxRuntime.jsx(errorPlaceholder.ErrorPlaceholder, { title, message });
16
16
  }
17
17
 
@@ -9,7 +9,7 @@ function DefaultError({ error }) {
9
9
  localization: AI_CHAT_LOCALIZATION
10
10
  });
11
11
  const title = localization?.CHAT_GENERIC_ERROR_TITLE ?? AI_CHAT_LOCALIZATION.CHAT_GENERIC_ERROR_TITLE;
12
- const message = process.env.NODE_ENV === "production" ? localization?.CHAT_GENERIC_ERROR_MESSAGE ?? AI_CHAT_LOCALIZATION.CHAT_GENERIC_ERROR_MESSAGE : error?.message ?? localization?.CHAT_GENERIC_ERROR_MESSAGE ?? AI_CHAT_LOCALIZATION.CHAT_GENERIC_ERROR_MESSAGE;
12
+ const message = process.env.NODE_ENV === "production" ? localization?.CHAT_GENERIC_ERROR_MESSAGE ?? AI_CHAT_LOCALIZATION.CHAT_GENERIC_ERROR_MESSAGE : (error instanceof Error ? error.message : void 0) ?? localization?.CHAT_GENERIC_ERROR_MESSAGE ?? AI_CHAT_LOCALIZATION.CHAT_GENERIC_ERROR_MESSAGE;
13
13
  return /* @__PURE__ */ jsx(ErrorPlaceholder, { title, message });
14
14
  }
15
15
 
@@ -179,18 +179,21 @@ const aiChatClientPlugin = (config) => {
179
179
  name: "ai-chat",
180
180
  routes: () => ({
181
181
  // Chat home - simple chat interface without history
182
- chat: yar.createRoute("/chat", () => ({
183
- PageComponent: () => /* @__PURE__ */ jsxRuntime.jsx(
184
- chatLayout.ChatLayout,
185
- {
186
- apiBaseURL: config.apiBaseURL,
187
- apiBasePath: config.apiBasePath,
188
- showSidebar: false
189
- }
190
- ),
191
- loader: createConversationsLoader(config),
192
- meta: createChatHomeMeta(config)
193
- }))
182
+ chat: yar.createRoute("/chat", () => {
183
+ const CustomChat = config.pageComponents?.chat;
184
+ return {
185
+ PageComponent: CustomChat ?? (() => /* @__PURE__ */ jsxRuntime.jsx(
186
+ chatLayout.ChatLayout,
187
+ {
188
+ apiBaseURL: config.apiBaseURL,
189
+ apiBasePath: config.apiBasePath,
190
+ showSidebar: false
191
+ }
192
+ )),
193
+ loader: createConversationsLoader(config),
194
+ meta: createChatHomeMeta(config)
195
+ };
196
+ })
194
197
  }),
195
198
  sitemap: async () => []
196
199
  });
@@ -199,30 +202,36 @@ const aiChatClientPlugin = (config) => {
199
202
  name: "ai-chat",
200
203
  routes: () => ({
201
204
  // Chat home - new conversation or list
202
- chat: yar.createRoute("/chat", () => ({
203
- PageComponent: () => /* @__PURE__ */ jsxRuntime.jsx(
204
- chatLayout.ChatLayout,
205
- {
206
- apiBaseURL: config.apiBaseURL,
207
- apiBasePath: config.apiBasePath
208
- }
209
- ),
210
- loader: createConversationsLoader(config),
211
- meta: createChatHomeMeta(config)
212
- })),
205
+ chat: yar.createRoute("/chat", () => {
206
+ const CustomChat = config.pageComponents?.chat;
207
+ return {
208
+ PageComponent: CustomChat ?? (() => /* @__PURE__ */ jsxRuntime.jsx(
209
+ chatLayout.ChatLayout,
210
+ {
211
+ apiBaseURL: config.apiBaseURL,
212
+ apiBasePath: config.apiBasePath
213
+ }
214
+ )),
215
+ loader: createConversationsLoader(config),
216
+ meta: createChatHomeMeta(config)
217
+ };
218
+ }),
213
219
  // Existing conversation
214
- chatConversation: yar.createRoute("/chat/:id", ({ params }) => ({
215
- PageComponent: () => /* @__PURE__ */ jsxRuntime.jsx(
216
- chatLayout.ChatLayout,
217
- {
218
- apiBaseURL: config.apiBaseURL,
219
- apiBasePath: config.apiBasePath,
220
- conversationId: params.id
221
- }
222
- ),
223
- loader: createConversationLoader(params.id, config),
224
- meta: createConversationMeta(params.id, config)
225
- }))
220
+ chatConversation: yar.createRoute("/chat/:id", ({ params }) => {
221
+ const CustomConversation = config.pageComponents?.chatConversation;
222
+ return {
223
+ PageComponent: CustomConversation ? () => /* @__PURE__ */ jsxRuntime.jsx(CustomConversation, { conversationId: params.id }) : () => /* @__PURE__ */ jsxRuntime.jsx(
224
+ chatLayout.ChatLayout,
225
+ {
226
+ apiBaseURL: config.apiBaseURL,
227
+ apiBasePath: config.apiBasePath,
228
+ conversationId: params.id
229
+ }
230
+ ),
231
+ loader: createConversationLoader(params.id, config),
232
+ meta: createConversationMeta(params.id, config)
233
+ };
234
+ })
226
235
  }),
227
236
  // Chat pages typically shouldn't be in sitemap, but we provide the option
228
237
  sitemap: async () => {
@@ -177,18 +177,21 @@ const aiChatClientPlugin = (config) => {
177
177
  name: "ai-chat",
178
178
  routes: () => ({
179
179
  // Chat home - simple chat interface without history
180
- chat: createRoute("/chat", () => ({
181
- PageComponent: () => /* @__PURE__ */ jsx(
182
- ChatLayout,
183
- {
184
- apiBaseURL: config.apiBaseURL,
185
- apiBasePath: config.apiBasePath,
186
- showSidebar: false
187
- }
188
- ),
189
- loader: createConversationsLoader(config),
190
- meta: createChatHomeMeta(config)
191
- }))
180
+ chat: createRoute("/chat", () => {
181
+ const CustomChat = config.pageComponents?.chat;
182
+ return {
183
+ PageComponent: CustomChat ?? (() => /* @__PURE__ */ jsx(
184
+ ChatLayout,
185
+ {
186
+ apiBaseURL: config.apiBaseURL,
187
+ apiBasePath: config.apiBasePath,
188
+ showSidebar: false
189
+ }
190
+ )),
191
+ loader: createConversationsLoader(config),
192
+ meta: createChatHomeMeta(config)
193
+ };
194
+ })
192
195
  }),
193
196
  sitemap: async () => []
194
197
  });
@@ -197,30 +200,36 @@ const aiChatClientPlugin = (config) => {
197
200
  name: "ai-chat",
198
201
  routes: () => ({
199
202
  // Chat home - new conversation or list
200
- chat: createRoute("/chat", () => ({
201
- PageComponent: () => /* @__PURE__ */ jsx(
202
- ChatLayout,
203
- {
204
- apiBaseURL: config.apiBaseURL,
205
- apiBasePath: config.apiBasePath
206
- }
207
- ),
208
- loader: createConversationsLoader(config),
209
- meta: createChatHomeMeta(config)
210
- })),
203
+ chat: createRoute("/chat", () => {
204
+ const CustomChat = config.pageComponents?.chat;
205
+ return {
206
+ PageComponent: CustomChat ?? (() => /* @__PURE__ */ jsx(
207
+ ChatLayout,
208
+ {
209
+ apiBaseURL: config.apiBaseURL,
210
+ apiBasePath: config.apiBasePath
211
+ }
212
+ )),
213
+ loader: createConversationsLoader(config),
214
+ meta: createChatHomeMeta(config)
215
+ };
216
+ }),
211
217
  // Existing conversation
212
- chatConversation: createRoute("/chat/:id", ({ params }) => ({
213
- PageComponent: () => /* @__PURE__ */ jsx(
214
- ChatLayout,
215
- {
216
- apiBaseURL: config.apiBaseURL,
217
- apiBasePath: config.apiBasePath,
218
- conversationId: params.id
219
- }
220
- ),
221
- loader: createConversationLoader(params.id, config),
222
- meta: createConversationMeta(params.id, config)
223
- }))
218
+ chatConversation: createRoute("/chat/:id", ({ params }) => {
219
+ const CustomConversation = config.pageComponents?.chatConversation;
220
+ return {
221
+ PageComponent: CustomConversation ? () => /* @__PURE__ */ jsx(CustomConversation, { conversationId: params.id }) : () => /* @__PURE__ */ jsx(
222
+ ChatLayout,
223
+ {
224
+ apiBaseURL: config.apiBaseURL,
225
+ apiBasePath: config.apiBasePath,
226
+ conversationId: params.id
227
+ }
228
+ ),
229
+ loader: createConversationLoader(params.id, config),
230
+ meta: createConversationMeta(params.id, config)
231
+ };
232
+ })
224
233
  }),
225
234
  // Chat pages typically shouldn't be in sitemap, but we provide the option
226
235
  sitemap: async () => {
@@ -11,7 +11,7 @@ function DefaultError({ error }) {
11
11
  localization: index.BLOG_LOCALIZATION
12
12
  });
13
13
  const title = localization.BLOG_GENERIC_ERROR_TITLE;
14
- const message = process.env.NODE_ENV === "production" ? localization.BLOG_GENERIC_ERROR_MESSAGE : error?.message ?? localization.BLOG_GENERIC_ERROR_MESSAGE;
14
+ const message = process.env.NODE_ENV === "production" ? localization.BLOG_GENERIC_ERROR_MESSAGE : (error instanceof Error ? error.message : void 0) ?? localization.BLOG_GENERIC_ERROR_MESSAGE;
15
15
  return /* @__PURE__ */ jsxRuntime.jsx(errorPlaceholder.ErrorPlaceholder, { title, message });
16
16
  }
17
17
 
@@ -9,7 +9,7 @@ function DefaultError({ error }) {
9
9
  localization: BLOG_LOCALIZATION
10
10
  });
11
11
  const title = localization.BLOG_GENERIC_ERROR_TITLE;
12
- const message = process.env.NODE_ENV === "production" ? localization.BLOG_GENERIC_ERROR_MESSAGE : error?.message ?? localization.BLOG_GENERIC_ERROR_MESSAGE;
12
+ const message = process.env.NODE_ENV === "production" ? localization.BLOG_GENERIC_ERROR_MESSAGE : (error instanceof Error ? error.message : void 0) ?? localization.BLOG_GENERIC_ERROR_MESSAGE;
13
13
  return /* @__PURE__ */ jsx(ErrorPlaceholder, { title, message });
14
14
  }
15
15
 
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const utils = require('../../utils.cjs');
3
4
  const React = require('react');
4
5
 
5
6
  function useDebounce(value, delay) {
@@ -12,5 +13,26 @@ function useDebounce(value, delay) {
12
13
  }, [value, delay]);
13
14
  return debouncedValue;
14
15
  }
16
+ function useThrottle(value, wait) {
17
+ const [throttledValue, setThrottledValue] = React.useState(value);
18
+ const valueRef = React.useRef(value);
19
+ valueRef.current = value;
20
+ const throttledSetter = React.useMemo(() => {
21
+ return utils.throttle((next) => {
22
+ setThrottledValue(next);
23
+ }, wait ?? 500);
24
+ }, [wait]);
25
+ React.useEffect(() => {
26
+ throttledSetter(valueRef.current);
27
+ return () => {
28
+ throttledSetter.cancel();
29
+ };
30
+ }, [throttledSetter]);
31
+ React.useEffect(() => {
32
+ throttledSetter(value);
33
+ }, [value, throttledSetter]);
34
+ return throttledValue;
35
+ }
15
36
 
16
37
  exports.useDebounce = useDebounce;
38
+ exports.useThrottle = useThrottle;
@@ -1,4 +1,5 @@
1
- import { useState, useEffect } from 'react';
1
+ import { throttle } from '../../utils.mjs';
2
+ import { useState, useEffect, useRef, useMemo } from 'react';
2
3
 
3
4
  function useDebounce(value, delay) {
4
5
  const [debouncedValue, setDebouncedValue] = useState(value);
@@ -10,5 +11,25 @@ function useDebounce(value, delay) {
10
11
  }, [value, delay]);
11
12
  return debouncedValue;
12
13
  }
14
+ function useThrottle(value, wait) {
15
+ const [throttledValue, setThrottledValue] = useState(value);
16
+ const valueRef = useRef(value);
17
+ valueRef.current = value;
18
+ const throttledSetter = useMemo(() => {
19
+ return throttle((next) => {
20
+ setThrottledValue(next);
21
+ }, wait ?? 500);
22
+ }, [wait]);
23
+ useEffect(() => {
24
+ throttledSetter(valueRef.current);
25
+ return () => {
26
+ throttledSetter.cancel();
27
+ };
28
+ }, [throttledSetter]);
29
+ useEffect(() => {
30
+ throttledSetter(value);
31
+ }, [value, throttledSetter]);
32
+ return throttledValue;
33
+ }
13
34
 
14
- export { useDebounce };
35
+ export { useDebounce, useThrottle };
@@ -422,43 +422,49 @@ const blogClientPlugin = (config) => client.defineClientPlugin({
422
422
  name: "blog",
423
423
  routes: () => ({
424
424
  posts: yar.createRoute("/blog", () => {
425
+ const CustomPosts = config.pageComponents?.posts;
425
426
  return {
426
- PageComponent: () => /* @__PURE__ */ jsxRuntime.jsx(homePage.HomePageComponent, { published: true }),
427
+ PageComponent: CustomPosts ?? (() => /* @__PURE__ */ jsxRuntime.jsx(homePage.HomePageComponent, { published: true })),
427
428
  loader: createPostsLoader(true, config),
428
429
  meta: createPostsListMeta(true, config)
429
430
  };
430
431
  }),
431
432
  drafts: yar.createRoute("/blog/drafts", () => {
433
+ const CustomDrafts = config.pageComponents?.drafts;
432
434
  return {
433
- PageComponent: () => /* @__PURE__ */ jsxRuntime.jsx(homePage.HomePageComponent, { published: false }),
435
+ PageComponent: CustomDrafts ?? (() => /* @__PURE__ */ jsxRuntime.jsx(homePage.HomePageComponent, { published: false })),
434
436
  loader: createPostsLoader(false, config),
435
437
  meta: createPostsListMeta(false, config)
436
438
  };
437
439
  }),
438
440
  newPost: yar.createRoute("/blog/new", () => {
441
+ const CustomNewPost = config.pageComponents?.newPost;
439
442
  return {
440
- PageComponent: newPostPage.NewPostPageComponent,
443
+ PageComponent: CustomNewPost ?? newPostPage.NewPostPageComponent,
441
444
  loader: createNewPostLoader(config),
442
445
  meta: createNewPostMeta(config)
443
446
  };
444
447
  }),
445
448
  editPost: yar.createRoute("/blog/:slug/edit", ({ params: { slug } }) => {
449
+ const CustomEditPost = config.pageComponents?.editPost;
446
450
  return {
447
- PageComponent: () => /* @__PURE__ */ jsxRuntime.jsx(editPostPage.EditPostPageComponent, { slug }),
451
+ PageComponent: CustomEditPost ? () => /* @__PURE__ */ jsxRuntime.jsx(CustomEditPost, { slug }) : () => /* @__PURE__ */ jsxRuntime.jsx(editPostPage.EditPostPageComponent, { slug }),
448
452
  loader: createPostLoader(slug, config, `/blog/${slug}/edit`),
449
453
  meta: createEditPostMeta(slug, config)
450
454
  };
451
455
  }),
452
456
  tag: yar.createRoute("/blog/tag/:tagSlug", ({ params: { tagSlug } }) => {
457
+ const CustomTag = config.pageComponents?.tag;
453
458
  return {
454
- PageComponent: () => /* @__PURE__ */ jsxRuntime.jsx(tagPage.TagPageComponent, { tagSlug }),
459
+ PageComponent: CustomTag ? () => /* @__PURE__ */ jsxRuntime.jsx(CustomTag, { tagSlug }) : () => /* @__PURE__ */ jsxRuntime.jsx(tagPage.TagPageComponent, { tagSlug }),
455
460
  loader: createTagLoader(tagSlug, config),
456
461
  meta: createTagMeta(tagSlug, config)
457
462
  };
458
463
  }),
459
464
  post: yar.createRoute("/blog/:slug", ({ params: { slug } }) => {
465
+ const CustomPost = config.pageComponents?.post;
460
466
  return {
461
- PageComponent: () => /* @__PURE__ */ jsxRuntime.jsx(postPage.PostPageComponent, { slug }),
467
+ PageComponent: CustomPost ? () => /* @__PURE__ */ jsxRuntime.jsx(CustomPost, { slug }) : () => /* @__PURE__ */ jsxRuntime.jsx(postPage.PostPageComponent, { slug }),
462
468
  loader: createPostLoader(slug, config),
463
469
  meta: createPostMeta(slug, config)
464
470
  };
@@ -420,43 +420,49 @@ const blogClientPlugin = (config) => defineClientPlugin({
420
420
  name: "blog",
421
421
  routes: () => ({
422
422
  posts: createRoute("/blog", () => {
423
+ const CustomPosts = config.pageComponents?.posts;
423
424
  return {
424
- PageComponent: () => /* @__PURE__ */ jsx(HomePageComponent, { published: true }),
425
+ PageComponent: CustomPosts ?? (() => /* @__PURE__ */ jsx(HomePageComponent, { published: true })),
425
426
  loader: createPostsLoader(true, config),
426
427
  meta: createPostsListMeta(true, config)
427
428
  };
428
429
  }),
429
430
  drafts: createRoute("/blog/drafts", () => {
431
+ const CustomDrafts = config.pageComponents?.drafts;
430
432
  return {
431
- PageComponent: () => /* @__PURE__ */ jsx(HomePageComponent, { published: false }),
433
+ PageComponent: CustomDrafts ?? (() => /* @__PURE__ */ jsx(HomePageComponent, { published: false })),
432
434
  loader: createPostsLoader(false, config),
433
435
  meta: createPostsListMeta(false, config)
434
436
  };
435
437
  }),
436
438
  newPost: createRoute("/blog/new", () => {
439
+ const CustomNewPost = config.pageComponents?.newPost;
437
440
  return {
438
- PageComponent: NewPostPageComponent,
441
+ PageComponent: CustomNewPost ?? NewPostPageComponent,
439
442
  loader: createNewPostLoader(config),
440
443
  meta: createNewPostMeta(config)
441
444
  };
442
445
  }),
443
446
  editPost: createRoute("/blog/:slug/edit", ({ params: { slug } }) => {
447
+ const CustomEditPost = config.pageComponents?.editPost;
444
448
  return {
445
- PageComponent: () => /* @__PURE__ */ jsx(EditPostPageComponent, { slug }),
449
+ PageComponent: CustomEditPost ? () => /* @__PURE__ */ jsx(CustomEditPost, { slug }) : () => /* @__PURE__ */ jsx(EditPostPageComponent, { slug }),
446
450
  loader: createPostLoader(slug, config, `/blog/${slug}/edit`),
447
451
  meta: createEditPostMeta(slug, config)
448
452
  };
449
453
  }),
450
454
  tag: createRoute("/blog/tag/:tagSlug", ({ params: { tagSlug } }) => {
455
+ const CustomTag = config.pageComponents?.tag;
451
456
  return {
452
- PageComponent: () => /* @__PURE__ */ jsx(TagPageComponent, { tagSlug }),
457
+ PageComponent: CustomTag ? () => /* @__PURE__ */ jsx(CustomTag, { tagSlug }) : () => /* @__PURE__ */ jsx(TagPageComponent, { tagSlug }),
453
458
  loader: createTagLoader(tagSlug, config),
454
459
  meta: createTagMeta(tagSlug, config)
455
460
  };
456
461
  }),
457
462
  post: createRoute("/blog/:slug", ({ params: { slug } }) => {
463
+ const CustomPost = config.pageComponents?.post;
458
464
  return {
459
- PageComponent: () => /* @__PURE__ */ jsx(PostPageComponent, { slug }),
465
+ PageComponent: CustomPost ? () => /* @__PURE__ */ jsx(CustomPost, { slug }) : () => /* @__PURE__ */ jsx(PostPageComponent, { slug }),
460
466
  loader: createPostLoader(slug, config),
461
467
  meta: createPostMeta(slug, config)
462
468
  };
@@ -9,7 +9,7 @@ function DefaultError({ error, resetErrorBoundary }) {
9
9
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-12 px-4 text-center", children: [
10
10
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-full bg-destructive/10 p-4 mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.AlertCircle, { className: "h-8 w-8 text-destructive" }) }),
11
11
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-medium text-foreground mb-2", children: "Something went wrong" }),
12
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-4 max-w-sm", children: error.message || "An unexpected error occurred" }),
12
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mb-4 max-w-sm", children: (error instanceof Error ? error.message : void 0) || "An unexpected error occurred" }),
13
13
  resetErrorBoundary && /* @__PURE__ */ jsxRuntime.jsx(button.Button, { variant: "outline", onClick: resetErrorBoundary, children: "Try again" })
14
14
  ] });
15
15
  }
@@ -7,7 +7,7 @@ function DefaultError({ error, resetErrorBoundary }) {
7
7
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-12 px-4 text-center", children: [
8
8
  /* @__PURE__ */ jsx("div", { className: "rounded-full bg-destructive/10 p-4 mb-4", children: /* @__PURE__ */ jsx(AlertCircle, { className: "h-8 w-8 text-destructive" }) }),
9
9
  /* @__PURE__ */ jsx("h3", { className: "text-lg font-medium text-foreground mb-2", children: "Something went wrong" }),
10
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mb-4 max-w-sm", children: error.message || "An unexpected error occurred" }),
10
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mb-4 max-w-sm", children: (error instanceof Error ? error.message : void 0) || "An unexpected error occurred" }),
11
11
  resetErrorBoundary && /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: resetErrorBoundary, children: "Try again" })
12
12
  ] });
13
13
  }