aboutsummaryrefslogtreecommitdiffstats
path: root/renderer/src/components/DocumentPane.tsx
diff options
context:
space:
mode:
authorClawd <ai@clawd.bot>2026-02-28 13:51:38 -0800
committerClawd <ai@clawd.bot>2026-02-28 13:51:38 -0800
commit812702aa9afbfe8de7672520852a6066341a4dc0 (patch)
tree17f778c2989b49b43ecb4a5975fe6f2979fe509b /renderer/src/components/DocumentPane.tsx
parentc386a1acfab0db99af57a9a18a49c72b89184f15 (diff)
Add CodeMirror 6 for markdown editing
- Install CodeMirror 6 with markdown language support - Add MarkdownEditor component with: - Syntax highlighting for markdown - Line numbers - Active line highlighting - History (undo/redo) - One Dark theme - Code block language highlighting - Keep react-markdown for preview mode - Add CSS for CodeMirror integration
Diffstat (limited to 'renderer/src/components/DocumentPane.tsx')
-rw-r--r--renderer/src/components/DocumentPane.tsx100
1 files changed, 94 insertions, 6 deletions
diff --git a/renderer/src/components/DocumentPane.tsx b/renderer/src/components/DocumentPane.tsx
index 95d7d03..cf777b1 100644
--- a/renderer/src/components/DocumentPane.tsx
+++ b/renderer/src/components/DocumentPane.tsx
@@ -1,6 +1,12 @@
1import React, { useState } from "react"; 1import React, { useState, useRef, useEffect } from "react";
2import ReactMarkdown from "react-markdown"; 2import ReactMarkdown from "react-markdown";
3import remarkGfm from "remark-gfm"; 3import remarkGfm from "remark-gfm";
4import { EditorState } from "@codemirror/state";
5import { EditorView, keymap, lineNumbers, highlightActiveLine } from "@codemirror/view";
6import { markdown } from "@codemirror/lang-markdown";
7import { languages } from "@codemirror/language-data";
8import { defaultKeymap, history, historyKeymap } from "@codemirror/commands";
9import { oneDark } from "@codemirror/theme-one-dark";
4import type { Phase } from "../types"; 10import type { Phase } from "../types";
5 11
6interface DocumentPaneProps { 12interface DocumentPaneProps {
@@ -11,6 +17,90 @@ interface DocumentPaneProps {
11 showOnboarding?: boolean; 17 showOnboarding?: boolean;
12} 18}
13 19
20function MarkdownEditor({
21 content,
22 onChange,
23 disabled,
24}: {
25 content: string;
26 onChange: (content: string) => void;
27 disabled: boolean;
28}) {
29 const editorRef = useRef<HTMLDivElement>(null);
30 const viewRef = useRef<EditorView | null>(null);
31
32 useEffect(() => {
33 if (!editorRef.current) return;
34
35 const updateListener = EditorView.updateListener.of((update) => {
36 if (update.docChanged) {
37 onChange(update.state.doc.toString());
38 }
39 });
40
41 const state = EditorState.create({
42 doc: content,
43 extensions: [
44 lineNumbers(),
45 highlightActiveLine(),
46 history(),
47 keymap.of([...defaultKeymap, ...historyKeymap]),
48 markdown({ codeLanguages: languages }),
49 oneDark,
50 updateListener,
51 EditorView.editable.of(!disabled),
52 EditorView.theme({
53 "&": {
54 height: "100%",
55 fontSize: "14px",
56 },
57 ".cm-scroller": {
58 overflow: "auto",
59 fontFamily: '"SF Mono", Monaco, "Cascadia Code", monospace',
60 },
61 ".cm-content": {
62 padding: "16px 0",
63 },
64 ".cm-line": {
65 padding: "0 16px",
66 },
67 }),
68 ],
69 });
70
71 const view = new EditorView({
72 state,
73 parent: editorRef.current,
74 });
75
76 viewRef.current = view;
77
78 return () => {
79 view.destroy();
80 viewRef.current = null;
81 };
82 }, [disabled]);
83
84 // Update content when it changes externally
85 useEffect(() => {
86 const view = viewRef.current;
87 if (!view) return;
88
89 const currentContent = view.state.doc.toString();
90 if (content !== currentContent) {
91 view.dispatch({
92 changes: {
93 from: 0,
94 to: currentContent.length,
95 insert: content,
96 },
97 });
98 }
99 }, [content]);
100
101 return <div ref={editorRef} className="codemirror-editor" />;
102}
103
14export function DocumentPane({ 104export function DocumentPane({
15 content, 105 content,
16 onChange, 106 onChange,
@@ -115,12 +205,10 @@ export function DocumentPane({
115 </div> 205 </div>
116 206
117 {isEditing ? ( 207 {isEditing ? (
118 <textarea 208 <MarkdownEditor
119 className="document-content editing" 209 content={content}
120 value={content} 210 onChange={onChange}
121 onChange={(e) => onChange(e.target.value)}
122 disabled={disabled} 211 disabled={disabled}
123 placeholder={`${filename} will appear here...`}
124 /> 212 />
125 ) : ( 213 ) : (
126 <div 214 <div