@dogsbay/render-html 0.2.0-beta.124 → 0.2.0-beta.125

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/README.md CHANGED
@@ -27,6 +27,15 @@ const html = renderBlocks(dogsbayMdToTree(markdown).tree, ctx);
27
27
  The output uses Tailwind utility classes and the theme tokens. Styling it needs the Dogsbay
28
28
  theme CSS, compiled for the classes the page uses.
29
29
 
30
+ ## Source lines (scroll sync)
31
+
32
+ Parse with `sourceLines: true` (`dogsbayMdToTree` in format-dogsbay-md, or
33
+ `ditaTopicToTree` in format-dita) and each block carries its 1-based source line
34
+ on `meta.line`. Render with `createRenderContext({ dataLines: true })` and
35
+ it is written as `data-line="N"` on each block's first element and on list
36
+ items, so an editor can map a click in the preview to the source and scroll the
37
+ preview to the cursor. Off by default; only a positive integer is ever written.
38
+
30
39
  ## API reference pages
31
40
 
32
41
  Autodoc symbols (`api-class`, `api-member`, `api-doc`, `api-params`,
package/css/docs.css CHANGED
@@ -35,10 +35,11 @@
35
35
  .docs-prose {
36
36
  line-height: 1.7;
37
37
 
38
- & h1:not(.dba-api *) { font-family: var(--font-heading); font-size: 2rem; font-weight: 700; margin-top: 0; margin-bottom: 1rem; line-height: 1.2; letter-spacing: -0.025em; }
39
- & h2:not(.dba-api *) { font-family: var(--font-heading); font-size: 1.5rem; font-weight: 600; margin-top: 2.5rem; margin-bottom: 0.75rem; line-height: 1.3; border-bottom: 1px solid var(--border); padding-bottom: 0.5rem; letter-spacing: -0.015em; }
40
- & h3:not(.dba-api *) { font-family: var(--font-heading); font-size: 1.25rem; font-weight: 600; margin-top: 2rem; margin-bottom: 0.5rem; line-height: 1.4; }
41
- & h4:not(.dba-api *) { font-family: var(--font-heading); font-size: 1.1rem; font-weight: 600; margin-top: 1.5rem; margin-bottom: 0.5rem; }
38
+ /* A heading that is part of a component (a card title: h3.not-prose) keeps the component look */
39
+ & h1:not(.dba-api *):not(.not-prose) { font-family: var(--font-heading); font-size: 2rem; font-weight: 700; margin-top: 0; margin-bottom: 1rem; line-height: 1.2; letter-spacing: -0.025em; }
40
+ & h2:not(.dba-api *):not(.not-prose) { font-family: var(--font-heading); font-size: 1.5rem; font-weight: 600; margin-top: 2.5rem; margin-bottom: 0.75rem; line-height: 1.3; border-bottom: 1px solid var(--border); padding-bottom: 0.5rem; letter-spacing: -0.015em; }
41
+ & h3:not(.dba-api *):not(.not-prose) { font-family: var(--font-heading); font-size: 1.25rem; font-weight: 600; margin-top: 2rem; margin-bottom: 0.5rem; line-height: 1.4; }
42
+ & h4:not(.dba-api *):not(.not-prose) { font-family: var(--font-heading); font-size: 1.1rem; font-weight: 600; margin-top: 1.5rem; margin-bottom: 0.5rem; }
42
43
 
43
44
  & p:not(.dba-api *) { margin-top: 0.75rem; margin-bottom: 0.75rem; }
44
45
 
package/dist/render.d.ts CHANGED
@@ -73,6 +73,13 @@ export interface RenderContext {
73
73
  lastHeadingLevel?: number;
74
74
  /** While rendering an autodoc symbol's body: that symbol's heading level. */
75
75
  apiParentLevel?: number;
76
+ /**
77
+ * Write each block's source line (`meta.line`, from the parsers'
78
+ * `sourceLines` option) as `data-line` on its first element, and on list
79
+ * items, so a host can map the page back to the source (an editor's scroll
80
+ * sync). Off by default; only a positive integer is ever written.
81
+ */
82
+ dataLines?: boolean;
76
83
  }
77
84
  export { FOREIGN_END, FOREIGN_START };
78
85
  export { scriptUrl };
package/dist/render.js CHANGED
@@ -276,9 +276,22 @@ export function renderBlocks(nodes, ctx) {
276
276
  plannedHeadingIds.set(ctx, planHeadingIds(nodes, new Set()));
277
277
  let out = "";
278
278
  for (const n of nodes)
279
- out += block(n, ctx);
279
+ out += ctx.dataLines ? withDataLine(block(n, ctx), n) : block(n, ctx);
280
280
  return out;
281
281
  }
282
+ /** A node's source line, when it is a positive integer (ctx.dataLines). */
283
+ const sourceLine = (n) => {
284
+ const line = n.meta?.line;
285
+ return typeof line === "number" && Number.isInteger(line) && line > 0 ? line : undefined;
286
+ };
287
+ /** `data-line` on the first element of a block's HTML (none when it starts with text or a comment). */
288
+ function withDataLine(html, n) {
289
+ const line = sourceLine(n);
290
+ if (line === undefined)
291
+ return html;
292
+ // The source's line wins over a `data-line` already in raw HTML (it would send scroll sync elsewhere)
293
+ return html.replace(/^(\s*<[A-Za-z][\w:-]*)(?=[\s/>])([^>]*?)(?:\sdata-line=(?:"[^"]*"|'[^']*'|[^\s>]*))?/, `$1 data-line="${line}"$2`);
294
+ }
282
295
  const str = (v) => (v == null || v === "" ? undefined : String(v));
283
296
  /** A list item or step: the item's own inline run followed by its block children. */
284
297
  function itemBody(n, ctx) {
@@ -292,7 +305,8 @@ function listItem(n, ctx) {
292
305
  const lead = n.inline && n.inline.length ? renderInline(n.inline, ctx) : "";
293
306
  const rest = renderBlocks(n.children, ctx);
294
307
  const body = rest && lead ? `<p>${task}${lead}</p>${rest}` : task + lead + rest;
295
- return `<li${attrs(passthrough(p))}>${body}</li>`;
308
+ const line = ctx.dataLines ? sourceLine(n) : undefined;
309
+ return `<li${attrs(passthrough(p))}${line ? ` data-line="${line}"` : ""}>${body}</li>`;
296
310
  }
297
311
  function block(n, ctx) {
298
312
  const p = (n.props ?? {});
@@ -910,7 +924,7 @@ function card(n, ctx) {
910
924
  // An image path is shown as an image, as the Astro pipeline's cardIconHtml does.
911
925
  const iconHtml = !ic ? "" : /\.(svg|png|jpe?g|webp|avif)$/i.test(ic) ? `<img src="${esc(assetUrl(ic, ctx))}" alt="" class="size-5 mb-2" loading="lazy">` : namedIcon(ic, "mb-2 size-5 text-muted-foreground", ctx);
912
926
  const inner = insideLink(!!href, ctx, () => iconHtml +
913
- (title ? `<h3 class="m-0 border-0 p-0 text-base font-semibold">${renderInline(parseInline(title), ctx)}</h3>` : "") +
927
+ (title ? `<h3 class="not-prose m-0 border-0 p-0 text-base font-semibold">${renderInline(parseInline(title), ctx)}</h3>` : "") +
914
928
  (desc ? `<p class="m-0 mt-1 text-sm text-muted-foreground [text-decoration:none]">${renderInline(parseInline(desc), ctx)}</p>` : "") +
915
929
  // Body blocks in the same description wrapper as the Astro pipeline's card (a <div>: they carry their own <p>).
916
930
  (n.children?.length ? `<div class="text-sm text-muted-foreground mt-1 [text-decoration:none]">${renderBlocks(n.children, ctx)}</div>` : ""));
@@ -918,7 +932,7 @@ function card(n, ctx) {
918
932
  const toned = lookup(CARD_TONE_CLASSES, str(p.tone));
919
933
  const cls = `rounded-lg border ${toned ?? "bg-card"} p-4 text-card-foreground shadow-sm${href ? ` transition-colors ${toned ? "hover:shadow-md" : "hover:border-foreground/20"}` : ""}`;
920
934
  return href
921
- ? `<div${attrs(passthrough(p, cls))}><div class="p-0"><a href="${esc(linkUrl(href, ctx))}" class="block text-foreground [text-decoration:none]">${inner}</a></div></div>`
935
+ ? `<div${attrs(passthrough(p, cls))}><div class="p-0"><a href="${esc(linkUrl(href, ctx))}" class="not-prose block text-foreground [text-decoration:none]">${inner}</a></div></div>`
922
936
  : `<div${attrs(passthrough(p, cls))}><div class="p-0">${inner}</div></div>`;
923
937
  }
924
938
  function linkCard(n, ctx) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dogsbay/render-html",
3
- "version": "0.2.0-beta.124",
3
+ "version": "0.2.0-beta.125",
4
4
  "description": "Dogsbay MD TreeNode → HTML with the Astro components' markup and classes. No highlighter, math or CSS compiler unless you pass one; Worker- and browser-safe",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -30,9 +30,9 @@
30
30
  "dependencies": {
31
31
  "htmlparser2": "^10.1.0",
32
32
  "markdown-it": "^14.3.0",
33
- "@dogsbay/format-dogsbay-md": "0.2.0-beta.124",
34
- "@dogsbay/serialize-core": "0.2.0-beta.124",
35
- "@dogsbay/types": "0.2.0-beta.124"
33
+ "@dogsbay/format-dogsbay-md": "0.2.0-beta.125",
34
+ "@dogsbay/serialize-core": "0.2.0-beta.125",
35
+ "@dogsbay/types": "0.2.0-beta.125"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "katex": "^0.16.0"