|
|
@@ -326,11 +326,17 @@ function PostEditor() {
|
|
|
|
|
|
isScrollingRef.current = true;
|
|
|
const { scrollTop, scrollHeight, clientHeight } = scrollContainer;
|
|
|
- const scrollPercent = scrollHeight > clientHeight ? scrollTop / (scrollHeight - clientHeight) : 0;
|
|
|
+ const maxScroll = scrollHeight - clientHeight;
|
|
|
|
|
|
const previewNode = previewRef.current;
|
|
|
- if (previewNode.scrollHeight > previewNode.clientHeight) {
|
|
|
- previewNode.scrollTop = scrollPercent * (previewNode.scrollHeight - previewNode.clientHeight);
|
|
|
+ const previewMaxScroll = previewNode.scrollHeight - previewNode.clientHeight;
|
|
|
+
|
|
|
+ // If at the end (or very close), snap to end
|
|
|
+ if (maxScroll <= 0 || scrollTop >= maxScroll - 2) {
|
|
|
+ previewNode.scrollTop = previewMaxScroll;
|
|
|
+ } else {
|
|
|
+ const scrollPercent = scrollHeight > clientHeight ? scrollTop / (scrollHeight - clientHeight) : 0;
|
|
|
+ previewNode.scrollTop = scrollPercent * previewMaxScroll;
|
|
|
}
|
|
|
|
|
|
setTimeout(() => { isScrollingRef.current = false; }, 50);
|
|
|
@@ -344,11 +350,22 @@ function PostEditor() {
|
|
|
isScrollingRef.current = true;
|
|
|
const previewNode = previewRef.current;
|
|
|
const { scrollTop, scrollHeight, clientHeight } = previewNode;
|
|
|
- const scrollPercent = scrollHeight > clientHeight ? scrollTop / (scrollHeight - clientHeight) : 0;
|
|
|
+ const maxScroll = scrollHeight - clientHeight;
|
|
|
|
|
|
const scrollContainer = editorWrapperRef.current.querySelector('.w-md-editor-area');
|
|
|
- if (scrollContainer && scrollContainer.scrollHeight > scrollContainer.clientHeight) {
|
|
|
- scrollContainer.scrollTop = scrollPercent * (scrollContainer.scrollHeight - scrollContainer.clientHeight);
|
|
|
+ if (!scrollContainer) {
|
|
|
+ isScrollingRef.current = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const editorMaxScroll = scrollContainer.scrollHeight - scrollContainer.clientHeight;
|
|
|
+
|
|
|
+ // If at the end (or very close), snap to end
|
|
|
+ if (maxScroll <= 0 || scrollTop >= maxScroll - 2) {
|
|
|
+ scrollContainer.scrollTop = editorMaxScroll;
|
|
|
+ } else {
|
|
|
+ const scrollPercent = scrollHeight > clientHeight ? scrollTop / (scrollHeight - clientHeight) : 0;
|
|
|
+ scrollContainer.scrollTop = scrollPercent * editorMaxScroll;
|
|
|
}
|
|
|
|
|
|
setTimeout(() => { isScrollingRef.current = false; }, 50);
|
|
|
@@ -387,32 +404,45 @@ function PostEditor() {
|
|
|
}, [formData.title]);
|
|
|
|
|
|
|
|
|
- // Scroll Sync & Flicker Effect
|
|
|
+ // Re-sync scroll when images finish loading (handles layout shifts)
|
|
|
useEffect(() => {
|
|
|
- if (activeTab === "preview" || viewMode === "split") {
|
|
|
- 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 (viewMode !== "split" || !previewRef.current) return;
|
|
|
|
|
|
- if (target) {
|
|
|
- target.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
|
+ const previewNode = previewRef.current;
|
|
|
+ const images = previewNode.querySelectorAll('img');
|
|
|
+
|
|
|
+ const handleImageLoad = () => {
|
|
|
+ // Small delay to let layout settle
|
|
|
+ setTimeout(() => {
|
|
|
+ // Re-apply current scroll sync from editor to preview
|
|
|
+ if (editorWrapperRef.current) {
|
|
|
+ const scrollContainer = editorWrapperRef.current.querySelector('.w-md-editor-area');
|
|
|
+ if (scrollContainer) {
|
|
|
+ const { scrollTop, scrollHeight, clientHeight } = scrollContainer;
|
|
|
+ const maxScroll = scrollHeight - clientHeight;
|
|
|
+ const previewMaxScroll = previewNode.scrollHeight - previewNode.clientHeight;
|
|
|
+
|
|
|
+ if (maxScroll > 0 && previewMaxScroll > 0) {
|
|
|
+ const scrollPercent = scrollTop / maxScroll;
|
|
|
+ previewNode.scrollTop = scrollPercent * previewMaxScroll;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, 100);
|
|
|
+ };
|
|
|
|
|
|
- // Add flicker effect
|
|
|
- target.classList.add('highlight-pulse');
|
|
|
- setTimeout(() => {
|
|
|
- target.classList.remove('highlight-pulse');
|
|
|
- }, 1500);
|
|
|
+ images.forEach(img => {
|
|
|
+ if (!img.complete) {
|
|
|
+ img.addEventListener('load', handleImageLoad);
|
|
|
}
|
|
|
- }
|
|
|
- }, [activeTab, cursorLine, viewMode]);
|
|
|
+ });
|
|
|
+
|
|
|
+ return () => {
|
|
|
+ images.forEach(img => {
|
|
|
+ img.removeEventListener('load', handleImageLoad);
|
|
|
+ });
|
|
|
+ };
|
|
|
+ }, [viewMode, sanitizedPreview]);
|
|
|
|
|
|
return (
|
|
|
<div className="min-h-screen theme-bg font-sans theme-text selection:bg-blue-100 selection:text-blue-900 flex flex-col transition-colors duration-300">
|