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