@flowsterix/react 0.10.1 → 0.11.1

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 (62) hide show
  1. package/dist/chunk-AJZMUYBN.mjs +21 -0
  2. package/dist/chunk-D5LQLRSU.cjs +289 -0
  3. package/dist/chunk-FCOKCGV3.cjs +21 -0
  4. package/dist/chunk-HPVLOLCD.cjs +1 -0
  5. package/dist/components/TourPopoverPortal.d.ts +1 -1
  6. package/dist/components/TourPopoverPortal.d.ts.map +1 -1
  7. package/dist/context.d.ts +6 -0
  8. package/dist/context.d.ts.map +1 -1
  9. package/dist/devtools/DevToolsContext.d.ts +31 -0
  10. package/dist/devtools/DevToolsContext.d.ts.map +1 -0
  11. package/dist/devtools/DevToolsProvider.d.ts +9 -0
  12. package/dist/devtools/DevToolsProvider.d.ts.map +1 -0
  13. package/dist/devtools/components/FlowEditModal.d.ts +11 -0
  14. package/dist/devtools/components/FlowEditModal.d.ts.map +1 -0
  15. package/dist/devtools/components/FlowItem.d.ts +8 -0
  16. package/dist/devtools/components/FlowItem.d.ts.map +1 -0
  17. package/dist/devtools/components/FlowsTab.d.ts +5 -0
  18. package/dist/devtools/components/FlowsTab.d.ts.map +1 -0
  19. package/dist/devtools/components/GrabberOverlay.d.ts +8 -0
  20. package/dist/devtools/components/GrabberOverlay.d.ts.map +1 -0
  21. package/dist/devtools/components/ShadowRoot.d.ts +7 -0
  22. package/dist/devtools/components/ShadowRoot.d.ts.map +1 -0
  23. package/dist/devtools/components/StepItem.d.ts +16 -0
  24. package/dist/devtools/components/StepItem.d.ts.map +1 -0
  25. package/dist/devtools/components/StepList.d.ts +17 -0
  26. package/dist/devtools/components/StepList.d.ts.map +1 -0
  27. package/dist/devtools/components/TabNav.d.ts +9 -0
  28. package/dist/devtools/components/TabNav.d.ts.map +1 -0
  29. package/dist/devtools/components/Toolbar.d.ts +11 -0
  30. package/dist/devtools/components/Toolbar.d.ts.map +1 -0
  31. package/dist/devtools/globalBridge.d.ts +24 -0
  32. package/dist/devtools/globalBridge.d.ts.map +1 -0
  33. package/dist/devtools/hooks/useElementInfo.d.ts +7 -0
  34. package/dist/devtools/hooks/useElementInfo.d.ts.map +1 -0
  35. package/dist/devtools/hooks/useFlowsData.d.ts +16 -0
  36. package/dist/devtools/hooks/useFlowsData.d.ts.map +1 -0
  37. package/dist/devtools/hooks/useGrabMode.d.ts +16 -0
  38. package/dist/devtools/hooks/useGrabMode.d.ts.map +1 -0
  39. package/dist/devtools/hooks/useStepStore.d.ts +22 -0
  40. package/dist/devtools/hooks/useStepStore.d.ts.map +1 -0
  41. package/dist/devtools/index.cjs +2715 -0
  42. package/dist/devtools/index.d.ts +32 -0
  43. package/dist/devtools/index.d.ts.map +1 -0
  44. package/dist/devtools/index.mjs +2715 -0
  45. package/dist/devtools/types.d.ts +66 -0
  46. package/dist/devtools/types.d.ts.map +1 -0
  47. package/dist/devtools/utils/selectorGenerator.d.ts +11 -0
  48. package/dist/devtools/utils/selectorGenerator.d.ts.map +1 -0
  49. package/dist/devtools/utils/sourceExtractor.d.ts +45 -0
  50. package/dist/devtools/utils/sourceExtractor.d.ts.map +1 -0
  51. package/dist/devtools/utils/storage.d.ts +5 -0
  52. package/dist/devtools/utils/storage.d.ts.map +1 -0
  53. package/dist/hooks/useHiddenTargetFallback.d.ts.map +1 -1
  54. package/dist/hooks/useTourOverlay.d.ts.map +1 -1
  55. package/dist/index.cjs +724 -892
  56. package/dist/index.mjs +145 -16
  57. package/dist/router/index.cjs +13 -202
  58. package/dist/router/nextAppRouterAdapter.cjs +11 -200
  59. package/dist/router/nextPagesRouterAdapter.cjs +10 -199
  60. package/dist/router/reactRouterAdapter.cjs +10 -199
  61. package/dist/router/tanstackRouterAdapter.cjs +22 -214
  62. package/package.json +10 -1
