@chaaskit/client 0.1.0 → 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 (96) hide show
  1. package/LICENSE +21 -0
  2. package/dist/lib/index.js +1023 -160
  3. package/dist/lib/index.js.map +1 -1
  4. package/dist/lib/routes/AcceptInviteRoute.js +1 -1
  5. package/dist/lib/routes/AcceptInviteRoute.js.map +1 -1
  6. package/dist/lib/routes/AdminDashboardRoute.js +1 -1
  7. package/dist/lib/routes/AdminDashboardRoute.js.map +1 -1
  8. package/dist/lib/routes/AdminPromoCodesRoute.js +19 -0
  9. package/dist/lib/routes/AdminPromoCodesRoute.js.map +1 -0
  10. package/dist/lib/routes/AdminTeamRoute.js +1 -1
  11. package/dist/lib/routes/AdminTeamRoute.js.map +1 -1
  12. package/dist/lib/routes/AdminTeamsRoute.js +1 -1
  13. package/dist/lib/routes/AdminTeamsRoute.js.map +1 -1
  14. package/dist/lib/routes/AdminUsersRoute.js +1 -1
  15. package/dist/lib/routes/AdminUsersRoute.js.map +1 -1
  16. package/dist/lib/routes/AdminWaitlistRoute.js +19 -0
  17. package/dist/lib/routes/AdminWaitlistRoute.js.map +1 -0
  18. package/dist/lib/routes/ApiKeysRoute.js +1 -1
  19. package/dist/lib/routes/ApiKeysRoute.js.map +1 -1
  20. package/dist/lib/routes/AutomationsRoute.js +1 -1
  21. package/dist/lib/routes/AutomationsRoute.js.map +1 -1
  22. package/dist/lib/routes/ChatRoute.js +1 -1
  23. package/dist/lib/routes/ChatRoute.js.map +1 -1
  24. package/dist/lib/routes/DocumentsRoute.js +1 -1
  25. package/dist/lib/routes/DocumentsRoute.js.map +1 -1
  26. package/dist/lib/routes/OAuthConsentRoute.js +1 -1
  27. package/dist/lib/routes/OAuthConsentRoute.js.map +1 -1
  28. package/dist/lib/routes/PricingRoute.js +1 -1
  29. package/dist/lib/routes/PricingRoute.js.map +1 -1
  30. package/dist/lib/routes/PrivacyRoute.js +1 -1
  31. package/dist/lib/routes/PrivacyRoute.js.map +1 -1
  32. package/dist/lib/routes/TeamSettingsRoute.js +1 -1
  33. package/dist/lib/routes/TeamSettingsRoute.js.map +1 -1
  34. package/dist/lib/routes/TermsRoute.js +1 -1
  35. package/dist/lib/routes/TermsRoute.js.map +1 -1
  36. package/dist/lib/routes/VerifyEmailRoute.js +1 -1
  37. package/dist/lib/routes/VerifyEmailRoute.js.map +1 -1
  38. package/dist/lib/routes.js +47 -37
  39. package/dist/lib/routes.js.map +1 -1
  40. package/dist/lib/ssr-utils.js +64 -1
  41. package/dist/lib/ssr-utils.js.map +1 -1
  42. package/dist/lib/ssr.js +23 -0
  43. package/dist/lib/ssr.js.map +1 -1
  44. package/dist/lib/styles.css +58 -62
  45. package/dist/lib/useExtensions-B5nX_8XD.js.map +1 -1
  46. package/package.json +25 -12
  47. package/src/components/MessageItem.tsx +35 -4
  48. package/src/components/MessageList.tsx +51 -5
  49. package/src/components/OAuthAppsSection.tsx +1 -1
  50. package/src/components/Sidebar.tsx +1 -3
  51. package/src/components/ToolCallDisplay.tsx +102 -11
  52. package/src/components/tool-renderers/DocumentListRenderer.tsx +44 -0
  53. package/src/components/tool-renderers/DocumentReadRenderer.tsx +33 -0
  54. package/src/components/tool-renderers/DocumentSaveRenderer.tsx +32 -0
  55. package/src/components/tool-renderers/DocumentSearchRenderer.tsx +33 -0
  56. package/src/components/tool-renderers/index.ts +36 -0
  57. package/src/components/tool-renderers/utils.ts +7 -0
  58. package/src/contexts/AuthContext.tsx +16 -6
  59. package/src/contexts/ConfigContext.tsx +60 -28
  60. package/src/contexts/ThemeContext.tsx +39 -68
  61. package/src/extensions/registry.ts +2 -1
  62. package/src/hooks/__tests__/basePath.test.ts +42 -0
  63. package/src/index.tsx +11 -2
  64. package/src/pages/AdminDashboardPage.tsx +15 -1
  65. package/src/pages/AdminPromoCodesPage.tsx +378 -0
  66. package/src/pages/AdminTeamPage.tsx +29 -1
  67. package/src/pages/AdminTeamsPage.tsx +15 -1
  68. package/src/pages/AdminUsersPage.tsx +15 -1
  69. package/src/pages/AdminWaitlistPage.tsx +156 -0
  70. package/src/pages/RegisterPage.tsx +91 -9
  71. package/src/routes/AcceptInviteRoute.tsx +1 -1
  72. package/src/routes/AdminDashboardRoute.tsx +1 -1
  73. package/src/routes/AdminPromoCodesRoute.tsx +24 -0
  74. package/src/routes/AdminTeamRoute.tsx +1 -1
  75. package/src/routes/AdminTeamsRoute.tsx +1 -1
  76. package/src/routes/AdminUsersRoute.tsx +1 -1
  77. package/src/routes/AdminWaitlistRoute.tsx +24 -0
  78. package/src/routes/ApiKeysRoute.tsx +1 -1
  79. package/src/routes/AutomationsRoute.tsx +1 -1
  80. package/src/routes/ChatRoute.tsx +2 -1
  81. package/src/routes/DocumentsRoute.tsx +1 -1
  82. package/src/routes/OAuthConsentRoute.tsx +1 -1
  83. package/src/routes/PricingRoute.tsx +1 -1
  84. package/src/routes/PrivacyRoute.tsx +1 -1
  85. package/src/routes/TeamSettingsRoute.tsx +1 -1
  86. package/src/routes/TermsRoute.tsx +1 -1
  87. package/src/routes/VerifyEmailRoute.tsx +1 -1
  88. package/src/routes/index.ts +2 -0
  89. package/src/ssr-utils.tsx +100 -1
  90. package/src/ssr.ts +59 -0
  91. package/src/stores/chatStore.ts +5 -0
  92. package/src/styles/index.css +16 -63
  93. package/src/tailwind-preset.js +360 -0
  94. package/dist/favicon.svg +0 -11
  95. package/dist/index.html +0 -17
  96. package/dist/logo.svg +0 -12
package/dist/lib/index.js CHANGED
@@ -1,11 +1,129 @@
1
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
+ import { c as clientRegistry, a as useSidebarPages, b as useExtensionTools } from "./useExtensions-B5nX_8XD.js";
2
3
  import { C, a, S } from "./LoadingSkeletons-IcIC2JPq.js";
3
4
  import React, { createContext, useState, useEffect, useContext, useCallback, useRef, forwardRef, createElement as createElement$1, useMemo, useImperativeHandle } from "react";
4
5
  import { useSearchParams, useNavigate, Link as Link$1, useMatch, useParams, Navigate } from "react-router";
5
6
  import { S as S2, b, u, a as a2, c } from "./ServerThemeProvider-DNF0LAyk.js";
6
- import { a as useSidebarPages } from "./useExtensions-B5nX_8XD.js";
7
- import { c as c2 } from "./useExtensions-B5nX_8XD.js";
8
7
  import { createPortal } from "react-dom";
