fix: unified UI for markdown buttons

This commit is contained in:
2026-02-14 15:38:55 +01:00
parent ce94d22d30
commit 43ca543934
2 changed files with 70 additions and 23 deletions

View File

@@ -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);

View File

@@ -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 = `![${alt}](${url})`;
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>
); );
}; };