From cebf277a5dace1171412451c9fe0805f838e4a03 Mon Sep 17 00:00:00 2001 From: JonKazama-Hellion Date: Thu, 16 Apr 2026 21:22:07 +0200 Subject: [PATCH] docs(spec): Calculator Upgrade v2.1.0 Design-Spec 6 Modi: Standard, Scientific, Unit-Converter, Satisfactory, Factorio, Stationeers. Tab-basierte Architektur, Registrierungs-Pattern, erweiteter Shunting-Yard Parser. Co-Authored-By: Claude Opus 4.6 --- .gitignore | 3 + .../2026-04-16-calculator-upgrade-design.md | 581 ++++++++++++++++++ 2 files changed, 584 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-16-calculator-upgrade-design.md diff --git a/.gitignore b/.gitignore index 90ae61e..7fcac2f 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,6 @@ updates.json # Persönliche Backup-Dateien (nicht ins Repo) favorites_*.html *_backup*.json +.mcp.json +.claude +.superpowers/ \ No newline at end of file diff --git a/docs/superpowers/specs/2026-04-16-calculator-upgrade-design.md b/docs/superpowers/specs/2026-04-16-calculator-upgrade-design.md new file mode 100644 index 0000000..b0841f6 --- /dev/null +++ b/docs/superpowers/specs/2026-04-16-calculator-upgrade-design.md @@ -0,0 +1,581 @@ +# Hellion NewTab — Calculator Upgrade Design + +**Datum:** 2026-04-16 +**Autor:** Florian Wathling / Claude Code +**Status:** Approved +**Scope:** Calculator erweitern um Scientific, Unit-Converter und Game-Rechner (Satisfactory, Factorio, Stationeers) +**Ziel-Version:** v2.1.0 + +--- + +## Kontext + +Der Calculator ist aktuell ein reiner Grundrechenarten-Taschenrechner (720 Zeilen, Shunting-Yard Parser, 4x5 Button-Grid, History). Das Upgrade macht ihn zum zentralen Tool-Widget mit 6 Modi: + +1. **Standard** (bestehend) +2. **Scientific** (Wurzel, Potenz, Pi, Formel-Helfer) +3. **Unit-Converter** (Länge, Gewicht, Temperatur, Volumen, Geschwindigkeit, Fläche) +4. **Satisfactory** (Items/Min, Overclock-Power, Maschinen-Rechner) +5. **Factorio** (Assembler-Ratios, Belt-Throughput, Maschinen-Rechner) +6. **Stationeers** (Idealgas, Furnace/Verbrennung, Solar/Batterie, Atmosphäre) + +--- + +## Sektion 1: Architektur und Dateistruktur + +### Datei-Aufteilung + +``` +src/js/ +├── calculator.js # Core: Tab-System, Standard-Modus, erweiterter Shunting-Yard Parser +├── calc-scientific.js # Scientific-Modus +├── calc-converter.js # Unit-Converter +├── calc-satisfactory.js # Satisfactory Calculator +├── calc-factorio.js # Factorio Calculator +└── calc-stationeers.js # Stationeers Calculator +``` + +### Load-Order in newtab.html + +``` +... → widgets.js → notes.js → calculator.js → calc-scientific.js → calc-converter.js → +calc-satisfactory.js → calc-factorio.js → calc-stationeers.js → timer.js → ... +``` + +Alle Mode-Dateien laden nach `calculator.js` und vor `timer.js`. Kein zirkulärer Dependency-Konflikt. + +### Registrierungs-Pattern + +Jede Mode-Datei registriert sich beim Calculator-Objekt: + +```javascript +Calculator.registerMode('scientific', { + label: '\uD83D\uDCD0', // Icon + shortName: 'Sci', // Tab-Label (3 Zeichen) + titleKey: 'calculator.tab.scientific', // i18n-Key + render(bodyEl) { /* UI aufbauen */ }, + destroy() { /* Cleanup, Event-Listener entfernen */ } +}); +``` + +`calculator.js` bekommt: + +```javascript +_modes: new Map(), +_activeMode: 'standard', + +registerMode(name, config) { + this._modes.set(name, config); +}, +``` + +Die Tab-Leiste wird dynamisch aus `_modes` gebaut. Standard-Modus ist immer registriert (intern, nicht per externer Datei). Die anderen Modi kommen dazu wenn ihre Script-Datei geladen ist. + +### Tab-Wechsel + +```javascript +switchMode(name) { + const mode = this._modes.get(name); + if (!mode) return; + this._activeMode = name; + const body = WidgetManager.getBody(this.WIDGET_ID); + if (!body) return; + + // Alten Modus aufräumen + const oldMode = this._modes.get(this._previousMode); + if (oldMode && oldMode.destroy) oldMode.destroy(); + + // Neuen Modus rendern + body.textContent = ''; + mode.render(body); + + // Tab-UI aktualisieren + this._updateTabBar(); + + // State speichern + this.save(); +} +``` + +### Storage + +Jeder Modus speichert seinen State als Sub-Key unter `calculator` im bestehenden `widgetStates`: + +```javascript +{ + calculator: { + x: 400, y: 120, width: 320, height: 480, + open: true, + activeMode: 'standard', + history: [{ expr: '42 × 7', result: '294' }], + converter: { lastCategory: 'length', fromUnit: 'cm', toUnit: 'in' }, + satisfactory: { lastSubMode: 'itemsPerMin' }, + factorio: { lastSubMode: 'ratio', lastAssembler: 'asm3' }, + stationeers: { lastSubMode: 'gas' } + } +} +``` + +Read-before-write Pattern bleibt: `const data = await Store.get(this.STORAGE_KEY) || {};` + +--- + +## Sektion 2: Standard-Modus (Änderungen) + +### Parser-Erweiterung + +Der Shunting-Yard Parser wird um zwei Operationen erweitert: + +**Potenz-Operator `^`:** +- Binärer Operator mit höchster Precedence (über `*` und `/`) +- Rechts-assoziativ: `2^3^2` = `2^(3^2)` = 512 +- Tokenizer erkennt `^` als `{ type: 'op', value: '^' }` +- parseFactor() → parsePower() → parseFactor() (neue Precedence-Stufe) + +**Wurzel-Funktion `sqrt`:** +- Wird vom Scientific-Modus als `sqrt(` in die Expression eingefügt +- Tokenizer erkennt `sqrt` als `{ type: 'func', value: 'sqrt' }` +- parseFactor() prüft auf Functions vor Numbers + +Die bestehende Operator-Hierarchie wird: +``` +parseExpr: + - +parseTerm: * / % +parsePower: ^ ← NEU +parseFactor: number | (expr) | func(expr) ← func NEU +``` + +### Keine Änderungen am Standard-UI + +Das 4x5 Button-Grid, History-Panel und Keyboard-Support bleiben identisch. Die Parser-Erweiterung ist rückwärtskompatibel (keine bestehende Expression bricht). + +--- + +## Sektion 3: Scientific-Modus + +### Zusätzliche Buttons + +2 neue Reihen über dem Standard-Grid: + +| Button | Wert | Aktion | +|---|---|---| +| √ | `sqrt(` | Unäre Funktion, öffnet Klammer | +| x² | `^2` | Hängt `^2` an Expression | +| xⁿ | `^` | Fügt Potenz-Operator ein | +| π | `3.14159265359` | Konstante einfügen | +| e | `2.71828182846` | Konstante einfügen | +| ± | toggle | Vorzeichen des letzten Werts wechseln | + +Darunter das Standard 4x5-Grid (C, Klammern, %, ÷, 0-9, Operatoren, =). Der Scientific-Modus nutzt den gleichen `_handleKey()`/`_calculate()`-Flow. + +### Formel-Helfer + +Ein Dropdown unter dem Button-Grid mit vorgefertigten Formeln: + +| Formel | Eingabefelder | Berechnung | +|---|---|---| +| Kreis-Fläche | Radius (r) | `π × r²` | +| Kreis-Umfang | Radius (r) | `2 × π × r` | +| °C → °F | Temperatur | `(C × 9/5) + 32` | +| °F → °C | Temperatur | `(F - 32) × 5/9` | +| Pythagoras | a, b | `√(a² + b²)` | +| Prozent-Wert | Wert, Prozent | `Wert × Prozent / 100` | + +Jede Formel öffnet inline Eingabefelder + Live-Ergebnis. Nutzt `_formatResult()` für einheitliche Zahlenformatierung. + +### Keyboard + +Gleicher Keyboard-Support wie Standard-Modus, plus: +- `p` → Pi einfügen +- `e` → Euler einfügen (kein Konflikt: `e` ist im Standard nicht belegt, nur `c`/`C` und `Escape` sind Clear) + +--- + +## Sektion 4: Unit-Converter + +### UI-Aufbau + +``` +┌──────────────────────────┐ +│ [Kategorie-Dropdown ▼]│ +│ │ +│ [123.45 ] [cm ▼] │ +│ ⇅ (Swap-Button) │ +│ [48.622 ] [in ▼] │ +│ │ +│ Schnellreferenz: │ +│ 1 cm = 0.3937 in │ +│ 1 in = 2.54 cm │ +└──────────────────────────┘ +``` + +### Kategorien und Einheiten + +| Kategorie | Einheiten | Basis-Einheit | +|---|---|---| +| Länge | mm, cm, m, km, in, ft, yd, mi | m | +| Gewicht | mg, g, kg, t, oz, lb | g | +| Temperatur | °C, °F, K | (Spezialfunktionen) | +| Volumen | ml, L, m³, gal(US), gal(UK), ft³ | ml | +| Geschwindigkeit | m/s, km/h, mph, kn | m/s | +| Fläche | mm², cm², m², km², ha, acre, ft², in² | m² | + +### Konvertierungs-Logik + +Jede Einheit hat `toBase(value)` und `fromBase(value)`: + +```javascript +const LENGTH_UNITS = { + mm: { toBase: v => v / 1000, fromBase: v => v * 1000 }, + cm: { toBase: v => v / 100, fromBase: v => v * 100 }, + m: { toBase: v => v, fromBase: v => v }, + km: { toBase: v => v * 1000, fromBase: v => v / 1000 }, + in: { toBase: v => v * 0.0254, fromBase: v => v / 0.0254 }, + ft: { toBase: v => v * 0.3048, fromBase: v => v / 0.3048 }, + yd: { toBase: v => v * 0.9144, fromBase: v => v / 0.9144 }, + mi: { toBase: v => v * 1609.344, fromBase: v => v / 1609.344 } +}; +``` + +Temperatur bekommt eigene Funktionen (nicht linear): + +```javascript +const TEMP_CONVERSIONS = { + 'C_F': v => (v * 9/5) + 32, + 'C_K': v => v + 273.15, + 'F_C': v => (v - 32) * 5/9, + 'F_K': v => (v - 32) * 5/9 + 273.15, + 'K_C': v => v - 273.15, + 'K_F': v => (v - 273.15) * 9/5 + 32 +}; +``` + +### Verhalten + +- Live-Update bei Eingabe (kein "Berechnen"-Button) +- Swap-Button (⇅) tauscht Quell- und Ziel-Einheit +- Schnellreferenz zeigt `1 [from] = x [to]` und umgekehrt +- Kein Keyboard-Override (native `` Felder) + +### Storage + +```javascript +converter: { lastCategory: 'length', fromUnit: 'cm', toUnit: 'in' } +``` + +--- + +## Sektion 5: Satisfactory Calculator + +### Sub-Modi + +Drei Buttons oben wählen den aktiven Rechner: + +#### 5a: Items/Min + +**Eingabefelder:** +- Items per Craft (default: 1) +- Craft Time in Sekunden (default: 4) +- Clock Speed in % (default: 100) + +**Formel:** +``` +Output = (ItemsPerCraft × 60) / CraftTime × (ClockSpeed / 100) +``` + +**Ausgabe:** `X.XX items/min` + +#### 5b: Overclock Power + +**Eingabefelder:** +- Base Power in MW (default: 30) +- Clock Speed in % (default: 100) + +**Formeln:** +``` +PowerUsage = BasePower × (ClockSpeed / 100) ^ 1.321928 +EnergyPerItem = (ClockSpeed / 100) ^ 0.321928 +``` + +**Ausgabe:** +- `Power Usage: X.X MW` +- `Efficiency: ↓ X.X% per item` (nur bei ClockSpeed > 100) + +#### 5c: Maschinen + +**Eingabefelder:** +- Target Output/Min (default: 60) +- Items per Craft (default: 1) +- Craft Time in Sekunden (default: 4) +- Clock Speed in % (default: 100) +- Base Power in MW (default: 30) + +**Formeln:** +``` +ItemsPerMin = (ItemsPerCraft × 60) / CraftTime × (ClockSpeed / 100) +Machines = ceil(TargetOutput / ItemsPerMin) +TotalPower = Machines × BasePower × (ClockSpeed / 100) ^ 1.321928 +``` + +**Ausgabe:** +- `Machines needed: X` +- `Total Power: X.X MW` + +### Verhalten + +Alle Felder berechnen live. `` mit `step`-Attribut für sinnvolle Schrittweiten. + +--- + +## Sektion 6: Factorio Calculator + +### Sub-Modi + +#### 6a: Assembler-Ratio + +**Eingabefelder:** +- Assembler-Dropdown: Assembler 1 (0.5), Assembler 2 (0.75), Assembler 3 (1.25) +- Recipe Output Count (default: 1) +- Recipe Time in Sekunden (default: 1) + +**Formel:** +``` +OutputPerSecond = RecipeOutput × CraftingSpeed / RecipeTime +OutputPerMinute = OutputPerSecond × 60 +``` + +**Ausgabe:** +- `X.XX items/s` +- `X.XX items/min` + +#### 6b: Belt-Throughput + +**Eingabefelder:** +- Belt-Dropdown: Yellow (15/s), Red (30/s), Blue (45/s) +- Items consumed per second per machine (default: 1) + +**Feste Werte:** + +| Belt | Total (items/s) | Per Side (items/s) | +|---|---|---| +| Yellow | 15 | 7.5 | +| Red | 30 | 15 | +| Blue | 45 | 22.5 | + +**Formel:** +``` +MachinesPerBelt = floor(BeltThroughput / ItemsConsumedPerSec) +Utilization = (ItemsConsumedPerSec × MachinesPerBelt) / BeltThroughput × 100 +``` + +**Ausgabe:** +- `Machines per belt: X` +- `Belt utilization: X%` + +#### 6c: Maschinen + +**Eingabefelder:** +- Assembler-Dropdown +- Target Output/s (default: 10) +- Recipe Output Count (default: 1) +- Recipe Time in Sekunden (default: 1) + +**Formel:** +``` +OutputPerMachine = RecipeOutput × CraftingSpeed / RecipeTime +Machines = ceil(TargetOutput / OutputPerMachine) +TotalThroughput = Machines × OutputPerMachine +BeltNeeded = kleinster Belt der TotalThroughput schafft +``` + +**Ausgabe:** +- `Machines needed: X` +- `Belt needed: [Color] (X% utilization)` + +--- + +## Sektion 7: Stationeers Calculator + +### Sub-Modi + +Vier Buttons oben (statt drei wie bei den anderen Game-Rechnern). + +#### 7a: Gas (Idealgas PV=nRT) + +**Eingabefelder:** +- Dropdown: Gesucht = P, V, n oder T +- Die drei anderen Variablen als Eingabefelder + +**Konstante:** R = 8314.46261815324 (Stationeers-spezifisch, Einheit: L·Pa / mol·K) + +**Formeln:** +``` +P = nRT / V +V = nRT / P +n = PV / RT +T = PV / nR +``` + +**Eingabe-Einheiten:** +- P in kPa (wird intern × 1000 zu Pa) +- V in Litern +- T in Kelvin (Hilfstext zeigt °C-Äquivalent) +- n in mol + +#### 7b: Furnace / Verbrennung + +**Eingabefelder:** +- Fuel Ratio (0 bis 1, Anteil Brennstoff am Gesamtgas) +- Start-Temperatur in Kelvin +- Start-Druck in kPa + +**Formeln:** +``` +T_nach = (T_vor × specificHeat + fuel × 563452) / (specificHeat + fuel × 172.615) +P_nach = P_vor × T_nach × (1 + 5.7 × fuel) / T_vor +``` + +Wobei: +- `fuel = min(ratioO2, ratioVolatile / 2)` +- `specificHeat` = gewichtete Summe der Gas-Wärmekapazitäten +- Vereinfachung: Fuel Ratio als einzelner Wert (0-1), `specificHeat(before)` wird aus reinem Fuel berechnet (61.9 J/mol·K für 1:2 O₂:H₂ Mischung) +- 563452 = Energie pro Mol bei 95% Effizienz +- 172.615 = 0.95 × (243.6 - 61.9) + +**Validierung:** +- Warnung wenn Fuel < 0.05 (unter 5% Minimum) +- Warnung wenn Start-Druck < 10 kPa + +**Ausgabe:** +- `T after ignition: X K (X °C)` +- `P after ignition: X kPa` + +#### 7c: Solar / Batterie + +**Eingabefelder:** +- Anzahl Panels (default: 12) +- Watt pro Panel (default: 500, Mond-Wert) +- Tag-Länge in Sekunden (default: 600) +- Nacht-Länge in Sekunden (default: 600) +- Verbrauch in Watt (default: 2000) + +**Formeln:** +``` +Generation = Panels × WattsPerPanel +Surplus = Generation - Consumption +NightEnergy = Consumption × NightLength (in Watt-Sekunden) +BatteriesNeeded = ceil(NightEnergy / 50000) (Station Battery = 50.000 Ws) +``` + +**Ausgabe:** +- `Generation: X W` +- `Surplus: X W` (rot wenn negativ) +- `Night Energy: X Ws` +- `Batteries needed: X` + +#### 7d: Atmosphäre / Gas-Mischer + +**Eingabefelder:** +- Target Temperatur in Kelvin +- Gas 1 Temperatur in Kelvin +- Gas 2 Temperatur in Kelvin + +**Formel:** +``` +M1 = |T2 - T0| / (|T1 - T0| + |T2 - T0|) +M2 = 1 - M1 +``` + +**Ausgabe:** +- `Mixer Input 1: X.X%` +- `Mixer Input 2: X.X%` + +**Aufklappbare Wärmekapazität-Referenz:** + +| Gas | Cp (J/mol·K) | +|---|---| +| O₂ | 21.1 | +| H₂ | 20.4 | +| CO₂ | 28.2 | +| N₂ | 20.6 | +| H₂O | 72.0 | +| N₂O | 23.0 | +| Pollutant | 24.8 | + +--- + +## Sektion 8: UI, i18n und Widget-Sizing + +### Tab-Leiste + +Horizontale Leiste direkt unter dem Widget-Header. Immer sichtbar (kein Scrollen). + +| Tab | Icon | Label | +|---|---|---| +| Standard | 🔢 | Std | +| Scientific | 📐 | Sci | +| Converter | ⚖️ | Unit | +| Satisfactory | ⚙️ | SAT | +| Factorio | 🏭 | FAC | +| Stationeers | 🚀 | STA | + +Aktiver Tab: `border-bottom: 2px solid var(--accent)`, Text in `var(--accent)`. +Inaktive Tabs: `color: rgba(255,255,255,0.5)`. +CSS-Klasse: `.calc-tab-bar` und `.calc-tab`. + +### Widget-Sizing + +- Standard-Modus Minimum: 280 × 400 px +- Komplexe Modi (Scientific, Game-Rechner): Auto-Resize auf 320 × 480 px (falls aktuell kleiner) +- User-Resize überschreibt Auto-Resize +- Widget-System-Minimum bleibt 200 × 150 px + +### i18n + +Geschätzt ~100 neue Keys in `STRINGS.de` und `STRINGS.en`: + +- 6 Tab-Labels +- 6 Kategorie-Namen (Converter) +- ~48 Einheiten-Langformen (Converter) +- ~30 Feld-Labels (Game-Rechner) +- ~10 Ergebnis-Labels + +Einheiten-Abkürzungen (cm, kg, °C, kPa) werden nicht übersetzt. + +### Keyboard + +- Standard-Modus: Bestehender Keyboard-Support (0-9, +, -, *, /, Enter, Backspace, Escape) +- Scientific-Modus: Gleicher Support + `p` (Pi), `^` (Potenz) +- Converter und Game-Modi: Kein Custom-Keyboard (native `` Felder) + +--- + +## Betroffene Dateien (Gesamt) + +| Datei | Änderung | +|---|---| +| `src/js/calculator.js` | Tab-System, registerMode(), switchMode(), Parser-Erweiterung (^, sqrt) | +| `src/js/calc-scientific.js` | NEU: Scientific-Modus | +| `src/js/calc-converter.js` | NEU: Unit-Converter | +| `src/js/calc-satisfactory.js` | NEU: Satisfactory Calculator | +| `src/js/calc-factorio.js` | NEU: Factorio Calculator | +| `src/js/calc-stationeers.js` | NEU: Stationeers Calculator | +| `src/css/main.css` | Tab-Bar Styles, Mode-spezifische Styles | +| `src/js/i18n.js` | ~100 neue Keys (DE + EN) | +| `newtab.html` | 5 neue `