wordeck/apps/web/src/lib/api/decks.ts
Till JS dc382a795d feat(api): URL-Kontext auch in /decks/generate + fetchUrlContent extrahieren
- `lib/url-fetch.ts`: fetchUrlContent aus decks-from-image herausgezogen
  — gemeinsam genutzte Logik für mana-search + direktes HTTP-Fetch-Fallback
- `decks-generate.ts`: optionales `url`-Feld im Input-Schema;
  URL-Inhalt wird an den Prompt angehängt wenn vorhanden
- `decks.ts` (web): `generateDeck()` akzeptiert jetzt `url?: string`
- UI: imageUrl wird für Text-KI + Bild-KI als Kontext genutzt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 16:39:39 +02:00

64 lines
2.3 KiB
TypeScript

import type { Deck, DeckCreate, DeckUpdate } from '@cards/domain';
import { api, apiForm } from './client.ts';
export function listDecks(opts: { forkedFromMarketplace?: boolean } = {}) {
const qs = opts.forkedFromMarketplace ? '?forked_from_marketplace=true' : '';
return api<{ decks: Deck[]; total: number }>(`/api/v1/decks${qs}`);
}
export function getDeck(id: string) {
return api<Deck>(`/api/v1/decks/${id}`);
}
export function createDeck(input: DeckCreate) {
return api<Deck>('/api/v1/decks', { method: 'POST', body: input });
}
export function updateDeck(id: string, patch: DeckUpdate) {
return api<Deck>(`/api/v1/decks/${id}`, { method: 'PATCH', body: patch });
}
export function deleteDeck(id: string) {
return api<{ deleted: string }>(`/api/v1/decks/${id}`, { method: 'DELETE' });
}
export function generateDeck(input: { prompt: string; language?: 'de' | 'en'; count?: number; url?: string }) {
return api<{ deck: Deck; cards_created: number }>('/api/v1/decks/generate', {
method: 'POST',
body: input,
});
}
export function fetchDistractors(
deckId: string,
opts: { cardId?: string; count?: number; field?: string } = {},
) {
const params = new URLSearchParams();
if (opts.cardId) params.set('card_id', opts.cardId);
if (opts.count) params.set('count', String(opts.count));
if (opts.field) params.set('field', opts.field);
const qs = params.size ? `?${params}` : '';
return api<{ distractors: string[] }>(`/api/v1/decks/${deckId}/distractors${qs}`);
}
export function generateDeckFromImage(
files: File | File[],
opts: { language?: 'de' | 'en'; count?: number; url?: string },
) {
const arr = Array.isArray(files) ? files : [files];
// URL-only (no files): send as JSON — FormData without file parts fails in Bun
if (arr.length === 0) {
return api<{ deck: Deck; cards_created: number }>('/api/v1/decks/from-image', {
method: 'POST',
body: { language: opts.language, count: opts.count, url: opts.url },
});
}
const form = new FormData();
for (const f of arr) form.append('file', f);
if (opts.language) form.append('language', opts.language);
if (opts.count != null) form.append('count', String(opts.count));
if (opts.url) form.append('url', opts.url);
return apiForm<{ deck: Deck; cards_created: number }>('/api/v1/decks/from-image', form);
}