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} -
+ * 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