jev-devtools 0.2.0 → 0.3.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 (39) hide show
  1. package/README.md +47 -3
  2. package/lib/flow/index.d.ts +82 -0
  3. package/lib/flow/index.js +588 -0
  4. package/lib/server/flows.d.ts +25 -0
  5. package/lib/server/flows.js +115 -0
  6. package/lib/server/index.d.ts +8 -0
  7. package/lib/server/index.js +85 -1
  8. package/lib/server/recorder.d.ts +4 -1
  9. package/lib/server/recorder.js +12 -1
  10. package/lib/src/ChoiceProbabilities.js +1 -1
  11. package/lib/src/ConnectedInspector.js +13 -4
  12. package/lib/src/DiagramContent.d.ts +6 -0
  13. package/lib/src/DiagramContent.js +32 -0
  14. package/lib/src/FlowOverview.d.ts +7 -0
  15. package/lib/src/FlowOverview.js +228 -0
  16. package/lib/src/JevDevtools.d.ts +6 -1
  17. package/lib/src/JevDevtools.js +18 -4
  18. package/lib/src/TraceDiagram.d.ts +4 -1
  19. package/lib/src/TraceDiagram.js +30 -12
  20. package/lib/src/condition-inputs.d.ts +9 -0
  21. package/lib/src/condition-inputs.js +100 -0
  22. package/lib/src/flow-condition.d.ts +4 -0
  23. package/lib/src/flow-condition.js +47 -0
  24. package/lib/src/flow-model.d.ts +7 -2
  25. package/lib/src/flow-model.js +2 -11
  26. package/lib/src/flow-protocol.d.ts +68 -0
  27. package/lib/src/flow-protocol.js +1 -0
  28. package/lib/src/flow-stream.d.ts +8 -0
  29. package/lib/src/flow-stream.js +80 -0
  30. package/lib/src/inspector-styles.js +1 -1
  31. package/lib/src/inspector.css +501 -8
  32. package/lib/src/protocol.d.ts +5 -0
  33. package/lib/src/useDiagramViewport.d.ts +4 -0
  34. package/lib/src/useDiagramViewport.js +20 -3
  35. package/package.json +6 -2
  36. package/skills/setup-jev-devtools/SKILL.md +6 -2
  37. package/skills/setup-jev-devtools/references/flows.md +150 -0
  38. package/skills/setup-jev-devtools/references/nextjs.md +1 -1
  39. package/skills/setup-jev-devtools/references/verify.md +1 -1
@@ -831,7 +831,11 @@
831
831
  min-height: 100px;
832
832
  overflow: hidden;
833
833
  background-color: #0d0a0f;
834
- background-image: radial-gradient(#463449 0.8px, transparent 0.8px);
834
+ background-image: radial-gradient(
835
+ rgb(70 52 73 / var(--diagram-grid-opacity, 1))
836
+ calc(0.8px * var(--diagram-grid-weight, 1)),
837
+ transparent calc(0.8px * var(--diagram-grid-weight, 1))
838
+ );
835
839
  background-size: 20px 20px;
836
840
  border-block: 1px solid #302634;
837
841
  cursor: grab;
@@ -889,7 +893,7 @@
889
893
  position: absolute;
890
894
  border: 1.3px solid #f5f3f6;
891
895
  border-radius: 14px;
892
- padding: 20px;
896
+ padding: 16px;
893
897
  background: #0d0a0f;
894
898
  overflow: hidden;
895
899
  }
@@ -900,19 +904,20 @@
900
904
  500 10px ui-monospace,
901
905
  monospace;
902
906
  letter-spacing: 1px;
903
- margin: 0 0 10px;
907
+ margin: 0 0 7px;
904
908
  }
905
909
  .jev-devtools .lab-card h3 {
906
- font-size: 18px;
907
- font-weight: 500;
908
- line-height: 1.25;
909
- margin: 0 0 16px;
910
+ font-size: 16px;
911
+ font-weight: 600;
912
+ line-height: 1.3;
913
+ letter-spacing: -0.2px;
914
+ margin: 0 0 10px;
910
915
  overflow: hidden;
911
916
  white-space: nowrap;
912
917
  text-overflow: ellipsis;
913
918
  }
