@franken-suite/franken-markdown 0.3.2 → 0.4.4

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/fmd-view.d.ts ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * `<fmd-view>` web component type surface (bead uito).
3
+ */
4
+
5
+ export interface FmdRenderedDetail {
6
+ bytes: number;
7
+ sourceLength: number;
8
+ diagnostics: Array<{
9
+ severity: "warning" | "error";
10
+ start: number;
11
+ end: number;
12
+ message: string;
13
+ }>;
14
+ }
15
+
16
+ export interface FmdViewEventMap {
17
+ "fmd-rendered": CustomEvent<FmdRenderedDetail>;
18
+ "fmd-error": CustomEvent<{ error: string }>;
19
+ }
20
+
21
+ export interface FmdView extends HTMLElement {
22
+ addEventListener<K extends keyof FmdViewEventMap>(
23
+ type: K,
24
+ listener: (this: FmdView, ev: FmdViewEventMap[K]) => void,
25
+ options?: boolean | AddEventListenerOptions
26
+ ): void;
27
+ addEventListener(
28
+ type: string,
29
+ listener: EventListenerOrEventListenerObject,
30
+ options?: boolean | AddEventListenerOptions
31
+ ): void;
32
+ }
33
+
34
+ /**
35
+ * Register `<fmd-view>` (idempotent). Auto-registered in browser globals on
36
+ * import; call this explicitly for a non-global registry.
37
+ */
38
+ export function registerFmdView(
39
+ registry?: Pick<CustomElementRegistry, "define" | "get">
40
+ ): "fmd-view";
package/fmd-view.js ADDED
@@ -0,0 +1,137 @@
1
+ // <fmd-view> — drop-in Markdown renderer web component (bead uito).
2
+ //
3
+ // Framework-free custom element over the package's createRenderer surface:
4
+ //
5
+ // <fmd-view src="./README.md"></fmd-view>
6
+ // <fmd-view>Six **bytes** of markdown</fmd-view>
7
+ //
8
+ // Attributes:
9
+ // src - fetch this URL as the Markdown source (slot content is the
10
+ // fallback when src is absent/unset)
11
+ // font - "sans" | "serif"
12
+ // dark-mode - "auto" | "disabled"
13
+ //
14
+ // The rendered document lands in a shadow root (style-isolated; the engine's
15
+ // self-contained HTML carries its own CSS). A "fmd-rendered" CustomEvent with
16
+ // { bytes, sourceLength, diagnostics } fires after each successful render;
17
+ // "fmd-error" fires on fetch/render failure. Bytes parity with renderHtml()
18
+ // is exact: the same call is used.
19
+ //
20
+ // Zero dependencies; the shared wasm payload is the package's.
21
+
22
+ import { createRenderer } from "./franken_markdown.js";
23
+
24
+ let rendererPromise = null;
25
+
26
+ function sharedRenderer(wasmInput) {
27
+ if (!rendererPromise) {
28
+ rendererPromise = createRenderer(wasmInput);
29
+ }
30
+ return rendererPromise;
31
+ }
32
+
33
+ const SLOT_RENDER = Symbol("slot source");
34
+
35
+ class FmdView extends HTMLElement {
36
+ static get observedAttributes() {
37
+ return ["src", "font", "dark-mode"];
38
+ }
39
+
40
+ #root = null;
41
+ #lastSrc = undefined;
42
+
43
+ constructor() {
44
+ super();
45
+ this.#root = this.attachShadow({ mode: "open" });
46
+ this.#root.innerHTML = "<style>:host{display:block}</style><slot></slot>";
47
+ }
48
+
49
+ connectedCallback() {
50
+ this.#render();
51
+ }
52
+
53
+ attributeChangedCallback(name, oldV, newV) {
54
+ if (oldV === newV || !this.#root) {
55
+ return;
56
+ }
57
+ if (name === "src" && newV !== this.#lastSrc) {
58
+ this.#render();
59
+ return;
60
+ }
61
+ if (name === "font" || name === "dark-mode") {
62
+ this.#render();
63
+ }
64
+ }
65
+
66
+ async #source() {
67
+ const src = this.getAttribute("src");
68
+ if (src) {
69
+ const res = await fetch(src);
70
+ if (!res.ok) {
71
+ throw new Error(`fmd-view: fetch ${src} -> HTTP ${res.status}`);
72
+ }
73
+ return await res.text();
74
+ }
75
+ // Slot content as inline Markdown. Preserve text exactly.
76
+ const slot = this.querySelector("script[type='text/markdown']");
77
+ if (slot) {
78
+ return slot.textContent ?? "";
79
+ }
80
+ return this.textContent ?? "";
81
+ }
82
+
83
+ async #render() {
84
+ const srcAttr = this.getAttribute("src");
85
+ this.#lastSrc = srcAttr;
86
+ let markdown;
87
+ try {
88
+ markdown = await this.#source();
89
+ } catch (err) {
90
+ this.dispatchEvent(new CustomEvent("fmd-error", { detail: { error: String(err) } }));
91
+ return;
92
+ }
93
+ let renderer;
94
+ try {
95
+ // If the host already initialized the engine with explicit wasm bytes,
96
+ // a package-level export would be needed; default path resolves the
97
+ // bundled artifact via the wrapper's own URL resolution.
98
+ renderer = await sharedRenderer(undefined);
99
+ } catch (err) {
100
+ this.dispatchEvent(new CustomEvent("fmd-error", { detail: { error: String(err) } }));
101
+ return;
102
+ }
103
+ const out = await renderer.renderHtml(markdown, {
104
+ font: this.getAttribute("font") === "serif" ? "serif" : "sans",
105
+ darkMode: this.getAttribute("dark-mode") === "disabled" ? "disabled" : "auto",
106
+ });
107
+ const html = out.text();
108
+ this.#root.innerHTML = `<style>:host{display:block}</style>${html}`;
109
+ this.dispatchEvent(
110
+ new CustomEvent("fmd-rendered", {
111
+ detail: {
112
+ bytes: out.bytes.byteLength,
113
+ sourceLength: out.sourceLength,
114
+ diagnostics: out.diagnostics,
115
+ },
116
+ })
117
+ );
118
+ }
119
+ }
120
+
121
+ const ELEMENT_NAME = "fmd-view";
122
+
123
+ /**
124
+ * Register <fmd-view> on a customElements registry (defaults to global).
125
+ * Idempotent; returns the element name.
126
+ */
127
+ export function registerFmdView(registry = globalThis.customElements) {
128
+ if (!registry.get(ELEMENT_NAME)) {
129
+ registry.define(ELEMENT_NAME, FmdView);
130
+ }
131
+ return ELEMENT_NAME;
132
+ }
133
+
134
+ // Auto-register in browser globals (no-op under Node/bundlers without it).
135
+ if (typeof globalThis.customElements !== "undefined") {
136
+ registerFmdView();
137
+ }
@@ -1,4 +1,12 @@
1
- export type FmdOutputFormat = "html" | "pdf";
1
+ export type FmdOutputFormat =
2
+ | "html"
3
+ | "pdf"
4
+ | "svg"
5
+ | "epub"
6
+ | "interactive-html"
7
+ | "diff-html"
8
+ | "book-site"
9
+ | "book-pdf";
2
10
  export type FmdFont = "sans" | "serif";
