@tomaszjarosz/react-visualizers 0.2.14 → 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.
package/dist/index.js CHANGED
@@ -501,17 +501,20 @@ const ControlPanel = ({
501
501
  extraControls
502
502
  }) => {
503
503
  const colors = ACCENT_COLORS$4[accentColor];
504
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between flex-wrap gap-3", children: [
505
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
504
+ const speedLabelId = React.useId();
505
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between flex-wrap gap-3", role: "toolbar", "aria-label": "Playback controls", children: [
506
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", role: "group", "aria-label": "Navigation", children: [
506
507
  isPlaying && /* @__PURE__ */ jsxs(
507
508
  "span",
508
509
  {
509
510
  className: `flex items-center gap-1 text-xs ${colors.playing} font-medium`,
511
+ "aria-live": "polite",
510
512
  children: [
511
513
  /* @__PURE__ */ jsx(
512
514
  "span",
513
515
  {
514
- className: `w-2 h-2 ${colors.playingDot} rounded-full animate-pulse`
516
+ className: `w-2 h-2 ${colors.playingDot} rounded-full animate-pulse`,
517
+ "aria-hidden": "true"
515
518
  }
516
519
  ),
517
520
  "Playing"
@@ -524,7 +527,9 @@ const ControlPanel = ({
524
527
  onClick: onPlayPause,
525
528
  className: `p-2 text-white rounded-lg transition-colors ${isPlaying ? colors.buttonActive : colors.button}`,
526
529
  title: "Play/Pause (P)",
527
- children: isPlaying ? /* @__PURE__ */ jsx(Pause, { className: "w-4 h-4" }) : /* @__PURE__ */ jsx(Play, { className: "w-4 h-4" })
530
+ "aria-label": isPlaying ? "Pause" : "Play",
531
+ "aria-pressed": isPlaying,
532
+ children: isPlaying ? /* @__PURE__ */ jsx(Pause, { className: "w-4 h-4", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Play, { className: "w-4 h-4", "aria-hidden": "true" })
528
533
  }
529
534
  ),
530
535
  /* @__PURE__ */ jsx(
@@ -534,7 +539,8 @@ const ControlPanel = ({
534
539
  disabled: isPlaying || currentStep <= 0,
535
540
  className: "p-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors disabled:opacity-50",
536
541
  title: "Step Back ([)",
537
- children: /* @__PURE__ */ jsx(SkipBack, { className: "w-4 h-4" })
542
+ "aria-label": "Step back",
543
+ children: /* @__PURE__ */ jsx(SkipBack, { className: "w-4 h-4", "aria-hidden": "true" })
538
544
  }
539
545
  ),
540
546
  /* @__PURE__ */ jsx(
@@ -544,7 +550,8 @@ const ControlPanel = ({
544
550
  disabled: isPlaying || currentStep >= totalSteps - 1,
545
551
  className: "p-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors disabled:opacity-50",
546
552
  title: "Step Forward (])",
547
- children: /* @__PURE__ */ jsx(SkipForward, { className: "w-4 h-4" })
553
+ "aria-label": "Step forward",
554
+ children: /* @__PURE__ */ jsx(SkipForward, { className: "w-4 h-4", "aria-hidden": "true" })
548
555
  }
549
556
  ),
550
557
  /* @__PURE__ */ jsx(
@@ -553,7 +560,8 @@ const ControlPanel = ({
553
560
  onClick: onReset,
554
561
  className: "p-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors",
555
562
  title: "Reset (R)",
556
- children: /* @__PURE__ */ jsx(RotateCcw, { className: "w-4 h-4" })
563
+ "aria-label": "Reset",
564
+ children: /* @__PURE__ */ jsx(RotateCcw, { className: "w-4 h-4", "aria-hidden": "true" })
557
565
  }
558
566
  ),
559
567
  showShuffle && onShuffle && /* @__PURE__ */ jsx(
@@ -563,13 +571,14 @@ const ControlPanel = ({
563
571
  disabled: isPlaying,
564
572
  className: `bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors disabled:opacity-50 ${shuffleLabel ? "px-3 py-2 text-sm" : "p-2"}`,
565
573
  title: shuffleLabel || "Shuffle",
566
- children: shuffleLabel || /* @__PURE__ */ jsx(Shuffle, { className: "w-4 h-4" })
574
+ "aria-label": shuffleLabel || "Shuffle",
575
+ children: shuffleLabel || /* @__PURE__ */ jsx(Shuffle, { className: "w-4 h-4", "aria-hidden": "true" })
567
576
  }
568
577
  )
569
578
  ] }),
570
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
579
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", role: "group", "aria-label": "Speed control", children: [
571
580
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
572
- /* @__PURE__ */ jsx("label", { className: "text-xs text-gray-500", children: "Speed" }),
581
+ /* @__PURE__ */ jsx("label", { id: speedLabelId, className: "text-xs text-gray-500", children: "Speed" }),
573
582
  /* @__PURE__ */ jsx(
574
583
  "input",
575
584
  {
@@ -578,7 +587,11 @@ const ControlPanel = ({
578
587
  max: "100",
579
588
  value: speed,
580
589
  onChange: (e) => onSpeedChange(Number(e.target.value)),
581
- className: "w-24 h-1 bg-gray-300 rounded-lg appearance-none cursor-pointer"
590
+ className: "w-24 h-1 bg-gray-300 rounded-lg appearance-none cursor-pointer",
591
+ "aria-labelledby": speedLabelId,
592
+ "aria-valuemin": 1,
593
+ "aria-valuemax": 100,
594
+ "aria-valuenow": speed
582
595
  }
583
596
  )
584
597
  ] }),
@@ -1446,11 +1459,11 @@ const InterviewModePanel = ({
1446
1459
  }) => {
1447
1460
  const colors = ACCENT_COLORS[accentColor];
1448
1461
  if (isComplete) {
1449
- return /* @__PURE__ */ jsx("div", { className: "p-6 bg-gradient-to-br from-gray-50 to-white rounded-xl border border-gray-200", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
1450
- /* @__PURE__ */ jsx(Trophy, { className: "w-16 h-16 mx-auto mb-4 text-yellow-500" }),
1462
+ return /* @__PURE__ */ jsx("div", { className: "p-6 bg-gradient-to-br from-gray-50 to-white rounded-xl border border-gray-200", role: "region", "aria-label": "Quiz results", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
1463
+ /* @__PURE__ */ jsx(Trophy, { className: "w-16 h-16 mx-auto mb-4 text-yellow-500", "aria-hidden": "true" }),
1451
1464
  /* @__PURE__ */ jsx("h3", { className: "text-2xl font-bold text-gray-900 mb-2", children: "Interview Complete!" }),
1452
- /* @__PURE__ */ jsxs("div", { className: "mb-6", children: [
1453
- /* @__PURE__ */ jsxs("div", { className: "text-5xl font-bold text-gray-900 mb-1", children: [
1465
+ /* @__PURE__ */ jsxs("div", { className: "mb-6", "aria-live": "polite", children: [
1466
+ /* @__PURE__ */ jsxs("div", { className: "text-5xl font-bold text-gray-900 mb-1", "aria-label": `Score: ${score.percentage} percent`, children: [
1454
1467
  score.percentage,
1455
1468
  "%"
1456
1469
  ] }),
@@ -1466,7 +1479,7 @@ const InterviewModePanel = ({
1466
1479
  ${score.percentage >= 80 ? "bg-green-100 text-green-700" : ""}
1467
1480
  ${score.percentage >= 60 && score.percentage < 80 ? "bg-yellow-100 text-yellow-700" : ""}
1468
1481
  ${score.percentage < 60 ? "bg-red-100 text-red-700" : ""}
1469
- `, children: [
1482
+ `, role: "status", children: [
1470
1483
  score.percentage >= 80 && "🎉 Excellent! Ready for interviews!",
1471
1484
  score.percentage >= 60 && score.percentage < 80 && "👍 Good job! Keep practicing!",
1472
1485
  score.percentage < 60 && "📚 Review the concepts and try again!"
@@ -1475,13 +1488,14 @@ const InterviewModePanel = ({
1475
1488
  "button",
1476
1489
  {
1477
1490
  onClick: onRestart,
1491
+ "aria-label": "Restart quiz",
1478
1492
  className: `
1479
1493
  flex items-center gap-2 mx-auto px-6 py-3 rounded-lg
1480
1494
  text-white font-medium transition-colors
1481
1495
  ${colors.button}
1482
1496
  `,
1483
1497
  children: [
1484
- /* @__PURE__ */ jsx(RotateCcw, { className: "w-5 h-5" }),
1498
+ /* @__PURE__ */ jsx(RotateCcw, { className: "w-5 h-5", "aria-hidden": "true" }),
1485
1499
  "Try Again"
1486
1500
  ]
1487
1501
  }
@@ -1521,7 +1535,7 @@ const InterviewModePanel = ({
1521
1535
  ] }),
1522
1536
  /* @__PURE__ */ jsxs("div", { className: "p-4", children: [
1523
1537
  /* @__PURE__ */ jsx("h4", { className: "text-lg font-semibold text-gray-900 mb-4", children: currentQuestion.question }),
1524
- /* @__PURE__ */ jsx("div", { className: "space-y-2 mb-4", children: currentQuestion.options.map((option, index) => {
1538
+ /* @__PURE__ */ jsx("div", { className: "space-y-2 mb-4", role: "radiogroup", "aria-label": "Answer options", children: currentQuestion.options.map((option, index) => {
1525
1539
  const isSelected = selectedAnswer === index;
1526
1540
  const isCorrect = index === currentQuestion.correctAnswer;
1527
1541
  const showResult = showExplanation;
@@ -1540,6 +1554,9 @@ const InterviewModePanel = ({
1540
1554
  {
1541
1555
  onClick: () => !isAnswered && onSelectAnswer(index),
1542
1556
  disabled: isAnswered,
1557
+ role: "radio",
1558
+ "aria-checked": isSelected,
1559
+ "aria-disabled": isAnswered,
1543
1560
  className: `
1544
1561
  w-full p-3 rounded-lg border-2 text-left transition-all
1545
1562
  flex items-center gap-3
@@ -1547,24 +1564,31 @@ const InterviewModePanel = ({
1547
1564
  ${isAnswered ? "cursor-default" : "cursor-pointer"}
1548
1565
  `,
1549
1566
  children: [
1550
- /* @__PURE__ */ jsxs("span", { className: `
1551
- w-6 h-6 rounded-full flex items-center justify-center text-xs font-bold
1552
- ${showResult && isCorrect ? "bg-green-500 text-white" : ""}
1553
- ${showResult && isSelected && !isCorrect ? "bg-red-500 text-white" : ""}
1554
- ${!showResult ? "bg-gray-200 text-gray-600" : ""}
1555
- `, children: [
1556
- showResult && isCorrect && /* @__PURE__ */ jsx(CircleCheckBig, { className: "w-4 h-4" }),
1557
- showResult && isSelected && !isCorrect && /* @__PURE__ */ jsx(CircleX, { className: "w-4 h-4" }),
1558
- !showResult && String.fromCharCode(65 + index)
1559
- ] }),
1567
+ /* @__PURE__ */ jsxs(
1568
+ "span",
1569
+ {
1570
+ className: `
1571
+ w-6 h-6 rounded-full flex items-center justify-center text-xs font-bold
1572
+ ${showResult && isCorrect ? "bg-green-500 text-white" : ""}
1573
+ ${showResult && isSelected && !isCorrect ? "bg-red-500 text-white" : ""}
1574
+ ${!showResult ? "bg-gray-200 text-gray-600" : ""}
1575
+ `,
1576
+ "aria-hidden": "true",
1577
+ children: [
1578
+ showResult && isCorrect && /* @__PURE__ */ jsx(CircleCheckBig, { className: "w-4 h-4" }),
1579
+ showResult && isSelected && !isCorrect && /* @__PURE__ */ jsx(CircleX, { className: "w-4 h-4" }),
1580
+ !showResult && String.fromCharCode(65 + index)
1581
+ ]
1582
+ }
1583
+ ),
1560
1584
  /* @__PURE__ */ jsx("span", { className: `flex-1 ${showResult && isCorrect ? "font-medium text-green-700" : ""}`, children: option })
1561
1585
  ]
1562
1586
  },
1563
1587
  index
1564
1588
  );
1565
1589
  }) }),
1566
- currentQuestion.hint && !showExplanation && /* @__PURE__ */ jsx("div", { className: "mb-4", children: showHint ? /* @__PURE__ */ jsx("div", { className: "p-3 bg-yellow-50 rounded-lg border border-yellow-200", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
1567
- /* @__PURE__ */ jsx(Lightbulb, { className: "w-5 h-5 text-yellow-600 flex-shrink-0 mt-0.5" }),
1590
+ currentQuestion.hint && !showExplanation && /* @__PURE__ */ jsx("div", { className: "mb-4", children: showHint ? /* @__PURE__ */ jsx("div", { className: "p-3 bg-yellow-50 rounded-lg border border-yellow-200", role: "note", "aria-label": "Hint", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
1591
+ /* @__PURE__ */ jsx(Lightbulb, { className: "w-5 h-5 text-yellow-600 flex-shrink-0 mt-0.5", "aria-hidden": "true" }),
1568
1592
  /* @__PURE__ */ jsxs("div", { children: [
1569
1593
  /* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-yellow-800 mb-1", children: "Hint" }),
1570
1594
  /* @__PURE__ */ jsx("div", { className: "text-sm text-yellow-700", children: currentQuestion.hint })
@@ -1574,8 +1598,9 @@ const InterviewModePanel = ({
1574
1598
  {
1575
1599
  onClick: onUseHint,
1576
1600
  className: "flex items-center gap-2 text-sm text-yellow-600 hover:text-yellow-700",
1601
+ "aria-label": "Show hint for this question",
1577
1602
  children: [
1578
- /* @__PURE__ */ jsx(Lightbulb, { className: "w-4 h-4" }),
1603
+ /* @__PURE__ */ jsx(Lightbulb, { className: "w-4 h-4", "aria-hidden": "true" }),
1579
1604
  "Show hint"
1580
1605
  ]
1581
1606
  }
@@ -1591,19 +1616,20 @@ const InterviewModePanel = ({
1591
1616
  ] })
1592
1617
  ] }) })
1593
1618
  ] }),
1594
- /* @__PURE__ */ jsxs("div", { className: "px-4 py-3 bg-gray-50 border-t border-gray-200 flex items-center justify-between", children: [
1619
+ /* @__PURE__ */ jsxs("nav", { className: "px-4 py-3 bg-gray-50 border-t border-gray-200 flex items-center justify-between", "aria-label": "Question navigation", children: [
1595
1620
  /* @__PURE__ */ jsxs(
1596
1621
  "button",
1597
1622
  {
1598
1623
  onClick: onPreviousQuestion,
1599
1624
  disabled: currentQuestionIndex === 0,
1625
+ "aria-label": "Previous question",
1600
1626
  className: `
1601
1627
  flex items-center gap-1 px-3 py-2 rounded-lg text-sm font-medium
1602
1628
  transition-colors
1603
1629
  ${currentQuestionIndex === 0 ? "text-gray-300 cursor-not-allowed" : "text-gray-600 hover:bg-gray-200"}
1604
1630
  `,
1605
1631
  children: [
1606
- /* @__PURE__ */ jsx(ChevronLeft, { className: "w-4 h-4" }),
1632
+ /* @__PURE__ */ jsx(ChevronLeft, { className: "w-4 h-4", "aria-hidden": "true" }),
1607
1633
  "Previous"
1608
1634
  ]
1609
1635
  }
@@ -1613,6 +1639,7 @@ const InterviewModePanel = ({
1613
1639
  {
1614
1640
  onClick: onNextQuestion,
1615
1641
  disabled: currentQuestionIndex === totalQuestions - 1,
1642
+ "aria-label": currentQuestionIndex === totalQuestions - 1 ? "Finish quiz" : "Next question",
1616
1643
  className: `
1617
1644
  flex items-center gap-1 px-4 py-2 rounded-lg text-sm font-medium
1618
1645
  transition-colors text-white
@@ -1620,7 +1647,7 @@ const InterviewModePanel = ({
1620
1647
  `,
1621
1648
  children: [
1622
1649
  currentQuestionIndex === totalQuestions - 1 ? "Finish" : "Next",
1623
- /* @__PURE__ */ jsx(ChevronRight, { className: "w-4 h-4" })
1650
+ /* @__PURE__ */ jsx(ChevronRight, { className: "w-4 h-4", "aria-hidden": "true" })
1624
1651
  ]
1625
1652
  }
1626
1653
  )
@@ -1729,7 +1756,7 @@ const BINARY_SEARCH_CODE = [
1729
1756
  " right = mid - 1",
1730
1757
  " return -1 // Not found"
1731
1758
  ];
1732
- const LEGEND_ITEMS$y = [
1759
+ const LEGEND_ITEMS$A = [
1733
1760
  { color: "bg-blue-100", label: "Search Space" },
1734
1761
  { color: "bg-gray-200", label: "Eliminated" },
1735
1762
  { color: "bg-purple-500", label: "Mid" },
@@ -2129,7 +2156,7 @@ const BinarySearchVisualizerComponent = ({
2129
2156
  accentColor: "green"
2130
2157
  }
2131
2158
  ),
2132
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$y })
2159
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$A })
2133
2160
  ] })
2134
2161
  ]
2135
2162
  }
@@ -2194,7 +2221,7 @@ const ALGORITHM_CODE = {
2194
2221
  " else arr[k++] = right[j++]"
2195
2222
  ]
2196
2223
  };
2197
- const LEGEND_ITEMS$x = [
2224
+ const LEGEND_ITEMS$z = [
2198
2225
  { color: "bg-blue-500", label: "Default" },
2199
2226
  { color: "bg-yellow-400", label: "Comparing" },
2200
2227
  { color: "bg-red-500", label: "Swapping" },
@@ -3056,7 +3083,7 @@ const SortingVisualizerComponent = ({
3056
3083
  extraControls: sizeControl
3057
3084
  }
3058
3085
  ),
3059
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$x })
3086
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$z })
3060
3087
  ] })
3061
3088
  ]
3062
3089
  }
@@ -3602,7 +3629,7 @@ const DIJKSTRA_CODE = [
3602
3629
  " dist[v] = dist[u]+w",
3603
3630
  " pq.add((dist[v], v))"
3604
3631
  ];
3605
- const LEGEND_ITEMS$w = [
3632
+ const LEGEND_ITEMS$y = [
3606
3633
  { color: "bg-blue-100", label: "Unvisited", border: "#60a5fa" },
3607
3634
  { color: "bg-yellow-400", label: "Current", border: "#ca8a04" },
3608
3635
  { color: "bg-green-400", label: "Visited", border: "#16a34a" }
@@ -3963,7 +3990,7 @@ const DijkstraVisualizerComponent = ({
3963
3990
  accentColor: "orange"
3964
3991
  }
3965
3992
  ),
3966
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$w })
3993
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$y })
3967
3994
  ] })
3968
3995
  ]
3969
3996
  }
@@ -3987,7 +4014,7 @@ const KNAPSACK_CODE = [
3987
4014
  " take = dp[i-1][w-weight] + value",
3988
4015
  " dp[i][w] = max(skip, take)"
3989
4016
  ];
3990
- const LEGEND_ITEMS$v = [
4017
+ const LEGEND_ITEMS$x = [
3991
4018
  { color: "bg-gray-50", label: "Not computed", border: "#d1d5db" },
3992
4019
  { color: "bg-blue-100", label: "Computed" },
3993
4020
  { color: "bg-green-400", label: "Take item" },
@@ -4283,7 +4310,7 @@ const DPVisualizerComponent = ({
4283
4310
  accentColor: "teal"
4284
4311
  }
4285
4312
  ),
4286
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$v })
4313
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$x })
4287
4314
  ] })
4288
4315
  ]
4289
4316
  }
@@ -4322,7 +4349,7 @@ const BFS_CODE = [
4322
4349
  " visited[neighbor] = true",
4323
4350
  " queue.enqueue(neighbor)"
4324
4351
  ];
4325
- const LEGEND_ITEMS$u = [
4352
+ const LEGEND_ITEMS$w = [
4326
4353
  { color: "bg-blue-100", label: "Unvisited", border: "#60a5fa" },
4327
4354
  { color: "bg-yellow-400", label: "Current", border: "#ca8a04" },
4328
4355
  { color: "bg-green-400", label: "Visited", border: "#16a34a" }
@@ -4749,7 +4776,7 @@ const GraphVisualizerComponent = ({
4749
4776
  accentColor: "purple"
4750
4777
  }
4751
4778
  ),
4752
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$u })
4779
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$w })
4753
4780
  ] })
4754
4781
  ]
4755
4782
  }
@@ -4757,7 +4784,7 @@ const GraphVisualizerComponent = ({
4757
4784
  };
4758
4785
  const GraphVisualizer = React.memo(GraphVisualizerComponent);
4759
4786
  const BUCKET_COUNT$2 = 8;
4760
- const OPERATIONS$h = [
4787
+ const OPERATIONS$j = [
4761
4788
  { op: "put", key: "Alice", value: 25 },
4762
4789
  { op: "put", key: "Bob", value: 30 },
4763
4790
  { op: "put", key: "Charlie", value: 35 },
@@ -4789,7 +4816,7 @@ const HASHMAP_CODE = [
4789
4816
  " return entry.value",
4790
4817
  " return null"
4791
4818
  ];
4792
- const LEGEND_ITEMS$t = [
4819
+ const LEGEND_ITEMS$v = [
4793
4820
  { color: "bg-blue-50", label: "Current bucket", border: "#60a5fa" },
4794
4821
  { color: "bg-blue-500", label: "Insert/Update" },
4795
4822
  { color: "bg-green-400", label: "Found" },
@@ -4814,7 +4841,7 @@ function generateHashMapSteps$1() {
4814
4841
  description: `Initialize HashMap with ${BUCKET_COUNT$2} buckets. Each bucket is a linked list for collision handling.`,
4815
4842
  codeLine: -1
4816
4843
  });
4817
- for (const { op, key, value } of OPERATIONS$h) {
4844
+ for (const { op, key, value } of OPERATIONS$j) {
4818
4845
  const hash = simpleHash$7(key);
4819
4846
  const index = hash % BUCKET_COUNT$2;
4820
4847
  if (op === "put") {
@@ -5101,7 +5128,7 @@ const HashMapVisualizerComponent = ({
5101
5128
  accentColor: "indigo"
5102
5129
  }
5103
5130
  ),
5104
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$t })
5131
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$v })
5105
5132
  ] })
5106
5133
  ]
5107
5134
  }
@@ -5109,7 +5136,7 @@ const HashMapVisualizerComponent = ({
5109
5136
  };
5110
5137
  const HashMapVisualizer = React.memo(HashMapVisualizerComponent);
5111
5138
  const BUCKET_COUNT$1 = 8;
5112
- const OPERATIONS$g = [
5139
+ const OPERATIONS$i = [
5113
5140
  { op: "put", key: "Alice", value: 25 },
5114
5141
  { op: "put", key: "Bob", value: 30 },
5115
5142
  { op: "put", key: "Charlie", value: 35 },
@@ -5118,7 +5145,7 @@ const OPERATIONS$g = [
5118
5145
  { op: "put", key: "Alice", value: 26 },
5119
5146
  { op: "get", key: "Frank" }
5120
5147
  ];
5121
- const LEGEND_ITEMS$s = [
5148
+ const LEGEND_ITEMS$u = [
5122
5149
  { color: "bg-blue-50", label: "Current bucket", border: "#60a5fa" },
5123
5150
  { color: "bg-blue-500", label: "Insert/Update" },
5124
5151
  { color: "bg-green-400", label: "Found" },
@@ -5247,7 +5274,7 @@ function generateHashMapSteps() {
5247
5274
  buckets: buckets.map((b) => ({ entries: [...b.entries] })),
5248
5275
  description: `Initialize HashMap with ${BUCKET_COUNT$1} buckets`
5249
5276
  });
5250
- for (const { op, key, value } of OPERATIONS$g) {
5277
+ for (const { op, key, value } of OPERATIONS$i) {
5251
5278
  const hash = simpleHash$6(key);
5252
5279
  const index = hash % BUCKET_COUNT$1;
5253
5280
  if (op === "put") {
@@ -5488,7 +5515,7 @@ const HashMapInterviewVisualizerComponent = ({
5488
5515
  accentColor: "indigo"
5489
5516
  }
5490
5517
  ),
5491
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$s })
5518
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$u })
5492
5519
  ] })
5493
5520
  ]
5494
5521
  }
@@ -5496,7 +5523,7 @@ const HashMapInterviewVisualizerComponent = ({
5496
5523
  };
5497
5524
  const HashMapInterviewVisualizer = React.memo(HashMapInterviewVisualizerComponent);
5498
5525
  const INITIAL_DATA = [10, 20, 30, 40, 50];
5499
- const OPERATIONS$f = [
5526
+ const OPERATIONS$h = [
5500
5527
  { op: "get", index: 2 },
5501
5528
  { op: "addLast", value: 60 },
5502
5529
  { op: "addFirst", value: 5 },
@@ -5505,7 +5532,7 @@ const OPERATIONS$f = [
5505
5532
  { op: "removeLast" },
5506
5533
  { op: "removeMiddle", index: 2 }
5507
5534
  ];
5508
- const LEGEND_ITEMS$r = [
5535
+ const LEGEND_ITEMS$t = [
5509
5536
  { color: "bg-blue-500", label: "ArrayList" },
5510
5537
  { color: "bg-green-500", label: "LinkedList" },
5511
5538
  { color: "bg-yellow-400", label: "Current element" },
@@ -5531,7 +5558,7 @@ function generateListComparisonSteps() {
5531
5558
  description: `Initialize both lists with [${INITIAL_DATA.join(", ")}]`,
5532
5559
  phase: "complete"
5533
5560
  });
5534
- for (const { op, index, value } of OPERATIONS$f) {
5561
+ for (const { op, index, value } of OPERATIONS$h) {
5535
5562
  switch (op) {
5536
5563
  case "get": {
5537
5564
  const idx = index;
@@ -6033,14 +6060,14 @@ const ListComparisonVisualizerComponent = ({
6033
6060
  accentColor: "blue"
6034
6061
  }
6035
6062
  ),
6036
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$r })
6063
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$t })
6037
6064
  ] })
6038
6065
  ]
6039
6066
  }
6040
6067
  );
6041
6068
  };
6042
6069
  const ListComparisonVisualizer = React.memo(ListComparisonVisualizerComponent);
6043
- const OPERATIONS$e = [
6070
+ const OPERATIONS$g = [
6044
6071
  { op: "add", value: 50 },
6045
6072
  { op: "add", value: 30 },
6046
6073
  { op: "add", value: 70 },
@@ -6051,7 +6078,7 @@ const OPERATIONS$e = [
6051
6078
  { op: "contains", value: 40 },
6052
6079
  { op: "contains", value: 55 }
6053
6080
  ];
6054
- const LEGEND_ITEMS$q = [
6081
+ const LEGEND_ITEMS$s = [
6055
6082
  { color: "bg-yellow-100", label: "Path", border: "#facc15" },
6056
6083
  { color: "bg-green-500", label: "Current/Found" },
6057
6084
  { color: "bg-red-500", label: "Not found" }
@@ -6215,7 +6242,7 @@ function generateTreeSetSteps$1() {
6215
6242
  path: [],
6216
6243
  description: "Initialize empty TreeSet (Binary Search Tree)"
6217
6244
  });
6218
- for (const { op, value } of OPERATIONS$e) {
6245
+ for (const { op, value } of OPERATIONS$g) {
6219
6246
  if (op === "add") {
6220
6247
  const path = tree ? findPath$1(tree, value) : [];
6221
6248
  if (tree) {
@@ -6514,7 +6541,7 @@ const TreeSetInterviewVisualizerComponent = ({
6514
6541
  accentColor: "green"
6515
6542
  }
6516
6543
  ),
6517
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$q })
6544
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$s })
6518
6545
  ] })
6519
6546
  ]
