@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,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/studio-app/modules/settings/components/navigation-settings
|
|
3
|
+
* UI for managing sidebar navigation categories.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as React from 'react';
|
|
7
|
+
import {
|
|
8
|
+
UISpace,
|
|
9
|
+
UITypographyText,
|
|
10
|
+
UITypographyParagraph,
|
|
11
|
+
UIButton,
|
|
12
|
+
UIInput,
|
|
13
|
+
UISelect,
|
|
14
|
+
UICard,
|
|
15
|
+
UIEmptyState,
|
|
16
|
+
UIPopconfirm,
|
|
17
|
+
UIIcon,
|
|
18
|
+
} from '@kb-labs/studio-ui-kit';
|
|
19
|
+
import { useSettings, type NavigationCategory } from '@/providers/settings-provider';
|
|
20
|
+
import { useNavigationItems } from '@/providers/navigation-items-provider';
|
|
21
|
+
|
|
22
|
+
export function NavigationSettings() {
|
|
23
|
+
const { settings, updateSettings } = useSettings();
|
|
24
|
+
const availableItems = useNavigationItems();
|
|
25
|
+
const categories = settings.navigation.categories;
|
|
26
|
+
|
|
27
|
+
// Set of valid item keys (for filtering stale references)
|
|
28
|
+
const validKeys = React.useMemo(
|
|
29
|
+
() => new Set(availableItems.map(i => i.key)),
|
|
30
|
+
[availableItems],
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
// Items already assigned to any category (only valid keys)
|
|
34
|
+
const assignedKeys = React.useMemo(() => {
|
|
35
|
+
const set = new Set<string>();
|
|
36
|
+
for (const cat of categories) {
|
|
37
|
+
for (const key of cat.itemKeys) {
|
|
38
|
+
if (validKeys.has(key)) {
|
|
39
|
+
set.add(key);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return set;
|
|
44
|
+
}, [categories, validKeys]);
|
|
45
|
+
|
|
46
|
+
const updateCategories = React.useCallback(
|
|
47
|
+
(newCategories: NavigationCategory[]) => {
|
|
48
|
+
updateSettings({ navigation: { categories: newCategories } });
|
|
49
|
+
},
|
|
50
|
+
[updateSettings],
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const addCategory = React.useCallback(() => {
|
|
54
|
+
const id = `cat-${Date.now()}`;
|
|
55
|
+
updateCategories([
|
|
56
|
+
...categories,
|
|
57
|
+
{ id, label: '', order: categories.length, itemKeys: [] },
|
|
58
|
+
]);
|
|
59
|
+
}, [categories, updateCategories]);
|
|
60
|
+
|
|
61
|
+
const removeCategory = React.useCallback(
|
|
62
|
+
(id: string) => {
|
|
63
|
+
updateCategories(categories.filter(c => c.id !== id));
|
|
64
|
+
},
|
|
65
|
+
[categories, updateCategories],
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const updateCategory = React.useCallback(
|
|
69
|
+
(id: string, updates: Partial<NavigationCategory>) => {
|
|
70
|
+
updateCategories(
|
|
71
|
+
categories.map(c => (c.id === id ? { ...c, ...updates } : c)),
|
|
72
|
+
);
|
|
73
|
+
},
|
|
74
|
+
[categories, updateCategories],
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
const moveCategory = React.useCallback(
|
|
78
|
+
(id: string, direction: -1 | 1) => {
|
|
79
|
+
const idx = categories.findIndex(c => c.id === id);
|
|
80
|
+
if (idx < 0) {return;}
|
|
81
|
+
const newIdx = idx + direction;
|
|
82
|
+
if (newIdx < 0 || newIdx >= categories.length) {return;}
|
|
83
|
+
const newCats = [...categories];
|
|
84
|
+
const temp = newCats[idx]!;
|
|
85
|
+
newCats[idx] = newCats[newIdx]!;
|
|
86
|
+
newCats[newIdx] = temp;
|
|
87
|
+
updateCategories(newCats.map((c, i) => ({ ...c, order: i })));
|
|
88
|
+
},
|
|
89
|
+
[categories, updateCategories],
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const resetNavigation = React.useCallback(() => {
|
|
93
|
+
updateCategories([]);
|
|
94
|
+
}, [updateCategories]);
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<UISpace direction="vertical" size="large" style={{ width: '100%' }}>
|
|
98
|
+
<div>
|
|
99
|
+
<UITypographyText strong style={{ fontSize: 16, display: 'block', marginBottom: 12 }}>
|
|
100
|
+
Sidebar Categories
|
|
101
|
+
</UITypographyText>
|
|
102
|
+
<UITypographyParagraph type="secondary" style={{ marginBottom: 16 }}>
|
|
103
|
+
Create categories to group sidebar items. Uncategorized items stay at the top.
|
|
104
|
+
Settings is always pinned to the bottom.
|
|
105
|
+
</UITypographyParagraph>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
{categories.length === 0 ? (
|
|
109
|
+
<UIEmptyState
|
|
110
|
+
image={UIEmptyState.PRESENTED_IMAGE_SIMPLE}
|
|
111
|
+
description="No categories. All sidebar items are shown as a flat list."
|
|
112
|
+
/>
|
|
113
|
+
) : (
|
|
114
|
+
<UISpace direction="vertical" size="middle" style={{ width: '100%' }}>
|
|
115
|
+
{categories.map((cat, idx) => {
|
|
116
|
+
// Items selectable for this category: unassigned + already in this category
|
|
117
|
+
const selectableOptions = availableItems.filter(
|
|
118
|
+
item => !assignedKeys.has(item.key) || cat.itemKeys.includes(item.key),
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
// Filter stale keys from display
|
|
122
|
+
const validItemKeys = cat.itemKeys.filter(k => validKeys.has(k));
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<UICard
|
|
126
|
+
key={cat.id}
|
|
127
|
+
size="small"
|
|
128
|
+
style={{ borderColor: 'var(--border-primary)' }}
|
|
129
|
+
>
|
|
130
|
+
<UISpace direction="vertical" size="small" style={{ width: '100%' }}>
|
|
131
|
+
<div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
|
|
132
|
+
<UIInput
|
|
133
|
+
value={cat.label}
|
|
134
|
+
onChange={value =>
|
|
135
|
+
updateCategory(cat.id, { label: value })
|
|
136
|
+
}
|
|
137
|
+
placeholder="Category name"
|
|
138
|
+
style={{ flex: 1 }}
|
|
139
|
+
/>
|
|
140
|
+
<UIButton
|
|
141
|
+
size="small"
|
|
142
|
+
icon={<UIIcon name="ArrowUpOutlined" />}
|
|
143
|
+
disabled={idx === 0}
|
|
144
|
+
onClick={() => moveCategory(cat.id, -1)}
|
|
145
|
+
/>
|
|
146
|
+
<UIButton
|
|
147
|
+
size="small"
|
|
148
|
+
icon={<UIIcon name="ArrowDownOutlined" />}
|
|
149
|
+
disabled={idx === categories.length - 1}
|
|
150
|
+
onClick={() => moveCategory(cat.id, 1)}
|
|
151
|
+
/>
|
|
152
|
+
<UIPopconfirm
|
|
153
|
+
title="Delete this category?"
|
|
154
|
+
description="Items will return to the uncategorized area."
|
|
155
|
+
onConfirm={() => removeCategory(cat.id)}
|
|
156
|
+
>
|
|
157
|
+
<UIButton size="small" danger icon={<UIIcon name="DeleteOutlined" />} />
|
|
158
|
+
</UIPopconfirm>
|
|
159
|
+
</div>
|
|
160
|
+
|
|
161
|
+
<UISelect
|
|
162
|
+
mode="multiple"
|
|
163
|
+
value={validItemKeys}
|
|
164
|
+
onChange={keys =>
|
|
165
|
+
updateCategory(cat.id, { itemKeys: (keys as string[]) })
|
|
166
|
+
}
|
|
167
|
+
options={selectableOptions.map(i => ({
|
|
168
|
+
value: i.key,
|
|
169
|
+
label: i.label,
|
|
170
|
+
}))}
|
|
171
|
+
placeholder="Select sidebar items for this category"
|
|
172
|
+
style={{ width: '100%' }}
|
|
173
|
+
optionFilterProp="label"
|
|
174
|
+
/>
|
|
175
|
+
</UISpace>
|
|
176
|
+
</UICard>
|
|
177
|
+
);
|
|
178
|
+
})}
|
|
179
|
+
</UISpace>
|
|
180
|
+
)}
|
|
181
|
+
|
|
182
|
+
<UISpace>
|
|
183
|
+
<UIButton variant="dashed" icon={<UIIcon name="PlusOutlined" />} onClick={addCategory}>
|
|
184
|
+
Add Category
|
|
185
|
+
</UIButton>
|
|
186
|
+
{categories.length > 0 && (
|
|
187
|
+
<UIPopconfirm
|
|
188
|
+
title="Reset all navigation customizations?"
|
|
189
|
+
description="All categories will be removed."
|
|
190
|
+
onConfirm={resetNavigation}
|
|
191
|
+
>
|
|
192
|
+
<UIButton danger>Reset Navigation</UIButton>
|
|
193
|
+
</UIPopconfirm>
|
|
194
|
+
)}
|
|
195
|
+
</UISpace>
|
|
196
|
+
</UISpace>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { UIForm, UIFormItem, UISwitch, UISpace, UIAlert, UIIcon } from '@kb-labs/studio-ui-kit';
|
|
3
|
+
|
|
4
|
+
export function NotificationsSettings() {
|
|
5
|
+
return (
|
|
6
|
+
<UISpace direction="vertical" size="large" style={{ width: '100%' }}>
|
|
7
|
+
<UIAlert
|
|
8
|
+
message="Feature in Development"
|
|
9
|
+
description="Notification preferences will be configurable soon. We're working on bringing you more control over alerts and notifications."
|
|
10
|
+
variant="warning"
|
|
11
|
+
showIcon
|
|
12
|
+
icon={<UIIcon name="BellOutlined" />}
|
|
13
|
+
/>
|
|
14
|
+
|
|
15
|
+
<div style={{ opacity: 0.5, pointerEvents: 'none' }}>
|
|
16
|
+
<UIForm layout="vertical">
|
|
17
|
+
<UIFormItem label="Toast Notifications" help="Show temporary notification popups">
|
|
18
|
+
<UISwitch disabled defaultChecked />
|
|
19
|
+
</UIFormItem>
|
|
20
|
+
|
|
21
|
+
<UIFormItem label="Sound Alerts" help="Play sounds for important events">
|
|
22
|
+
<UISwitch disabled />
|
|
23
|
+
</UIFormItem>
|
|
24
|
+
|
|
25
|
+
<UIFormItem label="Desktop Notifications" help="Enable browser notifications (requires permission)">
|
|
26
|
+
<UISwitch disabled />
|
|
27
|
+
</UIFormItem>
|
|
28
|
+
|
|
29
|
+
<UIFormItem label="Email Digest" help="Receive daily summary emails">
|
|
30
|
+
<UISwitch disabled />
|
|
31
|
+
</UIFormItem>
|
|
32
|
+
|
|
33
|
+
<UIFormItem label="Error Notifications" help="Get notified when errors occur">
|
|
34
|
+
<UISwitch disabled defaultChecked />
|
|
35
|
+
</UIFormItem>
|
|
36
|
+
</UIForm>
|
|
37
|
+
</div>
|
|
38
|
+
</UISpace>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
UITabs,
|
|
4
|
+
UIList,
|
|
5
|
+
UIListItem,
|
|
6
|
+
UIListItemMeta,
|
|
7
|
+
UIButton,
|
|
8
|
+
UISpace,
|
|
9
|
+
UITypographyText,
|
|
10
|
+
UITypographyParagraph,
|
|
11
|
+
UITitle,
|
|
12
|
+
UIMessage,
|
|
13
|
+
UIDivider,
|
|
14
|
+
UIIcon,
|
|
15
|
+
} from '@kb-labs/studio-ui-kit';
|
|
16
|
+
import { useDataSources } from '@/providers/data-sources-provider';
|
|
17
|
+
import { useHealthStatus } from '@kb-labs/studio-data-client';
|
|
18
|
+
import { HealthIndicator } from '@/components/health-indicator';
|
|
19
|
+
import { useRegistryV2 } from '@/providers/registry-v2-provider';
|
|
20
|
+
import { formatTimestamp } from '@/utils/format-date';
|
|
21
|
+
import { AppearanceSettings } from '../components/appearance-settings';
|
|
22
|
+
import { DataPrivacySettings } from '../components/data-privacy-settings';
|
|
23
|
+
import { NotificationsSettings } from '../components/notifications-settings';
|
|
24
|
+
import { ExperimentalSettings } from '../components/experimental-settings';
|
|
25
|
+
import { ConfigurationSettings } from '../components/configuration-settings';
|
|
26
|
+
import { RoleSwitcher } from '@/components/role-switcher';
|
|
27
|
+
import { ApiRoutesViewer } from '../components/api-routes-viewer';
|
|
28
|
+
import { NavigationSettings } from '../components/navigation-settings';
|
|
29
|
+
import { UICard, UISkeleton, UIStack } from '@kb-labs/studio-ui-kit';
|
|
30
|
+
import { UIPage, UIPageHeader, UIPageSection } from '@kb-labs/studio-ui-kit';
|
|
31
|
+
|
|
32
|
+
export function SettingsPage() {
|
|
33
|
+
const sources = useDataSources();
|
|
34
|
+
const { data, isLoading } = useHealthStatus(sources.system);
|
|
35
|
+
const { registry } = useRegistryV2();
|
|
36
|
+
const registryMeta = { generatedAt: registry.generatedAt, rev: null, partial: false, stale: false, expiresAt: undefined as string | undefined, ttlMs: undefined as number | undefined, checksum: undefined as string | undefined };
|
|
37
|
+
const refresh = async () => {}; // TODO: queryClient.invalidateQueries
|
|
38
|
+
const [invalidating, setInvalidating] = React.useState(false);
|
|
39
|
+
|
|
40
|
+
const handleInvalidateCache = async () => {
|
|
41
|
+
setInvalidating(true);
|
|
42
|
+
try {
|
|
43
|
+
const result = await sources.cache.invalidateCache();
|
|
44
|
+
UIMessage.success(
|
|
45
|
+
`Cache invalidated! Rev: ${result.previousRev ?? 'N/A'} → ${result.newRev}. ` +
|
|
46
|
+
`Discovered ${result.pluginsDiscovered} plugins.`
|
|
47
|
+
);
|
|
48
|
+
await refresh();
|
|
49
|
+
} catch (error) {
|
|
50
|
+
UIMessage.error(`Error invalidating cache: ${error instanceof Error ? error.message : String(error)}`);
|
|
51
|
+
} finally {
|
|
52
|
+
setInvalidating(false);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const handleRefreshRegistry = async () => {
|
|
57
|
+
try {
|
|
58
|
+
await refresh();
|
|
59
|
+
UIMessage.success('Registry refreshed successfully');
|
|
60
|
+
} catch (error) {
|
|
61
|
+
UIMessage.error(`Failed to refresh registry: ${error instanceof Error ? error.message : String(error)}`);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const tabItems = [
|
|
66
|
+
{
|
|
67
|
+
key: 'appearance',
|
|
68
|
+
label: 'Appearance',
|
|
69
|
+
icon: <UIIcon name="BgColorsOutlined" />,
|
|
70
|
+
children: (
|
|
71
|
+
<UIPageSection>
|
|
72
|
+
<UICard>
|
|
73
|
+
<AppearanceSettings />
|
|
74
|
+
</UICard>
|
|
75
|
+
</UIPageSection>
|
|
76
|
+
),
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
key: 'navigation',
|
|
80
|
+
label: 'Navigation',
|
|
81
|
+
icon: <UIIcon name="MenuOutlined" />,
|
|
82
|
+
children: (
|
|
83
|
+
<UIPageSection>
|
|
84
|
+
<UICard>
|
|
85
|
+
<NavigationSettings />
|
|
86
|
+
</UICard>
|
|
87
|
+
</UIPageSection>
|
|
88
|
+
),
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
key: 'notifications',
|
|
92
|
+
label: 'Notifications',
|
|
93
|
+
icon: <UIIcon name="BellOutlined" />,
|
|
94
|
+
children: (
|
|
95
|
+
<UIPageSection>
|
|
96
|
+
<UICard>
|
|
97
|
+
<NotificationsSettings />
|
|
98
|
+
</UICard>
|
|
99
|
+
</UIPageSection>
|
|
100
|
+
),
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
key: 'privacy',
|
|
104
|
+
label: 'Data & Privacy',
|
|
105
|
+
icon: <UIIcon name="LockOutlined" />,
|
|
106
|
+
children: (
|
|
107
|
+
<UIPageSection>
|
|
108
|
+
<UICard>
|
|
109
|
+
<DataPrivacySettings />
|
|
110
|
+
</UICard>
|
|
111
|
+
</UIPageSection>
|
|
112
|
+
),
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
key: 'system',
|
|
116
|
+
label: 'System Info',
|
|
117
|
+
icon: <UIIcon name="DatabaseOutlined" />,
|
|
118
|
+
children: (
|
|
119
|
+
<>
|
|
120
|
+
<UIPageSection>
|
|
121
|
+
<UICard title="Data Sources Health">
|
|
122
|
+
{isLoading ? (
|
|
123
|
+
<UIStack>
|
|
124
|
+
<UISkeleton active lines={3} />
|
|
125
|
+
</UIStack>
|
|
126
|
+
) : data ? (
|
|
127
|
+
<UIList
|
|
128
|
+
dataSource={data.sources}
|
|
129
|
+
renderItem={(source) => {
|
|
130
|
+
const status = source.ok
|
|
131
|
+
? 'ok'
|
|
132
|
+
: source.error === 'system_degraded'
|
|
133
|
+
? 'degraded'
|
|
134
|
+
: 'down';
|
|
135
|
+
return (
|
|
136
|
+
<UIListItem key={source.name} extra={<HealthIndicator status={status} />}>
|
|
137
|
+
<UIListItemMeta
|
|
138
|
+
title={source.name}
|
|
139
|
+
description={source.latency ? `${source.latency}ms` : undefined}
|
|
140
|
+
/>
|
|
141
|
+
</UIListItem>
|
|
142
|
+
);
|
|
143
|
+
}}
|
|
144
|
+
/>
|
|
145
|
+
) : (
|
|
146
|
+
<p>No health data available</p>
|
|
147
|
+
)}
|
|
148
|
+
</UICard>
|
|
149
|
+
</UIPageSection>
|
|
150
|
+
|
|
151
|
+
<UIPageSection>
|
|
152
|
+
<UICard title="Registry Status">
|
|
153
|
+
<UISpace direction="vertical" style={{ width: '100%' }}>
|
|
154
|
+
<div>
|
|
155
|
+
<UITypographyText strong>Revision:</UITypographyText> <UITypographyText>{registryMeta.rev ?? 'N/A'}</UITypographyText>
|
|
156
|
+
</div>
|
|
157
|
+
<div>
|
|
158
|
+
<UITypographyText strong>Generated At:</UITypographyText> <UITypographyText>{formatTimestamp(registryMeta.generatedAt)}</UITypographyText>
|
|
159
|
+
</div>
|
|
160
|
+
<div>
|
|
161
|
+
<UITypographyText strong>Expires At:</UITypographyText> <UITypographyText>{formatTimestamp(registryMeta.expiresAt)}</UITypographyText>
|
|
162
|
+
</div>
|
|
163
|
+
<div>
|
|
164
|
+
<UITypographyText strong>TTL:</UITypographyText> <UITypographyText>{registryMeta.ttlMs ? `${registryMeta.ttlMs / 1000}s` : 'N/A'}</UITypographyText>
|
|
165
|
+
</div>
|
|
166
|
+
<div>
|
|
167
|
+
<UITypographyText strong>Partial:</UITypographyText> <UITypographyText type={registryMeta.partial ? 'warning' : 'success'}>
|
|
168
|
+
{registryMeta.partial ? 'Yes' : 'No'}
|
|
169
|
+
</UITypographyText>
|
|
170
|
+
</div>
|
|
171
|
+
<div>
|
|
172
|
+
<UITypographyText strong>Stale:</UITypographyText> <UITypographyText type={registryMeta.stale ? 'danger' : 'success'}>
|
|
173
|
+
{registryMeta.stale ? 'Yes' : 'No'}
|
|
174
|
+
</UITypographyText>
|
|
175
|
+
</div>
|
|
176
|
+
{registryMeta.checksum && (
|
|
177
|
+
<div>
|
|
178
|
+
<UITypographyText strong>Checksum:</UITypographyText> <UITypographyText code style={{ fontSize: '0.85rem' }}>
|
|
179
|
+
{registryMeta.checksum.substring(0, 8)}...
|
|
180
|
+
</UITypographyText>
|
|
181
|
+
</div>
|
|
182
|
+
)}
|
|
183
|
+
</UISpace>
|
|
184
|
+
</UICard>
|
|
185
|
+
</UIPageSection>
|
|
186
|
+
</>
|
|
187
|
+
),
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
key: 'configuration',
|
|
191
|
+
label: 'Configuration',
|
|
192
|
+
icon: <UIIcon name="SettingOutlined" />,
|
|
193
|
+
children: (
|
|
194
|
+
<UIPageSection>
|
|
195
|
+
<UICard>
|
|
196
|
+
<ConfigurationSettings />
|
|
197
|
+
</UICard>
|
|
198
|
+
</UIPageSection>
|
|
199
|
+
),
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
key: 'experimental',
|
|
203
|
+
label: 'Experimental',
|
|
204
|
+
icon: <UIIcon name="ExperimentOutlined" />,
|
|
205
|
+
children: (
|
|
206
|
+
<UIPageSection>
|
|
207
|
+
<UICard>
|
|
208
|
+
<ExperimentalSettings />
|
|
209
|
+
</UICard>
|
|
210
|
+
</UIPageSection>
|
|
211
|
+
),
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
key: 'authentication',
|
|
215
|
+
label: 'Authentication',
|
|
216
|
+
icon: <UIIcon name="UserOutlined" />,
|
|
217
|
+
children: (
|
|
218
|
+
<UIPageSection>
|
|
219
|
+
<UICard>
|
|
220
|
+
<RoleSwitcher />
|
|
221
|
+
</UICard>
|
|
222
|
+
</UIPageSection>
|
|
223
|
+
),
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
key: 'developer',
|
|
227
|
+
label: 'Developer',
|
|
228
|
+
icon: <UIIcon name="ToolOutlined" />,
|
|
229
|
+
children: (
|
|
230
|
+
<>
|
|
231
|
+
<UIPageSection>
|
|
232
|
+
<UICard title="Development Tools">
|
|
233
|
+
<UISpace direction="vertical" style={{ width: '100%' }} size="large">
|
|
234
|
+
<div>
|
|
235
|
+
<UITitle level={5}>Refresh Registry</UITitle>
|
|
236
|
+
<UITypographyParagraph type="secondary">
|
|
237
|
+
Fetch fresh registry data from the REST API without clearing the cache.
|
|
238
|
+
Use this to get the latest changes without forcing re-discovery.
|
|
239
|
+
</UITypographyParagraph>
|
|
240
|
+
<UIButton
|
|
241
|
+
icon={<UIIcon name="ReloadOutlined" />}
|
|
242
|
+
onClick={handleRefreshRegistry}
|
|
243
|
+
>
|
|
244
|
+
Refresh Registry
|
|
245
|
+
</UIButton>
|
|
246
|
+
</div>
|
|
247
|
+
|
|
248
|
+
<UIDivider />
|
|
249
|
+
|
|
250
|
+
<div>
|
|
251
|
+
<UITitle level={5}>Invalidate Cache (Force Discovery)</UITitle>
|
|
252
|
+
<UITypographyParagraph type="secondary">
|
|
253
|
+
Force cache invalidation and trigger full plugin re-discovery on the REST API.
|
|
254
|
+
This clears the snapshot cache and rescans all plugin directories.
|
|
255
|
+
Useful when testing plugin changes or troubleshooting registry issues.
|
|
256
|
+
</UITypographyParagraph>
|
|
257
|
+
<UIButton
|
|
258
|
+
icon={<UIIcon name="DeleteOutlined" />}
|
|
259
|
+
onClick={handleInvalidateCache}
|
|
260
|
+
loading={invalidating}
|
|
261
|
+
variant="primary"
|
|
262
|
+
danger
|
|
263
|
+
>
|
|
264
|
+
Invalidate Cache & Re-discover
|
|
265
|
+
</UIButton>
|
|
266
|
+
</div>
|
|
267
|
+
</UISpace>
|
|
268
|
+
</UICard>
|
|
269
|
+
</UIPageSection>
|
|
270
|
+
|
|
271
|
+
<UIPageSection>
|
|
272
|
+
<UICard>
|
|
273
|
+
<ApiRoutesViewer />
|
|
274
|
+
</UICard>
|
|
275
|
+
</UIPageSection>
|
|
276
|
+
|
|
277
|
+
{/* DevToolsPanel is now a global floating panel rendered in App.tsx */}
|
|
278
|
+
</>
|
|
279
|
+
),
|
|
280
|
+
},
|
|
281
|
+
];
|
|
282
|
+
|
|
283
|
+
return (
|
|
284
|
+
<UIPage variant="document">
|
|
285
|
+
<UIPageHeader
|
|
286
|
+
title="Settings"
|
|
287
|
+
description="Configure your preferences, manage data, and customize your experience"
|
|
288
|
+
/>
|
|
289
|
+
|
|
290
|
+
<UITabs
|
|
291
|
+
items={tabItems}
|
|
292
|
+
syncUrl={{ mode: 'path', basePath: '/settings' }}
|
|
293
|
+
size="large"
|
|
294
|
+
style={{ marginTop: 24 }}
|
|
295
|
+
/>
|
|
296
|
+
</UIPage>
|
|
297
|
+
);
|
|
298
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/studio-app/modules/settings/routes
|
|
3
|
+
* Settings module routing configuration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { RouteObject } from 'react-router-dom';
|
|
7
|
+
import { SettingsPage } from '../pages/settings-page';
|
|
8
|
+
import { ErrorBoundary } from '../../../components/error-boundary';
|
|
9
|
+
import { renderIcon } from '../../../routes/helpers';
|
|
10
|
+
import type { NavigationItem } from '@/components/ui';
|
|
11
|
+
|
|
12
|
+
// Route paths
|
|
13
|
+
const PATHS = {
|
|
14
|
+
ROOT: '/settings',
|
|
15
|
+
TAB: '/settings/:tab',
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
// Route keys
|
|
19
|
+
const KEYS = {
|
|
20
|
+
MODULE: 'settings',
|
|
21
|
+
} as const;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Settings routes
|
|
25
|
+
*/
|
|
26
|
+
export const settingsRoutes: RouteObject[] = [
|
|
27
|
+
{
|
|
28
|
+
path: PATHS.ROOT,
|
|
29
|
+
element: <SettingsPage />,
|
|
30
|
+
errorElement: <ErrorBoundary />,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
path: PATHS.TAB,
|
|
34
|
+
element: <SettingsPage />,
|
|
35
|
+
errorElement: <ErrorBoundary />,
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Settings navigation item for sidebar
|
|
41
|
+
*/
|
|
42
|
+
export const settingsNavigation: NavigationItem = {
|
|
43
|
+
key: KEYS.MODULE,
|
|
44
|
+
label: 'Settings',
|
|
45
|
+
icon: renderIcon('SettingOutlined'),
|
|
46
|
+
path: PATHS.ROOT,
|
|
47
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# AI Platform Assistant - UI Mockup
|
|
2
|
+
|
|
3
|
+
## Status: In Development (UI Preview)
|
|
4
|
+
|
|
5
|
+
This is a UI mockup for the KB Labs AI Platform Assistant feature.
|
|
6
|
+
|
|
7
|
+
## Current Implementation
|
|
8
|
+
|
|
9
|
+
**What works:**
|
|
10
|
+
- ✅ Chat UI with message bubbles
|
|
11
|
+
- ✅ User/Assistant message display
|
|
12
|
+
- ✅ Sources display (file references with line numbers)
|
|
13
|
+
- ✅ Quick action tags
|
|
14
|
+
- ✅ Mock conversation for UX testing
|
|
15
|
+
- ✅ Loading states
|
|
16
|
+
- ✅ "In Development" alert banner
|
|
17
|
+
|
|
18
|
+
**What doesn't work yet:**
|
|
19
|
+
- ❌ Backend integration
|
|
20
|
+
- ❌ Real Mind RAG queries
|
|
21
|
+
- ❌ LLM streaming
|
|
22
|
+
- ❌ Actual assistant responses
|
|
23
|
+
|
|
24
|
+
## File Location
|
|
25
|
+
|
|
26
|
+
`/Users/kirillbaranov/Desktop/kb-labs/kb-labs-studio/apps/studio/src/pages/assistant-page.tsx`
|
|
27
|
+
|
|
28
|
+
## Access
|
|
29
|
+
|
|
30
|
+
Navigate to: `http://localhost:3000/assistant`
|
|
31
|
+
|
|
32
|
+
## UI Components
|
|
33
|
+
|
|
34
|
+
### Message Bubble
|
|
35
|
+
- User messages: Right-aligned, blue background
|
|
36
|
+
- Assistant messages: Left-aligned, gray background with robot avatar
|
|
37
|
+
- Timestamps for each message
|
|
38
|
+
|
|
39
|
+
### Sources Display
|
|
40
|
+
- File path with line numbers
|
|
41
|
+
- Code snippet preview
|
|
42
|
+
- Collapsible card format
|
|
43
|
+
|
|
44
|
+
### Quick Actions
|
|
45
|
+
- Pre-defined question tags
|
|
46
|
+
- Click to populate input
|
|
47
|
+
- Examples: "Create workflow", "Mind RAG", "DevKit usage"
|
|
48
|
+
|
|
49
|
+
## Mock Data
|
|
50
|
+
|
|
51
|
+
Current mock conversation demonstrates:
|
|
52
|
+
- User asking "How do I create a new plugin?"
|
|
53
|
+
- Assistant providing step-by-step instructions with code examples
|
|
54
|
+
- Source references to actual files (kb-labs-plugin/README.md, types.ts)
|
|
55
|
+
|
|
56
|
+
## Next Steps (Backend Integration)
|
|
57
|
+
|
|
58
|
+
When ready to implement backend:
|
|
59
|
+
|
|
60
|
+
1. **Create Assistant Service** (`kb-labs-rest-api/src/services/assistant-service.ts`)
|
|
61
|
+
- Use `useLLM()` for streaming
|
|
62
|
+
- Use Mind Orchestrator for search
|
|
63
|
+
- Implement SSE streaming
|
|
64
|
+
|
|
65
|
+
2. **Create REST Endpoint** (`kb-labs-rest-api/src/routes/assistant.ts`)
|
|
66
|
+
- POST `/api/v1/assistant/query`
|
|
67
|
+
- Server-Sent Events for streaming
|
|
68
|
+
|
|
69
|
+
3. **Update Frontend**
|
|
70
|
+
- Replace mock `handleSend()` with real API call
|
|
71
|
+
- Implement SSE client for streaming chunks
|
|
72
|
+
- Handle loading/error states
|
|
73
|
+
|
|
74
|
+
## Design Notes
|
|
75
|
+
|
|
76
|
+
- **Clean, minimal UI** - Focus on readability
|
|
77
|
+
- **Sources are prominent** - Users can verify information
|
|
78
|
+
- **Quick actions reduce friction** - Common questions one-click away
|
|
79
|
+
- **Loading states** - Pulse animation shows thinking state
|
|
80
|
+
- **Responsive layout** - Works on different screen sizes
|
|
81
|
+
|
|
82
|
+
## Future Enhancements
|
|
83
|
+
|
|
84
|
+
- [ ] Markdown rendering in messages
|
|
85
|
+
- [ ] Code syntax highlighting
|
|
86
|
+
- [ ] Copy code button
|
|
87
|
+
- [ ] Clear chat history
|
|
88
|
+
- [ ] Export conversation
|
|
89
|
+
- [ ] Keyboard shortcuts (Cmd+K to focus input)
|
|
90
|
+
- [ ] Suggested follow-up questions
|
|
91
|
+
- [ ] Conversation history/sessions
|