From 6d70c5f8a3ed90564b08616a3fb041409916059c Mon Sep 17 00:00:00 2001
From: Clawd
Date: Sat, 28 Feb 2026 07:30:40 -0800
Subject: Phase 4: React UI
- Add renderer/src/types.ts with Project, Session, Message, Phase types
- Add renderer/src/styles/globals.css with full styling
- Dark theme with accent colors
- Header, document pane, chat pane, action bar layouts
- Phase indicator, token usage bar, buttons
- Add renderer/src/components/Header.tsx
- Project/session dropdowns with create buttons
- Phase indicator showing current workflow state
- Add renderer/src/components/DocumentPane.tsx
- Markdown viewer/editor with toggle
- Syntax highlighting for review comments
- Task checkbox rendering
- Add renderer/src/components/ChatPane.tsx
- Message list with auto-scroll
- Input field with Enter to send
- Loading state indicator
- Add renderer/src/components/ActionBar.tsx
- Token usage bar with color coding
- Review/Submit buttons for workflow
- Permission mode toggle for implement phase
- Add renderer/src/App.tsx
- Full state management for projects, sessions, messages
- Claude message subscription
- Workflow handlers (review, submit, phase advance)
- Update renderer/src/main.tsx to render App
---
renderer/src/components/DocumentPane.tsx | 104 +++++++++++++++++++++++++++++++
1 file changed, 104 insertions(+)
create mode 100644 renderer/src/components/DocumentPane.tsx
(limited to 'renderer/src/components/DocumentPane.tsx')
diff --git a/renderer/src/components/DocumentPane.tsx b/renderer/src/components/DocumentPane.tsx
new file mode 100644
index 0000000..e44b0fb
--- /dev/null
+++ b/renderer/src/components/DocumentPane.tsx
@@ -0,0 +1,104 @@
+import React, { useState, useMemo } from "react";
+import type { Phase } from "../types";
+
+interface DocumentPaneProps {
+ content: string;
+ onChange: (content: string) => void;
+ phase: Phase;
+ disabled: boolean;
+}
+
+function renderMarkdown(md: string): string {
+ return (
+ md
+ // Headers
+ .replace(/^### (.*$)/gm, "$1
")
+ .replace(/^## (.*$)/gm, "$1
")
+ .replace(/^# (.*$)/gm, "$1
")
+ // Bold/italic
+ .replace(/\*\*([^*]+)\*\*/g, "$1")
+ .replace(/\*([^*]+)\*/g, "$1")
+ // Code blocks
+ .replace(
+ /```(\w*)\n([\s\S]*?)```/g,
+ '$2
'
+ )
+ .replace(/`([^`]+)`/g, "$1")
+ // Lists
+ .replace(/^- \[x\] (.*$)/gm, '☑ $1')
+ .replace(/^- \[ \] (.*$)/gm, '☐ $1')
+ .replace(/^- (.*$)/gm, "$1")
+ // Review comments (highlight them)
+ .replace(/(\/\/ REVIEW:.*$)/gm, '$1')
+ .replace(/(\/\/ NOTE:.*$)/gm, '$1')
+ // Paragraphs
+ .replace(/\n\n/g, "
")
+ .replace(/^(.+)$/gm, "
$1
")
+ // Clean up
+ .replace(/<\/p>/g, "")
+ .replace(/
()/g, "$1")
+ .replace(/(<\/h[1-3]>)<\/p>/g, "$1")
+ .replace(/(
)/g, "$1")
+ .replace(/(<\/pre>)<\/p>/g, "$1")
+ .replace(/(
)<\/p>/g, "$1")
+ );
+}
+
+export function DocumentPane({
+ content,
+ onChange,
+ phase,
+ disabled,
+}: DocumentPaneProps) {
+ const [isEditing, setIsEditing] = useState(false);
+ const renderedHtml = useMemo(() => renderMarkdown(content), [content]);
+
+ if (phase === "implement") {
+ return (
+
+
+ plan.md
+ Implementing...
+
+
+
+ );
+ }
+
+ const filename = phase === "research" ? "research.md" : "plan.md";
+
+ return (
+
+
+ {filename}
+
+
+
+ {isEditing ? (
+