PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.5
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.5
4.9.5 4.9.4 4.9.3 4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 All 203 releases
betterdocs / react-src / gutenberg / blocks / code-snippet / src / components / CodeMirrorControl.js

CodeMirrorControl.js in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.9.5, at react-src/gutenberg/blocks/code-snippet/src/components/CodeMirrorControl.js

233 lines 8.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { __ } from '@wordpress/i18n';
2 import { BaseControl } from '@wordpress/components';
3 import { useEffect, useRef, useState } from '@wordpress/element';
4
5 import './codemirror.scss';
6
7 /**
8 * CodeMirror Control Component for Gutenberg
9 *
10 * Uses WordPress's built-in wp.codeEditor API which wraps CodeMirror
11 */
12 const CodeMirrorControl = ({
13 label,
14 value,
15 onChange,
16 placeholder = '',
17 help = '',
18 rows = 15
19 }) => {
20 const textareaRef = useRef(null);
21 const editorRef = useRef(null);
22 const isInitialized = useRef(false);
23 const [isLoading, setIsLoading] = useState(true);
24 const [hasError, setHasError] = useState(false);
25 const [isCodeMirrorReady, setIsCodeMirrorReady] = useState(false);
26
27 // Note: Language mode mapping removed since we use plain text mode for editor
28
29 // Initialize CodeMirror with retry mechanism
30 useEffect(() => {
31 if (!textareaRef.current || isInitialized.current) {
32 return;
33 }
34
35 const initializeCodeMirror = () => {
36 // Check if wp.codeEditor is available
37 if (typeof window.wp === 'undefined' ||
38 typeof window.wp.codeEditor === 'undefined' ||
39 typeof window.wp.codeEditor.initialize !== 'function') {
40 console.warn('WordPress CodeEditor not available. Retrying...');
41 return false;
42 }
43
44 // Check if CodeMirror is available
45 if (typeof window.CodeMirror === 'undefined') {
46 console.warn('CodeMirror library not loaded. Retrying...');
47 return false;
48 }
49
50 console.log('Initializing CodeMirror for plain text editing (no syntax highlighting)');
51
52 // CodeMirror settings - plain text mode for editor
53 const settings = {
54 codemirror: {
55 mode: 'text/plain', // Always use plain text mode
56 lineNumbers: true,
57 lineWrapping: true,
58 indentUnit: 4,
59 tabSize: 4,
60 indentWithTabs: false,
61 matchBrackets: false, // Disable bracket matching
62 autoCloseBrackets: false, // Disable auto-closing brackets
63 foldGutter: false, // Disable code folding
64 gutters: ['CodeMirror-linenumbers'], // Only line numbers
65 extraKeys: {
66 'Ctrl-Space': 'autocomplete',
67 'Tab': function(cm) {
68 if (cm.somethingSelected()) {
69 cm.indentSelection('add');
70 } else {
71 cm.replaceSelection(' ', 'end');
72 }
73 },
74 'Shift-Tab': function(cm) {
75 cm.indentSelection('subtract');
76 }
77 },
78 theme: 'default',
79 placeholder: placeholder
80 }
81 };
82
83 try {
84 // Initialize CodeMirror
85 const editorInstance = window.wp.codeEditor.initialize(textareaRef.current, settings);
86 editorRef.current = editorInstance;
87 isInitialized.current = true;
88 setIsLoading(false);
89 setHasError(false);
90 setIsCodeMirrorReady(true);
91
92 // Hide the original textarea
93 if (textareaRef.current) {
94 textareaRef.current.style.display = 'none';
95 }
96
97 // Set initial value
98 if (value) {
99 editorInstance.codemirror.setValue(value);
100 }
101
102 // Listen for changes
103 editorInstance.codemirror.on('change', (cm) => {
104 onChange(cm.getValue());
105 });
106
107 // Focus handling
108 editorInstance.codemirror.on('focus', () => {
109 const control = textareaRef.current?.closest('.components-base-control');
110 if (control) {
111 control.classList.add('is-focused');
112 }
113 });
114
115 editorInstance.codemirror.on('blur', () => {
116 const control = textareaRef.current?.closest('.components-base-control');
117 if (control) {
118 control.classList.remove('is-focused');
119 }
120 });
121
122 // Refresh CodeMirror to ensure proper rendering
123 setTimeout(() => {
124 if (editorInstance.codemirror) {
125 editorInstance.codemirror.refresh();
126 }
127 }, 100);
128
129 console.log('CodeMirror initialized successfully in plain text mode');
130 return true;
131
132 } catch (error) {
133 console.error('Failed to initialize CodeMirror:', error);
134 setHasError(true);
135 setIsLoading(false);
136 setIsCodeMirrorReady(false);
137 return false;
138 }
139 };
140
141 // Try to initialize immediately
142 if (!initializeCodeMirror()) {
143 // If failed, retry after a short delay
144 const retryTimeout = setTimeout(() => {
145 if (!isInitialized.current) {
146 if (!initializeCodeMirror()) {
147 // Final fallback - show error state and show textarea
148 setIsLoading(false);
149 setHasError(true);
150 setIsCodeMirrorReady(false);
151 if (textareaRef.current) {
152 textareaRef.current.style.display = 'block';
153 }
154 console.warn('CodeMirror failed to initialize after retry. Using plain textarea.');
155 }
156 }
157 }, 1000);
158
159 return () => clearTimeout(retryTimeout);
160 }
161
162 // Cleanup function
163 return () => {
164 if (editorRef.current && editorRef.current.codemirror) {
165 try {
166 editorRef.current.codemirror.toTextArea();
167 } catch (error) {
168 console.warn('Error during CodeMirror cleanup:', error);
169 }
170 editorRef.current = null;
171 isInitialized.current = false;
172 setIsCodeMirrorReady(false);
173 // Show textarea again if CodeMirror is destroyed
174 if (textareaRef.current) {
175 textareaRef.current.style.display = 'block';
176 }
177 }
178 };
179 }, []); // Only run once on mount
180
181 // Note: Language mode updates removed since we use plain text mode
182
183 // Update value when prop changes (external updates)
184 useEffect(() => {
185 if (editorRef.current && editorRef.current.codemirror && isInitialized.current) {
186 const currentValue = editorRef.current.codemirror.getValue();
187 if (currentValue !== value) {
188 editorRef.current.codemirror.setValue(value || '');
189 }
190 }
191 }, [value]);
192
193 return (
194 <BaseControl
195 label={label}
196 help={help || (hasError ? __('CodeMirror failed to load. Using plain textarea.', 'betterdocs') : '')}
197 className={`betterdocs-codemirror-control ${isLoading ? 'is-loading' : ''} ${hasError ? 'has-error' : ''} ${isCodeMirrorReady ? 'is-codemirror-ready' : ''}`}
198 >
199 {isLoading && (
200 <div className="codemirror-loading">
201 <span>{__('Loading code editor...', 'betterdocs')}</span>
202 </div>
203 )}
204 <textarea
205 ref={textareaRef}
206 defaultValue={value || ''}
207 placeholder={placeholder}
208 rows={rows}
209 onChange={(e) => {
210 // Fallback for when CodeMirror is not available
211 if (!isInitialized.current) {
212 onChange(e.target.value);
213 }
214 }}
215 style={{
216 width: '100%',
217 fontFamily: 'Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace',
218 fontSize: '13px',
219 lineHeight: '1.4',
220 padding: '8px',
221 border: '1px solid #ddd',
222 borderRadius: '4px',
223 resize: 'vertical',
224 minHeight: '200px',
225 display: (isLoading || (!isCodeMirrorReady && hasError)) ? 'block' : 'none'
226 }}
227 />
228 </BaseControl>
229 );
230 };
231
232 export default CodeMirrorControl;
233