6520
6547
  }
@@ -6522,7 +6549,7 @@ const TreeSetInterviewVisualizerComponent = ({
6522
6549
  };
6523
6550
  const TreeSetInterviewVisualizer = React.memo(TreeSetInterviewVisualizerComponent);
6524
6551
  const INITIAL_ARRAY = [64, 34, 25, 12, 22, 11, 90];
6525
- const LEGEND_ITEMS$p = [
6552
+ const LEGEND_ITEMS$r = [
6526
6553
  { color: "bg-yellow-400", label: "Comparing" },
6527
6554
  { color: "bg-red-400", label: "Swapping" },
6528
6555
  { color: "bg-green-400", label: "Sorted" },
@@ -6845,7 +6872,7 @@ const SortingInterviewVisualizerComponent = ({
6845
6872
  accentColor: "orange"
6846
6873
  }
6847
6874
  ),
6848
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$p })
6875
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$r })
6849
6876
  ] })
6850
6877
  ]
6851
6878
  }
@@ -6870,7 +6897,7 @@ const EDGES$1 = [
6870
6897
  { from: "C", to: "G" },
6871
6898
  { from: "E", to: "F" }
6872
6899
  ];
6873
- const LEGEND_ITEMS$o = [
6900
+ const LEGEND_ITEMS$q = [
6874
6901
  { color: "bg-blue-500", label: "Current" },
6875
6902
  { color: "bg-green-400", label: "Visited" },
6876
6903
  { color: "bg-yellow-100", label: "In Queue/Stack", border: "#facc15" }
@@ -7292,7 +7319,7 @@ const GraphInterviewVisualizerComponent = ({
7292
7319
  accentColor: "blue"
7293
7320
  }
7294
7321
  ),
7295
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$o })
7322
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$q })
7296
7323
  ] })
