@rozenite/vite-plugin 1.13.0 → 2.1.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 (57) hide show
  1. package/README.md +12 -0
  2. package/dist/bundle-dts.d.ts.map +1 -1
  3. package/dist/client-plugin.d.ts.map +1 -1
  4. package/dist/dev-host/assets/index-BgAxGQdD.js +13 -0
  5. package/dist/dev-host/assets/index-ClIYIppU.css +1 -0
  6. package/dist/dev-host/index.html +3 -3
  7. package/dist/dev-host/manifest.json +2 -2
  8. package/dist/index.cjs +34 -63
  9. package/dist/index.d.ts +9 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +34 -63
  12. package/dist/load-config.d.ts.map +1 -1
  13. package/dist/package-json.d.ts.map +1 -1
  14. package/dist/react-native-plugin.d.ts.map +1 -1
  15. package/dist/require-plugin.d.ts.map +1 -1
  16. package/dist/sdk-plugin.d.ts.map +1 -1
  17. package/dist/server-plugin.d.ts.map +1 -1
  18. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  19. package/dist/utils.d.ts.map +1 -1
  20. package/package.json +29 -30
  21. package/src/bundle-dts.ts +6 -14
  22. package/src/client-plugin.ts +13 -36
  23. package/src/dev-host/App.tsx +184 -414
  24. package/src/dev-host/components/DispatchForm.tsx +97 -121
  25. package/src/dev-host/components/FlowList.tsx +70 -102
  26. package/src/dev-host/components/MessageDetailsPane.tsx +50 -83
  27. package/src/dev-host/components/MessageLogPane.tsx +75 -66
  28. package/src/dev-host/components/PanelTabs.tsx +19 -10
  29. package/src/dev-host/constants.ts +0 -19
  30. package/src/dev-host/flow-runtime.ts +15 -12
  31. package/src/dev-host/index.html +1 -1
  32. package/src/dev-host/main.tsx +1 -10
  33. package/src/dev-host/styles.css +162 -830
  34. package/src/dev-host/types.ts +0 -9
  35. package/src/dev-host/utils.ts +1 -7
  36. package/src/dev-host/vite.config.mts +2 -0
  37. package/src/index.ts +16 -16
  38. package/src/load-config.ts +9 -20
  39. package/src/package-json.ts +1 -3
  40. package/src/react-native-plugin.ts +4 -2
  41. package/src/require-plugin.ts +6 -18
  42. package/src/sdk-plugin.ts +3 -0
  43. package/src/server-plugin.ts +3 -0
  44. package/src/utils.ts +1 -4
  45. package/dist/dev-host/assets/index-CkxvBMpp.css +0 -1
  46. package/dist/dev-host/assets/index-cNKHRwej.js +0 -11
  47. package/src/dev-host/components/MessagePayloadDetail.tsx +0 -32
  48. package/src/dev-host/components/ResizeHandle.tsx +0 -33
  49. package/src/dev-host/components/icons.tsx +0 -79
  50. package/src/dev-host/components/ui/Button.tsx +0 -91
  51. package/src/dev-host/components/ui/DropdownMenu.tsx +0 -84
  52. package/src/dev-host/components/ui/IconButton.tsx +0 -41
  53. package/src/dev-host/components/ui/Input.tsx +0 -73
  54. package/src/dev-host/components/ui/ScrollArea.tsx +0 -20
  55. package/src/dev-host/components/ui/Tabs.tsx +0 -166
  56. package/src/dev-host/components/ui/Textarea.tsx +0 -79
  57. package/src/dev-host/components/ui/ToggleGroup.tsx +0 -120
