@kb-labs/studio-app 0.6.0 → 2.8.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.
- package/dist/index.html +1 -0
- package/dist/main.1d68e922.js +522 -0
- package/dist/main.1d68e922.js.map +1 -0
- package/dist/main.80b70197.css +2 -0
- package/dist/main.80b70197.css.map +1 -0
- package/dist/mf-manifest.json +241 -0
- package/dist/mf-stats.json +271 -0
- package/package.json +56 -39
- package/server.js +68 -0
- package/.env.example +0 -10
- package/UI-KIT-COMPONENTS.md +0 -783
- package/UI-KIT-PLAN.md +0 -326
- package/eslint-rules/no-direct-antd-import.js +0 -151
- package/eslint.config.ts +0 -27
- package/index.html +0 -17
- package/postcss.config.js +0 -7
- package/public/favicon.ico +0 -0
- package/rspack.config.mjs +0 -118
- package/src/App.tsx +0 -46
- package/src/components/devtools/devtools-log-feed.module.css +0 -165
- package/src/components/devtools/devtools-log-feed.tsx +0 -300
- package/src/components/devtools/devtools-panel.module.css +0 -121
- package/src/components/devtools/devtools-panel.tsx +0 -115
- package/src/components/empty-state.tsx +0 -28
- package/src/components/error-boundary.tsx +0 -210
- package/src/components/health-banner.module.css +0 -103
- package/src/components/health-banner.tsx +0 -134
- package/src/components/health-indicator.tsx +0 -12
- package/src/components/page-header.tsx +0 -49
- package/src/components/page-transition.tsx +0 -24
- package/src/components/role-switcher.tsx +0 -148
- package/src/components/run-badge.tsx +0 -13
- package/src/components/ui/icons.tsx +0 -509
- package/src/components/ui/index.ts +0 -25
- package/src/components/ui/kb-config-provider.tsx +0 -130
- package/src/components/ui/kb-header.module.css +0 -51
- package/src/components/ui/kb-header.tsx +0 -112
- package/src/components/ui/kb-notification-bell.module.css +0 -103
- package/src/components/ui/kb-notification-bell.tsx +0 -199
- package/src/components/ui/kb-page-layout.tsx +0 -66
- package/src/components/ui/kb-quick-search.module.css +0 -86
- package/src/components/ui/kb-quick-search.tsx +0 -196
- package/src/components/ui/kb-sidebar.module.css +0 -47
- package/src/components/ui/kb-sidebar.tsx +0 -196
- package/src/components/ui/kb-stat-card.module.css +0 -21
- package/src/components/ui/kb-stat-card.tsx +0 -22
- package/src/components/ui/kb-stat-grid.tsx +0 -22
- package/src/components/ui/kb-status-bar.module.css +0 -49
- package/src/components/ui/kb-status-bar.tsx +0 -145
- package/src/components/ui/kb-system-health-indicator.tsx +0 -229
- package/src/components/ui/kb-theme-toggle.tsx +0 -42
- package/src/components/ui/theme-adapter.ts +0 -812
- package/src/components/ui/theme-transition-overlay.tsx +0 -70
- package/src/components/ui/utils.ts +0 -6
- package/src/components/widget-error-boundary.tsx +0 -172
- package/src/config/env.ts +0 -5
- package/src/config/feature-flags.ts +0 -482
- package/src/config/studio.config.schema.ts +0 -88
- package/src/config/studio.config.ts +0 -84
- package/src/env.d.ts +0 -23
- package/src/hooks/use-command-palette.ts +0 -6
- package/src/hooks/use-devtools-store.ts +0 -25
- package/src/hooks/use-feature-flags.ts +0 -101
- package/src/index.css +0 -10
- package/src/layouts/dashboard-grid.tsx +0 -122
- package/src/layouts/index.ts +0 -12
- package/src/layouts/two-pane.tsx +0 -134
- package/src/main.tsx +0 -15
- package/src/modules/analytics/components/analytics-daily-stats-chart.tsx +0 -257
- package/src/modules/analytics/components/analytics-date-range-picker.tsx +0 -121
- package/src/modules/analytics/pages/analytics-cache-page.tsx +0 -285
- package/src/modules/analytics/pages/analytics-embeddings-page.tsx +0 -274
- package/src/modules/analytics/pages/analytics-events-page.tsx +0 -425
- package/src/modules/analytics/pages/analytics-llm-page.tsx +0 -434
- package/src/modules/analytics/pages/analytics-overview-page.tsx +0 -336
- package/src/modules/analytics/pages/analytics-page.tsx +0 -20
- package/src/modules/analytics/pages/analytics-storage-page.tsx +0 -344
- package/src/modules/analytics/pages/analytics-vectorstore-page.tsx +0 -279
- package/src/modules/analytics/routes/index.tsx +0 -133
- package/src/modules/analytics/utils/formatters.test.ts +0 -148
- package/src/modules/analytics/utils/formatters.ts +0 -100
- package/src/modules/auth/pages/login-page.module.css +0 -113
- package/src/modules/auth/pages/login-page.tsx +0 -80
- package/src/modules/auth/routes/index.tsx +0 -24
- package/src/modules/dashboard/components/ActivityTimelineWidget.tsx +0 -199
- package/src/modules/dashboard/components/AiInsightsWidget.tsx +0 -358
- package/src/modules/dashboard/components/AnimatedMetricCard.css +0 -88
- package/src/modules/dashboard/components/AnimatedMetricCard.tsx +0 -125
- package/src/modules/dashboard/components/HeroMetricCard.css +0 -114
- package/src/modules/dashboard/components/HeroMetricCard.tsx +0 -152
- package/src/modules/dashboard/components/HeroMetricsWidget.tsx +0 -146
- package/src/modules/dashboard/components/SystemResourcesWidget.tsx +0 -197
- package/src/modules/dashboard/components/dashboard-empty.tsx +0 -22
- package/src/modules/dashboard/components/dashboard-kpis-widget.tsx +0 -59
- package/src/modules/dashboard/components/dashboard-kpis.tsx +0 -13
- package/src/modules/dashboard/components/dashboard-latency-percentiles-widget.tsx +0 -85
- package/src/modules/dashboard/components/dashboard-metrics-widget.tsx +0 -78
- package/src/modules/dashboard/components/dashboard-plugins-widget.tsx +0 -67
- package/src/modules/dashboard/components/dashboard-requests-widget.tsx +0 -62
- package/src/modules/dashboard/layouts/dashboard-layout.tsx +0 -16
- package/src/modules/dashboard/pages/ai-insights-page.tsx +0 -563
- package/src/modules/dashboard/pages/dashboard-animations.css +0 -317
- package/src/modules/dashboard/pages/dashboard-page.tsx +0 -13
- package/src/modules/dashboard/routes/index.tsx +0 -70
- package/src/modules/observability/pages/devkit-page.tsx +0 -263
- package/src/modules/observability/pages/incident-detail-page.tsx +0 -571
- package/src/modules/observability/pages/incidents-page.tsx +0 -370
- package/src/modules/observability/pages/log-detail-page.tsx +0 -508
- package/src/modules/observability/pages/logs-page.tsx +0 -1129
- package/src/modules/observability/pages/prometheus-metrics-page.tsx +0 -486
- package/src/modules/observability/pages/state-broker-page.tsx +0 -229
- package/src/modules/observability/pages/system-events-page.tsx +0 -255
- package/src/modules/observability/routes/index.tsx +0 -133
- package/src/modules/plugins/components/marketplace-card.tsx +0 -153
- package/src/modules/plugins/components/plugin-ai-assistant-modal.tsx +0 -374
- package/src/modules/plugins/components/plugin-card.tsx +0 -136
- package/src/modules/plugins/components/plugin-detail/capabilities-summary.tsx +0 -101
- package/src/modules/plugins/components/plugin-detail/cli-commands-table.tsx +0 -101
- package/src/modules/plugins/components/plugin-detail/index.ts +0 -7
- package/src/modules/plugins/components/plugin-detail/jobs-table.tsx +0 -64
- package/src/modules/plugins/components/plugin-detail/permissions/env-permissions.tsx +0 -30
- package/src/modules/plugins/components/plugin-detail/permissions/filesystem-permissions.tsx +0 -68
- package/src/modules/plugins/components/plugin-detail/permissions/index.ts +0 -6
- package/src/modules/plugins/components/plugin-detail/permissions/network-permissions.tsx +0 -43
- package/src/modules/plugins/components/plugin-detail/permissions/platform-services.tsx +0 -242
- package/src/modules/plugins/components/plugin-detail/permissions/resource-quotas.tsx +0 -96
- package/src/modules/plugins/components/plugin-detail/permissions/shell-permissions.tsx +0 -55
- package/src/modules/plugins/components/plugin-detail/permissions-view.tsx +0 -47
- package/src/modules/plugins/components/plugin-detail/plugin-header.tsx +0 -169
- package/src/modules/plugins/components/plugin-detail/rest-routes-table.tsx +0 -159
- package/src/modules/plugins/components/plugin-detail/workflows-table.tsx +0 -40
- package/src/modules/plugins/components/plugin-grid.tsx +0 -20
- package/src/modules/plugins/components/plugin-table.tsx +0 -178
- package/src/modules/plugins/pages/marketplace-page.tsx +0 -200
- package/src/modules/plugins/pages/plugin-detail-page.tsx +0 -1145
- package/src/modules/plugins/routes/index.tsx +0 -48
- package/src/modules/plugins/utils/index.ts +0 -1
- package/src/modules/plugins/utils/schema-helpers.ts +0 -21
- package/src/modules/settings/components/api-routes-viewer.tsx +0 -415
- package/src/modules/settings/components/appearance-settings.tsx +0 -200
- package/src/modules/settings/components/configuration-settings.tsx +0 -321
- package/src/modules/settings/components/data-privacy-settings.tsx +0 -111
- package/src/modules/settings/components/experimental-settings.tsx +0 -243
- package/src/modules/settings/components/navigation-settings.tsx +0 -198
- package/src/modules/settings/components/notifications-settings.tsx +0 -40
- package/src/modules/settings/pages/settings-page.tsx +0 -298
- package/src/modules/settings/routes/index.tsx +0 -47
- package/src/pages/README-assistant.md +0 -91
- package/src/pages/login-page.tsx +0 -177
- package/src/pages/not-found-page.tsx +0 -85
- package/src/providers/auth-provider.tsx +0 -40
- package/src/providers/command-palette-provider.tsx +0 -193
- package/src/providers/data-sources-provider.tsx +0 -27
- package/src/providers/devtools-provider.tsx +0 -72
- package/src/providers/navigation-items-provider.tsx +0 -38
- package/src/providers/registry-v2-provider.tsx +0 -93
- package/src/providers/settings-provider.tsx +0 -158
- package/src/router.tsx +0 -302
- package/src/routes/helpers.tsx +0 -25
- package/src/routes/plugin-page-v2.tsx +0 -52
- package/src/routes/types.ts +0 -18
- package/src/styles/antd-overrides.css +0 -211
- package/src/styles/base.css +0 -190
- package/src/styles/settings.css +0 -75
- package/src/styles/variables.css +0 -94
- package/src/utils/analytics.ts +0 -183
- package/src/utils/apply-navigation-categories.ts +0 -83
- package/src/utils/commands.ts +0 -252
- package/src/utils/export.ts +0 -174
- package/src/utils/format-date.ts +0 -166
- package/src/utils/logger.ts +0 -110
- package/tailwind.config.ts +0 -13
- package/tsconfig.json +0 -13
- package/vitest-setup.ts +0 -1
- /package/{public → dist}/favicon.svg +0 -0
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import { UIEmptyState as UIEmptyStateBase, UIButton } from '@kb-labs/studio-ui-kit';
|
|
3
|
-
|
|
4
|
-
export interface EmptyStateProps {
|
|
5
|
-
icon?: React.ReactNode;
|
|
6
|
-
title: string;
|
|
7
|
-
description: string;
|
|
8
|
-
action?: { label: string; onClick: () => void };
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function EmptyState({ icon, title, description, action }: EmptyStateProps) {
|
|
12
|
-
// Convert icon component to React element if it's a component
|
|
13
|
-
const iconElement = React.isValidElement(icon)
|
|
14
|
-
? icon
|
|
15
|
-
: icon && typeof icon === 'function'
|
|
16
|
-
? React.createElement(icon as React.ComponentType)
|
|
17
|
-
: icon;
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<UIEmptyStateBase
|
|
21
|
-
icon={iconElement}
|
|
22
|
-
title={title}
|
|
23
|
-
description={description}
|
|
24
|
-
action={action ? <UIButton onClick={action.onClick}>{action.label}</UIButton> : undefined}
|
|
25
|
-
/>
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module @kb-labs/studio-app/components/ErrorBoundary
|
|
3
|
-
* Custom error boundary for React Router with friendly UI
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import * as React from 'react';
|
|
7
|
-
import { useRouteError, isRouteErrorResponse, Link } from 'react-router-dom';
|
|
8
|
-
import {
|
|
9
|
-
UIResult, UIButton, UICard, UITypographyText, UITypographyParagraph,
|
|
10
|
-
UISpace, UIAccordion, UITag, UIMessage, UIIcon,
|
|
11
|
-
} from '@kb-labs/studio-ui-kit';
|
|
12
|
-
|
|
13
|
-
export function ErrorBoundary() {
|
|
14
|
-
const error = useRouteError();
|
|
15
|
-
const [copied, setCopied] = React.useState(false);
|
|
16
|
-
|
|
17
|
-
let title = 'Упс! Что-то пошло не так';
|
|
18
|
-
let description = 'Произошла непредвиденная ошибка';
|
|
19
|
-
let status: 'error' | 'warning' | '404' | '500' | 'info' = 'error';
|
|
20
|
-
let suggestion = 'Попробуйте перезагрузить страницу или вернуться на главную';
|
|
21
|
-
|
|
22
|
-
if (isRouteErrorResponse(error)) {
|
|
23
|
-
// React Router error response
|
|
24
|
-
if (error.status === 404) {
|
|
25
|
-
status = '404';
|
|
26
|
-
title = 'Страница не найдена';
|
|
27
|
-
description = 'К сожалению, запрошенная страница не существует';
|
|
28
|
-
suggestion = 'Проверьте адрес или вернитесь на главную страницу';
|
|
29
|
-
} else {
|
|
30
|
-
status = '500';
|
|
31
|
-
title = `Ошибка ${error.status}`;
|
|
32
|
-
description = error.statusText || error.data?.message || description;
|
|
33
|
-
}
|
|
34
|
-
} else if (error instanceof Error) {
|
|
35
|
-
// JavaScript error
|
|
36
|
-
description = error.message;
|
|
37
|
-
|
|
38
|
-
// Friendly message for common errors
|
|
39
|
-
if (error.message.includes('Unsupported data source')) {
|
|
40
|
-
title = 'Ошибка конфигурации виджета';
|
|
41
|
-
description = 'Виджет имеет некорректную настройку источника данных';
|
|
42
|
-
suggestion = 'Это ошибка в манифесте плагина. Обратитесь к разработчику.';
|
|
43
|
-
status = 'warning';
|
|
44
|
-
} else if (error.message.includes('not found') || error.message.includes('404')) {
|
|
45
|
-
title = 'Ресурс не найден';
|
|
46
|
-
description = 'Запрошенный ресурс не существует';
|
|
47
|
-
suggestion = 'Проверьте настройки плагина или обратитесь к администратору';
|
|
48
|
-
status = '404';
|
|
49
|
-
} else if (error.message.includes('Failed to fetch')) {
|
|
50
|
-
title = 'Ошибка сети';
|
|
51
|
-
description = 'Не удалось подключиться к серверу';
|
|
52
|
-
suggestion = 'Проверьте подключение к интернету и попробуйте снова';
|
|
53
|
-
status = 'warning';
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Extract error details
|
|
58
|
-
const errorDetails = error instanceof Error ? {
|
|
59
|
-
name: error.name,
|
|
60
|
-
message: error.message,
|
|
61
|
-
stack: error.stack,
|
|
62
|
-
} : null;
|
|
63
|
-
|
|
64
|
-
return (
|
|
65
|
-
<div style={{
|
|
66
|
-
display: 'flex',
|
|
67
|
-
alignItems: 'center',
|
|
68
|
-
justifyContent: 'center',
|
|
69
|
-
minHeight: '100vh',
|
|
70
|
-
padding: '2rem',
|
|
71
|
-
backgroundColor: 'var(--bg-tertiary)',
|
|
72
|
-
}}>
|
|
73
|
-
<UICard
|
|
74
|
-
style={{
|
|
75
|
-
maxWidth: '800px',
|
|
76
|
-
width: '100%',
|
|
77
|
-
borderRadius: '16px',
|
|
78
|
-
boxShadow: '0 20px 60px rgba(0,0,0,0.3)',
|
|
79
|
-
backgroundColor: 'var(--bg-secondary)',
|
|
80
|
-
}}
|
|
81
|
-
styles={{ body: { padding: '3rem' } }}
|
|
82
|
-
>
|
|
83
|
-
<UIResult
|
|
84
|
-
status={status}
|
|
85
|
-
title={<span style={{ fontSize: '1.5rem', fontWeight: 600 }}>{title}</span>}
|
|
86
|
-
subTitle={
|
|
87
|
-
<UISpace direction="vertical" size="middle" style={{ width: '100%', marginTop: '1rem' }}>
|
|
88
|
-
<UITypographyParagraph style={{ fontSize: '1rem', color: 'var(--text-secondary)', margin: 0 }}>
|
|
89
|
-
{description}
|
|
90
|
-
</UITypographyParagraph>
|
|
91
|
-
<UITypographyText type="secondary" style={{ fontSize: '0.875rem' }}>
|
|
92
|
-
💡 {suggestion}
|
|
93
|
-
</UITypographyText>
|
|
94
|
-
</UISpace>
|
|
95
|
-
}
|
|
96
|
-
extra={
|
|
97
|
-
<UISpace direction="vertical" size="large" style={{ width: '100%', marginTop: '1.5rem' }}>
|
|
98
|
-
<UISpace size="middle">
|
|
99
|
-
<Link to="/">
|
|
100
|
-
<UIButton variant="primary" size="large" icon={<UIIcon name="HomeOutlined" />}>
|
|
101
|
-
На главную
|
|
102
|
-
</UIButton>
|
|
103
|
-
</Link>
|
|
104
|
-
<UIButton
|
|
105
|
-
size="large"
|
|
106
|
-
icon={<UIIcon name="ReloadOutlined" />}
|
|
107
|
-
onClick={() => window.location.reload()}
|
|
108
|
-
>
|
|
109
|
-
Перезагрузить
|
|
110
|
-
</UIButton>
|
|
111
|
-
</UISpace>
|
|
112
|
-
|
|
113
|
-
{/* Error Details Accordion */}
|
|
114
|
-
{errorDetails && (
|
|
115
|
-
<UIAccordion
|
|
116
|
-
ghost
|
|
117
|
-
style={{ width: '100%', textAlign: 'left' }}
|
|
118
|
-
items={[
|
|
119
|
-
{
|
|
120
|
-
key: 'details',
|
|
121
|
-
label: 'Детали ошибки (для разработчиков)',
|
|
122
|
-
children: (
|
|
123
|
-
<UISpace direction="vertical" size="middle" style={{ width: '100%' }}>
|
|
124
|
-
{/* Error Type */}
|
|
125
|
-
<div>
|
|
126
|
-
<UITypographyText type="secondary" style={{ fontSize: '0.75rem' }}>Тип ошибки:</UITypographyText>
|
|
127
|
-
<div style={{ marginTop: 4 }}>
|
|
128
|
-
<UITag variant="error" icon={<UIIcon name="CodeOutlined" />}>
|
|
129
|
-
{errorDetails.name}
|
|
130
|
-
</UITag>
|
|
131
|
-
</div>
|
|
132
|
-
</div>
|
|
133
|
-
|
|
134
|
-
{/* Error Message */}
|
|
135
|
-
<div>
|
|
136
|
-
<UITypographyText type="secondary" style={{ fontSize: '0.75rem' }}>Сообщение:</UITypographyText>
|
|
137
|
-
<div
|
|
138
|
-
style={{
|
|
139
|
-
marginTop: 4,
|
|
140
|
-
padding: '12px',
|
|
141
|
-
background: 'var(--bg-secondary)',
|
|
142
|
-
borderRadius: '8px',
|
|
143
|
-
fontFamily: 'monospace',
|
|
144
|
-
fontSize: '0.875rem',
|
|
145
|
-
wordBreak: 'break-word',
|
|
146
|
-
}}
|
|
147
|
-
>
|
|
148
|
-
<UITypographyText code>{errorDetails.message}</UITypographyText>
|
|
149
|
-
</div>
|
|
150
|
-
</div>
|
|
151
|
-
|
|
152
|
-
{/* Stack Trace */}
|
|
153
|
-
{errorDetails.stack && (
|
|
154
|
-
<div>
|
|
155
|
-
<UITypographyText type="secondary" style={{ fontSize: '0.75rem' }}>Stack Trace:</UITypographyText>
|
|
156
|
-
<div
|
|
157
|
-
style={{
|
|
158
|
-
marginTop: 4,
|
|
159
|
-
padding: '12px',
|
|
160
|
-
background: '#1e1e1e',
|
|
161
|
-
borderRadius: '8px',
|
|
162
|
-
maxHeight: '300px',
|
|
163
|
-
overflow: 'auto',
|
|
164
|
-
}}
|
|
165
|
-
>
|
|
166
|
-
<pre
|
|
167
|
-
style={{
|
|
168
|
-
margin: 0,
|
|
169
|
-
fontFamily: 'Monaco, Menlo, "Courier New", monospace',
|
|
170
|
-
fontSize: '0.75rem',
|
|
171
|
-
lineHeight: 1.5,
|
|
172
|
-
color: '#d4d4d4',
|
|
173
|
-
whiteSpace: 'pre-wrap',
|
|
174
|
-
wordBreak: 'break-word',
|
|
175
|
-
}}
|
|
176
|
-
>
|
|
177
|
-
{errorDetails.stack}
|
|
178
|
-
</pre>
|
|
179
|
-
</div>
|
|
180
|
-
</div>
|
|
181
|
-
)}
|
|
182
|
-
|
|
183
|
-
{/* Copy to Clipboard */}
|
|
184
|
-
<UIButton
|
|
185
|
-
size="small"
|
|
186
|
-
variant={copied ? 'primary' : 'default'}
|
|
187
|
-
icon={copied ? <UIIcon name="CheckOutlined" /> : <UIIcon name="CopyOutlined" />}
|
|
188
|
-
onClick={() => {
|
|
189
|
-
const text = `${errorDetails.name}: ${errorDetails.message}\n\n${errorDetails.stack || 'No stack trace'}`;
|
|
190
|
-
navigator.clipboard.writeText(text);
|
|
191
|
-
setCopied(true);
|
|
192
|
-
UIMessage.success('Ошибка скопирована в буфер обмена');
|
|
193
|
-
setTimeout(() => setCopied(false), 2000);
|
|
194
|
-
}}
|
|
195
|
-
>
|
|
196
|
-
{copied ? 'Скопировано!' : 'Скопировать в буфер обмена'}
|
|
197
|
-
</UIButton>
|
|
198
|
-
</UISpace>
|
|
199
|
-
),
|
|
200
|
-
},
|
|
201
|
-
]}
|
|
202
|
-
/>
|
|
203
|
-
)}
|
|
204
|
-
</UISpace>
|
|
205
|
-
}
|
|
206
|
-
/>
|
|
207
|
-
</UICard>
|
|
208
|
-
</div>
|
|
209
|
-
);
|
|
210
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/* ── Health banner — compact top strip ───────────────────────────────── */
|
|
2
|
-
|
|
3
|
-
.strip {
|
|
4
|
-
display: flex;
|
|
5
|
-
align-items: center;
|
|
6
|
-
gap: 8px;
|
|
7
|
-
padding: 6px 16px;
|
|
8
|
-
font-size: 12px;
|
|
9
|
-
line-height: 1.4;
|
|
10
|
-
border-bottom: 1px solid transparent;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
.stripError {
|
|
14
|
-
background: rgba(220, 38, 38, 0.07);
|
|
15
|
-
border-color: rgba(220, 38, 38, 0.2);
|
|
16
|
-
color: var(--error);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
.stripWarning {
|
|
20
|
-
background: rgba(245, 158, 11, 0.07);
|
|
21
|
-
border-color: rgba(245, 158, 11, 0.2);
|
|
22
|
-
color: var(--warning);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.stripInfo {
|
|
26
|
-
background: rgba(14, 165, 233, 0.07);
|
|
27
|
-
border-color: rgba(14, 165, 233, 0.2);
|
|
28
|
-
color: var(--info);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
.icon {
|
|
32
|
-
flex-shrink: 0;
|
|
33
|
-
display: flex;
|
|
34
|
-
align-items: center;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
.message {
|
|
38
|
-
font-weight: 600;
|
|
39
|
-
flex-shrink: 0;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
.description {
|
|
43
|
-
color: var(--text-secondary);
|
|
44
|
-
flex: 1;
|
|
45
|
-
overflow: hidden;
|
|
46
|
-
text-overflow: ellipsis;
|
|
47
|
-
white-space: nowrap;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.detailsBtn {
|
|
51
|
-
background: none;
|
|
52
|
-
border: none;
|
|
53
|
-
cursor: pointer;
|
|
54
|
-
font-size: 12px;
|
|
55
|
-
color: inherit;
|
|
56
|
-
opacity: 0.75;
|
|
57
|
-
padding: 0 4px;
|
|
58
|
-
flex-shrink: 0;
|
|
59
|
-
text-decoration: underline;
|
|
60
|
-
text-underline-offset: 2px;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
.detailsBtn:hover {
|
|
64
|
-
opacity: 1;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
.retryBtn {
|
|
68
|
-
background: none;
|
|
69
|
-
border: none;
|
|
70
|
-
cursor: pointer;
|
|
71
|
-
padding: 2px;
|
|
72
|
-
display: flex;
|
|
73
|
-
align-items: center;
|
|
74
|
-
color: inherit;
|
|
75
|
-
opacity: 0.6;
|
|
76
|
-
flex-shrink: 0;
|
|
77
|
-
border-radius: 3px;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
.retryBtn:hover {
|
|
81
|
-
opacity: 1;
|
|
82
|
-
background: rgba(0, 0, 0, 0.06);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/* ── Expanded details panel ──────────────────────────────────────────── */
|
|
86
|
-
|
|
87
|
-
.details {
|
|
88
|
-
padding: 8px 16px 10px 40px;
|
|
89
|
-
font-size: 12px;
|
|
90
|
-
border-bottom: 1px solid var(--border-primary);
|
|
91
|
-
background: var(--bg-tertiary);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
.detailsList {
|
|
95
|
-
margin: 4px 0 0 12px;
|
|
96
|
-
padding: 0;
|
|
97
|
-
list-style: disc;
|
|
98
|
-
color: var(--text-secondary);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
.detailsList li {
|
|
102
|
-
margin-bottom: 2px;
|
|
103
|
-
}
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { useDataSources } from '../providers/data-sources-provider';
|
|
2
|
-
import { useHealthStatus, useReadyStatus } from '@kb-labs/studio-data-client';
|
|
3
|
-
import { studioConfig } from '@/config/studio.config';
|
|
4
|
-
import { RefreshCw, AlertCircle, AlertTriangle, Info } from 'lucide-react';
|
|
5
|
-
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
6
|
-
import { useRegistryV2 } from '../providers/registry-v2-provider';
|
|
7
|
-
import type { HealthStatus } from '@kb-labs/studio-data-client';
|
|
8
|
-
import styles from './health-banner.module.css';
|
|
9
|
-
|
|
10
|
-
export function HealthBanner() {
|
|
11
|
-
const sources = useDataSources();
|
|
12
|
-
const _registry = useRegistryV2();
|
|
13
|
-
const sseHealth = null as { status: string; ts?: string; ready: boolean; reason?: string } | null;
|
|
14
|
-
const { data: healthRaw, error, refetch } = useHealthStatus(sources.system);
|
|
15
|
-
const { data: ready } = useReadyStatus(sources.system);
|
|
16
|
-
const [showDetails, setShowDetails] = useState(false);
|
|
17
|
-
const lastRefetchedTs = useRef<string | null>(null);
|
|
18
|
-
|
|
19
|
-
useEffect(() => {
|
|
20
|
-
if (!sseHealth) {return;}
|
|
21
|
-
const shouldRefetch =
|
|
22
|
-
(!sseHealth.ready || sseHealth.status === 'degraded') &&
|
|
23
|
-
sseHealth.ts &&
|
|
24
|
-
sseHealth.ts !== lastRefetchedTs.current;
|
|
25
|
-
if (shouldRefetch) {
|
|
26
|
-
lastRefetchedTs.current = sseHealth.ts ?? null;
|
|
27
|
-
void refetch();
|
|
28
|
-
}
|
|
29
|
-
}, [sseHealth, refetch]);
|
|
30
|
-
|
|
31
|
-
const healthSnapshot = (healthRaw as Record<string, unknown> | undefined)?.snapshot as
|
|
32
|
-
| HealthStatus['snapshot']
|
|
33
|
-
| undefined;
|
|
34
|
-
|
|
35
|
-
const fallbackStatus =
|
|
36
|
-
healthSnapshot?.status ?? ((healthRaw as { ok?: boolean } | undefined)?.ok ? 'healthy' : 'unknown');
|
|
37
|
-
const healthStatus = sseHealth?.status ?? fallbackStatus;
|
|
38
|
-
const readyStatus = sseHealth?.ready ?? ready?.ready ?? true;
|
|
39
|
-
const readyReason = !readyStatus ? sseHealth?.reason ?? ready?.reason : undefined;
|
|
40
|
-
|
|
41
|
-
const impactedComponents = (healthSnapshot?.checks ?? [])
|
|
42
|
-
.filter((check) => check.status !== 'ok')
|
|
43
|
-
.map((check) => ({ id: check.id, lastError: check.message ?? 'unknown issue' }));
|
|
44
|
-
|
|
45
|
-
const pluginMetrics = useMemo(() => {
|
|
46
|
-
const meta = healthSnapshot?.meta as Record<string, unknown> | undefined;
|
|
47
|
-
const pluginMounts = meta?.pluginMounts as Record<string, unknown> | undefined;
|
|
48
|
-
const succeeded =
|
|
49
|
-
pluginMounts && typeof pluginMounts.succeeded === 'number'
|
|
50
|
-
? (pluginMounts.succeeded as number)
|
|
51
|
-
: undefined;
|
|
52
|
-
const failed =
|
|
53
|
-
pluginMounts && typeof pluginMounts.failed === 'number'
|
|
54
|
-
? (pluginMounts.failed as number)
|
|
55
|
-
: undefined;
|
|
56
|
-
if (succeeded === undefined && failed === undefined) {return null;}
|
|
57
|
-
return { succeeded: succeeded ?? 0, failed: failed ?? 0 };
|
|
58
|
-
}, [healthSnapshot]);
|
|
59
|
-
|
|
60
|
-
const isDegraded =
|
|
61
|
-
(healthStatus === 'degraded' || healthSnapshot?.state === 'partial_observability') && readyStatus;
|
|
62
|
-
const isDown = !!error || !readyStatus;
|
|
63
|
-
const isMockMode =
|
|
64
|
-
studioConfig.dataSourceMode === 'mock' ||
|
|
65
|
-
(studioConfig.dataSourceMode === 'http' && !healthSnapshot && error);
|
|
66
|
-
|
|
67
|
-
if (!isDegraded && !isDown && !isMockMode) {return null;}
|
|
68
|
-
|
|
69
|
-
const variant = isDown ? 'error' : isDegraded ? 'warning' : 'info';
|
|
70
|
-
|
|
71
|
-
const stripCls = {
|
|
72
|
-
error: styles.stripError,
|
|
73
|
-
warning: styles.stripWarning,
|
|
74
|
-
info: styles.stripInfo,
|
|
75
|
-
}[variant];
|
|
76
|
-
|
|
77
|
-
const IconComponent = { error: AlertCircle, warning: AlertTriangle, info: Info }[variant];
|
|
78
|
-
const iconSize = 14;
|
|
79
|
-
|
|
80
|
-
const message = isDown
|
|
81
|
-
? 'REST API unavailable'
|
|
82
|
-
: isDegraded
|
|
83
|
-
? 'REST API degraded'
|
|
84
|
-
: 'Mock mode';
|
|
85
|
-
|
|
86
|
-
const description = isDown
|
|
87
|
-
? `Not ready at ${studioConfig.apiBaseUrl}${readyReason ? ` — ${readyReason}` : ''}`
|
|
88
|
-
: isDegraded
|
|
89
|
-
? `Some services may be unavailable${pluginMetrics?.failed ? ` · ${pluginMetrics.failed} plugin mount(s) failed` : ''}`
|
|
90
|
-
: 'Using mock data for development';
|
|
91
|
-
|
|
92
|
-
const hasDetails = isDegraded && impactedComponents.length > 0;
|
|
93
|
-
|
|
94
|
-
return (
|
|
95
|
-
<>
|
|
96
|
-
<div className={`${styles.strip} ${stripCls}`}>
|
|
97
|
-
<span className={styles.icon}>
|
|
98
|
-
<IconComponent size={iconSize} />
|
|
99
|
-
</span>
|
|
100
|
-
<span className={styles.message}>{message}</span>
|
|
101
|
-
<span className={styles.description}>{description}</span>
|
|
102
|
-
{hasDetails && (
|
|
103
|
-
<button
|
|
104
|
-
type="button"
|
|
105
|
-
className={styles.detailsBtn}
|
|
106
|
-
onClick={() => setShowDetails((v) => !v)}
|
|
107
|
-
>
|
|
108
|
-
{showDetails ? 'Hide details' : 'Details'}
|
|
109
|
-
</button>
|
|
110
|
-
)}
|
|
111
|
-
<button
|
|
112
|
-
type="button"
|
|
113
|
-
className={styles.retryBtn}
|
|
114
|
-
onClick={() => refetch()}
|
|
115
|
-
title="Retry"
|
|
116
|
-
>
|
|
117
|
-
<RefreshCw size={13} />
|
|
118
|
-
</button>
|
|
119
|
-
</div>
|
|
120
|
-
|
|
121
|
-
{showDetails && hasDetails && (
|
|
122
|
-
<div className={styles.details}>
|
|
123
|
-
<ul className={styles.detailsList}>
|
|
124
|
-
{impactedComponents.map((c) => (
|
|
125
|
-
<li key={c.id}>
|
|
126
|
-
<strong>{c.id}</strong>: {c.lastError}
|
|
127
|
-
</li>
|
|
128
|
-
))}
|
|
129
|
-
</ul>
|
|
130
|
-
</div>
|
|
131
|
-
)}
|
|
132
|
-
</>
|
|
133
|
-
);
|
|
134
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { UIBadge } from '@kb-labs/studio-ui-kit';
|
|
2
|
-
|
|
3
|
-
export function HealthIndicator({ status }: { status: 'ok' | 'degraded' | 'down' }) {
|
|
4
|
-
const variantMap = {
|
|
5
|
-
ok: 'success' as const,
|
|
6
|
-
degraded: 'warning' as const,
|
|
7
|
-
down: 'error' as const,
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
return <UIBadge variant={variantMap[status]}>{status.toUpperCase()}</UIBadge>;
|
|
11
|
-
}
|
|
12
|
-
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module @kb-labs/studio-app/components/page-header
|
|
3
|
-
* Page header component with title, description, and optional actions
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import * as React from 'react';
|
|
7
|
-
import { UITitle, UITypographyText } from '@kb-labs/studio-ui-kit';
|
|
8
|
-
import type { ReactNode } from 'react';
|
|
9
|
-
|
|
10
|
-
export interface PageHeaderProps {
|
|
11
|
-
title: string;
|
|
12
|
-
description?: string;
|
|
13
|
-
actions?: ReactNode;
|
|
14
|
-
breadcrumbs?: ReactNode;
|
|
15
|
-
className?: string;
|
|
16
|
-
style?: React.CSSProperties;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function PageHeader({
|
|
20
|
-
title,
|
|
21
|
-
description,
|
|
22
|
-
actions,
|
|
23
|
-
breadcrumbs,
|
|
24
|
-
className,
|
|
25
|
-
style,
|
|
26
|
-
}: PageHeaderProps): React.ReactElement {
|
|
27
|
-
return (
|
|
28
|
-
<div className={className} style={{ marginBottom: '24px', ...style }}>
|
|
29
|
-
{breadcrumbs && <div style={{ marginBottom: '12px' }}>{breadcrumbs}</div>}
|
|
30
|
-
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: '16px', flexWrap: 'wrap' }}>
|
|
31
|
-
<div style={{ flex: 1, minWidth: 0 }}>
|
|
32
|
-
<UITitle level={1} className="typo-page-title" style={{ margin: 0, marginBottom: description ? '8px' : 0, color: 'var(--text-primary)' }}>
|
|
33
|
-
{title}
|
|
34
|
-
</UITitle>
|
|
35
|
-
{description && (
|
|
36
|
-
<UITypographyText className="typo-description" style={{ color: 'var(--text-secondary)', display: 'block' }}>
|
|
37
|
-
{description}
|
|
38
|
-
</UITypographyText>
|
|
39
|
-
)}
|
|
40
|
-
</div>
|
|
41
|
-
{actions && (
|
|
42
|
-
<div style={{ display: 'flex', gap: '8px', alignItems: 'flex-start', flexWrap: 'wrap' }}>
|
|
43
|
-
{actions}
|
|
44
|
-
</div>
|
|
45
|
-
)}
|
|
46
|
-
</div>
|
|
47
|
-
</div>
|
|
48
|
-
);
|
|
49
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module @kb-labs/studio-app/components/page-transition
|
|
3
|
-
* Page transition wrapper with configurable animation types
|
|
4
|
-
*
|
|
5
|
-
* NOTE: Animation logic temporarily disabled.
|
|
6
|
-
* Infrastructure kept for future UI animation features.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import * as React from 'react';
|
|
10
|
-
|
|
11
|
-
export interface PageTransitionProps {
|
|
12
|
-
children: React.ReactNode;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Page transition wrapper component.
|
|
17
|
-
* Currently renders children directly without animations.
|
|
18
|
-
* The feature flag and settings infrastructure remain in place for future animation features.
|
|
19
|
-
*/
|
|
20
|
-
export function PageTransition({ children }: PageTransitionProps) {
|
|
21
|
-
// Animation logic temporarily disabled - instant navigation preferred for SaaS
|
|
22
|
-
// Infrastructure kept for potential future use with other UI animations
|
|
23
|
-
return <>{children}</>;
|
|
24
|
-
}
|