7297
7324
  ]
7298
7325
  }
@@ -7301,7 +7328,7 @@ const GraphInterviewVisualizerComponent = ({
7301
7328
  const GraphInterviewVisualizer = React.memo(GraphInterviewVisualizerComponent);
7302
7329
  const BIT_ARRAY_SIZE$1 = 16;
7303
7330
  const K_HASH_FUNCTIONS = 3;
7304
- const OPERATIONS$d = [
7331
+ const OPERATIONS$f = [
7305
7332
  { op: "add", element: "apple" },
7306
7333
  { op: "add", element: "banana" },
7307
7334
  { op: "add", element: "cherry" },
@@ -7309,7 +7336,7 @@ const OPERATIONS$d = [
7309
7336
  { op: "query", element: "grape" },
7310
7337
  { op: "query", element: "banana" }
7311
7338
  ];
7312
- const LEGEND_ITEMS$n = [
7339
+ const LEGEND_ITEMS$p = [
7313
7340
  { color: "bg-gray-200", label: "Bit = 0" },
7314
7341
  { color: "bg-purple-500", label: "Bit = 1" },
7315
7342
  { color: "bg-pink-400", label: "Current hash position" }
@@ -7467,7 +7494,7 @@ function generateBloomFilterSteps$1() {
7467
7494
  bitArray: [...bitArray],
7468
7495
  description: `Initialize Bloom Filter: ${BIT_ARRAY_SIZE$1} bits, ${K_HASH_FUNCTIONS} hash functions`
7469
7496
  });
7470
- for (const { op, element } of OPERATIONS$d) {
7497
+ for (const { op, element } of OPERATIONS$f) {
7471
7498
  const positions = getHashPositions(element);
7472
7499
  if (op === "add") {
7473
7500
  steps.push({
@@ -7657,7 +7684,7 @@ const BloomFilterInterviewVisualizerComponent = ({
7657
7684
  accentColor: "purple"
7658
7685
  }
7659
7686
  ),
7660
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$n })
7687
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$p })
7661
7688
  ] })
7662
7689
  ]
7663
7690
  }
@@ -7665,7 +7692,7 @@ const BloomFilterInterviewVisualizerComponent = ({
7665
7692
  };
7666
7693
  const BloomFilterInterviewVisualizer = React.memo(BloomFilterInterviewVisualizerComponent);
7667
7694
  const ORDER$1 = 3;
7668
- const OPERATIONS$c = [
7695
+ const OPERATIONS$e = [
7669
7696
  { op: "insert", value: 10 },
7670
7697
  { op: "insert", value: 20 },
7671
7698
  { op: "insert", value: 5 },
@@ -7674,7 +7701,7 @@ const OPERATIONS$c = [
7674
7701
  { op: "search", value: 15 },
7675
7702
  { op: "search", value: 12 }
7676
7703
  ];
7677
- const LEGEND_ITEMS$m = [
7704
+ const LEGEND_ITEMS$o = [
7678
7705
  { color: "bg-green-400", label: "Current node" },
7679
7706
  { color: "bg-yellow-100", label: "Search path", border: "#facc15" },
7680
7707
  { color: "bg-blue-400", label: "Found/Inserted" }
@@ -7866,7 +7893,7 @@ function generateBTreeSteps$1() {
7866
7893
  }
7867
7894
  return { node };
7868
7895
  }
7869
- for (const { op, value } of OPERATIONS$c) {
7896
+ for (const { op, value } of OPERATIONS$e) {
7870
7897
  if (op === "insert") {
7871
7898
  steps.push({
7872
7899
  operation: "insert",
@@ -8101,14 +8128,14 @@ const BTreeInterviewVisualizerComponent = ({
8101
8128
  accentColor: "green"
8102
8129
  }
8103
8130
  ),
8104
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$m })
8131
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$o })
8105
8132
  ] })
8106
8133
  ]
8107
8134
  }
8108
8135
  );
8109
8136
  };
8110
8137
  const BTreeInterviewVisualizer = React.memo(BTreeInterviewVisualizerComponent);
8111
- const LEGEND_ITEMS$l = [
8138
+ const LEGEND_ITEMS$n = [
8112
8139
  { color: "bg-blue-100", label: "Unvisited", border: "#60a5fa" },
8113
8140
  { color: "bg-yellow-400", label: "Current" },
8114
8141
  { color: "bg-green-400", label: "Visited" }
@@ -8502,14 +8529,14 @@ const DijkstraInterviewVisualizerComponent = ({
8502
8529
  accentColor: "orange"
8503
8530
  }
8504
8531
  ),
8505
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$l })
8532
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$n })
8506
8533
  ] })
8507
8534
  ]
8508
8535
  }
8509
8536
  );
8510
8537
  };
8511
8538
  const DijkstraInterviewVisualizer = React.memo(DijkstraInterviewVisualizerComponent);
8512
- const LEGEND_ITEMS$k = [
8539
+ const LEGEND_ITEMS$m = [
8513
8540
  { color: "bg-gray-50", label: "Not computed", border: "#d1d5db" },
8514
8541
  { color: "bg-blue-100", label: "Computed" },
8515
8542
  { color: "bg-green-400", label: "Take item" },
@@ -8896,14 +8923,14 @@ const DPInterviewVisualizerComponent = ({
8896
8923
  accentColor: "cyan"
8897
8924
  }
8898
8925
  ),
8899
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$k })
8926
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$m })
8900
8927
  ] })
8901
8928
  ]
8902
8929
  }
8903
8930
  );
8904
8931
  };
8905
8932
  const DPInterviewVisualizer = React.memo(DPInterviewVisualizerComponent);
8906
- const LEGEND_ITEMS$j = [
8933
+ const LEGEND_ITEMS$l = [
8907
8934
  { color: "bg-blue-500", label: "Server A" },
8908
8935
  { color: "bg-green-500", label: "Server B" },
8909
8936
  { color: "bg-purple-500", label: "Server C" },
@@ -9346,14 +9373,14 @@ const ConsistentHashingInterviewVisualizerComponent = ({
9346
9373
  accentColor: "cyan"
9347
9374
  }
9348
9375
  ),
9349
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$j })
9376
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$l })
9350
9377
  ] })
9351
9378
  ]
9352
9379
  }
9353
9380
  );
9354
9381
  };
9355
9382
  const ConsistentHashingInterviewVisualizer = React.memo(ConsistentHashingInterviewVisualizerComponent);
9356
- const LEGEND_ITEMS$i = [
9383
+ const LEGEND_ITEMS$k = [
9357
9384
  { color: "bg-gray-400", label: "Follower" },
9358
9385
  { color: "bg-yellow-500", label: "Candidate" },
9359
9386
  { color: "bg-green-500", label: "Leader" },
@@ -9510,7 +9537,7 @@ const STATE_COLORS$1 = {
9510
9537
  candidate: "bg-yellow-500",
9511
9538
  leader: "bg-green-500"
9512
9539
  };
9513
- function cloneNodes$2(nodes) {
9540
+ function cloneNodes$3(nodes) {
9514
9541
  return nodes.map((n) => ({
9515
9542
  ...n,
9516
9543
  log: n.log.map((e) => ({ ...e }))
@@ -9528,7 +9555,7 @@ function generateRaftSteps$1() {
9528
9555
  }));
9529
9556
  steps.push({
9530
9557
  operation: "init",
9531
- nodes: cloneNodes$2(nodes),
9558
+ nodes: cloneNodes$3(nodes),
9532
9559
  description: `Initialize ${NODE_IDS$1.length} nodes as followers (term 0)`
9533
9560
  });
9534
9561
  const candidateId = "N1";
@@ -9538,7 +9565,7 @@ function generateRaftSteps$1() {
9538
9565
  candidateNode.votedFor = candidateId;
9539
9566
  steps.push({
9540
9567
  operation: "timeout",
9541
- nodes: cloneNodes$2(nodes),
9568
+ nodes: cloneNodes$3(nodes),
9542
9569
  description: `${candidateId} timeout! Becomes candidate, term=1`,
9543
9570
  highlightNode: candidateId
9544
9571
  });
@@ -9550,7 +9577,7 @@ function generateRaftSteps$1() {
9550
9577
  votes++;
9551
9578
  steps.push({
9552
9579
  operation: "vote",
9553
- nodes: cloneNodes$2(nodes),
9580
+ nodes: cloneNodes$3(nodes),
9554
9581
  description: `${node.id} votes for ${candidateId} (${votes}/${NODE_IDS$1.length})`,
9555
9582
  highlightNode: node.id,
9556
9583
  highlightEdge: { from: node.id, to: candidateId, type: "vote" }
@@ -9559,7 +9586,7 @@ function generateRaftSteps$1() {
9559
9586
  candidateNode.state = "leader";
9560
9587
  steps.push({
9561
9588
  operation: "becomeLeader",
9562
- nodes: cloneNodes$2(nodes),
9589
+ nodes: cloneNodes$3(nodes),
9563
9590
  description: `${candidateId} wins election! (${votes} votes)`,
9564
9591
  highlightNode: candidateId
9565
9592
  });
@@ -9569,7 +9596,7 @@ function generateRaftSteps$1() {
9569
9596
  }
9570
9597
  steps.push({
9571
9598
  operation: "heartbeat",
9572
- nodes: cloneNodes$2(nodes),
9599
+ nodes: cloneNodes$3(nodes),
9573
9600
  description: `Leader ${candidateId} sends heartbeats`,
9574
9601
  highlightNode: candidateId,
9575
9602
  messageType: "Heartbeat"
@@ -9579,7 +9606,7 @@ function generateRaftSteps$1() {
9579
9606
  leader.log.push({ term: 1, command, committed: false });
9580
9607
  steps.push({
9581
9608
  operation: "replicate",
9582
- nodes: cloneNodes$2(nodes),
9609
+ nodes: cloneNodes$3(nodes),
9583
9610
  description: `Client request: "${command}"`,
9584
9611
  highlightNode: leader.id
9585
9612
  });
@@ -9599,7 +9626,7 @@ function generateRaftSteps$1() {
9599
9626
  }
9600
9627
  steps.push({
9601
9628
  operation: "commit",
9602
- nodes: cloneNodes$2(nodes),
9629
+ nodes: cloneNodes$3(nodes),
9603
9630
  description: `Entry committed! (${acks} acks)`,
9604
9631
  highlightNode: leader.id
9605
9632
  });
@@ -9609,7 +9636,7 @@ function generateRaftSteps$1() {
9609
9636
  }
9610
9637
  steps.push({
9611
9638
  operation: "done",
9612
- nodes: cloneNodes$2(nodes),
9639
+ nodes: cloneNodes$3(nodes),
9613
9640
  description: `✓ Consensus achieved! Leader: ${leader.id}`
9614
9641
  });
9615
9642
  return steps;
@@ -9813,14 +9840,14 @@ const RaftInterviewVisualizerComponent = ({
9813
9840
  accentColor: "orange"
9814
9841
  }
9815
9842
  ),
9816
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$i })
9843
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$k })
9817
9844
  ] })
9818
9845
  ]
9819
9846
  }
9820
9847
  );
9821
9848
  };
9822
9849
  const RaftInterviewVisualizer = React.memo(RaftInterviewVisualizerComponent);
9823
- const INITIAL_SIZE = 7;
9850
+ const INITIAL_SIZE$1 = 7;
9824
9851
  const LOAD_FACTOR_THRESHOLD = 0.7;
9825
9852
  const SAMPLE_KEYS = [
9826
9853
  "apple",
@@ -9841,7 +9868,7 @@ const HASH_TABLE_CODE = [
9841
9868
  " if loadFactor > threshold:",
9842
9869
  " rehash(newSize = size * 2)"
9843
9870
  ];
9844
- const LEGEND_ITEMS$h = [
9871
+ const LEGEND_ITEMS$j = [
9845
9872
  { color: "bg-gray-50", label: "Empty", border: "#d1d5db" },
9846
9873
  { color: "bg-yellow-50", label: "Hashing", border: "#facc15" },
9847
9874
  { color: "bg-red-50", label: "Collision", border: "#f87171" },
@@ -9856,7 +9883,7 @@ function simpleHash$3(key, size) {
9856
9883
  }
9857
9884
  function generateHashSteps(keys) {
9858
9885
  const steps = [];
9859
- let buckets = Array(INITIAL_SIZE).fill(null).map(() => []);
9886
+ let buckets = Array(INITIAL_SIZE$1).fill(null).map(() => []);
9860
9887
  let itemCount = 0;
9861
9888
  steps.push({
9862
9889
  operation: "done",
@@ -9864,7 +9891,7 @@ function generateHashSteps(keys) {
9864
9891
  hashValue: 0,
9865
9892
  bucketIndex: -1,
9866
9893
  buckets: buckets.map((b) => [...b]),
9867
- description: `Initialize hash table with ${INITIAL_SIZE} buckets (chaining)`,
9894
+ description: `Initialize hash table with ${INITIAL_SIZE$1} buckets (chaining)`,
9868
9895
  codeLine: -1
9869
9896
  });
9870
9897
  for (const key of keys) {
@@ -10198,14 +10225,14 @@ const HashTableVisualizerComponent = ({
10198
10225
  ] })
10199
10226
  }
10200
10227
  ),
10201
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$h })
10228
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$j })
10202
10229
  ] })
