返回 oh-my-ppt
page-html-builders.ts
根目录 / src / main / session / page-html-builders.ts
1 import * as cheerio from 'cheerio'
2 import type { AnyNode } from 'domhandler'
3 import { ensureMasterStyleLink, setMasterPageNumber } from '../presentation/html/master-link'
4
5 /**
6 * 纯函数:把 HTML 里出现的 oldPageId(按词边界匹配)整体替换为 nextPageId,
7 * 用于派生新页时避免 pageId 身份与源页串台。只依赖 cheerio,不碰 fs / electron / db,
8 * 便于在单测环境直接验证。
9 */
10 export const replacePageIdentity = (html: string, oldPageId: string, nextPageId: string): string => {
11 const oldId = oldPageId.trim()
12 if (!oldId || oldId === nextPageId) return html
13 const escapedOldId = oldId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
14 const boundaryPattern = new RegExp(`(^|[^A-Za-z0-9_-])${escapedOldId}(?=$|[^A-Za-z0-9_-])`, 'g')
15 return html.replace(boundaryPattern, `$1${nextPageId}`)
16 }
17
18 export const clearVisibleText = ($: cheerio.CheerioAPI, root: cheerio.Cheerio<AnyNode>): void => {
19 root.find('input, textarea').each((_, node) => {
20 const el = $(node)
21 el.removeAttr('value')
22 el.removeAttr('placeholder')
23 el.text('')
24 })
25 root.find('*').contents().each((_, node) => {
26 const parentTag = node.parent?.type === 'tag' ? node.parent.name.toLowerCase() : ''
27 if (parentTag === 'script' || parentTag === 'style') return
28 if (node.type === 'text' && node.data?.trim()) {
29 node.data = ''
30 }
31 })
32 }
33
34 /**
35 * 基于源页 HTML 生成一个**空白页**:重写 pageId 身份 + 改 title,并清空
36 * `.ppt-page-content` 内的可见文字、打上 data-blank-page 标记。
37 */
38 export function buildBlankPageHtmlFromSource(args: {
39 html: string
40 oldPageId: string
41 nextPageId: string
42 pageNumber?: number
43 title: string
44 }): string {
45 const rewritten = replacePageIdentity(args.html, args.oldPageId, args.nextPageId)
46 const $ = cheerio.load(rewritten, { scriptingEnabled: false })
47 $('title').text(args.title)
48 $('body').attr('data-page-id', args.nextPageId)
49 $('[data-page-id]').each((_, node) => {
50 const el = $(node)
51 if ((el.attr('data-page-id') || '').trim() === args.oldPageId) {
52 el.attr('data-page-id', args.nextPageId)
53 }
54 })
55
56 const content = $('.ppt-page-content').first()
57 if (content.length > 0) {
58 clearVisibleText($, content)
59 content.attr('data-blank-page', '1')
60 }
61
62 return typeof args.pageNumber === 'number'
63 ? setMasterPageNumber(ensureMasterStyleLink($.html()), args.pageNumber)
64 : ensureMasterStyleLink($.html())
65 }
66
67 /**
68 * 复制页面用:与 buildBlankPageHtmlFromSource 共用「换 pageId 身份 + 改 title」逻辑,
69 * 但**保留全部可见内容**(不调用 clearVisibleText,不打 data-blank-page 标记),
70 * 只把 pageId 相关身份重写到新页,避免两页之间 pageId/block 引用串台。
71 */
72 export function buildDuplicatePageHtmlFromSource(args: {
73 html: string
74 oldPageId: string
75 nextPageId: string
76 pageNumber?: number
77 title: string
78 }): string {
79 const rewritten = replacePageIdentity(args.html, args.oldPageId, args.nextPageId)
80 const $ = cheerio.load(rewritten, { scriptingEnabled: false })
81 $('title').text(args.title)
82 $('body').attr('data-page-id', args.nextPageId)
83 $('[data-page-id]').each((_, node) => {
84 const el = $(node)
85 if ((el.attr('data-page-id') || '').trim() === args.oldPageId) {
86 el.attr('data-page-id', args.nextPageId)
87 }
88 })
89 return typeof args.pageNumber === 'number'
90 ? setMasterPageNumber(ensureMasterStyleLink($.html()), args.pageNumber)
91 : ensureMasterStyleLink($.html())
92 }
93
93 lines TYPESCRIPT