@chaaskit/client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/dist/favicon.svg +11 -0
  2. package/dist/index.html +17 -0
  3. package/dist/lib/LoadingSkeletons-IcIC2JPq.js +132 -0
  4. package/dist/lib/LoadingSkeletons-IcIC2JPq.js.map +1 -0
  5. package/dist/lib/ServerThemeProvider-DNF0LAyk.js +42 -0
  6. package/dist/lib/ServerThemeProvider-DNF0LAyk.js.map +1 -0
  7. package/dist/lib/extensions.js +10 -0
  8. package/dist/lib/extensions.js.map +1 -0
  9. package/dist/lib/favicon.svg +11 -0
  10. package/dist/lib/index.js +74126 -0
  11. package/dist/lib/index.js.map +1 -0
  12. package/dist/lib/logo.svg +12 -0
  13. package/dist/lib/routes/AcceptInviteRoute.js +19 -0
  14. package/dist/lib/routes/AcceptInviteRoute.js.map +1 -0
  15. package/dist/lib/routes/AdminDashboardRoute.js +19 -0
  16. package/dist/lib/routes/AdminDashboardRoute.js.map +1 -0
  17. package/dist/lib/routes/AdminTeamRoute.js +19 -0
  18. package/dist/lib/routes/AdminTeamRoute.js.map +1 -0
  19. package/dist/lib/routes/AdminTeamsRoute.js +19 -0
  20. package/dist/lib/routes/AdminTeamsRoute.js.map +1 -0
  21. package/dist/lib/routes/AdminUsersRoute.js +19 -0
  22. package/dist/lib/routes/AdminUsersRoute.js.map +1 -0
  23. package/dist/lib/routes/ApiKeysRoute.js +19 -0
  24. package/dist/lib/routes/ApiKeysRoute.js.map +1 -0
  25. package/dist/lib/routes/AutomationsRoute.js +19 -0
  26. package/dist/lib/routes/AutomationsRoute.js.map +1 -0
  27. package/dist/lib/routes/ChatRoute.js +19 -0
  28. package/dist/lib/routes/ChatRoute.js.map +1 -0
  29. package/dist/lib/routes/DocumentsRoute.js +19 -0
  30. package/dist/lib/routes/DocumentsRoute.js.map +1 -0
  31. package/dist/lib/routes/OAuthConsentRoute.js +19 -0
  32. package/dist/lib/routes/OAuthConsentRoute.js.map +1 -0
  33. package/dist/lib/routes/PricingRoute.js +19 -0
  34. package/dist/lib/routes/PricingRoute.js.map +1 -0
  35. package/dist/lib/routes/PrivacyRoute.js +19 -0
  36. package/dist/lib/routes/PrivacyRoute.js.map +1 -0
  37. package/dist/lib/routes/TeamSettingsRoute.js +19 -0
  38. package/dist/lib/routes/TeamSettingsRoute.js.map +1 -0
  39. package/dist/lib/routes/TermsRoute.js +19 -0
  40. package/dist/lib/routes/TermsRoute.js.map +1 -0
  41. package/dist/lib/routes/VerifyEmailRoute.js +19 -0
  42. package/dist/lib/routes/VerifyEmailRoute.js.map +1 -0
  43. package/dist/lib/routes.js +79 -0
  44. package/dist/lib/routes.js.map +1 -0
  45. package/dist/lib/ssr-utils.js +29 -0
  46. package/dist/lib/ssr-utils.js.map +1 -0
  47. package/dist/lib/ssr.js +60 -0
  48. package/dist/lib/ssr.js.map +1 -0
  49. package/dist/lib/styles.css +2410 -0
  50. package/dist/lib/useExtensions-B5nX_8XD.js +155 -0
  51. package/dist/lib/useExtensions-B5nX_8XD.js.map +1 -0
  52. package/dist/logo.svg +12 -0
  53. package/package.json +84 -0
  54. package/src/components/AgentSelector.tsx +90 -0
  55. package/src/components/BranchModal.tsx +129 -0
  56. package/src/components/ClientOnly.tsx +27 -0
  57. package/src/components/ExportMenu.tsx +122 -0
  58. package/src/components/LoadingSkeletons.tsx +110 -0
  59. package/src/components/MCPCredentialsSection.tsx +309 -0
  60. package/src/components/MentionChip.tsx +149 -0
  61. package/src/components/MentionDropdown.tsx +175 -0
  62. package/src/components/MentionInput.tsx +293 -0
  63. package/src/components/MessageItem.tsx +300 -0
  64. package/src/components/MessageList.tsx +159 -0
  65. package/src/components/OAuthAppsSection.tsx +124 -0
  66. package/src/components/ProjectFolder.tsx +141 -0
  67. package/src/components/ProjectModal.tsx +296 -0
  68. package/src/components/SSRMessageList.tsx +153 -0
  69. package/src/components/SearchModal.tsx +173 -0
  70. package/src/components/SettingsModal.tsx +412 -0
  71. package/src/components/ShareModal.tsx +280 -0
  72. package/src/components/Sidebar.tsx +491 -0
  73. package/src/components/TeamSwitcher.tsx +273 -0
  74. package/src/components/ToolCallDisplay.tsx +473 -0
  75. package/src/components/ToolConfirmationModal.tsx +130 -0
  76. package/src/components/UsageChart.tsx +177 -0
  77. package/src/components/content/CodeBlock.tsx +69 -0
  78. package/src/components/content/MarkdownRenderer.tsx +64 -0
  79. package/src/components/content/SSRMarkdownRenderer.tsx +158 -0
  80. package/src/contexts/AuthContext.tsx +119 -0
  81. package/src/contexts/ConfigContext.tsx +214 -0
  82. package/src/contexts/ProjectContext.tsx +167 -0
  83. package/src/contexts/ServerConfigProvider.tsx +41 -0
  84. package/src/contexts/ServerThemeProvider.tsx +47 -0
  85. package/src/contexts/TeamContext.tsx +255 -0
  86. package/src/contexts/ThemeContext.tsx +113 -0
  87. package/src/extensions/index.ts +15 -0
  88. package/src/extensions/registry.ts +187 -0
  89. package/src/extensions/useExtensions.ts +52 -0
  90. package/src/hooks/useAppPath.ts +34 -0
  91. package/src/hooks/useBasePath.ts +13 -0
  92. package/src/hooks/useKeyboardShortcuts.ts +50 -0
  93. package/src/hooks/useMentionSearch.ts +106 -0
  94. package/src/index.tsx +116 -0
  95. package/src/layouts/MainLayout.tsx +98 -0
  96. package/src/pages/AcceptInvitePage.tsx +175 -0
  97. package/src/pages/AdminDashboardPage.tsx +362 -0
  98. package/src/pages/AdminTeamPage.tsx +304 -0
  99. package/src/pages/AdminTeamsPage.tsx +242 -0
  100. package/src/pages/AdminUsersPage.tsx +385 -0
  101. package/src/pages/ApiKeysPage.tsx +449 -0
  102. package/src/pages/ChatPage.tsx +310 -0
  103. package/src/pages/DocumentsPage.tsx +577 -0
  104. package/src/pages/LoginPage.tsx +232 -0
  105. package/src/pages/OAuthConsentPage.tsx +234 -0
  106. package/src/pages/PricingPage.tsx +314 -0
  107. package/src/pages/PrivacyPage.tsx +65 -0
  108. package/src/pages/RegisterPage.tsx +153 -0
  109. package/src/pages/ScheduledPromptsPage.tsx +702 -0
  110. package/src/pages/SharedThreadPage.tsx +116 -0
  111. package/src/pages/TeamSettingsPage.tsx +1085 -0
  112. package/src/pages/TermsPage.tsx +82 -0
  113. package/src/pages/VerifyEmailPage.tsx +202 -0
  114. package/src/routes/AcceptInviteRoute.tsx +24 -0
  115. package/src/routes/AdminDashboardRoute.tsx +24 -0
  116. package/src/routes/AdminTeamRoute.tsx +24 -0
  117. package/src/routes/AdminTeamsRoute.tsx +24 -0
  118. package/src/routes/AdminUsersRoute.tsx +24 -0
  119. package/src/routes/ApiKeysRoute.tsx +24 -0
  120. package/src/routes/AutomationsRoute.tsx +24 -0
  121. package/src/routes/ChatRoute.tsx +28 -0
  122. package/src/routes/DocumentsRoute.tsx +24 -0
  123. package/src/routes/OAuthConsentRoute.tsx +24 -0
  124. package/src/routes/PricingRoute.tsx +24 -0
  125. package/src/routes/PrivacyRoute.tsx +24 -0
  126. package/src/routes/TeamSettingsRoute.tsx +24 -0
  127. package/src/routes/TermsRoute.tsx +24 -0
  128. package/src/routes/VerifyEmailRoute.tsx +24 -0
  129. package/src/routes/index.ts +57 -0
  130. package/src/ssr-utils.tsx +84 -0
  131. package/src/ssr.ts +123 -0
  132. package/src/stores/chatStore.ts +670 -0
  133. package/src/styles/index.css +254 -0
  134. package/src/utils/api.ts +78 -0
  135. package/src/vite-env.d.ts +13 -0
