import { getPaperDimensionsMm, lengthToMillimeters, paperFormatLabels, supportedMermaidLayouts, supportedMermaidLooks, supportedMermaidThemes, supportedPaperFormats, type ExportConfig, type FooterConfig, type HeaderConfig, type MermaidExportConfig } from "@md-to-pdf/core"; import { useEffect, useState } from "react"; interface ExportSettingsDrawerProps { config: ExportConfig; onChange: (config: ExportConfig) => void; onClose: () => void; onReset: () => void; } interface LengthInputProps { label: string; value: string; maximum: number; onChange: (value: string) => void; } const pageNumberFormatLabels: Record = { page: "1", "page-total": "1 / 10", "chinese-page-total": "第 1 页 / 共 10 页", "dash-page": "- 1 -", custom: "自定义模板" }; const mermaidLayoutLabels: Record< MermaidExportConfig["layout"], string > = { dagre: "Dagre", elk: "ELK" }; const mermaidThemeLabels: Record< MermaidExportConfig["theme"], string > = { default: "Default", base: "Base(可定制基础主题)", dark: "Dark", forest: "Forest", neutral: "Neutral", neo: "Neo", "neo-dark": "Neo Dark", redux: "Redux", "redux-dark": "Redux Dark", "redux-color": "Redux Color", "redux-dark-color": "Redux Dark Color" }; const mermaidLookLabels: Record< MermaidExportConfig["look"], string > = { classic: "Classic", handDrawn: "Hand-drawn", neo: "Neo" }; const mermaidFontFamilies = [ "Segoe UI, Microsoft YaHei, sans-serif", "Microsoft YaHei, sans-serif", "Arial, Helvetica, sans-serif", "Georgia, Times New Roman, serif", "Consolas, Microsoft YaHei, monospace" ] as const; function fitMarginPair( first: number, second: number, available: number ) { const total = first + second; if (total <= available || total === 0) { return [first, second] as const; } const scale = available / total; const scaledFirst = Math.floor(first * scale * 10) / 10; return [ scaledFirst, Math.floor((available - scaledFirst) * 10) / 10 ] as const; } function LengthInput({ label, value, maximum, onChange }: LengthInputProps) { const [draft, setDraft] = useState( String(lengthToMillimeters(value)) ); useEffect(() => { setDraft(String(lengthToMillimeters(value))); }, [value]); function commit() { const number = Number.parseFloat(draft); if (!Number.isFinite(number)) { setDraft(String(lengthToMillimeters(value))); return; } const normalized = Math.min(Math.max(number, 0), maximum); const rounded = Math.round(normalized * 10) / 10; setDraft(String(rounded)); onChange(`${rounded}mm`); } return ( ); } function MermaidFontInput({ value, onChange }: { value: string; onChange: (value: string) => void; }) { const [draft, setDraft] = useState(value); useEffect(() => { setDraft(value); }, [value]); function commit() { const normalized = draft.trim(); if (!normalized) { setDraft(value); return; } setDraft(normalized); onChange(normalized); } return ( ); } function updateHeaderSlot( header: HeaderConfig, slot: "left" | "center" | "right", value: Partial ) { return { ...header, [slot]: { ...header[slot], ...value } }; } export function ExportSettingsDrawer({ config, onChange, onClose, onReset }: ExportSettingsDrawerProps) { const dimensions = getPaperDimensionsMm( config.paper.format, config.paper.orientation ); const margins = config.paper.margins; const horizontalGap = 1; const verticalGap = 1; function updatePaper(paper: Partial) { const nextPaper = { ...config.paper, ...paper }; const nextDimensions = getPaperDimensionsMm( nextPaper.format, nextPaper.orientation ); const [left, right] = fitMarginPair( lengthToMillimeters(nextPaper.margins.left), lengthToMillimeters(nextPaper.margins.right), nextDimensions.width - 1 ); const [top, bottom] = fitMarginPair( lengthToMillimeters(nextPaper.margins.top), lengthToMillimeters(nextPaper.margins.bottom), nextDimensions.height - 1 ); onChange({ ...config, paper: { ...nextPaper, margins: { top: `${top}mm`, right: `${right}mm`, bottom: `${bottom}mm`, left: `${left}mm` } } }); } function updateMargin( side: keyof ExportConfig["paper"]["margins"], value: string ) { updatePaper({ margins: { ...margins, [side]: value } }); } function updateHeader(header: Partial) { onChange({ ...config, header: { ...config.header, ...header } }); } function updateFooter(footer: Partial) { onChange({ ...config, footer: { ...config.footer, ...footer } }); } function updateMermaid(mermaid: Partial) { onChange({ ...config, mermaid: { ...config.mermaid, ...mermaid } }); } return (

纸张

方向

当前纸张:{dimensions.width} × {dimensions.height}mm

页边距

updateMargin("top", value)} /> updateMargin("right", value)} /> updateMargin("bottom", value)} /> updateMargin("left", value)} />

Mermaid 图表

updateMermaid({ fontFamily })} />

单个 Mermaid 代码块中的 config frontmatter 优先于这里的全局设置;style、classDef 和 themeVariables 会继续按 Mermaid 原生语法生效。

页眉

支持 {"${title}"}、{"${author}"} 和 {"${filename}"}。

{(["left", "center", "right"] as const).map((slot) => { const labels = { left: "左侧", center: "中间", right: "右侧" }; return (
updateHeader( updateHeaderSlot(config.header, slot, { content: event.target.value }) ) } />
); })}

页脚页码

{config.footer.format === "custom" ? ( ) : null}

页眉和页码会实时显示在分页预览中。

); }