@kb-labs/studio-app 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/.env.example +10 -0
  2. package/README.md +74 -0
  3. package/UI-KIT-COMPONENTS.md +783 -0
  4. package/UI-KIT-PLAN.md +326 -0
  5. package/eslint-rules/no-direct-antd-import.js +151 -0
  6. package/eslint.config.ts +27 -0
  7. package/index.html +17 -0
  8. package/package.json +72 -0
  9. package/postcss.config.js +7 -0
  10. package/public/favicon.ico +0 -0
  11. package/public/favicon.svg +7 -0
  12. package/rspack.config.mjs +118 -0
  13. package/src/App.tsx +46 -0
  14. package/src/components/devtools/devtools-log-feed.module.css +165 -0
  15. package/src/components/devtools/devtools-log-feed.tsx +300 -0
  16. package/src/components/devtools/devtools-panel.module.css +121 -0
  17. package/src/components/devtools/devtools-panel.tsx +115 -0
  18. package/src/components/empty-state.tsx +28 -0
  19. package/src/components/error-boundary.tsx +210 -0
  20. package/src/components/health-banner.module.css +103 -0
  21. package/src/components/health-banner.tsx +134 -0
  22. package/src/components/health-indicator.tsx +12 -0
  23. package/src/components/page-header.tsx +49 -0
  24. package/src/components/page-transition.tsx +24 -0
  25. package/src/components/role-switcher.tsx +148 -0
  26. package/src/components/run-badge.tsx +13 -0
  27. package/src/components/ui/icons.tsx +509 -0
  28. package/src/components/ui/index.ts +25 -0
  29. package/src/components/ui/kb-config-provider.tsx +130 -0
  30. package/src/components/ui/kb-header.module.css +51 -0
  31. package/src/components/ui/kb-header.tsx +112 -0
  32. package/src/components/ui/kb-notification-bell.module.css +103 -0
  33. package/src/components/ui/kb-notification-bell.tsx +199 -0
  34. package/src/components/ui/kb-page-layout.tsx +66 -0
  35. package/src/components/ui/kb-quick-search.module.css +86 -0
  36. package/src/components/ui/kb-quick-search.tsx +196 -0
  37. package/src/components/ui/kb-sidebar.module.css +47 -0
  38. package/src/components/ui/kb-sidebar.tsx +196 -0
  39. package/src/components/ui/kb-stat-card.module.css +21 -0
  40. package/src/components/ui/kb-stat-card.tsx +22 -0
  41. package/src/components/ui/kb-stat-grid.tsx +22 -0
  42. package/src/components/ui/kb-status-bar.module.css +49 -0
  43. package/src/components/ui/kb-status-bar.tsx +145 -0
  44. package/src/components/ui/kb-system-health-indicator.tsx +229 -0
  45. package/src/components/ui/kb-theme-toggle.tsx +42 -0
  46. package/src/components/ui/theme-adapter.ts +812 -0
  47. package/src/components/ui/theme-transition-overlay.tsx +70 -0
  48. package/src/components/ui/utils.ts +6 -0
  49. package/src/components/widget-error-boundary.tsx +172 -0
  50. package/src/config/env.ts +5 -0
  51. package/src/config/feature-flags.ts +482 -0
  52. package/src/config/studio.config.schema.ts +88 -0
  53. package/src/config/studio.config.ts +84 -0
  54. package/src/env.d.ts +23 -0
  55. package/src/hooks/use-command-palette.ts +6 -0
  56. package/src/hooks/use-devtools-store.ts +25 -0
  57. package/src/hooks/use-feature-flags.ts +101 -0
  58. package/src/index.css +10 -0
  59. package/src/layouts/dashboard-grid.tsx +122 -0
  60. package/src/layouts/index.ts +12 -0
  61. package/src/layouts/two-pane.tsx +134 -0
  62. package/src/main.tsx +15 -0
  63. package/src/modules/analytics/components/analytics-daily-stats-chart.tsx +257 -0
  64. package/src/modules/analytics/components/analytics-date-range-picker.tsx +121 -0
  65. package/src/modules/analytics/pages/analytics-cache-page.tsx +285 -0
  66. package/src/modules/analytics/pages/analytics-embeddings-page.tsx +274 -0
  67. package/src/modules/analytics/pages/analytics-events-page.tsx +425 -0
  68. package/src/modules/analytics/pages/analytics-llm-page.tsx +434 -0
  69. package/src/modules/analytics/pages/analytics-overview-page.tsx +336 -0
  70. package/src/modules/analytics/pages/analytics-page.tsx +20 -0
  71. package/src/modules/analytics/pages/analytics-storage-page.tsx +344 -0
  72. package/src/modules/analytics/pages/analytics-vectorstore-page.tsx +279 -0
  73. package/src/modules/analytics/routes/index.tsx +133 -0
  74. package/src/modules/analytics/utils/formatters.test.ts +148 -0
  75. package/src/modules/analytics/utils/formatters.ts +100 -0
  76. package/src/modules/auth/pages/login-page.module.css +113 -0
  77. package/src/modules/auth/pages/login-page.tsx +80 -0
  78. package/src/modules/auth/routes/index.tsx +24 -0
  79. package/src/modules/dashboard/components/ActivityTimelineWidget.tsx +199 -0
  80. package/src/modules/dashboard/components/AiInsightsWidget.tsx +358 -0
  81. package/src/modules/dashboard/components/AnimatedMetricCard.css +88 -0
  82. package/src/modules/dashboard/components/AnimatedMetricCard.tsx +125 -0
  83. package/src/modules/dashboard/components/HeroMetricCard.css +114 -0
  84. package/src/modules/dashboard/components/HeroMetricCard.tsx +152 -0
  85. package/src/modules/dashboard/components/HeroMetricsWidget.tsx +146 -0
  86. package/src/modules/dashboard/components/SystemResourcesWidget.tsx +197 -0
  87. package/src/modules/dashboard/components/dashboard-empty.tsx +22 -0
  88. package/src/modules/dashboard/components/dashboard-kpis-widget.tsx +59 -0
  89. package/src/modules/dashboard/components/dashboard-kpis.tsx +13 -0
  90. package/src/modules/dashboard/components/dashboard-latency-percentiles-widget.tsx +85 -0
  91. package/src/modules/dashboard/components/dashboard-metrics-widget.tsx +78 -0
  92. package/src/modules/dashboard/components/dashboard-plugins-widget.tsx +67 -0
  93. package/src/modules/dashboard/components/dashboard-requests-widget.tsx +62 -0
  94. package/src/modules/dashboard/layouts/dashboard-layout.tsx +16 -0
  95. package/src/modules/dashboard/pages/ai-insights-page.tsx +563 -0
  96. package/src/modules/dashboard/pages/dashboard-animations.css +317 -0
  97. package/src/modules/dashboard/pages/dashboard-page.tsx +13 -0
  98. package/src/modules/dashboard/routes/index.tsx +70 -0
  99. package/src/modules/observability/pages/devkit-page.tsx +263 -0
  100. package/src/modules/observability/pages/incident-detail-page.tsx +571 -0
  101. package/src/modules/observability/pages/incidents-page.tsx +370 -0
  102. package/src/modules/observability/pages/log-detail-page.tsx +508 -0
  103. package/src/modules/observability/pages/logs-page.tsx +1129 -0
  104. package/src/modules/observability/pages/prometheus-metrics-page.tsx +486 -0
  105. package/src/modules/observability/pages/state-broker-page.tsx +229 -0
  106. package/src/modules/observability/pages/system-events-page.tsx +255 -0
  107. package/src/modules/observability/routes/index.tsx +133 -0
  108. package/src/modules/plugins/components/marketplace-card.tsx +153 -0
  109. package/src/modules/plugins/components/plugin-ai-assistant-modal.tsx +374 -0
  110. package/src/modules/plugins/components/plugin-card.tsx +136 -0
  111. package/src/modules/plugins/components/plugin-detail/capabilities-summary.tsx +101 -0
  112. package/src/modules/plugins/components/plugin-detail/cli-commands-table.tsx +101 -0
  113. package/src/modules/plugins/components/plugin-detail/index.ts +7 -0
  114. package/src/modules/plugins/components/plugin-detail/jobs-table.tsx +64 -0
  115. package/src/modules/plugins/components/plugin-detail/permissions/env-permissions.tsx +30 -0
  116. package/src/modules/plugins/components/plugin-detail/permissions/filesystem-permissions.tsx +68 -0
  117. package/src/modules/plugins/components/plugin-detail/permissions/index.ts +6 -0
  118. package/src/modules/plugins/components/plugin-detail/permissions/network-permissions.tsx +43 -0
  119. package/src/modules/plugins/components/plugin-detail/permissions/platform-services.tsx +242 -0
  120. package/src/modules/plugins/components/plugin-detail/permissions/resource-quotas.tsx +96 -0
  121. package/src/modules/plugins/components/plugin-detail/permissions/shell-permissions.tsx +55 -0
  122. package/src/modules/plugins/components/plugin-detail/permissions-view.tsx +47 -0
  123. package/src/modules/plugins/components/plugin-detail/plugin-header.tsx +169 -0
  124. package/src/modules/plugins/components/plugin-detail/rest-routes-table.tsx +159 -0
  125. package/src/modules/plugins/components/plugin-detail/workflows-table.tsx +40 -0
  126. package/src/modules/plugins/components/plugin-grid.tsx +20 -0
  127. package/src/modules/plugins/components/plugin-table.tsx +178 -0
  128. package/src/modules/plugins/pages/marketplace-page.tsx +200 -0
  129. package/src/modules/plugins/pages/plugin-detail-page.tsx +1145 -0
  130. package/src/modules/plugins/routes/index.tsx +48 -0
  131. package/src/modules/plugins/utils/index.ts +1 -0
  132. package/src/modules/plugins/utils/schema-helpers.ts +21 -0
  133. package/src/modules/settings/components/api-routes-viewer.tsx +415 -0
  134. package/src/modules/settings/components/appearance-settings.tsx +200 -0
  135. package/src/modules/settings/components/configuration-settings.tsx +321 -0
  136. package/src/modules/settings/components/data-privacy-settings.tsx +111 -0
  137. package/src/modules/settings/components/experimental-settings.tsx +243 -0
  138. package/src/modules/settings/components/navigation-settings.tsx +198 -0
  139. package/src/modules/settings/components/notifications-settings.tsx +40 -0
  140. package/src/modules/settings/pages/settings-page.tsx +298 -0
  141. package/src/modules/settings/routes/index.tsx +47 -0
  142. package/src/pages/README-assistant.md +91 -0
  143. package/src/pages/login-page.tsx +177 -0
  144. package/src/pages/not-found-page.tsx +85 -0
  145. package/src/providers/auth-provider.tsx +40 -0
  146. package/src/providers/command-palette-provider.tsx +193 -0
  147. package/src/providers/data-sources-provider.tsx +27 -0
  148. package/src/providers/devtools-provider.tsx +72 -0
  149. package/src/providers/navigation-items-provider.tsx +38 -0
  150. package/src/providers/registry-v2-provider.tsx +93 -0
  151. package/src/providers/settings-provider.tsx +158 -0
  152. package/src/router.tsx +302 -0
  153. package/src/routes/helpers.tsx +25 -0
  154. package/src/routes/plugin-page-v2.tsx +52 -0
  155. package/src/routes/types.ts +18 -0
  156. package/src/styles/antd-overrides.css +211 -0
  157. package/src/styles/base.css +190 -0
  158. package/src/styles/settings.css +75 -0
  159. package/src/styles/variables.css +94 -0
  160. package/src/utils/analytics.ts +183 -0
  161. package/src/utils/apply-navigation-categories.ts +83 -0
  162. package/src/utils/commands.ts +252 -0
  163. package/src/utils/export.ts +174 -0
  164. package/src/utils/format-date.ts +166 -0
  165. package/src/utils/logger.ts +110 -0
  166. package/tailwind.config.ts +13 -0
  167. package/tsconfig.json +13 -0
  168. package/vitest-setup.ts +1 -0
