From bfa923dc223fb09ab40f519203f84c1974947454 Mon Sep 17 00:00:00 2001 From: Till JS Date: Fri, 24 Apr 2026 15:53:35 +0200 Subject: [PATCH] =?UTF-8?q?feat(writing):=20M5=20=E2=80=94=20cross-module?= =?UTF-8?q?=20references=20in=20the=20briefing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drafts can now pull in saved articles, notes, library entries, and raw URLs as prompt context. This is the Writing module's main differentiator against standalone LLM chat: the user's own SSOT flows straight into the ghostwriter without copy-paste. - utils/reference-resolver.ts: resolveReference() per kind (article, note, library, url) via scopedGet + decryptRecords + module type converter. Each ref truncates to MAX_CHARS_PER_REF=1500 (with a "[… gekürzt …]" marker); resolveReferences() caps the aggregate at MAX_TOTAL_REFERENCE_CHARS=8000 and drops extras rather than slicing mid-sentence. Deleted or missing refs silently fall out. - prompt-builder: buildDraftPrompt() takes resolvedReferences and renders them as a "--- Quellen ---" block in the user message with [Quelle N] headers + optional "Kontext:" lines (the user's own per-ref note). System prompt gets a sentence instructing the model to paraphrase from the sources and not fabricate facts when a source has nothing useful. - generations store: startDraftGeneration resolves references in parallel before building the prompt. No changes to the refineSelection path — M5 keeps selection-refinement context-free on purpose. - UI: ReferencePicker.svelte inline in the BriefingForm with four kind tabs (Artikel / Notiz / Library / URL). Searchable lists per kind for module refs (max 20 visible, debounced); URL kind takes a url + an optional context note. ReferenceChip.svelte pills render live- resolved titles; parent resolves labels via the module queries. Hard cap at 6 references per draft. - Scope limits: kontext / goal / me-image refs are on the roadmap but deliberately skipped in M5 — they require different resolution paths (singletons, structured metadata, image descriptors) that would sprawl this commit. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../writing/components/BriefingForm.svelte | 23 +- .../writing/components/ReferenceChip.svelte | 80 ++++ .../writing/components/ReferencePicker.svelte | 398 ++++++++++++++++++ .../writing/stores/generations.svelte.ts | 7 + .../modules/writing/utils/prompt-builder.ts | 39 +- .../writing/utils/reference-resolver.ts | 177 ++++++++ 6 files changed, 722 insertions(+), 2 deletions(-) create mode 100644 apps/mana/apps/web/src/lib/modules/writing/components/ReferenceChip.svelte create mode 100644 apps/mana/apps/web/src/lib/modules/writing/components/ReferencePicker.svelte create mode 100644 apps/mana/apps/web/src/lib/modules/writing/utils/reference-resolver.ts diff --git a/apps/mana/apps/web/src/lib/modules/writing/components/BriefingForm.svelte b/apps/mana/apps/web/src/lib/modules/writing/components/BriefingForm.svelte index 5ebb07f7b..bace8003f 100644 --- a/apps/mana/apps/web/src/lib/modules/writing/components/BriefingForm.svelte +++ b/apps/mana/apps/web/src/lib/modules/writing/components/BriefingForm.svelte @@ -8,7 +8,8 @@ import { KIND_LABELS, TONE_PRESETS, LENGTH_PRESETS, DEFAULT_LANGUAGE } from '../constants'; import { draftsStore } from '../stores/drafts.svelte'; import StylePicker from './StylePicker.svelte'; - import type { Draft, DraftKind, DraftBriefing } from '../types'; + import ReferencePicker from './ReferencePicker.svelte'; + import type { Draft, DraftKind, DraftBriefing, DraftReference } from '../types'; let { mode, @@ -62,6 +63,8 @@ let extraInstructions = $state(draft?.briefing.extraInstructions ?? ''); /* svelte-ignore state_referenced_locally */ let styleId = $state(draft?.styleId ?? null); + /* svelte-ignore state_referenced_locally */ + let references = $state([...(draft?.references ?? [])]); let saving = $state(false); let error = $state(null); @@ -100,6 +103,7 @@ title: title.trim(), briefing, styleId, + references, }); oncreated?.(created); } else if (draft) { @@ -107,6 +111,7 @@ title: title.trim(), kind, styleId, + references, }); await draftsStore.updateBriefing(draft.id, briefing); } @@ -189,6 +194,13 @@ (styleId = next)} /> +
+ + Quellen (optional — flowen als Kontext in den Prompt ein) + + (references = next)} /> +
+