914
919
  .jev-devtools .lab-card p {
915
- font-size: 15px;
920
+ font-size: 13px;
916
921
  line-height: 1.4;
917
922
  margin: 10px 0;
918
923
  }
@@ -1143,6 +1148,7 @@
1143
1148
  }
1144
1149
  .jev-devtools .lab-choice-table-scroll {
1145
1150
  overflow: auto;
1151
+ overscroll-behavior: contain;
1146
1152
  min-height: 0;
1147
1153
  flex: 1;
1148
1154
  }
@@ -1264,3 +1270,490 @@
1264
1270
  animation: none;
1265
1271
  }
1266
1272
  }
1273
+
1274
+ /* Connected requests share one camera and one opt-in full request detail. */
1275
+ .whole-flow {
1276
+ min-width: 0;
1277
+ overflow: auto;
1278
+ }
1279
+ .flow-overview-viewport {
1280
+ position: relative;
1281
+ min-height: 240px;
1282
+ flex: 1 0 260px;
1283
+ overflow: hidden;
1284
+ touch-action: none;
1285
+ cursor: grab;
1286
+ background-image: radial-gradient(
1287
+ rgb(83 67 87 / var(--diagram-grid-opacity, 1))
1288
+ calc(1px * var(--diagram-grid-weight, 1)),
1289
+ transparent calc(1px * var(--diagram-grid-weight, 1))
1290
+ );
1291
+ background-size: 20px 20px;
1292
+ }
1293
+ .flow-overview-viewport[data-panning] {
1294
+ cursor: grabbing;
1295
+ }
1296
+ .flow-overview-canvas {
1297
+ position: absolute;
1298
+ transform-origin: 0 0;
1299
+ }
1300
+ .flow-connections {
1301
+ position: absolute;
1302
+ inset: 0;
1303
+ color: #c0b8c4;
1304
+ pointer-events: none;
1305
+ }
1306
+ .flow-step {
1307
+ position: absolute;
1308
+ width: 220px;
1309
+ min-height: 98px;
1310
+ display: flex;
1311
+ flex-direction: column;
1312
+ gap: 8px;
1313
+ text-align: left;
1314
+ padding: 14px;
1315
+ border: 1px solid #736476;
1316
+ border-radius: 12px;
1317
+ background: #151017;
1318
+ color: inherit;
1319
+ }
1320
+ .flow-step strong {
1321
+ overflow: hidden;
1322
+ text-overflow: ellipsis;
1323
+ white-space: nowrap;
1324
+ width: 100%;
1325
+ }
1326
+ .flow-step-description {
1327
+ overflow-wrap: anywhere;
1328
+ font-size: 12px;
1329
+ }
1330
+ .flow-step.flow-request {
1331
+ display: block;
1332
+ padding: 0;
1333
+ background: #0d0a0f;
1334
+ /* Skip offscreen paint/layout while preserving each question/expansion state. */
1335
+ content-visibility: auto;
1336
+ contain-intrinsic-size: auto 640px;
1337
+ }
1338
+ .flow-request-heading {
1339
+ width: 100%;
1340
+ height: 64px;
1341
+ display: flex;
1342
+ align-items: center;
1343
+ justify-content: space-between;
1344
+ text-align: left;
1345
+ padding: 12px 20px;
1346
+ border: 0;
1347
+ border-bottom: 1px solid #342a38;
1348
+ border-radius: 12px 12px 0 0;
1349
+ background: transparent;
1350
+ color: inherit;
1351
+ }
1352
+ .flow-request-heading .eyebrow {
1353
+ display: block;
1354
+ margin-bottom: 4px;
1355
+ }
1356
+ .jev-devtools .lab-flow-embedded {
1357
+ width: 1120px;
1358
+ display: block;
1359
+ }
1360
+ .jev-devtools .lab-flow-surface {
1361
+ position: relative;
1362
+ width: 1120px;
1363
+ }
1364
+ .jev-devtools .lab-flow-surface[hidden] {
1365
+ display: none;
1366
+ }
1367
+ .jev-devtools .lab-flow-embedded .lab-flow-canvas {
1368
+ will-change: auto;
1369
+ }
1370
+ .jev-devtools .lab-flow-embedded .lab-choice-browser {
1371
+ height: 520px;
1372
+ }
1373
+ .flow-step.selected {
1374
+ outline: 2px solid #e551ba;
1375
+ outline-offset: 3px;
1376
+ }
1377
+ .flow-step[data-status="completed"] {
1378
+ border-color: #e551ba;
1379
+ }
1380
+ .flow-step[data-status="running"] {
1381
+ border-color: #e551ba;
1382
+ background: #2b1728;
1383
+ }
1384
+ .flow-step[data-status="untaken"],
1385
+ .flow-step[data-status="blocked"] {
1386
+ color: #948797;
1387
+ border-color: #403746;
1388
+ background: #151017;
1389
+ }
1390
+ .flow-step[data-status="error"] {
1391
+ border-color: #f17c78;
1392
+ }
1393
+ .flow-step[data-status="cancelled"] {
1394
+ border-style: dashed;
1395
+ }
1396
+ .flow-selection {
1397
+ padding: 12px 20px;
1398
+ border-top: 1px solid #342a38;
1399
+ }
1400
+ .flow-selection p {
1401
+ margin: 6px 0;
1402
+ overflow-wrap: anywhere;
1403
+ }
1404
+ .flow-request-detail {
1405
+ min-height: 450px;
1406
+ border-top: 1px solid #534357;
1407
+ }
1408
+ .flow-request-detail > .request-detail {
1409
+ min-height: 450px;
1410
+ }
1411
+
1412
+ .flow-card-heading,
1413
+ .flow-card-footer,
1414
+ .flow-join-summary,
1415
+ .flow-lane {
1416
+ display: flex;
1417
+ align-items: center;
1418
+ justify-content: space-between;
1419
+ gap: 8px;
1420
+ width: 100%;
1421
+ }
1422
+ .flow-card-heading {
1423
+ justify-content: flex-start;
1424
+ }
1425
+ .flow-kind-icon {
1426
+ width: 26px;
1427
+ height: 26px;
1428
+ color: #e551ba;
1429
+ flex-shrink: 0;
1430
+ }
1431
+ .flow-kind-icon svg {
1432
+ width: 100%;
1433
+ height: 100%;
1434
+ }
1435
+ .flow-card-heading .eyebrow {
1436
+ font-size: 10px;
1437
+ }
1438
+ .flow-status {
1439
+ margin-left: auto;
1440
+ font-size: 10px;
1441
+ border-radius: 20px;
1442
+ padding: 4px 8px;
1443
+ background: #342031;
1444
+ color: #f0b7e0;
1445
+ }
1446
+ .flow-step[data-status="untaken"] .flow-status {
1447
+ background: #241e28;
1448
+ color: #948797;
1449
+ }
1450
+ .flow-step[data-status="untaken"] .flow-kind-icon {
1451
+ color: #948797;
1452
+ }
1453
+ .flow-step > strong {
1454
+ white-space: normal;
1455
+ font-size: 18px;
1456
+ line-height: 1.25;
1457
+ display: -webkit-box;
1458
+ -webkit-line-clamp: 2;
1459
+ -webkit-box-orient: vertical;
1460
+ overflow-wrap: anywhere;
1461
+ }
1462
+ .flow-step-description {
1463
+ line-height: 1.5;
1464
+ color: #b9aebf;
1465
+ display: -webkit-box;
1466
+ -webkit-line-clamp: 3;
1467
+ -webkit-box-orient: vertical;
1468
+ overflow: hidden;
1469
+ }
1470
+ .flow-card-footer {
1471
+ margin-top: auto;
1472
+ padding-top: 10px;
1473
+ border-top: 1px solid #342a38;
1474
+ font-size: 10px;
1475
+ color: #a092a7;
1476
+ }
1477
+ .flow-card-footer code {
1478
+ max-width: 45%;
1479
+ overflow: hidden;
1480
+ text-overflow: ellipsis;
1481
+ white-space: nowrap;
1482
+ }
1483
+ .flow-card-footer small {
1484
+ text-align: right;
1485
+ }
1486
+ .flow-join-summary {
1487
+ margin-top: 4px;
1488
+ font-size: 11px;
1489
+ color: #c9b9cd;
1490
+ }
1491
+ .flow-join-summary > span {
1492
+ color: #ef9ed9;
1493
+ font-variant-numeric: tabular-nums;
1494
+ }
1495
+ .flow-lanes {
1496
+ display: flex;
1497
+ flex-direction: column;
1498
+ gap: 4px;
1499
+ width: 100%;
1500
+ }
1501
+ .flow-lane {
1502
+ border-radius: 5px;
1503
+ background: #211923;
1504
+ padding: 6px 8px;
1505
+ font-size: 11px;
1506
+ min-height: 28px;
1507
+ }
1508
+ .flow-lane > span {
1509
+ overflow: hidden;
1510
+ text-overflow: ellipsis;
1511
+ white-space: nowrap;
1512
+ }
1513
+ .flow-lane > span > span {
1514
+ color: #ef9ed9;
1515
+ margin-right: 4px;
1516
+ }
1517
+ .flow-lane small {
1518
+ color: #b9aebf;
1519
+ }
1520
+ .flow-lane-details {
1521
+ padding-left: 18px;
1522
+ font-size: 12px;
1523
+ }
1524
+ .flow-request-heading > span:first-child {
1525
+ min-width: 0;
1526
+ max-width: 80%;
1527
+ }
1528
+ .flow-request-description {
1529
+ display: -webkit-box;
1530
+ -webkit-line-clamp: 2;
1531
+ -webkit-box-orient: vertical;
1532
+ overflow: hidden;
1533
+ font-size: 12px;
1534
+ line-height: 1.5;
1535
+ color: #b9aebf;
1536
+ margin-top: 5px;
1537
+ overflow-wrap: anywhere;
1538
+ }
1539
+
1540
+ /* Structured context stays within the card; its gestures never move the camera. */
1541
+ .jev-devtools .lab-content-card {
1542
+ display: flex;
1543
+ flex-direction: column;
1544
+ padding: 14px;
1545
+ }
1546
+ .jev-devtools .lab-content-card h3 {
1547
+ font-size: 14px;
1548
+ margin-bottom: 8px;
1549
+ flex-shrink: 0;
1550
+ }
1551
+ .jev-devtools .lab-content-card .lab-eyebrow {
1552
+ font-size: 9px;
1553
+ margin-bottom: 5px;
1554
+ flex-shrink: 0;
1555
+ }
1556
+ .jev-devtools .diagram-content {
1557
+ min-height: 0;
1558
+ flex: 1;
1559
+ overflow: auto;
1560
+ overscroll-behavior: contain;
1561
+ touch-action: pan-y;
1562
+ scrollbar-width: thin;
1563
+ scrollbar-color: #514256 transparent;
1564
+ padding-right: 4px;
1565
+ font-size: 12px;
1566
+ line-height: 1.5;
1567
+ color: #d5cdd9;
1568
+ cursor: auto;
1569
+ user-select: text;
1570
+ }
1571
+ .jev-devtools .diagram-content:focus-visible {
1572
+ outline: 1px solid #e551ba;
1573
+ outline-offset: -1px;
1574
+ border-radius: 3px;
1575
+ }
1576
+ .jev-devtools .diagram-property {
1577
+ display: flex;
1578
+ justify-content: space-between;
1579
+ align-items: baseline;
1580
+ gap: 8px;
1581
+ padding: 5px 0;
1582
+ border-bottom: 1px solid #261e2a;
1583
+ }
1584
+ .jev-devtools .diagram-property-text {
1585
+ display: block;
1586
+ }
1587
+ .jev-devtools .diagram-property-key {
1588
+ font:
1589
+ 10px/1.5 ui-monospace,
1590
+ monospace;
1591
+ color: #c8a8c8;
1592
+ overflow-wrap: anywhere;
1593
+ min-width: 0;
1594
+ }
1595
+ .jev-devtools .diagram-property-text > .diagram-property-key {
1596
+ display: block;
1597
+ margin-bottom: 2px;
1598
+ }
1599
+ .jev-devtools .diagram-text-value {
1600
+ display: block;
1601
+ white-space: pre-wrap;
1602
+ overflow-wrap: anywhere;
1603
+ }
1604
+ .jev-devtools .diagram-text-preview {
1605
+ display: -webkit-box;
1606
+ -webkit-line-clamp: 3;
1607
+ -webkit-box-orient: vertical;
1608
+ overflow: hidden;
1609
+ }
1610
+ .jev-devtools .diagram-value-number {
1611
+ color: #a6d6ca;
1612
+ font:
1613
+ 11px/1.5 ui-monospace,
1614
+ monospace;
1615
+ font-variant-numeric: tabular-nums;
1616
+ overflow-wrap: anywhere;
1617
+ }
1618
+ .jev-devtools .diagram-value-boolean {
1619
+ color: #c4b1ed;
1620
+ font:
1621
+ 11px/1.5 ui-monospace,
1622
+ monospace;
1623
+ }
1624
+ .jev-devtools .diagram-value-empty {
1625
+ color: #9c8ca4;
1626
+ font:
1627
+ 10px/1.5 ui-monospace,
1628
+ monospace;
1629
+ }
1630
+ .jev-devtools .diagram-property-disclosure {
1631
+ width: 100%;
1632
+ display: flex;
1633
+ align-items: baseline;
1634
+ gap: 5px;
1635
+ padding: 6px 0;
1636
+ border: 0;
1637
+ border-bottom: 1px solid #261e2a;
1638
+ border-radius: 0;
1639
+ background: transparent;
1640
+ text-align: left;
1641
+ color: inherit;
1642
+ }
1643
+ .jev-devtools .diagram-property-disclosure:hover {
1644
+ background: #211723;
1645
+ }
1646
+ .jev-devtools .diagram-property-caret {
1647
+ color: #ab8aab;
1648
+ flex-shrink: 0;
1649
+ }
1650
+ .jev-devtools .diagram-property-type {
1651
+ color: #9c8ca4;
1652
+ font:
1653
+ 10px/1.5 ui-monospace,
1654
+ monospace;
1655
+ margin-left: auto;
1656
+ flex-shrink: 0;
1657
+ }
1658
+ .jev-devtools .diagram-property-children {
1659
+ margin-left: 4px;
1660
+ padding-left: 9px;
1661
+ border-left: 1px solid #49324b;
1662
+ }
1663
+ .jev-devtools .diagram-content-toggle {
1664
+ display: block;
1665
+ padding: 3px 0;
1666
+ border: 0;
1667
+ border-radius: 0;
1668
+ background: transparent;
1669
+ color: #e9a4d7;
1670
+ font-size: 10px;
1671
+ text-align: left;
1672
+ }
1673
+ .jev-devtools .diagram-content-toggle:hover {
1674
+ color: #ffd9f3;
1675
+ text-decoration: underline;
1676
+ }
1677
+
1678
+ /* Noul is a probability of true; its two outputs carry p and 1-p.
1679
+ Keep the fill opaque, with readable text even at zero probability. */
1680
+ .jev-devtools .lab-noul-output {
1681
+ border-color: rgb(
1682
+ 245 243 246 / calc(0.35 + 0.65 * var(--lab-noul-probability, 1))
1683
+ );
1684
+ }
1685
+ .jev-devtools .lab-noul-output > h3,
1686
+ .jev-devtools .lab-noul-output > p {
1687
+ opacity: calc(0.65 + 0.35 * var(--lab-noul-probability, 1));
1688
+ }
1689
+
1690
+ .flow-condition-rule {
1691
+ display: block;
1692
+ padding: 10px 12px;
1693
+ border: 1px solid #49324b;
1694
+ border-radius: 6px;
1695
+ background: #211723;
1696
+ width: 100%;
1697
+ }
1698
+ .flow-condition-label {
1699
+ display: block;
1700
+ font-size: 10px;
1701
+ color: #ba9dbe;
1702
+ margin-bottom: 5px;
1703
+ }
1704
+ .flow-condition-rule code {
1705
+ display: -webkit-box;
1706
+ -webkit-line-clamp: 3;
1707
+ -webkit-box-orient: vertical;
1708
+ overflow: hidden;
1709
+ overflow-wrap: anywhere;
1710
+ font:
1711
+ 12px/1.6 ui-monospace,
1712
+ monospace;
1713
+ color: #f2d7eb;
1714
+ }
1715
+
1716
+ .flow-condition-incoming {
1717
+ display: block;
1718
+ padding-bottom: 10px;
1719
+ margin-bottom: 10px;
1720
+ border-bottom: 1px solid #49324b;
1721
+ }
1722
+ .flow-condition-input {
1723
+ display: flex;
1724
+ justify-content: space-between;
1725
+ align-items: baseline;
1726
+ gap: 12px;
1727
+ font-size: 12px;
1728
+ padding: 3px 0;
1729
+ }
1730
+ .flow-condition-input > span {
1731
+ overflow: hidden;
1732
+ text-overflow: ellipsis;
1733
+ white-space: nowrap;
1734
+ }
1735
+ .flow-condition-input > b {
1736
+ font-size: 17px;
1737
+ font-weight: 600;
1738
+ color: #f2d7eb;
1739
+ overflow: hidden;
1740
+ text-overflow: ellipsis;
1741
+ white-space: nowrap;
1742
+ max-width: 65%;
1743
+ }
1744
+ .flow-input-details {
1745
+ font-size: 12px;
1746
+ }
1747
+ .flow-input-details > div {
1748
+ display: flex;
1749
+ gap: 16px;
1750
+ margin: 6px 0;
1751
+ }
1752
+ .flow-input-details code {
1753
+ color: #a092a7;
1754
+ margin-left: 8px;
1755
+ }
1756
+ .flow-input-details dd {
1757
+ margin: 0;
1758
+ overflow-wrap: anywhere;
1759
+ }
@@ -25,6 +25,11 @@ export type Trace = {
25
25
  version: 1;
26
26
  id: string;
27
27
  session: string;
28
+ flow?: {
29
+ runId: string;
30
+ stepId: string;
31
+ executionId: string;
32
+ };
28
33
  label: string;
29
34
  source: "sample" | "live";
30
35
  startedAt: number;
@@ -7,4 +7,8 @@ export declare function useDiagramViewport(width: number, height: number, scene:
7
7
  zoomBy: (factor: number, point?: Point) => void;
8
8
  fit: () => void;
9
9
  preserveCamera: () => void;
10
+ focusArea: (area: Point & {
11
+ width: number;
12
+ height: number;
13
+ }) => void;
10
14
  };
@@ -23,7 +23,12 @@ export function useDiagramViewport(width, height, scene) {
23
23
  canvas.current.style.transform = `translate(${x}px, ${y}px) scale(${scale})`;
24
24
  if (viewport.current) {
25
25
  viewport.current.style.backgroundPosition = `${x}px ${y}px`;
26
- viewport.current.style.backgroundSize = `${20 * scale}px ${20 * scale}px`;
26
+ // Thin out the decorative grid at overview zooms instead of packing
27
+ // more dots behind an increasingly small diagram.
28
+ const grid = 24 * Math.max(scale, 1 / Math.sqrt(scale));
29
+ viewport.current.style.backgroundSize = `${grid}px ${grid}px`;
30
+ viewport.current.style.setProperty("--diagram-grid-opacity", String(0.45 * Math.min(1, Math.sqrt(scale))));
31
+ viewport.current.style.setProperty("--diagram-grid-weight", String(Math.max(0.5, Math.min(0.75, Math.sqrt(scale)))));
27
32
  }
28
33
  setZoom(Math.round(scale * 100));
29
34
  });
