feat: Added "Page size" option when exporting Super notes as PDF
This commit is contained in:
@@ -128,7 +128,7 @@ async function configureWindow(remoteBridge: CrossProcessBridge) {
|
|||||||
/* Use custom title bar. Take the sn-titlebar-height off of
|
/* Use custom title bar. Take the sn-titlebar-height off of
|
||||||
the app content height so its not overflowing */
|
the app content height so its not overflowing */
|
||||||
sheet.insertRule(
|
sheet.insertRule(
|
||||||
'[role="dialog"]:not(.challenge-modal) { height: calc(100vh - var(--sn-desktop-titlebar-height)) !important; margin-top: var(--sn-desktop-titlebar-height); }',
|
'[role="dialog"]:not(.challenge-modal) { max-height: calc(100vh - var(--sn-desktop-titlebar-height)) !important; margin-top: var(--sn-desktop-titlebar-height); }',
|
||||||
sheet.cssRules.length,
|
sheet.cssRules.length,
|
||||||
)
|
)
|
||||||
sheet.insertRule(
|
sheet.insertRule(
|
||||||
|
|||||||
@@ -1,8 +1,18 @@
|
|||||||
|
import { FileItem, PrefKey, PrefValue } from '@standardnotes/models'
|
||||||
|
|
||||||
export interface SuperConverterServiceInterface {
|
export interface SuperConverterServiceInterface {
|
||||||
isValidSuperString(superString: string): boolean
|
isValidSuperString(superString: string): boolean
|
||||||
convertSuperStringToOtherFormat: (
|
convertSuperStringToOtherFormat: (
|
||||||
superString: string,
|
superString: string,
|
||||||
toFormat: 'txt' | 'md' | 'html' | 'json' | 'pdf',
|
toFormat: 'txt' | 'md' | 'html' | 'json' | 'pdf',
|
||||||
|
config?: {
|
||||||
|
embedBehavior?: PrefValue[PrefKey.SuperNoteExportEmbedBehavior]
|
||||||
|
getFileItem?: (id: string) => FileItem | undefined
|
||||||
|
getFileBase64?: (id: string) => Promise<string | undefined>
|
||||||
|
pdf?: {
|
||||||
|
pageSize?: PrefValue[PrefKey.SuperNoteExportPDFPageSize]
|
||||||
|
}
|
||||||
|
},
|
||||||
) => Promise<string>
|
) => Promise<string>
|
||||||
convertOtherFormatToSuperString: (
|
convertOtherFormatToSuperString: (
|
||||||
otherFormatString: string,
|
otherFormatString: string,
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export const PrefDefaults = {
|
|||||||
[PrefKey.SuperNoteExportFormat]: 'json',
|
[PrefKey.SuperNoteExportFormat]: 'json',
|
||||||
[PrefKey.SuperNoteExportEmbedBehavior]: 'reference',
|
[PrefKey.SuperNoteExportEmbedBehavior]: 'reference',
|
||||||
[PrefKey.SuperNoteExportUseMDFrontmatter]: true,
|
[PrefKey.SuperNoteExportUseMDFrontmatter]: true,
|
||||||
|
[PrefKey.SuperNoteExportPDFPageSize]: 'A4',
|
||||||
[PrefKey.SystemViewPreferences]: {},
|
[PrefKey.SystemViewPreferences]: {},
|
||||||
[PrefKey.AuthenticatorNames]: '',
|
[PrefKey.AuthenticatorNames]: '',
|
||||||
[PrefKey.ComponentPreferences]: {},
|
[PrefKey.ComponentPreferences]: {},
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export enum PrefKey {
|
|||||||
SuperNoteExportFormat = 'superNoteExportFormat',
|
SuperNoteExportFormat = 'superNoteExportFormat',
|
||||||
SuperNoteExportEmbedBehavior = 'superNoteExportEmbedBehavior',
|
SuperNoteExportEmbedBehavior = 'superNoteExportEmbedBehavior',
|
||||||
SuperNoteExportUseMDFrontmatter = 'superNoteExportUseMDFrontmatter',
|
SuperNoteExportUseMDFrontmatter = 'superNoteExportUseMDFrontmatter',
|
||||||
|
SuperNoteExportPDFPageSize = 'superNoteExportPDFPageSize',
|
||||||
AuthenticatorNames = 'authenticatorNames',
|
AuthenticatorNames = 'authenticatorNames',
|
||||||
PaneGesturesEnabled = 'paneGesturesEnabled',
|
PaneGesturesEnabled = 'paneGesturesEnabled',
|
||||||
ComponentPreferences = 'componentPreferences',
|
ComponentPreferences = 'componentPreferences',
|
||||||
@@ -90,6 +91,7 @@ export type PrefValue = {
|
|||||||
[PrefKey.SuperNoteExportFormat]: 'json' | 'md' | 'html' | 'pdf'
|
[PrefKey.SuperNoteExportFormat]: 'json' | 'md' | 'html' | 'pdf'
|
||||||
[PrefKey.SuperNoteExportEmbedBehavior]: 'reference' | 'inline' | 'separate'
|
[PrefKey.SuperNoteExportEmbedBehavior]: 'reference' | 'inline' | 'separate'
|
||||||
[PrefKey.SuperNoteExportUseMDFrontmatter]: boolean
|
[PrefKey.SuperNoteExportUseMDFrontmatter]: boolean
|
||||||
|
[PrefKey.SuperNoteExportPDFPageSize]: 'A3' | 'A4' | 'LETTER' | 'LEGAL' | 'TABLOID'
|
||||||
[PrefKey.AuthenticatorNames]: string
|
[PrefKey.AuthenticatorNames]: string
|
||||||
[PrefKey.PaneGesturesEnabled]: boolean
|
[PrefKey.PaneGesturesEnabled]: boolean
|
||||||
[PrefKey.ComponentPreferences]: AllComponentPreferences
|
[PrefKey.ComponentPreferences]: AllComponentPreferences
|
||||||
|
|||||||
@@ -499,7 +499,7 @@ const NotesOptions = ({ notes, closeMenu }: NotesOptionsProps) => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ModalOverlay isOpen={showExportSuperModal} close={closeSuperExportModal}>
|
<ModalOverlay isOpen={showExportSuperModal} close={closeSuperExportModal} className="md:max-w-[25vw]">
|
||||||
<SuperExportModal notes={notes} exportNotes={downloadSelectedItems} close={closeSuperExportModal} />
|
<SuperExportModal notes={notes} exportNotes={downloadSelectedItems} close={closeSuperExportModal} />
|
||||||
</ModalOverlay>
|
</ModalOverlay>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import RadioButtonGroup from '../RadioButtonGroup/RadioButtonGroup'
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import Switch from '../Switch/Switch'
|
import Switch from '../Switch/Switch'
|
||||||
import { noteHasEmbeddedFiles } from '@/Utils/NoteExportUtils'
|
import { noteHasEmbeddedFiles } from '@/Utils/NoteExportUtils'
|
||||||
|
import Dropdown from '../Dropdown/Dropdown'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
notes: SNNote[]
|
notes: SNNote[]
|
||||||
@@ -19,6 +20,7 @@ const SuperExportModal = ({ notes, exportNotes, close }: Props) => {
|
|||||||
const superNoteExportFormat = usePreference(PrefKey.SuperNoteExportFormat)
|
const superNoteExportFormat = usePreference(PrefKey.SuperNoteExportFormat)
|
||||||
const superNoteExportEmbedBehavior = usePreference(PrefKey.SuperNoteExportEmbedBehavior)
|
const superNoteExportEmbedBehavior = usePreference(PrefKey.SuperNoteExportEmbedBehavior)
|
||||||
const superNoteExportUseMDFrontmatter = usePreference(PrefKey.SuperNoteExportUseMDFrontmatter)
|
const superNoteExportUseMDFrontmatter = usePreference(PrefKey.SuperNoteExportUseMDFrontmatter)
|
||||||
|
const superNoteExportPDFPageSize = usePreference(PrefKey.SuperNoteExportPDFPageSize)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (superNoteExportFormat === 'json' && superNoteExportEmbedBehavior === 'separate') {
|
if (superNoteExportFormat === 'json' && superNoteExportEmbedBehavior === 'separate') {
|
||||||
@@ -60,23 +62,25 @@ const SuperExportModal = ({ notes, exportNotes, close }: Props) => {
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
<div className="mb-3 text-base">We detected your selection includes Super notes.</div>
|
<div className="mb-2 flex items-center justify-between">
|
||||||
<div className="mb-1">What format do you want to export them in?</div>
|
<div className="text-base">Choose export format {notes.length > 1 ? 'for Super notes' : ''}</div>
|
||||||
<RadioButtonGroup
|
<Dropdown
|
||||||
items={[
|
label="Export format"
|
||||||
{ label: 'Super (.json)', value: 'json' },
|
items={[
|
||||||
{ label: 'Markdown (.md)', value: 'md' },
|
{ label: 'Super (.json)', value: 'json' },
|
||||||
{ label: 'HTML', value: 'html' },
|
{ label: 'Markdown (.md)', value: 'md' },
|
||||||
{ label: 'PDF', value: 'pdf' },
|
{ label: 'HTML', value: 'html' },
|
||||||
]}
|
{ label: 'PDF', value: 'pdf' },
|
||||||
value={superNoteExportFormat}
|
]}
|
||||||
onChange={(value) => {
|
value={superNoteExportFormat}
|
||||||
void application.setPreference(
|
onChange={(value) => {
|
||||||
PrefKey.SuperNoteExportFormat,
|
void application.setPreference(
|
||||||
value as PrefValue[PrefKey.SuperNoteExportFormat],
|
PrefKey.SuperNoteExportFormat,
|
||||||
)
|
value as PrefValue[PrefKey.SuperNoteExportFormat],
|
||||||
}}
|
)
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
{superNoteExportFormat === 'md' && (
|
{superNoteExportFormat === 'md' && (
|
||||||
<div className="mt-2 text-xs text-passive-0">
|
<div className="mt-2 text-xs text-passive-0">
|
||||||
Note that conversion to Markdown is not lossless. Some features like collapsible blocks and formatting like
|
Note that conversion to Markdown is not lossless. Some features like collapsible blocks and formatting like
|
||||||
@@ -84,6 +88,33 @@ const SuperExportModal = ({ notes, exportNotes, close }: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{superNoteExportFormat === 'pdf' && (
|
||||||
|
<div className="mt-4 flex items-center justify-between">
|
||||||
|
<div className="text-base">Page size</div>
|
||||||
|
<Dropdown
|
||||||
|
label="Page size"
|
||||||
|
items={
|
||||||
|
[
|
||||||
|
{ label: 'A3', value: 'A3' },
|
||||||
|
{ label: 'A4', value: 'A4' },
|
||||||
|
{ label: 'Letter', value: 'LETTER' },
|
||||||
|
{ label: 'Legal', value: 'LEGAL' },
|
||||||
|
{ label: 'Tabloid', value: 'TABLOID' },
|
||||||
|
] satisfies {
|
||||||
|
label: string
|
||||||
|
value: PrefValue[PrefKey.SuperNoteExportPDFPageSize]
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
value={superNoteExportPDFPageSize}
|
||||||
|
onChange={(value) => {
|
||||||
|
void application.setPreference(
|
||||||
|
PrefKey.SuperNoteExportPDFPageSize,
|
||||||
|
value as PrefValue[PrefKey.SuperNoteExportPDFPageSize],
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{superNoteExportFormat === 'md' && (
|
{superNoteExportFormat === 'md' && (
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Switch
|
<Switch
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { $isCollapsibleTitleNode } from '../../../Plugins/CollapsiblePlugin/Coll
|
|||||||
import { PDFDataNode, PDFWorker } from './PDFWorker'
|
import { PDFDataNode, PDFWorker } from './PDFWorker'
|
||||||
import { wrap } from 'comlink'
|
import { wrap } from 'comlink'
|
||||||
import { getBase64FromBlob } from '@/Utils'
|
import { getBase64FromBlob } from '@/Utils'
|
||||||
|
import { PrefKey, PrefValue } from '@standardnotes/snjs'
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
page: {
|
page: {
|
||||||
@@ -422,7 +423,7 @@ const PDFWorkerComlink = wrap<PDFWorker>(new Worker(new URL('./PDFWorker.tsx', i
|
|||||||
/**
|
/**
|
||||||
* @returns The PDF as a base64 string
|
* @returns The PDF as a base64 string
|
||||||
*/
|
*/
|
||||||
export function $generatePDFFromNodes(editor: LexicalEditor) {
|
export function $generatePDFFromNodes(editor: LexicalEditor, pageSize: PrefValue[PrefKey.SuperNoteExportPDFPageSize]) {
|
||||||
return new Promise<string>((resolve) => {
|
return new Promise<string>((resolve) => {
|
||||||
editor.getEditorState().read(() => {
|
editor.getEditorState().read(() => {
|
||||||
const root = $getRoot()
|
const root = $getRoot()
|
||||||
@@ -430,7 +431,7 @@ export function $generatePDFFromNodes(editor: LexicalEditor) {
|
|||||||
|
|
||||||
const pdfDataNodes = getPDFDataNodesFromLexicalNodes(nodes)
|
const pdfDataNodes = getPDFDataNodesFromLexicalNodes(nodes)
|
||||||
|
|
||||||
void PDFWorkerComlink.renderPDF(pdfDataNodes).then((blob) => {
|
void PDFWorkerComlink.renderPDF(pdfDataNodes, pageSize).then((blob) => {
|
||||||
void getBase64FromBlob(blob).then((base64) => {
|
void getBase64FromBlob(blob).then((base64) => {
|
||||||
resolve(base64)
|
resolve(base64)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
TextProps,
|
TextProps,
|
||||||
SVGProps,
|
SVGProps,
|
||||||
ImageWithSrcProp,
|
ImageWithSrcProp,
|
||||||
|
PageProps,
|
||||||
} from '@react-pdf/renderer'
|
} from '@react-pdf/renderer'
|
||||||
import { expose } from 'comlink'
|
import { expose } from 'comlink'
|
||||||
|
|
||||||
@@ -72,10 +73,11 @@ const Node = ({ node }: { node: PDFDataNode }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const PDFDocument = ({ nodes }: { nodes: PDFDataNode[] }) => {
|
const PDFDocument = ({ nodes, pageSize }: { nodes: PDFDataNode[]; pageSize: PageProps['size'] }) => {
|
||||||
return (
|
return (
|
||||||
<Document>
|
<Document>
|
||||||
<Page
|
<Page
|
||||||
|
size={pageSize}
|
||||||
style={{
|
style={{
|
||||||
paddingVertical: 35,
|
paddingVertical: 35,
|
||||||
paddingHorizontal: 35,
|
paddingHorizontal: 35,
|
||||||
@@ -92,8 +94,8 @@ const PDFDocument = ({ nodes }: { nodes: PDFDataNode[] }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderPDF = (nodes: PDFDataNode[]) => {
|
const renderPDF = (nodes: PDFDataNode[], pageSize: PageProps['size']) => {
|
||||||
return pdf(<PDFDocument nodes={nodes} />).toBlob()
|
return pdf(<PDFDocument pageSize={pageSize} nodes={nodes} />).toBlob()
|
||||||
}
|
}
|
||||||
|
|
||||||
expose({
|
expose({
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface {
|
|||||||
embedBehavior?: PrefValue[PrefKey.SuperNoteExportEmbedBehavior]
|
embedBehavior?: PrefValue[PrefKey.SuperNoteExportEmbedBehavior]
|
||||||
getFileItem?: (id: string) => FileItem | undefined
|
getFileItem?: (id: string) => FileItem | undefined
|
||||||
getFileBase64?: (id: string) => Promise<string | undefined>
|
getFileBase64?: (id: string) => Promise<string | undefined>
|
||||||
|
pdf?: {
|
||||||
|
pageSize?: PrefValue[PrefKey.SuperNoteExportPDFPageSize]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
if (superString.length === 0) {
|
if (superString.length === 0) {
|
||||||
@@ -145,8 +148,8 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface {
|
|||||||
resolve()
|
resolve()
|
||||||
break
|
break
|
||||||
case 'pdf': {
|
case 'pdf': {
|
||||||
void import('../Lexical/Utils/PDFExport/PDFExport').then(({ $generatePDFFromNodes }) => {
|
void import('../Lexical/Utils/PDFExport/PDFExport').then(({ $generatePDFFromNodes }): void => {
|
||||||
void $generatePDFFromNodes(this.exportEditor).then((pdf) => {
|
void $generatePDFFromNodes(this.exportEditor, config?.pdf?.pageSize || 'A4').then((pdf) => {
|
||||||
content = pdf
|
content = pdf
|
||||||
resolve()
|
resolve()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -99,6 +99,12 @@ export const getNoteBlob = async (
|
|||||||
}
|
}
|
||||||
return await getBase64FromBlob(fileBlob)
|
return await getBase64FromBlob(fileBlob)
|
||||||
},
|
},
|
||||||
|
pdf: {
|
||||||
|
pageSize: application.getPreference(
|
||||||
|
PrefKey.SuperNoteExportPDFPageSize,
|
||||||
|
PrefDefaults[PrefKey.SuperNoteExportPDFPageSize],
|
||||||
|
),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
const useMDFrontmatter =
|
const useMDFrontmatter =
|
||||||
format === 'md' &&
|
format === 'md' &&
|
||||||
|
|||||||
Reference in New Issue
Block a user