3
11
  export type FmdDarkMode = "auto" | "disabled";
4
12
 
@@ -12,7 +20,7 @@ export interface FmdDiagnostic {
12
20
  export interface FmdPdfImageAsset {
13
21
  /** Markdown image destination, for example `images/chart.png` from `![Chart](images/chart.png)`. */
14
22
  destination: string;
15
- /** Browser-supplied image bytes. PNG and SVG assets are supported in PDF output. */
23
+ /** Browser-supplied image bytes. PNG and SVG are supported in HTML and PDF output. */
16
24
  bytes: Uint8Array | ArrayBuffer | ArrayBufferView;
17
25
  }
18
26
 
@@ -28,6 +36,12 @@ export interface FmdFontAsset {
28
36
  slot: FmdFontAssetSlot;
29
37
  /** Browser-supplied TrueType font bytes. */
30
38
  bytes: Uint8Array | ArrayBuffer | ArrayBufferView;
39
+ /**
40
+ * Optional CSS `font-weight` pin (integer 1..=1000) for variable `wght` faces.
41
+ * Static faces ignore the pin. When `body-bold` is omitted and `body-regular`
42
+ * is a variable face, bold instances from that same file at 700 (or this pin).
43
+ */
44
+ weight?: number;
31
45
  }
32
46
 
33
47
  export interface FmdRenderOptions {
@@ -40,7 +54,33 @@ export interface FmdRenderOptions {
40
54
  /** Finite non-negative integer seconds, <= Number.MAX_SAFE_INTEGER. */
41
55
  metadataEpochSeconds?: number;
42
56
  codeLineNumbers?: boolean;
43
- /** Host-supplied PDF image bytes; any number of assets may be supplied per render call. */
57
+ /** Render running page numbers in the bottom margin of PDF pages. */
58
+ pageNumbers?: boolean;
59
+ /** Base body size override in points (clamped by the core to [6, 24]). */
60
+ baseFontSize?: number;
61
+ /** Uniform typographic scale factor (e.g. 1.125 = 112.5% / Large) or preset name ('xs' | 'sm' | 'compact' | 'md' | 'lg' | 'xl' | '2xl' | 'huge'). Scales both HTML and PDF uniformly. */
62
+ fontScale?: number | string;
63
+ /** Alias for fontScale. */
64
+ typeSize?: number | string;
65
+ /** Per-step heading ratio, e.g. 1.25 (Major Third); clamped to [1.05, 2]. */
66
+ headingScale?: number;
67
+ /** Nominal table cell size override in points; clamped to [5, base]. */
68
+ tableFontSize?: number;
69
+ /** BCP-47 language tag used for HTML metadata and language-aware hyphenation. */
70
+ lang?: "en" | "de" | "fr" | "es" | "nl" | string;
71
+ /** Generate a document table of contents. */
72
+ toc?: boolean;
73
+ /** Maximum generated TOC heading depth (1...6). */
74
+ tocDepth?: number;
75
+ /** Adaptive PDF page-count target. */
76
+ fitToPages?: number;
77
+ /** Enable optical-margin punctuation protrusion for justified PDF text. */
78
+ microtype?: "disabled" | "protrusion";
79
+ /** Boolean alias for microtype: "protrusion". */
80
+ microtypeProtrusion?: boolean;
81
+ /** Standalone SVG poster width in points. */
82
+ maxWidthPt?: number;
83
+ /** Host-supplied image bytes (HTML data URIs and PDF embedding); any number per render. */
44
84
  pdfImages?: FmdPdfImageAsset[];
45
85
  /** Host-supplied TrueType font bytes by renderer slot. */
46
86
  fontAssets?: FmdFontAsset[];
@@ -66,13 +106,16 @@ export interface FmdCapabilities {
66
106
  mime_type: "text/html; charset=utf-8";
67
107
  self_contained: boolean;
68
108
  custom_css_utf8: boolean;
109
+ image_assets: "png_svg_v0_host_supplied_bytes";
69
110
  font_assets: "ttf_v0_host_supplied_bytes";
111
+ font_slot_weight: "css_1_to_1000_variable_wght";
70
112
  };
71
113
  pdf: {
72
114
  mime_type: "application/pdf";
73
115
  deterministic_metadata_epoch: boolean;
74
116
  image_assets: "png_svg_v0_host_supplied_bytes";
75
117
  font_assets: "ttf_v0_host_supplied_bytes";
118
+ font_slot_weight: "css_1_to_1000_variable_wght";
76
119
  };
77
120
  diagnostics: {
78
121
  source_spans: "byte_offsets";
@@ -89,14 +132,80 @@ export interface FmdCapabilities {
89
132
 
90
133
  export interface FmdRenderer {
91
134
  capabilities(): Promise<FmdCapabilities>;
135
+ accessibilityAudit(markdown: string): Promise<FmdAccessibilityReport>;
136
+ documentStats(markdown: string): Promise<FmdDocumentStats>;
137
+ renderBookPdf(files: FmdBookFile[], options?: FmdRenderOptions): Promise<FmdRenderOutput>;
138
+ renderBookSite(files: FmdBookFile[], options?: FmdRenderOptions): Promise<FmdRenderOutput>;
139
+ renderEpub(markdown: string, options?: FmdRenderOptions): Promise<FmdRenderOutput>;
92
140
  renderHtml(markdown: string, options?: FmdRenderOptions): Promise<FmdRenderOutput>;
141
+ renderInteractiveHtml(markdown: string, options?: FmdRenderOptions): Promise<FmdRenderOutput>;
93
142
  renderPdf(markdown: string, options?: FmdRenderOptions): Promise<FmdRenderOutput>;
143
+ renderSemanticDiff(oldMarkdown: string, newMarkdown: string, options?: FmdDiffOptions): Promise<FmdRenderOutput>;
144
+ renderSvg(markdown: string, options?: FmdRenderOptions): Promise<FmdRenderOutput>;
145
+ searchIndex(markdown: string): Promise<FmdSearchIndex>;
146
+ semanticDiff(oldMarkdown: string, newMarkdown: string, options?: FmdDiffOptions): Promise<FmdSemanticDiff>;
147
+ }
148
+
149
+ export interface FmdBookFile { path: string; source: string; }
150
+ export interface FmdDiffOptions { oldName?: string; newName?: string; }
151
+ export interface FmdFinding { severity?: string; code: string; message?: string; detail?: string; }
152
+ export interface FmdDocumentStats {
153
+ schema: "fmd-document-stats-v1";
154
+ bytes: number;
155
+ lines: number;
156
+ words: number;
157
+ characters: number;
158
+ sentences: number;
159
+ syllables: number;
160
+ reading_time_secs: number;
161
+ speaking_time_secs: number;
162
+ flesch_reading_ease: number;
163
+ flesch_kincaid_grade: number;
164
+ reading_ease_label: string;
165
+ structure: Record<string, unknown>;
166
+ outline: Array<{ level: number; text: string; slug: string }>;
167
+ findings: FmdFinding[];
168
+ }
169
+ export interface FmdAccessibilityReport {
170
+ schema_version: "1";
171
+ target: "pdf";
172
+ findings: FmdFinding[];
173
+ [key: string]: unknown;
174
+ }
175
+ export interface FmdSearchIndex {
176
+ schema: "fmd-search-index-v1";
177
+ entries: Array<{ kind: "heading" | "paragraph"; level?: number; anchor: string; text: string }>;
178
+ }
179
+ export interface FmdSemanticDiff {
180
+ schema: "fmd-diff-v1";
181
+ old_name: string;
182
+ new_name: string;
183
+ stats: {
184
+ unchanged_blocks: number;
185
+ inserted_blocks: number;
186
+ deleted_blocks: number;
187
+ modified_blocks: number;
188
+ words_inserted: number;
189
+ words_deleted: number;
190
+ similarity_ratio: number;
191
+ };
192
+ [key: string]: unknown;
94
193
  }
95
194
 
96
195
  export function init(input?: RequestInfo | URL | Response | BufferSource | WebAssembly.Module): Promise<void>;
97
196
  export function capabilities(): Promise<FmdCapabilities>;
197
+ export function accessibilityAudit(markdown: string): Promise<FmdAccessibilityReport>;
198
+ export function documentStats(markdown: string): Promise<FmdDocumentStats>;
199
+ export function renderBookPdf(files: FmdBookFile[], options?: FmdRenderOptions): Promise<FmdRenderOutput>;
200
+ export function renderBookSite(files: FmdBookFile[], options?: FmdRenderOptions): Promise<FmdRenderOutput>;
201
+ export function renderEpub(markdown: string, options?: FmdRenderOptions): Promise<FmdRenderOutput>;
98
202
  export function renderHtml(markdown: string, options?: FmdRenderOptions): Promise<FmdRenderOutput>;
203
+ export function renderInteractiveHtml(markdown: string, options?: FmdRenderOptions): Promise<FmdRenderOutput>;
99
204
  export function renderPdf(markdown: string, options?: FmdRenderOptions): Promise<FmdRenderOutput>;
205
+ export function renderSemanticDiff(oldMarkdown: string, newMarkdown: string, options?: FmdDiffOptions): Promise<FmdRenderOutput>;
206
+ export function renderSvg(markdown: string, options?: FmdRenderOptions): Promise<FmdRenderOutput>;
207
+ export function searchIndex(markdown: string): Promise<FmdSearchIndex>;
208
+ export function semanticDiff(oldMarkdown: string, newMarkdown: string, options?: FmdDiffOptions): Promise<FmdSemanticDiff>;
100
209
  export function createRenderer(
101
210
  input?: RequestInfo | URL | Response | BufferSource | WebAssembly.Module
102
211
  ): Promise<FmdRenderer>;