@dogsbay/render-html 0.2.0-beta.128 → 0.2.0-beta.129
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/dist/capabilities.js +1 -1
- package/dist/render.js +46 -9
- package/package.json +4 -4
package/dist/capabilities.js
CHANGED
|
@@ -40,7 +40,7 @@ export const RENDER_HTML_CAPABILITIES = [
|
|
|
40
40
|
note: "Deliberately no embed: the page must work offline and make no request to YouTube. The Astro pipeline embeds on click.",
|
|
41
41
|
},
|
|
42
42
|
// ── Components ───────────────────────────────────────────
|
|
43
|
-
{ feature: "callout", support: "full", rendersAs: "Alert markup (12 variants, aliases mapped); collapsible → CollapsibleAlert `<details>`" },
|
|
43
|
+
{ feature: "callout", support: "full", rendersAs: "Alert markup (12 variants, aliases mapped, a custom `icon`); collapsible → CollapsibleAlert `<details>`" },
|
|
44
44
|
{ feature: "details", support: "full", rendersAs: "CollapsibleAlert `<details>`, as the Astro serializer" },
|
|
45
45
|
{ feature: "details-sheet", support: "full", rendersAs: "`<db-details-sheet>` around the CollapsibleAlert `<details>` (a right-hand native `<dialog>` when the element script runs; a collapsible block with `js: none`)" },
|
|
46
46
|
{ feature: "theme", support: "full", rendersAs: "a `<div class=\"dark …\" data-theme>` (THEME_SCOPES); an unknown name keeps the content and is reported" },
|
package/dist/render.js
CHANGED
|
@@ -753,23 +753,52 @@ function calloutLabel(type, ctx) {
|
|
|
753
753
|
}
|
|
754
754
|
function callout(n, ctx) {
|
|
755
755
|
const p = (n.props ?? {});
|
|
756
|
-
|
|
756
|
+
// `variant` first, as format-astro reads it (Starlight and MkDocs importers write it)
|
|
757
|
+
const type = (str(p.variant) ?? str(p.type) ?? "note").toLowerCase();
|
|
757
758
|
const variant = ALERT_VARIANT[VARIANT_ALIAS[type] ?? type] ? VARIANT_ALIAS[type] ?? type : "note";
|
|
758
759
|
const title = str(p.title);
|
|
759
|
-
const titleHtml = title ?
|
|
760
|
+
const titleHtml = title ? titleMarkup(title, ctx) : esc(calloutLabel(type, ctx));
|
|
760
761
|
const body = renderBlocks(n.children, ctx);
|
|
761
|
-
if (p.collapsible
|
|
762
|
-
const open = p.collapsed === false || p.collapsed === "false" || p.open
|
|
763
|
-
return collapsibleAlert(variant, titleHtml, body, open, p);
|
|
762
|
+
if (flagOn(p.collapsible) || p.collapsed != null) {
|
|
763
|
+
const open = p.collapsed === false || p.collapsed === "false" || flagOn(p.open);
|
|
764
|
+
return collapsibleAlert(variant, titleHtml, body, open, { ...p, "data-type": p["data-type"] ?? type }, ctx);
|
|
764
765
|
}
|
|
765
766
|
return (`<div${attrs({ ...passthrough(p, `${ALERT_BASE} ${ALERT_VARIANT[variant]} my-4`), "data-variant": variant, "data-type": type, role: "note" })}>` +
|
|
766
|
-
(
|
|
767
|
+
alertIcon(variant, p, ctx) +
|
|
767
768
|
`<div data-part="title" class="mb-1 text-sm font-semibold leading-none tracking-tight">${titleHtml}</div>` +
|
|
768
769
|
`<div class="text-sm [&_p]:leading-relaxed">${body}</div></div>\n`);
|
|
769
770
|
}
|
|
770
|
-
|
|
771
|
+
/** Formatting tags with no attributes, the HTML a title keeps (format-astro `TITLE_TAG`). */
|
|
772
|
+
const TITLE_TAG = /^<\/?(?:code|kbd|b|strong|i|em|s|del|mark|sub|sup|br)\s*\/?>$/i;
|
|
773
|
+
/**
|
|
774
|
+
* A callout or details title: inline Markdown, and raw HTML other than bare formatting tags as text
|
|
775
|
+
* (format-astro `titleInline`). `Map<K, V>` from an importer lost words as HTML.
|
|
776
|
+
*/
|
|
777
|
+
function titleMarkup(title, ctx) {
|
|
778
|
+
return renderInline(parseInline(title).map((n) => (n.type === "html-inline" && !TITLE_TAG.test(n.html.trim()) ? { type: "text", text: n.html } : n)), ctx);
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* A presence flag: `open`, `open=""`, `open="true"` and `true` are on; absent, `false` and `"false"` are
|
|
782
|
+
* off. format-astro reads flags with the same rule (`flagOn`); `open="false"` used to open here.
|
|
783
|
+
*/
|
|
784
|
+
function flagOn(v) {
|
|
785
|
+
return v !== undefined && v !== null && v !== false && v !== "false";
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* The icon an alert draws: the author's `icon` when it resolves (Alert.astro's rule, through the same
|
|
789
|
+
* resolver as `:icon`), else the variant's. An `icon` that resolves to nothing is reported, as `:icon` is.
|
|
790
|
+
*/
|
|
791
|
+
function alertIcon(variant, p, ctx) {
|
|
792
|
+
const name = str(p.icon);
|
|
793
|
+
const custom = name ? resolveIconMarkup(name, ctx) : undefined;
|
|
794
|
+
if (name && custom === undefined)
|
|
795
|
+
(ctx.missingIcons ??= new Set()).add(name);
|
|
796
|
+
const svg = custom ?? ALERT_ICONS[variant] ?? "";
|
|
797
|
+
return svg.startsWith("<svg") && !/^<svg[^>]*aria-hidden/.test(svg) ? svg.replace("<svg ", '<svg aria-hidden="true" focusable="false" ') : svg;
|
|
798
|
+
}
|
|
799
|
+
function collapsibleAlert(variant, titleHtml, body, open, p, ctx) {
|
|
771
800
|
const c = COLLAPSIBLE[variant] ?? COLLAPSIBLE.note;
|
|
772
|
-
const icon = (
|
|
801
|
+
const icon = alertIcon(variant, p, ctx);
|
|
773
802
|
return (`<details${attrs({ ...passthrough(p, `group/collapsible relative my-4 w-full overflow-hidden rounded-lg border [&>summary]:list-none [&>summary::-webkit-details-marker]:hidden ${c.border}`), "data-variant": variant, open })}>` +
|
|
774
803
|
`<summary class="flex cursor-pointer select-none items-center gap-2 px-4 py-3 text-sm font-semibold ${c.summary}"><span class="shrink-0 [&>svg]:size-4 ${c.icon}">${icon}</span><span class="flex-1 text-foreground" data-part="title">${titleHtml}</span><span class="shrink-0 text-muted-foreground transition-transform duration-200 group-open/collapsible:rotate-180">${ALERT_CHEVRON.replace("<svg ", '<svg aria-hidden="true" focusable="false" ')}</span></summary>` +
|
|
775
804
|
`<div class="px-4 py-3 text-sm [&_p]:leading-relaxed ${c.body}">${body}</div></details>\n`);
|
|
@@ -899,7 +928,7 @@ function details(n, ctx) {
|
|
|
899
928
|
// `variant` as documented, then `type`, as the Astro pipeline reads it.
|
|
900
929
|
const type = (str(p.variant) ?? str(p.type) ?? "").toLowerCase();
|
|
901
930
|
const variant = ALERT_VARIANT[VARIANT_ALIAS[type] ?? type] ? VARIANT_ALIAS[type] ?? type : "note";
|
|
902
|
-
const html = collapsibleAlert(variant,
|
|
931
|
+
const html = collapsibleAlert(variant, titleMarkup(title, ctx), renderBlocks(n.children, ctx), flagOn(p.open), p, ctx);
|
|
903
932
|
// `sheet`: the same details, which <db-details-sheet> opens as a side
|
|
904
933
|
// sheet when its script runs (and which stays a details when it does not)
|
|
905
934
|
if (!isDetailsSheet(p))
|
|
@@ -934,6 +963,14 @@ function insideLink(yes, ctx, fn) {
|
|
|
934
963
|
* gives, so pages match the Astro pipeline's. Without one: the built-in Lucide set. An icon neither knows is
|
|
935
964
|
* recorded for a build warning and leaves a marked, empty span.
|
|
936
965
|
*/
|
|
966
|
+
/** An icon's markup without a wrapper: the host's resolver (an SVG, or an emoji in Alert.astro's span), then the built-in set. */
|
|
967
|
+
function resolveIconMarkup(name, ctx) {
|
|
968
|
+
const resolved = ctx.icons?.(name);
|
|
969
|
+
if (resolved)
|
|
970
|
+
return resolved.startsWith("<") ? resolved : `<span aria-hidden="true" class="inline-block w-[1em] text-center leading-none">${esc(resolved)}</span>`;
|
|
971
|
+
const builtin = name.replace(/^lucide:/, "");
|
|
972
|
+
return hasIcon(builtin) ? icon(builtin) : undefined;
|
|
973
|
+
}
|
|
937
974
|
function namedIcon(name, cls, ctx) {
|
|
938
975
|
const resolved = ctx.icons?.(name);
|
|
939
976
|
if (resolved)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dogsbay/render-html",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.129",
|
|
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.
|
|
34
|
-
"@dogsbay/serialize-core": "0.2.0-beta.
|
|
35
|
-
"@dogsbay/types": "0.2.0-beta.
|
|
33
|
+
"@dogsbay/format-dogsbay-md": "0.2.0-beta.129",
|
|
34
|
+
"@dogsbay/serialize-core": "0.2.0-beta.129",
|
|
35
|
+
"@dogsbay/types": "0.2.0-beta.129"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"katex": "^0.16.0"
|