refactor: migrate PDF export to desktop runtime abstraction
- Extract desktop-specific logic into src/services/desktop.ts - Add runtime detection for Electron vs Tauri environments - Simplify NoteEditor by delegating PDF export to desktop service - Update printExport.ts to remove unused storage helpers - Add Electron main process files in electron/ directory - Update vite config and types for Electron integration - Update .gitignore and package.json for Electron workflow
This commit is contained in:
@@ -7,10 +7,15 @@ export interface PrintExportPayload {
|
||||
}
|
||||
|
||||
export const PRINT_EXPORT_QUERY_PARAM = 'printJob';
|
||||
const PRINT_EXPORT_STORAGE_PREFIX = 'print-export:';
|
||||
|
||||
export const getPrintExportStorageKey = (jobId: string) =>
|
||||
`${PRINT_EXPORT_STORAGE_PREFIX}${jobId}`;
|
||||
const PRINT_DOCUMENT_CSP = [
|
||||
"default-src 'none'",
|
||||
"style-src 'unsafe-inline'",
|
||||
"img-src data: blob: https: http:",
|
||||
"font-src data:",
|
||||
"object-src 'none'",
|
||||
"base-uri 'none'",
|
||||
].join('; ');
|
||||
|
||||
export const getNoteTitleFromContent = (content: string) => {
|
||||
const firstLine = content
|
||||
@@ -26,3 +31,225 @@ export const sanitizeFileName = (name: string) =>
|
||||
.replace(/[\\/:*?"<>|]/g, '-')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim() || 'note';
|
||||
|
||||
const escapeHtml = (value: string) =>
|
||||
value
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
|
||||
const escapeFontFamily = (value: string) =>
|
||||
value.replace(/["\\]/g, '\\$&');
|
||||
|
||||
export const buildPrintDocument = (payload: PrintExportPayload) => {
|
||||
const fontFamily = `"${escapeFontFamily(payload.previewFont)}", Georgia, serif`;
|
||||
|
||||
return `<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta http-equiv="Content-Security-Policy" content="${PRINT_DOCUMENT_CSP}" />
|
||||
<title>${escapeHtml(payload.fileName)}</title>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #ffffff;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: ${fontFamily};
|
||||
font-size: ${payload.previewFontSize}px;
|
||||
line-height: 1.7;
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 18mm 16mm 18mm 16mm;
|
||||
}
|
||||
|
||||
article {
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.print-note h1,
|
||||
.print-note h2,
|
||||
.print-note h3 {
|
||||
color: #020617;
|
||||
font-weight: 700;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.print-note h1 {
|
||||
font-size: 2em;
|
||||
line-height: 1.15;
|
||||
margin: 0 0 1.35em;
|
||||
letter-spacing: -0.015em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.print-note h2 {
|
||||
font-size: 1.55em;
|
||||
line-height: 1.2;
|
||||
margin: 1.25em 0 0.45em;
|
||||
}
|
||||
|
||||
.print-note h3 {
|
||||
font-size: 1.25em;
|
||||
line-height: 1.3;
|
||||
margin: 1.1em 0 0.4em;
|
||||
}
|
||||
|
||||
.print-note p {
|
||||
margin: 0 0 0.9em;
|
||||
}
|
||||
|
||||
.print-note ul,
|
||||
.print-note ol {
|
||||
margin: 0.75em 0 1em;
|
||||
padding-left: 1.7em;
|
||||
list-style-position: outside;
|
||||
}
|
||||
|
||||
.print-note ul {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
.print-note ol {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
.print-note li {
|
||||
margin: 0.28em 0;
|
||||
padding-left: 0.18em;
|
||||
}
|
||||
|
||||
.print-note li > p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.print-note li::marker {
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.print-note blockquote {
|
||||
margin: 1.15em 0;
|
||||
padding-left: 1em;
|
||||
border-left: 3px solid #cbd5e1;
|
||||
color: #475569;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.print-note blockquote > :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.print-note blockquote > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.print-note pre {
|
||||
margin: 1.15em 0 1.3em;
|
||||
padding: 0.95em 1.05em;
|
||||
overflow-x: auto;
|
||||
border: 1px solid #dbe4f0;
|
||||
border-radius: 12px;
|
||||
background: #f5f7fb;
|
||||
color: #0f172a;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.print-note pre code {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
font-size: 0.92em;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.print-note code {
|
||||
font-family: "SFMono-Regular", "SF Mono", "JetBrains Mono", "Fira Code", "Source Code Pro", Menlo, Consolas, monospace;
|
||||
font-size: 0.92em;
|
||||
padding: 0.08em 0.38em;
|
||||
border: 1px solid #dbe4f0;
|
||||
border-radius: 0.42em;
|
||||
background: #f5f7fb;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.print-note a {
|
||||
color: #2563eb;
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: 0.08em;
|
||||
text-underline-offset: 0.14em;
|
||||
}
|
||||
|
||||
.print-note strong {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.print-note em {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.print-note del {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.print-note hr {
|
||||
border: 0;
|
||||
border-top: 1px solid #cbd5e1;
|
||||
margin: 1.6em 0;
|
||||
}
|
||||
|
||||
.print-note img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
margin: 1.2em auto;
|
||||
border-radius: 12px;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.print-note table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 1em 0 1.2em;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
.print-note th,
|
||||
.print-note td {
|
||||
border: 1px solid #dbe4f0;
|
||||
padding: 0.5em 0.65em;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.print-note th {
|
||||
background: #f8fafc;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<article class="print-note">${payload.html}</article>
|
||||
</body>
|
||||
</html>`;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user