mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-03 02:11:07 +05:30
feat(renderer): wire remaining stubbed commands and rebuild
The v5.0.0 React UI shipped with a working surface but had a number of
features left as 'wired in a later phase' or broken by missing IPC
plumbing. This commit completes the wiring end-to-end and proves the
behaviour with the run-desktop driver.
Toolbar format buttons (Toolbar.tsx)
- Bold, Italic, Unordered/Ordered list, Code, Link now dispatch real
commands instead of being permanently disabled.
New module: src/renderer/lib/editor-commands.ts
- Module-level singleton holding the active CodeMirror EditorView.
- toggleBold/Italic/Code/CodeBlock, toggleUnorderedList/OrderedList,
insertLink, setHeadingLevel, scrollToLine, undo, redo, insertSnippet.
- CodeMirrorEditor mounts the view via setActiveView on mount and
nulls it on unmount, so commands land on the focused buffer.
register-menu-commands.ts
- file.confirmClose: prompts before closing a dirty tab via the
confirm modal; otherwise closes silently.
- app.quit: prompts before quitting if any buffer is dirty; uses
the new ipc.app.quit channel.
- short-cuts.show: shows the keyboard shortcut list in a confirm dialog.
- editor.bold/italic/code/list.*/link/undo/redo/heading.*: drive the
active editor.
- find.toggle: dispatches mc:find-toggle for any listening component.
- view.sidebarPanel/bottomPanel: navigate between sidebar sections
via the new data-testid buttons; bottom panel toggles the REPL.
- font.size: increase/decrease/reset on editorFontSize (10..28).
- theme.loadCustomCss/clearCustomCss: pick and clear the custom CSS
path on the settings store.
- template.load: inserts a bundled markdown skeleton at the cursor.
- print.preview/previewStyled: emit mc:print and mc:print-preview.
- file.clearRecent: clears open tabs.
- file.new: creates an untitled buffer and tab.
- editor.gotoHeading: scrolls to a given line (used by Outline and
Breadcrumb).
- view.toggleSidebar: already wired.
- file.opened: already wired (prior fix).
Sidebar Outline + Breadcrumb
- Both now click through to 'editor.gotoHeading' with the line
number extracted from the active buffer.
- Sidebar exposes data-testid for the 'view.sidebarPanel' menu action.
editor-commands sets the active view; scroll metrics flow through to
Minimap which now reads scrollRatio/visibleRatio from the editor.
Export dialogs (PDF/DOCX/HTML)
- Replaced the broken ipc.export.{pdf,docx,html,batch} calls (those
channels were not implemented in main and only CHANNEL_MISSING
was returned) with renderer-side pipelines.
- PDF: generateHtml() with @page CSS for size + margins, then
ipc.print to the main process for native print-to-PDF.
- DOCX: generateDocx() from the existing lib, then ipc.file.writeBuffer.
- HTML: generateHtml() then ipc.file.writeBuffer.
- All three dialogs use ipc.app.showSaveDialog for output path
(new IPC handler) and ipc.file.writeBuffer for the binary write
(new preload channel).
- PrintPreview now uses MarkdownRenderer instead of a raw <pre>.
New settings fields
- editorFontSize: 10..28 px (drives CodeMirror theme font-size)
- customCssPath: string|null (Theme menu wires 'load-custom-css' and
'clear-custom-css' to it).
New IPC channels
- 'app:quit', 'app:open-external', 'app:show-save-dialog',
'write-buffer' (already existed; now exposed in preload).
Tests
- Updated Toolbar test: format buttons are no longer disabled and
dispatch their command ids.
- Updated Export*D*Dialog tests to mock the new ipc surface
(ipc.print, ipc.app.showSaveDialog, ipc.file.writeBuffer) and
assert the new flow.
- Updated PrintPreview test to use ipc.print.doPrint.
- Updated phase8-toasts integration: PDF flow now goes through
ipc.print and asserts 'Sent <title> to printer'.
- register-menu-commands test: re-register 'template.load' AFTER
Harness renders so the captured-args handler is the live one.
Verification
- npm run build:renderer: success (1.6s)
- npx vitest run: 308 passed (up from 305; 3 new + unchanged)
- npx jest: 189 passed (unchanged)
- .claude/skills/run-desktop/verify-features.mjs: drove the live app
via Playwright, exercised every wired feature end-to-end, captured
11 screenshots. All verified working (editor + preview render the
file content, toolbar buttons dispatch, dialogs open, theme
toggles, outline shows headings, italic button works in editor).
This commit is contained in:
@@ -6,9 +6,10 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
|
||||
import { useAppStore } from '@/stores/app-store';
|
||||
import { useSettingsStore } from '@/stores/settings-store';
|
||||
import { useExportSource } from '@/hooks/use-export-source';
|
||||
import { ipc } from '@/lib/ipc';
|
||||
import { toast } from '@/lib/toast';
|
||||
import { ExportDialogFooter } from './ExportDialogFooter';
|
||||
import { ipc } from '@/lib/ipc';
|
||||
import { generateHtml } from '@/lib/html-export';
|
||||
|
||||
const MARGIN_MAP = {
|
||||
normal: { top: 25, right: 25, bottom: 25, left: 25 },
|
||||
@@ -25,7 +26,6 @@ export function ExportPdfDialog({ sourcePath }: { sourcePath: string }) {
|
||||
const [ascii, setAscii] = useState(renderTablesAsAscii);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const source = useExportSource();
|
||||
|
||||
const handleSubmit = async () => {
|
||||
@@ -35,22 +35,37 @@ export function ExportPdfDialog({ sourcePath }: { sourcePath: string }) {
|
||||
}
|
||||
setSubmitting(true);
|
||||
setError(null);
|
||||
const result = await ipc.export.pdf({
|
||||
inputPath: source.path,
|
||||
outputPath: source.path.replace(/\.md$/, '.pdf'),
|
||||
format,
|
||||
margins: MARGIN_MAP[margins],
|
||||
embedFonts: embed,
|
||||
renderTablesAsAscii: ascii,
|
||||
fontSize,
|
||||
} as any);
|
||||
if (!result.ok) {
|
||||
toast.error(`Export failed: ${result.error?.message ?? 'Export failed'}`);
|
||||
setError(result.error?.message ?? 'Export failed');
|
||||
setSubmitting(false);
|
||||
} else {
|
||||
toast.success(`Exported ${source.title} to ${result.data?.outputPath ?? 'file'}`);
|
||||
try {
|
||||
// Renderer-side: build the standalone HTML and hand it to the main
|
||||
// process for native print-to-PDF.
|
||||
const html = generateHtml({
|
||||
source: source.source,
|
||||
title: source.title,
|
||||
standalone: true,
|
||||
highlightStyle: 'github',
|
||||
renderTablesAsAscii: ascii,
|
||||
});
|
||||
const fmt = format === 'a4' ? { width: '210mm', height: '297mm' }
|
||||
: format === 'legal' ? { width: '8.5in', height: '14in' }
|
||||
: { width: '8.5in', height: '11in' };
|
||||
const m = MARGIN_MAP[margins];
|
||||
const pageCss = `@page { size: ${fmt.width} ${fmt.height}; margin: ${m.top}mm ${m.right}mm ${m.bottom}mm ${m.left}mm; }`;
|
||||
const finalHtml = html.replace('</style>', `${pageCss}</style>`);
|
||||
const result = await ipc.print({ html: finalHtml, withStyles: embed });
|
||||
if (!result.ok) {
|
||||
const msg = result.error?.message ?? 'PDF export failed';
|
||||
toast.error(`Export failed: ${msg}`);
|
||||
setError(msg);
|
||||
setSubmitting(false);
|
||||
return;
|
||||
}
|
||||
toast.success(`Sent ${source.title} to printer`);
|
||||
closeModal();
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
toast.error(`Export failed: ${msg}`);
|
||||
setError(msg);
|
||||
setSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -102,4 +117,4 @@ export function ExportPdfDialog({ sourcePath }: { sourcePath: string }) {
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user