@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.
- package/.env.example +10 -0
- package/README.md +74 -0
- package/UI-KIT-COMPONENTS.md +783 -0
- package/UI-KIT-PLAN.md +326 -0
- package/eslint-rules/no-direct-antd-import.js +151 -0
- package/eslint.config.ts +27 -0
- package/index.html +17 -0
- package/package.json +72 -0
- package/postcss.config.js +7 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.svg +7 -0
- package/rspack.config.mjs +118 -0
- package/src/App.tsx +46 -0
- package/src/components/devtools/devtools-log-feed.module.css +165 -0
- package/src/components/devtools/devtools-log-feed.tsx +300 -0
- package/src/components/devtools/devtools-panel.module.css +121 -0
- package/src/components/devtools/devtools-panel.tsx +115 -0
- package/src/components/empty-state.tsx +28 -0
- package/src/components/error-boundary.tsx +210 -0
- package/src/components/health-banner.module.css +103 -0
- package/src/components/health-banner.tsx +134 -0
- package/src/components/health-indicator.tsx +12 -0
- package/src/components/page-header.tsx +49 -0
- package/src/components/page-transition.tsx +24 -0
- package/src/components/role-switcher.tsx +148 -0
- package/src/components/run-badge.tsx +13 -0
- package/src/components/ui/icons.tsx +509 -0
- package/src/components/ui/index.ts +25 -0
- package/src/components/ui/kb-config-provider.tsx +130 -0
- package/src/components/ui/kb-header.module.css +51 -0
- package/src/components/ui/kb-header.tsx +112 -0
- package/src/components/ui/kb-notification-bell.module.css +103 -0
- package/src/components/ui/kb-notification-bell.tsx +199 -0
- package/src/components/ui/kb-page-layout.tsx +66 -0
- package/src/components/ui/kb-quick-search.module.css +86 -0
- package/src/components/ui/kb-quick-search.tsx +196 -0
- package/src/components/ui/kb-sidebar.module.css +47 -0
- package/src/components/ui/kb-sidebar.tsx +196 -0
- package/src/components/ui/kb-stat-card.module.css +21 -0
- package/src/components/ui/kb-stat-card.tsx +22 -0
- package/src/components/ui/kb-stat-grid.tsx +22 -0
- package/src/components/ui/kb-status-bar.module.css +49 -0
- package/src/components/ui/kb-status-bar.tsx +145 -0
- package/src/components/ui/kb-system-health-indicator.tsx +229 -0
- package/src/components/ui/kb-theme-toggle.tsx +42 -0
- package/src/components/ui/theme-adapter.ts +812 -0
- package/src/components/ui/theme-transition-overlay.tsx +70 -0
- package/src/components/ui/utils.ts +6 -0
- package/src/components/widget-error-boundary.tsx +172 -0
- package/src/config/env.ts +5 -0
- package/src/config/feature-flags.ts +482 -0
- package/src/config/studio.config.schema.ts +88 -0
- package/src/config/studio.config.ts +84 -0
- package/src/env.d.ts +23 -0
- package/src/hooks/use-command-palette.ts +6 -0
- package/src/hooks/use-devtools-store.ts +25 -0
- package/src/hooks/use-feature-flags.ts +101 -0
- package/src/index.css +10 -0
- package/src/layouts/dashboard-grid.tsx +122 -0
- package/src/layouts/index.ts +12 -0
- package/src/layouts/two-pane.tsx +134 -0
- package/src/main.tsx +15 -0
- package/src/modules/analytics/components/analytics-daily-stats-chart.tsx +257 -0
- package/src/modules/analytics/components/analytics-date-range-picker.tsx +121 -0
- package/src/modules/analytics/pages/analytics-cache-page.tsx +285 -0
- package/src/modules/analytics/pages/analytics-embeddings-page.tsx +274 -0
- package/src/modules/analytics/pages/analytics-events-page.tsx +425 -0
- package/src/modules/analytics/pages/analytics-llm-page.tsx +434 -0
- package/src/modules/analytics/pages/analytics-overview-page.tsx +336 -0
- package/src/modules/analytics/pages/analytics-page.tsx +20 -0
- package/src/modules/analytics/pages/analytics-storage-page.tsx +344 -0
- package/src/modules/analytics/pages/analytics-vectorstore-page.tsx +279 -0
- package/src/modules/analytics/routes/index.tsx +133 -0
- package/src/modules/analytics/utils/formatters.test.ts +148 -0
- package/src/modules/analytics/utils/formatters.ts +100 -0
- package/src/modules/auth/pages/login-page.module.css +113 -0
- package/src/modules/auth/pages/login-page.tsx +80 -0
- package/src/modules/auth/routes/index.tsx +24 -0
- package/src/modules/dashboard/components/ActivityTimelineWidget.tsx +199 -0
- package/src/modules/dashboard/components/AiInsightsWidget.tsx +358 -0
- package/src/modules/dashboard/components/AnimatedMetricCard.css +88 -0
- package/src/modules/dashboard/components/AnimatedMetricCard.tsx +125 -0
- package/src/modules/dashboard/components/HeroMetricCard.css +114 -0
- package/src/modules/dashboard/components/HeroMetricCard.tsx +152 -0
- package/src/modules/dashboard/components/HeroMetricsWidget.tsx +146 -0
- package/src/modules/dashboard/components/SystemResourcesWidget.tsx +197 -0
- package/src/modules/dashboard/components/dashboard-empty.tsx +22 -0
- package/src/modules/dashboard/components/dashboard-kpis-widget.tsx +59 -0
- package/src/modules/dashboard/components/dashboard-kpis.tsx +13 -0
- package/src/modules/dashboard/components/dashboard-latency-percentiles-widget.tsx +85 -0
- package/src/modules/dashboard/components/dashboard-metrics-widget.tsx +78 -0
- package/src/modules/dashboard/components/dashboard-plugins-widget.tsx +67 -0
- package/src/modules/dashboard/components/dashboard-requests-widget.tsx +62 -0
- package/src/modules/dashboard/layouts/dashboard-layout.tsx +16 -0
- package/src/modules/dashboard/pages/ai-insights-page.tsx +563 -0
- package/src/modules/dashboard/pages/dashboard-animations.css +317 -0
- package/src/modules/dashboard/pages/dashboard-page.tsx +13 -0
- package/src/modules/dashboard/routes/index.tsx +70 -0
- package/src/modules/observability/pages/devkit-page.tsx +263 -0
- package/src/modules/observability/pages/incident-detail-page.tsx +571 -0
- package/src/modules/observability/pages/incidents-page.tsx +370 -0
- package/src/modules/observability/pages/log-detail-page.tsx +508 -0
- package/src/modules/observability/pages/logs-page.tsx +1129 -0
- package/src/modules/observability/pages/prometheus-metrics-page.tsx +486 -0
- package/src/modules/observability/pages/state-broker-page.tsx +229 -0
- package/src/modules/observability/pages/system-events-page.tsx +255 -0
- package/src/modules/observability/routes/index.tsx +133 -0
- package/src/modules/plugins/components/marketplace-card.tsx +153 -0
- package/src/modules/plugins/components/plugin-ai-assistant-modal.tsx +374 -0
- package/src/modules/plugins/components/plugin-card.tsx +136 -0
- package/src/modules/plugins/components/plugin-detail/capabilities-summary.tsx +101 -0
- package/src/modules/plugins/components/plugin-detail/cli-commands-table.tsx +101 -0
- package/src/modules/plugins/components/plugin-detail/index.ts +7 -0
- package/src/modules/plugins/components/plugin-detail/jobs-table.tsx +64 -0
- package/src/modules/plugins/components/plugin-detail/permissions/env-permissions.tsx +30 -0
- package/src/modules/plugins/components/plugin-detail/permissions/filesystem-permissions.tsx +68 -0
- package/src/modules/plugins/components/plugin-detail/permissions/index.ts +6 -0
- package/src/modules/plugins/components/plugin-detail/permissions/network-permissions.tsx +43 -0
- package/src/modules/plugins/components/plugin-detail/permissions/platform-services.tsx +242 -0
- package/src/modules/plugins/components/plugin-detail/permissions/resource-quotas.tsx +96 -0
- package/src/modules/plugins/components/plugin-detail/permissions/shell-permissions.tsx +55 -0
- package/src/modules/plugins/components/plugin-detail/permissions-view.tsx +47 -0
- package/src/modules/plugins/components/plugin-detail/plugin-header.tsx +169 -0
- package/src/modules/plugins/components/plugin-detail/rest-routes-table.tsx +159 -0
- package/src/modules/plugins/components/plugin-detail/workflows-table.tsx +40 -0
- package/src/modules/plugins/components/plugin-grid.tsx +20 -0
- package/src/modules/plugins/components/plugin-table.tsx +178 -0
- package/src/modules/plugins/pages/marketplace-page.tsx +200 -0
- package/src/modules/plugins/pages/plugin-detail-page.tsx +1145 -0
- package/src/modules/plugins/routes/index.tsx +48 -0
- package/src/modules/plugins/utils/index.ts +1 -0
- package/src/modules/plugins/utils/schema-helpers.ts +21 -0
- package/src/modules/settings/components/api-routes-viewer.tsx +415 -0
- package/src/modules/settings/components/appearance-settings.tsx +200 -0
- package/src/modules/settings/components/configuration-settings.tsx +321 -0
- package/src/modules/settings/components/data-privacy-settings.tsx +111 -0
- package/src/modules/settings/components/experimental-settings.tsx +243 -0
- package/src/modules/settings/components/navigation-settings.tsx +198 -0
- package/src/modules/settings/components/notifications-settings.tsx +40 -0
- package/src/modules/settings/pages/settings-page.tsx +298 -0
- package/src/modules/settings/routes/index.tsx +47 -0
- package/src/pages/README-assistant.md +91 -0
- package/src/pages/login-page.tsx +177 -0
- package/src/pages/not-found-page.tsx +85 -0
- package/src/providers/auth-provider.tsx +40 -0
- package/src/providers/command-palette-provider.tsx +193 -0
- package/src/providers/data-sources-provider.tsx +27 -0
- package/src/providers/devtools-provider.tsx +72 -0
- package/src/providers/navigation-items-provider.tsx +38 -0
- package/src/providers/registry-v2-provider.tsx +93 -0
- package/src/providers/settings-provider.tsx +158 -0
- package/src/router.tsx +302 -0
- package/src/routes/helpers.tsx +25 -0
- package/src/routes/plugin-page-v2.tsx +52 -0
- package/src/routes/types.ts +18 -0
- package/src/styles/antd-overrides.css +211 -0
- package/src/styles/base.css +190 -0
- package/src/styles/settings.css +75 -0
- package/src/styles/variables.css +94 -0
- package/src/utils/analytics.ts +183 -0
- package/src/utils/apply-navigation-categories.ts +83 -0
- package/src/utils/commands.ts +252 -0
- package/src/utils/export.ts +174 -0
- package/src/utils/format-date.ts +166 -0
- package/src/utils/logger.ts +110 -0
- package/tailwind.config.ts +13 -0
- package/tsconfig.json +13 -0
- package/vitest-setup.ts +1 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module studio/modules/settings/components/configuration-settings
|
|
3
|
+
* Platform configuration settings component
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as React from 'react';
|
|
7
|
+
import {
|
|
8
|
+
UISpace,
|
|
9
|
+
UITypographyText,
|
|
10
|
+
UITitle,
|
|
11
|
+
UITypographyParagraph,
|
|
12
|
+
UITag,
|
|
13
|
+
UIButton,
|
|
14
|
+
UIMessage,
|
|
15
|
+
UIAlert,
|
|
16
|
+
UIAccordion,
|
|
17
|
+
UICard,
|
|
18
|
+
UIDescriptions,
|
|
19
|
+
UIDescriptionsItem,
|
|
20
|
+
UIIcon,
|
|
21
|
+
} from '@kb-labs/studio-ui-kit';
|
|
22
|
+
import { useDataSources } from '@/providers/data-sources-provider';
|
|
23
|
+
import { usePlatformConfig } from '@kb-labs/studio-data-client';
|
|
24
|
+
import { UISkeleton, UIStack } from '@kb-labs/studio-ui-kit';
|
|
25
|
+
|
|
26
|
+
export function ConfigurationSettings() {
|
|
27
|
+
const sources = useDataSources();
|
|
28
|
+
const { data, isLoading, error } = usePlatformConfig(sources.platform);
|
|
29
|
+
const [copiedKey, setCopiedKey] = React.useState<string | null>(null);
|
|
30
|
+
|
|
31
|
+
const handleCopy = (text: string, key: string) => {
|
|
32
|
+
navigator.clipboard.writeText(text);
|
|
33
|
+
setCopiedKey(key);
|
|
34
|
+
UIMessage.success('Copied to clipboard');
|
|
35
|
+
setTimeout(() => setCopiedKey(null), 2000);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
if (isLoading) {
|
|
39
|
+
return (
|
|
40
|
+
<UIStack>
|
|
41
|
+
<UISkeleton active lines={5} />
|
|
42
|
+
</UIStack>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (error) {
|
|
47
|
+
return (
|
|
48
|
+
<UIAlert
|
|
49
|
+
message="Failed to load platform configuration"
|
|
50
|
+
description={
|
|
51
|
+
<UISpace direction="vertical" size="small">
|
|
52
|
+
<UITypographyText>{error instanceof Error ? error.message : 'Unknown error'}</UITypographyText>
|
|
53
|
+
<UITypographyText type="secondary" style={{ fontSize: '0.875rem' }}>
|
|
54
|
+
Make sure the REST API server is running and accessible.
|
|
55
|
+
</UITypographyText>
|
|
56
|
+
</UISpace>
|
|
57
|
+
}
|
|
58
|
+
variant="error"
|
|
59
|
+
showIcon
|
|
60
|
+
/>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!data) {
|
|
65
|
+
return (
|
|
66
|
+
<div>
|
|
67
|
+
<UITypographyParagraph type="secondary">No platform configuration available</UITypographyParagraph>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const adaptersArray = Object.entries(data.adapters || {});
|
|
73
|
+
const hasRedacted = data.redacted && data.redacted.length > 0;
|
|
74
|
+
|
|
75
|
+
// Helper to count adapters including nested ones
|
|
76
|
+
const countAdapters = (items: Array<[string, unknown]>): number => {
|
|
77
|
+
return items.reduce((count, [, packageName]) => {
|
|
78
|
+
// If it's an object (nested adapters), count its children
|
|
79
|
+
if (typeof packageName === 'object' && packageName !== null && !Array.isArray(packageName)) {
|
|
80
|
+
return count + Object.keys(packageName).length;
|
|
81
|
+
}
|
|
82
|
+
// Otherwise it's a single adapter
|
|
83
|
+
return count + 1;
|
|
84
|
+
}, 0);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// Count total adapters (including nested)
|
|
88
|
+
const totalAdaptersCount = countAdapters(adaptersArray);
|
|
89
|
+
|
|
90
|
+
// Count active adapters (non-null packages, including nested)
|
|
91
|
+
const activeAdaptersCount = adaptersArray.reduce((count, [, packageName]) => {
|
|
92
|
+
if (typeof packageName === 'object' && packageName !== null && !Array.isArray(packageName)) {
|
|
93
|
+
// Count non-null nested adapters
|
|
94
|
+
return count + Object.values(packageName).filter(pkg => pkg !== null).length;
|
|
95
|
+
}
|
|
96
|
+
return count + (packageName !== null ? 1 : 0);
|
|
97
|
+
}, 0);
|
|
98
|
+
|
|
99
|
+
// Adapter categories
|
|
100
|
+
const categories = [
|
|
101
|
+
{
|
|
102
|
+
name: 'AI & Intelligence',
|
|
103
|
+
icon: <UIIcon name="BulbOutlined" />,
|
|
104
|
+
color: 'purple',
|
|
105
|
+
adapters: ['llm', 'embeddings', 'vectorStore'],
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: 'Database',
|
|
109
|
+
icon: <UIIcon name="DatabaseOutlined" />,
|
|
110
|
+
color: 'blue',
|
|
111
|
+
adapters: ['database', 'db', 'documentDb'],
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: 'Infrastructure',
|
|
115
|
+
icon: <UIIcon name="CloudOutlined" />,
|
|
116
|
+
color: 'green',
|
|
117
|
+
adapters: ['storage', 'cache'],
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: 'Observability',
|
|
121
|
+
icon: <UIIcon name="ThunderboltOutlined" />,
|
|
122
|
+
color: 'orange',
|
|
123
|
+
adapters: ['logger', 'analytics', 'logRingBuffer', 'logPersistence'],
|
|
124
|
+
},
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
// Group adapters by category
|
|
128
|
+
const groupedAdapters = categories.map((category) => {
|
|
129
|
+
const items = adaptersArray.filter(([name]) => category.adapters.includes(name));
|
|
130
|
+
return {
|
|
131
|
+
...category,
|
|
132
|
+
items,
|
|
133
|
+
count: countAdapters(items),
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<UISpace direction="vertical" size="large" style={{ width: '100%' }}>
|
|
139
|
+
{/* System Information */}
|
|
140
|
+
<UICard size="small" title="System Information">
|
|
141
|
+
<UIDescriptions column={{ xs: 1, sm: 2 }} size="small">
|
|
142
|
+
<UIDescriptionsItem label="Execution Mode">
|
|
143
|
+
<UITag color="blue" icon={<UIIcon name="RocketOutlined" />}>
|
|
144
|
+
{data.execution.mode}
|
|
145
|
+
</UITag>
|
|
146
|
+
</UIDescriptionsItem>
|
|
147
|
+
<UIDescriptionsItem label="Total Adapters">
|
|
148
|
+
<UISpace size={4}>
|
|
149
|
+
<UITypographyText>{totalAdaptersCount}</UITypographyText>
|
|
150
|
+
<UITypographyText type="secondary">
|
|
151
|
+
({activeAdaptersCount} active)
|
|
152
|
+
</UITypographyText>
|
|
153
|
+
</UISpace>
|
|
154
|
+
</UIDescriptionsItem>
|
|
155
|
+
</UIDescriptions>
|
|
156
|
+
</UICard>
|
|
157
|
+
|
|
158
|
+
{/* Platform Adapters by Category */}
|
|
159
|
+
<div>
|
|
160
|
+
<UITitle level={4}>Platform Adapters</UITitle>
|
|
161
|
+
<UITypographyParagraph type="secondary">
|
|
162
|
+
Adapters are organized by category for better clarity.
|
|
163
|
+
</UITypographyParagraph>
|
|
164
|
+
|
|
165
|
+
<UISpace direction="vertical" size="middle" style={{ width: '100%' }}>
|
|
166
|
+
{groupedAdapters.map((category) => (
|
|
167
|
+
<UICard
|
|
168
|
+
key={category.name}
|
|
169
|
+
size="small"
|
|
170
|
+
title={
|
|
171
|
+
<UISpace>
|
|
172
|
+
<span style={{ fontSize: '1.1rem', color: `var(--ant-${category.color}-6)` }}>
|
|
173
|
+
{category.icon}
|
|
174
|
+
</span>
|
|
175
|
+
<UITypographyText strong>{category.name}</UITypographyText>
|
|
176
|
+
<UITag color={category.color}>{category.count}</UITag>
|
|
177
|
+
</UISpace>
|
|
178
|
+
}
|
|
179
|
+
>
|
|
180
|
+
<UISpace direction="vertical" size="small" style={{ width: '100%' }}>
|
|
181
|
+
{category.items.map(([name, packageName]) => {
|
|
182
|
+
// Helper to render a single adapter item
|
|
183
|
+
const renderAdapterItem = (itemName: string, itemPackage: string, itemKey: string) => {
|
|
184
|
+
const isEnabled = itemPackage !== null;
|
|
185
|
+
return (
|
|
186
|
+
<div
|
|
187
|
+
key={itemKey}
|
|
188
|
+
style={{
|
|
189
|
+
display: 'flex',
|
|
190
|
+
alignItems: 'center',
|
|
191
|
+
justifyContent: 'space-between',
|
|
192
|
+
padding: '8px 12px',
|
|
193
|
+
background: 'var(--kb-bg-secondary)',
|
|
194
|
+
borderRadius: 4,
|
|
195
|
+
borderLeft: isEnabled ? `3px solid var(--ant-${category.color}-6)` : '3px solid var(--ant-border-color)',
|
|
196
|
+
}}
|
|
197
|
+
>
|
|
198
|
+
<UISpace style={{ flex: 1, minWidth: 0 }}>
|
|
199
|
+
<UITypographyText strong style={{ minWidth: 100 }}>{itemName}</UITypographyText>
|
|
200
|
+
<UITypographyText
|
|
201
|
+
code
|
|
202
|
+
style={{
|
|
203
|
+
fontSize: '0.85rem',
|
|
204
|
+
overflow: 'hidden',
|
|
205
|
+
textOverflow: 'ellipsis',
|
|
206
|
+
whiteSpace: 'nowrap',
|
|
207
|
+
}}
|
|
208
|
+
>
|
|
209
|
+
{itemPackage || 'null'}
|
|
210
|
+
</UITypographyText>
|
|
211
|
+
</UISpace>
|
|
212
|
+
<UISpace>
|
|
213
|
+
{isEnabled ? (
|
|
214
|
+
<>
|
|
215
|
+
<UIIcon name="CheckCircleOutlined" style={{ color: 'var(--ant-success-color)' }} />
|
|
216
|
+
<UIButton
|
|
217
|
+
size="small"
|
|
218
|
+
variant="text"
|
|
219
|
+
icon={copiedKey === itemKey ? <UIIcon name="CheckOutlined" /> : <UIIcon name="CopyOutlined" />}
|
|
220
|
+
onClick={() => handleCopy(itemPackage, itemKey)}
|
|
221
|
+
>
|
|
222
|
+
{copiedKey === itemKey ? 'Copied' : 'Copy'}
|
|
223
|
+
</UIButton>
|
|
224
|
+
</>
|
|
225
|
+
) : (
|
|
226
|
+
<UIIcon name="CloseCircleOutlined" style={{ color: 'var(--ant-text-secondary)' }} />
|
|
227
|
+
)}
|
|
228
|
+
</UISpace>
|
|
229
|
+
</div>
|
|
230
|
+
);
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// Handle nested adapters (object with sub-adapters)
|
|
234
|
+
if (typeof packageName === 'object' && packageName !== null && !Array.isArray(packageName)) {
|
|
235
|
+
return (
|
|
236
|
+
<UISpace key={name} direction="vertical" size="small" style={{ width: '100%' }}>
|
|
237
|
+
{Object.entries(packageName as Record<string, string>).map(([subName, subPackage]) =>
|
|
238
|
+
renderAdapterItem(subName, subPackage, `${name}.${subName}`)
|
|
239
|
+
)}
|
|
240
|
+
</UISpace>
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Regular adapter (string package name)
|
|
245
|
+
return renderAdapterItem(name, packageName as string, name);
|
|
246
|
+
})}
|
|
247
|
+
</UISpace>
|
|
248
|
+
</UICard>
|
|
249
|
+
))}
|
|
250
|
+
</UISpace>
|
|
251
|
+
</div>
|
|
252
|
+
|
|
253
|
+
{/* Adapter Configuration */}
|
|
254
|
+
{data.adapterOptions && Object.keys(data.adapterOptions).length > 0 && (
|
|
255
|
+
<div>
|
|
256
|
+
<UITitle level={4}>Adapter Configuration</UITitle>
|
|
257
|
+
<UITypographyParagraph type="secondary">
|
|
258
|
+
Detailed configuration options for each adapter.
|
|
259
|
+
{hasRedacted && (
|
|
260
|
+
<UITypographyText type="warning" style={{ marginLeft: 8 }}>
|
|
261
|
+
Some sensitive values have been redacted for security.
|
|
262
|
+
</UITypographyText>
|
|
263
|
+
)}
|
|
264
|
+
</UITypographyParagraph>
|
|
265
|
+
|
|
266
|
+
<UIAccordion
|
|
267
|
+
accordion
|
|
268
|
+
items={Object.entries(data.adapterOptions).map(([adapterName, options]) => {
|
|
269
|
+
// Find category for this adapter
|
|
270
|
+
const category = categories.find((cat) => cat.adapters.includes(adapterName));
|
|
271
|
+
const _categoryColor = category?.color || 'blue';
|
|
272
|
+
|
|
273
|
+
const optionsCount = options ? Object.keys(options as object).length : 0;
|
|
274
|
+
return {
|
|
275
|
+
key: adapterName,
|
|
276
|
+
label: options ? `${adapterName} (${optionsCount} option${optionsCount !== 1 ? 's' : ''})` : adapterName,
|
|
277
|
+
children: options ? (
|
|
278
|
+
<pre
|
|
279
|
+
style={{
|
|
280
|
+
background: 'var(--kb-bg-secondary)',
|
|
281
|
+
padding: 12,
|
|
282
|
+
borderRadius: 4,
|
|
283
|
+
fontSize: '0.85rem',
|
|
284
|
+
overflow: 'auto',
|
|
285
|
+
margin: 0,
|
|
286
|
+
}}
|
|
287
|
+
>
|
|
288
|
+
{JSON.stringify(options, null, 2)}
|
|
289
|
+
</pre>
|
|
290
|
+
) : (
|
|
291
|
+
<UITypographyText type="secondary">No options configured</UITypographyText>
|
|
292
|
+
),
|
|
293
|
+
};
|
|
294
|
+
})}
|
|
295
|
+
/>
|
|
296
|
+
</div>
|
|
297
|
+
)}
|
|
298
|
+
|
|
299
|
+
{/* Redacted Fields Info */}
|
|
300
|
+
{hasRedacted && (
|
|
301
|
+
<UIAlert
|
|
302
|
+
variant="warning"
|
|
303
|
+
showIcon
|
|
304
|
+
message="Security Notice"
|
|
305
|
+
description={
|
|
306
|
+
<UISpace direction="vertical" size="small">
|
|
307
|
+
<UITypographyText>The following sensitive fields were redacted from the configuration:</UITypographyText>
|
|
308
|
+
<UISpace wrap>
|
|
309
|
+
{data.redacted.map((field) => (
|
|
310
|
+
<UITag key={field} color="orange">
|
|
311
|
+
{field}
|
|
312
|
+
</UITag>
|
|
313
|
+
))}
|
|
314
|
+
</UISpace>
|
|
315
|
+
</UISpace>
|
|
316
|
+
}
|
|
317
|
+
/>
|
|
318
|
+
)}
|
|
319
|
+
</UISpace>
|
|
320
|
+
);
|
|
321
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
UIButton,
|
|
4
|
+
UISpace,
|
|
5
|
+
UITypographyParagraph,
|
|
6
|
+
UITitle,
|
|
7
|
+
UIMessage,
|
|
8
|
+
UIUpload,
|
|
9
|
+
UIIcon,
|
|
10
|
+
} from '@kb-labs/studio-ui-kit';
|
|
11
|
+
import type { UploadProps } from 'antd/es/upload';
|
|
12
|
+
import { useSettings } from '@/providers/settings-provider';
|
|
13
|
+
|
|
14
|
+
export function DataPrivacySettings() {
|
|
15
|
+
const { exportSettings, importSettings, resetSettings } = useSettings();
|
|
16
|
+
|
|
17
|
+
const handleExport = () => {
|
|
18
|
+
try {
|
|
19
|
+
exportSettings();
|
|
20
|
+
UIMessage.success('Settings exported successfully');
|
|
21
|
+
} catch (error) {
|
|
22
|
+
UIMessage.error(`Failed to export settings: ${error instanceof Error ? error.message : String(error)}`);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const handleImport: UploadProps['beforeUpload'] = (file) => {
|
|
27
|
+
const reader = new FileReader();
|
|
28
|
+
reader.onload = (e) => {
|
|
29
|
+
try {
|
|
30
|
+
const content = e.target?.result as string;
|
|
31
|
+
importSettings(content);
|
|
32
|
+
UIMessage.success('Settings imported successfully');
|
|
33
|
+
} catch (error) {
|
|
34
|
+
UIMessage.error(`Failed to import settings: ${error instanceof Error ? error.message : String(error)}`);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
reader.readAsText(file);
|
|
38
|
+
return false; // Prevent auto upload
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const handleClearData = () => {
|
|
42
|
+
if (window.confirm('Are you sure you want to clear all local data? This will reset all settings to defaults.')) {
|
|
43
|
+
try {
|
|
44
|
+
resetSettings();
|
|
45
|
+
localStorage.clear();
|
|
46
|
+
UIMessage.success('All local data cleared successfully');
|
|
47
|
+
} catch (error) {
|
|
48
|
+
UIMessage.error(`Failed to clear data: ${error instanceof Error ? error.message : String(error)}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<UISpace direction="vertical" size="large" style={{ width: '100%' }}>
|
|
55
|
+
{/* Export Settings */}
|
|
56
|
+
<div>
|
|
57
|
+
<UITitle level={5} style={{ marginBottom: 8 }}>
|
|
58
|
+
Export Settings
|
|
59
|
+
</UITitle>
|
|
60
|
+
<UITypographyParagraph type="secondary" style={{ marginBottom: 12 }}>
|
|
61
|
+
Download your current settings as a JSON file. You can use this to backup your preferences
|
|
62
|
+
or transfer them to another device.
|
|
63
|
+
</UITypographyParagraph>
|
|
64
|
+
<UIButton
|
|
65
|
+
icon={<UIIcon name="DownloadOutlined" />}
|
|
66
|
+
onClick={handleExport}
|
|
67
|
+
>
|
|
68
|
+
Export Settings
|
|
69
|
+
</UIButton>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
{/* Import Settings */}
|
|
73
|
+
<div>
|
|
74
|
+
<UITitle level={5} style={{ marginBottom: 8 }}>
|
|
75
|
+
Import Settings
|
|
76
|
+
</UITitle>
|
|
77
|
+
<UITypographyParagraph type="secondary" style={{ marginBottom: 12 }}>
|
|
78
|
+
Restore your settings from a previously exported JSON file. This will overwrite your current settings.
|
|
79
|
+
</UITypographyParagraph>
|
|
80
|
+
<UIUpload
|
|
81
|
+
accept=".json"
|
|
82
|
+
showUploadList={false}
|
|
83
|
+
beforeUpload={handleImport}
|
|
84
|
+
>
|
|
85
|
+
<UIButton icon={<UIIcon name="UploadOutlined" />}>
|
|
86
|
+
Import Settings
|
|
87
|
+
</UIButton>
|
|
88
|
+
</UIUpload>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
{/* Clear Local Data */}
|
|
92
|
+
<div>
|
|
93
|
+
<UITitle level={5} style={{ marginBottom: 8 }}>
|
|
94
|
+
Clear Local Data
|
|
95
|
+
</UITitle>
|
|
96
|
+
<UITypographyParagraph type="secondary" style={{ marginBottom: 12 }}>
|
|
97
|
+
Clear all locally stored data including settings, cache, and preferences.
|
|
98
|
+
This will reset the application to its default state.
|
|
99
|
+
</UITypographyParagraph>
|
|
100
|
+
<UIButton
|
|
101
|
+
icon={<UIIcon name="DeleteOutlined" />}
|
|
102
|
+
onClick={handleClearData}
|
|
103
|
+
variant="primary"
|
|
104
|
+
danger
|
|
105
|
+
>
|
|
106
|
+
Clear All Data
|
|
107
|
+
</UIButton>
|
|
108
|
+
</div>
|
|
109
|
+
</UISpace>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/studio-app/modules/settings/components/experimental-settings
|
|
3
|
+
* Experimental features settings
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as React from 'react';
|
|
7
|
+
import {
|
|
8
|
+
UISwitch,
|
|
9
|
+
UISpace,
|
|
10
|
+
UITypographyText,
|
|
11
|
+
UITypographyParagraph,
|
|
12
|
+
UITitle,
|
|
13
|
+
UIAlert,
|
|
14
|
+
UITag,
|
|
15
|
+
UITooltip,
|
|
16
|
+
UIAccordion,
|
|
17
|
+
UIIcon,
|
|
18
|
+
} from '@kb-labs/studio-ui-kit';
|
|
19
|
+
import { useFeatureFlags } from '@/hooks/use-feature-flags';
|
|
20
|
+
import {
|
|
21
|
+
FEATURE_FLAGS,
|
|
22
|
+
FEATURE_GROUPS,
|
|
23
|
+
getFeaturesByGroup,
|
|
24
|
+
type FeatureGroup,
|
|
25
|
+
type FeatureStatus,
|
|
26
|
+
type FeatureRisk,
|
|
27
|
+
} from '@/config/feature-flags';
|
|
28
|
+
|
|
29
|
+
const _GROUP_ICONS: Record<FeatureGroup, React.ReactNode> = {
|
|
30
|
+
'ui-ux': <UIIcon name="BgColorsOutlined" />,
|
|
31
|
+
performance: <UIIcon name="ThunderboltOutlined" />,
|
|
32
|
+
ai: <UIIcon name="RobotOutlined" />,
|
|
33
|
+
data: <UIIcon name="DatabaseOutlined" />,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const STATUS_COLORS: Record<FeatureStatus, string> = {
|
|
37
|
+
alpha: 'red',
|
|
38
|
+
beta: 'orange',
|
|
39
|
+
stable: 'green',
|
|
40
|
+
deprecated: 'default',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const RISK_COLORS: Record<FeatureRisk, string> = {
|
|
44
|
+
low: 'success',
|
|
45
|
+
medium: 'warning',
|
|
46
|
+
high: 'error',
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const RISK_ICONS: Record<FeatureRisk, React.ReactNode> = {
|
|
50
|
+
low: <UIIcon name="InfoCircleOutlined" />,
|
|
51
|
+
medium: <UIIcon name="WarningOutlined" />,
|
|
52
|
+
high: <UIIcon name="WarningOutlined" />,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function isNewFeature(addedAt?: string): boolean {
|
|
56
|
+
if (!addedAt) {return false;}
|
|
57
|
+
const added = new Date(addedAt);
|
|
58
|
+
const now = new Date();
|
|
59
|
+
const daysDiff = (now.getTime() - added.getTime()) / (1000 * 60 * 60 * 24);
|
|
60
|
+
return daysDiff <= 7; // Consider "new" if added within last 7 days
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function ExperimentalSettings() {
|
|
64
|
+
const { isEnabled, toggleFeature, enabledFeatures, areDependenciesMet } = useFeatureFlags();
|
|
65
|
+
const groupedFeatures = React.useMemo(() => getFeaturesByGroup(), []);
|
|
66
|
+
|
|
67
|
+
const enabledCount = enabledFeatures.length;
|
|
68
|
+
const totalCount = Object.keys(FEATURE_FLAGS).length;
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<UISpace direction="vertical" size="large" style={{ width: '100%' }}>
|
|
72
|
+
{/* Header */}
|
|
73
|
+
<div>
|
|
74
|
+
<UITitle level={4} style={{ marginBottom: 8 }}>
|
|
75
|
+
<UIIcon name="ExperimentOutlined" style={{ marginRight: 8 }} />
|
|
76
|
+
Experimental Features
|
|
77
|
+
</UITitle>
|
|
78
|
+
<UITypographyParagraph type="secondary">
|
|
79
|
+
Enable experimental features to test new functionality before it's generally available.
|
|
80
|
+
These features are still in development and may have bugs or incomplete functionality.
|
|
81
|
+
</UITypographyParagraph>
|
|
82
|
+
<UITypographyParagraph type="secondary" style={{ marginTop: 0 }}>
|
|
83
|
+
<strong>{enabledCount}</strong> of <strong>{totalCount}</strong> features enabled
|
|
84
|
+
</UITypographyParagraph>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
{/* Warning Alert */}
|
|
88
|
+
<UIAlert
|
|
89
|
+
message="Experimental Features Notice"
|
|
90
|
+
description="Features marked as Alpha or Beta are actively being developed and may change or be removed without notice. High-risk features may affect application stability."
|
|
91
|
+
variant="warning"
|
|
92
|
+
showIcon
|
|
93
|
+
icon={<UIIcon name="WarningOutlined" />}
|
|
94
|
+
/>
|
|
95
|
+
|
|
96
|
+
{/* Feature Groups */}
|
|
97
|
+
<UIAccordion
|
|
98
|
+
defaultActiveKey={['ui-ux', 'performance', 'ai', 'data']}
|
|
99
|
+
style={{ backgroundColor: 'transparent' }}
|
|
100
|
+
items={(Object.keys(groupedFeatures) as FeatureGroup[]).map((groupId) => {
|
|
101
|
+
const group = FEATURE_GROUPS[groupId];
|
|
102
|
+
const features = groupedFeatures[groupId];
|
|
103
|
+
const enabledInGroup = features.filter((f) => isEnabled(f.id)).length;
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
key: groupId,
|
|
107
|
+
label: `${group.name} (${enabledInGroup}/${features.length})`,
|
|
108
|
+
style: { marginBottom: 8 },
|
|
109
|
+
children: (
|
|
110
|
+
<UISpace direction="vertical" size="middle" style={{ width: '100%' }}>
|
|
111
|
+
{features.map((feature) => {
|
|
112
|
+
const enabled = isEnabled(feature.id);
|
|
113
|
+
const depsNotMet = !areDependenciesMet(feature.id);
|
|
114
|
+
const isNew = isNewFeature(feature.addedAt);
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<div
|
|
118
|
+
key={feature.id}
|
|
119
|
+
style={{
|
|
120
|
+
padding: '12px 16px',
|
|
121
|
+
backgroundColor: enabled ? 'var(--accent-subtle)' : 'var(--bg-tertiary)',
|
|
122
|
+
borderRadius: 8,
|
|
123
|
+
border: `1px solid ${enabled ? 'var(--link)' : 'var(--border-secondary)'}`,
|
|
124
|
+
transition: 'all 200ms ease-in-out',
|
|
125
|
+
}}
|
|
126
|
+
>
|
|
127
|
+
<div style={{ display: 'flex', alignItems: 'flex-start', gap: 12 }}>
|
|
128
|
+
{/* Left: Switch */}
|
|
129
|
+
<div style={{ paddingTop: 2 }}>
|
|
130
|
+
<UISwitch
|
|
131
|
+
checked={enabled}
|
|
132
|
+
onChange={() => toggleFeature(feature.id)}
|
|
133
|
+
disabled={depsNotMet && !enabled}
|
|
134
|
+
/>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
{/* Middle: Content */}
|
|
138
|
+
<div style={{ flex: 1 }}>
|
|
139
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
|
|
140
|
+
<UITypographyText strong style={{ fontSize: 14 }}>
|
|
141
|
+
{feature.name}
|
|
142
|
+
</UITypographyText>
|
|
143
|
+
{isNew && <UITag color="cyan">New</UITag>}
|
|
144
|
+
<UITag color={STATUS_COLORS[feature.status]}>{feature.status.toUpperCase()}</UITag>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
<UITypographyParagraph
|
|
148
|
+
type="secondary"
|
|
149
|
+
style={{ fontSize: 13, marginBottom: 8, marginTop: 0 }}
|
|
150
|
+
>
|
|
151
|
+
{feature.description}
|
|
152
|
+
</UITypographyParagraph>
|
|
153
|
+
|
|
154
|
+
{/* Details Tooltip */}
|
|
155
|
+
{feature.details && (
|
|
156
|
+
<UITooltip title={feature.details} placement="bottomLeft">
|
|
157
|
+
<UITypographyText
|
|
158
|
+
type="secondary"
|
|
159
|
+
style={{
|
|
160
|
+
fontSize: 12,
|
|
161
|
+
cursor: 'help',
|
|
162
|
+
textDecoration: 'underline',
|
|
163
|
+
textDecorationStyle: 'dotted',
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
Learn more
|
|
167
|
+
</UITypographyText>
|
|
168
|
+
</UITooltip>
|
|
169
|
+
)}
|
|
170
|
+
|
|
171
|
+
{/* Dependencies warning */}
|
|
172
|
+
{depsNotMet && !enabled && feature.dependencies && (
|
|
173
|
+
<div style={{ marginTop: 8 }}>
|
|
174
|
+
<UIAlert
|
|
175
|
+
message={`Requires: ${feature.dependencies.map((d) => FEATURE_FLAGS[d].name).join(', ')}`}
|
|
176
|
+
variant="info"
|
|
177
|
+
showIcon
|
|
178
|
+
icon={<UIIcon name="InfoCircleOutlined" />}
|
|
179
|
+
style={{ fontSize: 12, padding: '4px 8px' }}
|
|
180
|
+
/>
|
|
181
|
+
</div>
|
|
182
|
+
)}
|
|
183
|
+
|
|
184
|
+
{/* Deprecated warning */}
|
|
185
|
+
{feature.status === 'deprecated' && (
|
|
186
|
+
<div style={{ marginTop: 8 }}>
|
|
187
|
+
<UIAlert
|
|
188
|
+
message="This feature is deprecated and will be removed soon"
|
|
189
|
+
variant="warning"
|
|
190
|
+
showIcon
|
|
191
|
+
style={{ fontSize: 12, padding: '4px 8px' }}
|
|
192
|
+
/>
|
|
193
|
+
</div>
|
|
194
|
+
)}
|
|
195
|
+
</div>
|
|
196
|
+
|
|
197
|
+
{/* Right: Risk Badge */}
|
|
198
|
+
<div style={{ paddingTop: 2 }}>
|
|
199
|
+
<UITooltip title={`Risk: ${feature.risk}`}>
|
|
200
|
+
<UITag
|
|
201
|
+
color={RISK_COLORS[feature.risk]}
|
|
202
|
+
icon={RISK_ICONS[feature.risk]}
|
|
203
|
+
style={{ margin: 0 }}
|
|
204
|
+
>
|
|
205
|
+
{feature.risk.toUpperCase()}
|
|
206
|
+
</UITag>
|
|
207
|
+
</UITooltip>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
);
|
|
212
|
+
})}
|
|
213
|
+
</UISpace>
|
|
214
|
+
),
|
|
215
|
+
};
|
|
216
|
+
})}
|
|
217
|
+
/>
|
|
218
|
+
|
|
219
|
+
{/* Footer Info */}
|
|
220
|
+
<UIAlert
|
|
221
|
+
message="Feature Stability Levels"
|
|
222
|
+
description={
|
|
223
|
+
<div>
|
|
224
|
+
<div style={{ marginBottom: 4 }}>
|
|
225
|
+
<UITag color="red">ALPHA</UITag> - Early development, expect breaking changes
|
|
226
|
+
</div>
|
|
227
|
+
<div style={{ marginBottom: 4 }}>
|
|
228
|
+
<UITag color="orange">BETA</UITag> - Feature-complete but needs testing
|
|
229
|
+
</div>
|
|
230
|
+
<div style={{ marginBottom: 4 }}>
|
|
231
|
+
<UITag color="green">STABLE</UITag> - Production-ready, safe to use
|
|
232
|
+
</div>
|
|
233
|
+
<div>
|
|
234
|
+
<UITag color="default">DEPRECATED</UITag> - Will be removed soon
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
}
|
|
238
|
+
variant="info"
|
|
239
|
+
showIcon
|
|
240
|
+
/>
|
|
241
|
+
</UISpace>
|
|
242
|
+
);
|
|
243
|
+
}
|