diff options
| author | soryu <soryu@soryu.co> | 2026-01-16 12:23:49 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-01-16 12:23:49 +0000 |
| commit | 205ab8a223ddf6591a3e8bfc9108506502977c11 (patch) | |
| tree | d768063acff233dbeea223d7b6ea69d7e3038300 /makima/frontend/src/components/history/ConversationView.tsx | |
| parent | 05931d19bc0c161d0177c3f983d0cd903d5e8ae3 (diff) | |
| download | soryu-205ab8a223ddf6591a3e8bfc9108506502977c11.tar.gz soryu-205ab8a223ddf6591a3e8bfc9108506502977c11.zip | |
Fixup: use default api.makima.jp URL and fix default branch detection
Also add checkpointing/history
Diffstat (limited to 'makima/frontend/src/components/history/ConversationView.tsx')
| -rw-r--r-- | makima/frontend/src/components/history/ConversationView.tsx | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/makima/frontend/src/components/history/ConversationView.tsx b/makima/frontend/src/components/history/ConversationView.tsx new file mode 100644 index 0000000..e3d1110 --- /dev/null +++ b/makima/frontend/src/components/history/ConversationView.tsx @@ -0,0 +1,114 @@ +import type { + TaskConversationResponse, + SupervisorConversationResponse, +} from "../../lib/api"; +import { ConversationMessage } from "./ConversationMessage"; + +interface ConversationViewProps { + conversation: TaskConversationResponse | SupervisorConversationResponse; +} + +// Type guard for task conversation +function isTaskConversation( + conv: TaskConversationResponse | SupervisorConversationResponse +): conv is TaskConversationResponse { + return "taskId" in conv; +} + +// Type guard for supervisor conversation +function isSupervisorConversation( + conv: TaskConversationResponse | SupervisorConversationResponse +): conv is SupervisorConversationResponse { + return "supervisorTaskId" in conv; +} + +export function ConversationView({ conversation }: ConversationViewProps) { + const messages = conversation.messages; + + return ( + <div className="flex flex-col h-full"> + {/* Header info */} + <div className="shrink-0 p-3 border-b border-[rgba(117,170,252,0.1)] bg-[rgba(0,0,0,0.2)]"> + <div className="flex items-center justify-between"> + <div> + {isTaskConversation(conversation) ? ( + <div className="font-mono text-xs text-[#9bc3ff]"> + {conversation.taskName} + <span + className={`ml-2 text-[9px] uppercase px-1.5 py-0.5 border ${ + conversation.status === "done" + ? "text-emerald-400 border-emerald-400/30" + : conversation.status === "running" + ? "text-green-400 border-green-400/30" + : conversation.status === "failed" + ? "text-red-400 border-red-400/30" + : "text-[#7788aa] border-[rgba(117,170,252,0.25)]" + }`} + > + {conversation.status} + </span> + </div> + ) : isSupervisorConversation(conversation) ? ( + <div className="font-mono text-xs text-[#9bc3ff]"> + Supervisor + <span className="ml-2 text-[9px] uppercase text-cyan-400 px-1.5 py-0.5 border border-cyan-400/30"> + {conversation.phase} + </span> + </div> + ) : null} + </div> + + <div className="flex items-center gap-4 font-mono text-[10px] text-[#556677]"> + <span>{messages.length} messages</span> + {isTaskConversation(conversation) && conversation.totalTokens && ( + <span>{conversation.totalTokens.toLocaleString()} tokens</span> + )} + {isTaskConversation(conversation) && + conversation.totalCost !== null && + conversation.totalCost > 0 && ( + <span>${conversation.totalCost.toFixed(4)}</span> + )} + </div> + </div> + + {/* Spawned tasks (supervisor only) */} + {isSupervisorConversation(conversation) && conversation.spawnedTasks.length > 0 && ( + <div className="mt-2 flex flex-wrap gap-2"> + <span className="font-mono text-[9px] text-[#7788aa] uppercase">Spawned:</span> + {conversation.spawnedTasks.map((task) => ( + <span + key={task.taskId} + className={`font-mono text-[9px] px-1.5 py-0.5 border ${ + task.status === "done" + ? "text-emerald-400 border-emerald-400/30" + : task.status === "running" + ? "text-green-400 border-green-400/30" + : task.status === "failed" + ? "text-red-400 border-red-400/30" + : "text-[#7788aa] border-[rgba(117,170,252,0.25)]" + }`} + > + {task.taskName} + </span> + ))} + </div> + )} + </div> + + {/* Messages */} + <div className="flex-1 overflow-y-auto"> + {messages.length === 0 ? ( + <div className="flex items-center justify-center h-32"> + <div className="font-mono text-[#7788aa] text-xs">No messages</div> + </div> + ) : ( + <div className="divide-y divide-[rgba(117,170,252,0.05)]"> + {messages.map((message, index) => ( + <ConversationMessage key={message.id || index} message={message} /> + ))} + </div> + )} + </div> + </div> + ); +} |
