@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,13 +1,8 @@
1
+ import { Button, Input, Select, Tabs, Textarea } from '@rozenite/ui';
2
+ import { RotateCcw, Send } from 'lucide-react';
1
3
  import { useState, type FormEvent } from 'react';
2
4
  import type { DevHostFlowEntry, DevHostFlowRunState, DevHostPresetEntry } from '../types.js';
3
- import { ClearIcon, PresetsIcon, SendIcon } from './icons.js';
4
5
  import { FlowList } from './FlowList.js';
5
- import { DropdownMenu, type DropdownMenuItem } from './ui/DropdownMenu.js';
6
- import { IconButton } from './ui/IconButton.js';
7
- import { Input } from './ui/Input.js';
8
- import { ScrollArea } from './ui/ScrollArea.js';
9
- import { Textarea } from './ui/Textarea.js';
10
- import { ToggleGroup } from './ui/ToggleGroup.js';
11
6
 
12
7
  type DispatchFormProps = {
13
8
  commandType: string;
@@ -43,127 +38,108 @@ export const DispatchForm = ({
43
38
  onSubmit,
44
39
  }: DispatchFormProps) => {
45
40
  const [activeTab, setActiveTab] = useState<'dispatch' | 'flows'>('dispatch');
46
- const presetItems: DropdownMenuItem<DevHostPresetEntry>[] = presets.map((preset) => ({
47
- id: preset.displayName,
48
- label: preset.displayName,
49
- item: preset,
50
- }));
51
- const presetButton = (
52
- <IconButton
53
- type="button"
54
- variant="default"
55
- aria-label={presets.length > 0 ? 'Open presets' : 'No presets available'}
56
- title={presets.length > 0 ? 'Presets' : 'No presets available'}
57
- disabled={presets.length === 0}
58
- overrides={{
59
- BaseButton: {
60
- style: {
61
- borderRadius: '6px',
62
- backgroundColor: 'rgba(255, 255, 255, 0.04)',
63
- color: 'rgba(255, 255, 255, 0.88)',
64
- },
65
- },
66
- }}
67
- >
68
- <PresetsIcon />
69
- </IconButton>
70
- );
71
41
 
72
42
  return (
73
- <div className="rz-pane">
74
- <div className="rz-sidebar">
75
- <div className="rz-sidebar-header">
76
- <div className="rz-sidebar-title">Actions</div>
77
- </div>
43
+ <section className="dev-host-pane">
44
+ <header className="dev-host-pane-header">
45
+ <h2 className="dev-host-pane-title">Actions</h2>
46
+ </header>
78
47
 
79
- <ScrollArea className="rz-sidebar-scroll">
80
- <div className="rz-action-tabs rz-command-form">
81
- <div className="rz-action-tabs-header">
82
- <ToggleGroup
83
- aria-label="Action modes"
84
- value={activeTab}
85
- onChange={(value) => setActiveTab(value as 'dispatch' | 'flows')}
86
- options={[
87
- { key: 'dispatch', label: 'Dispatch' },
88
- { key: 'flows', label: 'Flows' },
89
- ]}
90
- />
91
-
92
- {activeTab === 'dispatch'
93
- ? presets.length > 0
94
- ? <DropdownMenu items={presetItems} onSelect={onApplyPreset}>{presetButton}</DropdownMenu>
95
- : presetButton
96
- : null}
97
- </div>
48
+ <Tabs
49
+ className="flex min-h-0 flex-1 flex-col"
50
+ value={activeTab}
51
+ onValueChange={(value) => setActiveTab(value as 'dispatch' | 'flows')}
52
+ >
53
+ <div className="dev-host-tabs-header">
54
+ <Tabs.List aria-label="Action modes">
55
+ <Tabs.Tab value="dispatch">Dispatch</Tabs.Tab>
56
+ <Tabs.Tab value="flows">Flows</Tabs.Tab>
57
+ </Tabs.List>
98
58
 
99
- {activeTab === 'dispatch' ? (
100
- <div className="rz-action-tab-panel">
101
- <form className="rz-action-form" onSubmit={onSubmit}>
102
- <div className="rz-field">
103
- <label className="rz-label" htmlFor="command-type">
104
- Command
105
- </label>
106
- <Input
107
- id="command-type"
108
- value={commandType}
109
- onChange={(event) => onCommandTypeChange(event.currentTarget.value)}
110
- placeholder="get-snapshot"
111
- spellCheck={false}
112
- />
113
- </div>
59
+ {activeTab === 'dispatch' ? (
60
+ <Select
61
+ value={null}
62
+ onValueChange={(value) => {
63
+ const preset = presets.find((item) => item.name === value);
64
+ if (preset) {
65
+ onApplyPreset(preset);
66
+ }
67
+ }}
68
+ >
69
+ <Select.Trigger
70
+ className="w-auto"
71
+ aria-label={presets.length > 0 ? 'Open presets' : 'No presets available'}
72
+ disabled={presets.length === 0}
73
+ title={presets.length > 0 ? 'Presets' : 'No presets available'}
74
+ >
75
+ <Select.Value>Presets</Select.Value>
76
+ </Select.Trigger>
77
+ <Select.Content align="end">
78
+ {presets.map((preset) => (
79
+ <Select.Item key={preset.name} value={preset.name}>
80
+ {preset.displayName}
81
+ </Select.Item>
82
+ ))}
83
+ </Select.Content>
84
+ </Select>
85
+ ) : null}
86
+ </div>
114
87
 
115
- <div className="rz-field">
116
- <label className="rz-label" htmlFor="command-payload">
117
- Payload
118
- </label>
119
- <Textarea
120
- id="command-payload"
121
- value={commandPayload}
122
- onChange={(event) => onCommandPayloadChange(event.currentTarget.value)}
123
- placeholder='{"example": true}'
124
- spellCheck={false}
125
- />
126
- </div>
88
+ <Tabs.Panel value="dispatch" className="dev-host-tab-panel">
89
+ <form className="dev-host-form" onSubmit={onSubmit}>
90
+ <label className="dev-host-field">
91
+ <span className="dev-host-label">Command</span>
92
+ <Input
93
+ value={commandType}
94
+ onChange={(event) => onCommandTypeChange(event.currentTarget.value)}
95
+ placeholder="get-snapshot"
96
+ spellCheck={false}
97
+ />
98
+ </label>
127
99
 
128
- <div className="rz-button-row">
129
- <IconButton
130
- type="button"
131
- variant="default"
132
- aria-label="Reset dispatcher"
133
- title="Reset dispatcher"
134
- onClick={onReset}
135
- >
136
- <ClearIcon />
137
- </IconButton>
100
+ <label className="dev-host-field">
101
+ <span className="dev-host-label">Payload</span>
102
+ <Textarea
103
+ value={commandPayload}
104
+ onChange={(event) => onCommandPayloadChange(event.currentTarget.value)}
105
+ placeholder='{"example": true}'
106
+ spellCheck={false}
107
+ />
108
+ </label>
138
109
 
139
- <IconButton
140
- type="submit"
141
- variant="primary"
142
- aria-label="Dispatch message"
143
- title="Dispatch message"
144
- disabled={!canDispatch}
145
- >
146
- <SendIcon />
147
- </IconButton>
148
- </div>
149
- </form>
150
- </div>
151
- ) : null}
110
+ <div className="dev-host-actions">
111
+ <Button
112
+ variant="outline"
113
+ size="icon"
114
+ onClick={onReset}
115
+ aria-label="Reset dispatcher"
116
+ title="Reset dispatcher"
117
+ >
118
+ <RotateCcw />
119
+ </Button>
120
+ <Button
121
+ type="submit"
122
+ size="icon"
123
+ disabled={!canDispatch}
124
+ aria-label="Dispatch message"
125
+ title="Dispatch message"
126
+ >
127
+ <Send />
128
+ </Button>
129
+ </div>
130
+ </form>
131
+ </Tabs.Panel>
152
132
 
153
- {activeTab === 'flows' ? (
154
- <div className="rz-action-tab-panel">
155
- <FlowList
156
- flows={flows}
157
- flowRuns={flowRuns}
158
- hasRunningFlow={hasRunningFlow}
159
- onRunFlow={onRunFlow}
160
- onStopFlow={onStopFlow}
161
- />
162
- </div>
163
- ) : null}
164
- </div>
165
- </ScrollArea>
166
- </div>
167
- </div>
133
+ <Tabs.Panel value="flows" className="dev-host-tab-panel">
134
+ <FlowList
135
+ flows={flows}
136
+ flowRuns={flowRuns}
137
+ hasRunningFlow={hasRunningFlow}
138
+ onRunFlow={onRunFlow}
139
+ onStopFlow={onStopFlow}
140
+ />
141
+ </Tabs.Panel>
142
+ </Tabs>
143
+ </section>
168
144
  );
169
145
  };
@@ -1,6 +1,7 @@
1
+ import { Badge, Button, EmptyState } from '@rozenite/ui';
2
+ import { Play, Square } from 'lucide-react';
1
3
  import { formatPayloadForCommandInput } from '../utils.js';
2
4
  import type { DevHostFlowEntry, DevHostFlowRunState } from '../types.js';
3
- import { Button } from './ui/Button.js';
4
5
 
5
6
  type FlowListProps = {
6
7
  flows: DevHostFlowEntry[];
@@ -11,129 +12,96 @@ type FlowListProps = {
11
12
  };
12
13
 
13
14
  const getStatusLabel = (status: DevHostFlowRunState['status']) => {
14
- if (status === 'running') {
15
- return 'Running';
15
+ switch (status) {
16
+ case 'running':
17
+ return 'Running';
18
+ case 'succeeded':
19
+ return 'Completed';
20
+ case 'failed':
21
+ return 'Failed';
22
+ case 'aborted':
23
+ return 'Stopped';
16
24
  }
17
-
18
- if (status === 'succeeded') {
19
- return 'Completed';
20
- }
21
-
22
- if (status === 'failed') {
23
- return 'Failed';
24
- }
25
-
26
- if (status === 'aborted') {
27
- return 'Stopped';
28
- }
29
-
30
- return status;
31
25
  };
32
26
 
33
- export const FlowList = ({ flows, flowRuns, hasRunningFlow, onRunFlow, onStopFlow }: FlowListProps) => {
27
+ export const FlowList = ({
28
+ flows,
29
+ flowRuns,
30
+ hasRunningFlow,
31
+ onRunFlow,
32
+ onStopFlow,
33
+ }: FlowListProps) => {
34
34
  if (flows.length === 0) {
35
- return <div className="rz-flow-empty-state">No flows were configured for this plugin.</div>;
35
+ return (
36
+ <EmptyState
37
+ className="h-full"
38
+ title="No flows configured"
39
+ description="Add flows to rozenite.config.ts to run them here."
40
+ />
41
+ );
36
42
  }
37
43
 
38
- const hasExecutionState = flowRuns.length > 0;
39
-
40
44
  return (
41
- <div className="rz-flow-panel">
42
- <div className="rz-field">
43
- <div className="rz-label">Available flows</div>
44
-
45
- <div className="rz-flow-list">
46
- {flows.map((flow, index) => {
47
- const isActive = hasRunningFlow(flow.name);
45
+ <div className="dev-host-flow-list">
46
+ <div className="dev-host-field">
47
+ <span className="dev-host-label">Available flows</span>
48
+ <div className="grid gap-2">
49
+ {flows.map((flow) => {
50
+ const isRunning = hasRunningFlow(flow.name);
48
51
 
49
52
  return (
50
53
  <Button
51
- key={`${flow.name}-${index}`}
52
- type="button"
54
+ key={flow.name}
55
+ variant={isRunning ? 'secondary' : 'outline'}
56
+ className="dev-host-flow-button"
53
57
  onClick={() => onRunFlow(flow)}
54
- variant="default"
55
- isSelected={isActive}
56
- title={`Run ${flow.displayName}`}
57
- overrides={{
58
- BaseButton: {
59
- style: {
60
- display: 'flex',
61
- alignItems: 'center',
62
- justifyContent: 'space-between',
63
- gap: '12px',
64
- width: '100%',
65
- borderRadius: '8px',
66
- backgroundColor: isActive ? 'rgba(130, 50, 255, 0.14)' : 'rgba(255, 255, 255, 0.03)',
67
- borderColor: isActive ? 'rgba(130, 50, 255, 0.45)' : 'rgba(255, 255, 255, 0.08)',
68
- color: 'rgba(255, 255, 255, 0.88)',
69
- paddingTop: '10px',
70
- paddingRight: '12px',
71
- paddingBottom: '10px',
72
- paddingLeft: '12px',
73
- textAlign: 'left',
74
- },
75
- },
76
- }}
58
+ aria-label={`Run ${flow.displayName}`}
77
59
  >
78
- <span className="rz-flow-list-name">
60
+ <span className="dev-host-flow-name">
61
+ <Play />
79
62
  {flow.displayName}
80
- {flow.autoRun ? <span className="rz-flow-list-badge">Auto</span> : null}
63
+ {flow.autoRun ? <Badge variant="outline">Auto</Badge> : null}
64
+ </span>
65
+ <span className="text-xs text-muted-foreground">
66
+ {isRunning ? 'Running' : 'Run'}
81
67
  </span>
82
- <span className="rz-flow-list-action">{isActive ? 'Running' : 'Run'}</span>
83
68
  </Button>
84
69
  );
85
70
  })}
86
71
  </div>
87
72
  </div>
88
73
 
89
- {hasExecutionState ? (
90
- <div className="rz-field">
91
- <div className="rz-label">Flow runs</div>
92
-
93
- <div className="rz-flow-runs">
94
- {flowRuns.map((flowRun) => {
95
- const isRunning = flowRun.status === 'running';
96
-
97
- return (
98
- <div key={flowRun.id} className="rz-flow-state" data-status={flowRun.status}>
99
- <div className="rz-flow-state-header">
100
- <div className="rz-flow-state-title">
101
- {flowRun.flowDisplayName}
102
- {flowRun.autoRun ? <span className="rz-flow-state-badge">Auto</span> : null}
103
- </div>
104
-
105
- {isRunning ? (
106
- <Button
107
- type="button"
108
- variant="pill"
109
- onClick={() => onStopFlow(flowRun.id)}
110
- overrides={{
111
- BaseButton: {
112
- style: {
113
- flexShrink: 0,
114
- },
115
- },
116
- }}
117
- >
118
- Stop
119
- </Button>
120
- ) : null}
121
- </div>
122
-
123
- <div className="rz-flow-state-status">{getStatusLabel(flowRun.status)}</div>
124
-
125
- {flowRun.error ? (
126
- <div className="rz-flow-state-error">{flowRun.error}</div>
127
- ) : null}
128
-
129
- {flowRun.result != null ? (
130
- <pre className="rz-flow-state-result">
131
- {formatPayloadForCommandInput(flowRun.result)}
132
- </pre>
74
+ {flowRuns.length > 0 ? (
75
+ <div className="dev-host-field">
76
+ <span className="dev-host-label">Flow runs</span>
77
+ <div className="dev-host-flow-runs">
78
+ {flowRuns.map((flowRun) => (
79
+ <div key={flowRun.id} className="dev-host-flow-state" data-status={flowRun.status}>
80
+ <div className="dev-host-flow-state-header">
81
+ <span className="dev-host-flow-title">
82
+ {flowRun.flowDisplayName}
83
+ {flowRun.autoRun ? <Badge variant="outline">Auto</Badge> : null}
84
+ </span>
85
+ {flowRun.status === 'running' ? (
86
+ <Button variant="ghost" size="compact" onClick={() => onStopFlow(flowRun.id)}>
87
+ <Square />
88
+ Stop
89
+ </Button>
133
90
  ) : null}
134
91
  </div>
135
- );
136
- })}
92
+ <span className="text-xs text-muted-foreground">
93
+ {getStatusLabel(flowRun.status)}
94
+ </span>
95
+ {flowRun.error ? (
96
+ <div className="dev-host-flow-state-error">{flowRun.error}</div>
97
+ ) : null}
98
+ {flowRun.result != null ? (
99
+ <pre className="dev-host-flow-state-result">
100
+ {formatPayloadForCommandInput(flowRun.result)}
101
+ </pre>
102
+ ) : null}
103
+ </div>
104
+ ))}
137
105
  </div>
138
106
  </div>
139
107
  ) : null}
@@ -1,103 +1,70 @@
1
- import type { PointerEvent as ReactPointerEvent } from 'react';
2
- import type { MessageEntry, ResizeHandleId } from '../types.js';
1
+ import { Button, JsonInspector, ScrollArea } from '@rozenite/ui';
2
+ import { Send, X } from 'lucide-react';
3
+ import type { MessageEntry } from '../types.js';
3
4
  import { formatMessageDate, formatPayloadForCommandInput } from '../utils.js';
4
- import { MessagePayloadDetail } from './MessagePayloadDetail.js';
5
- import { ResizeHandle } from './ResizeHandle.js';
6
- import { ScrollArea } from './ui/ScrollArea.js';
7
- import { SendIcon } from './icons.js';
8
- import { IconButton } from './ui/IconButton.js';
9
5
 
10
6
  type MessageDetailsPaneProps = {
11
- selectedMessage: MessageEntry | null;
12
- isOpen: boolean;
13
- isNarrowViewport: boolean;
14
- activeResizeHandle: ResizeHandleId | null;
7
+ selectedMessage: MessageEntry;
15
8
  onClose: () => void;
16
9
  onUseMessage: (message: MessageEntry) => void;
17
- onResizeStart: (event: ReactPointerEvent<HTMLDivElement>) => void;
18
10
  };
19
11
 
20
12
  export const MessageDetailsPane = ({
21
13
  selectedMessage,
22
- isOpen,
23
- isNarrowViewport,
24
- activeResizeHandle,
25
14
  onClose,
26
15
  onUseMessage,
27
- onResizeStart,
28
16
  }: MessageDetailsPaneProps) => {
29
- const isHidden = !isOpen || !selectedMessage;
30
-
31
17
  return (
32
- <>
33
- {!isNarrowViewport ? (
34
- <ResizeHandle
35
- className="rz-column-resize-handle"
36
- isDragging={activeResizeHandle === 'details-width'}
37
- isHidden={isHidden}
38
- orientation="vertical"
39
- label="Resize message details"
40
- onPointerDown={onResizeStart}
41
- />
42
- ) : null}
43
-
44
- <div className="rz-pane" data-hidden={isHidden} aria-hidden={isHidden}>
45
- <div className="rz-sidebar">
46
- <div className="rz-sidebar-header">
47
- <div className="rz-sidebar-title">Message Details</div>
48
- <div className="rz-header-actions">
49
- <IconButton
50
- type="button"
51
- aria-label="Use message in dispatcher"
52
- title="Use message in dispatcher"
53
- onClick={() => {
54
- if (selectedMessage) {
55
- onUseMessage(selectedMessage);
56
- }
57
- }}
58
- >
59
- <SendIcon />
60
- </IconButton>
18
+ <section className="dev-host-pane">
19
+ <header className="dev-host-pane-header">
20
+ <h2 className="dev-host-pane-title">Message Details</h2>
21
+ <div className="dev-host-pane-actions">
22
+ <Button
23
+ variant="ghost"
24
+ size="icon"
25
+ onClick={() => onUseMessage(selectedMessage)}
26
+ aria-label="Use message in dispatcher"
27
+ title="Use message in dispatcher"
28
+ >
29
+ <Send />
30
+ </Button>
31
+ <Button
32
+ variant="ghost"
33
+ size="icon"
34
+ onClick={onClose}
35
+ aria-label="Close message details"
36
+ title="Close message details"
37
+ >
38
+ <X />
39
+ </Button>
40
+ </div>
41
+ </header>
61
42
 
62
- <IconButton
63
- type="button"
64
- aria-label="Close message details"
65
- onClick={onClose}
66
- >
67
- ×
68
- </IconButton>
43
+ <ScrollArea className="dev-host-scroll">
44
+ <div className="dev-host-details">
45
+ <div className="dev-host-detail-section">
46
+ <span className="dev-host-label">Date</span>
47
+ <div className="dev-host-detail-value">{formatMessageDate(selectedMessage.date)}</div>
48
+ </div>
49
+ <div className="dev-host-detail-section">
50
+ <span className="dev-host-label">Type</span>
51
+ <div className="dev-host-detail-value">{selectedMessage.type}</div>
52
+ </div>
53
+ <div className="dev-host-detail-section">
54
+ <span className="dev-host-label">Payload</span>
55
+ <div className="dev-host-detail-payload">
56
+ {typeof selectedMessage.payload === 'object' && selectedMessage.payload !== null ? (
57
+ <JsonInspector data={selectedMessage.payload} defaultExpandedDepth={2} />
58
+ ) : (
59
+ <pre className="m-0 whitespace-pre-wrap break-words">
60
+ {formatPayloadForCommandInput(selectedMessage.payload)}
61
+ </pre>
62
+ )}
69
63
  </div>
70
64
  </div>
71
-
72
- <ScrollArea className="rz-sidebar-scroll">
73
- {selectedMessage ? (
74
- <div className="rz-message-detail">
75
- <div className="rz-detail-section">
76
- <div className="rz-label">Date</div>
77
- <div className="rz-detail-value rz-detail-mono">
78
- {formatMessageDate(selectedMessage.date)}
79
- </div>
80
- </div>
81
-
82
- <div className="rz-detail-section">
83
- <div className="rz-label">Type</div>
84
- <div className="rz-detail-value rz-detail-mono">{selectedMessage.type}</div>
85
- </div>
86
-
87
- <div className="rz-detail-section">
88
- <div className="rz-label">Payload</div>
89
- <div className="rz-detail-payload">
90
- <MessagePayloadDetail payload={selectedMessage.payload} />
91
- </div>
92
- </div>
93
- </div>
94
- ) : (
95
- <div className="rz-empty-state">Select a message to inspect its details.</div>
96
- )}
97
- </ScrollArea>
98
65
  </div>
99
- </div>
100
- </>
66
+ </ScrollArea>
67
+ </section>
101
68
  );
102
69
  };
103
70