From 6d1a0849734fab246a0606fd0060136639737604 Mon Sep 17 00:00:00 2001 From: Peter Meier Date: Sun, 7 Jun 2026 11:02:48 +0200 Subject: [PATCH] =?UTF-8?q?refactor:=20Stellingnahme-Generator=20UX-=C3=9C?= =?UTF-8?q?berarbeitung=20Step=205?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Sticky farb-hinterlegter Stepper (Sprung zwischen Schritten klarer) - Step 5 reorganisiert: Setup oben, "═══ Ergebnis ═══" Trenner, Schreibvorlage- und KI-Cards primär sichtbar, Feintuning (Vorschau, Argument-Reihenfolge, Einleitung, Abschluss) in ein collapsed "Brief im Detail anpassen"-Accordion zusammengelegt - "Mit Platzhaltern füllen"-Button für Absender-Felder - KI-Block: nummerierte 1-2-3-4-Anleitung, "In Zwischenablage kopieren"-Button + .txt-Download, editierbare Inhalts-Regeln - KI-Prompt nachgeschärft: konkrete Rechtsgrundlagen, klare Anti- KI-Floskel-Liste, expliziter Hinweis "Entwurf-Banner entfernen", Verbot von em/en-dashes als Satz-Trenner - buildText post-processed em/en-dashes zu Kommas - UI-Texte: em/en-dashes durch Kommas ersetzt - "Anlagen geplant"-Disclaimer (Schätzung ~4-6 WEA/100ha) in Stellingnahme-Card und WindAreaPanel ergänzt - Reset setzt Step + Scroll zurück, History bleibt erhalten Co-Authored-By: Claude Sonnet 4.6 --- scripts/generate-icons.mjs | 4 + src/lib/components/WindAreaPanel.svelte | 18 +- .../blocks/StellingnahmeGeneratorBlock.svelte | 633 ++++++++++++------ src/lib/iconify-mdi-subset.generated.json | 30 +- 4 files changed, 471 insertions(+), 214 deletions(-) diff --git a/scripts/generate-icons.mjs b/scripts/generate-icons.mjs index 9498c59..7ff3d2e 100644 --- a/scripts/generate-icons.mjs +++ b/scripts/generate-icons.mjs @@ -69,6 +69,10 @@ const EXTRA_ICONS = [ 'cursor-default-click-outline', // CalendarBlock: social-image modal button 'image-outline', + // StellingnahmeGenerator: KI-Regeln-Accordion + Detail-Anpassen + 'tune-variant', + 'tune', + 'check-decagram', ]; const STATIC_PATTERN = /icon="mdi:([a-z0-9-]+)"/g; diff --git a/src/lib/components/WindAreaPanel.svelte b/src/lib/components/WindAreaPanel.svelte index 725ca39..e5f1fab 100644 --- a/src/lib/components/WindAreaPanel.svelte +++ b/src/lib/components/WindAreaPanel.svelte @@ -126,9 +126,15 @@ {/if} {#if area.anlagen_geplant != null} -
- {t(T.windkarte_label_anlagen_geplant)} - {area.anlagen_geplant} +
+ + {t(T.windkarte_label_anlagen_geplant)} + + + ~{area.anlagen_geplant}*
{/if} {#if area.hoehe_max_m != null} @@ -144,6 +150,12 @@
{/if} + {#if area.anlagen_geplant != null} +

+ * Anlagenzahl: + Schätzung (~4–6 Anlagen je 100 ha) — keine amtliche Festlegung. +

+ {/if} {#if area.gemeinden && area.gemeinden.length > 0} diff --git a/src/lib/components/blocks/StellingnahmeGeneratorBlock.svelte b/src/lib/components/blocks/StellingnahmeGeneratorBlock.svelte index 876fed6..72d7430 100644 --- a/src/lib/components/blocks/StellingnahmeGeneratorBlock.svelte +++ b/src/lib/components/blocks/StellingnahmeGeneratorBlock.svelte @@ -36,7 +36,7 @@ (block.allgemeineArgumente ?? []) as TextFragment[], ); - // Tag used to fetch allgemeineArgumente — exclude from grouping categories + // Tag used to fetch allgemeineArgumente, exclude from grouping categories const fetchTagSlug = $derived( typeof block.allgemeineArgumenteTag === "object" && block.allgemeineArgumenteTag !== null ? (block.allgemeineArgumenteTag as { _slug: string })._slug @@ -251,11 +251,11 @@ const gebietsBez = $derived( windArea - ? `${windArea.gebiets_nr}${windArea.bezeichnung ? " – " + windArea.bezeichnung : ""}` + ? `${windArea.gebiets_nr}${windArea.bezeichnung ? ", " + windArea.bezeichnung : ""}` : "", ); - // Editable opening/closing — null = use reactive default + // Editable opening/closing, null = use reactive default const defaultOpening = $derived( block.defaultOpeningText ? block.defaultOpeningText.replace("{gebiet}", gebietsBez) @@ -309,7 +309,7 @@ ]; }); - // Combined selection for output — respects drag order + // Combined selection for output, respects drag order const selectedFragments = $derived.by(() => { const allFrags = [...kriterien, ...allgemeineDeduped]; return fragmentOrder @@ -332,7 +332,7 @@ Karl-Liebknecht-Straße 4 const mailtoLink = $derived.by(() => { if (!outputText) return ""; - const subject = `Einwendung Regionalplan Südwestthüringen – Vorranggebiet ${gebietsBez}`; + const subject = `Einwendung Regionalplan Südwestthüringen, Vorranggebiet ${gebietsBez}`; const to = block.recipientEmail ?? ""; return `mailto:${to}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(outputText)}`; }); @@ -349,7 +349,7 @@ Karl-Liebknecht-Straße 4 ? `Mit freundlichen Grüßen\n\n${absenderName.trim()}` : "Mit freundlichen Grüßen"; const disclaimer = `${"=".repeat(64)} -!! ENTWURF – KEIN FERTIGER BRIEF !! +!! ENTWURF, KEIN FERTIGER BRIEF !! Stichpunkte müssen vor dem Einreichen in eigene, ausformulierte Sätze überführt werden. Text individuell anpassen. ${"=".repeat(64)}`; @@ -371,7 +371,11 @@ ${"=".repeat(64)}`; return [disclaimer, "", sender, "", heute, "", "An:", RECIPIENT, "", "Betreff: " + betreff.split("\n")[0], betreff.split("\n")[1] ?? "", "", "Sehr geehrte Damen und Herren,", "", body.trimEnd()] .join("\n") - .replace(/\n{3,}/g, "\n\n"); + .replace(/\n{3,}/g, "\n\n") + // Em- und En-Dashes als Satz-Trenner durch Kommas ersetzen + // ("Wort, Wort" / "Wort, Wort" → "Wort, Wort"). + // Bindestriche in Bezeichnungen wie "W-23" bleiben unangetastet. + .replace(/\s+[—–]\s+/g, ", "); } let sectionEl: HTMLElement | undefined; @@ -398,6 +402,25 @@ ${"=".repeat(64)}`; const aiToneLabels = ["sehr sachlich & rational", "eher sachlich", "ausgewogen", "eher emotional", "sehr emotional & persönlich betroffen"]; const aiSharpnessLabels = ["konstruktiv & kooperativ", "moderat", "bestimmt & klar", "kritisch", "fordernd & scharf"]; + // Editierbare Inhalts-Regeln für den KI-Prompt. Default-Set unten, + // Nutzer kann anpassen/ergänzen, wirkt als Override im Prompt. + const DEFAULT_AI_RULES = `1. Alle Argumente, Fakten, Zahlen, Gesetzes- und Quellenverweise aus der Vorlage MÜSSEN vollständig erhalten bleiben. Reihenfolge im Hauptteil darfst du variieren, Inhalt nicht. +2. KEINE neuen Fakten, Zahlen, Gesetzesnormen, Studien oder Quellen erfinden. Was nicht in der Vorlage steht, kommt nicht in den Output. (Der oben angegebene Kontext zum Vorranggebiet und die persönliche Betroffenheit zählen ebenfalls als Vorlage.) +3. Stichpunkte, Bullet-Listen oder Aufzählungen aus der Vorlage werden in zusammenhängende Fließtext-Absätze überführt. Im fertigen Brief stehen keine "•"/"-"-Listen mehr im Argumentationsteil. +4. Mindestens 70 % des Fließtextes sind neu formuliert. Übernimm keine ganzen Sätze 1:1 aus der Vorlage. Behalte juristische Fachbegriffe und Zitate bei. +5. Wenn der Verfasser persönliche Betroffenheit angegeben hat, wird sie konkret und ich-bezogen in einen passenden Absatz des Hauptteils eingewoben, nicht als separater Block angehängt. Ohne Angabe verzichtest du auf erfundene persönliche Bezüge.`; + let aiRules = $state(DEFAULT_AI_RULES); + + // Badges für "Brief im Detail anpassen"-Accordion + const tweakBadges = $derived( + [ + selectedFragments.length > 1 ? "Reihenfolge" : null, + openingOverride !== null ? "Einleitung" : null, + closingOverride !== null ? "Abschluss" : null, + hasManualEdit ? "bearbeitet" : null, + ].filter(Boolean) as string[], + ); + const stepFilled = $derived([ !!windArea, ortskonkreteSelectedCount > 0 || kriterien.length === 0, @@ -439,8 +462,15 @@ ${"=".repeat(64)}`; absenderOrt = ""; showOutput = false; outputText = ""; + hasManualEdit = false; + fragmentOrder = []; hadSavedState = false; + maxStepReached = 0; + step = 0; try { localStorage.removeItem(storageKey); } catch {} + if (typeof window !== "undefined") { + window.scrollTo({ top: 0, behavior: "smooth" }); + } } let dragIdx = $state(null); @@ -480,7 +510,7 @@ ${"=".repeat(64)}`; } } - async function copyAiPrompt() { + function buildAiPrompt(): string { const toneDesc = aiToneLabels[aiTone - 1]; const sharpnessDesc = aiSharpnessLabels[aiSharpness - 1]; const customPart = aiCustomNote.trim() @@ -490,42 +520,70 @@ ${"=".repeat(64)}`; ? `\nPersönliche Betroffenheit des Verfassers (zentral und authentisch einbinden, möglichst wörtlich verarbeiten, nichts dazuerfinden):\n${persoenlicheEingabe.trim()}\n` : ""; const gebietPart = windArea - ? `\nKontext zum betroffenen Windvorranggebiet: ${windArea.gebiets_nr ?? ""}${windArea.bezeichnung ? " – " + windArea.bezeichnung : ""}${windArea.gemeinden?.length ? " (Gemeinden: " + windArea.gemeinden.slice(0, 5).join(", ") + ")" : ""}\n` + ? `\nKontext zum betroffenen Windvorranggebiet: ${windArea.gebiets_nr ?? ""}${windArea.bezeichnung ? ", " + windArea.bezeichnung : ""}${windArea.gemeinden?.length ? " (Gemeinden: " + windArea.gemeinden.slice(0, 5).join(", ") + ")" : ""}\n` : ""; - const prompt = `Rolle: Du bist ein erfahrener Verfasser von Bürger-Einwendungen im Raumordnungs- und Genehmigungsverfahren. Du formulierst eine individuelle, juristisch und sachlich saubere Stellungnahme im Namen des Verfassers. + return `# Rolle +Du bist erfahrener Verfasser von Bürger-Einwendungen im Raumordnungs- und Genehmigungsverfahren nach BauGB, ROG und BImSchG. Du kennst die Sprache und Konventionen behördlicher Anschreiben in Deutschland und schreibst rechtlich tragfähig, sachlich, ohne Pathos. -Aufgabe: Schreibe die unten als "MUSTER-STELLUNGNAHME" beigefügte Vorlage so um, dass sie wie eine eigenständig verfasste Einwendung dieser einen Person wirkt – nicht wie eine Vorlage und nicht wie eine generierte Massenstellungnahme. +# Aufgabe +Aus der unten als "MUSTER-STELLUNGNAHME" beigefügten Vorlage (= Argument-Gerüst mit Stichpunkten) entsteht ein vollständig ausformulierter Einwendungstext, der wie eine eigenständig verfasste Einwendung dieser konkreten Person wirkt, nicht wie eine Vorlage, nicht wie eine generierte Massenstellungnahme, nicht wie ein offensichtlich KI-erzeugter Text. -Stilvorgaben: +# Stilvorgaben - Ton: ${toneDesc} - Haltung/Schärfe: ${sharpnessDesc} -- Sprache: durchgängig Deutsch, Sie-Form gegenüber der Behörde, klar adressiert. +- Sprache: Deutsch, durchgängig Sie-Form gegenüber der Behörde. Sätze klar, Hauptsatz-dominiert, ohne Verschachtelungen über drei Ebenen. +- Verwende juristisch-sachliche Begriffe ("erheben Einwendung", "Abwägungsfehler", "ersatzlose Streichung"), aber keine Floskeln und keine Übertreibungen. ${gebietPart}${personalPart}${customPart} -Zwingende Regeln: -1. ALLE inhaltlichen Argumente, Fakten, Zahlen, Gesetzes- und Quellenverweise aus der Muster-Stellungnahme MÜSSEN vollständig erhalten bleiben. Nichts weglassen, nichts inhaltlich abschwächen. -2. KEINE neuen Fakten, Zahlen, Gesetzesnormen oder Quellen erfinden. Wenn etwas in der Vorlage nicht steht, taucht es im Output nicht auf. -3. Briefkopf, Behörden-Adresse, Aktenzeichen/Betreff, Datum, Anrede und Schlussformel inkl. Absender bleiben strukturell erhalten (Reihenfolge, Felder, Angaben). Nur Formulierungen im Fließtext darfst du umarbeiten. -4. Argument-Reihenfolge im Hauptteil darfst du variieren, sofern Logik und Lesefluss stimmen. -5. Formulierungen variieren – keine Satzbausteine 1:1 übernehmen. Mindestens 70 % des Fließtextes neu formulieren, ohne Inhalt zu verändern. -6. Persönliche Betroffenheit (falls oben angegeben) wird konkret und ich-bezogen in den Text eingewoben, nicht als separater Absatz angehängt. -7. Länge: ungefähr im Rahmen der Vorlage (±20 %). Keine Aufblähung mit Floskeln, keine starke Kürzung. +# Inhaltliche Regeln (zwingend) +${aiRules.trim()} -Ausgabeformat: -- Gib AUSSCHLIESSLICH den fertigen Stellungnahme-Text aus. Kein Vorwort, keine Erklärung, keine Markdown-Codeblöcke, keine Hinweise wie "Hier ist deine Stellungnahme:". Direkt mit dem Briefkopf beginnen. +# Brief-Struktur (unveränderlich) +- Absender (Name, Anschrift, PLZ/Ort), Datum, Behörden-Adresse, Betreff und Schlussformel werden 1:1 aus der Vorlage übernommen, Reihenfolge, Felder, exakte Schreibweise. Auch dann, wenn ein Feld leer oder kurz wirkt (z.B. nur ein Vorname). +- Anrede und Grußformel ("Sehr geehrte Damen und Herren," / "Mit freundlichen Grüßen") bleiben unverändert. + +# Was du im Output ENTFERNST +- Alle Hinweis-Banner, Warnhinweise, Platzhalter oder Anweisungen für den Verfasser ("ENTWURF, KEIN FERTIGER BRIEF", "Stichpunkte ausformulieren", Trennstriche wie "====" oder "---"). Diese gehören nicht in den endgültigen Behördentext. +- Meta-Kommentare, Erklärungen, Markdown-Headlines (## …), Codeblöcke (\`\`\`). + +# Länge & Qualität +- Länge ungefähr im Rahmen der Vorlage (±25 %). Keine Aufblähung mit Floskeln ("vor diesem Hintergrund möchte ich betonen, dass …"), keine starke Kürzung argumentativer Substanz. +- Vermeide KI-typische Muster: "Es ist wichtig zu beachten, dass …", "darüber hinaus möchte ich anmerken …", "in einer Welt, in der …", übermäßige Adverbien ("zudem", "weiterhin", "des Weiteren" in jedem Absatz). + +# Zeichensetzung +- KEINE Gedankenstriche (em-dash "—" oder en-dash "–") als Satz-Trenner verwenden. Stattdessen Kommas, Klammern oder Doppelpunkte nutzen. Bindestriche in Bezeichnungen ("W-23", "Karl-Liebknecht-Straße") bleiben. +- Bevorzuge typografisch deutsche Anführungszeichen („…") wo passend. + +# Ausgabeformat +Gib AUSSCHLIESSLICH den fertigen Brieftext aus, beginnend mit dem Absenderblock, endend mit Namen unter der Grußformel. Kein Vorwort, keine Erklärung, keine Markdown-Codeblöcke, keine Schlussbemerkung wie "Bei Fragen stehe ich gerne zur Verfügung" (außer es steht so in der Vorlage). MUSTER-STELLUNGNAHME: --- ${outputText} ---`; + } + + async function copyAiPrompt() { + const prompt = buildAiPrompt(); try { await navigator.clipboard.writeText(prompt); - copyAiPromptFeedback = "Kopiert!"; - setTimeout(() => (copyAiPromptFeedback = ""), 2000); + copyAiPromptFeedback = "✓ In Zwischenablage kopiert"; + setTimeout(() => (copyAiPromptFeedback = ""), 2500); } catch { copyAiPromptFeedback = "Fehler"; } } + function downloadAiPrompt() { + const prompt = buildAiPrompt(); + const blob = new Blob([prompt], { type: "text/plain;charset=utf-8" }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = `ki-prompt-${windArea?.gebiets_nr ?? "stellungnahme"}.txt`; + a.click(); + URL.revokeObjectURL(url); + } + function printText() { const w = window.open("", "_blank"); if (!w) return; @@ -658,9 +716,9 @@ ${outputText} Einwendungsfrist: {deadlineFormatted} - {#if deadlineDays === 0}— heute letzter Tag! - {:else if deadlineDays === 1}— morgen letzter Tag! - {:else}— noch {deadlineDays} Tage + {#if deadlineDays === 0}, heute letzter Tag! + {:else if deadlineDays === 1}, morgen letzter Tag! + {:else}, noch {deadlineDays} Tage {/if} @@ -701,48 +759,50 @@ ${outputText}
{@html md(block.intro)}
{/if} - -
- {#each Array.from({ length: TOTAL_STEPS }, (_, i) => i) as s} - + {#if s < TOTAL_STEPS - 1} +
{/if} - - {#if s < TOTAL_STEPS - 1} -
+ {/each} +
+
+ Schritt {step + 1} / {TOTAL_STEPS}, {STEP_LABELS[step]} + {#if saveStatus === "saved"} + + {/if} - {/each} -
-
- {#if saveStatus === "saved"} - - - {/if} - Schritt {step + 1} von {TOTAL_STEPS} – {STEP_LABELS[step]} +
{#if step === 0}
-

Schritt 1 – Ihr Vorranggebiet

+

Schritt 1, Ihr Vorranggebiet

{#if windArea}
@@ -769,8 +829,16 @@ ${outputText} {#if windArea.anlagen_geplant}
{/if} {#if windArea.hoehe_max_m} @@ -795,6 +863,15 @@ ${outputText}
{/if} + {#if windArea.anlagen_geplant} +

+ * Hinweis zur Anlagenzahl: + keine amtliche Festlegung, eigene Schätzung nach Faustregel + ~4–6 Anlagen je 100 ha. Aus dem Prüfbogen ZU-14 stammen nur + Flächengröße und Windhöffigkeit. Die tatsächliche Anlagenzahl + wird erst im Genehmigungsverfahren bestimmt. +

+ {/if} {#if windArea.notizen}
@@ -867,7 +944,7 @@ ${outputText} {:else if step === 1}
-

Schritt 2 – Ortskonkrete Argumente

+

Schritt 2, Ortskonkrete Argumente

Diese Argumente beziehen sich direkt auf das Gebiet {windArea?.gebiets_nr ?? ""}. Wählen Sie die Punkte, die auf Ihre Situation zutreffen. @@ -936,7 +1013,7 @@ ${outputText} {:else if step === 2}

-

Schritt 3 – Grundsätzliche Einwände

+

Schritt 3, Grundsätzliche Einwände

Welche allgemeinen Argumente teilen Sie? Diese gelten unabhängig vom konkreten Standort.

@@ -1076,7 +1153,7 @@ ${outputText} {:else if step === 3}
-

Schritt 4 – Persönliche Betroffenheit

+

Schritt 4, Persönliche Betroffenheit

Beschreiben Sie kurz, wie Sie persönlich betroffen sind. Wichtig: Individuelle Betroffenheit stärkt Ihre Einwendung erheblich @@ -1106,8 +1183,25 @@ ${outputText} {:else if step === 4}

-

Schritt 5 – Absender & Ausgabe

-

Ihre Daten werden nur lokal zur Texterstellung genutzt.

+

Schritt 5, Absender & Ausgabe

+

Ihre Daten werden nur lokal zur Texterstellung genutzt.

+ +
+ + Erzeugt z.B. [Vor- und Nachname], später ersetzen. +
@@ -1124,160 +1218,289 @@ ${outputText}
- {#if selectedFragments.length > 1} -
- - Reihenfolge der Argumente ({selectedFragments.length}) - - -
- {#each selectedFragments as frag, i} -
onDragStart(i)} - ondragover={(e) => onDragOver(e, i)} - ondrop={() => onDrop(i)} - ondragend={onDragEnd} - class="flex cursor-grab items-center gap-3 px-4 py-2.5 text-sm transition-colors active:cursor-grabbing - {dragOverIdx === i && dragIdx !== i ? 'bg-wald-50 border-t-2 border-wald-400' : 'bg-white hover:bg-stein-50'}" - > - - {frag.title ?? frag._slug.replace(/_/g, " ")} - {i + 1} -
- {/each} -
-
- {/if} -
Einzureichen bei:
{RECIPIENT}
- -
-
- - Einleitung anpassen - - -
- - {#if openingOverride !== null} - - {/if} -
-
-
- - Abschluss anpassen - - -
- - {#if closingOverride !== null} - - {/if} -
-
-
- -
- - -
{#if !absenderName.trim()} -

Bitte Namen eintragen, um den Text zu erstellen.

- {:else} -
+

+ Bitte oben Ihren Namen eintragen, der Text wird automatisch erstellt. +

{/if} {#if showOutput} -
-
- Ihre Einwendung -
- - - - {#if mailtoLink && !mailTooLong} - {block.recipientEmail ? "Per E-Mail senden" : "Per E-Mail (an mich)"} - {/if} + +
+
+ + + Ergebnis + +
+
+ + +
+
+ +
+

Schreibvorlage verwenden

+

Text direkt übernehmen, kopieren, herunterladen oder drucken.

- - {#if mailTooLong} -
Für E-Mail zu lang — bitte Kopieren und in die Mail einfügen.
- {/if} -
-

Mit KI eine individuelle Version erstellen

-

Stichpunkte in einen persönlichen Fließtext verwandeln — mit ChatGPT, Claude oder einer anderen KI:
① Regler einstellen  ② „KI-Prompt kopieren" klicken  ③ Bei ChatGPT / Claude einfügen & senden

-
-
-
- -
-
- Sachlich - - Emotional -
-
-
-
- -
-
- Konstruktiv - - Fordernd -
-
-
-
- - -
+
+ class="inline-flex items-center gap-1.5 rounded-md border border-wald-300 bg-wald-50 px-3 py-1.5 text-xs font-medium text-wald-800 hover:bg-wald-100" + onclick={copyText} + > + + {copyFeedback || "Kopieren"} + + + + {#if mailtoLink && !mailTooLong} + + + {block.recipientEmail ? "Per E-Mail senden" : "Per E-Mail (an mich)"} + + {/if}
-
+ + +
+
+ +
+

Mit KI eine individuelle Version erstellen

+

+ Die Vorlage wird zu persönlichem Fließtext umformuliert, mit ChatGPT, Claude oder anderer KI. +

+
+
+
    +
  1. 1.Regler unten einstellen (Ton, Schärfe).
  2. +
  3. 2.Auf „In Zwischenablage kopieren“ klicken, der komplette Prompt wird automatisch kopiert. (Alternativ als .txt-Datei herunterladen.)
  4. +
  5. 3.ChatGPT, Claude o.ä. öffnen → ins Eingabefeld einfügen (Strg+V / ⌘+V) → senden.
  6. +
  7. 4.KI-Antwort prüfen, ggf. anpassen, fertig.
  8. +
+
+
+
+ +
+
+ Sachlich + + Emotional +
+
+
+
+ +
+
+ Konstruktiv + + Fordernd +
+
+
+
+ + +
+ +
+ + + + Inhaltliche Regeln für die KI feinjustieren + {#if aiRules.trim() !== DEFAULT_AI_RULES.trim()} + angepasst + {/if} + + + +
+

+ Diese Regeln werden 1:1 in den KI-Prompt übernommen. Anpassen, wenn die KI z.B. zu locker mit Fakten umgeht oder zu sehr abweicht. Bei Unsicherheit → zurücksetzen. +

+ +
+ {aiRules.length} Zeichen + +
+
+
+
+ + +
+
+ + +
+ + + + Brief im Detail anpassen + {#if tweakBadges.length > 0} + ({tweakBadges.join(" · ")}) + {/if} + + + + +
+ +
+

+ + Vorschau + {#if hasManualEdit} + bearbeitet + {/if} +

+ + {#if hasManualEdit} +
+ Manuelle Änderungen aktiv + +
+ {/if} + {#if mailTooLong} +

Für E-Mail zu lang, bitte Kopieren und in die Mail einfügen.

+ {/if} +
+ + {#if selectedFragments.length > 1} +
+

+ + Reihenfolge der Argumente ({selectedFragments.length}) +

+
+ {#each selectedFragments as frag, i} +
onDragStart(i)} + ondragover={(e) => onDragOver(e, i)} + ondrop={() => onDrop(i)} + ondragend={onDragEnd} + class="flex cursor-grab items-center gap-3 px-3 py-2 text-sm transition-colors active:cursor-grabbing + {dragOverIdx === i && dragIdx !== i ? 'bg-wald-50 border-t-2 border-wald-400' : 'bg-white hover:bg-stein-50'}" + > + + {frag.title ?? frag._slug.replace(/_/g, " ")} + {i + 1} +
+ {/each} +
+
+ {/if} + +
+
+

+ Einleitung + {#if openingOverride !== null} + + {/if} +

+ +
+
+

+ Abschluss + {#if closingOverride !== null} + + {/if} +

+ +
+
+
+
+ +
Hinweis: Bitte passen Sie den Text vor dem Einreichen individuell an. Einwendungen mit persönlichen Formulierungen sind wirksamer als identische Kopien.
{/if} + +
+ + +
+ {#if outputHistory.length > 0}
diff --git a/src/lib/iconify-mdi-subset.generated.json b/src/lib/iconify-mdi-subset.generated.json index 179d942..498f9bc 100644 --- a/src/lib/iconify-mdi-subset.generated.json +++ b/src/lib/iconify-mdi-subset.generated.json @@ -81,6 +81,9 @@ "download": { "body": "" }, + "information-outline": { + "body": "" + }, "loading": { "body": "" }, @@ -177,6 +180,27 @@ "flag-outline": { "body": "" }, + "format-text-variant-outline": { + "body": "" + }, + "check-decagram": { + "body": "" + }, + "printer": { + "body": "" + }, + "robot-outline": { + "body": "" + }, + "tune-variant": { + "body": "" + }, + "tune": { + "body": "" + }, + "file-document-outline": { + "body": "" + }, "drag-vertical": { "body": "" }, @@ -204,9 +228,6 @@ "home-outline": { "body": "" }, - "file-document-outline": { - "body": "" - }, "share-variant-outline": { "body": "" }, @@ -267,9 +288,6 @@ }, "tag-outline": { "body": "" - }, - "information-outline": { - "body": "" } }, "aliases": {