summaryrefslogtreecommitdiff
path: root/makima/frontend/src/routes
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-02-03 18:29:54 +0000
committersoryu <soryu@soryu.co>2026-02-03 18:29:54 +0000
commit1ce281adb89683a5fccfd153706383b14b944f32 (patch)
tree4d7eed114d31b99401c492301665c7aa36bccc5f /makima/frontend/src/routes
parent6a00463f77b46bc078ba43f77ca2723b76c25aca (diff)
downloadsoryu-1ce281adb89683a5fccfd153706383b14b944f32.tar.gz
soryu-1ce281adb89683a5fccfd153706383b14b944f32.zip
feat(frontend): Implement DiscussContractModal for chat-based contract creation
Add a new modal component that allows users to have a natural conversation with Makima to define and create contracts: - Create DiscussContractModal component with: - Chat interface with message history - Integration with useSpeakWebSocket for TTS - Auto-scroll to latest messages - Context-aware greeting based on transcript existence - Loading state and error handling - Contract creation success banner - ChatBubble sub-component with speak button - Add discussContract API function and types in api.ts: - DiscussContractRequest, DiscussContractResponse, CreatedContractInfo - Update ContractPickerModal with 'Discuss Contract' option - Update ControlPanel to pass onDiscussContract prop - Integrate DiscussContractModal into listen page: - Add modal state and open/close handlers - Extract transcript context for conversation - Handle contract creation callback Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'makima/frontend/src/routes')
-rw-r--r--makima/frontend/src/routes/listen.tsx38
1 files changed, 37 insertions, 1 deletions
diff --git a/makima/frontend/src/routes/listen.tsx b/makima/frontend/src/routes/listen.tsx
index 8af538e..a53cbd9 100644
--- a/makima/frontend/src/routes/listen.tsx
+++ b/makima/frontend/src/routes/listen.tsx
@@ -4,9 +4,10 @@ import { SpeakerPanel } from "../components/listen/SpeakerPanel";
import { TranscriptPanel } from "../components/listen/TranscriptPanel";
import { ControlPanel, type ContractOption } from "../components/listen/ControlPanel";
import { TranscriptAnalysisPanel } from "../components/listen/TranscriptAnalysisPanel";
+import { DiscussContractModal } from "../components/listen/DiscussContractModal";
import { useMicrophone } from "../hooks/useMicrophone";
import { useWebSocket } from "../hooks/useWebSocket";
-import { listContracts } from "../lib/api";
+import { listContracts, type CreatedContractInfo } from "../lib/api";
import { useAuth } from "../contexts/AuthContext";
export default function ListenPage() {
@@ -27,6 +28,9 @@ export default function ListenPage() {
contractId: string;
} | null>(null);
+ // Discuss contract modal state
+ const [isDiscussModalOpen, setIsDiscussModalOpen] = useState(false);
+
// Fetch contracts on mount
useEffect(() => {
if (!isAuthenticated) {
@@ -175,6 +179,29 @@ export default function ListenPage() {
setSavedTranscript(null);
}, []);
+ // Get current transcript context for discussion
+ const transcriptContext = useMemo(() => {
+ if (ws.transcripts.length === 0) return undefined;
+ return ws.transcripts
+ .map(t => `[${t.speaker}]: ${t.text}`)
+ .join("\n");
+ }, [ws.transcripts]);
+
+ const handleOpenDiscussModal = useCallback(() => {
+ setIsDiscussModalOpen(true);
+ }, []);
+
+ const handleContractCreated = useCallback((contract: CreatedContractInfo) => {
+ // Add to contracts list and select it
+ setContracts(prev => [
+ { id: contract.id, name: contract.name },
+ ...prev,
+ ]);
+ setSelectedContractId(contract.id);
+ // Close the modal after a short delay to show success
+ setTimeout(() => setIsDiscussModalOpen(false), 2000);
+ }, []);
+
const error = ws.error || mic.error;
return (
@@ -206,6 +233,7 @@ export default function ListenPage() {
contracts={contracts}
selectedContractId={selectedContractId}
onContractChange={setSelectedContractId}
+ onDiscussContract={handleOpenDiscussModal}
contractsLoading={contractsLoading}
connectionStatus={ws.status}
/>
@@ -236,6 +264,14 @@ export default function ListenPage() {
</div>
</div>
)}
+
+ {/* Discuss Contract Modal */}
+ <DiscussContractModal
+ isOpen={isDiscussModalOpen}
+ onClose={() => setIsDiscussModalOpen(false)}
+ transcriptContext={transcriptContext}
+ onContractCreated={handleContractCreated}
+ />
</div>
);
}