@kb-labs/studio-app 0.7.0 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.html +1 -0
- package/dist/main.1d68e922.js +522 -0
- package/dist/main.1d68e922.js.map +1 -0
- package/dist/main.80b70197.css +2 -0
- package/dist/main.80b70197.css.map +1 -0
- package/dist/mf-manifest.json +241 -0
- package/dist/mf-stats.json +271 -0
- package/package.json +56 -39
- package/server.js +68 -0
- package/.env.example +0 -10
- package/UI-KIT-COMPONENTS.md +0 -783
- package/UI-KIT-PLAN.md +0 -326
- package/eslint-rules/no-direct-antd-import.js +0 -151
- package/eslint.config.ts +0 -27
- package/index.html +0 -17
- package/postcss.config.js +0 -7
- package/public/favicon.ico +0 -0
- package/rspack.config.mjs +0 -118
- package/src/App.tsx +0 -46
- package/src/components/devtools/devtools-log-feed.module.css +0 -165
- package/src/components/devtools/devtools-log-feed.tsx +0 -300
- package/src/components/devtools/devtools-panel.module.css +0 -121
- package/src/components/devtools/devtools-panel.tsx +0 -115
- package/src/components/empty-state.tsx +0 -28
- package/src/components/error-boundary.tsx +0 -210
- package/src/components/health-banner.module.css +0 -103
- package/src/components/health-banner.tsx +0 -134
- package/src/components/health-indicator.tsx +0 -12
- package/src/components/page-header.tsx +0 -49
- package/src/components/page-transition.tsx +0 -24
- package/src/components/role-switcher.tsx +0 -148
- package/src/components/run-badge.tsx +0 -13
- package/src/components/ui/icons.tsx +0 -509
- package/src/components/ui/index.ts +0 -25
- package/src/components/ui/kb-config-provider.tsx +0 -130
- package/src/components/ui/kb-header.module.css +0 -51
- package/src/components/ui/kb-header.tsx +0 -112
- package/src/components/ui/kb-notification-bell.module.css +0 -103
- package/src/components/ui/kb-notification-bell.tsx +0 -199
- package/src/components/ui/kb-page-layout.tsx +0 -66
- package/src/components/ui/kb-quick-search.module.css +0 -86
- package/src/components/ui/kb-quick-search.tsx +0 -196
- package/src/components/ui/kb-sidebar.module.css +0 -47
- package/src/components/ui/kb-sidebar.tsx +0 -196
- package/src/components/ui/kb-stat-card.module.css +0 -21
- package/src/components/ui/kb-stat-card.tsx +0 -22
- package/src/components/ui/kb-stat-grid.tsx +0 -22
- package/src/components/ui/kb-status-bar.module.css +0 -49
- package/src/components/ui/kb-status-bar.tsx +0 -145
- package/src/components/ui/kb-system-health-indicator.tsx +0 -229
- package/src/components/ui/kb-theme-toggle.tsx +0 -42
- package/src/components/ui/theme-adapter.ts +0 -812
- package/src/components/ui/theme-transition-overlay.tsx +0 -70
- package/src/components/ui/utils.ts +0 -6
- package/src/components/widget-error-boundary.tsx +0 -172
- package/src/config/env.ts +0 -5
- package/src/config/feature-flags.ts +0 -482
- package/src/config/studio.config.schema.ts +0 -88
- package/src/config/studio.config.ts +0 -84
- package/src/env.d.ts +0 -23
- package/src/hooks/use-command-palette.ts +0 -6
- package/src/hooks/use-devtools-store.ts +0 -25
- package/src/hooks/use-feature-flags.ts +0 -101
- package/src/index.css +0 -10
- package/src/layouts/dashboard-grid.tsx +0 -122
- package/src/layouts/index.ts +0 -12
- package/src/layouts/two-pane.tsx +0 -134
- package/src/main.tsx +0 -15
- package/src/modules/analytics/components/analytics-daily-stats-chart.tsx +0 -257
- package/src/modules/analytics/components/analytics-date-range-picker.tsx +0 -121
- package/src/modules/analytics/pages/analytics-cache-page.tsx +0 -285
- package/src/modules/analytics/pages/analytics-embeddings-page.tsx +0 -274
- package/src/modules/analytics/pages/analytics-events-page.tsx +0 -425
- package/src/modules/analytics/pages/analytics-llm-page.tsx +0 -434
- package/src/modules/analytics/pages/analytics-overview-page.tsx +0 -336
- package/src/modules/analytics/pages/analytics-page.tsx +0 -20
- package/src/modules/analytics/pages/analytics-storage-page.tsx +0 -344
- package/src/modules/analytics/pages/analytics-vectorstore-page.tsx +0 -279
- package/src/modules/analytics/routes/index.tsx +0 -133
- package/src/modules/analytics/utils/formatters.test.ts +0 -148
- package/src/modules/analytics/utils/formatters.ts +0 -100
- package/src/modules/auth/pages/login-page.module.css +0 -113
- package/src/modules/auth/pages/login-page.tsx +0 -80
- package/src/modules/auth/routes/index.tsx +0 -24
- package/src/modules/dashboard/components/ActivityTimelineWidget.tsx +0 -199
- package/src/modules/dashboard/components/AiInsightsWidget.tsx +0 -358
- package/src/modules/dashboard/components/AnimatedMetricCard.css +0 -88
- package/src/modules/dashboard/components/AnimatedMetricCard.tsx +0 -125
- package/src/modules/dashboard/components/HeroMetricCard.css +0 -114
- package/src/modules/dashboard/components/HeroMetricCard.tsx +0 -152
- package/src/modules/dashboard/components/HeroMetricsWidget.tsx +0 -146
- package/src/modules/dashboard/components/SystemResourcesWidget.tsx +0 -197
- package/src/modules/dashboard/components/dashboard-empty.tsx +0 -22
- package/src/modules/dashboard/components/dashboard-kpis-widget.tsx +0 -59
- package/src/modules/dashboard/components/dashboard-kpis.tsx +0 -13
- package/src/modules/dashboard/components/dashboard-latency-percentiles-widget.tsx +0 -85
- package/src/modules/dashboard/components/dashboard-metrics-widget.tsx +0 -78
- package/src/modules/dashboard/components/dashboard-plugins-widget.tsx +0 -67
- package/src/modules/dashboard/components/dashboard-requests-widget.tsx +0 -62
- package/src/modules/dashboard/layouts/dashboard-layout.tsx +0 -16
- package/src/modules/dashboard/pages/ai-insights-page.tsx +0 -563
- package/src/modules/dashboard/pages/dashboard-animations.css +0 -317
- package/src/modules/dashboard/pages/dashboard-page.tsx +0 -13
- package/src/modules/dashboard/routes/index.tsx +0 -70
- package/src/modules/observability/pages/devkit-page.tsx +0 -263
- package/src/modules/observability/pages/incident-detail-page.tsx +0 -571
- package/src/modules/observability/pages/incidents-page.tsx +0 -370
- package/src/modules/observability/pages/log-detail-page.tsx +0 -508
- package/src/modules/observability/pages/logs-page.tsx +0 -1129
- package/src/modules/observability/pages/prometheus-metrics-page.tsx +0 -486
- package/src/modules/observability/pages/state-broker-page.tsx +0 -229
- package/src/modules/observability/pages/system-events-page.tsx +0 -255
- package/src/modules/observability/routes/index.tsx +0 -133
- package/src/modules/plugins/components/marketplace-card.tsx +0 -153
- package/src/modules/plugins/components/plugin-ai-assistant-modal.tsx +0 -374
- package/src/modules/plugins/components/plugin-card.tsx +0 -136
- package/src/modules/plugins/components/plugin-detail/capabilities-summary.tsx +0 -101
- package/src/modules/plugins/components/plugin-detail/cli-commands-table.tsx +0 -101
- package/src/modules/plugins/components/plugin-detail/index.ts +0 -7
- package/src/modules/plugins/components/plugin-detail/jobs-table.tsx +0 -64
- package/src/modules/plugins/components/plugin-detail/permissions/env-permissions.tsx +0 -30
- package/src/modules/plugins/components/plugin-detail/permissions/filesystem-permissions.tsx +0 -68
- package/src/modules/plugins/components/plugin-detail/permissions/index.ts +0 -6
- package/src/modules/plugins/components/plugin-detail/permissions/network-permissions.tsx +0 -43
- package/src/modules/plugins/components/plugin-detail/permissions/platform-services.tsx +0 -242
- package/src/modules/plugins/components/plugin-detail/permissions/resource-quotas.tsx +0 -96
- package/src/modules/plugins/components/plugin-detail/permissions/shell-permissions.tsx +0 -55
- package/src/modules/plugins/components/plugin-detail/permissions-view.tsx +0 -47
- package/src/modules/plugins/components/plugin-detail/plugin-header.tsx +0 -169
- package/src/modules/plugins/components/plugin-detail/rest-routes-table.tsx +0 -159
- package/src/modules/plugins/components/plugin-detail/workflows-table.tsx +0 -40
- package/src/modules/plugins/components/plugin-grid.tsx +0 -20
- package/src/modules/plugins/components/plugin-table.tsx +0 -178
- package/src/modules/plugins/pages/marketplace-page.tsx +0 -200
- package/src/modules/plugins/pages/plugin-detail-page.tsx +0 -1145
- package/src/modules/plugins/routes/index.tsx +0 -48
- package/src/modules/plugins/utils/index.ts +0 -1
- package/src/modules/plugins/utils/schema-helpers.ts +0 -21
- package/src/modules/settings/components/api-routes-viewer.tsx +0 -415
- package/src/modules/settings/components/appearance-settings.tsx +0 -200
- package/src/modules/settings/components/configuration-settings.tsx +0 -321
- package/src/modules/settings/components/data-privacy-settings.tsx +0 -111
- package/src/modules/settings/components/experimental-settings.tsx +0 -243
- package/src/modules/settings/components/navigation-settings.tsx +0 -198
- package/src/modules/settings/components/notifications-settings.tsx +0 -40
- package/src/modules/settings/pages/settings-page.tsx +0 -298
- package/src/modules/settings/routes/index.tsx +0 -47
- package/src/pages/README-assistant.md +0 -91
- package/src/pages/login-page.tsx +0 -177
- package/src/pages/not-found-page.tsx +0 -85
- package/src/providers/auth-provider.tsx +0 -40
- package/src/providers/command-palette-provider.tsx +0 -193
- package/src/providers/data-sources-provider.tsx +0 -27
- package/src/providers/devtools-provider.tsx +0 -72
- package/src/providers/navigation-items-provider.tsx +0 -38
- package/src/providers/registry-v2-provider.tsx +0 -93
- package/src/providers/settings-provider.tsx +0 -158
- package/src/router.tsx +0 -302
- package/src/routes/helpers.tsx +0 -25
- package/src/routes/plugin-page-v2.tsx +0 -52
- package/src/routes/types.ts +0 -18
- package/src/styles/antd-overrides.css +0 -211
- package/src/styles/base.css +0 -190
- package/src/styles/settings.css +0 -75
- package/src/styles/variables.css +0 -94
- package/src/utils/analytics.ts +0 -183
- package/src/utils/apply-navigation-categories.ts +0 -83
- package/src/utils/commands.ts +0 -252
- package/src/utils/export.ts +0 -174
- package/src/utils/format-date.ts +0 -166
- package/src/utils/logger.ts +0 -110
- package/tailwind.config.ts +0 -13
- package/tsconfig.json +0 -13
- package/vitest-setup.ts +0 -1
- /package/{public → dist}/favicon.svg +0 -0
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module @kb-labs/studio-app/modules/plugins/routes
|
|
3
|
-
* Plugins module routing configuration
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { RouteObject } from 'react-router-dom';
|
|
7
|
-
import { PluginsPage } from '../pages/marketplace-page';
|
|
8
|
-
import { PluginDetailPage } from '../pages/plugin-detail-page';
|
|
9
|
-
import { ErrorBoundary } from '../../../components/error-boundary';
|
|
10
|
-
import { renderIcon } from '../../../routes/helpers';
|
|
11
|
-
import type { NavigationItem } from '@/components/ui';
|
|
12
|
-
|
|
13
|
-
// Route paths
|
|
14
|
-
const PATHS = {
|
|
15
|
-
ROOT: '/marketplace',
|
|
16
|
-
DETAIL: '/marketplace/:pluginId',
|
|
17
|
-
} as const;
|
|
18
|
-
|
|
19
|
-
// Route keys
|
|
20
|
-
const KEYS = {
|
|
21
|
-
MODULE: 'plugins',
|
|
22
|
-
} as const;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Plugins routes
|
|
26
|
-
*/
|
|
27
|
-
export const pluginsRoutes: RouteObject[] = [
|
|
28
|
-
{
|
|
29
|
-
path: PATHS.ROOT,
|
|
30
|
-
element: <PluginsPage />,
|
|
31
|
-
errorElement: <ErrorBoundary />,
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
path: PATHS.DETAIL,
|
|
35
|
-
element: <PluginDetailPage />,
|
|
36
|
-
errorElement: <ErrorBoundary />,
|
|
37
|
-
},
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Plugins navigation item for sidebar
|
|
42
|
-
*/
|
|
43
|
-
export const pluginsNavigation: NavigationItem = {
|
|
44
|
-
key: KEYS.MODULE,
|
|
45
|
-
label: 'Marketplace',
|
|
46
|
-
icon: renderIcon('AppstoreOutlined'),
|
|
47
|
-
path: PATHS.ROOT,
|
|
48
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { renderSchema } from './schema-helpers';
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Helper to safely render schema objects
|
|
3
|
-
*/
|
|
4
|
-
export function renderSchema(schema: any): string {
|
|
5
|
-
if (!schema) {
|
|
6
|
-
return '—';
|
|
7
|
-
}
|
|
8
|
-
if (typeof schema === 'string') {
|
|
9
|
-
return schema;
|
|
10
|
-
}
|
|
11
|
-
if (typeof schema === 'object') {
|
|
12
|
-
if ('$ref' in schema) {
|
|
13
|
-
return schema.$ref;
|
|
14
|
-
}
|
|
15
|
-
if ('zod' in schema) {
|
|
16
|
-
return schema.zod;
|
|
17
|
-
}
|
|
18
|
-
return JSON.stringify(schema);
|
|
19
|
-
}
|
|
20
|
-
return String(schema);
|
|
21
|
-
}
|
|
@@ -1,415 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import { useState, useEffect, useMemo, useCallback } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
UIInput,
|
|
5
|
-
UITag,
|
|
6
|
-
UISpace,
|
|
7
|
-
UIButton,
|
|
8
|
-
UITypographyText,
|
|
9
|
-
UITitle,
|
|
10
|
-
UITypographyParagraph,
|
|
11
|
-
UIAlert,
|
|
12
|
-
UISpin,
|
|
13
|
-
UIEmptyState,
|
|
14
|
-
UITooltip,
|
|
15
|
-
UISelect,
|
|
16
|
-
UITree,
|
|
17
|
-
UISegmented,
|
|
18
|
-
UIIcon,
|
|
19
|
-
} from '@kb-labs/studio-ui-kit';
|
|
20
|
-
import type { DataNode } from 'antd/es/tree';
|
|
21
|
-
import { useDataSources } from '../../../providers/data-sources-provider';
|
|
22
|
-
import type { RoutesResponse, RouteInfo } from '@kb-labs/studio-data-client';
|
|
23
|
-
|
|
24
|
-
const METHOD_COLORS: Record<string, string> = {
|
|
25
|
-
GET: 'green',
|
|
26
|
-
POST: 'blue',
|
|
27
|
-
PUT: 'orange',
|
|
28
|
-
PATCH: 'purple',
|
|
29
|
-
DELETE: 'red',
|
|
30
|
-
HEAD: 'cyan',
|
|
31
|
-
OPTIONS: 'default',
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
type ViewMode = 'tree' | 'list';
|
|
35
|
-
|
|
36
|
-
interface RouteTreeNode {
|
|
37
|
-
segment: string;
|
|
38
|
-
fullPath: string;
|
|
39
|
-
routes: RouteInfo[];
|
|
40
|
-
children: Map<string, RouteTreeNode>;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function buildRouteTree(routes: RouteInfo[]): RouteTreeNode {
|
|
44
|
-
const root: RouteTreeNode = {
|
|
45
|
-
segment: '',
|
|
46
|
-
fullPath: '',
|
|
47
|
-
routes: [],
|
|
48
|
-
children: new Map(),
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
for (const route of routes) {
|
|
52
|
-
// Split URL into segments, e.g., /api/v1/plugins/:id -> ['api', 'v1', 'plugins', ':id']
|
|
53
|
-
const segments = route.url.split('/').filter(Boolean);
|
|
54
|
-
let current = root;
|
|
55
|
-
let pathSoFar = '';
|
|
56
|
-
|
|
57
|
-
for (const segment of segments) {
|
|
58
|
-
pathSoFar += '/' + segment;
|
|
59
|
-
|
|
60
|
-
if (!current.children.has(segment)) {
|
|
61
|
-
current.children.set(segment, {
|
|
62
|
-
segment,
|
|
63
|
-
fullPath: pathSoFar,
|
|
64
|
-
routes: [],
|
|
65
|
-
children: new Map(),
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
current = current.children.get(segment)!;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Add route to the leaf node
|
|
72
|
-
current.routes.push(route);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return root;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function treeNodeToAntdNode(
|
|
79
|
-
node: RouteTreeNode,
|
|
80
|
-
baseUrl: string,
|
|
81
|
-
copiedUrl: string | null,
|
|
82
|
-
onCopy: (url: string) => void
|
|
83
|
-
): DataNode[] {
|
|
84
|
-
const results: DataNode[] = [];
|
|
85
|
-
|
|
86
|
-
// Sort children alphabetically
|
|
87
|
-
const sortedChildren = Array.from(node.children.entries()).sort((a, b) =>
|
|
88
|
-
a[0].localeCompare(b[0])
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
for (const [segment, child] of sortedChildren) {
|
|
92
|
-
const hasChildren = child.children.size > 0;
|
|
93
|
-
const hasRoutes = child.routes.length > 0;
|
|
94
|
-
|
|
95
|
-
// Determine if this is a parameter segment
|
|
96
|
-
const isParam = segment.startsWith(':') || segment.startsWith('*');
|
|
97
|
-
|
|
98
|
-
const childNodes = treeNodeToAntdNode(child, baseUrl, copiedUrl, onCopy);
|
|
99
|
-
|
|
100
|
-
// Create route entries for this node
|
|
101
|
-
const routeNodes: DataNode[] = child.routes.map((route) => {
|
|
102
|
-
const fullUrl = `${baseUrl}${route.url}`;
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
key: `${route.method}-${route.url}`,
|
|
106
|
-
title: (
|
|
107
|
-
<UISpace size="small">
|
|
108
|
-
{route.method.split(',').map((m) => (
|
|
109
|
-
<UITag key={m} color={METHOD_COLORS[m.trim()] || 'default'} style={{ margin: 0 }}>
|
|
110
|
-
{m.trim()}
|
|
111
|
-
</UITag>
|
|
112
|
-
))}
|
|
113
|
-
<UITypographyText code style={{ fontSize: 12 }}>
|
|
114
|
-
{route.url}
|
|
115
|
-
</UITypographyText>
|
|
116
|
-
<UITooltip title={copiedUrl === fullUrl ? 'Copied!' : 'Copy full URL'}>
|
|
117
|
-
<UIButton
|
|
118
|
-
variant="text"
|
|
119
|
-
size="small"
|
|
120
|
-
icon={
|
|
121
|
-
copiedUrl === fullUrl ? (
|
|
122
|
-
<UIIcon name="CheckOutlined" style={{ color: '#52c41a' }} />
|
|
123
|
-
) : (
|
|
124
|
-
<UIIcon name="CopyOutlined" />
|
|
125
|
-
)
|
|
126
|
-
}
|
|
127
|
-
onClick={(e) => {
|
|
128
|
-
e.stopPropagation();
|
|
129
|
-
onCopy(fullUrl);
|
|
130
|
-
}}
|
|
131
|
-
/>
|
|
132
|
-
</UITooltip>
|
|
133
|
-
</UISpace>
|
|
134
|
-
),
|
|
135
|
-
isLeaf: true,
|
|
136
|
-
icon: <UIIcon name="FileOutlined" />,
|
|
137
|
-
};
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
if (hasChildren || hasRoutes) {
|
|
141
|
-
results.push({
|
|
142
|
-
key: child.fullPath,
|
|
143
|
-
title: (
|
|
144
|
-
<UISpace size="small">
|
|
145
|
-
<UITypographyText strong={hasChildren} type={isParam ? 'warning' : undefined}>
|
|
146
|
-
{segment}
|
|
147
|
-
</UITypographyText>
|
|
148
|
-
{hasRoutes && !hasChildren && (
|
|
149
|
-
<UITypographyText type="secondary" style={{ fontSize: 11 }}>
|
|
150
|
-
({child.routes.length})
|
|
151
|
-
</UITypographyText>
|
|
152
|
-
)}
|
|
153
|
-
</UISpace>
|
|
154
|
-
),
|
|
155
|
-
icon: hasChildren ? <UIIcon name="FolderOutlined" /> : undefined,
|
|
156
|
-
children: [...routeNodes, ...childNodes],
|
|
157
|
-
isLeaf: !hasChildren && routeNodes.length === 0,
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return results;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
export function ApiRoutesViewer() {
|
|
166
|
-
const { system } = useDataSources();
|
|
167
|
-
const [loading, setLoading] = useState(false);
|
|
168
|
-
const [error, setError] = useState<string | null>(null);
|
|
169
|
-
const [data, setData] = useState<RoutesResponse | null>(null);
|
|
170
|
-
const [searchQuery, setSearchQuery] = useState('');
|
|
171
|
-
const [methodFilter, setMethodFilter] = useState<string | null>(null);
|
|
172
|
-
const [copiedUrl, setCopiedUrl] = useState<string | null>(null);
|
|
173
|
-
const [viewMode, setViewMode] = useState<ViewMode>('tree');
|
|
174
|
-
const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]);
|
|
175
|
-
|
|
176
|
-
const baseUrl = system.getBaseUrl();
|
|
177
|
-
|
|
178
|
-
const fetchRoutes = useCallback(async () => {
|
|
179
|
-
setLoading(true);
|
|
180
|
-
setError(null);
|
|
181
|
-
try {
|
|
182
|
-
const result = await system.getRoutes();
|
|
183
|
-
setData(result);
|
|
184
|
-
|
|
185
|
-
// Auto-expand first two levels
|
|
186
|
-
if (result.routes.length > 0) {
|
|
187
|
-
const tree = buildRouteTree(result.routes);
|
|
188
|
-
const keysToExpand: string[] = [];
|
|
189
|
-
for (const [, child] of tree.children) {
|
|
190
|
-
keysToExpand.push(child.fullPath);
|
|
191
|
-
for (const [, grandChild] of child.children) {
|
|
192
|
-
keysToExpand.push(grandChild.fullPath);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
setExpandedKeys(keysToExpand);
|
|
196
|
-
}
|
|
197
|
-
} catch (err) {
|
|
198
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
199
|
-
} finally {
|
|
200
|
-
setLoading(false);
|
|
201
|
-
}
|
|
202
|
-
}, [system]);
|
|
203
|
-
|
|
204
|
-
useEffect(() => {
|
|
205
|
-
fetchRoutes();
|
|
206
|
-
}, [fetchRoutes]);
|
|
207
|
-
|
|
208
|
-
const handleCopyUrl = (url: string) => {
|
|
209
|
-
navigator.clipboard.writeText(url);
|
|
210
|
-
setCopiedUrl(url);
|
|
211
|
-
setTimeout(() => setCopiedUrl(null), 2000);
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
// Get unique methods for filter
|
|
215
|
-
const availableMethods = useMemo(() => {
|
|
216
|
-
if (!data?.routes) {return [];}
|
|
217
|
-
const methods = new Set<string>();
|
|
218
|
-
data.routes.forEach((route) => {
|
|
219
|
-
route.method.split(',').forEach((m) => methods.add(m.trim()));
|
|
220
|
-
});
|
|
221
|
-
return Array.from(methods).sort();
|
|
222
|
-
}, [data]);
|
|
223
|
-
|
|
224
|
-
// Filter routes
|
|
225
|
-
const filteredRoutes = useMemo(() => {
|
|
226
|
-
if (!data?.routes) {return [];}
|
|
227
|
-
return data.routes.filter((route) => {
|
|
228
|
-
// Search filter
|
|
229
|
-
const matchesSearch =
|
|
230
|
-
!searchQuery ||
|
|
231
|
-
route.url.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
232
|
-
route.method.toLowerCase().includes(searchQuery.toLowerCase());
|
|
233
|
-
|
|
234
|
-
// Method filter
|
|
235
|
-
const matchesMethod = !methodFilter || route.method.includes(methodFilter);
|
|
236
|
-
|
|
237
|
-
return matchesSearch && matchesMethod;
|
|
238
|
-
});
|
|
239
|
-
}, [data, searchQuery, methodFilter]);
|
|
240
|
-
|
|
241
|
-
// Build tree structure
|
|
242
|
-
const treeData = useMemo(() => {
|
|
243
|
-
if (filteredRoutes.length === 0) {return [];}
|
|
244
|
-
const tree = buildRouteTree(filteredRoutes);
|
|
245
|
-
return treeNodeToAntdNode(tree, baseUrl, copiedUrl, handleCopyUrl);
|
|
246
|
-
}, [filteredRoutes, baseUrl, copiedUrl]);
|
|
247
|
-
|
|
248
|
-
// Flat list for list view
|
|
249
|
-
const sortedRoutes = useMemo(() => {
|
|
250
|
-
return [...filteredRoutes].sort((a, b) => {
|
|
251
|
-
const urlCompare = a.url.localeCompare(b.url);
|
|
252
|
-
if (urlCompare !== 0) {return urlCompare;}
|
|
253
|
-
return a.method.localeCompare(b.method);
|
|
254
|
-
});
|
|
255
|
-
}, [filteredRoutes]);
|
|
256
|
-
|
|
257
|
-
return (
|
|
258
|
-
<UISpace direction="vertical" style={{ width: '100%' }} size="middle">
|
|
259
|
-
<div>
|
|
260
|
-
<UITitle level={5}>
|
|
261
|
-
<UIIcon name="ApiOutlined" /> API Routes Explorer
|
|
262
|
-
</UITitle>
|
|
263
|
-
<UITypographyParagraph type="secondary">
|
|
264
|
-
Browse all available REST API endpoints from the backend. Use search and filters to find
|
|
265
|
-
specific routes.
|
|
266
|
-
</UITypographyParagraph>
|
|
267
|
-
</div>
|
|
268
|
-
|
|
269
|
-
{/* Controls */}
|
|
270
|
-
<UISpace wrap>
|
|
271
|
-
<UIInput
|
|
272
|
-
placeholder="Search routes..."
|
|
273
|
-
prefix={<UIIcon name="SearchOutlined" />}
|
|
274
|
-
value={searchQuery}
|
|
275
|
-
onChange={(value) => setSearchQuery(value)}
|
|
276
|
-
style={{ width: 300 }}
|
|
277
|
-
allowClear
|
|
278
|
-
/>
|
|
279
|
-
<UISelect
|
|
280
|
-
placeholder="Filter by method"
|
|
281
|
-
allowClear
|
|
282
|
-
style={{ width: 150 }}
|
|
283
|
-
value={methodFilter ?? undefined}
|
|
284
|
-
onChange={(v) => setMethodFilter(v as string | null)}
|
|
285
|
-
options={availableMethods.map((m) => ({ label: m, value: m }))}
|
|
286
|
-
/>
|
|
287
|
-
<UISegmented
|
|
288
|
-
value={viewMode}
|
|
289
|
-
onChange={(v) => setViewMode(v as ViewMode)}
|
|
290
|
-
options={[
|
|
291
|
-
{ label: 'Tree', value: 'tree', icon: <UIIcon name="ApartmentOutlined" /> },
|
|
292
|
-
{ label: 'List', value: 'list', icon: <UIIcon name="UnorderedListOutlined" /> },
|
|
293
|
-
]}
|
|
294
|
-
/>
|
|
295
|
-
<UIButton icon={<UIIcon name="ReloadOutlined" />} onClick={fetchRoutes} loading={loading}>
|
|
296
|
-
Refresh
|
|
297
|
-
</UIButton>
|
|
298
|
-
</UISpace>
|
|
299
|
-
|
|
300
|
-
{/* Stats */}
|
|
301
|
-
{data && (
|
|
302
|
-
<UIAlert
|
|
303
|
-
variant="info"
|
|
304
|
-
showIcon={false}
|
|
305
|
-
message={
|
|
306
|
-
<UISpace split={<span style={{ color: '#d9d9d9' }}>|</span>}>
|
|
307
|
-
<span>
|
|
308
|
-
<UITypographyText strong>{data.count}</UITypographyText> total routes
|
|
309
|
-
</span>
|
|
310
|
-
<span>
|
|
311
|
-
<UITypographyText strong>{filteredRoutes.length}</UITypographyText> shown
|
|
312
|
-
</span>
|
|
313
|
-
<span>
|
|
314
|
-
<UITypographyText type="secondary">
|
|
315
|
-
Last fetched: {new Date(data.ts).toLocaleTimeString()}
|
|
316
|
-
</UITypographyText>
|
|
317
|
-
</span>
|
|
318
|
-
<span>
|
|
319
|
-
<UITypographyText type="secondary">Base: {baseUrl}</UITypographyText>
|
|
320
|
-
</span>
|
|
321
|
-
</UISpace>
|
|
322
|
-
}
|
|
323
|
-
/>
|
|
324
|
-
)}
|
|
325
|
-
|
|
326
|
-
{/* Error */}
|
|
327
|
-
{error && (
|
|
328
|
-
<UIAlert variant="error" message="Failed to load routes" description={error} showIcon />
|
|
329
|
-
)}
|
|
330
|
-
|
|
331
|
-
{/* Loading */}
|
|
332
|
-
{loading && !data && (
|
|
333
|
-
<div style={{ textAlign: 'center', padding: 40 }}>
|
|
334
|
-
<UISpin size="large" />
|
|
335
|
-
</div>
|
|
336
|
-
)}
|
|
337
|
-
|
|
338
|
-
{/* Tree View */}
|
|
339
|
-
{data && viewMode === 'tree' && treeData.length > 0 && (
|
|
340
|
-
<UITree
|
|
341
|
-
treeData={treeData}
|
|
342
|
-
showIcon
|
|
343
|
-
showLine={{ showLeafIcon: false }}
|
|
344
|
-
expandedKeys={expandedKeys}
|
|
345
|
-
onExpand={(keys) => setExpandedKeys(keys)}
|
|
346
|
-
style={{
|
|
347
|
-
background: 'transparent',
|
|
348
|
-
padding: '8px 0',
|
|
349
|
-
}}
|
|
350
|
-
/>
|
|
351
|
-
)}
|
|
352
|
-
|
|
353
|
-
{/* List View */}
|
|
354
|
-
{data && viewMode === 'list' && (
|
|
355
|
-
<div style={{ maxHeight: 500, overflow: 'auto' }}>
|
|
356
|
-
<UISpace direction="vertical" size="small" style={{ width: '100%' }}>
|
|
357
|
-
{sortedRoutes.map((route) => {
|
|
358
|
-
const fullUrl = route.url.startsWith('/api')
|
|
359
|
-
? `http://localhost:5050${route.url}`
|
|
360
|
-
: `${baseUrl}${route.url}`;
|
|
361
|
-
|
|
362
|
-
return (
|
|
363
|
-
<div
|
|
364
|
-
key={`${route.method}-${route.url}`}
|
|
365
|
-
style={{
|
|
366
|
-
display: 'flex',
|
|
367
|
-
alignItems: 'center',
|
|
368
|
-
gap: 8,
|
|
369
|
-
padding: '4px 8px',
|
|
370
|
-
borderRadius: 4,
|
|
371
|
-
background: 'rgba(0,0,0,0.02)',
|
|
372
|
-
}}
|
|
373
|
-
>
|
|
374
|
-
<UISpace size={4}>
|
|
375
|
-
{route.method.split(',').map((m) => (
|
|
376
|
-
<UITag
|
|
377
|
-
key={m}
|
|
378
|
-
color={METHOD_COLORS[m.trim()] || 'default'}
|
|
379
|
-
style={{ margin: 0, minWidth: 50, textAlign: 'center' }}
|
|
380
|
-
>
|
|
381
|
-
{m.trim()}
|
|
382
|
-
</UITag>
|
|
383
|
-
))}
|
|
384
|
-
</UISpace>
|
|
385
|
-
<UITypographyText code style={{ fontSize: 12, flex: 1 }}>
|
|
386
|
-
{route.url}
|
|
387
|
-
</UITypographyText>
|
|
388
|
-
<UITooltip title={copiedUrl === fullUrl ? 'Copied!' : 'Copy full URL'}>
|
|
389
|
-
<UIButton
|
|
390
|
-
variant="text"
|
|
391
|
-
size="small"
|
|
392
|
-
icon={
|
|
393
|
-
copiedUrl === fullUrl ? (
|
|
394
|
-
<UIIcon name="CheckOutlined" style={{ color: '#52c41a' }} />
|
|
395
|
-
) : (
|
|
396
|
-
<UIIcon name="CopyOutlined" />
|
|
397
|
-
)
|
|
398
|
-
}
|
|
399
|
-
onClick={() => handleCopyUrl(fullUrl)}
|
|
400
|
-
/>
|
|
401
|
-
</UITooltip>
|
|
402
|
-
</div>
|
|
403
|
-
);
|
|
404
|
-
})}
|
|
405
|
-
</UISpace>
|
|
406
|
-
</div>
|
|
407
|
-
)}
|
|
408
|
-
|
|
409
|
-
{/* Empty */}
|
|
410
|
-
{!loading && !error && filteredRoutes.length === 0 && data && (
|
|
411
|
-
<UIEmptyState description="No routes match your search" />
|
|
412
|
-
)}
|
|
413
|
-
</UISpace>
|
|
414
|
-
);
|
|
415
|
-
}
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
UIRadio,
|
|
4
|
-
UIRadioGroup,
|
|
5
|
-
UISwitch,
|
|
6
|
-
UISpace,
|
|
7
|
-
UICard,
|
|
8
|
-
UITypographyText,
|
|
9
|
-
UITypographyParagraph,
|
|
10
|
-
} from '@kb-labs/studio-ui-kit';
|
|
11
|
-
import { Sun, Moon, Monitor } from 'lucide-react';
|
|
12
|
-
import { useSettings } from '@/providers/settings-provider';
|
|
13
|
-
import { useKBTheme } from '@/components/ui';
|
|
14
|
-
|
|
15
|
-
const FONT_SIZES = [
|
|
16
|
-
{ value: 'small' as const, label: 'Small', description: '13px - Compact view' },
|
|
17
|
-
{ value: 'medium' as const, label: 'Medium', description: '14px - Default' },
|
|
18
|
-
{ value: 'large' as const, label: 'Large', description: '16px - Better readability' },
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
export function AppearanceSettings() {
|
|
22
|
-
const { theme, setTheme } = useKBTheme();
|
|
23
|
-
const { settings, updateSettings } = useSettings();
|
|
24
|
-
|
|
25
|
-
const handleThemeChange = (newTheme: 'light' | 'dark' | 'auto') => {
|
|
26
|
-
setTheme(newTheme);
|
|
27
|
-
updateSettings({
|
|
28
|
-
appearance: {
|
|
29
|
-
...settings.appearance,
|
|
30
|
-
theme: newTheme,
|
|
31
|
-
},
|
|
32
|
-
});
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const handleCompactModeChange = (checked: boolean) => {
|
|
36
|
-
updateSettings({
|
|
37
|
-
appearance: {
|
|
38
|
-
...settings.appearance,
|
|
39
|
-
compactMode: checked,
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const handleFontSizeChange = (e: any) => {
|
|
45
|
-
updateSettings({
|
|
46
|
-
appearance: {
|
|
47
|
-
...settings.appearance,
|
|
48
|
-
fontSize: e.target.value,
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<UISpace direction="vertical" size="large" style={{ width: '100%' }}>
|
|
55
|
-
{/* Theme Selection */}
|
|
56
|
-
<div>
|
|
57
|
-
<UITypographyText strong style={{ fontSize: 16, display: 'block', marginBottom: 12 }}>
|
|
58
|
-
Theme
|
|
59
|
-
</UITypographyText>
|
|
60
|
-
<UITypographyParagraph type="secondary" style={{ marginBottom: 16 }}>
|
|
61
|
-
Choose how KB Studio looks to you. Select a single theme, or sync with your system.
|
|
62
|
-
</UITypographyParagraph>
|
|
63
|
-
|
|
64
|
-
<UISpace direction="horizontal" size="middle" style={{ width: '100%' }}>
|
|
65
|
-
<UICard
|
|
66
|
-
hoverable
|
|
67
|
-
onClick={() => handleThemeChange('light')}
|
|
68
|
-
style={{
|
|
69
|
-
width: 200,
|
|
70
|
-
border: theme === 'light' ? '2px solid var(--link)' : '1px solid var(--border-primary)',
|
|
71
|
-
cursor: 'pointer',
|
|
72
|
-
}}
|
|
73
|
-
styles={{ body: { padding: 16 } }}
|
|
74
|
-
>
|
|
75
|
-
<div style={{ textAlign: 'center' }}>
|
|
76
|
-
<Sun size={32} style={{ marginBottom: 8, color: '#F59E0B' }} />
|
|
77
|
-
<div style={{ fontWeight: 600, marginBottom: 4 }}>Light</div>
|
|
78
|
-
<UITypographyText type="secondary" style={{ fontSize: 12 }}>
|
|
79
|
-
Bright and clean
|
|
80
|
-
</UITypographyText>
|
|
81
|
-
</div>
|
|
82
|
-
<div
|
|
83
|
-
style={{
|
|
84
|
-
marginTop: 12,
|
|
85
|
-
height: 40,
|
|
86
|
-
background: 'linear-gradient(to right, #F9FAFB, #FFFFFF)',
|
|
87
|
-
border: '1px solid #E5E7EB',
|
|
88
|
-
borderRadius: 4,
|
|
89
|
-
}}
|
|
90
|
-
/>
|
|
91
|
-
</UICard>
|
|
92
|
-
|
|
93
|
-
<UICard
|
|
94
|
-
hoverable
|
|
95
|
-
onClick={() => handleThemeChange('dark')}
|
|
96
|
-
style={{
|
|
97
|
-
width: 200,
|
|
98
|
-
border: theme === 'dark' ? '2px solid var(--link)' : '1px solid var(--border-primary)',
|
|
99
|
-
cursor: 'pointer',
|
|
100
|
-
}}
|
|
101
|
-
styles={{ body: { padding: 16 } }}
|
|
102
|
-
>
|
|
103
|
-
<div style={{ textAlign: 'center' }}>
|
|
104
|
-
<Moon size={32} style={{ marginBottom: 8, color: '#6366F1' }} />
|
|
105
|
-
<div style={{ fontWeight: 600, marginBottom: 4 }}>Dark</div>
|
|
106
|
-
<UITypographyText type="secondary" style={{ fontSize: 12 }}>
|
|
107
|
-
Easy on the eyes
|
|
108
|
-
</UITypographyText>
|
|
109
|
-
</div>
|
|
110
|
-
<div
|
|
111
|
-
style={{
|
|
112
|
-
marginTop: 12,
|
|
113
|
-
height: 40,
|
|
114
|
-
background: 'linear-gradient(to right, #0D0D0F, #1A1A1C)',
|
|
115
|
-
border: '1px solid #3F3F46',
|
|
116
|
-
borderRadius: 4,
|
|
117
|
-
}}
|
|
118
|
-
/>
|
|
119
|
-
</UICard>
|
|
120
|
-
|
|
121
|
-
<UICard
|
|
122
|
-
hoverable
|
|
123
|
-
onClick={() => handleThemeChange('auto')}
|
|
124
|
-
style={{
|
|
125
|
-
width: 200,
|
|
126
|
-
border: theme === 'auto' ? '2px solid var(--link)' : '1px solid var(--border-primary)',
|
|
127
|
-
cursor: 'pointer',
|
|
128
|
-
}}
|
|
129
|
-
styles={{ body: { padding: 16 } }}
|
|
130
|
-
>
|
|
131
|
-
<div style={{ textAlign: 'center' }}>
|
|
132
|
-
<Monitor size={32} style={{ marginBottom: 8, color: '#8B5CF6' }} />
|
|
133
|
-
<div style={{ fontWeight: 600, marginBottom: 4 }}>Auto</div>
|
|
134
|
-
<UITypographyText type="secondary" style={{ fontSize: 12 }}>
|
|
135
|
-
Sync with system
|
|
136
|
-
</UITypographyText>
|
|
137
|
-
</div>
|
|
138
|
-
<div
|
|
139
|
-
style={{
|
|
140
|
-
marginTop: 12,
|
|
141
|
-
height: 40,
|
|
142
|
-
background: 'linear-gradient(to right, #F9FAFB 50%, #1A1A1C 50%)',
|
|
143
|
-
border: '1px solid var(--border-primary)',
|
|
144
|
-
borderRadius: 4,
|
|
145
|
-
}}
|
|
146
|
-
/>
|
|
147
|
-
</UICard>
|
|
148
|
-
</UISpace>
|
|
149
|
-
</div>
|
|
150
|
-
|
|
151
|
-
{/* Compact Mode */}
|
|
152
|
-
<div>
|
|
153
|
-
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
|
|
154
|
-
<div>
|
|
155
|
-
<UITypographyText strong style={{ fontSize: 16, display: 'block' }}>
|
|
156
|
-
Compact Mode
|
|
157
|
-
</UITypographyText>
|
|
158
|
-
<UITypographyParagraph type="secondary" style={{ marginBottom: 0, marginTop: 4 }}>
|
|
159
|
-
Reduce spacing and padding for a denser layout
|
|
160
|
-
</UITypographyParagraph>
|
|
161
|
-
</div>
|
|
162
|
-
<UISwitch
|
|
163
|
-
checked={settings.appearance.compactMode}
|
|
164
|
-
onChange={handleCompactModeChange}
|
|
165
|
-
/>
|
|
166
|
-
</div>
|
|
167
|
-
</div>
|
|
168
|
-
|
|
169
|
-
{/* Font Size */}
|
|
170
|
-
<div>
|
|
171
|
-
<UITypographyText strong style={{ fontSize: 16, display: 'block', marginBottom: 12 }}>
|
|
172
|
-
Font Size
|
|
173
|
-
</UITypographyText>
|
|
174
|
-
<UITypographyParagraph type="secondary" style={{ marginBottom: 16 }}>
|
|
175
|
-
Adjust the base font size across the application
|
|
176
|
-
</UITypographyParagraph>
|
|
177
|
-
|
|
178
|
-
<UIRadioGroup
|
|
179
|
-
value={settings.appearance.fontSize}
|
|
180
|
-
onChange={handleFontSizeChange}
|
|
181
|
-
style={{ width: '100%' }}
|
|
182
|
-
>
|
|
183
|
-
<UISpace direction="vertical" style={{ width: '100%' }}>
|
|
184
|
-
{FONT_SIZES.map((size) => (
|
|
185
|
-
<UIRadio key={size.value} value={size.value} style={{ display: 'flex', alignItems: 'center' }}>
|
|
186
|
-
<div>
|
|
187
|
-
<UITypographyText strong>{size.label}</UITypographyText>
|
|
188
|
-
<br />
|
|
189
|
-
<UITypographyText type="secondary" style={{ fontSize: 12 }}>
|
|
190
|
-
{size.description}
|
|
191
|
-
</UITypographyText>
|
|
192
|
-
</div>
|
|
193
|
-
</UIRadio>
|
|
194
|
-
))}
|
|
195
|
-
</UISpace>
|
|
196
|
-
</UIRadioGroup>
|
|
197
|
-
</div>
|
|
198
|
-
</UISpace>
|
|
199
|
-
);
|
|
200
|
-
}
|