|
@@ -24,6 +24,7 @@ function PostEditor() {
|
|
|
tags: "",
|
|
tags: "",
|
|
|
hidden: false,
|
|
hidden: false,
|
|
|
});
|
|
});
|
|
|
|
|
+ const [cursorLine, setCursorLine] = useState(0);
|
|
|
|
|
|
|
|
const [loading, setLoading] = useState(isEditing);
|
|
const [loading, setLoading] = useState(isEditing);
|
|
|
const [saving, setSaving] = useState(false);
|
|
const [saving, setSaving] = useState(false);
|
|
@@ -81,8 +82,27 @@ function PostEditor() {
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const handleInputChange = (field, value) => {
|
|
|
|
|
|
|
+ const handleInputChange = (field, value, event) => {
|
|
|
setFormData((prev) => ({ ...prev, [field]: value }));
|
|
setFormData((prev) => ({ ...prev, [field]: value }));
|
|
|
|
|
+
|
|
|
|
|
+ // Track cursor line for sync
|
|
|
|
|
+ if (field === "content" && event) {
|
|
|
|
|
+ // MDEditor's onChange passes the value, but we need the event or access to the textarea
|
|
|
|
|
+ // However, uiw/react-md-editor's onChange only gives value.
|
|
|
|
|
+ // We need to use onSelect or capture it from the underlying textarea.
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // Improved cursor tracker
|
|
|
|
|
+ const handleEditorChange = (value, event, state) => {
|
|
|
|
|
+ handleInputChange("content", value || "");
|
|
|
|
|
+
|
|
|
|
|
+ // Calculate line number from selection start
|
|
|
|
|
+ // MDEditor expose state with selection
|
|
|
|
|
+ if (state && state.selection && state.selection.start) {
|
|
|
|
|
+ const lines = value.substr(0, state.selection.start).split("\n");
|
|
|
|
|
+ setCursorLine(lines.length);
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const insertTextAtCursor = (text) => {
|
|
const insertTextAtCursor = (text) => {
|
|
@@ -229,6 +249,29 @@ function PostEditor() {
|
|
|
}
|
|
}
|
|
|
}, [formData.title]);
|
|
}, [formData.title]);
|
|
|
|
|
|
|
|
|
|
+ // Scroll Sync Effect
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (activeTab === "preview") {
|
|
|
|
|
+ // Find the element with the closest data-line attribute
|
|
|
|
|
+ // We search for elements with data-line <= cursorLine, picking the largest one
|
|
|
|
|
+ const elements = document.querySelectorAll('[data-line]');
|
|
|
|
|
+ let target = null;
|
|
|
|
|
+ let maxLine = -1;
|
|
|
|
|
+
|
|
|
|
|
+ elements.forEach(el => {
|
|
|
|
|
+ const line = parseInt(el.getAttribute('data-line'), 10);
|
|
|
|
|
+ if (line <= cursorLine && line > maxLine) {
|
|
|
|
|
+ maxLine = line;
|
|
|
|
|
+ target = el;
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (target) {
|
|
|
|
|
+ target.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [activeTab]);
|
|
|
|
|
+
|
|
|
// ... existing interactions ...
|
|
// ... existing interactions ...
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
@@ -341,7 +384,7 @@ function PostEditor() {
|
|
|
<div className="prose-editor-wrapper -mx-4 md:mx-0">
|
|
<div className="prose-editor-wrapper -mx-4 md:mx-0">
|
|
|
<MDEditor
|
|
<MDEditor
|
|
|
value={formData.content}
|
|
value={formData.content}
|
|
|
- onChange={(value) => handleInputChange("content", value || "")}
|
|
|
|
|
|
|
+ onChange={handleEditorChange}
|
|
|
commands={[...commands.getCommands(), ...customCommands]}
|
|
commands={[...commands.getCommands(), ...customCommands]}
|
|
|
height={window.innerHeight * 0.7} // Increased height to 70% of viewport
|
|
height={window.innerHeight * 0.7} // Increased height to 70% of viewport
|
|
|
minHeight={500}
|
|
minHeight={500}
|