@@ -0,0 +1,2715 @@
1
+ import {
2
+ DevToolsContext,
3
+ useDevToolsContext,
4
+ useDevToolsContextRequired
5
+ } from "../chunk-AJZMUYBN.mjs";
6
+
7
+ // src/devtools/DevToolsProvider.tsx
8
+ import { useCallback as useCallback8, useEffect as useEffect5, useRef, useState as useState8 } from "react";
9
+ import { createPortal as createPortal3 } from "react-dom";
10
+ import { AnimatePresence as AnimatePresence3, motion as motion6 } from "motion/react";
11
+
12
+ // src/devtools/components/GrabberOverlay.tsx
13
+ import { createPortal } from "react-dom";
14
+ import { AnimatePresence, motion } from "motion/react";
15
+
16
+ // src/devtools/utils/sourceExtractor.ts
17
+ function getReactFiber(element) {
18
+ const hook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
19
+ if (hook?.renderers) {
20
+ for (const renderer of hook.renderers.values()) {
21
+ if (renderer.findFiberByHostInstance) {
22
+ const fiber = renderer.findFiberByHostInstance(element);
23
+ if (fiber) return fiber;
24
+ }
25
+ }
26
+ }
27
+ const keys = Object.keys(element);
28
+ for (const key of keys) {
29
+ if (key.startsWith("__reactFiber$") || key.startsWith("__reactInternalInstance$")) {
30
+ return element[key];
31
+ }
32
+ }
33
+ return null;
34
+ }
35
+ function getFiberName(fiber) {
36
+ const type = fiber.type || fiber.elementType;
37
+ if (!type) return null;
38
+ if (typeof type === "string") {
39
+ return type;
40
+ }
41
+ if (typeof type === "object") {
42
+ return type.displayName || type.name || null;
43
+ }
44
+ if (typeof type === "function") {
45
+ return type.displayName || type.name || null;
46
+ }
47
+ return null;
48
+ }
49
+ function findDebugSource(fiber) {
50
+ let current = fiber;
51
+ let depth = 0;
52
+ const maxDepth = 50;
53
+ while (current && depth < maxDepth) {
54
+ if (current._debugSource) {
55
+ const source = current._debugSource;
56
+ return {
57
+ fileName: source.fileName,
58
+ lineNumber: source.lineNumber,
59
+ columnNumber: source.columnNumber ?? 0
60
+ };
61
+ }
62
+ current = current.return ?? null;
63
+ depth++;
64
+ }
65
+ return null;
66
+ }
67
+ function extractSource(params) {
68
+ const { element } = params;
69
+ if (typeof window === "undefined") return null;
70
+ try {
71
+ const fiber = getReactFiber(element);
72
+ if (!fiber) return null;
73
+ return findDebugSource(fiber);
74
+ } catch {
75
+ return null;
76
+ }
77
+ }
78
+ function extractComponentHierarchy(params) {
79
+ const { element } = params;
80
+ const hierarchy = [];
81
+ if (typeof window === "undefined") return hierarchy;
82
+ try {
83
+ const fiber = getReactFiber(element);
84
+ if (!fiber) return hierarchy;
85
+ let current = fiber;
86
+ let depth = 0;
87
+ const maxDepth = 20;
88
+ while (current && depth < maxDepth) {
89
+ const name = getFiberName(current);
90
+ if (name && !hierarchy.includes(name)) {
91
+ hierarchy.push(name);
92
+ }
93
+ current = current.return ?? null;
94
+ depth++;
95
+ }
96
+ return hierarchy;
97
+ } catch {
98
+ return hierarchy;
99
+ }
100
+ }
101
+ function formatSourcePath(params) {
102
+ const { source } = params;
103
+ const fileName = source.fileName.replace(/^.*\/src\//, "src/");
104
+ return `${fileName}:${source.lineNumber}`;
105
+ }
106
+ function getVSCodeLink(params) {
107
+ const { source } = params;
108
+ return `vscode://file${source.fileName}:${source.lineNumber}:${source.columnNumber}`;
109
+ }
110
+
111
+ // src/devtools/components/GrabberOverlay.tsx
112
+ import { jsx, jsxs } from "react/jsx-runtime";
113
+ var styles = {
114
+ root: {
115
+ position: "fixed",
116
+ inset: 0,
117
+ pointerEvents: "none",
118
+ zIndex: 99999
119
+ },
120
+ highlight: {
121
+ position: "absolute",
122
+ border: "2px solid hsl(217 91% 60%)",
123
+ backgroundColor: "hsl(217 91% 60% / 0.08)",
124
+ borderRadius: 6,
125
+ pointerEvents: "none",
126
+ boxShadow: "0 0 0 4px hsl(217 91% 60% / 0.15), 0 4px 20px hsl(217 91% 60% / 0.2)"
127
+ },
128
+ label: {
129
+ position: "absolute",
130
+ bottom: "100%",
131
+ left: 0,
132
+ marginBottom: 6,
133
+ display: "flex",
134
+ flexDirection: "column",
135
+ gap: 4,
136
+ padding: "8px 10px",
137
+ backgroundColor: "hsl(222 47% 11%)",
138
+ border: "1px solid hsl(215 20% 22%)",
139
+ color: "hsl(215 20% 75%)",
140
+ fontSize: 11,
141
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
142
+ borderRadius: 8,
143
+ boxShadow: "0 8px 24px rgba(0, 0, 0, 0.4)",
144
+ maxWidth: 280
145
+ },
146
+ labelTop: {
147
+ display: "flex",
148
+ alignItems: "center",
149
+ gap: 6
150
+ },
151
+ tagBadge: {
152
+ display: "inline-flex",
153
+ alignItems: "center",
154
+ padding: "2px 6px",
155
+ backgroundColor: "hsl(215 20% 22%)",
156
+ color: "hsl(217 91% 70%)",
157
+ fontFamily: "ui-monospace, monospace",
158
+ fontSize: 10,
159
+ fontWeight: 500,
160
+ borderRadius: 4
161
+ },
162
+ selector: {
163
+ color: "hsl(265 83% 78%)",
164
+ fontFamily: "ui-monospace, monospace",
165
+ fontSize: 10,
166
+ overflow: "hidden",
167
+ textOverflow: "ellipsis",
168
+ whiteSpace: "nowrap",
169
+ padding: "4px 6px",
170
+ backgroundColor: "hsl(215 20% 15%)",
171
+ borderRadius: 4,
172
+ border: "1px solid hsl(215 20% 20%)"
173
+ },
174
+ source: {
175
+ display: "flex",
176
+ alignItems: "center",
177
+ gap: 4,
178
+ color: "hsl(142 71% 55%)",
179
+ fontFamily: "ui-monospace, monospace",
180
+ fontSize: 10
181
+ },
182
+ hint: {
183
+ position: "fixed",
184
+ bottom: 20,
185
+ left: "50%",
186
+ transform: "translateX(-50%)",
187
+ display: "flex",
188
+ alignItems: "center",
189
+ gap: 12,
190
+ padding: "10px 16px",
191
+ backgroundColor: "hsl(222 47% 11%)",
192
+ border: "1px solid hsl(215 20% 22%)",
193
+ color: "hsl(215 20% 70%)",
194
+ fontSize: 12,
195
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
196
+ borderRadius: 10,
197
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.5)"
198
+ },
199
+ hintItem: {
200
+ display: "flex",
201
+ alignItems: "center",
202
+ gap: 6,
203
+ color: "hsl(215 20% 65%)"
204
+ },
205
+ kbd: {
206
+ display: "inline-flex",
207
+ alignItems: "center",
208
+ padding: "2px 5px",
209
+ backgroundColor: "hsl(215 20% 15%)",
210
+ border: "1px solid hsl(215 20% 22%)",
211
+ borderRadius: 4,
212
+ fontSize: 10,
213
+ fontFamily: "ui-monospace, monospace",
214
+ color: "hsl(215 20% 60%)",
215
+ fontWeight: 500
216
+ },
217
+ divider: {
218
+ width: 1,
219
+ height: 16,
220
+ backgroundColor: "hsl(215 20% 25%)"
221
+ }
222
+ };
223
+ var springTransition = {
224
+ type: "spring",
225
+ damping: 30,
226
+ stiffness: 400,
227
+ mass: 0.8
228
+ };
229
+ function GrabberOverlay(props) {
230
+ const { isGrabbing, hoveredInfo, container } = props;
231
+ if (typeof window === "undefined") return null;
232
+ const portalContainer = container ?? document.body;
233
+ const labelStyle = {
234
+ ...styles.label
235
+ };
236
+ if (hoveredInfo && hoveredInfo.rect.top < 80) {
237
+ labelStyle.bottom = "auto";
238
+ labelStyle.top = "100%";
239
+ labelStyle.marginBottom = 0;
240
+ labelStyle.marginTop = 6;
241
+ }
242
+ if (hoveredInfo && hoveredInfo.rect.left > window.innerWidth - 300) {
243
+ labelStyle.left = "auto";
244
+ labelStyle.right = 0;
245
+ }
246
+ return createPortal(
247
+ /* @__PURE__ */ jsxs("div", { style: styles.root, "data-devtools-panel": "", children: [
248
+ /* @__PURE__ */ jsx(AnimatePresence, { children: isGrabbing && hoveredInfo && /* @__PURE__ */ jsx(
249
+ motion.div,
250
+ {
251
+ style: styles.highlight,
252
+ initial: {
253
+ top: hoveredInfo.rect.top,
254
+ left: hoveredInfo.rect.left,
255
+ width: hoveredInfo.rect.width,
256
+ height: hoveredInfo.rect.height,
257
+ opacity: 0
258
+ },
259
+ animate: {
260
+ top: hoveredInfo.rect.top,
261
+ left: hoveredInfo.rect.left,
262
+ width: hoveredInfo.rect.width,
263
+ height: hoveredInfo.rect.height,
264
+ opacity: 1
265
+ },
266
+ exit: {
267
+ opacity: 0
268
+ },
269
+ transition: springTransition,
270
+ children: /* @__PURE__ */ jsxs(
271
+ motion.div,
272
+ {
273
+ style: labelStyle,
274
+ initial: { opacity: 0, y: 4 },
275
+ animate: { opacity: 1, y: 0 },
276
+ transition: { delay: 0.03, duration: 0.12 },
277
+ children: [
278
+ /* @__PURE__ */ jsxs("div", { style: styles.labelTop, children: [
279
+ /* @__PURE__ */ jsxs("span", { style: styles.tagBadge, children: [
280
+ "<",
281
+ hoveredInfo.tag,
282
+ ">"
283
+ ] }),
284
+ hoveredInfo.text && /* @__PURE__ */ jsx(
285
+ "span",
286
+ {
287
+ style: {
288
+ color: "hsl(215 20% 65%)",
289
+ overflow: "hidden",
290
+ textOverflow: "ellipsis",
291
+ whiteSpace: "nowrap",
292
+ maxWidth: 150,
293
+ fontSize: 11
294
+ },
295
+ children: hoveredInfo.text
296
+ }
297
+ )
298
+ ] }),
299
+ /* @__PURE__ */ jsx("div", { style: styles.selector, children: hoveredInfo.selector }),
300
+ hoveredInfo.source && /* @__PURE__ */ jsxs("div", { style: styles.source, children: [
301
+ /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5h-2z" }) }),
302
+ formatSourcePath({ source: hoveredInfo.source })
303
+ ] })
304
+ ]
305
+ }
306
+ )
307
+ },
308
+ "grabber-highlight"
309
+ ) }),
310
+ /* @__PURE__ */ jsx(AnimatePresence, { children: isGrabbing && /* @__PURE__ */ jsxs(
311
+ motion.div,
312
+ {
313
+ style: styles.hint,
314
+ initial: { opacity: 0, y: 10, scale: 0.95 },
315
+ animate: { opacity: 1, y: 0, scale: 1 },
316
+ exit: { opacity: 0, y: 10, scale: 0.95 },
317
+ transition: { duration: 0.2, ease: [0.16, 1, 0.3, 1] },
318
+ children: [
319
+ /* @__PURE__ */ jsxs("div", { style: styles.hintItem, children: [
320
+ /* @__PURE__ */ jsx("span", { style: styles.kbd, children: "Click" }),
321
+ /* @__PURE__ */ jsx("span", { children: "Add step" })
322
+ ] }),
323
+ /* @__PURE__ */ jsx("div", { style: styles.divider }),
324
+ /* @__PURE__ */ jsxs("div", { style: styles.hintItem, children: [
325
+ /* @__PURE__ */ jsx("span", { style: styles.kbd, children: "ESC" }),
326
+ /* @__PURE__ */ jsx("span", { children: "Cancel" })
327
+ ] }),
328
+ /* @__PURE__ */ jsx("div", { style: styles.divider }),
329
+ /* @__PURE__ */ jsxs("div", { style: styles.hintItem, children: [
330
+ /* @__PURE__ */ jsx("span", { style: styles.kbd, children: "Ctrl+Shift+G" }),
331
+ /* @__PURE__ */ jsx("span", { children: "Toggle" })
332
+ ] })
333
+ ]
334
+ },
335
+ "grabber-hint"
336
+ ) })
337
+ ] }),
338
+ portalContainer
339
+ );
340
+ }
341
+
342
+ // src/devtools/components/StepList.tsx
343
+ import { useCallback, useState as useState2 } from "react";
344
+ import {
345
+ DndContext,
346
+ DragOverlay,
347
+ closestCenter,
348
+ KeyboardSensor,
349
+ PointerSensor,
350
+ useSensor,
351
+ useSensors
352
+ } from "@dnd-kit/core";
353
+ import {
354
+ SortableContext,
355
+ sortableKeyboardCoordinates,
356
+ verticalListSortingStrategy
357
+ } from "@dnd-kit/sortable";
358
+
359
+ // src/devtools/components/StepItem.tsx
360
+ import { useSortable } from "@dnd-kit/sortable";
361
+ import { CSS as CSS2 } from "@dnd-kit/utilities";
362
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
363
+ var styles2 = {
364
+ card: {
365
+ display: "flex",
366
+ alignItems: "stretch",
367
+ gap: 0,
368
+ backgroundColor: "hsl(215 20% 16%)",
369
+ borderRadius: 8,
370
+ border: "1px solid hsl(215 20% 22%)",
371
+ fontSize: 12,
372
+ fontFamily: "inherit",
373
+ overflow: "hidden"
374
+ },
375
+ cardGhost: {
376
+ opacity: 0.4,
377
+ border: "1px dashed hsl(217 91% 60%)",
378
+ backgroundColor: "hsl(217 91% 60% / 0.05)"
379
+ },
380
+ cardOverlay: {
381
+ boxShadow: "0 12px 24px rgba(0, 0, 0, 0.4)",
382
+ border: "1px solid hsl(217 91% 60%)"
383
+ },
384
+ dragHandle: {
385
+ display: "flex",
386
+ alignItems: "center",
387
+ justifyContent: "center",
388
+ width: 28,
389
+ cursor: "grab",
390
+ color: "hsl(215 20% 40%)",
391
+ flexShrink: 0,
392
+ backgroundColor: "hsl(215 20% 13%)",
393
+ borderRight: "1px solid hsl(215 20% 20%)",
394
+ transition: "color 0.15s ease, background-color 0.15s ease"
395
+ },
396
+ dragHandleActive: {
397
+ cursor: "grabbing",
398
+ backgroundColor: "hsl(217 91% 60% / 0.15)",
399
+ color: "hsl(217 91% 60%)"
400
+ },
401
+ content: {
402
+ flex: 1,
403
+ minWidth: 0,
404
+ display: "flex",
405
+ flexDirection: "column",
406
+ gap: 4,
407
+ padding: 10
408
+ },
409
+ header: {
410
+ display: "flex",
411
+ alignItems: "center",
412
+ gap: 6
413
+ },
414
+ order: {
415
+ display: "flex",
416
+ alignItems: "center",
417
+ justifyContent: "center",
418
+ width: 18,
419
+ height: 18,
420
+ backgroundColor: "hsl(217 91% 55% / 0.2)",
421
+ color: "hsl(217 91% 70%)",
422
+ fontSize: 10,
423
+ fontWeight: 600,
424
+ borderRadius: 5,
425
+ flexShrink: 0
426
+ },
427
+ tagBadge: {
428
+ display: "inline-flex",
429
+ alignItems: "center",
430
+ padding: "2px 6px",
431
+ backgroundColor: "hsl(215 20% 22%)",
432
+ color: "hsl(217 91% 70%)",
433
+ fontFamily: "ui-monospace, monospace",
434
+ fontSize: 10,
435
+ fontWeight: 500,
436
+ borderRadius: 4
437
+ },
438
+ text: {
439
+ color: "hsl(215 20% 65%)",
440
+ overflow: "hidden",
441
+ textOverflow: "ellipsis",
442
+ whiteSpace: "nowrap",
443
+ maxWidth: 140,
444
+ fontSize: 11
445
+ },
446
+ selector: {
447
+ display: "block",
448
+ color: "hsl(265 83% 75%)",
449
+ fontFamily: "ui-monospace, monospace",
450
+ fontSize: 10,
451
+ overflow: "hidden",
452
+ textOverflow: "ellipsis",
453
+ whiteSpace: "nowrap",
454
+ padding: "4px 6px",
455
+ backgroundColor: "hsl(215 20% 12%)",
456
+ borderRadius: 4,
457
+ border: "1px solid hsl(215 20% 18%)"
458
+ },
459
+ sourceRow: {
460
+ display: "flex",
461
+ alignItems: "center",
462
+ gap: 4,
463
+ marginTop: 2
464
+ },
465
+ sourceLink: {
466
+ display: "inline-flex",
467
+ alignItems: "center",
468
+ gap: 4,
469
+ color: "hsl(142 71% 55%)",
470
+ fontSize: 10,
471
+ fontFamily: "ui-monospace, monospace",
472
+ textDecoration: "none",
473
+ padding: "2px 0",
474
+ transition: "color 0.15s ease"
475
+ },
476
+ copyButton: {
477
+ display: "flex",
478
+ alignItems: "center",
479
+ justifyContent: "center",
480
+ width: 18,
481
+ height: 18,
482
+ backgroundColor: "transparent",
483
+ border: "none",
484
+ color: "hsl(215 20% 50%)",
485
+ cursor: "pointer",
486
+ padding: 0,
487
+ borderRadius: 4,
488
+ transition: "color 0.15s ease, background-color 0.15s ease",
489
+ outline: "none"
490
+ },
491
+ deleteButton: {
492
+ display: "flex",
493
+ alignItems: "center",
494
+ justifyContent: "center",
495
+ width: 22,
496
+ height: 22,
497
+ backgroundColor: "transparent",
498
+ border: "none",
499
+ color: "hsl(215 20% 45%)",
500
+ cursor: "pointer",
501
+ padding: 0,
502
+ borderRadius: 5,
503
+ flexShrink: 0,
504
+ alignSelf: "flex-start",
505
+ marginTop: 2,
506
+ transition: "color 0.15s ease, background-color 0.15s ease",
507
+ outline: "none"
508
+ }
509
+ };
510
+ function SortableStepItem(props) {
511
+ const { step, index, onDelete, isBeingDragged = false } = props;
512
+ const {
513
+ attributes,
514
+ listeners,
515
+ setNodeRef,
516
+ transform,
517
+ transition,
518
+ isDragging
519
+ } = useSortable({ id: step.id });
520
+ const handleCopySource = async () => {
521
+ if (step.source) {
522
+ const path = formatSourcePath({ source: step.source });
523
+ await navigator.clipboard.writeText(path);
524
+ }
525
+ };
526
+ const isGhost = isDragging || isBeingDragged;
527
+ const wrapperStyle = {
528
+ transform: CSS2.Transform.toString(transform),
529
+ transition: transition ?? "transform 200ms ease"
530
+ };
531
+ const cardStyle = {
532
+ ...styles2.card,
533
+ ...isGhost && styles2.cardGhost
534
+ };
535
+ const handleStyle = {
536
+ ...styles2.dragHandle
537
+ };
538
+ return /* @__PURE__ */ jsx2("div", { ref: setNodeRef, style: wrapperStyle, children: /* @__PURE__ */ jsxs2("div", { style: cardStyle, children: [
539
+ /* @__PURE__ */ jsx2("div", { style: handleStyle, ...attributes, ...listeners, children: /* @__PURE__ */ jsxs2("svg", { width: "10", height: "16", viewBox: "0 0 10 16", fill: "currentColor", children: [
540
+ /* @__PURE__ */ jsx2("circle", { cx: "3", cy: "2", r: "1.5" }),
541
+ /* @__PURE__ */ jsx2("circle", { cx: "7", cy: "2", r: "1.5" }),
542
+ /* @__PURE__ */ jsx2("circle", { cx: "3", cy: "8", r: "1.5" }),
543
+ /* @__PURE__ */ jsx2("circle", { cx: "7", cy: "8", r: "1.5" }),
544
+ /* @__PURE__ */ jsx2("circle", { cx: "3", cy: "14", r: "1.5" }),
545
+ /* @__PURE__ */ jsx2("circle", { cx: "7", cy: "14", r: "1.5" })
546
+ ] }) }),
547
+ /* @__PURE__ */ jsxs2("div", { style: styles2.content, children: [
548
+ /* @__PURE__ */ jsxs2("div", { style: styles2.header, children: [
549
+ /* @__PURE__ */ jsx2("span", { style: styles2.order, children: index + 1 }),
550
+ /* @__PURE__ */ jsxs2("span", { style: styles2.tagBadge, children: [
551
+ "<",
552
+ step.elementTag,
553
+ ">"
554
+ ] }),
555
+ step.elementText && /* @__PURE__ */ jsx2("span", { style: styles2.text, children: step.elementText })
556
+ ] }),
557
+ /* @__PURE__ */ jsx2("div", { style: styles2.selector, children: step.selector }),
558
+ step.source && /* @__PURE__ */ jsxs2("div", { style: styles2.sourceRow, children: [
559
+ /* @__PURE__ */ jsx2(
560
+ "svg",
561
+ {
562
+ width: "10",
563
+ height: "10",
564
+ viewBox: "0 0 16 16",
565
+ fill: "hsl(142 71% 55%)",
566
+ children: /* @__PURE__ */ jsx2("path", { d: "M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5h-2z" })
567
+ }
568
+ ),
569
+ /* @__PURE__ */ jsx2(
570
+ "a",
571
+ {
572
+ href: getVSCodeLink({ source: step.source }),
573
+ style: styles2.sourceLink,
574
+ title: "Open in VS Code",
575
+ children: formatSourcePath({ source: step.source })
576
+ }
577
+ ),
578
+ /* @__PURE__ */ jsx2(
579
+ "button",
580
+ {
581
+ type: "button",
582
+ style: styles2.copyButton,
583
+ onClick: handleCopySource,
584
+ title: "Copy path",
585
+ children: /* @__PURE__ */ jsxs2(
586
+ "svg",
587
+ {
588
+ width: "10",
589
+ height: "10",
590
+ viewBox: "0 0 16 16",
591
+ fill: "currentColor",
592
+ children: [
593
+ /* @__PURE__ */ jsx2("path", { d: "M10 2H4a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2zM4 1h6a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V4a3 3 0 0 1 3-3z" }),
594
+ /* @__PURE__ */ jsx2("path", { d: "M14 5a1 1 0 0 1 1 1v8a2 2 0 0 1-2 2H6a1 1 0 0 1 0-2h7V6a1 1 0 0 1 1-1z" })
595
+ ]
596
+ }
597
+ )
598
+ }
599
+ )
600
+ ] })
601
+ ] }),
602
+ /* @__PURE__ */ jsx2(
603
+ "button",
604
+ {
605
+ type: "button",
606
+ style: styles2.deleteButton,
607
+ onClick: onDelete,
608
+ title: "Delete step",
609
+ children: /* @__PURE__ */ jsx2("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx2("path", { d: "M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z" }) })
610
+ }
611
+ )
612
+ ] }) });
613
+ }
614
+ function StepItemDragPreview(props) {
615
+ const { step, index } = props;
616
+ const cardStyle = {
617
+ ...styles2.card,
618
+ ...styles2.cardOverlay,
619
+ width: 298
620
+ // Fixed width for overlay
621
+ };
622
+ const handleStyle = {
623
+ ...styles2.dragHandle,
624
+ ...styles2.dragHandleActive
625
+ };
626
+ return /* @__PURE__ */ jsxs2("div", { style: cardStyle, children: [
627
+ /* @__PURE__ */ jsx2("div", { style: handleStyle, children: /* @__PURE__ */ jsxs2("svg", { width: "10", height: "16", viewBox: "0 0 10 16", fill: "currentColor", children: [
628
+ /* @__PURE__ */ jsx2("circle", { cx: "3", cy: "2", r: "1.5" }),
629
+ /* @__PURE__ */ jsx2("circle", { cx: "7", cy: "2", r: "1.5" }),
630
+ /* @__PURE__ */ jsx2("circle", { cx: "3", cy: "8", r: "1.5" }),
631
+ /* @__PURE__ */ jsx2("circle", { cx: "7", cy: "8", r: "1.5" }),
632
+ /* @__PURE__ */ jsx2("circle", { cx: "3", cy: "14", r: "1.5" }),
633
+ /* @__PURE__ */ jsx2("circle", { cx: "7", cy: "14", r: "1.5" })
634
+ ] }) }),
635
+ /* @__PURE__ */ jsxs2("div", { style: styles2.content, children: [
636
+ /* @__PURE__ */ jsxs2("div", { style: styles2.header, children: [
637
+ /* @__PURE__ */ jsx2("span", { style: styles2.order, children: index + 1 }),
638
+ /* @__PURE__ */ jsxs2("span", { style: styles2.tagBadge, children: [
639
+ "<",
640
+ step.elementTag,
641
+ ">"
642
+ ] }),
643
+ step.elementText && /* @__PURE__ */ jsx2("span", { style: styles2.text, children: step.elementText })
644
+ ] }),
645
+ /* @__PURE__ */ jsx2("div", { style: styles2.selector, children: step.selector }),
646
+ step.source && /* @__PURE__ */ jsxs2("div", { style: styles2.sourceRow, children: [
647
+ /* @__PURE__ */ jsx2(
648
+ "svg",
649
+ {
650
+ width: "10",
651
+ height: "10",
652
+ viewBox: "0 0 16 16",
653
+ fill: "hsl(142 71% 55%)",
654
+ children: /* @__PURE__ */ jsx2("path", { d: "M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5h-2z" })
655
+ }
656
+ ),
657
+ /* @__PURE__ */ jsx2("span", { style: styles2.sourceLink, children: formatSourcePath({ source: step.source }) })
658
+ ] })
659
+ ] }),
660
+ /* @__PURE__ */ jsx2("div", { style: { ...styles2.deleteButton, visibility: "hidden" }, children: /* @__PURE__ */ jsx2("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx2("path", { d: "M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z" }) }) })
661
+ ] });
662
+ }
663
+
664
+ // src/devtools/components/Toolbar.tsx
665
+ import { useState } from "react";
666
+ import { motion as motion2 } from "motion/react";
667
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
668
+ var styles3 = {
669
+ toolbar: {
670
+ display: "flex",
671
+ flexDirection: "column",
672
+ gap: 8,
673
+ padding: "10px 10px 0"
674
+ },
675
+ grabRow: {
676
+ display: "flex",
677
+ gap: 6
678
+ },
679
+ grabButton: {
680
+ flex: 1,
681
+ display: "flex",
682
+ alignItems: "center",
683
+ justifyContent: "center",
684
+ gap: 6,
685
+ padding: "8px 12px",
686
+ backgroundColor: "#252a35",
687
+ border: "1px solid #353d4a",
688
+ borderRadius: 8,
689
+ color: "#a3adc2",
690
+ fontSize: 12,
691
+ fontWeight: 500,
692
+ fontFamily: "inherit",
693
+ cursor: "pointer",
694
+ transition: "all 0.15s ease",
695
+ outline: "none"
696
+ },
697
+ grabButtonActive: {
698
+ backgroundColor: "rgba(59, 130, 246, 0.15)",
699
+ borderColor: "rgba(59, 130, 246, 0.4)",
700
+ color: "#60a5fa",
701
+ boxShadow: "0 0 0 2px rgba(59, 130, 246, 0.1)"
702
+ },
703
+ actionRow: {
704
+ display: "flex",
705
+ gap: 6
706
+ },
707
+ actionButton: {
708
+ flex: 1,
709
+ display: "flex",
710
+ alignItems: "center",
711
+ justifyContent: "center",
712
+ gap: 5,
713
+ padding: "7px 8px",
714
+ backgroundColor: "transparent",
715
+ border: "1px solid #2d3544",
716
+ borderRadius: 6,
717
+ color: "#8b96a9",
718
+ fontSize: 11,
719
+ fontWeight: 500,
720
+ fontFamily: "inherit",
721
+ cursor: "pointer",
722
+ transition: "all 0.15s ease",
723
+ whiteSpace: "nowrap",
724
+ position: "relative",
725
+ outline: "none"
726
+ },
727
+ actionButtonDisabled: {
728
+ opacity: 0.4,
729
+ cursor: "not-allowed"
730
+ },
731
+ actionButtonDanger: {
732
+ borderColor: "#7a2c2c",
733
+ color: "#e05555"
734
+ },
735
+ kbd: {
736
+ display: "inline-flex",
737
+ alignItems: "center",
738
+ padding: "1px 4px",
739
+ marginLeft: 6,
740
+ backgroundColor: "#1e232c",
741
+ border: "1px solid #2d3544",
742
+ borderRadius: 3,
743
+ fontSize: 10,
744
+ fontFamily: "ui-monospace, monospace",
745
+ color: "#6b7685"
746
+ },
747
+ copiedBadge: {
748
+ position: "absolute",
749
+ top: -6,
750
+ right: -6,
751
+ padding: "2px 5px",
752
+ backgroundColor: "#166534",
753
+ color: "#86efac",
754
+ fontSize: 9,
755
+ fontWeight: 600,
756
+ borderRadius: 4,
757
+ boxShadow: "0 2px 8px rgba(0, 0, 0, 0.3)"
758
+ }
759
+ };
760
+ function Toolbar(props) {
761
+ const { mode, stepCount, onToggleGrab, onExport, onCopyForAI, onReset } = props;
762
+ const [copied, setCopied] = useState(false);
763
+ const isGrabbing = mode === "grabbing";
764
+ const hasSteps = stepCount > 0;
765
+ const handleCopy = async () => {
766
+ await onCopyForAI();
767
+ setCopied(true);
768
+ setTimeout(() => setCopied(false), 1500);
769
+ };
770
+ const grabButtonStyle = {
771
+ ...styles3.grabButton,
772
+ ...isGrabbing && styles3.grabButtonActive
773
+ };
774
+ const exportButtonStyle = {
775
+ ...styles3.actionButton,
776
+ ...!hasSteps && styles3.actionButtonDisabled
777
+ };
778
+ const copyButtonStyle = {
779
+ ...styles3.actionButton,
780
+ ...!hasSteps && styles3.actionButtonDisabled
781
+ };
782
+ const resetButtonStyle = {
783
+ ...styles3.actionButton,
784
+ ...!hasSteps && styles3.actionButtonDisabled,
785
+ ...hasSteps && styles3.actionButtonDanger
786
+ };
787
+ return /* @__PURE__ */ jsxs3("div", { style: styles3.toolbar, children: [
788
+ /* @__PURE__ */ jsx3("div", { style: styles3.grabRow, children: /* @__PURE__ */ jsx3(
789
+ motion2.button,
790
+ {
791
+ type: "button",
792
+ style: grabButtonStyle,
793
+ onClick: onToggleGrab,
794
+ whileHover: { scale: 1.01 },
795
+ whileTap: { scale: 0.98 },
796
+ children: isGrabbing ? /* @__PURE__ */ jsxs3(Fragment, { children: [
797
+ /* @__PURE__ */ jsxs3(
798
+ "svg",
799
+ {
800
+ width: "14",
801
+ height: "14",
802
+ viewBox: "0 0 16 16",
803
+ fill: "currentColor",
804
+ children: [
805
+ /* @__PURE__ */ jsxs3("circle", { cx: "8", cy: "8", r: "3", children: [
806
+ /* @__PURE__ */ jsx3(
807
+ "animate",
808
+ {
809
+ attributeName: "r",
810
+ values: "3;4;3",
811
+ dur: "1s",
812
+ repeatCount: "indefinite"
813
+ }
814
+ ),
815
+ /* @__PURE__ */ jsx3(
816
+ "animate",
817
+ {
818
+ attributeName: "opacity",
819
+ values: "1;0.5;1",
820
+ dur: "1s",
821
+ repeatCount: "indefinite"
822
+ }
823
+ )
824
+ ] }),
825
+ /* @__PURE__ */ jsxs3(
826
+ "circle",
827
+ {
828
+ cx: "8",
829
+ cy: "8",
830
+ r: "6",
831
+ fill: "none",
832
+ stroke: "currentColor",
833
+ strokeWidth: "1",
834
+ opacity: "0.3",
835
+ children: [
836
+ /* @__PURE__ */ jsx3(
837
+ "animate",
838
+ {
839
+ attributeName: "r",
840
+ values: "4;7;4",
841
+ dur: "1.5s",
842
+ repeatCount: "indefinite"
843
+ }
844
+ ),
845
+ /* @__PURE__ */ jsx3(
846
+ "animate",
847
+ {
848
+ attributeName: "opacity",
849
+ values: "0.3;0;0.3",
850
+ dur: "1.5s",
851
+ repeatCount: "indefinite"
852
+ }
853
+ )
854
+ ]
855
+ }
856
+ )
857
+ ]
858
+ }
859
+ ),
860
+ "Grabbing..."
861
+ ] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
862
+ /* @__PURE__ */ jsx3(
863
+ "svg",
864
+ {
865
+ width: "14",
866
+ height: "14",
867
+ viewBox: "0 0 16 16",
868
+ fill: "currentColor",
869
+ children: /* @__PURE__ */ jsx3("path", { d: "M14 0a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h12zM5.904 10.803L10 6.707v2.768a.5.5 0 0 0 1 0V5.5a.5.5 0 0 0-.5-.5H6.525a.5.5 0 1 0 0 1h2.768l-4.096 4.096a.5.5 0 0 0 .707.707z" })
870
+ }
871
+ ),
872
+ "Grab Element",
873
+ /* @__PURE__ */ jsx3("span", { style: styles3.kbd, children: "Ctrl+Shift+G" })
874
+ ] })
875
+ }
876
+ ) }),
877
+ /* @__PURE__ */ jsxs3("div", { style: styles3.actionRow, children: [
878
+ /* @__PURE__ */ jsxs3(
879
+ motion2.button,
880
+ {
881
+ type: "button",
882
+ style: exportButtonStyle,
883
+ onClick: onExport,
884
+ disabled: !hasSteps,
885
+ whileHover: hasSteps ? { scale: 1.02 } : {},
886
+ whileTap: hasSteps ? { scale: 0.98 } : {},
887
+ title: "Download JSON file",
888
+ children: [
889
+ /* @__PURE__ */ jsxs3("svg", { width: "11", height: "11", viewBox: "0 0 16 16", fill: "currentColor", children: [
890
+ /* @__PURE__ */ jsx3("path", { d: "M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z" }),
891
+ /* @__PURE__ */ jsx3("path", { d: "M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z" })
892
+ ] }),
893
+ "Export"
894
+ ]
895
+ }
896
+ ),
897
+ /* @__PURE__ */ jsxs3(
898
+ motion2.button,
899
+ {
900
+ type: "button",
901
+ style: copyButtonStyle,
902
+ onClick: handleCopy,
903
+ disabled: !hasSteps,
904
+ whileHover: hasSteps ? { scale: 1.02 } : {},
905
+ whileTap: hasSteps ? { scale: 0.98 } : {},
906
+ title: "Copy JSON to clipboard",
907
+ children: [
908
+ /* @__PURE__ */ jsxs3("svg", { width: "11", height: "11", viewBox: "0 0 16 16", fill: "currentColor", children: [
909
+ /* @__PURE__ */ jsx3("path", { d: "M10 2H4a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2zM4 1h6a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V4a3 3 0 0 1 3-3z" }),
910
+ /* @__PURE__ */ jsx3("path", { d: "M14 5a1 1 0 0 1 1 1v8a2 2 0 0 1-2 2H6a1 1 0 0 1 0-2h7V6a1 1 0 0 1 1-1z" })
911
+ ] }),
912
+ "Copy",
913
+ copied && /* @__PURE__ */ jsx3(
914
+ motion2.span,
915
+ {
916
+ style: styles3.copiedBadge,
917
+ initial: { opacity: 0, scale: 0.8, y: 4 },
918
+ animate: { opacity: 1, scale: 1, y: 0 },
919
+ exit: { opacity: 0, scale: 0.8 },
920
+ children: "Copied!"
921
+ }
922
+ )
923
+ ]
924
+ }
925
+ ),
926
+ /* @__PURE__ */ jsxs3(
927
+ motion2.button,
928
+ {
929
+ type: "button",
930
+ style: resetButtonStyle,
931
+ onClick: onReset,
932
+ disabled: !hasSteps,
933
+ whileHover: hasSteps ? { scale: 1.02 } : {},
934
+ whileTap: hasSteps ? { scale: 0.98 } : {},
935
+ title: "Clear all steps",
936
+ children: [
937
+ /* @__PURE__ */ jsxs3("svg", { width: "11", height: "11", viewBox: "0 0 16 16", fill: "currentColor", children: [
938
+ /* @__PURE__ */ jsx3("path", { d: "M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z" }),
939
+ /* @__PURE__ */ jsx3(
940
+ "path",
941
+ {
942
+ fillRule: "evenodd",
943
+ d: "M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4L4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"
944
+ }
945
+ )
946
+ ] }),
947
+ "Reset"
948
+ ]
949
+ }
950
+ )
951
+ ] })
952
+ ] });
953
+ }
954
+
955
+ // src/devtools/components/StepList.tsx
956
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
957
+ var styles4 = {
958
+ scrollArea: {
959
+ flex: 1,
960
+ overflowY: "auto",
961
+ overflowX: "hidden",
962
+ padding: 10
963
+ },
964
+ stepList: {
965
+ display: "flex",
966
+ flexDirection: "column",
967
+ gap: 6
968
+ },
969
+ empty: {
970
+ display: "flex",
971
+ flexDirection: "column",
972
+ alignItems: "center",
973
+ justifyContent: "center",
974
+ gap: 8,
975
+ padding: "32px 16px",
976
+ textAlign: "center",
977
+ color: "hsl(215 20% 55%)"
978
+ },
979
+ emptyIcon: {
980
+ width: 40,
981
+ height: 40,
982
+ borderRadius: 10,
983
+ backgroundColor: "hsl(215 20% 18%)",
984
+ display: "flex",
985
+ alignItems: "center",
986
+ justifyContent: "center",
987
+ marginBottom: 4
988
+ },
989
+ emptyText: {
990
+ fontSize: 13,
991
+ lineHeight: 1.5
992
+ },
993
+ kbd: {
994
+ display: "inline-flex",
995
+ alignItems: "center",
996
+ padding: "2px 5px",
997
+ backgroundColor: "hsl(215 20% 15%)",
998
+ border: "1px solid hsl(215 20% 22%)",
999
+ borderRadius: 4,
1000
+ fontSize: 11,
1001
+ fontFamily: "ui-monospace, monospace",
1002
+ color: "hsl(215 20% 55%)"
1003
+ }
1004
+ };
1005
+ function StepList(props) {
1006
+ const {
1007
+ steps,
1008
+ mode,
1009
+ onToggleGrab,
1010
+ onDeleteStep,
1011
+ onReorderSteps,
1012
+ onClearAll,
1013
+ onExport
1014
+ } = props;
1015
+ const [activeId, setActiveId] = useState2(null);
1016
+ const activeStep = activeId ? steps.find((s) => s.id === activeId) : null;
1017
+ const activeIndex = activeId ? steps.findIndex((s) => s.id === activeId) : -1;
1018
+ const sensors = useSensors(
1019
+ useSensor(PointerSensor, {
1020
+ activationConstraint: {
1021
+ distance: 4
1022
+ }
1023
+ }),
1024
+ useSensor(KeyboardSensor, {
1025
+ coordinateGetter: sortableKeyboardCoordinates
1026
+ })
1027
+ );
1028
+ const handleDragStart = useCallback((event) => {
1029
+ setActiveId(event.active.id);
1030
+ }, []);
1031
+ const handleDragEnd = useCallback(
1032
+ (event) => {
1033
+ const { active, over } = event;
1034
+ setActiveId(null);
1035
+ if (over && active.id !== over.id) {
1036
+ const oldIndex = steps.findIndex((s) => s.id === active.id);
1037
+ const newIndex = steps.findIndex((s) => s.id === over.id);
1038
+ if (oldIndex !== -1 && newIndex !== -1) {
1039
+ onReorderSteps({ fromIndex: oldIndex, toIndex: newIndex });
1040
+ }
1041
+ }
1042
+ },
1043
+ [steps, onReorderSteps]
1044
+ );
1045
+ const handleDragCancel = useCallback(() => {
1046
+ setActiveId(null);
1047
+ }, []);
1048
+ const handleExportClick = useCallback(() => {
1049
+ const data = onExport();
1050
+ const blob = new Blob([JSON.stringify(data, null, 2)], {
1051
+ type: "application/json"
1052
+ });
1053
+ const url = URL.createObjectURL(blob);
1054
+ const a = document.createElement("a");
1055
+ a.href = url;
1056
+ a.download = `flowsterix-steps-${Date.now()}.json`;
1057
+ a.click();
1058
+ URL.revokeObjectURL(url);
1059
+ }, [onExport]);
1060
+ const handleCopyForAI = useCallback(async () => {
1061
+ const data = onExport();
1062
+ await navigator.clipboard.writeText(JSON.stringify(data, null, 2));
1063
+ }, [onExport]);
1064
+ const handleReset = useCallback(() => {
1065
+ if (confirm("Clear all steps? This cannot be undone.")) {
1066
+ onClearAll();
1067
+ }
1068
+ }, [onClearAll]);
1069
+ if (typeof window === "undefined") return null;
1070
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1071
+ /* @__PURE__ */ jsx4(
1072
+ Toolbar,
1073
+ {
1074
+ mode,
1075
+ stepCount: steps.length,
1076
+ onToggleGrab,
1077
+ onExport: handleExportClick,
1078
+ onCopyForAI: handleCopyForAI,
1079
+ onReset: handleReset
1080
+ }
1081
+ ),
1082
+ /* @__PURE__ */ jsx4("div", { style: styles4.scrollArea, children: steps.length === 0 ? /* @__PURE__ */ jsxs4("div", { style: styles4.empty, children: [
1083
+ /* @__PURE__ */ jsx4("div", { style: styles4.emptyIcon, children: /* @__PURE__ */ jsx4(
1084
+ "svg",
1085
+ {
1086
+ width: "20",
1087
+ height: "20",
1088
+ viewBox: "0 0 16 16",
1089
+ fill: "hsl(215 20% 45%)",
1090
+ children: /* @__PURE__ */ jsx4("path", { d: "M14 0a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h12zM5.904 10.803L10 6.707v2.768a.5.5 0 0 0 1 0V5.5a.5.5 0 0 0-.5-.5H6.525a.5.5 0 1 0 0 1h2.768l-4.096 4.096a.5.5 0 0 0 .707.707z" })
1091
+ }
1092
+ ) }),
1093
+ /* @__PURE__ */ jsxs4("div", { style: styles4.emptyText, children: [
1094
+ /* @__PURE__ */ jsx4("div", { children: "No steps captured yet." }),
1095
+ /* @__PURE__ */ jsxs4("div", { style: { marginTop: 6 }, children: [
1096
+ "Press ",
1097
+ /* @__PURE__ */ jsx4("span", { style: styles4.kbd, children: "Ctrl+Shift+G" }),
1098
+ " to grab"
1099
+ ] })
1100
+ ] })
1101
+ ] }) : /* @__PURE__ */ jsxs4(
1102
+ DndContext,
1103
+ {
1104
+ sensors,
1105
+ collisionDetection: closestCenter,
1106
+ onDragStart: handleDragStart,
1107
+ onDragEnd: handleDragEnd,
1108
+ onDragCancel: handleDragCancel,
1109
+ children: [
1110
+ /* @__PURE__ */ jsx4(
1111
+ SortableContext,
1112
+ {
1113
+ items: steps.map((s) => s.id),
1114
+ strategy: verticalListSortingStrategy,
1115
+ children: /* @__PURE__ */ jsx4("div", { style: styles4.stepList, children: steps.map((step, index) => /* @__PURE__ */ jsx4(
1116
+ SortableStepItem,
1117
+ {
1118
+ step,
1119
+ index,
1120
+ onDelete: () => onDeleteStep({ id: step.id }),
1121
+ isDragActive: activeId !== null,
1122
+ isBeingDragged: step.id === activeId
1123
+ },
1124
+ step.id
1125
+ )) })
1126
+ }
1127
+ ),
1128
+ /* @__PURE__ */ jsx4(DragOverlay, { dropAnimation: null, children: activeStep && /* @__PURE__ */ jsx4(StepItemDragPreview, { step: activeStep, index: activeIndex }) })
1129
+ ]
1130
+ }
1131
+ ) })
1132
+ ] });
1133
+ }
1134
+
1135
+ // src/devtools/components/TabNav.tsx
1136
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1137
+ var styles5 = {
1138
+ container: {
1139
+ display: "flex",
1140
+ gap: 2,
1141
+ padding: "8px 10px 0",
1142
+ borderBottom: "1px solid hsl(215 20% 20%)"
1143
+ },
1144
+ tab: {
1145
+ flex: 1,
1146
+ display: "flex",
1147
+ alignItems: "center",
1148
+ justifyContent: "center",
1149
+ gap: 6,
1150
+ padding: "8px 12px",
1151
+ backgroundColor: "transparent",
1152
+ border: "none",
1153
+ borderBottom: "2px solid transparent",
1154
+ color: "hsl(215 20% 55%)",
1155
+ fontSize: 12,
1156
+ fontWeight: 500,
1157
+ fontFamily: "inherit",
1158
+ cursor: "pointer",
1159
+ transition: "all 0.15s ease",
1160
+ outline: "none",
1161
+ marginBottom: -1
1162
+ },
1163
+ tabActive: {
1164
+ color: "hsl(217 91% 70%)",
1165
+ borderBottomColor: "hsl(217 91% 60%)"
1166
+ },
1167
+ badge: {
1168
+ display: "inline-flex",
1169
+ alignItems: "center",
1170
+ justifyContent: "center",
1171
+ minWidth: 18,
1172
+ height: 18,
1173
+ padding: "0 5px",
1174
+ backgroundColor: "hsl(215 20% 22%)",
1175
+ color: "hsl(215 20% 60%)",
1176
+ fontSize: 10,
1177
+ fontWeight: 600,
1178
+ borderRadius: 9
1179
+ },
1180
+ badgeActive: {
1181
+ backgroundColor: "hsl(217 91% 55% / 0.2)",
1182
+ color: "hsl(217 91% 70%)"
1183
+ }
1184
+ };
1185
+ function TabNav(props) {
1186
+ const { activeTab, onTabChange, stepCount, flowCount } = props;
1187
+ const stepsTabStyle = {
1188
+ ...styles5.tab,
1189
+ ...activeTab === "steps" && styles5.tabActive
1190
+ };
1191
+ const flowsTabStyle = {
1192
+ ...styles5.tab,
1193
+ ...activeTab === "flows" && styles5.tabActive
1194
+ };
1195
+ const stepsBadgeStyle = {
1196
+ ...styles5.badge,
1197
+ ...activeTab === "steps" && styles5.badgeActive
1198
+ };
1199
+ const flowsBadgeStyle = {
1200
+ ...styles5.badge,
1201
+ ...activeTab === "flows" && styles5.badgeActive
1202
+ };
1203
+ return /* @__PURE__ */ jsxs5("div", { style: styles5.container, children: [
1204
+ /* @__PURE__ */ jsxs5(
1205
+ "button",
1206
+ {
1207
+ type: "button",
1208
+ style: stepsTabStyle,
1209
+ onClick: () => onTabChange("steps"),
1210
+ children: [
1211
+ /* @__PURE__ */ jsx5("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx5("path", { d: "M14 0a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h12zM5.904 10.803L10 6.707v2.768a.5.5 0 0 0 1 0V5.5a.5.5 0 0 0-.5-.5H6.525a.5.5 0 1 0 0 1h2.768l-4.096 4.096a.5.5 0 0 0 .707.707z" }) }),
1212
+ "Steps",
1213
+ stepCount > 0 && /* @__PURE__ */ jsx5("span", { style: stepsBadgeStyle, children: stepCount })
1214
+ ]
1215
+ }
1216
+ ),
1217
+ /* @__PURE__ */ jsxs5(
1218
+ "button",
1219
+ {
1220
+ type: "button",
1221
+ style: flowsTabStyle,
1222
+ onClick: () => onTabChange("flows"),
1223
+ children: [
1224
+ /* @__PURE__ */ jsx5("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx5("path", { d: "M6 3.5A1.5 1.5 0 0 1 7.5 2h1A1.5 1.5 0 0 1 10 3.5v1A1.5 1.5 0 0 1 8.5 6v1H14a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0V8h-5v.5a.5.5 0 0 1-1 0V8h-5v.5a.5.5 0 0 1-1 0v-1A.5.5 0 0 1 2 7h5.5V6A1.5 1.5 0 0 1 6 4.5v-1zm-3 8A1.5 1.5 0 0 1 4.5 10h1A1.5 1.5 0 0 1 7 11.5v1A1.5 1.5 0 0 1 5.5 14h-1A1.5 1.5 0 0 1 3 12.5v-1zm6 0a1.5 1.5 0 0 1 1.5-1.5h1a1.5 1.5 0 0 1 1.5 1.5v1a1.5 1.5 0 0 1-1.5 1.5h-1A1.5 1.5 0 0 1 9 12.5v-1z" }) }),
1225
+ "Flows",
1226
+ flowCount > 0 && /* @__PURE__ */ jsx5("span", { style: flowsBadgeStyle, children: flowCount })
1227
+ ]
1228
+ }
1229
+ )
1230
+ ] });
1231
+ }
1232
+
1233
+ // src/devtools/components/FlowsTab.tsx
1234
+ import { useCallback as useCallback4, useState as useState6 } from "react";
1235
+ import { motion as motion5 } from "motion/react";
1236
+
1237
+ // src/devtools/hooks/useFlowsData.ts
1238
+ import { useCallback as useCallback2, useEffect, useState as useState3, useSyncExternalStore } from "react";
1239
+
1240
+ // src/devtools/globalBridge.ts
1241
+ var BRIDGE_KEY = "__FLOWSTERIX_DEVTOOLS_BRIDGE__";
1242
+ function getBridge() {
1243
+ if (typeof window === "undefined") {
1244
+ return { value: null, listeners: /* @__PURE__ */ new Set() };
1245
+ }
1246
+ const w = window;
1247
+ if (!w[BRIDGE_KEY]) {
1248
+ w[BRIDGE_KEY] = { value: null, listeners: /* @__PURE__ */ new Set() };
1249
+ }
1250
+ return w[BRIDGE_KEY];
1251
+ }
1252
+ function getDevToolsBridge() {
1253
+ return getBridge().value;
1254
+ }
1255
+ function subscribeDevToolsBridge(listener) {
1256
+ const bridge = getBridge();
1257
+ bridge.listeners.add(listener);
1258
+ return () => {
1259
+ bridge.listeners.delete(listener);
1260
+ };
1261
+ }
1262
+
1263
+ // src/devtools/hooks/useFlowsData.ts
1264
+ function useBridge() {
1265
+ return useSyncExternalStore(
1266
+ subscribeDevToolsBridge,
1267
+ getDevToolsBridge,
1268
+ () => null
1269
+ // Server snapshot
1270
+ );
1271
+ }
1272
+ function useFlowsData() {
1273
+ const bridge = useBridge();
1274
+ const [flowsData, setFlowsData] = useState3([]);
1275
+ const loadFlowStates = useCallback2(async () => {
1276
+ if (!bridge) {
1277
+ setFlowsData([]);
1278
+ return;
1279
+ }
1280
+ const { flows, activeFlowId, state: activeState, getFlowState } = bridge;
1281
+ const flowDataPromises = [];
1282
+ for (const [flowId, definition] of flows) {
1283
+ const isActive = flowId === activeFlowId;
1284
+ flowDataPromises.push(
1285
+ (async () => {
1286
+ const flowState = isActive ? activeState : await getFlowState(flowId);
1287
+ return {
1288
+ flowId,
1289
+ definition,
1290
+ state: flowState,
1291
+ isActive
1292
+ };
1293
+ })()
1294
+ );
1295
+ }
1296
+ const results = await Promise.all(flowDataPromises);
1297
+ setFlowsData(results);
1298
+ }, [bridge]);
1299
+ useEffect(() => {
1300
+ void loadFlowStates();
1301
+ }, [loadFlowStates]);
1302
+ const deleteFlow = useCallback2(
1303
+ async (flowId) => {
1304
+ if (!bridge) return;
1305
+ if (bridge.activeFlowId === flowId) {
1306
+ bridge.cancel();
1307
+ }
1308
+ await bridge.deleteFlowStorage(flowId);
1309
+ await loadFlowStates();
1310
+ },
1311
+ [bridge, loadFlowStates]
1312
+ );
1313
+ const updateFlow = useCallback2(
1314
+ async (flowId, newState) => {
1315
+ if (!bridge) return;
1316
+ await bridge.updateFlowStorage(flowId, newState);
1317
+ await loadFlowStates();
1318
+ },
1319
+ [bridge, loadFlowStates]
1320
+ );
1321
+ return {
1322
+ flows: flowsData,
1323
+ refreshFlows: loadFlowStates,
1324
+ deleteFlow,
1325
+ updateFlow
1326
+ };
1327
+ }
1328
+
1329
+ // src/devtools/components/FlowItem.tsx
1330
+ import { useState as useState4 } from "react";
1331
+ import { motion as motion3 } from "motion/react";
1332
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1333
+ var styles6 = {
1334
+ card: {
1335
+ display: "flex",
1336
+ flexDirection: "column",
1337
+ gap: 8,
1338
+ padding: 12,
1339
+ backgroundColor: "hsl(215 20% 16%)",
1340
+ borderRadius: 8,
1341
+ border: "1px solid hsl(215 20% 22%)",
1342
+ fontSize: 12,
1343
+ fontFamily: "inherit"
1344
+ },
1345
+ cardActive: {
1346
+ borderColor: "hsl(217 91% 55% / 0.5)",
1347
+ backgroundColor: "hsl(217 91% 55% / 0.05)"
1348
+ },
1349
+ header: {
1350
+ display: "flex",
1351
+ alignItems: "center",
1352
+ justifyContent: "space-between",
1353
+ gap: 8
1354
+ },
1355
+ titleGroup: {
1356
+ display: "flex",
1357
+ alignItems: "center",
1358
+ gap: 8,
1359
+ minWidth: 0
1360
+ },
1361
+ flowId: {
1362
+ fontWeight: 600,
1363
+ color: "hsl(215 20% 85%)",
1364
+ overflow: "hidden",
1365
+ textOverflow: "ellipsis",
1366
+ whiteSpace: "nowrap"
1367
+ },
1368
+ statusBadge: {
1369
+ display: "inline-flex",
1370
+ alignItems: "center",
1371
+ padding: "2px 6px",
1372
+ fontSize: 10,
1373
+ fontWeight: 500,
1374
+ borderRadius: 4,
1375
+ flexShrink: 0
1376
+ },
1377
+ statusIdle: {
1378
+ backgroundColor: "hsl(215 20% 22%)",
1379
+ color: "hsl(215 20% 60%)"
1380
+ },
1381
+ statusRunning: {
1382
+ backgroundColor: "hsl(142 71% 45% / 0.2)",
1383
+ color: "hsl(142 71% 60%)"
1384
+ },
1385
+ statusPaused: {
1386
+ backgroundColor: "hsl(45 93% 47% / 0.2)",
1387
+ color: "hsl(45 93% 60%)"
1388
+ },
1389
+ statusCompleted: {
1390
+ backgroundColor: "hsl(217 91% 55% / 0.2)",
1391
+ color: "hsl(217 91% 70%)"
1392
+ },
1393
+ statusCancelled: {
1394
+ backgroundColor: "hsl(0 70% 50% / 0.2)",
1395
+ color: "hsl(0 70% 65%)"
1396
+ },
1397
+ activeBadge: {
1398
+ backgroundColor: "hsl(142 71% 45% / 0.2)",
1399
+ color: "hsl(142 71% 60%)"
1400
+ },
1401
+ infoRow: {
1402
+ display: "flex",
1403
+ alignItems: "center",
1404
+ gap: 12,
1405
+ color: "hsl(215 20% 55%)",
1406
+ fontSize: 11
1407
+ },
1408
+ infoItem: {
1409
+ display: "flex",
1410
+ alignItems: "center",
1411
+ gap: 4
1412
+ },
1413
+ label: {
1414
+ color: "hsl(215 20% 45%)"
1415
+ },
1416
+ value: {
1417
+ fontFamily: "ui-monospace, monospace",
1418
+ color: "hsl(215 20% 65%)"
1419
+ },
1420
+ actions: {
1421
+ display: "flex",
1422
+ gap: 6,
1423
+ marginTop: 4
1424
+ },
1425
+ actionButton: {
1426
+ display: "flex",
1427
+ alignItems: "center",
1428
+ justifyContent: "center",
1429
+ gap: 4,
1430
+ padding: "6px 10px",
1431
+ backgroundColor: "transparent",
1432
+ border: "1px solid hsl(215 20% 25%)",
1433
+ borderRadius: 5,
1434
+ color: "hsl(215 20% 60%)",
1435
+ fontSize: 10,
1436
+ fontWeight: 500,
1437
+ fontFamily: "inherit",
1438
+ cursor: "pointer",
1439
+ transition: "all 0.15s ease",
1440
+ outline: "none"
1441
+ },
1442
+ actionButtonDanger: {
1443
+ borderColor: "hsl(0 70% 40%)",
1444
+ color: "hsl(0 70% 60%)"
1445
+ },
1446
+ noState: {
1447
+ padding: "8px 0",
1448
+ color: "hsl(215 20% 45%)",
1449
+ fontSize: 11,
1450
+ fontStyle: "italic"
1451
+ }
1452
+ };
1453
+ var statusStyles = {
1454
+ idle: styles6.statusIdle,
1455
+ running: styles6.statusRunning,
1456
+ paused: styles6.statusPaused,
1457
+ completed: styles6.statusCompleted,
1458
+ cancelled: styles6.statusCancelled
1459
+ };
1460
+ function FlowItem(props) {
1461
+ const { flow, onEdit, onDelete } = props;
1462
+ const { flowId, definition, state, isActive } = flow;
1463
+ const [confirmDelete, setConfirmDelete] = useState4(false);
1464
+ const handleDelete = () => {
1465
+ if (confirmDelete) {
1466
+ onDelete();
1467
+ setConfirmDelete(false);
1468
+ } else {
1469
+ setConfirmDelete(true);
1470
+ setTimeout(() => setConfirmDelete(false), 3e3);
1471
+ }
1472
+ };
1473
+ const cardStyle = {
1474
+ ...styles6.card,
1475
+ ...isActive && styles6.cardActive
1476
+ };
1477
+ const statusBadgeStyle = {
1478
+ ...styles6.statusBadge,
1479
+ ...state ? statusStyles[state.status] || styles6.statusIdle : styles6.statusIdle
1480
+ };
1481
+ const deleteButtonStyle = {
1482
+ ...styles6.actionButton,
1483
+ ...confirmDelete && styles6.actionButtonDanger
1484
+ };
1485
+ const stepInfo = state ? `Step ${state.stepIndex + 1}/${definition.steps.length}` : null;
1486
+ return /* @__PURE__ */ jsxs6("div", { style: cardStyle, children: [
1487
+ /* @__PURE__ */ jsxs6("div", { style: styles6.header, children: [
1488
+ /* @__PURE__ */ jsxs6("div", { style: styles6.titleGroup, children: [
1489
+ /* @__PURE__ */ jsx6("span", { style: styles6.flowId, children: flowId }),
1490
+ isActive && /* @__PURE__ */ jsx6("span", { style: { ...styles6.statusBadge, ...styles6.activeBadge }, children: "Active" })
1491
+ ] }),
1492
+ /* @__PURE__ */ jsx6("span", { style: statusBadgeStyle, children: state?.status ?? "no state" })
1493
+ ] }),
1494
+ state ? /* @__PURE__ */ jsxs6("div", { style: styles6.infoRow, children: [
1495
+ stepInfo && /* @__PURE__ */ jsxs6("div", { style: styles6.infoItem, children: [
1496
+ /* @__PURE__ */ jsx6("span", { style: styles6.label, children: "Step:" }),
1497
+ /* @__PURE__ */ jsx6("span", { style: styles6.value, children: stepInfo })
1498
+ ] }),
1499
+ /* @__PURE__ */ jsxs6("div", { style: styles6.infoItem, children: [
1500
+ /* @__PURE__ */ jsx6("span", { style: styles6.label, children: "Version:" }),
1501
+ /* @__PURE__ */ jsx6("span", { style: styles6.value, children: state.version })
1502
+ ] }),
1503
+ state.stepId && /* @__PURE__ */ jsxs6("div", { style: styles6.infoItem, children: [
1504
+ /* @__PURE__ */ jsx6("span", { style: styles6.label, children: "ID:" }),
1505
+ /* @__PURE__ */ jsx6("span", { style: styles6.value, children: state.stepId })
1506
+ ] })
1507
+ ] }) : /* @__PURE__ */ jsx6("div", { style: styles6.noState, children: "No stored state" }),
1508
+ /* @__PURE__ */ jsxs6("div", { style: styles6.actions, children: [
1509
+ /* @__PURE__ */ jsxs6(
1510
+ motion3.button,
1511
+ {
1512
+ type: "button",
1513
+ style: styles6.actionButton,
1514
+ onClick: onEdit,
1515
+ disabled: !state,
1516
+ whileHover: state ? { scale: 1.02 } : {},
1517
+ whileTap: state ? { scale: 0.98 } : {},
1518
+ title: state ? "Edit flow state" : "No state to edit",
1519
+ children: [
1520
+ /* @__PURE__ */ jsx6("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx6("path", { d: "M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5L13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175l-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z" }) }),
1521
+ "Edit"
1522
+ ]
1523
+ }
1524
+ ),
1525
+ /* @__PURE__ */ jsxs6(
1526
+ motion3.button,
1527
+ {
1528
+ type: "button",
1529
+ style: deleteButtonStyle,
1530
+ onClick: handleDelete,
1531
+ disabled: !state,
1532
+ whileHover: state ? { scale: 1.02 } : {},
1533
+ whileTap: state ? { scale: 0.98 } : {},
1534
+ title: confirmDelete ? "Click again to confirm deletion" : state ? "Delete flow state" : "No state to delete",
1535
+ children: [
1536
+ /* @__PURE__ */ jsxs6("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "currentColor", children: [
1537
+ /* @__PURE__ */ jsx6("path", { d: "M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z" }),
1538
+ /* @__PURE__ */ jsx6(
1539
+ "path",
1540
+ {
1541
+ fillRule: "evenodd",
1542
+ d: "M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4L4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"
1543
+ }
1544
+ )
1545
+ ] }),
1546
+ confirmDelete ? "Confirm?" : "Delete"
1547
+ ]
1548
+ }
1549
+ )
1550
+ ] })
1551
+ ] });
1552
+ }
1553
+
1554
+ // src/devtools/components/FlowEditModal.tsx
1555
+ import { useCallback as useCallback3, useEffect as useEffect2, useState as useState5 } from "react";
1556
+ import { createPortal as createPortal2 } from "react-dom";
1557
+ import { AnimatePresence as AnimatePresence2, motion as motion4 } from "motion/react";
1558
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1559
+ var styles7 = {
1560
+ overlay: {
1561
+ position: "fixed",
1562
+ inset: 0,
1563
+ backgroundColor: "rgba(0, 0, 0, 0.6)",
1564
+ display: "flex",
1565
+ alignItems: "center",
1566
+ justifyContent: "center",
1567
+ zIndex: 1e5,
1568
+ padding: 20
1569
+ },
1570
+ modal: {
1571
+ width: "100%",
1572
+ maxWidth: 500,
1573
+ maxHeight: "calc(100vh - 40px)",
1574
+ backgroundColor: "hsl(222 47% 11%)",
1575
+ borderRadius: 12,
1576
+ border: "1px solid hsl(215 20% 25%)",
1577
+ boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5)",
1578
+ display: "flex",
1579
+ flexDirection: "column",
1580
+ overflow: "hidden",
1581
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"
1582
+ },
1583
+ header: {
1584
+ display: "flex",
1585
+ alignItems: "center",
1586
+ justifyContent: "space-between",
1587
+ padding: "14px 16px",
1588
+ borderBottom: "1px solid hsl(215 20% 20%)"
1589
+ },
1590
+ title: {
1591
+ fontSize: 14,
1592
+ fontWeight: 600,
1593
+ color: "hsl(215 20% 85%)"
1594
+ },
1595
+ closeButton: {
1596
+ display: "flex",
1597
+ alignItems: "center",
1598
+ justifyContent: "center",
1599
+ width: 28,
1600
+ height: 28,
1601
+ backgroundColor: "transparent",
1602
+ border: "none",
1603
+ color: "hsl(215 20% 55%)",
1604
+ cursor: "pointer",
1605
+ borderRadius: 6,
1606
+ transition: "all 0.15s ease",
1607
+ outline: "none"
1608
+ },
1609
+ body: {
1610
+ flex: 1,
1611
+ padding: 16,
1612
+ overflowY: "auto"
1613
+ },
1614
+ textarea: {
1615
+ width: "100%",
1616
+ minHeight: 300,
1617
+ padding: 12,
1618
+ backgroundColor: "hsl(215 20% 8%)",
1619
+ border: "1px solid hsl(215 20% 20%)",
1620
+ borderRadius: 8,
1621
+ color: "hsl(215 20% 85%)",
1622
+ fontSize: 12,
1623
+ fontFamily: "ui-monospace, monospace",
1624
+ resize: "vertical",
1625
+ outline: "none",
1626
+ lineHeight: 1.5
1627
+ },
1628
+ textareaError: {
1629
+ borderColor: "hsl(0 70% 50%)"
1630
+ },
1631
+ error: {
1632
+ marginTop: 8,
1633
+ padding: "8px 10px",
1634
+ backgroundColor: "hsl(0 70% 50% / 0.15)",
1635
+ border: "1px solid hsl(0 70% 50% / 0.3)",
1636
+ borderRadius: 6,
1637
+ color: "hsl(0 70% 70%)",
1638
+ fontSize: 11,
1639
+ fontFamily: "ui-monospace, monospace"
1640
+ },
1641
+ footer: {
1642
+ display: "flex",
1643
+ alignItems: "center",
1644
+ justifyContent: "flex-end",
1645
+ gap: 8,
1646
+ padding: "12px 16px",
1647
+ borderTop: "1px solid hsl(215 20% 20%)"
1648
+ },
1649
+ button: {
1650
+ display: "flex",
1651
+ alignItems: "center",
1652
+ justifyContent: "center",
1653
+ gap: 6,
1654
+ padding: "8px 16px",
1655
+ backgroundColor: "hsl(215 20% 20%)",
1656
+ border: "1px solid hsl(215 20% 28%)",
1657
+ borderRadius: 6,
1658
+ color: "hsl(215 20% 75%)",
1659
+ fontSize: 12,
1660
+ fontWeight: 500,
1661
+ fontFamily: "inherit",
1662
+ cursor: "pointer",
1663
+ transition: "all 0.15s ease",
1664
+ outline: "none"
1665
+ },
1666
+ buttonPrimary: {
1667
+ backgroundColor: "hsl(217 91% 55%)",
1668
+ borderColor: "hsl(217 91% 60%)",
1669
+ color: "white"
1670
+ },
1671
+ buttonDisabled: {
1672
+ opacity: 0.5,
1673
+ cursor: "not-allowed"
1674
+ }
1675
+ };
1676
+ function FlowEditModal(props) {
1677
+ const { isOpen, flowId, initialState, onClose, onSave, container } = props;
1678
+ const [jsonValue, setJsonValue] = useState5("");
1679
+ const [error, setError] = useState5(null);
1680
+ useEffect2(() => {
1681
+ if (isOpen && initialState) {
1682
+ setJsonValue(JSON.stringify(initialState, null, 2));
1683
+ setError(null);
1684
+ }
1685
+ }, [isOpen, initialState]);
1686
+ const handleSave = useCallback3(() => {
1687
+ try {
1688
+ const parsed = JSON.parse(jsonValue);
1689
+ if (typeof parsed.status !== "string") {
1690
+ throw new Error('Missing or invalid "status" field');
1691
+ }
1692
+ if (typeof parsed.stepIndex !== "number") {
1693
+ throw new Error('Missing or invalid "stepIndex" field');
1694
+ }
1695
+ if (typeof parsed.version !== "string") {
1696
+ throw new Error('Missing or invalid "version" field');
1697
+ }
1698
+ onSave(parsed);
1699
+ onClose();
1700
+ } catch (err) {
1701
+ setError(err instanceof Error ? err.message : "Invalid JSON");
1702
+ }
1703
+ }, [jsonValue, onSave, onClose]);
1704
+ const handleKeyDown = useCallback3(
1705
+ (e) => {
1706
+ if (e.key === "Escape") {
1707
+ onClose();
1708
+ }
1709
+ if (e.key === "s" && (e.metaKey || e.ctrlKey)) {
1710
+ e.preventDefault();
1711
+ handleSave();
1712
+ }
1713
+ },
1714
+ [onClose, handleSave]
1715
+ );
1716
+ if (typeof window === "undefined") return null;
1717
+ const portalContainer = container ?? document.body;
1718
+ const textareaStyle = {
1719
+ ...styles7.textarea,
1720
+ ...error && styles7.textareaError
1721
+ };
1722
+ const saveButtonStyle = {
1723
+ ...styles7.button,
1724
+ ...styles7.buttonPrimary,
1725
+ ...error && styles7.buttonDisabled
1726
+ };
1727
+ return createPortal2(
1728
+ /* @__PURE__ */ jsx7(AnimatePresence2, { children: isOpen && /* @__PURE__ */ jsx7(
1729
+ motion4.div,
1730
+ {
1731
+ style: styles7.overlay,
1732
+ initial: { opacity: 0 },
1733
+ animate: { opacity: 1 },
1734
+ exit: { opacity: 0 },
1735
+ transition: { duration: 0.15 },
1736
+ onClick: (e) => {
1737
+ if (e.target === e.currentTarget) onClose();
1738
+ },
1739
+ onKeyDown: handleKeyDown,
1740
+ children: /* @__PURE__ */ jsxs7(
1741
+ motion4.div,
1742
+ {
1743
+ style: styles7.modal,
1744
+ initial: { opacity: 0, scale: 0.95, y: 10 },
1745
+ animate: { opacity: 1, scale: 1, y: 0 },
1746
+ exit: { opacity: 0, scale: 0.95, y: 10 },
1747
+ transition: { duration: 0.15, ease: [0.16, 1, 0.3, 1] },
1748
+ onClick: (e) => e.stopPropagation(),
1749
+ children: [
1750
+ /* @__PURE__ */ jsxs7("div", { style: styles7.header, children: [
1751
+ /* @__PURE__ */ jsxs7("span", { style: styles7.title, children: [
1752
+ "Edit Flow: ",
1753
+ flowId
1754
+ ] }),
1755
+ /* @__PURE__ */ jsx7(
1756
+ "button",
1757
+ {
1758
+ type: "button",
1759
+ style: styles7.closeButton,
1760
+ onClick: onClose,
1761
+ title: "Close",
1762
+ children: /* @__PURE__ */ jsx7("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx7("path", { d: "M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z" }) })
1763
+ }
1764
+ )
1765
+ ] }),
1766
+ /* @__PURE__ */ jsxs7("div", { style: styles7.body, children: [
1767
+ /* @__PURE__ */ jsx7(
1768
+ "textarea",
1769
+ {
1770
+ style: textareaStyle,
1771
+ value: jsonValue,
1772
+ onChange: (e) => {
1773
+ setJsonValue(e.target.value);
1774
+ setError(null);
1775
+ },
1776
+ spellCheck: false,
1777
+ autoFocus: true
1778
+ }
1779
+ ),
1780
+ error && /* @__PURE__ */ jsx7("div", { style: styles7.error, children: error })
1781
+ ] }),
1782
+ /* @__PURE__ */ jsxs7("div", { style: styles7.footer, children: [
1783
+ /* @__PURE__ */ jsx7(
1784
+ "button",
1785
+ {
1786
+ type: "button",
1787
+ style: styles7.button,
1788
+ onClick: onClose,
1789
+ children: "Cancel"
1790
+ }
1791
+ ),
1792
+ /* @__PURE__ */ jsx7(
1793
+ motion4.button,
1794
+ {
1795
+ type: "button",
1796
+ style: saveButtonStyle,
1797
+ onClick: handleSave,
1798
+ disabled: !!error,
1799
+ whileHover: !error ? { scale: 1.02 } : {},
1800
+ whileTap: !error ? { scale: 0.98 } : {},
1801
+ children: "Save"
1802
+ }
1803
+ )
1804
+ ] })
1805
+ ]
1806
+ }
1807
+ )
1808
+ }
1809
+ ) }),
1810
+ portalContainer
1811
+ );
1812
+ }
1813
+
1814
+ // src/devtools/components/FlowsTab.tsx
1815
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1816
+ var styles8 = {
1817
+ container: {
1818
+ display: "flex",
1819
+ flexDirection: "column",
1820
+ gap: 10,
1821
+ padding: 10,
1822
+ flex: 1,
1823
+ overflowY: "auto"
1824
+ },
1825
+ toolbar: {
1826
+ display: "flex",
1827
+ alignItems: "center",
1828
+ justifyContent: "space-between",
1829
+ gap: 8,
1830
+ marginBottom: 4
1831
+ },
1832
+ refreshButton: {
1833
+ display: "flex",
1834
+ alignItems: "center",
1835
+ justifyContent: "center",
1836
+ gap: 6,
1837
+ padding: "6px 10px",
1838
+ backgroundColor: "transparent",
1839
+ border: "1px solid hsl(215 20% 25%)",
1840
+ borderRadius: 5,
1841
+ color: "hsl(215 20% 60%)",
1842
+ fontSize: 11,
1843
+ fontWeight: 500,
1844
+ fontFamily: "inherit",
1845
+ cursor: "pointer",
1846
+ transition: "all 0.15s ease",
1847
+ outline: "none"
1848
+ },
1849
+ flowList: {
1850
+ display: "flex",
1851
+ flexDirection: "column",
1852
+ gap: 8
1853
+ },
1854
+ empty: {
1855
+ display: "flex",
1856
+ flexDirection: "column",
1857
+ alignItems: "center",
1858
+ justifyContent: "center",
1859
+ gap: 8,
1860
+ padding: "32px 16px",
1861
+ textAlign: "center",
1862
+ color: "hsl(215 20% 55%)"
1863
+ },
1864
+ emptyIcon: {
1865
+ width: 40,
1866
+ height: 40,
1867
+ borderRadius: 10,
1868
+ backgroundColor: "hsl(215 20% 18%)",
1869
+ display: "flex",
1870
+ alignItems: "center",
1871
+ justifyContent: "center",
1872
+ marginBottom: 4
1873
+ },
1874
+ emptyText: {
1875
+ fontSize: 13,
1876
+ lineHeight: 1.5
1877
+ },
1878
+ noContext: {
1879
+ padding: "20px 16px",
1880
+ textAlign: "center",
1881
+ color: "hsl(215 20% 50%)",
1882
+ fontSize: 12
1883
+ }
1884
+ };
1885
+ function FlowsTab(props) {
1886
+ const { container } = props;
1887
+ const { flows, refreshFlows, deleteFlow, updateFlow } = useFlowsData();
1888
+ const [editModal, setEditModal] = useState6({
1889
+ isOpen: false,
1890
+ flowId: "",
1891
+ state: null
1892
+ });
1893
+ const handleEdit = useCallback4((flowId, state) => {
1894
+ if (!state) return;
1895
+ setEditModal({
1896
+ isOpen: true,
1897
+ flowId,
1898
+ state
1899
+ });
1900
+ }, []);
1901
+ const handleCloseModal = useCallback4(() => {
1902
+ setEditModal((prev) => ({ ...prev, isOpen: false }));
1903
+ }, []);
1904
+ const handleSave = useCallback4(
1905
+ async (newState) => {
1906
+ await updateFlow(editModal.flowId, newState);
1907
+ },
1908
+ [editModal.flowId, updateFlow]
1909
+ );
1910
+ const handleDelete = useCallback4(
1911
+ async (flowId) => {
1912
+ await deleteFlow(flowId);
1913
+ },
1914
+ [deleteFlow]
1915
+ );
1916
+ if (flows.length === 0) {
1917
+ return /* @__PURE__ */ jsx8("div", { style: styles8.container, children: /* @__PURE__ */ jsxs8("div", { style: styles8.empty, children: [
1918
+ /* @__PURE__ */ jsx8("div", { style: styles8.emptyIcon, children: /* @__PURE__ */ jsx8(
1919
+ "svg",
1920
+ {
1921
+ width: "20",
1922
+ height: "20",
1923
+ viewBox: "0 0 16 16",
1924
+ fill: "hsl(215 20% 45%)",
1925
+ children: /* @__PURE__ */ jsx8("path", { d: "M6 3.5A1.5 1.5 0 0 1 7.5 2h1A1.5 1.5 0 0 1 10 3.5v1A1.5 1.5 0 0 1 8.5 6v1H14a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0V8h-5v.5a.5.5 0 0 1-1 0V8h-5v.5a.5.5 0 0 1-1 0v-1A.5.5 0 0 1 2 7h5.5V6A1.5 1.5 0 0 1 6 4.5v-1zm-3 8A1.5 1.5 0 0 1 4.5 10h1A1.5 1.5 0 0 1 7 11.5v1A1.5 1.5 0 0 1 5.5 14h-1A1.5 1.5 0 0 1 3 12.5v-1zm6 0a1.5 1.5 0 0 1 1.5-1.5h1a1.5 1.5 0 0 1 1.5 1.5v1a1.5 1.5 0 0 1-1.5 1.5h-1A1.5 1.5 0 0 1 9 12.5v-1z" })
1926
+ }
1927
+ ) }),
1928
+ /* @__PURE__ */ jsxs8("div", { style: styles8.emptyText, children: [
1929
+ /* @__PURE__ */ jsx8("div", { children: "No flows registered." }),
1930
+ /* @__PURE__ */ jsx8("div", { style: { marginTop: 6, fontSize: 11, color: "hsl(215 20% 45%)" }, children: "Add flows to TourProvider to see them here." })
1931
+ ] })
1932
+ ] }) });
1933
+ }
1934
+ return /* @__PURE__ */ jsxs8("div", { style: styles8.container, children: [
1935
+ /* @__PURE__ */ jsxs8("div", { style: styles8.toolbar, children: [
1936
+ /* @__PURE__ */ jsxs8("span", { style: { fontSize: 11, color: "hsl(215 20% 50%)" }, children: [
1937
+ flows.length,
1938
+ " flow",
1939
+ flows.length !== 1 ? "s" : "",
1940
+ " registered"
1941
+ ] }),
1942
+ /* @__PURE__ */ jsxs8(
1943
+ motion5.button,
1944
+ {
1945
+ type: "button",
1946
+ style: styles8.refreshButton,
1947
+ onClick: () => void refreshFlows(),
1948
+ whileHover: { scale: 1.02 },
1949
+ whileTap: { scale: 0.98 },
1950
+ title: "Refresh flow states",
1951
+ children: [
1952
+ /* @__PURE__ */ jsxs8("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "currentColor", children: [
1953
+ /* @__PURE__ */ jsx8(
1954
+ "path",
1955
+ {
1956
+ fillRule: "evenodd",
1957
+ d: "M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z"
1958
+ }
1959
+ ),
1960
+ /* @__PURE__ */ jsx8("path", { d: "M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z" })
1961
+ ] }),
1962
+ "Refresh"
1963
+ ]
1964
+ }
1965
+ )
1966
+ ] }),
1967
+ /* @__PURE__ */ jsx8("div", { style: styles8.flowList, children: flows.map((flow) => /* @__PURE__ */ jsx8(
1968
+ FlowItem,
1969
+ {
1970
+ flow,
1971
+ onEdit: () => handleEdit(flow.flowId, flow.state),
1972
+ onDelete: () => void handleDelete(flow.flowId)
1973
+ },
1974
+ flow.flowId
1975
+ )) }),
1976
+ editModal.state && /* @__PURE__ */ jsx8(
1977
+ FlowEditModal,
1978
+ {
1979
+ isOpen: editModal.isOpen,
1980
+ flowId: editModal.flowId,
1981
+ initialState: editModal.state,
1982
+ onClose: handleCloseModal,
1983
+ onSave: handleSave,
1984
+ container
1985
+ }
1986
+ )
1987
+ ] });
1988
+ }
1989
+
1990
+ // src/devtools/hooks/useGrabMode.ts
1991
+ import { useCallback as useCallback6, useEffect as useEffect3, useState as useState7 } from "react";
1992
+
1993
+ // src/devtools/hooks/useElementInfo.ts
1994
+ import { useCallback as useCallback5 } from "react";
1995
+
1996
+ // src/devtools/utils/selectorGenerator.ts
1997
+ function generateSemanticName(element) {
1998
+ const tag = element.tagName.toLowerCase();
1999
+ const text = element.textContent?.trim().slice(0, 20).toLowerCase() || "";
2000
+ const className = element.className;
2001
+ const id = element.id;
2002
+ if (text) {
2003
+ const words = text.replace(/[^a-z0-9\s]/gi, "").split(/\s+/).filter(Boolean).slice(0, 3);
2004
+ if (words.length > 0) {
2005
+ return `${tag}-${words.join("-")}`;
2006
+ }
2007
+ }
2008
+ if (id) {
2009
+ return id;
2010
+ }
2011
+ if (typeof className === "string" && className) {
2012
+ const firstClass = className.split(/\s+/)[0];
2013
+ if (firstClass && !firstClass.startsWith("_") && firstClass.length < 30) {
2014
+ return `${tag}-${firstClass}`;
2015
+ }
2016
+ }
2017
+ return `${tag}-${Math.random().toString(36).slice(2, 6)}`;
2018
+ }
2019
+ function getExistingDataAttrs(element) {
2020
+ const attrs = [];
2021
+ for (const attr of element.attributes) {
2022
+ if (attr.name.startsWith("data-")) {
2023
+ attrs.push(attr.name);
2024
+ }
2025
+ }
2026
+ return attrs;
2027
+ }
2028
+ function isUnique(params) {
2029
+ try {
2030
+ return document.querySelectorAll(params.selector).length === 1;
2031
+ } catch {
2032
+ return false;
2033
+ }
2034
+ }
2035
+ function generateSelector(params) {
2036
+ const { element } = params;
2037
+ const existingAttrs = getExistingDataAttrs(element);
2038
+ const tourTarget = element.getAttribute("data-tour-target");
2039
+ if (tourTarget) {
2040
+ return {
2041
+ selector: `[data-tour-target="${tourTarget}"]`,
2042
+ selectorType: "data-attr",
2043
+ suggestedAttrName: tourTarget,
2044
+ existingAttrs
2045
+ };
2046
+ }
2047
+ if (element.id && isUnique({ selector: `#${CSS.escape(element.id)}` })) {
2048
+ return {
2049
+ selector: `#${CSS.escape(element.id)}`,
2050
+ selectorType: "auto",
2051
+ suggestedAttrName: element.id,
2052
+ existingAttrs
2053
+ };
2054
+ }
2055
+ const testId = element.getAttribute("data-testid");
2056
+ if (testId && isUnique({ selector: `[data-testid="${testId}"]` })) {
2057
+ return {
2058
+ selector: `[data-testid="${testId}"]`,
2059
+ selectorType: "data-attr",
2060
+ suggestedAttrName: testId,
2061
+ existingAttrs
2062
+ };
2063
+ }
2064
+ for (const attr of existingAttrs) {
2065
+ if (attr === "data-tour-target" || attr === "data-testid") continue;
2066
+ const value = element.getAttribute(attr);
2067
+ if (value) {
2068
+ const selector = `[${attr}="${value}"]`;
2069
+ if (isUnique({ selector })) {
2070
+ return {
2071
+ selector,
2072
+ selectorType: "data-attr",
2073
+ suggestedAttrName: value,
2074
+ existingAttrs
2075
+ };
2076
+ }
2077
+ }
2078
+ }
2079
+ const suggestedName = generateSemanticName(element);
2080
+ return {
2081
+ selector: `[data-tour-target="${suggestedName}"]`,
2082
+ selectorType: "auto",
2083
+ suggestedAttrName: suggestedName,
2084
+ existingAttrs
2085
+ };
2086
+ }
2087
+
2088
+ // src/devtools/hooks/useElementInfo.ts
2089
+ function useElementInfo() {
2090
+ const getElementInfo = useCallback5((params) => {
2091
+ const { element } = params;
2092
+ const selectorResult = generateSelector({ element });
2093
+ const source = extractSource({ element });
2094
+ const componentHierarchy = extractComponentHierarchy({ element });
2095
+ const rect = element.getBoundingClientRect();
2096
+ const htmlElement = element;
2097
+ const styleAttr = htmlElement.getAttribute?.("style") || void 0;
2098
+ return {
2099
+ element,
2100
+ selector: selectorResult.selector,
2101
+ selectorType: selectorResult.selectorType,
2102
+ suggestedAttrName: selectorResult.suggestedAttrName,
2103
+ tag: element.tagName.toLowerCase(),
2104
+ text: element.textContent?.trim().slice(0, 50) || void 0,
2105
+ className: typeof element.className === "string" ? element.className : void 0,
2106
+ style: styleAttr,
2107
+ existingAttrs: selectorResult.existingAttrs,
2108
+ componentHierarchy,
2109
+ rect,
2110
+ source: source ?? void 0
2111
+ };
2112
+ }, []);
2113
+ return { getElementInfo };
2114
+ }
2115
+
2116
+ // src/devtools/hooks/useGrabMode.ts
2117
+ function isDevToolsElement(element) {
2118
+ if (element.closest("[data-devtools-panel]")) return true;
2119
+ if (element.closest("[data-devtools-host]")) return true;
2120
+ const root = element.getRootNode();
2121
+ if (root instanceof ShadowRoot) {
2122
+ const host = root.host;
2123
+ if (host?.hasAttribute("data-devtools-host")) return true;
2124
+ }
2125
+ return false;
2126
+ }
2127
+ function useGrabMode() {
2128
+ const [mode, setMode] = useState7("idle");
2129
+ const [hoveredElement, setHoveredElement] = useState7(null);
2130
+ const { getElementInfo } = useElementInfo();
2131
+ const startGrabbing = useCallback6(() => {
2132
+ setMode("grabbing");
2133
+ setHoveredElement(null);
2134
+ }, []);
2135
+ const stopGrabbing = useCallback6(() => {
2136
+ setMode("idle");
2137
+ setHoveredElement(null);
2138
+ }, []);
2139
+ const toggleGrabbing = useCallback6(() => {
2140
+ setMode((prev) => prev === "grabbing" ? "idle" : "grabbing");
2141
+ setHoveredElement(null);
2142
+ }, []);
2143
+ const selectCurrent = useCallback6(() => {
2144
+ if (!hoveredElement) return null;
2145
+ return hoveredElement.info;
2146
+ }, [hoveredElement]);
2147
+ useEffect3(() => {
2148
+ if (mode !== "grabbing") return;
2149
+ const handleMouseMove = (e) => {
2150
+ const target = document.elementFromPoint(e.clientX, e.clientY);
2151
+ if (!target) {
2152
+ setHoveredElement(null);
2153
+ return;
2154
+ }
2155
+ if (isDevToolsElement(target)) {
2156
+ setHoveredElement(null);
2157
+ return;
2158
+ }
2159
+ if (target.tagName === "HTML" || target.tagName === "BODY") {
2160
+ setHoveredElement(null);
2161
+ return;
2162
+ }
2163
+ setHoveredElement({
2164
+ element: target,
2165
+ info: getElementInfo({ element: target })
2166
+ });
2167
+ };
2168
+ document.addEventListener("mousemove", handleMouseMove, { passive: true });
2169
+ return () => document.removeEventListener("mousemove", handleMouseMove);
2170
+ }, [mode, getElementInfo]);
2171
+ useEffect3(() => {
2172
+ const handleKeyDown = (e) => {
2173
+ if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === "g") {
2174
+ e.preventDefault();
2175
+ toggleGrabbing();
2176
+ return;
2177
+ }
2178
+ if (mode !== "grabbing") return;
2179
+ if (e.key === "Escape") {
2180
+ e.preventDefault();
2181
+ stopGrabbing();
2182
+ }
2183
+ };
2184
+ document.addEventListener("keydown", handleKeyDown);
2185
+ return () => document.removeEventListener("keydown", handleKeyDown);
2186
+ }, [mode, toggleGrabbing, stopGrabbing]);
2187
+ return {
2188
+ mode,
2189
+ hoveredElement,
2190
+ startGrabbing,
2191
+ stopGrabbing,
2192
+ toggleGrabbing,
2193
+ selectCurrent
2194
+ };
2195
+ }
2196
+
2197
+ // src/devtools/hooks/useStepStore.ts
2198
+ import { useCallback as useCallback7, useEffect as useEffect4, useSyncExternalStore as useSyncExternalStore2 } from "react";
2199
+
2200
+ // src/devtools/utils/storage.ts
2201
+ var STORAGE_KEY = "flowsterix-devtools-steps";
2202
+ function migrateStep(step) {
2203
+ return {
2204
+ ...step,
2205
+ componentHierarchy: step.componentHierarchy ?? [],
2206
+ existingAttrs: step.existingAttrs ?? []
2207
+ };
2208
+ }
2209
+ function loadSteps() {
2210
+ if (typeof window === "undefined") return [];
2211
+ try {
2212
+ const data = localStorage.getItem(STORAGE_KEY);
2213
+ if (!data) return [];
2214
+ const parsed = JSON.parse(data);
2215
+ if (!Array.isArray(parsed)) return [];
2216
+ return parsed.map(migrateStep);
2217
+ } catch {
2218
+ return [];
2219
+ }
2220
+ }
2221
+ function saveSteps(steps) {
2222
+ if (typeof window === "undefined") return;
2223
+ try {
2224
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(steps));
2225
+ } catch {
2226
+ }
2227
+ }
2228
+ function clearSteps() {
2229
+ if (typeof window === "undefined") return;
2230
+ try {
2231
+ localStorage.removeItem(STORAGE_KEY);
2232
+ } catch {
2233
+ }
2234
+ }
2235
+
2236
+ // src/devtools/hooks/useStepStore.ts
2237
+ var store = {
2238
+ steps: [],
2239
+ listeners: /* @__PURE__ */ new Set()
2240
+ };
2241
+ function notifyListeners() {
2242
+ for (const listener of store.listeners) {
2243
+ listener();
2244
+ }
2245
+ }
2246
+ function subscribe(listener) {
2247
+ store.listeners.add(listener);
2248
+ return () => store.listeners.delete(listener);
2249
+ }
2250
+ function getSnapshot() {
2251
+ return store.steps;
2252
+ }
2253
+ function getServerSnapshot() {
2254
+ return [];
2255
+ }
2256
+ function generateId() {
2257
+ return `step-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
2258
+ }
2259
+ function useStepStore() {
2260
+ const steps = useSyncExternalStore2(subscribe, getSnapshot, getServerSnapshot);
2261
+ useEffect4(() => {
2262
+ const saved = loadSteps();
2263
+ if (saved.length > 0) {
2264
+ store.steps = saved;
2265
+ notifyListeners();
2266
+ }
2267
+ }, []);
2268
+ useEffect4(() => {
2269
+ saveSteps(steps);
2270
+ }, [steps]);
2271
+ const addStep = useCallback7((params) => {
2272
+ const { info } = params;
2273
+ const newStep = {
2274
+ id: generateId(),
2275
+ order: store.steps.length,
2276
+ selector: info.selector,
2277
+ selectorType: info.selectorType,
2278
+ elementTag: info.tag,
2279
+ elementText: info.text,
2280
+ elementClassName: info.className,
2281
+ elementStyle: info.style,
2282
+ existingAttrs: info.existingAttrs,
2283
+ suggestedAttrName: info.suggestedAttrName,
2284
+ componentHierarchy: info.componentHierarchy,
2285
+ rect: {
2286
+ top: info.rect.top,
2287
+ left: info.rect.left,
2288
+ width: info.rect.width,
2289
+ height: info.rect.height
2290
+ },
2291
+ source: info.source,
2292
+ createdAt: Date.now()
2293
+ };
2294
+ store.steps = [...store.steps, newStep];
2295
+ notifyListeners();
2296
+ return newStep;
2297
+ }, []);
2298
+ const removeStep = useCallback7((params) => {
2299
+ const { id } = params;
2300
+ store.steps = store.steps.filter((s) => s.id !== id).map((s, i) => ({ ...s, order: i }));
2301
+ notifyListeners();
2302
+ }, []);
2303
+ const updateStep = useCallback7(
2304
+ (params) => {
2305
+ const { id, updates } = params;
2306
+ store.steps = store.steps.map(
2307
+ (s) => s.id === id ? { ...s, ...updates } : s
2308
+ );
2309
+ notifyListeners();
2310
+ },
2311
+ []
2312
+ );
2313
+ const reorderSteps = useCallback7(
2314
+ (params) => {
2315
+ const { fromIndex, toIndex } = params;
2316
+ const newSteps = [...store.steps];
2317
+ const [removed] = newSteps.splice(fromIndex, 1);
2318
+ newSteps.splice(toIndex, 0, removed);
2319
+ store.steps = newSteps.map((s, i) => ({ ...s, order: i }));
2320
+ notifyListeners();
2321
+ },
2322
+ []
2323
+ );
2324
+ const clearAllSteps = useCallback7(() => {
2325
+ store.steps = [];
2326
+ clearSteps();
2327
+ notifyListeners();
2328
+ }, []);
2329
+ const exportSteps = useCallback7(() => {
2330
+ return {
2331
+ version: "1.0",
2332
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2333
+ steps: store.steps.map((step) => {
2334
+ let elementStr = `<${step.elementTag}`;
2335
+ if (step.elementClassName) {
2336
+ elementStr += ` class="${step.elementClassName.slice(0, 60)}${step.elementClassName.length > 60 ? "..." : ""}"`;
2337
+ }
2338
+ if (step.elementStyle) {
2339
+ elementStr += ` style="${step.elementStyle.slice(0, 40)}${step.elementStyle.length > 40 ? "..." : ""}"`;
2340
+ }
2341
+ for (const attr of step.existingAttrs) {
2342
+ if (attr !== "class" && attr !== "style") {
2343
+ elementStr += ` ${attr}`;
2344
+ }
2345
+ }
2346
+ elementStr += ">";
2347
+ if (step.elementText) {
2348
+ elementStr += `${step.elementText.slice(0, 30)}${step.elementText.length > 30 ? "..." : ""}`;
2349
+ }
2350
+ elementStr += `</${step.elementTag}>`;
2351
+ const sourceStr = step.source ? `${step.source.fileName.replace(/^.*\/src\//, "src/")}:${step.source.lineNumber}` : void 0;
2352
+ return {
2353
+ order: step.order,
2354
+ element: elementStr,
2355
+ componentTree: step.componentHierarchy,
2356
+ source: sourceStr
2357
+ };
2358
+ })
2359
+ };
2360
+ }, []);
2361
+ return {
2362
+ steps,
2363
+ addStep,
2364
+ removeStep,
2365
+ updateStep,
2366
+ reorderSteps,
2367
+ clearAllSteps,
2368
+ exportSteps
2369
+ };
2370
+ }
2371
+
2372
+ // src/devtools/DevToolsProvider.tsx
2373
+ import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
2374
+ var styles9 = {
2375
+ panel: {
2376
+ position: "fixed",
2377
+ width: 320,
2378
+ maxWidth: "calc(100vw - 32px)",
2379
+ maxHeight: "calc(100vh - 32px)",
2380
+ display: "flex",
2381
+ flexDirection: "column",
2382
+ zIndex: 99998,
2383
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
2384
+ fontSize: 13,
2385
+ backgroundColor: "hsl(222 47% 11%)",
2386
+ borderRadius: 12,
2387
+ border: "1px solid hsl(215 20% 25%)",
2388
+ boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.05)",
2389
+ overflow: "hidden"
2390
+ },
2391
+ header: {
2392
+ display: "flex",
2393
+ alignItems: "center",
2394
+ justifyContent: "space-between",
2395
+ padding: "10px 12px",
2396
+ borderBottom: "1px solid hsl(215 20% 20%)",
2397
+ backgroundColor: "hsl(222 47% 9%)",
2398
+ cursor: "grab",
2399
+ userSelect: "none",
2400
+ touchAction: "none"
2401
+ },
2402
+ headerDragging: {
2403
+ cursor: "grabbing"
2404
+ },
2405
+ titleGroup: {
2406
+ display: "flex",
2407
+ alignItems: "center",
2408
+ gap: 8
2409
+ },
2410
+ logo: {
2411
+ display: "flex",
2412
+ alignItems: "center",
2413
+ justifyContent: "center",
2414
+ width: 24,
2415
+ height: 24,
2416
+ borderRadius: 6,
2417
+ backgroundColor: "hsl(217 91% 55% / 0.2)"
2418
+ },
2419
+ title: {
2420
+ fontSize: 13,
2421
+ fontWeight: 600,
2422
+ color: "hsl(215 20% 75%)",
2423
+ letterSpacing: "-0.01em"
2424
+ },
2425
+ headerButtons: {
2426
+ display: "flex",
2427
+ alignItems: "center",
2428
+ gap: 2
2429
+ },
2430
+ iconButton: {
2431
+ display: "flex",
2432
+ alignItems: "center",
2433
+ justifyContent: "center",
2434
+ width: 26,
2435
+ height: 26,
2436
+ backgroundColor: "transparent",
2437
+ border: "none",
2438
+ color: "hsl(215 20% 55%)",
2439
+ cursor: "pointer",
2440
+ padding: 0,
2441
+ borderRadius: 6,
2442
+ transition: "all 0.15s ease",
2443
+ outline: "none"
2444
+ },
2445
+ body: {
2446
+ flex: 1,
2447
+ minHeight: 0,
2448
+ display: "flex",
2449
+ flexDirection: "column",
2450
+ overflow: "hidden"
2451
+ }
2452
+ };
2453
+ function DevToolsProvider(props) {
2454
+ const { children, enabled = true, defaultTab = "steps" } = props;
2455
+ const [mounted, setMounted] = useState8(false);
2456
+ const [shadowRoot, setShadowRoot] = useState8(null);
2457
+ const [activeTab, setActiveTab] = useState8(defaultTab);
2458
+ useEffect5(() => {
2459
+ setMounted(true);
2460
+ const host = document.createElement("div");
2461
+ host.setAttribute("data-devtools-host", "");
2462
+ host.style.cssText = "position: fixed; top: 0; left: 0; z-index: 99999; pointer-events: none;";
2463
+ document.body.appendChild(host);
2464
+ const shadow = host.attachShadow({ mode: "open" });
2465
+ const style = document.createElement("style");
2466
+ style.textContent = `
2467
+ :host { all: initial; }
2468
+ *, *::before, *::after { box-sizing: border-box; }
2469
+ `;
2470
+ shadow.appendChild(style);
2471
+ const container = document.createElement("div");
2472
+ container.style.cssText = "pointer-events: auto;";
2473
+ shadow.appendChild(container);
2474
+ setShadowRoot(shadow);
2475
+ return () => {
2476
+ document.body.removeChild(host);
2477
+ };
2478
+ }, []);
2479
+ const { mode, hoveredElement, toggleGrabbing, selectCurrent } = useGrabMode();
2480
+ const {
2481
+ steps,
2482
+ addStep,
2483
+ removeStep,
2484
+ reorderSteps,
2485
+ clearAllSteps,
2486
+ exportSteps
2487
+ } = useStepStore();
2488
+ const { flows } = useFlowsData();
2489
+ const [collapsed, setCollapsed] = useState8(false);
2490
+ const [position, setPosition] = useState8({ x: 16, y: 16 });
2491
+ const [isPanelDragging, setIsPanelDragging] = useState8(false);
2492
+ const dragStartRef = useRef(null);
2493
+ const handleClick = useCallback8(
2494
+ (e) => {
2495
+ if (mode !== "grabbing") return;
2496
+ const target = e.target;
2497
+ if (target.closest("[data-devtools-panel]")) return;
2498
+ if (target.closest("[data-devtools-host]")) return;
2499
+ const root = target.getRootNode();
2500
+ if (root instanceof ShadowRoot) {
2501
+ const host = root.host;
2502
+ if (host?.hasAttribute("data-devtools-host")) return;
2503
+ }
2504
+ e.preventDefault();
2505
+ e.stopPropagation();
2506
+ const info = selectCurrent();
2507
+ if (info) {
2508
+ addStep({ info });
2509
+ }
2510
+ },
2511
+ [mode, selectCurrent, addStep]
2512
+ );
2513
+ useEffect5(() => {
2514
+ if (mode !== "grabbing") return;
2515
+ document.addEventListener("click", handleClick, { capture: true });
2516
+ return () => document.removeEventListener("click", handleClick, { capture: true });
2517
+ }, [mode, handleClick]);
2518
+ useEffect5(() => {
2519
+ if (mode !== "grabbing") return;
2520
+ const preventDefault = (e) => {
2521
+ const target = e.target;
2522
+ if (target.closest("[data-devtools-panel]")) return;
2523
+ e.preventDefault();
2524
+ };
2525
+ document.addEventListener("submit", preventDefault, { capture: true });
2526
+ return () => {
2527
+ document.removeEventListener("submit", preventDefault, { capture: true });
2528
+ };
2529
+ }, [mode]);
2530
+ useEffect5(() => {
2531
+ const handleKeyDown = (e) => {
2532
+ if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === "m") {
2533
+ e.preventDefault();
2534
+ setCollapsed((c) => !c);
2535
+ }
2536
+ };
2537
+ document.addEventListener("keydown", handleKeyDown);
2538
+ return () => document.removeEventListener("keydown", handleKeyDown);
2539
+ }, []);
2540
+ const handlePointerDown = useCallback8(
2541
+ (e) => {
2542
+ const target = e.target;
2543
+ if (target.closest("button")) return;
2544
+ e.preventDefault();
2545
+ setIsPanelDragging(true);
2546
+ dragStartRef.current = {
2547
+ x: e.clientX,
2548
+ y: e.clientY,
2549
+ posX: position.x,
2550
+ posY: position.y
2551
+ };
2552
+ e.target.setPointerCapture(e.pointerId);
2553
+ },
2554
+ [position]
2555
+ );
2556
+ const handlePointerMove = useCallback8(
2557
+ (e) => {
2558
+ if (!dragStartRef.current || !isPanelDragging) return;
2559
+ const dx = e.clientX - dragStartRef.current.x;
2560
+ const dy = e.clientY - dragStartRef.current.y;
2561
+ const newX = Math.max(8, dragStartRef.current.posX - dx);
2562
+ const newY = Math.max(8, dragStartRef.current.posY + dy);
2563
+ setPosition({ x: newX, y: newY });
2564
+ },
2565
+ [isPanelDragging]
2566
+ );
2567
+ const handlePointerUp = useCallback8((e) => {
2568
+ setIsPanelDragging(false);
2569
+ dragStartRef.current = null;
2570
+ e.target.releasePointerCapture(e.pointerId);
2571
+ }, []);
2572
+ if (!enabled || !mounted) {
2573
+ return /* @__PURE__ */ jsx9(Fragment3, { children });
2574
+ }
2575
+ const shadowContainer = shadowRoot?.lastElementChild ?? null;
2576
+ const panelStyle = {
2577
+ ...styles9.panel,
2578
+ right: position.x,
2579
+ top: position.y
2580
+ };
2581
+ const headerStyle = {
2582
+ ...styles9.header,
2583
+ ...isPanelDragging && styles9.headerDragging
2584
+ };
2585
+ const portalContainer = shadowContainer ?? document.body;
2586
+ return /* @__PURE__ */ jsxs9(Fragment3, { children: [
2587
+ children,
2588
+ /* @__PURE__ */ jsx9(
2589
+ GrabberOverlay,
2590
+ {
2591
+ isGrabbing: mode === "grabbing",
2592
+ hoveredInfo: hoveredElement?.info ?? null,
2593
+ container: shadowContainer
2594
+ }
2595
+ ),
2596
+ createPortal3(
2597
+ /* @__PURE__ */ jsxs9(
2598
+ motion6.div,
2599
+ {
2600
+ style: panelStyle,
2601
+ initial: { opacity: 0, x: 20, scale: 0.95 },
2602
+ animate: { opacity: 1, x: 0, scale: 1 },
2603
+ transition: { duration: 0.2, ease: [0.16, 1, 0.3, 1] },
2604
+ "data-devtools-panel": "",
2605
+ children: [
2606
+ /* @__PURE__ */ jsxs9(
2607
+ "div",
2608
+ {
2609
+ style: headerStyle,
2610
+ onPointerDown: handlePointerDown,
2611
+ onPointerMove: handlePointerMove,
2612
+ onPointerUp: handlePointerUp,
2613
+ onPointerCancel: handlePointerUp,
2614
+ children: [
2615
+ /* @__PURE__ */ jsxs9("div", { style: styles9.titleGroup, children: [
2616
+ /* @__PURE__ */ jsx9("div", { style: styles9.logo, children: /* @__PURE__ */ jsxs9("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "hsl(217 91% 70%)", children: [
2617
+ /* @__PURE__ */ jsx9("path", { d: "M8 0L14.9 4v8L8 16 1.1 12V4L8 0zm0 2.3L3.1 5.2v5.6L8 13.7l4.9-2.9V5.2L8 2.3z" }),
2618
+ /* @__PURE__ */ jsx9("path", { d: "M8 5l3 1.7v3.4L8 11.8 5 10.1V6.7L8 5z" })
2619
+ ] }) }),
2620
+ /* @__PURE__ */ jsx9("span", { style: styles9.title, children: "DevTools" })
2621
+ ] }),
2622
+ /* @__PURE__ */ jsx9("div", { style: styles9.headerButtons, children: /* @__PURE__ */ jsx9(
2623
+ "button",
2624
+ {
2625
+ type: "button",
2626
+ style: styles9.iconButton,
2627
+ onClick: () => setCollapsed(!collapsed),
2628
+ title: collapsed ? "Expand (Ctrl+Shift+M)" : "Collapse (Ctrl+Shift+M)",
2629
+ children: /* @__PURE__ */ jsx9(
2630
+ "svg",
2631
+ {
2632
+ width: "14",
2633
+ height: "14",
2634
+ viewBox: "0 0 16 16",
2635
+ fill: "currentColor",
2636
+ style: {
2637
+ transform: collapsed ? "rotate(180deg)" : "none",
2638
+ transition: "transform 0.2s ease"
2639
+ },
2640
+ children: /* @__PURE__ */ jsx9("path", { d: "M7.646 4.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1-.708.708L8 5.707l-5.646 5.647a.5.5 0 0 1-.708-.708l6-6z" })
2641
+ }
2642
+ )
2643
+ }
2644
+ ) })
2645
+ ]
2646
+ }
2647
+ ),
2648
+ /* @__PURE__ */ jsx9(AnimatePresence3, { initial: false, children: !collapsed && /* @__PURE__ */ jsxs9(
2649
+ motion6.div,
2650
+ {
2651
+ style: styles9.body,
2652
+ initial: { height: 0, opacity: 0 },
2653
+ animate: { height: "auto", opacity: 1 },
2654
+ exit: { height: 0, opacity: 0 },
2655
+ transition: { duration: 0.2, ease: [0.16, 1, 0.3, 1] },
2656
+ children: [
2657
+ /* @__PURE__ */ jsx9(
2658
+ TabNav,
2659
+ {
2660
+ activeTab,
2661
+ onTabChange: setActiveTab,
2662
+ stepCount: steps.length,
2663
+ flowCount: flows.length
2664
+ }
2665
+ ),
2666
+ activeTab === "steps" ? /* @__PURE__ */ jsx9(
2667
+ StepList,
2668
+ {
2669
+ steps,
2670
+ mode,
2671
+ onToggleGrab: toggleGrabbing,
2672
+ onDeleteStep: removeStep,
2673
+ onReorderSteps: reorderSteps,
2674
+ onClearAll: clearAllSteps,
2675
+ onExport: exportSteps
2676
+ }
2677
+ ) : /* @__PURE__ */ jsx9(FlowsTab, { container: shadowContainer })
2678
+ ]
2679
+ }
2680
+ ) })
2681
+ ]
2682
+ }
2683
+ ),
2684
+ portalContainer
2685
+ )
2686
+ ] });
2687
+ }
2688
+ export {
2689
+ DevToolsContext,
2690
+ DevToolsProvider,
2691
+ FlowEditModal,
2692
+ FlowItem,
2693
+ FlowsTab,
2694
+ GrabberOverlay,
2695
+ SortableStepItem,
2696
+ SortableStepItem as StepItem,
2697
+ StepItemDragPreview,
2698
+ StepList,
2699
+ TabNav,
2700
+ Toolbar,
2701
+ clearSteps,
2702
+ extractComponentHierarchy,
2703
+ extractSource,
2704
+ formatSourcePath,
2705
+ generateSelector,
2706
+ getVSCodeLink,
2707
+ loadSteps,
2708
+ saveSteps,
2709
+ useDevToolsContext,
2710
+ useDevToolsContextRequired,
2711
+ useElementInfo,
2712
+ useFlowsData,
2713
+ useGrabMode,
2714
+ useStepStore
2715
+ };