@@ -1,34 +1,14 @@
1
- import {
2
- useEffect,
3
- useRef,
4
- useState,
5
- type CSSProperties,
6
- type FormEvent,
7
- type PointerEvent as ReactPointerEvent,
8
- } from 'react';
9
- import {
10
- DEFAULT_COMMAND_WIDTH,
11
- DEFAULT_DEVTOOLS_HEIGHT,
12
- DETAILS_PANEL_WIDTH,
13
- MIN_COMMAND_WIDTH,
14
- MIN_DETAILS_WIDTH,
15
- MIN_DEVTOOLS_HEIGHT,
16
- MIN_IFRAME_HEIGHT,
17
- MIN_NARROW_IFRAME_HEIGHT,
18
- SPLITTER_SIZE,
19
- } from './constants.js';
1
+ import { useEffect, useRef, useState, type FormEvent } from 'react';
2
+ import { PluginHeader, PluginShell, Split, Tabs } from '@rozenite/ui';
20
3
  import type {
21
4
  DevHostFlowEntry,
22
5
  DevHostPanelEntry,
23
6
  DevHostPresetEntry,
24
7
  DevHostState,
25
8
  MessageEntry,
26
- ResizeHandleId,
27
- ResizeSession,
28
9
  } from './types.js';
29
10
  import { useFlowRunner } from './flow-runtime.js';
