// export.jsx — 把一页打成「自带样式、可独立打开、可分享」的网页 / 幻灯片 / Markdown
// 依赖 engine.jsx 的 downloadFile / safeName / contentHash
function escHtml(s) {
return String(s == null ? '' : s).replace(/[&<>"']/g, c => (
{ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]
));
}
// 媒体标记 [名称:完成态] → 识别成图 / 视频 / 音乐 / 配音 / 链接
function mediaKind(name) {
const n = String(name || '');
if (/视频|短片|youtube|影/i.test(n)) return 'video';
if (/配音|语音|tts|朗读/i.test(n)) return 'voice';
if (/音乐|配乐|谱|声/i.test(n)) return 'audio';
if (/连接|链接|link/i.test(n)) return 'link';
if (/图|画|插画|image/i.test(n)) return 'image';
return 'image';
}
const MEDIA_GLYPH = {
image: '',
video: '',
audio: '',
voice: '',
link: '',
};
const MEDIA_LABEL = { image: '图像', video: '影像', audio: '配乐', voice: '配音', link: '连接' };
function mediaCardHtml(name, done) {
const k = mediaKind(name);
return ''
+ (m.name ? '
$1');
s = s.replace(/\*\*([^*]+)\*\*/g, '$1');
s = s.replace(/(^|[^*])\*([^*\n]+)\*/g, '$1$2');
s = s.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1');
return s;
}
// 正文(markdown 文本 + [媒体标记])→ HTML;media 为按序的真实媒体(内联用)
function exportBodyToHtml(body, media) {
const lines = String(body || '').split('\n');
let html = '', para = [], mi = 0;
const flush = () => { if (para.length) { html += '
' + para.map(mdInlineExport).join('
') + '
' + escHtml(buf.join('\n')) + ''; continue;
}
// 媒体标记 [名称:完成态] —— 优先内联真实媒体,缺失时回落为占位卡
const m = t.match(/^\[([^::]+)[::]\s*(.*?)\]$/);
if (m) {
flush();
const payload = media && media[mi++];
html += (payload && realMediaHtml(payload)) || mediaCardHtml(m[1].trim(), m[2].trim());
continue;
}
// 列表
if (/^[-*]\s/.test(t)) {
flush(); const buf = [];
while (i < lines.length && /^\s*[-*]\s/.test(lines[i])) { buf.push(lines[i].trim().replace(/^[-*]\s/, '')); i++; }
i--; html += '' + buf.filter(Boolean).map(b => ''; continue; } if (/^#{1,3}\s/.test(t)) { flush(); const lv = t.match(/^#+/)[0].length; html += '' + mdInlineExport(b) + '
').join('') + '
这一页还没有正文。
'; } const SHOWCASE_CSS = ` :root{--paper:#FAF6EE;--paper2:#F3EDE0;--ink:#2a2a2a;--ink2:#6a6a6a;--ink3:#a6a097;--sea:#1a2840;--flame:#8B2020;--gold:#E8C840;--rule:#E4DBCB;--mono:'JetBrains Mono','SF Mono',Menlo,monospace;--serif:'EB Garamond',Georgia,serif;--cjk:'Noto Serif SC','Songti SC',serif;} *{box-sizing:border-box;} html,body{margin:0;padding:0;} body{background:var(--paper);color:var(--ink);font-family:var(--cjk);-webkit-font-smoothing:antialiased;line-height:1.95;} body::before{content:'';position:fixed;inset:0;pointer-events:none;background:radial-gradient(1200px 600px at 50% -10%,rgba(26,40,64,.05),transparent 60%);} .pp-wrap{max-width:680px;margin:0 auto;padding:0 28px 96px;} .pp-mast{display:flex;align-items:center;gap:10px;padding:30px 0 0;} .pp-mark{font-family:var(--mono);font-size:11px;letter-spacing:.46em;color:var(--sea);text-transform:uppercase;} .pp-rule{flex:1;height:1px;background:var(--rule);} .pp-flame{width:26px;height:3px;background:var(--flame);border-radius:2px;} .pp-hero{padding:54px 0 30px;border-bottom:1px solid var(--rule);} .pp-kicker{font-family:var(--mono);font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--ink3);margin-bottom:18px;} .pp-title{font-family:var(--cjk);font-weight:500;font-size:39px;line-height:1.28;margin:0;letter-spacing:.01em;} .pp-en{font-family:var(--serif);font-style:italic;font-weight:400;font-size:22px;color:var(--ink2);margin:10px 0 0;} .pp-coords{display:flex;flex-wrap:wrap;gap:8px;margin-top:24px;} .pp-coord{font-family:var(--mono);font-size:11px;color:var(--ink2);background:var(--paper2);border:1px solid var(--rule);border-radius:6px;padding:4px 10px;} .pp-coord b{color:var(--ink3);font-weight:400;margin-right:6px;} .pp-tags{display:flex;flex-wrap:wrap;gap:6px;margin-top:14px;} .pp-tag{font-size:11px;padding:3px 9px;border-radius:5px;border:1px solid;} .pp-tag.cat{color:#534AB7;background:#EEEDFE;border-color:#cfccf5;} .pp-tag.note{color:#085041;background:#E1F5EE;border-color:#bfe6d8;} .pp-tag.loc{color:#7a5b00;background:#FDF6E0;border-color:#ecdca0;} .pp-body{padding:34px 0 0;font-family:var(--cjk);font-size:17px;line-height:1.98;color:#222;} .pp-body p{margin:0 0 18px;text-wrap:pretty;} .pp-body .pp-sub{font-family:var(--cjk);font-weight:500;margin:30px 0 12px;line-height:1.4;} .pp-body h2.pp-sub{font-size:21px;}.pp-body h3.pp-sub{font-size:18px;}.pp-body h4.pp-sub{font-size:16px;color:var(--ink2);} .pp-body blockquote{margin:0 0 18px;padding:6px 0 6px 20px;border-left:2px solid var(--gold);background:linear-gradient(90deg,#FDF6E0,transparent 80%);font-style:italic;color:#4a4636;} .pp-body blockquote p{margin:0 0 6px;}.pp-body blockquote p:last-child{margin:0;} .pp-body ul{margin:0 0 18px;padding-left:22px;}.pp-body li{margin:0 0 7px;} .pp-body strong{font-weight:600;color:#1c1c1c;}.pp-body em{font-style:italic;} .pp-body code{font-family:var(--mono);font-size:14px;background:var(--paper2);padding:1px 6px;border-radius:4px;} .pp-body pre{font-family:var(--mono);font-size:13.5px;line-height:1.7;background:var(--paper2);border:1px solid var(--rule);padding:14px 16px;border-radius:10px;overflow-x:auto;margin:0 0 18px;} .pp-body pre code{background:none;padding:0;font-size:13.5px;} .pp-body a{color:var(--flame);text-decoration:none;border-bottom:1px solid rgba(139,32,32,.32);} .pp-body hr{border:0;border-top:1px solid var(--rule);margin:24px 0;} .pp-empty{color:var(--ink3);font-style:italic;} .pp-media{display:flex;align-items:center;gap:14px;margin:0 0 18px;padding:16px 18px;border:1px solid var(--rule);border-radius:12px;background:repeating-linear-gradient(135deg,#fff,#fff 9px,#FBF7EF 9px,#FBF7EF 18px);} .pp-media-ico{flex:none;width:38px;height:38px;border-radius:9px;background:#fff;border:1px solid var(--rule);display:grid;place-items:center;color:var(--sea);} .pp-media-ico svg{width:21px;height:21px;} .pp-media-meta{display:flex;flex-direction:column;gap:3px;} .pp-media-meta b{font-family:var(--cjk);font-weight:500;font-size:14px;color:var(--ink);} .pp-media-meta i{font-family:var(--mono);font-style:normal;font-size:11px;color:var(--ink3);} .pp-video .pp-media-ico{color:var(--flame);}.pp-audio .pp-media-ico,.pp-voice .pp-media-ico{color:#085041;}.pp-link .pp-media-ico{color:#534AB7;} .pp-fig{margin:0 0 22px;} .pp-fig img{display:block;width:100%;height:auto;border-radius:12px;border:1px solid var(--rule);} .pp-fig-video video{display:block;width:100%;border-radius:12px;border:1px solid var(--rule);background:#000;} .pp-fig figcaption{font-family:var(--mono);font-size:11px;color:var(--ink3);margin-top:8px;text-align:center;} .pp-embed-frame{position:relative;width:100%;aspect-ratio:16/9;border-radius:12px;overflow:hidden;background:#000;border:1px solid var(--rule);} .pp-embed-frame iframe{position:absolute;inset:0;width:100%;height:100%;} .pp-audio{margin:0 0 22px;padding:16px 18px;border:1px solid var(--rule);border-radius:12px;background:var(--paper2);} .pp-audio .pp-audio-head{display:flex;align-items:center;gap:13px;margin-bottom:12px;} .pp-audio-ico{flex:none;width:38px;height:38px;border-radius:9px;background:#fff;border:1px solid var(--rule);display:grid;place-items:center;color:#085041;} .pp-audio-ico svg{width:21px;height:21px;} .pp-audio-meta{display:flex;flex-direction:column;gap:3px;} .pp-audio-meta b{font-family:var(--cjk);font-weight:500;font-size:14px;color:var(--ink);} .pp-audio-meta i{font-family:var(--mono);font-style:normal;font-size:11px;color:var(--ink3);} .pp-audio audio{width:100%;display:block;} .pp-voice{background:linear-gradient(180deg,#FBF3EE,var(--paper2));border-color:#E8C9BC;} .pp-voice .pp-audio-ico{color:var(--flame);border-color:#E8C9BC;} .pp-seal{display:flex;gap:16px;margin:46px 0 0;padding:20px 22px;background:var(--paper2);border:1px solid var(--rule);border-radius:14px;position:relative;} .pp-seal-fp{flex:none;width:40px;height:40px;border-radius:50%;background:#fff;border:1px solid var(--rule);display:grid;place-items:center;color:var(--sea);} .pp-seal-fp svg{width:23px;height:23px;} .pp-seal-body{flex:1;min-width:0;} .pp-seal-h{font-family:var(--mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink3);font-weight:600;margin-bottom:11px;} .pp-row{display:flex;gap:12px;font-size:12px;margin-bottom:7px;align-items:baseline;} .pp-row .k{flex:none;width:64px;color:var(--ink3);font-family:var(--cjk);} .pp-row .v{font-family:var(--mono);color:var(--ink2);word-break:break-all;} .pp-row .v.hash{color:var(--sea);} .pp-row .v.link{color:var(--flame);} .pp-seal-note{font-family:var(--cjk);font-size:11px;color:var(--ink3);line-height:1.7;margin-top:10px;padding-top:10px;border-top:1px solid var(--rule);} .pp-chop{flex:none;align-self:flex-start;width:32px;height:32px;background:var(--flame);color:#F6E4DC;font-family:var(--cjk);font-size:11px;line-height:1.12;text-align:center;padding-top:4px;border-radius:5px;letter-spacing:.04em;} .pp-foot{margin:40px 0 0;padding-top:22px;border-top:1px solid var(--rule);display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;} .pp-foot-l{font-family:var(--cjk);font-size:12px;color:var(--ink2);} .pp-foot-l b{font-family:var(--mono);letter-spacing:.3em;color:var(--sea);font-weight:400;text-transform:uppercase;font-size:11px;} .pp-foot-r{font-family:var(--cjk);font-size:11px;color:var(--ink3);font-style:italic;} @media(max-width:560px){.pp-wrap{padding:0 18px 64px;}.pp-title{font-size:30px;}} `; const FP_SVG = ''; // 完整自带样式的网页 function buildShowcaseHTML(o) { o = o || {}; const title = o.title || '未命名'; const coords = []; if (o.date) coords.push('制作' + escHtml(o.date) + ''); if (o.city) coords.push('地点' + escHtml(o.city) + ''); if (o.scene) coords.push('场景' + escHtml(o.scene) + ''); if (o.gps) coords.push('GPS' + escHtml(o.gps) + ''); const tags = [] .concat(o.cat ? ['' + escHtml(o.cat) + ''] : []) .concat((o.notes || []).map(t => '' + escHtml(t) + '')) .concat((o.locs || []).map(t => '' + escHtml(t) + '')); const kicker = [o.date, o.city].filter(Boolean).join(' · '); return '' + '' + '