10203
10230
  ]
10204
10231
  }
10205
10232
  );
10206
10233
  };
10207
10234
  const HashTableVisualizer = React.memo(HashTableVisualizerComponent);
10208
- const OPERATIONS$b = [
10235
+ const OPERATIONS$d = [
10209
10236
  { op: "addFirst", value: 10 },
10210
10237
  { op: "addLast", value: 20 },
10211
10238
  { op: "addLast", value: 30 },
@@ -10237,25 +10264,25 @@ const LINKEDLIST_CODE = [
10237
10264
  " node = node.next",
10238
10265
  " return node.value"
10239
10266
  ];
10240
- const LEGEND_ITEMS$g = [
10267
+ const LEGEND_ITEMS$i = [
10241
10268
  { color: "bg-white", label: "Node", border: "#d1d5db" },
10242
10269
  { color: "bg-blue-500", label: "Active" }
10243
10270
  ];
10244
- let nodeIdCounter$1 = 0;
10271
+ let nodeIdCounter$2 = 0;
10245
10272
  function generateLinkedListSteps() {
10246
10273
  var _a, _b, _c;
10247
10274
  const steps = [];
10248
10275
  let nodes = [];
10249
- nodeIdCounter$1 = 0;
10276
+ nodeIdCounter$2 = 0;
10250
10277
  steps.push({
10251
10278
  operation: "init",
10252
10279
  nodes: [],
10253
10280
  description: "Initialize empty LinkedList (doubly-linked). O(1) for add/remove at ends, O(n) for indexed access.",
10254
10281
  codeLine: -1
10255
10282
  });
10256
- for (const { op, value, index } of OPERATIONS$b) {
10283
+ for (const { op, value, index } of OPERATIONS$d) {
10257
10284
  if (op === "addFirst" && value !== void 0) {
10258
- const newNode = { value, id: nodeIdCounter$1++ };
10285
+ const newNode = { value, id: nodeIdCounter$2++ };
10259
10286
  nodes = [newNode, ...nodes];
10260
10287
  steps.push({
10261
10288
  operation: "addFirst",
@@ -10267,7 +10294,7 @@ function generateLinkedListSteps() {
10267
10294
  highlightNode: newNode.id
10268
10295
  });
10269
10296
  } else if (op === "addLast" && value !== void 0) {
10270
- const newNode = { value, id: nodeIdCounter$1++ };
10297
+ const newNode = { value, id: nodeIdCounter$2++ };
10271
10298
  nodes = [...nodes, newNode];
10272
10299
  steps.push({
10273
10300
  operation: "addLast",
@@ -10483,7 +10510,7 @@ const LinkedListVisualizerComponent = ({
10483
10510
  accentColor: "blue"
10484
10511
  }
10485
10512
  ),
10486
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$g })
10513
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$i })
10487
10514
  ] })
10488
10515
  ]
10489
10516
  }
@@ -10491,7 +10518,7 @@ const LinkedListVisualizerComponent = ({
10491
10518
  };
10492
10519
  const LinkedListVisualizer = React.memo(LinkedListVisualizerComponent);
10493
10520
  const BUCKET_COUNT = 6;
10494
- const OPERATIONS$a = [
10521
+ const OPERATIONS$c = [
10495
10522
  { op: "put", key: "A", value: 10 },
10496
10523
  { op: "put", key: "B", value: 20 },
10497
10524
  { op: "put", key: "C", value: 30 },
@@ -10527,7 +10554,7 @@ const LINKEDHASHMAP_CODE = [
10527
10554
  " }",
10528
10555
  "}"
10529
10556
  ];
10530
- const LEGEND_ITEMS$f = [
10557
+ const LEGEND_ITEMS$h = [
10531
10558
  { color: "bg-blue-50", label: "Current bucket", border: "#60a5fa" },
10532
10559
  { color: "bg-blue-500", label: "Insert/Update" },
10533
10560
  { color: "bg-green-400", label: "Found" },
@@ -10556,7 +10583,7 @@ function generateLinkedHashMapSteps(accessOrder = true) {
10556
10583
  codeLine: 0,
10557
10584
  accessOrder
10558
10585
  });
10559
- for (const { op, key, value } of OPERATIONS$a) {
10586
+ for (const { op, key, value } of OPERATIONS$c) {
10560
10587
  const hash = simpleHash$2(key);
10561
10588
  const index = hash % BUCKET_COUNT;
10562
10589
  if (op === "put") {
@@ -10992,7 +11019,7 @@ const LinkedHashMapVisualizerComponent = ({
10992
11019
  accentColor: "orange"
10993
11020
  }
10994
11021
  ),
10995
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$f })
11022
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$h })
10996
11023
  ] })
10997
11024
  ]
10998
11025
  }
@@ -11000,7 +11027,7 @@ const LinkedHashMapVisualizerComponent = ({
11000
11027
  };
11001
11028
  const LinkedHashMapVisualizer = React.memo(LinkedHashMapVisualizerComponent);
11002
11029
  const INITIAL_CAPACITY$1 = 4;
11003
- const OPERATIONS$9 = [
11030
+ const OPERATIONS$b = [
11004
11031
  { op: "add", value: 10 },
11005
11032
  { op: "add", value: 20 },
11006
11033
  { op: "add", value: 30 },
@@ -11031,7 +11058,7 @@ const ARRAYLIST_CODE = [
11031
11058
  " shift elements left",
11032
11059
  " size--"
11033
11060
  ];
11034
- const LEGEND_ITEMS$e = [
11061
+ const LEGEND_ITEMS$g = [
11035
11062
  { color: "bg-white border-gray-300", label: "Used", border: "#d1d5db" },
11036
11063
  { color: "bg-gray-100", label: "Empty" },
11037
11064
  { color: "bg-orange-500", label: "Active" },
@@ -11050,7 +11077,7 @@ function generateArrayListSteps() {
11050
11077
  description: `Initialize ArrayList with capacity ${capacity}. Auto-grows when full (amortized O(1) add).`,
11051
11078
  codeLine: -1
11052
11079
  });
11053
- for (const { op, value, index } of OPERATIONS$9) {
11080
+ for (const { op, value, index } of OPERATIONS$b) {
11054
11081
  if (op === "add" && value !== void 0) {
11055
11082
  if (size === capacity) {
11056
11083
  const oldCapacity = capacity;
@@ -11371,7 +11398,7 @@ const ArrayListVisualizerComponent = ({
11371
11398
  accentColor: "orange"
11372
11399
  }
11373
11400
  ),
11374
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$e })
11401
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$g })
11375
11402
  ] })
11376
11403
  ]
11377
11404
  }
@@ -11379,7 +11406,7 @@ const ArrayListVisualizerComponent = ({
11379
11406
  };
11380
11407
  const ArrayListVisualizer = React.memo(ArrayListVisualizerComponent);
11381
11408
  const INITIAL_CAPACITY = 8;
11382
- const OPERATIONS$8 = [
11409
+ const OPERATIONS$a = [
11383
11410
  { op: "addLast", value: 10 },
11384
11411
  { op: "addLast", value: 20 },
11385
11412
  { op: "addFirst", value: 5 },
@@ -11425,7 +11452,7 @@ const ARRAYDEQUE_CODE = [
11425
11452
  " }",
11426
11453
  "}"
11427
11454
  ];
11428
- const LEGEND_ITEMS$d = [
11455
+ const LEGEND_ITEMS$f = [
11429
11456
  { color: "bg-teal-500", label: "Head pointer" },
11430
11457
  { color: "bg-violet-500", label: "Tail pointer" },
11431
11458
  { color: "bg-green-400", label: "Added element" },
@@ -11449,7 +11476,7 @@ function generateArrayDequeSteps() {
11449
11476
  codeLine: 0,
11450
11477
  variables: { head, tail, capacity, size: 0 }
11451
11478
  });
11452
- for (const { op, value } of OPERATIONS$8) {
11479
+ for (const { op, value } of OPERATIONS$a) {
11453
11480
  if (op === "addFirst" && value !== void 0) {
11454
11481
  if (getSize() === capacity - 1) {
11455
11482
  const oldCapacity = capacity;
@@ -11831,14 +11858,14 @@ const ArrayDequeVisualizerComponent = ({
11831
11858
  accentColor: "teal"
11832
11859
  }
11833
11860
  ),
11834
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$d })
11861
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$f })
11835
11862
  ] })
11836
11863
  ]
11837
11864
  }
11838
11865
  );
11839
11866
  };
11840
11867
  const ArrayDequeVisualizer = React.memo(ArrayDequeVisualizerComponent);
11841
- const OPERATIONS$7 = [
11868
+ const OPERATIONS$9 = [
11842
11869
  { op: "add", value: 50 },
11843
11870
  { op: "add", value: 30 },
11844
11871
  { op: "add", value: 70 },
@@ -11869,7 +11896,7 @@ const TREESET_CODE = [
11869
11896
  " node = node.right",
11870
11897
  " else: return // duplicate"
11871
11898
  ];
11872
- const LEGEND_ITEMS$c = [
11899
+ const LEGEND_ITEMS$e = [
11873
11900
  { color: "bg-yellow-100", label: "Path", border: "#facc15" },
11874
11901
  { color: "bg-blue-500", label: "Current/Insert" },
11875
11902
  { color: "bg-green-500", label: "Found" },
@@ -11918,7 +11945,7 @@ function generateTreeSetSteps() {
11918
11945
  description: "Initialize empty TreeSet (Binary Search Tree). Values are stored in sorted order.",
11919
11946
  codeLine: -1
11920
11947
  });
11921
- for (const { op, value } of OPERATIONS$7) {
11948
+ for (const { op, value } of OPERATIONS$9) {
11922
11949
  if (op === "add") {
11923
11950
  const path = tree ? findPath(tree, value) : [];
11924
11951
  if (tree) {
@@ -12298,7 +12325,7 @@ const TreeSetVisualizerComponent = ({
12298
12325
  accentColor: "green"
12299
12326
  }
12300
12327
  ),
12301
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$c })
12328
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$e })
12302
12329
  ] })
12303
12330
  ]
12304
12331
  }
@@ -12314,7 +12341,7 @@ const DAYS_OF_WEEK = [
12314
12341
  "SATURDAY",
12315
12342
  "SUNDAY"
12316
12343
  ];
12317
- const OPERATIONS$6 = [
12344
+ const OPERATIONS$8 = [
12318
12345
  { op: "add", value: "MONDAY" },
12319
12346
  { op: "add", value: "WEDNESDAY" },
12320
12347
  { op: "add", value: "FRIDAY" },
@@ -12347,7 +12374,7 @@ const ENUMSET_CODE = [
12347
12374
  " }",
12348
12375
  "}"
12349
12376
  ];
12350
- const LEGEND_ITEMS$b = [
12377
+ const LEGEND_ITEMS$d = [
12351
12378
  { color: "bg-green-500", label: "Bit set (1)" },
12352
12379
  { color: "bg-gray-200", label: "Bit clear (0)" },
12353
12380
  { color: "bg-blue-500", label: "Current operation" },
@@ -12366,7 +12393,7 @@ function generateEnumSetSteps() {
12366
12393
  codeLine: 1,
12367
12394
  variables: { elements: "0b0000000" }
12368
12395
  });
12369
- for (const { op, value } of OPERATIONS$6) {
12396
+ for (const { op, value } of OPERATIONS$8) {
12370
12397
  const bitPos = getBitPosition(value);
12371
12398
  const bitMaskForValue = 1 << bitPos;
12372
12399
  const previousBitmask = bitmask;
@@ -12667,14 +12694,14 @@ const EnumSetVisualizerComponent = ({
12667
12694
  accentColor: "lime"
12668
12695
  }
12669
12696
  ),
12670
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$b })
12697
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$d })
12671
12698
  ] })
12672
12699
  ]
12673
12700
  }
12674
12701
  );
12675
12702
  };
12676
12703
  const EnumSetVisualizer = React.memo(EnumSetVisualizerComponent);
12677
- const OPERATIONS$5 = [
12704
+ const OPERATIONS$7 = [
12678
12705
  { op: "offer", value: 50 },
12679
12706
  { op: "offer", value: 30 },
12680
12707
  { op: "offer", value: 70 },
@@ -12703,7 +12730,7 @@ const HEAP_CODE = [
12703
12730
  " siftDown(0)",
12704
12731
  " return result"
12705
12732
  ];
12706
- const LEGEND_ITEMS$a = [
12733
+ const LEGEND_ITEMS$c = [
12707
12734
  { color: "bg-purple-100", label: "Root (min)", border: "#c4b5fd" },
12708
12735
  { color: "bg-purple-500", label: "Active" },
12709
12736
  { color: "bg-purple-200", label: "Sift Path", border: "#a78bfa" },
@@ -12719,7 +12746,7 @@ function generateHeapSteps() {
12719
12746
  description: "Initialize min-heap PriorityQueue. Parent is always smaller than children. O(log n) insert/remove.",
12720
12747
  codeLine: -1
12721
12748
  });
12722
- for (const { op, value } of OPERATIONS$5) {
12749
+ for (const { op, value } of OPERATIONS$7) {
12723
12750
  if (op === "offer" && value !== void 0) {
12724
12751
  heap.push(value);
12725
12752
  let idx = heap.length - 1;
@@ -13225,7 +13252,7 @@ const PriorityQueueVisualizerComponent = ({ showControls = true, showCode = true
13225
13252
  accentColor: "purple"
13226
13253
  }
13227
13254
  ),
13228
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$a })
13255
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$c })
13229
13256
  ] })
13230
13257
  ]
13231
13258
  }
@@ -13235,7 +13262,7 @@ const PriorityQueueVisualizer = React.memo(
13235
13262
  PriorityQueueVisualizerComponent
13236
13263
  );
13237
13264
  const SEGMENT_COUNT = 4;
