From 3c03eca895a35457779649cb1b0c07aafebf04cf Mon Sep 17 00:00:00 2001 From: LeafYeeXYZ Date: Tue, 27 Aug 2024 20:32:18 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=B8=BB=E8=A6=81=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=85=A8=E9=83=A8=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- bun.lockb | Bin 295440 -> 295440 bytes electron.vite.config.ts | 2 +- {resources/lib => lib}/render.ts | 0 {resources/lib => lib}/theme.ts | 14 ++++ src/main/index.ts | 79 ++++++++++++++++++++++- src/preload/index.d.ts | 8 +++ src/preload/index.ts | 30 ++++++--- src/renderer/src/components/Editor.tsx | 10 +-- src/renderer/src/components/Preview.tsx | 12 ++-- src/renderer/src/components/Toolbar.tsx | 82 ++++++++++++++++++++---- src/renderer/src/lib/useStore.tsx | 2 +- tsconfig.node.json | 2 +- tsconfig.web.json | 3 +- 14 files changed, 209 insertions(+), 37 deletions(-) rename {resources/lib => lib}/render.ts (100%) rename {resources/lib => lib}/theme.ts (96%) diff --git a/README.md b/README.md index 8f3e267..0207ca0 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ 相比于 LaTeX, 本软件更加简单易用, 省去了复杂的配置和学习成本, 但仍然能够满足大部分学术论文的排版需求; 相比于 Word, 本软件完全接管了格式调整的工作, 你只需选择指定的论文模板 (目前仅支持心理学报格式), 专心写作即可 -如果你有一定的计算机基础, 也推荐您使用命令行程序 [MarkdownPaper](https://github.com/LeafYeeXYZ/MarkdownPaper), 它的处理逻辑与本软件一致, 但更加灵活, 您可以使用任何您喜欢的编辑器撰写学术论文 +如果你有一定的计算机基础, 也推荐您使用命令行程序 [MarkdownPaper](https://github.com/LeafYeeXYZ/MarkdownPaper), 它的处理逻辑与本软件基本一致, 但更加灵活和强大 (如支持导出 DOCX 文件), 您还可以使用任何您喜欢的编辑器撰写学术论文 ## 使用方法 diff --git a/bun.lockb b/bun.lockb index 198a789d2070f22aff14991c54cf2dccfbb3220e..b83b8d3710038e1eb2f3b8089a52f351fb87992a 100755 GIT binary patch delta 180 zcmbQxA~XSr^c2iqy_y=-r7bW|(&!JDVDCZOOXunyZE)nQtjv^4kDtT2(eIeWWRc0H zn>{Uao;&>N_mQ4<-c=xVV#vyxNS`d;=kJ-N6sP@2oVF%7fB*O8|L&Arco6Tb$-eH+ z`b+(`VV7R6do6u+b#U(`t+0}l*>~E%Dll&Us=)N`wjvV)6jYTKmn7z;Fr2utIYvxK gX$li#oQa;Pp6PaxzfAu&Oc#=4QQB^@j`_}I0Kv9S!2kdN delta 180 zcmV;l089Ungc6X15|Az+kT5)f%1PODJ6|;}g$MKN8Cxb+oksp4uvV_5l5Sp$u};P~ zlT\ @@ -33,7 +37,17 @@ export interface MarkdownPaperTheme { pdfOptions: PDFOptions } +export const getTheme = (themeName: string): MarkdownPaperTheme => { + switch (themeName) { + case 'aps': + return APS + default: + return APS + } +} + export const APS: MarkdownPaperTheme = { + themeName: 'aps', css: ` * { font-family: 'Times', 'Times New Roman', '宋体', 'SimSun', '华文宋体', 'STSong'; /* 所有数字和英文字体都用 Times New Roman */ diff --git a/src/main/index.ts b/src/main/index.ts index 9265704..8a2c3e2 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -3,12 +3,16 @@ import { join } from 'path' import { electronApp, optimizer, is } from '@electron-toolkit/utils' import icon from '../../resources/icon.png?asset' import fs from 'node:fs/promises' +import { readFileSync } from 'node:fs' import path from 'node:path' +import puppeteer from 'puppeteer' +import { mdToHtml } from '../../lib/render' +import { getTheme } from '../../lib/theme' function createWindow(): void { // Create the browser window. const mainWindow = new BrowserWindow({ - width: 1000, + width: 905, height: 670, minWidth: 768, minHeight: 512, @@ -98,6 +102,79 @@ app.whenReady().then(() => { await fs.writeFile(path.resolve(filepath, filename), content, 'utf-8') } ) + ipcMain.handle( + 'createTextFile', + async (_, content: string, ext: string, defaultName: string): Promise => { + // 弹出文件选择框 + const { filePath, canceled } = await dialog.showSaveDialog({ + title: '导出论文', + filters: [{ name: '文本文件', extensions: [ext] }], + defaultPath: defaultName + '.' + ext, + showsTagField: false, + properties: ['createDirectory'] + }) + if (canceled) { + return false + } else { + await fs.writeFile(filePath, content, 'utf-8') + return true + } + } + ) + ipcMain.handle( + 'embedImageIntoHtml', + async (_, html: string, filepath: string): Promise => { + return html.replace(/ { + if (p1.startsWith('http')) return match + try { + const url = path.resolve(filepath, decodeURI(p1)) + const data = readFileSync(url).toString('base64') + return ` => { + const { filePath, canceled } = await dialog.showSaveDialog({ + title: '导出论文', + filters: [{ name: 'PDF 文件', extensions: ['pdf'] }], + defaultPath: filename.split('.')[0] + '.pdf', + showsTagField: false, + properties: ['createDirectory'] + }) + if (canceled) { + return false + } + const dist = path.resolve(filePath) + const browser = await puppeteer.launch() + const page = await browser.newPage() + const theme = getTheme(themeName) + const html = (await mdToHtml(markdown, theme)).replace(/ { + if (p1.startsWith('http')) return match + try { + const url = path.resolve(filepath, decodeURI(p1)) + const data = readFileSync(url).toString('base64') + return ` Promise<{ filepath: string; filename: string }> openPaper: () => Promise<{ filepath: string; filename: string; content: string }> savePaper: (filepath: string, filename: string, content: string) => Promise + createTextFile: (content: string, ext: string, defaultName: string) => Promise + embedImageIntoHtml: (html: string, filepath: string) => Promise + createPdf: ( + markdown: string, + themeName: string, + filepath: string, + filename: string + ) => Promise } } } diff --git a/src/preload/index.ts b/src/preload/index.ts index eac9a9f..dff9d4b 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -3,17 +3,31 @@ import { electronAPI } from '@electron-toolkit/preload' // Custom APIs for renderer const api = { - openBrowser: async (url: string): Promise => { - await electronAPI.ipcRenderer.invoke('openBrowser', url) + openBrowser: (url: string): Promise => { + return electronAPI.ipcRenderer.invoke('openBrowser', url) }, - newPaper: async (): Promise<{ filepath: string; filename: string }> => { - return await electronAPI.ipcRenderer.invoke('newPaper') + newPaper: (): Promise<{ filepath: string; filename: string }> => { + return electronAPI.ipcRenderer.invoke('newPaper') }, - openPaper: async (): Promise<{ filepath: string; filename: string; content: string }> => { - return await electronAPI.ipcRenderer.invoke('openPaper') + openPaper: (): Promise<{ filepath: string; filename: string; content: string }> => { + return electronAPI.ipcRenderer.invoke('openPaper') }, - savePaper: async (filepath: string, filename: string, content: string): Promise => { - await electronAPI.ipcRenderer.invoke('savePaper', filepath, filename, content) + savePaper: (filepath: string, filename: string, content: string): Promise => { + return electronAPI.ipcRenderer.invoke('savePaper', filepath, filename, content) + }, + createTextFile: (content: string, ext: string, defaultName: string): Promise => { + return electronAPI.ipcRenderer.invoke('createTextFile', content, ext, defaultName) + }, + embedImageIntoHtml: (html: string, filepath: string): Promise => { + return electronAPI.ipcRenderer.invoke('embedImageIntoHtml', html, filepath) + }, + createPdf: ( + markdown: string, + themeName: string, + filepath: string, + filename: string + ): Promise => { + return electronAPI.ipcRenderer.invoke('createPdf', markdown, themeName, filepath, filename) } } diff --git a/src/renderer/src/components/Editor.tsx b/src/renderer/src/components/Editor.tsx index 5983e27..50f8b56 100644 --- a/src/renderer/src/components/Editor.tsx +++ b/src/renderer/src/components/Editor.tsx @@ -2,7 +2,7 @@ import { Input } from 'antd' import { useStore } from '../lib/useStore' export default function Editor(): JSX.Element { - const { setMarkdown, filepath, filename, markdown } = useStore() + const { setMarkdown, filepath, filename, markdown, disabled } = useStore() return (
- 当前文件: {filename || '...'} + 当前文件:   + {filename || '...'}
- 存放位置: {filepath || '...'} + 存放位置:   + {filepath || '...'}
setMarkdown(e.target.value)} - disabled={filepath === '' || filename === ''} + disabled={filepath === '' || filename === '' || disabled} value={markdown} />
diff --git a/src/renderer/src/components/Preview.tsx b/src/renderer/src/components/Preview.tsx index a01d637..28874cc 100644 --- a/src/renderer/src/components/Preview.tsx +++ b/src/renderer/src/components/Preview.tsx @@ -1,13 +1,15 @@ -import { mdToHtml } from '../../../../resources/lib/render' +import { mdToHtml } from '../../../../lib/render' import { useStore } from '../lib/useStore' import { useState, useEffect } from 'react' export default function Preview(): JSX.Element { - const { markdown, theme } = useStore() + const { markdown, theme, filepath } = useStore() const [html, setHtml] = useState('') useEffect(() => { - mdToHtml(markdown, theme).then(setHtml) - }, [markdown, theme]) + mdToHtml(markdown, theme) + .then((html) => window.api.embedImageIntoHtml(html, filepath)) + .then((html) => setHtml(html)) + }, [markdown, theme, filepath]) return (
) : ( - 内容预览 + 论文内容预览 )}
diff --git a/src/renderer/src/components/Toolbar.tsx b/src/renderer/src/components/Toolbar.tsx index d1c7453..29aa9a7 100644 --- a/src/renderer/src/components/Toolbar.tsx +++ b/src/renderer/src/components/Toolbar.tsx @@ -6,28 +6,42 @@ import { QuestionCircleOutlined, FileMarkdownOutlined, FilePdfOutlined, - FileTextOutlined, - FileWordOutlined + FileTextOutlined } from '@ant-design/icons' import { useStore } from '../lib/useStore' +import { mdToHtml } from '../../../../lib/render' +import { flushSync } from 'react-dom' export default function Toolbar(): JSX.Element { - const { filepath, filename, setFilename, setFilepath, setMarkdown, messageApi, markdown } = - useStore() + const { + filepath, + filename, + setFilename, + setFilepath, + setMarkdown, + messageApi, + markdown, + theme, + disabled, + setDisabled + } = useStore() return (

导出:

- - - -

论文格式:

- 心理学报   diff --git a/src/renderer/src/lib/useStore.tsx b/src/renderer/src/lib/useStore.tsx index 5eea495..28b2ada 100644 --- a/src/renderer/src/lib/useStore.tsx +++ b/src/renderer/src/lib/useStore.tsx @@ -1,5 +1,5 @@ import { create } from 'zustand' -import { MarkdownPaperTheme, APS } from '../../../../resources/lib/theme' +import { MarkdownPaperTheme, APS } from '../../../../lib/theme' import type { MessageInstance } from 'antd/es/message/interface' interface State { diff --git a/tsconfig.node.json b/tsconfig.node.json index db23a68..a9fd368 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -1,6 +1,6 @@ { "extends": "@electron-toolkit/tsconfig/tsconfig.node.json", - "include": ["electron.vite.config.*", "src/main/**/*", "src/preload/**/*"], + "include": ["electron.vite.config.*", "src/main/**/*", "src/preload/**/*", "lib/**/*.ts"], "compilerOptions": { "composite": true, "types": ["electron-vite/node"] diff --git a/tsconfig.web.json b/tsconfig.web.json index 5eb6d41..c0e614e 100644 --- a/tsconfig.web.json +++ b/tsconfig.web.json @@ -4,7 +4,8 @@ "src/renderer/src/env.d.ts", "src/renderer/src/**/*", "src/renderer/src/**/*.tsx", - "src/preload/*.d.ts" + "src/preload/*.d.ts", + "lib/**/*.ts" , "resources/lib" ], "compilerOptions": { "composite": true,