@chaaskit/client 0.1.1 → 0.1.2

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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/dist/lib/index.js +970 -80
  3. package/dist/lib/index.js.map +1 -1
  4. package/dist/lib/routes/AdminPromoCodesRoute.js +19 -0
  5. package/dist/lib/routes/AdminPromoCodesRoute.js.map +1 -0
  6. package/dist/lib/routes/AdminWaitlistRoute.js +19 -0
  7. package/dist/lib/routes/AdminWaitlistRoute.js.map +1 -0
  8. package/dist/lib/routes.js +47 -37
  9. package/dist/lib/routes.js.map +1 -1
  10. package/dist/lib/ssr-utils.js +36 -16
  11. package/dist/lib/ssr-utils.js.map +1 -1
  12. package/dist/lib/styles.css +37 -0
  13. package/dist/lib/useExtensions-B5nX_8XD.js.map +1 -1
  14. package/package.json +20 -12
  15. package/src/components/MessageItem.tsx +35 -4
  16. package/src/components/MessageList.tsx +51 -5
  17. package/src/components/OAuthAppsSection.tsx +1 -1
  18. package/src/components/Sidebar.tsx +1 -3
  19. package/src/components/ToolCallDisplay.tsx +102 -11
  20. package/src/components/tool-renderers/DocumentListRenderer.tsx +44 -0
  21. package/src/components/tool-renderers/DocumentReadRenderer.tsx +33 -0
  22. package/src/components/tool-renderers/DocumentSaveRenderer.tsx +32 -0
  23. package/src/components/tool-renderers/DocumentSearchRenderer.tsx +33 -0
  24. package/src/components/tool-renderers/index.ts +36 -0
  25. package/src/components/tool-renderers/utils.ts +7 -0
  26. package/src/contexts/AuthContext.tsx +16 -6
  27. package/src/contexts/ConfigContext.tsx +22 -28
  28. package/src/extensions/registry.ts +2 -1
  29. package/src/hooks/__tests__/basePath.test.ts +42 -0
  30. package/src/index.tsx +5 -2
  31. package/src/pages/AdminDashboardPage.tsx +15 -1
  32. package/src/pages/AdminPromoCodesPage.tsx +378 -0
  33. package/src/pages/AdminTeamPage.tsx +29 -1
  34. package/src/pages/AdminTeamsPage.tsx +15 -1
  35. package/src/pages/AdminUsersPage.tsx +15 -1
  36. package/src/pages/AdminWaitlistPage.tsx +156 -0
  37. package/src/pages/RegisterPage.tsx +91 -9
  38. package/src/routes/AdminPromoCodesRoute.tsx +24 -0
  39. package/src/routes/AdminWaitlistRoute.tsx +24 -0
  40. package/src/routes/index.ts +2 -0
  41. package/src/ssr-utils.tsx +32 -12
  42. package/src/stores/chatStore.ts +5 -0
  43. package/dist/favicon.svg +0 -11
  44. package/dist/index.html +0 -17
  45. package/dist/logo.svg +0 -12
@@ -0,0 +1,19 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { ChatProviders, AdminPromoCodesPage } from "../index.js";
3
+ import { S } from "../LoadingSkeletons-IcIC2JPq.js";
4
+ function meta() {
5
+ return [{ title: "Admin - Promo Codes" }];
6
+ }
7
+ function links() {
8
+ return [];
9
+ }
10
+ function AdminPromoCodesRoute() {
11
+ return /* @__PURE__ */ jsx(ChatProviders, { children: /* @__PURE__ */ jsx(AdminPromoCodesPage, {}) });
12
+ }
13
+ export {
14
+ S as LoadingSkeleton,
15
+ AdminPromoCodesRoute as default,
16
+ links,
17
+ meta
18
+ };
19
+ //# sourceMappingURL=AdminPromoCodesRoute.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminPromoCodesRoute.js","sources":["../../../src/routes/AdminPromoCodesRoute.tsx"],"sourcesContent":["/**\n * Admin Promo Codes Route - Promo code management\n */\nimport { ChatProviders } from '../index';\nimport AdminPromoCodesPage from '../pages/AdminPromoCodesPage';\nimport { SimpleLoadingSkeleton } from '../components/LoadingSkeletons';\n\nexport function meta() {\n return [{ title: 'Admin - Promo Codes' }];\n}\n\nexport function links() {\n return [];\n}\n\nexport default function AdminPromoCodesRoute() {\n return (\n <ChatProviders>\n <AdminPromoCodesPage />\n </ChatProviders>\n );\n}\n\nexport { SimpleLoadingSkeleton as LoadingSkeleton };\n"],"names":[],"mappings":";;;AAOO,SAAS,OAAO;AACrB,SAAO,CAAC,EAAE,OAAO,uBAAuB;AAC1C;AAEO,SAAS,QAAQ;AACtB,SAAO,CAAA;AACT;AAEA,SAAwB,uBAAuB;AAC7C,SACE,oBAAC,eAAA,EACC,UAAA,oBAAC,qBAAA,CAAA,CAAoB,GACvB;AAEJ;"}
@@ -0,0 +1,19 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { ChatProviders, AdminWaitlistPage } from "../index.js";
3
+ import { S } from "../LoadingSkeletons-IcIC2JPq.js";
4
+ function meta() {
5
+ return [{ title: "Admin - Waitlist" }];
6
+ }
7
+ function links() {
8
+ return [];
9
+ }
10
+ function AdminWaitlistRoute() {
11
+ return /* @__PURE__ */ jsx(ChatProviders, { children: /* @__PURE__ */ jsx(AdminWaitlistPage, {}) });
12
+ }
13
+ export {
14
+ S as LoadingSkeleton,
15
+ AdminWaitlistRoute as default,
16
+ links,
17
+ meta
18
+ };
19
+ //# sourceMappingURL=AdminWaitlistRoute.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminWaitlistRoute.js","sources":["../../../src/routes/AdminWaitlistRoute.tsx"],"sourcesContent":["/**\n * Admin Waitlist Route - Waitlist management\n */\nimport { ChatProviders } from '../index';\nimport AdminWaitlistPage from '../pages/AdminWaitlistPage';\nimport { SimpleLoadingSkeleton } from '../components/LoadingSkeletons';\n\nexport function meta() {\n return [{ title: 'Admin - Waitlist' }];\n}\n\nexport function links() {\n return [];\n}\n\nexport default function AdminWaitlistRoute() {\n return (\n <ChatProviders>\n <AdminWaitlistPage />\n </ChatProviders>\n );\n}\n\nexport { SimpleLoadingSkeleton as LoadingSkeleton };\n"],"names":[],"mappings":";;;AAOO,SAAS,OAAO;AACrB,SAAO,CAAC,EAAE,OAAO,oBAAoB;AACvC;AAEO,SAAS,QAAQ;AACtB,SAAO,CAAA;AACT;AAEA,SAAwB,qBAAqB;AAC3C,SACE,oBAAC,eAAA,EACC,UAAA,oBAAC,mBAAA,CAAA,CAAkB,GACrB;AAEJ;"}
@@ -7,54 +7,64 @@ import { default as default7, links as links6, meta as meta6 } from "./routes/Ad
7
7
  import { default as default8, links as links7, meta as meta7 } from "./routes/AdminUsersRoute.js";
8
8
  import { default as default9, links as links8, meta as meta8 } from "./routes/AdminTeamsRoute.js";
