minor UI fixes

This commit is contained in:
itsamejms
2026-07-13 10:39:51 +01:00
parent a24f3615e0
commit a6171a8158
31 changed files with 1528 additions and 508 deletions
+26 -11
View File
@@ -5,6 +5,7 @@ import { addToLore } from "../lib/lore";
import { useToast } from "./Toast";
import { addGeneration, extractTitle, type Generation } from "../lib/generations";
import { usePrefillEffect } from "../lib/usePrefill";
import { usePersistentState } from "../lib/usePersistentState";
interface WorldResponse {
name: string;
@@ -21,12 +22,18 @@ interface Props {
}
export function WorldBuilder({ prefill, onPrefillConsumed }: Props = {}) {
const [theme, setTheme] = useState("high fantasy");
const [world, setWorld] = useState<WorldResponse | null>(null);
// ponytail: persist theme + last world so a reload keeps the DM's work.
const [theme, setTheme] = usePersistentState<string>("world.theme", "high fantasy");
const [world, setWorld] = usePersistentState<WorldResponse | null>("world.last", null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const { addToast } = useToast();
// ponytail: genre presets as a quick-pick row.
const THEME_PRESETS = [
"high fantasy", "dark fantasy", "sword & sorcery",
"steampunk", "post-apocalyptic", "horror", "sci-fantasy",
];
usePrefillEffect(prefill ?? null, "world", () => onPrefillConsumed?.(), (g) => {
const parsed = parseLlmJson<WorldResponse>(g.data);
if (parsed) {
@@ -37,7 +44,6 @@ export function WorldBuilder({ prefill, onPrefillConsumed }: Props = {}) {
async function generate() {
setLoading(true);
setError("");
setWorld(null);
try {
const result = await invoke<string>("generate", {
@@ -75,11 +81,9 @@ IMPORTANT: Return ONLY a raw JSON object. NO markdown fences, NO code blocks, NO
`${parsed.name || "World"} (${theme}). ${parsed.description}\nRegions: ${regions}\nLandmarks: ${landmarks}\nConflicts: ${conflicts}\nCultures: ${cultures}`,
);
} else {
setError("Could not parse LLM response. Try again or check your LLM connection.");
addToast("LLM returned an unparseable response", "error");
}
} catch (e) {
setError(String(e));
addToast(`World generation failed: ${e}`, "error");
}
setLoading(false);
@@ -91,6 +95,22 @@ IMPORTANT: Return ONLY a raw JSON object. NO markdown fences, NO code blocks, NO
<label className="text-[var(--color-text-secondary)] text-xs font-medium">
World Theme
</label>
<div className="flex flex-wrap gap-1.5 mb-1">
{THEME_PRESETS.map((t) => (
<button
key={t}
type="button"
onClick={() => setTheme(t)}
className={`rounded-lg px-2.5 py-1 text-xs border transition-colors cursor-pointer ${
theme === t
? "bg-[var(--color-gold-glow)] border-[var(--color-gold-bright)] text-[var(--color-gold-bright)]"
: "bg-[var(--color-bg-surface)] border-[var(--color-border-glass)] text-[var(--color-text-dim)] hover:text-[var(--color-text-secondary)]"
}`}
>
{t}
</button>
))}
</div>
<input
className="rounded-lg bg-[var(--color-bg-surface)] border border-[var(--color-border-glass)] px-3 py-2 text-[var(--color-text-primary)] placeholder-[var(--color-text-dim)] focus:outline-none focus:border-[var(--color-gold-bright)] text-sm"
value={theme}
@@ -107,11 +127,6 @@ IMPORTANT: Return ONLY a raw JSON object. NO markdown fences, NO code blocks, NO
</button>
</div>
{error && (
<div className="rounded-lg bg-[var(--color-danger)]/10 border border-[var(--color-danger)]/30 p-3 text-[var(--color-danger)] text-xs overflow-x-auto">
{error}
</div>
)}
{world && (
<div className="flex flex-col gap-3">