@@ -0,0 +1,310 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+ import { useParams, useNavigate, Link } from 'react-router';
3
+ import { Send, Paperclip, Loader2, AlertCircle, X, GitBranch, ArrowLeft, Folder } from 'lucide-react';
4
+ import { useConfig } from '../contexts/ConfigContext';
5
+ import { useProject } from '../contexts/ProjectContext';
6
+ import { useChatStore } from '../stores/chatStore';
7
+ import { useAppPath } from '../hooks/useAppPath';
8
+ import MessageList from '../components/MessageList';
9
+ import ToolConfirmationModal from '../components/ToolConfirmationModal';
10
+ import AgentSelector from '../components/AgentSelector';
11
+ import MentionInput, { type MentionInputHandle } from '../components/MentionInput';
12
+
13
+ export default function ChatPage() {
14
+ const { threadId } = useParams();
15
+ const navigate = useNavigate();
16
+ const appPath = useAppPath();
17
+ const config = useConfig();
18
+ const { projects, currentProjectId, projectsEnabled } = useProject();
19
+ const inputRef = useRef<MentionInputHandle>(null);
20
+ const [input, setInput] = useState('');
21
+ const [files, setFiles] = useState<File[]>([]);
22
+ const [error, setError] = useState<string | null>(null);
23
+
24
+ const {
25
+ currentThread,
26
+ threads,
27
+ isStreaming,
28
+ streamingContent,
29
+ pendingToolCalls,
30
+ completedToolCalls,
31
+ pendingConfirmation,
32
+ availableAgents,
33
+ loadThread,
34
+ sendMessage,
35
+ clearCurrentThread,
36
+ confirmTool,
37
+ loadAgents,
38
+ } = useChatStore();
39
+
40
+ // Find parent thread info if this is a branch
41
+ const parentThread = currentThread?.parentThreadId
42
+ ? threads.find((t) => t.id === currentThread.parentThreadId)
43
+ : null;
44
+
45
+ useEffect(() => {
46
+ if (threadId) {
47
+ loadThread(threadId);
48
+ } else {
49
+ clearCurrentThread();
50
+ }
51
+ }, [threadId]);
52
+
53
+ // Navigate to thread URL when a new thread is created from the welcome screen
54
+ useEffect(() => {
55
+ // Wait for real thread ID (not 'pending' placeholder)
56
+ if (!threadId && currentThread?.id && currentThread.id !== 'pending') {
57
+ navigate(appPath(`/thread/${currentThread.id}`), { replace: true });
58
+ }
59
+ }, [threadId, currentThread?.id, navigate, appPath]);
60
+
61
+ useEffect(() => {
62
+ // Load available agents on mount
63
+ loadAgents();
64
+ }, [loadAgents]);
65
+
66
+ useEffect(() => {
67
+ // Focus input on mount
68
+ inputRef.current?.focus();
69
+ }, []);
70
+
71
+ async function handleSubmit(e: React.FormEvent) {
72
+ e.preventDefault();
73
+ if (!input.trim() && files.length === 0) return;
74
+ if (isStreaming) return;
75
+
76
+ const content = input.trim();
77
+ const attachedFiles = [...files];
78
+
79
+ setInput('');
80
+ setFiles([]);
81
+ setError(null);
82
+
83
+ try {
84
+ await sendMessage(content, attachedFiles);
85
+ } catch (err) {
86
+ // Parse error message
87
+ let errorMessage = 'Failed to send message. Please try again.';
88
+ if (err instanceof Error) {
89
+ if (err.message.includes('429') || err.message.toLowerCase().includes('usage limit')) {
90
+ errorMessage = 'You\'ve reached your monthly message limit. Please upgrade your plan to continue.';
91
+ } else if (err.message.includes('401') || err.message.includes('403')) {
92
+ errorMessage = 'Please sign in to continue.';
93
+ } else {
94
+ errorMessage = err.message;
95
+ }
96
+ }
97
+ setError(errorMessage);
98
+ // Restore input so user doesn't lose their message
99
+ setInput(content);
100
+ setFiles(attachedFiles);
101
+ }
102
+ }
103
+
104
+ function handleKeyDown(e: React.KeyboardEvent) {
105
+ if (e.key === 'Enter' && !e.shiftKey) {
106
+ e.preventDefault();
107
+ handleSubmit(e);
108
+ }
109
+ }
110
+
111
+ function handleFileSelect(e: React.ChangeEvent<HTMLInputElement>) {
112
+ const selectedFiles = Array.from(e.target.files || []);
113
+ setFiles((prev) => [...prev, ...selectedFiles]);
114
+ e.target.value = '';
115
+ }
116
+
117
+ function removeFile(index: number) {
118
+ setFiles((prev) => prev.filter((_, i) => i !== index));
119
+ }
120
+
121
+ function handleSamplePrompt(prompt: string) {
122
+ setInput(prompt);
123
+ inputRef.current?.focus();
124
+ }
125
+
126
+ const showWelcome = !currentThread || currentThread.messages.length === 0;
127
+ const showAgentSelector = showWelcome && availableAgents.length > 1;
128
+
129
+ // Show project indicator when:
130
+ // 1. Viewing a thread that belongs to a project, OR
131
+ // 2. Creating a new chat with a project selected
132
+ const activeProjectId = currentThread?.projectId || (showWelcome ? currentProjectId : null);
133
+ const activeProject = activeProjectId ? projects.find(p => p.id === activeProjectId) : null;
134
+ const showProjectIndicator = projectsEnabled && activeProject;
135
+
136
+ return (
137
+ <div className="flex h-full flex-col">
138
+ {/* Project Context Bar */}
139
+ {showProjectIndicator && activeProject && (
140
+ <div className="flex items-center gap-2 border-b border-border bg-background-secondary px-4 py-2">
141
+ <Folder
142
+ size={14}
143
+ style={{ color: activeProject.color }}
144
+ fill={activeProject.color}
145
+ />
146
+ <span className="text-xs text-text-primary font-medium">{activeProject.name}</span>
147
+ {showWelcome && (
148
+ <span className="text-xs text-text-muted">• New chat will be added to this project</span>
149
+ )}
150
+ </div>
151
+ )}
152
+
153
+ {/* Branch Breadcrumb */}
154
+ {currentThread?.parentThreadId && (
155
+ <div className="flex items-center gap-2 border-b border-border bg-background-secondary px-4 py-2">
156
+ <GitBranch size={14} className="text-text-muted" />
157
+ <span className="text-xs text-text-muted">Branched from</span>
158
+ <Link
159
+ to={`/thread/${currentThread.parentThreadId}`}
160
+ className="flex items-center gap-1 text-xs text-primary hover:underline"
161
+ >
162
+ <ArrowLeft size={12} />
163
+ {parentThread?.title || 'Parent conversation'}
164
+ </Link>
165
+ </div>
166
+ )}
167
+
168
+ {/* Messages or Welcome */}
169
+ <div className="min-h-0 flex-1 overflow-y-auto">
170
+ {showWelcome ? (
171
+ <div className="flex min-h-full flex-col items-center justify-center px-4 py-8 sm:px-6">
172
+ <div className="max-w-2xl text-center">
173
+ <h1 className="mb-2 text-2xl font-bold text-text-primary sm:text-3xl">
174
+ {config.ui.welcomeTitle}
175
+ </h1>
176
+ <p className="mb-6 text-sm text-text-secondary sm:mb-8 sm:text-base">
177
+ {config.ui.welcomeSubtitle}
178
+ </p>
179
+
180
+ {/* Agent Selector - only shown when multiple agents available */}
181
+ {showAgentSelector && (
182
+ <div className="mb-6 flex justify-center">
183
+ <AgentSelector />
184
+ </div>
185
+ )}
186
+
187
+ {/* Sample Prompts */}
188
+ {config.ui.samplePrompts.length > 0 && (
189
+ <div className="flex flex-wrap justify-center gap-2">
190
+ {config.ui.samplePrompts.map((prompt, index) => (
191
+ <button
192
+ key={index}
193
+ onClick={() => handleSamplePrompt(prompt.prompt)}
194
+ className="rounded-lg border border-border bg-background-secondary px-3 py-2 text-sm text-text-secondary hover:border-primary hover:text-primary active:bg-primary/10 sm:px-4"
195
+ >
196
+ {prompt.label}
197
+ </button>
198
+ ))}
199
+ </div>
200
+ )}
201
+ </div>
202
+ </div>
203
+ ) : (
204
+ <MessageList
205
+ messages={currentThread.messages}
206
+ streamingContent={isStreaming ? streamingContent : undefined}
207
+ pendingToolCalls={isStreaming ? pendingToolCalls : undefined}
208
+ completedToolCalls={isStreaming ? completedToolCalls : undefined}
209
+ />
210
+ )}
211
+ </div>
212
+
213
+ {/* Input Area */}
214
+ <div
215
+ className="flex-shrink-0 border-t border-border bg-background p-3 sm:p-4"
216
+ style={{ paddingBottom: 'max(0.75rem, env(safe-area-inset-bottom))' }}
217
+ >
218
+ {/* Error Banner */}
219
+ {error && (
220
+ <div className="mb-3 flex items-start gap-2 rounded-lg border border-error/30 bg-error/10 p-3 text-sm text-error">
221
+ <AlertCircle size={18} className="mt-0.5 flex-shrink-0" />
222
+ <span className="flex-1">{error}</span>
223
+ <button
224
+ onClick={() => setError(null)}
225
+ className="flex-shrink-0 p-0.5 hover:bg-error/20 rounded"
226
+ aria-label="Dismiss error"
227
+ >
228
+ <X size={16} />
229
+ </button>
230
+ </div>
231
+ )}
232
+
233
+ {/* Attached Files */}
234
+ {files.length > 0 && (
235
+ <div className="mb-2 flex flex-wrap gap-2">
236
+ {files.map((file, index) => (
237
+ <div
238
+ key={index}
239
+ className="flex items-center gap-2 rounded-lg bg-background-secondary px-3 py-1.5 text-sm"
240
+ >
241
+ <span className="max-w-[120px] truncate sm:max-w-[150px]">{file.name}</span>
242
+ <button
243
+ onClick={() => removeFile(index)}
244
+ className="p-0.5 text-text-muted hover:text-error"
245
+ aria-label={`Remove ${file.name}`}
246
+ >
247
+ &times;
248
+ </button>
249
+ </div>
250
+ ))}
251
+ </div>
252
+ )}
253
+
254
+ <form onSubmit={handleSubmit} className="flex items-start gap-2">
255
+ {/* File Upload (uploads as documents with @mentions) */}
256
+ {config.documents?.enabled && (
257
+ <label className="flex-shrink-0 cursor-pointer rounded-lg p-2.5 text-text-secondary hover:bg-background-secondary hover:text-text-primary active:bg-background-secondary">
258
+ <Paperclip size={20} />
259
+ <input
260
+ type="file"
261
+ className="hidden"
262
+ multiple
263
+ accept=".txt,.md,.csv,.json"
264
+ onChange={handleFileSelect}
265
+ />
266
+ </label>
267
+ )}
268
+
269
+ {/* Text Input */}
270
+ <div className="flex-1 rounded-lg border border-input-border bg-input-background focus-within:border-primary">
271
+ <MentionInput
272
+ ref={inputRef}
273
+ value={input}
274
+ onChange={setInput}
275
+ onKeyDown={handleKeyDown}
276
+ placeholder={config.ui.inputPlaceholder}
277
+ rows={1}
278
+ maxHeight={200}
279
+ autoGrow
280
+ className="max-h-[200px] min-h-[44px] w-full resize-none bg-transparent px-3 py-3 text-base text-text-primary placeholder-text-muted focus:outline-none sm:px-4 sm:text-sm"
281
+ />
282
+ </div>
283
+
284
+ {/* Send Button */}
285
+ <button
286
+ type="submit"
287
+ disabled={isStreaming || (!input.trim() && files.length === 0)}
288
+ className="flex h-11 w-11 flex-shrink-0 items-center justify-center rounded-lg bg-primary text-white disabled:opacity-50 active:bg-primary-hover"
289
+ >
290
+ {isStreaming ? (
291
+ <Loader2 size={20} className="animate-spin" />
292
+ ) : (
293
+ <Send size={20} />
294
+ )}
295
+ </button>
296
+ </form>
297
+ </div>
298
+
299
+ {/* Tool Confirmation Modal */}
300
+ {pendingConfirmation && (
301
+ <ToolConfirmationModal
302
+ confirmation={pendingConfirmation}
303
+ onConfirm={(approved, scope) => {
304
+ confirmTool(pendingConfirmation.confirmationId, approved, scope);
305
+ }}
306
+ />
307
+ )}
308
+ </div>
309
+ );
310
+ }