diff --git a/core/patterns.py b/core/patterns.py index 45812e6..cda3be3 100644 --- a/core/patterns.py +++ b/core/patterns.py @@ -321,12 +321,16 @@ def _detect(piv, cur, atr, last_idx): # Verkleinert: 7,5 MB -> 379 KB fuer alle 15. # ⚠ Doppeltop und Doppelboden teilen sich ein Bild (die Vorlage zeigt beide), # ebenso Dreifach-Top/-Boden und Flagge bullisch/baerisch. -# ⚠ `inv_cup` hat KEIN eigenes Bild — lieber keins als ein falsches. +# ⚠ `inv_cup` hat seit dem 20.08. ein eigenes Bild (vorher bewusst keins). BILD = { "double_top": "double", "double_bottom": "double", "triple_top": "triple", "triple_bottom": "triple", "hs": "hs", "inv_hs": "inv_hs", "cup": "cup", + # ⚠ Am 2026-08-20 nachgereicht (User: "scanne neue Bilder"). Bis dahin + # stand hier ausdruecklich KEIN Eintrag — lieber kein Bild als ein + # falsches; die normale Tasse zu zeigen waere die falsche Richtung gewesen. + "inv_cup": "inv_cup", "flag": "flag", "pennant": "pennant", "wedge_rise": "wedge_rise", "wedge_fall": "wedge_fall", "tri_asc": "tri_asc", "tri_desc": "tri_desc", "tri_sym": "tri_sym", diff --git a/tools/check_nfalle.py b/tools/check_nfalle.py index bec3f91..0003949 100644 --- a/tools/check_nfalle.py +++ b/tools/check_nfalle.py @@ -478,7 +478,15 @@ def _stufe_ids(): js = re.sub(r"(?m)^\s*//.*$", "", js) html_ids = re.findall(r'id="([^"]+)"', html) doppelt = sorted({i for i in html_ids if html_ids.count(i) > 1}) - fehlt = sorted(set(re.findall(r'\$(?:streng)?\("([^"]+)"\)', js)) - set(html_ids)) + # ⚠⚠ ABSICHTLICH ausgeblendete Elemente. Der JS-Block bleibt stehen — + # er ist null-sicher (`$streng` + Waechter) — damit ein Zurueckholen eine + # reine HTML-Aenderung ist. Ohne diese Liste meldete die Stufe drei + # Dauer-Treffer, und eine Pruefung mit Dauer-Treffern wird ignoriert (die + # Lehre vom Linter). ⚠ Wer hier etwas eintraegt, MUSS den Zugriff im JS + # auf `$streng` + Waechter umstellen — sonst bricht der GESAMTE Render. + AUSGEBLENDET = {"hl-note", "pat-note", "trail-note"} # User-Wunsch 2026-08-20 + fehlt = sorted(set(re.findall(r'\$(?:streng)?\("([^"]+)"\)', js)) + - set(html_ids) - AUSGEBLENDET) for i in fehlt: befunde.append(f"ID verwaist: $(\"{i}\") ohne Element in index.html") print(f" FEHLER verwaiste ID: {i}") diff --git a/tools/render_check.mjs b/tools/render_check.mjs index 2655d52..4c78ccd 100644 --- a/tools/render_check.mjs +++ b/tools/render_check.mjs @@ -26,6 +26,14 @@ import vm from "node:vm"; const pfad = process.argv[2] || "tests/fixtures/snapshot.json"; const snap = JSON.parse(fs.readFileSync(pfad, "utf-8")); const src = fs.readFileSync("web/app.js", "utf-8"); +// Die real vorhandenen Element-IDs aus der ausgelieferten HTML. +const HTML_IDS = new Set( + [...fs.readFileSync("web/index.html", "utf-8").matchAll(/id="([^"]+)"/g)] + .map((m) => m[1])); +if (HTML_IDS.size < 50) { + console.log(` ❌ nur ${HTML_IDS.size} IDs aus index.html gelesen — Probe greift nicht`); + process.exit(1); +} function el() { const o = { @@ -44,7 +52,15 @@ function el() { const ctx = { console, document: { - getElementById: () => el(), querySelector: () => el(), + // ⚠⚠ NUR IDs, die es in `index.html` wirklich gibt (Fix 2026-08-20). + // Vorher lieferte die Attrappe fuer JEDE ID ein Element — damit konnte + // `$streng()` in der Probe NIE `null` zurueckgeben, und genau der Fall, + // fuer den es `$streng` gibt, war ungeprueft. Real aufgefallen beim + // Ausblenden von `#hl-note`: der Hauptzweig griff ungeschuetzt auf + // `hlEl.classList` zu, die Probe blieb gruen, im Browser haette es den + // GESAMTEN Render abgebrochen. + getElementById: (id) => (HTML_IDS.has(id) ? el() : null), + querySelector: () => el(), querySelectorAll: () => [], createElement: () => el(), addEventListener() {}, body: el(), documentElement: el(), activeElement: null, hidden: false, visibilityState: "visible", diff --git a/web/app.js b/web/app.js index aa8d01a..188a896 100644 --- a/web/app.js +++ b/web/app.js @@ -748,9 +748,19 @@ function renderKontext(snap) { trN.textContent = trTxt || ""; trN.classList.toggle("hidden", !trTxt); } - const hlEl = $("hl-note"); + // ⚠ `$streng`, nicht das Auffang-`$`: das Element ist am 2026-08-20 auf + // User-Wunsch aus der Meldungen-Karte ENTFERNT. Mit dem Auffang-`$` liefe + // der Block ins Leere und meldete bei jedem Laden eine Konsolen-Warnung — + // ein Dauer-Fehlalarm fuer einen gewollten Zustand. + const hlEl = $streng("hl-note"); const h = snap && snap.hl_ctx; - if (h && h.premium != null) { + // ⚠⚠ `hlEl` MUSS mitgeprueft werden. Das Element ist am 2026-08-20 auf + // User-Wunsch entfernt worden; ohne diese Bedingung wirft `hlEl.classList` + // einen TypeError — und der bricht den GESAMTEN Render ab (Dashboard + // friert auf Altwerten ein). Die else-Zweige hatten den Waechter, der + // Hauptzweig nicht. Gefunden von der Render-Probe, nachdem deren eigener + // blinder Fleck behoben war (die Attrappe gab fuer JEDE ID ein Element). + if (hlEl && h && h.premium != null) { const fr = (h.funding != null) ? (h.funding * 100).toFixed(4) + " %" : "—"; const oi = (h.open_interest != null) ? (h.open_interest / 1e6).toFixed(2) + " Mio" : "—"; @@ -761,8 +771,8 @@ function renderKontext(snap) { + "der Perp notiert über dem Index, Longs zahlen. ⚠ Gemessen über 5.095 " + "Funding-Stundenwerte (7 Monate) gegen den CFD: kein belastbarer " + "Prognosewert."; - } else { - hlEl.classList.add("hidden"); + } else if (hlEl) { + hlEl.classList.add("hidden"); } } @@ -1013,6 +1023,49 @@ function renderCone(cn) { // ⚠ Bilder mit `loading="lazy"`: die 15 Vorlagen liegen verkleinert bei ~25 KB // (Originale 436–611 KB), aber über WireGuard soll nur geladen werden, was // wirklich sichtbar ist. +// ══ BILD-MODALBOX für die Chartmuster (User-Wunsch 2026-08-20) ═════════════ +// ⚠⚠ BEWUSST OHNE BIBLIOTHEK — Begründung im Markup (`#bildbox`). Kurz: ein +// Lightbox-Paket wäre 15–40 KB plus ein zweites Bedienkonzept, das Dashboard +// läuft nur über WireGuard (kein CDN erreichbar), und das Projekt hat aus +// demselben Grund Tailwind abgelehnt und die vendorte Chart-Bibliothek +// entfernt. Was hier fehlt, sind ~30 Zeilen. +// ⚠ Delegierter Klick auf `document`: die Kacheln werden bei JEDEM Render neu +// erzeugt (`innerHTML = …`). Ein Handler direkt am `` wäre nach dem +// nächsten Snapshot weg — genau die Sorte Fehler, die nur sporadisch auffällt. +function bildboxOeffnen(bild, text) { + const box = $streng("bildbox"), img = $streng("bildbox-img"); + if (!box || !img) return; + // Grosse Fassung (~133 KB) erst JETZT laden, nicht mit dem Dashboard. + img.src = `muster/gross/${bild}.jpg`; + img.alt = text || ""; + const t = $streng("bildbox-txt"); + if (t) t.textContent = text || ""; + box.classList.remove("hidden"); +} + +function bildboxSchliessen() { + const box = $streng("bildbox"); + if (box) box.classList.add("hidden"); +} + +document.addEventListener("click", (e) => { + const t = e.target; + if (t && t.classList && t.classList.contains("pt-img") && t.dataset + && t.dataset.bild) { + bildboxOeffnen(t.dataset.bild, t.dataset.txt || t.alt || ""); + return; + } + // Klick auf den Hintergrund ODER den Schliessen-Knopf schliesst. + const box = $streng("bildbox"); + if (box && !box.classList.contains("hidden") + && (t === box || (t && t.id === "bildbox-zu"))) { + bildboxSchliessen(); + } +}); +document.addEventListener("keydown", (e) => { + if (e.key === "Escape") bildboxSchliessen(); +}); + function renderPatternsTF(d) { const el = $streng("pt-tfgrid"); if (!el) return; @@ -1028,7 +1081,9 @@ function renderPatternsTF(d) { const arr = m.dir === "bull" ? "▲" : m.dir === "bear" ? "▼" : "◆"; const best = m.status === "confirmed"; const bild = m.bild - ? `${m.name}` + ? `${m.name}` : `
kein Bild
`; const q = (m.quality != null ? m.quality : 0).toFixed(2); return `
` @@ -2405,7 +2460,12 @@ async function setMgGlobal() { // Ein zugeklapptes
ist nur nicht sichtbar - `textContent` wird // weiter gesetzt, und die naechste Oeffnung zeigt den aktuellen Stand. { - const KEY = "oil_m15_akk"; + // ⚠⚠ SCHLUESSEL GEBUMPT (2026-08-20). Der Default ist jetzt + // "eingeklappt" — aber im `localStorage` steht bei jedem, der die Karte je + // aufgeklappt hatte, noch `true`, und das gewinnt gegen den neuen Default. + // Ein neuer Schluessel setzt genau EINMAL zurueck; danach merkt sich die + // Karte die eigene Wahl wie bisher. + const KEY = "oil_m15_akk2"; let stand = {}; try { stand = JSON.parse(localStorage.getItem(KEY) || "{}"); } catch (e) { stand = {}; } document.querySelectorAll("details.m15-akk").forEach(el => { @@ -2459,7 +2519,7 @@ async function closeSlot(slot, label, pnl) { // und der Browser zeigte Altes. Statt das jedes Mal von Hand zu diagnostizieren, // sagt die Seite es jetzt selbst. // ⚠ Bewusst SICHTBAR statt nur Konsole: die Konsole sieht am Handy niemand. -const _JS_V = "205"; +const _JS_V = "207"; { const htmlV = document.body && document.body.dataset ? document.body.dataset.htmlV : null; diff --git a/web/index.html b/web/index.html index 4e2f0ed..b1aa13d 100644 --- a/web/index.html +++ b/web/index.html @@ -12,9 +12,9 @@ ⚠ LOKAL eingebunden, kein CDN: die PWA laeuft ueber WireGuard und muss offline funktionieren. --> - + - +