9
9
  import { default as default10, links as links9, meta as meta9 } from "./routes/AdminTeamRoute.js";
10
- import { default as default11, links as links10, meta as meta10 } from "./routes/VerifyEmailRoute.js";
11
- import { default as default12, links as links11, meta as meta11 } from "./routes/AcceptInviteRoute.js";
12
- import { default as default13, links as links12, meta as meta12 } from "./routes/PricingRoute.js";
13
- import { default as default14, links as links13, meta as meta13 } from "./routes/OAuthConsentRoute.js";
14
- import { default as default15, links as links14, meta as meta14 } from "./routes/PrivacyRoute.js";
15
- import { default as default16, links as links15, meta as meta15 } from "./routes/TermsRoute.js";
16
- import { S, S as S2, S as S3, S as S4, S as S5, S as S6, S as S7, C, S as S8, S as S9, S as S10, S as S11, S as S12, S as S13, S as S14 } from "./LoadingSkeletons-IcIC2JPq.js";
10
+ import { default as default11, links as links10, meta as meta10 } from "./routes/AdminWaitlistRoute.js";
11
+ import { default as default12, links as links11, meta as meta11 } from "./routes/AdminPromoCodesRoute.js";
12
+ import { default as default13, links as links12, meta as meta12 } from "./routes/VerifyEmailRoute.js";
13
+ import { default as default14, links as links13, meta as meta13 } from "./routes/AcceptInviteRoute.js";
14
+ import { default as default15, links as links14, meta as meta14 } from "./routes/PricingRoute.js";
15
+ import { default as default16, links as links15, meta as meta15 } from "./routes/OAuthConsentRoute.js";
16
+ import { default as default17, links as links16, meta as meta16 } from "./routes/PrivacyRoute.js";
17
+ import { default as default18, links as links17, meta as meta17 } from "./routes/TermsRoute.js";
18
+ import { S, S as S2, S as S3, S as S4, S as S5, S as S6, S as S7, S as S8, S as S9, C, S as S10, S as S11, S as S12, S as S13, S as S14, S as S15, S as S16 } from "./LoadingSkeletons-IcIC2JPq.js";
17
19
  export {
18
20
  S as AcceptInviteLoadingSkeleton,
19
- default12 as AcceptInviteRoute,
21
+ default14 as AcceptInviteRoute,
20
22
  S2 as AdminDashboardLoadingSkeleton,
21
23
  default7 as AdminDashboardRoute,
22
- S3 as AdminTeamLoadingSkeleton,
24
+ S3 as AdminPromoCodesLoadingSkeleton,
25
+ default12 as AdminPromoCodesRoute,
26
+ S4 as AdminTeamLoadingSkeleton,
23
27
  default10 as AdminTeamRoute,
24
- S4 as AdminTeamsLoadingSkeleton,
28
+ S5 as AdminTeamsLoadingSkeleton,
25
29
  default9 as AdminTeamsRoute,
26
- S5 as AdminUsersLoadingSkeleton,
30
+ S6 as AdminUsersLoadingSkeleton,
27
31
  default8 as AdminUsersRoute,
28
- S6 as ApiKeysLoadingSkeleton,
32
+ S7 as AdminWaitlistLoadingSkeleton,
33
+ default11 as AdminWaitlistRoute,
34
+ S8 as ApiKeysLoadingSkeleton,
29
35
  default3 as ApiKeysRoute,
30
- S7 as AutomationsLoadingSkeleton,
36
+ S9 as AutomationsLoadingSkeleton,
31
37
  default5 as AutomationsRoute,
32
38
  C as ChatLoadingSkeleton,
33
39
  default2 as ChatRoute,
34
- S8 as DocumentsLoadingSkeleton,
40
+ S10 as DocumentsLoadingSkeleton,
35
41
  default4 as DocumentsRoute,
36
- S9 as OAuthConsentLoadingSkeleton,
37
- default14 as OAuthConsentRoute,
38
- S10 as PricingLoadingSkeleton,
39
- default13 as PricingRoute,
40
- S11 as PrivacyLoadingSkeleton,
41
- default15 as PrivacyRoute,
42
- S12 as TeamSettingsLoadingSkeleton,
42
+ S11 as OAuthConsentLoadingSkeleton,
43
+ default16 as OAuthConsentRoute,
44
+ S12 as PricingLoadingSkeleton,
45
+ default15 as PricingRoute,
46
+ S13 as PrivacyLoadingSkeleton,
47
+ default17 as PrivacyRoute,
48
+ S14 as TeamSettingsLoadingSkeleton,
43
49
  default6 as TeamSettingsRoute,
44
- S13 as TermsLoadingSkeleton,
45
- default16 as TermsRoute,
46
- S14 as VerifyEmailLoadingSkeleton,
47
- default11 as VerifyEmailRoute,
48
- links11 as acceptInviteLinks,
49
- meta11 as acceptInviteMeta,
50
+ S15 as TermsLoadingSkeleton,
51
+ default18 as TermsRoute,
52
+ S16 as VerifyEmailLoadingSkeleton,
53
+ default13 as VerifyEmailRoute,
54
+ links13 as acceptInviteLinks,
55
+ meta13 as acceptInviteMeta,
50
56
  links6 as adminDashboardLinks,
51
57
  meta6 as adminDashboardMeta,
58
+ links11 as adminPromoCodesLinks,
59
+ meta11 as adminPromoCodesMeta,
52
60
  links9 as adminTeamLinks,
53
61
  meta9 as adminTeamMeta,
54
62
  links8 as adminTeamsLinks,
55
63
  meta8 as adminTeamsMeta,
56
64
  links7 as adminUsersLinks,
57
65
  meta7 as adminUsersMeta,
66
+ links10 as adminWaitlistLinks,
67
+ meta10 as adminWaitlistMeta,
58
68
  links2 as apiKeysLinks,
59
69
  meta2 as apiKeysMeta,
60
70
  links4 as automationsLinks,
@@ -63,17 +73,17 @@ export {
63
73
  meta as chatMeta,
64
74
  links3 as documentsLinks,
65
75
  meta3 as documentsMeta,
66
- links13 as oauthConsentLinks,
67
- meta13 as oauthConsentMeta,
68
- links12 as pricingLinks,
69
- meta12 as pricingMeta,
70
- links14 as privacyLinks,
71
- meta14 as privacyMeta,
76
+ links15 as oauthConsentLinks,
77
+ meta15 as oauthConsentMeta,
78
+ links14 as pricingLinks,
79
+ meta14 as pricingMeta,
80
+ links16 as privacyLinks,
81
+ meta16 as privacyMeta,
72
82
  links5 as teamSettingsLinks,
73
83
  meta5 as teamSettingsMeta,
74
- links15 as termsLinks,
75
- meta15 as termsMeta,
76
- links10 as verifyEmailLinks,
77
- meta10 as verifyEmailMeta
84
+ links17 as termsLinks,
85
+ meta17 as termsMeta,
86
+ links12 as verifyEmailLinks,
87
+ meta12 as verifyEmailMeta
78
88
  };
79
89
  //# sourceMappingURL=routes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"routes.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;"}
@@ -2,37 +2,57 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { lazy, Suspense } from "react";
3
3
  import { a as ClientOnly, C as ChatLoadingSkeleton, S as SimpleLoadingSkeleton } from "./LoadingSkeletons-IcIC2JPq.js";
4
4
  function ConfigScript({ config }) {
5
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
5
+ var _a, _b, _c, _d, _e, _f, _g, _h;
6
6
  const safeConfig = {
7
7
  app: config.app,
8
8
  ui: config.ui,
9
9
  theming: config.theming,
10
10
  auth: {
11
11
  methods: (_a = config.auth) == null ? void 0 : _a.methods,
12
- allowUnauthenticated: (_b = config.auth) == null ? void 0 : _b.allowUnauthenticated
12
+ allowUnauthenticated: (_b = config.auth) == null ? void 0 : _b.allowUnauthenticated,
13
+ magicLink: (_c = config.auth) == null ? void 0 : _c.magicLink,
14
+ emailVerification: (_d = config.auth) == null ? void 0 : _d.emailVerification,
15
+ gating: (_e = config.auth) == null ? void 0 : _e.gating
13
16
  },
14
17
  payments: {
15
- enabled: (_c = config.payments) == null ? void 0 : _c.enabled,
16
- plans: (_e = (_d = config.payments) == null ? void 0 : _d.plans) == null ? void 0 : _e.map((plan) => ({
17
- id: plan.id,
18
- name: plan.name,
19
- type: plan.type
20
- }))
18
+ enabled: (_f = config.payments) == null ? void 0 : _f.enabled,
19
+ provider: (_g = config.payments) == null ? void 0 : _g.provider
21
20
  },
22
21
  legal: config.legal,
23
22
  sharing: config.sharing,
24
23
  teams: config.teams,
25
24
  projects: config.projects,
26
- documents: {
27
- enabled: (_f = config.documents) == null ? void 0 : _f.enabled
28
- },
25
+ documents: config.documents ? {
26
+ enabled: config.documents.enabled,
27
+ maxFileSizeMB: config.documents.maxFileSizeMB,
28
+ hybridThreshold: config.documents.hybridThreshold,
29
+ acceptedTypes: config.documents.acceptedTypes
30
+ } : void 0,
29
31
  api: {
30
- enabled: (_g = config.api) == null ? void 0 : _g.enabled
32
+ enabled: (_h = config.api) == null ? void 0 : _h.enabled
31
33
  },
32
- promptTemplates: {
33
- enabled: (_h = config.promptTemplates) == null ? void 0 : _h.enabled,
34
- allowUserTemplates: (_i = config.promptTemplates) == null ? void 0 : _i.allowUserTemplates
35
- }
34
+ promptTemplates: config.promptTemplates ? {
35
+ enabled: config.promptTemplates.enabled,
36
+ allowUserTemplates: config.promptTemplates.allowUserTemplates
37
+ } : void 0,
38
+ scheduledPrompts: config.scheduledPrompts ? {
39
+ enabled: config.scheduledPrompts.enabled,
40
+ featureName: config.scheduledPrompts.featureName,
41
+ allowUserPrompts: config.scheduledPrompts.allowUserPrompts,
42
+ allowTeamPrompts: config.scheduledPrompts.allowTeamPrompts,
43
+ defaultTimezone: config.scheduledPrompts.defaultTimezone,
44
+ defaultMaxUserPrompts: config.scheduledPrompts.defaultMaxUserPrompts,
45
+ defaultMaxTeamPrompts: config.scheduledPrompts.defaultMaxTeamPrompts
46
+ } : void 0,
47
+ credits: config.credits ? {
48
+ enabled: config.credits.enabled,
49
+ expiryEnabled: config.credits.expiryEnabled,
50
+ promoEnabled: config.credits.promoEnabled
51
+ } : void 0,
52
+ metering: config.metering ? {
53
+ enabled: config.metering.enabled,
54
+ recordPromptCompletion: config.metering.recordPromptCompletion
55
+ } : void 0
36
56
  };
37
57
  return /* @__PURE__ */ jsx(
38
58
  "script",
@@ -1 +1 @@
1
- {"version":3,"file":"ssr-utils.js","sources":["../../src/ssr-utils.tsx"],"sourcesContent":["/**\n * SSR-safe utilities for @chaaskit/client\n *\n * This entry point ONLY exports components that are safe to import during SSR.\n * Use this in route files instead of importing from the main entry point\n * to avoid pulling in browser-only dependencies like react-markdown.\n *\n * @example\n * ```tsx\n * // In your route file\n * import { createRoute, SimpleLoadingSkeleton } from '@chaaskit/client/ssr-utils';\n *\n * export const { meta, links, default: Component } = createRoute({\n * title: 'Verify Email',\n * load: () => import('@chaaskit/client/routes/VerifyEmailRoute'),\n * });\n * export default Component;\n * ```\n */\n\nimport { lazy, Suspense, type ComponentType, type LazyExoticComponent } from 'react';\nimport type { AppConfig } from '@chaaskit/shared';\nimport { ClientOnly } from './components/ClientOnly';\nimport { ChatLoadingSkeleton, SimpleLoadingSkeleton } from './components/LoadingSkeletons';\n\nexport { ClientOnly, ChatLoadingSkeleton, SimpleLoadingSkeleton };\n\n/**\n * Props for ConfigScript component.\n */\ninterface ConfigScriptProps {\n /**\n * The app config to inject into the page.\n * This should come from the SSR loader.\n */\n config: AppConfig;\n}\n\n/**\n * Injects the app config into the page as a script tag.\n * Place this in the <head> of your root layout to make the config\n * available immediately on page load, avoiding flash of default values.\n *\n * @example\n * ```tsx\n * // app/root.tsx\n * import { ConfigScript } from '@chaaskit/client/ssr-utils';\n * import { config } from '../config/app.config';\n *\n * export default function Root() {\n * return (\n * <html>\n * <head>\n * <ConfigScript config={config} />\n * </head>\n * <body>...</body>\n * </html>\n * );\n * }\n * ```\n */\nexport function ConfigScript({ config }: ConfigScriptProps) {\n // Only include the UI-related config to avoid exposing sensitive data\n const safeConfig = {\n app: config.app,\n ui: config.ui,\n theming: config.theming,\n auth: {\n methods: config.auth?.methods,\n allowUnauthenticated: config.auth?.allowUnauthenticated,\n },\n payments: {\n enabled: config.payments?.enabled,\n plans: config.payments?.plans?.map(plan => ({\n id: plan.id,\n name: plan.name,\n type: plan.type,\n })),\n },\n legal: config.legal,\n sharing: config.sharing,\n teams: config.teams,\n projects: config.projects,\n documents: {\n enabled: config.documents?.enabled,\n },\n api: {\n enabled: config.api?.enabled,\n },\n promptTemplates: {\n enabled: config.promptTemplates?.enabled,\n allowUserTemplates: config.promptTemplates?.allowUserTemplates,\n },\n };\n\n return (\n <script\n dangerouslySetInnerHTML={{\n __html: `window.__CHAASKIT_CONFIG__=${JSON.stringify(safeConfig)};`,\n }}\n />\n );\n}\n\n/**\n * Route configuration options\n */\nexport interface RouteConfig {\n /** Page title for meta tag */\n title: string;\n /** Dynamic import function for the route component */\n load: () => Promise<{ default: ComponentType<any> }>;\n /** Loading skeleton to show during SSR and lazy load (defaults to SimpleLoadingSkeleton) */\n skeleton?: 'simple' | 'chat';\n}\n\n/**\n * Creates a route module with meta, links, and default component.\n * This reduces route files to just a few lines.\n *\n * @example\n * ```tsx\n * import { createRoute } from '@chaaskit/client/ssr-utils';\n *\n * export const { meta, links, default: VerifyEmail } = createRoute({\n * title: 'Verify Email',\n * load: () => import('@chaaskit/client/routes/VerifyEmailRoute'),\n * });\n * export default VerifyEmail;\n * ```\n */\nexport function createRoute(config: RouteConfig) {\n const { title, load, skeleton = 'simple' } = config;\n const LazyComponent = lazy(load);\n const Skeleton = skeleton === 'chat' ? ChatLoadingSkeleton : SimpleLoadingSkeleton;\n\n function meta() {\n return [{ title }];\n }\n\n function links() {\n // CSS is bundled via app's Tailwind preset - no separate stylesheet needed\n return [];\n }\n\n function RouteComponent() {\n return (\n <ClientOnly fallback={<Skeleton />}>\n {() => (\n <Suspense fallback={<Skeleton />}>\n <LazyComponent />\n </Suspense>\n )}\n </ClientOnly>\n );\n }\n\n return {\n meta,\n links,\n default: RouteComponent,\n };\n}\n"],"names":[],"mappings":";;;AA6DO,SAAS,aAAa,EAAE,UAA6B;;AAE1D,QAAM,aAAa;AAAA,IACjB,KAAK,OAAO;AAAA,IACZ,IAAI,OAAO;AAAA,IACX,SAAS,OAAO;AAAA,IAChB,MAAM;AAAA,MACJ,UAAS,YAAO,SAAP,mBAAa;AAAA,MACtB,uBAAsB,YAAO,SAAP,mBAAa;AAAA,IAAA;AAAA,IAErC,UAAU;AAAA,MACR,UAAS,YAAO,aAAP,mBAAiB;AAAA,MAC1B,QAAO,kBAAO,aAAP,mBAAiB,UAAjB,mBAAwB,IAAI,CAAA,UAAS;AAAA,QAC1C,IAAI,KAAK;AAAA,QACT,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,MAAA;AAAA,IACX;AAAA,IAEJ,OAAO,OAAO;AAAA,IACd,SAAS,OAAO;AAAA,IAChB,OAAO,OAAO;AAAA,IACd,UAAU,OAAO;AAAA,IACjB,WAAW;AAAA,MACT,UAAS,YAAO,cAAP,mBAAkB;AAAA,IAAA;AAAA,IAE7B,KAAK;AAAA,MACH,UAAS,YAAO,QAAP,mBAAY;AAAA,IAAA;AAAA,IAEvB,iBAAiB;AAAA,MACf,UAAS,YAAO,oBAAP,mBAAwB;AAAA,MACjC,qBAAoB,YAAO,oBAAP,mBAAwB;AAAA,IAAA;AAAA,EAC9C;AAGF,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,yBAAyB;AAAA,QACvB,QAAQ,8BAA8B,KAAK,UAAU,UAAU,CAAC;AAAA,MAAA;AAAA,IAClE;AAAA,EAAA;AAGN;AA6BO,SAAS,YAAY,QAAqB;AAC/C,QAAM,EAAE,OAAO,MAAM,WAAW,aAAa;AAC7C,QAAM,gBAAgB,KAAK,IAAI;AAC/B,QAAM,WAAW,aAAa,SAAS,sBAAsB;AAE7D,WAAS,OAAO;AACd,WAAO,CAAC,EAAE,OAAO;AAAA,EACnB;AAEA,WAAS,QAAQ;AAEf,WAAO,CAAA;AAAA,EACT;AAEA,WAAS,iBAAiB;AACxB,+BACG,YAAA,EAAW,UAAU,oBAAC,UAAA,EAAS,GAC7B,UAAA,MACC,oBAAC,UAAA,EAAS,8BAAW,UAAA,CAAA,CAAS,GAC5B,UAAA,oBAAC,eAAA,CAAA,CAAc,GACjB,GAEJ;AAAA,EAEJ;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,SAAS;AAAA,EAAA;AAEb;"}
1
+ {"version":3,"file":"ssr-utils.js","sources":["../../src/ssr-utils.tsx"],"sourcesContent":["/**\n * SSR-safe utilities for @chaaskit/client\n *\n * This entry point ONLY exports components that are safe to import during SSR.\n * Use this in route files instead of importing from the main entry point\n * to avoid pulling in browser-only dependencies like react-markdown.\n *\n * @example\n * ```tsx\n * // In your route file\n * import { createRoute, SimpleLoadingSkeleton } from '@chaaskit/client/ssr-utils';\n *\n * export const { meta, links, default: Component } = createRoute({\n * title: 'Verify Email',\n * load: () => import('@chaaskit/client/routes/VerifyEmailRoute'),\n * });\n * export default Component;\n * ```\n */\n\nimport { lazy, Suspense, type ComponentType, type LazyExoticComponent } from 'react';\nimport type { AppConfig } from '@chaaskit/shared';\nimport { ClientOnly } from './components/ClientOnly';\nimport { ChatLoadingSkeleton, SimpleLoadingSkeleton } from './components/LoadingSkeletons';\n\nexport { ClientOnly, ChatLoadingSkeleton, SimpleLoadingSkeleton };\n\n/**\n * Props for ConfigScript component.\n */\ninterface ConfigScriptProps {\n /**\n * The app config to inject into the page.\n * This should come from the SSR loader.\n */\n config: AppConfig;\n}\n\n/**\n * Injects the app config into the page as a script tag.\n * Place this in the <head> of your root layout to make the config\n * available immediately on page load, avoiding flash of default values.\n *\n * @example\n * ```tsx\n * // app/root.tsx\n * import { ConfigScript } from '@chaaskit/client/ssr-utils';\n * import { config } from '../config/app.config';\n *\n * export default function Root() {\n * return (\n * <html>\n * <head>\n * <ConfigScript config={config} />\n * </head>\n * <body>...</body>\n * </html>\n * );\n * }\n * ```\n */\nexport function ConfigScript({ config }: ConfigScriptProps) {\n // Only include the UI-related config to avoid exposing sensitive data\n const safeConfig = {\n app: config.app,\n ui: config.ui,\n theming: config.theming,\n auth: {\n methods: config.auth?.methods,\n allowUnauthenticated: config.auth?.allowUnauthenticated,\n magicLink: config.auth?.magicLink,\n emailVerification: config.auth?.emailVerification,\n gating: config.auth?.gating,\n },\n payments: {\n enabled: config.payments?.enabled,\n provider: config.payments?.provider,\n },\n legal: config.legal,\n sharing: config.sharing,\n teams: config.teams,\n projects: config.projects,\n documents: config.documents ? {\n enabled: config.documents.enabled,\n maxFileSizeMB: config.documents.maxFileSizeMB,\n hybridThreshold: config.documents.hybridThreshold,\n acceptedTypes: config.documents.acceptedTypes,\n } : undefined,\n api: {\n enabled: config.api?.enabled,\n },\n promptTemplates: config.promptTemplates ? {\n enabled: config.promptTemplates.enabled,\n allowUserTemplates: config.promptTemplates.allowUserTemplates,\n } : undefined,\n scheduledPrompts: config.scheduledPrompts ? {\n enabled: config.scheduledPrompts.enabled,\n featureName: config.scheduledPrompts.featureName,\n allowUserPrompts: config.scheduledPrompts.allowUserPrompts,\n allowTeamPrompts: config.scheduledPrompts.allowTeamPrompts,\n defaultTimezone: config.scheduledPrompts.defaultTimezone,\n defaultMaxUserPrompts: config.scheduledPrompts.defaultMaxUserPrompts,\n defaultMaxTeamPrompts: config.scheduledPrompts.defaultMaxTeamPrompts,\n } : undefined,\n credits: config.credits ? {\n enabled: config.credits.enabled,\n expiryEnabled: config.credits.expiryEnabled,\n promoEnabled: config.credits.promoEnabled,\n } : undefined,\n metering: config.metering ? {\n enabled: config.metering.enabled,\n recordPromptCompletion: config.metering.recordPromptCompletion,\n } : undefined,\n };\n\n return (\n <script\n dangerouslySetInnerHTML={{\n __html: `window.__CHAASKIT_CONFIG__=${JSON.stringify(safeConfig)};`,\n }}\n />\n );\n}\n\n/**\n * Route configuration options\n */\nexport interface RouteConfig {\n /** Page title for meta tag */\n title: string;\n /** Dynamic import function for the route component */\n load: () => Promise<{ default: ComponentType<any> }>;\n /** Loading skeleton to show during SSR and lazy load (defaults to SimpleLoadingSkeleton) */\n skeleton?: 'simple' | 'chat';\n}\n\n/**\n * Creates a route module with meta, links, and default component.\n * This reduces route files to just a few lines.\n *\n * @example\n * ```tsx\n * import { createRoute } from '@chaaskit/client/ssr-utils';\n *\n * export const { meta, links, default: VerifyEmail } = createRoute({\n * title: 'Verify Email',\n * load: () => import('@chaaskit/client/routes/VerifyEmailRoute'),\n * });\n * export default VerifyEmail;\n * ```\n */\nexport function createRoute(config: RouteConfig) {\n const { title, load, skeleton = 'simple' } = config;\n const LazyComponent = lazy(load);\n const Skeleton = skeleton === 'chat' ? ChatLoadingSkeleton : SimpleLoadingSkeleton;\n\n function meta() {\n return [{ title }];\n }\n\n function links() {\n // CSS is bundled via app's Tailwind preset - no separate stylesheet needed\n return [];\n }\n\n function RouteComponent() {\n return (\n <ClientOnly fallback={<Skeleton />}>\n {() => (\n <Suspense fallback={<Skeleton />}>\n <LazyComponent />\n </Suspense>\n )}\n </ClientOnly>\n );\n }\n\n return {\n meta,\n links,\n default: RouteComponent,\n };\n}\n"],"names":[],"mappings":";;;AA6DO,SAAS,aAAa,EAAE,UAA6B;;AAE1D,QAAM,aAAa;AAAA,IACjB,KAAK,OAAO;AAAA,IACZ,IAAI,OAAO;AAAA,IACX,SAAS,OAAO;AAAA,IAChB,MAAM;AAAA,MACJ,UAAS,YAAO,SAAP,mBAAa;AAAA,MACtB,uBAAsB,YAAO,SAAP,mBAAa;AAAA,MACnC,YAAW,YAAO,SAAP,mBAAa;AAAA,MACxB,oBAAmB,YAAO,SAAP,mBAAa;AAAA,MAChC,SAAQ,YAAO,SAAP,mBAAa;AAAA,IAAA;AAAA,IAEvB,UAAU;AAAA,MACR,UAAS,YAAO,aAAP,mBAAiB;AAAA,MAC1B,WAAU,YAAO,aAAP,mBAAiB;AAAA,IAAA;AAAA,IAE7B,OAAO,OAAO;AAAA,IACd,SAAS,OAAO;AAAA,IAChB,OAAO,OAAO;AAAA,IACd,UAAU,OAAO;AAAA,IACjB,WAAW,OAAO,YAAY;AAAA,MAC5B,SAAS,OAAO,UAAU;AAAA,MAC1B,eAAe,OAAO,UAAU;AAAA,MAChC,iBAAiB,OAAO,UAAU;AAAA,MAClC,eAAe,OAAO,UAAU;AAAA,IAAA,IAC9B;AAAA,IACJ,KAAK;AAAA,MACH,UAAS,YAAO,QAAP,mBAAY;AAAA,IAAA;AAAA,IAEvB,iBAAiB,OAAO,kBAAkB;AAAA,MACxC,SAAS,OAAO,gBAAgB;AAAA,MAChC,oBAAoB,OAAO,gBAAgB;AAAA,IAAA,IACzC;AAAA,IACJ,kBAAkB,OAAO,mBAAmB;AAAA,MAC1C,SAAS,OAAO,iBAAiB;AAAA,MACjC,aAAa,OAAO,iBAAiB;AAAA,MACrC,kBAAkB,OAAO,iBAAiB;AAAA,MAC1C,kBAAkB,OAAO,iBAAiB;AAAA,MAC1C,iBAAiB,OAAO,iBAAiB;AAAA,MACzC,uBAAuB,OAAO,iBAAiB;AAAA,MAC/C,uBAAuB,OAAO,iBAAiB;AAAA,IAAA,IAC7C;AAAA,IACJ,SAAS,OAAO,UAAU;AAAA,MACxB,SAAS,OAAO,QAAQ;AAAA,MACxB,eAAe,OAAO,QAAQ;AAAA,MAC9B,cAAc,OAAO,QAAQ;AAAA,IAAA,IAC3B;AAAA,IACJ,UAAU,OAAO,WAAW;AAAA,MAC1B,SAAS,OAAO,SAAS;AAAA,MACzB,wBAAwB,OAAO,SAAS;AAAA,IAAA,IACtC;AAAA,EAAA;AAGN,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,yBAAyB;AAAA,QACvB,QAAQ,8BAA8B,KAAK,UAAU,UAAU,CAAC;AAAA,MAAA;AAAA,IAClE;AAAA,EAAA;AAGN;AA6BO,SAAS,YAAY,QAAqB;AAC/C,QAAM,EAAE,OAAO,MAAM,WAAW,aAAa;AAC7C,QAAM,gBAAgB,KAAK,IAAI;AAC/B,QAAM,WAAW,aAAa,SAAS,sBAAsB;AAE7D,WAAS,OAAO;AACd,WAAO,CAAC,EAAE,OAAO;AAAA,EACnB;AAEA,WAAS,QAAQ;AAEf,WAAO,CAAA;AAAA,EACT;AAEA,WAAS,iBAAiB;AACxB,+BACG,YAAA,EAAW,UAAU,oBAAC,UAAA,EAAS,GAC7B,UAAA,MACC,oBAAC,UAAA,EAAS,8BAAW,UAAA,CAAA,CAAS,GAC5B,UAAA,oBAAC,eAAA,CAAA,CAAc,GACjB,GAEJ;AAAA,EAEJ;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,SAAS;AAAA,EAAA;AAEb;"}
@@ -801,6 +801,9 @@ video {
801
801
  .min-w-0 {
802
802
  min-width: 0px;
803
803
  }
804
+ .min-w-\[160px\] {
805
+ min-width: 160px;
806
+ }
804
807
  .min-w-\[200px\] {
805
808
  min-width: 200px;
806
809
  }
@@ -1179,6 +1182,9 @@ video {
1179
1182
  .border-purple-500\/20 {
1180
1183
  border-color: rgb(168 85 247 / 0.2);
1181
1184
  }
1185
+ .border-success\/30 {
1186
+ border-color: rgb(var(--color-success) / 0.3);
1187
+ }
1182
1188
  .border-success\/50 {
1183
1189
  border-color: rgb(var(--color-success) / 0.5);
1184
1190
  }
@@ -1219,6 +1225,9 @@ video {
1219
1225
  .bg-background-secondary\/30 {
1220
1226
  background-color: rgb(var(--color-background-secondary) / 0.3);
1221
1227
  }
1228
+ .bg-background-secondary\/40 {
1229
+ background-color: rgb(var(--color-background-secondary) / 0.4);
1230
+ }
1222
1231
  .bg-background-secondary\/50 {
1223
1232
  background-color: rgb(var(--color-background-secondary) / 0.5);
1224
1233
  }
@@ -2286,6 +2295,18 @@ kbd {
2286
2295
  }
2287
2296
  @media (min-width: 768px) {
2288
2297
 
2298
+ .md\:col-span-2 {
2299
+ grid-column: span 2 / span 2;
2300
+ }
2301
+
2302
+ .md\:col-span-3 {
2303
+ grid-column: span 3 / span 3;
2304
+ }
2305
+
2306
+ .md\:col-span-4 {
2307
+ grid-column: span 4 / span 4;
2308
+ }
2309
+
2289
2310
  .md\:block {
2290
2311
  display: block;
2291
2312
  }
@@ -2298,6 +2319,10 @@ kbd {
2298
2319
  display: none;
2299
2320
  }
2300
2321
 
2322
+ .md\:grid-cols-12 {
2323
+ grid-template-columns: repeat(12, minmax(0, 1fr));
2324
+ }
2325
+
2301
2326
  .md\:grid-cols-2 {
2302
2327
  grid-template-columns: repeat(2, minmax(0, 1fr));
2303
2328
  }
@@ -2313,6 +2338,10 @@ kbd {
2313
2338
  .md\:justify-between {
2314
2339
  justify-content: space-between;
2315
2340
  }
2341
+
2342
+ .md\:gap-4 {
2343
+ gap: 1rem;
2344
+ }
2316
2345
  }
2317
2346
  @media (min-width: 1024px) {
2318
2347
 
@@ -2320,6 +2349,14 @@ kbd {
2320
2349
  position: relative;
2321
2350
  }
2322
2351
 
2352
+ .lg\:col-span-1 {
2353
+ grid-column: span 1 / span 1;
2354
+ }
2355
+
2356
+ .lg\:col-span-2 {
2357
+ grid-column: span 2 / span 2;
2358
+ }
2359
+
2323
2360
  .lg\:hidden {
2324
2361
  display: none;
2325
2362
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useExtensions-B5nX_8XD.js","sources":["../../src/extensions/registry.ts","../../src/extensions/useExtensions.ts"],"sourcesContent":["import type { ComponentType } from 'react';\n\n/**\n * Page extension configuration\n */\nexport interface PageExtension {\n /** Unique identifier for the page */\n id: string;\n /** URL path for the page (e.g., \"/analytics\") */\n path: string;\n /** Display label for navigation */\n label: string;\n /** Optional icon name from lucide-react */\n icon?: string;\n /** React component to render for this page */\n component: ComponentType;\n /** Whether to show in the sidebar navigation */\n showInSidebar?: boolean;\n /** Whether this page requires authentication */\n requiresAuth?: boolean;\n /** Whether this page requires admin access */\n requiresAdmin?: boolean;\n}\n\n/**\n * Tool result renderer extension\n */\nexport interface ToolExtension {\n /** Tool name to match */\n name: string;\n /** Description of the tool */\n description: string;\n /** Custom renderer for tool results */\n resultRenderer?: ComponentType<{ result: unknown }>;\n}\n\n/**\n * Component override extension\n */\nexport interface ComponentOverride {\n /** Component slot to override */\n slot: 'header' | 'footer' | 'sidebar-header' | 'sidebar-footer' | 'message-actions';\n /** Component to render in the slot */\n component: ComponentType<Record<string, unknown>>;\n}\n\n/**\n * Client-side extension registry for customizing the ChaasKit UI\n *\n * Note: getPages(), getSidebarPages(), and getTools() cache their results\n * to support React's useSyncExternalStore which requires stable references.\n */\nclass ClientExtensionRegistry {\n private pages: Map<string, PageExtension> = new Map();\n private tools: Map<string, ToolExtension> = new Map();\n private overrides: Map<string, ComponentOverride> = new Map();\n private listeners: Set<() => void> = new Set();\n\n // Cached arrays for useSyncExternalStore compatibility\n // These are only updated when the underlying data changes\n private cachedPages: PageExtension[] = [];\n private cachedSidebarPages: PageExtension[] = [];\n private cachedTools: ToolExtension[] = [];\n\n /**\n * Register a custom page\n */\n registerPage(page: PageExtension): void {\n this.pages.set(page.id, page);\n this.invalidateCaches();\n this.notifyListeners();\n }\n\n /**\n * Unregister a custom page\n */\n unregisterPage(id: string): boolean {\n const result = this.pages.delete(id);\n if (result) {\n this.invalidateCaches();\n this.notifyListeners();\n }\n return result;\n }\n\n /**\n * Get all registered pages\n * Returns a cached array for useSyncExternalStore compatibility\n */\n getPages(): PageExtension[] {\n return this.cachedPages;\n }\n\n /**\n * Get pages that should appear in the sidebar\n * Returns a cached array for useSyncExternalStore compatibility\n */\n getSidebarPages(): PageExtension[] {\n return this.cachedSidebarPages;\n }\n\n /**\n * Register a custom tool renderer\n */\n registerTool(tool: ToolExtension): void {\n this.tools.set(tool.name, tool);\n this.invalidateCaches();\n this.notifyListeners();\n }\n\n /**\n * Unregister a tool renderer\n */\n unregisterTool(name: string): boolean {\n const result = this.tools.delete(name);\n if (result) {\n this.invalidateCaches();\n this.notifyListeners();\n }\n return result;\n }\n\n /**\n * Get all registered tools\n * Returns a cached array for useSyncExternalStore compatibility\n */\n getTools(): ToolExtension[] {\n return this.cachedTools;\n }\n\n /**\n * Get a specific tool by name\n */\n getTool(name: string): ToolExtension | undefined {\n return this.tools.get(name);\n }\n\n /**\n * Register a component override\n */\n registerOverride(override: ComponentOverride): void {\n this.overrides.set(override.slot, override);\n this.notifyListeners();\n }\n\n /**\n * Get a component override for a slot\n */\n getOverride(slot: string): ComponentOverride | undefined {\n return this.overrides.get(slot);\n }\n\n /**\n * Subscribe to registry changes\n */\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private invalidateCaches(): void {\n this.cachedPages = Array.from(this.pages.values());\n this.cachedSidebarPages = this.cachedPages.filter(p => p.showInSidebar);\n this.cachedTools = Array.from(this.tools.values());\n }\n\n private notifyListeners(): void {\n this.listeners.forEach(listener => listener());\n }\n\n /**\n * Clear all registered extensions\n */\n clear(): void {\n this.pages.clear();\n this.tools.clear();\n this.overrides.clear();\n this.invalidateCaches();\n this.notifyListeners();\n }\n}\n\n// Singleton instance\nexport const clientRegistry = new ClientExtensionRegistry();\n\n// Export for use in user extensions\nexport default clientRegistry;\n","import { useSyncExternalStore, useCallback } from 'react';\nimport { clientRegistry, type PageExtension, type ToolExtension, type ComponentOverride } from './registry';\n\n/**\n * Hook to access extension pages with automatic updates\n */\nexport function useExtensionPages(): PageExtension[] {\n const getSnapshot = useCallback(() => clientRegistry.getPages(), []);\n const subscribe = useCallback((callback: () => void) => clientRegistry.subscribe(callback), []);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Hook to access sidebar pages with automatic updates\n */\nexport function useSidebarPages(): PageExtension[] {\n const getSnapshot = useCallback(() => clientRegistry.getSidebarPages(), []);\n const subscribe = useCallback((callback: () => void) => clientRegistry.subscribe(callback), []);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Hook to access extension tools with automatic updates\n */\nexport function useExtensionTools(): ToolExtension[] {\n const getSnapshot = useCallback(() => clientRegistry.getTools(), []);\n const subscribe = useCallback((callback: () => void) => clientRegistry.subscribe(callback), []);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Hook to get a specific tool renderer\n */\nexport function useToolRenderer(toolName: string): ToolExtension | undefined {\n const getSnapshot = useCallback(() => clientRegistry.getTool(toolName), [toolName]);\n const subscribe = useCallback((callback: () => void) => clientRegistry.subscribe(callback), []);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Hook to get a component override\n */\nexport function useComponentOverride(slot: string): ComponentOverride | undefined {\n const getSnapshot = useCallback(() => clientRegistry.getOverride(slot), [slot]);\n const subscribe = useCallback((callback: () => void) => clientRegistry.subscribe(callback), []);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useExtensions-B5nX_8XD.js","sources":["../../src/extensions/registry.ts","../../src/extensions/useExtensions.ts"],"sourcesContent":["import type { ComponentType } from 'react';\nimport type { ToolCall, ToolResult } from '@chaaskit/shared';\n\n/**\n * Page extension configuration\n */\nexport interface PageExtension {\n /** Unique identifier for the page */\n id: string;\n /** URL path for the page (e.g., \"/analytics\") */\n path: string;\n /** Display label for navigation */\n label: string;\n /** Optional icon name from lucide-react */\n icon?: string;\n /** React component to render for this page */\n component: ComponentType;\n /** Whether to show in the sidebar navigation */\n showInSidebar?: boolean;\n /** Whether this page requires authentication */\n requiresAuth?: boolean;\n /** Whether this page requires admin access */\n requiresAdmin?: boolean;\n}\n\n/**\n * Tool result renderer extension\n */\nexport interface ToolExtension {\n /** Tool name to match */\n name: string;\n /** Description of the tool */\n description: string;\n /** Custom renderer for tool results */\n resultRenderer?: ComponentType<{ toolCall: ToolCall; toolResult: ToolResult }>;\n}\n\n/**\n * Component override extension\n */\nexport interface ComponentOverride {\n /** Component slot to override */\n slot: 'header' | 'footer' | 'sidebar-header' | 'sidebar-footer' | 'message-actions';\n /** Component to render in the slot */\n component: ComponentType<Record<string, unknown>>;\n}\n\n/**\n * Client-side extension registry for customizing the ChaasKit UI\n *\n * Note: getPages(), getSidebarPages(), and getTools() cache their results\n * to support React's useSyncExternalStore which requires stable references.\n */\nclass ClientExtensionRegistry {\n private pages: Map<string, PageExtension> = new Map();\n private tools: Map<string, ToolExtension> = new Map();\n private overrides: Map<string, ComponentOverride> = new Map();\n private listeners: Set<() => void> = new Set();\n\n // Cached arrays for useSyncExternalStore compatibility\n // These are only updated when the underlying data changes\n private cachedPages: PageExtension[] = [];\n private cachedSidebarPages: PageExtension[] = [];\n private cachedTools: ToolExtension[] = [];\n\n /**\n * Register a custom page\n */\n registerPage(page: PageExtension): void {\n this.pages.set(page.id, page);\n this.invalidateCaches();\n this.notifyListeners();\n }\n\n /**\n * Unregister a custom page\n */\n unregisterPage(id: string): boolean {\n const result = this.pages.delete(id);\n if (result) {\n this.invalidateCaches();\n this.notifyListeners();\n }\n return result;\n }\n\n /**\n * Get all registered pages\n * Returns a cached array for useSyncExternalStore compatibility\n */\n getPages(): PageExtension[] {\n return this.cachedPages;\n }\n\n /**\n * Get pages that should appear in the sidebar\n * Returns a cached array for useSyncExternalStore compatibility\n */\n getSidebarPages(): PageExtension[] {\n return this.cachedSidebarPages;\n }\n\n /**\n * Register a custom tool renderer\n */\n registerTool(tool: ToolExtension): void {\n this.tools.set(tool.name, tool);\n this.invalidateCaches();\n this.notifyListeners();\n }\n\n /**\n * Unregister a tool renderer\n */\n unregisterTool(name: string): boolean {\n const result = this.tools.delete(name);\n if (result) {\n this.invalidateCaches();\n this.notifyListeners();\n }\n return result;\n }\n\n /**\n * Get all registered tools\n * Returns a cached array for useSyncExternalStore compatibility\n */\n getTools(): ToolExtension[] {\n return this.cachedTools;\n }\n\n /**\n * Get a specific tool by name\n */\n getTool(name: string): ToolExtension | undefined {\n return this.tools.get(name);\n }\n\n /**\n * Register a component override\n */\n registerOverride(override: ComponentOverride): void {\n this.overrides.set(override.slot, override);\n this.notifyListeners();\n }\n\n /**\n * Get a component override for a slot\n */\n getOverride(slot: string): ComponentOverride | undefined {\n return this.overrides.get(slot);\n }\n\n /**\n * Subscribe to registry changes\n */\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private invalidateCaches(): void {\n this.cachedPages = Array.from(this.pages.values());\n this.cachedSidebarPages = this.cachedPages.filter(p => p.showInSidebar);\n this.cachedTools = Array.from(this.tools.values());\n }\n\n private notifyListeners(): void {\n this.listeners.forEach(listener => listener());\n }\n\n /**\n * Clear all registered extensions\n */\n clear(): void {\n this.pages.clear();\n this.tools.clear();\n this.overrides.clear();\n this.invalidateCaches();\n this.notifyListeners();\n }\n}\n\n// Singleton instance\nexport const clientRegistry = new ClientExtensionRegistry();\n\n// Export for use in user extensions\nexport default clientRegistry;\n","import { useSyncExternalStore, useCallback } from 'react';\nimport { clientRegistry, type PageExtension, type ToolExtension, type ComponentOverride } from './registry';\n\n/**\n * Hook to access extension pages with automatic updates\n */\nexport function useExtensionPages(): PageExtension[] {\n const getSnapshot = useCallback(() => clientRegistry.getPages(), []);\n const subscribe = useCallback((callback: () => void) => clientRegistry.subscribe(callback), []);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Hook to access sidebar pages with automatic updates\n */\nexport function useSidebarPages(): PageExtension[] {\n const getSnapshot = useCallback(() => clientRegistry.getSidebarPages(), []);\n const subscribe = useCallback((callback: () => void) => clientRegistry.subscribe(callback), []);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Hook to access extension tools with automatic updates\n */\nexport function useExtensionTools(): ToolExtension[] {\n const getSnapshot = useCallback(() => clientRegistry.getTools(), []);\n const subscribe = useCallback((callback: () => void) => clientRegistry.subscribe(callback), []);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Hook to get a specific tool renderer\n */\nexport function useToolRenderer(toolName: string): ToolExtension | undefined {\n const getSnapshot = useCallback(() => clientRegistry.getTool(toolName), [toolName]);\n const subscribe = useCallback((callback: () => void) => clientRegistry.subscribe(callback), []);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Hook to get a component override\n */\nexport function useComponentOverride(slot: string): ComponentOverride | undefined {\n const getSnapshot = useCallback(() => clientRegistry.getOverride(slot), [slot]);\n const subscribe = useCallback((callback: () => void) => clientRegistry.subscribe(callback), []);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chaaskit/client",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "React frontend client for ChaasKit AI chat applications",
5
5
  "license": "MIT",
6
6
  "author": "Matt Ferrante <@ferrants>",
@@ -13,7 +13,15 @@
13
13
  "bugs": {
14
14
  "url": "https://github.com/ferrants/chaaskit/issues"
15
15
  },
16
- "keywords": ["chaaskit", "ai", "chat", "saas", "client", "react", "vite"],
16
+ "keywords": [
17
+ "chaaskit",
18
+ "ai",
19
+ "chat",
20
+ "saas",
21
+ "client",
22
+ "react",
23
+ "vite"
24
+ ],
17
25
  "type": "module",
18
26
  "exports": {
19
27
  ".": {
@@ -52,16 +60,8 @@
52
60
  "dist",
53
61
  "src"
54
62
  ],
55
- "scripts": {
56
- "dev": "vite --host",
57
- "build": "tsc && vite build --config vite.config.lib.ts",
58
- "build:lib": "vite build --config vite.config.lib.ts",
59
- "preview": "vite preview",
60
- "typecheck": "tsc --noEmit",
61
- "clean": "rm -rf dist"
62
- },
63
63
  "dependencies": {
64
- "@chaaskit/shared": "^0.1.1",
64
+ "@chaaskit/shared": "^0.1.2",
65
65
  "lucide-react": "^0.321.0",
66
66
  "react": "^18.2.0",
67
67
  "react-dom": "^18.2.0",
@@ -85,5 +85,13 @@
85
85
  "react": "^18.2.0",
86
86
  "react-dom": "^18.2.0",
87
87
  "react-router": "^7.0.0"
88
+ },
89
+ "scripts": {
90
+ "dev": "vite --host",
91
+ "build": "tsc && vite build --config vite.config.lib.ts",
92
+ "build:lib": "vite build --config vite.config.lib.ts",
93
+ "preview": "vite preview",
94
+ "typecheck": "tsc --noEmit",
95
+ "clean": "rm -rf dist"
88
96
  }
89
- }
97
+ }
@@ -1,4 +1,4 @@
1
- import { useState } from 'react';
1
+ import { useMemo, useState } from 'react';
2
2
  import { Copy, Check, RefreshCw, ThumbsUp, ThumbsDown, User, Bot, GitBranch } from 'lucide-react';
3
3
  import { useNavigate } from 'react-router';
4
4
  import type { Message } from '@chaaskit/shared';
@@ -6,6 +6,7 @@ import { useChatStore } from '../stores/chatStore';
6
6
  import { useTheme } from '../contexts/ThemeContext';
7
7
  import { useConfig } from '../contexts/ConfigContext';
8
8
  import { useAppPath } from '../hooks/useAppPath';
9
+ import { useExtensionTools } from '../extensions/useExtensions';
9
10
  import MarkdownRenderer from './content/MarkdownRenderer';
10
11
  import ToolCallDisplay, { UIResourceWidget } from './ToolCallDisplay';
11
12
  import BranchModal from './BranchModal';
@@ -27,6 +28,12 @@ export default function MessageItem({ message, isStreaming, messageIndex = 0, pr
27
28
  const config = useConfig();
28
29
  const navigate = useNavigate();
29
30
  const appPath = useAppPath();
31
+ const extensionTools = useExtensionTools();
32
+ const toolRendererMap = useMemo(() => {
33
+ const map = new Map<string, typeof extensionTools[number]>();
34
+ extensionTools.forEach((tool) => map.set(tool.name, tool));
35
+ return map;
36
+ }, [extensionTools]);
30
37
 
31
38
  const isUser = message.role === 'user';
32
39
  const showToolCalls = config.mcp?.showToolCalls !== false;
@@ -88,9 +95,22 @@ export default function MessageItem({ message, isStreaming, messageIndex = 0, pr
88
95
  toolResult: message.toolResults?.find((r) => r.toolCallId === toolCall.id),
89
96
  })) || [];
90
97
 
98
+ const renderedToolCalls = toolCallsWithResults
99
+ .filter(({ toolCall, toolResult }) =>
100
+ toolCall.serverId === 'native' &&
101
+ !!toolResult &&
102
+ !!toolRendererMap.get(toolCall.toolName)?.resultRenderer
103
+ )
104
+ .map(({ toolCall, toolResult }) => {
105
+ const renderer = toolRendererMap.get(toolCall.toolName)!.resultRenderer!;
106
+ return { toolCall, toolResult: toolResult!, Renderer: renderer };
107
+ });
108
+
109
+ const renderedToolCallIds = new Set(renderedToolCalls.map((entry) => entry.toolCall.id));
110
+
91
111
  // Check if any tool has a UI resource to render
92
112
  const uiResources = toolCallsWithResults
93
- .filter((tc) => tc.toolResult?.uiResource?.text)
113
+ .filter((tc) => tc.toolResult?.uiResource?.text && !renderedToolCallIds.has(tc.toolCall.id))
94
114
  .map((tc) => tc.toolResult!.uiResource!);
95
115
 
96
116
  // Debug logging
@@ -188,7 +208,18 @@ export default function MessageItem({ message, isStreaming, messageIndex = 0, pr
188
208
  </div>
189
209
  )}
190
210
 
191
- {/* 2. UI Resource Widgets (outside bubble, full width) */}
211
+ {/* 2. Tool Renderers (native tools) */}
212
+ {renderedToolCalls.length > 0 && (
213
+ <div className="space-y-3">
214
+ {renderedToolCalls.map(({ toolCall, toolResult, Renderer }) => (
215
+ <div key={toolCall.id} className="rounded-lg border border-border bg-background-secondary/40 p-3">
216
+ <Renderer toolCall={toolCall} toolResult={toolResult} />
217
+ </div>
218
+ ))}
219
+ </div>
220
+ )}
221
+
222
+ {/* 3. UI Resource Widgets (outside bubble, full width) */}
192
223
  {uiResources.length > 0 && (
193
224
  <div className="space-y-3">
194
225
  {uiResources.map((uiResource, index) => (
@@ -197,7 +228,7 @@ export default function MessageItem({ message, isStreaming, messageIndex = 0, pr
197
228
  </div>
198
229
  )}
199
230
 
200
- {/* 3. Text Response Bubble (with avatar) */}
231
+ {/* 4. Text Response Bubble (with avatar) */}
201
232
  {(message.content || isStreaming) && (
202
233
  <div className="group flex gap-3">
203
234
  {/* Avatar */}