@workflow/web-shared 4.1.0-beta.52 → 4.1.0-beta.54
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/components/error-boundary.d.ts +15 -20
- package/dist/components/error-boundary.d.ts.map +1 -1
- package/dist/components/error-boundary.js +17 -31
- package/dist/components/error-boundary.js.map +1 -1
- package/dist/components/event-list-view.d.ts +7 -6
- package/dist/components/event-list-view.d.ts.map +1 -1
- package/dist/components/event-list-view.js +492 -109
- package/dist/components/event-list-view.js.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/run-trace-view.d.ts +2 -1
- package/dist/components/run-trace-view.d.ts.map +1 -1
- package/dist/components/run-trace-view.js +2 -2
- package/dist/components/run-trace-view.js.map +1 -1
- package/dist/components/sidebar/attribute-panel.d.ts +2 -1
- package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
- package/dist/components/sidebar/attribute-panel.js +53 -142
- package/dist/components/sidebar/attribute-panel.js.map +1 -1
- package/dist/components/sidebar/conversation-view.d.ts.map +1 -1
- package/dist/components/sidebar/conversation-view.js +3 -17
- package/dist/components/sidebar/conversation-view.js.map +1 -1
- package/dist/components/sidebar/entity-detail-panel.d.ts +3 -1
- package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
- package/dist/components/sidebar/entity-detail-panel.js +63 -10
- package/dist/components/sidebar/entity-detail-panel.js.map +1 -1
- package/dist/components/sidebar/events-list.d.ts.map +1 -1
- package/dist/components/sidebar/events-list.js +4 -8
- package/dist/components/sidebar/events-list.js.map +1 -1
- package/dist/components/sidebar/resolve-hook-modal.d.ts +3 -0
- package/dist/components/sidebar/resolve-hook-modal.d.ts.map +1 -1
- package/dist/components/sidebar/resolve-hook-modal.js +152 -3
- package/dist/components/sidebar/resolve-hook-modal.js.map +1 -1
- package/dist/components/stream-viewer.d.ts +7 -5
- package/dist/components/stream-viewer.d.ts.map +1 -1
- package/dist/components/stream-viewer.js +54 -22
- package/dist/components/stream-viewer.js.map +1 -1
- package/dist/components/trace-viewer/components/markers.d.ts +2 -1
- package/dist/components/trace-viewer/components/markers.d.ts.map +1 -1
- package/dist/components/trace-viewer/components/markers.js +59 -20
- package/dist/components/trace-viewer/components/markers.js.map +1 -1
- package/dist/components/trace-viewer/components/node.d.ts +5 -1
- package/dist/components/trace-viewer/components/node.d.ts.map +1 -1
- package/dist/components/trace-viewer/components/node.js +250 -68
- package/dist/components/trace-viewer/components/node.js.map +1 -1
- package/dist/components/trace-viewer/components/span-content.d.ts +19 -0
- package/dist/components/trace-viewer/components/span-content.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/span-content.js +137 -0
- package/dist/components/trace-viewer/components/span-content.js.map +1 -0
- package/dist/components/trace-viewer/components/span-detail-panel.d.ts.map +1 -1
- package/dist/components/trace-viewer/components/span-detail-panel.js +3 -2
- package/dist/components/trace-viewer/components/span-detail-panel.js.map +1 -1
- package/dist/components/trace-viewer/components/span-segments.d.ts +50 -0
- package/dist/components/trace-viewer/components/span-segments.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/span-segments.js +392 -0
- package/dist/components/trace-viewer/components/span-segments.js.map +1 -0
- package/dist/components/trace-viewer/components/span-strategies.d.ts +46 -0
- package/dist/components/trace-viewer/components/span-strategies.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/span-strategies.js +108 -0
- package/dist/components/trace-viewer/components/span-strategies.js.map +1 -0
- package/dist/components/trace-viewer/context.d.ts +7 -6
- package/dist/components/trace-viewer/context.d.ts.map +1 -1
- package/dist/components/trace-viewer/context.js +47 -18
- package/dist/components/trace-viewer/context.js.map +1 -1
- package/dist/components/trace-viewer/trace-viewer.d.ts +5 -1
- package/dist/components/trace-viewer/trace-viewer.d.ts.map +1 -1
- package/dist/components/trace-viewer/trace-viewer.js +87 -11
- package/dist/components/trace-viewer/trace-viewer.js.map +1 -1
- package/dist/components/trace-viewer/trace-viewer.module.css +179 -6
- package/dist/components/trace-viewer/util/timing.d.ts +5 -0
- package/dist/components/trace-viewer/util/timing.d.ts.map +1 -1
- package/dist/components/trace-viewer/util/timing.js +12 -0
- package/dist/components/trace-viewer/util/timing.js.map +1 -1
- package/dist/components/trace-viewer/util/use-streaming-spans.d.ts +1 -1
- package/dist/components/trace-viewer/util/use-streaming-spans.d.ts.map +1 -1
- package/dist/components/trace-viewer/util/use-streaming-spans.js +29 -17
- package/dist/components/trace-viewer/util/use-streaming-spans.js.map +1 -1
- package/dist/components/trace-viewer/worker.js +3 -1
- package/dist/components/trace-viewer/worker.js.map +1 -1
- package/dist/components/ui/alert.js +3 -3
- package/dist/components/ui/alert.js.map +1 -1
- package/dist/components/ui/card.d.ts.map +1 -1
- package/dist/components/ui/card.js +2 -2
- package/dist/components/ui/card.js.map +1 -1
- package/dist/components/ui/data-inspector.d.ts +17 -0
- package/dist/components/ui/data-inspector.d.ts.map +1 -0
- package/dist/components/ui/data-inspector.js +184 -0
- package/dist/components/ui/data-inspector.js.map +1 -0
- package/dist/components/ui/error-card.d.ts.map +1 -1
- package/dist/components/ui/error-card.js +4 -1
- package/dist/components/ui/error-card.js.map +1 -1
- package/dist/components/ui/inspector-theme.d.ts +39 -24
- package/dist/components/ui/inspector-theme.d.ts.map +1 -1
- package/dist/components/ui/inspector-theme.js +90 -38
- package/dist/components/ui/inspector-theme.js.map +1 -1
- package/dist/components/ui/skeleton.d.ts +1 -1
- package/dist/components/ui/skeleton.d.ts.map +1 -1
- package/dist/components/ui/skeleton.js +2 -2
- package/dist/components/ui/skeleton.js.map +1 -1
- package/dist/components/workflow-trace-view.d.ts +3 -1
- package/dist/components/workflow-trace-view.d.ts.map +1 -1
- package/dist/components/workflow-trace-view.js +435 -21
- package/dist/components/workflow-trace-view.js.map +1 -1
- package/dist/components/workflow-traces/trace-span-construction.d.ts +1 -1
- package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -1
- package/dist/components/workflow-traces/trace-span-construction.js +2 -2
- package/dist/components/workflow-traces/trace-span-construction.js.map +1 -1
- package/dist/lib/hydration.d.ts.map +1 -1
- package/dist/lib/hydration.js +17 -3
- package/dist/lib/hydration.js.map +1 -1
- package/dist/styles.css +186 -0
- package/package.json +8 -7
- package/src/components/error-boundary.tsx +29 -40
- package/src/components/event-list-view.tsx +1000 -287
- package/src/components/index.ts +1 -0
- package/src/components/run-trace-view.tsx +3 -0
- package/src/components/sidebar/attribute-panel.tsx +58 -258
- package/src/components/sidebar/conversation-view.tsx +30 -27
- package/src/components/sidebar/entity-detail-panel.tsx +86 -20
- package/src/components/sidebar/events-list.tsx +4 -11
- package/src/components/sidebar/resolve-hook-modal.tsx +206 -47
- package/src/components/stream-viewer.tsx +138 -61
- package/src/components/trace-viewer/components/markers.tsx +69 -21
- package/src/components/trace-viewer/components/node.tsx +346 -100
- package/src/components/trace-viewer/components/span-content.tsx +247 -0
- package/src/components/trace-viewer/components/span-detail-panel.tsx +7 -2
- package/src/components/trace-viewer/components/span-segments.ts +516 -0
- package/src/components/trace-viewer/components/span-strategies.ts +205 -0
- package/src/components/trace-viewer/context.tsx +92 -40
- package/src/components/trace-viewer/trace-viewer.module.css +179 -6
- package/src/components/trace-viewer/trace-viewer.tsx +115 -11
- package/src/components/trace-viewer/util/timing.ts +13 -0
- package/src/components/trace-viewer/util/use-streaming-spans.ts +28 -17
- package/src/components/trace-viewer/worker.ts +4 -1
- package/src/components/ui/alert.tsx +3 -3
- package/src/components/ui/card.tsx +3 -5
- package/src/components/ui/data-inspector.tsx +318 -0
- package/src/components/ui/error-card.tsx +17 -6
- package/src/components/ui/inspector-theme.ts +127 -39
- package/src/components/ui/skeleton.tsx +3 -1
- package/src/components/workflow-trace-view.tsx +625 -26
- package/src/components/workflow-traces/trace-span-construction.ts +3 -2
- package/src/lib/hydration.ts +17 -8
- package/src/styles.css +186 -0
|
@@ -53,6 +53,7 @@ export interface SelectedSpanInfo {
|
|
|
53
53
|
*/
|
|
54
54
|
export function EntityDetailPanel({
|
|
55
55
|
run,
|
|
56
|
+
hooks,
|
|
56
57
|
onStreamClick,
|
|
57
58
|
spanDetailData,
|
|
58
59
|
spanDetailError,
|
|
@@ -64,6 +65,8 @@ export function EntityDetailPanel({
|
|
|
64
65
|
selectedSpan,
|
|
65
66
|
}: {
|
|
66
67
|
run: WorkflowRun;
|
|
68
|
+
/** All hooks for the current run (used as fallback for token lookup). */
|
|
69
|
+
hooks?: Hook[];
|
|
67
70
|
/** Callback when a stream reference is clicked */
|
|
68
71
|
onStreamClick?: (streamId: string) => void;
|
|
69
72
|
/** Pre-fetched span detail data for the selected span. */
|
|
@@ -96,6 +99,11 @@ export function EntityDetailPanel({
|
|
|
96
99
|
const [stoppingSleep, setStoppingSleep] = useState(false);
|
|
97
100
|
const [showResolveHookModal, setShowResolveHookModal] = useState(false);
|
|
98
101
|
const [resolvingHook, setResolvingHook] = useState(false);
|
|
102
|
+
// Track hooks that were resolved in this session so the button hides
|
|
103
|
+
// immediately without waiting for the next event poll.
|
|
104
|
+
const [resolvedHookIds, setResolvedHookIds] = useState<Set<string>>(
|
|
105
|
+
new Set()
|
|
106
|
+
);
|
|
99
107
|
|
|
100
108
|
const data = selectedSpan?.data;
|
|
101
109
|
const rawEvents = selectedSpan?.rawEvents;
|
|
@@ -167,7 +175,11 @@ export function EntityDetailPanel({
|
|
|
167
175
|
// Check if this hook can be resolved
|
|
168
176
|
const canResolveHook = useMemo(() => {
|
|
169
177
|
void rawEventsLength;
|
|
170
|
-
if (resource !== 'hook' || !rawEvents) return false;
|
|
178
|
+
if (resource !== 'hook' || !rawEvents || !resourceId) return false;
|
|
179
|
+
|
|
180
|
+
// Check if we already resolved this hook in this session
|
|
181
|
+
if (resolvedHookIds.has(resourceId)) return false;
|
|
182
|
+
|
|
171
183
|
const terminalStates = ['completed', 'failed', 'cancelled'];
|
|
172
184
|
if (terminalStates.includes(run.status)) return false;
|
|
173
185
|
const hasHookDisposed = rawEvents.some(
|
|
@@ -175,17 +187,36 @@ export function EntityDetailPanel({
|
|
|
175
187
|
);
|
|
176
188
|
if (hasHookDisposed) return false;
|
|
177
189
|
return true;
|
|
178
|
-
}, [
|
|
190
|
+
}, [
|
|
191
|
+
resource,
|
|
192
|
+
resourceId,
|
|
193
|
+
rawEvents,
|
|
194
|
+
rawEventsLength,
|
|
195
|
+
run.status,
|
|
196
|
+
resolvedHookIds,
|
|
197
|
+
]);
|
|
179
198
|
|
|
180
199
|
const error = spanDetailError ?? undefined;
|
|
181
200
|
const loading = spanDetailLoading ?? false;
|
|
182
201
|
|
|
183
|
-
// Get the hook token for resolving
|
|
202
|
+
// Get the hook token for resolving (prefer fetched data, then hooks array fallback)
|
|
184
203
|
const hookToken = useMemo(() => {
|
|
185
|
-
if (resource !== 'hook') return undefined;
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
204
|
+
if (resource !== 'hook' || !resourceId) return undefined;
|
|
205
|
+
// 1. Try the externally-fetched detail data first
|
|
206
|
+
if (isHook(spanDetailData) && spanDetailData.token) {
|
|
207
|
+
return spanDetailData.token;
|
|
208
|
+
}
|
|
209
|
+
// 2. Try the hooks array (always has tokens)
|
|
210
|
+
const hookFromArray = hooks?.find((h) => h.hookId === resourceId);
|
|
211
|
+
if (hookFromArray?.token) {
|
|
212
|
+
return hookFromArray.token;
|
|
213
|
+
}
|
|
214
|
+
// 3. Try the span's inline data (partial hook from events - may lack token)
|
|
215
|
+
if (isHook(data) && (data as Hook).token) {
|
|
216
|
+
return (data as Hook).token;
|
|
217
|
+
}
|
|
218
|
+
return undefined;
|
|
219
|
+
}, [resource, resourceId, spanDetailData, data, hooks]);
|
|
189
220
|
|
|
190
221
|
useEffect(() => {
|
|
191
222
|
if (error && selectedSpan && resource) {
|
|
@@ -254,6 +285,10 @@ export function EntityDetailPanel({
|
|
|
254
285
|
description: 'The payload has been sent and the hook resolved.',
|
|
255
286
|
});
|
|
256
287
|
setShowResolveHookModal(false);
|
|
288
|
+
// Mark this hook as resolved locally so the button hides immediately
|
|
289
|
+
if (resourceId) {
|
|
290
|
+
setResolvedHookIds((prev) => new Set(prev).add(resourceId));
|
|
291
|
+
}
|
|
257
292
|
} catch (err) {
|
|
258
293
|
console.error('Failed to resolve hook:', err);
|
|
259
294
|
toast.error('Failed to resolve hook', {
|
|
@@ -271,34 +306,58 @@ export function EntityDetailPanel({
|
|
|
271
306
|
return null;
|
|
272
307
|
}
|
|
273
308
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
309
|
+
// For sleep spans, spanDetailData from the host is typically an events array
|
|
310
|
+
// (not a single entity), so always prefer the inline wait entity from span
|
|
311
|
+
// attributes which contains waitId, runId, createdAt, resumeAt, completedAt.
|
|
312
|
+
const displayData = (
|
|
313
|
+
resource === 'sleep' ? data : (spanDetailData ?? data)
|
|
314
|
+
) as WorkflowRun | Step | Hook | Event;
|
|
315
|
+
const moduleSpecifier = useMemo(() => {
|
|
316
|
+
const displayRecord = displayData as Record<string, unknown>;
|
|
317
|
+
const displayStepName = displayRecord.stepName;
|
|
318
|
+
const displayWorkflowName = displayRecord.workflowName;
|
|
319
|
+
if (typeof displayStepName === 'string') {
|
|
320
|
+
return displayStepName;
|
|
321
|
+
}
|
|
322
|
+
if (typeof displayWorkflowName === 'string') {
|
|
323
|
+
return displayWorkflowName;
|
|
324
|
+
}
|
|
325
|
+
if (typeof run.workflowName === 'string') {
|
|
326
|
+
return run.workflowName;
|
|
327
|
+
}
|
|
328
|
+
return undefined;
|
|
329
|
+
}, [displayData, run.workflowName]);
|
|
279
330
|
|
|
280
331
|
return (
|
|
281
|
-
<div
|
|
332
|
+
<div
|
|
333
|
+
className={clsx('flex flex-col px-3')}
|
|
334
|
+
style={{ paddingTop: 12, gap: 16 }}
|
|
335
|
+
>
|
|
282
336
|
{/* Wake up button for pending sleep calls */}
|
|
283
337
|
{resource === 'sleep' && canWakeUp && (
|
|
284
|
-
<div
|
|
338
|
+
<div
|
|
339
|
+
className="mb-3 pb-3"
|
|
340
|
+
style={{ borderBottom: '1px solid var(--ds-gray-alpha-400)' }}
|
|
341
|
+
>
|
|
285
342
|
<button
|
|
286
343
|
type="button"
|
|
287
344
|
onClick={handleWakeUp}
|
|
288
345
|
disabled={stoppingSleep}
|
|
289
346
|
className={clsx(
|
|
290
347
|
'flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md w-full',
|
|
291
|
-
'bg-amber-100 dark:bg-amber-900/30 text-amber-800 dark:text-amber-200',
|
|
292
|
-
'hover:bg-amber-200 dark:hover:bg-amber-900/50',
|
|
293
348
|
'disabled:opacity-50 disabled:cursor-not-allowed',
|
|
294
349
|
'transition-colors',
|
|
295
350
|
stoppingSleep ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'
|
|
296
351
|
)}
|
|
352
|
+
style={{
|
|
353
|
+
background: 'var(--ds-amber-200)',
|
|
354
|
+
color: 'var(--ds-amber-900)',
|
|
355
|
+
}}
|
|
297
356
|
>
|
|
298
357
|
<Zap className="h-4 w-4" />
|
|
299
358
|
{stoppingSleep ? 'Waking up...' : 'Wake up'}
|
|
300
359
|
</button>
|
|
301
|
-
<p className="mt-1.5 text-xs
|
|
360
|
+
<p className="mt-1.5 text-xs" style={{ color: 'var(--ds-gray-900)' }}>
|
|
302
361
|
Interrupt this sleep call and wake up the run.
|
|
303
362
|
</p>
|
|
304
363
|
</div>
|
|
@@ -306,23 +365,29 @@ export function EntityDetailPanel({
|
|
|
306
365
|
|
|
307
366
|
{/* Resolve hook button for pending hooks */}
|
|
308
367
|
{resource === 'hook' && canResolveHook && (
|
|
309
|
-
<div
|
|
368
|
+
<div
|
|
369
|
+
className="mb-3 pb-3"
|
|
370
|
+
style={{ borderBottom: '1px solid var(--ds-gray-alpha-400)' }}
|
|
371
|
+
>
|
|
310
372
|
<button
|
|
311
373
|
type="button"
|
|
312
374
|
onClick={() => setShowResolveHookModal(true)}
|
|
313
375
|
disabled={resolvingHook}
|
|
314
376
|
className={clsx(
|
|
315
377
|
'flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md w-full',
|
|
316
|
-
'bg-primary text-primary-foreground hover:bg-primary/90',
|
|
317
378
|
'disabled:opacity-50 disabled:cursor-not-allowed',
|
|
318
379
|
'transition-colors',
|
|
319
380
|
resolvingHook ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'
|
|
320
381
|
)}
|
|
382
|
+
style={{
|
|
383
|
+
background: 'var(--ds-gray-1000)',
|
|
384
|
+
color: 'var(--ds-background-100)',
|
|
385
|
+
}}
|
|
321
386
|
>
|
|
322
387
|
<Send className="h-4 w-4" />
|
|
323
388
|
Resolve Hook
|
|
324
389
|
</button>
|
|
325
|
-
<p className="mt-1.5 text-xs
|
|
390
|
+
<p className="mt-1.5 text-xs" style={{ color: 'var(--ds-gray-900)' }}>
|
|
326
391
|
Send a JSON payload to resolve this hook.
|
|
327
392
|
</p>
|
|
328
393
|
</div>
|
|
@@ -339,6 +404,7 @@ export function EntityDetailPanel({
|
|
|
339
404
|
{/* Content display */}
|
|
340
405
|
<AttributePanel
|
|
341
406
|
data={displayData}
|
|
407
|
+
moduleSpecifier={moduleSpecifier}
|
|
342
408
|
expiredAt={run.expiredAt}
|
|
343
409
|
isLoading={loading}
|
|
344
410
|
error={error ?? undefined}
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import type { Event } from '@workflow/world';
|
|
4
4
|
import { useCallback, useMemo, useState } from 'react';
|
|
5
|
-
import {
|
|
6
|
-
import { useDarkMode } from '../../hooks/use-dark-mode';
|
|
7
|
-
import { inspectorThemeDark, inspectorThemeLight } from '../ui/inspector-theme';
|
|
5
|
+
import { DataInspector } from '../ui/data-inspector';
|
|
8
6
|
import { localMillisecondTime } from './attribute-panel';
|
|
9
7
|
import { DetailCard } from './detail-card';
|
|
10
8
|
|
|
@@ -15,6 +13,8 @@ const DATA_EVENT_TYPES = new Set([
|
|
|
15
13
|
'step_created',
|
|
16
14
|
'step_completed',
|
|
17
15
|
'step_failed',
|
|
16
|
+
'hook_created',
|
|
17
|
+
'hook_received',
|
|
18
18
|
'run_created',
|
|
19
19
|
'run_completed',
|
|
20
20
|
]);
|
|
@@ -32,7 +32,6 @@ function EventItem({
|
|
|
32
32
|
eventId: string
|
|
33
33
|
) => Promise<unknown | null>;
|
|
34
34
|
}) {
|
|
35
|
-
const isDark = useDarkMode();
|
|
36
35
|
const [loadedData, setLoadedData] = useState<unknown | null>(null);
|
|
37
36
|
const [isLoading, setIsLoading] = useState(false);
|
|
38
37
|
const [loadError, setLoadError] = useState<string | null>(null);
|
|
@@ -170,13 +169,7 @@ function EventItem({
|
|
|
170
169
|
className="mt-2 overflow-x-auto rounded-md border p-3"
|
|
171
170
|
style={{ borderColor: 'var(--ds-gray-300)' }}
|
|
172
171
|
>
|
|
173
|
-
<
|
|
174
|
-
data={displayData}
|
|
175
|
-
// @ts-expect-error react-inspector accepts theme objects at runtime
|
|
176
|
-
// see https://github.com/storybookjs/react-inspector/blob/main/README.md#theme
|
|
177
|
-
theme={isDark ? inspectorThemeDark : inspectorThemeLight}
|
|
178
|
-
expandLevel={2}
|
|
179
|
-
/>
|
|
172
|
+
<DataInspector data={displayData} />
|
|
180
173
|
</div>
|
|
181
174
|
)}
|
|
182
175
|
</DetailCard>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import clsx from 'clsx';
|
|
4
3
|
import { Send, X } from 'lucide-react';
|
|
5
4
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
6
5
|
|
|
@@ -17,6 +16,9 @@ interface ResolveHookModalProps {
|
|
|
17
16
|
|
|
18
17
|
/**
|
|
19
18
|
* Modal component for resolving a hook by entering a JSON payload.
|
|
19
|
+
*
|
|
20
|
+
* Styled to match the Geist design-system dialog component used in the
|
|
21
|
+
* Vercel dashboard so it looks native when rendered inside `front`.
|
|
20
22
|
*/
|
|
21
23
|
export function ResolveHookModal({
|
|
22
24
|
isOpen,
|
|
@@ -82,6 +84,15 @@ export function ResolveHookModal({
|
|
|
82
84
|
[submitPayload]
|
|
83
85
|
);
|
|
84
86
|
|
|
87
|
+
const isMacPlatform =
|
|
88
|
+
typeof navigator !== 'undefined' &&
|
|
89
|
+
(
|
|
90
|
+
(navigator as Navigator & { userAgentData?: { platform?: string } })
|
|
91
|
+
.userAgentData?.platform ?? navigator.userAgent
|
|
92
|
+
)
|
|
93
|
+
.toLowerCase()
|
|
94
|
+
.includes('mac');
|
|
95
|
+
|
|
85
96
|
// Handle Cmd/Ctrl + Enter to submit
|
|
86
97
|
const handleKeyDown = useCallback(
|
|
87
98
|
(e: React.KeyboardEvent) => {
|
|
@@ -99,30 +110,60 @@ export function ResolveHookModal({
|
|
|
99
110
|
|
|
100
111
|
return (
|
|
101
112
|
<div
|
|
102
|
-
|
|
113
|
+
style={{
|
|
114
|
+
position: 'fixed',
|
|
115
|
+
inset: 0,
|
|
116
|
+
zIndex: 50,
|
|
117
|
+
display: 'flex',
|
|
118
|
+
alignItems: 'center',
|
|
119
|
+
justifyContent: 'center',
|
|
120
|
+
}}
|
|
103
121
|
role="dialog"
|
|
104
122
|
aria-modal="true"
|
|
105
123
|
aria-labelledby="resolve-hook-modal-title"
|
|
106
124
|
>
|
|
107
|
-
{/* Backdrop */}
|
|
125
|
+
{/* Backdrop — matches Geist dialog ::backdrop */}
|
|
108
126
|
<div
|
|
109
|
-
|
|
127
|
+
style={{
|
|
128
|
+
position: 'absolute',
|
|
129
|
+
inset: 0,
|
|
130
|
+
background: 'rgba(0, 0, 0, 0.7)',
|
|
131
|
+
}}
|
|
110
132
|
onClick={isSubmitting ? undefined : onClose}
|
|
111
133
|
/>
|
|
112
134
|
|
|
113
|
-
{/* Modal
|
|
135
|
+
{/* Modal card — matches Geist dialog.geist-dialog */}
|
|
114
136
|
<div
|
|
115
|
-
|
|
116
|
-
'relative
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
137
|
+
style={{
|
|
138
|
+
position: 'relative',
|
|
139
|
+
zIndex: 10,
|
|
140
|
+
width: 480,
|
|
141
|
+
maxWidth: 'calc(100% - 32px)',
|
|
142
|
+
borderRadius: 12,
|
|
143
|
+
border: 'none',
|
|
144
|
+
boxShadow: 'var(--ds-shadow-menu)',
|
|
145
|
+
background: 'var(--ds-background-100)',
|
|
146
|
+
color: 'var(--ds-gray-1000)',
|
|
147
|
+
overflow: 'hidden',
|
|
148
|
+
}}
|
|
120
149
|
>
|
|
121
150
|
{/* Header */}
|
|
122
|
-
<div
|
|
151
|
+
<div
|
|
152
|
+
style={{
|
|
153
|
+
display: 'flex',
|
|
154
|
+
alignItems: 'center',
|
|
155
|
+
justifyContent: 'space-between',
|
|
156
|
+
padding: '16px 24px',
|
|
157
|
+
}}
|
|
158
|
+
>
|
|
123
159
|
<h2
|
|
124
160
|
id="resolve-hook-modal-title"
|
|
125
|
-
|
|
161
|
+
style={{
|
|
162
|
+
margin: 0,
|
|
163
|
+
fontSize: 16,
|
|
164
|
+
fontWeight: 600,
|
|
165
|
+
color: 'var(--ds-gray-1000)',
|
|
166
|
+
}}
|
|
126
167
|
>
|
|
127
168
|
Resolve Hook
|
|
128
169
|
</h2>
|
|
@@ -130,30 +171,69 @@ export function ResolveHookModal({
|
|
|
130
171
|
type="button"
|
|
131
172
|
onClick={onClose}
|
|
132
173
|
disabled={isSubmitting}
|
|
133
|
-
className={clsx(
|
|
134
|
-
'p-1 rounded-md transition-colors',
|
|
135
|
-
'text-muted-foreground hover:text-foreground',
|
|
136
|
-
'hover:bg-muted',
|
|
137
|
-
'disabled:opacity-50 disabled:cursor-not-allowed'
|
|
138
|
-
)}
|
|
139
174
|
aria-label="Close modal"
|
|
175
|
+
style={{
|
|
176
|
+
display: 'flex',
|
|
177
|
+
alignItems: 'center',
|
|
178
|
+
justifyContent: 'center',
|
|
179
|
+
padding: 4,
|
|
180
|
+
borderRadius: 6,
|
|
181
|
+
border: 'none',
|
|
182
|
+
background: 'transparent',
|
|
183
|
+
color: 'var(--ds-gray-900)',
|
|
184
|
+
cursor: isSubmitting ? 'not-allowed' : 'pointer',
|
|
185
|
+
opacity: isSubmitting ? 0.5 : 1,
|
|
186
|
+
transition: 'background 0.15s',
|
|
187
|
+
}}
|
|
188
|
+
onMouseEnter={(e) => {
|
|
189
|
+
e.currentTarget.style.background = 'var(--ds-gray-alpha-200)';
|
|
190
|
+
}}
|
|
191
|
+
onMouseLeave={(e) => {
|
|
192
|
+
e.currentTarget.style.background = 'transparent';
|
|
193
|
+
}}
|
|
140
194
|
>
|
|
141
|
-
<X
|
|
195
|
+
<X style={{ width: 16, height: 16 }} />
|
|
142
196
|
</button>
|
|
143
197
|
</div>
|
|
144
198
|
|
|
145
199
|
{/* Body */}
|
|
146
200
|
<form onSubmit={handleSubmit}>
|
|
147
|
-
<div
|
|
201
|
+
<div style={{ padding: '0 24px 16px' }}>
|
|
148
202
|
<label
|
|
149
203
|
htmlFor="json-payload"
|
|
150
|
-
|
|
204
|
+
style={{
|
|
205
|
+
display: 'block',
|
|
206
|
+
fontSize: 14,
|
|
207
|
+
fontWeight: 500,
|
|
208
|
+
marginBottom: 6,
|
|
209
|
+
color: 'var(--ds-gray-1000)',
|
|
210
|
+
}}
|
|
151
211
|
>
|
|
152
212
|
JSON Payload
|
|
153
213
|
</label>
|
|
154
|
-
<p
|
|
214
|
+
<p
|
|
215
|
+
style={{
|
|
216
|
+
fontSize: 13,
|
|
217
|
+
marginBottom: 12,
|
|
218
|
+
marginTop: 0,
|
|
219
|
+
color: 'var(--ds-gray-900)',
|
|
220
|
+
lineHeight: 1.5,
|
|
221
|
+
}}
|
|
222
|
+
>
|
|
155
223
|
Enter a JSON value to send to the hook. Leave empty to send{' '}
|
|
156
|
-
<code
|
|
224
|
+
<code
|
|
225
|
+
style={{
|
|
226
|
+
padding: '2px 6px',
|
|
227
|
+
borderRadius: 4,
|
|
228
|
+
fontSize: 12,
|
|
229
|
+
fontFamily:
|
|
230
|
+
'var(--font-mono, ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace)',
|
|
231
|
+
background: 'var(--ds-gray-alpha-200)',
|
|
232
|
+
color: 'var(--ds-gray-1000)',
|
|
233
|
+
}}
|
|
234
|
+
>
|
|
235
|
+
null
|
|
236
|
+
</code>
|
|
157
237
|
.
|
|
158
238
|
</p>
|
|
159
239
|
<textarea
|
|
@@ -167,34 +247,97 @@ export function ResolveHookModal({
|
|
|
167
247
|
onKeyDown={handleKeyDown}
|
|
168
248
|
disabled={isSubmitting}
|
|
169
249
|
placeholder='{"key": "value"}'
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
'
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
250
|
+
style={{
|
|
251
|
+
width: '100%',
|
|
252
|
+
height: 160,
|
|
253
|
+
padding: '8px 12px',
|
|
254
|
+
fontFamily:
|
|
255
|
+
'var(--font-mono, ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace)',
|
|
256
|
+
fontSize: 13,
|
|
257
|
+
lineHeight: 1.5,
|
|
258
|
+
borderRadius: 8,
|
|
259
|
+
border: `1px solid ${parseError ? 'var(--ds-red-700)' : 'var(--ds-gray-alpha-400)'}`,
|
|
260
|
+
color: 'var(--ds-gray-1000)',
|
|
261
|
+
background: 'var(--ds-background-100)',
|
|
262
|
+
outline: 'none',
|
|
263
|
+
resize: 'none',
|
|
264
|
+
opacity: isSubmitting ? 0.5 : 1,
|
|
265
|
+
cursor: isSubmitting ? 'not-allowed' : 'text',
|
|
266
|
+
boxSizing: 'border-box',
|
|
267
|
+
}}
|
|
180
268
|
/>
|
|
181
269
|
{parseError && (
|
|
182
|
-
<p
|
|
270
|
+
<p
|
|
271
|
+
style={{
|
|
272
|
+
marginTop: 8,
|
|
273
|
+
fontSize: 13,
|
|
274
|
+
color: 'var(--ds-red-900)',
|
|
275
|
+
margin: '8px 0 0',
|
|
276
|
+
}}
|
|
277
|
+
>
|
|
278
|
+
{parseError}
|
|
279
|
+
</p>
|
|
183
280
|
)}
|
|
281
|
+
<p
|
|
282
|
+
style={{
|
|
283
|
+
marginTop: 8,
|
|
284
|
+
fontSize: 12,
|
|
285
|
+
color: 'var(--ds-gray-800)',
|
|
286
|
+
margin: '8px 0 0',
|
|
287
|
+
}}
|
|
288
|
+
>
|
|
289
|
+
Press{' '}
|
|
290
|
+
<kbd
|
|
291
|
+
style={{
|
|
292
|
+
padding: '2px 5px',
|
|
293
|
+
borderRadius: 4,
|
|
294
|
+
fontSize: 11,
|
|
295
|
+
fontFamily:
|
|
296
|
+
'var(--font-mono, ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace)',
|
|
297
|
+
background: 'var(--ds-gray-alpha-200)',
|
|
298
|
+
border: '1px solid var(--ds-gray-alpha-400)',
|
|
299
|
+
}}
|
|
300
|
+
>
|
|
301
|
+
{isMacPlatform ? '⌘' : 'Ctrl'}
|
|
302
|
+
+Enter
|
|
303
|
+
</kbd>{' '}
|
|
304
|
+
to submit
|
|
305
|
+
</p>
|
|
184
306
|
</div>
|
|
185
307
|
|
|
186
308
|
{/* Footer */}
|
|
187
|
-
<div
|
|
309
|
+
<div
|
|
310
|
+
style={{
|
|
311
|
+
display: 'flex',
|
|
312
|
+
alignItems: 'center',
|
|
313
|
+
justifyContent: 'flex-end',
|
|
314
|
+
gap: 8,
|
|
315
|
+
padding: '12px 24px',
|
|
316
|
+
borderTop: '1px solid var(--ds-gray-alpha-400)',
|
|
317
|
+
}}
|
|
318
|
+
>
|
|
188
319
|
<button
|
|
189
320
|
type="button"
|
|
190
321
|
onClick={onClose}
|
|
191
322
|
disabled={isSubmitting}
|
|
192
|
-
|
|
193
|
-
'
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
323
|
+
style={{
|
|
324
|
+
padding: '8px 16px',
|
|
325
|
+
fontSize: 14,
|
|
326
|
+
fontWeight: 500,
|
|
327
|
+
borderRadius: 8,
|
|
328
|
+
border: '1px solid var(--ds-gray-alpha-400)',
|
|
329
|
+
background: 'var(--ds-background-100)',
|
|
330
|
+
color: 'var(--ds-gray-1000)',
|
|
331
|
+
cursor: isSubmitting ? 'not-allowed' : 'pointer',
|
|
332
|
+
opacity: isSubmitting ? 0.5 : 1,
|
|
333
|
+
transition: 'background 0.15s',
|
|
334
|
+
}}
|
|
335
|
+
onMouseEnter={(e) => {
|
|
336
|
+
e.currentTarget.style.background = 'var(--ds-gray-alpha-100)';
|
|
337
|
+
}}
|
|
338
|
+
onMouseLeave={(e) => {
|
|
339
|
+
e.currentTarget.style.background = 'var(--ds-background-100)';
|
|
340
|
+
}}
|
|
198
341
|
>
|
|
199
342
|
Cancel
|
|
200
343
|
</button>
|
|
@@ -202,13 +345,29 @@ export function ResolveHookModal({
|
|
|
202
345
|
type="button"
|
|
203
346
|
onClick={() => void submitPayload()}
|
|
204
347
|
disabled={isSubmitting}
|
|
205
|
-
|
|
206
|
-
'flex
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
348
|
+
style={{
|
|
349
|
+
display: 'flex',
|
|
350
|
+
alignItems: 'center',
|
|
351
|
+
gap: 6,
|
|
352
|
+
padding: '8px 16px',
|
|
353
|
+
fontSize: 14,
|
|
354
|
+
fontWeight: 500,
|
|
355
|
+
borderRadius: 8,
|
|
356
|
+
border: 'none',
|
|
357
|
+
background: 'var(--ds-gray-1000)',
|
|
358
|
+
color: 'var(--ds-background-100)',
|
|
359
|
+
cursor: isSubmitting ? 'not-allowed' : 'pointer',
|
|
360
|
+
opacity: isSubmitting ? 0.5 : 1,
|
|
361
|
+
transition: 'opacity 0.15s',
|
|
362
|
+
}}
|
|
363
|
+
onMouseEnter={(e) => {
|
|
364
|
+
if (!isSubmitting) e.currentTarget.style.opacity = '0.9';
|
|
365
|
+
}}
|
|
366
|
+
onMouseLeave={(e) => {
|
|
367
|
+
if (!isSubmitting) e.currentTarget.style.opacity = '1';
|
|
368
|
+
}}
|
|
210
369
|
>
|
|
211
|
-
<Send
|
|
370
|
+
<Send style={{ width: 14, height: 14 }} />
|
|
212
371
|
{isSubmitting ? 'Sending...' : 'Send Payload'}
|
|
213
372
|
</button>
|
|
214
373
|
</div>
|