@@ -0,0 +1,563 @@
1
+ import React, { useState, useRef, useEffect, useCallback } from 'react';
2
+ import {
3
+ UICard,
4
+ UIInputTextArea,
5
+ UIButton,
6
+ UITypographyText,
7
+ UITypographyParagraph,
8
+ UISpace,
9
+ UISpaceCompact,
10
+ UITag,
11
+ UISpin,
12
+ UIStatistic,
13
+ UICheckbox,
14
+ UISelect,
15
+ UITooltip,
16
+ UIMessage,
17
+ } from '@kb-labs/studio-ui-kit';
18
+ import {
19
+ SendOutlined,
20
+ RobotOutlined,
21
+ UserOutlined,
22
+ BulbOutlined,
23
+ CopyOutlined,
24
+ CheckOutlined,
25
+ ReloadOutlined,
26
+ SettingOutlined,
27
+ InfoCircleOutlined,
28
+ } from '@ant-design/icons';
29
+ import { useDataSources } from '../../../providers/data-sources-provider';
30
+ import { usePrometheusMetrics, useIncidents } from '@kb-labs/studio-data-client';
31
+ import { UIMarkdownViewer } from '@kb-labs/studio-ui-kit';
32
+
33
+ interface Message {
34
+ id: string;
35
+ role: 'user' | 'assistant';
36
+ content: string;
37
+ timestamp: number;
38
+ context?: string[];
39
+ thinking?: boolean;
40
+ }
41
+
42
+ interface ContextConfig {
43
+ includeMetrics: boolean;
44
+ includeIncidents: boolean;
45
+ includeHistory: boolean;
46
+ timeRange: '1h' | '6h' | '24h' | '7d';
47
+ plugins: string[];
48
+ }
49
+
50
+ const SUGGESTED_QUESTIONS = [
51
+ { text: 'Why is latency higher than usual?', category: 'Performance' },
52
+ { text: 'Which plugin should I optimize first?', category: 'Optimization' },
53
+ { text: 'Are there any anomalies in the last 24 hours?', category: 'Anomalies' },
54
+ { text: 'What caused the recent error spike?', category: 'Errors' },
55
+ { text: 'How is the system health compared to yesterday?', category: 'Health' },
56
+ { text: 'What are the most resource-intensive operations?', category: 'Resources' },
57
+ ];
58
+
59
+ const TIME_RANGE_OPTIONS = [
60
+ { value: '1h', label: 'Last 1 hour' },
61
+ { value: '6h', label: 'Last 6 hours' },
62
+ { value: '24h', label: 'Last 24 hours' },
63
+ { value: '7d', label: 'Last 7 days' },
64
+ ];
65
+
66
+ /**
67
+ * AI Insights Page
68
+ *
69
+ * Full-featured chat interface for AI-powered system analysis.
70
+ * Uses real metrics, incidents, and historical data as context for LLM.
71
+ */
72
+ export function AIInsightsPage() {
73
+ const sources = useDataSources();
74
+ const { data: metrics, isLoading: metricsLoading } = usePrometheusMetrics(sources.observability);
75
+ const { data: incidents } = useIncidents(sources.observability, { limit: 10 });
76
+
77
+ const [messages, setMessages] = useState<Message[]>([]);
78
+ const [input, setInput] = useState('');
79
+ const [isLoading, setIsLoading] = useState(false);
80
+ const [copiedId, setCopiedId] = useState<string | null>(null);
81
+ const [showConfig, setShowConfig] = useState(false);
82
+ const [contextConfig, setContextConfig] = useState<ContextConfig>({
83
+ includeMetrics: true,
84
+ includeIncidents: true,
85
+ includeHistory: true,
86
+ timeRange: '24h',
87
+ plugins: [],
88
+ });
89
+
90
+ const messagesEndRef = useRef<HTMLDivElement>(null);
91
+ const inputRef = useRef<HTMLTextAreaElement>(null);
92
+
93
+ // Auto-scroll to bottom when new messages arrive
94
+ useEffect(() => {
95
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
96
+ }, [messages]);
97
+
98
+ // Build context summary for display
99
+ const contextSummary = React.useMemo(() => {
100
+ if (!metrics) {return null;}
101
+
102
+ const errorRate = metrics.requests?.total
103
+ ? (((metrics.requests.clientErrors ?? 0) + (metrics.requests.serverErrors ?? 0)) / metrics.requests.total * 100)
104
+ : 0;
105
+
106
+ return {
107
+ requests: metrics.requests?.total ?? 0,
108
+ errorRate: errorRate.toFixed(2),
109
+ p99Latency: metrics.latency?.p99?.toFixed(0) ?? 'N/A',
110
+ pluginCount: metrics.perPlugin?.length ?? 0,
111
+ incidentCount: incidents?.length ?? 0,
112
+ };
113
+ }, [metrics, incidents]);
114
+
115
+ // Get available plugins for context filtering
116
+ const availablePlugins = React.useMemo(() => {
117
+ return metrics?.perPlugin?.map(p => p.pluginId) ?? [];
118
+ }, [metrics]);
119
+
120
+ // Generate AI response via backend API
121
+ const generateResponse = useCallback(async (question: string): Promise<string> => {
122
+ const response = await sources.observability.chatWithInsights(question, contextConfig);
123
+ return response.answer;
124
+ }, [sources.observability, contextConfig]);
125
+
126
+ // Send message
127
+ const handleSend = async () => {
128
+ if (!input.trim() || isLoading) {return;}
129
+
130
+ const userMessage: Message = {
131
+ id: `user-${Date.now()}`,
132
+ role: 'user',
133
+ content: input.trim(),
134
+ timestamp: Date.now(),
135
+ };
136
+
137
+ const loadingMessage: Message = {
138
+ id: `assistant-${Date.now()}`,
139
+ role: 'assistant',
140
+ content: '',
141
+ timestamp: Date.now(),
142
+ thinking: true,
143
+ context: [
144
+ contextConfig.includeMetrics ? 'metrics' : '',
145
+ contextConfig.includeIncidents ? 'incidents' : '',
146
+ contextConfig.includeHistory ? 'history' : '',
147
+ ].filter(Boolean),
148
+ };
149
+
150
+ setMessages(prev => [...prev, userMessage, loadingMessage]);
151
+ setInput('');
152
+ setIsLoading(true);
153
+
154
+ try {
155
+ const response = await generateResponse(userMessage.content);
156
+
157
+ setMessages(prev =>
158
+ prev.map(m =>
159
+ m.id === loadingMessage.id
160
+ ? { ...m, content: response, thinking: false }
161
+ : m
162
+ )
163
+ );
164
+ } catch (_error) {
165
+ setMessages(prev =>
166
+ prev.map(m =>
167
+ m.id === loadingMessage.id
168
+ ? { ...m, content: 'Sorry, I encountered an error analyzing your request. Please try again.', thinking: false }
169
+ : m
170
+ )
171
+ );
172
+ } finally {
173
+ setIsLoading(false);
174
+ }
175
+ };
176
+
177
+ // Copy message to clipboard
178
+ const handleCopy = async (content: string, id: string) => {
179
+ await navigator.clipboard.writeText(content);
180
+ setCopiedId(id);
181
+ UIMessage.success('Copied to clipboard');
182
+ setTimeout(() => setCopiedId(null), 2000);
183
+ };
184
+
185
+ // Handle suggested question click
186
+ const handleSuggestion = (question: string) => {
187
+ setInput(question);
188
+ inputRef.current?.focus();
189
+ };
190
+
191
+ // Clear conversation
192
+ const handleClear = () => {
193
+ setMessages([]);
194
+ };
195
+
196
+ return (
197
+ <div style={{ display: 'flex', gap: 24, height: 'calc(100vh - 280px)', minHeight: 500 }}>
198
+ {/* Main Chat Panel */}
199
+ <UICard
200
+ style={{ flex: 1, display: 'flex', flexDirection: 'column' }}
201
+ styles={{ body: { flex: 1, display: 'flex', flexDirection: 'column', padding: 0, overflow: 'hidden' } }}
202
+ title={
203
+ <UISpace>
204
+ <RobotOutlined />
205
+ <span>AI Assistant</span>
206
+ {metricsLoading && <UISpin size="small" />}
207
+ </UISpace>
208
+ }
209
+ extra={
210
+ <UISpace>
211
+ <UITooltip title="Clear conversation">
212
+ <UIButton icon={<ReloadOutlined />} onClick={handleClear} disabled={messages.length === 0} />
213
+ </UITooltip>
214
+ <UITooltip title="Configure context">
215
+ <UIButton
216
+ icon={<SettingOutlined />}
217
+ onClick={() => setShowConfig(!showConfig)}
218
+ variant={showConfig ? 'primary' : 'default'}
219
+ />
220
+ </UITooltip>
221
+ </UISpace>
222
+ }
223
+ >
224
+ {/* Messages Area */}
225
+ <div style={{ flex: 1, overflow: 'auto', padding: 16 }}>
226
+ {messages.length === 0 ? (
227
+ <div style={{ textAlign: 'center', padding: '48px 24px' }}>
228
+ <RobotOutlined style={{ fontSize: 48, color: '#bfbfbf', marginBottom: 16 }} />
229
+ <UITypographyText type="secondary" style={{ display: 'block', marginBottom: 24 }}>
230
+ Ask me anything about your system's performance, errors, or health.
231
+ I'll analyze real-time metrics and provide actionable insights.
232
+ </UITypographyText>
233
+
234
+ <div style={{ marginTop: 24 }}>
235
+ <UITypographyText strong style={{ display: 'block', marginBottom: 12 }}>
236
+ <BulbOutlined /> Suggested questions:
237
+ </UITypographyText>
238
+ <UISpace wrap style={{ justifyContent: 'center' }}>
239
+ {SUGGESTED_QUESTIONS.map((q, i) => (
240
+ <UITag
241
+ key={i}
242
+ color="blue"
243
+ style={{ cursor: 'pointer', padding: '4px 12px' }}
244
+ onClick={() => handleSuggestion(q.text)}
245
+ >
246
+ {q.text}
247
+ </UITag>
248
+ ))}
249
+ </UISpace>
250
+ </div>
251
+ </div>
252
+ ) : (
253
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
254
+ {messages.map(msg => (
255
+ <div
256
+ key={msg.id}
257
+ style={{
258
+ display: 'flex',
259
+ justifyContent: msg.role === 'user' ? 'flex-end' : 'flex-start',
260
+ }}
261
+ >
262
+ <div
263
+ style={{
264
+ maxWidth: '80%',
265
+ padding: '12px 16px',
266
+ borderRadius: 12,
267
+ background: msg.role === 'user' ? '#1890ff' : '#f5f5f5',
268
+ color: msg.role === 'user' ? '#fff' : '#000',
269
+ }}
270
+ >
271
+ <div style={{ display: 'flex', alignItems: 'flex-start', gap: 8 }}>
272
+ {msg.role === 'assistant' && (
273
+ <RobotOutlined style={{ marginTop: 4, color: '#1890ff' }} />
274
+ )}
275
+ <div style={{ flex: 1 }}>
276
+ {msg.thinking ? (
277
+ <UISpace>
278
+ <UISpin size="small" />
279
+ <UITypographyText type="secondary">Analyzing...</UITypographyText>
280
+ </UISpace>
281
+ ) : msg.role === 'assistant' ? (
282
+ <UIMarkdownViewer content={msg.content} className="ai-insights-markdown" />
283
+ ) : (
284
+ <UITypographyParagraph
285
+ style={{
286
+ margin: 0,
287
+ whiteSpace: 'pre-wrap',
288
+ color: '#fff',
289
+ }}
290
+ >
291
+ {msg.content}
292
+ </UITypographyParagraph>
293
+ )}
294
+ {msg.context && msg.context.length > 0 && (
295
+ <div style={{ marginTop: 8 }}>
296
+ {msg.context.map(c => (
297
+ <UITag key={c} color="default" style={{ fontSize: 10 }}>
298
+ {c}
299
+ </UITag>
300
+ ))}
301
+ </div>
302
+ )}
303
+ </div>
304
+ {msg.role === 'user' && <UserOutlined style={{ marginTop: 4 }} />}
305
+ </div>
306
+ {msg.role === 'assistant' && !msg.thinking && (
307
+ <div style={{ marginTop: 8, textAlign: 'right' }}>
308
+ <UIButton
309
+ variant="text"
310
+ size="small"
311
+ icon={copiedId === msg.id ? <CheckOutlined /> : <CopyOutlined />}
312
+ onClick={() => handleCopy(msg.content, msg.id)}
313
+ style={{ color: '#8c8c8c' }}
314
+ >
315
+ {copiedId === msg.id ? 'Copied' : 'Copy'}
316
+ </UIButton>
317
+ </div>
318
+ )}
319
+ </div>
320
+ </div>
321
+ ))}
322
+ <div ref={messagesEndRef} />
323
+ </div>
324
+ )}
325
+ </div>
326
+
327
+ {/* Input Area */}
328
+ <div style={{ padding: 16, borderTop: '1px solid #f0f0f0' }}>
329
+ <UISpaceCompact style={{ width: '100%' }}>
330
+ <UIInputTextArea
331
+ ref={inputRef}
332
+ value={input}
333
+ onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setInput(e.target.value)}
334
+ placeholder="Ask about your system..."
335
+ autoSize={{ minRows: 1, maxRows: 4 }}
336
+ onPressEnter={(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
337
+ if (!e.shiftKey) {
338
+ e.preventDefault();
339
+ handleSend();
340
+ }
341
+ }}
342
+ style={{ flex: 1 }}
343
+ />
344
+ <UIButton
345
+ variant="primary"
346
+ icon={<SendOutlined />}
347
+ onClick={handleSend}
348
+ loading={isLoading}
349
+ disabled={!input.trim()}
350
+ >
351
+ Send
352
+ </UIButton>
353
+ </UISpaceCompact>
354
+ <UITypographyText type="secondary" style={{ fontSize: 11, marginTop: 4, display: 'block' }}>
355
+ Press Enter to send, Shift+Enter for new line
356
+ </UITypographyText>
357
+ </div>
358
+ </UICard>
359
+
360
+ {/* Context Panel */}
361
+ <UICard
362
+ title={
363
+ <UISpace>
364
+ <InfoCircleOutlined />
365
+ <span>Context</span>
366
+ </UISpace>
367
+ }
368
+ style={{ width: 320, flexShrink: 0 }}
369
+ styles={{ body: { padding: 16 } }}
370
+ >
371
+ {/* System Summary */}
372
+ {contextSummary && (
373
+ <div style={{ marginBottom: 24 }}>
374
+ <UITypographyText strong style={{ display: 'block', marginBottom: 12 }}>System Summary</UITypographyText>
375
+ <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
376
+ <UIStatistic title="Requests" value={contextSummary.requests} valueStyle={{ fontSize: 18 }} />
377
+ <UIStatistic
378
+ title="Error Rate"
379
+ value={contextSummary.errorRate}
380
+ suffix="%"
381
+ valueStyle={{
382
+ fontSize: 18,
383
+ color: parseFloat(contextSummary.errorRate) > 5 ? '#ff4d4f' : parseFloat(contextSummary.errorRate) > 1 ? '#faad14' : '#52c41a',
384
+ }}
385
+ />
386
+ <UIStatistic title="P99 Latency" value={contextSummary.p99Latency} suffix="ms" valueStyle={{ fontSize: 18 }} />
387
+ <UIStatistic title="Incidents" value={contextSummary.incidentCount} valueStyle={{ fontSize: 18 }} />
388
+ </div>
389
+ </div>
390
+ )}
391
+
392
+ {/* Recent Incidents */}
393
+ {incidents && incidents.length > 0 && (
394
+ <div style={{ marginBottom: 24 }}>
395
+ <UITypographyText strong style={{ display: 'block', marginBottom: 12 }}>Recent Incidents</UITypographyText>
396
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
397
+ {incidents.slice(0, 5).map(incident => (
398
+ <div
399
+ key={incident.id}
400
+ style={{
401
+ padding: 8,
402
+ background: '#fafafa',
403
+ borderRadius: 4,
404
+ borderLeft: `3px solid ${
405
+ incident.severity === 'critical' ? '#ff4d4f' :
406
+ incident.severity === 'warning' ? '#faad14' : '#1890ff'
407
+ }`,
408
+ }}
409
+ >
410
+ <UITypographyText style={{ fontSize: 12 }}>{incident.title}</UITypographyText>
411
+ </div>
412
+ ))}
413
+ </div>
414
+ </div>
415
+ )}
416
+
417
+ {/* Context Configuration */}
418
+ {showConfig && (
419
+ <div style={{ borderTop: '1px solid #f0f0f0', paddingTop: 16 }}>
420
+ <UITypographyText strong style={{ display: 'block', marginBottom: 12 }}>Context Settings</UITypographyText>
421
+
422
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
423
+ <UICheckbox
424
+ checked={contextConfig.includeMetrics}
425
+ onChange={(checked) => setContextConfig(prev => ({ ...prev, includeMetrics: checked }))}
426
+ >
427
+ Include current metrics
428
+ </UICheckbox>
429
+ <UICheckbox
430
+ checked={contextConfig.includeIncidents}
431
+ onChange={(checked) => setContextConfig(prev => ({ ...prev, includeIncidents: checked }))}
432
+ >
433
+ Include incidents
434
+ </UICheckbox>
435
+ <UICheckbox
436
+ checked={contextConfig.includeHistory}
437
+ onChange={(checked) => setContextConfig(prev => ({ ...prev, includeHistory: checked }))}
438
+ >
439
+ Include historical trends
440
+ </UICheckbox>
441
+
442
+ <div>
443
+ <UITypographyText type="secondary" style={{ fontSize: 12, marginBottom: 4, display: 'block' }}>
444
+ Time Range
445
+ </UITypographyText>
446
+ <UISelect
447
+ value={contextConfig.timeRange}
448
+ onChange={value => setContextConfig(prev => ({ ...prev, timeRange: value as ContextConfig['timeRange'] }))}
449
+ options={TIME_RANGE_OPTIONS}
450
+ style={{ width: '100%' }}
451
+ size="small"
452
+ />
453
+ </div>
454
+
455
+ {availablePlugins.length > 0 && (
456
+ <div>
457
+ <UITypographyText type="secondary" style={{ fontSize: 12, marginBottom: 4, display: 'block' }}>
458
+ Focus on plugins
459
+ </UITypographyText>
460
+ <UISelect
461
+ mode="multiple"
462
+ value={contextConfig.plugins}
463
+ onChange={value => setContextConfig(prev => ({ ...prev, plugins: value as string[] }))}
464
+ options={availablePlugins.map(p => ({ value: p, label: p }))}
465
+ style={{ width: '100%' }}
466
+ size="small"
467
+ placeholder="All plugins"
468
+ maxTagCount={2}
469
+ />
470
+ </div>
471
+ )}
472
+ </div>
473
+ </div>
474
+ )}
475
+
476
+ {/* Suggested Questions */}
477
+ <div style={{ marginTop: 24 }}>
478
+ <UITypographyText strong style={{ display: 'block', marginBottom: 12 }}>
479
+ <BulbOutlined /> Quick Questions
480
+ </UITypographyText>
481
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
482
+ {SUGGESTED_QUESTIONS.slice(0, 4).map((q, i) => (
483
+ <UIButton
484
+ key={i}
485
+ variant="text"
486
+ size="small"
487
+ style={{ textAlign: 'left', height: 'auto', padding: '4px 8px' }}
488
+ onClick={() => handleSuggestion(q.text)}
489
+ >
490
+ <UITypographyText ellipsis style={{ fontSize: 12 }}>{q.text}</UITypographyText>
491
+ </UIButton>
492
+ ))}
493
+ </div>
494
+ </div>
495
+ </UICard>
496
+
497
+ {/* Custom styles for markdown in chat context */}
498
+ <style>{`
499
+ .ai-insights-markdown {
500
+ padding: 0;
501
+ background: transparent;
502
+ border-radius: 0;
503
+ }
504
+ .ai-insights-markdown p {
505
+ margin: 0 0 8px 0;
506
+ }
507
+ .ai-insights-markdown p:last-child {
508
+ margin-bottom: 0;
509
+ }
510
+ .ai-insights-markdown h1,
511
+ .ai-insights-markdown h2,
512
+ .ai-insights-markdown h3 {
513
+ margin-top: 12px;
514
+ margin-bottom: 8px;
515
+ font-size: 1em;
516
+ font-weight: 600;
517
+ border: none;
518
+ padding: 0;
519
+ }
520
+ .ai-insights-markdown h1 {
521
+ font-size: 1.1em;
522
+ }
523
+ .ai-insights-markdown ul,
524
+ .ai-insights-markdown ol {
525
+ margin: 8px 0;
526
+ padding-left: 20px;
527
+ }
528
+ .ai-insights-markdown li {
529
+ margin: 2px 0;
530
+ }
531
+ .ai-insights-markdown code {
532
+ background: rgba(0, 0, 0, 0.06);
533
+ padding: 1px 4px;
534
+ border-radius: 3px;
535
+ font-size: 0.85em;
536
+ }
537
+ .ai-insights-markdown pre {
538
+ background: rgba(0, 0, 0, 0.04);
539
+ padding: 8px 12px;
540
+ border-radius: 6px;
541
+ margin: 8px 0;
542
+ }
543
+ .ai-insights-markdown pre code {
544
+ background: none;
545
+ padding: 0;
546
+ }
547
+ .ai-insights-markdown table {
548
+ margin: 8px 0;
549
+ font-size: 0.9em;
550
+ }
551
+ .ai-insights-markdown th,
552
+ .ai-insights-markdown td {
553
+ padding: 4px 8px;
554
+ }
555
+ .ai-insights-markdown blockquote {
556
+ margin: 8px 0;
557
+ padding-left: 12px;
558
+ border-left-width: 3px;
559
+ }
560
+ `}</style>
561
+ </div>
562
+ );
563
+ }