30
11
  import {
31
- clamp,
32
12
  createMessageEntry,
33
13
  formatPayloadForCommandInput,
34
14
  getInitialPanel,
@@ -41,11 +21,7 @@ import {
41
21
  } from './components/MessageDetailsPane.js';
42
22
  import { MessageLogPane } from './components/MessageLogPane.js';
43
23
  import { PanelTabs } from './components/PanelTabs.js';
44
- import { ResizeHandle } from './components/ResizeHandle.js';
45
- import { ToggleGroup } from './components/ui/ToggleGroup.js';
46
- import { RozeniteLogo } from './components/icons.js';
47
-
48
- type CSSVariables = CSSProperties & Record<`--${string}`, string>;
24
+ import './styles.css';
49
25
 
50
26
  type AppProps = DevHostState & {
51
27
  flows: DevHostFlowEntry[];
@@ -54,74 +30,67 @@ type AppProps = DevHostState & {
54
30
 
55
31
  type MobileDevtoolsTab = 'log' | 'actions';
56
32
 
57
- const getViewportMatch = () => {
58
- return window.matchMedia('(max-width: 960px)').matches;
33
+ const useNarrowViewport = () => {
34
+ const [isNarrow, setIsNarrow] = useState(() => window.matchMedia('(max-width: 960px)').matches);
35
+
36
+ useEffect(() => {
37
+ const mediaQuery = window.matchMedia('(max-width: 960px)');
38
+ const handleChange = (event: MediaQueryListEvent) => {
39
+ setIsNarrow(event.matches);
40
+ };
41
+
42
+ setIsNarrow(mediaQuery.matches);
43
+ mediaQuery.addEventListener('change', handleChange);
44
+
45
+ return () => mediaQuery.removeEventListener('change', handleChange);
46
+ }, []);
47
+
48
+ return isNarrow;
59
49
  };
60
50
 
61
51
  export const App = ({ packageName, packageDescription, panels, flows, presets }: AppProps) => {
62
- const [activePanel, setActivePanel] = useState<DevHostPanelEntry | null>(() => {
63
- return getInitialPanel(panels);
64
- });
52
+ const [activePanel, setActivePanel] = useState<DevHostPanelEntry | null>(() =>
53
+ getInitialPanel(panels),
54
+ );
65
55
  const [commandType, setCommandType] = useState('');
66
56
  const [commandPayload, setCommandPayload] = useState('');
67
57
  const [messages, setMessages] = useState<MessageEntry[]>([]);
68
58
  const [selectedMessageId, setSelectedMessageId] = useState<string | null>(null);
69
59
  const [isDetailsOpen, setIsDetailsOpen] = useState(false);
70
- const [devToolsHeight, setDevToolsHeight] = useState(DEFAULT_DEVTOOLS_HEIGHT);
71
- const [commandWidth, setCommandWidth] = useState(DEFAULT_COMMAND_WIDTH);
72
- const [detailsWidth, setDetailsWidth] = useState(DETAILS_PANEL_WIDTH);
73
- const [activeResizeHandle, setActiveResizeHandle] = useState<ResizeHandleId | null>(null);
74
- const [isNarrowViewport, setIsNarrowViewport] = useState(getViewportMatch);
75
60
  const [activeMobileTab, setActiveMobileTab] = useState<MobileDevtoolsTab>('log');
76
61
  const [iframeLoadNonce, setIframeLoadNonce] = useState(0);
77
- const workspaceRef = useRef<HTMLElement | null>(null);
78
- const logWorkspaceRef = useRef<HTMLDivElement | null>(null);
79
- const devtoolsRef = useRef<HTMLElement | null>(null);
80
- const resizeSessionRef = useRef<ResizeSession | null>(null);
81
62
  const iframeRef = useRef<HTMLIFrameElement | null>(null);
82
63
  const lastAutoRunLoadRef = useRef(0);
83
- const { flowRuns, runFlow, stopFlow, hasRunningFlow, registerMessage, resetMessages } = useFlowRunner({
84
- sendMessage: (type, payload) => {
85
- iframeRef.current?.contentWindow?.postMessage(
86
- {
87
- pluginId: packageName,
88
- type,
89
- payload,
90
- },
91
- '*',
92
- );
93
-
94
- appendMessage({
95
- direction: 'in',
96
- type,
97
- payload,
98
- });
99
- },
100
- });
64
+ const isNarrowViewport = useNarrowViewport();
65
+ const { flowRuns, runFlow, stopFlow, hasRunningFlow, registerMessage, resetMessages } =
66
+ useFlowRunner({
67
+ sendMessage: (type, payload) => {
68
+ iframeRef.current?.contentWindow?.postMessage(
69
+ { pluginId: packageName, type, payload },
70
+ '*',
71
+ );
72
+ appendMessage({ direction: 'in', type, payload });
73
+ },
74
+ });
101
75
 
102
76
  const activeSource = activePanel?.source ?? '';
103
77
  const activeLabel = activePanel?.label ?? '';
104
- const emptyState = panels.length === 0;
105
78
  const selectedMessage = messages.find((message) => message.id === selectedMessageId) ?? null;
79
+ const panelDescription = packageDescription.trim();
106
80
  const trimmedCommandType = commandType.trim();
107
81
  const trimmedCommandPayload = commandPayload.trim();
108
- const hasCommandType = trimmedCommandType.length > 0;
109
- const hasValidCommandPayload = (() => {
110
- if (!trimmedCommandPayload) {
82
+ const canDispatch = (() => {
83
+ if (!trimmedCommandType || !trimmedCommandPayload) {
111
84
  return false;
112
85
  }
113
86
 
114
87
  try {
115
- JSON.parse(commandPayload);
88
+ JSON.parse(trimmedCommandPayload);
116
89
  return true;
117
90
  } catch {
118
91
  return false;
119
92
  }
120
93
  })();
121
- const canDispatch = hasCommandType && hasValidCommandPayload;
122
- const panelDescription = packageDescription.trim();
123
- const isDetailsVisible = isDetailsOpen && selectedMessage !== null;
124
- const iframeMinHeight = isNarrowViewport ? MIN_NARROW_IFRAME_HEIGHT : MIN_IFRAME_HEIGHT;
125
94
 
126
95
  useEffect(() => {
127
96
  document.title = `${packageName} Dev Host`;
@@ -145,106 +114,6 @@ export const App = ({ packageName, packageDescription, panels, flows, presets }:
145
114
  });
146
115
  }, [flows, iframeLoadNonce, runFlow]);
147
116
 
148
- useEffect(() => {
149
- const mediaQuery = window.matchMedia('(max-width: 960px)');
150
-
151
- const handleChange = (event: MediaQueryListEvent) => {
152
- setIsNarrowViewport(event.matches);
153
- };
154
-
155
- setIsNarrowViewport(mediaQuery.matches);
156
- mediaQuery.addEventListener('change', handleChange);
157
-
158
- return () => {
159
- mediaQuery.removeEventListener('change', handleChange);
160
- };
161
- }, []);
162
-
163
- useEffect(() => {
164
- const workspace = workspaceRef.current;
165
- if (!workspace) {
166
- return;
167
- }
168
-
169
- const bounds = workspace.getBoundingClientRect();
170
- const maxHeight = Math.max(MIN_DEVTOOLS_HEIGHT, bounds.height - iframeMinHeight - 12);
171
-
172
- setDevToolsHeight((current) => clamp(current, MIN_DEVTOOLS_HEIGHT, maxHeight));
173
- }, [iframeMinHeight]);
174
-
175
- useEffect(() => {
176
- if (!isNarrowViewport) {
177
- return;
178
- }
179
-
180
- setActiveMobileTab('log');
181
- }, [isNarrowViewport]);
182
-
183
- const selectPanel = (value: string) => {
184
- const nextPanel = panels.find((panel) => panel.source === value);
185
- if (!nextPanel) {
186
- return;
187
- }
188
-
189
- setActivePanel(nextPanel);
190
- const nextUrl = new URL(window.location.href);
191
- nextUrl.searchParams.set('panel', nextPanel.label);
192
- window.history.replaceState(null, '', nextUrl);
193
- };
194
-
195
- const appendMessage = (input: Omit<MessageEntry, 'id' | 'date'>) => {
196
- const nextEntry = createMessageEntry(input);
197
-
198
- registerMessage(nextEntry);
199
- setMessages((current) => [nextEntry, ...current]);
200
- };
201
-
202
- useEffect(() => {
203
- const handlePointerMove = (event: PointerEvent) => {
204
- resizeSessionRef.current?.onMove(event);
205
- };
206
-
207
- const stopDragging = () => {
208
- const session = resizeSessionRef.current;
209
- if (!session) {
210
- return;
211
- }
212
-
213
- if (session.element.hasPointerCapture(session.pointerId)) {
214
- session.element.releasePointerCapture(session.pointerId);
215
- }
216
-
217
- resizeSessionRef.current = null;
218
- setActiveResizeHandle(null);
219
- };
220
-
221
- window.addEventListener('pointermove', handlePointerMove);
222
- window.addEventListener('pointerup', stopDragging);
223
- window.addEventListener('pointercancel', stopDragging);
224
-
225
- return () => {
226
- window.removeEventListener('pointermove', handlePointerMove);
227
- window.removeEventListener('pointerup', stopDragging);
228
- window.removeEventListener('pointercancel', stopDragging);
229
- };
230
- }, []);
231
-
232
- const startResize = (
233
- handleId: ResizeHandleId,
234
- event: ReactPointerEvent<HTMLDivElement>,
235
- onMove: (event: PointerEvent) => void,
236
- ) => {
237
- event.preventDefault();
238
- event.currentTarget.setPointerCapture(event.pointerId);
239
- resizeSessionRef.current = {
240
- handleId,
241
- pointerId: event.pointerId,
242
- element: event.currentTarget,
243
- onMove,
244
- };
245
- setActiveResizeHandle(handleId);
246
- };
247
-
248
117
  useEffect(() => {
249
118
  const handleMessage = (event: MessageEvent) => {
250
119
  if (event.source !== iframeRef.current?.contentWindow) {
@@ -254,19 +123,14 @@ export const App = ({ packageName, packageDescription, panels, flows, presets }:
254
123
  if (
255
124
  typeof event.data !== 'object' ||
256
125
  event.data === null ||
257
- !('type' in event.data) ||
258
126
  event.data.type !== 'rozenite-message' ||
259
- !('payload' in event.data)
127
+ !('payload' in event.data) ||
128
+ !isPluginMessage(event.data.payload)
260
129
  ) {
261
130
  return;
262
131
  }
263
132
 
264
133
  const payload = event.data.payload;
265
-
266
- if (!isPluginMessage(payload)) {
267
- return;
268
- }
269
-
270
134
  appendMessage({
271
135
  direction: 'out',
272
136
  type: payload.type,
@@ -275,20 +139,25 @@ export const App = ({ packageName, packageDescription, panels, flows, presets }:
275
139
  };
276
140
 
277
141
  window.addEventListener('message', handleMessage);
278
-
279
- return () => {
280
- window.removeEventListener('message', handleMessage);
281
- };
142
+ return () => window.removeEventListener('message', handleMessage);
282
143
  }, []);
283
144
 
284
- const resetForm = () => {
285
- setCommandType('');
286
- setCommandPayload('');
145
+ const appendMessage = (input: Omit<MessageEntry, 'id' | 'date'>) => {
146
+ const nextEntry = createMessageEntry(input);
147
+ registerMessage(nextEntry);
148
+ setMessages((current) => [nextEntry, ...current]);
287
149
  };
288
150
 
289
- const applyPreset = (preset: DevHostPresetEntry) => {
290
- setCommandType(preset.type);
291
- setCommandPayload(formatPayloadForCommandInput(preset.payload));
151
+ const selectPanel = (value: string) => {
152
+ const nextPanel = panels.find((panel) => panel.source === value);
153
+ if (!nextPanel) {
154
+ return;
155
+ }
156
+
157
+ setActivePanel(nextPanel);
158
+ const nextUrl = new URL(window.location.href);
159
+ nextUrl.searchParams.set('panel', nextPanel.label);
160
+ window.history.replaceState(null, '', nextUrl);
292
161
  };
293
162
 
294
163
  const clearMessages = () => {
@@ -299,269 +168,170 @@ export const App = ({ packageName, packageDescription, panels, flows, presets }:
299
168
  setActiveMobileTab('log');
300
169
  };
301
170
 
302
- const resizeDevtoolsHeight = (event: PointerEvent) => {
303
- const workspace = workspaceRef.current;
304
- if (!workspace) {
305
- return;
306
- }
307
-
308
- const bounds = workspace.getBoundingClientRect();
309
- const nextHeight = bounds.bottom - event.clientY;
310
- const maxHeight = Math.max(MIN_DEVTOOLS_HEIGHT, bounds.height - iframeMinHeight - 12);
311
-
312
- setDevToolsHeight(clamp(nextHeight, MIN_DEVTOOLS_HEIGHT, maxHeight));
171
+ const resetForm = () => {
172
+ setCommandType('');
173
+ setCommandPayload('');
313
174
  };
314
175
 
315
- const resizeCommandPane = (event: PointerEvent) => {
316
- const devtools = devtoolsRef.current;
317
- if (!devtools) {
318
- return;
319
- }
320
-
321
- const bounds = devtools.getBoundingClientRect();
322
- const nextWidth = bounds.right - event.clientX;
323
- const maxWidth = Math.max(MIN_COMMAND_WIDTH, bounds.width - 280);
324
-
325
- setCommandWidth(clamp(nextWidth, MIN_COMMAND_WIDTH, maxWidth));
176
+ const applyPreset = (preset: DevHostPresetEntry) => {
177
+ setCommandType(preset.type);
178
+ setCommandPayload(formatPayloadForCommandInput(preset.payload));
326
179
  };
327
180
 
328
- const resizeDetailsPane = (event: PointerEvent) => {
329
- const logWorkspace = logWorkspaceRef.current;
330
- if (!logWorkspace || !isDetailsVisible || isNarrowViewport) {
181
+ const handleDispatch = (event: FormEvent<HTMLFormElement>) => {
182
+ event.preventDefault();
183
+ if (!canDispatch) {
331
184
  return;
332
185
  }
333
186
 
334
- const bounds = logWorkspace.getBoundingClientRect();
335
- const nextWidth = bounds.right - event.clientX;
336
- const maxWidth = Math.max(MIN_DETAILS_WIDTH, bounds.width - 280 - SPLITTER_SIZE);
337
-
338
- setDetailsWidth(clamp(nextWidth, MIN_DETAILS_WIDTH, maxWidth));
187
+ const payload = JSON.parse(trimmedCommandPayload) as unknown;
188
+ iframeRef.current?.contentWindow?.postMessage(
189
+ { pluginId: packageName, type: trimmedCommandType, payload },
190
+ '*',
191
+ );
192
+ appendMessage({
193
+ direction: 'in',
194
+ type: trimmedCommandType,
195
+ payload,
196
+ });
197
+ resetForm();
339
198
  };
340
199
 
341
200
  const handleMessageSelect = (messageId: string) => {
342
201
  setSelectedMessageId(messageId);
343
202
  setIsDetailsOpen(true);
344
-
345
203
  if (isNarrowViewport) {
346
204
  setActiveMobileTab('log');
347
205
  }
348
206
  };
349
207
 
350
- const handleDetailsClose = () => {
208
+ const useMessage = (message: MessageEntry) => {
209
+ const nextValues = getDispatcherValuesFromMessage(message);
210
+ setCommandType(nextValues.commandType);
211
+ setCommandPayload(nextValues.commandPayload);
351
212
  setIsDetailsOpen(false);
352
-
353
213
  if (isNarrowViewport) {
354
- setActiveMobileTab('log');
214
+ setActiveMobileTab('actions');
355
215
  }
356
216
  };
357
217
 
358
- const handleMobileTabChange = (value: string) => {
359
- const nextTab = value as MobileDevtoolsTab;
360
- setActiveMobileTab(nextTab);
361
- };
362
-
363
- const handleDispatch = (event: FormEvent<HTMLFormElement>) => {
364
- event.preventDefault();
365
-
366
- if (!canDispatch) {
367
- return;
368
- }
369
-
370
- const type = trimmedCommandType;
371
-
372
- let payload: unknown;
373
-
374
- try {
375
- payload = JSON.parse(commandPayload);
376
- } catch (error) {
377
- window.alert(
378
- error instanceof Error
379
- ? `Payload must be valid JSON. ${error.message}`
380
- : 'Payload must be valid JSON.',
381
- );
382
- return;
383
- }
384
-
385
- iframeRef.current?.contentWindow?.postMessage({ pluginId: packageName, type, payload }, '*');
218
+ const messageLog = (
219
+ <MessageLogPane
220
+ messages={messages}
221
+ onSelectMessage={handleMessageSelect}
222
+ onClearMessages={clearMessages}
223
+ />
224
+ );
386
225
 
387
- appendMessage({ direction: 'in', type, payload });
226
+ const actions = (
227
+ <DispatchForm
228
+ commandType={commandType}
229
+ commandPayload={commandPayload}
230
+ flows={flows}
231
+ flowRuns={flowRuns}
232
+ hasRunningFlow={hasRunningFlow}
233
+ presets={presets}
234
+ canDispatch={canDispatch}
235
+ onRunFlow={runFlow}
236
+ onStopFlow={stopFlow}
237
+ onCommandTypeChange={setCommandType}
238
+ onCommandPayloadChange={setCommandPayload}
239
+ onApplyPreset={applyPreset}
240
+ onReset={resetForm}
241
+ onSubmit={handleDispatch}
242
+ />
243
+ );
388
244
 
389
- resetForm();
390
- };
245
+ const devtools = isNarrowViewport ? (
246
+ <Tabs
247
+ className="dev-host-mobile-tabs"
248
+ value={activeMobileTab}
249
+ onValueChange={(value) => setActiveMobileTab(value as MobileDevtoolsTab)}
250
+ >
251
+ <Tabs.List className="dev-host-mobile-tab-list" aria-label="DevTools sections">
252
+ <Tabs.Tab value="log">Log</Tabs.Tab>
253
+ <Tabs.Tab value="actions">Actions</Tabs.Tab>
254
+ </Tabs.List>
255
+ <Tabs.Panel value="log" className="dev-host-mobile-tab-panel">
256
+ {isDetailsOpen && selectedMessage ? (
257
+ <MessageDetailsPane
258
+ selectedMessage={selectedMessage}
259
+ onClose={() => setIsDetailsOpen(false)}
260
+ onUseMessage={useMessage}
261
+ />
262
+ ) : (
263
+ messageLog
264
+ )}
265
+ </Tabs.Panel>
266
+ <Tabs.Panel value="actions" className="dev-host-mobile-tab-panel">
267
+ {actions}
268
+ </Tabs.Panel>
269
+ </Tabs>
270
+ ) : (
271
+ <Split direction="horizontal" className="dev-host-devtools">
272
+ <Split.Pane minSize={35}>
273
+ {isDetailsOpen && selectedMessage ? (
274
+ <Split direction="horizontal">
275
+ <Split.Pane minSize={55}>{messageLog}</Split.Pane>
276
+ <Split.Handle />
277
+ <Split.Pane defaultSize={30} minSize={20}>
278
+ <MessageDetailsPane
279
+ selectedMessage={selectedMessage}
280
+ onClose={() => setIsDetailsOpen(false)}
281
+ onUseMessage={useMessage}
282
+ />
283
+ </Split.Pane>
284
+ </Split>
285
+ ) : (
286
+ messageLog
287
+ )}
288
+ </Split.Pane>
289
+ <Split.Handle />
290
+ <Split.Pane defaultSize={28} minSize={20}>
291
+ {actions}
292
+ </Split.Pane>
293
+ </Split>
294
+ );
391
295
 
392
296
  return (
393
- <div className="rz-shell">
394
- <header className="rz-topbar">
395
- <div className="rz-topbar-brand" aria-label="Rozenite">
396
- <RozeniteLogo />
297
+ <PluginShell className="dev-host">
298
+ <PluginHeader className="dev-host-header">
299
+ <div className="dev-host-header-title">
300
+ <PluginHeader.Title>Rozenite Dev Host</PluginHeader.Title>
301
+ <PluginHeader.Subtitle className="dev-host-header-subtitle" title={panelDescription}>
302
+ {packageName}
303
+ </PluginHeader.Subtitle>
397
304
  </div>
398
-
399
- <div className="rz-topbar-panel-picker" title={panelDescription || undefined}>
305
+ <PluginHeader.Actions>
306
+ <PluginHeader.ThemeSwitcher />
400
307
  <PanelTabs panels={panels} activeSource={activeSource} onValueChange={selectPanel} />
401
- </div>
402
- </header>
403
-
404
- <main
405
- ref={workspaceRef}
406
- className="rz-workspace"
407
- style={{ '--rz-devtools-height': `${devToolsHeight}px` } as CSSVariables}
408
- >
409
- <section className="rz-card">
410
- {emptyState ? (
411
- <div className="rz-empty-state">No panels were defined in rozenite.config.ts.</div>
412
- ) : (
308
+ </PluginHeader.Actions>
309
+ </PluginHeader>
310
+
311
+ <PluginShell.Body className="dev-host-body">
312
+ <Split direction="vertical" className="dev-host-main-split" autoSaveId="dev-host">
313
+ <Split.Pane defaultSize={64} minSize={30} className="dev-host-preview">
314
+ {panels.length === 0 ? (
315
+ <div className="flex h-full items-center justify-center p-8 text-center text-sm text-muted-foreground">
316
+ No panels were defined in rozenite.config.ts.
317
+ </div>
318
+ ) : (
413
319
  <iframe
414
320
  key={activeSource}
415
321
  ref={iframeRef}
416
322
  title={activeLabel || 'Rozenite panel preview'}
417
323
  src={activeSource}
418
- className="rz-iframe"
419
- data-resizing={activeResizeHandle === 'devtools-height'}
324
+ className="dev-host-iframe"
420
325
  onLoad={() => setIframeLoadNonce((value) => value + 1)}
421
326
  />
422
- )}
423
- </section>
424
-
425
- <ResizeHandle
426
- className="rz-resize-handle"
427
- isDragging={activeResizeHandle === 'devtools-height'}
428
- orientation="horizontal"
429
- label="Resize DevTools"
430
- onPointerDown={(event) => startResize('devtools-height', event, resizeDevtoolsHeight)}
431
- />
432
-
433
- {isNarrowViewport ? (
434
- <section ref={devtoolsRef} className="rz-devtools-mobile">
435
- <div className="rz-devtools-mobile-tabs">
436
- <div className="rz-devtools-mobile-toggle">
437
- <ToggleGroup
438
- aria-label="DevTools sections"
439
- value={activeMobileTab}
440
- onChange={handleMobileTabChange}
441
- options={[
442
- { key: 'log', label: 'Log' },
443
- { key: 'actions', label: 'Actions' },
444
- ]}
445
- />
446
- </div>
447
-
448
- <div className="rz-devtools-mobile-panel">
449
- {activeMobileTab === 'log' ? (
450
- isDetailsVisible ? (
451
- <MessageDetailsPane
452
- selectedMessage={selectedMessage}
453
- isOpen={true}
454
- isNarrowViewport={true}
455
- activeResizeHandle={activeResizeHandle}
456
- onClose={handleDetailsClose}
457
- onUseMessage={(message) => {
458
- const nextValues = getDispatcherValuesFromMessage(message);
459
- setCommandType(nextValues.commandType);
460
- setCommandPayload(nextValues.commandPayload);
461
- setIsDetailsOpen(false);
462
- setActiveMobileTab('actions');
463
- }}
464
- onResizeStart={(event) => startResize('details-width', event, resizeDetailsPane)}
465
- />
466
- ) : (
467
- <MessageLogPane
468
- messages={messages}
469
- selectedMessageId={selectedMessageId}
470
- onSelectMessage={handleMessageSelect}
471
- onClearMessages={clearMessages}
472
- />
473
- )
474
- ) : (
475
- <DispatchForm
476
- commandType={commandType}
477
- commandPayload={commandPayload}
478
- flows={flows}
479
- flowRuns={flowRuns}
480
- hasRunningFlow={hasRunningFlow}
481
- presets={presets}
482
- canDispatch={canDispatch}
483
- onRunFlow={runFlow}
484
- onStopFlow={stopFlow}
485
- onCommandTypeChange={setCommandType}
486
- onCommandPayloadChange={setCommandPayload}
487
- onApplyPreset={applyPreset}
488
- onReset={resetForm}
489
- onSubmit={handleDispatch}
490
- />
491
- )}
492
- </div>
493
- </div>
494
- </section>
495
- ) : (
496
- <section
497
- ref={devtoolsRef}
498
- className="rz-devtools"
499
- style={
500
- {
501
- '--rz-command-width': `${commandWidth}px`,
502
- '--rz-command-splitter-width': `${SPLITTER_SIZE}px`,
503
- } as CSSVariables
504
- }
505
- >
506
- <div
507
- ref={logWorkspaceRef}
508
- className="rz-log-workspace"
509
- style={
510
- {
511
- '--rz-details-width': isDetailsVisible ? `${detailsWidth}px` : '0px',
512
- '--rz-details-splitter-width': isDetailsVisible ? `${SPLITTER_SIZE}px` : '0px',
513
- } as CSSVariables
514
- }
515
- >
516
- <MessageLogPane
517
- messages={messages}
518
- selectedMessageId={selectedMessageId}
519
- onSelectMessage={handleMessageSelect}
520
- onClearMessages={clearMessages}
521
- />
522
-
523
- <MessageDetailsPane
524
- selectedMessage={selectedMessage}
525
- isOpen={isDetailsOpen}
526
- isNarrowViewport={false}
527
- activeResizeHandle={activeResizeHandle}
528
- onClose={handleDetailsClose}
529
- onUseMessage={(message) => {
530
- const nextValues = getDispatcherValuesFromMessage(message);
531
- setCommandType(nextValues.commandType);
532
- setCommandPayload(nextValues.commandPayload);
533
- }}
534
- onResizeStart={(event) => startResize('details-width', event, resizeDetailsPane)}
535
- />
536
- </div>
537
-
538
- <ResizeHandle
539
- className="rz-column-resize-handle"
540
- isDragging={activeResizeHandle === 'command-width'}
541
- orientation="vertical"
542
- label="Resize command dispatcher"
543
- onPointerDown={(event) => startResize('command-width', event, resizeCommandPane)}
544
- />
545
-
546
- <DispatchForm
547
- commandType={commandType}
548
- commandPayload={commandPayload}
549
- flows={flows}
550
- flowRuns={flowRuns}
551
- hasRunningFlow={hasRunningFlow}
552
- presets={presets}
553
- canDispatch={canDispatch}
554
- onRunFlow={runFlow}
555
- onStopFlow={stopFlow}
556
- onCommandTypeChange={setCommandType}
557
- onCommandPayloadChange={setCommandPayload}
558
- onApplyPreset={applyPreset}
559
- onReset={resetForm}
560
- onSubmit={handleDispatch}
561
- />
562
- </section>
563
- )}
564
- </main>
565
- </div>
327
+ )}
328
+ </Split.Pane>
329
+ <Split.Handle />
330
+ <Split.Pane defaultSize={36} minSize={25}>
331
+ {devtools}
332
+ </Split.Pane>
333
+ </Split>
334
+ </PluginShell.Body>
335
+ </PluginShell>
566
336
  );
567
337
  };