返回 oh-my-ppt
session-scaffold.ts
根目录 / src / main / ipc / runtime / session-scaffold.ts
1 import fs from 'fs'
2 import path from 'path'
3 import { is } from '@electron-toolkit/utils'
4 import {
5 buildPageScaffoldHtml,
6 buildProjectIndexHtml,
7 SESSION_ASSET_FILE_NAMES,
8 type DeckPageFile
9 } from '../../session/template-builder'
10 import { createSessionMasterIfMissing } from '../../session/master-service'
11
12 export type SessionScaffold = {
13 resolveSessionAssetSourcePath(fileName: string): string
14 ensureSessionAssets(projectDir: string): Promise<void>
15 scaffoldProjectFiles(args: {
16 deckTitle: string
17 indexPath: string
18 pages: Array<{ pageNumber: number; pageId: string; title: string; htmlPath: string }>
19 slideSize: import('@shared/slide-size').SlideSizePreset
20 }): Promise<void>
21 }
22
23 export function createSessionScaffold(): SessionScaffold {
24 const resolveSessionAssetSourcePath = (fileName: string): string => {
25 const baseDir = is.dev
26 ? path.join(process.cwd(), 'resources')
27 : path.join(process.resourcesPath, 'app.asar.unpacked', 'resources')
28 const sourcePath = path.join(baseDir, fileName)
29 if (fs.existsSync(sourcePath)) return sourcePath
30 throw new Error(`缺少资源文件 ${fileName}。期望路径: ${sourcePath}`)
31 }
32
33 const ensureSessionAssets = async (projectDir: string): Promise<void> => {
34 const assetsDir = path.join(projectDir, 'assets')
35 const imagesDir = path.join(projectDir, 'images')
36 const videosDir = path.join(projectDir, 'videos')
37 const docsDir = path.join(projectDir, 'docs')
38 await fs.promises.mkdir(assetsDir, { recursive: true })
39 await fs.promises.mkdir(imagesDir, { recursive: true })
40 await fs.promises.mkdir(videosDir, { recursive: true })
41 await fs.promises.mkdir(docsDir, { recursive: true })
42 await Promise.all(
43 SESSION_ASSET_FILE_NAMES.map(async (sourceRelPath) => {
44 const sourcePath = resolveSessionAssetSourcePath(sourceRelPath)
45 const targetPath = path.join(assetsDir, sourceRelPath)
46 await fs.promises.mkdir(path.dirname(targetPath), { recursive: true })
47 await fs.promises.copyFile(sourcePath, targetPath)
48 })
49 )
50 const katexFontsSource = resolveSessionAssetSourcePath('katex/fonts')
51 const katexFontsTarget = path.join(assetsDir, 'katex', 'fonts')
52 await fs.promises.mkdir(katexFontsTarget, { recursive: true })
53 const katexFontFiles = await fs.promises.readdir(katexFontsSource)
54 await Promise.all(
55 katexFontFiles
56 .filter((fileName) => fileName.endsWith('.woff2'))
57 .map((fileName) =>
58 fs.promises.copyFile(
59 path.join(katexFontsSource, fileName),
60 path.join(katexFontsTarget, fileName)
61 )
62 )
63 )
64 }
65
66 const scaffoldProjectFiles = async (args: {
67 deckTitle: string
68 indexPath: string
69 pages: Array<{ pageNumber: number; pageId: string; title: string; htmlPath: string }>
70 slideSize: import('@shared/slide-size').SlideSizePreset
71 }): Promise<void> => {
72 const { deckTitle, indexPath, pages, slideSize } = args
73 await createSessionMasterIfMissing(path.dirname(indexPath))
74 await Promise.all(
75 pages.map((page) =>
76 fs.promises.writeFile(
77 page.htmlPath,
78 buildPageScaffoldHtml(
79 {
80 pageNumber: page.pageNumber,
81 pageId: page.pageId,
82 title: page.title
83 },
84 slideSize
85 ),
86 'utf-8'
87 )
88 )
89 )
90 await fs.promises.writeFile(
91 indexPath,
92 buildProjectIndexHtml(
93 deckTitle,
94 pages.map(
95 (page): DeckPageFile => ({
96 pageNumber: page.pageNumber,
97 pageId: page.pageId,
98 title: page.title,
99 htmlPath: path.basename(page.htmlPath)
100 })
101 ),
102 slideSize
103 ),
104 'utf-8'
105 )
106 }
107
108 return { resolveSessionAssetSourcePath, ensureSessionAssets, scaffoldProjectFiles }
109 }
110
110 lines TYPESCRIPT