// Photo help for a student (STU-6): a sheet to photograph a problem and send it to the teacher, then follow the request: waiting, being // worked on, and then the AI's hints one at a time (the steps and the answer only when asked), or the teacher's note. import { api } from "./api.js"; import { currentSubject } from "./ui.js"; import { esc, fmt } from "./math.js"; import { isTouch, prepare, pictureUrl, sendPicture } from "./media.js"; let layer = null, timer = null, shown = { hints: 1, works: new Set(), answer: false }; let statusPromise = null, statusAt = 0; /** {on, requests}: whether the student's classes have the AI on, and their latest requests. Remembered for a minute. */ export function status(fresh = false) { if (!statusPromise || fresh || Date.now() - statusAt > 60000) { statusAt = Date.now(); statusPromise = api("/api/ai/status").catch(() => ({ on: false, requests: [] })); } return statusPromise; } const STATE = { waiting: "ממתין למורה", processing: "ה־AI עובד על זה", done: "מוכן", declined: "המורה לא שלח/ה", cancelled: "בוטל", failed: "לא הצלחנו", closed: "טופל" }; export const stateWord = state => STATE[state] || state; const q = selector => layer?.querySelector(selector); const reminderSeen = () => { try { return localStorage.getItem("ai-reminder") === "1"; } catch { return false; } }; const seenReminder = () => { try { localStorage.setItem("ai-reminder", "1"); } catch { /* private window */ } }; function mount() { if (layer) return; layer = document.createElement("div"); layer.className = "ai-window"; layer.innerHTML = `
`; document.body.append(layer); layer.addEventListener("click", event => { if (event.target === layer || event.target.closest("[data-ai-close]")) close(); }); } export function close() { clearTimeout(timer); timer = null; layer?.remove(); layer = null; } const body = html => { q(".sheet").innerHTML = `המורה רואה את התמונה לפני שהיא נשלחת ל־AI, ולכן התשובה לא מגיעה מיד.
אל תכתבו שם או פרטים אישיים בתמונה.
${isTouch() ? `` : ""}
`); let chosen = null; layer.querySelectorAll("[data-ai-file]").forEach(input => input.addEventListener("change", async () => { const file = input.files[0]; input.value = ""; if (!file) return; chosen = await prepare(file); q("#ai-preview").innerHTML = `${esc(error.message)}
`); return; } if (!layer) return; draw(request); if (["waiting", "processing"].includes(request.state)) timer = setTimeout(() => layer && follow(id), 4000); } function draw(request) { const photo = request.picture ? `${esc(stateWord(request.state))}
${photo}`; if (request.state === "waiting") content += `המורה רואה את התמונה ומחליט/ה אם לשלוח אותה. אפשר לסגור, והבקשה תחכה.
`; if (request.state === "processing") content += `המורה שלח/ה את התמונה. עוד רגע התשובה כאן.
`; if (request.state === "declined") content += `המורה: ${request.teacher_note ? fmt(request.teacher_note) : "לא נשלח. אפשר לשאול את המורה."}
`; if (request.state === "failed") content += `${esc(request.failure?.message || "לא הצלחנו כרגע. נסו שוב מאוחר יותר.")}
`; if (request.state === "done") content += result(request); body(content); if (request.picture) pictureUrl(request.picture.id).then(url => { const img = q("#ai-photo"); if (img) { img.src = url; img.hidden = false; } }).catch(() => {}); q("#ai-cancel")?.addEventListener("click", async () => { await api(`/api/ai/requests/${request.id}/cancel`, { method: "POST" }); status(true); follow(request.id); }); layer.querySelectorAll("[data-more]").forEach(b => b.addEventListener("click", () => { reveal(request, b.dataset.more, Number(b.dataset.i)); })); } function result(request) { const r = request.result; if (!r.readable) return `${fmt(r.note || "לא הצלחנו לקרוא את התמונה. צלמו שוב.")}
`; const steps = r.steps || []; const hints = steps.slice(0, shown.hints).map((s, i) => `💡 ${fmt(s.hint)}
${shown.works.has(i) ? `${fmt(s.work)}
` : ``}התשובה: ${fmt(r.answer)}
` : ``; const skill = r.skill ? `המיומנות במסלול: ${fmt(r.skill.name)}
` : ""; return `כך קראנו את התרגיל: ${fmt(r.problem)}
${r.note ? `${fmt(r.note)}
` : ""}${nextHint}
${answer}
${skill}`; } function reveal(request, what, i) { if (what === "hint") shown.hints += 1; if (what === "work") shown.works.add(i); if (what === "answer") shown.answer = true; draw(request); } /** Open the sheet for a request (a new photo when `id` is not given); the hints start closed each time. */ export function open({ id = null, skillId = null, classId = null } = {}) { shown = { hints: 1, works: new Set(), answer: false }; return id ? follow(id) : openNew({ skillId, classId }); } export const isOpen = () => !!layer;