@opnpress/opnpress-cli 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/{build.js → cli/buildSite.js} +4 -4
  2. package/dist/{init.js → cli/initProject.js} +2 -2
  3. package/dist/{cli.js → cli/main.js} +2 -2
  4. package/dist/integrations/booking-calendar.js +23 -0
  5. package/dist/integrations/company-info.js +24 -0
  6. package/dist/integrations/contact-form.js +35 -0
  7. package/dist/integrations/contact-links.js +87 -0
  8. package/dist/integrations/index.js +18 -0
  9. package/dist/integrations/maps.js +23 -0
  10. package/dist/integrations/shareable-links.js +58 -0
  11. package/dist/integrations/socials-links.js +198 -0
  12. package/dist/integrations/video.js +74 -0
  13. package/dist/{linkEmbedding.js → renderer/aioHelper.js} +1 -16
  14. package/dist/renderer/footerHtmlBuilder.js +11 -0
  15. package/dist/renderer/headerHtmlBuilder.js +20 -0
  16. package/dist/renderer/mdBodyHtmlBuilder.js +117 -0
  17. package/dist/renderer/pageGenerator.js +204 -0
  18. package/dist/renderer/pageHelpers.js +132 -0
  19. package/dist/renderer/rawBodyHtmlBuilder.js +3 -0
  20. package/dist/{jsEmbedding.js → renderer/scriptBuilder.js} +14 -5
  21. package/dist/renderer/themeBuilder.js +723 -0
  22. package/dist/rendering/contact.js +149 -0
  23. package/dist/rendering/shared.js +75 -0
  24. package/dist/shortcodes/cardrow.js +85 -0
  25. package/dist/shortcodes/contact-card.js +24 -0
  26. package/dist/shortcodes/index.js +14 -0
  27. package/dist/shortcodes/js.js +7 -0
  28. package/dist/shortcodes/mailto.js +31 -0
  29. package/dist/shortcodes/pagelist.js +82 -0
  30. package/dist/shortcodes/tel.js +24 -0
  31. package/package.json +6 -6
  32. package/dist/render.js +0 -2041
  33. /package/dist/{server.js → cli/server.js} +0 -0
