building out the components, updating the UI in overview and adding a little sqlite viewer

This commit is contained in:
itsamejms
2026-07-17 09:31:51 +01:00
parent 82f2dcf3df
commit b89a39ec9d
30 changed files with 2664 additions and 635 deletions
+35 -4
View File
@@ -7,12 +7,20 @@ import { useToast } from "./Toast";
import { addGeneration, extractTitle, type Generation } from "../lib/generations";
import { usePrefillEffect } from "../lib/usePrefill";
interface ItemMechanics {
attunement: string; // "none", "yes", or a condition string
charges: string; // "0" or "N (regen 1d6 per dawn)"
value: string; // "500 gp"
weight: string; // "2 lbs"
effect: string; // one-line effect description
}
interface ItemResponse {
name: string;
rarity: string;
type: string;
description: string;
mechanical: string | Record<string, unknown>;
mechanical: string | Record<string, unknown> | ItemMechanics;
lore: string;
}
@@ -67,11 +75,11 @@ The JSON must have exactly these keys:
- "rarity": "${rarity}" (string)
- "type": "${itemType}" (string)
- "description": a vivid 2-3 sentence physical description (string)
- "mechanical": the item's game mechanics as a single descriptive paragraph (string, NOT an object)
- "mechanical": an object with keys attunement (string: "none", "yes", or a condition), charges (string), value (string), weight (string), and effect (a one-line string describing the item's mechanical effect)
- "lore": a 1-2 sentence piece of lore or history (string)`,
system: "You are a creative D&D item designer. You MUST respond with ONLY valid JSON. No markdown fences, no code blocks, no explanation. Just the JSON object.",
temperature: 0.85,
max_tokens: 500,
max_tokens: 600,
ragQuery: `${rarity} ${itemType} ${prompt}`,
},
});
@@ -108,6 +116,12 @@ The JSON must have exactly these keys:
};
const mechanicalText = item ? flattenValue(item.mechanical) : "";
// ponytail: structured mechanics if the LLM returned the object form; fall
// back to a paragraph string for older generations.
const mech = item?.mechanical;
const isStructured =
typeof mech === "object" && mech !== null && "attunement" in mech && "effect" in mech;
const structured = isStructured ? (mech as ItemMechanics) : null;
const hasParseError = !item && rawResponse;
// ponytail: one-click "surprise me" — random rarity + type, no typed prompt.
@@ -217,7 +231,24 @@ The JSON must have exactly these keys:
<div className="rounded-lg bg-[var(--color-bg-deep)] border border-[var(--color-border-glass)] p-3">
<span className="text-[var(--color-gold-bright)] text-[10px] font-medium uppercase tracking-wider">Mechanics</span>
<p className="text-[var(--color-text-primary)] text-sm mt-0.5">{mechanicalText}</p>
{structured ? (
<div className="mt-1 flex flex-col gap-1">
{/* ponytail: structured fields — attunement/charges/value/weight + one-line effect */}
<div className="flex flex-wrap gap-x-3 gap-y-0.5 text-xs">
<span className="text-[var(--color-text-dim)]">Attunement: </span>
<span className="text-[var(--color-text-primary)]">{structured.attunement === "none" || !structured.attunement ? "No" : structured.attunement}</span>
<span className="text-[var(--color-text-dim)]">Charges: </span>
<span className="text-[var(--color-text-primary)]">{structured.charges || "—"}</span>
<span className="text-[var(--color-text-dim)]">Value: </span>
<span className="text-[var(--color-text-primary)]">{structured.value || "—"}</span>
<span className="text-[var(--color-text-dim)]">Weight: </span>
<span className="text-[var(--color-text-primary)]">{structured.weight || "—"}</span>
</div>
<p className="text-[var(--color-text-primary)] text-sm mt-1">{structured.effect}</p>
</div>
) : (
<p className="text-[var(--color-text-primary)] text-sm mt-0.5">{mechanicalText}</p>
)}
</div>
<div>