@@ -38,6 +43,16 @@ export function useDiagramViewport(width, height, scene) {
38
43
  const preserveCamera = useCallback(() => {
39
44
  fitted.current = false;
40
45
  }, []);
46
+ const focusArea = useCallback((area) => {
47
+ fitted.current = false;
48
+ const focused = fitCamera(size.current, area);
49
+ camera.current = {
50
+ ...focused,
51
+ x: focused.x - area.x * focused.scale,
52
+ y: focused.y - area.y * focused.scale,
53
+ };
54
+ paint();
55
+ }, [paint]);
41
56
  useEffect(() => {
42
57
  if (fitted.current)
43
58
  fit();
@@ -76,7 +91,7 @@ export function useDiagramViewport(width, height, scene) {
76
91
  const down = (event) => {
77
92
  if (event.button !== 0 ||
78
93
  pointers.size >= 2 ||
79
- event.target.closest("button, input, select, textarea, a, summary"))
94
+ event.target.closest("button, input, select, textarea, a, summary, [data-diagram-scroll]"))
80
95
  return;
81
96
  event.preventDefault();
82
97
  el.focus({ preventScroll: true });
@@ -113,6 +128,8 @@ export function useDiagramViewport(width, height, scene) {
113
128
  delete el.dataset.panning;
114
129
  };
115
130
  const wheel = (event) => {
131
+ if (event.target.closest("[data-diagram-scroll]"))
132
+ return;
116
133
  event.preventDefault();
117
134
  const units = event.deltaMode === 1
118
135
  ? 16
@@ -179,5 +196,5 @@ export function useDiagramViewport(width, height, scene) {
179
196
  frame.current = null;
180
197
  };
181
198
  }, [fit, zoomBy, paint, scene]);
182
- return { viewport, canvas, zoom, zoomBy, fit, preserveCamera };
199
+ return { viewport, canvas, zoom, zoomBy, fit, preserveCamera, focusArea };
183
200
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jev-devtools",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22.12"
@@ -19,7 +19,11 @@
19
19
  "types": "./lib/server/index.d.ts",
20
20
  "import": "./lib/server/index.js"
21
21
  },
22
- "./styles.css": "./lib/src/inspector.css"
22
+ "./styles.css": "./lib/src/inspector.css",
23
+ "./flow": {
24
+ "types": "./lib/flow/index.d.ts",
25
+ "import": "./lib/flow/index.js"
26
+ }
23
27
  },
24
28
  "files": [
25
29
  "lib",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: setup-jev-devtools
3
- description: Install and verify Jev DevTools in an existing React application with a Node backend or Next.js App Router. Use when asked to add the Jev request inspector, wire capture, or troubleshoot its setup.
3
+ description: Install and verify Jev DevTools in an existing React application with a Node backend or Next.js App Router. Use when asked to add the Jev request inspector, wire capture, build connected Jev flows with branches and parallel checks, or troubleshoot its setup.
4
4
  ---
5
5
 
6
6
  # Set up Jev DevTools
@@ -17,9 +17,13 @@ Inspect the app's package manager/lockfile, React and Node versions, root compon
17
17
 
18
18
  Install `jev-devtools` using the app's existing package manager (npm: `npm install jev-devtools`; pnpm: `pnpm add jev-devtools`; Yarn: `yarn add jev-devtools`). Install in the workspace(s) that actually import it. It is a regular dependency when application modules import it, even though capture/display are development-only. React 18/19 and Node 22.12+ are required. Do not replace the app's framework or upgrade unrelated dependencies to force compatibility.
19
19
 
20
+ ## Optional connected flow API (0.3.0+)
21
+
22
+ When asked to build or inspect connected calls, read [references/flows.md](references/flows.md) after the framework recipe. It includes an executable parallel-checks/branch example, step titles/descriptions, scope and execution rules, sample verification, and privacy limits. Preserve existing application policies; do not convert unrelated calls just to install the drawer. Flow execution is server-only and continues even with the inspector disabled.
23
+
20
24
  ## Integration invariants
21
25
 
22
- - Use only public imports: `jev-devtools`, `jev-devtools/styles.css`, and server-only `jev-devtools/server`. No provider, TanStack, Tailwind, or shadcn installation is needed.
26
+ - Use only public imports: `jev-devtools`, `jev-devtools/styles.css`, and server-only `jev-devtools/server` and `jev-devtools/flow`. No provider, TanStack DevTools shell, Tailwind, or shadcn setup is needed; TanStack Virtual is an internal dependency.
23
27
  - Mount `<JevDevtools />` once with an explicit development flag and import the CSS. Server capture must also be explicitly enabled for development; both default to off.
24
28
  - Create a shared adapter; the inspector routes and application client must use it in the same Node realm/process. Replace existing `new TypeSafeClient(options)` construction with `inspector.createClient(options)` while preserving keys, transport options, retries, and call semantics. Keep existing `systemOne(...)` inputs and application behavior.
25
29
  - Browser and server `endpoint` paths must agree (default `/api/jev-devtools`). Route the base path and its subpaths. The server `origin` must exactly match the browser scheme, hostname, and port. A browser endpoint is a same-origin path, not an absolute backend URL.