@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.
Files changed (175) hide show
  1. package/dist/index.html +1 -0
  2. package/dist/main.80b70197.css +2 -0
  3. package/dist/main.80b70197.css.map +1 -0
  4. package/dist/main.ad42ef4a.js +522 -0
  5. package/dist/main.ad42ef4a.js.map +1 -0
  6. package/dist/mf-manifest.json +241 -0
  7. package/dist/mf-stats.json +271 -0
  8. package/package.json +56 -39
  9. package/server.js +68 -0
  10. package/.env.example +0 -10
  11. package/UI-KIT-COMPONENTS.md +0 -783
  12. package/UI-KIT-PLAN.md +0 -326
  13. package/eslint-rules/no-direct-antd-import.js +0 -151
  14. package/eslint.config.ts +0 -27
  15. package/index.html +0 -17
  16. package/postcss.config.js +0 -7
  17. package/public/favicon.ico +0 -0
  18. package/rspack.config.mjs +0 -118
  19. package/src/App.tsx +0 -46
  20. package/src/components/devtools/devtools-log-feed.module.css +0 -165
  21. package/src/components/devtools/devtools-log-feed.tsx +0 -300
  22. package/src/components/devtools/devtools-panel.module.css +0 -121
  23. package/src/components/devtools/devtools-panel.tsx +0 -115
  24. package/src/components/empty-state.tsx +0 -28
  25. package/src/components/error-boundary.tsx +0 -210
  26. package/src/components/health-banner.module.css +0 -103
  27. package/src/components/health-banner.tsx +0 -134
  28. package/src/components/health-indicator.tsx +0 -12
  29. package/src/components/page-header.tsx +0 -49
  30. package/src/components/page-transition.tsx +0 -24
  31. package/src/components/role-switcher.tsx +0 -148
  32. package/src/components/run-badge.tsx +0 -13
  33. package/src/components/ui/icons.tsx +0 -509
  34. package/src/components/ui/index.ts +0 -25
  35. package/src/components/ui/kb-config-provider.tsx +0 -130
  36. package/src/components/ui/kb-header.module.css +0 -51
  37. package/src/components/ui/kb-header.tsx +0 -112
  38. package/src/components/ui/kb-notification-bell.module.css +0 -103
  39. package/src/components/ui/kb-notification-bell.tsx +0 -199
  40. package/src/components/ui/kb-page-layout.tsx +0 -66
  41. package/src/components/ui/kb-quick-search.module.css +0 -86
  42. package/src/components/ui/kb-quick-search.tsx +0 -196
  43. package/src/components/ui/kb-sidebar.module.css +0 -47
  44. package/src/components/ui/kb-sidebar.tsx +0 -196
  45. package/src/components/ui/kb-stat-card.module.css +0 -21
  46. package/src/components/ui/kb-stat-card.tsx +0 -22
  47. package/src/components/ui/kb-stat-grid.tsx +0 -22
  48. package/src/components/ui/kb-status-bar.module.css +0 -49
  49. package/src/components/ui/kb-status-bar.tsx +0 -145
  50. package/src/components/ui/kb-system-health-indicator.tsx +0 -229
  51. package/src/components/ui/kb-theme-toggle.tsx +0 -42
  52. package/src/components/ui/theme-adapter.ts +0 -812
  53. package/src/components/ui/theme-transition-overlay.tsx +0 -70
  54. package/src/components/ui/utils.ts +0 -6
  55. package/src/components/widget-error-boundary.tsx +0 -172
  56. package/src/config/env.ts +0 -5
  57. package/src/config/feature-flags.ts +0 -482
  58. package/src/config/studio.config.schema.ts +0 -88
  59. package/src/config/studio.config.ts +0 -84
  60. package/src/env.d.ts +0 -23
  61. package/src/hooks/use-command-palette.ts +0 -6
  62. package/src/hooks/use-devtools-store.ts +0 -25
  63. package/src/hooks/use-feature-flags.ts +0 -101
  64. package/src/index.css +0 -10
  65. package/src/layouts/dashboard-grid.tsx +0 -122
  66. package/src/layouts/index.ts +0 -12
  67. package/src/layouts/two-pane.tsx +0 -134
  68. package/src/main.tsx +0 -15
  69. package/src/modules/analytics/components/analytics-daily-stats-chart.tsx +0 -257
  70. package/src/modules/analytics/components/analytics-date-range-picker.tsx +0 -121
  71. package/src/modules/analytics/pages/analytics-cache-page.tsx +0 -285
  72. package/src/modules/analytics/pages/analytics-embeddings-page.tsx +0 -274
  73. package/src/modules/analytics/pages/analytics-events-page.tsx +0 -425
  74. package/src/modules/analytics/pages/analytics-llm-page.tsx +0 -434
  75. package/src/modules/analytics/pages/analytics-overview-page.tsx +0 -336
  76. package/src/modules/analytics/pages/analytics-page.tsx +0 -20
  77. package/src/modules/analytics/pages/analytics-storage-page.tsx +0 -344
  78. package/src/modules/analytics/pages/analytics-vectorstore-page.tsx +0 -279
  79. package/src/modules/analytics/routes/index.tsx +0 -133
  80. package/src/modules/analytics/utils/formatters.test.ts +0 -148
  81. package/src/modules/analytics/utils/formatters.ts +0 -100
  82. package/src/modules/auth/pages/login-page.module.css +0 -113
  83. package/src/modules/auth/pages/login-page.tsx +0 -80
  84. package/src/modules/auth/routes/index.tsx +0 -24
  85. package/src/modules/dashboard/components/ActivityTimelineWidget.tsx +0 -199
  86. package/src/modules/dashboard/components/AiInsightsWidget.tsx +0 -358
  87. package/src/modules/dashboard/components/AnimatedMetricCard.css +0 -88
  88. package/src/modules/dashboard/components/AnimatedMetricCard.tsx +0 -125
  89. package/src/modules/dashboard/components/HeroMetricCard.css +0 -114
  90. package/src/modules/dashboard/components/HeroMetricCard.tsx +0 -152
  91. package/src/modules/dashboard/components/HeroMetricsWidget.tsx +0 -146
  92. package/src/modules/dashboard/components/SystemResourcesWidget.tsx +0 -197
  93. package/src/modules/dashboard/components/dashboard-empty.tsx +0 -22
  94. package/src/modules/dashboard/components/dashboard-kpis-widget.tsx +0 -59
  95. package/src/modules/dashboard/components/dashboard-kpis.tsx +0 -13
  96. package/src/modules/dashboard/components/dashboard-latency-percentiles-widget.tsx +0 -85
  97. package/src/modules/dashboard/components/dashboard-metrics-widget.tsx +0 -78
  98. package/src/modules/dashboard/components/dashboard-plugins-widget.tsx +0 -67
  99. package/src/modules/dashboard/components/dashboard-requests-widget.tsx +0 -62
  100. package/src/modules/dashboard/layouts/dashboard-layout.tsx +0 -16
  101. package/src/modules/dashboard/pages/ai-insights-page.tsx +0 -563
  102. package/src/modules/dashboard/pages/dashboard-animations.css +0 -317
  103. package/src/modules/dashboard/pages/dashboard-page.tsx +0 -13
  104. package/src/modules/dashboard/routes/index.tsx +0 -70
  105. package/src/modules/observability/pages/devkit-page.tsx +0 -263
  106. package/src/modules/observability/pages/incident-detail-page.tsx +0 -571
  107. package/src/modules/observability/pages/incidents-page.tsx +0 -370
  108. package/src/modules/observability/pages/log-detail-page.tsx +0 -508
  109. package/src/modules/observability/pages/logs-page.tsx +0 -1129
  110. package/src/modules/observability/pages/prometheus-metrics-page.tsx +0 -486
  111. package/src/modules/observability/pages/state-broker-page.tsx +0 -229
  112. package/src/modules/observability/pages/system-events-page.tsx +0 -255
  113. package/src/modules/observability/routes/index.tsx +0 -133
  114. package/src/modules/plugins/components/marketplace-card.tsx +0 -153
  115. package/src/modules/plugins/components/plugin-ai-assistant-modal.tsx +0 -374
  116. package/src/modules/plugins/components/plugin-card.tsx +0 -136
  117. package/src/modules/plugins/components/plugin-detail/capabilities-summary.tsx +0 -101
  118. package/src/modules/plugins/components/plugin-detail/cli-commands-table.tsx +0 -101
  119. package/src/modules/plugins/components/plugin-detail/index.ts +0 -7
  120. package/src/modules/plugins/components/plugin-detail/jobs-table.tsx +0 -64
  121. package/src/modules/plugins/components/plugin-detail/permissions/env-permissions.tsx +0 -30
  122. package/src/modules/plugins/components/plugin-detail/permissions/filesystem-permissions.tsx +0 -68
  123. package/src/modules/plugins/components/plugin-detail/permissions/index.ts +0 -6
  124. package/src/modules/plugins/components/plugin-detail/permissions/network-permissions.tsx +0 -43
  125. package/src/modules/plugins/components/plugin-detail/permissions/platform-services.tsx +0 -242
  126. package/src/modules/plugins/components/plugin-detail/permissions/resource-quotas.tsx +0 -96
  127. package/src/modules/plugins/components/plugin-detail/permissions/shell-permissions.tsx +0 -55
  128. package/src/modules/plugins/components/plugin-detail/permissions-view.tsx +0 -47
  129. package/src/modules/plugins/components/plugin-detail/plugin-header.tsx +0 -169
  130. package/src/modules/plugins/components/plugin-detail/rest-routes-table.tsx +0 -159
  131. package/src/modules/plugins/components/plugin-detail/workflows-table.tsx +0 -40
  132. package/src/modules/plugins/components/plugin-grid.tsx +0 -20
  133. package/src/modules/plugins/components/plugin-table.tsx +0 -178
  134. package/src/modules/plugins/pages/marketplace-page.tsx +0 -200
  135. package/src/modules/plugins/pages/plugin-detail-page.tsx +0 -1145
  136. package/src/modules/plugins/routes/index.tsx +0 -48
  137. package/src/modules/plugins/utils/index.ts +0 -1
  138. package/src/modules/plugins/utils/schema-helpers.ts +0 -21
  139. package/src/modules/settings/components/api-routes-viewer.tsx +0 -415
  140. package/src/modules/settings/components/appearance-settings.tsx +0 -200
  141. package/src/modules/settings/components/configuration-settings.tsx +0 -321
  142. package/src/modules/settings/components/data-privacy-settings.tsx +0 -111
  143. package/src/modules/settings/components/experimental-settings.tsx +0 -243
  144. package/src/modules/settings/components/navigation-settings.tsx +0 -198
  145. package/src/modules/settings/components/notifications-settings.tsx +0 -40
  146. package/src/modules/settings/pages/settings-page.tsx +0 -298
  147. package/src/modules/settings/routes/index.tsx +0 -47
  148. package/src/pages/README-assistant.md +0 -91
  149. package/src/pages/login-page.tsx +0 -177
  150. package/src/pages/not-found-page.tsx +0 -85
  151. package/src/providers/auth-provider.tsx +0 -40
  152. package/src/providers/command-palette-provider.tsx +0 -193
  153. package/src/providers/data-sources-provider.tsx +0 -27
  154. package/src/providers/devtools-provider.tsx +0 -72
  155. package/src/providers/navigation-items-provider.tsx +0 -38
  156. package/src/providers/registry-v2-provider.tsx +0 -93
  157. package/src/providers/settings-provider.tsx +0 -158
  158. package/src/router.tsx +0 -302
  159. package/src/routes/helpers.tsx +0 -25
  160. package/src/routes/plugin-page-v2.tsx +0 -52
  161. package/src/routes/types.ts +0 -18
  162. package/src/styles/antd-overrides.css +0 -211
  163. package/src/styles/base.css +0 -190
  164. package/src/styles/settings.css +0 -75
  165. package/src/styles/variables.css +0 -94
  166. package/src/utils/analytics.ts +0 -183
  167. package/src/utils/apply-navigation-categories.ts +0 -83
  168. package/src/utils/commands.ts +0 -252
  169. package/src/utils/export.ts +0 -174
  170. package/src/utils/format-date.ts +0 -166
  171. package/src/utils/logger.ts +0 -110
  172. package/tailwind.config.ts +0 -13
  173. package/tsconfig.json +0 -13
  174. package/vitest-setup.ts +0 -1
  175. /package/{public → dist}/favicon.svg +0 -0
