storion 0.2.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +771 -561
  2. package/dist/async/async.d.ts +87 -0
  3. package/dist/async/async.d.ts.map +1 -0
  4. package/dist/async/index.d.ts +13 -0
  5. package/dist/async/index.d.ts.map +1 -0
  6. package/dist/async/index.js +451 -0
  7. package/dist/async/types.d.ts +275 -0
  8. package/dist/async/types.d.ts.map +1 -0
  9. package/dist/collection.d.ts +42 -0
  10. package/dist/collection.d.ts.map +1 -0
  11. package/dist/core/container.d.ts +33 -2
  12. package/dist/core/container.d.ts.map +1 -1
  13. package/dist/core/createResolver.d.ts +47 -0
  14. package/dist/core/createResolver.d.ts.map +1 -0
  15. package/dist/core/effect.d.ts.map +1 -1
  16. package/dist/core/equality.d.ts +23 -3
  17. package/dist/core/equality.d.ts.map +1 -1
  18. package/dist/core/fnWrapper.d.ts +54 -0
  19. package/dist/core/fnWrapper.d.ts.map +1 -0
  20. package/dist/core/middleware.d.ts +10 -10
  21. package/dist/core/middleware.d.ts.map +1 -1
  22. package/dist/core/pick.d.ts +6 -6
  23. package/dist/core/pick.d.ts.map +1 -1
  24. package/dist/core/store.d.ts +8 -8
  25. package/dist/core/store.d.ts.map +1 -1
  26. package/dist/core/storeContext.d.ts +63 -0
  27. package/dist/core/storeContext.d.ts.map +1 -0
  28. package/dist/devtools/controller.d.ts +4 -0
  29. package/dist/devtools/controller.d.ts.map +1 -0
  30. package/dist/devtools/index.d.ts +16 -0
  31. package/dist/devtools/index.d.ts.map +1 -0
  32. package/dist/devtools/index.js +239 -0
  33. package/dist/devtools/middleware.d.ts +22 -0
  34. package/dist/devtools/middleware.d.ts.map +1 -0
  35. package/dist/devtools/types.d.ts +116 -0
  36. package/dist/devtools/types.d.ts.map +1 -0
  37. package/dist/devtools-panel/DevtoolsPanel.d.ts +17 -0
  38. package/dist/devtools-panel/DevtoolsPanel.d.ts.map +1 -0
  39. package/dist/devtools-panel/components/CompareModal.d.ts +10 -0
  40. package/dist/devtools-panel/components/CompareModal.d.ts.map +1 -0
  41. package/dist/devtools-panel/components/EventEntry.d.ts +14 -0
  42. package/dist/devtools-panel/components/EventEntry.d.ts.map +1 -0
  43. package/dist/devtools-panel/components/EventFilterBar.d.ts +10 -0
  44. package/dist/devtools-panel/components/EventFilterBar.d.ts.map +1 -0
  45. package/dist/devtools-panel/components/EventsTab.d.ts +15 -0
  46. package/dist/devtools-panel/components/EventsTab.d.ts.map +1 -0
  47. package/dist/devtools-panel/components/ResizeHandle.d.ts +8 -0
  48. package/dist/devtools-panel/components/ResizeHandle.d.ts.map +1 -0
  49. package/dist/devtools-panel/components/StoreEntry.d.ts +13 -0
  50. package/dist/devtools-panel/components/StoreEntry.d.ts.map +1 -0
  51. package/dist/devtools-panel/components/StoresTab.d.ts +12 -0
  52. package/dist/devtools-panel/components/StoresTab.d.ts.map +1 -0
  53. package/dist/devtools-panel/components/TabLayout.d.ts +48 -0
  54. package/dist/devtools-panel/components/TabLayout.d.ts.map +1 -0
  55. package/dist/devtools-panel/components/icons.d.ts +27 -0
  56. package/dist/devtools-panel/components/icons.d.ts.map +1 -0
  57. package/dist/devtools-panel/components/index.d.ts +15 -0
  58. package/dist/devtools-panel/components/index.d.ts.map +1 -0
  59. package/dist/devtools-panel/hooks.d.ts +23 -0
  60. package/dist/devtools-panel/hooks.d.ts.map +1 -0
  61. package/dist/devtools-panel/index.d.ts +25 -0
  62. package/dist/devtools-panel/index.d.ts.map +1 -0
  63. package/dist/devtools-panel/index.js +3326 -0
  64. package/dist/devtools-panel/mount.d.ts +41 -0
  65. package/dist/devtools-panel/mount.d.ts.map +1 -0
  66. package/dist/devtools-panel/styles.d.ts +50 -0
  67. package/dist/devtools-panel/styles.d.ts.map +1 -0
  68. package/dist/devtools-panel/types.d.ts +15 -0
  69. package/dist/devtools-panel/types.d.ts.map +1 -0
  70. package/dist/devtools-panel/utils.d.ts +21 -0
  71. package/dist/devtools-panel/utils.d.ts.map +1 -0
  72. package/dist/index.d.ts +6 -1
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/is.d.ts +69 -0
  75. package/dist/is.d.ts.map +1 -0
  76. package/dist/react/create.d.ts +1 -1
  77. package/dist/react/index.d.ts +1 -0
  78. package/dist/react/index.d.ts.map +1 -1
  79. package/dist/react/index.js +210 -34
  80. package/dist/react/useLocalStore.d.ts.map +1 -1
  81. package/dist/react/useStore.d.ts +2 -2
  82. package/dist/react/useStore.d.ts.map +1 -1
  83. package/dist/react/withStore.d.ts +140 -0
  84. package/dist/react/withStore.d.ts.map +1 -0
  85. package/dist/{index-rLf6DusB.js → store-Yv-9gPVf.js} +543 -742
  86. package/dist/storion.js +809 -9
  87. package/dist/trigger.d.ts +40 -0
  88. package/dist/trigger.d.ts.map +1 -0
  89. package/dist/types.d.ts +538 -71
  90. package/dist/types.d.ts.map +1 -1
  91. package/package.json +13 -1
