@analogjs/content 3.0.0-alpha.10 → 3.0.0-alpha.12

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.
@@ -0,0 +1,290 @@
1
+ import { d as ContentRenderer, o as withContentFileLoader, r as withContentListLoader } from "./content-list-loader.mjs";
2
+ import * as i0 from "@angular/core";
3
+ import { Injectable, InjectionToken, inject } from "@angular/core";
4
+ //#region packages/content/md4x/src/lib/md4x-content-renderer.service.ts
5
+ var MD4X_RENDERER_OPTIONS = new InjectionToken("md4x_renderer_options");
6
+ function makeSlug$1(text, slugCounts) {
7
+ const baseSlug = text.toLowerCase().replace(/[^\w\s-]/g, "").trim().replace(/\s+/g, "-");
8
+ const count = slugCounts.get(baseSlug) ?? 0;
9
+ slugCounts.set(baseSlug, count + 1);
10
+ return count === 0 ? baseSlug : `${baseSlug}-${count}`;
11
+ }
12
+ function headingsToToc$1(headings) {
13
+ const slugCounts = /* @__PURE__ */ new Map();
14
+ return headings.map((h) => ({
15
+ id: makeSlug$1(h.text, slugCounts),
16
+ level: h.level,
17
+ text: h.text
18
+ }));
19
+ }
20
+ function injectHeadingIds$1(html, toc) {
21
+ let tocIndex = 0;
22
+ return html.replace(/<h([1-6])>/g, (match, level) => {
23
+ const item = toc[tocIndex++];
24
+ return item ? `<h${level} id="${item.id}">` : match;
25
+ });
26
+ }
27
+ /**
28
+ * Content renderer backed by md4x (C-based CommonMark parser compiled with Zig).
29
+ * 50-70x faster than marked for complex documents.
30
+ *
31
+ * @experimental md4x integration is experimental and may change in future releases.
32
+ */
33
+ var Md4xContentRendererService = class Md4xContentRendererService extends ContentRenderer {
34
+ constructor() {
35
+ super(...arguments);
36
+ this.options = inject(MD4X_RENDERER_OPTIONS, { optional: true });
37
+ }
38
+ async render(content) {
39
+ const { renderToHtml, parseMeta } = await import("md4x/napi");
40
+ const html = renderToHtml(content, {
41
+ heal: this.options?.heal,
42
+ highlighter: this.options?.highlighter
43
+ });
44
+ const toc = headingsToToc$1(parseMeta(content).headings);
45
+ return {
46
+ content: injectHeadingIds$1(html, toc),
47
+ toc
48
+ };
49
+ }
50
+ getContentHeadings(content) {
51
+ const lines = content.split("\n");
52
+ const toc = [];
53
+ const slugCounts = /* @__PURE__ */ new Map();
54
+ for (const line of lines) {
55
+ const match = /^(#{1,6})\s+(.+?)\s*$/.exec(line);
56
+ if (!match) continue;
57
+ const level = match[1].length;
58
+ const text = match[2].trim();
59
+ if (!text) continue;
60
+ toc.push({
61
+ id: makeSlug$1(text, slugCounts),
62
+ level,
63
+ text
64
+ });
65
+ }
66
+ return toc;
67
+ }
68
+ static {
69
+ this.ɵfac = i0.ɵɵngDeclareFactory({
70
+ minVersion: "12.0.0",
71
+ version: "21.1.1",
72
+ ngImport: i0,
73
+ type: Md4xContentRendererService,
74
+ deps: null,
75
+ target: i0.ɵɵFactoryTarget.Injectable
76
+ });
77
+ }
78
+ static {
79
+ this.ɵprov = i0.ɵɵngDeclareInjectable({
80
+ minVersion: "12.0.0",
81
+ version: "21.1.1",
82
+ ngImport: i0,
83
+ type: Md4xContentRendererService
84
+ });
85
+ }
86
+ };
87
+ i0.ɵɵngDeclareClassMetadata({
88
+ minVersion: "12.0.0",
89
+ version: "21.1.1",
90
+ ngImport: i0,
91
+ type: Md4xContentRendererService,
92
+ decorators: [{ type: Injectable }]
93
+ });
94
+ //#endregion
95
+ //#region packages/content/md4x/src/lib/md4x-wasm-content-renderer.service.ts
96
+ function makeSlug(text, slugCounts) {
97
+ const baseSlug = text.toLowerCase().replace(/[^\w\s-]/g, "").trim().replace(/\s+/g, "-");
98
+ const count = slugCounts.get(baseSlug) ?? 0;
99
+ slugCounts.set(baseSlug, count + 1);
100
+ return count === 0 ? baseSlug : `${baseSlug}-${count}`;
101
+ }
102
+ function headingsToToc(headings) {
103
+ const slugCounts = /* @__PURE__ */ new Map();
104
+ return headings.map((h) => ({
105
+ id: makeSlug(h.text, slugCounts),
106
+ level: h.level,
107
+ text: h.text
108
+ }));
109
+ }
110
+ function injectHeadingIds(html, toc) {
111
+ let tocIndex = 0;
112
+ return html.replace(/<h([1-6])>/g, (match, level) => {
113
+ const item = toc[tocIndex++];
114
+ return item ? `<h${level} id="${item.id}">` : match;
115
+ });
116
+ }
117
+ /**
118
+ * Content renderer backed by md4x/wasm for client-side (browser) rendering.
119
+ * ~100KB gzip, 3-6x faster than marked in the browser.
120
+ *
121
+ * @experimental md4x integration is experimental and may change in future releases.
122
+ */
123
+ var Md4xWasmContentRendererService = class Md4xWasmContentRendererService extends ContentRenderer {
124
+ constructor() {
125
+ super(...arguments);
126
+ this.options = inject(MD4X_RENDERER_OPTIONS, { optional: true });
127
+ this.initPromise = null;
128
+ }
129
+ async render(content) {
130
+ const wasm = await import("md4x/wasm");
131
+ if (!this.initPromise) this.initPromise = wasm.init();
132
+ await this.initPromise;
133
+ const html = wasm.renderToHtml(content, {
134
+ heal: this.options?.heal,
135
+ highlighter: this.options?.highlighter
136
+ });
137
+ const toc = headingsToToc(wasm.parseMeta(content).headings);
138
+ return {
139
+ content: injectHeadingIds(html, toc),
140
+ toc
141
+ };
142
+ }
143
+ getContentHeadings(content) {
144
+ const lines = content.split("\n");
145
+ const toc = [];
146
+ const slugCounts = /* @__PURE__ */ new Map();
147
+ for (const line of lines) {
148
+ const match = /^(#{1,6})\s+(.+?)\s*$/.exec(line);
149
+ if (!match) continue;
150
+ const level = match[1].length;
151
+ const text = match[2].trim();
152
+ if (!text) continue;
153
+ toc.push({
154
+ id: makeSlug(text, slugCounts),
155
+ level,
156
+ text
157
+ });
158
+ }
159
+ return toc;
160
+ }
161
+ static {
162
+ this.ɵfac = i0.ɵɵngDeclareFactory({
163
+ minVersion: "12.0.0",
164
+ version: "21.1.1",
165
+ ngImport: i0,
166
+ type: Md4xWasmContentRendererService,
167
+ deps: null,
168
+ target: i0.ɵɵFactoryTarget.Injectable
169
+ });
170
+ }
171
+ static {
172
+ this.ɵprov = i0.ɵɵngDeclareInjectable({
173
+ minVersion: "12.0.0",
174
+ version: "21.1.1",
175
+ ngImport: i0,
176
+ type: Md4xWasmContentRendererService
177
+ });
178
+ }
179
+ };
180
+ i0.ɵɵngDeclareClassMetadata({
181
+ minVersion: "12.0.0",
182
+ version: "21.1.1",
183
+ ngImport: i0,
184
+ type: Md4xWasmContentRendererService,
185
+ decorators: [{ type: Injectable }]
186
+ });
187
+ //#endregion
188
+ //#region packages/content/md4x/src/lib/provide-md4x.ts
189
+ /**
190
+ * Provides the experimental md4x-based content renderer (NAPI, server/build-time).
191
+ *
192
+ * @experimental md4x integration is experimental and may change in future releases.
193
+ *
194
+ * @example
195
+ * ```typescript
196
+ * provideContent(withMd4xRenderer());
197
+ * provideContent(withMd4xRenderer({ heal: true }));
198
+ * ```
199
+ */
200
+ function withMd4xRenderer(options) {
201
+ return [
202
+ {
203
+ provide: ContentRenderer,
204
+ useClass: Md4xContentRendererService
205
+ },
206
+ options ? {
207
+ provide: MD4X_RENDERER_OPTIONS,
208
+ useValue: options
209
+ } : [],
210
+ withContentFileLoader(),
211
+ withContentListLoader()
212
+ ];
213
+ }
214
+ /**
215
+ * Provides the experimental md4x WASM content renderer (browser/CSR).
216
+ * ~100KB gzip, 3-6x faster than marked in the browser.
217
+ *
218
+ * @experimental md4x integration is experimental and may change in future releases.
219
+ *
220
+ * @example
221
+ * ```typescript
222
+ * provideContent(withMd4xWasmRenderer());
223
+ * ```
224
+ */
225
+ function withMd4xWasmRenderer(options) {
226
+ return [
227
+ {
228
+ provide: ContentRenderer,
229
+ useClass: Md4xWasmContentRendererService
230
+ },
231
+ options ? {
232
+ provide: MD4X_RENDERER_OPTIONS,
233
+ useValue: options
234
+ } : [],
235
+ withContentFileLoader(),
236
+ withContentListLoader()
237
+ ];
238
+ }
239
+ //#endregion
240
+ //#region packages/content/md4x/src/lib/streaming-markdown-renderer.ts
241
+ /**
242
+ * Transforms a stream of markdown chunks into a stream of rendered HTML.
243
+ * Uses md4x's `heal()` to fix incomplete markdown from streaming sources
244
+ * (LLMs, collaborative editing) so each emitted HTML chunk is valid.
245
+ *
246
+ * @experimental Streaming markdown support is experimental and may change in future releases.
247
+ *
248
+ * @example
249
+ * ```typescript
250
+ * // In a Nitro API route
251
+ * import { streamMarkdown } from '@analogjs/content';
252
+ *
253
+ * export default defineEventHandler(async (event) => {
254
+ * const llmStream = getAIStream(prompt);
255
+ * return streamMarkdown(llmStream, { heal: true });
256
+ * });
257
+ * ```
258
+ */
259
+ async function streamMarkdown(input, options) {
260
+ const { renderToHtml, heal } = await import("md4x/napi");
261
+ let buffer = "";
262
+ const reader = input.getReader();
263
+ return new ReadableStream({
264
+ async pull(controller) {
265
+ try {
266
+ const { done, value } = await reader.read();
267
+ if (done) {
268
+ if (buffer) {
269
+ const source = options?.heal ? heal(buffer) : buffer;
270
+ controller.enqueue(renderToHtml(source));
271
+ }
272
+ controller.close();
273
+ return;
274
+ }
275
+ buffer += value;
276
+ const source = options?.heal ? heal(buffer) : buffer;
277
+ controller.enqueue(renderToHtml(source));
278
+ } catch (error) {
279
+ controller.error(error);
280
+ }
281
+ },
282
+ cancel() {
283
+ reader.cancel();
284
+ }
285
+ });
286
+ }
287
+ //#endregion
288
+ export { MD4X_RENDERER_OPTIONS, Md4xContentRendererService, Md4xWasmContentRendererService, streamMarkdown, withMd4xRenderer, withMd4xWasmRenderer };
289
+
290
+ //# sourceMappingURL=analogjs-content-md4x.mjs.map
@@ -0,0 +1,170 @@
1
+ import * as i0 from "@angular/core";
2
+ import { Directive, ElementRef, InjectionToken, Renderer2, ViewContainerRef, effect, inject, input } from "@angular/core";
3
+ //#region packages/content/mdc/src/lib/mdc-component-registry.ts
4
+ /**
5
+ * Registry mapping MDC component names to lazy-loaded Angular components.
6
+ *
7
+ * @experimental MDC component support is experimental and may change in future releases.
8
+ */
9
+ var MDC_COMPONENTS = new InjectionToken("mdc_components");
10
+ /**
11
+ * Provides a registry of Angular components that can be used in MDC
12
+ * (Markdown Components) syntax within markdown content.
13
+ *
14
+ * @experimental MDC component support is experimental and may change in future releases.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * provideContent(
19
+ * withMd4xRenderer(),
20
+ * withMdcComponents({
21
+ * alert: () => import('./components/alert.component').then(m => m.AlertComponent),
22
+ * card: () => import('./components/card.component').then(m => m.CardComponent),
23
+ * }),
24
+ * );
25
+ * ```
26
+ */
27
+ function withMdcComponents(components) {
28
+ return {
29
+ provide: MDC_COMPONENTS,
30
+ useValue: new Map(Object.entries(components))
31
+ };
32
+ }
33
+ //#endregion
34
+ //#region packages/content/mdc/src/lib/mdc-renderer.directive.ts
35
+ /**
36
+ * Directive that renders MDC (Markdown Components) AST nodes as Angular components.
37
+ *
38
+ * Walks the ComarkTree AST from md4x's `parseAST()`, matches component nodes
39
+ * to the registered MDC_COMPONENTS map, and instantiates them via
40
+ * `ViewContainerRef.createComponent()` with MDC attributes bound as inputs.
41
+ *
42
+ * @experimental MDC component support is experimental and may change in future releases.
43
+ *
44
+ * @example
45
+ * ```html
46
+ * <div [mdcAst]="parsedAst"></div>
47
+ * ```
48
+ */
49
+ var MdcRendererDirective = class MdcRendererDirective {
50
+ constructor() {
51
+ this.ast = input(null, { alias: "mdcAst" });
52
+ this.viewContainer = inject(ViewContainerRef);
53
+ this.renderer = inject(Renderer2);
54
+ this.el = inject(ElementRef);
55
+ this.components = inject(MDC_COMPONENTS, { optional: true });
56
+ this.renderId = 0;
57
+ effect(() => {
58
+ const ast = this.ast();
59
+ const currentRenderId = ++this.renderId;
60
+ this.viewContainer.clear();
61
+ const host = this.el.nativeElement;
62
+ host.innerHTML = "";
63
+ if (!ast?.nodes) return;
64
+ this.renderNodes(ast.nodes, host, currentRenderId);
65
+ });
66
+ }
67
+ async renderNodes(nodes, parent, renderId) {
68
+ for (const node of nodes) {
69
+ if (this.renderId !== renderId) return;
70
+ await this.renderNode(node, parent, renderId);
71
+ }
72
+ }
73
+ async renderNode(node, parent, renderId) {
74
+ if (this.renderId !== renderId) return;
75
+ if (typeof node === "string") {
76
+ const text = this.renderer.createText(node);
77
+ this.renderer.appendChild(parent, text);
78
+ return;
79
+ }
80
+ const [tag, props, ...children] = node;
81
+ if (!tag) {
82
+ for (const child of children) {
83
+ if (this.renderId !== renderId) return;
84
+ await this.renderNode(child, parent, renderId);
85
+ }
86
+ return;
87
+ }
88
+ const componentLoader = this.components?.get(tag);
89
+ if (componentLoader) {
90
+ try {
91
+ const componentType = await componentLoader();
92
+ if (this.renderId !== renderId) return;
93
+ const tempContainer = this.renderer.createElement("div");
94
+ for (const child of children) {
95
+ if (this.renderId !== renderId) return;
96
+ await this.renderNode(child, tempContainer, renderId);
97
+ }
98
+ if (this.renderId !== renderId) return;
99
+ const projectableNodes = [Array.from(tempContainer.childNodes)];
100
+ const componentRef = this.viewContainer.createComponent(componentType, { projectableNodes });
101
+ for (const [key, value] of Object.entries(props)) componentRef.setInput(key, value);
102
+ const componentEl = componentRef.location.nativeElement;
103
+ this.renderer.appendChild(parent, componentEl);
104
+ } catch (e) {
105
+ console.error(`[MdcRenderer] Failed to load component "${tag}":`, e);
106
+ }
107
+ return;
108
+ }
109
+ const el = this.renderer.createElement(tag);
110
+ for (const [key, value] of Object.entries(props)) this.renderer.setAttribute(el, key, String(value));
111
+ for (const child of children) {
112
+ if (this.renderId !== renderId) return;
113
+ await this.renderNode(child, el, renderId);
114
+ }
115
+ this.renderer.appendChild(parent, el);
116
+ }
117
+ static {
118
+ this.ɵfac = i0.ɵɵngDeclareFactory({
119
+ minVersion: "12.0.0",
120
+ version: "21.1.1",
121
+ ngImport: i0,
122
+ type: MdcRendererDirective,
123
+ deps: [],
124
+ target: i0.ɵɵFactoryTarget.Directive
125
+ });
126
+ }
127
+ static {
128
+ this.ɵdir = i0.ɵɵngDeclareDirective({
129
+ minVersion: "17.1.0",
130
+ version: "21.1.1",
131
+ type: MdcRendererDirective,
132
+ isStandalone: true,
133
+ selector: "[mdcAst]",
134
+ inputs: { ast: {
135
+ classPropertyName: "ast",
136
+ publicName: "mdcAst",
137
+ isSignal: true,
138
+ isRequired: false,
139
+ transformFunction: null
140
+ } },
141
+ ngImport: i0
142
+ });
143
+ }
144
+ };
145
+ i0.ɵɵngDeclareClassMetadata({
146
+ minVersion: "12.0.0",
147
+ version: "21.1.1",
148
+ ngImport: i0,
149
+ type: MdcRendererDirective,
150
+ decorators: [{
151
+ type: Directive,
152
+ args: [{
153
+ selector: "[mdcAst]",
154
+ standalone: true
155
+ }]
156
+ }],
157
+ ctorParameters: () => [],
158
+ propDecorators: { ast: [{
159
+ type: i0.Input,
160
+ args: [{
161
+ isSignal: true,
162
+ alias: "mdcAst",
163
+ required: false
164
+ }]
165
+ }] }
166
+ });
167
+ //#endregion
168
+ export { MDC_COMPONENTS, MdcRendererDirective, withMdcComponents };
169
+
170
+ //# sourceMappingURL=analogjs-content-mdc.mjs.map