From 6673fe5d50ae80ab5535d3668d476937b3ce0f9d Mon Sep 17 00:00:00 2001 From: ZigHolding Date: Mon, 7 Jul 2025 21:44:08 +0800 Subject: [PATCH] wxmp section card-abulum --- src/commands.ts | 13 +--- src/wxmp.ts | 202 ++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 173 insertions(+), 42 deletions(-) diff --git a/src/commands.ts b/src/commands.ts index 75f5de0..907033e 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -224,22 +224,13 @@ const cmd_export_wxmp = (plugin:NoteSyncPlugin) => ({ let htmls = []; let ctx = plugin.easyapi.ceditor.getSelection(); if(!ctx){ - ctx = await plugin.app.vault.read(plugin.easyapi.cfile); - for(let section of plugin.easyapi.cmeta?.sections || []){ - if(section.type=='yaml'){ - continue - } - let sec = plugin.easyapi.editor.slice_by_position(ctx,section.position); - let html = plugin.wxmp.marked.marked(sec); - let rhtml = await plugin.wxmp.html_to_wxmp(html); - htmls.push(rhtml); - } + plugin.wxmp.tfile_to_wxmp(plugin.easyapi.cfile); }else{ let html = plugin.wxmp.marked.marked(ctx); let rhtml = await plugin.wxmp.html_to_wxmp(html); htmls.push(rhtml); + plugin.wxmp.copy_as_html(htmls); } - plugin.wxmp.copy_as_html(htmls); new Notice(`${plugin.strings.cmd_export_wxmp}: OK`,5000) } }); diff --git a/src/wxmp.ts b/src/wxmp.ts index 1a9a1b0..67c89fb 100644 --- a/src/wxmp.ts +++ b/src/wxmp.ts @@ -16,31 +16,38 @@ export class Wxmp { this.hljs = require('highlight.js'); } - get ctx_map(){ - let config:{[key:string]:any} = {}; - for(let line of this.plugin.settings.wxmp_config.split('\n')){ - let [key, value] = line.split(':'); - if(!key || !value){continue} - key = key.trim(); - value = value.trim(); - if(!key || !value){continue} - config[key] = value; + get ctx_map() { + let msg = this.plugin.settings.wxmp_config.trim(); + if(msg.trim()==''){ + return {}; + } + let config = this.plugin.easyapi.editor.yamljs.load(msg); + if(!config){ + return {}; } if(!config['h1']){ config['h1'] = this.format_wxmp_h1; } - if(!config['h2']){ + + if (!config['h1']) { + config['h1'] = this.format_wxmp_h1; + } + if (!config['h2']) { config['h2'] = this.format_wxmp_h2; } - if(!config['h3']){ + if (!config['h3']) { config['h3'] = this.format_wxmp_h3; } - if(!config['p code']){ + if (!config['p code']) { config['p code'] = this.format_wxmp_p_code; } - if(!config['li code']){ + if (!config['li code']) { config['li code'] = this.format_wxmp_li_code; } + + if (!config['section@cards-album']) { + config['section@cards-album'] = this.format_code_block_cards_album.bind(this); + } return config; } @@ -64,7 +71,7 @@ export class Wxmp { } } - async replace_regx_with_tpl(rhtml:string, regx:RegExp, tpl:string) { + async replace_regx_with_tpl(rhtml: string, regx: RegExp, tpl: string) { let matches = [...rhtml.matchAll(regx)]; let replacements = await Promise.all( @@ -82,13 +89,13 @@ export class Wxmp { return rhtml; } - convertVaultImageLinksToImgTag(htmlString:string) { + convertVaultImageLinksToImgTag(htmlString: string) { return htmlString.replace(/!\[\[([^\]]+?)\]\]/g, (match, filename) => { return ``; }); } - async convertImageTagsToBase64(htmlString:string) { + async convertImageTagsToBase64(htmlString: string) { const parser = new DOMParser(); const doc = parser.parseFromString(htmlString, 'text/html'); @@ -114,8 +121,57 @@ export class Wxmp { return new XMLSerializer().serializeToString(doc.body); } + async tfile_to_wxmp(tfile: TFile) { + let htmls = []; + let ctx = await this.app.vault.read(tfile); + for (let section of this.app.metadataCache.getFileCache(tfile)?.sections || []) { + if (section.type == 'yaml') { + continue + } + let sec = this.plugin.easyapi.editor.slice_by_position(ctx, section.position); - async html_to_wxmp(html:string) { + let rhtml:any = null; + + for(let k in this.ctx_map){ + if(k.startsWith('section@')){ + let tpl = this.ctx_map[k]; + if (typeof tpl == 'function') { + rhtml = await tpl(section,sec); + } else { + let rendered = await this.plugin.easyapi.tpl.parse_templater( + tpl, true,{section:section,sec:sec},[0] + ); + if (rendered.length > 0 && rendered[0].trim() != '') { + rhtml = rendered[0]; + } + } + if(rhtml){ + break; + } + } + } + + if(!rhtml){ + if(section.type == 'code'){ + let items = sec.split('\n'); + items[0] = items[0].slice(0,3)+'js'+'\n//'+items[0].slice(3); + sec = items.join('\n'); + } + let html = this.marked.marked(sec); + rhtml = await this.html_to_wxmp(html); + htmls.push(rhtml); + }else{ + if(Array.isArray(rhtml)){ + htmls.push(...rhtml); + }else{ + htmls.push(rhtml); + } + } + } + this.plugin.wxmp.copy_as_html(htmls); + } + + async html_to_wxmp(html: string) { let rhtml; // 替换图片 @@ -129,6 +185,7 @@ export class Wxmp { // 替换标题 for (let k in this.ctx_map) { + if(k.contains('@')){continue;} rhtml = await this.set_tag_with_tpl(rhtml, k, this.ctx_map[k]); } @@ -142,7 +199,7 @@ export class Wxmp { return rhtml } - async set_tag_with_tpl(htmlString:string, selector:string, tpl:string|Function) { + async set_tag_with_tpl(htmlString: string, selector: string, tpl: string | Function) { let parser = new DOMParser(); let doc = parser.parseFromString(htmlString, 'text/html'); let items = doc.querySelectorAll(selector); @@ -150,12 +207,12 @@ export class Wxmp { await Promise.all(Array.from(items).map(async (item) => { let content = item.textContent; - if(typeof tpl == 'function'){ + if (typeof tpl == 'function') { let rendered = tpl(content); - if(rendered){ + if (rendered) { item.innerHTML = rendered; } - }else{ + } else { // 模板渲染,传入content let rendered = await this.plugin.easyapi.tpl.parse_templater(tpl, true, content); if (rendered.length > 0) { @@ -171,7 +228,7 @@ export class Wxmp { } - setLastLiMargin(htmlString:string) { + setLastLiMargin(htmlString: string) { let parser = new DOMParser(); let doc = parser.parseFromString(htmlString, 'text/html'); @@ -197,7 +254,7 @@ export class Wxmp { } - setParagraphSpacingBeforeList(htmlString:string) { + setParagraphSpacingBeforeList(htmlString: string) { // 创建一个新的DOM解析器 let parser = new DOMParser(); // 将HTML字符串解析为文档对象 @@ -222,7 +279,7 @@ export class Wxmp { return modifiedHtmlString; } - html_replace_url(html:string) { + html_replace_url(html: string) { let regx = /]*class="external-link"[^>]*href="(.*?)"[^>]*?>([\s\S]*?)<\/a>/g let rhtml = html.replace(regx, (m, href, text) => { @@ -245,7 +302,7 @@ export class Wxmp { } - html_replace_code(html:string) { + html_replace_code(html: string) { const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const codeBlocks = Array.from(doc.querySelectorAll('pre > code[class^="language-"]')); @@ -359,7 +416,7 @@ export class Wxmp { await navigator.clipboard.write([data]); } - formatWeChatImageLink(inputHtml:string) { + formatWeChatImageLink(inputHtml: string) { // 创建一个临时的 div 元素来解析 HTML let tempDiv = document.createElement('div'); tempDiv.innerHTML = inputHtml; @@ -397,36 +454,119 @@ export class Wxmp { return tempDiv.innerHTML; } - format_wxmp_h1(title:string){ + format_wxmp_h1(title: string) { let css = `

${title}

`.trim() return css; } - format_wxmp_h2(title:string){ + format_wxmp_h2(title: string) { let css = `

${title}

`.trim() return css; } - format_wxmp_h3(title:string){ + format_wxmp_h3(title: string) { let css = `

${title}

`.trim() return css; } - format_wxmp_p_code(code:string){ + format_wxmp_p_code(code: string) { let css = ` ${code}`.trim() return css; } - format_wxmp_li_code(code:string){ + format_wxmp_li_code(code: string) { let css = ` ${code}`.trim() return css; } + + is_code_balck(section:any,sec:string,lang:string){ + return section.type == 'code' && sec.trim().slice(3).startsWith(lang) + } + + async format_code_block_cards_album(section: any, sec:string) { + if(section.type != 'code' || !sec.trim().slice(3).startsWith('cards-album')){ + return null; + } + let items = []; + for(let ctx of sec.split('images:')[1].split(/\n\s+/)){ + if(ctx.trim() == ''){ + continue; + } + let img = await this.images2html(ctx); + if(img){ + items.push(img);; + } + } + if(items.length == 0){ + return null; + } + return items; + } + + + generateSideBySideImages(base64Images: string[]) { + const sectionStart = `
`; + + const sectionEnd = `
`; + + const imageSections = base64Images.map((base64, index) => { + return ` +
+
+
+ Image +
+
+
+ `; + }).join(""); + + return sectionStart + imageSections + sectionEnd; + } + + async images2html(imgs: string | string[]): Promise { + if (Array.isArray(imgs)) { + let ximgs = []; + for (let x of imgs) { + if (x.startsWith('http')) { + ximgs.push(x) + } else { + let c = await this.image_to_img(x, true); + ximgs.push(c) + } + } + ximgs = ximgs.filter((x): x is string => x !== undefined && x !== null); + if (ximgs.length > 0) { + let html = this.generateSideBySideImages(ximgs); + return html + } else { + return null; + } + } else if (typeof imgs == 'string') { + let obsidianImgs = imgs.match(/!?\[\[([^|\]]+\.(?:png|jpg|jpeg|gif|webp|bmp|svg))(?:\|.*?)?]]/g) || []; + + let markdownImgs = Array.from( + imgs.matchAll(/!\[\]\((https?:\/\/[^\s)]+)\)/g), + m => m[1] + ); + + // 合并两种图片链接 + let ximgs = [...obsidianImgs, ...markdownImgs]; + return this.images2html(ximgs); + }else{ + return null; + } + } + + }