diff --git a/src/components/charts/KeyboardHeatmap.ts b/src/components/charts/KeyboardHeatmap.ts
index 41b9d11..a8f522c 100644
--- a/src/components/charts/KeyboardHeatmap.ts
+++ b/src/components/charts/KeyboardHeatmap.ts
@@ -235,7 +235,7 @@ export class KeyboardHeatmap {
">
- 🟢 Starke Tasten
+ Starke Tasten
${bestKeys
.map(
diff --git a/src/components/typing-area/TypingArea.ts b/src/components/typing-area/TypingArea.ts
index 4b56db8..c010bec 100644
--- a/src/components/typing-area/TypingArea.ts
+++ b/src/components/typing-area/TypingArea.ts
@@ -46,6 +46,40 @@ export class TypingArea {
}
}
+ /**
+ * Handle backspace - allow correction of mistakes
+ * Returns true if backspace was processed, false if at start
+ */
+ handleBackspace(): boolean {
+ if (this.currentPosition === 0) {
+ return false;
+ }
+
+ // Move position back
+ this.currentPosition--;
+
+ // Reset the character at the current position
+ const charEl = this.charElements[this.currentPosition];
+ if (charEl) {
+ charEl.classList.remove('correct', 'incorrect', 'upcoming');
+ charEl.classList.add('current');
+ }
+
+ // Reset the next character to upcoming (if exists)
+ if (this.currentPosition + 1 < this.charElements.length) {
+ const nextCharEl = this.charElements[this.currentPosition + 1];
+ if (nextCharEl) {
+ nextCharEl.classList.remove('current');
+ nextCharEl.classList.add('upcoming');
+ }
+ }
+
+ // Emit backspace event
+ EventBus.emit('typing:backspace', { position: this.currentPosition });
+
+ return true;
+ }
+
/**
* Process a keystroke
*/
diff --git a/src/components/visualization/AlgorithmVisualizer.ts b/src/components/visualization/AlgorithmVisualizer.ts
new file mode 100644
index 0000000..fd33f44
--- /dev/null
+++ b/src/components/visualization/AlgorithmVisualizer.ts
@@ -0,0 +1,1227 @@
+/**
+ * Algorithm Visualizer Component
+ * Beautiful step-by-step visualizations for algorithms
+ */
+
+export type VisualizationState = {
+ array: number[];
+ comparing: number[];
+ swapping: number[];
+ sorted: number[];
+ pivot?: number;
+ current?: number;
+ left?: number;
+ right?: number;
+ mid?: number;
+ found?: number;
+ message: string;
+};
+
+export type AlgorithmStep = {
+ state: VisualizationState;
+ code: string;
+ lineHighlight: number;
+};
+
+/**
+ * Algorithm Visualizer - Creates beautiful animations
+ */
+export class AlgorithmVisualizer {
+ private readonly containerId: string;
+ private steps: AlgorithmStep[] = [];
+ private currentStep: number = 0;
+ private isPlaying: boolean = false;
+ private speed: number = 500; // ms
+ private animationFrame: number | null = null;
+
+ constructor(containerId: string) {
+ this.containerId = containerId;
+ }
+
+ /**
+ * Generate Bubble Sort steps
+ */
+ static generateBubbleSortSteps(arr: number[]): AlgorithmStep[] {
+ const steps: AlgorithmStep[] = [];
+ const array = [...arr];
+ const n = array.length;
+ const sorted: number[] = [];
+
+ const code = `function bubbleSort(arr) {
+ for (let i = 0; i < n - 1; i++) {
+ for (let j = 0; j < n - i - 1; j++) {
+ if (arr[j] > arr[j + 1]) {
+ swap(arr[j], arr[j + 1]);
+ }
+ }
+ }
+}`;
+
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [],
+ sorted: [],
+ message: 'Start Bubble Sort',
+ },
+ code,
+ lineHighlight: 1,
+ });
+
+ for (let i = 0; i < n - 1; i++) {
+ for (let j = 0; j < n - i - 1; j++) {
+ // Comparing
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [j, j + 1],
+ swapping: [],
+ sorted: [...sorted],
+ message: `Vergleiche ${array[j]} und ${array[j + 1]}`,
+ },
+ code,
+ lineHighlight: 3,
+ });
+
+ if (array[j] > array[j + 1]) {
+ // Swapping
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [j, j + 1],
+ sorted: [...sorted],
+ message: `Tausche ${array[j]} und ${array[j + 1]}`,
+ },
+ code,
+ lineHighlight: 4,
+ });
+
+ [array[j], array[j + 1]] = [array[j + 1], array[j]];
+
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [],
+ sorted: [...sorted],
+ message: `Getauscht!`,
+ },
+ code,
+ lineHighlight: 4,
+ });
+ }
+ }
+ sorted.unshift(n - 1 - i);
+ }
+
+ sorted.unshift(0);
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [],
+ sorted: [...sorted],
+ message: 'Array sortiert!',
+ },
+ code,
+ lineHighlight: 7,
+ });
+
+ return steps;
+ }
+
+ /**
+ * Generate Quick Sort steps
+ */
+ static generateQuickSortSteps(arr: number[]): AlgorithmStep[] {
+ const steps: AlgorithmStep[] = [];
+ const array = [...arr];
+ const sorted: number[] = [];
+
+ const code = `function quickSort(arr, low, high) {
+ if (low < high) {
+ const pivot = partition(arr, low, high);
+ quickSort(arr, low, pivot - 1);
+ quickSort(arr, pivot + 1, high);
+ }
+}`;
+
+ function partition(low: number, high: number): number {
+ const pivotValue = array[high];
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [],
+ sorted: [...sorted],
+ pivot: high,
+ message: `Pivot gewählt: ${pivotValue}`,
+ },
+ code,
+ lineHighlight: 3,
+ });
+
+ let i = low - 1;
+
+ for (let j = low; j < high; j++) {
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [j, high],
+ swapping: [],
+ sorted: [...sorted],
+ pivot: high,
+ current: j,
+ message: `Vergleiche ${array[j]} mit Pivot ${pivotValue}`,
+ },
+ code,
+ lineHighlight: 3,
+ });
+
+ if (array[j] < pivotValue) {
+ i++;
+ if (i !== j) {
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [i, j],
+ sorted: [...sorted],
+ pivot: high,
+ message: `Tausche ${array[i]} und ${array[j]}`,
+ },
+ code,
+ lineHighlight: 3,
+ });
+
+ [array[i], array[j]] = [array[j], array[i]];
+ }
+ }
+ }
+
+ if (i + 1 !== high) {
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [i + 1, high],
+ sorted: [...sorted],
+ pivot: high,
+ message: `Platziere Pivot an Position ${i + 1}`,
+ },
+ code,
+ lineHighlight: 3,
+ });
+
+ [array[i + 1], array[high]] = [array[high], array[i + 1]];
+ }
+
+ sorted.push(i + 1);
+ return i + 1;
+ }
+
+ function quickSort(low: number, high: number): void {
+ if (low < high) {
+ const pi = partition(low, high);
+ quickSort(low, pi - 1);
+ quickSort(pi + 1, high);
+ }
+ }
+
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [],
+ sorted: [],
+ message: 'Start Quick Sort',
+ },
+ code,
+ lineHighlight: 1,
+ });
+
+ quickSort(0, array.length - 1);
+
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [],
+ sorted: Array.from({ length: array.length }, (_, i) => i),
+ message: 'Array sortiert!',
+ },
+ code,
+ lineHighlight: 6,
+ });
+
+ return steps;
+ }
+
+ /**
+ * Generate Binary Search steps
+ */
+ static generateBinarySearchSteps(arr: number[], target: number): AlgorithmStep[] {
+ const steps: AlgorithmStep[] = [];
+ const array = [...arr].sort((a, b) => a - b);
+
+ const code = `function binarySearch(arr, target) {
+ let left = 0, right = arr.length - 1;
+ while (left <= right) {
+ const mid = Math.floor((left + right) / 2);
+ if (arr[mid] === target) return mid;
+ if (arr[mid] < target) left = mid + 1;
+ else right = mid - 1;
+ }
+ return -1;
+}`;
+
+ let left = 0;
+ let right = array.length - 1;
+
+ steps.push({
+ state: {
+ array,
+ comparing: [],
+ swapping: [],
+ sorted: Array.from({ length: array.length }, (_, i) => i),
+ left,
+ right,
+ message: `Suche nach ${target} in sortiertem Array`,
+ },
+ code,
+ lineHighlight: 2,
+ });
+
+ while (left <= right) {
+ const mid = Math.floor((left + right) / 2);
+
+ steps.push({
+ state: {
+ array,
+ comparing: [mid],
+ swapping: [],
+ sorted: Array.from({ length: array.length }, (_, i) => i),
+ left,
+ right,
+ mid,
+ message: `Mitte: Index ${mid}, Wert ${array[mid]}`,
+ },
+ code,
+ lineHighlight: 4,
+ });
+
+ if (array[mid] === target) {
+ steps.push({
+ state: {
+ array,
+ comparing: [],
+ swapping: [],
+ sorted: Array.from({ length: array.length }, (_, i) => i),
+ found: mid,
+ message: `Gefunden! ${target} ist an Index ${mid}`,
+ },
+ code,
+ lineHighlight: 5,
+ });
+ return steps;
+ }
+
+ if (array[mid] < target) {
+ steps.push({
+ state: {
+ array,
+ comparing: [],
+ swapping: [],
+ sorted: Array.from({ length: array.length }, (_, i) => i),
+ left: mid + 1,
+ right,
+ message: `${array[mid]} < ${target}, suche rechts`,
+ },
+ code,
+ lineHighlight: 6,
+ });
+ left = mid + 1;
+ } else {
+ steps.push({
+ state: {
+ array,
+ comparing: [],
+ swapping: [],
+ sorted: Array.from({ length: array.length }, (_, i) => i),
+ left,
+ right: mid - 1,
+ message: `${array[mid]} > ${target}, suche links`,
+ },
+ code,
+ lineHighlight: 7,
+ });
+ right = mid - 1;
+ }
+ }
+
+ steps.push({
+ state: {
+ array,
+ comparing: [],
+ swapping: [],
+ sorted: Array.from({ length: array.length }, (_, i) => i),
+ message: `${target} nicht gefunden`,
+ },
+ code,
+ lineHighlight: 9,
+ });
+
+ return steps;
+ }
+
+ /**
+ * Generate Insertion Sort steps
+ */
+ static generateInsertionSortSteps(arr: number[]): AlgorithmStep[] {
+ const steps: AlgorithmStep[] = [];
+ const array = [...arr];
+ const n = array.length;
+
+ const code = `function insertionSort(arr) {
+ for (let i = 1; i < arr.length; i++) {
+ const key = arr[i];
+ let j = i - 1;
+ while (j >= 0 && arr[j] > key) {
+ arr[j + 1] = arr[j];
+ j--;
+ }
+ arr[j + 1] = key;
+ }
+}`;
+
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [],
+ sorted: [0],
+ message: 'Start Insertion Sort',
+ },
+ code,
+ lineHighlight: 1,
+ });
+
+ for (let i = 1; i < n; i++) {
+ const key = array[i];
+ let j = i - 1;
+
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [i],
+ swapping: [],
+ sorted: Array.from({ length: i }, (_, idx) => idx),
+ current: i,
+ message: `Schlüssel: ${key}`,
+ },
+ code,
+ lineHighlight: 3,
+ });
+
+ while (j >= 0 && array[j] > key) {
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [j, i],
+ swapping: [],
+ sorted: Array.from({ length: i }, (_, idx) => idx),
+ message: `${array[j]} > ${key}, verschiebe nach rechts`,
+ },
+ code,
+ lineHighlight: 5,
+ });
+
+ array[j + 1] = array[j];
+ j--;
+
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [],
+ sorted: Array.from({ length: i }, (_, idx) => idx),
+ message: `Verschoben`,
+ },
+ code,
+ lineHighlight: 6,
+ });
+ }
+
+ array[j + 1] = key;
+
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [],
+ sorted: Array.from({ length: i + 1 }, (_, idx) => idx),
+ message: `${key} eingefügt an Position ${j + 1}`,
+ },
+ code,
+ lineHighlight: 9,
+ });
+ }
+
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [],
+ sorted: Array.from({ length: n }, (_, i) => i),
+ message: 'Array sortiert!',
+ },
+ code,
+ lineHighlight: 10,
+ });
+
+ return steps;
+ }
+
+ /**
+ * Generate Selection Sort steps
+ */
+ static generateSelectionSortSteps(arr: number[]): AlgorithmStep[] {
+ const steps: AlgorithmStep[] = [];
+ const array = [...arr];
+ const n = array.length;
+
+ const code = `function selectionSort(arr) {
+ for (let i = 0; i < arr.length - 1; i++) {
+ let minIdx = i;
+ for (let j = i + 1; j < arr.length; j++) {
+ if (arr[j] < arr[minIdx]) {
+ minIdx = j;
+ }
+ }
+ swap(arr[i], arr[minIdx]);
+ }
+}`;
+
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [],
+ sorted: [],
+ message: 'Start Selection Sort',
+ },
+ code,
+ lineHighlight: 1,
+ });
+
+ for (let i = 0; i < n - 1; i++) {
+ let minIdx = i;
+
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [i],
+ swapping: [],
+ sorted: Array.from({ length: i }, (_, idx) => idx),
+ current: i,
+ message: `Suche Minimum ab Index ${i}`,
+ },
+ code,
+ lineHighlight: 3,
+ });
+
+ for (let j = i + 1; j < n; j++) {
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [minIdx, j],
+ swapping: [],
+ sorted: Array.from({ length: i }, (_, idx) => idx),
+ message: `Vergleiche ${array[minIdx]} und ${array[j]}`,
+ },
+ code,
+ lineHighlight: 5,
+ });
+
+ if (array[j] < array[minIdx]) {
+ minIdx = j;
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [minIdx],
+ swapping: [],
+ sorted: Array.from({ length: i }, (_, idx) => idx),
+ message: `Neues Minimum: ${array[minIdx]}`,
+ },
+ code,
+ lineHighlight: 6,
+ });
+ }
+ }
+
+ if (minIdx !== i) {
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [i, minIdx],
+ sorted: Array.from({ length: i }, (_, idx) => idx),
+ message: `Tausche ${array[i]} und ${array[minIdx]}`,
+ },
+ code,
+ lineHighlight: 9,
+ });
+
+ [array[i], array[minIdx]] = [array[minIdx], array[i]];
+ }
+
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [],
+ sorted: Array.from({ length: i + 1 }, (_, idx) => idx),
+ message: `Position ${i} sortiert`,
+ },
+ code,
+ lineHighlight: 9,
+ });
+ }
+
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [],
+ sorted: Array.from({ length: n }, (_, i) => i),
+ message: 'Array sortiert!',
+ },
+ code,
+ lineHighlight: 10,
+ });
+
+ return steps;
+ }
+
+ /**
+ * Generate Merge Sort steps
+ */
+ static generateMergeSortSteps(arr: number[]): AlgorithmStep[] {
+ const steps: AlgorithmStep[] = [];
+ const array = [...arr];
+
+ const code = `function mergeSort(arr) {
+ if (arr.length <= 1) return arr;
+ const mid = Math.floor(arr.length / 2);
+ const left = mergeSort(arr.slice(0, mid));
+ const right = mergeSort(arr.slice(mid));
+ return merge(left, right);
+}`;
+
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [],
+ sorted: [],
+ message: 'Start Merge Sort',
+ },
+ code,
+ lineHighlight: 1,
+ });
+
+ function mergeSortHelper(start: number, end: number): void {
+ if (start >= end) {
+ return;
+ }
+
+ const mid = Math.floor((start + end) / 2);
+
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [mid],
+ swapping: [],
+ sorted: [],
+ message: `Teile Array bei Index ${mid}`,
+ },
+ code,
+ lineHighlight: 3,
+ });
+
+ mergeSortHelper(start, mid);
+ mergeSortHelper(mid + 1, end);
+
+ // Merge
+ const temp: number[] = [];
+ let i = start;
+ let j = mid + 1;
+
+ while (i <= mid && j <= end) {
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [i, j],
+ swapping: [],
+ sorted: [],
+ message: `Vergleiche ${array[i]} und ${array[j]}`,
+ },
+ code,
+ lineHighlight: 6,
+ });
+
+ if (array[i] <= array[j]) {
+ temp.push(array[i++]);
+ } else {
+ temp.push(array[j++]);
+ }
+ }
+
+ while (i <= mid) {
+ temp.push(array[i++]);
+ }
+ while (j <= end) {
+ temp.push(array[j++]);
+ }
+
+ for (let k = 0; k < temp.length; k++) {
+ array[start + k] = temp[k];
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [start + k],
+ sorted: [],
+ message: `Setze ${temp[k]} an Position ${start + k}`,
+ },
+ code,
+ lineHighlight: 6,
+ });
+ }
+ }
+
+ mergeSortHelper(0, array.length - 1);
+
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [],
+ sorted: Array.from({ length: array.length }, (_, i) => i),
+ message: 'Array sortiert!',
+ },
+ code,
+ lineHighlight: 6,
+ });
+
+ return steps;
+ }
+
+ /**
+ * Generate Heap Sort steps
+ */
+ static generateHeapSortSteps(arr: number[]): AlgorithmStep[] {
+ const steps: AlgorithmStep[] = [];
+ const array = [...arr];
+ const n = array.length;
+
+ const code = `function heapSort(arr) {
+ buildMaxHeap(arr);
+ for (let i = n - 1; i > 0; i--) {
+ swap(arr[0], arr[i]);
+ heapify(arr, i, 0);
+ }
+}`;
+
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [],
+ sorted: [],
+ message: 'Start Heap Sort - baue Max-Heap',
+ },
+ code,
+ lineHighlight: 1,
+ });
+
+ function heapify(size: number, root: number): void {
+ let largest = root;
+ const left = 2 * root + 1;
+ const right = 2 * root + 2;
+
+ if (left < size) {
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [largest, left],
+ swapping: [],
+ sorted: Array.from({ length: n - size }, (_, i) => n - 1 - i),
+ message: `Vergleiche ${array[largest]} mit linkem Kind ${array[left]}`,
+ },
+ code,
+ lineHighlight: 5,
+ });
+
+ if (array[left] > array[largest]) {
+ largest = left;
+ }
+ }
+
+ if (right < size) {
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [largest, right],
+ swapping: [],
+ sorted: Array.from({ length: n - size }, (_, i) => n - 1 - i),
+ message: `Vergleiche ${array[largest]} mit rechtem Kind ${array[right]}`,
+ },
+ code,
+ lineHighlight: 5,
+ });
+
+ if (array[right] > array[largest]) {
+ largest = right;
+ }
+ }
+
+ if (largest !== root) {
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [root, largest],
+ sorted: Array.from({ length: n - size }, (_, i) => n - 1 - i),
+ message: `Tausche ${array[root]} und ${array[largest]}`,
+ },
+ code,
+ lineHighlight: 5,
+ });
+
+ [array[root], array[largest]] = [array[largest], array[root]];
+ heapify(size, largest);
+ }
+ }
+
+ // Build max heap
+ for (let i = Math.floor(n / 2) - 1; i >= 0; i--) {
+ heapify(n, i);
+ }
+
+ // Extract elements from heap
+ for (let i = n - 1; i > 0; i--) {
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [0, i],
+ sorted: Array.from({ length: n - i }, (_, j) => n - 1 - j),
+ message: `Tausche Maximum ${array[0]} ans Ende`,
+ },
+ code,
+ lineHighlight: 4,
+ });
+
+ [array[0], array[i]] = [array[i], array[0]];
+ heapify(i, 0);
+ }
+
+ steps.push({
+ state: {
+ array: [...array],
+ comparing: [],
+ swapping: [],
+ sorted: Array.from({ length: n }, (_, i) => i),
+ message: 'Array sortiert!',
+ },
+ code,
+ lineHighlight: 6,
+ });
+
+ return steps;
+ }
+
+ /**
+ * Generate Linear Search steps
+ */
+ static generateLinearSearchSteps(arr: number[], target: number): AlgorithmStep[] {
+ const steps: AlgorithmStep[] = [];
+ const array = [...arr];
+
+ const code = `function linearSearch(arr, target) {
+ for (let i = 0; i < arr.length; i++) {
+ if (arr[i] === target) {
+ return i;
+ }
+ }
+ return -1;
+}`;
+
+ steps.push({
+ state: {
+ array,
+ comparing: [],
+ swapping: [],
+ sorted: [],
+ message: `Suche nach ${target} - durchlaufe alle Elemente`,
+ },
+ code,
+ lineHighlight: 1,
+ });
+
+ for (let i = 0; i < array.length; i++) {
+ steps.push({
+ state: {
+ array,
+ comparing: [i],
+ swapping: [],
+ sorted: [],
+ current: i,
+ message: `Prufe Index ${i}: ${array[i]} === ${target}?`,
+ },
+ code,
+ lineHighlight: 3,
+ });
+
+ if (array[i] === target) {
+ steps.push({
+ state: {
+ array,
+ comparing: [],
+ swapping: [],
+ sorted: [],
+ found: i,
+ message: `Gefunden! ${target} ist an Index ${i}`,
+ },
+ code,
+ lineHighlight: 4,
+ });
+ return steps;
+ }
+ }
+
+ steps.push({
+ state: {
+ array,
+ comparing: [],
+ swapping: [],
+ sorted: [],
+ message: `${target} nicht gefunden`,
+ },
+ code,
+ lineHighlight: 7,
+ });
+
+ return steps;
+ }
+
+ /**
+ * Set steps
+ */
+ setSteps(steps: AlgorithmStep[]): void {
+ this.steps = steps;
+ this.currentStep = 0;
+ this.render();
+ }
+
+ /**
+ * Render visualization
+ */
+ render(): void {
+ const container = document.getElementById(this.containerId);
+ if (!container) {
+ return;
+ }
+
+ const step = this.steps[this.currentStep];
+ if (!step) {
+ return;
+ }
+
+ const maxValue = Math.max(...step.state.array);
+ const barWidth = Math.min(50, Math.floor(400 / step.state.array.length) - 4);
+
+ container.innerHTML = `
+
+
+
+ ${step.state.array
+ .map((value, index) => {
+ let barClass = 'algo-bar';
+ if (step.state.comparing.includes(index)) {
+ barClass += ' comparing';
+ }
+ if (step.state.swapping.includes(index)) {
+ barClass += ' swapping';
+ }
+ if (step.state.sorted.includes(index)) {
+ barClass += ' sorted';
+ }
+ if (step.state.pivot === index) {
+ barClass += ' pivot';
+ }
+ if (step.state.found === index) {
+ barClass += ' found';
+ }
+ if (step.state.current === index) {
+ barClass += ' current';
+ }
+ if (step.state.left === index) {
+ barClass += ' left-bound';
+ }
+ if (step.state.right === index) {
+ barClass += ' right-bound';
+ }
+ if (step.state.mid === index) {
+ barClass += ' mid';
+ }
+
+ const height = (value / maxValue) * 180;
+ return `
+
+ ${value}
+
+ `;
+ })
+ .join('')}
+
+
+
${step.state.message}
+
+
+
+
+
+
+
+
+
+
+
+ ${this.currentStep + 1} / ${this.steps.length}
+
+
+
+
+
+ ${this.speed}ms
+
+
+
+
+
+
+
${this.highlightCode(step.code, step.lineHighlight)}
+
+
+
+
+
+ `;
+
+ this.attachEventListeners();
+ }
+
+ /**
+ * Highlight code line
+ */
+ private highlightCode(code: string, line: number): string {
+ const lines = code.split('\n');
+ return lines
+ .map((l, i) => {
+ const lineNum = i + 1;
+ const isHighlighted = lineNum === line;
+ const escapedLine = l.replace(//g, '>');
+ return `
${escapedLine}`;
+ })
+ .join('\n');
+ }
+
+ /**
+ * Attach event listeners
+ */
+ private attachEventListeners(): void {
+ document.getElementById('algo-first')?.addEventListener('click', () => this.goToStep(0));
+ document.getElementById('algo-prev')?.addEventListener('click', () => this.prevStep());
+ document.getElementById('algo-play')?.addEventListener('click', () => this.togglePlay());
+ document.getElementById('algo-next')?.addEventListener('click', () => this.nextStep());
+ document
+ .getElementById('algo-last')
+ ?.addEventListener('click', () => this.goToStep(this.steps.length - 1));
+
+ document.getElementById('algo-slider')?.addEventListener('input', e => {
+ const value = parseInt((e.target as HTMLInputElement).value);
+ this.goToStep(value);
+ });
+
+ document.getElementById('algo-speed')?.addEventListener('input', e => {
+ const value = parseInt((e.target as HTMLInputElement).value);
+ this.speed = 2100 - value;
+ this.render();
+ });
+ }
+
+ /**
+ * Go to specific step
+ */
+ goToStep(step: number): void {
+ this.currentStep = Math.max(0, Math.min(step, this.steps.length - 1));
+ this.render();
+ }
+
+ /**
+ * Next step
+ */
+ nextStep(): void {
+ if (this.currentStep < this.steps.length - 1) {
+ this.currentStep++;
+ this.render();
+ } else {
+ this.stop();
+ }
+ }
+
+ /**
+ * Previous step
+ */
+ prevStep(): void {
+ if (this.currentStep > 0) {
+ this.currentStep--;
+ this.render();
+ }
+ }
+
+ /**
+ * Toggle play/pause
+ */
+ togglePlay(): void {
+ if (this.isPlaying) {
+ this.stop();
+ } else {
+ this.play();
+ }
+ }
+
+ /**
+ * Play animation
+ */
+ play(): void {
+ this.isPlaying = true;
+ this.render();
+ this.animate();
+ }
+
+ /**
+ * Stop animation
+ */
+ stop(): void {
+ this.isPlaying = false;
+ if (this.animationFrame) {
+ cancelAnimationFrame(this.animationFrame);
+ this.animationFrame = null;
+ }
+ this.render();
+ }
+
+ /**
+ * Animate steps
+ */
+ private animate(): void {
+ if (!this.isPlaying) {
+ return;
+ }
+
+ const now = performance.now();
+ const step = () => {
+ if (!this.isPlaying) {
+ return;
+ }
+ if (performance.now() - now >= this.speed) {
+ this.nextStep();
+ if (this.isPlaying && this.currentStep < this.steps.length - 1) {
+ this.animate();
+ }
+ } else {
+ this.animationFrame = requestAnimationFrame(step);
+ }
+ };
+ this.animationFrame = requestAnimationFrame(step);
+ }
+
+ /**
+ * Reset visualization
+ */
+ reset(): void {
+ this.stop();
+ this.currentStep = 0;
+ this.render();
+ }
+
+ /**
+ * Destroy
+ */
+ destroy(): void {
+ this.stop();
+ const container = document.getElementById(this.containerId);
+ if (container) {
+ container.innerHTML = '';
+ }
+ }
+}
diff --git a/src/components/visualization/GraphVisualizer.ts b/src/components/visualization/GraphVisualizer.ts
new file mode 100644
index 0000000..5a5c402
--- /dev/null
+++ b/src/components/visualization/GraphVisualizer.ts
@@ -0,0 +1,2168 @@
+/**
+ *
+ * Graph & Tree Visualizer Component
+ * Visualizes graph algorithms like Dijkstra, BFS, DFS and tree operations
+ */
+
+export interface GraphNode {
+ id: string;
+ x: number;
+ y: number;
+ value?: number;
+ label?: string;
+}
+
+export interface GraphEdge {
+ from: string;
+ to: string;
+ weight?: number;
+}
+
+export interface TreeNode {
+ id: string;
+ value: number;
+ left?: TreeNode;
+ right?: TreeNode;
+ x?: number;
+ y?: number;
+}
+
+export type GraphVisualizationState = {
+ nodes: GraphNode[];
+ edges: GraphEdge[];
+ visited: string[];
+ current: string | null;
+ path: string[];
+ queue: string[];
+ stack: string[];
+ distances: Map
;
+ message: string;
+};
+
+export type TreeVisualizationState = {
+ root: TreeNode | null;
+ visited: string[];
+ current: string | null;
+ path: string[];
+ comparing: string | null;
+ message: string;
+};
+
+export type GraphAlgorithmStep = {
+ state: GraphVisualizationState;
+ code: string;
+ lineHighlight: number;
+};
+
+export type TreeAlgorithmStep = {
+ state: TreeVisualizationState;
+ code: string;
+ lineHighlight: number;
+};
+
+/**
+ * Graph Visualizer - Creates beautiful graph and tree animations
+ */
+export class GraphVisualizer {
+ private readonly containerId: string;
+ private steps: (GraphAlgorithmStep | TreeAlgorithmStep)[] = [];
+ private currentStep: number = 0;
+ private isPlaying: boolean = false;
+ private speed: number = 800;
+ private animationFrame: number | null = null;
+ private mode: 'graph' | 'tree' = 'graph';
+
+ constructor(containerId: string) {
+ this.containerId = containerId;
+ }
+
+ /**
+ * Create a sample graph for demonstrations
+ */
+ static createSampleGraph(): { nodes: GraphNode[]; edges: GraphEdge[] } {
+ const nodes: GraphNode[] = [
+ { id: 'A', x: 150, y: 50, label: 'A' },
+ { id: 'B', x: 50, y: 150, label: 'B' },
+ { id: 'C', x: 250, y: 150, label: 'C' },
+ { id: 'D', x: 100, y: 250, label: 'D' },
+ { id: 'E', x: 200, y: 250, label: 'E' },
+ { id: 'F', x: 150, y: 350, label: 'F' },
+ ];
+
+ const edges: GraphEdge[] = [
+ { from: 'A', to: 'B', weight: 4 },
+ { from: 'A', to: 'C', weight: 2 },
+ { from: 'B', to: 'D', weight: 5 },
+ { from: 'C', to: 'D', weight: 8 },
+ { from: 'C', to: 'E', weight: 3 },
+ { from: 'D', to: 'E', weight: 2 },
+ { from: 'D', to: 'F', weight: 6 },
+ { from: 'E', to: 'F', weight: 1 },
+ ];
+
+ return { nodes, edges };
+ }
+
+ /**
+ * Create a sample weighted graph for Dijkstra
+ */
+ static createDijkstraGraph(): { nodes: GraphNode[]; edges: GraphEdge[] } {
+ const nodes: GraphNode[] = [
+ { id: '0', x: 80, y: 100, label: 'Start' },
+ { id: '1', x: 200, y: 50, label: '1' },
+ { id: '2', x: 200, y: 180, label: '2' },
+ { id: '3', x: 320, y: 100, label: '3' },
+ { id: '4', x: 440, y: 50, label: '4' },
+ { id: '5', x: 440, y: 180, label: 'Ziel' },
+ ];
+
+ const edges: GraphEdge[] = [
+ { from: '0', to: '1', weight: 4 },
+ { from: '0', to: '2', weight: 2 },
+ { from: '1', to: '2', weight: 1 },
+ { from: '1', to: '3', weight: 5 },
+ { from: '2', to: '3', weight: 8 },
+ { from: '2', to: '4', weight: 10 },
+ { from: '3', to: '4', weight: 2 },
+ { from: '3', to: '5', weight: 6 },
+ { from: '4', to: '5', weight: 3 },
+ ];
+
+ return { nodes, edges };
+ }
+
+ /**
+ * Generate Dijkstra's Algorithm steps
+ */
+ static generateDijkstraSteps(
+ nodes: GraphNode[],
+ edges: GraphEdge[],
+ startId: string,
+ endId: string
+ ): GraphAlgorithmStep[] {
+ const steps: GraphAlgorithmStep[] = [];
+
+ const code = `function dijkstra(graph, start) {
+ const dist = new Map(); // Distanzen
+ const prev = new Map(); // Vorgänger
+ const pq = new PriorityQueue();
+
+ // Initialisiere alle Distanzen mit ∞
+ for (const node of graph.nodes) {
+ dist.set(node, Infinity);
+ }
+ dist.set(start, 0);
+ pq.add(start, 0);
+
+ while (!pq.isEmpty()) {
+ const u = pq.extractMin();
+ for (const neighbor of getNeighbors(u)) {
+ const alt = dist.get(u) + weight(u, neighbor);
+ if (alt < dist.get(neighbor)) {
+ dist.set(neighbor, alt);
+ prev.set(neighbor, u);
+ pq.decreaseKey(neighbor, alt);
+ }
+ }
+ }
+ return { dist, prev };
+}`;
+
+ // Build adjacency list
+ const adj = new Map();
+ nodes.forEach(n => adj.set(n.id, []));
+ edges.forEach(e => {
+ adj.get(e.from)?.push({ node: e.to, weight: e.weight || 1 });
+ adj.get(e.to)?.push({ node: e.from, weight: e.weight || 1 });
+ });
+
+ const distances = new Map();
+ const previous = new Map();
+ const visited = new Set();
+
+ nodes.forEach(n => distances.set(n.id, Infinity));
+ distances.set(startId, 0);
+
+ const pq: { node: string; dist: number }[] = [{ node: startId, dist: 0 }];
+
+ // Initial step
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: [],
+ current: null,
+ path: [],
+ queue: [startId],
+ stack: [],
+ distances: new Map(distances),
+ message: `Starte Dijkstra von Knoten ${startId}. Alle Distanzen auf ∞ gesetzt, Start = 0`,
+ },
+ code,
+ lineHighlight: 7,
+ });
+
+ while (pq.length > 0) {
+ pq.sort((a, b) => a.dist - b.dist);
+ const { node: current } = pq.shift()!;
+
+ if (visited.has(current)) {
+ continue;
+ }
+ visited.add(current);
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: Array.from(visited),
+ current,
+ path: [],
+ queue: pq.map(p => p.node),
+ stack: [],
+ distances: new Map(distances),
+ message: `Besuche Knoten ${current} mit Distanz ${distances.get(current)}`,
+ },
+ code,
+ lineHighlight: 13,
+ });
+
+ if (current === endId) {
+ // Reconstruct path
+ const path: string[] = [];
+ let curr: string | undefined = endId;
+ while (curr) {
+ path.unshift(curr);
+ curr = previous.get(curr);
+ }
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: Array.from(visited),
+ current: endId,
+ path,
+ queue: [],
+ stack: [],
+ distances: new Map(distances),
+ message: `Ziel erreicht! Kürzester Pfad: ${path.join(' → ')} (Distanz: ${distances.get(endId)})`,
+ },
+ code,
+ lineHighlight: 21,
+ });
+ return steps;
+ }
+
+ const neighbors = adj.get(current) || [];
+ for (const { node: neighbor, weight } of neighbors) {
+ if (visited.has(neighbor)) {
+ continue;
+ }
+
+ const alt = (distances.get(current) || 0) + weight;
+ const currentDist = distances.get(neighbor) || Infinity;
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: Array.from(visited),
+ current,
+ path: [current, neighbor],
+ queue: pq.map(p => p.node),
+ stack: [],
+ distances: new Map(distances),
+ message: `Prüfe Kante ${current} → ${neighbor}: ${distances.get(current)} + ${weight} = ${alt} ${alt < currentDist ? '< ' + currentDist + ' → Update!' : '>= ' + currentDist}`,
+ },
+ code,
+ lineHighlight: 15,
+ });
+
+ if (alt < currentDist) {
+ distances.set(neighbor, alt);
+ previous.set(neighbor, current);
+
+ const existing = pq.findIndex(p => p.node === neighbor);
+ if (existing >= 0) {
+ pq[existing].dist = alt;
+ } else {
+ pq.push({ node: neighbor, dist: alt });
+ }
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: Array.from(visited),
+ current,
+ path: [],
+ queue: pq.map(p => p.node),
+ stack: [],
+ distances: new Map(distances),
+ message: `Distanz zu ${neighbor} aktualisiert: ${alt}`,
+ },
+ code,
+ lineHighlight: 17,
+ });
+ }
+ }
+ }
+
+ return steps;
+ }
+
+ /**
+ * Generate BFS (Breadth-First Search) steps
+ */
+ static generateBFSSteps(
+ nodes: GraphNode[],
+ edges: GraphEdge[],
+ startId: string
+ ): GraphAlgorithmStep[] {
+ const steps: GraphAlgorithmStep[] = [];
+
+ const code = `function bfs(graph, start) {
+ const visited = new Set();
+ const queue = [start];
+ visited.add(start);
+
+ while (queue.length > 0) {
+ const current = queue.shift();
+ process(current);
+
+ for (const neighbor of getNeighbors(current)) {
+ if (!visited.has(neighbor)) {
+ visited.add(neighbor);
+ queue.push(neighbor);
+ }
+ }
+ }
+}`;
+
+ // Build adjacency list
+ const adj = new Map();
+ nodes.forEach(n => adj.set(n.id, []));
+ edges.forEach(e => {
+ adj.get(e.from)?.push(e.to);
+ adj.get(e.to)?.push(e.from);
+ });
+
+ const visited = new Set();
+ const queue: string[] = [startId];
+ visited.add(startId);
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: [startId],
+ current: null,
+ path: [],
+ queue: [...queue],
+ stack: [],
+ distances: new Map(),
+ message: `Starte BFS von Knoten ${startId}. Queue initialisiert.`,
+ },
+ code,
+ lineHighlight: 3,
+ });
+
+ while (queue.length > 0) {
+ const current = queue.shift()!;
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: Array.from(visited),
+ current,
+ path: [],
+ queue: [...queue],
+ stack: [],
+ distances: new Map(),
+ message: `Besuche Knoten ${current} (aus Queue entnommen)`,
+ },
+ code,
+ lineHighlight: 7,
+ });
+
+ const neighbors = (adj.get(current) || []).sort();
+ for (const neighbor of neighbors) {
+ if (!visited.has(neighbor)) {
+ visited.add(neighbor);
+ queue.push(neighbor);
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: Array.from(visited),
+ current,
+ path: [current, neighbor],
+ queue: [...queue],
+ stack: [],
+ distances: new Map(),
+ message: `Nachbar ${neighbor} entdeckt - zur Queue hinzugefügt`,
+ },
+ code,
+ lineHighlight: 12,
+ });
+ }
+ }
+ }
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: Array.from(visited),
+ current: null,
+ path: [],
+ queue: [],
+ stack: [],
+ distances: new Map(),
+ message: `BFS abgeschlossen! Alle ${visited.size} Knoten besucht.`,
+ },
+ code,
+ lineHighlight: 16,
+ });
+
+ return steps;
+ }
+
+ /**
+ * Generate DFS (Depth-First Search) steps
+ */
+ static generateDFSSteps(
+ nodes: GraphNode[],
+ edges: GraphEdge[],
+ startId: string
+ ): GraphAlgorithmStep[] {
+ const steps: GraphAlgorithmStep[] = [];
+
+ const code = `function dfs(graph, start) {
+ const visited = new Set();
+ const stack = [start];
+
+ while (stack.length > 0) {
+ const current = stack.pop();
+ if (visited.has(current)) continue;
+
+ visited.add(current);
+ process(current);
+
+ for (const neighbor of getNeighbors(current)) {
+ if (!visited.has(neighbor)) {
+ stack.push(neighbor);
+ }
+ }
+ }
+}`;
+
+ // Build adjacency list
+ const adj = new Map();
+ nodes.forEach(n => adj.set(n.id, []));
+ edges.forEach(e => {
+ adj.get(e.from)?.push(e.to);
+ adj.get(e.to)?.push(e.from);
+ });
+
+ const visited = new Set();
+ const stack: string[] = [startId];
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: [],
+ current: null,
+ path: [],
+ queue: [],
+ stack: [...stack],
+ distances: new Map(),
+ message: `Starte DFS von Knoten ${startId}. Stack initialisiert.`,
+ },
+ code,
+ lineHighlight: 3,
+ });
+
+ while (stack.length > 0) {
+ const current = stack.pop()!;
+
+ if (visited.has(current)) {
+ continue;
+ }
+ visited.add(current);
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: Array.from(visited),
+ current,
+ path: [],
+ queue: [],
+ stack: [...stack],
+ distances: new Map(),
+ message: `Besuche Knoten ${current} (vom Stack genommen)`,
+ },
+ code,
+ lineHighlight: 9,
+ });
+
+ const neighbors = (adj.get(current) || []).sort().reverse();
+ for (const neighbor of neighbors) {
+ if (!visited.has(neighbor)) {
+ stack.push(neighbor);
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: Array.from(visited),
+ current,
+ path: [current, neighbor],
+ queue: [],
+ stack: [...stack],
+ distances: new Map(),
+ message: `Nachbar ${neighbor} auf Stack gelegt`,
+ },
+ code,
+ lineHighlight: 14,
+ });
+ }
+ }
+ }
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: Array.from(visited),
+ current: null,
+ path: [],
+ queue: [],
+ stack: [],
+ distances: new Map(),
+ message: `DFS abgeschlossen! Alle ${visited.size} Knoten besucht.`,
+ },
+ code,
+ lineHighlight: 17,
+ });
+
+ return steps;
+ }
+
+ /**
+ * Create a sample Binary Search Tree
+ */
+ static createSampleBST(): TreeNode {
+ return {
+ id: '1',
+ value: 50,
+ left: {
+ id: '2',
+ value: 30,
+ left: { id: '4', value: 20 },
+ right: { id: '5', value: 40 },
+ },
+ right: {
+ id: '3',
+ value: 70,
+ left: { id: '6', value: 60 },
+ right: { id: '7', value: 80 },
+ },
+ };
+ }
+
+ /**
+ * Generate BST Search steps
+ */
+ static generateBSTSearchSteps(root: TreeNode, target: number): TreeAlgorithmStep[] {
+ const steps: TreeAlgorithmStep[] = [];
+
+ const code = `function searchBST(node, target) {
+ if (node === null) {
+ return null; // Nicht gefunden
+ }
+
+ if (target === node.value) {
+ return node; // Gefunden!
+ }
+
+ if (target < node.value) {
+ return searchBST(node.left, target);
+ } else {
+ return searchBST(node.right, target);
+ }
+}`;
+
+ const visited: string[] = [];
+ let current: TreeNode | null = root;
+
+ steps.push({
+ state: {
+ root,
+ visited: [],
+ current: null,
+ path: [],
+ comparing: null,
+ message: `Suche nach ${target} im Binary Search Tree`,
+ },
+ code,
+ lineHighlight: 1,
+ });
+
+ while (current) {
+ visited.push(current.id);
+
+ steps.push({
+ state: {
+ root,
+ visited: [...visited],
+ current: current.id,
+ path: [...visited],
+ comparing: current.id,
+ message: `Vergleiche ${target} mit Knoten ${current.value}`,
+ },
+ code,
+ lineHighlight: 6,
+ });
+
+ if (target === current.value) {
+ steps.push({
+ state: {
+ root,
+ visited: [...visited],
+ current: current.id,
+ path: [...visited],
+ comparing: null,
+ message: `Gefunden! ${target} ist im Baum.`,
+ },
+ code,
+ lineHighlight: 7,
+ });
+ return steps;
+ }
+
+ if (target < current.value) {
+ steps.push({
+ state: {
+ root,
+ visited: [...visited],
+ current: current.id,
+ path: [...visited],
+ comparing: null,
+ message: `${target} < ${current.value} → gehe nach links`,
+ },
+ code,
+ lineHighlight: 11,
+ });
+ current = current.left || null;
+ } else {
+ steps.push({
+ state: {
+ root,
+ visited: [...visited],
+ current: current.id,
+ path: [...visited],
+ comparing: null,
+ message: `${target} > ${current.value} → gehe nach rechts`,
+ },
+ code,
+ lineHighlight: 13,
+ });
+ current = current.right || null;
+ }
+ }
+
+ steps.push({
+ state: {
+ root,
+ visited: [...visited],
+ current: null,
+ path: [...visited],
+ comparing: null,
+ message: `${target} nicht im Baum gefunden.`,
+ },
+ code,
+ lineHighlight: 3,
+ });
+
+ return steps;
+ }
+
+ /**
+ * Generate A* (A-Star) Algorithm steps - The industry standard for pathfinding
+ * Used by: Google Maps, Video Games (Starcraft, Age of Empires), Robotics
+ */
+ static generateAStarSteps(
+ nodes: GraphNode[],
+ edges: GraphEdge[],
+ startId: string,
+ endId: string
+ ): GraphAlgorithmStep[] {
+ const steps: GraphAlgorithmStep[] = [];
+
+ const code = `function aStar(graph, start, goal) {
+ const openSet = new PriorityQueue();
+ const gScore = new Map(); // Kosten vom Start
+ const fScore = new Map(); // g + heuristic
+ const cameFrom = new Map();
+
+ gScore.set(start, 0);
+ fScore.set(start, heuristic(start, goal));
+ openSet.add(start, fScore.get(start));
+
+ while (!openSet.isEmpty()) {
+ const current = openSet.extractMin();
+ if (current === goal) {
+ return reconstructPath(cameFrom, current);
+ }
+
+ for (const neighbor of getNeighbors(current)) {
+ const tentative = gScore.get(current) + weight(current, neighbor);
+ if (tentative < gScore.get(neighbor)) {
+ cameFrom.set(neighbor, current);
+ gScore.set(neighbor, tentative);
+ fScore.set(neighbor, tentative + heuristic(neighbor, goal));
+ openSet.add(neighbor, fScore.get(neighbor));
+ }
+ }
+ }
+ return null; // Kein Pfad gefunden
+}`;
+
+ // Build adjacency list
+ const adj = new Map();
+ nodes.forEach(n => adj.set(n.id, []));
+ edges.forEach(e => {
+ adj.get(e.from)?.push({ node: e.to, weight: e.weight || 1 });
+ adj.get(e.to)?.push({ node: e.from, weight: e.weight || 1 });
+ });
+
+ // Heuristic function (Euclidean distance)
+ const heuristic = (a: string, b: string): number => {
+ const nodeA = nodes.find(n => n.id === a);
+ const nodeB = nodes.find(n => n.id === b);
+ if (!nodeA || !nodeB) {
+ return 0;
+ }
+ return Math.sqrt(Math.pow(nodeA.x - nodeB.x, 2) + Math.pow(nodeA.y - nodeB.y, 2)) / 50;
+ };
+
+ const gScore = new Map();
+ const fScore = new Map();
+ const cameFrom = new Map();
+ const visited = new Set();
+
+ nodes.forEach(n => {
+ gScore.set(n.id, Infinity);
+ fScore.set(n.id, Infinity);
+ });
+ gScore.set(startId, 0);
+ fScore.set(startId, heuristic(startId, endId));
+
+ const openSet: { node: string; f: number }[] = [{ node: startId, f: fScore.get(startId)! }];
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: [],
+ current: null,
+ path: [],
+ queue: [startId],
+ stack: [],
+ distances: new Map(gScore),
+ message: `A* startet von ${startId}. Heuristik: Euklidische Distanz zum Ziel`,
+ },
+ code,
+ lineHighlight: 6,
+ });
+
+ while (openSet.length > 0) {
+ openSet.sort((a, b) => a.f - b.f);
+ const { node: current } = openSet.shift()!;
+
+ if (visited.has(current)) {
+ continue;
+ }
+ visited.add(current);
+
+ const h = heuristic(current, endId).toFixed(1);
+ const g = gScore.get(current)!;
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: Array.from(visited),
+ current,
+ path: [],
+ queue: openSet.map(p => p.node),
+ stack: [],
+ distances: new Map(gScore),
+ message: `Besuche ${current}: g=${g}, h=${h}, f=${(g + parseFloat(h)).toFixed(1)}`,
+ },
+ code,
+ lineHighlight: 12,
+ });
+
+ if (current === endId) {
+ const path: string[] = [];
+ let curr: string | undefined = endId;
+ while (curr) {
+ path.unshift(curr);
+ curr = cameFrom.get(curr);
+ }
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: Array.from(visited),
+ current: endId,
+ path,
+ queue: [],
+ stack: [],
+ distances: new Map(gScore),
+ message: `Ziel erreicht! Optimaler Pfad: ${path.join(' → ')} (Kosten: ${gScore.get(endId)})`,
+ },
+ code,
+ lineHighlight: 14,
+ });
+ return steps;
+ }
+
+ const neighbors = adj.get(current) || [];
+ for (const { node: neighbor, weight } of neighbors) {
+ if (visited.has(neighbor)) {
+ continue;
+ }
+
+ const tentativeG = (gScore.get(current) || 0) + weight;
+ const currentG = gScore.get(neighbor) || Infinity;
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: Array.from(visited),
+ current,
+ path: [current, neighbor],
+ queue: openSet.map(p => p.node),
+ stack: [],
+ distances: new Map(gScore),
+ message: `Prüfe ${current} → ${neighbor}: g_neu=${tentativeG} ${tentativeG < currentG ? '< ' + currentG + ' → Update!' : '>= ' + currentG}`,
+ },
+ code,
+ lineHighlight: 18,
+ });
+
+ if (tentativeG < currentG) {
+ cameFrom.set(neighbor, current);
+ gScore.set(neighbor, tentativeG);
+ const h = heuristic(neighbor, endId);
+ fScore.set(neighbor, tentativeG + h);
+
+ const existing = openSet.findIndex(p => p.node === neighbor);
+ if (existing >= 0) {
+ openSet[existing].f = tentativeG + h;
+ } else {
+ openSet.push({ node: neighbor, f: tentativeG + h });
+ }
+ }
+ }
+ }
+
+ return steps;
+ }
+
+ /**
+ * Generate Topological Sort steps (Kahn's Algorithm)
+ * Used by: npm/yarn dependency resolution, Build systems (Make, Gradle), Task scheduling
+ */
+ static generateTopologicalSortSteps(
+ nodes: GraphNode[],
+ edges: GraphEdge[]
+ ): GraphAlgorithmStep[] {
+ const steps: GraphAlgorithmStep[] = [];
+
+ const code = `function topologicalSort(graph) {
+ const inDegree = new Map();
+ const queue = [];
+ const result = [];
+
+ // Berechne In-Degree für jeden Knoten
+ for (const node of graph.nodes) {
+ inDegree.set(node, 0);
+ }
+ for (const edge of graph.edges) {
+ inDegree.set(edge.to, inDegree.get(edge.to) + 1);
+ }
+
+ // Füge Knoten mit In-Degree 0 zur Queue
+ for (const [node, degree] of inDegree) {
+ if (degree === 0) queue.push(node);
+ }
+
+ while (queue.length > 0) {
+ const node = queue.shift();
+ result.push(node);
+
+ for (const neighbor of getOutgoing(node)) {
+ inDegree.set(neighbor, inDegree.get(neighbor) - 1);
+ if (inDegree.get(neighbor) === 0) {
+ queue.push(neighbor);
+ }
+ }
+ }
+
+ return result.length === graph.nodes.length ? result : null;
+}`;
+
+ // Calculate in-degrees
+ const inDegree = new Map();
+ const outgoing = new Map();
+
+ nodes.forEach(n => {
+ inDegree.set(n.id, 0);
+ outgoing.set(n.id, []);
+ });
+
+ edges.forEach(e => {
+ inDegree.set(e.to, (inDegree.get(e.to) || 0) + 1);
+ outgoing.get(e.from)?.push(e.to);
+ });
+
+ const queue: string[] = [];
+ const result: string[] = [];
+ const visited: string[] = [];
+
+ // Initial step
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: [],
+ current: null,
+ path: [],
+ queue: [],
+ stack: [],
+ distances: new Map(Array.from(inDegree.entries()).map(([k, v]) => [k, v])),
+ message: `Berechne In-Degree für jeden Knoten (Anzahl eingehender Kanten)`,
+ },
+ code,
+ lineHighlight: 7,
+ });
+
+ // Find nodes with in-degree 0
+ nodes.forEach(n => {
+ if (inDegree.get(n.id) === 0) {
+ queue.push(n.id);
+ }
+ });
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: [],
+ current: null,
+ path: [],
+ queue: [...queue],
+ stack: [],
+ distances: new Map(Array.from(inDegree.entries()).map(([k, v]) => [k, v])),
+ message: `Knoten mit In-Degree 0: [${queue.join(', ')}] → Keine Abhängigkeiten`,
+ },
+ code,
+ lineHighlight: 15,
+ });
+
+ while (queue.length > 0) {
+ const current = queue.shift()!;
+ result.push(current);
+ visited.push(current);
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: [...visited],
+ current,
+ path: [...result],
+ queue: [...queue],
+ stack: [],
+ distances: new Map(Array.from(inDegree.entries()).map(([k, v]) => [k, v])),
+ message: `Verarbeite ${current} → Reihenfolge: [${result.join(', ')}]`,
+ },
+ code,
+ lineHighlight: 20,
+ });
+
+ const neighbors = outgoing.get(current) || [];
+ for (const neighbor of neighbors) {
+ const newDegree = (inDegree.get(neighbor) || 0) - 1;
+ inDegree.set(neighbor, newDegree);
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: [...visited],
+ current,
+ path: [current, neighbor],
+ queue: [...queue],
+ stack: [],
+ distances: new Map(Array.from(inDegree.entries()).map(([k, v]) => [k, v])),
+ message: `Reduziere In-Degree von ${neighbor}: ${newDegree + 1} → ${newDegree}`,
+ },
+ code,
+ lineHighlight: 24,
+ });
+
+ if (newDegree === 0) {
+ queue.push(neighbor);
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: [...visited],
+ current,
+ path: [],
+ queue: [...queue],
+ stack: [],
+ distances: new Map(Array.from(inDegree.entries()).map(([k, v]) => [k, v])),
+ message: `${neighbor} hat jetzt In-Degree 0 → zur Queue hinzugefügt`,
+ },
+ code,
+ lineHighlight: 26,
+ });
+ }
+ }
+ }
+
+ const success = result.length === nodes.length;
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: [...visited],
+ current: null,
+ path: [...result],
+ queue: [],
+ stack: [],
+ distances: new Map(),
+ message: success
+ ? `Topologische Sortierung: [${result.join(' → ')}]`
+ : `Zyklus erkannt! Keine topologische Sortierung möglich.`,
+ },
+ code,
+ lineHighlight: 30,
+ });
+
+ return steps;
+ }
+
+ /**
+ * Create a DAG (Directed Acyclic Graph) for topological sort
+ */
+ static createDAGGraph(): { nodes: GraphNode[]; edges: GraphEdge[] } {
+ const nodes: GraphNode[] = [
+ { id: 'A', x: 80, y: 80, label: 'A' },
+ { id: 'B', x: 200, y: 50, label: 'B' },
+ { id: 'C', x: 200, y: 150, label: 'C' },
+ { id: 'D', x: 320, y: 80, label: 'D' },
+ { id: 'E', x: 320, y: 180, label: 'E' },
+ { id: 'F', x: 440, y: 120, label: 'F' },
+ ];
+
+ // Directed edges (dependencies)
+ const edges: GraphEdge[] = [
+ { from: 'A', to: 'B' },
+ { from: 'A', to: 'C' },
+ { from: 'B', to: 'D' },
+ { from: 'C', to: 'D' },
+ { from: 'C', to: 'E' },
+ { from: 'D', to: 'F' },
+ { from: 'E', to: 'F' },
+ ];
+
+ return { nodes, edges };
+ }
+
+ /**
+ * Generate Tree Preorder Traversal steps
+ * Used by: Creating a copy of tree, Prefix expression, Serialization
+ */
+ static generatePreorderSteps(root: TreeNode): TreeAlgorithmStep[] {
+ const steps: TreeAlgorithmStep[] = [];
+ const result: number[] = [];
+
+ const code = `function preorder(node) {
+ if (node === null) return;
+
+ process(node.value); // Wurzel ZUERST
+ preorder(node.left); // Links
+ preorder(node.right); // Rechts
+}
+// Anwendung: Tree-Kopie, Prefix-Notation`;
+
+ function traverse(node: TreeNode | undefined, path: string[]): void {
+ if (!node) {
+ return;
+ }
+
+ result.push(node.value);
+ steps.push({
+ state: {
+ root,
+ visited: result.map((_, i) => `node-${i}`),
+ current: node.id,
+ path: [...path, node.id],
+ comparing: node.id,
+ message: `Besuche ${node.value} ZUERST - Ergebnis: [${result.join(', ')}]`,
+ },
+ code,
+ lineHighlight: 4,
+ });
+
+ steps.push({
+ state: {
+ root,
+ visited: result.map((_, i) => `node-${i}`),
+ current: node.id,
+ path: [...path, node.id],
+ comparing: null,
+ message: `Gehe zum linken Kind von ${node.value}`,
+ },
+ code,
+ lineHighlight: 5,
+ });
+ traverse(node.left, [...path, node.id]);
+
+ steps.push({
+ state: {
+ root,
+ visited: result.map((_, i) => `node-${i}`),
+ current: node.id,
+ path: [...path, node.id],
+ comparing: null,
+ message: `Gehe zum rechten Kind von ${node.value}`,
+ },
+ code,
+ lineHighlight: 6,
+ });
+ traverse(node.right, [...path, node.id]);
+ }
+
+ steps.push({
+ state: {
+ root,
+ visited: [],
+ current: null,
+ path: [],
+ comparing: null,
+ message: 'Starte Preorder-Traversierung (Wurzel → Links → Rechts)',
+ },
+ code,
+ lineHighlight: 1,
+ });
+
+ traverse(root, []);
+
+ steps.push({
+ state: {
+ root,
+ visited: [],
+ current: null,
+ path: [],
+ comparing: null,
+ message: `Preorder abgeschlossen: [${result.join(', ')}]`,
+ },
+ code,
+ lineHighlight: 8,
+ });
+
+ return steps;
+ }
+
+ /**
+ * Generate Tree Postorder Traversal steps
+ * Used by: Deleting tree, Postfix expression, Directory size calculation
+ */
+ static generatePostorderSteps(root: TreeNode): TreeAlgorithmStep[] {
+ const steps: TreeAlgorithmStep[] = [];
+ const result: number[] = [];
+
+ const code = `function postorder(node) {
+ if (node === null) return;
+
+ postorder(node.left); // Links
+ postorder(node.right); // Rechts
+ process(node.value); // Wurzel ZULETZT
+}
+// Anwendung: Tree löschen, Postfix, du -sh`;
+
+ function traverse(node: TreeNode | undefined, path: string[]): void {
+ if (!node) {
+ return;
+ }
+
+ steps.push({
+ state: {
+ root,
+ visited: result.map((_, i) => `node-${i}`),
+ current: node.id,
+ path: [...path, node.id],
+ comparing: null,
+ message: `Bei ${node.value} - gehe erst zum linken Kind`,
+ },
+ code,
+ lineHighlight: 4,
+ });
+ traverse(node.left, [...path, node.id]);
+
+ steps.push({
+ state: {
+ root,
+ visited: result.map((_, i) => `node-${i}`),
+ current: node.id,
+ path: [...path, node.id],
+ comparing: null,
+ message: `Bei ${node.value} - gehe zum rechten Kind`,
+ },
+ code,
+ lineHighlight: 5,
+ });
+ traverse(node.right, [...path, node.id]);
+
+ result.push(node.value);
+ steps.push({
+ state: {
+ root,
+ visited: result.map((_, i) => `node-${i}`),
+ current: node.id,
+ path: [...path, node.id],
+ comparing: node.id,
+ message: `Besuche ${node.value} ZULETZT - Ergebnis: [${result.join(', ')}]`,
+ },
+ code,
+ lineHighlight: 6,
+ });
+ }
+
+ steps.push({
+ state: {
+ root,
+ visited: [],
+ current: null,
+ path: [],
+ comparing: null,
+ message: 'Starte Postorder-Traversierung (Links → Rechts → Wurzel)',
+ },
+ code,
+ lineHighlight: 1,
+ });
+
+ traverse(root, []);
+
+ steps.push({
+ state: {
+ root,
+ visited: [],
+ current: null,
+ path: [],
+ comparing: null,
+ message: `Postorder abgeschlossen: [${result.join(', ')}]`,
+ },
+ code,
+ lineHighlight: 8,
+ });
+
+ return steps;
+ }
+
+ /**
+ * Generate Bellman-Ford Algorithm steps
+ * Used by: Network routing (RIP protocol), Currency arbitrage detection, Negative weight handling
+ */
+ static generateBellmanFordSteps(
+ nodes: GraphNode[],
+ edges: GraphEdge[],
+ startId: string
+ ): GraphAlgorithmStep[] {
+ const steps: GraphAlgorithmStep[] = [];
+
+ const code = `function bellmanFord(graph, start) {
+ const dist = new Map();
+ const prev = new Map();
+
+ // Initialisiere alle Distanzen mit ∞
+ for (const node of graph.nodes) {
+ dist.set(node, Infinity);
+ }
+ dist.set(start, 0);
+
+ // Relaxiere alle Kanten |V|-1 mal
+ for (let i = 0; i < nodes.length - 1; i++) {
+ for (const edge of graph.edges) {
+ if (dist.get(edge.from) + edge.weight < dist.get(edge.to)) {
+ dist.set(edge.to, dist.get(edge.from) + edge.weight);
+ prev.set(edge.to, edge.from);
+ }
+ }
+ }
+
+ // Prüfe auf negative Zyklen
+ for (const edge of graph.edges) {
+ if (dist.get(edge.from) + edge.weight < dist.get(edge.to)) {
+ return "Negativer Zyklus erkannt!";
+ }
+ }
+ return { dist, prev };
+}`;
+
+ const distances = new Map();
+ const previous = new Map();
+
+ nodes.forEach(n => distances.set(n.id, Infinity));
+ distances.set(startId, 0);
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: [],
+ current: null,
+ path: [],
+ queue: [],
+ stack: [],
+ distances: new Map(distances),
+ message: `Bellman-Ford startet von ${startId}. Alle Distanzen = ∞, Start = 0`,
+ },
+ code,
+ lineHighlight: 6,
+ });
+
+ // Relax all edges V-1 times
+ for (let i = 0; i < nodes.length - 1; i++) {
+ let updated = false;
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: [],
+ current: null,
+ path: [],
+ queue: [],
+ stack: [],
+ distances: new Map(distances),
+ message: `Iteration ${i + 1}/${nodes.length - 1}: Relaxiere alle Kanten`,
+ },
+ code,
+ lineHighlight: 12,
+ });
+
+ for (const edge of edges) {
+ const fromDist = distances.get(edge.from) || Infinity;
+ const toDist = distances.get(edge.to) || Infinity;
+ const weight = edge.weight || 1;
+
+ if (fromDist !== Infinity && fromDist + weight < toDist) {
+ distances.set(edge.to, fromDist + weight);
+ previous.set(edge.to, edge.from);
+ updated = true;
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: [edge.from],
+ current: edge.to,
+ path: [edge.from, edge.to],
+ queue: [],
+ stack: [],
+ distances: new Map(distances),
+ message: `Kante ${edge.from}→${edge.to}: ${fromDist} + ${weight} = ${fromDist + weight} < ${toDist} → Update!`,
+ },
+ code,
+ lineHighlight: 15,
+ });
+ }
+ }
+
+ // Also check reverse edges (undirected graph)
+ for (const edge of edges) {
+ const fromDist = distances.get(edge.to) || Infinity;
+ const toDist = distances.get(edge.from) || Infinity;
+ const weight = edge.weight || 1;
+
+ if (fromDist !== Infinity && fromDist + weight < toDist) {
+ distances.set(edge.from, fromDist + weight);
+ previous.set(edge.from, edge.to);
+ updated = true;
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: [edge.to],
+ current: edge.from,
+ path: [edge.to, edge.from],
+ queue: [],
+ stack: [],
+ distances: new Map(distances),
+ message: `Kante ${edge.to}→${edge.from}: ${fromDist} + ${weight} = ${fromDist + weight} < ${toDist} → Update!`,
+ },
+ code,
+ lineHighlight: 15,
+ });
+ }
+ }
+
+ if (!updated) {
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: [],
+ current: null,
+ path: [],
+ queue: [],
+ stack: [],
+ distances: new Map(distances),
+ message: `Keine Änderungen in Iteration ${i + 1} → Früher Abbruch möglich`,
+ },
+ code,
+ lineHighlight: 18,
+ });
+ break;
+ }
+ }
+
+ steps.push({
+ state: {
+ nodes: [...nodes],
+ edges: [...edges],
+ visited: Array.from(distances.keys()),
+ current: null,
+ path: [],
+ queue: [],
+ stack: [],
+ distances: new Map(distances),
+ message: `Bellman-Ford abgeschlossen! Kürzeste Distanzen von ${startId}: ${Array.from(
+ distances.entries()
+ )
+ .map(([k, v]) => `${k}=${v}`)
+ .join(', ')}`,
+ },
+ code,
+ lineHighlight: 27,
+ });
+
+ return steps;
+ }
+
+ /**
+ * Generate Tree Inorder Traversal steps
+ */
+ static generateInorderSteps(root: TreeNode): TreeAlgorithmStep[] {
+ const steps: TreeAlgorithmStep[] = [];
+ const result: number[] = [];
+
+ const code = `function inorder(node) {
+ if (node === null) return;
+
+ inorder(node.left); // Links
+ process(node.value); // Wurzel
+ inorder(node.right); // Rechts
+}
+// Ergebnis: sortierte Reihenfolge!`;
+
+ function traverse(node: TreeNode | undefined, path: string[]): void {
+ if (!node) {
+ return;
+ }
+
+ steps.push({
+ state: {
+ root,
+ visited: [...result.map((_, i) => path[i])].filter(Boolean),
+ current: node.id,
+ path: [...path, node.id],
+ comparing: null,
+ message: `Gehe zu linkem Kind von ${node.value}`,
+ },
+ code,
+ lineHighlight: 4,
+ });
+
+ traverse(node.left, [...path, node.id]);
+
+ result.push(node.value);
+ steps.push({
+ state: {
+ root,
+ visited: result.map((_, i) => `node-${i}`),
+ current: node.id,
+ path: [...path, node.id],
+ comparing: node.id,
+ message: `Besuche ${node.value} - Ergebnis: [${result.join(', ')}]`,
+ },
+ code,
+ lineHighlight: 5,
+ });
+
+ traverse(node.right, [...path, node.id]);
+ }
+
+ steps.push({
+ state: {
+ root,
+ visited: [],
+ current: null,
+ path: [],
+ comparing: null,
+ message: 'Starte Inorder-Traversierung (Links → Wurzel → Rechts)',
+ },
+ code,
+ lineHighlight: 1,
+ });
+
+ traverse(root, []);
+
+ steps.push({
+ state: {
+ root,
+ visited: [],
+ current: null,
+ path: [],
+ comparing: null,
+ message: `Inorder abgeschlossen: [${result.join(', ')}] - sortiert!`,
+ },
+ code,
+ lineHighlight: 8,
+ });
+
+ return steps;
+ }
+
+ /**
+ * Set steps
+ */
+ setGraphSteps(steps: GraphAlgorithmStep[]): void {
+ this.steps = steps;
+ this.currentStep = 0;
+ this.mode = 'graph';
+ this.render();
+ }
+
+ setTreeSteps(steps: TreeAlgorithmStep[]): void {
+ this.steps = steps;
+ this.currentStep = 0;
+ this.mode = 'tree';
+ this.render();
+ }
+
+ /**
+ * Render graph visualization
+ */
+ private renderGraph(state: GraphVisualizationState): string {
+ const width = 500;
+ const height = 400;
+
+ return `
+
+
+
+ ${state.queue.length > 0 ? `
Queue: [${state.queue.join(', ')}]
` : ''}
+ ${state.stack.length > 0 ? `
Stack: [${state.stack.join(', ')}]
` : ''}
+ ${
+ state.distances.size > 0
+ ? `
+
+ Distanzen:
+ ${Array.from(state.distances.entries())
+ .map(([k, v]) => `${k}:${v === Infinity ? '∞' : v}`)
+ .join(' | ')}
+
+ `
+ : ''
+ }
+
+ `;
+ }
+
+ /**
+ * Calculate tree node positions
+ */
+ private calculateTreePositions(
+ node: TreeNode | undefined,
+ x: number,
+ y: number,
+ level: number,
+ positions: Map
+ ): void {
+ if (!node) {
+ return;
+ }
+ // Reduzierter Spread für bessere Darstellung
+ const spread = 120 / Math.pow(2, level);
+ positions.set(node.id, { x, y });
+
+ this.calculateTreePositions(node.left, x - spread, y + 60, level + 1, positions);
+ this.calculateTreePositions(node.right, x + spread, y + 60, level + 1, positions);
+ }
+
+ /**
+ * Render tree visualization
+ */
+ private renderTree(state: TreeVisualizationState): string {
+ if (!state.root) {
+ return 'Kein Baum vorhanden
';
+ }
+
+ const positions = new Map();
+ // Starte in der Mitte mit mehr Platz für Knoten
+ this.calculateTreePositions(state.root, 220, 35, 0, positions);
+
+ const renderNode = (node: TreeNode | undefined): string => {
+ if (!node) {
+ return '';
+ }
+ const pos = positions.get(node.id);
+ if (!pos) {
+ return '';
+ }
+
+ let nodeClass = 'tree-node';
+ if (state.current === node.id) {
+ nodeClass += ' current';
+ }
+ if (state.comparing === node.id) {
+ nodeClass += ' comparing';
+ }
+ if (state.path.includes(node.id)) {
+ nodeClass += ' in-path';
+ }
+
+ let edges = '';
+ if (node.left) {
+ const leftPos = positions.get(node.left.id);
+ if (leftPos) {
+ edges += ``;
+ }
+ }
+ if (node.right) {
+ const rightPos = positions.get(node.right.id);
+ if (rightPos) {
+ edges += ``;
+ }
+ }
+
+ return `
+ ${edges}
+
+
+ ${node.value}
+
+ ${renderNode(node.left)}
+ ${renderNode(node.right)}
+ `;
+ };
+
+ return `
+
+ `;
+ }
+
+ /**
+ * Render visualization
+ */
+ render(): void {
+ const container = document.getElementById(this.containerId);
+ if (!container) {
+ return;
+ }
+
+ const step = this.steps[this.currentStep];
+ if (!step) {
+ return;
+ }
+
+ const visualization =
+ this.mode === 'graph'
+ ? this.renderGraph(step.state as GraphVisualizationState)
+ : this.renderTree(step.state as TreeVisualizationState);
+
+ container.innerHTML = `
+
+
+
+ ${visualization}
+
+
+
${step.state.message}
+
+
+
+
+
+
+
+
+
+
+
+ ${this.currentStep + 1} / ${this.steps.length}
+
+
+
+
+
+ ${this.speed}ms
+
+
+
+
+
+
+
${this.highlightCode(step.code, step.lineHighlight)}
+
+
+
+
+
+ `;
+
+ this.attachEventListeners();
+ }
+
+ /**
+ * Highlight code line
+ */
+ private highlightCode(code: string, line: number): string {
+ const lines = code.split('\n');
+ return lines
+ .map((l, i) => {
+ const lineNum = i + 1;
+ const isHighlighted = lineNum === line;
+ const escapedLine = l.replace(//g, '>');
+ return `${escapedLine}`;
+ })
+ .join('\n');
+ }
+
+ /**
+ * Attach event listeners
+ */
+ private attachEventListeners(): void {
+ document.getElementById('graph-first')?.addEventListener('click', () => this.goToStep(0));
+ document.getElementById('graph-prev')?.addEventListener('click', () => this.prevStep());
+ document.getElementById('graph-play')?.addEventListener('click', () => this.togglePlay());
+ document.getElementById('graph-next')?.addEventListener('click', () => this.nextStep());
+ document
+ .getElementById('graph-last')
+ ?.addEventListener('click', () => this.goToStep(this.steps.length - 1));
+
+ document.getElementById('graph-slider')?.addEventListener('input', e => {
+ const value = parseInt((e.target as HTMLInputElement).value);
+ this.goToStep(value);
+ });
+
+ document.getElementById('graph-speed')?.addEventListener('input', e => {
+ const value = parseInt((e.target as HTMLInputElement).value);
+ this.speed = 2200 - value;
+ this.render();
+ });
+ }
+
+ goToStep(step: number): void {
+ this.currentStep = Math.max(0, Math.min(step, this.steps.length - 1));
+ this.render();
+ }
+
+ nextStep(): void {
+ if (this.currentStep < this.steps.length - 1) {
+ this.currentStep++;
+ this.render();
+ } else {
+ this.stop();
+ }
+ }
+
+ prevStep(): void {
+ if (this.currentStep > 0) {
+ this.currentStep--;
+ this.render();
+ }
+ }
+
+ togglePlay(): void {
+ if (this.isPlaying) {
+ this.stop();
+ } else {
+ this.play();
+ }
+ }
+
+ play(): void {
+ this.isPlaying = true;
+ this.render();
+ this.animate();
+ }
+
+ stop(): void {
+ this.isPlaying = false;
+ if (this.animationFrame) {
+ cancelAnimationFrame(this.animationFrame);
+ this.animationFrame = null;
+ }
+ this.render();
+ }
+
+ private animate(): void {
+ if (!this.isPlaying) {
+ return;
+ }
+
+ const now = performance.now();
+ const step = () => {
+ if (!this.isPlaying) {
+ return;
+ }
+ if (performance.now() - now >= this.speed) {
+ this.nextStep();
+ if (this.isPlaying && this.currentStep < this.steps.length - 1) {
+ this.animate();
+ }
+ } else {
+ this.animationFrame = requestAnimationFrame(step);
+ }
+ };
+ this.animationFrame = requestAnimationFrame(step);
+ }
+
+ reset(): void {
+ this.stop();
+ this.currentStep = 0;
+ this.render();
+ }
+
+ destroy(): void {
+ this.stop();
+ const container = document.getElementById(this.containerId);
+ if (container) {
+ container.innerHTML = '';
+ }
+ }
+}
diff --git a/src/core/EventBus.ts b/src/core/EventBus.ts
index 25cb74f..f2327ef 100644
--- a/src/core/EventBus.ts
+++ b/src/core/EventBus.ts
@@ -16,6 +16,7 @@ export interface AppEvents {
'typing:start': { lessonId: string; exerciseId: string };
'typing:keystroke': { key: string; isCorrect: boolean; position: number };
'typing:complete': { wpm: number; accuracy: number; time: number };
+ 'typing:backspace': { position: number };
'typing:pause': void;
'typing:resume': void;
'typing:reset': void;
diff --git a/src/core/i18n.ts b/src/core/i18n.ts
index 5156ae1..5bdec07 100644
--- a/src/core/i18n.ts
+++ b/src/core/i18n.ts
@@ -124,10 +124,201 @@ const en: Translations = {
'lessons.completed': '{completed} of {total} lessons completed',
'lessons.exerciseOf': 'Exercise {current} of {total}',
'lessons.exercises': '{count} exercises',
+ 'lessons.exercisesCount': '{count} exercises',
'lessons.pressToStart': 'Press a key to start.',
'lessons.goal': 'Goal: {wpm} WPM · {accuracy}% Accuracy',
'lessons.passed': 'Lesson passed! {wpm} WPM · {accuracy}% · +{xp} XP',
'lessons.ended': 'Lesson ended. {wpm} WPM · {accuracy}%',
+ 'lessons.completedCount': '{count} completed',
+
+ // Lesson Categories
+ 'lessons.category.beginner': 'Beginner',
+ 'lessons.category.beginner.desc': 'Absolute basics - home row only',
+ 'lessons.category.basics': 'Basics',
+ 'lessons.category.basics.desc': 'All letters and first sentences',
+ 'lessons.category.intermediate': 'Intermediate',
+ 'lessons.category.intermediate.desc': 'Numbers and special characters',
+ 'lessons.category.words': 'Words & Sentences',
+ 'lessons.category.words.desc': 'Vocabulary and text exercises',
+ 'lessons.category.advanced': 'Advanced',
+ 'lessons.category.advanced.desc': 'Complex texts and all characters',
+ 'lessons.category.expert': 'Expert',
+ 'lessons.category.expert.desc': 'Maximum challenge',
+ 'lessons.category.programming': 'Programming',
+ 'lessons.category.programming.desc': 'Code snippets in various languages',
+ 'lessons.category.shortcuts': 'IDE Shortcuts',
+ 'lessons.category.shortcuts.desc': 'Keyboard shortcuts for VS Code, IntelliJ, Vim',
+
+ // Difficulty Labels
+ 'lessons.difficulty.beginner': 'Beginner',
+ 'lessons.difficulty.easy': 'Easy',
+ 'lessons.difficulty.medium': 'Medium',
+ 'lessons.difficulty.hard': 'Hard',
+ 'lessons.difficulty.expert': 'Expert',
+
+ // Beginner Lessons
+ 'lessons.beginner-01.title': 'First Steps: F and J',
+ 'lessons.beginner-01.desc': 'Learn the most important keys: F and J have bumps for orientation',
+ 'lessons.beginner-02.title': 'Left Hand: A S D F',
+ 'lessons.beginner-02.desc': 'The left hand on the home position',
+ 'lessons.beginner-03.title': 'Right Hand: J K L ;',
+ 'lessons.beginner-03.desc': 'The right hand on the home position',
+ 'lessons.beginner-04.title': 'Complete Home Row',
+ 'lessons.beginner-04.desc': 'Both hands together on the home row',
+ 'lessons.beginner-05.title': 'First Simple Words',
+ 'lessons.beginner-05.desc': 'Words using only home row letters',
+ 'lessons.beginner-06.title': 'Adding G and H',
+ 'lessons.beginner-06.desc': 'Index fingers move to the center',
+ 'lessons.beginner-07.title': 'The Space Bar',
+ 'lessons.beginner-07.desc': 'Learn to use the space bar with your thumb',
+ 'lessons.beginner-08.title': 'Very Short Sentences',
+ 'lessons.beginner-08.desc': 'First complete sentences with home row',
+
+ // Basic Lessons
+ 'lessons.basics-01.title': 'Top Row: E and I',
+ 'lessons.basics-01.desc': 'The most important vowels on the top row',
+ 'lessons.basics-02.title': 'Top Row: R and U',
+ 'lessons.basics-02.desc': 'Index fingers on the top row',
+ 'lessons.basics-03.title': 'Complete Top Row',
+ 'lessons.basics-03.desc': 'All letters of the top row: QWERTYUIOP',
+ 'lessons.basics-04.title': 'Bottom Row: N and M',
+ 'lessons.basics-04.desc': 'The most important letters of the bottom row',
+ 'lessons.basics-05.title': 'Complete Bottom Row',
+ 'lessons.basics-05.desc': 'All letters of the bottom row: ZXCVBNM',
+ 'lessons.basics-06.title': 'All Letters Together',
+ 'lessons.basics-06.desc': 'Exercises with all three rows',
+ 'lessons.basics-07.title': 'Common Words Level 1',
+ 'lessons.basics-07.desc': 'The 50 most common words',
+ 'lessons.basics-08.title': 'Learning Capital Letters',
+ 'lessons.basics-08.desc': 'The Shift key for capitalization',
+
+ // Intermediate Lessons
+ 'lessons.inter-01.title': 'Numbers 1-5',
+ 'lessons.inter-01.desc': 'Numbers for the left hand',
+ 'lessons.inter-02.title': 'Numbers 6-0',
+ 'lessons.inter-02.desc': 'Numbers for the right hand',
+ 'lessons.inter-03.title': 'All Numbers Together',
+ 'lessons.inter-03.desc': 'Exercises with all numbers 0-9',
+ 'lessons.inter-04.title': 'Period and Comma',
+ 'lessons.inter-04.desc': 'The most important punctuation marks',
+ 'lessons.inter-05.title': 'Question and Exclamation Marks',
+ 'lessons.inter-05.desc': 'Asking questions and exclaiming',
+ 'lessons.inter-06.title': 'Hyphen and Apostrophe',
+ 'lessons.inter-06.desc': 'Compound words and contractions',
+ 'lessons.inter-07.title': 'Brackets',
+ 'lessons.inter-07.desc': 'Round and square brackets',
+ 'lessons.inter-08.title': 'Quotation Marks',
+ 'lessons.inter-08.desc': 'Quotes and direct speech',
+
+ // Word Lessons
+ 'lessons.words-01.title': 'The 100 Most Common Words',
+ 'lessons.words-01.desc': 'The most frequently used words',
+ 'lessons.words-02.title': 'Everyday Verbs',
+ 'lessons.words-02.desc': 'Commonly used verbs',
+ 'lessons.words-03.title': 'Adjectives',
+ 'lessons.words-03.desc': 'Descriptive words',
+ 'lessons.words-04.title': 'Short Sentences',
+ 'lessons.words-04.desc': 'Simple sentences',
+ 'lessons.words-05.title': 'Medium Sentences',
+ 'lessons.words-05.desc': 'Sentences with more details',
+ 'lessons.words-06.title': 'Long Sentences',
+ 'lessons.words-06.desc': 'More complex sentences',
+ 'lessons.words-07.title': 'Practice Paragraphs',
+ 'lessons.words-07.desc': 'Coherent text passages',
+ 'lessons.words-08.title': 'Topic: Computer',
+ 'lessons.words-08.desc': 'Technical vocabulary from the computer world',
+ 'lessons.words-09.title': 'Topic: Office',
+ 'lessons.words-09.desc': 'Words from office life',
+ 'lessons.words-10.title': 'Topic: Travel',
+ 'lessons.words-10.desc': 'Vocabulary about traveling',
+
+ // Advanced Lessons
+ 'lessons.adv-01.title': 'All Special Characters Overview',
+ 'lessons.adv-01.desc': 'Systematic training of all important special characters',
+ 'lessons.adv-02.title': 'Mathematical Symbols',
+ 'lessons.adv-02.desc': 'For technical and scientific texts',
+ 'lessons.adv-03.title': 'Literary Classics 1',
+ 'lessons.adv-03.desc': 'Quotes from classic literature',
+ 'lessons.adv-04.title': 'Business Letters',
+ 'lessons.adv-04.desc': 'Formal correspondence',
+ 'lessons.adv-05.title': 'Scientific Texts',
+ 'lessons.adv-05.desc': 'Academic writing',
+ 'lessons.adv-06.title': 'Speed Drill 1',
+ 'lessons.adv-06.desc': 'High-frequency words for maximum speed',
+ 'lessons.adv-07.title': 'Speed Drill 2',
+ 'lessons.adv-07.desc': 'Longer words at high speed',
+
+ // Expert Lessons
+ 'lessons.exp-01.title': 'Philosophical Texts',
+ 'lessons.exp-01.desc': 'Challenging philosophical passages',
+ 'lessons.exp-02.title': 'Legal Texts',
+ 'lessons.exp-02.desc': 'Legal formulations',
+ 'lessons.exp-03.title': 'Medical Texts',
+ 'lessons.exp-03.desc': 'Medical terminology',
+ 'lessons.exp-04.title': 'Technical Documentation',
+ 'lessons.exp-04.desc': 'Complex technical texts',
+ 'lessons.exp-05.title': 'Symbol Marathon',
+ 'lessons.exp-05.desc': 'All special characters in complex combinations',
+ 'lessons.exp-06.title': 'Speed Challenge',
+ 'lessons.exp-06.desc': 'For absolute pros - 60+ WPM goal',
+
+ // Programming Lessons
+ 'lessons.prog-js-01.title': 'JavaScript Beginner',
+ 'lessons.prog-js-01.desc': 'First steps with JavaScript',
+ 'lessons.prog-js-02.title': 'JavaScript Functions',
+ 'lessons.prog-js-02.desc': 'Functions in JavaScript',
+ 'lessons.prog-js-03.title': 'JavaScript Intermediate',
+ 'lessons.prog-js-03.desc': 'Advanced JavaScript concepts',
+ 'lessons.prog-js-04.title': 'JavaScript Advanced',
+ 'lessons.prog-js-04.desc': 'Async/Await and Promises',
+ 'lessons.prog-ts-01.title': 'TypeScript Basics',
+ 'lessons.prog-ts-01.desc': 'Learning TypeScript typing',
+ 'lessons.prog-ts-02.title': 'TypeScript Interfaces',
+ 'lessons.prog-ts-02.desc': 'Interfaces and Types',
+ 'lessons.prog-ts-03.title': 'TypeScript Advanced',
+ 'lessons.prog-ts-03.desc': 'Generics and advanced types',
+ 'lessons.prog-py-01.title': 'Python Beginner',
+ 'lessons.prog-py-01.desc': 'Python basics',
+ 'lessons.prog-py-02.title': 'Python Functions',
+ 'lessons.prog-py-02.desc': 'Functions in Python',
+ 'lessons.prog-py-03.title': 'Python Intermediate',
+ 'lessons.prog-py-03.desc': 'Advanced Python',
+ 'lessons.prog-py-04.title': 'Python OOP',
+ 'lessons.prog-py-04.desc': 'Object-oriented Python',
+ 'lessons.prog-py-05.title': 'Python Async',
+ 'lessons.prog-py-05.desc': 'Asynchronous Python',
+ 'lessons.prog-java-01.title': 'Java Beginner',
+ 'lessons.prog-java-01.desc': 'Java basics',
+ 'lessons.prog-java-02.title': 'Java OOP',
+ 'lessons.prog-java-02.desc': 'Object orientation in Java',
+ 'lessons.prog-java-03.title': 'Java Collections & Streams',
+ 'lessons.prog-java-03.desc': 'Modern Java features',
+ 'lessons.prog-html-01.title': 'HTML Basics',
+ 'lessons.prog-html-01.desc': 'HTML fundamentals',
+ 'lessons.prog-css-01.title': 'CSS Basics',
+ 'lessons.prog-css-01.desc': 'CSS fundamentals',
+ 'lessons.prog-react-01.title': 'React Basics',
+ 'lessons.prog-react-01.desc': 'React component syntax',
+ 'lessons.prog-angular-01.title': 'Angular Basics',
+ 'lessons.prog-angular-01.desc': 'Angular component syntax',
+ 'lessons.prog-sql-01.title': 'SQL Basics',
+ 'lessons.prog-sql-01.desc': 'SQL fundamentals',
+ 'lessons.prog-sql-02.title': 'SQL Advanced',
+ 'lessons.prog-sql-02.desc': 'Advanced SQL queries',
+ 'lessons.prog-bash-01.title': 'Bash/Shell Basics',
+ 'lessons.prog-bash-01.desc': 'Shell scripting basics',
+ 'lessons.prog-git-01.title': 'Git Commands',
+ 'lessons.prog-git-01.desc': 'The most important Git commands',
+
+ // Shortcut Lessons
+ 'lessons.shortcuts-vscode-01.title': 'VS Code Essentials',
+ 'lessons.shortcuts-vscode-01.desc': 'The most important VS Code shortcuts',
+ 'lessons.shortcuts-vscode-02.title': 'VS Code Advanced',
+ 'lessons.shortcuts-vscode-02.desc': 'Advanced VS Code shortcuts',
+ 'lessons.shortcuts-intellij-01.title': 'IntelliJ Essentials',
+ 'lessons.shortcuts-intellij-01.desc': 'The most important IntelliJ shortcuts',
+ 'lessons.shortcuts-vim-01.title': 'Vim Basics',
+ 'lessons.shortcuts-vim-01.desc': 'Basic Vim commands',
// Common
'common.wpm': 'WPM',
@@ -251,16 +442,48 @@ const en: Translations = {
'regex.playground': 'Playground',
// Dev Tools - Algorithms
- 'algo.title': 'Algorithm Training',
+ 'algo.title': 'Algorithm & Framework Training',
+ 'algo.subtitle': 'Improve your programming skills by typing real code',
'algo.browse': 'Browse',
'algo.practice': 'Practice',
'algo.challenges': 'Challenges',
+ 'algo.algorithmsData': 'Algorithms & Data Structures',
+ 'algo.reactTs': 'React & TypeScript',
+ 'algo.exercises': 'Exercises',
+ 'algo.all': 'All',
+ 'algo.beginner': 'Beginner',
+ 'algo.intermediate': 'Intermediate',
+ 'algo.advanced': 'Advanced',
+ 'algo.noExercises': 'No exercises found in this category.',
+ 'algo.noExercisesHint': 'Choose another category or remove the difficulty filter.',
+ 'algo.back': '← Back',
+ 'algo.next': 'Next →',
+ 'algo.start': 'Start',
+ 'algo.reset': 'Reset',
+ 'algo.wpm': 'WPM',
+ 'algo.accuracy': 'Accuracy',
+ 'algo.characters': 'Characters',
+ 'algo.errors': 'Errors',
+ 'algo.exerciseComplete': 'Exercise complete! {wpm} WPM, {accuracy}% accuracy',
+ 'algo.allComplete': 'All exercises complete! {wpm} WPM, {accuracy}% accuracy',
// Dev Tools - SQL
'sql.title': 'SQL Training',
'sql.commands': 'Commands',
'sql.training': 'Training',
'sql.playground': 'Playground',
+ 'sql.subtitle': 'Learn SQL by typing real queries',
+ 'sql.exercises': 'Exercises',
+ 'sql.selectExercise': 'Select an exercise',
+ 'sql.selectExerciseDesc': 'Click on a SQL exercise on the left to start',
+ 'sql.explanation': 'Explanation',
+ 'sql.skipBtn': 'Skip →',
+ 'sql.nextBtn': 'Next Exercise →',
+ 'sql.queryCopied': 'Query copied!',
+ 'sql.completed': 'Done! +{xp} XP',
+ 'sql.difficulty.beginner': 'Beginner',
+ 'sql.difficulty.intermediate': 'Intermediate',
+ 'sql.difficulty.advanced': 'Expert',
// Common Dev Tools
'devtools.difficulty': 'Difficulty',
@@ -434,10 +657,202 @@ const de: Translations = {
'lessons.completed': '{completed} von {total} Lektionen abgeschlossen',
'lessons.exerciseOf': 'Übung {current} von {total}',
'lessons.exercises': '{count} Übungen',
+ 'lessons.exercisesCount': '{count} Übungen',
'lessons.pressToStart': 'Drücke eine Taste, um zu beginnen.',
'lessons.goal': 'Ziel: {wpm} WPM · {accuracy}% Genauigkeit',
'lessons.passed': 'Lektion bestanden! {wpm} WPM · {accuracy}% · +{xp} XP',
'lessons.ended': 'Lektion beendet. {wpm} WPM · {accuracy}%',
+ 'lessons.completedCount': '{count} abgeschlossen',
+
+ // Lesson Categories
+ 'lessons.category.beginner': 'Anfänger',
+ 'lessons.category.beginner.desc': 'Absolute Grundlagen - nur Grundreihe',
+ 'lessons.category.basics': 'Grundlagen',
+ 'lessons.category.basics.desc': 'Alle Buchstaben und erste Sätze',
+ 'lessons.category.intermediate': 'Fortgeschritten',
+ 'lessons.category.intermediate.desc': 'Zahlen und Sonderzeichen',
+ 'lessons.category.words': 'Wörter & Sätze',
+ 'lessons.category.words.desc': 'Wortschatz und Textübungen',
+ 'lessons.category.advanced': 'Fortgeschritten+',
+ 'lessons.category.advanced.desc': 'Komplexe Texte und alle Zeichen',
+ 'lessons.category.expert': 'Experte',
+ 'lessons.category.expert.desc': 'Maximale Herausforderung',
+ 'lessons.category.programming': 'Programmierung',
+ 'lessons.category.programming.desc': 'Code-Snippets in verschiedenen Sprachen',
+ 'lessons.category.shortcuts': 'IDE Shortcuts',
+ 'lessons.category.shortcuts.desc': 'Tastenkürzel für VS Code, IntelliJ, Vim',
+
+ // Difficulty Labels
+ 'lessons.difficulty.beginner': 'Anfänger',
+ 'lessons.difficulty.easy': 'Leicht',
+ 'lessons.difficulty.medium': 'Mittel',
+ 'lessons.difficulty.hard': 'Schwer',
+ 'lessons.difficulty.expert': 'Experte',
+
+ // Beginner Lessons
+ 'lessons.beginner-01.title': 'Erste Schritte: F und J',
+ 'lessons.beginner-01.desc':
+ 'Lerne die wichtigsten Tasten: F und J haben Erhebungen zur Orientierung',
+ 'lessons.beginner-02.title': 'Linke Hand: A S D F',
+ 'lessons.beginner-02.desc': 'Die linke Hand auf der Grundposition',
+ 'lessons.beginner-03.title': 'Rechte Hand: J K L Ö',
+ 'lessons.beginner-03.desc': 'Die rechte Hand auf der Grundposition',
+ 'lessons.beginner-04.title': 'Grundreihe komplett',
+ 'lessons.beginner-04.desc': 'Beide Hände zusammen auf der Grundreihe',
+ 'lessons.beginner-05.title': 'Erste einfache Wörter',
+ 'lessons.beginner-05.desc': 'Wörter nur mit Grundreihe-Buchstaben',
+ 'lessons.beginner-06.title': 'G und H hinzufügen',
+ 'lessons.beginner-06.desc': 'Die Zeigefinger bewegen sich zur Mitte',
+ 'lessons.beginner-07.title': 'Die Leertaste',
+ 'lessons.beginner-07.desc': 'Lerne die Leertaste mit dem Daumen zu bedienen',
+ 'lessons.beginner-08.title': 'Ganz kurze Sätze',
+ 'lessons.beginner-08.desc': 'Erste vollständige Sätze mit Grundreihe',
+
+ // Basic Lessons
+ 'lessons.basics-01.title': 'Obere Reihe: E und I',
+ 'lessons.basics-01.desc': 'Die wichtigsten Vokale auf der oberen Reihe',
+ 'lessons.basics-02.title': 'Obere Reihe: R und U',
+ 'lessons.basics-02.desc': 'Zeigefinger auf der oberen Reihe',
+ 'lessons.basics-03.title': 'Obere Reihe komplett',
+ 'lessons.basics-03.desc': 'Alle Buchstaben der oberen Reihe: QWERTZUIOP',
+ 'lessons.basics-04.title': 'Untere Reihe: N und M',
+ 'lessons.basics-04.desc': 'Die wichtigsten Buchstaben der unteren Reihe',
+ 'lessons.basics-05.title': 'Untere Reihe komplett',
+ 'lessons.basics-05.desc': 'Alle Buchstaben der unteren Reihe: YXCVBNM',
+ 'lessons.basics-06.title': 'Alle Buchstaben zusammen',
+ 'lessons.basics-06.desc': 'Übungen mit allen drei Reihen',
+ 'lessons.basics-07.title': 'Häufige Wörter Stufe 1',
+ 'lessons.basics-07.desc': 'Die 50 häufigsten deutschen Wörter',
+ 'lessons.basics-08.title': 'Großbuchstaben lernen',
+ 'lessons.basics-08.desc': 'Die Shift-Taste für Großschreibung',
+
+ // Intermediate Lessons
+ 'lessons.inter-01.title': 'Zahlen 1-5',
+ 'lessons.inter-01.desc': 'Die Zahlen der linken Hand',
+ 'lessons.inter-02.title': 'Zahlen 6-0',
+ 'lessons.inter-02.desc': 'Die Zahlen der rechten Hand',
+ 'lessons.inter-03.title': 'Alle Zahlen zusammen',
+ 'lessons.inter-03.desc': 'Übungen mit allen Zahlen 0-9',
+ 'lessons.inter-04.title': 'Punkt und Komma',
+ 'lessons.inter-04.desc': 'Die wichtigsten Satzzeichen',
+ 'lessons.inter-05.title': 'Fragezeichen und Ausrufezeichen',
+ 'lessons.inter-05.desc': 'Fragen stellen und ausrufen',
+ 'lessons.inter-06.title': 'Bindestrich und Apostroph',
+ 'lessons.inter-06.desc': 'Zusammengesetzte Wörter und Auslassungen',
+ 'lessons.inter-07.title': 'Klammern',
+ 'lessons.inter-07.desc': 'Runde und eckige Klammern',
+ 'lessons.inter-08.title': 'Anführungszeichen',
+ 'lessons.inter-08.desc': 'Zitate und direkte Rede',
+
+ // Word Lessons
+ 'lessons.words-01.title': 'Die 100 häufigsten Wörter',
+ 'lessons.words-01.desc': 'Die am meisten verwendeten deutschen Wörter',
+ 'lessons.words-02.title': 'Verben im Alltag',
+ 'lessons.words-02.desc': 'Häufig verwendete Verben',
+ 'lessons.words-03.title': 'Adjektive',
+ 'lessons.words-03.desc': 'Beschreibende Wörter',
+ 'lessons.words-04.title': 'Kurze Sätze',
+ 'lessons.words-04.desc': 'Einfache deutsche Sätze',
+ 'lessons.words-05.title': 'Mittellange Sätze',
+ 'lessons.words-05.desc': 'Sätze mit mehr Details',
+ 'lessons.words-06.title': 'Lange Sätze',
+ 'lessons.words-06.desc': 'Komplexere deutsche Sätze',
+ 'lessons.words-07.title': 'Absätze üben',
+ 'lessons.words-07.desc': 'Zusammenhängende Textpassagen',
+ 'lessons.words-08.title': 'Thema: Computer',
+ 'lessons.words-08.desc': 'Fachvokabular aus der Computerwelt',
+ 'lessons.words-09.title': 'Thema: Büro',
+ 'lessons.words-09.desc': 'Wörter aus dem Büroalltag',
+ 'lessons.words-10.title': 'Thema: Reisen',
+ 'lessons.words-10.desc': 'Vokabular rund ums Reisen',
+
+ // Advanced Lessons
+ 'lessons.adv-01.title': 'Alle Sonderzeichen Übersicht',
+ 'lessons.adv-01.desc': 'Systematisches Training aller wichtigen Sonderzeichen',
+ 'lessons.adv-02.title': 'Mathematische Zeichen',
+ 'lessons.adv-02.desc': 'Für technische und wissenschaftliche Texte',
+ 'lessons.adv-03.title': 'Deutsche Klassiker 1',
+ 'lessons.adv-03.desc': 'Zitate aus der deutschen Literatur',
+ 'lessons.adv-04.title': 'Geschäftsbriefe',
+ 'lessons.adv-04.desc': 'Formelle Korrespondenz',
+ 'lessons.adv-05.title': 'Wissenschaftliche Texte',
+ 'lessons.adv-05.desc': 'Akademisches Schreiben',
+ 'lessons.adv-06.title': 'Speed Drill 1',
+ 'lessons.adv-06.desc': 'Hochfrequenz-Wörter für maximale Geschwindigkeit',
+ 'lessons.adv-07.title': 'Speed Drill 2',
+ 'lessons.adv-07.desc': 'Längere Wörter im Schnelldurchlauf',
+
+ // Expert Lessons
+ 'lessons.exp-01.title': 'Philosophische Texte',
+ 'lessons.exp-01.desc': 'Anspruchsvolle philosophische Passagen',
+ 'lessons.exp-02.title': 'Rechtliche Texte',
+ 'lessons.exp-02.desc': 'Juristische Formulierungen',
+ 'lessons.exp-03.title': 'Medizinische Texte',
+ 'lessons.exp-03.desc': 'Fachvokabular aus der Medizin',
+ 'lessons.exp-04.title': 'Technische Dokumentation',
+ 'lessons.exp-04.desc': 'Anspruchsvolle technische Texte',
+ 'lessons.exp-05.title': 'Symbol-Marathon',
+ 'lessons.exp-05.desc': 'Alle Sonderzeichen in komplexen Kombinationen',
+ 'lessons.exp-06.title': 'Geschwindigkeits-Challenge',
+ 'lessons.exp-06.desc': 'Für absolute Profis - 60+ WPM Ziel',
+
+ // Programming Lessons
+ 'lessons.prog-js-01.title': 'JavaScript Anfänger',
+ 'lessons.prog-js-01.desc': 'Erste Schritte mit JavaScript',
+ 'lessons.prog-js-02.title': 'JavaScript Funktionen',
+ 'lessons.prog-js-02.desc': 'Funktionen in JavaScript',
+ 'lessons.prog-js-03.title': 'JavaScript Intermediate',
+ 'lessons.prog-js-03.desc': 'Fortgeschrittene JavaScript-Konzepte',
+ 'lessons.prog-js-04.title': 'JavaScript Advanced',
+ 'lessons.prog-js-04.desc': 'Async/Await und Promises',
+ 'lessons.prog-ts-01.title': 'TypeScript Basics',
+ 'lessons.prog-ts-01.desc': 'TypeScript-Typisierung lernen',
+ 'lessons.prog-ts-02.title': 'TypeScript Interfaces',
+ 'lessons.prog-ts-02.desc': 'Interfaces und Types',
+ 'lessons.prog-ts-03.title': 'TypeScript Advanced',
+ 'lessons.prog-ts-03.desc': 'Generics und fortgeschrittene Typen',
+ 'lessons.prog-py-01.title': 'Python Anfänger',
+ 'lessons.prog-py-01.desc': 'Python-Grundlagen',
+ 'lessons.prog-py-02.title': 'Python Funktionen',
+ 'lessons.prog-py-02.desc': 'Funktionen in Python',
+ 'lessons.prog-py-03.title': 'Python Intermediate',
+ 'lessons.prog-py-03.desc': 'Fortgeschrittenes Python',
+ 'lessons.prog-py-04.title': 'Python OOP',
+ 'lessons.prog-py-04.desc': 'Objektorientiertes Python',
+ 'lessons.prog-py-05.title': 'Python Async',
+ 'lessons.prog-py-05.desc': 'Asynchrones Python',
+ 'lessons.prog-java-01.title': 'Java Anfänger',
+ 'lessons.prog-java-01.desc': 'Java-Grundlagen',
+ 'lessons.prog-java-02.title': 'Java OOP',
+ 'lessons.prog-java-02.desc': 'Objektorientierung in Java',
+ 'lessons.prog-java-03.title': 'Java Collections & Streams',
+ 'lessons.prog-java-03.desc': 'Moderne Java Features',
+ 'lessons.prog-html-01.title': 'HTML Basics',
+ 'lessons.prog-html-01.desc': 'HTML-Grundlagen',
+ 'lessons.prog-css-01.title': 'CSS Basics',
+ 'lessons.prog-css-01.desc': 'CSS-Grundlagen',
+ 'lessons.prog-react-01.title': 'React Basics',
+ 'lessons.prog-react-01.desc': 'React Component Syntax',
+ 'lessons.prog-angular-01.title': 'Angular Basics',
+ 'lessons.prog-angular-01.desc': 'Angular Component Syntax',
+ 'lessons.prog-sql-01.title': 'SQL Basics',
+ 'lessons.prog-sql-01.desc': 'SQL-Grundlagen',
+ 'lessons.prog-sql-02.title': 'SQL Advanced',
+ 'lessons.prog-sql-02.desc': 'Fortgeschrittene SQL-Abfragen',
+ 'lessons.prog-bash-01.title': 'Bash/Shell Basics',
+ 'lessons.prog-bash-01.desc': 'Shell-Scripting Grundlagen',
+ 'lessons.prog-git-01.title': 'Git Commands',
+ 'lessons.prog-git-01.desc': 'Die wichtigsten Git-Befehle',
+
+ // Shortcut Lessons
+ 'lessons.shortcuts-vscode-01.title': 'VS Code Essentials',
+ 'lessons.shortcuts-vscode-01.desc': 'Die wichtigsten VS Code Shortcuts',
+ 'lessons.shortcuts-vscode-02.title': 'VS Code Advanced',
+ 'lessons.shortcuts-vscode-02.desc': 'Fortgeschrittene VS Code Shortcuts',
+ 'lessons.shortcuts-intellij-01.title': 'IntelliJ Essentials',
+ 'lessons.shortcuts-intellij-01.desc': 'Die wichtigsten IntelliJ Shortcuts',
+ 'lessons.shortcuts-vim-01.title': 'Vim Basics',
+ 'lessons.shortcuts-vim-01.desc': 'Grundlegende Vim-Befehle',
// Common
'common.wpm': 'WPM',
@@ -562,16 +977,48 @@ const de: Translations = {
'regex.playground': 'Spielplatz',
// Dev Tools - Algorithms
- 'algo.title': 'Algorithmen Training',
+ 'algo.title': 'Algorithmen & Framework Training',
+ 'algo.subtitle': 'Verbessere deine Programmierfähigkeiten durch Tippen von echtem Code',
'algo.browse': 'Durchsuchen',
'algo.practice': 'Üben',
'algo.challenges': 'Herausforderungen',
+ 'algo.algorithmsData': 'Algorithmen & Datenstrukturen',
+ 'algo.reactTs': 'React & TypeScript',
+ 'algo.exercises': 'Übungen',
+ 'algo.all': 'Alle',
+ 'algo.beginner': 'Anfänger',
+ 'algo.intermediate': 'Mittel',
+ 'algo.advanced': 'Fortgeschritten',
+ 'algo.noExercises': 'Keine Übungen in dieser Kategorie gefunden.',
+ 'algo.noExercisesHint': 'Wähle eine andere Kategorie oder entferne den Schwierigkeitsfilter.',
+ 'algo.back': '← Zurück',
+ 'algo.next': 'Weiter →',
+ 'algo.start': 'Starten',
+ 'algo.reset': 'Reset',
+ 'algo.wpm': 'WPM',
+ 'algo.accuracy': 'Genauigkeit',
+ 'algo.characters': 'Zeichen',
+ 'algo.errors': 'Fehler',
+ 'algo.exerciseComplete': 'Übung abgeschlossen! {wpm} WPM, {accuracy}% Genauigkeit',
+ 'algo.allComplete': 'Alle Übungen abgeschlossen! {wpm} WPM, {accuracy}% Genauigkeit',
// Dev Tools - SQL
'sql.title': 'SQL Training',
'sql.commands': 'Befehle',
'sql.training': 'Training',
'sql.playground': 'Spielplatz',
+ 'sql.subtitle': 'Lerne SQL durch Tippen echter Queries',
+ 'sql.exercises': 'Übungen',
+ 'sql.selectExercise': 'Wähle eine Übung',
+ 'sql.selectExerciseDesc': 'Klicke links auf eine SQL-Übung um zu starten',
+ 'sql.explanation': 'Erklärung',
+ 'sql.skipBtn': 'Überspringen →',
+ 'sql.nextBtn': 'Nächste Übung →',
+ 'sql.queryCopied': 'Query kopiert!',
+ 'sql.completed': 'Geschafft! +{xp} XP',
+ 'sql.difficulty.beginner': 'Anfänger',
+ 'sql.difficulty.intermediate': 'Fortgeschritten',
+ 'sql.difficulty.advanced': 'Experte',
// Common Dev Tools
'devtools.difficulty': 'Schwierigkeit',
diff --git a/src/data/algorithmExercises.ts b/src/data/algorithmExercises.ts
index 7a0e30e..f0e36db 100644
--- a/src/data/algorithmExercises.ts
+++ b/src/data/algorithmExercises.ts
@@ -622,13 +622,13 @@ export const ALL_ALGORITHM_SNIPPETS: CodeSnippet[] = [
// Categories for UI display
export const ALGORITHM_CATEGORIES = [
{ id: 'sorting', name: 'Sortierung', icon: '↕️', snippets: SORTING_ALGORITHMS },
- { id: 'search', name: 'Suche', icon: '🔍', snippets: SEARCH_ALGORITHMS },
- { id: 'data-structures', name: 'Datenstrukturen', icon: '📦', snippets: DATA_STRUCTURES },
+ { id: 'search', name: 'Suche', icon: 'search', snippets: SEARCH_ALGORITHMS },
+ { id: 'data-structures', name: 'Datenstrukturen', icon: 'box', snippets: DATA_STRUCTURES },
{
id: 'dynamic-programming',
name: 'Dynamic Programming',
- icon: '🧮',
+ icon: 'calc',
snippets: DYNAMIC_PROGRAMMING,
},
- { id: 'leetcode', name: 'LeetCode', icon: '💻', snippets: LEETCODE_PROBLEMS },
+ { id: 'leetcode', name: 'LeetCode', icon: 'code', snippets: LEETCODE_PROBLEMS },
];
diff --git a/src/data/frameworkExercises.ts b/src/data/frameworkExercises.ts
index b0a335e..6c8b8d3 100644
--- a/src/data/frameworkExercises.ts
+++ b/src/data/frameworkExercises.ts
@@ -835,9 +835,14 @@ export const ALL_FRAMEWORK_SNIPPETS: CodeSnippet[] = [
// Categories for UI display
export const FRAMEWORK_CATEGORIES = [
- { id: 'react-hooks', name: 'React Hooks', icon: '⚛️', snippets: REACT_HOOKS },
- { id: 'react-components', name: 'React Components', icon: '🧩', snippets: REACT_COMPONENTS },
- { id: 'custom-hooks', name: 'Custom Hooks', icon: '🪝', snippets: CUSTOM_HOOKS },
- { id: 'typescript', name: 'TypeScript Patterns', icon: '📘', snippets: TYPESCRIPT_PATTERNS },
- { id: 'testing', name: 'Testing', icon: '🧪', snippets: TESTING_PATTERNS },
+ { id: 'react-hooks', name: 'React Hooks', icon: 'react', snippets: REACT_HOOKS },
+ {
+ id: 'react-components',
+ name: 'React Components',
+ icon: 'component',
+ snippets: REACT_COMPONENTS,
+ },
+ { id: 'custom-hooks', name: 'Custom Hooks', icon: 'hook', snippets: CUSTOM_HOOKS },
+ { id: 'typescript', name: 'TypeScript Patterns', icon: 'ts', snippets: TYPESCRIPT_PATTERNS },
+ { id: 'testing', name: 'Testing', icon: 'test', snippets: TESTING_PATTERNS },
];
diff --git a/src/data/shortcuts.ts b/src/data/shortcuts.ts
index 5cf5600..ade0f73 100644
--- a/src/data/shortcuts.ts
+++ b/src/data/shortcuts.ts
@@ -7,7 +7,9 @@ export interface Shortcut {
id: string;
keys: string[];
description: string;
+ descriptionEn?: string;
category: string;
+ categoryEn?: string;
difficulty?: 'beginner' | 'intermediate' | 'advanced';
tags?: string[];
}
@@ -16,9 +18,11 @@ export interface Command {
id: string;
command: string;
description: string;
+ descriptionEn?: string;
category: string;
+ categoryEn?: string;
example?: string;
- flags?: { flag: string; description: string }[];
+ flags?: { flag: string; description: string; descriptionEn?: string }[];
difficulty?: 'beginner' | 'intermediate' | 'advanced';
tags?: string[];
}
@@ -26,8 +30,10 @@ export interface Command {
export interface ShortcutCollection {
id: string;
name: string;
+ nameEn?: string;
icon: string;
description: string;
+ descriptionEn?: string;
color: string;
shortcuts: Shortcut[];
}
@@ -35,8 +41,10 @@ export interface ShortcutCollection {
export interface CommandCollection {
id: string;
name: string;
+ nameEn?: string;
icon: string;
description: string;
+ descriptionEn?: string;
color: string;
commands: Command[];
}
@@ -8013,3 +8021,958 @@ export function searchAll(query: string): { shortcuts: Shortcut[]; commands: Com
export function getCategories(collection: ShortcutCollection): string[] {
return getShortcutCategories(collection);
}
+
+// ============================================================================
+// TRANSLATION MAPS - German to English
+// ============================================================================
+
+/**
+ * Category translations from German to English
+ */
+export const CATEGORY_TRANSLATIONS: Record = {
+ // VS Code Categories
+ Allgemein: 'General',
+ Bearbeiten: 'Editing',
+ 'Bearbeiten Erweitert': 'Advanced Editing',
+ Auswahl: 'Selection',
+ 'Multi-Cursor': 'Multi-Cursor',
+ 'Suchen & Ersetzen': 'Search & Replace',
+ Navigation: 'Navigation',
+ Folding: 'Folding',
+ Ansicht: 'View',
+ 'Editor-Verwaltung': 'Editor Management',
+ Debugging: 'Debugging',
+ Terminal: 'Terminal',
+ Git: 'Git',
+ Refactoring: 'Refactoring',
+ Snippets: 'Snippets',
+ IntelliSense: 'IntelliSense',
+ Breadcrumbs: 'Breadcrumbs',
+ Problems: 'Problems',
+ Output: 'Output',
+ Workspace: 'Workspace',
+
+ // macOS Categories
+ System: 'System',
+ Finder: 'Finder',
+ 'Finder Schnellzugriff': 'Finder Quick Access',
+ Text: 'Text',
+ 'Text Navigation': 'Text Navigation',
+ 'Mission Control': 'Mission Control',
+ Fenster: 'Window',
+ Safari: 'Safari',
+ Bedienungshilfen: 'Accessibility',
+ 'Microsoft Word': 'Microsoft Word',
+ 'Microsoft Excel': 'Microsoft Excel',
+ 'Microsoft PowerPoint': 'Microsoft PowerPoint',
+ Sonderzeichen: 'Special Characters',
+
+ // IntelliJ Categories
+ 'Code Intelligence': 'Code Intelligence',
+ Suchen: 'Search',
+ Ausführen: 'Run',
+ Debug: 'Debug',
+ Lesezeichen: 'Bookmarks',
+ Tabs: 'Tabs',
+ 'Live Templates': 'Live Templates',
+
+ // Chrome DevTools Categories
+ Elements: 'Elements',
+ Styles: 'Styles',
+ Console: 'Console',
+ Debugger: 'Debugger',
+ Network: 'Network',
+ Performance: 'Performance',
+
+ // Vim Categories
+ Modi: 'Modes',
+ Dateien: 'Files',
+
+ // Xcode Categories
+ 'Build & Run': 'Build & Run',
+ 'Interface Builder': 'Interface Builder',
+
+ // Terminal Categories
+ Auflisten: 'Listing',
+ Dateioperationen: 'File Operations',
+ Anzeigen: 'Viewing',
+ Textverarbeitung: 'Text Processing',
+ Prozesse: 'Processes',
+ Netzwerk: 'Network',
+ Komprimierung: 'Compression',
+ Pipes: 'Pipes',
+ History: 'History',
+
+ // Git Categories
+ Konfiguration: 'Configuration',
+ Repository: 'Repository',
+ Workflow: 'Workflow',
+ Branches: 'Branches',
+ Merging: 'Merging',
+ Rückgängig: 'Undo',
+ Stash: 'Stash',
+ Tags: 'Tags',
+ Submodules: 'Submodules',
+
+ // Docker Categories
+ Images: 'Images',
+ Container: 'Container',
+ Volumes: 'Volumes',
+ Compose: 'Compose',
+ Cleanup: 'Cleanup',
+
+ // NPM Categories
+ 'NPM Basics': 'NPM Basics',
+ 'NPM Erweitert': 'NPM Advanced',
+ Yarn: 'Yarn',
+ NPX: 'NPX',
+
+ // Homebrew Categories
+ Basics: 'Basics',
+ Wartung: 'Maintenance',
+ Info: 'Info',
+ Services: 'Services',
+ Taps: 'Taps',
+ Cask: 'Cask',
+ Erweitert: 'Advanced',
+};
+
+/**
+ * Description translations from German to English
+ * Note: This is a comprehensive mapping of all shortcut descriptions
+ */
+export const DESCRIPTION_TRANSLATIONS: Record = {
+ // VS Code - General
+ 'Schneller Dateizugriff (Quick Open)': 'Quick Open File',
+ 'Befehlspalette öffnen': 'Open Command Palette',
+ 'Einstellungen öffnen': 'Open Settings',
+ 'Tastaturkürzel anzeigen': 'Show Keyboard Shortcuts',
+ 'Aktiven Tab schließen': 'Close Active Tab',
+ 'Fenster schließen': 'Close Window',
+ 'Neues Fenster öffnen': 'Open New Window',
+ 'Neue Datei erstellen': 'Create New File',
+ 'Datei öffnen': 'Open File',
+ 'Datei speichern': 'Save File',
+ 'Datei speichern unter': 'Save File As',
+ 'Alle Dateien speichern': 'Save All Files',
+ 'Alle Tabs schließen': 'Close All Tabs',
+ 'Tab fixieren (Keep Open)': 'Keep Tab Open',
+ 'VS Code beenden': 'Quit VS Code',
+ 'Integriertes Terminal öffnen': 'Open Integrated Terminal',
+ 'Zuletzt geschlossenen Tab öffnen': 'Reopen Closed Tab',
+ 'Zwischen offenen Tabs wechseln': 'Switch Between Open Tabs',
+ 'Datei im Explorer anzeigen': 'Reveal File in Explorer',
+ 'Dateipfad kopieren': 'Copy File Path',
+
+ // VS Code - Editing
+ 'Zeile ausschneiden (ohne Auswahl)': 'Cut Line (no selection)',
+ 'Zeile kopieren (ohne Auswahl)': 'Copy Line (no selection)',
+ Einfügen: 'Paste',
+ 'Zeile nach oben verschieben': 'Move Line Up',
+ 'Zeile nach unten verschieben': 'Move Line Down',
+ 'Zeile nach oben kopieren': 'Copy Line Up',
+ 'Zeile nach unten kopieren': 'Copy Line Down',
+ 'Zeile löschen': 'Delete Line',
+ 'Neue Zeile darunter einfügen': 'Insert Line Below',
+ 'Neue Zeile darüber einfügen': 'Insert Line Above',
+ 'Zur passenden Klammer springen': 'Jump to Matching Bracket',
+ 'Zeile einrücken': 'Indent Line',
+ 'Zeile ausrücken': 'Outdent Line',
+ 'Zeilen-Kommentar umschalten': 'Toggle Line Comment',
+ 'Block-Kommentar umschalten': 'Toggle Block Comment',
+ Rückgängig: 'Undo',
+ Wiederholen: 'Redo',
+ 'Letzte Cursor-Aktion rückgängig': 'Undo Last Cursor Action',
+ 'IntelliSense auslösen': 'Trigger IntelliSense',
+ 'Quick Fix / Schnelle Aktionen': 'Quick Fix / Quick Actions',
+
+ // VS Code - Advanced Editing
+ 'Auswahl formatieren': 'Format Selection',
+ 'Dokument formatieren': 'Format Document',
+ 'Trailing Whitespace entfernen': 'Remove Trailing Whitespace',
+ 'Sprache der Datei ändern': 'Change File Language',
+ 'Zu Zeile gehen': 'Go to Line',
+ 'Zeilen-Kommentar hinzufügen': 'Add Line Comment',
+ 'Zeilen-Kommentar entfernen': 'Remove Line Comment',
+ 'Zeichen vertauschen': 'Transpose Characters',
+ 'Groß-/Kleinschreibung ändern': 'Change Case',
+ 'Zeilen verbinden': 'Join Lines',
+
+ // VS Code - Selection
+ 'Wort auswählen / Nächstes Vorkommen hinzufügen': 'Select Word / Add Next Occurrence',
+ 'Letztes Vorkommen überspringen': 'Skip Last Occurrence',
+ 'Alle Vorkommen auswählen': 'Select All Occurrences',
+ 'Aktuelle Zeile auswählen': 'Select Current Line',
+ 'Alles auswählen': 'Select All',
+ 'Auswahl erweitern': 'Expand Selection',
+ 'Auswahl verkleinern': 'Shrink Selection',
+ 'Auswahl wortweise erweitern': 'Expand Selection by Word',
+ 'Auswahl wortweise verkleinern': 'Shrink Selection by Word',
+ 'Bis zum Zeilenende auswählen': 'Select to End of Line',
+ 'Bis zum Zeilenanfang auswählen': 'Select to Start of Line',
+ 'Zeile nach oben auswählen': 'Select Line Above',
+ 'Zeile nach unten auswählen': 'Select Line Below',
+ 'Alle Vorkommen des Wortes löschen': 'Delete All Occurrences',
+
+ // VS Code - Multi-Cursor
+ 'Cursor an Position hinzufügen': 'Add Cursor at Position',
+ 'Cursor oberhalb hinzufügen': 'Add Cursor Above',
+ 'Cursor unterhalb hinzufügen': 'Add Cursor Below',
+ 'Cursor bei allen Vorkommen': 'Add Cursor at All Occurrences',
+ 'Alle Vorkommen ändern': 'Change All Occurrences',
+ 'Cursor oben einfügen (Column)': 'Insert Cursor Above (Column)',
+ 'Cursor unten einfügen (Column)': 'Insert Cursor Below (Column)',
+ 'Cursor am Ende jeder Zeile': 'Add Cursor at End of Each Line',
+ 'Multi-Cursor beenden': 'Exit Multi-Cursor',
+ 'Letzten Cursor rückgängig': 'Undo Last Cursor',
+
+ // VS Code - Search & Replace
+ 'Suchen in Datei': 'Find in File',
+ 'Ersetzen in Datei': 'Replace in File',
+ 'In Dateien suchen': 'Find in Files',
+ 'In Dateien ersetzen': 'Replace in Files',
+ 'Nächstes Vorkommen finden': 'Find Next Occurrence',
+ 'Vorheriges Vorkommen finden': 'Find Previous Occurrence',
+ 'Nächstes Vorkommen (Alternative)': 'Find Next (Alternative)',
+ 'Vorheriges Vorkommen (Alternative)': 'Find Previous (Alternative)',
+ 'Alle Treffer auswählen': 'Select All Matches',
+ 'Groß-/Kleinschreibung umschalten': 'Toggle Case Sensitive',
+ 'Ganzes Wort umschalten': 'Toggle Whole Word',
+ 'Regex umschalten': 'Toggle Regex',
+ 'Suche schließen': 'Close Search',
+
+ // VS Code - Navigation
+ 'Zu Datei gehen': 'Go to File',
+ 'Zu Symbol in Datei gehen': 'Go to Symbol in File',
+ 'Zu Symbol im Workspace gehen': 'Go to Symbol in Workspace',
+ 'Zum Dateianfang': 'Go to Start of File',
+ 'Zum Dateiende': 'Go to End of File',
+ 'Zurück navigieren': 'Navigate Back',
+ 'Vorwärts navigieren': 'Navigate Forward',
+ 'Zur Definition gehen': 'Go to Definition',
+ 'Definition anzeigen (Peek)': 'Peek Definition',
+ 'Definition zur Seite öffnen': 'Open Definition to Side',
+ 'Alle Referenzen anzeigen': 'Show All References',
+ 'Symbol umbenennen': 'Rename Symbol',
+ 'Hover-Info anzeigen': 'Show Hover Info',
+ 'Zum nächsten Problem gehen': 'Go to Next Problem',
+ 'Zum vorherigen Problem gehen': 'Go to Previous Problem',
+ 'Zum Zeilenanfang': 'Go to Start of Line',
+ 'Zum Zeilenende': 'Go to End of Line',
+ 'Wort zurück': 'Word Back',
+ 'Wort vor': 'Word Forward',
+
+ // VS Code - Folding
+ 'Bereich einklappen': 'Fold Region',
+ 'Bereich ausklappen': 'Unfold Region',
+ 'Alle Unterbereiche einklappen': 'Fold All Subregions',
+ 'Alle Unterbereiche ausklappen': 'Unfold All Subregions',
+ 'Alle einklappen': 'Fold All',
+ 'Alle ausklappen': 'Unfold All',
+ 'Level 1 einklappen': 'Fold Level 1',
+ 'Level 2 einklappen': 'Fold Level 2',
+ 'Level 3 einklappen': 'Fold Level 3',
+ 'Level 4 einklappen': 'Fold Level 4',
+
+ // VS Code - View
+ 'Seitenleiste umschalten': 'Toggle Sidebar',
+ 'Panel umschalten': 'Toggle Panel',
+ 'Terminal umschalten': 'Toggle Terminal',
+ 'Explorer anzeigen': 'Show Explorer',
+ 'Suche anzeigen': 'Show Search',
+ 'Source Control anzeigen': 'Show Source Control',
+ 'Debug-Ansicht anzeigen': 'Show Debug',
+ 'Extensions anzeigen': 'Show Extensions',
+ 'Zen-Modus umschalten': 'Toggle Zen Mode',
+ 'Editor-Layout: Einzeln': 'Editor Layout: Single',
+ 'Editor-Layout: Zwei Spalten': 'Editor Layout: Two Columns',
+ 'Editor-Layout: Drei Spalten': 'Editor Layout: Three Columns',
+ 'Vollbild umschalten': 'Toggle Full Screen',
+ 'Zoom vergrößern': 'Zoom In',
+ 'Zoom verkleinern': 'Zoom Out',
+ 'Zoom zurücksetzen': 'Reset Zoom',
+ 'Editor teilen': 'Split Editor',
+ 'Editor orthogonal teilen': 'Split Editor Orthogonally',
+ 'Markdown-Vorschau': 'Markdown Preview',
+ 'Markdown-Vorschau zur Seite': 'Markdown Preview to Side',
+
+ // VS Code - Editor Management
+ 'Erste Editor-Gruppe fokussieren': 'Focus First Editor Group',
+ 'Zweite Editor-Gruppe fokussieren': 'Focus Second Editor Group',
+ 'Dritte Editor-Gruppe fokussieren': 'Focus Third Editor Group',
+ 'Fokus auf linke Gruppe': 'Focus Left Group',
+ 'Fokus auf rechte Gruppe': 'Focus Right Group',
+ 'Fokus auf obere Gruppe': 'Focus Above Group',
+ 'Fokus auf untere Gruppe': 'Focus Below Group',
+ 'Editor in linke Gruppe verschieben': 'Move Editor to Left Group',
+ 'Editor in rechte Gruppe verschieben': 'Move Editor to Right Group',
+ 'Nächsten Tab öffnen': 'Open Next Tab',
+ 'Vorherigen Tab öffnen': 'Open Previous Tab',
+
+ // VS Code - Debugging
+ 'Debugging starten / fortsetzen': 'Start / Continue Debugging',
+ 'Debugging stoppen': 'Stop Debugging',
+ 'Debugging neu starten': 'Restart Debugging',
+ 'Breakpoint umschalten': 'Toggle Breakpoint',
+ 'Step Over': 'Step Over',
+ 'Step Into': 'Step Into',
+ 'Step Out': 'Step Out',
+ 'Debug Hover Info': 'Debug Hover Info',
+ 'Inline Breakpoint': 'Inline Breakpoint',
+ 'Debugging pausieren': 'Pause Debugging',
+
+ // VS Code - Terminal
+ 'Terminal anzeigen/verbergen': 'Show/Hide Terminal',
+ 'Neues Terminal erstellen': 'Create New Terminal',
+ 'Terminal teilen': 'Split Terminal',
+ 'Im Terminal nach oben scrollen': 'Scroll Up in Terminal',
+ 'Im Terminal nach unten scrollen': 'Scroll Down in Terminal',
+ 'Terminal leeren': 'Clear Terminal',
+ 'Vorheriges Terminal': 'Previous Terminal',
+ 'Nächstes Terminal': 'Next Terminal',
+ 'Auswahl kopieren (Terminal)': 'Copy Selection (Terminal)',
+ 'In Terminal einfügen': 'Paste into Terminal',
+
+ // VS Code - Git
+ 'Source Control öffnen': 'Open Source Control',
+ 'Git: Diff anzeigen': 'Git: Show Diff',
+ 'Commit (in Source Control)': 'Commit (in Source Control)',
+
+ // VS Code - Refactoring
+ 'Code-Aktionen / Quick Fix': 'Code Actions / Quick Fix',
+ 'Refactoring-Menü öffnen': 'Open Refactoring Menu',
+ 'Alle Referenzen umbenennen': 'Rename All References',
+
+ // VS Code - Snippets
+ 'Snippet-Vorschläge anzeigen': 'Show Snippet Suggestions',
+ 'Snippet einfügen/erweitern': 'Insert/Expand Snippet',
+ 'Zum vorherigen Snippet-Placeholder': 'Previous Snippet Placeholder',
+
+ // VS Code - IntelliSense
+ 'Parameter-Hinweise anzeigen': 'Show Parameter Hints',
+ 'Quick Info anzeigen': 'Show Quick Info',
+ 'Vorschlag akzeptieren': 'Accept Suggestion',
+ 'Vorschlag akzeptieren (Tab)': 'Accept Suggestion (Tab)',
+ 'IntelliSense schließen': 'Close IntelliSense',
+
+ // VS Code - Breadcrumbs
+ 'Breadcrumbs fokussieren': 'Focus Breadcrumbs',
+ 'Breadcrumb auswählen': 'Select Breadcrumb',
+ 'Zum vorherigen Breadcrumb': 'Previous Breadcrumb',
+ 'Zum nächsten Breadcrumb': 'Next Breadcrumb',
+
+ // VS Code - Problems
+ 'Problems-Panel umschalten': 'Toggle Problems Panel',
+
+ // VS Code - Output
+ 'Output-Panel anzeigen': 'Show Output Panel',
+ 'Debug Console anzeigen': 'Show Debug Console',
+
+ // VS Code - Workspace
+ 'Ordner in neuem Fenster öffnen': 'Open Folder in New Window',
+ 'Ordner aus Workspace entfernen': 'Remove Folder from Workspace',
+ 'Aktive Datei in neuem Fenster': 'Open Active File in New Window',
+
+ // macOS - System
+ 'Spotlight öffnen': 'Open Spotlight',
+ 'Screenshot (ganzer Bildschirm)': 'Screenshot (Full Screen)',
+ 'Screenshot (Auswahl)': 'Screenshot (Selection)',
+ 'Screenshot (Fenster)': 'Screenshot (Window)',
+ 'Screenshot-Werkzeug öffnen': 'Open Screenshot Tool',
+ 'Screenshot Touch Bar': 'Screenshot Touch Bar',
+ 'App wechseln (vorwärts)': 'Switch App (Forward)',
+ 'App wechseln (rückwärts)': 'Switch App (Backward)',
+ 'Zwischen Fenstern einer App wechseln': 'Switch Between Windows of App',
+ 'App beenden': 'Quit App',
+ 'App ausblenden': 'Hide App',
+ 'Andere Apps ausblenden': 'Hide Other Apps',
+ 'Fenster minimieren': 'Minimize Window',
+ 'Alle Fenster minimieren': 'Minimize All Windows',
+ 'Fenster/Tab schließen': 'Close Window/Tab',
+ 'Alle Fenster schließen': 'Close All Windows',
+ 'Einstellungen der App': 'App Settings',
+ 'Sofort beenden Dialog': 'Force Quit Dialog',
+ 'Bildschirm sperren': 'Lock Screen',
+ 'Mac neu starten': 'Restart Mac',
+ 'Bildschirme ausschalten': 'Turn Off Displays',
+ 'Alle Apps beenden und neu starten': 'Quit All Apps and Restart',
+ 'Mac in Ruhezustand': 'Put Mac to Sleep',
+ 'Emoji & Symbole': 'Emoji & Symbols',
+
+ // macOS - Finder
+ 'Neues Finder-Fenster': 'New Finder Window',
+ 'Neuen Ordner erstellen': 'Create New Folder',
+ 'Neuer intelligenter Ordner': 'New Smart Folder',
+ 'Neuer Tab': 'New Tab',
+ 'In Papierkorb verschieben': 'Move to Trash',
+ 'Papierkorb leeren': 'Empty Trash',
+ 'Sofort löschen (ohne Papierkorb)': 'Delete Immediately (Without Trash)',
+ Öffnen: 'Open',
+ 'Informationen anzeigen': 'Get Info',
+ 'Informationen (ein Fenster für alle)': 'Get Info (Single Window)',
+ Duplizieren: 'Duplicate',
+ 'Volume auswerfen': 'Eject Volume',
+ 'Quick Look': 'Quick Look',
+ 'Quick Look (Alternative)': 'Quick Look (Alternative)',
+ 'Versteckte Dateien anzeigen/verbergen': 'Show/Hide Hidden Files',
+ 'Gehe zu Ordner...': 'Go to Folder...',
+ 'Übergeordneten Ordner öffnen': 'Open Parent Folder',
+ 'Ausgewähltes Element öffnen': 'Open Selected Item',
+ 'Übergeordneten Ordner in neuem Fenster': 'Open Parent in New Window',
+ 'Symbol-Ansicht': 'Icon View',
+ 'Listen-Ansicht': 'List View',
+ 'Spalten-Ansicht': 'Column View',
+ 'Galerie-Ansicht': 'Gallery View',
+ Darstellungsoptionen: 'View Options',
+ 'Mit Server verbinden': 'Connect to Server',
+ 'Alias erstellen': 'Create Alias',
+ 'Original anzeigen (bei Alias)': 'Show Original (for Alias)',
+ Suchen: 'Search',
+ Zurück: 'Back',
+ Vorwärts: 'Forward',
+
+ // macOS - Finder Quick Access
+ 'Programme-Ordner': 'Applications Folder',
+ Computer: 'Computer',
+ Schreibtisch: 'Desktop',
+ 'Alle meine Dateien': 'All My Files',
+ 'Gehe zu Ordner': 'Go to Folder',
+ Benutzerordner: 'Home Folder',
+ 'iCloud Drive': 'iCloud Drive',
+ Netzwerk: 'Network',
+ Dokumente: 'Documents',
+ AirDrop: 'AirDrop',
+ Dienstprogramme: 'Utilities',
+ Downloads: 'Downloads',
+
+ // macOS - Text
+ Kopieren: 'Copy',
+ Ausschneiden: 'Cut',
+ 'Einfügen ohne Formatierung': 'Paste Without Formatting',
+ 'Einfügen mit Stil anpassen': 'Paste and Match Style',
+ Fett: 'Bold',
+ Kursiv: 'Italic',
+ Unterstrichen: 'Underline',
+ 'Schriften einblenden': 'Show Fonts',
+ Weitersuchen: 'Find Next',
+ 'Rückwärts suchen': 'Find Previous',
+ 'Auswahl zum Suchen verwenden': 'Use Selection for Find',
+ 'Zur Auswahl springen': 'Jump to Selection',
+ 'Rechtschreibung prüfen': 'Check Spelling',
+ 'Rechtschreibung und Grammatik': 'Spelling and Grammar',
+
+ // macOS - Text Navigation
+ 'Zum Dokumentanfang': 'Go to Start of Document',
+ 'Zum Dokumentende': 'Go to End of Document',
+ 'Ein Wort zurück': 'One Word Back',
+ 'Ein Wort vor': 'One Word Forward',
+ 'Zum Zeilenanfang (Emacs)': 'Go to Start of Line (Emacs)',
+ 'Zum Zeilenende (Emacs)': 'Go to End of Line (Emacs)',
+ 'Eine Zeile hoch': 'One Line Up',
+ 'Eine Zeile runter': 'One Line Down',
+ 'Ein Zeichen zurück': 'One Character Back',
+ 'Ein Zeichen vor': 'One Character Forward',
+ 'Zeichen nach Cursor löschen': 'Delete Character After Cursor',
+ 'Text bis Zeilenende löschen': 'Delete to End of Line',
+ 'Zeichen vor Cursor löschen': 'Delete Character Before Cursor',
+ 'Neue Zeile nach Cursor': 'New Line After Cursor',
+ 'Entf (Forward Delete)': 'Forward Delete',
+ 'Wort löschen': 'Delete Word',
+ 'Bis Zeilenanfang löschen': 'Delete to Start of Line',
+
+ // macOS - Mission Control
+ 'App-Fenster anzeigen': 'Show App Windows',
+ 'Einen Space nach links': 'Move One Space Left',
+ 'Einen Space nach rechts': 'Move One Space Right',
+ 'Zu Space 1 wechseln': 'Switch to Space 1',
+ 'Zu Space 2 wechseln': 'Switch to Space 2',
+ 'Zu Space 3 wechseln': 'Switch to Space 3',
+ 'Schreibtisch anzeigen': 'Show Desktop',
+ 'Dashboard anzeigen': 'Show Dashboard',
+
+ // macOS - Window
+ 'Vollbildmodus umschalten': 'Toggle Full Screen Mode',
+ 'Andere ausblenden': 'Hide Others',
+ 'Neues Fenster': 'New Window',
+
+ // macOS - Safari
+ 'Adresszeile fokussieren': 'Focus Address Bar',
+ 'Tab schließen': 'Close Tab',
+ 'Letzten Tab wiederherstellen': 'Restore Last Tab',
+ 'Seite neu laden': 'Reload Page',
+ 'Seite ohne Cache neu laden': 'Reload Page Without Cache',
+ 'Lesezeichen-Leiste ein/aus': 'Toggle Bookmarks Bar',
+ 'Leseliste anzeigen': 'Show Reading List',
+ 'Lesezeichen hinzufügen': 'Add Bookmark',
+ 'Web-Suche (Google etc.)': 'Web Search (Google etc.)',
+ 'Zu Tab 1': 'Go to Tab 1',
+ 'Zu Tab 2': 'Go to Tab 2',
+ 'Zum letzten Tab': 'Go to Last Tab',
+ 'Tab-Übersicht': 'Tab Overview',
+ 'Web Inspector': 'Web Inspector',
+ 'JavaScript-Konsole': 'JavaScript Console',
+
+ // macOS - Accessibility
+ 'VoiceOver ein/aus': 'Toggle VoiceOver',
+ 'Zoom ein/aus': 'Toggle Zoom',
+ Vergrößern: 'Zoom In',
+ Verkleinern: 'Zoom Out',
+ 'Farben umkehren': 'Invert Colors',
+ 'Tastatursteuerung aktivieren': 'Enable Keyboard Control',
+
+ // macOS - Microsoft Word
+ 'Neues Dokument': 'New Document',
+ 'Dokument öffnen': 'Open Document',
+ Speichern: 'Save',
+ 'Speichern unter': 'Save As',
+ Drucken: 'Print',
+ 'Zentriert ausrichten': 'Center Align',
+ 'Links ausrichten': 'Left Align',
+ 'Rechts ausrichten': 'Right Align',
+ Blocksatz: 'Justify',
+ 'Schrift vergrößern': 'Increase Font Size',
+ 'Schrift verkleinern': 'Decrease Font Size',
+ 'Hyperlink einfügen': 'Insert Hyperlink',
+ KAPITÄLCHEN: 'Small Caps',
+ 'Schriftart Dialog': 'Font Dialog',
+ 'Formatierung entfernen': 'Remove Formatting',
+ 'Überschrift 1': 'Heading 1',
+ 'Überschrift 2': 'Heading 2',
+ 'Überschrift 3': 'Heading 3',
+ Seitenumbruch: 'Page Break',
+ Abschnittswechsel: 'Section Break',
+ 'Suchen und Ersetzen': 'Find and Replace',
+
+ // macOS - Microsoft Excel
+ 'Neue Arbeitsmappe': 'New Workbook',
+ 'Arbeitsmappe öffnen': 'Open Workbook',
+ 'Spalte auswählen': 'Select Column',
+ 'Zeile auswählen': 'Select Row',
+ 'Zeile/Spalte einfügen': 'Insert Row/Column',
+ 'Zeile/Spalte löschen': 'Delete Row/Column',
+ Währungsformat: 'Currency Format',
+ Prozentformat: 'Percentage Format',
+ Datumsformat: 'Date Format',
+ Zahlenformat: 'Number Format',
+ 'Zelle bearbeiten': 'Edit Cell',
+ 'Eingabe bestätigen (runter)': 'Confirm Input (Down)',
+ 'Eingabe bestätigen (rechts)': 'Confirm Input (Right)',
+ 'Eingabe abbrechen': 'Cancel Input',
+ AutoSumme: 'AutoSum',
+ 'Formeln anzeigen/verbergen': 'Show/Hide Formulas',
+ 'Zu Zelle A1': 'Go to Cell A1',
+ 'Zur letzten Zelle': 'Go to Last Cell',
+ 'Gehe zu Zelle': 'Go to Cell',
+ 'Absoluter/Relativer Zellbezug': 'Toggle Absolute/Relative Reference',
+ 'Aktuelles Datum einfügen': 'Insert Current Date',
+ 'Aktuelle Uhrzeit einfügen': 'Insert Current Time',
+ AutoFilter: 'AutoFilter',
+ 'AutoSumme (Alternative)': 'AutoSum (Alternative)',
+
+ // macOS - Microsoft PowerPoint
+ 'Neue Präsentation': 'New Presentation',
+ 'Präsentation öffnen': 'Open Presentation',
+ 'Neue Folie': 'New Slide',
+ 'Folie duplizieren': 'Duplicate Slide',
+ 'Präsentation starten (Anfang)': 'Start Presentation (Beginning)',
+ 'Präsentation starten (aktuelle Folie)': 'Start Presentation (Current Slide)',
+ 'Präsentation beenden': 'End Presentation',
+ 'Nächste Folie (Präsentation)': 'Next Slide (Presentation)',
+ 'Vorherige Folie (Präsentation)': 'Previous Slide (Presentation)',
+ 'Schwarzer Bildschirm': 'Black Screen',
+ 'Weißer Bildschirm': 'White Screen',
+ 'Objekte gruppieren': 'Group Objects',
+ 'Gruppierung aufheben': 'Ungroup Objects',
+ Zentriert: 'Center',
+ 'Objekt duplizieren': 'Duplicate Object',
+ 'In Vordergrund': 'Bring to Front',
+ 'In Hintergrund': 'Send to Back',
+ 'Eine Ebene nach vorne': 'Bring Forward',
+ 'Eine Ebene nach hinten': 'Send Backward',
+
+ // macOS - Special Characters
+ 'É (Akut)': 'É (Acute)',
+ 'Ü (Umlaut)': 'Ü (Umlaut)',
+ 'ß (Eszett)': 'ß (Eszett)',
+ '© (Copyright)': '© (Copyright)',
+ '® (Registered)': '® (Registered)',
+ '™ (Trademark)': '™ (Trademark)',
+ '• (Bullet)': '• (Bullet)',
+ '– (Gedankenstrich)': '– (En Dash)',
+ '— (langer Strich)': '— (Em Dash)',
+
+ // IntelliJ - General
+ 'Aktion suchen': 'Find Action',
+ 'Überall suchen (Search Everywhere)': 'Search Everywhere',
+ Projektstruktur: 'Project Structure',
+ 'Editor maximieren/minimieren': 'Toggle Editor Maximization',
+ 'Alles speichern': 'Save All',
+ 'Dateisystem synchronisieren': 'Synchronize File System',
+ 'Kürzlich ausgeführte Konfigurationen': 'Recent Run Configurations',
+ 'IDE beenden': 'Quit IDE',
+ 'Spaltenauswahl-Modus': 'Column Selection Mode',
+
+ // IntelliJ - Navigation
+ 'Klasse öffnen': 'Open Class',
+ 'Symbol öffnen': 'Open Symbol',
+ 'Letzte Dateien': 'Recent Files',
+ 'Letzte Bearbeitungsstellen': 'Recent Edit Locations',
+ 'Zur Deklaration gehen': 'Go to Declaration',
+ 'Zur Implementierung gehen': 'Go to Implementation',
+ 'Zur Super-Methode/Klasse gehen': 'Go to Super Method/Class',
+ 'Zum Typ gehen': 'Go to Type',
+ 'Zum nächsten Fehler': 'Go to Next Error',
+ 'Zum vorherigen Fehler': 'Go to Previous Error',
+ 'Zur letzten Position': 'Go to Last Position',
+ 'Zur nächsten Position': 'Go to Next Position',
+ 'Dateistruktur anzeigen': 'Show File Structure',
+ 'Typ-Hierarchie': 'Type Hierarchy',
+ 'Methoden-Hierarchie': 'Method Hierarchy',
+ 'Aufruf-Hierarchie': 'Call Hierarchy',
+ 'Zur Navigationsleiste': 'Go to Navigation Bar',
+ 'Quelle bearbeiten': 'Edit Source',
+ 'Quelle anzeigen': 'View Source',
+ 'Datei in View auswählen': 'Select File in View',
+ 'Terminal öffnen': 'Open Terminal',
+
+ // IntelliJ - Editing
+ 'Zeile duplizieren': 'Duplicate Line',
+ 'Statement vervollständigen': 'Complete Statement',
+ 'Statement nach oben verschieben': 'Move Statement Up',
+ 'Statement nach unten verschieben': 'Move Statement Down',
+ 'Zeilen-Kommentar': 'Line Comment',
+ 'Block-Kommentar': 'Block Comment',
+ 'Code formatieren': 'Format Code',
+ 'Einrückung korrigieren': 'Correct Indentation',
+ 'Imports optimieren': 'Optimize Imports',
+ 'Code-Block aufklappen': 'Expand Code Block',
+ 'Code-Block zuklappen': 'Collapse Code Block',
+ 'Alle aufklappen': 'Expand All',
+ 'Alle zuklappen': 'Collapse All',
+
+ // IntelliJ - Code Intelligence
+ 'Basic Autocomplete': 'Basic Autocomplete',
+ 'Smart Autocomplete': 'Smart Autocomplete',
+ 'Parameter-Info': 'Parameter Info',
+ 'Quick Documentation': 'Quick Documentation',
+ 'Code generieren (Generate)': 'Generate Code',
+ 'Methoden überschreiben': 'Override Methods',
+ 'Methoden implementieren': 'Implement Methods',
+ 'Surround with...': 'Surround with...',
+ 'Intention Actions / Quick Fix': 'Intention Actions / Quick Fix',
+ 'Live Template einfügen': 'Insert Live Template',
+ 'Mit Live Template umschließen': 'Surround with Live Template',
+ 'Externe Dokumentation': 'External Documentation',
+
+ // IntelliJ - Selection
+ 'Nächstes Vorkommen auswählen': 'Select Next Occurrence',
+ 'Auswahl aufheben': 'Deselect',
+
+ // IntelliJ - Refactoring
+ Umbenennen: 'Rename',
+ 'Signatur ändern': 'Change Signature',
+ Inline: 'Inline',
+ 'Methode extrahieren': 'Extract Method',
+ 'Variable extrahieren': 'Extract Variable',
+ 'Feld extrahieren': 'Extract Field',
+ 'Konstante extrahieren': 'Extract Constant',
+ 'Parameter extrahieren': 'Extract Parameter',
+ 'Refactor This (Menü)': 'Refactor This (Menu)',
+ Verschieben: 'Move',
+ 'Safe Delete': 'Safe Delete',
+
+ // IntelliJ - Search
+ Ersetzen: 'Replace',
+ 'Nächstes Vorkommen': 'Next Occurrence',
+ 'Vorheriges Vorkommen': 'Previous Occurrence',
+ 'Verwendungen finden': 'Find Usages',
+ 'Verwendungen anzeigen': 'Show Usages',
+ 'Verwendungen in Datei hervorheben': 'Highlight Usages in File',
+
+ // IntelliJ - Run & Debug
+ Ausführen: 'Run',
+ 'Debug starten': 'Start Debug',
+ 'Konfiguration wählen und ausführen': 'Select Configuration and Run',
+ 'Konfiguration wählen und debuggen': 'Select Configuration and Debug',
+ Stoppen: 'Stop',
+ 'Force Step Into': 'Force Step Into',
+ 'Zum Cursor ausführen': 'Run to Cursor',
+ 'Weiter (Resume)': 'Resume',
+ 'Breakpoints anzeigen': 'Show Breakpoints',
+ 'Ausdruck auswerten': 'Evaluate Expression',
+ 'Ausführungskonfiguration bearbeiten': 'Edit Run Configuration',
+
+ // IntelliJ - Git
+ Commit: 'Commit',
+ Push: 'Push',
+ 'Update/Pull': 'Update/Pull',
+ 'VCS-Operationen-Menü': 'VCS Operations Menu',
+ 'Datei zu VCS hinzufügen': 'Add File to VCS',
+ 'Diff anzeigen': 'Show Diff',
+ 'Änderungen verwerfen': 'Discard Changes',
+ 'Commit-Verlauf': 'Commit History',
+ 'Annotate (Blame)': 'Annotate (Blame)',
+
+ // IntelliJ - Tool Windows
+ 'Projekt-Fenster': 'Project Window',
+ Favoriten: 'Favorites',
+ 'Find-Fenster': 'Find Window',
+ 'Run-Fenster': 'Run Window',
+ 'Debug-Fenster': 'Debug Window',
+ 'Problems-Fenster': 'Problems Window',
+ 'Struktur-Fenster': 'Structure Window',
+ 'Services-Fenster': 'Services Window',
+ 'Git-Fenster': 'Git Window',
+ 'Alle Tool-Fenster verbergen': 'Hide All Tool Windows',
+ 'Zurück zum Editor': 'Return to Editor',
+ 'Tool-Fenster schließen und fokussieren': 'Close and Focus Tool Window',
+
+ // IntelliJ - Bookmarks
+ 'Lesezeichen umschalten': 'Toggle Bookmark',
+ 'Lesezeichen mit Mnemonik': 'Bookmark with Mnemonic',
+ 'Zu Lesezeichen 0 gehen': 'Go to Bookmark 0',
+ 'Alle Lesezeichen anzeigen': 'Show All Bookmarks',
+
+ // IntelliJ - Tabs
+ 'Vorheriger Tab': 'Previous Tab',
+ 'Nächster Tab': 'Next Tab',
+
+ // IntelliJ - Live Templates
+ 'Code mit Template umschließen': 'Surround with Template',
+
+ // IntelliJ - Multi-Cursor
+ 'Cursor hinzufügen': 'Add Cursor',
+ 'Mehrere Cursor (gedrückt halten + Pfeile)': 'Multiple Cursors (Hold + Arrows)',
+
+ // Chrome DevTools
+ 'DevTools öffnen/schließen': 'Open/Close DevTools',
+ 'Console öffnen': 'Open Console',
+ 'Element untersuchen': 'Inspect Element',
+ 'Element-Selektor': 'Element Selector',
+ 'Command Menu': 'Command Menu',
+ 'Device Mode umschalten': 'Toggle Device Mode',
+ 'Drawer umschalten': 'Toggle Drawer',
+ 'Dock-Position wechseln': 'Change Dock Position',
+ 'Vorheriges Panel': 'Previous Panel',
+ 'Nächstes Panel': 'Next Panel',
+ 'Element auswählen': 'Select Element',
+ 'Element auf-/zuklappen': 'Expand/Collapse Element',
+ 'Attribut bearbeiten': 'Edit Attribute',
+ 'Nächstes Attribut': 'Next Attribute',
+ 'Element verstecken': 'Hide Element',
+ 'Als HTML bearbeiten': 'Edit as HTML',
+ 'Element löschen': 'Delete Element',
+ 'Eigenschaft bearbeiten': 'Edit Property',
+ 'Nächste Eigenschaft': 'Next Property',
+ 'Farbformat wechseln': 'Change Color Format',
+ 'Wert um 1 ändern': 'Change Value by 1',
+ 'Wert um 10 ändern': 'Change Value by 10',
+ 'Wert um 100 ändern': 'Change Value by 100',
+ 'Wert um 0.1 ändern': 'Change Value by 0.1',
+ 'Console leeren': 'Clear Console',
+ Autovervollständigung: 'Autocomplete',
+ 'Vorherige Befehle': 'Previous Commands',
+ 'Ausdruck ausführen': 'Execute Expression',
+ 'Mehrzeilige Eingabe': 'Multi-line Input',
+ 'Pausieren/Fortsetzen': 'Pause/Resume',
+ 'Breakpoint setzen': 'Set Breakpoint',
+ 'Breakpoint aktivieren/deaktivieren': 'Enable/Disable Breakpoint',
+ 'Alle Breakpoints deaktivieren': 'Disable All Breakpoints',
+ 'Zu Funktion gehen': 'Go to Function',
+ 'Recording starten/stoppen': 'Start/Stop Recording',
+ 'Requests löschen': 'Clear Requests',
+ 'Ausgewählten Request wiederholen': 'Repeat Selected Request',
+ 'In Requests suchen': 'Search Requests',
+ 'Profil speichern': 'Save Profile',
+ 'Profil laden': 'Load Profile',
+
+ // Vim - Modes
+ 'Insert Mode (vor Cursor)': 'Insert Mode (Before Cursor)',
+ 'Insert am Zeilenanfang': 'Insert at Line Start',
+ 'Append (nach Cursor)': 'Append (After Cursor)',
+ 'Append am Zeilenende': 'Append at Line End',
+ 'Neue Zeile darunter': 'New Line Below',
+ 'Neue Zeile darüber': 'New Line Above',
+ 'Normal Mode': 'Normal Mode',
+ 'Visual Mode': 'Visual Mode',
+ 'Visual Line Mode': 'Visual Line Mode',
+ 'Visual Block Mode': 'Visual Block Mode',
+
+ // Vim - Navigation
+ Links: 'Left',
+ Runter: 'Down',
+ Hoch: 'Up',
+ Rechts: 'Right',
+ 'Nächstes Wort': 'Next Word',
+ 'Vorheriges Wort': 'Previous Word',
+ Wortende: 'End of Word',
+ Zeilenanfang: 'Start of Line',
+ 'Erstes Zeichen': 'First Character',
+ Dateianfang: 'Start of File',
+ Dateiende: 'End of File',
+ 'Zu Zeile n': 'Go to Line n',
+ 'Passende Klammer': 'Matching Bracket',
+ 'Seite vor': 'Page Down',
+ 'Seite zurück': 'Page Up',
+
+ // Vim - Editing
+ 'Zeichen löschen': 'Delete Character',
+ 'Bis Zeilenende löschen': 'Delete to End of Line',
+ 'Zeile kopieren': 'Copy Line',
+ 'Wort kopieren': 'Copy Word',
+ 'Nach Cursor einfügen': 'Paste After Cursor',
+ 'Vor Cursor einfügen': 'Paste Before Cursor',
+ 'Letzten Befehl wiederholen': 'Repeat Last Command',
+ 'Zeichen ersetzen': 'Replace Character',
+ 'Replace Mode': 'Replace Mode',
+ 'Wort ändern': 'Change Word',
+ 'Zeile ändern': 'Change Line',
+ Einrücken: 'Indent',
+ Ausrücken: 'Outdent',
+
+ // Vim - Search
+ 'Vorwärts suchen': 'Search Forward',
+ 'Rückwärts suchen (Vim)': 'Search Backward',
+ 'Nächstes Ergebnis': 'Next Result',
+ 'Vorheriges Ergebnis': 'Previous Result',
+ 'Wort unter Cursor suchen': 'Search Word Under Cursor',
+ 'In Zeile ersetzen': 'Replace in Line',
+ 'Global ersetzen': 'Replace Globally',
+
+ // Vim - Files
+ Beenden: 'Quit',
+ 'Speichern und beenden': 'Save and Quit',
+ 'Ohne speichern beenden': 'Quit Without Saving',
+
+ // Xcode
+ 'Quick Open': 'Quick Open',
+ 'Im Navigator zeigen': 'Reveal in Navigator',
+ Einstellungen: 'Preferences',
+ 'Navigator ein/aus': 'Toggle Navigator',
+ 'Utilities ein/aus': 'Toggle Utilities',
+ 'Debug-Bereich ein/aus': 'Toggle Debug Area',
+ Build: 'Build',
+ Run: 'Run',
+ Stop: 'Stop',
+ 'Tests ausführen': 'Run Tests',
+ 'Clean Build Folder': 'Clean Build Folder',
+ Analyze: 'Analyze',
+ 'Run ohne Build': 'Run Without Building',
+ 'Project Navigator': 'Project Navigator',
+ 'Source Control Navigator': 'Source Control Navigator',
+ 'Symbol Navigator': 'Symbol Navigator',
+ 'Find Navigator': 'Find Navigator',
+ 'Issue Navigator': 'Issue Navigator',
+ 'Test Navigator': 'Test Navigator',
+ 'Debug Navigator': 'Debug Navigator',
+ 'Breakpoint Navigator': 'Breakpoint Navigator',
+ 'Report Navigator': 'Report Navigator',
+ 'Zur Definition': 'Go to Definition',
+ 'Kommentar umschalten': 'Toggle Comment',
+ 'Einrücken links': 'Indent Left',
+ 'Einrücken rechts': 'Indent Right',
+ 'Code falten': 'Fold Code',
+ 'Code auffalten': 'Unfold Code',
+ 'Breakpoints aktivieren/deaktivieren': 'Enable/Disable Breakpoints',
+ Continue: 'Continue',
+ 'Assistant Editor': 'Assistant Editor',
+ 'Standard Editor': 'Standard Editor',
+ 'Size to Fit': 'Size to Fit',
+ 'File Inspector': 'File Inspector',
+ 'Attributes Inspector': 'Attributes Inspector',
+ 'Size Inspector': 'Size Inspector',
+ 'Library öffnen': 'Open Library',
+
+ // Terminal Commands - additional descriptions would go here
+ // Git Commands, Docker Commands, NPM Commands, Homebrew Commands
+ // (These are already largely technical terms that don't need translation)
+};
+
+/**
+ * Collection name translations
+ */
+export const COLLECTION_TRANSLATIONS: Record = {
+ vscode: {
+ name: 'VS Code',
+ description: 'Visual Studio Code keyboard shortcuts for macOS',
+ },
+ macos: {
+ name: 'macOS',
+ description: 'macOS system keyboard shortcuts',
+ },
+ intellij: {
+ name: 'IntelliJ IDEA',
+ description: 'IntelliJ IDEA / JetBrains IDE keyboard shortcuts',
+ },
+ devtools: {
+ name: 'Chrome DevTools',
+ description: 'Chrome Developer Tools keyboard shortcuts',
+ },
+ vim: {
+ name: 'Vim',
+ description: 'Vim editor basic commands',
+ },
+ xcode: {
+ name: 'Xcode',
+ description: 'Xcode IDE keyboard shortcuts for iOS/macOS development',
+ },
+ terminal: {
+ name: 'Terminal',
+ description: 'Shell commands for macOS Terminal (zsh/bash)',
+ },
+ git: {
+ name: 'Git',
+ description: 'Git version control commands',
+ },
+ docker: {
+ name: 'Docker',
+ description: 'Docker container commands',
+ },
+ npm: {
+ name: 'NPM / Yarn',
+ description: 'Node Package Manager commands',
+ },
+ homebrew: {
+ name: 'Homebrew',
+ description: 'macOS package manager',
+ },
+};
+
+/**
+ * Get translated description based on language
+ */
+export function getTranslatedDescription(description: string, language: 'en' | 'de'): string {
+ if (language === 'de') {
+ return description;
+ }
+ return DESCRIPTION_TRANSLATIONS[description] || description;
+}
+
+/**
+ * Get translated category based on language
+ */
+export function getTranslatedCategory(category: string, language: 'en' | 'de'): string {
+ if (language === 'de') {
+ return category;
+ }
+ return CATEGORY_TRANSLATIONS[category] || category;
+}
+
+/**
+ * Get translated collection info based on language
+ */
+export function getTranslatedCollectionInfo(
+ collectionId: string,
+ language: 'en' | 'de'
+): { name: string; description: string } | undefined {
+ if (language === 'de') {
+ const collection =
+ ALL_SHORTCUT_COLLECTIONS.find(c => c.id === collectionId) ||
+ ALL_COMMAND_COLLECTIONS.find(c => c.id === collectionId);
+ if (collection) {
+ return { name: collection.name, description: collection.description };
+ }
+ return undefined;
+ }
+ return COLLECTION_TRANSLATIONS[collectionId];
+}
diff --git a/src/main.ts b/src/main.ts
index 1a89159..346c8d6 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -152,7 +152,7 @@ function shutdownServer(): void {
color: var(--text-primary);
font-family: var(--font-sans);
">
- Auf Wiedersehen! 👋
+ Auf Wiedersehen!
KeyboardWriter wird beendet...
diff --git a/src/pages/AlgorithmTrainingPage.ts b/src/pages/AlgorithmTrainingPage.ts
index 9dd85b5..975c023 100644
--- a/src/pages/AlgorithmTrainingPage.ts
+++ b/src/pages/AlgorithmTrainingPage.ts
@@ -1,916 +1,1068 @@
/**
* Algorithm Training Page
- * Specialized typing practice for algorithms and data structures
+ * Beautiful step-by-step algorithm visualizations
+ * Includes sorting, searching, graph, and tree algorithms
*/
-import { VirtualKeyboard } from '../components/keyboard/VirtualKeyboard';
-import { EventBus, Store } from '../core';
-import { ALGORITHM_CATEGORIES } from '../data/algorithmExercises';
-import { FRAMEWORK_CATEGORIES } from '../data/frameworkExercises';
-import { CodeSnippet } from '../data/programmingExercises';
-
-type CategoryType = 'algorithms' | 'frameworks';
-type Difficulty = 'beginner' | 'intermediate' | 'advanced';
-
-interface Category {
- id: string;
+import { AlgorithmVisualizer } from '../components/visualization/AlgorithmVisualizer';
+import { GraphVisualizer } from '../components/visualization/GraphVisualizer';
+
+type AlgorithmType =
+ | 'bubble-sort'
+ | 'quick-sort'
+ | 'insertion-sort'
+ | 'selection-sort'
+ | 'merge-sort'
+ | 'heap-sort'
+ | 'binary-search'
+ | 'linear-search'
+ | 'dijkstra'
+ | 'a-star'
+ | 'bellman-ford'
+ | 'bfs'
+ | 'dfs'
+ | 'topological-sort'
+ | 'bst-search'
+ | 'tree-inorder'
+ | 'tree-preorder'
+ | 'tree-postorder';
+
+type CategoryType = 'sorting' | 'search' | 'graph' | 'tree';
+
+interface AlgorithmInfo {
+ id: AlgorithmType;
name: string;
- icon: string;
- snippets: CodeSnippet[];
+ description: string;
+ complexity: string;
+ category: CategoryType;
+ realWorld?: string;
}
+const ALGORITHMS: AlgorithmInfo[] = [
+ // Sorting Algorithms
+ {
+ id: 'bubble-sort',
+ name: 'Bubble Sort',
+ description: 'Vergleicht benachbarte Elemente und tauscht sie wenn nötig',
+ complexity: 'O(n²)',
+ category: 'sorting',
+ realWorld: 'Lehrzwecke, kleine Datensätze',
+ },
+ {
+ id: 'selection-sort',
+ name: 'Selection Sort',
+ description: 'Findet das Minimum und setzt es an die richtige Position',
+ complexity: 'O(n²)',
+ category: 'sorting',
+ realWorld: 'Speicherplatz-kritische Anwendungen',
+ },
+ {
+ id: 'insertion-sort',
+ name: 'Insertion Sort',
+ description: 'Fügt jedes Element an der richtigen Stelle ein - gut für fast sortierte Daten',
+ complexity: 'O(n²)',
+ category: 'sorting',
+ realWorld: 'Echtzeit-Datenstromsortierung, kleine Arrays',
+ },
+ {
+ id: 'quick-sort',
+ name: 'Quick Sort',
+ description: 'Divide & Conquer mit Pivot - Standard in vielen Sprachen',
+ complexity: 'O(n log n)',
+ category: 'sorting',
+ realWorld: 'Arrays.sort() in Java, qsort() in C',
+ },
+ {
+ id: 'merge-sort',
+ name: 'Merge Sort',
+ description: 'Stabiler Sortieralgorithmus - teilt und verschmilzt',
+ complexity: 'O(n log n)',
+ category: 'sorting',
+ realWorld: 'Externe Sortierung, Linked Lists, Git Merge',
+ },
+ {
+ id: 'heap-sort',
+ name: 'Heap Sort',
+ description: 'Nutzt Heap-Datenstruktur - garantiert O(n log n)',
+ complexity: 'O(n log n)',
+ category: 'sorting',
+ realWorld: 'Priority Queues, Scheduling-Algorithmen',
+ },
+ // Search Algorithms
+ {
+ id: 'binary-search',
+ name: 'Binary Search',
+ description: 'Sucht effizient in sortierten Arrays durch Halbierung',
+ complexity: 'O(log n)',
+ category: 'search',
+ realWorld: 'Datenbank-Indizes, Git bisect, Telefonbuch',
+ },
+ {
+ id: 'linear-search',
+ name: 'Linear Search',
+ description: 'Durchsucht jedes Element sequentiell',
+ complexity: 'O(n)',
+ category: 'search',
+ realWorld: 'Unsortierte Daten, kleine Listen',
+ },
+ // Graph Algorithms
+ {
+ id: 'dijkstra',
+ name: 'Dijkstra',
+ description: 'Findet kürzeste Pfade in gewichteten Graphen',
+ complexity: 'O(V² / E log V)',
+ category: 'graph',
+ realWorld: 'Google Maps, GPS Navigation, Netzwerk-Routing',
+ },
+ {
+ id: 'a-star',
+ name: 'A* (A-Star)',
+ description: 'Heuristik-basiertes Pathfinding - schneller als Dijkstra',
+ complexity: 'O(E)',
+ category: 'graph',
+ realWorld: 'Google Maps, Videospiele (Starcraft, AoE), Robotik',
+ },
+ {
+ id: 'bellman-ford',
+ name: 'Bellman-Ford',
+ description: 'Kürzeste Pfade auch mit negativen Gewichten',
+ complexity: 'O(V × E)',
+ category: 'graph',
+ realWorld: 'RIP-Protokoll, Währungsarbitrage, Netzwerk-Routing',
+ },
+ {
+ id: 'bfs',
+ name: 'BFS (Breitensuche)',
+ description: 'Durchsucht Graph ebenenweise - findet kürzesten Pfad',
+ complexity: 'O(V + E)',
+ category: 'graph',
+ realWorld: 'Social Networks, Web Crawler, Schachcomputer',
+ },
+ {
+ id: 'dfs',
+ name: 'DFS (Tiefensuche)',
+ description: 'Durchsucht Graph in die Tiefe - gut für Zykluserkennung',
+ complexity: 'O(V + E)',
+ category: 'graph',
+ realWorld: 'Labyrinth-Lösung, Topologische Sortierung, Compiler',
+ },
+ {
+ id: 'topological-sort',
+ name: 'Topologische Sortierung',
+ description: 'Ordnet DAG-Knoten nach Abhängigkeiten',
+ complexity: 'O(V + E)',
+ category: 'graph',
+ realWorld: 'npm/yarn, Build-Systeme (Make, Gradle), Task-Scheduling',
+ },
+ // Tree Algorithms
+ {
+ id: 'bst-search',
+ name: 'BST Suche',
+ description: 'Suche im Binary Search Tree - links kleiner, rechts größer',
+ complexity: 'O(log n)',
+ category: 'tree',
+ realWorld: 'Datenbanken, Dateisysteme, Auto-Complete',
+ },
+ {
+ id: 'tree-inorder',
+ name: 'Inorder Traversierung',
+ description: 'Links → Wurzel → Rechts - gibt sortierte Reihenfolge',
+ complexity: 'O(n)',
+ category: 'tree',
+ realWorld: 'Sortierte Ausgabe von BST, Expression Trees',
+ },
+ {
+ id: 'tree-preorder',
+ name: 'Preorder Traversierung',
+ description: 'Wurzel → Links → Rechts - für Kopieren/Serialisierung',
+ complexity: 'O(n)',
+ category: 'tree',
+ realWorld: 'Tree-Kopie, Prefix-Notation, JSON-Serialisierung',
+ },
+ {
+ id: 'tree-postorder',
+ name: 'Postorder Traversierung',
+ description: 'Links → Rechts → Wurzel - für Löschen/Auswertung',
+ complexity: 'O(n)',
+ category: 'tree',
+ realWorld: 'Tree löschen, Postfix-Notation, du -sh (Verzeichnisgröße)',
+ },
+];
+
+const CATEGORY_INFO: Record = {
+ sorting: { title: 'Sortierung', icon: '' },
+ search: { title: 'Suche', icon: '' },
+ graph: { title: 'Graphen', icon: '' },
+ tree: { title: 'Bäume', icon: '' },
+};
+
/**
- * Algorithm Training Page Controller
+ * Algorithm Training Page
*/
export class AlgorithmTrainingPage {
- private keyboard: VirtualKeyboard | null = null;
- private currentCategoryType: CategoryType = 'algorithms';
- private currentCategoryIndex: number = 0;
- private currentSnippetIndex: number = 0;
- private currentDifficulty: Difficulty | 'all' = 'all';
- private currentInput: string = '';
- private isTyping: boolean = false;
- private startTime: number = 0;
- private errors: number = 0;
- private readonly boundKeydownHandler: ((e: KeyboardEvent) => void) | null = null;
+ private arrayVisualizer: AlgorithmVisualizer | null = null;
+ private graphVisualizer: GraphVisualizer | null = null;
+ private currentAlgorithm: AlgorithmType = 'bubble-sort';
+ private arraySize: number = 8;
+ private searchTarget: number = 0;
+ private bstSearchTarget: number = 40;
+ private currentArray: number[] = [];
constructor() {
- this.boundKeydownHandler = this.handleKeyboardInput.bind(this);
+ this.generateNewArray();
}
/**
- * Get current categories based on type
+ * Generate random array
*/
- private getCategories(): Category[] {
- return this.currentCategoryType === 'algorithms' ? ALGORITHM_CATEGORIES : FRAMEWORK_CATEGORIES;
+ private generateNewArray(): void {
+ this.currentArray = Array.from(
+ { length: this.arraySize },
+ () => Math.floor(Math.random() * 50) + 5
+ );
+ this.searchTarget = this.currentArray[Math.floor(Math.random() * this.currentArray.length)];
}
/**
- * Get current category
+ * Get current algorithm info
*/
- private getCurrentCategory(): Category | undefined {
- const categories = this.getCategories();
- return categories[this.currentCategoryIndex];
- }
-
- /**
- * Get filtered snippets
- */
- private getFilteredSnippets(): CodeSnippet[] {
- const category = this.getCurrentCategory();
- if (!category) {
- return [];
- }
-
- if (this.currentDifficulty === 'all') {
- return category.snippets;
- }
-
- return category.snippets.filter(s => s.difficulty === this.currentDifficulty);
- }
-
- /**
- * Get current snippet
- */
- private getCurrentSnippet(): CodeSnippet | undefined {
- const snippets = this.getFilteredSnippets();
- return snippets[this.currentSnippetIndex];
+ private getCurrentAlgorithmInfo(): AlgorithmInfo | undefined {
+ return ALGORITHMS.find(a => a.id === this.currentAlgorithm);
}
/**
* Render the page
*/
render(): string {
+ const currentAlgo = this.getCurrentAlgorithmInfo();
+
return `
-
- ${this.renderHeader()}
- ${this.renderTypeSelector()}
- ${this.renderCategorySelector()}
- ${this.renderDifficultyFilter()}
- ${this.renderCodeArea()}
- ${this.renderKeyboard()}
- ${this.renderStats()}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${this.renderControls()}
+
+
+
+
- `;
- }
-
- /**
- * Render header
- */
- private renderHeader(): string {
- return `
-
- `;
- }
-
- /**
- * Render type selector
- */
- private renderTypeSelector(): string {
- return `
-
-
-
-
- `;
- }
- /**
- * Render category selector
- */
- private renderCategorySelector(): string {
- const categories = this.getCategories();
+ .algo-vis-speed {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ justify-content: center;
+ font-size: var(--font-size-sm);
+ color: var(--text-secondary);
+ }
- return `
-
- ${categories
- .map(
- (cat, index) => `
-
- ${cat.icon}
- ${cat.name}
- ${cat.snippets.length} Übungen
-
- `
- )
- .join('')}
-
- `;
- }
+ .algo-vis-speed input[type="range"] {
+ width: 150px;
+ cursor: pointer;
+ }
- /**
- * Render difficulty filter
- */
- private renderDifficultyFilter(): string {
- const difficulties: { id: Difficulty | 'all'; label: string }[] = [
- { id: 'all', label: 'Alle' },
- { id: 'beginner', label: '🟢 Anfänger' },
- { id: 'intermediate', label: '🟡 Mittel' },
- { id: 'advanced', label: '🔴 Fortgeschritten' },
- ];
+ .algo-vis-speed span {
+ font-family: var(--font-mono);
+ min-width: 60px;
+ }
- return `
-
- ${difficulties
- .map(
- d => `
-
- `
- )
- .join('')}
-
+ @media (max-width: 900px) {
+ .algo-layout {
+ grid-template-columns: 1fr;
+ }
+
+ .algo-sidebar {
+ position: static;
+ max-height: none;
+ }
+
+ .algo-list {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
+ }
+
+ .algo-controls-card {
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .algo-current-array {
+ margin-left: 0;
+ justify-content: center;
+ }
+ }
+
`;
}
/**
- * Render code area
+ * Render algorithm-specific controls
*/
- private renderCodeArea(): string {
- const snippet = this.getCurrentSnippet();
- const snippets = this.getFilteredSnippets();
+ private renderControls(): string {
+ const algo = this.getCurrentAlgorithmInfo();
- if (!snippet) {
+ if (algo?.category === 'sorting') {
return `
-
-
-
Keine Übungen in dieser Kategorie gefunden.
-
Wähle eine andere Kategorie oder entferne den Schwierigkeitsfilter.
+
+
+
+
+ ${this.arraySize}
+
+
+
+ Array:
+ [${this.currentArray.join(', ')}]
`;
}
- const targetChars = snippet.code.split('');
- const inputChars = this.currentInput.split('');
-
- return `
-
-