@preact/signals-devtools-ui 0.3.0 → 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.
package/dist/styles.css CHANGED
@@ -7,7 +7,8 @@
7
7
  }
8
8
 
9
9
  body {
10
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
10
+ font-family:
11
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
11
12
  font-size: 13px;
12
13
  line-height: 1.4;
13
14
  color: #333;
@@ -54,10 +55,10 @@ body {
54
55
  }
55
56
 
56
57
  .divider {
57
- width: 100%;
58
- height: 2px;
59
- background: #e0e0e0;
60
- margin: 8px 0;
58
+ width: 100%;
59
+ height: 2px;
60
+ background: #e0e0e0;
61
+ margin: 8px 0;
61
62
  }
62
63
 
63
64
  .status-indicator {
@@ -86,8 +87,13 @@ body {
86
87
  }
87
88
 
88
89
  @keyframes pulse {
89
- 0%, 100% { opacity: 1; }
90
- 50% { opacity: 0.5; }
90
+ 0%,
91
+ 100% {
92
+ opacity: 1;
93
+ }
94
+ 50% {
95
+ opacity: 0.5;
96
+ }
91
97
  }
92
98
 
93
99
  .header-controls {
@@ -151,7 +157,7 @@ body {
151
157
  background: white;
152
158
  border: 1px solid #d0d0d0;
153
159
  border-radius: 8px;
154
- box-shadow: 0 4px 16px rgba(0,0,0,0.15);
160
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
155
161
  z-index: 1000;
156
162
  overflow-y: auto;
157
163
  }
@@ -314,16 +320,40 @@ body {
314
320
  border-left: 4px solid #ff9800;
315
321
  }
316
322
 
323
+ .update-item.component {
324
+ border-left: 4px solid #4caf50;
325
+ background-color: #f8fff8;
326
+ }
327
+
328
+ .update-type-badge {
329
+ font-size: 10px;
330
+ padding: 2px 6px;
331
+ border-radius: 3px;
332
+ background: #e0e0e0;
333
+ color: #666;
334
+ margin-left: 8px;
335
+ }
336
+
337
+ .update-item.component .update-type-badge {
338
+ background: #e8f5e9;
339
+ color: #2e7d32;
340
+ }
341
+
342
+ .update-item.effect .update-type-badge {
343
+ background: #fff3e0;
344
+ color: #e65100;
345
+ }
346
+
317
347
  .component-name-header {
318
- margin-right: 8px;
348
+ margin-right: 8px;
319
349
  }
320
350
 
321
351
  .component-list {
322
- list-style: none;
323
- display: flex;
324
- padding: 0;
325
- margin: 0;
326
- font-family: monospace;
352
+ list-style: none;
353
+ display: flex;
354
+ padding: 0;
355
+ margin: 0;
356
+ font-family: monospace;
327
357
  background: #f8f9fa;
328
358
  padding: 4px 6px;
329
359
  border-radius: 3px;
@@ -439,8 +469,7 @@ body {
439
469
  position: relative;
440
470
  background:
441
471
  linear-gradient(90deg, #e5e7eb 1px, transparent 1px),
442
- linear-gradient(180deg, #e5e7eb 1px, transparent 1px),
443
- #f8fafc;
472
+ linear-gradient(180deg, #e5e7eb 1px, transparent 1px), #f8fafc;
444
473
  background-size: 40px 40px;
445
474
  overflow: hidden;
446
475
  user-select: none;
@@ -472,7 +501,7 @@ body {
472
501
  font-size: 12px;
473
502
  font-weight: 500;
474
503
  cursor: pointer;
475
- box-shadow: 0 2px 6px rgba(0,0,0,0.08);
504
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
476
505
  transition: all 0.15s ease;
477
506
  }
478
507
 
@@ -480,7 +509,7 @@ body {
480
509
  .graph-export-button:hover {
481
510
  background: #fff;
482
511
  border-color: #cbd5e1;
483
- box-shadow: 0 4px 8px rgba(0,0,0,0.12);
512
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.12);
484
513
  transform: translateY(-1px);
485
514
  }
486
515
 
@@ -502,7 +531,7 @@ body {
502
531
  background: white;
503
532
  border: 1px solid #e0e0e0;
504
533
  border-radius: 4px;
505
- box-shadow: 0 4px 8px rgba(0,0,0,0.15);
534
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
506
535
  overflow: hidden;
507
536
  min-width: 180px;
508
537
  }
@@ -542,21 +571,23 @@ body {
542
571
  font-size: 11px;
543
572
  font-weight: 600;
544
573
  font-family: monospace;
545
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
574
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
546
575
  min-width: 48px;
547
576
  text-align: center;
548
577
  }
549
578
 
550
579
  .graph-node {
551
580
  cursor: pointer;
552
- transition: transform 0.15s ease, filter 0.15s ease;
581
+ transition:
582
+ transform 0.15s ease,
583
+ filter 0.15s ease;
553
584
  stroke: #fff;
554
585
  stroke-width: 2.5;
555
- filter: drop-shadow(0 3px 6px rgba(0,0,0,0.15));
586
+ filter: drop-shadow(0 3px 6px rgba(0, 0, 0, 0.15));
556
587
  }
557
588
 
558
589
  .graph-node-group.hovered .graph-node {
559
- filter: brightness(1.15) drop-shadow(0 4px 12px rgba(0,0,0,0.25));
590
+ filter: brightness(1.15) drop-shadow(0 4px 12px rgba(0, 0, 0, 0.25));
560
591
  stroke-width: 3;
561
592
  }
562
593
 
@@ -580,7 +611,9 @@ body {
580
611
  stroke: #94a3b8;
581
612
  stroke-width: 2;
582
613
  fill: none;
583
- transition: stroke 0.2s, stroke-width 0.2s;
614
+ transition:
615
+ stroke 0.2s,
616
+ stroke-width 0.2s;
584
617
  }
585
618
 
586
619
  .graph-link.highlighted {
@@ -595,7 +628,7 @@ body {
595
628
  font-size: 12px;
596
629
  font-weight: 600;
597
630
  pointer-events: none;
598
- text-shadow: 0 1px 3px rgba(0,0,0,0.4);
631
+ text-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
599
632
  letter-spacing: 0.2px;
600
633
  }
601
634
 
@@ -607,7 +640,7 @@ body {
607
640
  padding: 10px 14px;
608
641
  border-radius: 8px;
609
642
  font-size: 12px;
610
- box-shadow: 0 8px 24px rgba(0,0,0,0.25);
643
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
611
644
  z-index: 1000;
612
645
  pointer-events: none;
613
646
  transform: translateY(-50%);
@@ -679,7 +712,7 @@ body {
679
712
  border-radius: 8px;
680
713
  padding: 14px 16px;
681
714
  font-size: 12px;
682
- box-shadow: 0 4px 12px rgba(0,0,0,0.08);
715
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
683
716
  }
684
717
 
685
718
  .legend-item {
@@ -697,7 +730,7 @@ body {
697
730
  width: 14px;
698
731
  height: 14px;
699
732
  border-radius: 50%;
700
- box-shadow: 0 2px 4px rgba(0,0,0,0.15);
733
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
701
734
  }
702
735
 
703
736
  .component-boundary {
@@ -733,7 +766,7 @@ body {
733
766
  border-radius: 4px;
734
767
  font-size: 13px;
735
768
  font-weight: 500;
736
- box-shadow: 0 4px 12px rgba(0,0,0,0.3);
769
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
737
770
  z-index: 1000;
738
771
  animation: slideInFade 0.3s ease-out;
739
772
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@preact/signals-devtools-ui",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "license": "MIT",
5
5
  "description": "DevTools UI components for @preact/signals",
6
6
  "keywords": [
@@ -44,7 +44,7 @@
44
44
  "README.md"
45
45
  ],
46
46
  "dependencies": {
47
- "@preact/signals-devtools-adapter": "0.3.0"
47
+ "@preact/signals-devtools-adapter": "0.4.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/node": "^20.0.0",
@@ -54,7 +54,7 @@
54
54
  "typescript": "~5.8.3",
55
55
  "vite": "^7.0.0",
56
56
  "vitest": "^4.0.17",
57
- "@preact/signals": "2.6.1"
57
+ "@preact/signals": "2.7.0"
58
58
  },
59
59
  "publishConfig": {
60
60
  "access": "public",
@@ -212,7 +212,8 @@ export function GraphVisualization() {
212
212
  if (!update.signalId) continue;
213
213
  if (!showDisposed && disposed.has(update.signalId)) continue;
214
214
 
215
- const type: "signal" | "computed" | "effect" = update.signalType;
215
+ const type: "signal" | "computed" | "effect" | "component" =
216
+ update.signalType;
216
217
 
217
218
  if (!nodes.has(update.signalId)) {
218
219
  nodes.set(update.signalId, {
@@ -424,6 +425,9 @@ export function GraphVisualization() {
424
425
  case "effect":
425
426
  lines.push(` ${id}([${name}])`);
426
427
  break;
428
+ case "component":
429
+ lines.push(` ${id}{{${name}}}`);
430
+ break;
427
431
  }
428
432
  });
429
433
 
@@ -655,6 +659,13 @@ export function GraphVisualization() {
655
659
  ></div>
656
660
  <span>Effect</span>
657
661
  </div>
662
+ <div className="legend-item">
663
+ <div
664
+ className="legend-color"
665
+ style={{ backgroundColor: "#9c27b0" }}
666
+ ></div>
667
+ <span>Component</span>
668
+ </div>
658
669
  </div>
659
670
  </div>
660
671
  </div>
@@ -31,14 +31,17 @@ export function UpdateItem({ update, count, firstUpdate }: UpdateItemProps) {
31
31
  </span>
32
32
  );
33
33
 
34
- if (update.type === "effect") {
34
+ if (update.type === "effect" || update.type === "component") {
35
+ const icon = update.type === "component" ? "🔄" : "↪️";
36
+ const label = update.type === "component" ? "Component render" : "Effect";
35
37
  return (
36
38
  <div className={`update-item ${update.type}`}>
37
39
  <div className="update-header">
38
40
  <span className="signal-name">
39
- ↪️ {update.signalName}
41
+ {icon} {update.signalName}
40
42
  {countLabel}
41
43
  </span>
44
+ <span className="update-type-badge">{label}</span>
42
45
  <span className="update-time">{time}</span>
43
46
  </div>
44
47
  </div>
package/src/context.ts CHANGED
@@ -65,8 +65,8 @@ export function createConnectionStore(adapter: DevToolsAdapter) {
65
65
  }
66
66
 
67
67
  export interface SignalUpdate {
68
- type: "update" | "effect";
69
- signalType: "signal" | "computed" | "effect";
68
+ type: "update" | "effect" | "component";
69
+ signalType: "signal" | "computed" | "effect" | "component";
70
70
  signalName: string;
71
71
  signalId?: string;
72
72
  prevValue?: any;
package/src/styles.css CHANGED
@@ -7,7 +7,8 @@
7
7
  }
8
8
 
9
9
  body {
10
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
10
+ font-family:
11
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
11
12
  font-size: 13px;
12
13
  line-height: 1.4;
13
14
  color: #333;
@@ -54,10 +55,10 @@ body {
54
55
  }
55
56
 
56
57
  .divider {
57
- width: 100%;
58
- height: 2px;
59
- background: #e0e0e0;
60
- margin: 8px 0;
58
+ width: 100%;
59
+ height: 2px;
60
+ background: #e0e0e0;
61
+ margin: 8px 0;
61
62
  }
62
63
 
63
64
  .status-indicator {
@@ -86,8 +87,13 @@ body {
86
87
  }
87
88
 
88
89
  @keyframes pulse {
89
- 0%, 100% { opacity: 1; }
90
- 50% { opacity: 0.5; }
90
+ 0%,
91
+ 100% {
92
+ opacity: 1;
93
+ }
94
+ 50% {
95
+ opacity: 0.5;
96
+ }
91
97
  }
92
98
 
93
99
  .header-controls {
@@ -151,7 +157,7 @@ body {
151
157
  background: white;
152
158
  border: 1px solid #d0d0d0;
153
159
  border-radius: 8px;
154
- box-shadow: 0 4px 16px rgba(0,0,0,0.15);
160
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
155
161
  z-index: 1000;
156
162
  overflow-y: auto;
157
163
  }
@@ -314,16 +320,40 @@ body {
314
320
  border-left: 4px solid #ff9800;
315
321
  }
316
322
 
323
+ .update-item.component {
324
+ border-left: 4px solid #4caf50;
325
+ background-color: #f8fff8;
326
+ }
327
+
328
+ .update-type-badge {
329
+ font-size: 10px;
330
+ padding: 2px 6px;
331
+ border-radius: 3px;
332
+ background: #e0e0e0;
333
+ color: #666;
334
+ margin-left: 8px;
335
+ }
336
+
337
+ .update-item.component .update-type-badge {
338
+ background: #e8f5e9;
339
+ color: #2e7d32;
340
+ }
341
+
342
+ .update-item.effect .update-type-badge {
343
+ background: #fff3e0;
344
+ color: #e65100;
345
+ }
346
+
317
347
  .component-name-header {
318
- margin-right: 8px;
348
+ margin-right: 8px;
319
349
  }
320
350
 
321
351
  .component-list {
322
- list-style: none;
323
- display: flex;
324
- padding: 0;
325
- margin: 0;
326
- font-family: monospace;
352
+ list-style: none;
353
+ display: flex;
354
+ padding: 0;
355
+ margin: 0;
356
+ font-family: monospace;
327
357
  background: #f8f9fa;
328
358
  padding: 4px 6px;
329
359
  border-radius: 3px;
@@ -439,8 +469,7 @@ body {
439
469
  position: relative;
440
470
  background:
441
471
  linear-gradient(90deg, #e5e7eb 1px, transparent 1px),
442
- linear-gradient(180deg, #e5e7eb 1px, transparent 1px),
443
- #f8fafc;
472
+ linear-gradient(180deg, #e5e7eb 1px, transparent 1px), #f8fafc;
444
473
  background-size: 40px 40px;
445
474
  overflow: hidden;
446
475
  user-select: none;
@@ -472,7 +501,7 @@ body {
472
501
  font-size: 12px;
473
502
  font-weight: 500;
474
503
  cursor: pointer;
475
- box-shadow: 0 2px 6px rgba(0,0,0,0.08);
504
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
476
505
  transition: all 0.15s ease;
477
506
  }
478
507
 
@@ -480,7 +509,7 @@ body {
480
509
  .graph-export-button:hover {
481
510
  background: #fff;
482
511
  border-color: #cbd5e1;
483
- box-shadow: 0 4px 8px rgba(0,0,0,0.12);
512
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.12);
484
513
  transform: translateY(-1px);
485
514
  }
486
515
 
@@ -502,7 +531,7 @@ body {
502
531
  background: white;
503
532
  border: 1px solid #e0e0e0;
504
533
  border-radius: 4px;
505
- box-shadow: 0 4px 8px rgba(0,0,0,0.15);
534
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
506
535
  overflow: hidden;
507
536
  min-width: 180px;
508
537
  }
@@ -542,21 +571,23 @@ body {
542
571
  font-size: 11px;
543
572
  font-weight: 600;
544
573
  font-family: monospace;
545
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
574
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
546
575
  min-width: 48px;
547
576
  text-align: center;
548
577
  }
549
578
 
550
579
  .graph-node {
551
580
  cursor: pointer;
552
- transition: transform 0.15s ease, filter 0.15s ease;
581
+ transition:
582
+ transform 0.15s ease,
583
+ filter 0.15s ease;
553
584
  stroke: #fff;
554
585
  stroke-width: 2.5;
555
- filter: drop-shadow(0 3px 6px rgba(0,0,0,0.15));
586
+ filter: drop-shadow(0 3px 6px rgba(0, 0, 0, 0.15));
556
587
  }
557
588
 
558
589
  .graph-node-group.hovered .graph-node {
559
- filter: brightness(1.15) drop-shadow(0 4px 12px rgba(0,0,0,0.25));
590
+ filter: brightness(1.15) drop-shadow(0 4px 12px rgba(0, 0, 0, 0.25));
560
591
  stroke-width: 3;
561
592
  }
562
593
 
@@ -580,7 +611,9 @@ body {
580
611
  stroke: #94a3b8;
581
612
  stroke-width: 2;
582
613
  fill: none;
583
- transition: stroke 0.2s, stroke-width 0.2s;
614
+ transition:
615
+ stroke 0.2s,
616
+ stroke-width 0.2s;
584
617
  }
585
618
 
586
619
  .graph-link.highlighted {
@@ -595,7 +628,7 @@ body {
595
628
  font-size: 12px;
596
629
  font-weight: 600;
597
630
  pointer-events: none;
598
- text-shadow: 0 1px 3px rgba(0,0,0,0.4);
631
+ text-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
599
632
  letter-spacing: 0.2px;
600
633
  }
601
634
 
@@ -607,7 +640,7 @@ body {
607
640
  padding: 10px 14px;
608
641
  border-radius: 8px;
609
642
  font-size: 12px;
610
- box-shadow: 0 8px 24px rgba(0,0,0,0.25);
643
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
611
644
  z-index: 1000;
612
645
  pointer-events: none;
613
646
  transform: translateY(-50%);
@@ -679,7 +712,7 @@ body {
679
712
  border-radius: 8px;
680
713
  padding: 14px 16px;
681
714
  font-size: 12px;
682
- box-shadow: 0 4px 12px rgba(0,0,0,0.08);
715
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
683
716
  }
684
717
 
685
718
  .legend-item {
@@ -697,7 +730,7 @@ body {
697
730
  width: 14px;
698
731
  height: 14px;
699
732
  border-radius: 50%;
700
- box-shadow: 0 2px 4px rgba(0,0,0,0.15);
733
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
701
734
  }
702
735
 
703
736
  .component-boundary {
@@ -733,7 +766,7 @@ body {
733
766
  border-radius: 4px;
734
767
  font-size: 13px;
735
768
  font-weight: 500;
736
- box-shadow: 0 4px 12px rgba(0,0,0,0.3);
769
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
737
770
  z-index: 1000;
738
771
  animation: slideInFade 0.3s ease-out;
739
772
  }
package/src/types.ts CHANGED
@@ -17,7 +17,7 @@ export type {
17
17
  export interface GraphNode {
18
18
  id: string;
19
19
  name: string;
20
- type: "signal" | "computed" | "effect";
20
+ type: "signal" | "computed" | "effect" | "component";
21
21
  x: number;
22
22
  y: number;
23
23
  depth: number;