@markii/host 0.0.0-stage → 0.17.0-next.2
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.
- package/LICENSE +21 -0
- package/README.md +19 -2
- package/dist/browser-worker.js +639 -0
- package/dist/browser.d.ts +57 -0
- package/dist/browser.js +167 -0
- package/dist/conformance/host/01-plain-open/expected.json +5 -0
- package/dist/conformance/host/01-plain-open/note.mk.md +8 -0
- package/dist/conformance/host/01-plain-open/scenario.json +6 -0
- package/dist/conformance/host/02-run-manual-grant-accepted/expected.json +16 -0
- package/dist/conformance/host/02-run-manual-grant-accepted/note.mk.md +5 -0
- package/dist/conformance/host/02-run-manual-grant-accepted/scenario.json +14 -0
- package/dist/conformance/host/03-run-manual-grant-refused/expected.json +18 -0
- package/dist/conformance/host/03-run-manual-grant-refused/note.mk.md +5 -0
- package/dist/conformance/host/03-run-manual-grant-refused/scenario.json +14 -0
- package/dist/conformance/host/04-run-auto-write-denied/expected.json +16 -0
- package/dist/conformance/host/04-run-auto-write-denied/note.mk.md +10 -0
- package/dist/conformance/host/04-run-auto-write-denied/scenario.json +13 -0
- package/dist/conformance/host/05-export-html-pack-directive/expected.json +14 -0
- package/dist/conformance/host/05-export-html-pack-directive/note.mk.md +8 -0
- package/dist/conformance/host/05-export-html-pack-directive/scenario.json +6 -0
- package/dist/conformance/host/06-install-pack-then-render/expected.json +18 -0
- package/dist/conformance/host/06-install-pack-then-render/files/ana-1.0.0/pack.json +13 -0
- package/dist/conformance/host/06-install-pack-then-render/files/ana-1.0.0/webview.js +1 -0
- package/dist/conformance/host/06-install-pack-then-render/note.mk.md +5 -0
- package/dist/conformance/host/06-install-pack-then-render/scenario.json +15 -0
- package/dist/conformance/host/07-complete-in-directive/expected.json +14 -0
- package/dist/conformance/host/07-complete-in-directive/note.mk.md +3 -0
- package/dist/conformance/host/07-complete-in-directive/scenario.json +6 -0
- package/dist/conformance/host/08-hover-standard-component/expected.json +5 -0
- package/dist/conformance/host/08-hover-standard-component/note.mk.md +5 -0
- package/dist/conformance/host/08-hover-standard-component/scenario.json +6 -0
- package/dist/conformance/host/09-insert-skeleton/expected.json +5 -0
- package/dist/conformance/host/09-insert-skeleton/note.mk.md +4 -0
- package/dist/conformance/host/09-insert-skeleton/scenario.json +6 -0
- package/dist/conformance/host/10-scripts-disabled/expected.json +11 -0
- package/dist/conformance/host/10-scripts-disabled/note.mk.md +5 -0
- package/dist/conformance/host/10-scripts-disabled/scenario.json +9 -0
- package/dist/conformance/host/11-complete-closing-fence/expected.json +5 -0
- package/dist/conformance/host/11-complete-closing-fence/note.mk.md +5 -0
- package/dist/conformance/host/11-complete-closing-fence/scenario.json +12 -0
- package/dist/conformance/host/12-complete-nested-container/expected.json +5 -0
- package/dist/conformance/host/12-complete-nested-container/note.mk.md +5 -0
- package/dist/conformance/host/12-complete-nested-container/scenario.json +12 -0
- package/dist/conformance/host/13-install-pack-declined/expected.json +12 -0
- package/dist/conformance/host/13-install-pack-declined/files/ana-1.0.0/pack.json +13 -0
- package/dist/conformance/host/13-install-pack-declined/files/ana-1.0.0/webview.js +1 -0
- package/dist/conformance/host/13-install-pack-declined/note.mk.md +5 -0
- package/dist/conformance/host/13-install-pack-declined/scenario.json +10 -0
- package/dist/conformance/host/14-remove-pack/expected.json +14 -0
- package/dist/conformance/host/14-remove-pack/files/ana-1.0.0/pack.json +13 -0
- package/dist/conformance/host/14-remove-pack/files/ana-1.0.0/webview.js +1 -0
- package/dist/conformance/host/14-remove-pack/note.mk.md +5 -0
- package/dist/conformance/host/14-remove-pack/scenario.json +11 -0
- package/dist/conformance/host/15-load-packs-engine-views/expected.json +19 -0
- package/dist/conformance/host/15-load-packs-engine-views/files/duo-1.0.0/pack.json +9 -0
- package/dist/conformance/host/15-load-packs-engine-views/files/duo-1.0.0/webview.js +1 -0
- package/dist/conformance/host/15-load-packs-engine-views/note.mk.md +9 -0
- package/dist/conformance/host/15-load-packs-engine-views/scenario.json +10 -0
- package/dist/conformance/host/16-pack-trust/expected.json +37 -0
- package/dist/conformance/host/16-pack-trust/files/ana-1.0.0/pack.json +12 -0
- package/dist/conformance/host/16-pack-trust/files/ana-1.0.0/webview.js +1 -0
- package/dist/conformance/host/16-pack-trust/files/sneaky-1.0.0/pack.json +12 -0
- package/dist/conformance/host/16-pack-trust/files/sneaky-1.0.0/webview.js +1 -0
- package/dist/conformance/host/16-pack-trust/note.mk.md +5 -0
- package/dist/conformance/host/16-pack-trust/scenario.json +26 -0
- package/dist/conformance/host/README.md +221 -0
- package/dist/conformance.d.ts +2 -0
- package/dist/conformance.js +5 -0
- package/dist/diagnostics/render-diagnostics.d.ts +41 -0
- package/dist/diagnostics/render-diagnostics.js +46 -0
- package/dist/export/cascade.d.ts +98 -0
- package/dist/export/cascade.js +159 -0
- package/dist/export/export-zip.d.ts +19 -0
- package/dist/export/export-zip.js +38 -0
- package/dist/export/image-embed.d.ts +126 -0
- package/dist/export/image-embed.js +274 -0
- package/dist/export/note-export.d.ts +332 -0
- package/dist/export/note-export.js +366 -0
- package/dist/export/note-links.d.ts +83 -0
- package/dist/export/note-links.js +241 -0
- package/dist/glue.wasm +0 -0
- package/dist/host/adapter.d.ts +223 -0
- package/dist/host/adapter.js +9 -0
- package/dist/host/cascade-export-behavior.d.ts +120 -0
- package/dist/host/cascade-export-behavior.js +151 -0
- package/dist/host/conformance-runner.d.ts +156 -0
- package/dist/host/conformance-runner.js +634 -0
- package/dist/host/create-host.d.ts +253 -0
- package/dist/host/create-host.js +329 -0
- package/dist/host/editor-behavior.d.ts +157 -0
- package/dist/host/editor-behavior.js +227 -0
- package/dist/host/export/md-plain.d.ts +15 -0
- package/dist/host/export/md-plain.js +125 -0
- package/dist/host/export-behavior.d.ts +113 -0
- package/dist/host/export-behavior.js +257 -0
- package/dist/host/labels.d.ts +24 -0
- package/dist/host/labels.js +32 -0
- package/dist/host/pack-archive.d.ts +30 -0
- package/dist/host/pack-archive.js +68 -0
- package/dist/host/pack-install.d.ts +88 -0
- package/dist/host/pack-install.js +189 -0
- package/dist/host/pack-load.d.ts +72 -0
- package/dist/host/pack-load.js +57 -0
- package/dist/host/pack-remove.d.ts +21 -0
- package/dist/host/pack-remove.js +65 -0
- package/dist/host/pack-trust-behavior.d.ts +79 -0
- package/dist/host/pack-trust-behavior.js +262 -0
- package/dist/host/refresh-interval.d.ts +39 -0
- package/dist/host/refresh-interval.js +61 -0
- package/dist/host/run-behavior.d.ts +89 -0
- package/dist/host/run-behavior.js +168 -0
- package/dist/host/run-diagnostics.d.ts +26 -0
- package/dist/host/run-diagnostics.js +43 -0
- package/dist/host/script-execution.d.ts +72 -0
- package/dist/host/script-execution.js +68 -0
- package/dist/host/terminal-packs.d.ts +39 -0
- package/dist/host/terminal-packs.js +51 -0
- package/dist/html-engine.d.ts +2 -0
- package/dist/html-engine.js +29 -0
- package/dist/index.d.ts +95 -0
- package/dist/index.js +70 -0
- package/dist/insert/component-catalog.d.ts +23 -0
- package/dist/insert/component-catalog.js +96 -0
- package/dist/node-fs.d.ts +14 -0
- package/dist/node-fs.js +70 -0
- package/dist/node.d.ts +5 -0
- package/dist/node.js +13 -0
- package/dist/package.json +3 -0
- package/dist/packs/discover.d.ts +119 -0
- package/dist/packs/discover.js +266 -0
- package/dist/packs/pack-diagnostics.d.ts +81 -0
- package/dist/packs/pack-diagnostics.js +105 -0
- package/dist/packs/pack-paths.d.ts +53 -0
- package/dist/packs/pack-paths.js +106 -0
- package/dist/packs/pack-render-registry.d.ts +83 -0
- package/dist/packs/pack-render-registry.js +182 -0
- package/dist/packs/pack-scripts.d.ts +20 -0
- package/dist/packs/pack-scripts.js +96 -0
- package/dist/packs/pack-trust.d.ts +57 -0
- package/dist/packs/pack-trust.js +111 -0
- package/dist/packs/prebuilt.d.ts +49 -0
- package/dist/packs/prebuilt.js +97 -0
- package/dist/path-util.d.ts +24 -0
- package/dist/path-util.js +82 -0
- package/dist/run/browser-isolate.d.ts +45 -0
- package/dist/run/browser-isolate.js +81 -0
- package/dist/run/bundle-document.d.ts +29 -0
- package/dist/run/bundle-document.js +40 -0
- package/dist/run/bundle-run.d.ts +188 -0
- package/dist/run/bundle-run.js +232 -0
- package/dist/run/grant-flow.d.ts +232 -0
- package/dist/run/grant-flow.js +390 -0
- package/dist/run/ip-address.d.ts +41 -0
- package/dist/run/ip-address.js +255 -0
- package/dist/run/isolate.d.ts +57 -0
- package/dist/run/isolate.js +22 -0
- package/dist/run/lua-resolver.d.ts +21 -0
- package/dist/run/lua-resolver.js +63 -0
- package/dist/run/net-bridge-worker.d.ts +26 -0
- package/dist/run/net-bridge-worker.js +66 -0
- package/dist/run/net-bridge.d.ts +56 -0
- package/dist/run/net-bridge.js +59 -0
- package/dist/run/net-pinning.d.ts +82 -0
- package/dist/run/net-pinning.js +137 -0
- package/dist/run/net-provider.d.ts +70 -0
- package/dist/run/net-provider.js +289 -0
- package/dist/run/node-isolate.d.ts +30 -0
- package/dist/run/node-isolate.js +117 -0
- package/dist/run/run-flow.d.ts +187 -0
- package/dist/run/run-flow.js +323 -0
- package/dist/run/run-host.d.ts +107 -0
- package/dist/run/run-host.js +241 -0
- package/dist/run/run-job.d.ts +130 -0
- package/dist/run/run-job.js +279 -0
- package/dist/run/run-progress.d.ts +55 -0
- package/dist/run/run-progress.js +42 -0
- package/dist/run/run-trace.d.ts +44 -0
- package/dist/run/run-trace.js +36 -0
- package/dist/run/script-requirements.d.ts +98 -0
- package/dist/run/script-requirements.js +212 -0
- package/dist/run/snapshot-storage.d.ts +25 -0
- package/dist/run/snapshot-storage.js +31 -0
- package/dist/run/stale-values.d.ts +25 -0
- package/dist/run/stale-values.js +32 -0
- package/dist/run/worker-entry-browser.d.ts +42 -0
- package/dist/run/worker-entry-browser.js +77 -0
- package/dist/run/worker-entry.d.ts +1 -0
- package/dist/run/worker-entry.js +96 -0
- package/dist/values-failure.d.ts +17 -0
- package/dist/values-failure.js +1 -0
- package/dist/values-merge.d.ts +39 -0
- package/dist/values-merge.js +33 -0
- package/package.json +84 -4
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedding a note's local images into its exported HTML (GitHub issue #28
|
|
3
|
+
* slice 3, part 1), so an exported file is genuinely self-contained: one
|
|
4
|
+
* document a user can mail, archive, or open on a machine that has never
|
|
5
|
+
* seen the vault, with its pictures still in it.
|
|
6
|
+
*
|
|
7
|
+
* WHERE THIS SITS. `./note-export.ts` composes an already-rendered body
|
|
8
|
+
* string into the export page. This module rewrites that body BEFORE the
|
|
9
|
+
* composition, turning every embeddable `<img src>` into a `data:` URI. It
|
|
10
|
+
* works on the rendered string rather than on the note's AST on purpose:
|
|
11
|
+
* the body is whatever engine rendered it, React or `@markii/html`, and a
|
|
12
|
+
* pack component's own images have to be embedded too. A string rewrite is
|
|
13
|
+
* the one place both engines and every component meet.
|
|
14
|
+
*
|
|
15
|
+
* WHAT STAYS A URL. Anything with a scheme, so `http:` and `https:` sources
|
|
16
|
+
* stay live URLs exactly as authored, and a `data:` URI is already
|
|
17
|
+
* embedded. A protocol-relative `//host/x.png` is a remote source too. Only
|
|
18
|
+
* a genuinely local, relative path is a candidate.
|
|
19
|
+
*
|
|
20
|
+
* THE HOST SEAM. This package knows nothing about vaults, workspace
|
|
21
|
+
* folders, or filesystems, so reading the bytes is injected: a host hands
|
|
22
|
+
* in an `ExportImageReader` that resolves a source the way its own preview
|
|
23
|
+
* resolves one and reads it, or reports that it could not. That keeps
|
|
24
|
+
* `@markii/host` free of host APIs while the size cap, the MIME rules, the
|
|
25
|
+
* base64 encoding, and the reporting stay in ONE place, shared by every
|
|
26
|
+
* host.
|
|
27
|
+
*
|
|
28
|
+
* NOTHING HERE EVER FAILS AN EXPORT. An image that is too large, of an
|
|
29
|
+
* unknown type, missing, or unreadable keeps the source the author wrote
|
|
30
|
+
* and is recorded in the returned report. The host turns that report into
|
|
31
|
+
* diagnostics lines: per AGENTS.md, a skipped image is a quiet fact on the
|
|
32
|
+
* diagnostics surface, never a notice and never a failed export.
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* The per-image ceiling for embedding, 2 MiB. An image above it keeps its
|
|
36
|
+
* original path rather than inflating the exported file by a third of its
|
|
37
|
+
* own size in base64. Exported so a host with a cheap size check can skip
|
|
38
|
+
* reading such a file at all and report `oversize` straight away.
|
|
39
|
+
*/
|
|
40
|
+
export const MAX_EMBEDDED_IMAGE_BYTES = 2 * 1024 * 1024;
|
|
41
|
+
/**
|
|
42
|
+
* The extensions this embeds, and the MIME type each becomes. Hand-rolled
|
|
43
|
+
* and deliberately small, in the same spirit as this repo's other
|
|
44
|
+
* hand-rolled validation: an extension that is not on this list is not
|
|
45
|
+
* embedded at all, rather than guessed at with a generic type that a
|
|
46
|
+
* browser would then have to sniff.
|
|
47
|
+
*/
|
|
48
|
+
const IMAGE_MIME_TYPES = {
|
|
49
|
+
png: 'image/png',
|
|
50
|
+
jpg: 'image/jpeg',
|
|
51
|
+
jpeg: 'image/jpeg',
|
|
52
|
+
gif: 'image/gif',
|
|
53
|
+
webp: 'image/webp',
|
|
54
|
+
avif: 'image/avif',
|
|
55
|
+
bmp: 'image/bmp',
|
|
56
|
+
ico: 'image/x-icon',
|
|
57
|
+
svg: 'image/svg+xml',
|
|
58
|
+
};
|
|
59
|
+
/** Every extension `mimeTypeForImageSrc` recognizes, lowercase and without the dot. Exported for a host that wants to name them in its own documentation or tests. */
|
|
60
|
+
export const EMBEDDABLE_IMAGE_EXTENSIONS = Object.keys(IMAGE_MIME_TYPES);
|
|
61
|
+
/**
|
|
62
|
+
* The MIME type for one image source, read from its extension, or
|
|
63
|
+
* `undefined` when the extension is missing or unrecognized. Any `?query`
|
|
64
|
+
* or `#fragment` is ignored first, so `logo.png?v=2` still reads as a PNG.
|
|
65
|
+
*/
|
|
66
|
+
export function mimeTypeForImageSrc(src) {
|
|
67
|
+
const withoutQuery = src.split(/[?#]/, 1)[0] ?? '';
|
|
68
|
+
const lastDot = withoutQuery.lastIndexOf('.');
|
|
69
|
+
if (lastDot === -1)
|
|
70
|
+
return undefined;
|
|
71
|
+
const extension = withoutQuery.slice(lastDot + 1).toLowerCase();
|
|
72
|
+
if (extension.length === 0)
|
|
73
|
+
return undefined;
|
|
74
|
+
return Object.prototype.hasOwnProperty.call(IMAGE_MIME_TYPES, extension)
|
|
75
|
+
? IMAGE_MIME_TYPES[extension]
|
|
76
|
+
: undefined;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* True when `value` begins with a URL scheme, using the same rule as
|
|
80
|
+
* `@markii/core`'s `isSafeUrl` and `apps/vscode/src/webview/
|
|
81
|
+
* document-images.ts`: text before the first `:`, but only when that `:`
|
|
82
|
+
* comes before any `/`, `?` or `#`. So `notes/a:b.png` is correctly a
|
|
83
|
+
* relative path, while `https://x/y.png` and `data:image/png;base64,...`
|
|
84
|
+
* are not.
|
|
85
|
+
*/
|
|
86
|
+
function hasScheme(value) {
|
|
87
|
+
const colon = value.indexOf(':');
|
|
88
|
+
if (colon === -1)
|
|
89
|
+
return false;
|
|
90
|
+
const slash = value.indexOf('/');
|
|
91
|
+
const questionMark = value.indexOf('?');
|
|
92
|
+
const numberSign = value.indexOf('#');
|
|
93
|
+
return ((slash === -1 || colon < slash) &&
|
|
94
|
+
(questionMark === -1 || colon < questionMark) &&
|
|
95
|
+
(numberSign === -1 || colon < numberSign));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* True for a source this will try to embed: a non-empty, local, relative
|
|
99
|
+
* path. A scheme of any kind, a protocol-relative `//host/...`, and an
|
|
100
|
+
* empty or fragment-only value are all left exactly as authored.
|
|
101
|
+
*/
|
|
102
|
+
export function isEmbeddableImageSrc(src) {
|
|
103
|
+
if (src.length === 0)
|
|
104
|
+
return false;
|
|
105
|
+
if (src.startsWith('#'))
|
|
106
|
+
return false;
|
|
107
|
+
if (src.startsWith('//'))
|
|
108
|
+
return false;
|
|
109
|
+
return !hasScheme(src);
|
|
110
|
+
}
|
|
111
|
+
/** An empty report, for an export that embedded nothing because no host reader was offered. */
|
|
112
|
+
export const EMPTY_IMAGE_REPORT = {
|
|
113
|
+
embedded: [],
|
|
114
|
+
embeddedBytes: 0,
|
|
115
|
+
skipped: [],
|
|
116
|
+
remote: 0,
|
|
117
|
+
};
|
|
118
|
+
const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
119
|
+
/**
|
|
120
|
+
* Base64 for a byte array, hand-rolled so this module needs neither
|
|
121
|
+
* `Buffer` (Node only) nor `btoa` (which cannot take bytes without a
|
|
122
|
+
* lossy string round trip) and stays environment-free, matching how this
|
|
123
|
+
* repo hand-rolls its manifest validation and `uses:` reader rather than
|
|
124
|
+
* taking a dependency for something this small.
|
|
125
|
+
*/
|
|
126
|
+
export function encodeBase64(bytes) {
|
|
127
|
+
let result = '';
|
|
128
|
+
const full = bytes.length - (bytes.length % 3);
|
|
129
|
+
for (let index = 0; index < full; index += 3) {
|
|
130
|
+
const chunk = ((bytes[index] ?? 0) << 16) |
|
|
131
|
+
((bytes[index + 1] ?? 0) << 8) |
|
|
132
|
+
(bytes[index + 2] ?? 0);
|
|
133
|
+
result +=
|
|
134
|
+
BASE64_ALPHABET[(chunk >> 18) & 63] +
|
|
135
|
+
BASE64_ALPHABET[(chunk >> 12) & 63] +
|
|
136
|
+
BASE64_ALPHABET[(chunk >> 6) & 63] +
|
|
137
|
+
BASE64_ALPHABET[chunk & 63];
|
|
138
|
+
}
|
|
139
|
+
const remaining = bytes.length - full;
|
|
140
|
+
if (remaining === 1) {
|
|
141
|
+
const chunk = (bytes[full] ?? 0) << 16;
|
|
142
|
+
result +=
|
|
143
|
+
BASE64_ALPHABET[(chunk >> 18) & 63] +
|
|
144
|
+
BASE64_ALPHABET[(chunk >> 12) & 63] +
|
|
145
|
+
'==';
|
|
146
|
+
}
|
|
147
|
+
else if (remaining === 2) {
|
|
148
|
+
const chunk = ((bytes[full] ?? 0) << 16) | ((bytes[full + 1] ?? 0) << 8);
|
|
149
|
+
result +=
|
|
150
|
+
BASE64_ALPHABET[(chunk >> 18) & 63] +
|
|
151
|
+
BASE64_ALPHABET[(chunk >> 12) & 63] +
|
|
152
|
+
BASE64_ALPHABET[(chunk >> 6) & 63] +
|
|
153
|
+
'=';
|
|
154
|
+
}
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
/** One image's bytes as a `data:` URI. Base64 output is alphanumeric plus `+/=`, so it is always safe inside a double-quoted HTML attribute with no further escaping. */
|
|
158
|
+
export function toImageDataUri(bytes, mimeType) {
|
|
159
|
+
return `data:${mimeType};base64,${encodeBase64(bytes)}`;
|
|
160
|
+
}
|
|
161
|
+
/** The five entities a renderer escapes in an attribute value, decoded back to the path the author actually wrote. `&` is decoded last so `&lt;` does not become `<`. */
|
|
162
|
+
function decodeAttributeValue(value) {
|
|
163
|
+
return value
|
|
164
|
+
.replace(/</g, '<')
|
|
165
|
+
.replace(/>/g, '>')
|
|
166
|
+
.replace(/"/g, '"')
|
|
167
|
+
.replace(/&#(?:39|x27);/g, "'")
|
|
168
|
+
.replace(/&/g, '&');
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Matches one `<img ...>` start tag. Both renderers emit attribute values
|
|
172
|
+
* in double quotes, and every `<` inside rendered text or code is escaped
|
|
173
|
+
* to `<` before it reaches here, so a `>`-terminated scan cannot run
|
|
174
|
+
* past a tag or match a literal `<img` a note merely talks about.
|
|
175
|
+
*/
|
|
176
|
+
const IMG_TAG_PATTERN = /<img\b[^>]*>/gi;
|
|
177
|
+
/** Matches the `src="..."` attribute inside one already-matched `<img>` tag. */
|
|
178
|
+
const SRC_ATTRIBUTE_PATTERN = /(\ssrc=")([^"]*)(")/i;
|
|
179
|
+
/**
|
|
180
|
+
* Rewrites every embeddable local `<img src>` in `html` into a `data:` URI,
|
|
181
|
+
* using `readImage` to fetch each source's bytes, and reports what happened
|
|
182
|
+
* to every image it saw.
|
|
183
|
+
*
|
|
184
|
+
* Each distinct source is read at most once, so a note that shows the same
|
|
185
|
+
* picture five times reads and encodes it once and embeds the same URI five
|
|
186
|
+
* times. Never throws: a reader that throws is recorded as `unreadable` and
|
|
187
|
+
* that one image keeps its original source.
|
|
188
|
+
*/
|
|
189
|
+
export async function embedImagesInHtml(html, readImage) {
|
|
190
|
+
const embedded = [];
|
|
191
|
+
const skipped = [];
|
|
192
|
+
let embeddedBytes = 0;
|
|
193
|
+
let remote = 0;
|
|
194
|
+
// Resolved once per distinct source: `undefined` means "keep the original".
|
|
195
|
+
const dataUris = new Map();
|
|
196
|
+
const tags = html.match(IMG_TAG_PATTERN) ?? [];
|
|
197
|
+
for (const tag of tags) {
|
|
198
|
+
const attribute = SRC_ATTRIBUTE_PATTERN.exec(tag);
|
|
199
|
+
if (!attribute)
|
|
200
|
+
continue;
|
|
201
|
+
const rawSrc = attribute[2] ?? '';
|
|
202
|
+
const src = decodeAttributeValue(rawSrc);
|
|
203
|
+
if (!isEmbeddableImageSrc(src)) {
|
|
204
|
+
if (src.length > 0 && !src.startsWith('#'))
|
|
205
|
+
remote += 1;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (dataUris.has(src))
|
|
209
|
+
continue;
|
|
210
|
+
const mimeType = mimeTypeForImageSrc(src);
|
|
211
|
+
if (mimeType === undefined) {
|
|
212
|
+
dataUris.set(src, undefined);
|
|
213
|
+
skipped.push({ src, reason: 'unsupported-type' });
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
let result;
|
|
217
|
+
try {
|
|
218
|
+
result = await readImage(src);
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
result = {
|
|
222
|
+
kind: 'unreadable',
|
|
223
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
if (result.kind === 'oversize') {
|
|
227
|
+
dataUris.set(src, undefined);
|
|
228
|
+
skipped.push({
|
|
229
|
+
src,
|
|
230
|
+
reason: 'too-large',
|
|
231
|
+
byteLength: result.byteLength,
|
|
232
|
+
});
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (result.kind === 'unreadable') {
|
|
236
|
+
dataUris.set(src, undefined);
|
|
237
|
+
skipped.push({
|
|
238
|
+
src,
|
|
239
|
+
reason: 'unreadable',
|
|
240
|
+
...(result.detail !== undefined ? { detail: result.detail } : {}),
|
|
241
|
+
});
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
// Re-checked here even though a host may have checked already: the cap
|
|
245
|
+
// is this module's rule, and a host that cannot cheaply stat a file
|
|
246
|
+
// legitimately reads first and lets this decide.
|
|
247
|
+
if (result.bytes.byteLength > MAX_EMBEDDED_IMAGE_BYTES) {
|
|
248
|
+
dataUris.set(src, undefined);
|
|
249
|
+
skipped.push({
|
|
250
|
+
src,
|
|
251
|
+
reason: 'too-large',
|
|
252
|
+
byteLength: result.bytes.byteLength,
|
|
253
|
+
});
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
dataUris.set(src, toImageDataUri(result.bytes, mimeType));
|
|
257
|
+
embedded.push(src);
|
|
258
|
+
embeddedBytes += result.bytes.byteLength;
|
|
259
|
+
}
|
|
260
|
+
const rewritten = html.replace(IMG_TAG_PATTERN, (tag) => {
|
|
261
|
+
const attribute = SRC_ATTRIBUTE_PATTERN.exec(tag);
|
|
262
|
+
if (!attribute)
|
|
263
|
+
return tag;
|
|
264
|
+
const src = decodeAttributeValue(attribute[2] ?? '');
|
|
265
|
+
const dataUri = dataUris.get(src);
|
|
266
|
+
if (dataUri === undefined)
|
|
267
|
+
return tag;
|
|
268
|
+
return tag.replace(SRC_ATTRIBUTE_PATTERN, (_match, before, _value, after) => `${before}${dataUri}${after}`);
|
|
269
|
+
});
|
|
270
|
+
return {
|
|
271
|
+
html: rewritten,
|
|
272
|
+
report: { embedded, embeddedBytes, skipped, remote },
|
|
273
|
+
};
|
|
274
|
+
}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import type { ValueStore } from '@markii/runtime';
|
|
2
|
+
import type { EmbeddedImageReport, ExportImageReader } from './image-embed.js';
|
|
3
|
+
import type { StoredValue } from '@markii/runtime';
|
|
4
|
+
import type { OnDiagnostic, UnavailableComponent } from '@markii/stdlib';
|
|
5
|
+
/** The canonical Markii file extension, matching `apps/vscode/src/mark-document.ts`'s `MARK_EXTENSION`. */
|
|
6
|
+
export declare const MARK_EXTENSION = ".mk.md";
|
|
7
|
+
/** The extensions an export can be written as. */
|
|
8
|
+
export type ExportExtension = '.html' | '.pdf' | '.txt' | '.md';
|
|
9
|
+
/**
|
|
10
|
+
* The name used when a note has no usable base name at all — a file called
|
|
11
|
+
* exactly `.mk.md`, or an empty path segment. Rare enough that neither host
|
|
12
|
+
* offers it as a normal path, but a file name is not optional here, so
|
|
13
|
+
* there has to be one rather than an empty string.
|
|
14
|
+
*/
|
|
15
|
+
export declare const FALLBACK_EXPORT_BASE_NAME = "markii-note";
|
|
16
|
+
/**
|
|
17
|
+
* The base name an export is built from: the file name with a recognized
|
|
18
|
+
* Markii/markdown extension removed, case-insensitively. `notes.mk.md` and
|
|
19
|
+
* `notes.md` both give `notes`; a name with any other extension keeps it
|
|
20
|
+
* (`notes.txt` gives `notes.txt`), because guessing at an unknown extension
|
|
21
|
+
* would be more surprising than leaving it alone. A `/`-separated path is
|
|
22
|
+
* accepted too: only its last segment is read.
|
|
23
|
+
*/
|
|
24
|
+
export declare function exportBaseName(pathOrName: string): string;
|
|
25
|
+
/** The exported file's name for `fileName`: `notes.mk.md` + `.html` gives `notes.html`. */
|
|
26
|
+
export declare function exportedFileName(fileName: string, extension: ExportExtension): string;
|
|
27
|
+
/**
|
|
28
|
+
* The path an export is written to when it lands beside its source note:
|
|
29
|
+
* the same folder, the same base name, the new extension. `notePath` is a
|
|
30
|
+
* `/`-separated path — a `vscode.Uri.path` or an Obsidian vault-relative
|
|
31
|
+
* path, never a platform `fsPath` — so no `node:path` import and no
|
|
32
|
+
* platform branching is needed, and this module stays environment-free.
|
|
33
|
+
*/
|
|
34
|
+
export declare function exportedSiblingPath(notePath: string, extension: ExportExtension): string;
|
|
35
|
+
/** The exported document's `<title>`: the note's own base name, so a browser tab and a PDF's metadata name the note rather than the format. */
|
|
36
|
+
export declare function exportDocumentTitle(fileName: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* The page-level CSS an exported file carries in addition to `doc.css`.
|
|
39
|
+
*
|
|
40
|
+
* `doc.css` is deliberately a LIBRARY stylesheet: it styles `.doc` and
|
|
41
|
+
* never claims `:root` or `body`, because hosts embed it into pages they
|
|
42
|
+
* do not own. A standalone exported file has no host to supply the page
|
|
43
|
+
* around it, so this is that page: a ground color, a readable measure, and
|
|
44
|
+
* print rules, all of them host-authored and trusted (they are passed to
|
|
45
|
+
* the engine's `composeDocument` as `extraCss`, which is inserted verbatim).
|
|
46
|
+
*
|
|
47
|
+
* The print block matters more than it looks: the Obsidian PDF command
|
|
48
|
+
* prints exactly this document, so the paper layout is decided here rather
|
|
49
|
+
* than in a host-specific branch. `@page` sets the sheet's own margins
|
|
50
|
+
* (the PDF command has no separate page-setup step of its own), the
|
|
51
|
+
* `break-inside: avoid` list keeps a card/callout/table from splitting
|
|
52
|
+
* across two pages wherever it fits on one, and the collapsed script
|
|
53
|
+
* marker and the VS Code webview's run marker are hidden on paper: both
|
|
54
|
+
* are live-preview affordances (a disclosure toggle, a status line about
|
|
55
|
+
* the last run) that mean nothing on a printed or archived page.
|
|
56
|
+
*/
|
|
57
|
+
/**
|
|
58
|
+
* The class `composeNoteHtmlExport`/`buildNoteHtmlExport` put on the `.doc`
|
|
59
|
+
* wrapper when `hideScriptBlocks` is on, and the class `EXPORT_PAGE_CSS`'s
|
|
60
|
+
* rule below is scoped to.
|
|
61
|
+
*
|
|
62
|
+
* Both hosts already have this preference (VS Code's
|
|
63
|
+
* `markii.hideScriptBlocks`, Obsidian's "Hide script blocks" setting) for
|
|
64
|
+
* their live preview, hiding only the collapsed `⚙ name` marker and
|
|
65
|
+
* nothing else — a failure a reader needs to see is never hidden along
|
|
66
|
+
* with it. An export carries whichever value the exporting host's own
|
|
67
|
+
* preview was showing, so a note exported with scripts hidden in the
|
|
68
|
+
* editor stays that way in the file.
|
|
69
|
+
*/
|
|
70
|
+
export declare const EXPORT_HIDE_SCRIPT_BLOCKS_CLASS = "mk-export--hide-scripts";
|
|
71
|
+
export declare const EXPORT_PAGE_CSS = "\nbody {\n margin: 0;\n background: #fff;\n}\n.doc {\n box-sizing: border-box;\n max-width: 46rem;\n margin: 0 auto;\n padding: 2.5rem 1.5rem 4rem;\n}\n.doc.mk-export--hide-scripts .mk-script {\n display: none;\n}\n@page {\n margin: 2cm;\n}\n@media print {\n .doc {\n max-width: none;\n margin: 0;\n padding: 0;\n }\n .doc .mk-card,\n .doc .mk-callout,\n .doc .mk-stat,\n .doc .mk-figure,\n .doc .mk-unknown,\n .doc table {\n break-inside: avoid;\n }\n /* A collapsed `details` prints as its summary alone, which loses the\n content the author wrote. On paper there is nothing to expand, so\n every disclosure prints open. */\n .doc details > *:not(summary) {\n display: block;\n }\n .doc .mk-script,\n .doc .mk-preview__run-marker {\n display: none;\n }\n}\n";
|
|
72
|
+
/**
|
|
73
|
+
* The static HTML engine, injected so this module imports no renderer.
|
|
74
|
+
* A host passes `createHtmlExportEngine()` from `@markii/host/node`, which
|
|
75
|
+
* wraps `@markii/html`; a test may pass a stub.
|
|
76
|
+
*/
|
|
77
|
+
export interface HtmlExportEngine {
|
|
78
|
+
/** Renders one note's source to a body string with the standard component set, last-run values bound through `store`. Never throws. */
|
|
79
|
+
renderBody(text: string, store: ValueStore | undefined, diagnostics: {
|
|
80
|
+
readonly unavailableComponents?: readonly UnavailableComponent[];
|
|
81
|
+
readonly onDiagnostic?: OnDiagnostic;
|
|
82
|
+
}): string;
|
|
83
|
+
/** Wraps a body in the full-page shell (`doc.css` embedded). */
|
|
84
|
+
composeDocument(bodyHtml: string, options: {
|
|
85
|
+
readonly title: string;
|
|
86
|
+
readonly extraCss: string;
|
|
87
|
+
readonly docClassName?: string;
|
|
88
|
+
}): string;
|
|
89
|
+
/** The one HTML-escaping primitive. */
|
|
90
|
+
escapeHtml(value: string): string;
|
|
91
|
+
}
|
|
92
|
+
/** What `buildNoteHtmlExport` needs to turn one note into a standalone file. */
|
|
93
|
+
export interface NoteHtmlExportOptions {
|
|
94
|
+
/** The static HTML engine. */
|
|
95
|
+
readonly engine: HtmlExportEngine;
|
|
96
|
+
/** The note's full source text. */
|
|
97
|
+
readonly text: string;
|
|
98
|
+
/** The note's file name, used for the document title. A path is accepted; only the last segment is read. */
|
|
99
|
+
readonly fileName: string;
|
|
100
|
+
/**
|
|
101
|
+
* The note's last-run values, exactly as the host persisted them
|
|
102
|
+
* (`readPersistedValues`). Bound components and `:value[...]` markers
|
|
103
|
+
* bake these in. Omitted or empty means the note exports with its
|
|
104
|
+
* standard empty states, which is what a note that has never been run
|
|
105
|
+
* should show.
|
|
106
|
+
*/
|
|
107
|
+
readonly values?: Record<string, StoredValue>;
|
|
108
|
+
/** Extra host CSS appended after `doc.css` and `EXPORT_PAGE_CSS`. Trusted, inserted verbatim. */
|
|
109
|
+
readonly extraCss?: string;
|
|
110
|
+
/**
|
|
111
|
+
* Hides the collapsed script marker in the exported file, mirroring the
|
|
112
|
+
* exporting host's own `hideScriptBlocks` preview preference. Defaults to
|
|
113
|
+
* `false`, which exports every script marker exactly as the preview
|
|
114
|
+
* would show it with the preference off.
|
|
115
|
+
*/
|
|
116
|
+
readonly hideScriptBlocks?: boolean;
|
|
117
|
+
/** Installed pack components the static engine cannot draw; see `NoteExportBuildRequest`. */
|
|
118
|
+
readonly unavailableComponents?: readonly UnavailableComponent[];
|
|
119
|
+
/** Receives the static engine's render diagnostics. */
|
|
120
|
+
readonly onDiagnostic?: OnDiagnostic;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Renders one note to a complete, self-contained HTML document string.
|
|
124
|
+
*
|
|
125
|
+
* Never throws: `renderMarkToHtml` already returns its own failure fallback
|
|
126
|
+
* rather than propagating, so a caller always gets a document it can write.
|
|
127
|
+
*/
|
|
128
|
+
export declare function buildNoteHtmlExport(options: NoteHtmlExportOptions): string;
|
|
129
|
+
/**
|
|
130
|
+
* ISSUE #28 SLICE 2: rendering an export through a host's REACT engine.
|
|
131
|
+
*
|
|
132
|
+
* Slice 1 rendered every export with `@markii/html`, so a pack directive
|
|
133
|
+
* came out as that engine's unknown-component box. The static engine cannot
|
|
134
|
+
* load a pack, because a pack component is a React module. The fix is not a
|
|
135
|
+
* second component system: it is to let a host that ALREADY has React and a
|
|
136
|
+
* merged registry in front of it (both do, for the preview) render the
|
|
137
|
+
* export body with exactly what the preview renders, and hand the resulting
|
|
138
|
+
* STRING back here.
|
|
139
|
+
*
|
|
140
|
+
* That keeps this package React-free. Composition is the host-neutral part
|
|
141
|
+
* (the page shell, `doc.css`, the print rules, the pack stylesheets, the
|
|
142
|
+
* file name), and it needs no renderer at all once it has a body string.
|
|
143
|
+
* The React render itself happens where React already lives: the VS Code
|
|
144
|
+
* webview, and the Obsidian plugin's own renderer process.
|
|
145
|
+
*/
|
|
146
|
+
/** One loaded pack's emitted stylesheet, as a host already holds it for its preview. */
|
|
147
|
+
export interface ExportPackStylesheet {
|
|
148
|
+
/** The pack's namespace, used only to label the block in the exported file. */
|
|
149
|
+
readonly namespace: string;
|
|
150
|
+
/** The stylesheet's text, exactly as the host injects it into its preview. */
|
|
151
|
+
readonly cssText: string;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Why an export fell back to the static engine. Diagnostics-facing: each
|
|
155
|
+
* host words these itself, since the thing a user would do about it differs
|
|
156
|
+
* per host.
|
|
157
|
+
*
|
|
158
|
+
* - `no-packs`: no pack components were loaded, so the static engine
|
|
159
|
+
* renders precisely what the React engine would have. Not a degradation.
|
|
160
|
+
* - `no-renderer`: the host had no React surface to render through at all
|
|
161
|
+
* (VS Code with no preview panel open).
|
|
162
|
+
* - `timeout`: the host's renderer was asked and did not answer in time.
|
|
163
|
+
* - `render-failed`: the renderer answered with a failure, or threw.
|
|
164
|
+
*/
|
|
165
|
+
export type StaticExportReason = 'no-packs' | 'no-renderer' | 'timeout' | 'render-failed';
|
|
166
|
+
/** Which engine actually rendered an export's body, and what that means. */
|
|
167
|
+
export type ExportRenderInfo = {
|
|
168
|
+
readonly engine: 'react';
|
|
169
|
+
/** How many loaded packs contributed components to the registry that rendered this file. */
|
|
170
|
+
readonly packCount: number;
|
|
171
|
+
/** How many of those packs contributed a stylesheet embedded in the file. */
|
|
172
|
+
readonly stylesheetCount: number;
|
|
173
|
+
} | {
|
|
174
|
+
readonly engine: 'static';
|
|
175
|
+
readonly reason: StaticExportReason;
|
|
176
|
+
/** The verbatim detail behind a `timeout`/`render-failed`, for the diagnostics surface only. */
|
|
177
|
+
readonly detail?: string;
|
|
178
|
+
};
|
|
179
|
+
/** What a host's React renderer returns: the body markup, or why it could not produce one. */
|
|
180
|
+
export type ExportBodyResult = {
|
|
181
|
+
readonly ok: true;
|
|
182
|
+
readonly html: string;
|
|
183
|
+
} | {
|
|
184
|
+
readonly ok: false;
|
|
185
|
+
readonly reason: Extract<StaticExportReason, 'timeout' | 'render-failed'>;
|
|
186
|
+
readonly detail?: string;
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* A host's React render of one note's body, as a string. Given the note's
|
|
190
|
+
* text and the values to bake in, it returns the markup that goes INSIDE
|
|
191
|
+
* the exported document's `.doc` wrapper. It is expected not to throw; one
|
|
192
|
+
* that does is caught here and treated as `render-failed`, so a caller
|
|
193
|
+
* always gets a document it can write.
|
|
194
|
+
*/
|
|
195
|
+
export type ExportBodyRenderer = (text: string, values: Record<string, StoredValue>) => ExportBodyResult | Promise<ExportBodyResult>;
|
|
196
|
+
/**
|
|
197
|
+
* The pack stylesheets as one CSS block, in the order the host loaded them
|
|
198
|
+
* — the same order its preview injects them, which is what decides the
|
|
199
|
+
* cascade between two packs that style the same thing. Each block is
|
|
200
|
+
* labeled with its pack's namespace so a reader of the exported file can
|
|
201
|
+
* tell whose rules these are.
|
|
202
|
+
*
|
|
203
|
+
* Placed AFTER `doc.css` and `EXPORT_PAGE_CSS` by `composeNoteHtmlExport`,
|
|
204
|
+
* matching docs/packs.md's load order: a pack sees resolved `--mk-*` token
|
|
205
|
+
* values and is not overridden by the document stylesheet's broader rules.
|
|
206
|
+
*/
|
|
207
|
+
export declare function packStylesheetsCss(sheets: readonly ExportPackStylesheet[]): string;
|
|
208
|
+
/** What `composeNoteHtmlExport` needs to wrap an already-rendered body in the export shell. */
|
|
209
|
+
export interface ComposeNoteHtmlExportOptions {
|
|
210
|
+
/** The static HTML engine. */
|
|
211
|
+
readonly engine: HtmlExportEngine;
|
|
212
|
+
/** The rendered body markup that goes inside the `.doc` wrapper. Inserted verbatim; it is expected to be renderer output, already escaped. */
|
|
213
|
+
readonly bodyHtml: string;
|
|
214
|
+
/** The note's file name, used for the document title. A path is accepted; only the last segment is read. */
|
|
215
|
+
readonly fileName: string;
|
|
216
|
+
/** The loaded packs' stylesheets, appended after `doc.css` and `EXPORT_PAGE_CSS`. */
|
|
217
|
+
readonly packStylesheets?: readonly ExportPackStylesheet[];
|
|
218
|
+
/** Extra host CSS, appended after `EXPORT_PAGE_CSS` and before the pack stylesheets. Trusted, inserted verbatim. */
|
|
219
|
+
readonly extraCss?: string;
|
|
220
|
+
/** Hides the collapsed script marker in the exported file. See `NoteHtmlExportOptions.hideScriptBlocks`. Defaults to `false`. */
|
|
221
|
+
readonly hideScriptBlocks?: boolean;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Wraps an already-rendered body in the standalone export page: the same
|
|
225
|
+
* shell, `doc.css`, and print rules `buildNoteHtmlExport` produces, plus
|
|
226
|
+
* every loaded pack's stylesheet so pack components in the body are styled.
|
|
227
|
+
*
|
|
228
|
+
* Host-neutral and renderer-neutral on purpose: it takes a STRING, so the
|
|
229
|
+
* React path and the static path compose the identical page and can never
|
|
230
|
+
* drift on what an exported file carries.
|
|
231
|
+
*/
|
|
232
|
+
export declare function composeNoteHtmlExport(options: ComposeNoteHtmlExportOptions): string;
|
|
233
|
+
/** What `buildNoteExport` needs. Everything except `text`/`fileName` is optional; omitting `renderBody` is the plain static export. */
|
|
234
|
+
export interface NoteExportBuildRequest {
|
|
235
|
+
/** The static HTML engine. */
|
|
236
|
+
readonly engine: HtmlExportEngine;
|
|
237
|
+
/** The note's full source text. */
|
|
238
|
+
readonly text: string;
|
|
239
|
+
/** The note's file name, used for the document title. A path is accepted; only the last segment is read. */
|
|
240
|
+
readonly fileName: string;
|
|
241
|
+
/** The note's last-run values, baked in wherever the note binds one. Omitted or empty exports the note's standard empty states. */
|
|
242
|
+
readonly values?: Record<string, StoredValue>;
|
|
243
|
+
/**
|
|
244
|
+
* The host's React render of the body. Omitted when the host has nothing
|
|
245
|
+
* to render through, in which case `staticReason` says which case that
|
|
246
|
+
* is and the static engine renders instead.
|
|
247
|
+
*/
|
|
248
|
+
readonly renderBody?: ExportBodyRenderer;
|
|
249
|
+
/** Why the static engine is being used, when `renderBody` is omitted. Defaults to `no-packs`. */
|
|
250
|
+
readonly staticReason?: StaticExportReason;
|
|
251
|
+
/** The loaded packs' stylesheets. Embedded only when the React path actually rendered the body; the static engine's output never references them. */
|
|
252
|
+
readonly packStylesheets?: readonly ExportPackStylesheet[];
|
|
253
|
+
/** How many loaded packs contributed components to the registry that rendered this file. Diagnostics only. */
|
|
254
|
+
readonly packCount?: number;
|
|
255
|
+
/** Extra host CSS appended after `EXPORT_PAGE_CSS`. Trusted, inserted verbatim. */
|
|
256
|
+
readonly extraCss?: string;
|
|
257
|
+
/**
|
|
258
|
+
* Reads one of the note's local images, so the export can embed it as a
|
|
259
|
+
* `data:` URI and stand on its own (`./image-embed.ts`, issue #28 slice
|
|
260
|
+
* 3). Omitted leaves every image source exactly as the author wrote it,
|
|
261
|
+
* which is slice 2's behavior.
|
|
262
|
+
*/
|
|
263
|
+
readonly embedImages?: ExportImageReader;
|
|
264
|
+
/** Hides the collapsed script marker in the exported file. See `NoteHtmlExportOptions.hideScriptBlocks`. Defaults to `false`. */
|
|
265
|
+
readonly hideScriptBlocks?: boolean;
|
|
266
|
+
/**
|
|
267
|
+
* Installed pack components the static engine cannot draw. A directive
|
|
268
|
+
* naming one reads "no HTML view for" instead of "unknown component" in
|
|
269
|
+
* the exported body. Ignored on the React path, whose host passes the
|
|
270
|
+
* same list to its own renderer inside `renderBody`.
|
|
271
|
+
*/
|
|
272
|
+
readonly unavailableComponents?: readonly UnavailableComponent[];
|
|
273
|
+
/** Receives the static engine's render diagnostics (the `no-engine-view` event). Same scope as `unavailableComponents`. */
|
|
274
|
+
readonly onDiagnostic?: OnDiagnostic;
|
|
275
|
+
}
|
|
276
|
+
/** One built export: the complete document, how it was rendered, and how many values it baked in. */
|
|
277
|
+
export interface NoteExportDocument {
|
|
278
|
+
/** The complete, self-contained HTML document. */
|
|
279
|
+
readonly html: string;
|
|
280
|
+
/** Which engine rendered the body, and why when it was the static one. */
|
|
281
|
+
readonly render: ExportRenderInfo;
|
|
282
|
+
/** How many last-run values were baked in. */
|
|
283
|
+
readonly valueCount: number;
|
|
284
|
+
/** What image embedding did, for the host's diagnostics surface. Empty when no `embedImages` reader was offered. */
|
|
285
|
+
readonly images: EmbeddedImageReport;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Builds one note's standalone export document, through the host's React
|
|
289
|
+
* engine when it offered one and through `@markii/html` otherwise.
|
|
290
|
+
*
|
|
291
|
+
* Never throws and never leaves a caller without a file: a renderer that
|
|
292
|
+
* fails, times out, or throws is classified, recorded in `render`, and
|
|
293
|
+
* replaced by the static engine's output, which is the same document slice
|
|
294
|
+
* 1 produced.
|
|
295
|
+
*/
|
|
296
|
+
export declare function buildNoteExport(request: NoteExportBuildRequest): Promise<NoteExportDocument>;
|
|
297
|
+
/**
|
|
298
|
+
* Whether `text` contains at least one Markii script fence, i.e. a code
|
|
299
|
+
* fence whose info string names `lua`. Both hosts' export notices use this
|
|
300
|
+
* so a note with no scripts is never told to run itself first.
|
|
301
|
+
*/
|
|
302
|
+
export declare function noteHasScripts(text: string): boolean;
|
|
303
|
+
/**
|
|
304
|
+
* One note a cascade index page links to (GitHub issue #28 slice 3, part
|
|
305
|
+
* 2's `index.html`).
|
|
306
|
+
*/
|
|
307
|
+
export interface CascadeIndexEntry {
|
|
308
|
+
/**
|
|
309
|
+
* The note's title as shown on the index page — the same base name
|
|
310
|
+
* `exportDocumentTitle` gives that note's own `<title>` tag, so the two
|
|
311
|
+
* pages never name a note two different ways.
|
|
312
|
+
*/
|
|
313
|
+
readonly title: string;
|
|
314
|
+
/** The note's exported file name inside the archive, e.g. `week-2.html`. Used as the link's `href`, relative to `index.html`. */
|
|
315
|
+
readonly fileName: string;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Builds a cascade archive's `index.html`: a self-contained document
|
|
319
|
+
* listing every exported note by title, each linking to its own file.
|
|
320
|
+
*
|
|
321
|
+
* A cascade archive has no single note to open first once it is
|
|
322
|
+
* unzipped, and without this a reader has to guess a file name or open the
|
|
323
|
+
* archive's contents blind. `entries` is expected in the same order the
|
|
324
|
+
* cascade walked (root note first, breadth first), which is what makes the
|
|
325
|
+
* list read as a sensible table of contents rather than an alphabetized
|
|
326
|
+
* jumble.
|
|
327
|
+
*
|
|
328
|
+
* Uses the same page shell every exported note gets (`doc.css`,
|
|
329
|
+
* `EXPORT_PAGE_CSS`), so the index reads as part of the same export rather
|
|
330
|
+
* than a bare, unstyled file dropped in beside it.
|
|
331
|
+
*/
|
|
332
|
+
export declare function buildCascadeIndexHtml(entries: readonly CascadeIndexEntry[], engine: HtmlExportEngine): string;
|