diff --git a/src/lib/useSystemPrompts.ts b/src/lib/useSystemPrompts.ts new file mode 100644 index 0000000..77cbfc5 --- /dev/null +++ b/src/lib/useSystemPrompts.ts @@ -0,0 +1,71 @@ +import { useState, useEffect, useCallback } from "react"; + +export type SystemPrompts = { + all: string; + partial: string; + style_normal: string; + style_crazy: string; + lyrics_partial: string; +}; + +export function useSystemPrompts() { + const [prompts, setPrompts] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const fetchPrompts = useCallback(async () => { + try { + setLoading(true); + const res = await fetch("/api/system-prompts"); + if (!res.ok) throw new Error("Failed to fetch system prompts"); + const data = await res.json(); + setPrompts(data); + setError(null); + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + } + }, []); + + const savePrompts = async (newPrompts: SystemPrompts) => { + try { + setLoading(true); + const res = await fetch("/api/system-prompts", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(newPrompts), + }); + if (!res.ok) throw new Error("Failed to save system prompts"); + setPrompts(newPrompts); + setError(null); + return true; + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + return false; + } finally { + setLoading(false); + } + }; + + const resetPrompts = async () => { + try { + setLoading(true); + const res = await fetch("/api/system-prompts", { method: "DELETE" }); + if (!res.ok) throw new Error("Failed to reset system prompts"); + await fetchPrompts(); + return true; + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + return false; + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchPrompts(); + }, [fetchPrompts]); + + return { prompts, loading, error, savePrompts, resetPrompts }; +} diff --git a/src/lib/useVideoAiLinks.ts b/src/lib/useVideoAiLinks.ts new file mode 100644 index 0000000..f493343 --- /dev/null +++ b/src/lib/useVideoAiLinks.ts @@ -0,0 +1,61 @@ +import { useState, useEffect } from "react"; +import { getConfig } from "./llm"; +import type { VideoAiLink } from "./types"; + +const LOCAL_STORAGE_KEY = "melodymuse-video-links"; + +export function useVideoAiLinks() { + const [links, setLinks] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + let active = true; + + async function fetchLinks() { + try { + const local = window.localStorage.getItem(LOCAL_STORAGE_KEY); + if (local) { + const parsed = JSON.parse(local); + if (Array.isArray(parsed)) { + if (active) { + setLinks(parsed); + setLoading(false); + } + return; + } + } + + // Fetch from backend + const res = await getConfig(); + if (active) { + setLinks(res.videoAiLinks || []); + setLoading(false); + } + } catch (err) { + if (active) { + setLoading(false); + } + } + } + + fetchLinks(); + return () => { active = false; }; + }, []); + + const saveLinks = (newLinks: VideoAiLink[]) => { + setLinks(newLinks); + window.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(newLinks)); + }; + + const clearLocalLinks = () => { + window.localStorage.removeItem(LOCAL_STORAGE_KEY); + // Refresh to fetch defaults again + setLoading(true); + getConfig().then(res => { + setLinks(res.videoAiLinks || []); + setLoading(false); + }).catch(() => setLoading(false)); + }; + + return { links, loading, saveLinks, clearLocalLinks, hasLocal: window.localStorage.getItem(LOCAL_STORAGE_KEY) !== null }; +} diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx index c751ca2..c462046 100644 --- a/src/pages/SettingsPage.tsx +++ b/src/pages/SettingsPage.tsx @@ -364,7 +364,7 @@ export function SettingsPage() { ) : (