@@ -0,0 +1,3326 @@
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
+ import { useState, useRef, useCallback, useEffect, memo, useMemo } from "react";
3
+ import { createRoot } from "react-dom/client";
4
+ const colors = {
5
+ // Dark theme - zinc palette inspired by shadcn
6
+ bg: {
7
+ base: "#09090b",
8
+ // zinc-950
9
+ card: "#18181b",
10
+ // zinc-900
11
+ panel: "#18181b",
12
+ // zinc-900 - for modals/panels
13
+ elevated: "#27272a",
14
+ // zinc-800
15
+ hover: "#3f3f46",
16
+ // zinc-700
17
+ muted: "#52525b"
18
+ // zinc-600
19
+ },
20
+ text: {
21
+ primary: "#fafafa",
22
+ // zinc-50
23
+ secondary: "#a1a1aa",
24
+ // zinc-400
25
+ muted: "#71717a",
26
+ // zinc-500
27
+ dim: "#52525b"
28
+ // zinc-600
29
+ },
30
+ border: {
31
+ default: "#27272a",
32
+ // zinc-800
33
+ subtle: "#3f3f46",
34
+ // zinc-700
35
+ focus: "#a1a1aa"
36
+ // zinc-400
37
+ },
38
+ // Accent colors for syntax highlighting
39
+ syntax: {
40
+ // fuchsia-400
41
+ string: "#fbbf24",
42
+ // amber-400
43
+ number: "#60a5fa",
44
+ // blue-400
45
+ boolean: "#f472b6",
46
+ // pink-400
47
+ null: "#71717a"
48
+ // zinc-500
49
+ },
50
+ // UI accents
51
+ accent: {
52
+ primary: "#a1a1aa",
53
+ // zinc-400
54
+ success: "#4ade80",
55
+ // green-400
56
+ warning: "#fbbf24",
57
+ // amber-400
58
+ danger: "#f87171",
59
+ // red-400
60
+ info: "#38bdf8",
61
+ // sky-400
62
+ blue: "#60a5fa",
63
+ // blue-400
64
+ purple: "#a78bfa",
65
+ // violet-400
66
+ // Aliases for compare modal
67
+ green: "#4ade80",
68
+ // green-400
69
+ red: "#f87171",
70
+ // red-400
71
+ yellow: "#fbbf24"
72
+ // amber-400
73
+ }
74
+ };
75
+ const fonts = {
76
+ sans: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
77
+ mono: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
78
+ };
79
+ const panelStyles = `
80
+ /* ============================================
81
+ Base & Reset
82
+ ============================================ */
83
+ .storion-devtools {
84
+ font-family: ${fonts.sans};
85
+ font-size: 12px;
86
+ line-height: 1.4;
87
+ color: ${colors.text.primary};
88
+ background: ${colors.bg.base};
89
+ height: 100%;
90
+ display: flex;
91
+ flex-direction: column;
92
+ overflow: hidden;
93
+ -webkit-font-smoothing: antialiased;
94
+ -moz-osx-font-smoothing: grayscale;
95
+ }
96
+
97
+ .storion-devtools * {
98
+ box-sizing: border-box;
99
+ }
100
+
101
+ /* ============================================
102
+ Header
103
+ ============================================ */
104
+ .sdt-header {
105
+ display: flex;
106
+ align-items: center;
107
+ gap: 6px;
108
+ padding: 6px 8px;
109
+ border-bottom: 1px solid ${colors.border.default};
110
+ background: ${colors.bg.card};
111
+ flex-shrink: 0;
112
+ position: relative;
113
+ z-index: 1; /* Keep header above transparent content */
114
+ }
115
+
116
+ /* Transparency mode: keep header interactive */
117
+ .storion-devtools.transparent .sdt-header {
118
+ pointer-events: auto;
119
+ }
120
+
121
+ /* Transparency mode: make content non-interactive */
122
+ .storion-devtools.transparent .sdt-tabs,
123
+ .storion-devtools.transparent .sdt-tab-content,
124
+ .storion-devtools.transparent .sdt-main-content {
125
+ pointer-events: none;
126
+ }
127
+
128
+ .sdt-logo {
129
+ display: flex;
130
+ align-items: center;
131
+ gap: 6px;
132
+ }
133
+
134
+ .sdt-logo-icon {
135
+ width: 18px;
136
+ height: 18px;
137
+ background: linear-gradient(135deg, #a855f7 0%, #6366f1 100%);
138
+ border-radius: 4px;
139
+ display: flex;
140
+ align-items: center;
141
+ justify-content: center;
142
+ color: white;
143
+ font-size: 10px;
144
+ font-weight: 600;
145
+ }
146
+
147
+ .sdt-title {
148
+ font-size: 12px;
149
+ font-weight: 600;
150
+ color: ${colors.text.primary};
151
+ letter-spacing: -0.01em;
152
+ }
153
+
154
+ .sdt-header-actions {
155
+ margin-left: auto;
156
+ display: flex;
157
+ gap: 2px;
158
+ }
159
+
160
+ /* ============================================
161
+ Buttons
162
+ ============================================ */
163
+ .sdt-btn {
164
+ background: transparent;
165
+ border: 1px solid transparent;
166
+ color: ${colors.text.secondary};
167
+ cursor: pointer;
168
+ padding: 3px 5px;
169
+ border-radius: 4px;
170
+ display: inline-flex;
171
+ align-items: center;
172
+ justify-content: center;
173
+ gap: 4px;
174
+ font-family: ${fonts.sans};
175
+ font-size: 11px;
176
+ font-weight: 500;
177
+ transition: all 0.15s ease;
178
+ }
179
+
180
+ .sdt-btn:hover {
181
+ background: ${colors.bg.elevated};
182
+ color: ${colors.text.primary};
183
+ }
184
+
185
+ .sdt-btn:active {
186
+ background: ${colors.bg.hover};
187
+ }
188
+
189
+ .sdt-btn.active {
190
+ background: ${colors.bg.elevated};
191
+ color: ${colors.text.primary};
192
+ border-color: rgba(168, 85, 247, 0.4);
193
+ }
194
+
195
+ .sdt-btn-primary {
196
+ background: ${colors.text.primary};
197
+ color: ${colors.bg.base};
198
+ }
199
+
200
+ .sdt-btn-primary:hover {
201
+ background: ${colors.text.secondary};
202
+ color: ${colors.bg.base};
203
+ }
204
+
205
+ .sdt-btn-ghost {
206
+ background: transparent;
207
+ border-color: ${colors.border.subtle};
208
+ }
209
+
210
+ .sdt-btn-ghost:hover {
211
+ background: ${colors.bg.elevated};
212
+ border-color: ${colors.border.focus};
213
+ }
214
+
215
+ .sdt-btn-toggle {
216
+ background: transparent;
217
+ border: 1px solid ${colors.border.default};
218
+ color: ${colors.text.muted};
219
+ }
220
+
221
+ .sdt-btn-toggle:hover {
222
+ background: ${colors.bg.elevated};
223
+ color: ${colors.text.secondary};
224
+ }
225
+
226
+ .sdt-btn-toggle.active {
227
+ background: ${colors.bg.elevated};
228
+ border-color: ${colors.accent.info};
229
+ color: ${colors.accent.info};
230
+ }
231
+
232
+ .sdt-btn-toggle.active:hover {
233
+ background: ${colors.bg.hover};
234
+ }
235
+
236
+ /* Stores action bar */
237
+ .sdt-stores-actions {
238
+ display: flex;
239
+ align-items: center;
240
+ gap: 4px;
241
+ padding: 4px 8px;
242
+ border-bottom: 1px solid ${colors.border.default};
243
+ }
244
+
245
+ /* ============================================
246
+ Tab Content Container
247
+ ============================================ */
248
+ .sdt-tab-content {
249
+ display: flex;
250
+ flex-direction: column;
251
+ flex: 1;
252
+ overflow: hidden;
253
+ }
254
+
255
+ /* ============================================
256
+ Search Input
257
+ ============================================ */
258
+ .sdt-search {
259
+ position: relative;
260
+ display: flex;
261
+ align-items: center;
262
+ padding: 6px 8px;
263
+ border-bottom: 1px solid ${colors.border.default};
264
+ flex-shrink: 0;
265
+ }
266
+
267
+ .sdt-search-icon {
268
+ position: absolute;
269
+ left: 16px;
270
+ display: flex;
271
+ align-items: center;
272
+ justify-content: center;
273
+ color: ${colors.text.muted};
274
+ pointer-events: none;
275
+ z-index: 1;
276
+ }
277
+
278
+ .sdt-search-input {
279
+ width: 100%;
280
+ padding: 5px 28px 5px 28px;
281
+ background: ${colors.bg.card};
282
+ border: 1px solid ${colors.border.default};
283
+ border-radius: 4px;
284
+ color: ${colors.text.primary};
285
+ font-family: ${fonts.mono};
286
+ font-size: 11px;
287
+ outline: none;
288
+ transition: all 0.15s ease;
289
+ }
290
+
291
+ .sdt-search-input:hover {
292
+ border-color: ${colors.border.subtle};
293
+ }
294
+
295
+ .sdt-search-input:focus {
296
+ border-color: ${colors.border.focus};
297
+ box-shadow: 0 0 0 1px rgba(161, 161, 170, 0.1);
298
+ }
299
+
300
+ .sdt-search-input::placeholder {
301
+ color: ${colors.text.dim};
302
+ font-family: ${fonts.sans};
303
+ }
304
+
305
+ .sdt-search-clear {
306
+ position: absolute;
307
+ right: 14px;
308
+ display: flex;
309
+ align-items: center;
310
+ justify-content: center;
311
+ width: 16px;
312
+ height: 16px;
313
+ padding: 0;
314
+ background: transparent;
315
+ border: none;
316
+ border-radius: 2px;
317
+ color: ${colors.text.muted};
318
+ cursor: pointer;
319
+ transition: all 0.15s ease;
320
+ }
321
+
322
+ .sdt-search-clear:hover {
323
+ color: ${colors.text.primary};
324
+ background: ${colors.bg.elevated};
325
+ }
326
+
327
+ /* ============================================
328
+ Filter Bar
329
+ ============================================ */
330
+ .sdt-filter-bar {
331
+ display: flex;
332
+ gap: 4px;
333
+ padding: 5px 8px;
334
+ border-bottom: 1px solid ${colors.border.default};
335
+ flex-wrap: wrap;
336
+ flex-shrink: 0;
337
+ }
338
+
339
+ .sdt-filter-item {
340
+ display: inline-flex;
341
+ align-items: center;
342
+ gap: 4px;
343
+ padding: 2px 6px;
344
+ border-radius: 4px;
345
+ font-family: ${fonts.sans};
346
+ font-size: 10px;
347
+ font-weight: 500;
348
+ background: ${colors.bg.elevated};
349
+ color: ${colors.text.secondary};
350
+ border: 1px solid transparent;
351
+ cursor: pointer;
352
+ transition: all 0.15s ease;
353
+ }
354
+
355
+ .sdt-filter-item:hover {
356
+ background: ${colors.bg.hover};
357
+ color: ${colors.text.primary};
358
+ }
359
+
360
+ .sdt-filter-item.active {
361
+ background: ${colors.text.primary};
362
+ color: ${colors.bg.base};
363
+ }
364
+
365
+ .sdt-filter-item .count {
366
+ font-family: ${fonts.mono};
367
+ background: rgba(0, 0, 0, 0.2);
368
+ padding: 0px 4px;
369
+ border-radius: 3px;
370
+ font-size: 10px;
371
+ font-weight: 600;
372
+ }
373
+
374
+ /* ============================================
375
+ Action Bar
376
+ ============================================ */
377
+ .sdt-action-bar {
378
+ display: flex;
379
+ gap: 4px;
380
+ padding: 5px 8px;
381
+ border-bottom: 1px solid ${colors.border.default};
382
+ flex-shrink: 0;
383
+ }
384
+
385
+ .sdt-action-btn {
386
+ display: inline-flex;
387
+ align-items: center;
388
+ gap: 4px;
389
+ padding: 3px 8px;
390
+ border-radius: 4px;
391
+ font-family: ${fonts.sans};
392
+ font-size: 10px;
393
+ font-weight: 500;
394
+ background: ${colors.bg.elevated};
395
+ color: ${colors.text.secondary};
396
+ border: 1px solid ${colors.border.default};
397
+ cursor: pointer;
398
+ transition: all 0.15s ease;
399
+ }
400
+
401
+ .sdt-action-btn:hover:not(:disabled) {
402
+ background: ${colors.bg.hover};
403
+ border-color: ${colors.border.subtle};
404
+ color: ${colors.text.primary};
405
+ }
406
+
407
+ .sdt-action-btn:disabled {
408
+ opacity: 0.4;
409
+ cursor: not-allowed;
410
+ }
411
+
412
+ /* ============================================
413
+ Main Content
414
+ ============================================ */
415
+ .sdt-main-content {
416
+ flex: 1;
417
+ overflow-y: auto;
418
+ padding: 6px 8px;
419
+ }
420
+
421
+ /* Custom scrollbar */
422
+ .sdt-main-content::-webkit-scrollbar {
423
+ width: 6px;
424
+ }
425
+
426
+ .sdt-main-content::-webkit-scrollbar-track {
427
+ background: transparent;
428
+ }
429
+
430
+ .sdt-main-content::-webkit-scrollbar-thumb {
431
+ background: ${colors.bg.muted};
432
+ border-radius: 3px;
433
+ }
434
+
435
+ .sdt-main-content::-webkit-scrollbar-thumb:hover {
436
+ background: ${colors.text.muted};
437
+ }
438
+
439
+ /* ============================================
440
+ Store Entry Card
441
+ ============================================ */
442
+ .sdt-store-entry {
443
+ background: ${colors.bg.card};
444
+ border-radius: 6px;
445
+ margin-bottom: 4px;
446
+ overflow: hidden;
447
+ border: 1px solid ${colors.border.default};
448
+ transition: all 0.15s ease;
449
+ }
450
+
451
+ .sdt-store-entry:hover {
452
+ border-color: ${colors.border.subtle};
453
+ }
454
+
455
+ .sdt-store-entry.selected {
456
+ border-color: ${colors.accent.primary};
457
+ }
458
+
459
+ .sdt-store-header {
460
+ display: flex;
461
+ align-items: center;
462
+ gap: 6px;
463
+ padding: 8px 10px;
464
+ cursor: pointer;
465
+ transition: background 0.15s ease;
466
+ background: ${colors.bg.elevated};
467
+ }
468
+
469
+ .sdt-store-header:hover {
470
+ background: ${colors.bg.hover};
471
+ }
472
+
473
+ /* Flash animation for store header */
474
+ @keyframes sdt-store-flash {
475
+ 0% { background-color: rgba(168, 85, 247, 0.25); }
476
+ 100% { background-color: transparent; }
477
+ }
478
+
479
+ .sdt-store-header.flash {
480
+ animation: sdt-store-flash 0.5s ease-out;
481
+ }
482
+
483
+ .sdt-store-icon {
484
+ display: flex;
485
+ align-items: center;
486
+ justify-content: center;
487
+ color: ${colors.accent.primary};
488
+ opacity: 0.7;
489
+ }
490
+
491
+ .sdt-expand-btn {
492
+ background: transparent;
493
+ border: none;
494
+ color: ${colors.text.muted};
495
+ cursor: pointer;
496
+ padding: 2px;
497
+ display: flex;
498
+ align-items: center;
499
+ justify-content: center;
500
+ border-radius: 3px;
501
+ transition: all 0.15s ease;
502
+ margin-left: auto;
503
+ }
504
+
505
+ .sdt-expand-btn:hover {
506
+ background: ${colors.bg.hover};
507
+ color: ${colors.text.primary};
508
+ }
509
+
510
+ .sdt-store-name {
511
+ font-family: ${fonts.mono};
512
+ font-size: 12px;
513
+ font-weight: 600;
514
+ color: #ffffff;
515
+ flex: 1;
516
+ min-width: 0;
517
+ white-space: nowrap;
518
+ overflow: hidden;
519
+ text-overflow: ellipsis;
520
+ }
521
+
522
+ .sdt-store-actions {
523
+ display: flex;
524
+ gap: 2px;
525
+ }
526
+
527
+ /* ============================================
528
+ Store Details
529
+ ============================================ */
530
+ .sdt-store-details {
531
+ padding: 8px;
532
+ border-top: 1px solid ${colors.border.default};
533
+ background: ${colors.bg.base};
534
+ }
535
+
536
+ .sdt-section-title {
537
+ font-family: ${fonts.sans};
538
+ font-size: 10px;
539
+ font-weight: 600;
540
+ text-transform: uppercase;
541
+ letter-spacing: 0.04em;
542
+ color: ${colors.text.muted};
543
+ margin-bottom: 6px;
544
+ }
545
+
546
+ /* ============================================
547
+ State JSON Textarea
548
+ ============================================ */
549
+ .sdt-state-json {
550
+ font-family: ${fonts.mono};
551
+ font-size: 11px;
552
+ line-height: 1.4;
553
+ width: 100%;
554
+ min-height: 160px;
555
+ max-height: 400px;
556
+ padding: 6px 8px;
557
+ background: ${colors.bg.base};
558
+ border: 1px solid ${colors.border.default};
559
+ border-radius: 4px;
560
+ color: ${colors.text.secondary};
561
+ resize: vertical;
562
+ outline: none;
563
+ box-sizing: border-box;
564
+ }
565
+
566
+ .sdt-state-json:focus {
567
+ border-color: ${colors.accent};
568
+ }
569
+
570
+ /* Keep state-value for metadata section */
571
+ .sdt-state-value {
572
+ color: ${colors.syntax.string};
573
+ }
574
+
575
+ .sdt-state-value.string {
576
+ color: ${colors.syntax.string};
577
+ }
578
+
579
+ .sdt-state-value.number {
580
+ color: ${colors.syntax.number};
581
+ }
582
+
583
+ .sdt-state-value.boolean {
584
+ color: ${colors.syntax.boolean};
585
+ }
586
+
587
+ .sdt-state-value.null {
588
+ color: ${colors.syntax.null};
589
+ font-style: italic;
590
+ }
591
+
592
+ .sdt-state-value.object {
593
+ color: ${colors.text.secondary};
594
+ }
595
+
596
+ /* ============================================
597
+ History
598
+ ============================================ */
599
+ .sdt-history {
600
+ margin-top: 8px;
601
+ }
602
+
603
+ .sdt-history-item {
604
+ display: flex;
605
+ align-items: center;
606
+ gap: 6px;
607
+ padding: 4px 6px;
608
+ border-radius: 4px;
609
+ margin-bottom: 2px;
610
+ transition: background 0.15s ease;
611
+ border: 1px solid transparent;
612
+ }
613
+
614
+ .sdt-history-item:hover {
615
+ background: ${colors.bg.elevated};
616
+ }
617
+
618
+ .sdt-history-item.initial .sdt-history-time {
619
+ color: #ffffff;
620
+ font-weight: 500;
621
+ }
622
+
623
+ .sdt-history-time {
624
+ font-family: ${fonts.mono};
625
+ font-size: 10px;
626
+ color: ${colors.text.secondary};
627
+ white-space: nowrap;
628
+ }
629
+
630
+ .sdt-history-props {
631
+ font-family: ${fonts.mono};
632
+ font-size: 10px;
633
+ color: ${colors.text.muted};
634
+ flex: 1;
635
+ overflow: hidden;
636
+ text-overflow: ellipsis;
637
+ white-space: nowrap;
638
+ }
639
+
640
+ .sdt-history-actions {
641
+ display: flex;
642
+ gap: 4px;
643
+ margin-left: auto;
644
+ }
645
+
646
+ .sdt-history-action-btn {
647
+ background: transparent;
648
+ border: none;
649
+ color: ${colors.text.muted};
650
+ cursor: pointer;
651
+ padding: 2px 4px;
652
+ border-radius: 3px;
653
+ display: flex;
654
+ align-items: center;
655
+ opacity: 0.6;
656
+ transition: all 0.15s;
657
+ }
658
+
659
+ .sdt-history-action-btn:hover {
660
+ opacity: 1;
661
+ background: ${colors.bg.hover};
662
+ color: ${colors.text.primary};
663
+ }
664
+
665
+ .sdt-history-expand {
666
+ display: flex;
667
+ justify-content: center;
668
+ padding: 4px;
669
+ }
670
+
671
+ .sdt-history-expand-btn {
672
+ font-family: ${fonts.sans};
673
+ font-size: 10px;
674
+ font-weight: 500;
675
+ background: transparent;
676
+ border: 1px solid ${colors.border.default};
677
+ color: ${colors.text.secondary};
678
+ padding: 3px 10px;
679
+ border-radius: 4px;
680
+ cursor: pointer;
681
+ transition: all 0.15s ease;
682
+ }
683
+
684
+ .sdt-history-expand-btn:hover {
685
+ background: ${colors.bg.elevated};
686
+ border-color: ${colors.border.subtle};
687
+ color: ${colors.text.primary};
688
+ }
689
+
690
+ /* ============================================
691
+ Metadata Section
692
+ ============================================ */
693
+ .sdt-metadata {
694
+ margin-top: 8px;
695
+ }
696
+
697
+ .sdt-metadata-row {
698
+ display: flex;
699
+ align-items: baseline;
700
+ margin-bottom: 3px;
701
+ font-size: 10px;
702
+ }
703
+
704
+ .sdt-metadata-row:last-child {
705
+ margin-bottom: 0;
706
+ }
707
+
708
+ .sdt-metadata-label {
709
+ font-family: ${fonts.sans};
710
+ font-weight: 500;
711
+ color: ${colors.text.muted};
712
+ min-width: 60px;
713
+ flex-shrink: 0;
714
+ }
715
+
716
+ .sdt-metadata-value {
717
+ font-family: ${fonts.mono};
718
+ color: ${colors.text.secondary};
719
+ word-break: break-all;
720
+ }
721
+
722
+ /* ============================================
723
+ Resize Handle
724
+ ============================================ */
725
+ .sdt-resize-handle {
726
+ position: absolute;
727
+ background: ${colors.border.subtle};
728
+ z-index: 10;
729
+ transition: background 0.15s ease;
730
+ /* Touch-friendly: add invisible padding for larger hit area */
731
+ touch-action: none; /* Prevent default touch behaviors */
732
+ }
733
+
734
+ .sdt-resize-handle:hover,
735
+ .sdt-resize-handle.active {
736
+ background: ${colors.accent.primary};
737
+ }
738
+
739
+ .sdt-resize-handle.horizontal {
740
+ width: 4px;
741
+ height: 100%;
742
+ top: 0;
743
+ cursor: ew-resize;
744
+ /* Add larger hit area for touch devices using ::before */
745
+ }
746
+
747
+ .sdt-resize-handle.horizontal::before {
748
+ content: '';
749
+ position: absolute;
750
+ top: 0;
751
+ bottom: 0;
752
+ left: -8px;
753
+ right: -8px;
754
+ /* Invisible 20px wide hit area (4px + 8px on each side) */
755
+ }
756
+
757
+ .sdt-resize-handle.vertical {
758
+ height: 4px;
759
+ width: 100%;
760
+ left: 0;
761
+ cursor: ns-resize;
762
+ }
763
+
764
+ .sdt-resize-handle.vertical::before {
765
+ content: '';
766
+ position: absolute;
767
+ left: 0;
768
+ right: 0;
769
+ top: -8px;
770
+ bottom: -8px;
771
+ /* Invisible 20px tall hit area (4px + 8px on each side) */
772
+ }
773
+
774
+ .sdt-resize-handle.left { left: 0; }
775
+ .sdt-resize-handle.right { right: 0; }
776
+ .sdt-resize-handle.top { top: 0; }
777
+ .sdt-resize-handle.bottom { bottom: 0; }
778
+
779
+ /* ============================================
780
+ Empty State
781
+ ============================================ */
782
+ .sdt-empty {
783
+ display: flex;
784
+ flex-direction: column;
785
+ align-items: center;
786
+ justify-content: center;
787
+ padding: 24px 12px;
788
+ color: ${colors.text.muted};
789
+ text-align: center;
790
+ }
791
+
792
+ .sdt-empty-icon {
793
+ font-size: 28px;
794
+ margin-bottom: 8px;
795
+ opacity: 0.5;
796
+ }
797
+
798
+ .sdt-empty-message {
799
+ font-family: ${fonts.sans};
800
+ font-size: 12px;
801
+ font-weight: 500;
802
+ color: ${colors.text.secondary};
803
+ margin-bottom: 4px;
804
+ }
805
+
806
+ .sdt-empty-hint {
807
+ font-family: ${fonts.sans};
808
+ font-size: 11px;
809
+ color: ${colors.text.muted};
810
+ }
811
+
812
+ /* ============================================
813
+ Position Variants
814
+ ============================================ */
815
+ .storion-devtools.position-bottom {
816
+ flex-direction: column;
817
+ }
818
+
819
+ .storion-devtools.position-right {
820
+ flex-direction: column;
821
+ }
822
+
823
+ /* When bottom position and wide screen, display stores in 2 columns */
824
+ .storion-devtools.position-bottom .sdt-main-content {
825
+ display: grid;
826
+ grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
827
+ grid-auto-rows: min-content;
828
+ gap: 4px;
829
+ align-content: start;
830
+ align-items: start;
831
+ overflow-y: auto; /* Ensure scrolling still works */
832
+ }
833
+
834
+ .storion-devtools.position-bottom .sdt-store-entry {
835
+ margin-bottom: 0;
836
+ height: auto;
837
+ min-height: 0;
838
+ }
839
+
840
+ /* ============================================
841
+ Tabs
842
+ ============================================ */
843
+ .sdt-tabs {
844
+ display: flex;
845
+ border-bottom: 1px solid ${colors.border.default};
846
+ background: ${colors.bg.card};
847
+ padding: 0 4px;
848
+ gap: 2px;
849
+ }
850
+
851
+ .sdt-tab {
852
+ font-family: ${fonts.sans};
853
+ font-size: 11px;
854
+ font-weight: 500;
855
+ padding: 6px 10px;
856
+ background: transparent;
857
+ border: none;
858
+ color: ${colors.text.muted};
859
+ cursor: pointer;
860
+ border-bottom: 2px solid transparent;
861
+ margin-bottom: -1px;
862
+ display: flex;
863
+ align-items: center;
864
+ gap: 6px;
865
+ transition: color 0.15s, border-color 0.15s;
866
+ }
867
+
868
+ .sdt-tab:hover {
869
+ color: ${colors.text.secondary};
870
+ }
871
+
872
+ .sdt-tab.active {
873
+ color: ${colors.text.primary};
874
+ border-bottom-color: ${colors.accent.primary};
875
+ }
876
+
877
+ .sdt-tab-count {
878
+ font-family: ${fonts.mono};
879
+ font-size: 10px;
880
+ padding: 1px 5px;
881
+ border-radius: 8px;
882
+ background: ${colors.bg.elevated};
883
+ color: ${colors.text.muted};
884
+ }
885
+
886
+ .sdt-tab.active .sdt-tab-count {
887
+ background: ${colors.bg.hover};
888
+ color: ${colors.text.secondary};
889
+ }
890
+
891
+ /* Flash animation for tabs */
892
+ @keyframes sdt-flash {
893
+ 0% { background-color: rgba(168, 85, 247, 0.3); }
894
+ 100% { background-color: transparent; }
895
+ }
896
+
897
+ .sdt-tab.flash {
898
+ animation: sdt-flash 0.6s ease-out;
899
+ }
900
+
901
+ .sdt-tab.flash .sdt-tab-count {
902
+ animation: sdt-flash 0.6s ease-out;
903
+ }
904
+
905
+ /* ============================================
906
+ Event Entry
907
+ ============================================ */
908
+ .sdt-event-entry {
909
+ display: flex;
910
+ align-items: center;
911
+ gap: 6px;
912
+ padding: 4px 8px;
913
+ border-bottom: 1px solid ${colors.border.default};
914
+ font-family: ${fonts.mono};
915
+ font-size: 10px;
916
+ min-height: 28px;
917
+ max-height: 44px;
918
+ overflow: hidden;
919
+ }
920
+
921
+ .sdt-event-entry:hover {
922
+ background: ${colors.bg.elevated};
923
+ }
924
+
925
+ .sdt-event-time {
926
+ color: ${colors.text.muted};
927
+ font-size: 10px;
928
+ flex-shrink: 0;
929
+ width: 80px;
930
+ margin-right: 4px;
931
+ }
932
+
933
+ .sdt-event-type {
934
+ font-family: ${fonts.sans};
935
+ font-size: 9px;
936
+ font-weight: 600;
937
+ padding: 2px 5px;
938
+ border-radius: 3px;
939
+ flex-shrink: 0;
940
+ text-transform: uppercase;
941
+ letter-spacing: 0.02em;
942
+ }
943
+
944
+ .sdt-event-target {
945
+ color: ${colors.text.secondary};
946
+ flex-shrink: 0;
947
+ max-width: 100px;
948
+ overflow: hidden;
949
+ text-overflow: ellipsis;
950
+ white-space: nowrap;
951
+ }
952
+
953
+ .sdt-event-target.clickable {
954
+ cursor: pointer;
955
+ color: ${colors.accent.info};
956
+ text-decoration: underline;
957
+ text-decoration-color: transparent;
958
+ transition: text-decoration-color 0.15s;
959
+ }
960
+
961
+ .sdt-event-target.clickable:hover {
962
+ text-decoration-color: ${colors.accent.info};
963
+ }
964
+
965
+ .sdt-event-extra {
966
+ color: ${colors.text.muted};
967
+ flex: 1;
968
+ min-width: 0;
969
+ overflow: hidden;
970
+ text-overflow: ellipsis;
971
+ white-space: nowrap;
972
+ display: -webkit-box;
973
+ -webkit-line-clamp: 2;
974
+ -webkit-box-orient: vertical;
975
+ }
976
+
977
+ .sdt-event-copy {
978
+ background: transparent;
979
+ border: none;
980
+ color: ${colors.text.muted};
981
+ cursor: pointer;
982
+ padding: 2px 4px;
983
+ border-radius: 3px;
984
+ flex-shrink: 0;
985
+ display: flex;
986
+ align-items: center;
987
+ opacity: 0.6;
988
+ transition: opacity 0.15s, color 0.15s, background 0.15s;
989
+ }
990
+
991
+ .sdt-event-copy:hover {
992
+ opacity: 1;
993
+ color: ${colors.text.secondary};
994
+ background: ${colors.bg.hover};
995
+ }
996
+
997
+ /* ============================================
998
+ Event Filter Bar
999
+ ============================================ */
1000
+ .sdt-event-filters {
1001
+ display: flex;
1002
+ align-items: center;
1003
+ gap: 4px;
1004
+ padding: 4px 8px;
1005
+ border-bottom: 1px solid ${colors.border.default};
1006
+ background: ${colors.bg.card};
1007
+ }
1008
+
1009
+ .sdt-event-filter-chips {
1010
+ display: flex;
1011
+ flex-wrap: wrap;
1012
+ gap: 3px;
1013
+ flex: 1;
1014
+ }
1015
+
1016
+ .sdt-event-filter-chip {
1017
+ font-family: ${fonts.sans};
1018
+ font-size: 9px;
1019
+ font-weight: 500;
1020
+ padding: 2px 6px;
1021
+ border-radius: 3px;
1022
+ border: 1px solid ${colors.border.default};
1023
+ background: transparent;
1024
+ color: ${colors.text.muted};
1025
+ cursor: pointer;
1026
+ transition: all 0.15s;
1027
+ }
1028
+
1029
+ .sdt-event-filter-chip:hover {
1030
+ border-color: ${colors.border.subtle};
1031
+ color: ${colors.text.secondary};
1032
+ }
1033
+
1034
+ .sdt-event-filter-chip.active {
1035
+ color: ${colors.text.primary};
1036
+ }
1037
+
1038
+ .sdt-event-clear-btn {
1039
+ background: transparent;
1040
+ border: none;
1041
+ color: ${colors.text.muted};
1042
+ cursor: pointer;
1043
+ padding: 4px;
1044
+ border-radius: 3px;
1045
+ transition: color 0.15s, background 0.15s;
1046
+ }
1047
+
1048
+ .sdt-event-clear-btn:hover {
1049
+ color: ${colors.accent.danger};
1050
+ background: ${colors.bg.elevated};
1051
+ }
1052
+
1053
+ /* ============================================
1054
+ Floating Button (Collapsed State)
1055
+ ============================================ */
1056
+ .sdt-floating-btn {
1057
+ position: fixed;
1058
+ bottom: 16px;
1059
+ left: 16px;
1060
+ width: 32px;
1061
+ height: 32px;
1062
+ border-radius: 50%;
1063
+ background: ${colors.bg.card};
1064
+ border: 1px solid ${colors.border.default};
1065
+ color: ${colors.text.primary};
1066
+ font-size: 14px;
1067
+ cursor: pointer;
1068
+ display: flex;
1069
+ align-items: center;
1070
+ justify-content: center;
1071
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
1072
+ transition: transform 0.15s, box-shadow 0.15s, background 0.15s;
1073
+ z-index: 999999;
1074
+ }
1075
+
1076
+ .sdt-floating-btn:hover {
1077
+ background: ${colors.bg.elevated};
1078
+ transform: scale(1.1);
1079
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
1080
+ }
1081
+
1082
+ .sdt-floating-btn:active {
1083
+ transform: scale(0.95);
1084
+ }
1085
+
1086
+ /* ============================================
1087
+ Store Actions in Header
1088
+ ============================================ */
1089
+ .sdt-store-actions {
1090
+ display: flex;
1091
+ gap: 4px;
1092
+ margin-left: auto;
1093
+ }
1094
+
1095
+ .sdt-store-action-btn {
1096
+ background: transparent;
1097
+ border: none;
1098
+ color: ${colors.text.muted};
1099
+ cursor: pointer;
1100
+ padding: 2px 4px;
1101
+ border-radius: 3px;
1102
+ display: flex;
1103
+ align-items: center;
1104
+ opacity: 0.6;
1105
+ transition: opacity 0.15s, background 0.15s, color 0.15s;
1106
+ }
1107
+
1108
+ .sdt-store-action-btn:hover {
1109
+ opacity: 1;
1110
+ background: ${colors.bg.hover};
1111
+ color: ${colors.text.primary};
1112
+ }
1113
+
1114
+ /* ============================================
1115
+ Section Header with Actions
1116
+ ============================================ */
1117
+ .sdt-section-header {
1118
+ display: flex;
1119
+ align-items: center;
1120
+ justify-content: space-between;
1121
+ margin-bottom: 6px;
1122
+ }
1123
+
1124
+ .sdt-section-actions {
1125
+ display: flex;
1126
+ gap: 4px;
1127
+ }
1128
+
1129
+ .sdt-edit-btn,
1130
+ .sdt-save-btn,
1131
+ .sdt-cancel-btn {
1132
+ background: transparent;
1133
+ border: 1px solid ${colors.border.subtle};
1134
+ color: ${colors.text.muted};
1135
+ cursor: pointer;
1136
+ padding: 3px 6px;
1137
+ border-radius: 3px;
1138
+ display: flex;
1139
+ align-items: center;
1140
+ gap: 4px;
1141
+ font-size: 10px;
1142
+ transition: all 0.15s;
1143
+ }
1144
+
1145
+ .sdt-edit-btn:hover {
1146
+ background: ${colors.bg.hover};
1147
+ color: ${colors.text.primary};
1148
+ border-color: ${colors.border.default};
1149
+ }
1150
+
1151
+ .sdt-save-btn {
1152
+ border-color: ${colors.accent.green};
1153
+ color: ${colors.accent.green};
1154
+ }
1155
+
1156
+ .sdt-save-btn:hover {
1157
+ background: ${colors.accent.green}20;
1158
+ }
1159
+
1160
+ .sdt-cancel-btn:hover {
1161
+ background: ${colors.accent.red}20;
1162
+ color: ${colors.accent.red};
1163
+ border-color: ${colors.accent.red};
1164
+ }
1165
+
1166
+ .sdt-state-json.editing {
1167
+ background: ${colors.bg.elevated};
1168
+ border-color: ${colors.accent.primary};
1169
+ color: ${colors.text.primary};
1170
+ }
1171
+
1172
+ /* ============================================
1173
+ Event Replay Button
1174
+ ============================================ */
1175
+ .sdt-event-actions {
1176
+ display: flex;
1177
+ gap: 4px;
1178
+ margin-left: auto;
1179
+ }
1180
+
1181
+ .sdt-event-replay {
1182
+ background: transparent;
1183
+ border: none;
1184
+ color: ${colors.text.muted};
1185
+ cursor: pointer;
1186
+ padding: 2px 4px;
1187
+ border-radius: 3px;
1188
+ display: flex;
1189
+ align-items: center;
1190
+ opacity: 0.6;
1191
+ transition: all 0.15s;
1192
+ }
1193
+
1194
+ .sdt-event-replay:hover {
1195
+ opacity: 1;
1196
+ background: ${colors.accent.purple}20;
1197
+ color: ${colors.accent.purple};
1198
+ }
1199
+
1200
+ /* ============================================
1201
+ Compare Modal
1202
+ ============================================ */
1203
+ .sdt-modal-overlay {
1204
+ position: fixed;
1205
+ top: 0;
1206
+ left: 0;
1207
+ right: 0;
1208
+ bottom: 0;
1209
+ background: rgba(0, 0, 0, 0.6);
1210
+ display: flex;
1211
+ align-items: center;
1212
+ justify-content: center;
1213
+ z-index: 1000000;
1214
+ backdrop-filter: blur(2px);
1215
+ padding: 16px;
1216
+ touch-action: manipulation;
1217
+ }
1218
+
1219
+ .sdt-modal {
1220
+ background: ${colors.bg.panel};
1221
+ border: 1px solid ${colors.border.default};
1222
+ border-radius: 8px;
1223
+ width: 100%;
1224
+ min-width: 300px;
1225
+ max-width: 800px;
1226
+ max-height: 80vh;
1227
+ display: flex;
1228
+ flex-direction: column;
1229
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
1230
+ }
1231
+
1232
+ @media (min-width: 640px) {
1233
+ .sdt-modal {
1234
+ min-width: 500px;
1235
+ }
1236
+ }
1237
+
1238
+ .sdt-modal-header {
1239
+ display: flex;
1240
+ align-items: center;
1241
+ justify-content: space-between;
1242
+ padding: 12px 16px;
1243
+ border-bottom: 1px solid ${colors.border.default};
1244
+ }
1245
+
1246
+ .sdt-modal-title {
1247
+ font-weight: 600;
1248
+ color: ${colors.text.primary};
1249
+ font-size: 13px;
1250
+ }
1251
+
1252
+ .sdt-modal-close {
1253
+ background: transparent;
1254
+ border: none;
1255
+ color: ${colors.text.muted};
1256
+ cursor: pointer;
1257
+ padding: 4px;
1258
+ border-radius: 4px;
1259
+ display: flex;
1260
+ align-items: center;
1261
+ transition: all 0.15s;
1262
+ }
1263
+
1264
+ .sdt-modal-close:hover {
1265
+ background: ${colors.bg.hover};
1266
+ color: ${colors.text.primary};
1267
+ }
1268
+
1269
+ .sdt-modal-body {
1270
+ padding: 16px;
1271
+ padding-top: 0;
1272
+ overflow: auto;
1273
+ flex: 1;
1274
+ }
1275
+
1276
+ /* Sticky header container - legend + column headers */
1277
+ .sdt-compare-sticky-header {
1278
+ position: sticky;
1279
+ top: 0;
1280
+ z-index: 10;
1281
+ background: ${colors.bg.panel};
1282
+ margin: 0 -16px;
1283
+ padding: 16px 16px 0 16px;
1284
+ }
1285
+
1286
+ .sdt-compare-legend {
1287
+ display: flex;
1288
+ gap: 16px;
1289
+ margin-bottom: 12px;
1290
+ font-size: 11px;
1291
+ }
1292
+
1293
+ .sdt-legend-item {
1294
+ display: flex;
1295
+ align-items: center;
1296
+ gap: 4px;
1297
+ }
1298
+
1299
+ .sdt-legend-item.changed { color: ${colors.accent.yellow}; }
1300
+ .sdt-legend-item.added { color: ${colors.accent.green}; }
1301
+ .sdt-legend-item.removed { color: ${colors.accent.red}; }
1302
+ .sdt-legend-item.unchanged { color: ${colors.text.muted}; }
1303
+
1304
+ /* Two-column header: Snapshot | Current */
1305
+ .sdt-compare-columns-header {
1306
+ display: grid;
1307
+ grid-template-columns: 1fr 1fr;
1308
+ gap: 1px;
1309
+ background: ${colors.border.subtle};
1310
+ border: 1px solid ${colors.border.subtle};
1311
+ border-radius: 4px 4px 0 0;
1312
+ margin-bottom: 0px; /* Remove gap with content below */
1313
+ }
1314
+
1315
+ .sdt-compare-col-old,
1316
+ .sdt-compare-col-new {
1317
+ padding: 8px 10px;
1318
+ font-size: 10px;
1319
+ font-weight: 600;
1320
+ text-transform: uppercase;
1321
+ letter-spacing: 0.5px;
1322
+ color: ${colors.text.secondary};
1323
+ background: ${colors.bg.elevated};
1324
+ }
1325
+
1326
+ .sdt-compare-col-old {
1327
+ background: linear-gradient(${colors.accent.red}15, ${colors.accent.red}15), ${colors.bg.elevated};
1328
+ }
1329
+
1330
+ .sdt-compare-col-new {
1331
+ background: linear-gradient(${colors.accent.green}15, ${colors.accent.green}15), ${colors.bg.elevated};
1332
+ }
1333
+
1334
+ /* Content area */
1335
+ .sdt-compare-content {
1336
+ display: flex;
1337
+ flex-direction: column;
1338
+ border: 1px solid ${colors.border.subtle};
1339
+ border-top: none;
1340
+ border-radius: 0 0 4px 4px;
1341
+ }
1342
+
1343
+ /* Each property item */
1344
+ .sdt-compare-item {
1345
+ border-bottom: 1px solid ${colors.border.subtle};
1346
+ }
1347
+
1348
+ .sdt-compare-item:last-child {
1349
+ border-bottom: none;
1350
+ }
1351
+
1352
+ .sdt-compare-item.unchanged {
1353
+ opacity: 0.5;
1354
+ }
1355
+
1356
+ /* Property name - sticky within its section */
1357
+ .sdt-compare-prop-name {
1358
+ font-weight: 600;
1359
+ padding: 6px 10px;
1360
+ background: ${colors.bg.elevated};
1361
+ font-size: 11px;
1362
+ color: ${colors.text.primary};
1363
+ position: sticky;
1364
+ top: 75px; /* Below the sticky header (legend ~27px + columns ~35px + padding 16px) */
1365
+ z-index: 5;
1366
+ border-bottom: 1px solid ${colors.border.subtle};
1367
+ }
1368
+
1369
+ .sdt-compare-item.changed .sdt-compare-prop-name {
1370
+ box-shadow: inset 3px 0 0 ${colors.accent.yellow};
1371
+ }
1372
+
1373
+ .sdt-compare-item.added .sdt-compare-prop-name {
1374
+ box-shadow: inset 3px 0 0 ${colors.accent.green};
1375
+ }
1376
+
1377
+ .sdt-compare-item.removed .sdt-compare-prop-name {
1378
+ box-shadow: inset 3px 0 0 ${colors.accent.red};
1379
+ }
1380
+
1381
+ /* Two-column values */
1382
+ .sdt-compare-values {
1383
+ display: grid;
1384
+ grid-template-columns: 1fr 1fr;
1385
+ gap: 1px;
1386
+ background: ${colors.border.subtle};
1387
+ }
1388
+
1389
+ .sdt-compare-old,
1390
+ .sdt-compare-new {
1391
+ padding: 8px 10px;
1392
+ background: ${colors.bg.base};
1393
+ min-height: 40px;
1394
+ }
1395
+
1396
+ .sdt-compare-old {
1397
+ background: linear-gradient(${colors.accent.red}08, ${colors.accent.red}08), ${colors.bg.base};
1398
+ }
1399
+
1400
+ .sdt-compare-new {
1401
+ background: linear-gradient(${colors.accent.green}08, ${colors.accent.green}08), ${colors.bg.base};
1402
+ }
1403
+
1404
+ .sdt-compare-old pre,
1405
+ .sdt-compare-new pre {
1406
+ margin: 0;
1407
+ font-family: ${fonts.mono};
1408
+ font-size: 11px;
1409
+ white-space: pre-wrap;
1410
+ word-break: break-all;
1411
+ color: #ffffff;
1412
+ }
1413
+
1414
+ /* ============================================
1415
+ Responsive
1416
+ ============================================ */
1417
+ @media (max-width: 768px) {
1418
+ .storion-devtools {
1419
+ font-size: 11px;
1420
+ }
1421
+
1422
+ .sdt-header {
1423
+ padding: 5px 6px;
1424
+ }
1425
+
1426
+ .sdt-search {
1427
+ padding: 5px 6px;
1428
+ }
1429
+
1430
+ .sdt-main-content {
1431
+ padding: 5px 6px;
1432
+ }
1433
+
1434
+ .sdt-store-header {
1435
+ padding: 5px 6px;
1436
+ }
1437
+
1438
+ .sdt-store-details {
1439
+ padding: 6px;
1440
+ }
1441
+ }
1442
+ `;
1443
+ const IconChevron = ({ open }) => /* @__PURE__ */ jsx(
1444
+ "svg",
1445
+ {
1446
+ width: "12",
1447
+ height: "12",
1448
+ viewBox: "0 0 24 24",
1449
+ fill: "none",
1450
+ stroke: "currentColor",
1451
+ strokeWidth: "2",
1452
+ style: {
1453
+ transform: open ? "rotate(90deg)" : "rotate(0)",
1454
+ transition: "transform 0.15s"
1455
+ },
1456
+ children: /* @__PURE__ */ jsx("path", { d: "M9 18l6-6-6-6" })
1457
+ }
1458
+ );
1459
+ const IconStore = () => /* @__PURE__ */ jsxs(
1460
+ "svg",
1461
+ {
1462
+ width: "14",
1463
+ height: "14",
1464
+ viewBox: "0 0 24 24",
1465
+ fill: "none",
1466
+ stroke: "currentColor",
1467
+ strokeWidth: "2",
1468
+ children: [
1469
+ /* @__PURE__ */ jsx("ellipse", { cx: "12", cy: "5", rx: "9", ry: "3" }),
1470
+ /* @__PURE__ */ jsx("path", { d: "M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" }),
1471
+ /* @__PURE__ */ jsx("path", { d: "M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" })
1472
+ ]
1473
+ }
1474
+ );
1475
+ const IconMinimize = () => /* @__PURE__ */ jsx(
1476
+ "svg",
1477
+ {
1478
+ width: "14",
1479
+ height: "14",
1480
+ viewBox: "0 0 24 24",
1481
+ fill: "none",
1482
+ stroke: "currentColor",
1483
+ strokeWidth: "2",
1484
+ children: /* @__PURE__ */ jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
1485
+ }
1486
+ );
1487
+ const IconTransparency = () => /* @__PURE__ */ jsxs(
1488
+ "svg",
1489
+ {
1490
+ width: "14",
1491
+ height: "14",
1492
+ viewBox: "0 0 24 24",
1493
+ fill: "none",
1494
+ stroke: "currentColor",
1495
+ strokeWidth: "2",
1496
+ children: [
1497
+ /* @__PURE__ */ jsx("path", { d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" }),
1498
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" })
1499
+ ]
1500
+ }
1501
+ );
1502
+ const IconDockLeft = () => /* @__PURE__ */ jsxs(
1503
+ "svg",
1504
+ {
1505
+ width: "14",
1506
+ height: "14",
1507
+ viewBox: "0 0 24 24",
1508
+ fill: "none",
1509
+ stroke: "currentColor",
1510
+ strokeWidth: "2",
1511
+ children: [
1512
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
1513
+ /* @__PURE__ */ jsx("line", { x1: "9", y1: "3", x2: "9", y2: "21" })
1514
+ ]
1515
+ }
1516
+ );
1517
+ const IconDockBottom = () => /* @__PURE__ */ jsxs(
1518
+ "svg",
1519
+ {
1520
+ width: "14",
1521
+ height: "14",
1522
+ viewBox: "0 0 24 24",
1523
+ fill: "none",
1524
+ stroke: "currentColor",
1525
+ strokeWidth: "2",
1526
+ children: [
1527
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
1528
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "15", x2: "21", y2: "15" })
1529
+ ]
1530
+ }
1531
+ );
1532
+ const IconDockRight = () => /* @__PURE__ */ jsxs(
1533
+ "svg",
1534
+ {
1535
+ width: "14",
1536
+ height: "14",
1537
+ viewBox: "0 0 24 24",
1538
+ fill: "none",
1539
+ stroke: "currentColor",
1540
+ strokeWidth: "2",
1541
+ children: [
1542
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
1543
+ /* @__PURE__ */ jsx("line", { x1: "15", y1: "3", x2: "15", y2: "21" })
1544
+ ]
1545
+ }
1546
+ );
1547
+ const IconCopy = () => /* @__PURE__ */ jsxs(
1548
+ "svg",
1549
+ {
1550
+ width: "12",
1551
+ height: "12",
1552
+ viewBox: "0 0 24 24",
1553
+ fill: "none",
1554
+ stroke: "currentColor",
1555
+ strokeWidth: "2",
1556
+ children: [
1557
+ /* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }),
1558
+ /* @__PURE__ */ jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
1559
+ ]
1560
+ }
1561
+ );
1562
+ const IconClear$1 = () => /* @__PURE__ */ jsx(
1563
+ "svg",
1564
+ {
1565
+ width: "12",
1566
+ height: "12",
1567
+ viewBox: "0 0 24 24",
1568
+ fill: "none",
1569
+ stroke: "currentColor",
1570
+ strokeWidth: "2",
1571
+ children: /* @__PURE__ */ jsx("path", { d: "M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
1572
+ }
1573
+ );
1574
+ const IconActivity = () => /* @__PURE__ */ jsxs(
1575
+ "svg",
1576
+ {
1577
+ width: "12",
1578
+ height: "12",
1579
+ viewBox: "0 0 24 24",
1580
+ fill: "none",
1581
+ stroke: "currentColor",
1582
+ strokeWidth: "2",
1583
+ children: [
1584
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
1585
+ /* @__PURE__ */ jsx("polyline", { points: "12 6 12 12 16 14" })
1586
+ ]
1587
+ }
1588
+ );
1589
+ const IconExpandAll = () => /* @__PURE__ */ jsxs(
1590
+ "svg",
1591
+ {
1592
+ width: "12",
1593
+ height: "12",
1594
+ viewBox: "0 0 24 24",
1595
+ fill: "none",
1596
+ stroke: "currentColor",
1597
+ strokeWidth: "2",
1598
+ children: [
1599
+ /* @__PURE__ */ jsx("polyline", { points: "15 3 21 3 21 9" }),
1600
+ /* @__PURE__ */ jsx("polyline", { points: "9 21 3 21 3 15" }),
1601
+ /* @__PURE__ */ jsx("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
1602
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
1603
+ ]
1604
+ }
1605
+ );
1606
+ const IconCollapseAll = () => /* @__PURE__ */ jsxs(
1607
+ "svg",
1608
+ {
1609
+ width: "12",
1610
+ height: "12",
1611
+ viewBox: "0 0 24 24",
1612
+ fill: "none",
1613
+ stroke: "currentColor",
1614
+ strokeWidth: "2",
1615
+ children: [
1616
+ /* @__PURE__ */ jsx("polyline", { points: "4 14 10 14 10 20" }),
1617
+ /* @__PURE__ */ jsx("polyline", { points: "20 10 14 10 14 4" }),
1618
+ /* @__PURE__ */ jsx("line", { x1: "14", y1: "10", x2: "21", y2: "3" }),
1619
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
1620
+ ]
1621
+ }
1622
+ );
1623
+ const IconReplay = () => /* @__PURE__ */ jsx(
1624
+ "svg",
1625
+ {
1626
+ width: "12",
1627
+ height: "12",
1628
+ viewBox: "0 0 24 24",
1629
+ fill: "none",
1630
+ stroke: "currentColor",
1631
+ strokeWidth: "2",
1632
+ children: /* @__PURE__ */ jsx("polygon", { points: "5 3 19 12 5 21 5 3" })
1633
+ }
1634
+ );
1635
+ const IconEdit = () => /* @__PURE__ */ jsxs(
1636
+ "svg",
1637
+ {
1638
+ width: "12",
1639
+ height: "12",
1640
+ viewBox: "0 0 24 24",
1641
+ fill: "none",
1642
+ stroke: "currentColor",
1643
+ strokeWidth: "2",
1644
+ children: [
1645
+ /* @__PURE__ */ jsx("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }),
1646
+ /* @__PURE__ */ jsx("path", { d: "M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" })
1647
+ ]
1648
+ }
1649
+ );
1650
+ const IconSave = () => /* @__PURE__ */ jsx(
1651
+ "svg",
1652
+ {
1653
+ width: "12",
1654
+ height: "12",
1655
+ viewBox: "0 0 24 24",
1656
+ fill: "none",
1657
+ stroke: "currentColor",
1658
+ strokeWidth: "2",
1659
+ children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" })
1660
+ }
1661
+ );
1662
+ const IconCancel = () => /* @__PURE__ */ jsxs(
1663
+ "svg",
1664
+ {
1665
+ width: "12",
1666
+ height: "12",
1667
+ viewBox: "0 0 24 24",
1668
+ fill: "none",
1669
+ stroke: "currentColor",
1670
+ strokeWidth: "2",
1671
+ children: [
1672
+ /* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1673
+ /* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
1674
+ ]
1675
+ }
1676
+ );
1677
+ const IconEvents = () => /* @__PURE__ */ jsxs(
1678
+ "svg",
1679
+ {
1680
+ width: "12",
1681
+ height: "12",
1682
+ viewBox: "0 0 24 24",
1683
+ fill: "none",
1684
+ stroke: "currentColor",
1685
+ strokeWidth: "2",
1686
+ children: [
1687
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "6", x2: "21", y2: "6" }),
1688
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "12", x2: "21", y2: "12" }),
1689
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "18", x2: "21", y2: "18" }),
1690
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "6", x2: "3.01", y2: "6" }),
1691
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "12", x2: "3.01", y2: "12" }),
1692
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "18", x2: "3.01", y2: "18" })
1693
+ ]
1694
+ }
1695
+ );
1696
+ const IconCompare = () => /* @__PURE__ */ jsxs(
1697
+ "svg",
1698
+ {
1699
+ width: "12",
1700
+ height: "12",
1701
+ viewBox: "0 0 24 24",
1702
+ fill: "none",
1703
+ stroke: "currentColor",
1704
+ strokeWidth: "2",
1705
+ children: [
1706
+ /* @__PURE__ */ jsx("line", { x1: "18", y1: "20", x2: "18", y2: "10" }),
1707
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "20", x2: "12", y2: "4" }),
1708
+ /* @__PURE__ */ jsx("line", { x1: "6", y1: "20", x2: "6", y2: "14" })
1709
+ ]
1710
+ }
1711
+ );
1712
+ const IconRevert = () => /* @__PURE__ */ jsxs(
1713
+ "svg",
1714
+ {
1715
+ width: "12",
1716
+ height: "12",
1717
+ viewBox: "0 0 24 24",
1718
+ fill: "none",
1719
+ stroke: "currentColor",
1720
+ strokeWidth: "2",
1721
+ children: [
1722
+ /* @__PURE__ */ jsx("path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }),
1723
+ /* @__PURE__ */ jsx("path", { d: "M3 3v5h5" })
1724
+ ]
1725
+ }
1726
+ );
1727
+ function ResizeHandle({ position, onResize }) {
1728
+ const [active, setActive] = useState(false);
1729
+ const lastPosRef = useRef({ x: 0, y: 0 });
1730
+ const handleMouseDown = useCallback(
1731
+ (e) => {
1732
+ e.preventDefault();
1733
+ setActive(true);
1734
+ lastPosRef.current = { x: e.clientX, y: e.clientY };
1735
+ document.body.style.userSelect = "none";
1736
+ document.body.style.cursor = position === "bottom" ? "ns-resize" : "ew-resize";
1737
+ const handleMouseMove = (moveEvent) => {
1738
+ const lastPos = lastPosRef.current;
1739
+ if (position === "bottom") {
1740
+ const delta = lastPos.y - moveEvent.clientY;
1741
+ onResize(delta);
1742
+ } else {
1743
+ const delta = moveEvent.clientX - lastPos.x;
1744
+ onResize(delta);
1745
+ }
1746
+ lastPosRef.current = { x: moveEvent.clientX, y: moveEvent.clientY };
1747
+ };
1748
+ const handleMouseUp = () => {
1749
+ setActive(false);
1750
+ document.body.style.userSelect = "";
1751
+ document.body.style.cursor = "";
1752
+ document.removeEventListener("mousemove", handleMouseMove);
1753
+ document.removeEventListener("mouseup", handleMouseUp);
1754
+ };
1755
+ document.addEventListener("mousemove", handleMouseMove);
1756
+ document.addEventListener("mouseup", handleMouseUp);
1757
+ },
1758
+ [position, onResize]
1759
+ );
1760
+ const handleTouchStart = useCallback(
1761
+ (e) => {
1762
+ e.preventDefault();
1763
+ setActive(true);
1764
+ const touch = e.touches[0];
1765
+ lastPosRef.current = { x: touch.clientX, y: touch.clientY };
1766
+ document.body.style.userSelect = "none";
1767
+ const handleTouchMove = (moveEvent) => {
1768
+ const touch2 = moveEvent.touches[0];
1769
+ const lastPos = lastPosRef.current;
1770
+ if (position === "bottom") {
1771
+ const delta = lastPos.y - touch2.clientY;
1772
+ onResize(delta);
1773
+ } else {
1774
+ const delta = touch2.clientX - lastPos.x;
1775
+ onResize(delta);
1776
+ }
1777
+ lastPosRef.current = { x: touch2.clientX, y: touch2.clientY };
1778
+ };
1779
+ const handleTouchEnd = () => {
1780
+ setActive(false);
1781
+ document.body.style.userSelect = "";
1782
+ document.removeEventListener("touchmove", handleTouchMove);
1783
+ document.removeEventListener("touchend", handleTouchEnd);
1784
+ };
1785
+ document.addEventListener("touchmove", handleTouchMove);
1786
+ document.addEventListener("touchend", handleTouchEnd);
1787
+ },
1788
+ [position, onResize]
1789
+ );
1790
+ const isVertical = position === "bottom";
1791
+ const handleClass = isVertical ? "vertical top" : position === "right" ? "horizontal left" : "horizontal right";
1792
+ return /* @__PURE__ */ jsx(
1793
+ "div",
1794
+ {
1795
+ className: `sdt-resize-handle ${handleClass} ${active ? "active" : ""}`,
1796
+ onMouseDown: handleMouseDown,
1797
+ onTouchStart: handleTouchStart
1798
+ }
1799
+ );
1800
+ }
1801
+ const STORAGE_KEY$1 = "storion-devtools-settings";
1802
+ function loadSettings() {
1803
+ try {
1804
+ const stored = localStorage.getItem(STORAGE_KEY$1);
1805
+ if (stored) {
1806
+ return JSON.parse(stored);
1807
+ }
1808
+ } catch {
1809
+ }
1810
+ return {};
1811
+ }
1812
+ function saveSettings(settings) {
1813
+ try {
1814
+ const current = loadSettings();
1815
+ const merged = { ...current, ...settings };
1816
+ localStorage.setItem(STORAGE_KEY$1, JSON.stringify(merged));
1817
+ } catch {
1818
+ }
1819
+ }
1820
+ function clearDevtoolsSettings() {
1821
+ try {
1822
+ localStorage.removeItem(STORAGE_KEY$1);
1823
+ } catch {
1824
+ }
1825
+ }
1826
+ function usePersistentState(key, initialValue, transform) {
1827
+ const [value, setValue] = useState(() => {
1828
+ const stored = loadSettings();
1829
+ if (key in stored) {
1830
+ const storedValue = stored[key];
1831
+ return (transform == null ? void 0 : transform.fromStorage) ? transform.fromStorage(storedValue) : storedValue;
1832
+ }
1833
+ return initialValue;
1834
+ });
1835
+ const isFirstMount = useRef(true);
1836
+ useEffect(() => {
1837
+ if (isFirstMount.current) {
1838
+ isFirstMount.current = false;
1839
+ return;
1840
+ }
1841
+ const toStore = (transform == null ? void 0 : transform.toStorage) ? transform.toStorage(value) : value;
1842
+ saveSettings({ [key]: toStore });
1843
+ }, [value, key, transform]);
1844
+ return [value, setValue];
1845
+ }
1846
+ const IconSearch = () => /* @__PURE__ */ jsxs(
1847
+ "svg",
1848
+ {
1849
+ width: "12",
1850
+ height: "12",
1851
+ viewBox: "0 0 24 24",
1852
+ fill: "none",
1853
+ stroke: "currentColor",
1854
+ strokeWidth: "2",
1855
+ strokeLinecap: "round",
1856
+ strokeLinejoin: "round",
1857
+ children: [
1858
+ /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "8" }),
1859
+ /* @__PURE__ */ jsx("path", { d: "m21 21-4.3-4.3" })
1860
+ ]
1861
+ }
1862
+ );
1863
+ const IconClear = () => /* @__PURE__ */ jsxs(
1864
+ "svg",
1865
+ {
1866
+ width: "12",
1867
+ height: "12",
1868
+ viewBox: "0 0 24 24",
1869
+ fill: "none",
1870
+ stroke: "currentColor",
1871
+ strokeWidth: "2",
1872
+ strokeLinecap: "round",
1873
+ strokeLinejoin: "round",
1874
+ children: [
1875
+ /* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
1876
+ /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
1877
+ ]
1878
+ }
1879
+ );
1880
+ const SearchBar = memo(function SearchBar2({
1881
+ value,
1882
+ onChange,
1883
+ placeholder = "Search...",
1884
+ autoFocus = false
1885
+ }) {
1886
+ const handleClear = useCallback(() => {
1887
+ onChange("");
1888
+ }, [onChange]);
1889
+ return /* @__PURE__ */ jsxs("div", { className: "sdt-search", children: [
1890
+ /* @__PURE__ */ jsx("div", { className: "sdt-search-icon", children: /* @__PURE__ */ jsx(IconSearch, {}) }),
1891
+ /* @__PURE__ */ jsx(
1892
+ "input",
1893
+ {
1894
+ type: "text",
1895
+ className: "sdt-search-input",
1896
+ placeholder,
1897
+ value,
1898
+ onChange: (e) => onChange(e.target.value),
1899
+ autoFocus
1900
+ }
1901
+ ),
1902
+ value && /* @__PURE__ */ jsx(
1903
+ "button",
1904
+ {
1905
+ className: "sdt-search-clear",
1906
+ onClick: handleClear,
1907
+ title: "Clear search",
1908
+ type: "button",
1909
+ children: /* @__PURE__ */ jsx(IconClear, {})
1910
+ }
1911
+ )
1912
+ ] });
1913
+ });
1914
+ const FilterBar = memo(function FilterBar2({
1915
+ filters,
1916
+ onToggle
1917
+ }) {
1918
+ if (filters.length === 0) return null;
1919
+ return /* @__PURE__ */ jsx("div", { className: "sdt-filter-bar", children: filters.map((filter) => /* @__PURE__ */ jsxs(
1920
+ "button",
1921
+ {
1922
+ className: `sdt-filter-item ${filter.active ? "active" : ""}`,
1923
+ onClick: () => onToggle(filter.key),
1924
+ children: [
1925
+ /* @__PURE__ */ jsx("span", { children: filter.label }),
1926
+ filter.count !== void 0 && /* @__PURE__ */ jsx("span", { className: "count", children: filter.count })
1927
+ ]
1928
+ },
1929
+ filter.key
1930
+ )) });
1931
+ });
1932
+ const ActionBar = memo(function ActionBar2({ actions }) {
1933
+ if (actions.length === 0) return null;
1934
+ return /* @__PURE__ */ jsx("div", { className: "sdt-action-bar", children: actions.map((action) => /* @__PURE__ */ jsxs(
1935
+ "button",
1936
+ {
1937
+ className: "sdt-action-btn",
1938
+ onClick: action.onClick,
1939
+ disabled: action.disabled,
1940
+ title: action.label,
1941
+ children: [
1942
+ action.icon,
1943
+ /* @__PURE__ */ jsx("span", { children: action.label })
1944
+ ]
1945
+ },
1946
+ action.key
1947
+ )) });
1948
+ });
1949
+ const MainContent = memo(function MainContent2({
1950
+ children,
1951
+ isEmpty,
1952
+ emptyIcon = "📦",
1953
+ emptyMessage = "No items found",
1954
+ emptyHint
1955
+ }) {
1956
+ if (isEmpty) {
1957
+ return /* @__PURE__ */ jsx("div", { className: "sdt-main-content", children: /* @__PURE__ */ jsxs("div", { className: "sdt-empty", children: [
1958
+ /* @__PURE__ */ jsx("div", { className: "sdt-empty-icon", children: emptyIcon }),
1959
+ /* @__PURE__ */ jsx("div", { className: "sdt-empty-message", children: emptyMessage }),
1960
+ emptyHint && /* @__PURE__ */ jsx("div", { className: "sdt-empty-hint", children: emptyHint })
1961
+ ] }) });
1962
+ }
1963
+ return /* @__PURE__ */ jsx("div", { className: "sdt-main-content", children });
1964
+ });
1965
+ const TabContent = memo(function TabContent2({
1966
+ searchBar,
1967
+ filterBar,
1968
+ actionBar,
1969
+ children
1970
+ }) {
1971
+ return /* @__PURE__ */ jsxs("div", { className: "sdt-tab-content", children: [
1972
+ searchBar,
1973
+ filterBar,
1974
+ actionBar,
1975
+ children
1976
+ ] });
1977
+ });
1978
+ function formatTime(timestamp) {
1979
+ const date = new Date(timestamp);
1980
+ const time = date.toLocaleTimeString([], {
1981
+ hour: "2-digit",
1982
+ minute: "2-digit",
1983
+ second: "2-digit",
1984
+ hour12: false
1985
+ });
1986
+ const ms = String(date.getMilliseconds()).padStart(3, "0");
1987
+ return `${time}.${ms}`;
1988
+ }
1989
+ function formatDateTime(timestamp) {
1990
+ return formatTime(timestamp);
1991
+ }
1992
+ function formatValue(value) {
1993
+ if (value === null) return { text: "null", type: "null" };
1994
+ if (value === void 0) return { text: "undefined", type: "null" };
1995
+ if (typeof value === "string") return { text: `"${value}"`, type: "string" };
1996
+ if (typeof value === "number") return { text: String(value), type: "number" };
1997
+ if (typeof value === "boolean")
1998
+ return { text: String(value), type: "boolean" };
1999
+ if (Array.isArray(value))
2000
+ return { text: `Array(${value.length})`, type: "object" };
2001
+ if (typeof value === "object") {
2002
+ const keys = Object.keys(value);
2003
+ return {
2004
+ text: `{${keys.slice(0, 3).join(", ")}${keys.length > 3 ? "..." : ""}}`,
2005
+ type: "object"
2006
+ };
2007
+ }
2008
+ return { text: String(value), type: "unknown" };
2009
+ }
2010
+ const DEFAULT_HISTORY_COUNT = 5;
2011
+ const MAX_HISTORY_COUNT = 50;
2012
+ const StoreEntry = memo(function StoreEntry2({
2013
+ entry,
2014
+ onRevert,
2015
+ onShowEvents,
2016
+ onCompare,
2017
+ onStateEdit,
2018
+ flash = false,
2019
+ forceExpanded = null
2020
+ }) {
2021
+ const [expanded, setExpanded] = useState(true);
2022
+ const [historyExpanded, setHistoryExpanded] = useState(false);
2023
+ const [isEditing, setIsEditing] = useState(false);
2024
+ const [editValue, setEditValue] = useState("");
2025
+ useEffect(() => {
2026
+ if (forceExpanded !== null) {
2027
+ setExpanded(forceExpanded);
2028
+ }
2029
+ }, [forceExpanded]);
2030
+ const toggleExpand = useCallback(() => {
2031
+ setExpanded((prev) => !prev);
2032
+ }, []);
2033
+ const totalHistory = entry.history.length;
2034
+ const hasMoreHistory = totalHistory > DEFAULT_HISTORY_COUNT;
2035
+ const displayCount = historyExpanded ? Math.min(totalHistory, MAX_HISTORY_COUNT) : Math.min(totalHistory, DEFAULT_HISTORY_COUNT);
2036
+ const initialSnapshot = entry.history[0];
2037
+ const recentEntries = entry.history.slice(1);
2038
+ const recentDisplayCount = Math.max(0, displayCount - 1);
2039
+ const displayRecentEntries = recentEntries.slice(-recentDisplayCount).reverse();
2040
+ const displayHistory = totalHistory > 0 ? [...displayRecentEntries, initialSnapshot] : [];
2041
+ const getChangedProps = useCallback(
2042
+ (snapshotIndex) => {
2043
+ const snapshot = entry.history[snapshotIndex];
2044
+ if (!snapshot) return [];
2045
+ if (snapshotIndex === 0) {
2046
+ return Object.keys(snapshot.state);
2047
+ }
2048
+ const prevSnapshot = entry.history[snapshotIndex - 1];
2049
+ if (!prevSnapshot) return Object.keys(snapshot.state);
2050
+ const changedKeys = [];
2051
+ const allKeys = /* @__PURE__ */ new Set([
2052
+ ...Object.keys(snapshot.state),
2053
+ ...Object.keys(prevSnapshot.state)
2054
+ ]);
2055
+ for (const key of allKeys) {
2056
+ const curr = snapshot.state[key];
2057
+ const prev = prevSnapshot.state[key];
2058
+ if (curr !== prev) {
2059
+ changedKeys.push(key);
2060
+ }
2061
+ }
2062
+ return changedKeys;
2063
+ },
2064
+ [entry.history]
2065
+ );
2066
+ const getSnapshotIndex = useCallback(
2067
+ (snapshotId) => {
2068
+ return entry.history.findIndex((s) => s.id === snapshotId);
2069
+ },
2070
+ [entry.history]
2071
+ );
2072
+ const metaEntries = entry.meta ? Object.entries(entry.meta) : [];
2073
+ const handleStartEdit = useCallback(() => {
2074
+ setEditValue(JSON.stringify(entry.state, null, 2));
2075
+ setIsEditing(true);
2076
+ }, [entry.state]);
2077
+ const handleCancelEdit = useCallback(() => {
2078
+ setIsEditing(false);
2079
+ setEditValue("");
2080
+ }, []);
2081
+ const handleSaveEdit = useCallback(() => {
2082
+ try {
2083
+ const newState = JSON.parse(editValue);
2084
+ onStateEdit == null ? void 0 : onStateEdit(entry.id, newState);
2085
+ setIsEditing(false);
2086
+ setEditValue("");
2087
+ } catch {
2088
+ alert("Invalid JSON. Please fix the syntax and try again.");
2089
+ }
2090
+ }, [editValue, entry.id, onStateEdit]);
2091
+ const handleShowEvents = useCallback(
2092
+ (e) => {
2093
+ e.stopPropagation();
2094
+ onShowEvents == null ? void 0 : onShowEvents(entry.id);
2095
+ },
2096
+ [entry.id, onShowEvents]
2097
+ );
2098
+ return /* @__PURE__ */ jsxs("div", { className: "sdt-store-entry", children: [
2099
+ /* @__PURE__ */ jsxs(
2100
+ "div",
2101
+ {
2102
+ className: `sdt-store-header ${flash ? "flash" : ""}`,
2103
+ onClick: toggleExpand,
2104
+ children: [
2105
+ /* @__PURE__ */ jsx("span", { className: "sdt-store-icon", children: /* @__PURE__ */ jsx(IconStore, {}) }),
2106
+ /* @__PURE__ */ jsx("div", { className: "sdt-store-name", children: entry.id }),
2107
+ /* @__PURE__ */ jsx("div", { className: "sdt-store-actions", children: /* @__PURE__ */ jsx(
2108
+ "button",
2109
+ {
2110
+ className: "sdt-store-action-btn",
2111
+ onClick: handleShowEvents,
2112
+ title: "Show events for this store",
2113
+ children: /* @__PURE__ */ jsx(IconEvents, {})
2114
+ }
2115
+ ) }),
2116
+ /* @__PURE__ */ jsx(
2117
+ "button",
2118
+ {
2119
+ className: "sdt-expand-btn",
2120
+ onClick: (e) => {
2121
+ e.stopPropagation();
2122
+ toggleExpand();
2123
+ },
2124
+ children: /* @__PURE__ */ jsx(IconChevron, { open: expanded })
2125
+ }
2126
+ )
2127
+ ]
2128
+ }
2129
+ ),
2130
+ expanded && /* @__PURE__ */ jsxs("div", { className: "sdt-store-details", children: [
2131
+ /* @__PURE__ */ jsxs("div", { className: "sdt-section-header", children: [
2132
+ /* @__PURE__ */ jsx("div", { className: "sdt-section-title", children: "State" }),
2133
+ /* @__PURE__ */ jsx("div", { className: "sdt-section-actions", children: !isEditing ? /* @__PURE__ */ jsx(
2134
+ "button",
2135
+ {
2136
+ className: "sdt-edit-btn",
2137
+ onClick: handleStartEdit,
2138
+ title: "Edit state",
2139
+ children: /* @__PURE__ */ jsx(IconEdit, {})
2140
+ }
2141
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
2142
+ /* @__PURE__ */ jsx(
2143
+ "button",
2144
+ {
2145
+ className: "sdt-save-btn",
2146
+ onClick: handleSaveEdit,
2147
+ title: "Save changes",
2148
+ children: /* @__PURE__ */ jsx(IconSave, {})
2149
+ }
2150
+ ),
2151
+ /* @__PURE__ */ jsx(
2152
+ "button",
2153
+ {
2154
+ className: "sdt-cancel-btn",
2155
+ onClick: handleCancelEdit,
2156
+ title: "Cancel editing",
2157
+ children: /* @__PURE__ */ jsx(IconCancel, {})
2158
+ }
2159
+ )
2160
+ ] }) })
2161
+ ] }),
2162
+ /* @__PURE__ */ jsx(
2163
+ "textarea",
2164
+ {
2165
+ className: `sdt-state-json ${isEditing ? "editing" : ""}`,
2166
+ value: isEditing ? editValue : JSON.stringify(entry.state, null, 2),
2167
+ onChange: (e) => setEditValue(e.target.value),
2168
+ readOnly: !isEditing,
2169
+ spellCheck: false
2170
+ }
2171
+ ),
2172
+ totalHistory > 0 && /* @__PURE__ */ jsxs("div", { className: "sdt-history", children: [
2173
+ /* @__PURE__ */ jsxs("div", { className: "sdt-section-title", children: [
2174
+ "History (",
2175
+ totalHistory,
2176
+ ")"
2177
+ ] }),
2178
+ displayHistory.map((snapshot) => {
2179
+ const historyIndex = getSnapshotIndex(snapshot.id);
2180
+ const isInitial = historyIndex === 0;
2181
+ const changedProps = getChangedProps(historyIndex);
2182
+ return /* @__PURE__ */ jsxs(
2183
+ "div",
2184
+ {
2185
+ className: `sdt-history-item ${isInitial ? "initial" : ""}`,
2186
+ children: [
2187
+ /* @__PURE__ */ jsx("span", { className: "sdt-history-time", children: isInitial ? "Initial" : formatTime(snapshot.timestamp) }),
2188
+ /* @__PURE__ */ jsx("span", { className: "sdt-history-props", children: changedProps.join(", ") }),
2189
+ /* @__PURE__ */ jsxs("div", { className: "sdt-history-actions", children: [
2190
+ /* @__PURE__ */ jsx(
2191
+ "button",
2192
+ {
2193
+ className: "sdt-history-action-btn",
2194
+ onClick: (e) => {
2195
+ e.stopPropagation();
2196
+ onCompare == null ? void 0 : onCompare(entry.id, snapshot);
2197
+ },
2198
+ title: "Compare with current state",
2199
+ children: /* @__PURE__ */ jsx(IconCompare, {})
2200
+ }
2201
+ ),
2202
+ /* @__PURE__ */ jsx(
2203
+ "button",
2204
+ {
2205
+ className: "sdt-history-action-btn",
2206
+ onClick: (e) => {
2207
+ e.stopPropagation();
2208
+ if (confirm("Revert state to this snapshot?")) {
2209
+ onRevert(snapshot.id);
2210
+ }
2211
+ },
2212
+ title: "Revert to this state",
2213
+ children: /* @__PURE__ */ jsx(IconRevert, {})
2214
+ }
2215
+ )
2216
+ ] })
2217
+ ]
2218
+ },
2219
+ snapshot.id
2220
+ );
2221
+ }),
2222
+ hasMoreHistory && !historyExpanded && /* @__PURE__ */ jsx("div", { className: "sdt-history-expand", children: /* @__PURE__ */ jsxs(
2223
+ "button",
2224
+ {
2225
+ className: "sdt-history-expand-btn",
2226
+ onClick: () => setHistoryExpanded(true),
2227
+ children: [
2228
+ "Show",
2229
+ " ",
2230
+ Math.min(totalHistory, MAX_HISTORY_COUNT) - DEFAULT_HISTORY_COUNT,
2231
+ " ",
2232
+ "more"
2233
+ ]
2234
+ }
2235
+ ) }),
2236
+ historyExpanded && totalHistory > DEFAULT_HISTORY_COUNT && /* @__PURE__ */ jsx("div", { className: "sdt-history-expand", children: /* @__PURE__ */ jsx(
2237
+ "button",
2238
+ {
2239
+ className: "sdt-history-expand-btn",
2240
+ onClick: () => setHistoryExpanded(false),
2241
+ children: "Show less"
2242
+ }
2243
+ ) })
2244
+ ] }),
2245
+ /* @__PURE__ */ jsxs("div", { className: "sdt-metadata", children: [
2246
+ /* @__PURE__ */ jsx("div", { className: "sdt-section-title", children: "Metadata" }),
2247
+ /* @__PURE__ */ jsxs("div", { className: "sdt-metadata-row", children: [
2248
+ /* @__PURE__ */ jsx("span", { className: "sdt-metadata-label", children: "Created:" }),
2249
+ /* @__PURE__ */ jsx("span", { className: "sdt-metadata-value", children: formatDateTime(entry.createdAt) })
2250
+ ] }),
2251
+ metaEntries.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
2252
+ /* @__PURE__ */ jsx(
2253
+ "div",
2254
+ {
2255
+ className: "sdt-section-title",
2256
+ style: { marginTop: 8, marginBottom: 4 },
2257
+ children: "spec.meta"
2258
+ }
2259
+ ),
2260
+ metaEntries.map(([key, value]) => {
2261
+ const formatted = formatValue(value);
2262
+ return /* @__PURE__ */ jsxs("div", { className: "sdt-metadata-row", children: [
2263
+ /* @__PURE__ */ jsxs("span", { className: "sdt-metadata-label", children: [
2264
+ key,
2265
+ ":"
2266
+ ] }),
2267
+ /* @__PURE__ */ jsx(
2268
+ "span",
2269
+ {
2270
+ className: `sdt-metadata-value sdt-state-value ${formatted.type}`,
2271
+ children: formatted.text
2272
+ }
2273
+ )
2274
+ ] }, key);
2275
+ })
2276
+ ] })
2277
+ ] })
2278
+ ] })
2279
+ ] });
2280
+ });
2281
+ function CompareModal({
2282
+ storeId,
2283
+ snapshot,
2284
+ currentState,
2285
+ onClose
2286
+ }) {
2287
+ const diff = useMemo(() => {
2288
+ const allKeys = /* @__PURE__ */ new Set([
2289
+ ...Object.keys(snapshot.state),
2290
+ ...Object.keys(currentState)
2291
+ ]);
2292
+ const changes = [];
2293
+ for (const key of allKeys) {
2294
+ const oldValue = snapshot.state[key];
2295
+ const newValue = currentState[key];
2296
+ const oldExists = key in snapshot.state;
2297
+ const newExists = key in currentState;
2298
+ if (!oldExists && newExists) {
2299
+ changes.push({ key, type: "added", oldValue: void 0, newValue });
2300
+ } else if (oldExists && !newExists) {
2301
+ changes.push({ key, type: "removed", oldValue, newValue: void 0 });
2302
+ } else if (JSON.stringify(oldValue) !== JSON.stringify(newValue)) {
2303
+ changes.push({ key, type: "changed", oldValue, newValue });
2304
+ } else {
2305
+ changes.push({ key, type: "unchanged", oldValue, newValue });
2306
+ }
2307
+ }
2308
+ return changes.sort((a, b) => {
2309
+ const order = { changed: 0, added: 1, removed: 2, unchanged: 3 };
2310
+ return order[a.type] - order[b.type];
2311
+ });
2312
+ }, [snapshot.state, currentState]);
2313
+ const counts = useMemo(() => {
2314
+ return {
2315
+ changed: diff.filter((d) => d.type === "changed").length,
2316
+ added: diff.filter((d) => d.type === "added").length,
2317
+ removed: diff.filter((d) => d.type === "removed").length,
2318
+ unchanged: diff.filter((d) => d.type === "unchanged").length
2319
+ };
2320
+ }, [diff]);
2321
+ const formatJsonValue = (value) => {
2322
+ if (value === void 0) return "undefined";
2323
+ return JSON.stringify(value, null, 2);
2324
+ };
2325
+ const handleOverlayClick = (e) => {
2326
+ if (e.target === e.currentTarget) {
2327
+ onClose();
2328
+ }
2329
+ };
2330
+ const handleOverlayTouchEnd = (e) => {
2331
+ if (e.target === e.currentTarget) {
2332
+ onClose();
2333
+ }
2334
+ };
2335
+ return /* @__PURE__ */ jsx(
2336
+ "div",
2337
+ {
2338
+ className: "sdt-modal-overlay",
2339
+ onClick: handleOverlayClick,
2340
+ onTouchEnd: handleOverlayTouchEnd,
2341
+ children: /* @__PURE__ */ jsxs("div", { className: "sdt-modal", onClick: (e) => e.stopPropagation(), children: [
2342
+ /* @__PURE__ */ jsxs("div", { className: "sdt-modal-header", children: [
2343
+ /* @__PURE__ */ jsxs("span", { className: "sdt-modal-title", children: [
2344
+ "Compare: ",
2345
+ storeId,
2346
+ " @ ",
2347
+ formatTime(snapshot.timestamp),
2348
+ " vs Current"
2349
+ ] }),
2350
+ /* @__PURE__ */ jsx("button", { className: "sdt-modal-close", onClick: onClose, children: /* @__PURE__ */ jsx(IconCancel, {}) })
2351
+ ] }),
2352
+ /* @__PURE__ */ jsxs("div", { className: "sdt-modal-body", children: [
2353
+ /* @__PURE__ */ jsxs("div", { className: "sdt-compare-sticky-header", children: [
2354
+ /* @__PURE__ */ jsxs("div", { className: "sdt-compare-legend", children: [
2355
+ /* @__PURE__ */ jsxs("span", { className: "sdt-legend-item changed", children: [
2356
+ "● Changed (",
2357
+ counts.changed,
2358
+ ")"
2359
+ ] }),
2360
+ /* @__PURE__ */ jsxs("span", { className: "sdt-legend-item added", children: [
2361
+ "● Added (",
2362
+ counts.added,
2363
+ ")"
2364
+ ] }),
2365
+ /* @__PURE__ */ jsxs("span", { className: "sdt-legend-item removed", children: [
2366
+ "● Removed (",
2367
+ counts.removed,
2368
+ ")"
2369
+ ] }),
2370
+ /* @__PURE__ */ jsxs("span", { className: "sdt-legend-item unchanged", children: [
2371
+ "● Unchanged (",
2372
+ counts.unchanged,
2373
+ ")"
2374
+ ] })
2375
+ ] }),
2376
+ /* @__PURE__ */ jsxs("div", { className: "sdt-compare-columns-header", children: [
2377
+ /* @__PURE__ */ jsx("div", { className: "sdt-compare-col-old", children: "Snapshot" }),
2378
+ /* @__PURE__ */ jsx("div", { className: "sdt-compare-col-new", children: "Current" })
2379
+ ] })
2380
+ ] }),
2381
+ /* @__PURE__ */ jsx("div", { className: "sdt-compare-content", children: diff.map(({ key, type, oldValue, newValue }) => /* @__PURE__ */ jsxs("div", { className: `sdt-compare-item ${type}`, children: [
2382
+ /* @__PURE__ */ jsx("div", { className: "sdt-compare-prop-name", children: key }),
2383
+ /* @__PURE__ */ jsxs("div", { className: "sdt-compare-values", children: [
2384
+ /* @__PURE__ */ jsx("div", { className: "sdt-compare-old", children: /* @__PURE__ */ jsx("pre", { children: formatJsonValue(oldValue) }) }),
2385
+ /* @__PURE__ */ jsx("div", { className: "sdt-compare-new", children: /* @__PURE__ */ jsx("pre", { children: formatJsonValue(newValue) }) })
2386
+ ] })
2387
+ ] }, key)) })
2388
+ ] })
2389
+ ] })
2390
+ }
2391
+ );
2392
+ }
2393
+ function StoresTab({
2394
+ controller,
2395
+ stores,
2396
+ searchQuery,
2397
+ onSearchQueryChange,
2398
+ onNavigateToEvents
2399
+ }) {
2400
+ var _a;
2401
+ const [sortByActivity, setSortByActivity] = usePersistentState(
2402
+ "sortByActivity",
2403
+ true
2404
+ );
2405
+ const storeVersionsRef = useRef(/* @__PURE__ */ new Map());
2406
+ const [flashingStores, setFlashingStores] = useState(/* @__PURE__ */ new Set());
2407
+ const [forceExpanded, setForceExpanded] = useState(null);
2408
+ const [compareData, setCompareData] = useState(null);
2409
+ useEffect(() => {
2410
+ const newFlashing = /* @__PURE__ */ new Set();
2411
+ for (const store of stores) {
2412
+ const prevVersion = storeVersionsRef.current.get(store.id) ?? 0;
2413
+ const newVersion = store.history.length;
2414
+ if (prevVersion > 0 && newVersion > prevVersion) {
2415
+ newFlashing.add(store.id);
2416
+ }
2417
+ storeVersionsRef.current.set(store.id, newVersion);
2418
+ }
2419
+ if (newFlashing.size > 0) {
2420
+ setFlashingStores(newFlashing);
2421
+ const timer = setTimeout(() => setFlashingStores(/* @__PURE__ */ new Set()), 500);
2422
+ return () => clearTimeout(timer);
2423
+ }
2424
+ }, [stores]);
2425
+ useEffect(() => {
2426
+ if (forceExpanded !== null) {
2427
+ const timer = setTimeout(() => setForceExpanded(null), 50);
2428
+ return () => clearTimeout(timer);
2429
+ }
2430
+ }, [forceExpanded]);
2431
+ const filteredStores = useMemo(() => {
2432
+ let result = stores;
2433
+ if (searchQuery) {
2434
+ const query = searchQuery.toLowerCase();
2435
+ result = result.filter(
2436
+ (s) => s.name.toLowerCase().includes(query) || s.id.toLowerCase().includes(query)
2437
+ );
2438
+ }
2439
+ if (sortByActivity) {
2440
+ result = [...result].sort((a, b) => {
2441
+ const aLastActivity = a.history.length > 0 ? a.history[a.history.length - 1].timestamp : a.createdAt;
2442
+ const bLastActivity = b.history.length > 0 ? b.history[b.history.length - 1].timestamp : b.createdAt;
2443
+ return bLastActivity - aLastActivity;
2444
+ });
2445
+ }
2446
+ return result;
2447
+ }, [stores, searchQuery, sortByActivity]);
2448
+ const handleRevert = useCallback(
2449
+ (storeId, snapshotId) => {
2450
+ controller.revertToSnapshot(storeId, snapshotId);
2451
+ },
2452
+ [controller]
2453
+ );
2454
+ const toggleSortByActivity = useCallback(() => {
2455
+ setSortByActivity((prev) => !prev);
2456
+ }, []);
2457
+ const handleShowEvents = useCallback(
2458
+ (storeId) => {
2459
+ onNavigateToEvents(storeId);
2460
+ },
2461
+ [onNavigateToEvents]
2462
+ );
2463
+ const handleCompare = useCallback(
2464
+ (storeId, snapshot) => {
2465
+ setCompareData({ storeId, snapshot });
2466
+ },
2467
+ []
2468
+ );
2469
+ const handleCloseCompare = useCallback(() => {
2470
+ setCompareData(null);
2471
+ }, []);
2472
+ const handleStateEdit = useCallback(
2473
+ (storeId, newState) => {
2474
+ const store = controller.getStore(storeId);
2475
+ if (store && store.instance) {
2476
+ const actions = store.instance.actions;
2477
+ if (typeof actions.__revertState === "function") {
2478
+ actions.__revertState(newState);
2479
+ }
2480
+ }
2481
+ },
2482
+ [controller]
2483
+ );
2484
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2485
+ /* @__PURE__ */ jsx(
2486
+ TabContent,
2487
+ {
2488
+ searchBar: /* @__PURE__ */ jsx(
2489
+ SearchBar,
2490
+ {
2491
+ value: searchQuery,
2492
+ onChange: onSearchQueryChange,
2493
+ placeholder: "Search stores..."
2494
+ }
2495
+ ),
2496
+ actionBar: /* @__PURE__ */ jsxs("div", { className: "sdt-stores-actions", children: [
2497
+ /* @__PURE__ */ jsxs(
2498
+ "button",
2499
+ {
2500
+ className: `sdt-btn sdt-btn-toggle ${sortByActivity ? "active" : ""}`,
2501
+ onClick: toggleSortByActivity,
2502
+ title: sortByActivity ? "Showing recent activity first" : "Show recent activity first",
2503
+ children: [
2504
+ /* @__PURE__ */ jsx(IconActivity, {}),
2505
+ /* @__PURE__ */ jsx("span", { children: "Recent" })
2506
+ ]
2507
+ }
2508
+ ),
2509
+ /* @__PURE__ */ jsx(
2510
+ "button",
2511
+ {
2512
+ className: "sdt-btn sdt-btn-ghost",
2513
+ onClick: () => setForceExpanded(true),
2514
+ title: "Expand all stores",
2515
+ children: /* @__PURE__ */ jsx(IconExpandAll, {})
2516
+ }
2517
+ ),
2518
+ /* @__PURE__ */ jsx(
2519
+ "button",
2520
+ {
2521
+ className: "sdt-btn sdt-btn-ghost",
2522
+ onClick: () => setForceExpanded(false),
2523
+ title: "Collapse all stores",
2524
+ children: /* @__PURE__ */ jsx(IconCollapseAll, {})
2525
+ }
2526
+ )
2527
+ ] }),
2528
+ children: /* @__PURE__ */ jsx(
2529
+ MainContent,
2530
+ {
2531
+ isEmpty: filteredStores.length === 0,
2532
+ emptyIcon: "📦",
2533
+ emptyMessage: "No stores found",
2534
+ emptyHint: "Add devtoolsMiddleware to your container",
2535
+ children: filteredStores.map((entry) => /* @__PURE__ */ jsx(
2536
+ StoreEntry,
2537
+ {
2538
+ entry,
2539
+ onRevert: (snapshotId) => handleRevert(entry.id, snapshotId),
2540
+ onShowEvents: handleShowEvents,
2541
+ onCompare: handleCompare,
2542
+ onStateEdit: handleStateEdit,
2543
+ flash: flashingStores.has(entry.id),
2544
+ forceExpanded
2545
+ },
2546
+ entry.id
2547
+ ))
2548
+ }
2549
+ )
2550
+ }
2551
+ ),
2552
+ compareData && /* @__PURE__ */ jsx(
2553
+ CompareModal,
2554
+ {
2555
+ storeId: compareData.storeId,
2556
+ snapshot: compareData.snapshot,
2557
+ currentState: ((_a = stores.find((s) => s.id === compareData.storeId)) == null ? void 0 : _a.state) ?? {},
2558
+ onClose: handleCloseCompare
2559
+ }
2560
+ )
2561
+ ] });
2562
+ }
2563
+ const EVENT_TYPE_LABELS = {
2564
+ change: { label: "CHG", color: "#60a5fa" },
2565
+ // blue
2566
+ create: { label: "NEW", color: "#4ade80" },
2567
+ // green
2568
+ dispose: { label: "DEL", color: "#f87171" },
2569
+ // red
2570
+ dispatch: { label: "ACT", color: "#a78bfa" },
2571
+ // purple
2572
+ error: { label: "ERR", color: "#f87171" }
2573
+ // red
2574
+ };
2575
+ const ALL_EVENT_TYPES = [
2576
+ "change",
2577
+ "create",
2578
+ "dispose",
2579
+ "dispatch",
2580
+ "error"
2581
+ ];
2582
+ const EventEntry = memo(function EventEntry2({
2583
+ event,
2584
+ onTargetClick,
2585
+ onReplay
2586
+ }) {
2587
+ const typeInfo = EVENT_TYPE_LABELS[event.type];
2588
+ const isStoreTarget = event.target !== "window";
2589
+ const isDispatch = event.type === "dispatch";
2590
+ const handleCopy = useCallback(() => {
2591
+ const copyData = {
2592
+ type: event.type,
2593
+ target: event.target,
2594
+ timestamp: new Date(event.timestamp).toISOString(),
2595
+ extra: event.extra,
2596
+ data: event.data
2597
+ };
2598
+ navigator.clipboard.writeText(JSON.stringify(copyData, null, 2));
2599
+ }, [event]);
2600
+ const handleTargetClick = useCallback(() => {
2601
+ if (isStoreTarget && onTargetClick) {
2602
+ onTargetClick(event.target);
2603
+ }
2604
+ }, [event.target, isStoreTarget, onTargetClick]);
2605
+ const handleReplay = useCallback(() => {
2606
+ onReplay == null ? void 0 : onReplay(event);
2607
+ }, [event, onReplay]);
2608
+ return /* @__PURE__ */ jsxs("div", { className: "sdt-event-entry", children: [
2609
+ /* @__PURE__ */ jsx("span", { className: "sdt-event-time", children: formatTime(event.timestamp) }),
2610
+ /* @__PURE__ */ jsx(
2611
+ "span",
2612
+ {
2613
+ className: "sdt-event-type",
2614
+ style: {
2615
+ backgroundColor: typeInfo.color + "20",
2616
+ color: typeInfo.color
2617
+ },
2618
+ children: typeInfo.label
2619
+ }
2620
+ ),
2621
+ /* @__PURE__ */ jsx(
2622
+ "span",
2623
+ {
2624
+ className: `sdt-event-target ${isStoreTarget ? "clickable" : ""}`,
2625
+ onClick: handleTargetClick,
2626
+ title: isStoreTarget ? "Click to view store" : void 0,
2627
+ children: event.target
2628
+ }
2629
+ ),
2630
+ event.extra && /* @__PURE__ */ jsx("span", { className: "sdt-event-extra", title: event.extra, children: event.extra }),
2631
+ /* @__PURE__ */ jsxs("div", { className: "sdt-event-actions", children: [
2632
+ isDispatch && /* @__PURE__ */ jsx(
2633
+ "button",
2634
+ {
2635
+ className: "sdt-event-replay",
2636
+ onClick: handleReplay,
2637
+ title: "Replay this action",
2638
+ children: /* @__PURE__ */ jsx(IconReplay, {})
2639
+ }
2640
+ ),
2641
+ /* @__PURE__ */ jsx(
2642
+ "button",
2643
+ {
2644
+ className: "sdt-event-copy",
2645
+ onClick: handleCopy,
2646
+ title: "Copy event data",
2647
+ children: /* @__PURE__ */ jsx(IconCopy, {})
2648
+ }
2649
+ )
2650
+ ] })
2651
+ ] });
2652
+ });
2653
+ function EventFilterBar({
2654
+ activeFilters,
2655
+ onFilterChange,
2656
+ onClear
2657
+ }) {
2658
+ const isAllSelected = activeFilters === null;
2659
+ const handleAllClick = useCallback(() => {
2660
+ onFilterChange(null);
2661
+ }, [onFilterChange]);
2662
+ const handleTypeClick = useCallback(
2663
+ (type) => {
2664
+ if (isAllSelected) {
2665
+ onFilterChange(/* @__PURE__ */ new Set([type]));
2666
+ } else {
2667
+ const next = new Set(activeFilters);
2668
+ if (next.has(type)) {
2669
+ next.delete(type);
2670
+ if (next.size === 0) {
2671
+ onFilterChange(null);
2672
+ } else {
2673
+ onFilterChange(next);
2674
+ }
2675
+ } else {
2676
+ next.add(type);
2677
+ onFilterChange(next);
2678
+ }
2679
+ }
2680
+ },
2681
+ [activeFilters, isAllSelected, onFilterChange]
2682
+ );
2683
+ return /* @__PURE__ */ jsxs("div", { className: "sdt-event-filters", children: [
2684
+ /* @__PURE__ */ jsxs("div", { className: "sdt-event-filter-chips", children: [
2685
+ /* @__PURE__ */ jsx(
2686
+ "button",
2687
+ {
2688
+ className: `sdt-event-filter-chip ${isAllSelected ? "active" : ""}`,
2689
+ onClick: handleAllClick,
2690
+ style: {
2691
+ borderColor: isAllSelected ? "#a1a1aa" : void 0,
2692
+ backgroundColor: isAllSelected ? "#a1a1aa20" : void 0
2693
+ },
2694
+ children: "All"
2695
+ }
2696
+ ),
2697
+ ALL_EVENT_TYPES.map((type) => {
2698
+ const info = EVENT_TYPE_LABELS[type];
2699
+ const isActive = !isAllSelected && activeFilters.has(type);
2700
+ return /* @__PURE__ */ jsx(
2701
+ "button",
2702
+ {
2703
+ className: `sdt-event-filter-chip ${isActive ? "active" : ""}`,
2704
+ onClick: () => handleTypeClick(type),
2705
+ style: {
2706
+ borderColor: isActive ? info.color : void 0,
2707
+ backgroundColor: isActive ? info.color + "20" : void 0
2708
+ },
2709
+ children: info.label
2710
+ },
2711
+ type
2712
+ );
2713
+ })
2714
+ ] }),
2715
+ /* @__PURE__ */ jsx(
2716
+ "button",
2717
+ {
2718
+ className: "sdt-event-clear-btn",
2719
+ onClick: onClear,
2720
+ title: "Clear all events",
2721
+ children: /* @__PURE__ */ jsx(IconClear$1, {})
2722
+ }
2723
+ )
2724
+ ] });
2725
+ }
2726
+ function EventsTab({
2727
+ controller,
2728
+ events,
2729
+ searchQuery,
2730
+ onSearchQueryChange,
2731
+ filters,
2732
+ onFiltersChange,
2733
+ onNavigateToStore
2734
+ }) {
2735
+ const filteredEvents = useMemo(() => {
2736
+ let filtered = events;
2737
+ if (filters !== null) {
2738
+ filtered = filtered.filter((e) => filters.has(e.type));
2739
+ }
2740
+ if (searchQuery) {
2741
+ const query = searchQuery.toLowerCase();
2742
+ filtered = filtered.filter(
2743
+ (e) => {
2744
+ var _a;
2745
+ return e.target.toLowerCase().includes(query) || e.type.toLowerCase().includes(query) || ((_a = e.extra) == null ? void 0 : _a.toLowerCase().includes(query));
2746
+ }
2747
+ );
2748
+ }
2749
+ return [...filtered].reverse();
2750
+ }, [events, searchQuery, filters]);
2751
+ const handleFilterChange = useCallback(
2752
+ (newFilters) => {
2753
+ onFiltersChange(newFilters);
2754
+ },
2755
+ [onFiltersChange]
2756
+ );
2757
+ const handleClearEvents = useCallback(() => {
2758
+ controller.clearEvents();
2759
+ }, [controller]);
2760
+ const handleTargetClick = useCallback(
2761
+ (storeId) => {
2762
+ onNavigateToStore(storeId);
2763
+ },
2764
+ [onNavigateToStore]
2765
+ );
2766
+ const handleReplay = useCallback(
2767
+ (event) => {
2768
+ var _a;
2769
+ if (event.type !== "dispatch") return;
2770
+ const store = controller.getStore(event.target);
2771
+ if (!store || !store.instance) return;
2772
+ const actionName = (_a = event.extra) == null ? void 0 : _a.split("(")[0];
2773
+ if (!actionName) return;
2774
+ const actions = store.instance.actions;
2775
+ const action = actions[actionName];
2776
+ if (typeof action !== "function") return;
2777
+ const args = event.data;
2778
+ if (args && Array.isArray(args)) {
2779
+ action(...args);
2780
+ } else {
2781
+ action();
2782
+ }
2783
+ },
2784
+ [controller]
2785
+ );
2786
+ return /* @__PURE__ */ jsx(
2787
+ TabContent,
2788
+ {
2789
+ searchBar: /* @__PURE__ */ jsx(
2790
+ SearchBar,
2791
+ {
2792
+ value: searchQuery,
2793
+ onChange: onSearchQueryChange,
2794
+ placeholder: "Search events..."
2795
+ }
2796
+ ),
2797
+ filterBar: /* @__PURE__ */ jsx(
2798
+ EventFilterBar,
2799
+ {
2800
+ activeFilters: filters,
2801
+ onFilterChange: handleFilterChange,
2802
+ onClear: handleClearEvents
2803
+ }
2804
+ ),
2805
+ children: /* @__PURE__ */ jsx(
2806
+ MainContent,
2807
+ {
2808
+ isEmpty: filteredEvents.length === 0,
2809
+ emptyIcon: "📋",
2810
+ emptyMessage: "No events yet",
2811
+ emptyHint: "Events will appear as stores change",
2812
+ children: filteredEvents.map((event) => /* @__PURE__ */ jsx(
2813
+ EventEntry,
2814
+ {
2815
+ event,
2816
+ onTargetClick: handleTargetClick,
2817
+ onReplay: handleReplay
2818
+ },
2819
+ event.id
2820
+ ))
2821
+ }
2822
+ )
2823
+ }
2824
+ );
2825
+ }
2826
+ function DevtoolsPanel({
2827
+ controller,
2828
+ position: initialPosition = "left",
2829
+ onPositionChange,
2830
+ onCollapsedChange,
2831
+ onTransparencyChange,
2832
+ onResize,
2833
+ initialSize = 360,
2834
+ initialCollapsed = false
2835
+ }) {
2836
+ const eventFiltersTransform = useMemo(
2837
+ () => ({
2838
+ toStorage: (value) => value ? Array.from(value) : null,
2839
+ fromStorage: (stored) => Array.isArray(stored) ? new Set(stored) : null
2840
+ }),
2841
+ []
2842
+ );
2843
+ const [activeTab, setActiveTab] = usePersistentState(
2844
+ "activeTab",
2845
+ "stores"
2846
+ );
2847
+ const [collapsed, setCollapsed] = usePersistentState(
2848
+ "collapsed",
2849
+ initialCollapsed
2850
+ );
2851
+ const [size, setSize] = usePersistentState("size", initialSize);
2852
+ const [position, setPosition] = usePersistentState(
2853
+ "position",
2854
+ initialPosition
2855
+ );
2856
+ const [stores, setStores] = useState([]);
2857
+ const [events, setEvents] = useState([]);
2858
+ const [storeSearchQuery, setStoreSearchQuery] = usePersistentState(
2859
+ "storeSearchQuery",
2860
+ ""
2861
+ );
2862
+ const [eventSearchQuery, setEventSearchQuery] = usePersistentState(
2863
+ "eventSearchQuery",
2864
+ ""
2865
+ );
2866
+ const [eventFilters, setEventFilters] = usePersistentState(
2867
+ "eventFilters",
2868
+ null,
2869
+ eventFiltersTransform
2870
+ );
2871
+ const [isTransparent, setIsTransparent] = useState(false);
2872
+ const prevStoresVersion = useRef(0);
2873
+ const prevEventsCount = useRef(0);
2874
+ const [storesTabFlash, setStoresTabFlash] = useState(false);
2875
+ const [eventsTabFlash, setEventsTabFlash] = useState(false);
2876
+ useEffect(() => {
2877
+ const update = () => {
2878
+ const newStores = controller.getStores();
2879
+ const newEvents = controller.getEvents();
2880
+ const newStoresVersion = newStores.reduce(
2881
+ (sum, s) => sum + s.history.length,
2882
+ 0
2883
+ );
2884
+ if (prevStoresVersion.current > 0 && newStoresVersion > prevStoresVersion.current) {
2885
+ setStoresTabFlash(true);
2886
+ setTimeout(() => setStoresTabFlash(false), 600);
2887
+ }
2888
+ prevStoresVersion.current = newStoresVersion;
2889
+ if (prevEventsCount.current > 0 && newEvents.length > prevEventsCount.current) {
2890
+ setEventsTabFlash(true);
2891
+ setTimeout(() => setEventsTabFlash(false), 600);
2892
+ }
2893
+ prevEventsCount.current = newEvents.length;
2894
+ setStores(newStores);
2895
+ setEvents(newEvents);
2896
+ };
2897
+ update();
2898
+ return controller.subscribe(update);
2899
+ }, [controller]);
2900
+ const toggleCollapsed = useCallback(() => {
2901
+ setCollapsed((prev) => {
2902
+ const next = !prev;
2903
+ onCollapsedChange == null ? void 0 : onCollapsedChange(next);
2904
+ return next;
2905
+ });
2906
+ }, [onCollapsedChange]);
2907
+ const togglePosition = useCallback(() => {
2908
+ const nextPosition = {
2909
+ left: "bottom",
2910
+ bottom: "right",
2911
+ right: "left"
2912
+ };
2913
+ const newPosition = nextPosition[position];
2914
+ setPosition(newPosition);
2915
+ onPositionChange == null ? void 0 : onPositionChange(newPosition);
2916
+ }, [position, onPositionChange]);
2917
+ const toggleTransparency = useCallback(() => {
2918
+ setIsTransparent((prev) => {
2919
+ const next = !prev;
2920
+ onTransparencyChange == null ? void 0 : onTransparencyChange(next);
2921
+ return next;
2922
+ });
2923
+ }, [onTransparencyChange]);
2924
+ const handleResize = useCallback(
2925
+ (delta) => {
2926
+ setSize((prev) => {
2927
+ const minSize = 200;
2928
+ const maxSize = position === "bottom" ? 600 : 800;
2929
+ const adjustedDelta = position === "right" ? -delta : delta;
2930
+ const newSize = Math.min(maxSize, Math.max(minSize, prev + adjustedDelta));
2931
+ onResize == null ? void 0 : onResize(newSize);
2932
+ return newSize;
2933
+ });
2934
+ },
2935
+ [position, onResize]
2936
+ );
2937
+ const handleNavigateToEvents = useCallback((storeId) => {
2938
+ setActiveTab("events");
2939
+ setEventSearchQuery(storeId);
2940
+ }, []);
2941
+ const handleNavigateToStore = useCallback((storeId) => {
2942
+ setActiveTab("stores");
2943
+ setStoreSearchQuery(storeId);
2944
+ }, []);
2945
+ useEffect(() => {
2946
+ onResize == null ? void 0 : onResize(size);
2947
+ }, []);
2948
+ const positionClass = `position-${position}`;
2949
+ const expandedStyle = collapsed ? position === "left" ? { left: "-9999px" } : position === "right" ? { right: "-9999px" } : { bottom: "-9999px" } : {};
2950
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2951
+ /* @__PURE__ */ jsx("style", { children: panelStyles }),
2952
+ /* @__PURE__ */ jsxs(
2953
+ "div",
2954
+ {
2955
+ className: `storion-devtools ${positionClass} ${isTransparent ? "transparent" : ""}`,
2956
+ style: expandedStyle,
2957
+ children: [
2958
+ /* @__PURE__ */ jsx(ResizeHandle, { position, onResize: handleResize }),
2959
+ /* @__PURE__ */ jsxs("div", { className: "sdt-header", children: [
2960
+ /* @__PURE__ */ jsxs("div", { className: "sdt-logo", children: [
2961
+ /* @__PURE__ */ jsx("div", { className: "sdt-logo-icon", children: "⚡" }),
2962
+ /* @__PURE__ */ jsx("span", { className: "sdt-title", children: "Storion" })
2963
+ ] }),
2964
+ /* @__PURE__ */ jsxs("div", { className: "sdt-header-actions", children: [
2965
+ /* @__PURE__ */ jsx(
2966
+ "button",
2967
+ {
2968
+ className: `sdt-btn ${isTransparent ? "active" : ""}`,
2969
+ onClick: toggleTransparency,
2970
+ title: "Toggle transparency",
2971
+ children: /* @__PURE__ */ jsx(IconTransparency, {})
2972
+ }
2973
+ ),
2974
+ /* @__PURE__ */ jsx(
2975
+ "button",
2976
+ {
2977
+ className: "sdt-btn",
2978
+ onClick: togglePosition,
2979
+ title: position === "left" ? "Dock to bottom" : position === "bottom" ? "Dock to right" : "Dock to left",
2980
+ children: position === "left" ? /* @__PURE__ */ jsx(IconDockBottom, {}) : position === "bottom" ? /* @__PURE__ */ jsx(IconDockRight, {}) : /* @__PURE__ */ jsx(IconDockLeft, {})
2981
+ }
2982
+ ),
2983
+ /* @__PURE__ */ jsx(
2984
+ "button",
2985
+ {
2986
+ className: "sdt-btn",
2987
+ onClick: toggleCollapsed,
2988
+ title: "Minimize",
2989
+ children: /* @__PURE__ */ jsx(IconMinimize, {})
2990
+ }
2991
+ )
2992
+ ] })
2993
+ ] }),
2994
+ /* @__PURE__ */ jsxs("div", { className: "sdt-tabs", children: [
2995
+ /* @__PURE__ */ jsxs(
2996
+ "button",
2997
+ {
2998
+ className: `sdt-tab ${activeTab === "stores" ? "active" : ""} ${storesTabFlash ? "flash" : ""}`,
2999
+ onClick: () => setActiveTab("stores"),
3000
+ children: [
3001
+ "Stores",
3002
+ /* @__PURE__ */ jsx("span", { className: "sdt-tab-count", children: stores.length })
3003
+ ]
3004
+ }
3005
+ ),
3006
+ /* @__PURE__ */ jsxs(
3007
+ "button",
3008
+ {
3009
+ className: `sdt-tab ${activeTab === "events" ? "active" : ""} ${eventsTabFlash ? "flash" : ""}`,
3010
+ onClick: () => setActiveTab("events"),
3011
+ children: [
3012
+ "Events",
3013
+ /* @__PURE__ */ jsx("span", { className: "sdt-tab-count", children: events.length })
3014
+ ]
3015
+ }
3016
+ )
3017
+ ] }),
3018
+ activeTab === "stores" && /* @__PURE__ */ jsx(
3019
+ StoresTab,
3020
+ {
3021
+ controller,
3022
+ stores,
3023
+ searchQuery: storeSearchQuery,
3024
+ onSearchQueryChange: setStoreSearchQuery,
3025
+ onNavigateToEvents: handleNavigateToEvents
3026
+ }
3027
+ ),
3028
+ activeTab === "events" && /* @__PURE__ */ jsx(
3029
+ EventsTab,
3030
+ {
3031
+ controller,
3032
+ events,
3033
+ searchQuery: eventSearchQuery,
3034
+ onSearchQueryChange: setEventSearchQuery,
3035
+ filters: eventFilters,
3036
+ onFiltersChange: setEventFilters,
3037
+ onNavigateToStore: handleNavigateToStore
3038
+ }
3039
+ )
3040
+ ]
3041
+ }
3042
+ ),
3043
+ collapsed && /* @__PURE__ */ jsx(
3044
+ "button",
3045
+ {
3046
+ className: "sdt-floating-btn",
3047
+ onClick: toggleCollapsed,
3048
+ title: "Open Storion DevTools",
3049
+ children: "⚡"
3050
+ }
3051
+ )
3052
+ ] });
3053
+ }
3054
+ const STORAGE_KEY = "storion-devtools-settings";
3055
+ function loadStoredSettings() {
3056
+ try {
3057
+ const stored = localStorage.getItem(STORAGE_KEY);
3058
+ if (stored) {
3059
+ return JSON.parse(stored);
3060
+ }
3061
+ } catch {
3062
+ }
3063
+ return {};
3064
+ }
3065
+ let panelRoot = null;
3066
+ let panelContainer = null;
3067
+ let currentPosition = "left";
3068
+ let originalBodyPadding = "";
3069
+ function updateBodyPadding(position, size, collapsed) {
3070
+ if (position === "bottom" && !collapsed) {
3071
+ if (!originalBodyPadding) {
3072
+ originalBodyPadding = document.body.style.paddingBottom || "";
3073
+ }
3074
+ document.body.style.paddingBottom = `${size}px`;
3075
+ } else {
3076
+ document.body.style.paddingBottom = originalBodyPadding;
3077
+ }
3078
+ }
3079
+ function resetBodyPadding() {
3080
+ document.body.style.paddingBottom = originalBodyPadding;
3081
+ originalBodyPadding = "";
3082
+ }
3083
+ let wasCollapsed = false;
3084
+ function updateContainerPosition(position, size, collapsed = false) {
3085
+ if (!panelContainer) return;
3086
+ currentPosition = position;
3087
+ const isExpanding = wasCollapsed && !collapsed;
3088
+ wasCollapsed = collapsed;
3089
+ panelContainer.style.top = "";
3090
+ panelContainer.style.right = "";
3091
+ panelContainer.style.bottom = "";
3092
+ panelContainer.style.left = "";
3093
+ panelContainer.style.width = "";
3094
+ panelContainer.style.height = "";
3095
+ panelContainer.style.borderTop = "";
3096
+ panelContainer.style.borderRight = "";
3097
+ panelContainer.style.borderBottom = "";
3098
+ panelContainer.style.borderLeft = "";
3099
+ if (collapsed) {
3100
+ panelContainer.style.transition = "none";
3101
+ panelContainer.style.opacity = "1";
3102
+ panelContainer.style.left = "-9999px";
3103
+ panelContainer.style.top = "0";
3104
+ panelContainer.style.width = `${size}px`;
3105
+ panelContainer.style.height = "100vh";
3106
+ updateBodyPadding(position, size, true);
3107
+ return;
3108
+ }
3109
+ if (isExpanding) {
3110
+ panelContainer.style.transition = "none";
3111
+ panelContainer.style.opacity = "0";
3112
+ }
3113
+ if (position === "left") {
3114
+ panelContainer.style.top = "0";
3115
+ panelContainer.style.left = "0";
3116
+ panelContainer.style.width = `${size}px`;
3117
+ panelContainer.style.height = "100vh";
3118
+ panelContainer.style.borderRight = "1px solid #27272a";
3119
+ } else if (position === "right") {
3120
+ panelContainer.style.top = "0";
3121
+ panelContainer.style.right = "0";
3122
+ panelContainer.style.width = `${size}px`;
3123
+ panelContainer.style.height = "100vh";
3124
+ panelContainer.style.borderLeft = "1px solid #27272a";
3125
+ } else {
3126
+ panelContainer.style.bottom = "0";
3127
+ panelContainer.style.left = "0";
3128
+ panelContainer.style.right = "0";
3129
+ panelContainer.style.height = `${size}px`;
3130
+ panelContainer.style.width = "100%";
3131
+ panelContainer.style.borderTop = "1px solid #27272a";
3132
+ }
3133
+ if (isExpanding) {
3134
+ panelContainer.style.transition = "opacity 0.2s ease-out";
3135
+ panelContainer.style.opacity = "1";
3136
+ }
3137
+ updateBodyPadding(position, size, collapsed);
3138
+ }
3139
+ function updateContainerSize(size) {
3140
+ if (!panelContainer) return;
3141
+ panelContainer.style.transition = "none";
3142
+ if (currentPosition === "bottom") {
3143
+ panelContainer.style.height = `${size}px`;
3144
+ } else {
3145
+ panelContainer.style.width = `${size}px`;
3146
+ }
3147
+ updateBodyPadding(currentPosition, size, false);
3148
+ }
3149
+ function mountDevtoolsPanel(options = {}) {
3150
+ const stored = loadStoredSettings();
3151
+ const { container, zIndex = 999999 } = options;
3152
+ const position = stored.position ?? options.position ?? "left";
3153
+ const initialSize = stored.size ?? options.size ?? 360;
3154
+ const initialCollapsed = stored.collapsed ?? options.collapsed ?? false;
3155
+ currentPosition = position;
3156
+ const controller = window.__STORION_DEVTOOLS__;
3157
+ if (!controller) {
3158
+ console.warn(
3159
+ "[Storion Devtools] No devtools controller found. Make sure to add devtoolsMiddleware to your container."
3160
+ );
3161
+ return () => {
3162
+ };
3163
+ }
3164
+ if (panelRoot) {
3165
+ panelRoot.unmount();
3166
+ panelRoot = null;
3167
+ }
3168
+ if (panelContainer && !container) {
3169
+ panelContainer.remove();
3170
+ panelContainer = null;
3171
+ }
3172
+ let containerEl;
3173
+ if (container) {
3174
+ if (typeof container === "string") {
3175
+ const el = document.querySelector(container);
3176
+ if (!el) {
3177
+ console.error(`[Storion Devtools] Container not found: ${container}`);
3178
+ return () => {
3179
+ };
3180
+ }
3181
+ containerEl = el;
3182
+ } else {
3183
+ containerEl = container;
3184
+ }
3185
+ } else {
3186
+ containerEl = document.createElement("div");
3187
+ containerEl.id = "storion-devtools-panel";
3188
+ const baseStyles = {
3189
+ position: "fixed",
3190
+ zIndex: String(zIndex),
3191
+ background: "#09090b",
3192
+ boxShadow: "0 0 20px rgba(0,0,0,0.5)",
3193
+ overflow: "hidden",
3194
+ display: "block"
3195
+ };
3196
+ const positionStyles = {
3197
+ left: {
3198
+ top: "0",
3199
+ left: initialCollapsed ? "-9999px" : "0",
3200
+ width: `${initialSize}px`,
3201
+ height: "100vh",
3202
+ borderRight: "1px solid #27272a"
3203
+ },
3204
+ right: {
3205
+ top: "0",
3206
+ right: initialCollapsed ? "-9999px" : "0",
3207
+ width: `${initialSize}px`,
3208
+ height: "100vh",
3209
+ borderLeft: "1px solid #27272a"
3210
+ },
3211
+ bottom: {
3212
+ bottom: initialCollapsed ? "-9999px" : "0",
3213
+ left: "0",
3214
+ right: "",
3215
+ height: `${initialSize}px`,
3216
+ width: "100%",
3217
+ borderTop: "1px solid #27272a"
3218
+ }
3219
+ };
3220
+ Object.assign(containerEl.style, baseStyles, positionStyles[position]);
3221
+ document.body.appendChild(containerEl);
3222
+ panelContainer = containerEl;
3223
+ updateBodyPadding(position, initialSize, initialCollapsed);
3224
+ }
3225
+ let currentSize = initialSize;
3226
+ let isCollapsed = initialCollapsed;
3227
+ wasCollapsed = initialCollapsed;
3228
+ const handleResize = (newSize) => {
3229
+ currentSize = newSize;
3230
+ if (!isCollapsed) {
3231
+ updateContainerSize(newSize);
3232
+ }
3233
+ };
3234
+ const handleCollapsedChange = (collapsed) => {
3235
+ isCollapsed = collapsed;
3236
+ updateContainerPosition(currentPosition, currentSize, collapsed);
3237
+ };
3238
+ const handlePositionChange = (newPosition) => {
3239
+ currentPosition = newPosition;
3240
+ updateContainerPosition(newPosition, currentSize, isCollapsed);
3241
+ };
3242
+ const handleTransparencyChange = (transparent) => {
3243
+ if (!panelContainer) return;
3244
+ panelContainer.style.opacity = transparent ? "0.3" : "1";
3245
+ };
3246
+ panelRoot = createRoot(containerEl);
3247
+ panelRoot.render(
3248
+ /* @__PURE__ */ jsx(
3249
+ DevtoolsPanel,
3250
+ {
3251
+ controller,
3252
+ position,
3253
+ initialSize,
3254
+ initialCollapsed,
3255
+ onCollapsedChange: handleCollapsedChange,
3256
+ onPositionChange: handlePositionChange,
3257
+ onTransparencyChange: handleTransparencyChange,
3258
+ onResize: handleResize
3259
+ }
3260
+ )
3261
+ );
3262
+ const handleWindowError = (event) => {
3263
+ var _a;
3264
+ controller.recordEvent("error", "window", event.message, {
3265
+ message: event.message,
3266
+ filename: event.filename,
3267
+ lineno: event.lineno,
3268
+ colno: event.colno,
3269
+ stack: (_a = event.error) == null ? void 0 : _a.stack
3270
+ });
3271
+ };
3272
+ const handleUnhandledRejection = (event) => {
3273
+ const reason = event.reason;
3274
+ const message = reason instanceof Error ? reason.message : typeof reason === "string" ? reason : "Unhandled promise rejection";
3275
+ controller.recordEvent("error", "window", message, {
3276
+ message,
3277
+ stack: reason instanceof Error ? reason.stack : void 0,
3278
+ reason
3279
+ });
3280
+ };
3281
+ window.addEventListener("error", handleWindowError);
3282
+ window.addEventListener("unhandledrejection", handleUnhandledRejection);
3283
+ return () => {
3284
+ window.removeEventListener("error", handleWindowError);
3285
+ window.removeEventListener("unhandledrejection", handleUnhandledRejection);
3286
+ if (panelRoot) {
3287
+ panelRoot.unmount();
3288
+ panelRoot = null;
3289
+ }
3290
+ if (panelContainer) {
3291
+ panelContainer.remove();
3292
+ panelContainer = null;
3293
+ }
3294
+ resetBodyPadding();
3295
+ };
3296
+ }
3297
+ function unmountDevtoolsPanel() {
3298
+ if (panelRoot) {
3299
+ panelRoot.unmount();
3300
+ panelRoot = null;
3301
+ }
3302
+ if (panelContainer) {
3303
+ panelContainer.remove();
3304
+ panelContainer = null;
3305
+ }
3306
+ resetBodyPadding();
3307
+ }
3308
+ function toggleDevtoolsPanel(options) {
3309
+ if (panelRoot) {
3310
+ unmountDevtoolsPanel();
3311
+ } else {
3312
+ mountDevtoolsPanel(options);
3313
+ }
3314
+ }
3315
+ export {
3316
+ ActionBar,
3317
+ DevtoolsPanel,
3318
+ FilterBar,
3319
+ MainContent,
3320
+ SearchBar,
3321
+ TabContent,
3322
+ clearDevtoolsSettings,
3323
+ mountDevtoolsPanel,
3324
+ toggleDevtoolsPanel,
3325
+ unmountDevtoolsPanel
3326
+ };