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 ? ( +