13238
- const OPERATIONS$4 = [
13265
+ const OPERATIONS$6 = [
13239
13266
  { thread: "T1", op: "put", key: "Alice", value: 100 },
13240
13267
  { thread: "T2", op: "put", key: "Bob", value: 200 },
13241
13268
  { thread: "T1", op: "put", key: "Charlie", value: 150 },
@@ -13258,7 +13285,7 @@ const CHM_CODE = [
13258
13285
  " // Volatile reads ensure visibility",
13259
13286
  " return bucket.get(key)"
13260
13287
  ];
13261
- const LEGEND_ITEMS$9 = [
13288
+ const LEGEND_ITEMS$b = [
13262
13289
  { color: "bg-blue-50", label: "Active segment", border: "#60a5fa" },
13263
13290
  { color: "bg-red-50", label: "Locked", border: "#f87171" },
13264
13291
  { color: "bg-gray-50", label: "Unlocked", border: "#d1d5db" }
@@ -13285,7 +13312,7 @@ function generateConcurrentSteps() {
13285
13312
  codeLine: -1,
13286
13313
  activeThreads: []
13287
13314
  });
13288
- for (const { thread, op, key, value } of OPERATIONS$4) {
13315
+ for (const { thread, op, key, value } of OPERATIONS$6) {
13289
13316
  const hash = simpleHash$1(key);
13290
13317
  const segmentId = hash % SEGMENT_COUNT;
13291
13318
  if (op === "put") {
@@ -13614,7 +13641,7 @@ const ConcurrentHashMapVisualizerComponent = ({ showControls = true, showCode =
13614
13641
  accentColor: "red"
13615
13642
  }
13616
13643
  ),
13617
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$9 })
13644
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$b })
13618
13645
  ] })
13619
13646
  ]
13620
13647
  }
@@ -13624,7 +13651,7 @@ const ConcurrentHashMapVisualizer = React.memo(
13624
13651
  ConcurrentHashMapVisualizerComponent
13625
13652
  );
13626
13653
  const CAPACITY = 3;
13627
- const OPERATIONS$3 = [
13654
+ const OPERATIONS$5 = [
13628
13655
  { thread: "P1", role: "producer", value: "Task-A" },
13629
13656
  { thread: "P2", role: "producer", value: "Task-B" },
13630
13657
  { thread: "C1", role: "consumer" },
@@ -13657,7 +13684,7 @@ const BQ_CODE = [
13657
13684
  " notFull.signal()",
13658
13685
  " lock.unlock()"
13659
13686
  ];
13660
- const LEGEND_ITEMS$8 = [
13687
+ const LEGEND_ITEMS$a = [
13661
13688
  { color: "bg-green-100", label: "Producer" },
13662
13689
  { color: "bg-blue-100", label: "Consumer" },
13663
13690
  { color: "bg-red-100", label: "Blocked", border: "#fca5a5" }
@@ -13679,7 +13706,7 @@ function generateBlockingQueueSteps() {
13679
13706
  blockedProducers: [],
13680
13707
  blockedConsumers: []
13681
13708
  });
13682
- for (const { thread, role, value } of OPERATIONS$3) {
13709
+ for (const { thread, role, value } of OPERATIONS$5) {
13683
13710
  if (role === "producer") {
13684
13711
  if (queue.length >= CAPACITY) {
13685
13712
  blockedProducers.push(thread);
@@ -14012,7 +14039,7 @@ const BlockingQueueVisualizerComponent = ({ showControls = true, showCode = true
14012
14039
  accentColor: "cyan"
14013
14040
  }
14014
14041
  ),
14015
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$8 })
14042
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$a })
14016
14043
  ] })
14017
14044
  ]
14018
14045
  }
@@ -14021,7 +14048,7 @@ const BlockingQueueVisualizerComponent = ({ showControls = true, showCode = true
14021
14048
  const BlockingQueueVisualizer = React.memo(
14022
14049
  BlockingQueueVisualizerComponent
14023
14050
  );
14024
- const OPERATIONS$2 = [
14051
+ const OPERATIONS$4 = [
14025
14052
  { thread: "R1", op: "read" },
14026
14053
  { thread: "R2", op: "read" },
14027
14054
  { thread: "W1", op: "write", value: "D" },
@@ -14043,7 +14070,7 @@ const COW_CODE = [
14043
14070
  " array = newArray // Atomic swap",
14044
14071
  " lock.unlock()"
14045
14072
  ];
14046
- const LEGEND_ITEMS$7 = [
14073
+ const LEGEND_ITEMS$9 = [
14047
14074
  { color: "bg-blue-500", label: "Reader" },
14048
14075
  { color: "bg-orange-500", label: "Writer" },
14049
14076
  { color: "bg-green-500", label: "New element" }
@@ -14061,7 +14088,7 @@ function generateCOWSteps() {
14061
14088
  codeLine: -1,
14062
14089
  activeReaders: []
14063
14090
  });
14064
- for (const { thread, op, value } of OPERATIONS$2) {
14091
+ for (const { thread, op, value } of OPERATIONS$4) {
14065
14092
  if (op === "read") {
14066
14093
  if (!activeReaders.includes(thread)) {
14067
14094
  activeReaders.push(thread);
@@ -14355,7 +14382,7 @@ const CopyOnWriteVisualizerComponent = ({
14355
14382
  accentColor: "lime"
14356
14383
  }
14357
14384
  ),
14358
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$7 })
14385
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$9 })
14359
14386
  ] })
14360
14387
  ]
14361
14388
  }
@@ -14378,7 +14405,7 @@ const IMMUTABLE_CODE = [
14378
14405
  "var newList = new ArrayList<>(list)",
14379
14406
  'newList.add("D") // Works!'
14380
14407
  ];
14381
- const LEGEND_ITEMS$6 = [
14408
+ const LEGEND_ITEMS$8 = [
14382
14409
  { color: "bg-violet-100", label: "Immutable", border: "#c4b5fd" },
14383
14410
  { color: "bg-green-100", label: "Mutable copy", border: "#86efac" },
14384
14411
  { color: "bg-red-100", label: "Error", border: "#fca5a5" }
@@ -14656,7 +14683,7 @@ const ImmutableCollectionsVisualizerComponent = ({ showControls = true, showCode
14656
14683
  accentColor: "violet"
14657
14684
  }
14658
14685
  ),
14659
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$6 })
14686
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$8 })
14660
14687
  ] })
14661
14688
  ]
14662
14689
  }
@@ -14687,7 +14714,7 @@ const BLOOM_FILTER_CODE = [
14687
14714
  " return False # definitely not",
14688
14715
  " return True # probably yes"
14689
14716
  ];
14690
- const LEGEND_ITEMS$5 = [
14717
+ const LEGEND_ITEMS$7 = [
14691
14718
  { color: "bg-gray-200", label: "Bit = 0" },
14692
14719
  { color: "bg-indigo-500", label: "Bit = 1" },
14693
14720
  { color: "bg-yellow-400", label: "Currently checking", border: "#fbbf24" },
@@ -15066,7 +15093,7 @@ const BloomFilterVisualizerComponent = ({
15066
15093
  accentColor: "purple"
15067
15094
  }
15068
15095
  ),
15069
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$5 })
15096
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$7 })
15070
15097
  ] })
15071
15098
  ]
15072
15099
  }