@@ -0,0 +1,20 @@
1
+ import { escapeHtml } from '../utils.js';
2
+ export function buildHeaderHtml(params) {
3
+ const { siteName, homeHref, navigationHtml, discoveryHeaderNav, logoUrl } = params;
4
+ return `
5
+ <header class="site-header">
6
+ <a class="site-brand" href="${escapeHtml(homeHref)}">
7
+ ${logoUrl ? `<img class="site-logo" src="${escapeHtml(logoUrl)}" alt="" aria-hidden="true" />` : ''}
8
+ <span>${escapeHtml(siteName)}</span>
9
+ </a>
10
+ <nav class="site-nav" aria-label="Main navigation">${navigationHtml}</nav>
11
+ ${discoveryHeaderNav}
12
+ </header>
13
+ `;
14
+ }
15
+ export function renderNavList(items) {
16
+ if (!items.length) {
17
+ return '';
18
+ }
19
+ return items.map((item) => `<a class="nav-link" href="${escapeHtml(item.url)}">${escapeHtml(item.title)}</a>`).join('');
20
+ }
@@ -0,0 +1,117 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { unified } from 'unified';
4
+ import remarkParse from 'remark-parse';
5
+ import remarkGfm from 'remark-gfm';
6
+ import remarkRehype from 'remark-rehype';
7
+ import rehypeRaw from 'rehype-raw';
8
+ import rehypeSlug from 'rehype-slug';
9
+ import rehypeAutolinkHeadings from 'rehype-autolink-headings';
10
+ import rehypeStringify from 'rehype-stringify';
11
+ import YAML from 'yaml';
12
+ import { escapeHtml } from '../utils.js';
13
+ import { shortcodeHandlers } from '../shortcodes/index.js';
14
+ import { integrationHandlers } from '../integrations/index.js';
15
+ export async function renderMarkdown(markdown) {
16
+ const file = await unified()
17
+ .use(remarkParse)
18
+ .use(remarkGfm)
19
+ .use(remarkRehype, { allowDangerousHtml: true })
20
+ .use(rehypeRaw)
21
+ .use(rehypeSlug)
22
+ .use(rehypeAutolinkHeadings, { behavior: 'append' })
23
+ .use(rehypeStringify, { allowDangerousHtml: true })
24
+ .process(markdown);
25
+ return String(file);
26
+ }
27
+ const blockHandlers = new Map([
28
+ ...shortcodeHandlers,
29
+ ...integrationHandlers
30
+ ]);
31
+ function parseBlockConfig(body, handler) {
32
+ const trimmed = body.trim();
33
+ if (!trimmed) {
34
+ return {};
35
+ }
36
+ if (handler?.parseConfig) {
37
+ return handler.parseConfig(body);
38
+ }
39
+ try {
40
+ const parsed = YAML.parse(trimmed);
41
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
42
+ return parsed;
43
+ }
44
+ }
45
+ catch {
46
+ // Fall through to the empty config below.
47
+ }
48
+ return {};
49
+ }
50
+ async function renderSemanticBlock(params) {
51
+ const { blockName, body, site, title, canonicalUrl, source, allSources } = params;
52
+ const handler = blockHandlers.get(blockName);
53
+ if (!handler) {
54
+ return `
55
+ <section class="card integration integration-unknown">
56
+ <h2>${escapeHtml(blockName)}</h2>
57
+ <p class="empty-state">Unknown integration block: ${escapeHtml(blockName)}</p>
58
+ </section>
59
+ `;
60
+ }
61
+ const config = parseBlockConfig(body, handler);
62
+ return handler.render(config, {
63
+ site,
64
+ title,
65
+ canonicalUrl,
66
+ source,
67
+ allSources
68
+ });
69
+ }
70
+ async function expandSemanticBlocks(input, params) {
71
+ const blockPattern = /:::([a-z-]+)\n([\s\S]*?)\n:::/g;
72
+ let result = '';
73
+ let lastIndex = 0;
74
+ let match;
75
+ const blocks = [];
76
+ while ((match = blockPattern.exec(input))) {
77
+ result += input.slice(lastIndex, match.index);
78
+ const html = await renderSemanticBlock({
79
+ blockName: match[1],
80
+ body: match[2],
81
+ site: params.site,
82
+ title: params.title,
83
+ canonicalUrl: params.canonicalUrl,
84
+ source: params.source,
85
+ allSources: params.allSources
86
+ });
87
+ const token = `opnpress-block-${blocks.length + 1}`;
88
+ blocks.push({ token, html });
89
+ result += `\n\n<div data-opnpress-block="${token}"></div>\n\n`;
90
+ lastIndex = match.index + match[0].length;
91
+ }
92
+ result += input.slice(lastIndex);
93
+ return { source: result, blocks };
94
+ }
95
+ function injectSemanticBlocks(html, blocks) {
96
+ let result = html;
97
+ for (const block of blocks) {
98
+ const placeholder = `<div data-opnpress-block="${block.token}"></div>`;
99
+ result = result.replace(placeholder, block.html);
100
+ }
101
+ return result;
102
+ }
103
+ export async function buildMarkdownBodyHtml(params) {
104
+ const { source, site, title, canonicalUrl, allSources } = params;
105
+ const semanticResult = await expandSemanticBlocks(source.body, {
106
+ site,
107
+ title,
108
+ canonicalUrl,
109
+ source,
110
+ allSources
111
+ });
112
+ return injectSemanticBlocks(await renderMarkdown(semanticResult.source), semanticResult.blocks);
113
+ }
114
+ export async function writeRenderedPage(outputPath, html) {
115
+ await fs.mkdir(path.dirname(outputPath), { recursive: true });
116
+ await fs.writeFile(outputPath, html, 'utf8');
117
+ }
@@ -0,0 +1,204 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ import YAML from 'yaml';
4
+ import { escapeHtml, isFullHtmlDocument } from '../utils.js';
5
+ import { buildDiscoveryBodyMarkup, buildDiscoveryFooterMarkup, buildDiscoveryHeadMarkup, buildDiscoveryHeaderMarkup, buildSystemReminderMarkup } from './aioHelper.js';
6
+ import { buildCustomScriptTags } from './scriptBuilder.js';
7
+ import { buildThemeCss } from './themeBuilder.js';
8
+ import { buildHeaderHtml, renderNavList } from './headerHtmlBuilder.js';
9
+ import { buildFooterHtml } from './footerHtmlBuilder.js';
10
+ import { buildMarkdownBodyHtml } from './mdBodyHtmlBuilder.js';
11
+ import { buildRawBodyHtml } from './rawBodyHtmlBuilder.js';
12
+ import { analyticsSnippet, canonicalUrlForRoute, extractExcerpt, extractHeadings, hrefForAsset, hrefForRoute, hrefForSourceMirror, normalizeDateValue, pageClass, serializeJsonLd, socialMetaHandle, stripHtml, wordCount } from './pageHelpers.js';
13
+ import { buildPageTitle } from '../content.js';
14
+ function pageJsonToText(pageJson) {
15
+ return JSON.stringify(pageJson, null, 2) + '\n';
16
+ }
17
+ function sourceMirrorContent(source) {
18
+ if (source.kind === 'markdown') {
19
+ return {
20
+ path: 'source.md',
21
+ content: `---\n${YAML.stringify(source.frontmatter).trim()}\n---\n\n${source.body}\n`
22
+ };
23
+ }
24
+ return {
25
+ path: 'source.html',
26
+ content: source.body
27
+ };
28
+ }
29
+ export async function buildPageArtifact(params) {
30
+ const { source, site, theme, navigation, allPublishedSources, headerLogoUrl, customScriptUrls = [] } = params;
31
+ const title = buildPageTitle(source, site.site.name);
32
+ const description = source.frontmatter.description ?? site.site.description ?? '';
33
+ const canonicalUrl = source.frontmatter.canonical
34
+ ? source.frontmatter.canonical
35
+ : canonicalUrlForRoute(site.site.domain, source.routePath);
36
+ const pageUrl = canonicalUrlForRoute(site.site.domain, source.routePath);
37
+ const bodyHtml = source.kind === 'markdown'
38
+ ? await buildMarkdownBodyHtml({
39
+ source,
40
+ site,
41
+ title,
42
+ canonicalUrl,
43
+ allSources: allPublishedSources
44
+ })
45
+ : buildRawBodyHtml(source.body);
46
+ const content = `<section class="page-content">${bodyHtml}</section>`;
47
+ const navigationHtml = renderNavList(navigation.main.map((item) => ({
48
+ ...item,
49
+ url: hrefForRoute(source.routePath, item.url)
50
+ })));
51
+ const footerNavHtml = renderNavList(navigation.footer.map((item) => ({
52
+ ...item,
53
+ url: hrefForRoute(source.routePath, item.url)
54
+ })));
55
+ const customScriptTags = buildCustomScriptTags(customScriptUrls.map((scriptUrl) => hrefForAsset(source.routePath, scriptUrl)));
56
+ const isRawHtml = source.kind === 'html' && isFullHtmlDocument(source.body);
57
+ const ogImage = site.seo.defaultImage
58
+ ? canonicalUrlForRoute(site.site.domain, site.seo.defaultImage)
59
+ : undefined;
60
+ const renderedBodyText = stripHtml(bodyHtml);
61
+ const headings = extractHeadings(bodyHtml);
62
+ const excerpt = description || extractExcerpt(renderedBodyText);
63
+ const tags = source.frontmatter.tags ?? [];
64
+ const categories = source.frontmatter.categories ?? [];
65
+ const date = normalizeDateValue(source.frontmatter.date);
66
+ const updated = normalizeDateValue(source.frontmatter.updated);
67
+ const jsonLd = {
68
+ '@context': 'https://schema.org',
69
+ '@type': 'WebPage',
70
+ name: title,
71
+ description,
72
+ url: canonicalUrl
73
+ };
74
+ const analyticsId = site.integrations.analytics.id ?? site.integrations.analytics.measurementId;
75
+ const analytics = analyticsId ? analyticsSnippet(analyticsId) : '';
76
+ const poweredBy = site.branding.poweredByOpnPress
77
+ ? `<a class="nav-link" href="https://opnpress.com" rel="noopener noreferrer">Built with OpnPress</a>`
78
+ : '';
79
+ const logoUrl = headerLogoUrl ?? (site.site.logo ? hrefForAsset(source.routePath, site.site.logo) : '');
80
+ const faviconAsset = site.site.favicon ?? site.site.logo;
81
+ const faviconUrl = faviconAsset ? hrefForAsset(source.routePath, faviconAsset) : '';
82
+ const twitterSiteHandle = socialMetaHandle(site.socials.twitter ?? site.socials.x);
83
+ const pageJson = {
84
+ generator: 'OpnPress',
85
+ title,
86
+ description,
87
+ routePath: source.routePath,
88
+ url: pageUrl,
89
+ canonicalUrl,
90
+ section: source.section,
91
+ kind: source.kind,
92
+ published: true,
93
+ draft: false,
94
+ date,
95
+ updated,
96
+ image: source.frontmatter.image,
97
+ tags,
98
+ categories,
99
+ headings,
100
+ entities: [...tags, ...categories],
101
+ wordCount: wordCount(bodyHtml),
102
+ excerpt
103
+ };
104
+ const sourceMirrorUrl = source.kind === 'markdown'
105
+ ? new URL('source.md', canonicalUrl).toString()
106
+ : new URL('source.html', canonicalUrl).toString();
107
+ const sourceMirrorRelativeUrl = source.kind === 'markdown'
108
+ ? hrefForSourceMirror(source.routePath, source.routePath)
109
+ : hrefForSourceMirror(source.routePath, source.routePath, 'source.html');
110
+ const llmsRelativeUrl = hrefForAsset(source.routePath, '/llms.txt');
111
+ const fullSiteContentRelativeUrl = hrefForAsset(source.routePath, '/fullSiteContent.md');
112
+ const discoveryTargets = {
113
+ llmsRelativeUrl,
114
+ fullSiteContentRelativeUrl,
115
+ sourceMirrorRelativeUrl
116
+ };
117
+ const discoveryHead = buildDiscoveryHeadMarkup({
118
+ llmsRelativeUrl,
119
+ fullSiteContentRelativeUrl,
120
+ contentSource: source.kind === 'markdown' ? 'markdown' : 'html',
121
+ sourceMirrorRelativeUrl
122
+ });
123
+ const discoveryHeaderNav = buildDiscoveryHeaderMarkup(discoveryTargets);
124
+ const discoveryFooterNav = buildDiscoveryFooterMarkup(discoveryTargets);
125
+ const discoveryBodyMarkup = buildDiscoveryBodyMarkup(discoveryTargets);
126
+ if (isRawHtml) {
127
+ const bodyDiscoveryMarkup = `${customScriptTags}\n${discoveryBodyMarkup}\n${buildSystemReminderMarkup(fullSiteContentRelativeUrl)}`;
128
+ const wrappedHtml = source.body
129
+ .replace(/<\/head>/i, `${discoveryHead}\n</head>`)
130
+ .replace(/<\/body>/i, `${bodyDiscoveryMarkup}\n</body>`);
131
+ return {
132
+ html: isFullHtmlDocument(source.body) ? wrappedHtml : source.body,
133
+ pageJson,
134
+ sourceMirrorUrl,
135
+ sourceMirror: sourceMirrorContent(source)
136
+ };
137
+ }
138
+ return {
139
+ html: `
140
+ <!doctype html>
141
+ <html lang="${escapeHtml(site.site.language)}">
142
+ <head>
143
+ <!-- opnpress:generator=OpnPress -->
144
+ <meta charset="utf-8" />
145
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
146
+ ${discoveryHead}
147
+ ${faviconUrl ? `<link rel="icon" href="${escapeHtml(faviconUrl)}" type="image/png" />` : ''}
148
+ ${faviconUrl ? `<link rel="apple-touch-icon" href="${escapeHtml(faviconUrl)}" />` : ''}
149
+ <title>${escapeHtml(title)}</title>
150
+ ${description ? `<meta name="description" content="${escapeHtml(description)}" />` : ''}
151
+ <link rel="canonical" href="${escapeHtml(canonicalUrl)}" />
152
+ <meta property="og:type" content="article" />
153
+ <meta property="og:title" content="${escapeHtml(title)}" />
154
+ ${description ? `<meta property="og:description" content="${escapeHtml(description)}" />` : ''}
155
+ <meta property="og:url" content="${escapeHtml(canonicalUrl)}" />
156
+ ${ogImage ? `<meta property="og:image" content="${escapeHtml(ogImage)}" />` : ''}
157
+ <meta name="twitter:card" content="${ogImage ? 'summary_large_image' : 'summary'}" />
158
+ <meta name="twitter:title" content="${escapeHtml(title)}" />
159
+ ${description ? `<meta name="twitter:description" content="${escapeHtml(description)}" />` : ''}
160
+ ${twitterSiteHandle ? `<meta name="twitter:site" content="${escapeHtml(twitterSiteHandle)}" />` : ''}
161
+ ${ogImage ? `<meta name="twitter:image" content="${escapeHtml(ogImage)}" />` : ''}
162
+ <script type="application/ld+json">${serializeJsonLd(jsonLd)}</script>
163
+ ${analytics}
164
+ ${customScriptTags}
165
+ <style>${buildThemeCss(theme)}</style>
166
+ </head>
167
+ <body>
168
+ <div class="site-shell">
169
+ ${buildHeaderHtml({
170
+ siteName: site.site.name,
171
+ siteDescription: site.site.description,
172
+ homeHref: hrefForRoute(source.routePath, '/'),
173
+ navigationHtml,
174
+ discoveryHeaderNav,
175
+ logoUrl
176
+ })}
177
+ <main class="${pageClass()}">
178
+ ${content}
179
+ ${discoveryBodyMarkup}
180
+ </main>
181
+ ${buildSystemReminderMarkup(fullSiteContentRelativeUrl)}
182
+ ${buildFooterHtml({
183
+ siteDescription: site.site.description,
184
+ poweredBy,
185
+ footerHtml: footerNavHtml,
186
+ discoveryFooterNav
187
+ })}
188
+ </div>
189
+ </body>
190
+ </html>
191
+ `,
192
+ pageJson,
193
+ sourceMirrorUrl,
194
+ sourceMirror: sourceMirrorContent(source)
195
+ };
196
+ }
197
+ export async function renderPageDocument(params) {
198
+ const artifact = await buildPageArtifact(params);
199
+ return artifact.html;
200
+ }
201
+ export async function writeRenderedPage(outputPath, html) {
202
+ await fs.mkdir(path.dirname(outputPath), { recursive: true });
203
+ await fs.writeFile(outputPath, html, 'utf8');
204
+ }
@@ -0,0 +1,132 @@
1
+ import path from 'node:path';
2
+ import { escapeHtml } from '../utils.js';
3
+ function isExternalUrl(url) {
4
+ return /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(url) || url.startsWith('//');
5
+ }
6
+ function normalizeRoutePath(routePath) {
7
+ if (routePath === '/') {
8
+ return '/';
9
+ }
10
+ return routePath.endsWith('/') ? routePath : `${routePath}/`;
11
+ }
12
+ export function hrefForRoute(currentRoute, targetRoute) {
13
+ if (isExternalUrl(targetRoute)) {
14
+ return targetRoute;
15
+ }
16
+ const current = normalizeRoutePath(currentRoute);
17
+ const target = normalizeRoutePath(targetRoute);
18
+ const fromDir = current === '/' ? '' : current.replace(/^\//, '');
19
+ const toDir = target === '/' ? '' : target.replace(/^\//, '');
20
+ const relative = path.posix.relative(fromDir, toDir);
21
+ if (!relative) {
22
+ return './';
23
+ }
24
+ return `${relative}/`;
25
+ }
26
+ export function hrefForAsset(currentRoute, assetPath) {
27
+ if (isExternalUrl(assetPath)) {
28
+ return assetPath;
29
+ }
30
+ const current = normalizeRoutePath(currentRoute);
31
+ const fromDir = current === '/' ? '' : current.replace(/^\//, '');
32
+ const toPath = assetPath.startsWith('/') ? assetPath.replace(/^\//, '') : assetPath;
33
+ const relative = path.posix.relative(fromDir, toPath);
34
+ return relative || path.posix.basename(toPath);
35
+ }
36
+ export function hrefForSourceMirror(currentRoute, targetRoute, fileName = 'source.md') {
37
+ const current = normalizeRoutePath(currentRoute);
38
+ const target = normalizeRoutePath(targetRoute);
39
+ const fromDir = current === '/' ? '' : current.replace(/^\//, '');
40
+ const toDir = target === '/' ? '' : target.replace(/^\//, '');
41
+ const relative = path.posix.relative(fromDir, path.posix.join(toDir, fileName));
42
+ return relative || fileName;
43
+ }
44
+ export function getSiteBaseUrl(domain) {
45
+ if (/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(domain)) {
46
+ return domain.endsWith('/') ? domain : `${domain}/`;
47
+ }
48
+ return `https://${domain.replace(/\/+$/, '')}/`;
49
+ }
50
+ export function canonicalUrlForRoute(domain, routePath) {
51
+ const baseUrl = getSiteBaseUrl(domain);
52
+ return new URL(routePath === '/' ? '/' : routePath, baseUrl).toString();
53
+ }
54
+ export function serializeJsonLd(value) {
55
+ return JSON.stringify(value).replace(/</g, '\\u003c');
56
+ }
57
+ export function normalizeDateValue(value) {
58
+ if (value instanceof Date) {
59
+ return value.toISOString();
60
+ }
61
+ if (typeof value === 'string' && value.trim()) {
62
+ return value;
63
+ }
64
+ return undefined;
65
+ }
66
+ export function stripHtml(input) {
67
+ return input
68
+ .replace(/<script[\s\S]*?<\/script>/gi, ' ')
69
+ .replace(/<style[\s\S]*?<\/style>/gi, ' ')
70
+ .replace(/<[^>]+>/g, ' ')
71
+ .replace(/\s+/g, ' ')
72
+ .trim();
73
+ }
74
+ export function extractHeadings(html) {
75
+ const headings = [];
76
+ const pattern = /<h([1-6])[^>]*>([\s\S]*?)<\/h\1>/gi;
77
+ let match;
78
+ while ((match = pattern.exec(html))) {
79
+ headings.push(stripHtml(match[2]));
80
+ }
81
+ return headings;
82
+ }
83
+ export function extractExcerpt(text, maxLength = 180) {
84
+ const stripped = stripHtml(text);
85
+ if (stripped.length <= maxLength) {
86
+ return stripped;
87
+ }
88
+ return `${stripped.slice(0, maxLength).trimEnd()}…`;
89
+ }
90
+ export function wordCount(text) {
91
+ const stripped = stripHtml(text);
92
+ if (!stripped) {
93
+ return 0;
94
+ }
95
+ return stripped.split(/\s+/).filter(Boolean).length;
96
+ }
97
+ export function pageClass() {
98
+ return 'page page-default';
99
+ }
100
+ export function analyticsSnippet(analyticsId) {
101
+ const escapedId = escapeHtml(analyticsId);
102
+ return `
103
+ <script async src="https://www.googletagmanager.com/gtag/js?id=${escapedId}"></script>
104
+ <script>
105
+ window.dataLayer = window.dataLayer || [];
106
+ function gtag(){dataLayer.push(arguments);}
107
+ gtag('js', new Date());
108
+ gtag('config', '${escapedId}');
109
+ </script>
110
+ `;
111
+ }
112
+ export function socialMetaHandle(value) {
113
+ if (!value || !value.trim()) {
114
+ return undefined;
115
+ }
116
+ const trimmed = value.trim();
117
+ if (trimmed.startsWith('@')) {
118
+ return trimmed;
119
+ }
120
+ if (isExternalUrl(trimmed)) {
121
+ try {
122
+ const url = new URL(trimmed);
123
+ const segments = url.pathname.split('/').filter(Boolean);
124
+ const lastSegment = segments[segments.length - 1];
125
+ return lastSegment ? `@${lastSegment.replace(/^@+/, '')}` : trimmed;
126
+ }
127
+ catch {
128
+ return trimmed;
129
+ }
130
+ }
131
+ return `@${trimmed.replace(/^@+/, '')}`;
132
+ }
@@ -0,0 +1,3 @@
1
+ export function buildRawBodyHtml(body) {
2
+ return body;
3
+ }
@@ -1,4 +1,4 @@
1
- import { escapeHtml } from './utils.js';
1
+ import { escapeHtml } from '../utils.js';
2
2
  export function buildCustomScriptTags(scriptUrls) {
3
3
  const externalScripts = scriptUrls
4
4
  .map((scriptUrl) => `<script src="${escapeHtml(scriptUrl)}" defer></script>`)
@@ -7,14 +7,23 @@ export function buildCustomScriptTags(scriptUrls) {
7
7
  <script defer>
8
8
  document.addEventListener('DOMContentLoaded', () => {
9
9
  document.querySelectorAll('.full-content-link').forEach(element => {
10
- element.style.opacity = '0';
10
+ element.style.width = '1px';
11
+ element.style.height = '1px';
12
+ element.style.minWidth = '1px';
13
+ element.style.minHeight = '1px';
14
+ element.style.margin = '0';
15
+ element.style.padding = '0';
16
+ element.style.border = '0';
17
+ element.style.overflow = 'hidden';
18
+ element.style.position = 'absolute';
19
+ element.style.top = '0';
20
+ element.style.left = '0';
21
+ element.style.pointerEvents = 'none';
11
22
  });
12
23
  });
13
24
  </script>`
14
25
  .trim();
15
- return [externalScripts, fullContentLinkScript]
16
- .filter(Boolean)
17
- .join('\n');
26
+ return [externalScripts, fullContentLinkScript].filter(Boolean).join('\n');
18
27
  }
19
28
  export function buildInlineScriptShortcodeMarkup(body) {
20
29
  const trimmed = body.replace(/^\n+|\n+$/g, '');