From 663d409a464d2438029e5223b06f7a2bd162944b Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Wed, 15 Jul 2026 19:01:12 +0530 Subject: [PATCH] feat(builder): implement dynamic UUID generation --- apps/gui/src/app/builder/page.tsx | 56 ++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/apps/gui/src/app/builder/page.tsx b/apps/gui/src/app/builder/page.tsx index 1680582..ca1a694 100644 --- a/apps/gui/src/app/builder/page.tsx +++ b/apps/gui/src/app/builder/page.tsx @@ -82,6 +82,17 @@ interface EntityData { initialMemories: MemoryData[]; } +const generateUUID = () => { + if (typeof window !== "undefined" && window.crypto && window.crypto.randomUUID) { + return window.crypto.randomUUID(); + } + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => { + const r = (Math.random() * 16) | 0; + const v = c === "x" ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); +}; + export default function BuilderPage() { const router = useRouter(); @@ -95,17 +106,32 @@ export default function BuilderPage() { const [activeTab, setActiveTab] = useState<"metadata" | "locations" | "entities" | "json">("metadata"); // Form State - const [scenarioId, setScenarioId] = useState("custom-scenario"); + const [scenarioId, setScenarioId] = useState(""); const [name, setName] = useState("My Custom Scenario"); const [description, setDescription] = useState("A custom scenario template created via builder."); const [startTime, setStartTime] = useState("2026-07-06T12:00:00.000Z"); const [worldAttributes, setWorldAttributes] = useState([]); - const [locations, setLocations] = useState([ - { id: "room-1", attributes: [], connections: [] } - ]); - const [entities, setEntities] = useState([ - { id: "hero-1", locationId: "room-1", attributes: [{ name: "role", value: "adventurer", visibility: "PUBLIC", allowedEntities: [] }], aliases: {}, initialMemories: [] } - ]); + const [locations, setLocations] = useState([]); + const [entities, setEntities] = useState([]); + + // Initialize dynamic UUIDs client-side to prevent NextJS SSR hydration mismatch + useEffect(() => { + if (!scenarioId) { + const uId = generateUUID(); + const locId = generateUUID(); + const entId = generateUUID(); + + setScenarioId(uId); + setLocations([{ id: locId, attributes: [], connections: [] }]); + setEntities([{ + id: entId, + locationId: locId, + attributes: [{ name: "role", value: "adventurer", visibility: "PUBLIC", allowedEntities: [] }], + aliases: {}, + initialMemories: [] + }]); + } + }, [scenarioId]); // Selected sub-items for active editing lists const [selectedLocIndex, setSelectedLocIndex] = useState(0); @@ -192,7 +218,7 @@ export default function BuilderPage() { bidirectional: c.bidirectional ?? true, })), })); - setLocations(locs.length > 0 ? locs : [{ id: "room-1", attributes: [], connections: [] }]); + setLocations(locs.length > 0 ? locs : [{ id: generateUUID(), attributes: [], connections: [] }]); setSelectedLocIndex(0); // Entities @@ -207,7 +233,7 @@ export default function BuilderPage() { })), aliases: e.aliases || {}, initialMemories: (e.initialMemories || []).map(m => ({ - id: m.id || "mem-" + Math.random().toString(36).substr(2, 9), + id: m.id || generateUUID(), timestamp: m.timestamp || s.startTime, locationId: m.locationId || null, intent: { @@ -225,7 +251,7 @@ export default function BuilderPage() { } : undefined, })), })); - setEntities(ents.length > 0 ? ents : [{ id: "hero-1", locationId: locs[0]?.id || "room-1", attributes: [], aliases: {}, initialMemories: [] }]); + setEntities(ents.length > 0 ? ents : [{ id: generateUUID(), locationId: locs[0]?.id || generateUUID(), attributes: [], aliases: {}, initialMemories: [] }]); setSelectedEntIndex(0); setStatusMessage({ text: "Template loaded successfully!", type: "success" }); @@ -587,7 +613,7 @@ export default function BuilderPage() {

- Scenario Builder + Scenario Builder

@@ -716,7 +742,7 @@ export default function BuilderPage() { type="button" size="sm" onClick={() => { - const newId = `room-${locations.length + 1}`; + const newId = generateUUID(); setLocations([...locations, { id: newId, attributes: [], connections: [] }]); setSelectedLocIndex(locations.length); }} @@ -944,7 +970,7 @@ export default function BuilderPage() { type="button" size="sm" onClick={() => { - const newId = `actor-${entities.length + 1}`; + const newId = generateUUID(); setEntities([...entities, { id: newId, locationId: locationIds[0], attributes: [], aliases: {}, initialMemories: [] }]); setSelectedEntIndex(entities.length); }} @@ -1031,7 +1057,7 @@ export default function BuilderPage() { onChange={(newAttrs) => { const copy = [...entities]; copy[selectedEntIndex].attributes = newAttrs; - setLocations(copy); // Typo check: actually it's setEntities. Let's make sure it calls setEntities(copy) instead of setLocations(copy)! + setEntities(copy); }} onAdd={() => { const copy = [...entities]; @@ -1125,7 +1151,7 @@ export default function BuilderPage() { onClick={() => { const copy = [...entities]; const newMem: MemoryData = { - id: "mem-" + Math.random().toString(36).substr(2, 9), + id: generateUUID(), timestamp: startTime, locationId: copy[selectedEntIndex].locationId || null, intent: {