8
+ function getTextContent(content2) {
9
+ if (!content2) return null;
10
+ const firstText = content2.find((item) => item.type === "text" && typeof item.text === "string");
11
+ return (firstText == null ? void 0 : firstText.text) ?? null;
12
+ }
13
+ function DocumentListRenderer({ toolCall, toolResult }) {
14
+ const structured = toolResult.structuredContent;
15
+ const documents = (structured == null ? void 0 : structured.documents) ?? [];
16
+ const fallback = getTextContent(toolResult.content);
17
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
18
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-text-primary", children: "Documents" }),
19
+ documents.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "space-y-2", children: documents.map((doc, index2) => /* @__PURE__ */ jsxs("li", { className: "rounded-md border border-border bg-background px-3 py-2", children: [
20
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-text-primary", children: doc.path ?? doc.name ?? "Untitled document" }),
21
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-text-muted", children: [
22
+ doc.mimeType ?? "text/plain",
23
+ typeof doc.charCount === "number" ? ` • ${doc.charCount} chars` : "",
24
+ doc.teamId ? " • team" : "",
25
+ doc.projectId ? " • project" : ""
26
+ ] })
27
+ ] }, doc.id ?? doc.path ?? index2)) }) : /* @__PURE__ */ jsx("div", { className: "text-sm text-text-secondary", children: fallback ?? "No documents found." }),
28
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-text-muted", children: [
29
+ "Tool: ",
30
+ toolCall.toolName
31
+ ] })
32
+ ] });
33
+ }
34
+ function DocumentReadRenderer({ toolCall, toolResult }) {
35
+ const structured = toolResult.structuredContent;
36
+ const fallback = getTextContent(toolResult.content);
37
+ const rangeText = structured ? `Lines ${Number(structured.offset ?? 0) + 1}-${Number(structured.offset ?? 0) + Number(structured.linesReturned ?? 0)} of ${structured.totalLines ?? "unknown"}` : null;
38
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
39
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-text-primary", children: "Document Preview" }),
40
+ structured ? /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-text-secondary", children: [
41
+ /* @__PURE__ */ jsx("div", { className: "font-medium text-text-primary", children: structured.path ?? "Document" }),
42
+ rangeText && /* @__PURE__ */ jsxs("div", { className: "text-xs text-text-muted", children: [
43
+ rangeText,
44
+ structured.truncated ? " (truncated)" : ""
45
+ ] })
46
+ ] }) : /* @__PURE__ */ jsx("div", { className: "text-sm text-text-secondary", children: fallback ?? "No structured document details." }),
47
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-text-muted", children: [
48
+ "Tool: ",
49
+ toolCall.toolName
50
+ ] })
51
+ ] });
52
+ }
53
+ function DocumentSearchRenderer({ toolCall, toolResult }) {
54
+ const structured = toolResult.structuredContent;
55
+ const fallback = getTextContent(toolResult.content);
56
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
57
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-text-primary", children: "Document Search" }),
58
+ structured ? /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-text-secondary", children: [
59
+ /* @__PURE__ */ jsx("div", { className: "font-medium text-text-primary", children: structured.path ?? "Document" }),
60
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-text-muted", children: [
61
+ "Query: ",
62
+ structured.query ?? "unknown"
63
+ ] }),
64
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-text-muted", children: [
65
+ "Matches: ",
66
+ structured.matchCount ?? 0
67
+ ] }),
68
+ structured.matchLines && structured.matchLines.length > 0 && /* @__PURE__ */ jsxs("div", { className: "text-xs text-text-muted", children: [
69
+ "Lines: ",
70
+ structured.matchLines.join(", ")
71
+ ] })
72
+ ] }) : /* @__PURE__ */ jsx("div", { className: "text-sm text-text-secondary", children: fallback ?? "No structured search data." }),
73
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-text-muted", children: [
74
+ "Tool: ",
75
+ toolCall.toolName
76
+ ] })
77
+ ] });
78
+ }
79
+ function DocumentSaveRenderer({ toolCall, toolResult }) {
80
+ const structured = toolResult.structuredContent;
81
+ const fallback = getTextContent(toolResult.content);
82
+ const success = (structured == null ? void 0 : structured.success) !== false && !toolResult.isError;
83
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
84
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-text-primary", children: "Save Document" }),
85
+ structured ? /* @__PURE__ */ jsxs("div", { className: `rounded-md border px-3 py-2 text-sm ${success ? "border-success/30 bg-success/10 text-success" : "border-error/30 bg-error/10 text-error"}`, children: [
86
+ /* @__PURE__ */ jsx("div", { className: "font-medium", children: success ? "Saved" : "Failed" }),
87
+ structured.path && /* @__PURE__ */ jsxs("div", { className: "text-xs", children: [
88
+ "Path: ",
89
+ structured.path
90
+ ] }),
91
+ structured.id && /* @__PURE__ */ jsxs("div", { className: "text-xs", children: [
92
+ "ID: ",
93
+ structured.id
94
+ ] }),
95
+ typeof structured.charCount === "number" && /* @__PURE__ */ jsxs("div", { className: "text-xs", children: [
96
+ "Size: ",
97
+ structured.charCount,
98
+ " chars"
99
+ ] })
100
+ ] }) : /* @__PURE__ */ jsx("div", { className: "text-sm text-text-secondary", children: fallback ?? "No structured save data." }),
101
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-text-muted", children: [
102
+ "Tool: ",
103
+ toolCall.toolName
104
+ ] })
105
+ ] });
106
+ }
107
+ clientRegistry.registerTool({
108
+ name: "list_documents",
109
+ description: "Render document list results",
110
+ resultRenderer: DocumentListRenderer
111
+ });
112
+ clientRegistry.registerTool({
113
+ name: "read_document",
114
+ description: "Render document read results",
115
+ resultRenderer: DocumentReadRenderer
116
+ });
117
+ clientRegistry.registerTool({
118
+ name: "search_in_document",
119
+ description: "Render document search results",
120
+ resultRenderer: DocumentSearchRenderer
121
+ });
122
+ clientRegistry.registerTool({
123
+ name: "save_document",
124
+ description: "Render document save results",
125
+ resultRenderer: DocumentSaveRenderer
126
+ });
9
127
  class ApiError extends Error {
10
128
  constructor(status, message, code2) {
11
129
  super(message);
@@ -85,8 +203,8 @@ function AuthProvider({ children }) {
85
203
  setUser(response.user);
86
204
  return { requiresVerification: response.requiresVerification ?? false };
87
205
  }
88
- async function register2(email, password, name2) {
89
- const response = await api.post("/api/auth/register", { email, password, name: name2 });
206
+ async function register2(email, password, name2, options) {
207
+ const response = await api.post("/api/auth/register", { email, password, name: name2, ...options });
90
208
  setUser(response.user);
91
209
  return { requiresVerification: response.requiresVerification ?? false };
92
210
  }
@@ -94,8 +212,8 @@ function AuthProvider({ children }) {
94
212
  await api.post("/api/auth/logout", {});
95
213
  setUser(null);
96
214
  }
97
- async function sendMagicLink(email) {
98
- await api.post("/api/auth/magic-link", { email });
215
+ async function sendMagicLink(email, inviteToken) {
216
+ await api.post("/api/auth/magic-link", { email, inviteToken });
99
217
  }
100
218
  async function verifyEmail(code2) {
101
219
  await api.post(
@@ -132,11 +250,54 @@ function useAuth() {
132
250
  }
133
251
  return context;
134
252
  }
253
+ const ThemeContext = createContext(void 0);
254
+ const DEFAULT_THEMES = ["light", "dark"];
255
+ const DEFAULT_THEME = "dark";
256
+ function ThemeProvider({
257
+ children,
258
+ availableThemes = DEFAULT_THEMES,
259
+ defaultTheme = DEFAULT_THEME
260
+ }) {
261
+ const [theme, setThemeState] = useState(() => {
262
+ if (typeof window !== "undefined") {
263
+ const stored = localStorage.getItem("theme");
264
+ if (stored && availableThemes.includes(stored)) {
265
+ return stored;
266
+ }
267
+ }
268
+ return defaultTheme;
269
+ });
270
+ useEffect(() => {
271
+ document.documentElement.setAttribute("data-theme", theme);
272
+ localStorage.setItem("theme", theme);
273
+ document.cookie = `theme=${theme};path=/;max-age=31536000;SameSite=Lax`;
274
+ }, [theme]);
275
+ function setTheme(newTheme) {
276
+ if (availableThemes.includes(newTheme)) {
277
+ setThemeState(newTheme);
278
+ }
279
+ }
280
+ return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: { theme, setTheme, availableThemes }, children });
281
+ }
282
+ function useTheme() {
283
+ const context = useContext(ThemeContext);
284
+ if (context === void 0) {
285
+ throw new Error("useTheme must be used within a ThemeProvider");
286
+ }
287
+ return context;
288
+ }
289
+ function getInjectedConfig() {
290
+ if (typeof window !== "undefined" && window.__CHAASKIT_CONFIG__) {
291
+ return window.__CHAASKIT_CONFIG__;
292
+ }
293
+ return void 0;
294
+ }
135
295
  const defaultConfig = {
136
296
  app: {
137
297
  name: "AI Chat",
138
298
  description: "Your AI assistant",
139
- url: "http://localhost:5173"
299
+ url: "http://localhost:5173",
300
+ basePath: "/chat"
140
301
  },
141
302
  ui: {
142
303
  welcomeTitle: "Welcome to AI Chat",
@@ -219,28 +380,16 @@ const defaultConfig = {
219
380
  magicLink: {
220
381
  enabled: true,
221
382
  expiresInMinutes: 15
222
- }
223
- },
224
- agent: {
225
- type: "built-in",
226
- provider: "anthropic",
227
- model: "claude-sonnet-4-20250514",
228
- systemPrompt: "You are a helpful AI assistant.",
229
- maxTokens: 4096
383
+ },
384
+ gating: {
385
+ mode: "open",
386
+ waitlistEnabled: false
387
+ },
388
+ isAdmin: false
230
389
  },
231
390
  payments: {
232
391
  enabled: false,
233
- provider: "stripe",
234
- plans: [
235
- {
236
- id: "free",
237
- name: "Free",
238
- type: "free",
239
- params: {
240
- monthlyMessageLimit: 20
241
- }
242
- }
243
- ]
392
+ provider: "stripe"
244
393
  },
245
394
  legal: {
246
395
  privacyPolicyUrl: "/privacy",
@@ -269,7 +418,6 @@ const defaultConfig = {
269
418
  },
270
419
  promptTemplates: {
271
420
  enabled: true,
272
- builtIn: [],
273
421
  allowUserTemplates: true
274
422
  },
275
423
  teams: {
@@ -277,9 +425,6 @@ const defaultConfig = {
277
425
  },
278
426
  documents: {
279
427
  enabled: false,
280
- storage: {
281
- provider: "database"
282
- },
283
428
  maxFileSizeMB: 10,
284
429
  hybridThreshold: 1e3,
285
430
  acceptedTypes: ["text/plain", "text/markdown", "application/json"]
@@ -290,16 +435,30 @@ const defaultConfig = {
290
435
  },
291
436
  api: {
292
437
  enabled: false
438
+ },
439
+ credits: {
440
+ enabled: false,
441
+ expiryEnabled: false,
442
+ promoEnabled: false
443
+ },
444
+ metering: {
445
+ enabled: false,
446
+ recordPromptCompletion: true
293
447
  }
294
448
  };
295
449
  const ConfigContext = createContext({
296
450
  config: defaultConfig,
297
451
  configLoaded: false
298
452
  });
299
- function ConfigProvider({ children }) {
300
- const [config, setConfig] = useState(defaultConfig);
301
- const [configLoaded, setConfigLoaded] = useState(false);
453
+ function ConfigProvider({ children, initialConfig }) {
454
+ const injectedConfig = getInjectedConfig();
455
+ const preloadedConfig = initialConfig || injectedConfig;
456
+ const [config, setConfig] = useState(
457
+ preloadedConfig ? { ...defaultConfig, ...preloadedConfig } : defaultConfig
458
+ );
459
+ const [configLoaded, setConfigLoaded] = useState(!!preloadedConfig);
302
460
  useEffect(() => {
461
+ if (preloadedConfig) return;
303
462
  async function loadConfig() {
304
463
  try {
305
464
  const response = await fetch("/api/config");
@@ -317,7 +476,7 @@ function ConfigProvider({ children }) {
317
476
  }
318
477
  }
319
478
  loadConfig();
320
- }, []);
479
+ }, [preloadedConfig]);
321
480
  return /* @__PURE__ */ jsx(ConfigContext.Provider, { value: { config, configLoaded }, children });
322
481
  }
323
482
  function useConfig() {
@@ -326,80 +485,6 @@ function useConfig() {
326
485
  function useConfigLoaded() {
327
486
  return useContext(ConfigContext).configLoaded;
328
487
  }
329
- const ThemeContext = createContext(void 0);
330
- function ThemeProvider({ children }) {
331
- const config = useConfig();
332
- const [theme, setThemeState] = useState(() => {
333
- const stored = localStorage.getItem("theme");
334
- if (stored && config.theming.themes[stored]) {
335
- return stored;
336
- }
337
- return config.theming.defaultTheme;
338
- });
339
- const availableThemes = Object.keys(config.theming.themes);
340
- useEffect(() => {
341
- document.documentElement.setAttribute("data-theme", theme);
342
- localStorage.setItem("theme", theme);
343
- document.cookie = `theme=${theme};path=/;max-age=31536000;SameSite=Lax`;
344
- const themeConfig = config.theming.themes[theme];
345
- if (themeConfig) {
346
- const root2 = document.documentElement;
347
- const colors = themeConfig.colors;
348
- Object.entries(colors).forEach(([key, value]) => {
349
- const cssKey = `--color-${key.replace(/([A-Z])/g, "-$1").toLowerCase()}`;
350
- const rgb = hexToRgb(value);
351
- if (rgb) {
352
- root2.style.setProperty(cssKey, `${rgb.r} ${rgb.g} ${rgb.b}`);
353
- }
354
- });
355
- }
356
- document.documentElement.style.setProperty(
357
- "--font-sans",
358
- config.theming.fonts.sans
359
- );
360
- document.documentElement.style.setProperty(
361
- "--font-mono",
362
- config.theming.fonts.mono
363
- );
364
- document.documentElement.style.setProperty(
365
- "--radius-sm",
366
- config.theming.borderRadius.sm
367
- );
368
- document.documentElement.style.setProperty(
369
- "--radius-md",
370
- config.theming.borderRadius.md
371
- );
372
- document.documentElement.style.setProperty(
373
- "--radius-lg",
374
- config.theming.borderRadius.lg
375
- );
376
- document.documentElement.style.setProperty(
377
- "--radius-full",
378
- config.theming.borderRadius.full
379
- );
380
- }, [theme, config.theming]);
381
- function setTheme(newTheme) {
382
- if (config.theming.themes[newTheme]) {
383
- setThemeState(newTheme);
384
- }
385
- }
386
- return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: { theme, setTheme, availableThemes }, children });
387
- }
388
- function useTheme() {
389
- const context = useContext(ThemeContext);
390
- if (context === void 0) {
391
- throw new Error("useTheme must be used within a ThemeProvider");
392
- }
393
- return context;
394
- }
395
- function hexToRgb(hex) {
396
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
397
- return result ? {
398
- r: parseInt(result[1], 16),
399
- g: parseInt(result[2], 16),
400
- b: parseInt(result[3], 16)
401
- } : null;
402
- }
403
488
  const TeamContext = createContext(void 0);
404
489
  function TeamProvider({ children }) {
405
490
  var _a;
@@ -1328,7 +1413,8 @@ ${mentions}` : mentions;
1328
1413
  ...pendingCall,
1329
1414
  result: data2.content || [],
1330
1415
  isError: data2.isError,
1331
- uiResource: data2.uiResource
1416
+ uiResource: data2.uiResource,
1417
+ structuredContent: data2.structuredContent
1332
1418
  } : {
1333
1419
  // Fallback: create from tool_result event data (server now includes name/serverId/input)
1334
1420
  id: data2.id,
@@ -1337,7 +1423,8 @@ ${mentions}` : mentions;
1337
1423
  input: data2.input || {},
1338
1424
  result: data2.content || [],
1339
1425
  isError: data2.isError,
1340
- uiResource: data2.uiResource
1426
+ uiResource: data2.uiResource,
1427
+ structuredContent: data2.structuredContent
1341
1428
  };
1342
1429
  return {
1343
1430
  pendingToolCalls: state2.pendingToolCalls.filter((tc) => tc.id !== data2.id),
@@ -1409,7 +1496,8 @@ ${mentions}` : mentions;
1409
1496
  toolCallId: tc.id,
1410
1497
  content: tc.result,
1411
1498
  isError: tc.isError,
1412
- uiResource: tc.uiResource
1499
+ uiResource: tc.uiResource,
1500
+ structuredContent: tc.structuredContent
1413
1501
  })) : void 0,
1414
1502
  createdAt: /* @__PURE__ */ new Date()
1415
1503
  };
@@ -27180,12 +27268,12 @@ function MCPCredentialsSection({ onCredentialChange }) {
27180
27268
  ] });
27181
27269
  }
27182
27270
  function OAuthAppsSection() {
27183
- var _a, _b, _c;
27271
+ var _a, _b;
27184
27272
  const config = useConfig();
27185
27273
  const [apps, setApps] = useState([]);
27186
27274
  const [isLoading, setIsLoading] = useState(false);
27187
27275
  const [revokingId, setRevokingId] = useState(null);
27188
- const oauthEnabled = (_c = (_b = (_a = config.mcp) == null ? void 0 : _a.server) == null ? void 0 : _b.oauth) == null ? void 0 : _c.enabled;
27276
+ const oauthEnabled = (_b = (_a = config.mcp) == null ? void 0 : _a.servers) == null ? void 0 : _b.some((server) => server.authMode === "user-oauth");
27189
27277
  useEffect(() => {
27190
27278
  if (oauthEnabled) {
27191
27279
  loadApps();
@@ -28006,16 +28094,16 @@ function ProjectModal({ isOpen, onClose, project }) {
28006
28094
  ] }),
28007
28095
  /* @__PURE__ */ jsxs("div", { children: [
28008
28096
  /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-text-secondary mb-2", children: "Color" }),
28009
- /* @__PURE__ */ jsx("div", { className: "flex gap-2 flex-wrap", children: projectColors.map((c3) => /* @__PURE__ */ jsx(
28097
+ /* @__PURE__ */ jsx("div", { className: "flex gap-2 flex-wrap", children: projectColors.map((c2) => /* @__PURE__ */ jsx(
28010
28098
  "button",
28011
28099
  {
28012
28100
  type: "button",
28013
- onClick: () => canEdit && setColor(c3),
28014
- className: `w-8 h-8 rounded-full border-2 transition-all ${color2 === c3 ? "border-text-primary scale-110" : "border-transparent hover:scale-105"} ${!canEdit ? "cursor-not-allowed opacity-50" : ""}`,
28015
- style: { backgroundColor: c3 },
28101
+ onClick: () => canEdit && setColor(c2),
28102
+ className: `w-8 h-8 rounded-full border-2 transition-all ${color2 === c2 ? "border-text-primary scale-110" : "border-transparent hover:scale-105"} ${!canEdit ? "cursor-not-allowed opacity-50" : ""}`,
28103
+ style: { backgroundColor: c2 },
28016
28104
  disabled: !canEdit
28017
28105
  },
28018
- c3
28106
+ c2
28019
28107
  )) })
28020
28108
  ] }),
28021
28109
  /* @__PURE__ */ jsxs("div", { children: [
@@ -28537,7 +28625,7 @@ function getIconComponent(iconName) {
28537
28625
  return Icon || Puzzle;
28538
28626
  }
28539
28627
  function Sidebar({ onClose, onOpenSearch }) {
28540
- var _a, _b, _c, _d, _e, _f;
28628
+ var _a, _b, _c, _d, _e;
28541
28629
  const navigate = useNavigate();
28542
28630
  const appPath = useAppPath();
28543
28631
  const basePath = useBasePath();
@@ -28787,12 +28875,7 @@ function Sidebar({ onClose, onOpenSearch }) {
28787
28875
  ]
28788
28876
  }
28789
28877
  ),
28790
- ((user == null ? void 0 : user.isAdmin) || ((_d = (_c = config.admin) == null ? void 0 : _c.emails) == null ? void 0 : _d.some(
28791
- (email) => {
28792
- var _a2;
28793
- return email.toLowerCase() === ((_a2 = user == null ? void 0 : user.email) == null ? void 0 : _a2.toLowerCase());
28794
- }
28795
- ))) && /* @__PURE__ */ jsxs(
28878
+ ((user == null ? void 0 : user.isAdmin) || ((_c = config.auth) == null ? void 0 : _c.isAdmin)) && /* @__PURE__ */ jsxs(
28796
28879
  Link$1,
28797
28880
  {
28798
28881
  to: appPath("/admin"),
@@ -28818,7 +28901,7 @@ function Sidebar({ onClose, onOpenSearch }) {
28818
28901
  page.id
28819
28902
  );
28820
28903
  }),
28821
- ((_e = config.documents) == null ? void 0 : _e.enabled) && /* @__PURE__ */ jsxs(
28904
+ ((_d = config.documents) == null ? void 0 : _d.enabled) && /* @__PURE__ */ jsxs(
28822
28905
  Link$1,
28823
28906
  {
28824
28907
  to: appPath("/documents"),
@@ -28829,7 +28912,7 @@ function Sidebar({ onClose, onOpenSearch }) {
28829
28912
  ]
28830
28913
  }
28831
28914
  ),
28832
- ((_f = config.scheduledPrompts) == null ? void 0 : _f.enabled) && /* @__PURE__ */ jsxs(
28915
+ ((_e = config.scheduledPrompts) == null ? void 0 : _e.enabled) && /* @__PURE__ */ jsxs(
28833
28916
  Link$1,
28834
28917
  {
28835
28918
  to: appPath("/automations"),
@@ -30418,11 +30501,11 @@ function index$1(style2, options) {
30418
30501
  match(WHITESPACE_REGEX);
30419
30502
  }
30420
30503
  function comments(rules) {
30421
- var c3;
30504
+ var c2;
30422
30505
  rules = rules || [];
30423
- while (c3 = comment()) {
30424
- if (c3 !== false) {
30425
- rules.push(c3);
30506
+ while (c2 = comment()) {
30507
+ if (c2 !== false) {
30508
+ rules.push(c2);
30426
30509
  }
30427
30510
  }
30428
30511
  return rules;
@@ -46243,10 +46326,10 @@ var hasRequiredC;
46243
46326
  function requireC() {
46244
46327
  if (hasRequiredC) return c_1;
46245
46328
  hasRequiredC = 1;
46246
- c_1 = c3;
46247
- c3.displayName = "c";
46248
- c3.aliases = [];
46249
- function c3(Prism2) {
46329
+ c_1 = c2;
46330
+ c2.displayName = "c";
46331
+ c2.aliases = [];
46332
+ function c2(Prism2) {
46250
46333
  Prism2.languages.c = Prism2.languages.extend("clike", {
46251
46334
  comment: {
46252
46335
  pattern: /\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,
@@ -58775,7 +58858,7 @@ function requireNaniscript() {
58775
58858
  var tokens = env2.tokens;
58776
58859
  tokens.forEach(function(token) {
58777
58860
  if (typeof token !== "string" && token.type === "generic-text") {
58778
- var content2 = getTextContent(token);
58861
+ var content2 = getTextContent2(token);
58779
58862
  if (!isBracketsBalanced(content2)) {
58780
58863
  token.type = "bad-line";
58781
58864
  token.content = content2;
@@ -58799,13 +58882,13 @@ function requireNaniscript() {
58799
58882
  }
58800
58883
  return stack.length === 0;
58801
58884
  }
58802
- function getTextContent(token) {
58885
+ function getTextContent2(token) {
58803
58886
  if (typeof token === "string") {
58804
58887
  return token;
58805
58888
  } else if (Array.isArray(token)) {
58806
- return token.map(getTextContent).join("");
58889
+ return token.map(getTextContent2).join("");
58807
58890
  } else {
58808
- return getTextContent(token.content);
58891
+ return getTextContent2(token.content);
58809
58892
  }
58810
58893
  }
58811
58894
  })(Prism2);
@@ -67976,6 +68059,7 @@ const AUTO_APPROVE_LABELS = {
67976
68059
  user_always: "You always allowed this tool",
67977
68060
  thread_allowed: "Allowed for this chat"
67978
68061
  };
68062
+ const TOOL_UI_MESSAGE_SOURCE = "chaaskit-tool-ui";
67979
68063
  function generateOpenAiScript(toolInput, toolOutput, theme) {
67980
68064
  const openAiTheme = theme === "dark" ? "dark" : "light";
67981
68065
  const backgroundColor = theme === "dark" ? "#111827" : "#ffffff";
@@ -68018,6 +68102,46 @@ function generateOpenAiScript(toolInput, toolOutput, theme) {
68018
68102
  </style>
68019
68103
  <script>
68020
68104
  (function() {
68105
+ const MESSAGE_SOURCE = '${TOOL_UI_MESSAGE_SOURCE}';
68106
+ let requestId = 0;
68107
+ const pending = new Map();
68108
+
68109
+ function resolvePending(id, data) {
68110
+ const entry = pending.get(id);
68111
+ if (!entry) return;
68112
+ pending.delete(id);
68113
+ if (data && data.ok) {
68114
+ entry.resolve(data.result);
68115
+ return;
68116
+ }
68117
+ entry.resolve({ error: (data && data.error) || 'Tool UI bridge error' });
68118
+ }
68119
+
68120
+ window.addEventListener('message', (event) => {
68121
+ const data = event.data || {};
68122
+ if (data.source !== MESSAGE_SOURCE || !data.id) return;
68123
+ resolvePending(data.id, data);
68124
+ });
68125
+
68126
+ function sendToParent(type, payload, timeoutMs = 15000) {
68127
+ return new Promise((resolve) => {
68128
+ const id = String(Date.now()) + '-' + String(requestId++);
68129
+ pending.set(id, { resolve });
68130
+ try {
68131
+ window.parent.postMessage({ source: MESSAGE_SOURCE, type, id, payload }, '*');
68132
+ } catch (err) {
68133
+ pending.delete(id);
68134
+ resolve({ error: 'Unable to reach host window' });
68135
+ return;
68136
+ }
68137
+ setTimeout(() => {
68138
+ if (!pending.has(id)) return;
68139
+ pending.delete(id);
68140
+ resolve({ error: 'Tool UI request timed out' });
68141
+ }, timeoutMs);
68142
+ });
68143
+ }
68144
+
68021
68145
  // Initialize window.openai with the OpenAI Apps SDK spec
68022
68146
  window.openai = {
68023
68147
  // Core globals
@@ -68042,17 +68166,20 @@ function generateOpenAiScript(toolInput, toolOutput, theme) {
68042
68166
  // API methods
68043
68167
  callTool: async (name, args) => {
68044
68168
  console.log('window.openai.callTool called:', { name, args });
68045
- // TODO: Implement actual tool calling via parent window messaging
68046
- return {
68047
- content: [{ type: 'text', text: 'Tool calling not yet implemented' }],
68048
- isError: false
68049
- };
68169
+ const response = await sendToParent('callTool', { name, args });
68170
+ if (response && response.error) {
68171
+ return {
68172
+ content: [{ type: 'text', text: response.error }],
68173
+ isError: true
68174
+ };
68175
+ }
68176
+ return response || { content: [{ type: 'text', text: 'No response from host' }], isError: true };
68050
68177
  },
68051
68178
 
68052
68179
  sendFollowUpMessage: async (args) => {
68053
68180
  console.log('window.openai.sendFollowUpMessage called:', args);
68054
- // TODO: Implement via parent window messaging
68055
- return {};
68181
+ const response = await sendToParent('sendFollowUpMessage', args);
68182
+ return response && response.error ? { error: response.error } : (response || {});
68056
68183
  },
68057
68184
 
68058
68185
  openExternal: (payload) => {
@@ -68064,27 +68191,41 @@ function generateOpenAiScript(toolInput, toolOutput, theme) {
68064
68191
 
68065
68192
  requestDisplayMode: async (args) => {
68066
68193
  console.log('window.openai.requestDisplayMode called:', args);
68067
- return { mode: args.mode };
68194
+ const response = await sendToParent('requestDisplayMode', args);
68195
+ if (response && response.error) {
68196
+ return { mode: args.mode };
68197
+ }
68198
+ return response || { mode: args.mode };
68068
68199
  },
68069
68200
 
68070
68201
  setWidgetState: async (state) => {
68071
68202
  console.log('window.openai.setWidgetState called:', state);
68072
68203
  window.openai.widgetState = state;
68073
- return {};
68204
+ const response = await sendToParent('setWidgetState', state);
68205
+ return response && response.error ? {} : (response || {});
68074
68206
  },
68075
68207
 
68076
68208
  requestClose: () => {
68077
68209
  console.log('window.openai.requestClose called');
68210
+ sendToParent('requestClose', {});
68078
68211
  },
68079
68212
 
68080
68213
  getFileDownloadUrl: async ({ fileId }) => {
68081
68214
  console.log('window.openai.getFileDownloadUrl called:', fileId);
68082
- return { url: '' };
68215
+ const response = await sendToParent('getFileDownloadUrl', { fileId });
68216
+ if (response && response.error) {
68217
+ return { url: '' };
68218
+ }
68219
+ return response || { url: '' };
68083
68220
  },
68084
68221
 
68085
68222
  uploadFile: async (file) => {
68086
68223
  console.log('window.openai.uploadFile called:', file);
68087
- return { fileId: '' };
68224
+ const response = await sendToParent('uploadFile', { file });
68225
+ if (response && response.error) {
68226
+ return { fileId: '' };
68227
+ }
68228
+ return response || { fileId: '' };
68088
68229
  }
68089
68230
  };
68090
68231
 
@@ -68136,6 +68277,31 @@ function UIResourceWidget({ uiResource, theme }) {
68136
68277
  }
68137
68278
  function ToolCallDisplay({ toolCall, toolResult, isPending, uiResource, hideUiResource, autoApproveReason }) {
68138
68279
  var _a;
68280
+ useEffect(() => {
68281
+ async function handleMessage(event) {
68282
+ const data2 = event.data || {};
68283
+ if (data2.source !== TOOL_UI_MESSAGE_SOURCE || !data2.id) return;
68284
+ const targetOrigin = event.origin && event.origin !== "null" ? event.origin : "*";
68285
+ try {
68286
+ const handler = window.chaaskitToolUiHandler;
68287
+ const result = typeof handler === "function" ? await handler({ type: data2.type, payload: data2.payload }) : { error: "Tool UI bridge not configured" };
68288
+ const sourceWindow = event.source;
68289
+ sourceWindow == null ? void 0 : sourceWindow.postMessage(
68290
+ { source: TOOL_UI_MESSAGE_SOURCE, id: data2.id, ok: true, result },
68291
+ targetOrigin
68292
+ );
68293
+ } catch (err) {
68294
+ const message = err instanceof Error ? err.message : "Tool UI bridge error";
68295
+ const sourceWindow = event.source;
68296
+ sourceWindow == null ? void 0 : sourceWindow.postMessage(
68297
+ { source: TOOL_UI_MESSAGE_SOURCE, id: data2.id, ok: false, error: message },
68298
+ targetOrigin
68299
+ );
68300
+ }
68301
+ }
68302
+ window.addEventListener("message", handleMessage);
68303
+ return () => window.removeEventListener("message", handleMessage);
68304
+ }, []);
68139
68305
  const hasHtmlResource = !hideUiResource && (uiResource == null ? void 0 : uiResource.text) && (((_a = uiResource.mimeType) == null ? void 0 : _a.includes("html")) || uiResource.text.trim().startsWith("<"));
68140
68306
  const [isExpanded, setIsExpanded] = useState(hasHtmlResource);
68141
68307
  const [showRawResult, setShowRawResult] = useState(false);
@@ -68469,6 +68635,12 @@ function MessageItem({ message, isStreaming, messageIndex = 0, previousMessage }
68469
68635
  const config = useConfig();
68470
68636
  const navigate = useNavigate();
68471
68637
  const appPath = useAppPath();
68638
+ const extensionTools = useExtensionTools();
68639
+ const toolRendererMap = useMemo(() => {
68640
+ const map2 = /* @__PURE__ */ new Map();
68641
+ extensionTools.forEach((tool) => map2.set(tool.name, tool));
68642
+ return map2;
68643
+ }, [extensionTools]);
68472
68644
  const isUser = message.role === "user";
68473
68645
  const showToolCalls = ((_a = config.mcp) == null ? void 0 : _a.showToolCalls) !== false;
68474
68646
  const canBranch = messageIndex > 0;
@@ -68515,9 +68687,19 @@ function MessageItem({ message, isStreaming, messageIndex = 0, previousMessage }
68515
68687
  toolResult: (_a2 = message.toolResults) == null ? void 0 : _a2.find((r) => r.toolCallId === toolCall.id)
68516
68688
  };
68517
68689
  })) || [];
68690
+ const renderedToolCalls = toolCallsWithResults.filter(
68691
+ ({ toolCall, toolResult }) => {
68692
+ var _a2;
68693
+ return toolCall.serverId === "native" && !!toolResult && !!((_a2 = toolRendererMap.get(toolCall.toolName)) == null ? void 0 : _a2.resultRenderer);
68694
+ }
68695
+ ).map(({ toolCall, toolResult }) => {
68696
+ const renderer = toolRendererMap.get(toolCall.toolName).resultRenderer;
68697
+ return { toolCall, toolResult, Renderer: renderer };
68698
+ });
68699
+ const renderedToolCallIds = new Set(renderedToolCalls.map((entry) => entry.toolCall.id));
68518
68700
  const uiResources = toolCallsWithResults.filter((tc) => {
68519
68701
  var _a2, _b2;
68520
- return (_b2 = (_a2 = tc.toolResult) == null ? void 0 : _a2.uiResource) == null ? void 0 : _b2.text;
68702
+ return ((_b2 = (_a2 = tc.toolResult) == null ? void 0 : _a2.uiResource) == null ? void 0 : _b2.text) && !renderedToolCallIds.has(tc.toolCall.id);
68521
68703
  }).map((tc) => tc.toolResult.uiResource);
68522
68704
  if (!isUser && ((_c = message.toolCalls) == null ? void 0 : _c.length)) {
68523
68705
  console.log("[MessageItem] Rendering message with toolCalls:", message.toolCalls.length);
@@ -68588,6 +68770,7 @@ function MessageItem({ message, isStreaming, messageIndex = 0, previousMessage }
68588
68770
  },
68589
68771
  toolCall.id
68590
68772
  )) }),
68773
+ renderedToolCalls.length > 0 && /* @__PURE__ */ jsx("div", { className: "space-y-3", children: renderedToolCalls.map(({ toolCall, toolResult, Renderer }) => /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-border bg-background-secondary/40 p-3", children: /* @__PURE__ */ jsx(Renderer, { toolCall, toolResult }) }, toolCall.id)) }),
68591
68774
  uiResources.length > 0 && /* @__PURE__ */ jsx("div", { className: "space-y-3", children: uiResources.map((uiResource, index2) => /* @__PURE__ */ jsx(UIResourceWidget, { uiResource, theme }, index2)) }),
68592
68775
  (message.content || isStreaming) && /* @__PURE__ */ jsxs("div", { className: "group flex gap-3", children: [
68593
68776
  /* @__PURE__ */ jsx("div", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-secondary overflow-hidden", children: ((_f = config.ui) == null ? void 0 : _f.logo) ? /* @__PURE__ */ jsx("img", { src: typeof config.ui.logo === "string" ? config.ui.logo : theme === "dark" ? config.ui.logo.dark : config.ui.logo.light, alt: "", className: "h-full w-full object-cover" }) : /* @__PURE__ */ jsx(Bot, { size: 12, className: "text-white" }) }),
@@ -68678,6 +68861,12 @@ function MessageList({
68678
68861
  const bottomRef = useRef(null);
68679
68862
  const { theme } = useTheme();
68680
68863
  const config = useConfig();
68864
+ const extensionTools = useExtensionTools();
68865
+ const toolRendererMap = useMemo(() => {
68866
+ const map2 = /* @__PURE__ */ new Map();
68867
+ extensionTools.forEach((tool) => map2.set(tool.name, tool));
68868
+ return map2;
68869
+ }, [extensionTools]);
68681
68870
  const showToolCalls = ((_a = config.mcp) == null ? void 0 : _a.showToolCalls) !== false;
68682
68871
  useEffect(() => {
68683
68872
  var _a2;
@@ -68685,9 +68874,33 @@ function MessageList({
68685
68874
  }, [messages2, streamingContent, pendingToolCalls, completedToolCalls]);
68686
68875
  const hasToolActivity = pendingToolCalls.length > 0 || completedToolCalls.length > 0;
68687
68876
  const isStreaming = Boolean(streamingContent) || hasToolActivity;
68877
+ const renderedToolCalls = completedToolCalls.filter(
68878
+ (call) => {
68879
+ var _a2;
68880
+ return call.serverId === "native" && !!((_a2 = toolRendererMap.get(call.name)) == null ? void 0 : _a2.resultRenderer);
68881
+ }
68882
+ ).map((call) => {
68883
+ const renderer = toolRendererMap.get(call.name).resultRenderer;
68884
+ const toolCall = {
68885
+ id: call.id,
68886
+ serverId: call.serverId,
68887
+ toolName: call.name,
68888
+ arguments: call.input,
68889
+ status: call.isError ? "error" : "completed"
68890
+ };
68891
+ const toolResult = {
68892
+ toolCallId: call.id,
68893
+ content: call.result,
68894
+ isError: call.isError,
68895
+ uiResource: call.uiResource,
68896
+ structuredContent: call.structuredContent
68897
+ };
68898
+ return { toolCall, toolResult, Renderer: renderer };
68899
+ });
68900
+ const renderedToolCallIds = new Set(renderedToolCalls.map((entry) => entry.toolCall.id));
68688
68901
  const uiResources = completedToolCalls.filter((tc) => {
68689
68902
  var _a2;
68690
- return (_a2 = tc.uiResource) == null ? void 0 : _a2.text;
68903
+ return ((_a2 = tc.uiResource) == null ? void 0 : _a2.text) && !renderedToolCallIds.has(tc.id);
68691
68904
  }).map((tc) => tc.uiResource);
68692
68905
  console.log("[MessageList] Rendering messages:", messages2.length, "isStreaming:", isStreaming);
68693
68906
  messages2.forEach((msg, i) => {
@@ -68734,7 +68947,8 @@ function MessageList({
68734
68947
  toolResult: {
68735
68948
  toolCallId: call.id,
68736
68949
  content: call.result,
68737
- isError: call.isError
68950
+ isError: call.isError,
68951
+ structuredContent: call.structuredContent
68738
68952
  },
68739
68953
  hideUiResource: true
68740
68954
  },
@@ -68755,6 +68969,7 @@ function MessageList({
68755
68969
  call.id
68756
68970
  ))
68757
68971
  ] }),
68972
+ renderedToolCalls.length > 0 && /* @__PURE__ */ jsx("div", { className: "space-y-3", children: renderedToolCalls.map(({ toolCall, toolResult, Renderer }) => /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-border bg-background-secondary/40 p-3", children: /* @__PURE__ */ jsx(Renderer, { toolCall, toolResult }) }, toolCall.id)) }),
68758
68973
  uiResources.length > 0 && /* @__PURE__ */ jsx("div", { className: "space-y-3", children: uiResources.map((uiResource, index2) => /* @__PURE__ */ jsx(UIResourceWidget, { uiResource, theme }, index2)) }),
68759
68974
  streamingContent && /* @__PURE__ */ jsxs("div", { className: "group flex gap-3", children: [
68760
68975
  /* @__PURE__ */ jsx("div", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-secondary", children: /* @__PURE__ */ jsx(Bot, { size: 12, className: "text-white" }) }),
@@ -69790,11 +70005,20 @@ function RegisterPage() {
69790
70005
  const { register: register2 } = useAuth();
69791
70006
  const config = useConfig();
69792
70007
  const { theme } = useTheme();
70008
+ const [searchParams] = useSearchParams();
70009
+ const inviteToken = searchParams.get("invite") || void 0;
70010
+ const referralCode = searchParams.get("ref") || void 0;
70011
+ const gating = config.auth.gating;
70012
+ const signupsRestricted = (gating == null ? void 0 : gating.mode) && gating.mode !== "open" && !inviteToken;
70013
+ const waitlistEnabled = (gating == null ? void 0 : gating.waitlistEnabled) ?? false;
70014
+ const showWaitlist = !!signupsRestricted && waitlistEnabled;
70015
+ const showRestrictedMessage = !!signupsRestricted && !waitlistEnabled;
69793
70016
  const [name2, setName] = useState("");
69794
70017
  const [email, setEmail] = useState("");
69795
70018
  const [password, setPassword] = useState("");
69796
70019
  const [error, setError] = useState("");
69797
70020
  const [isLoading, setIsLoading] = useState(false);
70021
+ const [waitlistStatus, setWaitlistStatus] = useState("idle");
69798
70022
  async function handleSubmit(e) {
69799
70023
  e.preventDefault();
69800
70024
  setError("");
@@ -69804,7 +70028,10 @@ function RegisterPage() {
69804
70028
  }
69805
70029
  setIsLoading(true);
69806
70030
  try {
69807
- const { requiresVerification } = await register2(email, password, name2 || void 0);
70031
+ const { requiresVerification } = await register2(email, password, name2 || void 0, {
70032
+ inviteToken,
70033
+ referralCode
70034
+ });
69808
70035
  if (requiresVerification) {
69809
70036
  navigate("/verify-email");
69810
70037
  } else {
@@ -69816,6 +70043,16 @@ function RegisterPage() {
69816
70043
  setIsLoading(false);
69817
70044
  }
69818
70045
  }
70046
+ async function handleWaitlistSubmit(e) {
70047
+ e.preventDefault();
70048
+ setError("");
70049
+ try {
70050
+ await api.post("/api/auth/waitlist", { email, name: name2 || void 0 });
70051
+ setWaitlistStatus("submitted");
70052
+ } catch (err) {
70053
+ setError(err instanceof Error ? err.message : "Failed to join waitlist");
70054
+ }
70055
+ }
69819
70056
  return /* @__PURE__ */ jsx("div", { className: "flex min-h-screen items-center justify-center bg-background p-4", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-md", children: [
69820
70057
  /* @__PURE__ */ jsxs("div", { className: "mb-8 text-center", children: [
69821
70058
  config.ui.logo && /* @__PURE__ */ jsx(
@@ -69833,7 +70070,46 @@ function RegisterPage() {
69833
70070
  ] })
69834
70071
  ] }),
69835
70072
  error && /* @__PURE__ */ jsx("div", { className: "mb-4 rounded-lg bg-error/10 p-3 text-sm text-error", children: error }),
69836
- /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
70073
+ showRestrictedMessage ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-border bg-background-secondary p-4 text-sm text-text-secondary", children: "Signups are currently closed. Please check back later." }) : showWaitlist ? /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border bg-background-secondary p-4", children: [
70074
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-text-secondary", children: "Signups are currently restricted. Join the waitlist to get an invite." }),
70075
+ waitlistStatus === "submitted" ? /* @__PURE__ */ jsx("div", { className: "mt-4 rounded-lg bg-success/10 p-3 text-sm text-success", children: "Thanks! You’re on the waitlist." }) : /* @__PURE__ */ jsxs("form", { onSubmit: handleWaitlistSubmit, className: "mt-4 space-y-3", children: [
70076
+ /* @__PURE__ */ jsxs("div", { children: [
70077
+ /* @__PURE__ */ jsx("label", { htmlFor: "waitlist-name", className: "block text-sm font-medium text-text-primary", children: "Name (optional)" }),
70078
+ /* @__PURE__ */ jsx(
70079
+ "input",
70080
+ {
70081
+ type: "text",
70082
+ id: "waitlist-name",
70083
+ value: name2,
70084
+ onChange: (e) => setName(e.target.value),
70085
+ className: "mt-1 w-full rounded-lg border border-input-border bg-input-background px-4 py-2 text-text-primary focus:border-primary focus:outline-none"
70086
+ }
70087
+ )
70088
+ ] }),
70089
+ /* @__PURE__ */ jsxs("div", { children: [
70090
+ /* @__PURE__ */ jsx("label", { htmlFor: "waitlist-email", className: "block text-sm font-medium text-text-primary", children: "Email" }),
70091
+ /* @__PURE__ */ jsx(
70092
+ "input",
70093
+ {
70094
+ type: "email",
70095
+ id: "waitlist-email",
70096
+ value: email,
70097
+ onChange: (e) => setEmail(e.target.value),
70098
+ required: true,
70099
+ className: "mt-1 w-full rounded-lg border border-input-border bg-input-background px-4 py-2 text-text-primary focus:border-primary focus:outline-none"
70100
+ }
70101
+ )
70102
+ ] }),
70103
+ /* @__PURE__ */ jsx(
70104
+ "button",
70105
+ {
70106
+ type: "submit",
70107
+ className: "w-full rounded-lg bg-primary px-4 py-2 font-medium text-white hover:bg-primary-hover",
70108
+ children: "Join waitlist"
70109
+ }
70110
+ )
70111
+ ] })
70112
+ ] }) : /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
69837
70113
  /* @__PURE__ */ jsxs("div", { children: [
69838
70114
  /* @__PURE__ */ jsx(
69839
70115
  "label",
@@ -69908,7 +70184,7 @@ function RegisterPage() {
69908
70184
  }
69909
70185
  )
69910
70186
  ] }),
69911
- /* @__PURE__ */ jsxs("p", { className: "mt-6 text-center text-sm text-text-secondary", children: [
70187
+ !signupsRestricted && /* @__PURE__ */ jsxs("p", { className: "mt-6 text-center text-sm text-text-secondary", children: [
69912
70188
  "Already have an account?",
69913
70189
  " ",
69914
70190
  /* @__PURE__ */ jsx(Link$1, { to: "/login", className: "text-primary hover:underline", children: "Sign in" })
@@ -73180,6 +73456,28 @@ function AdminDashboardPage() {
73180
73456
  "Teams"
73181
73457
  ]
73182
73458
  }
73459
+ ),
73460
+ /* @__PURE__ */ jsxs(
73461
+ Link$1,
73462
+ {
73463
+ to: appPath("/admin/waitlist"),
73464
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
73465
+ children: [
73466
+ /* @__PURE__ */ jsx(Mail, { size: 16 }),
73467
+ "Waitlist"
73468
+ ]
73469
+ }
73470
+ ),
73471
+ /* @__PURE__ */ jsxs(
73472
+ Link$1,
73473
+ {
73474
+ to: appPath("/admin/promo-codes"),
73475
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
73476
+ children: [
73477
+ /* @__PURE__ */ jsx(Tag, { size: 16 }),
73478
+ "Promo Codes"
73479
+ ]
73480
+ }
73183
73481
  )
73184
73482
  ] }),
73185
73483
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-8", children: [
@@ -73454,6 +73752,28 @@ function AdminUsersPage() {
73454
73752
  "Teams"
73455
73753
  ]
73456
73754
  }
73755
+ ),
73756
+ /* @__PURE__ */ jsxs(
73757
+ Link$1,
73758
+ {
73759
+ to: appPath("/admin/waitlist"),
73760
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
73761
+ children: [
73762
+ /* @__PURE__ */ jsx(Mail, { size: 16 }),
73763
+ "Waitlist"
73764
+ ]
73765
+ }
73766
+ ),
73767
+ /* @__PURE__ */ jsxs(
73768
+ Link$1,
73769
+ {
73770
+ to: appPath("/admin/promo-codes"),
73771
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
73772
+ children: [
73773
+ /* @__PURE__ */ jsx(Tag, { size: 16 }),
73774
+ "Promo Codes"
73775
+ ]
73776
+ }
73457
73777
  )
73458
73778
  ] }),
73459
73779
  error && /* @__PURE__ */ jsx("div", { className: "mb-6 rounded-lg bg-error/10 p-4 text-sm text-error", children: error }),
@@ -73736,6 +74056,28 @@ function AdminTeamsPage() {
73736
74056
  "Teams"
73737
74057
  ]
73738
74058
  }
74059
+ ),
74060
+ /* @__PURE__ */ jsxs(
74061
+ Link$1,
74062
+ {
74063
+ to: appPath("/admin/waitlist"),
74064
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74065
+ children: [
74066
+ /* @__PURE__ */ jsx(Mail, { size: 16 }),
74067
+ "Waitlist"
74068
+ ]
74069
+ }
74070
+ ),
74071
+ /* @__PURE__ */ jsxs(
74072
+ Link$1,
74073
+ {
74074
+ to: appPath("/admin/promo-codes"),
74075
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74076
+ children: [
74077
+ /* @__PURE__ */ jsx(Tag, { size: 16 }),
74078
+ "Promo Codes"
74079
+ ]
74080
+ }
73739
74081
  )
73740
74082
  ] }),
73741
74083
  error && /* @__PURE__ */ jsx("div", { className: "mb-6 rounded-lg bg-error/10 p-4 text-sm text-error", children: error }),
@@ -73911,6 +74253,28 @@ function AdminTeamPage() {
73911
74253
  "Teams"
73912
74254
  ]
73913
74255
  }
74256
+ ),
74257
+ /* @__PURE__ */ jsxs(
74258
+ Link$1,
74259
+ {
74260
+ to: appPath("/admin/waitlist"),
74261
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74262
+ children: [
74263
+ /* @__PURE__ */ jsx(Mail, { size: 16 }),
74264
+ "Waitlist"
74265
+ ]
74266
+ }
74267
+ ),
74268
+ /* @__PURE__ */ jsxs(
74269
+ Link$1,
74270
+ {
74271
+ to: appPath("/admin/promo-codes"),
74272
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74273
+ children: [
74274
+ /* @__PURE__ */ jsx(Tag, { size: 16 }),
74275
+ "Promo Codes"
74276
+ ]
74277
+ }
73914
74278
  )
73915
74279
  ] }),
73916
74280
  /* @__PURE__ */ jsx("div", { className: "rounded-lg bg-error/10 p-4 text-sm text-error", children: error || "Team not found" }),
@@ -73970,6 +74334,28 @@ function AdminTeamPage() {
73970
74334
  "Teams"
73971
74335
  ]
73972
74336
  }
74337
+ ),
74338
+ /* @__PURE__ */ jsxs(
74339
+ Link$1,
74340
+ {
74341
+ to: appPath("/admin/waitlist"),
74342
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74343
+ children: [
74344
+ /* @__PURE__ */ jsx(Mail, { size: 16 }),
74345
+ "Waitlist"
74346
+ ]
74347
+ }
74348
+ ),
74349
+ /* @__PURE__ */ jsxs(
74350
+ Link$1,
74351
+ {
74352
+ to: appPath("/admin/promo-codes"),
74353
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74354
+ children: [
74355
+ /* @__PURE__ */ jsx(Tag, { size: 16 }),
74356
+ "Promo Codes"
74357
+ ]
74358
+ }
73973
74359
  )
73974
74360
  ] }),
73975
74361
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mb-6", children: [
@@ -74070,16 +74456,493 @@ function AdminTeamPage() {
74070
74456
  ] })
74071
74457
  ] }) });
74072
74458
  }
74459
+ function AdminWaitlistPage() {
74460
+ var _a;
74461
+ const config = useConfig();
74462
+ const appPath = useAppPath();
74463
+ const [entries, setEntries] = useState([]);
74464
+ const [isLoading, setIsLoading] = useState(true);
74465
+ const [error, setError] = useState("");
74466
+ const [success, setSuccess] = useState("");
74467
+ async function loadWaitlist() {
74468
+ setIsLoading(true);
74469
+ setError("");
74470
+ try {
74471
+ const response = await api.get("/api/admin/waitlist");
74472
+ setEntries(response.entries);
74473
+ } catch (err) {
74474
+ setError(err instanceof Error ? err.message : "Failed to load waitlist");
74475
+ } finally {
74476
+ setIsLoading(false);
74477
+ }
74478
+ }
74479
+ useEffect(() => {
74480
+ loadWaitlist();
74481
+ }, []);
74482
+ async function handleInvite(entryId, email) {
74483
+ setError("");
74484
+ setSuccess("");
74485
+ try {
74486
+ await api.post(`/api/admin/waitlist/${entryId}/invite`, {});
74487
+ setSuccess(`Invite sent to ${email}`);
74488
+ loadWaitlist();
74489
+ } catch (err) {
74490
+ setError(err instanceof Error ? err.message : "Failed to send invite");
74491
+ }
74492
+ }
74493
+ return /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-background p-4 sm:p-8", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-6xl", children: [
74494
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
74495
+ /* @__PURE__ */ jsx("h1", { className: "text-xl sm:text-2xl font-bold text-text-primary", children: "Admin" }),
74496
+ /* @__PURE__ */ jsx(
74497
+ Link$1,
74498
+ {
74499
+ to: appPath("/"),
74500
+ className: "flex items-center justify-center rounded-lg p-2 text-text-muted hover:text-text-primary hover:bg-background-secondary",
74501
+ "aria-label": "Close",
74502
+ children: /* @__PURE__ */ jsx(X, { size: 20 })
74503
+ }
74504
+ )
74505
+ ] }),
74506
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-6 sm:mb-8", children: [
74507
+ /* @__PURE__ */ jsxs(
74508
+ Link$1,
74509
+ {
74510
+ to: appPath("/admin"),
74511
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74512
+ children: [
74513
+ /* @__PURE__ */ jsx(LayoutDashboard, { size: 16 }),
74514
+ "Overview"
74515
+ ]
74516
+ }
74517
+ ),
74518
+ /* @__PURE__ */ jsxs(
74519
+ Link$1,
74520
+ {
74521
+ to: appPath("/admin/users"),
74522
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74523
+ children: [
74524
+ /* @__PURE__ */ jsx(Users, { size: 16 }),
74525
+ "Users"
74526
+ ]
74527
+ }
74528
+ ),
74529
+ ((_a = config.teams) == null ? void 0 : _a.enabled) && /* @__PURE__ */ jsxs(
74530
+ Link$1,
74531
+ {
74532
+ to: appPath("/admin/teams"),
74533
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74534
+ children: [
74535
+ /* @__PURE__ */ jsx(Building2, { size: 16 }),
74536
+ "Teams"
74537
+ ]
74538
+ }
74539
+ ),
74540
+ /* @__PURE__ */ jsxs(
74541
+ Link$1,
74542
+ {
74543
+ to: appPath("/admin/promo-codes"),
74544
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74545
+ children: [
74546
+ /* @__PURE__ */ jsx(Tag, { size: 16 }),
74547
+ "Promo Codes"
74548
+ ]
74549
+ }
74550
+ ),
74551
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 rounded-full bg-primary px-4 py-2 text-sm font-medium text-white", children: [
74552
+ /* @__PURE__ */ jsx(Mail, { size: 16 }),
74553
+ "Waitlist"
74554
+ ] })
74555
+ ] }),
74556
+ error && /* @__PURE__ */ jsx("div", { className: "mb-6 rounded-lg bg-error/10 p-4 text-sm text-error", children: error }),
74557
+ success && /* @__PURE__ */ jsx("div", { className: "mb-6 rounded-lg bg-success/10 p-4 text-sm text-success", children: success }),
74558
+ /* @__PURE__ */ jsxs("div", { className: "rounded-lg bg-background-secondary overflow-hidden", children: [
74559
+ /* @__PURE__ */ jsx("div", { className: "hidden md:block px-4 py-3 bg-background", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-12 gap-4 text-sm font-medium text-text-muted", children: [
74560
+ /* @__PURE__ */ jsx("div", { className: "col-span-4", children: "Email" }),
74561
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: "Name" }),
74562
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: "Status" }),
74563
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: "Joined" }),
74564
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: "Action" })
74565
+ ] }) }),
74566
+ /* @__PURE__ */ jsx("div", { className: "divide-y divide-background", children: isLoading ? /* @__PURE__ */ jsx("div", { className: "px-4 py-8 text-center text-text-muted", children: "Loading..." }) : entries.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-4 py-8 text-center text-text-muted", children: "No waitlist entries" }) : entries.map((entry) => /* @__PURE__ */ jsx("div", { className: "px-4 py-3 hover:bg-background/50", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-12 gap-2 md:gap-4 text-sm", children: [
74567
+ /* @__PURE__ */ jsx("div", { className: "md:col-span-4 text-text-primary", children: entry.email }),
74568
+ /* @__PURE__ */ jsx("div", { className: "md:col-span-2 text-text-secondary", children: entry.name || "-" }),
74569
+ /* @__PURE__ */ jsx("div", { className: "md:col-span-2 capitalize text-text-secondary", children: entry.status.replace("_", " ") }),
74570
+ /* @__PURE__ */ jsx("div", { className: "md:col-span-2 text-text-muted", children: new Date(entry.createdAt).toLocaleDateString() }),
74571
+ /* @__PURE__ */ jsx("div", { className: "md:col-span-2", children: /* @__PURE__ */ jsx(
74572
+ "button",
74573
+ {
74574
+ type: "button",
74575
+ onClick: () => handleInvite(entry.id, entry.email),
74576
+ disabled: entry.status === "invited",
74577
+ className: "rounded-lg bg-primary px-3 py-1.5 text-xs font-medium text-white hover:bg-primary-hover disabled:opacity-50",
74578
+ children: entry.status === "invited" ? "Invited" : "Invite"
74579
+ }
74580
+ ) })
74581
+ ] }) }, entry.id)) })
74582
+ ] })
74583
+ ] }) });
74584
+ }
74585
+ function AdminPromoCodesPage() {
74586
+ var _a, _b, _c;
74587
+ const config = useConfig();
74588
+ const appPath = useAppPath();
74589
+ const [promoCodes, setPromoCodes] = useState([]);
74590
+ const [isLoading, setIsLoading] = useState(true);
74591
+ const [error, setError] = useState("");
74592
+ const [success, setSuccess] = useState("");
74593
+ const [search2, setSearch] = useState("");
74594
+ const [statusFilter, setStatusFilter] = useState("all");
74595
+ const [copiedCode, setCopiedCode] = useState(null);
74596
+ const [form, setForm] = useState({
74597
+ code: "",
74598
+ credits: 10,
74599
+ maxUses: 100,
74600
+ startsAt: "",
74601
+ endsAt: "",
74602
+ creditsExpiresAt: ""
74603
+ });
74604
+ async function loadPromoCodes() {
74605
+ setIsLoading(true);
74606
+ setError("");
74607
+ try {
74608
+ const params = new URLSearchParams();
74609
+ if (search2.trim()) {
74610
+ params.set("search", search2.trim());
74611
+ }
74612
+ if (statusFilter !== "all") {
74613
+ params.set("status", statusFilter);
74614
+ }
74615
+ const query = params.toString();
74616
+ const response = await api.get(`/api/admin/promo-codes${query ? `?${query}` : ""}`);
74617
+ setPromoCodes(response.promoCodes);
74618
+ } catch (err) {
74619
+ setError(err instanceof Error ? err.message : "Failed to load promo codes");
74620
+ } finally {
74621
+ setIsLoading(false);
74622
+ }
74623
+ }
74624
+ useEffect(() => {
74625
+ loadPromoCodes();
74626
+ }, [search2, statusFilter]);
74627
+ async function handleCreatePromoCode(e) {
74628
+ e.preventDefault();
74629
+ setError("");
74630
+ setSuccess("");
74631
+ try {
74632
+ const payload = {
74633
+ code: form.code.trim(),
74634
+ credits: Number(form.credits),
74635
+ maxUses: Number(form.maxUses),
74636
+ startsAt: form.startsAt || void 0,
74637
+ endsAt: form.endsAt || void 0,
74638
+ creditsExpiresAt: form.creditsExpiresAt || void 0
74639
+ };
74640
+ const response = await api.post("/api/admin/promo-codes", payload);
74641
+ setPromoCodes((prev) => [response.promoCode, ...prev]);
74642
+ setSuccess(`Promo code ${response.promoCode.code} created`);
74643
+ setForm({
74644
+ code: "",
74645
+ credits: 10,
74646
+ maxUses: 100,
74647
+ startsAt: "",
74648
+ endsAt: "",
74649
+ creditsExpiresAt: ""
74650
+ });
74651
+ } catch (err) {
74652
+ setError(err instanceof Error ? err.message : "Failed to create promo code");
74653
+ }
74654
+ }
74655
+ const promoEnabled = ((_a = config.credits) == null ? void 0 : _a.enabled) && ((_b = config.credits) == null ? void 0 : _b.promoEnabled) !== false;
74656
+ async function handleDeactivate(promo) {
74657
+ setError("");
74658
+ setSuccess("");
74659
+ try {
74660
+ const response = await api.patch(`/api/admin/promo-codes/${promo.id}`, {
74661
+ endsAt: (/* @__PURE__ */ new Date()).toISOString()
74662
+ });
74663
+ setPromoCodes((prev) => prev.map((p) => p.id === promo.id ? response.promoCode : p));
74664
+ setSuccess(`Promo code ${promo.code} deactivated`);
74665
+ } catch (err) {
74666
+ setError(err instanceof Error ? err.message : "Failed to deactivate promo code");
74667
+ }
74668
+ }
74669
+ async function handleReactivate(promo) {
74670
+ setError("");
74671
+ setSuccess("");
74672
+ try {
74673
+ const response = await api.patch(`/api/admin/promo-codes/${promo.id}`, {
74674
+ endsAt: null
74675
+ });
74676
+ setPromoCodes((prev) => prev.map((p) => p.id === promo.id ? response.promoCode : p));
74677
+ setSuccess(`Promo code ${promo.code} reactivated`);
74678
+ } catch (err) {
74679
+ setError(err instanceof Error ? err.message : "Failed to reactivate promo code");
74680
+ }
74681
+ }
74682
+ async function handleCopy(code2) {
74683
+ try {
74684
+ await navigator.clipboard.writeText(code2);
74685
+ setCopiedCode(code2);
74686
+ setTimeout(() => setCopiedCode((current) => current === code2 ? null : current), 1500);
74687
+ } catch (err) {
74688
+ setError(err instanceof Error ? err.message : "Failed to copy code");
74689
+ }
74690
+ }
74691
+ return /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-background p-4 sm:p-8", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-6xl", children: [
74692
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
74693
+ /* @__PURE__ */ jsx("h1", { className: "text-xl sm:text-2xl font-bold text-text-primary", children: "Admin" }),
74694
+ /* @__PURE__ */ jsx(
74695
+ Link$1,
74696
+ {
74697
+ to: appPath("/"),
74698
+ className: "flex items-center justify-center rounded-lg p-2 text-text-muted hover:text-text-primary hover:bg-background-secondary",
74699
+ "aria-label": "Close",
74700
+ children: /* @__PURE__ */ jsx(X, { size: 20 })
74701
+ }
74702
+ )
74703
+ ] }),
74704
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-6 sm:mb-8", children: [
74705
+ /* @__PURE__ */ jsxs(
74706
+ Link$1,
74707
+ {
74708
+ to: appPath("/admin"),
74709
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74710
+ children: [
74711
+ /* @__PURE__ */ jsx(LayoutDashboard, { size: 16 }),
74712
+ "Overview"
74713
+ ]
74714
+ }
74715
+ ),
74716
+ /* @__PURE__ */ jsxs(
74717
+ Link$1,
74718
+ {
74719
+ to: appPath("/admin/users"),
74720
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74721
+ children: [
74722
+ /* @__PURE__ */ jsx(Users, { size: 16 }),
74723
+ "Users"
74724
+ ]
74725
+ }
74726
+ ),
74727
+ ((_c = config.teams) == null ? void 0 : _c.enabled) && /* @__PURE__ */ jsxs(
74728
+ Link$1,
74729
+ {
74730
+ to: appPath("/admin/teams"),
74731
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74732
+ children: [
74733
+ /* @__PURE__ */ jsx(Building2, { size: 16 }),
74734
+ "Teams"
74735
+ ]
74736
+ }
74737
+ ),
74738
+ /* @__PURE__ */ jsxs(
74739
+ Link$1,
74740
+ {
74741
+ to: appPath("/admin/waitlist"),
74742
+ className: "flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80",
74743
+ children: [
74744
+ /* @__PURE__ */ jsx(Mail, { size: 16 }),
74745
+ "Waitlist"
74746
+ ]
74747
+ }
74748
+ ),
74749
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 rounded-full bg-primary px-4 py-2 text-sm font-medium text-white", children: [
74750
+ /* @__PURE__ */ jsx(Tag, { size: 16 }),
74751
+ "Promo Codes"
74752
+ ] })
74753
+ ] }),
74754
+ !promoEnabled && /* @__PURE__ */ jsx("div", { className: "mb-6 rounded-lg bg-warning/10 p-4 text-sm text-warning", children: "Promo codes are disabled in config. Enable `credits.promoEnabled` to use this page." }),
74755
+ error && /* @__PURE__ */ jsx("div", { className: "mb-6 rounded-lg bg-error/10 p-4 text-sm text-error", children: error }),
74756
+ success && /* @__PURE__ */ jsx("div", { className: "mb-6 rounded-lg bg-success/10 p-4 text-sm text-success", children: success }),
74757
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-3 gap-6", children: [
74758
+ /* @__PURE__ */ jsxs("div", { className: "lg:col-span-1 rounded-lg bg-background-secondary p-4", children: [
74759
+ /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-text-primary mb-4", children: "Create Promo Code" }),
74760
+ /* @__PURE__ */ jsxs("form", { onSubmit: handleCreatePromoCode, className: "space-y-3", children: [
74761
+ /* @__PURE__ */ jsxs("div", { children: [
74762
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-text-primary", htmlFor: "promo-code", children: "Code" }),
74763
+ /* @__PURE__ */ jsx(
74764
+ "input",
74765
+ {
74766
+ id: "promo-code",
74767
+ value: form.code,
74768
+ onChange: (e) => setForm((prev) => ({ ...prev, code: e.target.value })),
74769
+ className: "mt-1 w-full rounded-lg border border-input-border bg-input-background px-3 py-2 text-text-primary focus:border-primary focus:outline-none",
74770
+ placeholder: "WELCOME10",
74771
+ required: true
74772
+ }
74773
+ )
74774
+ ] }),
74775
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
74776
+ /* @__PURE__ */ jsxs("div", { children: [
74777
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-text-primary", htmlFor: "promo-credits", children: "Credits" }),
74778
+ /* @__PURE__ */ jsx(
74779
+ "input",
74780
+ {
74781
+ id: "promo-credits",
74782
+ type: "number",
74783
+ min: 1,
74784
+ value: form.credits,
74785
+ onChange: (e) => setForm((prev) => ({ ...prev, credits: Number(e.target.value) })),
74786
+ className: "mt-1 w-full rounded-lg border border-input-border bg-input-background px-3 py-2 text-text-primary focus:border-primary focus:outline-none",
74787
+ required: true
74788
+ }
74789
+ )
74790
+ ] }),
74791
+ /* @__PURE__ */ jsxs("div", { children: [
74792
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-text-primary", htmlFor: "promo-uses", children: "Max Uses" }),
74793
+ /* @__PURE__ */ jsx(
74794
+ "input",
74795
+ {
74796
+ id: "promo-uses",
74797
+ type: "number",
74798
+ min: 1,
74799
+ value: form.maxUses,
74800
+ onChange: (e) => setForm((prev) => ({ ...prev, maxUses: Number(e.target.value) })),
74801
+ className: "mt-1 w-full rounded-lg border border-input-border bg-input-background px-3 py-2 text-text-primary focus:border-primary focus:outline-none",
74802
+ required: true
74803
+ }
74804
+ )
74805
+ ] })
74806
+ ] }),
74807
+ /* @__PURE__ */ jsxs("div", { children: [
74808
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-text-primary", htmlFor: "promo-start", children: "Starts At (optional)" }),
74809
+ /* @__PURE__ */ jsx(
74810
+ "input",
74811
+ {
74812
+ id: "promo-start",
74813
+ type: "datetime-local",
74814
+ value: form.startsAt,
74815
+ onChange: (e) => setForm((prev) => ({ ...prev, startsAt: e.target.value })),
74816
+ className: "mt-1 w-full rounded-lg border border-input-border bg-input-background px-3 py-2 text-text-primary focus:border-primary focus:outline-none"
74817
+ }
74818
+ )
74819
+ ] }),
74820
+ /* @__PURE__ */ jsxs("div", { children: [
74821
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-text-primary", htmlFor: "promo-end", children: "Ends At (optional)" }),
74822
+ /* @__PURE__ */ jsx(
74823
+ "input",
74824
+ {
74825
+ id: "promo-end",
74826
+ type: "datetime-local",
74827
+ value: form.endsAt,
74828
+ onChange: (e) => setForm((prev) => ({ ...prev, endsAt: e.target.value })),
74829
+ className: "mt-1 w-full rounded-lg border border-input-border bg-input-background px-3 py-2 text-text-primary focus:border-primary focus:outline-none"
74830
+ }
74831
+ )
74832
+ ] }),
74833
+ /* @__PURE__ */ jsxs("div", { children: [
74834
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-text-primary", htmlFor: "promo-expires", children: "Credits Expire At (optional)" }),
74835
+ /* @__PURE__ */ jsx(
74836
+ "input",
74837
+ {
74838
+ id: "promo-expires",
74839
+ type: "datetime-local",
74840
+ value: form.creditsExpiresAt,
74841
+ onChange: (e) => setForm((prev) => ({ ...prev, creditsExpiresAt: e.target.value })),
74842
+ className: "mt-1 w-full rounded-lg border border-input-border bg-input-background px-3 py-2 text-text-primary focus:border-primary focus:outline-none"
74843
+ }
74844
+ )
74845
+ ] }),
74846
+ /* @__PURE__ */ jsx(
74847
+ "button",
74848
+ {
74849
+ type: "submit",
74850
+ disabled: !promoEnabled,
74851
+ className: "w-full rounded-lg bg-primary px-4 py-2 font-medium text-white hover:bg-primary-hover disabled:opacity-50",
74852
+ children: "Create promo code"
74853
+ }
74854
+ )
74855
+ ] })
74856
+ ] }),
74857
+ /* @__PURE__ */ jsxs("div", { className: "lg:col-span-2 rounded-lg bg-background-secondary overflow-hidden", children: [
74858
+ /* @__PURE__ */ jsxs("div", { className: "px-4 py-3 bg-background flex flex-wrap items-center gap-3", children: [
74859
+ /* @__PURE__ */ jsx(
74860
+ "input",
74861
+ {
74862
+ type: "text",
74863
+ placeholder: "Search code...",
74864
+ value: search2,
74865
+ onChange: (e) => setSearch(e.target.value),
74866
+ className: "flex-1 min-w-[160px] rounded-lg border border-input-border bg-input-background px-3 py-2 text-sm text-text-primary focus:border-primary focus:outline-none"
74867
+ }
74868
+ ),
74869
+ /* @__PURE__ */ jsxs(
74870
+ "select",
74871
+ {
74872
+ value: statusFilter,
74873
+ onChange: (e) => setStatusFilter(e.target.value),
74874
+ className: "rounded-lg border border-input-border bg-input-background px-3 py-2 text-sm text-text-primary focus:border-primary focus:outline-none",
74875
+ children: [
74876
+ /* @__PURE__ */ jsx("option", { value: "all", children: "All" }),
74877
+ /* @__PURE__ */ jsx("option", { value: "active", children: "Active" }),
74878
+ /* @__PURE__ */ jsx("option", { value: "scheduled", children: "Scheduled" }),
74879
+ /* @__PURE__ */ jsx("option", { value: "expired", children: "Expired" })
74880
+ ]
74881
+ }
74882
+ )
74883
+ ] }),
74884
+ /* @__PURE__ */ jsx("div", { className: "hidden md:block px-4 py-3 bg-background", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-12 gap-4 text-sm font-medium text-text-muted", children: [
74885
+ /* @__PURE__ */ jsx("div", { className: "col-span-3", children: "Code" }),
74886
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: "Credits" }),
74887
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: "Uses" }),
74888
+ /* @__PURE__ */ jsx("div", { className: "col-span-2", children: "Active" }),
74889
+ /* @__PURE__ */ jsx("div", { className: "col-span-3", children: "Actions" })
74890
+ ] }) }),
74891
+ /* @__PURE__ */ jsx("div", { className: "divide-y divide-background", children: isLoading ? /* @__PURE__ */ jsx("div", { className: "px-4 py-8 text-center text-text-muted", children: "Loading..." }) : promoCodes.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-4 py-8 text-center text-text-muted", children: "No promo codes yet" }) : promoCodes.map((promo) => {
74892
+ const now = /* @__PURE__ */ new Date();
74893
+ const startsAt = promo.startsAt ? new Date(promo.startsAt) : null;
74894
+ const endsAt = promo.endsAt ? new Date(promo.endsAt) : null;
74895
+ const active = (!startsAt || startsAt <= now) && (!endsAt || endsAt >= now);
74896
+ return /* @__PURE__ */ jsx("div", { className: "px-4 py-3 hover:bg-background/50", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-12 gap-2 md:gap-4 text-sm", children: [
74897
+ /* @__PURE__ */ jsxs("div", { className: "md:col-span-3 font-medium text-text-primary flex items-center gap-2", children: [
74898
+ /* @__PURE__ */ jsx("span", { children: promo.code }),
74899
+ /* @__PURE__ */ jsx(
74900
+ "button",
74901
+ {
74902
+ type: "button",
74903
+ onClick: () => handleCopy(promo.code),
74904
+ className: "text-xs text-primary hover:underline",
74905
+ children: copiedCode === promo.code ? "Copied" : "Copy"
74906
+ }
74907
+ )
74908
+ ] }),
74909
+ /* @__PURE__ */ jsx("div", { className: "md:col-span-2 text-text-secondary", children: promo.credits }),
74910
+ /* @__PURE__ */ jsxs("div", { className: "md:col-span-2 text-text-secondary", children: [
74911
+ promo.redeemedCount,
74912
+ " / ",
74913
+ promo.maxUses
74914
+ ] }),
74915
+ /* @__PURE__ */ jsx("div", { className: `md:col-span-2 ${active ? "text-success" : "text-text-muted"}`, children: active ? "Active" : "Inactive" }),
74916
+ /* @__PURE__ */ jsxs("div", { className: "md:col-span-3 text-text-muted flex items-center gap-2", children: [
74917
+ /* @__PURE__ */ jsx("span", { children: new Date(promo.createdAt).toLocaleDateString() }),
74918
+ /* @__PURE__ */ jsx(
74919
+ "button",
74920
+ {
74921
+ type: "button",
74922
+ onClick: () => active ? handleDeactivate(promo) : handleReactivate(promo),
74923
+ className: "rounded-lg border border-input-border px-2 py-1 text-xs text-text-primary hover:bg-background",
74924
+ children: active ? "Deactivate" : "Reactivate"
74925
+ }
74926
+ )
74927
+ ] })
74928
+ ] }) }, promo.id);
74929
+ }) })
74930
+ ] })
74931
+ ] })
74932
+ ] }) });
74933
+ }
74073
74934
  const styles = "@chaaskit/client/src/styles/index.css";
74074
- function ChatProviders({ children }) {
74075
- return /* @__PURE__ */ jsx(ConfigProvider, { children: /* @__PURE__ */ jsx(ThemeProvider, { children: /* @__PURE__ */ jsx(AuthProvider, { children: /* @__PURE__ */ jsx(TeamProvider, { children: /* @__PURE__ */ jsx(ProjectProvider, { children }) }) }) }) });
74935
+ function ChatProviders({ children, initialConfig }) {
74936
+ return /* @__PURE__ */ jsx(ConfigProvider, { initialConfig, children: /* @__PURE__ */ jsx(ThemeProvider, { children: /* @__PURE__ */ jsx(AuthProvider, { children: /* @__PURE__ */ jsx(TeamProvider, { children: /* @__PURE__ */ jsx(ProjectProvider, { children }) }) }) }) });
74076
74937
  }
74077
74938
  export {
74078
74939
  AcceptInvitePage,
74079
74940
  AdminDashboardPage,
74941
+ AdminPromoCodesPage,
74080
74942
  AdminTeamPage,
74081
74943
  AdminTeamsPage,
74082
74944
  AdminUsersPage,
74945
+ AdminWaitlistPage,
74083
74946
  ApiKeysPage,
74084
74947
  AuthProvider,
74085
74948
  C as ChatLoadingSkeleton,
@@ -74108,7 +74971,7 @@ export {
74108
74971
  TermsPage,
74109
74972
  ThemeProvider,
74110
74973
  VerifyEmailPage,
74111
- c2 as clientRegistry,
74974
+ clientRegistry,
74112
74975
  styles,
74113
74976
  useAppPath,
74114
74977
  useAuth,