@kb-labs/studio-app 0.7.0 → 2.9.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.80b70197.css +2 -0
- package/dist/main.80b70197.css.map +1 -0
- package/dist/main.ad42ef4a.js +522 -0
- package/dist/main.ad42ef4a.js.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,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Registry V2 provider — fetches StudioRegistryV2 and initializes Module Federation.
|
|
3
|
-
*
|
|
4
|
-
* Fetches StudioRegistryV2 and initializes Module Federation remotes.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import * as React from 'react';
|
|
8
|
-
import { useQuery } from '@tanstack/react-query';
|
|
9
|
-
import { initFederation, type StudioRegistryV2 } from '@kb-labs/studio-federation';
|
|
10
|
-
import { studioConfig } from '../config/studio.config';
|
|
11
|
-
|
|
12
|
-
interface RegistryV2ContextValue {
|
|
13
|
-
registry: StudioRegistryV2;
|
|
14
|
-
loading: boolean;
|
|
15
|
-
error: Error | null;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const EMPTY_REGISTRY: StudioRegistryV2 = {
|
|
19
|
-
schema: 'kb.studio/2',
|
|
20
|
-
schemaVersion: 2,
|
|
21
|
-
generatedAt: new Date().toISOString(),
|
|
22
|
-
plugins: [],
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const RegistryV2Context = React.createContext<RegistryV2ContextValue>({
|
|
26
|
-
registry: EMPTY_REGISTRY,
|
|
27
|
-
loading: true,
|
|
28
|
-
error: null,
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
async function fetchRegistryV2(apiBaseUrl: string, token?: string): Promise<StudioRegistryV2> {
|
|
32
|
-
const headers: Record<string, string> = { Accept: 'application/json' };
|
|
33
|
-
if (token) {headers['Authorization'] = `Bearer ${token}`;}
|
|
34
|
-
|
|
35
|
-
const res = await fetch(`${apiBaseUrl}/studio/registry`, { headers });
|
|
36
|
-
if (!res.ok) {
|
|
37
|
-
throw new Error(`Failed to fetch studio registry: ${res.status} ${res.statusText}`);
|
|
38
|
-
}
|
|
39
|
-
const json = await res.json() as { ok?: boolean; data?: StudioRegistryV2 } | StudioRegistryV2;
|
|
40
|
-
// REST API wraps response in { ok, data, meta } envelope
|
|
41
|
-
if ('data' in json && json.data) {
|
|
42
|
-
return json.data as StudioRegistryV2;
|
|
43
|
-
}
|
|
44
|
-
return json as StudioRegistryV2;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function RegistryV2Provider({
|
|
48
|
-
children,
|
|
49
|
-
apiBaseUrl = '/api/v1',
|
|
50
|
-
}: {
|
|
51
|
-
children: React.ReactNode;
|
|
52
|
-
apiBaseUrl?: string;
|
|
53
|
-
}) {
|
|
54
|
-
const { data, isLoading, error } = useQuery({
|
|
55
|
-
queryKey: ['studio-registry-v2', apiBaseUrl],
|
|
56
|
-
queryFn: () => fetchRegistryV2(apiBaseUrl, studioConfig.gatewayToken),
|
|
57
|
-
staleTime: 10_000,
|
|
58
|
-
gcTime: 60_000,
|
|
59
|
-
retry: 2,
|
|
60
|
-
retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 10_000),
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
const federationInitialized = React.useRef(false);
|
|
64
|
-
|
|
65
|
-
// Initialize Module Federation once when registry is first loaded.
|
|
66
|
-
// Cache invalidation is handled lazily at navigation time via syncRemoteEntry()
|
|
67
|
-
// inside loadPageComponent — so the running page is never disrupted mid-session.
|
|
68
|
-
React.useEffect(() => {
|
|
69
|
-
if (data && data.plugins.length > 0 && !federationInitialized.current) {
|
|
70
|
-
initFederation(data.plugins);
|
|
71
|
-
federationInitialized.current = true;
|
|
72
|
-
}
|
|
73
|
-
}, [data]);
|
|
74
|
-
|
|
75
|
-
const value = React.useMemo<RegistryV2ContextValue>(
|
|
76
|
-
() => ({
|
|
77
|
-
registry: data ?? EMPTY_REGISTRY,
|
|
78
|
-
loading: isLoading,
|
|
79
|
-
error: error instanceof Error ? error : null,
|
|
80
|
-
}),
|
|
81
|
-
[data, isLoading, error],
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
return (
|
|
85
|
-
<RegistryV2Context.Provider value={value}>
|
|
86
|
-
{children}
|
|
87
|
-
</RegistryV2Context.Provider>
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export function useRegistryV2(): RegistryV2ContextValue {
|
|
92
|
-
return React.useContext(RegistryV2Context);
|
|
93
|
-
}
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import type { FeatureId } from '@/config/feature-flags';
|
|
3
|
-
|
|
4
|
-
export interface NavigationCategory {
|
|
5
|
-
id: string;
|
|
6
|
-
label: string;
|
|
7
|
-
order: number;
|
|
8
|
-
itemKeys: string[];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface UserSettings {
|
|
12
|
-
appearance: {
|
|
13
|
-
theme: 'light' | 'dark' | 'auto';
|
|
14
|
-
compactMode: boolean;
|
|
15
|
-
fontSize: 'small' | 'medium' | 'large';
|
|
16
|
-
pageTransition: 'none' | 'fade' | 'slide';
|
|
17
|
-
};
|
|
18
|
-
experimental: {
|
|
19
|
-
/** User-enabled feature flags */
|
|
20
|
-
enabledFeatures: FeatureId[];
|
|
21
|
-
};
|
|
22
|
-
navigation: {
|
|
23
|
-
categories: NavigationCategory[];
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const DEFAULT_SETTINGS: UserSettings = {
|
|
28
|
-
appearance: {
|
|
29
|
-
theme: 'light',
|
|
30
|
-
compactMode: false,
|
|
31
|
-
fontSize: 'medium',
|
|
32
|
-
pageTransition: 'none',
|
|
33
|
-
},
|
|
34
|
-
experimental: {
|
|
35
|
-
enabledFeatures: [],
|
|
36
|
-
},
|
|
37
|
-
navigation: {
|
|
38
|
-
categories: [],
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
interface SettingsContextValue {
|
|
43
|
-
settings: UserSettings;
|
|
44
|
-
updateSettings: (updates: Partial<UserSettings>) => void;
|
|
45
|
-
resetSettings: () => void;
|
|
46
|
-
exportSettings: () => void;
|
|
47
|
-
importSettings: (jsonString: string) => void;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const SettingsContext = React.createContext<SettingsContextValue | undefined>(undefined);
|
|
51
|
-
|
|
52
|
-
const STORAGE_KEY = 'kb-studio-settings';
|
|
53
|
-
|
|
54
|
-
export function SettingsProvider({ children }: { children: React.ReactNode }) {
|
|
55
|
-
const [settings, setSettings] = React.useState<UserSettings>(() => {
|
|
56
|
-
try {
|
|
57
|
-
const stored = localStorage.getItem(STORAGE_KEY);
|
|
58
|
-
if (stored) {
|
|
59
|
-
const parsed = JSON.parse(stored);
|
|
60
|
-
// Deep merge to ensure new fields are properly initialized
|
|
61
|
-
return {
|
|
62
|
-
appearance: { ...DEFAULT_SETTINGS.appearance, ...(parsed.appearance || {}) },
|
|
63
|
-
experimental: { ...DEFAULT_SETTINGS.experimental, ...(parsed.experimental || {}) },
|
|
64
|
-
navigation: { ...DEFAULT_SETTINGS.navigation, ...(parsed.navigation || {}) },
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
} catch (error) {
|
|
68
|
-
console.error('Failed to load settings:', error);
|
|
69
|
-
}
|
|
70
|
-
return DEFAULT_SETTINGS;
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// Save to localStorage whenever settings change
|
|
74
|
-
React.useEffect(() => {
|
|
75
|
-
try {
|
|
76
|
-
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
|
|
77
|
-
} catch (error) {
|
|
78
|
-
console.error('Failed to save settings:', error);
|
|
79
|
-
}
|
|
80
|
-
}, [settings]);
|
|
81
|
-
|
|
82
|
-
// Apply settings to document
|
|
83
|
-
React.useEffect(() => {
|
|
84
|
-
const { compactMode, fontSize } = settings.appearance;
|
|
85
|
-
|
|
86
|
-
// Apply compact mode class
|
|
87
|
-
if (compactMode) {
|
|
88
|
-
document.documentElement.classList.add('compact-mode');
|
|
89
|
-
} else {
|
|
90
|
-
document.documentElement.classList.remove('compact-mode');
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Apply font size class
|
|
94
|
-
document.documentElement.classList.remove('font-small', 'font-medium', 'font-large');
|
|
95
|
-
document.documentElement.classList.add(`font-${fontSize}`);
|
|
96
|
-
}, [settings]);
|
|
97
|
-
|
|
98
|
-
const updateSettings = React.useCallback((updates: Partial<UserSettings>) => {
|
|
99
|
-
setSettings((prev) => ({
|
|
100
|
-
...prev,
|
|
101
|
-
...updates,
|
|
102
|
-
appearance: {
|
|
103
|
-
...prev.appearance,
|
|
104
|
-
...(updates.appearance || {}),
|
|
105
|
-
},
|
|
106
|
-
experimental: {
|
|
107
|
-
...prev.experimental,
|
|
108
|
-
...(updates.experimental || {}),
|
|
109
|
-
},
|
|
110
|
-
navigation: {
|
|
111
|
-
...prev.navigation,
|
|
112
|
-
...(updates.navigation || {}),
|
|
113
|
-
},
|
|
114
|
-
}));
|
|
115
|
-
}, []);
|
|
116
|
-
|
|
117
|
-
const resetSettings = React.useCallback(() => {
|
|
118
|
-
setSettings(DEFAULT_SETTINGS);
|
|
119
|
-
localStorage.removeItem(STORAGE_KEY);
|
|
120
|
-
}, []);
|
|
121
|
-
|
|
122
|
-
const exportSettings = React.useCallback(() => {
|
|
123
|
-
const json = JSON.stringify(settings, null, 2);
|
|
124
|
-
const blob = new Blob([json], { type: 'application/json' });
|
|
125
|
-
const url = URL.createObjectURL(blob);
|
|
126
|
-
const a = document.createElement('a');
|
|
127
|
-
a.href = url;
|
|
128
|
-
a.download = `kb-studio-settings-${new Date().toISOString().split('T')[0]}.json`;
|
|
129
|
-
document.body.appendChild(a);
|
|
130
|
-
a.click();
|
|
131
|
-
document.body.removeChild(a);
|
|
132
|
-
URL.revokeObjectURL(url);
|
|
133
|
-
}, [settings]);
|
|
134
|
-
|
|
135
|
-
const importSettings = React.useCallback((jsonString: string) => {
|
|
136
|
-
try {
|
|
137
|
-
const parsed = JSON.parse(jsonString);
|
|
138
|
-
setSettings({ ...DEFAULT_SETTINGS, ...parsed });
|
|
139
|
-
} catch {
|
|
140
|
-
throw new Error('Invalid settings file');
|
|
141
|
-
}
|
|
142
|
-
}, []);
|
|
143
|
-
|
|
144
|
-
const value = React.useMemo(
|
|
145
|
-
() => ({ settings, updateSettings, resetSettings, exportSettings, importSettings }),
|
|
146
|
-
[settings, updateSettings, resetSettings, exportSettings, importSettings]
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
return <SettingsContext.Provider value={value}>{children}</SettingsContext.Provider>;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export function useSettings() {
|
|
153
|
-
const context = React.useContext(SettingsContext);
|
|
154
|
-
if (!context) {
|
|
155
|
-
throw new Error('useSettings must be used within SettingsProvider');
|
|
156
|
-
}
|
|
157
|
-
return context;
|
|
158
|
-
}
|
package/src/router.tsx
DELETED
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import { createBrowserRouter, Outlet, Link, useLocation, useNavigate } from 'react-router-dom';
|
|
3
|
-
import { useAuth } from './providers/auth-provider';
|
|
4
|
-
import { useRegistryV2 } from './providers/registry-v2-provider';
|
|
5
|
-
import { CommandPaletteProvider, useCommandPalette } from './providers/command-palette-provider';
|
|
6
|
-
import { useNotifications } from '@kb-labs/studio-data-client';
|
|
7
|
-
import { useDataSources } from './providers/data-sources-provider';
|
|
8
|
-
import { HealthBanner } from './components/health-banner';
|
|
9
|
-
import { NotFoundPage } from './pages/not-found-page';
|
|
10
|
-
import { LoginPage } from './pages/login-page';
|
|
11
|
-
import { PluginPageV2 } from './routes/plugin-page-v2';
|
|
12
|
-
import { PageTransition } from './components/page-transition';
|
|
13
|
-
import { createStudioLogger } from './utils/logger';
|
|
14
|
-
import { ErrorBoundary } from './components/error-boundary';
|
|
15
|
-
import { useSettings } from './providers/settings-provider';
|
|
16
|
-
import type { StudioRegistryV2 } from '@kb-labs/studio-federation';
|
|
17
|
-
import { NavigationItemsProvider } from './providers/navigation-items-provider';
|
|
18
|
-
import { applyNavigationCategories } from './utils/apply-navigation-categories';
|
|
19
|
-
import { AVAILABLE_ICONS, KBPageLayout, KBStatusBar, type NavigationItem, StatusBarItem, StatusBarPresets } from '@/components/ui';
|
|
20
|
-
|
|
21
|
-
// Module routes
|
|
22
|
-
import { dashboardRoutes, dashboardNavigation } from './modules/dashboard/routes';
|
|
23
|
-
// Workflow routes removed — now served via Module Federation from @kb-labs/workflow-cli studio plugin
|
|
24
|
-
import { pluginsRoutes, pluginsNavigation } from './modules/plugins/routes';
|
|
25
|
-
import { analyticsRoutes, analyticsNavigation } from './modules/analytics/routes';
|
|
26
|
-
import { observabilityRoutes, observabilityNavigation } from './modules/observability/routes';
|
|
27
|
-
import { settingsRoutes, settingsNavigation } from './modules/settings/routes';
|
|
28
|
-
import { authRoutes } from './modules/auth/routes';
|
|
29
|
-
|
|
30
|
-
type PluginNavRoute = {
|
|
31
|
-
key: string;
|
|
32
|
-
label: string;
|
|
33
|
-
path: string;
|
|
34
|
-
icon?: string;
|
|
35
|
-
order?: number;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
type PluginNavModel = {
|
|
39
|
-
pluginId: string;
|
|
40
|
-
displayName: string;
|
|
41
|
-
icon?: string;
|
|
42
|
-
routes: PluginNavRoute[];
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
// Helper function to render icon from available icons registry
|
|
46
|
-
function renderPluginIcon(iconName?: string): React.ReactElement | undefined {
|
|
47
|
-
if (!iconName) {return undefined;}
|
|
48
|
-
|
|
49
|
-
const IconComponent = AVAILABLE_ICONS[iconName];
|
|
50
|
-
|
|
51
|
-
if (!IconComponent) {
|
|
52
|
-
console.warn(`Icon "${iconName}" not available in registry`);
|
|
53
|
-
return undefined;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return React.createElement(IconComponent, { style: { fontSize: 16 } });
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// System module navigation items (order matters)
|
|
60
|
-
const systemNavigation: NavigationItem[] = [
|
|
61
|
-
dashboardNavigation,
|
|
62
|
-
pluginsNavigation,
|
|
63
|
-
analyticsNavigation,
|
|
64
|
-
observabilityNavigation,
|
|
65
|
-
];
|
|
66
|
-
|
|
67
|
-
function LayoutContent() {
|
|
68
|
-
const logger = React.useMemo(() => createStudioLogger('router'), []);
|
|
69
|
-
|
|
70
|
-
const auth = useAuth();
|
|
71
|
-
const location = useLocation();
|
|
72
|
-
const navigate = useNavigate();
|
|
73
|
-
const { registry, error } = useRegistryV2();
|
|
74
|
-
const commandPalette = useCommandPalette();
|
|
75
|
-
const sources = useDataSources();
|
|
76
|
-
const { settings } = useSettings();
|
|
77
|
-
|
|
78
|
-
// Notifications hook
|
|
79
|
-
const {
|
|
80
|
-
notifications,
|
|
81
|
-
unreadCount,
|
|
82
|
-
markAsRead,
|
|
83
|
-
markAllAsRead,
|
|
84
|
-
clearAll,
|
|
85
|
-
clearNotification,
|
|
86
|
-
} = useNotifications(sources.observability);
|
|
87
|
-
|
|
88
|
-
const handleLogout = React.useCallback(() => {
|
|
89
|
-
logger.info('User logout initiated');
|
|
90
|
-
localStorage.removeItem('studio-user-role');
|
|
91
|
-
navigate('/login');
|
|
92
|
-
}, [logger, navigate]);
|
|
93
|
-
|
|
94
|
-
const hasData = registry.plugins.length > 0;
|
|
95
|
-
|
|
96
|
-
// Build plugin nav from V2 registry menus
|
|
97
|
-
const pluginNavModel = React.useMemo<PluginNavModel[]>(() => {
|
|
98
|
-
if (!hasData) {return [];}
|
|
99
|
-
return buildPluginNavModel(registry);
|
|
100
|
-
}, [registry, hasData]);
|
|
101
|
-
|
|
102
|
-
// Build flat navigation items (system modules + plugins + settings)
|
|
103
|
-
const flatNavigationItems = React.useMemo<NavigationItem[]>(() => {
|
|
104
|
-
const items: NavigationItem[] = [...systemNavigation];
|
|
105
|
-
|
|
106
|
-
// Add plugin navigation items
|
|
107
|
-
for (const model of pluginNavModel) {
|
|
108
|
-
if (model.routes.length === 1) {
|
|
109
|
-
// Single page plugin — flat nav item (no submenu)
|
|
110
|
-
const route = model.routes[0]!;
|
|
111
|
-
items.push({
|
|
112
|
-
key: `plugin-${model.pluginId}`,
|
|
113
|
-
label: model.displayName,
|
|
114
|
-
icon: renderPluginIcon(model.icon),
|
|
115
|
-
path: route.path,
|
|
116
|
-
});
|
|
117
|
-
} else if (model.routes.length > 1) {
|
|
118
|
-
// Multi-page plugin — group with children
|
|
119
|
-
items.push({
|
|
120
|
-
key: `plugin-${model.pluginId}`,
|
|
121
|
-
label: model.displayName,
|
|
122
|
-
icon: renderPluginIcon(model.icon),
|
|
123
|
-
children: model.routes.map(route => ({
|
|
124
|
-
key: route.key,
|
|
125
|
-
label: route.label,
|
|
126
|
-
path: route.path,
|
|
127
|
-
icon: renderPluginIcon(route.icon),
|
|
128
|
-
})),
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Settings always at the bottom
|
|
134
|
-
items.push(settingsNavigation);
|
|
135
|
-
|
|
136
|
-
return items;
|
|
137
|
-
}, [pluginNavModel]);
|
|
138
|
-
|
|
139
|
-
// Available items for NavigationSettings UI (top-level items except settings)
|
|
140
|
-
const availableNavItems = React.useMemo(
|
|
141
|
-
() =>
|
|
142
|
-
flatNavigationItems
|
|
143
|
-
.filter(item => item.key !== 'settings')
|
|
144
|
-
.map(item => ({ key: item.key, label: item.label })),
|
|
145
|
-
[flatNavigationItems],
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
// Apply user's category configuration
|
|
149
|
-
const allNavigationItems = React.useMemo<NavigationItem[]>(
|
|
150
|
-
() => applyNavigationCategories(flatNavigationItems, settings.navigation.categories),
|
|
151
|
-
[flatNavigationItems, settings.navigation.categories],
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
React.useEffect(() => {
|
|
155
|
-
if (error) {
|
|
156
|
-
logger.error('Registry load failure in router', error);
|
|
157
|
-
}
|
|
158
|
-
}, [error, logger]);
|
|
159
|
-
|
|
160
|
-
return (
|
|
161
|
-
<NavigationItemsProvider items={availableNavItems}>
|
|
162
|
-
<KBPageLayout
|
|
163
|
-
headerProps={{
|
|
164
|
-
LinkComponent: Link as any,
|
|
165
|
-
onLogout: handleLogout,
|
|
166
|
-
userName: auth.role,
|
|
167
|
-
onSearchClick: () => commandPalette.open(),
|
|
168
|
-
// Notifications
|
|
169
|
-
notifications,
|
|
170
|
-
unreadNotificationsCount: unreadCount,
|
|
171
|
-
onMarkNotificationAsRead: markAsRead,
|
|
172
|
-
onMarkAllNotificationsAsRead: markAllAsRead,
|
|
173
|
-
onClearAllNotifications: clearAll,
|
|
174
|
-
onClearNotification: clearNotification,
|
|
175
|
-
onNotificationClick: (notification) => {
|
|
176
|
-
navigate(`/observability/logs/${notification.id}`);
|
|
177
|
-
},
|
|
178
|
-
}}
|
|
179
|
-
sidebarProps={{
|
|
180
|
-
items: allNavigationItems,
|
|
181
|
-
width: 240,
|
|
182
|
-
collapsedWidth: 80,
|
|
183
|
-
currentPath: location.pathname,
|
|
184
|
-
onNavigate: (path) => navigate(path),
|
|
185
|
-
}}
|
|
186
|
-
statusBar={
|
|
187
|
-
<KBStatusBar
|
|
188
|
-
leftItems={
|
|
189
|
-
<>
|
|
190
|
-
<StatusBarPresets.ApiConnection
|
|
191
|
-
connected={hasData}
|
|
192
|
-
onClick={() => navigate('/observability/system-events')}
|
|
193
|
-
/>
|
|
194
|
-
{hasData && (
|
|
195
|
-
<StatusBarItem
|
|
196
|
-
label={`${registry.plugins.length} extension${registry.plugins.length === 1 ? '' : 's'}`}
|
|
197
|
-
tooltip={`${registry.plugins.length} extensions registered`}
|
|
198
|
-
status="success"
|
|
199
|
-
onClick={() => navigate('/settings')}
|
|
200
|
-
clickable
|
|
201
|
-
/>
|
|
202
|
-
)}
|
|
203
|
-
</>
|
|
204
|
-
}
|
|
205
|
-
rightItems={
|
|
206
|
-
<>
|
|
207
|
-
<StatusBarPresets.User email={auth.role} onClick={() => navigate('/settings')} />
|
|
208
|
-
<StatusBarPresets.Version version="0.1.0" />
|
|
209
|
-
<StatusBarPresets.Help onClick={() => window.open('https://github.com/kb-labs', '_blank')} />
|
|
210
|
-
</>
|
|
211
|
-
}
|
|
212
|
-
/>
|
|
213
|
-
}
|
|
214
|
-
>
|
|
215
|
-
<HealthBanner />
|
|
216
|
-
<PageTransition>
|
|
217
|
-
<div style={{ paddingBottom: '20px' }}>
|
|
218
|
-
<Outlet />
|
|
219
|
-
</div>
|
|
220
|
-
</PageTransition>
|
|
221
|
-
</KBPageLayout>
|
|
222
|
-
</NavigationItemsProvider>
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
function Layout() {
|
|
227
|
-
return (
|
|
228
|
-
<CommandPaletteProvider>
|
|
229
|
-
<LayoutContent />
|
|
230
|
-
</CommandPaletteProvider>
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
export const router = createBrowserRouter([
|
|
235
|
-
{
|
|
236
|
-
path: '/login',
|
|
237
|
-
element: <LoginPage />,
|
|
238
|
-
errorElement: <ErrorBoundary />,
|
|
239
|
-
},
|
|
240
|
-
...authRoutes,
|
|
241
|
-
{
|
|
242
|
-
element: <Layout />,
|
|
243
|
-
errorElement: <ErrorBoundary />,
|
|
244
|
-
children: [
|
|
245
|
-
...dashboardRoutes,
|
|
246
|
-
...pluginsRoutes,
|
|
247
|
-
...analyticsRoutes,
|
|
248
|
-
...observabilityRoutes,
|
|
249
|
-
...settingsRoutes,
|
|
250
|
-
{
|
|
251
|
-
// V2 plugin pages (Module Federation remotes) — inside layout
|
|
252
|
-
path: '/p/*',
|
|
253
|
-
element: <PluginPageV2 />,
|
|
254
|
-
errorElement: <ErrorBoundary />,
|
|
255
|
-
},
|
|
256
|
-
{
|
|
257
|
-
path: '*',
|
|
258
|
-
element: <NotFoundPage />,
|
|
259
|
-
},
|
|
260
|
-
],
|
|
261
|
-
},
|
|
262
|
-
]);
|
|
263
|
-
|
|
264
|
-
function buildPluginNavModel(registry: StudioRegistryV2): PluginNavModel[] {
|
|
265
|
-
const groups = new Map<string, PluginNavModel>();
|
|
266
|
-
|
|
267
|
-
for (const plugin of registry.plugins) {
|
|
268
|
-
if (!plugin.menus || plugin.menus.length === 0) {continue;}
|
|
269
|
-
|
|
270
|
-
const parentMenu = plugin.menus.find(m => !m.parentId);
|
|
271
|
-
|
|
272
|
-
if (!groups.has(plugin.pluginId)) {
|
|
273
|
-
groups.set(plugin.pluginId, {
|
|
274
|
-
pluginId: plugin.pluginId,
|
|
275
|
-
displayName: parentMenu?.label ?? plugin.displayName ?? plugin.pluginId,
|
|
276
|
-
icon: parentMenu?.icon,
|
|
277
|
-
routes: [],
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const group = groups.get(plugin.pluginId)!;
|
|
282
|
-
|
|
283
|
-
for (const menu of plugin.menus) {
|
|
284
|
-
const targetPage = plugin.pages.find(p => p.id === menu.target);
|
|
285
|
-
const path = targetPage?.route ?? `/p/${menu.target}`;
|
|
286
|
-
|
|
287
|
-
group.routes.push({
|
|
288
|
-
key: menu.id,
|
|
289
|
-
label: menu.label,
|
|
290
|
-
path,
|
|
291
|
-
icon: menu.icon,
|
|
292
|
-
order: menu.order,
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
for (const group of groups.values()) {
|
|
298
|
-
group.routes.sort((a, b) => (a.order ?? 999) - (b.order ?? 999));
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
return Array.from(groups.values()).sort((a, b) => a.displayName.localeCompare(b.displayName));
|
|
302
|
-
}
|
package/src/routes/helpers.tsx
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module @kb-labs/studio-app/routes/helpers
|
|
3
|
-
* Common helpers for routing
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import * as React from 'react';
|
|
7
|
-
import { AVAILABLE_ICONS } from '@/components/ui';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Render icon from available icons registry
|
|
11
|
-
*/
|
|
12
|
-
export function renderIcon(iconName?: string): React.ReactElement | undefined {
|
|
13
|
-
if (!iconName) {
|
|
14
|
-
return undefined;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const IconComponent = AVAILABLE_ICONS[iconName];
|
|
18
|
-
|
|
19
|
-
if (!IconComponent) {
|
|
20
|
-
console.warn(`Icon "${iconName}" not available in registry`);
|
|
21
|
-
return undefined;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return React.createElement(IconComponent, { style: { fontSize: 16 } });
|
|
25
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dynamic plugin page resolver.
|
|
3
|
-
*
|
|
4
|
-
* Studio router mounts this component at /p/*. Plugin page routes are declared
|
|
5
|
-
* as absolute paths like /p/workflows/runs/:runId. We render them as a <Routes>
|
|
6
|
-
* tree with relative paths (stripping the /p/ prefix) so React Router resolves
|
|
7
|
-
* them correctly within the /p/* wildcard and sets useParams() for plugin pages.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import * as React from 'react';
|
|
11
|
-
import { Routes, Route } from 'react-router-dom';
|
|
12
|
-
import { PageContainer } from '@kb-labs/studio-federation';
|
|
13
|
-
import { useRegistryV2 } from '../providers/registry-v2-provider';
|
|
14
|
-
import { NotFoundPage } from '../pages/not-found-page';
|
|
15
|
-
|
|
16
|
-
const P_PREFIX = /^\/p\//;
|
|
17
|
-
|
|
18
|
-
export function PluginPageV2() {
|
|
19
|
-
const { registry, loading } = useRegistryV2();
|
|
20
|
-
|
|
21
|
-
if (loading) {return null;}
|
|
22
|
-
|
|
23
|
-
const allPages = registry.plugins.flatMap((plugin) =>
|
|
24
|
-
plugin.pages.map((page) => ({ plugin, page })),
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<Routes>
|
|
29
|
-
{allPages.map(({ plugin, page }) => {
|
|
30
|
-
// Strip /p/ prefix → relative path for <Routes> inside /p/*
|
|
31
|
-
const relativePath = page.route.replace(P_PREFIX, '');
|
|
32
|
-
return (
|
|
33
|
-
<Route
|
|
34
|
-
key={`${plugin.remoteName}::${page.entry}`}
|
|
35
|
-
path={relativePath}
|
|
36
|
-
element={
|
|
37
|
-
<PageContainer
|
|
38
|
-
remoteName={plugin.remoteName}
|
|
39
|
-
entry={page.entry}
|
|
40
|
-
remoteEntryUrl={plugin.remoteEntryUrl}
|
|
41
|
-
pageId={page.id}
|
|
42
|
-
pluginId={plugin.pluginId}
|
|
43
|
-
permissions={page.permissions}
|
|
44
|
-
/>
|
|
45
|
-
}
|
|
46
|
-
/>
|
|
47
|
-
);
|
|
48
|
-
})}
|
|
49
|
-
<Route path="*" element={<NotFoundPage />} />
|
|
50
|
-
</Routes>
|
|
51
|
-
);
|
|
52
|
-
}
|
package/src/routes/types.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module @kb-labs/studio-app/routes/types
|
|
3
|
-
* Common types for modular routing system
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { RouteObject } from 'react-router-dom';
|
|
7
|
-
import type { NavigationItem } from '@/components/ui';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Module routes configuration
|
|
11
|
-
* Each module exports this to register its routes
|
|
12
|
-
*/
|
|
13
|
-
export interface ModuleRoutes {
|
|
14
|
-
/** Routes for react-router */
|
|
15
|
-
routes: RouteObject[];
|
|
16
|
-
/** Navigation items for sidebar */
|
|
17
|
-
navigation?: NavigationItem[];
|
|
18
|
-
}
|