// @flow import fs from 'fs' import path from 'path' import * as React from 'react' import ReactDOM from 'react-dom' import { markdownRenderer, logger, PrintingContext } from 'inkdrop' import { Provider } from 'react-redux' import { addTitleToMarkdown } from './add-title-to-markdown' import type { Note } from 'inkdrop-model' export { addTitleToMarkdown } export async function renderHTML(markdown: string): Promise { const file = await markdownRenderer.render(markdown) const container = document.createElement('div') container.classList.add('inkdrop-export') container.style.position = 'absolute' container.style.zIndex = '-1000' const { body } = document if (body) { body.appendChild(container) ReactDOM.render( {file.result} , container ) // wait for `useEffect` update await new Promise(resolve => setTimeout(resolve, 30)) const html = container.innerHTML body.removeChild(container) return html } else { throw new Error('Unexpected error') } } export function getStylesheets(): string { return inkdrop.styles .getStyleElements() .filter(el => { return ( typeof el.context === 'undefined' || el.context === 'inkdrop-preview' ) }) .reduce((styles, el) => { return styles + el.outerHTML }, '') } export async function replaceImages( body: string, dirToSave: string, basePath?: string ): Promise { // find attachments const uris = body.match(/inkdrop:\/\/file:[^) "']*/g) || [] for (let i = 0; i < uris.length; ++i) { const uri = uris[i] let imagePath = await exportImage(uri, dirToSave) if (typeof imagePath === 'string') { if (basePath) imagePath = path.relative(basePath, imagePath) body = body.replace(uri, imagePath) } } return body } export async function replaceHTMLImagesWithDataURI( html: string ): Promise { const { File: IDFile } = require('inkdrop').models const m = html.match(/inkdrop-file:\/\/file:[^"]*/g) if (m instanceof Array && m.length > 0) { for (const uri of m) { try { const [, docId] = uri.match(/\/\/(file:.*)$/) const file = await IDFile.loadWithId(docId) const base64 = file.getAsBase64() html = html.replace( uri, () => `data:${file.contentType};base64,${base64}` ) } catch (e) { logger.error('Failed to replace images with data URI:', uri) logger.error(e) } } } return html } export async function exportImage( uri: string, dirToSave: string ): Promise { try { const { dataStore } = inkdrop.main const db = dataStore.getLocalDB() const [, docId] = uri.match(/^inkdrop:\/\/(file:.*)$/) || [] if (docId) { const file = await db.files.get(docId) const buffer = await db.utils.getBufferFromFile(docId) const name = file.name || 'index' const fileName = docId.split(':')[1] + '-' + name const filePath = path.join(dirToSave, fileName) fs.writeFileSync(filePath, buffer) return filePath } } catch (e) { console.error('Failed to export image file:', e) return false } } export async function createHTML( note: Note, options: { addTitle?: boolean, templateHtml?: string } = { addTitle: true } ): Promise { const templateFilePath = require.resolve( path.join('inkdrop-export-utils', 'assets', 'template.html') ) const templateHtml = options.templateHtml || fs.readFileSync(templateFilePath, 'utf-8') const markdown = options.addTitle ? addTitleToMarkdown(note.body, note.title) : note.body const htmlBody = await renderHTML(markdown) const htmlStyles = getStylesheets() const outputHtml = templateHtml .replace('{%body%}', () => htmlBody) .replace('{%styles%}', () => htmlStyles) .replace('{%title%}', () => note.title) return outputHtml } export async function createWebView(note: Note): Object { const outputHtml = await createHTML(note, { addTitle: true }) const fn = saveHTMLToTmp(outputHtml) const webView: Object = document.createElement('webview') window.document.body.appendChild(webView) webView.style.position = 'absolute' webView.src = fn await new Promise((resolve, reject) => { let resolved = false const done = () => { if (!resolved) { resolved = true resolve() } } webView.addEventListener('did-finish-load', done) webView.addEventListener('did-fail-load', reject) setTimeout(done, 1000 * 5) }) return webView } export function removeWebView(webView: Object, delay: number = 30 * 60 * 1000) { setTimeout(() => window.document.body.removeChild(webView), delay) } export function saveHTMLToTmp(html: string): string { const fn = path.join(require('os').tmpdir(), 'inkdrop-export.html') fs.writeFileSync(fn, html, 'utf-8') return fn }