@@ -15074,7 +15101,7 @@ const BloomFilterVisualizerComponent = ({
15074
15101
  };
15075
15102
  const BloomFilterVisualizer = React.memo(BloomFilterVisualizerComponent);
15076
15103
  const ORDER = 3;
15077
- const OPERATIONS$1 = [
15104
+ const OPERATIONS$3 = [
15078
15105
  { op: "insert", key: 10 },
15079
15106
  { op: "insert", key: 20 },
15080
15107
  { op: "insert", key: 5 },
@@ -15105,7 +15132,7 @@ const BTREE_CODE = [
15105
15132
  " split root, create new root",
15106
15133
  " insertNonFull(root, key)"
15107
15134
  ];
15108
- const LEGEND_ITEMS$4 = [
15135
+ const LEGEND_ITEMS$6 = [
15109
15136
  { color: "bg-gray-100", label: "Node", border: "#d1d5db" },
15110
15137
  { color: "bg-yellow-200", label: "Current node", border: "#fbbf24" },
15111
15138
  { color: "bg-blue-500", label: "Comparing key" },
@@ -15113,11 +15140,11 @@ const LEGEND_ITEMS$4 = [
15113
15140
  { color: "bg-red-400", label: "Not found" },
15114
15141
  { color: "bg-purple-400", label: "Splitting" }
15115
15142
  ];
15116
- let nodeIdCounter = 0;
15117
- function generateNodeId() {
15118
- return `node_${nodeIdCounter++}`;
15143
+ let nodeIdCounter$1 = 0;
15144
+ function generateNodeId$1() {
15145
+ return `node_${nodeIdCounter$1++}`;
15119
15146
  }
15120
- function cloneNodes$1(nodes) {
15147
+ function cloneNodes$2(nodes) {
15121
15148
  const cloned = /* @__PURE__ */ new Map();
15122
15149
  nodes.forEach((node, id) => {
15123
15150
  cloned.set(id, {
@@ -15129,11 +15156,11 @@ function cloneNodes$1(nodes) {
15129
15156
  return cloned;
15130
15157
  }
15131
15158
  function generateBTreeSteps() {
15132
- nodeIdCounter = 0;
15159
+ nodeIdCounter$1 = 0;
15133
15160
  const steps = [];
15134
15161
  let nodes = /* @__PURE__ */ new Map();
15135
15162
  let rootId = "";
15136
- const initialRoot = generateNodeId();
15163
+ const initialRoot = generateNodeId$1();
15137
15164
  rootId = initialRoot;
15138
15165
  nodes.set(initialRoot, {
15139
15166
  id: initialRoot,
@@ -15143,18 +15170,18 @@ function generateBTreeSteps() {
15143
15170
  });
15144
15171
  steps.push({
15145
15172
  operation: "init",
15146
- nodes: cloneNodes$1(nodes),
15173
+ nodes: cloneNodes$2(nodes),
15147
15174
  rootId,
15148
15175
  description: `Initialize empty B-Tree of order ${ORDER} (max ${ORDER - 1} keys per node)`,
15149
15176
  codeLine: 0,
15150
15177
  variables: { order: ORDER, maxKeys: ORDER - 1 }
15151
15178
  });
15152
- for (const { op, key } of OPERATIONS$1) {
15179
+ for (const { op, key } of OPERATIONS$3) {
15153
15180
  if (op === "insert") {
15154
15181
  const root = nodes.get(rootId);
15155
15182
  steps.push({
15156
15183
  operation: "insert",
15157
- nodes: cloneNodes$1(nodes),
15184
+ nodes: cloneNodes$2(nodes),
15158
15185
  rootId,
15159
15186
  targetKey: key,
15160
15187
  currentNodeId: rootId,
@@ -15171,7 +15198,7 @@ function generateBTreeSteps() {
15171
15198
  insertNode2.keys.sort((a, b) => a - b);
15172
15199
  steps.push({
15173
15200
  operation: "insert",
15174
- nodes: cloneNodes$1(nodes),
15201
+ nodes: cloneNodes$2(nodes),
15175
15202
  rootId,
15176
15203
  targetKey: key,
15177
15204
  currentNodeId: rootId,
@@ -15187,7 +15214,7 @@ function generateBTreeSteps() {
15187
15214
  const rightKeys = insertNode2.keys.slice(medianIndex + 1);
15188
15215
  steps.push({
15189
15216
  operation: "split",
15190
- nodes: cloneNodes$1(nodes),
15217
+ nodes: cloneNodes$2(nodes),
15191
15218
  rootId,
15192
15219
  currentNodeId: rootId,
15193
15220
  description: `Node full (${insertNode2.keys.length} keys)! Split: median=${medianKey}, left=[${leftKeys.join(",")}], right=[${rightKeys.join(",")}]`,
@@ -15200,9 +15227,9 @@ function generateBTreeSteps() {
15200
15227
  rightKeys
15201
15228
  }
15202
15229
  });
15203
- const leftId = generateNodeId();
15204
- const rightId = generateNodeId();
15205
- const newRootId = generateNodeId();
15230
+ const leftId = generateNodeId$1();
15231
+ const rightId = generateNodeId$1();
15232
+ const newRootId = generateNodeId$1();
15206
15233
  nodes.set(leftId, {
15207
15234
  id: leftId,
15208
15235
  keys: leftKeys,
@@ -15225,7 +15252,7 @@ function generateBTreeSteps() {
15225
15252
  rootId = newRootId;
15226
15253
  steps.push({
15227
15254
  operation: "split",
15228
- nodes: cloneNodes$1(nodes),
15255
+ nodes: cloneNodes$2(nodes),
15229
15256
  rootId,
15230
15257
  description: `Split complete! New root with median ${medianKey}`,
15231
15258
  codeLine: 17,
@@ -15235,7 +15262,7 @@ function generateBTreeSteps() {
15235
15262
  } else {
15236
15263
  steps.push({
15237
15264
  operation: "search",
15238
- nodes: cloneNodes$1(nodes),
15265
+ nodes: cloneNodes$2(nodes),
15239
15266
  rootId,
15240
15267
  targetKey: key,
15241
15268
  currentNodeId: rootId,
@@ -15248,7 +15275,7 @@ function generateBTreeSteps() {
15248
15275
  for (let i = 0; i < node.keys.length; i++) {
15249
15276
  steps.push({
15250
15277
  operation: "compare",
15251
- nodes: cloneNodes$1(nodes),
15278
+ nodes: cloneNodes$2(nodes),
15252
15279
  rootId,
15253
15280
  targetKey: key,
15254
15281
  currentNodeId: nodeId,
@@ -15260,7 +15287,7 @@ function generateBTreeSteps() {
15260
15287
  if (key === node.keys[i]) {
15261
15288
  steps.push({
15262
15289
  operation: "found",
15263
- nodes: cloneNodes$1(nodes),
15290
+ nodes: cloneNodes$2(nodes),
15264
15291
  rootId,
15265
15292
  targetKey: key,
15266
15293
  currentNodeId: nodeId,
@@ -15275,7 +15302,7 @@ function generateBTreeSteps() {
15275
15302
  if (node.isLeaf) {
15276
15303
  steps.push({
15277
15304
  operation: "notFound",
15278
- nodes: cloneNodes$1(nodes),
15305
+ nodes: cloneNodes$2(nodes),
15279
15306
  rootId,
15280
15307
  targetKey: key,
15281
15308
  currentNodeId: nodeId,
@@ -15291,7 +15318,7 @@ function generateBTreeSteps() {
15291
15318
  if (node.isLeaf) {
15292
15319
  steps.push({
15293
15320
  operation: "notFound",
15294
- nodes: cloneNodes$1(nodes),
15321
+ nodes: cloneNodes$2(nodes),
15295
15322
  rootId,
15296
15323
  targetKey: key,
15297
15324
  currentNodeId: nodeId,
@@ -15310,7 +15337,7 @@ function generateBTreeSteps() {
15310
15337
  nodes.forEach((node) => allKeys.push(...node.keys));
15311
15338
  steps.push({
15312
15339
  operation: "done",
15313
- nodes: cloneNodes$1(nodes),
15340
+ nodes: cloneNodes$2(nodes),
15314
15341
  rootId,
15315
15342
  description: `✓ Done! B-Tree contains ${allKeys.length} keys across ${nodes.size} nodes.`,
15316
15343
  codeLine: -1,
@@ -15495,7 +15522,7 @@ const BTreeVisualizerComponent = ({
15495
15522
  ] }),
15496
15523
  /* @__PURE__ */ jsxs("div", { className: "mb-4 p-2 bg-blue-50 rounded-lg border border-blue-200", children: [
15497
15524
  /* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-blue-800 mb-1", children: "Operations" }),
15498
- /* @__PURE__ */ jsx("div", { className: "text-xs text-blue-700 font-mono", children: OPERATIONS$1.map((op, i) => /* @__PURE__ */ jsxs("span", { className: "mr-2", children: [
15525
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-blue-700 font-mono", children: OPERATIONS$3.map((op, i) => /* @__PURE__ */ jsxs("span", { className: "mr-2", children: [
15499
15526
  op.op,
15500
15527
  "(",
15501
15528
  op.key,
@@ -15540,13 +15567,899 @@ const BTreeVisualizerComponent = ({
15540
15567
  accentColor: "green"
15541
15568
  }
15542
15569
  ),
15543
- /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$4 })
15570
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$6 })
15544
15571
  ] })
15545
15572
  ]
15546
15573
  }
15547
15574
  );
15548
15575
  };
15549
15576
  const BTreeVisualizer = React.memo(BTreeVisualizerComponent);
15577
+ const OPERATIONS$2 = [
15578
+ { op: "insert", word: "cat" },
15579
+ { op: "insert", word: "car" },
15580
+ { op: "insert", word: "card" },
15581
+ { op: "insert", word: "care" },
15582
+ { op: "insert", word: "dog" },
15583
+ { op: "search", word: "car" },
15584
+ { op: "search", word: "cab" },
15585
+ { op: "prefix", word: "car" }
15586
+ ];
15587
+ const TRIE_CODE = [
15588
+ "class TrieNode:",
15589
+ " children = {} # char -> node",
15590
+ " isEndOfWord = false",
15591
+ "",
15592
+ "function insert(word):",
15593
+ " node = root",
15594
+ " for char in word:",
15595
+ " if char not in node.children:",
15596
+ " node.children[char] = TrieNode()",
15597
+ " node = node.children[char]",
15598
+ " node.isEndOfWord = true",
15599
+ "",
15600
+ "function search(word):",
15601
+ " node = root",
15602
+ " for char in word:",
15603
+ " if char not in node.children:",
15604
+ " return false",
15605
+ " node = node.children[char]",
15606
+ " return node.isEndOfWord"
15607
+ ];
15608
+ const LEGEND_ITEMS$5 = [
15609
+ { color: "bg-gray-100", label: "Node", border: "#d1d5db" },
15610
+ { color: "bg-blue-200", label: "Current", border: "#60a5fa" },
15611
+ { color: "bg-green-400", label: "End of word" },
15612
+ { color: "bg-yellow-200", label: "Path", border: "#fbbf24" },
15613
+ { color: "bg-purple-400", label: "Match found" },
15614
+ { color: "bg-red-400", label: "Not found" }
15615
+ ];
15616
+ let nodeIdCounter = 0;
15617
+ function generateNodeId() {
15618
+ return `trie_${nodeIdCounter++}`;
15619
+ }
15620
+ function cloneNodes$1(nodes) {
15621
+ const cloned = /* @__PURE__ */ new Map();
15622
+ nodes.forEach((node, id) => {
15623
+ cloned.set(id, {
15624
+ ...node,
15625
+ children: new Map(node.children)
15626
+ });
15627
+ });
15628
+ return cloned;
15629
+ }
15630
+ function generateTrieSteps() {
15631
+ nodeIdCounter = 0;
15632
+ const steps = [];
15633
+ const nodes = /* @__PURE__ */ new Map();
15634
+ const rootId = generateNodeId();
15635
+ nodes.set(rootId, {
15636
+ id: rootId,
15637
+ char: "",
15638
+ children: /* @__PURE__ */ new Map(),
15639
+ isEndOfWord: false
15640
+ });
15641
+ steps.push({
15642
+ operation: "init",
15643
+ nodes: cloneNodes$1(nodes),
15644
+ rootId,
15645
+ description: "Initialize empty Trie with root node. Each node stores a character and links to children.",
15646
+ codeLine: 0
15647
+ });
15648
+ for (const { op, word } of OPERATIONS$2) {
15649
+ if (op === "insert") {
15650
+ steps.push({
15651
+ operation: "insert",
15652
+ nodes: cloneNodes$1(nodes),
15653
+ rootId,
15654
+ word,
15655
+ currentNodeId: rootId,
15656
+ description: `insert("${word}"): Start at root, traverse/create nodes for each character`,
15657
+ codeLine: 4,
15658
+ variables: { word: `"${word}"` }
15659
+ });
15660
+ let currentId = rootId;
15661
+ const path = [rootId];
15662
+ for (let i = 0; i < word.length; i++) {
15663
+ const char = word[i];
15664
+ const currentNode = nodes.get(currentId);
15665
+ if (currentNode.children.has(char)) {
15666
+ const nextId = currentNode.children.get(char);
15667
+ path.push(nextId);
15668
+ steps.push({
15669
+ operation: "traverse",
15670
+ nodes: cloneNodes$1(nodes),
15671
+ rootId,
15672
+ word,
15673
+ currentNodeId: nextId,
15674
+ highlightChar: char,
15675
+ highlightPath: [...path],
15676
+ description: `'${char}' exists → traverse to child node`,
15677
+ codeLine: 9,
15678
+ variables: { char: `'${char}'`, i, word: `"${word}"` }
15679
+ });
15680
+ currentId = nextId;
15681
+ } else {
15682
+ const newId = generateNodeId();
15683
+ nodes.set(newId, {
15684
+ id: newId,
15685
+ char,
15686
+ children: /* @__PURE__ */ new Map(),
15687
+ isEndOfWord: false
15688
+ });
15689
+ currentNode.children.set(char, newId);
15690
+ path.push(newId);
15691
+ steps.push({
15692
+ operation: "create",
15693
+ nodes: cloneNodes$1(nodes),
15694
+ rootId,
15695
+ word,
15696
+ currentNodeId: newId,
15697
+ highlightChar: char,
15698
+ highlightPath: [...path],
15699
+ description: `'${char}' not found → create new node`,
15700
+ codeLine: 8,
15701
+ variables: { char: `'${char}'`, i, word: `"${word}"` }
15702
+ });
15703
+ currentId = newId;
15704
+ }
15705
+ }
15706
+ const endNode = nodes.get(currentId);
15707
+ endNode.isEndOfWord = true;
15708
+ endNode.word = word;
15709
+ steps.push({
15710
+ operation: "markEnd",
15711
+ nodes: cloneNodes$1(nodes),
15712
+ rootId,
15713
+ word,
15714
+ currentNodeId: currentId,
15715
+ highlightPath: path,
15716
+ description: `Mark node as end of word "${word}"`,
15717
+ codeLine: 10,
15718
+ variables: { word: `"${word}"`, isEndOfWord: "true" }
15719
+ });
15720
+ } else if (op === "search") {
15721
+ steps.push({
15722
+ operation: "search",
15723
+ nodes: cloneNodes$1(nodes),
15724
+ rootId,
15725
+ word,
15726
+ currentNodeId: rootId,
15727
+ description: `search("${word}"): Check if word exists in Trie`,
15728
+ codeLine: 12,
15729
+ variables: { word: `"${word}"` }
15730
+ });
15731
+ let currentId = rootId;
15732
+ const path = [rootId];
15733
+ let found = true;
15734
+ for (let i = 0; i < word.length; i++) {
15735
+ const char = word[i];
15736
+ const currentNode = nodes.get(currentId);
15737
+ if (currentNode.children.has(char)) {
15738
+ const nextId = currentNode.children.get(char);
15739
+ path.push(nextId);
15740
+ steps.push({
15741
+ operation: "traverse",
15742
+ nodes: cloneNodes$1(nodes),
15743
+ rootId,
15744
+ word,
15745
+ currentNodeId: nextId,
15746
+ highlightChar: char,
15747
+ highlightPath: [...path],
15748
+ description: `'${char}' found → continue searching`,
15749
+ codeLine: 17,
15750
+ variables: { char: `'${char}'`, i, word: `"${word}"` }
15751
+ });
15752
+ currentId = nextId;
15753
+ } else {
15754
+ found = false;
15755
+ steps.push({
15756
+ operation: "notFound",
15757
+ nodes: cloneNodes$1(nodes),
15758
+ rootId,
15759
+ word,
15760
+ currentNodeId: currentId,
15761
+ highlightChar: char,
15762
+ highlightPath: path,
15763
+ description: `'${char}' not found → word "${word}" does not exist`,
15764
+ codeLine: 16,
15765
+ variables: { char: `'${char}'`, result: "false" }
15766
+ });
15767
+ break;
15768
+ }
15769
+ }
15770
+ if (found) {
15771
+ const endNode = nodes.get(currentId);
15772
+ if (endNode.isEndOfWord) {
15773
+ steps.push({
15774
+ operation: "found",
15775
+ nodes: cloneNodes$1(nodes),
15776
+ rootId,
15777
+ word,
15778
+ currentNodeId: currentId,
15779
+ highlightPath: path,
15780
+ description: `✓ Found! "${word}" exists in Trie`,
15781
+ codeLine: 18,
15782
+ variables: { word: `"${word}"`, result: "true" }
15783
+ });
15784
+ } else {
15785
+ steps.push({
15786
+ operation: "notFound",
15787
+ nodes: cloneNodes$1(nodes),
15788
+ rootId,
15789
+ word,
15790
+ currentNodeId: currentId,
15791
+ highlightPath: path,
15792
+ description: `Path exists but not marked as word end → "${word}" is only a prefix`,
15793
+ codeLine: 18,
15794
+ variables: { word: `"${word}"`, result: "false" }
15795
+ });
15796
+ }
15797
+ }
15798
+ } else if (op === "prefix") {
15799
+ steps.push({
15800
+ operation: "prefix",
15801
+ nodes: cloneNodes$1(nodes),
15802
+ rootId,
15803
+ prefix: word,
15804
+ currentNodeId: rootId,
15805
+ description: `startsWith("${word}"): Find all words with this prefix`,
15806
+ codeLine: 12,
15807
+ variables: { prefix: `"${word}"` }
15808
+ });
15809
+ let currentId = rootId;
15810
+ const path = [rootId];
15811
+ let prefixExists = true;
15812
+ for (let i = 0; i < word.length; i++) {
15813
+ const char = word[i];
15814
+ const currentNode = nodes.get(currentId);
15815
+ if (currentNode.children.has(char)) {
15816
+ const nextId = currentNode.children.get(char);
15817
+ path.push(nextId);
15818
+ currentId = nextId;
15819
+ } else {
15820
+ prefixExists = false;
15821
+ break;
15822
+ }
15823
+ }
15824
+ if (prefixExists) {
15825
+ const matchedWords = [];
15826
+ const collectWords = (nodeId) => {
15827
+ const node = nodes.get(nodeId);
15828
+ if (node.isEndOfWord && node.word) {
15829
+ matchedWords.push(node.word);
15830
+ }
15831
+ node.children.forEach((childId) => collectWords(childId));
15832
+ };
15833
+ collectWords(currentId);
15834
+ steps.push({
15835
+ operation: "found",
15836
+ nodes: cloneNodes$1(nodes),
15837
+ rootId,
15838
+ prefix: word,
15839
+ currentNodeId: currentId,
15840
+ highlightPath: path,
15841
+ matchedWords,
15842
+ description: `Found ${matchedWords.length} word(s) with prefix "${word}": ${matchedWords.join(", ")}`,
15843
+ codeLine: 18,
15844
+ variables: { prefix: `"${word}"`, count: matchedWords.length }
15845
+ });
15846
+ } else {
15847
+ steps.push({
15848
+ operation: "notFound",
15849
+ nodes: cloneNodes$1(nodes),
15850
+ rootId,
15851
+ prefix: word,
15852
+ highlightPath: path,
15853
+ description: `No words found with prefix "${word}"`,
15854
+ codeLine: 16,
15855
+ variables: { prefix: `"${word}"`, count: 0 }
15856
+ });
15857
+ }
15858
+ }
15859
+ }
15860
+ const allWords = [];
15861
+ const collectAllWords = (nodeId) => {
15862
+ const node = nodes.get(nodeId);
15863
+ if (node.isEndOfWord && node.word) {
15864
+ allWords.push(node.word);
15865
+ }
15866
+ node.children.forEach((childId) => collectAllWords(childId));
15867
+ };
15868
+ collectAllWords(rootId);
15869
+ steps.push({
15870
+ operation: "done",
15871
+ nodes: cloneNodes$1(nodes),
15872
+ rootId,
15873
+ matchedWords: allWords,
15874
+ description: `✓ Done! Trie contains ${allWords.length} words: ${allWords.join(", ")}`,
15875
+ codeLine: -1
15876
+ });
15877
+ return steps;
15878
+ }
15879
+ const TrieNodeComponent = ({ nodeId, nodes, currentNodeId, highlightPath, depth }) => {
15880
+ const node = nodes.get(nodeId);
15881
+ if (!node) return null;
15882
+ const isCurrent = currentNodeId === nodeId;
15883
+ const isInPath = highlightPath == null ? void 0 : highlightPath.includes(nodeId);
15884
+ const children = Array.from(node.children.entries());
15885
+ let nodeStyle = "border-gray-300 bg-gray-50";
15886
+ if (isCurrent) {
15887
+ nodeStyle = "border-blue-400 bg-blue-100 ring-2 ring-blue-300";
15888
+ } else if (isInPath) {
15889
+ nodeStyle = "border-yellow-400 bg-yellow-100";
15890
+ }
15891
+ if (node.isEndOfWord) {
15892
+ nodeStyle += " ring-2 ring-green-400";
15893
+ }
15894
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center", children: [
15895
+ /* @__PURE__ */ jsx(
15896
+ "div",
15897
+ {
15898
+ className: `
15899
+ w-10 h-10 rounded-lg border-2 flex items-center justify-center
15900
+ text-sm font-bold transition-all duration-300
15901
+ ${nodeStyle}
15902
+ `,
15903
+ children: node.char || "∅"
15904
+ }
15905
+ ),
15906
+ node.isEndOfWord && /* @__PURE__ */ jsx("div", { className: "text-[9px] text-green-600 font-medium mt-0.5", children: node.word }),
15907
+ children.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
15908
+ /* @__PURE__ */ jsx("div", { className: "w-0.5 h-4 bg-gray-300" }),
15909
+ /* @__PURE__ */ jsx("div", { className: "flex gap-2", children: children.map(([char, childId]) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center", children: [
15910
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] text-gray-500 mb-1", children: char }),
15911
+ /* @__PURE__ */ jsx(
15912
+ TrieNodeComponent,
15913
+ {
15914
+ nodeId: childId,
15915
+ nodes,
15916
+ currentNodeId,
15917
+ highlightPath,
15918
+ depth: depth + 1
15919
+ }
15920
+ )
15921
+ ] }, childId)) })
15922
+ ] })
15923
+ ] });
15924
+ };
15925
+ const TrieVisualizerComponent = ({
15926
+ showControls = true,
15927
+ showCode = true,
15928
+ className = ""
15929
+ }) => {
15930
+ const VISUALIZER_ID = "trie-visualizer";
15931
+ const { copyUrlToClipboard } = useUrlState({ prefix: "trie", scrollToId: VISUALIZER_ID });
15932
+ const generateSteps = useMemo(() => generateTrieSteps, []);
15933
+ const {
15934
+ steps,
15935
+ currentStep,
15936
+ currentStepData,
15937
+ isPlaying,
15938
+ speed,
15939
+ setSpeed,
15940
+ handlePlayPause,
15941
+ handleStep,
15942
+ handleStepBack,
15943
+ handleReset
15944
+ } = useVisualizerPlayback({
15945
+ generateSteps
15946
+ });
15947
+ const stepData = currentStepData || {
15948
+ operation: "init",
15949
+ nodes: /* @__PURE__ */ new Map(),
15950
+ rootId: "",
15951
+ description: ""
15952
+ };
15953
+ const getStatusVariant = () => {
15954
+ if (stepData.operation === "notFound") return "error";
15955
+ if (stepData.operation === "found" || stepData.operation === "markEnd") return "success";
15956
+ if (stepData.operation === "done") return "warning";
15957
+ return "default";
15958
+ };
15959
+ const handleShare = useCallback(async () => {
15960
+ return copyUrlToClipboard({ step: currentStep });
15961
+ }, [copyUrlToClipboard, currentStep]);
15962
+ return /* @__PURE__ */ jsxs(
15963
+ "div",
15964
+ {
15965
+ id: VISUALIZER_ID,
15966
+ className: `bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden ${className}`,
15967
+ children: [
15968
+ /* @__PURE__ */ jsx("div", { className: "px-4 py-3 bg-gradient-to-r from-teal-50 to-cyan-50 border-b border-gray-200", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between flex-wrap gap-2", children: [
15969
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
15970
+ /* @__PURE__ */ jsx("h3", { className: "font-semibold text-gray-900", children: "Trie (Prefix Tree)" }),
15971
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
15972
+ /* @__PURE__ */ jsx("span", { className: "px-2 py-0.5 text-xs font-medium bg-teal-100 text-teal-700 rounded", children: "Insert: O(m)" }),
15973
+ /* @__PURE__ */ jsx("span", { className: "px-2 py-0.5 text-xs font-medium bg-cyan-100 text-cyan-700 rounded", children: "Search: O(m)" })
15974
+ ] })
15975
+ ] }),
15976
+ /* @__PURE__ */ jsx(ShareButton, { onShare: handleShare })
15977
+ ] }) }),
15978
+ /* @__PURE__ */ jsx("div", { className: "p-4", children: /* @__PURE__ */ jsxs("div", { className: `flex gap-4 ${showCode ? "flex-col lg:flex-row" : ""}`, children: [
15979
+ /* @__PURE__ */ jsxs(VisualizationArea, { minHeight: 400, className: showCode ? "flex-1" : "w-full", children: [
15980
+ /* @__PURE__ */ jsxs("div", { className: "mb-4 p-3 bg-gradient-to-r from-teal-50 to-cyan-50 rounded-lg border border-teal-200", children: [
15981
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-teal-800 mb-2", children: "🌲 Trie - Prefix Tree" }),
15982
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-teal-700 space-y-1", children: [
15983
+ /* @__PURE__ */ jsx("div", { children: "• Each node represents a character" }),
15984
+ /* @__PURE__ */ jsx("div", { children: "• Words share common prefixes (space efficient)" }),
15985
+ /* @__PURE__ */ jsx("div", { children: "• Perfect for autocomplete & spell checking" }),
15986
+ /* @__PURE__ */ jsx("div", { children: "• m = word length, n = number of words" })
15987
+ ] })
15988
+ ] }),
15989
+ /* @__PURE__ */ jsx("div", { className: "flex justify-center overflow-x-auto py-4", children: stepData.nodes.size > 0 && /* @__PURE__ */ jsx(
15990
+ TrieNodeComponent,
15991
+ {
15992
+ nodeId: stepData.rootId,
15993
+ nodes: stepData.nodes,
15994
+ currentNodeId: stepData.currentNodeId,
15995
+ highlightPath: stepData.highlightPath,
15996
+ depth: 0
15997
+ }
15998
+ ) }),
15999
+ stepData.matchedWords && stepData.matchedWords.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mt-4 p-3 bg-purple-50 rounded-lg border border-purple-200", children: [
16000
+ /* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-purple-800 mb-2", children: "Words found:" }),
16001
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: stepData.matchedWords.map((word) => /* @__PURE__ */ jsx(
16002
+ "span",
16003
+ {
16004
+ className: "px-2 py-1 bg-purple-100 text-purple-700 text-sm rounded font-mono",
16005
+ children: word
16006
+ },
16007
+ word
16008
+ )) })
16009
+ ] }),
16010
+ /* @__PURE__ */ jsx(
16011
+ StatusPanel,
16012
+ {
16013
+ description: stepData.description,
16014
+ currentStep,
16015
+ totalSteps: steps.length,
16016
+ variant: getStatusVariant()
16017
+ }
16018
+ )
16019
+ ] }),
16020
+ showCode && /* @__PURE__ */ jsxs("div", { className: "w-full lg:w-56 flex-shrink-0 space-y-2", children: [
16021
+ /* @__PURE__ */ jsx(
16022
+ CodePanel,
16023
+ {
16024
+ code: TRIE_CODE,
16025
+ activeLine: (currentStepData == null ? void 0 : currentStepData.codeLine) ?? -1,
16026
+ variables: currentStepData == null ? void 0 : currentStepData.variables
16027
+ }
16028
+ ),
16029
+ /* @__PURE__ */ jsx(HelpPanel, {})
16030
+ ] })
16031
+ ] }) }),
16032
+ showControls && /* @__PURE__ */ jsxs("div", { className: "px-4 py-3 bg-gray-50 border-t border-gray-200", children: [
16033
+ /* @__PURE__ */ jsx(
16034
+ ControlPanel,
16035
+ {
16036
+ isPlaying,
16037
+ currentStep,
16038
+ totalSteps: steps.length,
16039
+ speed,
16040
+ onPlayPause: handlePlayPause,
16041
+ onStep: handleStep,
16042
+ onStepBack: handleStepBack,
16043
+ onReset: handleReset,
16044
+ onSpeedChange: setSpeed,
16045
+ accentColor: "teal"
16046
+ }
16047
+ ),
16048
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$5 })
16049
+ ] })
16050
+ ]
16051
+ }
16052
+ );
16053
+ };
16054
+ const TrieVisualizer = React.memo(TrieVisualizerComponent);
16055
+ const INITIAL_SIZE = 8;
16056
+ const OPERATIONS$1 = [
16057
+ { op: "union", a: 0, b: 1 },
16058
+ { op: "union", a: 2, b: 3 },
16059
+ { op: "union", a: 4, b: 5 },
16060
+ { op: "union", a: 6, b: 7 },
16061
+ { op: "union", a: 0, b: 2 },
16062
+ { op: "union", a: 4, b: 6 },
16063
+ { op: "find", a: 3 },
16064
+ { op: "connected", a: 1, b: 3 },
16065
+ { op: "connected", a: 0, b: 5 },
16066
+ { op: "union", a: 0, b: 4 },
16067
+ { op: "connected", a: 3, b: 7 }
16068
+ ];
16069
+ const UF_CODE = [
16070
+ "class UnionFind:",
16071
+ " parent[] = [0, 1, ..., n-1]",
16072
+ " rank[] = [0, 0, ..., 0]",
16073
+ "",
16074
+ "function find(x):",
16075
+ " if parent[x] != x:",
16076
+ " parent[x] = find(parent[x])",
16077
+ " # path compression",
16078
+ " return parent[x]",
16079
+ "",
16080
+ "function union(x, y):",
16081
+ " rootX = find(x)",
16082
+ " rootY = find(y)",
16083
+ " if rootX == rootY: return",
16084
+ " # union by rank",
16085
+ " if rank[rootX] < rank[rootY]:",
16086
+ " parent[rootX] = rootY",
16087
+ " else if rank[rootX] > rank[rootY]:",
16088
+ " parent[rootY] = rootX",
16089
+ " else:",
16090
+ " parent[rootY] = rootX",
16091
+ " rank[rootX] += 1"
16092
+ ];
16093
+ const LEGEND_ITEMS$4 = [
16094
+ { color: "bg-gray-100", label: "Element", border: "#d1d5db" },
16095
+ { color: "bg-blue-200", label: "Current", border: "#60a5fa" },
16096
+ { color: "bg-yellow-200", label: "Path to root", border: "#fbbf24" },
16097
+ { color: "bg-green-400", label: "Root / Same set" },
16098
+ { color: "bg-red-400", label: "Different sets" },
16099
+ { color: "bg-purple-400", label: "Union edge" }
16100
+ ];
16101
+ function cloneElements(elements) {
16102
+ return elements.map((el) => ({ ...el }));
16103
+ }
16104
+ function generateUnionFindSteps() {
16105
+ const steps = [];
16106
+ const elements = Array.from({ length: INITIAL_SIZE }, (_, i) => ({
16107
+ id: i,
16108
+ parent: i,
16109
+ rank: 0
16110
+ }));
16111
+ steps.push({
16112
+ operation: "init",
16113
+ elements: cloneElements(elements),
16114
+ description: `Initialize ${INITIAL_SIZE} elements. Each element is its own parent (self-loop).`,
16115
+ codeLine: 1,
16116
+ variables: { n: INITIAL_SIZE }
16117
+ });
16118
+ const find = (x, trackPath = false) => {
16119
+ const path = [x];
16120
+ let current = x;
16121
+ while (elements[current].parent !== current) {
16122
+ current = elements[current].parent;
16123
+ path.push(current);
16124
+ }
16125
+ if (trackPath && path.length > 2) {
16126
+ for (let i = 0; i < path.length - 1; i++) {
16127
+ elements[path[i]].parent = current;
16128
+ }
16129
+ }
16130
+ return { root: current, path };
16131
+ };
16132
+ for (const operation of OPERATIONS$1) {
16133
+ if (operation.op === "find") {
16134
+ const x = operation.a;
16135
+ steps.push({
16136
+ operation: "find",
16137
+ elements: cloneElements(elements),
16138
+ highlightElements: [x],
16139
+ description: `find(${x}): Find the root of element ${x}`,
16140
+ codeLine: 4,
16141
+ variables: { x }
16142
+ });
16143
+ const { root, path } = find(x);
16144
+ steps.push({
16145
+ operation: "find",
16146
+ elements: cloneElements(elements),
16147
+ highlightElements: [root],
16148
+ pathToRoot: path,
16149
+ description: `Path: ${path.join(" → ")}. Root is ${root}`,
16150
+ codeLine: 8,
16151
+ variables: { x, root }
16152
+ });
16153
+ if (path.length > 2) {
16154
+ find(x, true);
16155
+ steps.push({
16156
+ operation: "pathCompress",
16157
+ elements: cloneElements(elements),
16158
+ highlightElements: path.slice(0, -1),
16159
+ pathToRoot: path,
16160
+ description: `Path compression: All nodes on path now point directly to root ${root}`,
16161
+ codeLine: 6,
16162
+ variables: { x, root }
16163
+ });
16164
+ }
16165
+ } else if (operation.op === "union") {
16166
+ const x = operation.a;
16167
+ const y = operation.b;
16168
+ steps.push({
16169
+ operation: "union",
16170
+ elements: cloneElements(elements),
16171
+ highlightElements: [x, y],
16172
+ description: `union(${x}, ${y}): Merge sets containing ${x} and ${y}`,
16173
+ codeLine: 10,
16174
+ variables: { x, y }
16175
+ });
16176
+ const { root: rootX, path: pathX } = find(x);
16177
+ const { root: rootY, path: pathY } = find(y);
16178
+ steps.push({
16179
+ operation: "find",
16180
+ elements: cloneElements(elements),
16181
+ highlightElements: [rootX, rootY],
16182
+ pathToRoot: [...pathX, ...pathY],
16183
+ description: `find(${x}) = ${rootX}, find(${y}) = ${rootY}`,
16184
+ codeLine: 11,
16185
+ variables: { x, y, rootX, rootY }
16186
+ });
16187
+ if (rootX === rootY) {
16188
+ steps.push({
16189
+ operation: "sameSet",
16190
+ elements: cloneElements(elements),
16191
+ highlightElements: [rootX],
16192
+ description: `Already in same set! No union needed.`,
16193
+ codeLine: 13,
16194
+ variables: { rootX, rootY, "same": "true" }
16195
+ });
16196
+ } else {
16197
+ const rankX = elements[rootX].rank;
16198
+ const rankY = elements[rootY].rank;
16199
+ let newRoot;
16200
+ if (rankX < rankY) {
16201
+ elements[rootX].parent = rootY;
16202
+ newRoot = rootY;
16203
+ } else if (rankX > rankY) {
16204
+ elements[rootY].parent = rootX;
16205
+ newRoot = rootX;
16206
+ } else {
16207
+ elements[rootY].parent = rootX;
16208
+ elements[rootX].rank++;
16209
+ newRoot = rootX;
16210
+ }
16211
+ steps.push({
16212
+ operation: "unionByRank",
16213
+ elements: cloneElements(elements),
16214
+ highlightElements: [newRoot],
16215
+ highlightEdge: [rootX, rootY],
16216
+ description: `Union by rank: rank[${rootX}]=${rankX}, rank[${rootY}]=${rankY}. ${newRoot} becomes root.`,
16217
+ codeLine: rankX === rankY ? 20 : rankX < rankY ? 16 : 18,
16218
+ variables: { rootX, rootY, rankX, rankY, newRoot }
16219
+ });
16220
+ }
16221
+ } else if (operation.op === "connected") {
16222
+ const x = operation.a;
16223
+ const y = operation.b;
16224
+ steps.push({
16225
+ operation: "find",
16226
+ elements: cloneElements(elements),
16227
+ highlightElements: [x, y],
16228
+ description: `connected(${x}, ${y}): Check if ${x} and ${y} are in the same set`,
16229
+ codeLine: 4,
16230
+ variables: { x, y }
16231
+ });
16232
+ const { root: rootX, path: pathX } = find(x);
16233
+ const { root: rootY, path: pathY } = find(y);
16234
+ const connected = rootX === rootY;
16235
+ steps.push({
16236
+ operation: "sameSet",
16237
+ elements: cloneElements(elements),
16238
+ highlightElements: connected ? [rootX] : [rootX, rootY],
16239
+ pathToRoot: [...pathX, ...pathY],
16240
+ description: connected ? `✓ Yes! Both ${x} and ${y} have root ${rootX}` : `✗ No! ${x} has root ${rootX}, ${y} has root ${rootY}`,
16241
+ codeLine: 13,
16242
+ variables: { x, y, rootX, rootY, connected: connected ? "true" : "false" },
16243
+ queryResult: connected
16244
+ });
16245
+ }
16246
+ }
16247
+ const roots = /* @__PURE__ */ new Set();
16248
+ for (let i = 0; i < INITIAL_SIZE; i++) {
16249
+ roots.add(find(i).root);
16250
+ }
16251
+ steps.push({
16252
+ operation: "done",
16253
+ elements: cloneElements(elements),
16254
+ description: `✓ Done! ${roots.size} distinct set(s) remain.`,
16255
+ codeLine: -1,
16256
+ variables: { sets: roots.size }
16257
+ });
16258
+ return steps;
16259
+ }
16260
+ const UnionFindVisualizerComponent = ({
16261
+ showControls = true,
16262
+ showCode = true,
16263
+ className = ""
16264
+ }) => {
16265
+ const VISUALIZER_ID = "unionfind-visualizer";
16266
+ const { copyUrlToClipboard } = useUrlState({ prefix: "uf", scrollToId: VISUALIZER_ID });
16267
+ const generateSteps = useMemo(() => generateUnionFindSteps, []);
16268
+ const {
16269
+ steps,
16270
+ currentStep,
16271
+ currentStepData,
16272
+ isPlaying,
16273
+ speed,
16274
+ setSpeed,
16275
+ handlePlayPause,
16276
+ handleStep,
16277
+ handleStepBack,
16278
+ handleReset
16279
+ } = useVisualizerPlayback({
16280
+ generateSteps
16281
+ });
16282
+ const stepData = currentStepData || {
16283
+ operation: "init",
16284
+ elements: [],
16285
+ description: ""
16286
+ };
16287
+ const groups = useMemo(() => {
16288
+ const groupMap = /* @__PURE__ */ new Map();
16289
+ for (const el of stepData.elements) {
16290
+ let current = el.id;
16291
+ while (stepData.elements[current].parent !== current) {
16292
+ current = stepData.elements[current].parent;
16293
+ }
16294
+ const root = current;
16295
+ if (!groupMap.has(root)) {
16296
+ groupMap.set(root, []);
16297
+ }
16298
+ groupMap.get(root).push(el.id);
16299
+ }
16300
+ return groupMap;
16301
+ }, [stepData.elements]);
16302
+ const getElementStyle = (id) => {
16303
+ var _a, _b, _c;
16304
+ const isHighlighted = (_a = stepData.highlightElements) == null ? void 0 : _a.includes(id);
16305
+ const isInPath = (_b = stepData.pathToRoot) == null ? void 0 : _b.includes(id);
16306
+ const isRoot = ((_c = stepData.elements[id]) == null ? void 0 : _c.parent) === id;
16307
+ if (isHighlighted && isRoot) return "border-green-500 bg-green-100 ring-2 ring-green-300";
16308
+ if (isHighlighted) return "border-blue-400 bg-blue-100 ring-2 ring-blue-300";
16309
+ if (isInPath) return "border-yellow-400 bg-yellow-100";
16310
+ if (isRoot) return "border-green-300 bg-green-50";
16311
+ return "border-gray-300 bg-gray-50";
16312
+ };
16313
+ const getStatusVariant = () => {
16314
+ if (stepData.queryResult === false) return "error";
16315
+ if (stepData.queryResult === true) return "success";
16316
+ if (stepData.operation === "done") return "warning";
16317
+ if (stepData.operation === "pathCompress" || stepData.operation === "unionByRank") return "success";
16318
+ return "default";
16319
+ };
16320
+ const handleShare = useCallback(async () => {
16321
+ return copyUrlToClipboard({ step: currentStep });
16322
+ }, [copyUrlToClipboard, currentStep]);
16323
+ const groupColors = ["bg-blue-50", "bg-green-50", "bg-yellow-50", "bg-purple-50", "bg-pink-50", "bg-cyan-50", "bg-orange-50", "bg-rose-50"];
16324
+ return /* @__PURE__ */ jsxs(
16325
+ "div",
16326
+ {
16327
+ id: VISUALIZER_ID,
16328
+ className: `bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden ${className}`,
16329
+ children: [
16330
+ /* @__PURE__ */ jsx("div", { className: "px-4 py-3 bg-gradient-to-r from-violet-50 to-purple-50 border-b border-gray-200", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between flex-wrap gap-2", children: [
16331
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
16332
+ /* @__PURE__ */ jsx("h3", { className: "font-semibold text-gray-900", children: "Union-Find (Disjoint Set)" }),
16333
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
16334
+ /* @__PURE__ */ jsx("span", { className: "px-2 py-0.5 text-xs font-medium bg-violet-100 text-violet-700 rounded", children: "Find: O(α(n))" }),
16335
+ /* @__PURE__ */ jsx("span", { className: "px-2 py-0.5 text-xs font-medium bg-purple-100 text-purple-700 rounded", children: "Union: O(α(n))" })
16336
+ ] })
16337
+ ] }),
16338
+ /* @__PURE__ */ jsx(ShareButton, { onShare: handleShare })
16339
+ ] }) }),
16340
+ /* @__PURE__ */ jsx("div", { className: "p-4", children: /* @__PURE__ */ jsxs("div", { className: `flex gap-4 ${showCode ? "flex-col lg:flex-row" : ""}`, children: [
16341
+ /* @__PURE__ */ jsxs(VisualizationArea, { minHeight: 400, className: showCode ? "flex-1" : "w-full", children: [
16342
+ /* @__PURE__ */ jsxs("div", { className: "mb-4 p-3 bg-gradient-to-r from-violet-50 to-purple-50 rounded-lg border border-violet-200", children: [
16343
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-violet-800 mb-2", children: "🔗 Union-Find / Disjoint Set Union (DSU)" }),
16344
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-violet-700 space-y-1", children: [
16345
+ /* @__PURE__ */ jsxs("div", { children: [
16346
+ "• ",
16347
+ /* @__PURE__ */ jsx("strong", { children: "Path Compression" }),
16348
+ ": Flattens tree on find()"
16349
+ ] }),
16350
+ /* @__PURE__ */ jsxs("div", { children: [
16351
+ "• ",
16352
+ /* @__PURE__ */ jsx("strong", { children: "Union by Rank" }),
16353
+ ": Attach smaller tree to larger"
16354
+ ] }),
16355
+ /* @__PURE__ */ jsx("div", { children: "• α(n) = inverse Ackermann, practically ≤ 4" }),
16356
+ /* @__PURE__ */ jsx("div", { children: "• Used in Kruskal's MST, cycle detection, networks" })
16357
+ ] })
16358
+ ] }),
16359
+ /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
16360
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-gray-700 mb-2", children: "Elements (parent pointers shown as arrows)" }),
16361
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-3 justify-center", children: stepData.elements.map((el) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center", children: [
16362
+ /* @__PURE__ */ jsxs(
16363
+ "div",
16364
+ {
16365
+ className: `
16366
+ w-12 h-12 rounded-lg border-2 flex flex-col items-center justify-center
16367
+ text-sm font-bold transition-all duration-300
16368
+ ${getElementStyle(el.id)}
16369
+ `,
16370
+ children: [
16371
+ /* @__PURE__ */ jsx("span", { children: el.id }),
16372
+ /* @__PURE__ */ jsxs("span", { className: "text-[9px] text-gray-500", children: [
16373
+ "r:",
16374
+ el.rank
16375
+ ] })
16376
+ ]
16377
+ }
16378
+ ),
16379
+ el.parent !== el.id && /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-500 mt-1", children: [
16380
+ "↑ ",
16381
+ el.parent
16382
+ ] }),
16383
+ el.parent === el.id && /* @__PURE__ */ jsx("div", { className: "text-xs text-green-600 mt-1 font-medium", children: "root" })
16384
+ ] }, el.id)) })
16385
+ ] }),
16386
+ /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
16387
+ /* @__PURE__ */ jsxs("div", { className: "text-sm font-medium text-gray-700 mb-2", children: [
16388
+ "Connected Components (",
16389
+ groups.size,
16390
+ " set",
16391
+ groups.size !== 1 ? "s" : "",
16392
+ ")"
16393
+ ] }),
16394
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: Array.from(groups.entries()).map(([root, members], idx) => /* @__PURE__ */ jsxs(
16395
+ "div",
16396
+ {
16397
+ className: `px-3 py-2 rounded-lg border ${groupColors[idx % groupColors.length]} border-gray-300`,
16398
+ children: [
16399
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-600 mb-1", children: [
16400
+ "Root: ",
16401
+ root
16402
+ ] }),
16403
+ /* @__PURE__ */ jsx("div", { className: "flex gap-1", children: members.sort((a, b) => a - b).map((m) => /* @__PURE__ */ jsx(
16404
+ "span",
16405
+ {
16406
+ className: `w-6 h-6 rounded flex items-center justify-center text-xs font-medium
16407
+ ${m === root ? "bg-green-200 text-green-800" : "bg-white text-gray-700 border border-gray-300"}
16408
+ `,
16409
+ children: m
16410
+ },
16411
+ m
16412
+ )) })
16413
+ ]
16414
+ },
16415
+ root
16416
+ )) })
16417
+ ] }),
16418
+ /* @__PURE__ */ jsx(
16419
+ StatusPanel,
16420
+ {
16421
+ description: stepData.description,
16422
+ currentStep,
16423
+ totalSteps: steps.length,
16424
+ variant: getStatusVariant()
16425
+ }
16426
+ )
16427
+ ] }),
16428
+ showCode && /* @__PURE__ */ jsxs("div", { className: "w-full lg:w-56 flex-shrink-0 space-y-2", children: [
16429
+ /* @__PURE__ */ jsx(
16430
+ CodePanel,
16431
+ {
16432
+ code: UF_CODE,
16433
+ activeLine: (currentStepData == null ? void 0 : currentStepData.codeLine) ?? -1,
16434
+ variables: currentStepData == null ? void 0 : currentStepData.variables
16435
+ }
16436
+ ),
16437
+ /* @__PURE__ */ jsx(HelpPanel, {})
16438
+ ] })
16439
+ ] }) }),
16440
+ showControls && /* @__PURE__ */ jsxs("div", { className: "px-4 py-3 bg-gray-50 border-t border-gray-200", children: [
16441
+ /* @__PURE__ */ jsx(
16442
+ ControlPanel,
16443
+ {
16444
+ isPlaying,
16445
+ currentStep,
16446
+ totalSteps: steps.length,
16447
+ speed,
16448
+ onPlayPause: handlePlayPause,
16449
+ onStep: handleStep,
16450
+ onStepBack: handleStepBack,
16451
+ onReset: handleReset,
16452
+ onSpeedChange: setSpeed,
16453
+ accentColor: "violet"
16454
+ }
16455
+ ),
16456
+ /* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$4 })
16457
+ ] })
16458
+ ]
16459
+ }
16460
+ );
16461
+ };
16462
+ const UnionFindVisualizer = React.memo(UnionFindVisualizerComponent);
15550
16463
  const VIRTUAL_NODES_PER_SERVER = 3;
15551
16464
  const OPERATIONS = [
15552
16465
  { op: "addServer", server: "Server-A" },
@@ -17711,6 +18624,8 @@ export {
17711
18624
  StepHistory,
17712
18625
  TreeSetInterviewVisualizer,
17713
18626
  TreeSetVisualizer,
18627
+ TrieVisualizer,
18628
+ UnionFindVisualizer,
17714
18629
  VisualizationArea,
17715
18630
  useInterviewMode,
17716
18631
  useUrlState,