1<!doctype html> 2<html lang="de"> 3 <head> 4 <meta charset="UTF-8" /> 5 <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> 6 <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png" /> 7 <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" /> 8 <link rel="manifest" href="/manifest.webmanifest" /> 9 <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" /> 10 <meta name="theme-color" content="#0f172a" media="(prefers-color-scheme: dark)" /> 11 <meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)" /> 12 13 <title>Unterrichtsplanung mit KI, die deine Klasse kennt | DSGVO</title> 14 <meta name="description" content="Unterrichtsplanung mit KI, die auf dem Lernstand deiner Klasse aufbaut â Vorschläge für Stunden, Arbeitsblätter in 3 Niveaus und Zeugnistexte. DSGVO-konform." /> 15 <meta name="author" content="tk comp UG (haftungsbeschränkt)" /> 16 <!-- KEIN statischer canonical-/hreflang-Link in der SPA-Shell (#296). 17 (Tag-Syntax hier bewusst NICHT ausgeschrieben, damit Textguards/greps 18 diesen Kommentar nicht als echten Treffer werten.) 19 Die Shell bedient JEDE Route â gemessen 18.07.: alle 7 Sitemap-URLs lieferten 20 byte-identisches HTML (md5 96c8bcad, 14.515 B) mit diesem Wert. Damit erklaerte 21 jede Unterseite die Startseite zu ihrem Original und konnte strukturell nie 22 indexiert werden (GSC fuehrt / aktuell als Soft 404). 23 PageMeta.tsx setzt canonical + og:url pro Route via react-helmet-async. 24 Der hreflang stand hier zusaetzlich FALSCH, weil PageMeta ihn gar nicht 25 ueberschreibt â er zeigte auf allen Routen dauerhaft auf die Startseite. --> 26 <meta name="robots" content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1" /> 27 28 <!-- Performance: DB-Roundtrip vorwärmen --> 29 <link rel="preconnect" href="https://nkvayuhxgdiskwdicrpk.supabase.co" crossorigin /> 30 <link rel="dns-prefetch" href="https://nkvayuhxgdiskwdicrpk.supabase.co" /> 31 32 <!-- Open Graph --> 33 <meta property="og:type" content="website" /> 34 <meta property="og:site_name" content="unterrichtsplanung.ai" /> 35 <meta property="og:title" content="Unterrichtsplanung mit KI, die deine Klasse kennt" /> 36 <meta property="og:description" content="Der ⨠Vorschlag baut auf dem Lernstand deiner Klasse auf: Stunden, Arbeitsblätter in 3 Niveaus, Zeugnistexte aus dem Lernfortschritt. 30 Tage kostenlos testen." /> 37 <meta property="og:url" content="https://unterrichtsplanung.ai/" /> 38 <meta property="og:image" content="https://unterrichtsplanung.ai/og-image.png" /> 39 <meta property="og:image:width" content="1200" /> 40 <meta property="og:image:height" content="630" /> 41 <meta property="og:image:alt" content="unterrichtsplanung.ai â KI-gestützte Unterrichtsplanung für Lehrkräfte" /> 42 <meta property="og:locale" content="de_DE" /> 43 44 <!-- Twitter / X --> 45 <meta name="twitter:card" content="summary_large_image" /> 46 <meta name="twitter:url" content="https://unterrichtsplanung.ai/" /> 47 <meta name="twitter:title" content="Unterrichtsplanung mit KI, die deine Klasse kennt" /> 48 <meta name="twitter:description" content="Stundenentwürfe und Arbeitsblätter aus dem Lernstand deiner Klasse. DSGVO-konform, aus Deutschland." /> 49 <meta name="twitter:image" content="https://unterrichtsplanung.ai/og-image.png" /> 50 <meta name="twitter:image:alt" content="unterrichtsplanung.ai" /> 51
51<script type="module" crossorigin src="/assets/index-BrUBw8xl.js"></script>
51 52 <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-DF2fYuay.js"> 53 <link rel="modulepreload" crossorigin href="/assets/lucide-FiUllUuf.js"> 54 <link rel="modulepreload" crossorigin href="/assets/radix-DA0TVB9t.js"> 55 <link rel="modulepreload" crossorigin href="/assets/tanstack-CXE1wJKK.js"> 56 <link rel="modulepreload" crossorigin href="/assets/react-vendor-DkRFjetv.js"> 57 <link rel="modulepreload" crossorigin href="/assets/client-CgQ6iHoJ.js"> 58 <link rel="modulepreload" crossorigin href="/assets/utils-CWd_IMVQ.js"> 59 <link rel="modulepreload" crossorigin href="/assets/offline-queue-BhNypXC-.js"> 60 <link rel="modulepreload" crossorigin href="/assets/defineProperty-CV_TUbzF.js"> 61 <link rel="modulepreload" crossorigin href="/assets/index.esm-dwxpSC8g.js"> 62 <link rel="modulepreload" crossorigin href="/assets/errors-BVF_zu8c.js"> 63 <link rel="modulepreload" crossorigin href="/assets/Brand-C1Z3Aa48.js"> 64 <link rel="modulepreload" crossorigin href="/assets/dist-iZKIbZxm.js"> 65 <link rel="modulepreload" crossorigin href="/assets/button-DWibT-Lh.js"> 66 <link rel="modulepreload" crossorigin href="/assets/input-DMUCdaa7.js"> 67 <link rel="modulepreload" crossorigin href="/assets/label-Cmbd6Sco.js"> 68 <link rel="modulepreload" crossorigin href="/assets/spinner-DBN64AJ6.js"> 69 <link rel="modulepreload" crossorigin href="/assets/useClassFilter-D5fbxnri.js"> 70 <link rel="modulepreload" crossorigin href="/assets/useSession-BdcD8JJU.js"> 71 <link rel="modulepreload" crossorigin href="/assets/useRatingStyle-Dc73Atqa.js"> 72 <link rel="modulepreload" crossorigin href="/assets/QuickCaptureProvider-D_Oo8sIg.js"> 73 <link rel="modulepreload" crossorigin href="/assets/checkbox-BDL0c6dG.js"> 74 <link rel="modulepreload" crossorigin href="/assets/textarea-BLiD0cpI.js"> 75 <link rel="modulepreload" crossorigin href="/assets/dialog-Cy9aBvcj.js"> 76 <link rel="modulepreload" crossorigin href="/assets/WizardFrame-DMjju2mk.js"> 77 <link rel="modulepreload" crossorigin href="/assets/prompts-JeE8OUpN.js"> 78 <link rel="modulepreload" crossorigin href="/assets/ai-BWizyc0-.js"> 79 <link rel="modulepreload" crossorigin href="/assets/shared-KTpItWuq.js"> 80 <link rel="modulepreload" crossorigin href="/assets/hooks-B_6g3KfP.js"> 81 <link rel="modulepreload" crossorigin href="/assets/learning-state-summary-Ipvx6olu.js"> 82 <link rel="modulepreload" crossorigin href="/assets/badge-DpGSl0Ur.js"> 83 <link rel="modulepreload" crossorigin href="/assets/card-BS9mZtL8.js"> 84 <link rel="modulepreload" crossorigin href="/assets/alert-dialog-D5LeFOfY.js"> 85 <link rel="modulepreload" crossorigin href="/assets/confirm-BHXvXeJy.js"> 86 <link rel="modulepreload" crossorigin href="/assets/select-2FJm2fXs.js"> 87 <link rel="modulepreload" crossorigin href="/assets/Timetable-y9jMkS1u.js"> 88 <link rel="modulepreload" crossorigin href="/assets/NewLessonWizard-DDZUibx8.js"> 89 <link rel="modulepreload" crossorigin href="/assets/quick-capture-BfugqtFM.js"> 90 <link rel="modulepreload" crossorigin href="/assets/useQuickActionLauncher-BxY3oIsj.js"> 91 <link rel="modulepreload" crossorigin href="/assets/dropdown-menu-BjJzRrO3.js"> 92 <link rel="modulepreload" crossorigin href="/assets/popover-BumsDl_V.js"> 93 <link rel="modulepreload" crossorigin href="/assets/useProfile-DDgiWaHw.js"> 94 <link rel="modulepreload" crossorigin href="/assets/useSubscription-BigbcYP2.js"> 95 <link rel="modulepreload" crossorigin href="/assets/useUiMode-BB3JcOCj.js"> 96 <link rel="modulepreload" crossorigin href="/assets/analytics-BFFxC8_o.js"> 97 <link rel="stylesheet" crossorigin href="/assets/index-BFI-nUAo.css"> 98 <link rel="manifest" href="/manifest.webmanifest">
98<script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script>
98</head> 99 <body> 100 <div id="root"></div> 101 <noscript>JavaScript ist erforderlich.</noscript> 102
102<script> 103 (function () { 104 // SW-Reset-Escape-Hatch: ?reset_sw=1 killt Service Worker + alle Caches 105 // und lädt dann sauber neu. Robust: läuft auch ohne SW-Support und lädt 106 // IMMER neu (sonst blieb bei fehlendem navigator.serviceWorker eine 107 // leere Seite zurück). 108 if (new URLSearchParams(location.search).get("reset_sw") === "1") { 109 var done = function () { location.replace("/"); }; 110 var clearCaches = function () { 111 if (typeof caches === "undefined") return done(); 112 caches.keys() 113 .then(function (keys) { return Promise.all(keys.map(function (k) { return caches.delete(k); })); }) 114 .then(done, done); 115 }; 116 if (navigator.serviceWorker && navigator.serviceWorker.getRegistrations) { 117 navigator.serviceWorker.getRegistrations() 118 .then(function (rs) { return Promise.all(rs.map(function (r) { return r.unregister(); })); }) 119 .then(clearCaches, clearCaches); 120 } else { 121 clearCaches(); 122 } 123 return; 124 } 125 126 // Boot-Error-Logger: schreibt Fehler in localStorage bevor React geladen hat 127 window.addEventListener("error", function (e) { 128 try { 129 localStorage.setItem("lp_boot_error", JSON.stringify({ 130 msg: String(e.message), 131 src: String(e.filename), 132 line: e.lineno, 133 col: e.colno, 134 stack: e.error ? String(e.error.stack) : null, 135 at: new Date().toISOString(), 136 ua: navigator.userAgent, 137 })); 138 } catch (_) {} 139 }); 140 window.addEventListener("unhandledrejection", function (e) { 141 try { 142 localStorage.setItem("lp_boot_error", JSON.stringify({ 143 msg: "unhandledrejection: " + String(e.reason), 144 at: new Date().toISOString(), 145 ua: navigator.userAgent, 146 })); 147 } catch (_) {} 148 }); 149 150 // Boot-Timeout: wenn nach 5s kein React-Mount erfolgt ist, zeige Recovery-UI 151 window.__lpBootCheck = setTimeout(function () { 152 var root = document.getElementById("root"); 153 if (root && root.children.length === 0) { 154 var err = ""; 155 try { err = localStorage.getItem("lp_boot_error") || ""; } catch (_) {} 156 var safe = err.replace(/[<>&]/g, function (c) { 157 return { "<": "<", ">": ">", "&": "&" }[c]; 158 }); 159 root.innerHTML = 160 '<div style="padding:20px;font-family:system-ui;max-width:600px;margin:40px auto">' + 161 '<h1 style="font-size:18px">App konnte nicht geladen werden</h1>' + 162 '<p>Wenn das wiederholt passiert, versuche bitte:</p>' + 163 '<ol><li>Seite neu laden mit gedrückter Shift-Taste</li>' + 164 '<li>Oder rufe <a href="/?reset_sw=1">unterrichtsplanung.ai/?reset_sw=1</a> auf</li></ol>' + 165 (safe 166 ? '<details style="margin-top:20px"><summary>Technische Details</summary>' + 167 '<pre style="white-space:pre-wrap;font-size:11px;background:#f5f5f5;padding:10px;border-radius:4px">' + 168 safe + "</pre></details>" 169 : "") + 170 "</div>"; 171 } 172 }, 5000); 173 })(); 174 </script>
174 175 </body> 176</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.