@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,229 @@
1
+ import * as React from 'react';
2
+ import { Dropdown, Badge, Divider } from 'antd';
3
+ import { Activity, CheckCircle, AlertTriangle, XCircle, Database, Package, Clock } from 'lucide-react';
4
+ import { UIButton } from '@kb-labs/studio-ui-kit';
5
+
6
+ export interface SystemHealthData {
7
+ status: 'healthy' | 'degraded' | 'down' | 'unknown';
8
+ ready: boolean;
9
+ reason?: string;
10
+ pluginsMounted?: number;
11
+ pluginsFailed?: number;
12
+ registryRev?: number | null;
13
+ registryGeneratedAt?: string | null;
14
+ registryStale?: boolean;
15
+ registryPartial?: boolean;
16
+ redisEnabled?: boolean;
17
+ redisHealthy?: boolean;
18
+ }
19
+
20
+ export interface KBSystemHealthIndicatorProps {
21
+ health?: SystemHealthData;
22
+ loading?: boolean;
23
+ onClick?: () => void;
24
+ }
25
+
26
+ const STATUS_CONFIG = {
27
+ healthy: {
28
+ color: '#52c41a',
29
+ icon: CheckCircle,
30
+ label: 'Healthy',
31
+ badgeStatus: 'success' as const,
32
+ },
33
+ degraded: {
34
+ color: '#faad14',
35
+ icon: AlertTriangle,
36
+ label: 'Degraded',
37
+ badgeStatus: 'warning' as const,
38
+ },
39
+ down: {
40
+ color: '#ff4d4f',
41
+ icon: XCircle,
42
+ label: 'Down',
43
+ badgeStatus: 'error' as const,
44
+ },
45
+ unknown: {
46
+ color: '#8c8c8c',
47
+ icon: Activity,
48
+ label: 'Unknown',
49
+ badgeStatus: 'default' as const,
50
+ },
51
+ } as const;
52
+
53
+ function formatTimeAgo(isoString: string | null | undefined): string {
54
+ if (!isoString) {return 'unknown';}
55
+
56
+ const now = Date.now();
57
+ const timestamp = new Date(isoString).getTime();
58
+ const diffMs = now - timestamp;
59
+
60
+ if (diffMs < 60_000) {return 'just now';}
61
+ if (diffMs < 3600_000) {return `${Math.floor(diffMs / 60_000)}min ago`;}
62
+ if (diffMs < 86400_000) {return `${Math.floor(diffMs / 3600_000)}h ago`;}
63
+ return `${Math.floor(diffMs / 86400_000)}d ago`;
64
+ }
65
+
66
+ export function KBSystemHealthIndicator({
67
+ health,
68
+ loading = false,
69
+ onClick,
70
+ }: KBSystemHealthIndicatorProps) {
71
+ const status = health?.status ?? 'unknown';
72
+ const config = STATUS_CONFIG[status];
73
+ const StatusIcon = config.icon;
74
+
75
+ // Build dropdown content
76
+ const dropdownContent = React.useMemo(() => {
77
+ if (!health) {
78
+ return (
79
+ <div style={{
80
+ padding: '12px 16px',
81
+ minWidth: 280,
82
+ backgroundColor: 'var(--bg-primary)',
83
+ borderRadius: 8,
84
+ boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
85
+ }}>
86
+ <div style={{ fontSize: 13, color: 'var(--text-secondary)' }}>
87
+ No health data available
88
+ </div>
89
+ </div>
90
+ );
91
+ }
92
+
93
+ return (
94
+ <div style={{
95
+ padding: '12px 16px',
96
+ minWidth: 280,
97
+ backgroundColor: 'var(--bg-primary)',
98
+ borderRadius: 8,
99
+ boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
100
+ }}>
101
+ {/* Status */}
102
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
103
+ <StatusIcon size={18} color={config.color} />
104
+ <strong style={{ fontSize: 14 }}>System Status: {config.label}</strong>
105
+ </div>
106
+ {!health.ready && health.reason && (
107
+ <div style={{ fontSize: 12, color: 'var(--text-secondary)', marginBottom: 12, paddingLeft: 26 }}>
108
+ Reason: {health.reason}
109
+ </div>
110
+ )}
111
+
112
+ <Divider style={{ margin: '12px 0' }} />
113
+
114
+ {/* Plugins */}
115
+ {(health.pluginsMounted !== undefined || health.pluginsFailed !== undefined) && (
116
+ <>
117
+ <div style={{ display: 'flex', alignItems: 'flex-start', gap: 8, marginBottom: 12 }}>
118
+ <Package size={16} style={{ marginTop: 2, flexShrink: 0 }} />
119
+ <div style={{ flex: 1 }}>
120
+ <div style={{ fontSize: 13 }}>Plugins: {health.pluginsMounted ?? 0} mounted</div>
121
+ {(health.pluginsFailed ?? 0) > 0 && (
122
+ <div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 2 }}>
123
+ {health.pluginsFailed} failed
124
+ </div>
125
+ )}
126
+ </div>
127
+ </div>
128
+ </>
129
+ )}
130
+
131
+ {/* Registry */}
132
+ {(health.registryRev !== undefined || health.registryGeneratedAt) && (
133
+ <>
134
+ <div style={{ display: 'flex', alignItems: 'flex-start', gap: 8, marginBottom: 12 }}>
135
+ <Database size={16} style={{ marginTop: 2, flexShrink: 0 }} />
136
+ <div style={{ flex: 1 }}>
137
+ <div style={{ fontSize: 13 }}>
138
+ Registry: rev #{health.registryRev ?? 'unknown'}
139
+ {(() => {
140
+ const badges: string[] = [];
141
+ if (health.registryStale) {badges.push('stale');}
142
+ if (health.registryPartial) {badges.push('partial');}
143
+ return badges.length > 0 ? ` (${badges.join(', ')})` : '';
144
+ })()}
145
+ </div>
146
+ <div style={{ fontSize: 12, color: 'var(--text-secondary)', marginTop: 2 }}>
147
+ Updated {formatTimeAgo(health.registryGeneratedAt)}
148
+ </div>
149
+ </div>
150
+ </div>
151
+ </>
152
+ )}
153
+
154
+ {/* Redis */}
155
+ {health.redisEnabled !== undefined && (
156
+ <>
157
+ <div style={{ display: 'flex', alignItems: 'flex-start', gap: 8 }}>
158
+ <Database size={16} style={{ marginTop: 2, flexShrink: 0 }} />
159
+ <div style={{ flex: 1 }}>
160
+ <div style={{ fontSize: 13 }}>
161
+ Redis: {health.redisEnabled ? 'enabled' : 'disabled'}
162
+ </div>
163
+ {health.redisEnabled && (
164
+ <div style={{ fontSize: 12, color: health.redisHealthy ? '#52c41a' : '#ff4d4f', marginTop: 2 }}>
165
+ {health.redisHealthy ? 'healthy ✓' : 'unhealthy ✗'}
166
+ </div>
167
+ )}
168
+ </div>
169
+ </div>
170
+ </>
171
+ )}
172
+ </div>
173
+ );
174
+ }, [health, config, StatusIcon]);
175
+
176
+ if (loading) {
177
+ return (
178
+ <UIButton
179
+ variant="text"
180
+ size="small"
181
+ icon={<Clock size={16} />}
182
+ style={{ display: 'flex', alignItems: 'center', gap: 6 }}
183
+ >
184
+ Loading...
185
+ </UIButton>
186
+ );
187
+ }
188
+
189
+ // Show badge only if there are issues
190
+ const showBadge =
191
+ status === 'degraded' ||
192
+ status === 'down' ||
193
+ (health?.pluginsFailed ?? 0) > 0 ||
194
+ health?.registryStale ||
195
+ health?.registryPartial;
196
+
197
+ const buttonElement = (
198
+ <UIButton
199
+ variant="text"
200
+ onClick={onClick}
201
+ icon={<StatusIcon size={18} color={config.color} strokeWidth={2} />}
202
+ style={{ display: 'flex', alignItems: 'center', gap: 4 }}
203
+ >
204
+ System
205
+ </UIButton>
206
+ );
207
+
208
+ return (
209
+ <Dropdown
210
+ dropdownRender={() => dropdownContent}
211
+ placement="bottomRight"
212
+ trigger={['click']}
213
+ >
214
+ <span style={{ display: 'inline-flex', alignItems: 'center' }}>
215
+ {showBadge ? (
216
+ <Badge
217
+ dot
218
+ status={config.badgeStatus}
219
+ offset={[-2, 2]}
220
+ >
221
+ {buttonElement}
222
+ </Badge>
223
+ ) : (
224
+ buttonElement
225
+ )}
226
+ </span>
227
+ </Dropdown>
228
+ );
229
+ }
@@ -0,0 +1,42 @@
1
+ import * as React from 'react';
2
+ import { Dropdown } from 'antd';
3
+ import type { MenuProps } from 'antd';
4
+ import { Sun, Moon, Monitor } from 'lucide-react';
5
+ import { UIButton } from '@kb-labs/studio-ui-kit';
6
+ import { useKBTheme, type ThemeMode } from './kb-config-provider';
7
+
8
+ const themeOptions: { key: ThemeMode; label: string; icon: React.ReactNode }[] = [
9
+ { key: 'light', label: 'Light', icon: <Sun size={16} /> },
10
+ { key: 'dark', label: 'Dark', icon: <Moon size={16} /> },
11
+ { key: 'auto', label: 'Auto', icon: <Monitor size={16} /> },
12
+ ];
13
+
14
+ export function KBThemeToggle() {
15
+ const { theme, setTheme } = useKBTheme();
16
+
17
+ const currentTheme = themeOptions.find((opt) => opt.key === theme) ?? themeOptions[0]!;
18
+
19
+ const items: MenuProps['items'] = themeOptions.map((option) => ({
20
+ key: option.key,
21
+ label: (
22
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
23
+ {option.icon}
24
+ <span>{option.label}</span>
25
+ </div>
26
+ ),
27
+ onClick: () => setTheme(option.key),
28
+ }));
29
+
30
+ return (
31
+ <Dropdown menu={{ items, selectedKeys: [theme] }} placement="bottomRight">
32
+ <UIButton
33
+ variant="text"
34
+ icon={React.cloneElement(currentTheme.icon as React.ReactElement, { size: 16 })}
35
+ style={{ height: 32, display: 'flex', alignItems: 'center', gap: 4, fontSize: 13 }}
36
+ >
37
+ {currentTheme.label}
38
+ </UIButton>
39
+ </Dropdown>
40
+ );
41
+ }
42
+