From 12072c6b6c87e761f7f683ac84698fcfc2043646 Mon Sep 17 00:00:00 2001 From: Till JS Date: Wed, 15 Apr 2026 14:24:38 +0200 Subject: [PATCH] =?UTF-8?q?feat(kontext):=20URL=20import=20helpers=20?= =?UTF-8?q?=E2=80=94=20API=20client=20+=20appendContent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Frontend plumbing for the Kontext "Aus URL" inline panel (the UI itself was committed earlier as part of 003f75f7e): - kontext/api.ts: new module-scoped fetch wrapper that talks to /api/v1/context/import-url with a Bearer token. Kept in kontext/ rather than a shared helper because it's the only caller; moving it to `context/` would mix two unrelated modules again. - kontext/stores/kontext.svelte.ts: new appendContent(chunk) method. The singleton row is encrypted at rest, so we decrypt the current content before concatenating with a '\n\n---\n\n' separator and writing back — going through setContent() to keep encryption + Dexie hook behaviour consistent. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../apps/web/src/lib/modules/kontext/api.ts | 44 +++++++++++++++++++ .../modules/kontext/stores/kontext.svelte.ts | 11 ++++- 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 apps/mana/apps/web/src/lib/modules/kontext/api.ts diff --git a/apps/mana/apps/web/src/lib/modules/kontext/api.ts b/apps/mana/apps/web/src/lib/modules/kontext/api.ts new file mode 100644 index 000000000..6af5b8dba --- /dev/null +++ b/apps/mana/apps/web/src/lib/modules/kontext/api.ts @@ -0,0 +1,44 @@ +/** + * Kontext API client — talks to apps/api `POST /api/v1/context/import-url`. + * + * The server route lives under /context for historical reasons (shared + * crawler + LLM wrapper). Only the kontext singleton consumes it. + */ + +import { authStore } from '$lib/stores/auth.svelte'; +import { getManaApiUrl } from '$lib/api/config'; + +export type CrawlMode = 'single' | 'deep'; + +export interface ImportInput { + url: string; + mode: CrawlMode; + summarize: boolean; +} + +export interface ImportResponse { + title: string; + content: string; + sourceUrl: string; + crawlMode: CrawlMode; + crawledAt: string; + pageCount: number; +} + +export async function crawlUrlViaApi(input: ImportInput): Promise { + const token = await authStore.getValidToken(); + if (!token) throw new Error('not authenticated'); + const res = await fetch(`${getManaApiUrl()}/api/v1/context/import-url`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify(input), + }); + if (!res.ok) { + const body = (await res.json().catch(() => ({}))) as { error?: string }; + throw new Error(body.error || `import failed (${res.status})`); + } + return (await res.json()) as ImportResponse; +} diff --git a/apps/mana/apps/web/src/lib/modules/kontext/stores/kontext.svelte.ts b/apps/mana/apps/web/src/lib/modules/kontext/stores/kontext.svelte.ts index 791506e56..712b790c1 100644 --- a/apps/mana/apps/web/src/lib/modules/kontext/stores/kontext.svelte.ts +++ b/apps/mana/apps/web/src/lib/modules/kontext/stores/kontext.svelte.ts @@ -6,7 +6,7 @@ */ import { kontextDocTable } from '../collections'; -import { encryptRecord } from '$lib/data/crypto'; +import { encryptRecord, decryptRecords } from '$lib/data/crypto'; import { KONTEXT_SINGLETON_ID, type LocalKontextDoc } from '../types'; export const kontextStore = { @@ -30,4 +30,13 @@ export const kontextStore = { await encryptRecord('kontextDoc', diff); await kontextDocTable.update(KONTEXT_SINGLETON_ID, diff); }, + + async appendContent(chunk: string): Promise { + await this.ensureDoc(); + const row = await kontextDocTable.get(KONTEXT_SINGLETON_ID); + const [decrypted] = row ? await decryptRecords('kontextDoc', [row]) : []; + const current = decrypted?.content ?? ''; + const separator = current.trim() ? '\n\n---\n\n' : ''; + await this.setContent(`${current}${separator}${chunk}`); + }, };