@dogsbay/render-html 0.2.0-beta.122 → 0.2.0-beta.124
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 +10 -8
- package/css/docs.css +6 -2
- package/dist/badge-variants.js +3 -1
- package/dist/capabilities.js +4 -0
- package/dist/context.d.ts +24 -0
- package/dist/context.js +25 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/render.d.ts +1 -1
- package/dist/render.js +59 -12
- package/dist/tone-classes.d.ts +8 -0
- package/dist/tone-classes.js +24 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -9,17 +9,19 @@ markup and Tailwind classes of the Dogsbay Astro components.
|
|
|
9
9
|
|
|
10
10
|
```ts
|
|
11
11
|
import { dogsbayMdToTree } from "@dogsbay/format-dogsbay-md/browser";
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
import { createRenderContext, renderBlocks } from "@dogsbay/render-html";
|
|
13
|
+
|
|
14
|
+
// One context per page. Pass only what you choose; the renderer's own state
|
|
15
|
+
// (headings, ids, …) is created for you, and UI strings default to English.
|
|
16
|
+
const ctx = createRenderContext({
|
|
17
|
+
// link: (href) => …, // default: unchanged
|
|
18
|
+
// asset: (src) => …, // default: unchanged
|
|
19
|
+
// t: (key) => myStrings[key], // empty answers fall back to UI_STRINGS
|
|
19
20
|
// highlighter: await createHighlighter(), // @dogsbay/highlight
|
|
20
21
|
// math: katexMath, // @dogsbay/render-html/math (needs katex)
|
|
21
|
-
};
|
|
22
|
+
});
|
|
22
23
|
const html = renderBlocks(dogsbayMdToTree(markdown).tree, ctx);
|
|
24
|
+
// ctx.headings: the h2/h3 for a table of contents; ctx.unknown: node types it did not know
|
|
23
25
|
```
|
|
24
26
|
|
|
25
27
|
The output uses Tailwind utility classes and the theme tokens. Styling it needs the Dogsbay
|
package/css/docs.css
CHANGED
|
@@ -51,8 +51,9 @@
|
|
|
51
51
|
looked like a colour bug in one button rather than a cascade bug in both.
|
|
52
52
|
(No backticks in this comment: the whole stylesheet is a template
|
|
53
53
|
literal.) */
|
|
54
|
-
|
|
55
|
-
& a:
|
|
54
|
+
/* Not a whole-card link (LinkCard, the video card: `a.not-prose`): its underline ran under every line of the card */
|
|
55
|
+
& a:not(.dba-api *):not([data-variant]):not(.not-prose) { color: var(--primary); text-decoration: underline; text-underline-offset: 2px; }
|
|
56
|
+
& a:hover:not(.dba-api *):not([data-variant]):not(.not-prose) { opacity: 0.8; }
|
|
56
57
|
|
|
57
58
|
& strong:not(.dba-api *) { font-weight: 600; }
|
|
58
59
|
& code:not(.dba-api *) { font-size: 0.875em; padding: 0.15em 0.35em; border-radius: 0.25rem; background: var(--muted); font-family: var(--font-code, ui-monospace, monospace); }
|
|
@@ -66,6 +67,9 @@
|
|
|
66
67
|
& ol:not(.dba-api *) { list-style: decimal; padding-left: 1.5rem; margin: 0.75rem 0; }
|
|
67
68
|
& li:not(.dba-api *) { margin: 0.25rem 0; }
|
|
68
69
|
& li > ul:not(.dba-api *), & li > ol:not(.dba-api *) { margin: 0.25rem 0; }
|
|
70
|
+
/* A file tree draws its own folder and file icons (files/markup.ts): the list rules above gave it bullets too */
|
|
71
|
+
& [data-component="files"] ul:not(.dba-api *) { list-style: none; padding-left: 0; }
|
|
72
|
+
& [data-component="files"] li > ul:not(.dba-api *) { padding-left: 1rem; }
|
|
69
73
|
|
|
70
74
|
& blockquote:not(.dba-api *) { border-left: 4px solid var(--border); padding-left: 1rem; color: var(--muted-foreground); font-style: italic; margin: 1rem 0; }
|
|
71
75
|
|
package/dist/badge-variants.js
CHANGED
|
@@ -28,6 +28,8 @@ export const BADGE_VARIANTS = {
|
|
|
28
28
|
};
|
|
29
29
|
/** The class list for a variant; an unknown variant gets the base classes only. */
|
|
30
30
|
export function badgeClass(variant) {
|
|
31
|
-
|
|
31
|
+
// Own keys only: `toString` or `__proto__` must not read Object.prototype
|
|
32
|
+
const key = variant ?? "default";
|
|
33
|
+
const v = Object.prototype.hasOwnProperty.call(BADGE_VARIANTS, key) ? BADGE_VARIANTS[key] : undefined;
|
|
32
34
|
return v ? `${BADGE_BASE} ${v}` : BADGE_BASE;
|
|
33
35
|
}
|
package/dist/capabilities.js
CHANGED
|
@@ -43,6 +43,9 @@ export const RENDER_HTML_CAPABILITIES = [
|
|
|
43
43
|
{ feature: "callout", support: "full", rendersAs: "Alert markup (12 variants, aliases mapped); 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
|
+
{ feature: "theme", support: "full", rendersAs: "a `<div class=\"dark …\" data-theme>` (THEME_SCOPES); an unknown name keeps the content and is reported" },
|
|
47
|
+
{ feature: "tabs-variant", support: "full", rendersAs: "the variant's complete list and trigger classes in place of the base ones (TAB_VARIANTS)" },
|
|
48
|
+
{ feature: "tone", support: "full", rendersAs: "each card's border and tint from the tone (CARD_TONE_CLASSES)" },
|
|
46
49
|
{
|
|
47
50
|
feature: "tabs",
|
|
48
51
|
support: "full",
|
|
@@ -57,6 +60,7 @@ export const RENDER_HTML_CAPABILITIES = [
|
|
|
57
60
|
{ feature: "cards", support: "full", rendersAs: "a responsive card grid `<div>`" },
|
|
58
61
|
{ feature: "card", support: "full", rendersAs: "Card.astro markup; icon, title, description, body; linked when `href` is set" },
|
|
59
62
|
{ feature: "link-card", support: "full", rendersAs: "LinkCard.astro markup; a body of several blocks renders as a linked Card" },
|
|
63
|
+
{ feature: "files", support: "full", rendersAs: "FileTree's markup: folder and file icons, nested" },
|
|
60
64
|
{ feature: "grid", support: "full", rendersAs: "a 1–6 column grid; grid-item spans and placeholder cells" },
|
|
61
65
|
{ feature: "link-button", support: "full", rendersAs: "an `<a>` with Button.astro's classes and variant" },
|
|
62
66
|
{ feature: "related", support: "full", rendersAs: "`<nav class=\"related-resources\">`" },
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A RenderContext with defaults. RenderContext mixes what a host chooses
|
|
3
|
+
* (links, assets, highlighter, math, icons, strings) with the renderer's own
|
|
4
|
+
* per-page state (headings, ids, seq, unknown …); hosts built it by hand,
|
|
5
|
+
* reading render.d.ts to get the internals right, and a new internal field
|
|
6
|
+
* broke every host silently. Build it here instead: pass only what you choose.
|
|
7
|
+
*/
|
|
8
|
+
import type { RenderContext } from "./render.js";
|
|
9
|
+
/** The UI strings the renderer asks for (ctx.t), in English. */
|
|
10
|
+
export declare const UI_STRINGS: Readonly<Record<string, string>>;
|
|
11
|
+
/** The per-page state the renderer keeps in the context: always fresh, never passed. */
|
|
12
|
+
type Internal = "headings" | "ids" | "unknown" | "seq" | "t" | "fnRefs" | "usedMath" | "inLink" | "lastHeadingLevel" | "apiParentLevel" | "missingIcons" | "unsafeUrls";
|
|
13
|
+
export type RenderContextOptions = Partial<Omit<RenderContext, Internal>> & {
|
|
14
|
+
/**
|
|
15
|
+
* Localised UI strings. An empty or missing answer falls back to English
|
|
16
|
+
* (UI_STRINGS), then to the renderer's own label (callout titles).
|
|
17
|
+
*/
|
|
18
|
+
t?: (key: string) => string | undefined;
|
|
19
|
+
/** Ids the page already uses outside the rendered content (a skip link's target): generated ids avoid them. */
|
|
20
|
+
reservedIds?: Iterable<string>;
|
|
21
|
+
};
|
|
22
|
+
/** A fresh context for one page: identity links and assets, English strings, empty state. */
|
|
23
|
+
export declare function createRenderContext(options?: RenderContextOptions): RenderContext;
|
|
24
|
+
export {};
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** The UI strings the renderer asks for (ctx.t), in English. */
|
|
2
|
+
export const UI_STRINGS = {
|
|
3
|
+
backToContent: "Back to content",
|
|
4
|
+
close: "Close",
|
|
5
|
+
copyCode: "Copy code",
|
|
6
|
+
details: "Details",
|
|
7
|
+
related: "Related links",
|
|
8
|
+
};
|
|
9
|
+
/** A fresh context for one page: identity links and assets, English strings, empty state. */
|
|
10
|
+
export function createRenderContext(options = {}) {
|
|
11
|
+
const { t, reservedIds, ...chosen } = options;
|
|
12
|
+
return {
|
|
13
|
+
...chosen,
|
|
14
|
+
// An explicit `link: undefined` must not remove the default
|
|
15
|
+
link: chosen.link ?? ((href) => href),
|
|
16
|
+
asset: chosen.asset ?? ((src) => src),
|
|
17
|
+
headings: [],
|
|
18
|
+
ids: new Set(reservedIds ?? []),
|
|
19
|
+
unknown: new Set(),
|
|
20
|
+
missingIcons: new Set(),
|
|
21
|
+
unsafeUrls: new Set(),
|
|
22
|
+
seq: 0,
|
|
23
|
+
t: (key) => t?.(key) || UI_STRINGS[key] || "",
|
|
24
|
+
};
|
|
25
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* imports neither, so it carries no Shiki, KaTeX or Node built-ins.
|
|
7
7
|
*/
|
|
8
8
|
export * from "./render.js";
|
|
9
|
+
export { createRenderContext, UI_STRINGS, type RenderContextOptions } from "./context.js";
|
|
9
10
|
export * from "./lines.js";
|
|
10
11
|
export { esc, attrs, passthrough, slugify, unesc } from "./html.js";
|
|
11
12
|
export { icon, hasIcon, CALLOUTS } from "./icons.js";
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* imports neither, so it carries no Shiki, KaTeX or Node built-ins.
|
|
7
7
|
*/
|
|
8
8
|
export * from "./render.js";
|
|
9
|
+
export { createRenderContext, UI_STRINGS } from "./context.js";
|
|
9
10
|
export * from "./lines.js";
|
|
10
11
|
export { esc, attrs, passthrough, slugify, unesc } from "./html.js";
|
|
11
12
|
export { icon, hasIcon, CALLOUTS } from "./icons.js";
|
package/dist/render.d.ts
CHANGED
|
@@ -136,4 +136,4 @@ export interface TabItem {
|
|
|
136
136
|
body: () => string;
|
|
137
137
|
}
|
|
138
138
|
/** A `db-tabs` group with Tabs.astro's markup, the first tab selected. */
|
|
139
|
-
export declare function tabGroup(items: TabItem[], ctx: RenderContext, rootAttrs: Record<string, unknown>, syncKey?: string): string;
|
|
139
|
+
export declare function tabGroup(items: TabItem[], ctx: RenderContext, rootAttrs: Record<string, unknown>, syncKey?: string, variant?: string): string;
|
package/dist/render.js
CHANGED
|
@@ -10,7 +10,7 @@ import { attrs, esc, FOREIGN_END, FOREIGN_START, passthrough, slugify, unesc } f
|
|
|
10
10
|
import { CALLOUTS, hasIcon, icon } from "./icons.js";
|
|
11
11
|
import { badgeClass } from "./badge-variants.js";
|
|
12
12
|
import { KBD_CLASS } from "./kbd-variants.js";
|
|
13
|
-
import { TONE_CLASSES } from "./tone-classes.js";
|
|
13
|
+
import { CARD_TONE_CLASSES, TAB_VARIANTS, THEME_SCOPES, TONE_CLASSES, lookup } from "./tone-classes.js";
|
|
14
14
|
import { fileTreeHtml } from "./file-tree.js";
|
|
15
15
|
import { stripMarkersForCopy } from "./code-markers.js";
|
|
16
16
|
import { icons as ALERT_ICONS, chevronSvg as ALERT_CHEVRON } from "./alert-icons.js";
|
|
@@ -101,7 +101,7 @@ function inline(n, ctx) {
|
|
|
101
101
|
const variant = n.button ? buttonVariant(n.button.variant) : undefined;
|
|
102
102
|
const a = {
|
|
103
103
|
// Button.astro's small size, whose text-xs replaces the base text-sm (tailwind-merge in tv()).
|
|
104
|
-
...passthrough(n.attrs, variant ? `${BUTTON_BASE.split(" ").filter((c) => c !== "text-sm").join(" ")} ${BUTTON_VARIANTS
|
|
104
|
+
...passthrough(n.attrs, variant ? `${BUTTON_BASE.split(" ").filter((c) => c !== "text-sm").join(" ")} ${lookup(BUTTON_VARIANTS, variant) ?? BUTTON_VARIANTS.default} h-9 px-3 text-xs no-underline` : undefined),
|
|
105
105
|
href,
|
|
106
106
|
title: n.title,
|
|
107
107
|
"data-variant": variant,
|
|
@@ -253,9 +253,27 @@ function proseHeadings(html, ctx) {
|
|
|
253
253
|
});
|
|
254
254
|
}
|
|
255
255
|
// ─── Blocks ──────────────────────────────────────────────────────────
|
|
256
|
+
/**
|
|
257
|
+
* The heading ids a page will use, collected on the first (outermost)
|
|
258
|
+
* renderBlocks call for a context: a side sheet's title id must yield to a
|
|
259
|
+
* heading of the same name even when the sheet comes first, or the heading's
|
|
260
|
+
* TOC entry and links would open the sheet instead (#101).
|
|
261
|
+
*/
|
|
262
|
+
const plannedHeadingIds = new WeakMap();
|
|
263
|
+
function planHeadingIds(nodes, out) {
|
|
264
|
+
for (const n of nodes) {
|
|
265
|
+
if (n.type === "heading")
|
|
266
|
+
out.add(str(n.props?.id) ?? str(n.props?.slug) ?? slugify(inlineText(n.inline)));
|
|
267
|
+
if (n.children)
|
|
268
|
+
planHeadingIds(n.children, out);
|
|
269
|
+
}
|
|
270
|
+
return out;
|
|
271
|
+
}
|
|
256
272
|
export function renderBlocks(nodes, ctx) {
|
|
257
273
|
if (!nodes)
|
|
258
274
|
return "";
|
|
275
|
+
if (!plannedHeadingIds.has(ctx))
|
|
276
|
+
plannedHeadingIds.set(ctx, planHeadingIds(nodes, new Set()));
|
|
259
277
|
let out = "";
|
|
260
278
|
for (const n of nodes)
|
|
261
279
|
out += block(n, ctx);
|
|
@@ -389,8 +407,21 @@ function block(n, ctx) {
|
|
|
389
407
|
return avatar(p, ctx);
|
|
390
408
|
case "example":
|
|
391
409
|
return example(n, ctx);
|
|
392
|
-
case "cards":
|
|
393
|
-
|
|
410
|
+
case "cards": {
|
|
411
|
+
// A group tone applies to every card that has none of its own
|
|
412
|
+
const tone = str(p.tone);
|
|
413
|
+
const kids = tone ? (n.children ?? []).map((c) => (c.type === "card" && !c.props?.tone ? { ...c, props: { ...c.props, tone } } : c)) : n.children;
|
|
414
|
+
return `<div${attrs(passthrough(p, "not-prose my-4 grid grid-cols-1 gap-4 sm:grid-cols-2"))}>${renderBlocks(kids, ctx)}</div>\n`;
|
|
415
|
+
}
|
|
416
|
+
// A scoped theme: `.dark` re-declares the tokens for its subtree (THEME_SCOPES)
|
|
417
|
+
case "theme": {
|
|
418
|
+
const scope = lookup(THEME_SCOPES, str(p.name));
|
|
419
|
+
if (!scope) {
|
|
420
|
+
ctx.unknown.add(`theme "${str(p.name) ?? ""}"`);
|
|
421
|
+
return renderBlocks(n.children, ctx);
|
|
422
|
+
}
|
|
423
|
+
return `<div${attrs({ ...passthrough(p, scope), "data-theme": str(p.name) })}>${renderBlocks(n.children, ctx)}</div>\n`;
|
|
424
|
+
}
|
|
394
425
|
case "card":
|
|
395
426
|
return card(n, ctx);
|
|
396
427
|
case "link-card":
|
|
@@ -406,7 +437,7 @@ function block(n, ctx) {
|
|
|
406
437
|
// `label` draws a placeholder cell in the `tone` colours, as GridItem does in the Astro pipeline.
|
|
407
438
|
const label = str(p.label);
|
|
408
439
|
const inner = label !== undefined
|
|
409
|
-
? `<div class="flex h-full min-h-16 items-center justify-center rounded-lg font-mono text-sm font-medium ${TONE_CLASSES
|
|
440
|
+
? `<div class="flex h-full min-h-16 items-center justify-center rounded-lg font-mono text-sm font-medium ${lookup(TONE_CLASSES, str(p.tone)) ?? TONE_CLASSES["primary-light"]}">${esc(label)}</div>`
|
|
410
441
|
: renderBlocks(n.children, ctx);
|
|
411
442
|
const { label: _l, tone: _t, rowSpan: _r, ...rest } = p;
|
|
412
443
|
const pass = passthrough(rest);
|
|
@@ -430,7 +461,7 @@ function block(n, ctx) {
|
|
|
430
461
|
const href = str(p.href);
|
|
431
462
|
const variant = buttonVariant(str(p.variant));
|
|
432
463
|
const label = insideLink(true, ctx, () => (n.children?.length === 1 && n.children[0].type === "paragraph" ? renderInline(n.children[0].inline, ctx) : renderBlocks(n.children, ctx)));
|
|
433
|
-
return `<p><a${attrs({ ...passthrough(p, `${BUTTON_BASE} ${BUTTON_VARIANTS
|
|
464
|
+
return `<p><a${attrs({ ...passthrough(p, `${BUTTON_BASE} ${lookup(BUTTON_VARIANTS, variant) ?? BUTTON_VARIANTS.default} h-10 px-4 py-2 no-underline`), "data-variant": variant, "data-size": "default", href: href ? linkUrl(href, ctx) : undefined })}>${label}</a></p>\n`;
|
|
434
465
|
}
|
|
435
466
|
case "youtube": {
|
|
436
467
|
// A card linking to the video, not an embed: the page must work offline, with no request to YouTube
|
|
@@ -752,10 +783,12 @@ function tabs(n, ctx) {
|
|
|
752
783
|
return { value: v, label: esc(str(k.props?.label) ?? `Tab ${i + 1}`), body: () => renderBlocks(k.children, ctx) };
|
|
753
784
|
});
|
|
754
785
|
const syncKey = str(p.syncKey) ?? str(p["sync-key"]) ?? str(p.group);
|
|
755
|
-
return tabGroup(items, ctx, passthrough(p, "my-4 w-full overflow-hidden rounded-lg border border-border"), syncKey);
|
|
786
|
+
return tabGroup(items, ctx, passthrough(p, "my-4 w-full overflow-hidden rounded-lg border border-border"), syncKey, str(p.variant));
|
|
756
787
|
}
|
|
757
788
|
/** A `db-tabs` group with Tabs.astro's markup, the first tab selected. */
|
|
758
|
-
export function tabGroup(items, ctx, rootAttrs, syncKey) {
|
|
789
|
+
export function tabGroup(items, ctx, rootAttrs, syncKey, variant) {
|
|
790
|
+
// A variant's class strings are complete: they replace the base ones (TAB_VARIANTS)
|
|
791
|
+
const v = lookup(TAB_VARIANTS, variant);
|
|
759
792
|
const group = ++ctx.seq;
|
|
760
793
|
let list = "";
|
|
761
794
|
let panels = "";
|
|
@@ -763,10 +796,10 @@ export function tabGroup(items, ctx, rootAttrs, syncKey) {
|
|
|
763
796
|
const tid = `tab-${group}-${i + 1}`;
|
|
764
797
|
const pid = `tabpanel-${group}-${i + 1}`;
|
|
765
798
|
const sel = i === 0;
|
|
766
|
-
list += `<button data-part="trigger" data-value="${esc(it.value)}" role="tab" type="button" id="${tid}" aria-controls="${pid}" aria-selected="${sel}" tabindex="${sel ? 0 : -1}"${sel ? ' data-state="active"' : ""} class="${TAB_TRIGGER}">${it.label}</button>`;
|
|
799
|
+
list += `<button data-part="trigger" data-value="${esc(it.value)}" role="tab" type="button" id="${tid}" aria-controls="${pid}" aria-selected="${sel}" tabindex="${sel ? 0 : -1}"${sel ? ' data-state="active"' : ""} class="${v?.trigger ?? TAB_TRIGGER}">${it.label}</button>`;
|
|
767
800
|
panels += `<div data-part="content" data-value="${esc(it.value)}" role="tabpanel" id="${pid}" aria-labelledby="${tid}" tabindex="0"${sel ? ' data-state="active"' : " hidden"} class="${TAB_CONTENT}">${it.body()}</div>`;
|
|
768
801
|
});
|
|
769
|
-
return `<db-tabs${attrs({ ...rootAttrs, "default-value": items[0]?.value, "sync-key": syncKey })}><div data-part="list" role="tablist" aria-orientation="horizontal" class="flex items-center border-b border-border bg-muted p-1 text-muted-foreground">${list}</div>${panels}</db-tabs>\n`;
|
|
802
|
+
return `<db-tabs${attrs({ ...rootAttrs, "default-value": items[0]?.value, "sync-key": syncKey })}><div data-part="list" role="tablist" aria-orientation="horizontal" class="${v?.list ?? "flex items-center border-b border-border bg-muted p-1 text-muted-foreground"}">${list}</div>${panels}</db-tabs>\n`;
|
|
770
803
|
}
|
|
771
804
|
// ─── Accordion (Accordion*.astro + db-accordion) ────────────────────
|
|
772
805
|
function accordion(n, ctx) {
|
|
@@ -794,7 +827,19 @@ function accordion(n, ctx) {
|
|
|
794
827
|
}
|
|
795
828
|
// `:::details` renders as a CollapsibleAlert, as in the Astro serializer.
|
|
796
829
|
function details(n, ctx) {
|
|
797
|
-
|
|
830
|
+
let p = (n.props ?? {});
|
|
831
|
+
// A side sheet with no id takes one from its title, so a link written as for
|
|
832
|
+
// a heading opens it (#101; extractHeadings does the same for a site build)
|
|
833
|
+
if (isDetailsSheet(p) && !str(p.id)) {
|
|
834
|
+
const base = slugify(str(p.title) ?? str(p.summary) ?? "");
|
|
835
|
+
if (base) {
|
|
836
|
+
const planned = plannedHeadingIds.get(ctx);
|
|
837
|
+
let id = base;
|
|
838
|
+
for (let k = 2; planned?.has(id) || ctx.ids.has(id); k++)
|
|
839
|
+
id = `${base}-${k}`;
|
|
840
|
+
p = { ...p, id: uniqueId(ctx, id) };
|
|
841
|
+
}
|
|
842
|
+
}
|
|
798
843
|
const title = str(p.title) ?? str(p.summary) ?? ctx.t("details");
|
|
799
844
|
// `variant` as documented, then `type`, as the Astro pipeline reads it.
|
|
800
845
|
const type = (str(p.variant) ?? str(p.type) ?? "").toLowerCase();
|
|
@@ -869,7 +914,9 @@ function card(n, ctx) {
|
|
|
869
914
|
(desc ? `<p class="m-0 mt-1 text-sm text-muted-foreground [text-decoration:none]">${renderInline(parseInline(desc), ctx)}</p>` : "") +
|
|
870
915
|
// Body blocks in the same description wrapper as the Astro pipeline's card (a <div>: they carry their own <p>).
|
|
871
916
|
(n.children?.length ? `<div class="text-sm text-muted-foreground mt-1 [text-decoration:none]">${renderBlocks(n.children, ctx)}</div>` : ""));
|
|
872
|
-
|
|
917
|
+
// A tone (TONES) replaces the card's background and border colour
|
|
918
|
+
const toned = lookup(CARD_TONE_CLASSES, str(p.tone));
|
|
919
|
+
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"}` : ""}`;
|
|
873
920
|
return href
|
|
874
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>`
|
|
875
922
|
: `<div${attrs(passthrough(p, cls))}><div class="p-0">${inner}</div></div>`;
|
package/dist/tone-classes.d.ts
CHANGED
|
@@ -1 +1,9 @@
|
|
|
1
1
|
export declare const TONE_CLASSES: Record<string, string>;
|
|
2
|
+
export declare const TONES: readonly ["info", "success", "warning", "danger"];
|
|
3
|
+
export declare const CARD_TONE_CLASSES: Record<string, string>;
|
|
4
|
+
export declare const TAB_VARIANTS: Record<string, {
|
|
5
|
+
list: string;
|
|
6
|
+
trigger: string;
|
|
7
|
+
}>;
|
|
8
|
+
export declare const THEME_SCOPES: Record<string, string>;
|
|
9
|
+
export declare function lookup<T>(table: Record<string, T>, key: unknown): T | undefined;
|
package/dist/tone-classes.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Generated by packages/render-html/scripts/sync-sources.mjs from @dogsbay/format-astro
|
|
1
|
+
// Generated by packages/render-html/scripts/sync-sources.mjs from @dogsbay/format-astro tone-classes.ts. Do not edit.
|
|
2
2
|
export const TONE_CLASSES = {
|
|
3
3
|
"primary": "bg-primary text-primary-foreground",
|
|
4
4
|
"primary-strong": "bg-primary/80 text-primary-foreground",
|
|
@@ -13,3 +13,26 @@ export const TONE_CLASSES = {
|
|
|
13
13
|
"purple": "bg-purple-500/15 text-purple-700 dark:text-purple-300",
|
|
14
14
|
"red": "bg-red-500/15 text-red-700 dark:text-red-300"
|
|
15
15
|
};
|
|
16
|
+
export const TONES = ["info", "success", "warning", "danger"];
|
|
17
|
+
export const CARD_TONE_CLASSES = {
|
|
18
|
+
"info": "border-info/50 bg-info/5",
|
|
19
|
+
"success": "border-success/50 bg-success/5",
|
|
20
|
+
"warning": "border-warning/50 bg-warning/5",
|
|
21
|
+
"danger": "border-danger/50 bg-danger/5"
|
|
22
|
+
};
|
|
23
|
+
export const TAB_VARIANTS = {
|
|
24
|
+
"pills": {
|
|
25
|
+
"list": "flex flex-wrap items-center gap-2 border-b-0 border-transparent bg-transparent p-0 px-3 pt-3 pb-1 text-muted-foreground",
|
|
26
|
+
"trigger": "inline-flex items-center justify-center whitespace-nowrap rounded-full border border-border px-4 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 aria-selected:border-primary aria-selected:bg-primary aria-selected:text-primary-foreground aria-selected:shadow-none"
|
|
27
|
+
},
|
|
28
|
+
"underline": {
|
|
29
|
+
"list": "flex items-center gap-6 border-b border-border bg-transparent p-0 px-4 text-muted-foreground",
|
|
30
|
+
"trigger": "-mb-px inline-flex items-center justify-center whitespace-nowrap rounded-none border-b-2 border-transparent p-0 px-0.5 pt-3 pb-2 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 hover:text-foreground aria-selected:border-primary aria-selected:bg-transparent aria-selected:text-foreground aria-selected:shadow-none"
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
export const THEME_SCOPES = {
|
|
34
|
+
"dark": "dark my-6 rounded-xl border border-border bg-background p-6 text-foreground [&>:first-child]:mt-0! [&>:last-child]:mb-0!"
|
|
35
|
+
};
|
|
36
|
+
export function lookup(table, key) {
|
|
37
|
+
return typeof key === "string" && Object.prototype.hasOwnProperty.call(table, key) ? table[key] : undefined;
|
|
38
|
+
}
|
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.124",
|
|
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.124",
|
|
34
|
+
"@dogsbay/serialize-core": "0.2.0-beta.124",
|
|
35
|
+
"@dogsbay/types": "0.2.0-beta.124"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"katex": "^0.16.0"
|