Refactor Study Desk application structure
This commit is contained in:
parent
faaccf8a7e
commit
089439ed90
145 changed files with 8087 additions and 3412 deletions
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
|
||||
export function GenerateTab({ importType }: { importType: "flashcards" | "quizzes" | "connections" | "crossword" }) {
|
||||
const [instructions, setInstructions] = useState("");
|
||||
|
|
@ -9,19 +9,39 @@ export function GenerateTab({ importType }: { importType: "flashcards" | "quizze
|
|||
const [saveResult, setSaveResult] = useState<"success" | "error" | null>(null);
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [packCount, setPackCount] = useState(1);
|
||||
const [loadError, setLoadError] = useState<string | null>(null);
|
||||
|
||||
const batchOverride = importType === "connections" && packCount > 1
|
||||
? `TEMPORARY BATCH OVERRIDE:\nGenerate exactly ${packCount} distinct Connections packs. Return one raw JSON array containing exactly ${packCount} objects that each follow the schema below. Make the packs meaningfully different from one another. This override takes precedence over any later instruction to return one object.\n\n`
|
||||
: "";
|
||||
const displayedInstructions = `${batchOverride}${instructions}`;
|
||||
|
||||
useEffect(() => {
|
||||
fetch(`/api/settings/llm-instructions?type=${importType}`)
|
||||
.then((res) => res.json())
|
||||
.then((data) => setInstructions(data.value))
|
||||
.finally(() => setLoading(false));
|
||||
const loadInstructions = useCallback(async (signal?: AbortSignal) => {
|
||||
setLoading(true);
|
||||
setLoadError(null);
|
||||
try {
|
||||
const response = await fetch(`/api/settings/llm-instructions?type=${importType}`, { signal });
|
||||
if (!response.ok) throw new Error("Instructions could not be loaded.");
|
||||
const data = await response.json() as { value?: unknown };
|
||||
if (typeof data.value !== "string") throw new Error("The server returned invalid instructions.");
|
||||
setInstructions(data.value);
|
||||
} catch (error) {
|
||||
if (signal?.aborted) return;
|
||||
setLoadError(error instanceof Error ? error.message : "Instructions could not be loaded.");
|
||||
} finally {
|
||||
if (!signal?.aborted) setLoading(false);
|
||||
}
|
||||
}, [importType]);
|
||||
|
||||
useEffect(() => {
|
||||
const controller = new AbortController();
|
||||
const timer = window.setTimeout(() => void loadInstructions(controller.signal), 0);
|
||||
return () => {
|
||||
window.clearTimeout(timer);
|
||||
controller.abort();
|
||||
};
|
||||
}, [loadInstructions]);
|
||||
|
||||
async function handleSave() {
|
||||
setSaving(true);
|
||||
setSaveResult(null);
|
||||
|
|
@ -50,25 +70,44 @@ export function GenerateTab({ importType }: { importType: "flashcards" | "quizze
|
|||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ value: "__RESET__" }),
|
||||
});
|
||||
if (!res.ok) throw new Error("Failed to reset instructions");
|
||||
const data = await res.json();
|
||||
if (typeof data.value !== "string") throw new Error("Invalid reset response");
|
||||
setInstructions(data.value);
|
||||
setSaveResult("success");
|
||||
} catch {
|
||||
setSaveResult("error");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCopy() {
|
||||
await navigator.clipboard.writeText(displayedInstructions);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
try {
|
||||
await navigator.clipboard.writeText(displayedInstructions);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch {
|
||||
setLoadError("Clipboard access failed. Select and copy the instructions manually.");
|
||||
}
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <div className="animate-subtle-pulse text-text-muted">Loading instructions...</div>;
|
||||
}
|
||||
|
||||
if (loadError && !instructions) {
|
||||
return (
|
||||
<div role="alert" className="rounded-xl border border-error/30 bg-error-bg p-4 text-error">
|
||||
<p>{loadError}</p>
|
||||
<button onClick={() => void loadInstructions()} className="mt-3 min-h-10 rounded-lg border border-error/40 px-4 text-sm font-bold">Retry</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{loadError && <p role="alert" className="rounded-lg bg-error-bg p-3 text-sm text-error">{loadError}</p>}
|
||||
<p className="text-sm text-text-secondary">
|
||||
Copy these instructions and paste them into an LLM chat along with your study material. The LLM will generate JSON you can paste into the Import tab.
|
||||
</p>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue