fix: unified UI for markdown buttons
This commit is contained in:
@@ -240,6 +240,23 @@
|
|||||||
background-color: var(--vscode-button-secondaryHoverBackground);
|
background-color: var(--vscode-button-secondaryHoverBackground);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.insert-post-link-button,
|
||||||
|
.insert-media-button {
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: var(--vscode-button-secondaryBackground);
|
||||||
|
color: var(--vscode-button-secondaryForeground);
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.insert-post-link-button:hover,
|
||||||
|
.insert-media-button:hover {
|
||||||
|
background-color: var(--vscode-button-secondaryHoverBackground);
|
||||||
|
}
|
||||||
|
|
||||||
.editor-preview {
|
.editor-preview {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background-color: var(--vscode-input-background);
|
background-color: var(--vscode-input-background);
|
||||||
|
|||||||
@@ -15,16 +15,9 @@ import { ChatPanel } from '../ChatPanel';
|
|||||||
import { ImportAnalysisView } from '../ImportAnalysisView';
|
import { ImportAnalysisView } from '../ImportAnalysisView';
|
||||||
import { AutoSaveManager } from '../../utils';
|
import { AutoSaveManager } from '../../utils';
|
||||||
import { parseMacros, getMacro } from '../../macros/registry';
|
import { parseMacros, getMacro } from '../../macros/registry';
|
||||||
import { PostSearchModal } from '../PostSearchModal';
|
import { InsertModal } from '../InsertModal';
|
||||||
import './Editor.css';
|
import './Editor.css';
|
||||||
|
|
||||||
interface SearchResult {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
slug: string;
|
|
||||||
excerpt?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Module-level AutoSaveManager for idle-time based auto-saving
|
// Module-level AutoSaveManager for idle-time based auto-saving
|
||||||
const autoSaveManager = new AutoSaveManager({
|
const autoSaveManager = new AutoSaveManager({
|
||||||
idleTimeMs: 3000, // Save after 3 seconds of idle time
|
idleTimeMs: 3000, // Save after 3 seconds of idle time
|
||||||
@@ -577,6 +570,7 @@ const PostEditor: React.FC<PostEditorProps> = ({ postId }) => {
|
|||||||
const [lightboxIndex, setLightboxIndex] = useState(0);
|
const [lightboxIndex, setLightboxIndex] = useState(0);
|
||||||
const [galleryImages, setGalleryImages] = useState<{ src: string; alt: string }[]>([]);
|
const [galleryImages, setGalleryImages] = useState<{ src: string; alt: string }[]>([]);
|
||||||
const [showPostSearch, setShowPostSearch] = useState(false);
|
const [showPostSearch, setShowPostSearch] = useState(false);
|
||||||
|
const [showMediaSearch, setShowMediaSearch] = useState(false);
|
||||||
const editorRef = useRef<unknown>(null);
|
const editorRef = useRef<unknown>(null);
|
||||||
const previewRef = useRef<HTMLDivElement>(null);
|
const previewRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@@ -908,8 +902,8 @@ const PostEditor: React.FC<PostEditorProps> = ({ postId }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle post selection from search modal
|
// Handle link insertion from InsertModal (for posts or external URLs)
|
||||||
const handlePostSelected = useCallback((post: SearchResult) => {
|
const handleInsertLink = useCallback((url: string, text?: string) => {
|
||||||
const editor = editorRef.current as any;
|
const editor = editorRef.current as any;
|
||||||
if (!editor) return;
|
if (!editor) return;
|
||||||
|
|
||||||
@@ -919,11 +913,10 @@ const PostEditor: React.FC<PostEditorProps> = ({ postId }) => {
|
|||||||
const selection = editor.getSelection();
|
const selection = editor.getSelection();
|
||||||
const selectedText = selection ? model.getValueInRange(selection) : '';
|
const selectedText = selection ? model.getValueInRange(selection) : '';
|
||||||
|
|
||||||
const linkText = selectedText || post.title;
|
const linkText = text || selectedText || url;
|
||||||
const linkUrl = `/posts/${post.slug}`;
|
const linkMarkdown = `[${linkText}](${url})`;
|
||||||
const linkMarkdown = `[${linkText}](${linkUrl})`;
|
|
||||||
|
|
||||||
editor.executeEdits('insert-post-link', [{
|
editor.executeEdits('insert-link', [{
|
||||||
range: selection || editor.getSelection(),
|
range: selection || editor.getSelection(),
|
||||||
text: linkMarkdown,
|
text: linkMarkdown,
|
||||||
forceMoveMarkers: true
|
forceMoveMarkers: true
|
||||||
@@ -932,6 +925,23 @@ const PostEditor: React.FC<PostEditorProps> = ({ postId }) => {
|
|||||||
setShowPostSearch(false);
|
setShowPostSearch(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Handle image insertion from InsertModal (for media library)
|
||||||
|
const handleInsertImage = useCallback((url: string, alt: string) => {
|
||||||
|
const editor = editorRef.current as any;
|
||||||
|
if (!editor) return;
|
||||||
|
|
||||||
|
const selection = editor.getSelection();
|
||||||
|
const imageMarkdown = ``;
|
||||||
|
|
||||||
|
editor.executeEdits('insert-image', [{
|
||||||
|
range: selection || editor.getSelection(),
|
||||||
|
text: imageMarkdown,
|
||||||
|
forceMoveMarkers: true
|
||||||
|
}]);
|
||||||
|
|
||||||
|
setShowMediaSearch(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Configure Monaco before mount to add macro syntax highlighting
|
// Configure Monaco before mount to add macro syntax highlighting
|
||||||
const handleEditorWillMount = (monaco: Monaco) => {
|
const handleEditorWillMount = (monaco: Monaco) => {
|
||||||
// Register a custom language that extends markdown with macro support
|
// Register a custom language that extends markdown with macro support
|
||||||
@@ -1165,13 +1175,22 @@ const PostEditor: React.FC<PostEditorProps> = ({ postId }) => {
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{editorMode === 'markdown' && (
|
{editorMode === 'markdown' && (
|
||||||
<button
|
<>
|
||||||
className="insert-post-link-button"
|
<button
|
||||||
onClick={() => setShowPostSearch(true)}
|
className="insert-post-link-button"
|
||||||
title="Link to post (Ctrl+K)"
|
onClick={() => setShowPostSearch(true)}
|
||||||
>
|
title="Link to post (Ctrl+K)"
|
||||||
📝
|
>
|
||||||
</button>
|
📝
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="insert-media-button"
|
||||||
|
onClick={() => setShowMediaSearch(true)}
|
||||||
|
title="Insert image from media library"
|
||||||
|
>
|
||||||
|
🖼️
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1244,11 +1263,22 @@ const PostEditor: React.FC<PostEditorProps> = ({ postId }) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showPostSearch && (
|
{showPostSearch && (
|
||||||
<PostSearchModal
|
<InsertModal
|
||||||
onSelect={handlePostSelected}
|
mode="link"
|
||||||
|
onInsertLink={handleInsertLink}
|
||||||
|
onInsertImage={() => {}}
|
||||||
onClose={() => setShowPostSearch(false)}
|
onClose={() => setShowPostSearch(false)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{showMediaSearch && (
|
||||||
|
<InsertModal
|
||||||
|
mode="image"
|
||||||
|
onInsertImage={handleInsertImage}
|
||||||
|
onInsertLink={() => {}}
|
||||||
|
onClose={() => setShowMediaSearch(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user