// Thin wrappers around the /api/... contract in frontend/API_CONTRACT.md. // All calls are relative (e.g. "/api/lessons") so they work both: // - in dev, via the Vite proxy (vite.config.js -> http://localhost:7861) // - in production, served same-origin by app_custom.py at /custom/ const BASE = '/api' async function request(path, options = {}) { const res = await fetch(`${BASE}${path}`, { headers: options.body instanceof FormData ? undefined : { 'Content-Type': 'application/json' }, ...options, }) if (!res.ok) { let detail = res.statusText try { const data = await res.json() detail = data.error || data.detail || detail } catch { /* ignore */ } throw new Error(detail) } return res.json() } const get = (path) => request(path) const post = (path, body) => request(path, { method: 'POST', body: body instanceof FormData ? body : JSON.stringify(body ?? {}) }) const put = (path, body) => request(path, { method: 'PUT', body: JSON.stringify(body ?? {}) }) const patch = (path, body) => request(path, { method: 'PATCH', body: JSON.stringify(body ?? {}) }) const del = (path) => request(path, { method: 'DELETE' }) // Lessons export const listLessons = () => get('/lessons') export const getLesson = (id) => get(`/lessons/${id}`) export const saveLesson = (text, annotations) => post('/lessons', { text, annotations }) export const updateLesson = (id, text, annotations) => put(`/lessons/${id}`, { text, annotations }) export const renameLesson = (id, title) => patch(`/lessons/${id}/title`, { title }) export const deleteLesson = (id) => del(`/lessons/${id}`) // Resources export const listResources = () => get('/resources') export const updateResourceLinks = (pageId, links) => put(`/resources/${pageId}/links`, { links }) // Annotation / word card export const annotate = (text, colorsOn) => post('/annotate', { text, colors_on: colorsOn }) export const renderAnnotations = (annotations, colorsOn) => post('/render', { annotations, colors_on: colorsOn }) export const wordCard = (token, meanings) => post('/word-card', { ...token, meanings }) // Gender Checker export const genderCheck = (word) => post('/gender-check', { word }) // Translator export const translateText = (text, direction, lessonText) => post('/translate', { text, direction, lesson_text: lessonText }) // Chat export const sendChat = (message, history, lessonText) => post('/chat', { message, history, lesson_text: lessonText }) // Exercises — coach (mixed set) export const generateCoachSet = (lessonText, topic) => post('/exercises/coach', { lesson_text: lessonText, topic }) export const checkCoachExercise = (exercise, answer) => post('/exercises/coach/check', { exercise, answer }) // Exercises — dialogue export const startDialogue = (lessonText, topic) => post('/exercises/dialogue', { lesson_text: lessonText, topic }) export const sendDialogueReply = (dialogue, replies, reply) => post('/exercises/dialogue/reply', { dialogue, replies, reply }) // Exercises — visual export const generateSampleVisualExercise = (lessonText, topic) => post('/exercises/visual/sample', { lesson_text: lessonText, topic }) // Exercises — pronunciation export const getPronunciationTarget = (lessonText, topic, avoid) => post('/exercises/pronunciation/target', { lesson_text: lessonText, topic, avoid }) export const checkPronunciation = (target, transcription) => post('/exercises/pronunciation/check', { target, transcription }) // Summary export const getSummary = () => get('/summary')