diff --git a/apps/gui/src/app/actions.ts b/apps/gui/src/app/actions.ts index 8ace413..4eb5d22 100644 --- a/apps/gui/src/app/actions.ts +++ b/apps/gui/src/app/actions.ts @@ -271,6 +271,12 @@ export async function createProviderInstance( ); } +export async function duplicateProviderInstance( + id: string, +): Promise { + return ProviderManager.duplicate(id); +} + export async function deleteProviderInstance(id: string): Promise { ProviderManager.delete(id); } diff --git a/apps/gui/src/components/config/ProviderInstancesConfig.tsx b/apps/gui/src/components/config/ProviderInstancesConfig.tsx index ad84f2b..c56efb5 100644 --- a/apps/gui/src/components/config/ProviderInstancesConfig.tsx +++ b/apps/gui/src/components/config/ProviderInstancesConfig.tsx @@ -7,6 +7,7 @@ import { setActiveProviderInstance, regenerateEmbeddings, deleteProviderInstance, + duplicateProviderInstance, fetchAvailableModels, fetchAvailableModelsForInstance, } from "@/app/actions"; @@ -338,6 +339,23 @@ export function ProviderInstancesConfig({ } }; + const handleDuplicate = async () => { + if (selectedInstanceId === "new" || selectedInstanceId === null) return; + try { + setLoading(true); + setError(""); + const duplicated = await duplicateProviderInstance(selectedInstanceId); + if (duplicated) { + setSelectedInstanceId(duplicated.id); + await onChanged(); + } + } catch (err) { + setError(err instanceof Error ? err.message : String(err)); + } finally { + setLoading(false); + } + }; + return (
{error && ( @@ -646,16 +664,26 @@ export function ProviderInstancesConfig({
-
+
{selectedInstanceId !== "new" && ( - + <> + + + )}
+ {modalEntities.map((ent) => ( + + ))} +
)} diff --git a/apps/gui/src/components/play/HandoffModal.tsx b/apps/gui/src/components/play/HandoffModal.tsx index 55af187..af97efa 100644 --- a/apps/gui/src/components/play/HandoffModal.tsx +++ b/apps/gui/src/components/play/HandoffModal.tsx @@ -33,7 +33,7 @@ export function HandoffModal({ entry, onClose }: HandoffModalProps) { return ( !open && onClose()}> - + Memory Handoff Details — {entry.entityName} @@ -240,7 +240,7 @@ export function HandoffModal({ entry, onClose }: HandoffModalProps) {

Raw JSON Output

-
+              
                 {handoffResult
                   ? JSON.stringify(handoffResult, null, 2)
                   : "No JSON Output recorded."}
diff --git a/apps/gui/src/components/play/InteractView.tsx b/apps/gui/src/components/play/InteractView.tsx
index 503b484..d2e4692 100644
--- a/apps/gui/src/components/play/InteractView.tsx
+++ b/apps/gui/src/components/play/InteractView.tsx
@@ -8,6 +8,7 @@ import { Textarea } from "@/components/ui/textarea";
 import { Spinner } from "@/components/ui/spinner";
 import { cn } from "@/lib/utils";
 import { hydrate } from "@omnia/voice";
+import { Brain, PersonStanding, Speech } from "lucide-react";
 import {
   Alert,
   AlertAction,
@@ -26,19 +27,14 @@ function IntentTag({
   playerId: string;
   entities: SimSnapshot["entities"];
 }) {
-  const labels: Record = {
-    monologue: "thought",
-    thought: "thought",
-    dialogue: "dialogue",
-    action: "action",
+  const icons: Record = {
+    monologue: ,
+    thought: ,
+    dialogue: ,
+    action: ,
   };
 
-  const label = labels[intent.type] || intent.type;
-
-  let outcome = "";
-  if (intent.type === "action") {
-    outcome = intent.isValid ? " ✅" : ` ❌ (${intent.reason})`;
-  }
+  const icon = icons[intent.type] || null;
 
   const viewerAliasesMap = new Map();
   if (entities) {
@@ -69,12 +65,28 @@ function IntentTag({
       
     ) : null;
 
+  const invalidActionReason =
+    intent.type === "action" && !intent.isValid && intent.reason
+      ? ` (${intent.reason})`
+      : "";
+
+  const invalidActionClassName =
+    intent.type === "action" && !intent.isValid ? " text-destructive" : "";
+
   return (
-    
-      [{label}] “{textToDisplay}”{modifiersStr}
-      {outcome}
-      {intent.minutesToAdvance ? ` [+${intent.minutesToAdvance}min]` : ""}
-    
+    <>
+      
+        
+          {icon}
+        
+        
+          “{textToDisplay}”{modifiersStr}
+          {invalidActionReason}
+          {intent.minutesToAdvance ? ` [+${intent.minutesToAdvance}min]` : ""}
+        
+      
+      
+ ); } @@ -105,45 +117,47 @@ function LogEntryCard({ entry: SimSnapshot["log"][number]; onShowPrompt: (entry: SimSnapshot["log"][number]) => void; isPlayerCard: boolean; - playerAliases: Record; + playerAlciases: Record; playerId: string; entities: SimSnapshot["entities"]; }) { const showMenu = !!(entry.rawPrompt || entry.decoderPrompt); return ( -
-
-
- - {entry.entityName} - - - Turn {entry.turn} · {formatSimTime(entry.timestamp)} - -
- {showMenu && ( - +
+
+
+
+ + {entry.entityName} + + + Turn {entry.turn} · {formatSimTime(entry.timestamp)} + +
+ {showMenu && ( + + )} +
+
+ {entry.narrativeProse} +
-
- {entry.narrativeProse} -
-
+
{entry.intents.map((intent, i) => ( {/* Scrollable Center Viewport */}
-
+
{snapshot.log.map((entry, i) => { if (entry.isHandoff) { return ( @@ -245,14 +259,14 @@ export function InteractView({ {/* Sticky Chat / Interaction Input Footer */}