@@ -1,165 +0,0 @@
1
- /* ── Log feed container ──────────────────────────────────────────────── */
2
-
3
- .container {
4
- display: flex;
5
- flex-direction: column;
6
- height: 100%;
7
- overflow: hidden;
8
- }
9
-
10
- /* ── Toolbar ─────────────────────────────────────────────────────────── */
11
-
12
- .toolbar {
13
- display: flex;
14
- align-items: center;
15
- gap: 8px;
16
- padding: 5px 10px;
17
- border-bottom: 1px solid var(--border-primary);
18
- flex-shrink: 0;
19
- min-height: 36px;
20
- background: var(--bg-secondary);
21
- }
22
-
23
- .toolbarTitle {
24
- flex-shrink: 0;
25
- }
26
-
27
- .toolbarSearch {
28
- flex: 0 1 220px;
29
- }
30
-
31
- .toolbarChips {
32
- flex: 1;
33
- display: flex;
34
- gap: 4px;
35
- flex-wrap: wrap;
36
- }
37
-
38
- .chip {
39
- cursor: pointer;
40
- user-select: none;
41
- font-size: 11px !important;
42
- }
43
-
44
- .clearBtn {
45
- font-size: 11px;
46
- color: var(--text-secondary);
47
- cursor: pointer;
48
- flex-shrink: 0;
49
- user-select: none;
50
- background: none;
51
- border: none;
52
- padding: 2px 4px;
53
- border-radius: 3px;
54
- transition: color 0.15s;
55
- }
56
-
57
- .clearBtn:hover {
58
- color: var(--text-primary);
59
- }
60
-
61
- /* ── Log list ────────────────────────────────────────────────────────── */
62
-
63
- .logList {
64
- flex: 1;
65
- overflow-y: auto;
66
- font-family: var(--font-mono, 'JetBrains Mono', 'Fira Code', 'Menlo', monospace);
67
- }
68
-
69
- /* ── Log row ─────────────────────────────────────────────────────────── */
70
-
71
- .row {
72
- padding: 3px 8px;
73
- border-bottom: 1px solid var(--border-primary);
74
- cursor: default;
75
- }
76
-
77
- .row.clickable {
78
- cursor: pointer;
79
- }
80
-
81
- .row.clickable:hover {
82
- background: var(--bg-hover);
83
- }
84
-
85
- .row.expanded {
86
- background: var(--bg-tertiary);
87
- }
88
-
89
- .rowError {
90
- background: rgba(220, 38, 38, 0.06);
91
- border-left: 2px solid var(--error);
92
- }
93
-
94
- .rowWarning {
95
- background: rgba(245, 158, 11, 0.06);
96
- border-left: 2px solid var(--warning);
97
- }
98
-
99
- .rowPending {
100
- opacity: 0.6;
101
- }
102
-
103
- /* ── Status badge ────────────────────────────────────────────────────── */
104
-
105
- .statusBadge {
106
- display: inline-flex;
107
- align-items: center;
108
- justify-content: center;
109
- min-width: 52px;
110
- padding: 1px 5px;
111
- border-radius: 3px;
112
- font-size: 10px;
113
- font-weight: 600;
114
- letter-spacing: 0.03em;
115
- text-transform: uppercase;
116
- flex-shrink: 0;
117
- }
118
-
119
- .statusSuccess {
120
- background: rgba(22, 163, 74, 0.12);
121
- color: var(--success);
122
- }
123
-
124
- .statusWarning {
125
- background: rgba(245, 158, 11, 0.15);
126
- color: var(--warning);
127
- }
128
-
129
- .statusError {
130
- background: rgba(220, 38, 38, 0.12);
131
- color: var(--error);
132
- }
133
-
134
- .statusPending {
135
- background: var(--bg-tertiary);
136
- color: var(--text-secondary);
137
- }
138
-
139
- /* ── Expand detail ───────────────────────────────────────────────────── */
140
-
141
- .detail {
142
- padding: 4px 8px 4px 16px;
143
- background: var(--bg-tertiary);
144
- }
145
-
146
- .detailPre {
147
- margin: 0;
148
- padding: 8px;
149
- background: var(--bg-primary);
150
- border: 1px solid var(--border-primary);
151
- border-radius: 4px;
152
- font-size: 11px;
153
- font-family: inherit;
154
- overflow-x: auto;
155
- white-space: pre-wrap;
156
- word-break: break-word;
157
- color: var(--text-primary);
158
- }
159
-
160
- /* ── Empty state ─────────────────────────────────────────────────────── */
161
-
162
- .empty {
163
- padding: 24px 16px;
164
- text-align: center;
165
- }
@@ -1,300 +0,0 @@
1
- import * as React from 'react';
2
- import { useState, useMemo, useCallback } from 'react';
3
- import { UITag, UIBadge, UIFlex, UIInput, UITypographyText } from '@kb-labs/studio-ui-kit';
4
- import type { DevToolsChannel, MFEvent, EventBusEvent } from '@kb-labs/studio-devtools';
5
- import { useDevToolsStore } from '@/hooks/use-devtools-store';
6
- import styles from './devtools-log-feed.module.css';
7
-
8
- // ---------------------------------------------------------------------------
9
- // Types
10
- // ---------------------------------------------------------------------------
11
-
12
- interface LogEntry {
13
- id: string;
14
- channelId: string;
15
- channelLabel: string;
16
- timestamp: number;
17
- status: 'success' | 'warning' | 'error' | 'pending';
18
- summary: string;
19
- meta?: string;
20
- raw: unknown;
21
- }
22
-
23
- // ---------------------------------------------------------------------------
24
- // Row builders
25
- // ---------------------------------------------------------------------------
26
-
27
- function buildMFEntry(event: MFEvent, channelLabel: string): LogEntry {
28
- let status: LogEntry['status'] = 'pending';
29
- if (event.status === 'success') {status = 'success';}
30
- else if (event.status === 'warning') {status = 'warning';}
31
- else if (event.status === 'error') {status = 'error';}
32
-
33
- return {
34
- id: event.id,
35
- channelId: 'mf-events',
36
- channelLabel,
37
- timestamp: event.startedAt,
38
- status,
39
- summary: `${event.remoteName} / ${event.exposedModule}`,
40
- meta: event.durationMs != null ? `${event.durationMs}ms` : undefined,
41
- raw: event,
42
- };
43
- }
44
-
45
- function buildEventBusEntry(event: EventBusEvent, channelLabel: string): LogEntry {
46
- return {
47
- id: event.id,
48
- channelId: 'eventbus',
49
- channelLabel,
50
- timestamp: event.timestamp,
51
- status: 'success',
52
- summary: event.event,
53
- meta: event.sourcePluginId !== '' ? event.sourcePluginId : undefined,
54
- raw: event,
55
- };
56
- }
57
-
58
- function buildGenericEntry(event: unknown, channelId: string, channelLabel: string): LogEntry {
59
- const id = (event as { id?: string }).id ?? `${Date.now()}-${Math.random()}`;
60
- const timestamp =
61
- (event as { timestamp?: number }).timestamp ??
62
- (event as { startedAt?: number }).startedAt ??
63
- Date.now();
64
- return {
65
- id, channelId, channelLabel, timestamp,
66
- status: 'success',
67
- summary: JSON.stringify(event).slice(0, 80),
68
- raw: event,
69
- };
70
- }
71
-
72
- function channelToEntries(channel: DevToolsChannel): LogEntry[] {
73
- return (channel.getEvents() as unknown[]).map((e) => {
74
- if (channel.id === 'mf-events') {return buildMFEntry(e as MFEvent, channel.label);}
75
- if (channel.id === 'eventbus') {return buildEventBusEntry(e as EventBusEvent, channel.label);}
76
- return buildGenericEntry(e, channel.id, channel.label);
77
- });
78
- }
79
-
80
- // ---------------------------------------------------------------------------
81
- // Status badge
82
- // ---------------------------------------------------------------------------
83
-
84
- const STATUS_LABEL: Record<LogEntry['status'], string> = {
85
- success: 'ok',
86
- warning: 'warn',
87
- error: 'error',
88
- pending: '...',
89
- };
90
-
91
- function StatusBadge({ status }: { status: LogEntry['status'] }) {
92
- const cls = {
93
- success: styles.statusSuccess,
94
- warning: styles.statusWarning,
95
- error: styles.statusError,
96
- pending: styles.statusPending,
97
- }[status];
98
- return <span className={`${styles.statusBadge} ${cls}`}>{STATUS_LABEL[status]}</span>;
99
- }
100
-
101
- // ---------------------------------------------------------------------------
102
- // Expanded detail
103
- // ---------------------------------------------------------------------------
104
-
105
- function ExpandedDetail({ entry }: { entry: LogEntry }) {
106
- if (entry.channelId === 'mf-events') {
107
- const mf = entry.raw as MFEvent;
108
- return (
109
- <div className={styles.detail}>
110
- {mf.isDefaultUndefined && (
111
- <UITag color="orange" style={{ marginBottom: 8 }}>
112
- Missing default export — add <code>export default</code> to the component
113
- </UITag>
114
- )}
115
- {mf.error && (
116
- <pre className={styles.detailPre}>
117
- {mf.error.message}
118
- {mf.error.cause ? `\n\nCause: ${mf.error.cause}` : ''}
119
- </pre>
120
- )}
121
- {!mf.isDefaultUndefined && !mf.error && (
122
- <UITypographyText type="secondary" style={{ fontSize: 12 }}>
123
- attempt {mf.attempt}
124
- </UITypographyText>
125
- )}
126
- </div>
127
- );
128
- }
129
-
130
- if (entry.channelId === 'eventbus') {
131
- const eb = entry.raw as EventBusEvent;
132
- return (
133
- <div className={styles.detail}>
134
- <pre className={styles.detailPre}>
135
- {JSON.stringify(eb.payload, null, 2)}
136
- </pre>
137
- </div>
138
- );
139
- }
140
-
141
- return (
142
- <div className={styles.detail}>
143
- <pre className={styles.detailPre}>
144
- {JSON.stringify(entry.raw, null, 2)}
145
- </pre>
146
- </div>
147
- );
148
- }
149
-
150
- // ---------------------------------------------------------------------------
151
- // Log row
152
- // ---------------------------------------------------------------------------
153
-
154
- function LogRow({ entry }: { entry: LogEntry }) {
155
- const [expanded, setExpanded] = useState(false);
156
- const hasDetail =
157
- entry.channelId === 'mf-events'
158
- ? !!(entry.raw as MFEvent).error || !!(entry.raw as MFEvent).isDefaultUndefined
159
- : entry.channelId === 'eventbus';
160
-
161
- const time = new Date(entry.timestamp).toLocaleTimeString('en-GB', {
162
- hour: '2-digit', minute: '2-digit', second: '2-digit',
163
- });
164
-
165
- const rowStatusCls = entry.status === 'error' ? styles.rowError
166
- : entry.status === 'warning' ? styles.rowWarning
167
- : entry.status === 'pending' ? styles.rowPending
168
- : '';
169
-
170
- return (
171
- <>
172
- <div
173
- className={`${styles.row} ${rowStatusCls} ${hasDetail ? styles.clickable : ''} ${expanded ? styles.expanded : ''}`}
174
- onClick={hasDetail ? () => setExpanded((v) => !v) : undefined}
175
- >
176
- <UIFlex align="center" gap={8}>
177
- <span style={{ fontSize: 10, width: 8, color: 'var(--text-secondary)', flexShrink: 0 }}>
178
- {hasDetail ? (expanded ? '▼' : '▶') : ''}
179
- </span>
180
- <span style={{ fontSize: 11, color: 'var(--text-secondary)', width: 64, flexShrink: 0 }}>
181
- {time}
182
- </span>
183
- <UITag
184
- color={entry.channelId === 'mf-events' ? 'blue' : 'purple'}
185
- style={{ fontSize: 10, lineHeight: '16px', padding: '0 4px', flexShrink: 0 }}
186
- >
187
- {entry.channelLabel}
188
- </UITag>
189
- <StatusBadge status={entry.status} />
190
- <span style={{ fontSize: 12, flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: 'var(--text-primary)' }}>
191
- {entry.summary}
192
- {(entry.raw as MFEvent).isDefaultUndefined && (
193
- <UITag color="orange" style={{ marginLeft: 6, fontSize: 10 }}>default=undefined</UITag>
194
- )}
195
- </span>
196
- {entry.meta && (
197
- <span style={{ fontSize: 11, color: 'var(--text-secondary)', flexShrink: 0 }}>
198
- {entry.meta}
199
- </span>
200
- )}
201
- </UIFlex>
202
- </div>
203
- {expanded && hasDetail && <ExpandedDetail entry={entry} />}
204
- </>
205
- );
206
- }
207
-
208
- // ---------------------------------------------------------------------------
209
- // Main log feed
210
- // ---------------------------------------------------------------------------
211
-
212
- export function DevToolsLogFeed() {
213
- const { channels, clear } = useDevToolsStore();
214
- const [activeChannel, setActiveChannel] = useState<string>('all');
215
- const [search, setSearch] = useState('');
216
-
217
- const allEntries = useMemo<LogEntry[]>(() => {
218
- const entries: LogEntry[] = [];
219
- channels.forEach((ch) => entries.push(...channelToEntries(ch)));
220
- return entries.sort((a, b) => a.timestamp - b.timestamp);
221
- }, [channels]);
222
-
223
- const filtered = useMemo(() => {
224
- let result = activeChannel === 'all'
225
- ? allEntries
226
- : allEntries.filter((e) => e.channelId === activeChannel);
227
- if (search.trim()) {
228
- const q = search.toLowerCase();
229
- result = result.filter((e) =>
230
- e.summary.toLowerCase().includes(q) ||
231
- e.channelLabel.toLowerCase().includes(q) ||
232
- (e.meta ?? '').toLowerCase().includes(q),
233
- );
234
- }
235
- return result;
236
- }, [allEntries, activeChannel, search]);
237
-
238
- const errorCount = allEntries.filter((e) => e.status === 'error' || e.status === 'warning').length;
239
-
240
- const countFor = useCallback(
241
- (id: string) => id === 'all' ? allEntries.length : allEntries.filter((e) => e.channelId === id).length,
242
- [allEntries],
243
- );
244
-
245
- return (
246
- <div className={styles.container}>
247
- <div className={styles.toolbar}>
248
- <UIFlex align="center" gap={4} className={styles.toolbarTitle}>
249
- <UITypographyText strong style={{ fontSize: 12 }}>DevTools</UITypographyText>
250
- {errorCount > 0 && <UIBadge count={errorCount} size="small" />}
251
- </UIFlex>
252
-
253
- <UIInput
254
- className={styles.toolbarSearch}
255
- placeholder="Search..."
256
- value={search}
257
- onChange={(value) => setSearch(value)}
258
- allowClear
259
- size="small"
260
- />
261
-
262
- <div className={styles.toolbarChips}>
263
- <UITag
264
- className={styles.chip}
265
- color={activeChannel === 'all' ? 'blue' : undefined}
266
- onClick={() => setActiveChannel('all')}
267
- >
268
- All{countFor('all') > 0 ? ` ×${countFor('all')}` : ''}
269
- </UITag>
270
- {channels.map((ch) => (
271
- <UITag
272
- key={ch.id}
273
- className={styles.chip}
274
- color={activeChannel === ch.id ? 'blue' : undefined}
275
- onClick={() => setActiveChannel(ch.id)}
276
- >
277
- {ch.label}{countFor(ch.id) > 0 ? ` ×${countFor(ch.id)}` : ''}
278
- </UITag>
279
- ))}
280
- </div>
281
-
282
- <button className={styles.clearBtn} onClick={clear}>Clear</button>
283
- </div>
284
-
285
- <div className={styles.logList}>
286
- {filtered.length === 0 ? (
287
- <div className={styles.empty}>
288
- <UITypographyText type="secondary" style={{ fontSize: 12 }}>
289
- {allEntries.length === 0
290
- ? 'No events captured yet. Navigate to a plugin page to see MF events.'
291
- : 'No events match the current filter.'}
292
- </UITypographyText>
293
- </div>
294
- ) : (
295
- filtered.map((entry) => <LogRow key={entry.id} entry={entry} />)
296
- )}
297
- </div>
298
- </div>
299
- );
300
- }
@@ -1,121 +0,0 @@
1
- /* ── Toggle button ───────────────────────────────────────────────────── */
2
-
3
- .toggleBtn {
4
- position: fixed;
5
- left: 50%;
6
- transform: translateX(-50%);
7
- z-index: 9999;
8
- transition: bottom 0.2s ease;
9
- width: 96px;
10
- height: 22px;
11
- display: flex;
12
- align-items: center;
13
- justify-content: center;
14
- gap: 6px;
15
- padding: 0 10px;
16
- background: var(--bg-spotlight, #1f1f1f);
17
- color: var(--text-inverse);
18
- border: none;
19
- cursor: pointer;
20
- font-size: 11px;
21
- font-weight: 500;
22
- letter-spacing: 0.02em;
23
- box-shadow: 0 -2px 8px var(--shadow-elevated);
24
- user-select: none;
25
- border-radius: 6px 6px 0 0;
26
- }
27
-
28
- .toggleBtn.open {
29
- border-radius: 0 0 6px 6px;
30
- }
31
-
32
- .toggleArrow {
33
- width: 0;
34
- height: 0;
35
- border-left: 5px solid transparent;
36
- border-right: 5px solid transparent;
37
- flex-shrink: 0;
38
- }
39
-
40
- .toggleArrow.arrowUp {
41
- border-bottom: 5px solid var(--text-inverse);
42
- }
43
-
44
- .toggleArrow.arrowDown {
45
- border-top: 5px solid var(--text-inverse);
46
- }
47
-
48
- /* ── Floating panel ──────────────────────────────────────────────────── */
49
-
50
- .panel {
51
- position: fixed;
52
- bottom: 0;
53
- left: 0;
54
- right: 0;
55
- z-index: 9998;
56
- background: var(--bg-secondary);
57
- border-top: 1px solid var(--border-primary);
58
- box-shadow: 0 -4px 20px var(--shadow-elevated);
59
- display: flex;
60
- flex-direction: column;
61
- overflow: hidden;
62
- }
63
-
64
- /* ── Drag handle ─────────────────────────────────────────────────────── */
65
-
66
- .dragHandle {
67
- height: 6px;
68
- cursor: ns-resize;
69
- background: transparent;
70
- flex-shrink: 0;
71
- position: relative;
72
- }
73
-
74
- .dragGrip {
75
- position: absolute;
76
- top: 2px;
77
- left: 50%;
78
- transform: translateX(-50%);
79
- width: 36px;
80
- height: 3px;
81
- border-radius: 2px;
82
- background: var(--border-secondary);
83
- pointer-events: none;
84
- }
85
-
86
- /* ── Top bar ─────────────────────────────────────────────────────────── */
87
-
88
- .topBar {
89
- display: flex;
90
- align-items: center;
91
- justify-content: flex-end;
92
- gap: 2px;
93
- padding: 0 8px;
94
- height: 28px;
95
- border-bottom: 1px solid var(--border-primary);
96
- flex-shrink: 0;
97
- }
98
-
99
- .iconBtn {
100
- background: none;
101
- border: none;
102
- cursor: pointer;
103
- padding: 2px 6px;
104
- font-size: 13px;
105
- color: var(--text-secondary);
106
- border-radius: 4px;
107
- line-height: 1;
108
- transition: background 0.15s, color 0.15s;
109
- }
110
-
111
- .iconBtn:hover {
112
- background: var(--bg-hover);
113
- color: var(--text-primary);
114
- }
115
-
116
- /* ── Content ─────────────────────────────────────────────────────────── */
117
-
118
- .content {
119
- flex: 1;
120
- overflow: hidden;
121
- }
@@ -1,115 +0,0 @@
1
- import * as React from 'react';
2
- import { useState, useRef, useCallback, useEffect } from 'react';
3
- import { UIBadge } from '@kb-labs/studio-ui-kit';
4
- import { useDevToolsStore } from '@/hooks/use-devtools-store';
5
- import { useFeatureFlags } from '@/hooks/use-feature-flags';
6
- import { DevToolsLogFeed } from './devtools-log-feed';
7
- import styles from './devtools-panel.module.css';
8
-
9
- const MIN_HEIGHT = 120;
10
- const DEFAULT_HEIGHT = 280;
11
-
12
- export function DevToolsPanel() {
13
- const { isEnabled } = useFeatureFlags();
14
- if (!isEnabled('devtools-panel')) {return null;}
15
- return <DevToolsPanelInner />;
16
- }
17
-
18
- function DevToolsPanelInner() {
19
- const { channels } = useDevToolsStore();
20
- const [open, setOpen] = useState(false);
21
- const [maximized, setMaximized] = useState(false);
22
- const [height, setHeight] = useState(DEFAULT_HEIGHT);
23
-
24
- const errorCount = React.useMemo(() => {
25
- let count = 0;
26
- channels.forEach((ch) => {
27
- (ch.getEvents() as Array<{ status?: string }>).forEach((e) => {
28
- if (e.status === 'error' || e.status === 'warning') {count++;}
29
- });
30
- });
31
- return count;
32
- }, [channels]);
33
-
34
- // ── Drag to resize ──────────────────────────────────────────────────────
35
- const dragRef = useRef<{ startY: number; startH: number } | null>(null);
36
-
37
- const onResizeMouseDown = useCallback((e: React.MouseEvent) => {
38
- e.preventDefault();
39
- dragRef.current = { startY: e.clientY, startH: height };
40
-
41
- const onMove = (ev: MouseEvent) => {
42
- if (!dragRef.current) {return;}
43
- const delta = dragRef.current.startY - ev.clientY;
44
- setHeight(Math.max(MIN_HEIGHT, dragRef.current.startH + delta));
45
- setMaximized(false);
46
- };
47
- const onUp = () => {
48
- dragRef.current = null;
49
- window.removeEventListener('mousemove', onMove);
50
- window.removeEventListener('mouseup', onUp);
51
- };
52
- window.addEventListener('mousemove', onMove);
53
- window.addEventListener('mouseup', onUp);
54
- }, [height]);
55
-
56
- // ── Keyboard shortcut: Ctrl+Shift+D ────────────────────────────────────
57
- useEffect(() => {
58
- const handler = (e: KeyboardEvent) => {
59
- if (e.ctrlKey && e.shiftKey && e.key === 'D') {
60
- e.preventDefault();
61
- setOpen((v) => !v);
62
- }
63
- };
64
- window.addEventListener('keydown', handler);
65
- return () => window.removeEventListener('keydown', handler);
66
- }, []);
67
-
68
- const panelHeight = maximized ? 'calc(100vh - 48px)' : height;
69
- const toggleBottom = open ? (maximized ? 'calc(100vh - 48px)' : height) : 0;
70
-
71
- return (
72
- <>
73
- {/* Toggle button */}
74
- <button
75
- className={`${styles.toggleBtn} ${open ? styles.open : ''}`}
76
- style={{ bottom: toggleBottom }}
77
- onClick={() => setOpen((v) => !v)}
78
- >
79
- <span className={`${styles.toggleArrow} ${open ? styles.arrowDown : styles.arrowUp}`} />
80
- <span>DevTools</span>
81
- {errorCount > 0 && <UIBadge count={errorCount} size="small" style={{ marginLeft: 2 }} />}
82
- </button>
83
-
84
- {/* Floating panel */}
85
- {open && (
86
- <div className={styles.panel} style={{ height: panelHeight }}>
87
- <div className={styles.dragHandle} onMouseDown={onResizeMouseDown}>
88
- <div className={styles.dragGrip} />
89
- </div>
90
-
91
- <div className={styles.topBar}>
92
- <button
93
- className={styles.iconBtn}
94
- onClick={() => setMaximized((v) => !v)}
95
- title={maximized ? 'Restore' : 'Maximize'}
96
- >
97
- {maximized ? '⊡' : '⊞'}
98
- </button>
99
- <button
100
- className={styles.iconBtn}
101
- onClick={() => setOpen(false)}
102
- title="Close (Ctrl+Shift+D)"
103
- >
104
- ✕
105
- </button>
106
- </div>
107
-
108
- <div className={styles.content}>
109
- <DevToolsLogFeed />
110
- </div>
111
- </div>
112
- )}
113
- </>
114
- );
115
- }