From 205ab8a223ddf6591a3e8bfc9108506502977c11 Mon Sep 17 00:00:00 2001
From: soryu <soryu@soryu.co>
Date: Fri, 16 Jan 2026 12:23:49 +0000
Subject: Fixup: use default api.makima.jp URL and fix default branch detection

Also add checkpointing/history
---
 .../src/components/history/ConversationMessage.tsx | 147 +++++++++++++++++++++
 1 file changed, 147 insertions(+)
 create mode 100644 makima/frontend/src/components/history/ConversationMessage.tsx

(limited to 'makima/frontend/src/components/history/ConversationMessage.tsx')

diff --git a/makima/frontend/src/components/history/ConversationMessage.tsx b/makima/frontend/src/components/history/ConversationMessage.tsx
new file mode 100644
index 0000000..43c0ed0
--- /dev/null
+++ b/makima/frontend/src/components/history/ConversationMessage.tsx
@@ -0,0 +1,147 @@
+import { useState } from "react";
+import type { ConversationMessage as ConversationMessageType } from "../../lib/api";
+
+interface ConversationMessageProps {
+  message: ConversationMessageType;
+}
+
+// Get role styling
+function getRoleStyle(role: string) {
+  switch (role.toLowerCase()) {
+    case "user":
+      return { label: "User", color: "text-[#9bc3ff]", bg: "bg-[rgba(155,195,255,0.1)]" };
+    case "assistant":
+      return { label: "Assistant", color: "text-emerald-400", bg: "bg-[rgba(52,211,153,0.1)]" };
+    case "system":
+      return { label: "System", color: "text-yellow-400", bg: "bg-[rgba(250,204,21,0.1)]" };
+    case "tool":
+      return { label: "Tool", color: "text-purple-400", bg: "bg-[rgba(192,132,252,0.1)]" };
+    default:
+      return { label: role, color: "text-[#7788aa]", bg: "bg-[rgba(119,136,170,0.1)]" };
+  }
+}
+
+// Format JSON for display
+function formatJson(data: unknown): string {
+  try {
+    return JSON.stringify(data, null, 2);
+  } catch {
+    return String(data);
+  }
+}
+
+export function ConversationMessage({ message }: ConversationMessageProps) {
+  const [showToolDetails, setShowToolDetails] = useState(false);
+  const { label, color, bg } = getRoleStyle(message.role);
+
+  const hasToolInfo = message.toolName || message.toolCalls?.length;
+
+  return (
+    <div className={`p-3 ${bg} border-l-2 border-transparent hover:border-[rgba(117,170,252,0.3)]`}>
+      {/* Header */}
+      <div className="flex items-center justify-between mb-2">
+        <div className="flex items-center gap-2">
+          <span className={`font-mono text-[10px] uppercase ${color}`}>{label}</span>
+          {message.toolName && (
+            <span className="font-mono text-[9px] text-purple-400 px-1.5 py-0.5 border border-[rgba(192,132,252,0.3)]">
+              {message.toolName}
+            </span>
+          )}
+        </div>
+        <div className="flex items-center gap-3">
+          {message.tokenCount && (
+            <span className="font-mono text-[9px] text-[#556677]">
+              {message.tokenCount.toLocaleString()} tokens
+            </span>
+          )}
+          {message.costUsd !== undefined && message.costUsd > 0 && (
+            <span className="font-mono text-[9px] text-[#556677]">
+              ${message.costUsd.toFixed(4)}
+            </span>
+          )}
+          <span className="font-mono text-[9px] text-[#556677]">
+            {new Date(message.timestamp).toLocaleTimeString()}
+          </span>
+        </div>
+      </div>
+
+      {/* Content */}
+      <div className="font-mono text-xs text-[#dbe7ff] whitespace-pre-wrap break-words">
+        {message.content}
+      </div>
+
+      {/* Tool calls */}
+      {hasToolInfo && (
+        <div className="mt-2">
+          <button
+            onClick={() => setShowToolDetails(!showToolDetails)}
+            className="font-mono text-[9px] text-purple-400 hover:text-purple-300 uppercase flex items-center gap-1"
+          >
+            <svg
+              className={`w-3 h-3 transition-transform ${showToolDetails ? "rotate-90" : ""}`}
+              fill="none"
+              stroke="currentColor"
+              viewBox="0 0 24 24"
+            >
+              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
+            </svg>
+            {message.toolCalls?.length
+              ? `${message.toolCalls.length} tool call${message.toolCalls.length > 1 ? "s" : ""}`
+              : "Tool details"}
+          </button>
+
+          {showToolDetails && (
+            <div className="mt-2 space-y-2">
+              {/* Tool input */}
+              {message.toolInput && (
+                <div className="p-2 bg-[rgba(0,0,0,0.3)] border border-[rgba(192,132,252,0.2)]">
+                  <div className="font-mono text-[9px] text-purple-400 uppercase mb-1">Input</div>
+                  <pre className="font-mono text-[10px] text-[#9bc3ff] overflow-x-auto">
+                    {formatJson(message.toolInput)}
+                  </pre>
+                </div>
+              )}
+
+              {/* Tool result */}
+              {message.toolResult && (
+                <div
+                  className={`p-2 border ${
+                    message.isError
+                      ? "bg-[rgba(239,68,68,0.1)] border-[rgba(239,68,68,0.3)]"
+                      : "bg-[rgba(0,0,0,0.3)] border-[rgba(192,132,252,0.2)]"
+                  }`}
+                >
+                  <div
+                    className={`font-mono text-[9px] uppercase mb-1 ${
+                      message.isError ? "text-red-400" : "text-purple-400"
+                    }`}
+                  >
+                    {message.isError ? "Error" : "Result"}
+                  </div>
+                  <pre className="font-mono text-[10px] text-[#9bc3ff] overflow-x-auto max-h-48 overflow-y-auto">
+                    {message.toolResult}
+                  </pre>
+                </div>
+              )}
+
+              {/* Multiple tool calls */}
+              {message.toolCalls?.map((call, i) => (
+                <div
+                  key={call.id || i}
+                  className="p-2 bg-[rgba(0,0,0,0.3)] border border-[rgba(192,132,252,0.2)]"
+                >
+                  <div className="font-mono text-[9px] text-purple-400 uppercase mb-1">
+                    {call.name}
+                  </div>
+                  <pre className="font-mono text-[10px] text-[#9bc3ff] overflow-x-auto">
+                    {formatJson(call.input)}
+                  </pre>
+                </div>
+              ))}
+            </div>
+          )}
+        </div>
+      )}
+    </div>
+  );
+}
-- 
cgit v1.2.3

