@dogsbay/format-astro 0.2.0-beta.152 → 0.2.0-beta.154
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/granularity.js +1 -1
- package/dist/project.d.ts +29 -0
- package/dist/project.js +80 -8
- package/dist/serialize.js +27 -6
- package/dist/theme-css.js +41 -38
- package/package.json +8 -8
package/dist/granularity.js
CHANGED
|
@@ -113,7 +113,7 @@ export function openAsPageNode(slug, hrefPrefix) {
|
|
|
113
113
|
const route = hrefPrefix ? `${hrefPrefix}/${slug}` : `/${slug}`;
|
|
114
114
|
return {
|
|
115
115
|
type: "paragraph",
|
|
116
|
-
props: { class: "dsb-open-as-page" },
|
|
116
|
+
props: { class: "dsb-open-as-page not-prose" },
|
|
117
117
|
children: [{ type: "prose", inline: [{ type: "link", href: route, children: [{ type: "text", text: "Open as page ↗" }] }] }],
|
|
118
118
|
};
|
|
119
119
|
}
|
package/dist/project.d.ts
CHANGED
|
@@ -102,6 +102,7 @@ export interface AstroProjectOptions {
|
|
|
102
102
|
into?: string;
|
|
103
103
|
mediaOnly?: boolean;
|
|
104
104
|
root?: string;
|
|
105
|
+
skip?: string[];
|
|
105
106
|
}[];
|
|
106
107
|
/**
|
|
107
108
|
* Per-source asset directories with the URL prefix their content refs
|
|
@@ -168,6 +169,12 @@ export interface AstroProjectOptions {
|
|
|
168
169
|
* See plans/search-facets.md.
|
|
169
170
|
*/
|
|
170
171
|
taxonomyDisplay?: import("@dogsbay/types").SiteConfig["taxonomyDisplay"];
|
|
172
|
+
/**
|
|
173
|
+
* Declared taxonomies with `hierarchical: true`: a page's nested value in one
|
|
174
|
+
* of them is a search facet at every level (`ai/llm` → `ai`, `ai/llm`), as
|
|
175
|
+
* `pageFacets` (@dogsbay/site-files) maps it for DocsLayout and dogsbay-ssg.
|
|
176
|
+
*/
|
|
177
|
+
hierarchicalTaxonomies?: string[];
|
|
171
178
|
/** Canonical site URL — required for sitemap.xml + canonical tags */
|
|
172
179
|
siteUrl?: string;
|
|
173
180
|
/** Site-wide description used as default <meta name="description"> */
|
|
@@ -685,7 +692,29 @@ export interface SwitcherMap {
|
|
|
685
692
|
label: string;
|
|
686
693
|
home: string;
|
|
687
694
|
}>>;
|
|
695
|
+
/**
|
|
696
|
+
* The version banner each unit's pages carry, keyed by site-files `unitKeyString` (product,
|
|
697
|
+
* locale, version; just the version for site-wide versions): the version's own text, or a kind
|
|
698
|
+
* the layout words (older, no longer maintained, not released), and the default version it points
|
|
699
|
+
* to with its home URL (where the link goes when the page is not in that version). As dogsbay-ssg:
|
|
700
|
+
* with a `products:` block from site-files `unitOf`; site-wide, an older version only. Left out
|
|
701
|
+
* when no page has one.
|
|
702
|
+
*/
|
|
703
|
+
banners?: Record<string, VersionBannerData>;
|
|
688
704
|
}
|
|
705
|
+
/** A unit's version banner in switcherMap.json (site-files `UnitBanner`, `latest.home` as served). */
|
|
706
|
+
export type VersionBannerData = ({
|
|
707
|
+
kind: "custom";
|
|
708
|
+
text: string;
|
|
709
|
+
} | {
|
|
710
|
+
kind: "eol" | "prerelease" | "old";
|
|
711
|
+
}) & {
|
|
712
|
+
latest?: {
|
|
713
|
+
id: string;
|
|
714
|
+
label: string;
|
|
715
|
+
home: string;
|
|
716
|
+
};
|
|
717
|
+
};
|
|
689
718
|
export interface SwitcherVariant {
|
|
690
719
|
/** Present when the locale axis is active for this site. */
|
|
691
720
|
locale?: string;
|
package/dist/project.js
CHANGED
|
@@ -26,10 +26,10 @@ function pageLiteral(value, indent) {
|
|
|
26
26
|
}
|
|
27
27
|
import { emitPluginRuntime } from "./plugins.js";
|
|
28
28
|
import { buildLlmsTxt, buildSectionLlmsTxt, buildLlmsFullTxt } from "./llms-txt.js";
|
|
29
|
-
import { buildSitemapFiles, buildSitemapIndex } from "./sitemap.js";
|
|
29
|
+
import { buildSitemapFiles, buildSitemapIndex, pageHref } from "./sitemap.js";
|
|
30
30
|
import { canonicalPlan } from "./canonical.js";
|
|
31
31
|
import { headMetadata } from "./head-metadata.js";
|
|
32
|
-
import { buildHeadersFile, buildRobotsTxt as buildRobotsTxtFile } from "@dogsbay/site-files";
|
|
32
|
+
import { buildHeadersFile, buildRobotsTxt as buildRobotsTxtFile, pageFacets } from "@dogsbay/site-files";
|
|
33
33
|
import { mergeHeaders, mergeRedirects, productRootRedirects, siteFiles, siteRootTarget, switcher as productSwitcherRows, productSwitcher, isVersionMenuHref, versionMenuHref, toPagesScript, toScript, unitKeyString, unitManifest, unitOf, } from "@dogsbay/site-files";
|
|
34
34
|
import { assembleBook, estimateTreeBytes } from "./granularity.js";
|
|
35
35
|
/**
|
|
@@ -41,7 +41,7 @@ function singlePageLinkLines(href, indent) {
|
|
|
41
41
|
if (!href)
|
|
42
42
|
return [];
|
|
43
43
|
const pad = " ".repeat(indent);
|
|
44
|
-
return [`${pad}<a class="dsb-read-single-page" href="${href}">Read as single page →</a>`];
|
|
44
|
+
return [`${pad}<a class="dsb-read-single-page not-prose" href="${href}">Read as single page →</a>`];
|
|
45
45
|
}
|
|
46
46
|
import { normalizeBasePath, basePathSegments, buildCurrentPath, withBasePath, parseSiteUrl, combinePrefix, } from "./base-path.js";
|
|
47
47
|
import { buildBlogData, blogAdjacency } from "./blog.js";
|
|
@@ -1658,10 +1658,11 @@ export async function emitAstroPages(pages, nav, outputDir, options) {
|
|
|
1658
1658
|
const dest = mount.into
|
|
1659
1659
|
? join(outputDir, "public", mount.root ?? "_assets", mount.into)
|
|
1660
1660
|
: join(outputDir, "public", mount.root ?? "_assets");
|
|
1661
|
+
const skipped = new Set((mount.skip ?? []).map((f) => join(mount.dir, f)));
|
|
1661
1662
|
if (mount.mediaOnly)
|
|
1662
|
-
copyMediaTree(mount.dir, dest);
|
|
1663
|
+
copyMediaTree(mount.dir, dest, skipped);
|
|
1663
1664
|
else
|
|
1664
|
-
cpSync(mount.dir, dest, { recursive: true });
|
|
1665
|
+
cpSync(mount.dir, dest, { recursive: true, filter: (src) => !skipped.has(src) });
|
|
1665
1666
|
}
|
|
1666
1667
|
}
|
|
1667
1668
|
let generated = 0;
|
|
@@ -1933,6 +1934,14 @@ export async function emitAstroPages(pages, nav, outputDir, options) {
|
|
|
1933
1934
|
const pageTaxonomies = pageMeta?.taxonomies && Object.keys(pageMeta.taxonomies).length > 0
|
|
1934
1935
|
? pageMeta.taxonomies
|
|
1935
1936
|
: undefined;
|
|
1937
|
+
// The page's search facets, by the mapping dogsbay-ssg's index uses
|
|
1938
|
+
// too; DocsLayout writes them as Pagefind filters. Computed here
|
|
1939
|
+
// because only the build knows which taxonomies are hierarchical.
|
|
1940
|
+
const pageFacetValues = pageFacets(pageMeta, {
|
|
1941
|
+
tagPrefixes: options.tagPrefixes,
|
|
1942
|
+
hierarchical: options.hierarchicalTaxonomies,
|
|
1943
|
+
unit: { product: page.multiSource?.namespace, version: page.multiSource?.version },
|
|
1944
|
+
});
|
|
1936
1945
|
// `tagsIndexPath` flows to `<TagList>` for chip hrefs
|
|
1937
1946
|
// (`${indexPath}/${tag}/`). Caller passes the raw config value
|
|
1938
1947
|
// (e.g. `/tags`); we bake the COMBINED prefix (urlBase from
|
|
@@ -2095,6 +2104,7 @@ export async function emitAstroPages(pages, nav, outputDir, options) {
|
|
|
2095
2104
|
`const pageAudience = ${pageLiteral(pageAudience ?? null)};`,
|
|
2096
2105
|
`const pageCategory = ${pageLiteral(pageCategory ?? null)};`,
|
|
2097
2106
|
`const pageTaxonomies = ${pageLiteral(pageTaxonomies ?? null)};`,
|
|
2107
|
+
`const pageFacets = ${pageLiteral(pageFacetValues)};`,
|
|
2098
2108
|
`const tagsIndexPath = ${pageLiteral(tagsIndexPath)};`,
|
|
2099
2109
|
`const lastUpdated = ${pageLiteral(pageLastUpdated)} || undefined;`,
|
|
2100
2110
|
`const llmActionsProps = ${pageLiteral(llmActionsEnabled
|
|
@@ -2150,6 +2160,7 @@ export async function emitAstroPages(pages, nav, outputDir, options) {
|
|
|
2150
2160
|
` category={pageCategory ?? undefined}`,
|
|
2151
2161
|
` lastUpdated={lastUpdated}`,
|
|
2152
2162
|
` taxonomies={pageTaxonomies ?? undefined}`,
|
|
2163
|
+
` facets={pageFacets}`,
|
|
2153
2164
|
` autoH1={${autoH1}}`,
|
|
2154
2165
|
` autoLede={${autoLede}}`,
|
|
2155
2166
|
` llmActions={llmActionsProps}`,
|
|
@@ -2636,13 +2647,17 @@ export function emitSwitcherMap(pages, outputDir, options) {
|
|
|
2636
2647
|
const variant = {
|
|
2637
2648
|
...(ms.locale !== undefined ? { locale: ms.locale } : {}),
|
|
2638
2649
|
...(ms.version !== undefined ? { version: ms.version } : {}),
|
|
2639
|
-
|
|
2650
|
+
// As the site serves the page (a folder, with the slash): a raw slug cost each menu link a redirect
|
|
2651
|
+
url: pageHref(page.slug, combined),
|
|
2640
2652
|
};
|
|
2641
2653
|
if (!byLogicalKey[key])
|
|
2642
2654
|
byLogicalKey[key] = [];
|
|
2643
2655
|
byLogicalKey[key].push(variant);
|
|
2644
2656
|
}
|
|
2645
2657
|
const data = { versions, locales, byLogicalKey };
|
|
2658
|
+
const banners = versionBanners(options, combined, pages);
|
|
2659
|
+
if (banners)
|
|
2660
|
+
data.banners = banners;
|
|
2646
2661
|
const menus = versionMenus(options, combined);
|
|
2647
2662
|
if (menus)
|
|
2648
2663
|
data.versionMenu = menus;
|
|
@@ -2656,6 +2671,61 @@ export function emitSwitcherMap(pages, outputDir, options) {
|
|
|
2656
2671
|
mkdirSync(dataDir, { recursive: true });
|
|
2657
2672
|
writeFileSync(outPath, JSON.stringify(data, null, 2));
|
|
2658
2673
|
}
|
|
2674
|
+
/**
|
|
2675
|
+
* Each unit's version banner, as dogsbay-ssg shows it: with a `products:` block the shared model's
|
|
2676
|
+
* (site-files `unitOf`), none on a hidden version (the number a `latest` alias serves); site-wide,
|
|
2677
|
+
* an older version's, pointing at the default version's unit with the same source and language
|
|
2678
|
+
* (its home read from that unit's pages), and none when the default version has no such unit.
|
|
2679
|
+
*/
|
|
2680
|
+
function versionBanners(options, combined, pages) {
|
|
2681
|
+
const out = {};
|
|
2682
|
+
if (options.products) {
|
|
2683
|
+
const model = options.products;
|
|
2684
|
+
for (const u of model.units) {
|
|
2685
|
+
if (u.version === undefined)
|
|
2686
|
+
continue;
|
|
2687
|
+
const product = model.products.find((p) => p.name === u.product);
|
|
2688
|
+
if (product?.versions.find((v) => v.id === u.version)?.hidden)
|
|
2689
|
+
continue;
|
|
2690
|
+
const b = unitOf(model, u).banner;
|
|
2691
|
+
if (b)
|
|
2692
|
+
out[unitKeyString(u)] = b.latest ? { ...b, latest: { ...b.latest, home: `${combined}${b.latest.home}` } } : b;
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
else if (options.defaultVersion) {
|
|
2696
|
+
const def = options.versions?.find((v) => v.id === options.defaultVersion);
|
|
2697
|
+
if (!def)
|
|
2698
|
+
return undefined;
|
|
2699
|
+
const hidden = new Set((options.versions ?? []).filter((v) => v.hidden).map((v) => v.id));
|
|
2700
|
+
// Each unit (source, language, version) and the URL prefix its pages are served under
|
|
2701
|
+
const home = new Map();
|
|
2702
|
+
const units = new Map();
|
|
2703
|
+
const bare = (x) => x.replace(/^\/+|\/+$/g, "").replace(/(^|\/)index$/, "");
|
|
2704
|
+
for (const page of pages) {
|
|
2705
|
+
const ms = page.multiSource;
|
|
2706
|
+
if (!ms?.version || page.redirect)
|
|
2707
|
+
continue;
|
|
2708
|
+
const u = { ...(ms.namespace ? { product: ms.namespace } : {}), ...(ms.locale ? { locale: ms.locale } : {}), version: ms.version };
|
|
2709
|
+
const key = unitKeyString(u);
|
|
2710
|
+
units.set(key, u);
|
|
2711
|
+
if (home.has(key))
|
|
2712
|
+
continue;
|
|
2713
|
+
const slug = bare(page.slug);
|
|
2714
|
+
const own = bare(ms.originalSlug);
|
|
2715
|
+
const prefix = own === "" ? slug : slug.endsWith(`/${own}`) ? slug.slice(0, -own.length - 1) : slug === own ? "" : undefined;
|
|
2716
|
+
if (prefix !== undefined)
|
|
2717
|
+
home.set(key, `${combined}/${prefix ? `${prefix}/` : ""}`);
|
|
2718
|
+
}
|
|
2719
|
+
for (const [key, u] of units) {
|
|
2720
|
+
if (u.version === def.id || hidden.has(u.version))
|
|
2721
|
+
continue;
|
|
2722
|
+
const target = home.get(unitKeyString({ ...u, version: def.id }));
|
|
2723
|
+
if (target)
|
|
2724
|
+
out[key] = { kind: "old", latest: { id: def.id, label: def.label ?? def.id, home: target } };
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
return Object.keys(out).length ? out : undefined;
|
|
2728
|
+
}
|
|
2659
2729
|
/** The product menu per locale (`<locale|_>`), or nothing with fewer than two products. */
|
|
2660
2730
|
function productMenuRows(model, combined) {
|
|
2661
2731
|
const locales = model.localeAxis ? [...new Set(model.units.map((u) => u.locale))] : [undefined];
|
|
@@ -3523,11 +3593,13 @@ const MOUNT_MEDIA_EXTS = new Set([
|
|
|
3523
3593
|
".svg", ".ico", ".pdf", ".mp4", ".webm",
|
|
3524
3594
|
]);
|
|
3525
3595
|
/** Recursively copy only media files, preserving relative structure. */
|
|
3526
|
-
function copyMediaTree(srcDir, destDir) {
|
|
3596
|
+
function copyMediaTree(srcDir, destDir, skip) {
|
|
3527
3597
|
for (const entry of readdirSync(srcDir)) {
|
|
3528
3598
|
const full = join(srcDir, entry);
|
|
3599
|
+
if (skip?.has(full))
|
|
3600
|
+
continue;
|
|
3529
3601
|
if (statSync(full).isDirectory()) {
|
|
3530
|
-
copyMediaTree(full, join(destDir, entry));
|
|
3602
|
+
copyMediaTree(full, join(destDir, entry), skip);
|
|
3531
3603
|
continue;
|
|
3532
3604
|
}
|
|
3533
3605
|
const dot = entry.lastIndexOf(".");
|
package/dist/serialize.js
CHANGED
|
@@ -308,9 +308,13 @@ function nodeToAstroBase(node, ctx) {
|
|
|
308
308
|
case "tr":
|
|
309
309
|
ctx.imports.add("table");
|
|
310
310
|
return `<TableRow>\n${childrenToAstro(node, ctx)}\n</TableRow>`;
|
|
311
|
-
case "th":
|
|
311
|
+
case "th": {
|
|
312
312
|
ctx.imports.add("table");
|
|
313
|
-
|
|
313
|
+
// An empty header cell is no header (axe `empty-table-header`): a plain cell in the header row
|
|
314
|
+
const content = leafContent(node, ctx);
|
|
315
|
+
// Written here, not through TableHead: a site keeps the component it was scaffolded with
|
|
316
|
+
return content.trim() ? `<TableHead>${content}</TableHead>` : `<td class="${EMPTY_HEADER_CELL_CLASS}"></td>`;
|
|
317
|
+
}
|
|
314
318
|
case "td":
|
|
315
319
|
ctx.imports.add("table");
|
|
316
320
|
return `<TableCell>${leafContent(node, ctx)}</TableCell>`;
|
|
@@ -640,8 +644,15 @@ function proseToAstro(node, ctx) {
|
|
|
640
644
|
return renderHtml(node.html, ctx);
|
|
641
645
|
return "";
|
|
642
646
|
}
|
|
647
|
+
/** TableHead's classes, for a header row's empty cell written as a plain `td`. */
|
|
648
|
+
const EMPTY_HEADER_CELL_CLASS = "px-2 py-2 text-left align-bottom font-semibold text-muted-foreground";
|
|
649
|
+
/** A card title's heading: one level below the last heading on the page, h6 at most. */
|
|
650
|
+
function cardTitleTag(ctx) {
|
|
651
|
+
return `h${Math.min((ctx.lastHeading ?? 1) + 1, 6)}`;
|
|
652
|
+
}
|
|
643
653
|
function headingToAstro(node, ctx) {
|
|
644
654
|
const level = node.props?.level ?? 1;
|
|
655
|
+
ctx.lastHeading = level;
|
|
645
656
|
// `id` takes precedence over `slug` (slug is auto-generated, id is explicit user override)
|
|
646
657
|
const id = node.props?.id ?? node.props?.slug ?? "";
|
|
647
658
|
const anchor = id
|
|
@@ -1347,6 +1358,7 @@ function apiSymbolToAstro(node, ctx) {
|
|
|
1347
1358
|
// By position, not by type (apiSymbolLevels): a module-level function was
|
|
1348
1359
|
// an h3 straight under the page's h1, and a class inside a class an h2.
|
|
1349
1360
|
const level = ctx.apiSymbolLevels.get(node) ?? (node.type === "api-class" ? 2 : 3);
|
|
1361
|
+
ctx.lastHeading = level;
|
|
1350
1362
|
const attrs = [
|
|
1351
1363
|
componentAttr("name", name).trimStart(),
|
|
1352
1364
|
componentAttr("kind", kind).trimStart(),
|
|
@@ -1534,9 +1546,14 @@ function cardsToAstro(node, ctx) {
|
|
|
1534
1546
|
const href = safeHref(card.props?.href);
|
|
1535
1547
|
const description = card.props?.description;
|
|
1536
1548
|
const icon = card.props?.icon;
|
|
1549
|
+
// The title's level before the body is written: a heading inside it must not move this
|
|
1550
|
+
// card's title, nor the next card's (each starts from the level before the group)
|
|
1551
|
+
const tag = cardTitleTag(ctx);
|
|
1552
|
+
const before = ctx.lastHeading;
|
|
1537
1553
|
// Description sources: inline content, children, or props.description.
|
|
1538
1554
|
// Child blocks carry their own <p>, so they get a <div>: <p><p>…</p></p> is invalid HTML.
|
|
1539
1555
|
const blocks = wholeCardLink(!!href, () => childrenToAstro(card, ctx));
|
|
1556
|
+
ctx.lastHeading = before;
|
|
1540
1557
|
const inner = blocks
|
|
1541
1558
|
|| (card.inline ? wholeCardLink(!!href, () => inlineToTemplate(card.inline)) : "")
|
|
1542
1559
|
|| (description ? escapeTemplate(description) : "");
|
|
@@ -1550,7 +1567,7 @@ function cardsToAstro(node, ctx) {
|
|
|
1550
1567
|
if (icon) {
|
|
1551
1568
|
iconHtml = cardIconHtml(icon, title, ctx);
|
|
1552
1569
|
}
|
|
1553
|
-
const titleHtml = title ?
|
|
1570
|
+
const titleHtml = title ? `<${tag} class="${CARD_TITLE_CLASS}">${escapeTemplate(title)}</${tag}>` : "";
|
|
1554
1571
|
// A description and a body are both shown (the description used to be dropped when a body existed).
|
|
1555
1572
|
const lead = blocks && description
|
|
1556
1573
|
? `<p class="text-sm text-muted-foreground mt-1 [text-decoration:none]">${escapeTemplate(description)}</p>`
|
|
@@ -1569,7 +1586,7 @@ function cardsToAstro(node, ctx) {
|
|
|
1569
1586
|
"title", "href", "description", "icon", "variant", "class", "tone",
|
|
1570
1587
|
]);
|
|
1571
1588
|
if (href) {
|
|
1572
|
-
return `<Card${cardClassAttr}${cardPass}>\n<CardContent class="p-0">\n<a href="${escapeAttr(href)}" class="
|
|
1589
|
+
return `<Card${cardClassAttr}${cardPass}>\n<CardContent class="p-0">\n<a href="${escapeAttr(href)}" data-variant="card" class="[text-decoration:none] text-foreground">\n${iconHtml}\n${titleHtml}\n${desc}\n</a>\n</CardContent>\n</Card>`;
|
|
1573
1590
|
}
|
|
1574
1591
|
return `<Card${cardClassAttr}${cardPass}>\n<CardContent class="p-0">\n${iconHtml}\n${titleHtml}\n${desc}\n</CardContent>\n</Card>`;
|
|
1575
1592
|
}).join("\n");
|
|
@@ -1601,8 +1618,12 @@ function standaloneCardToAstro(node, ctx) {
|
|
|
1601
1618
|
const href = safeHref(node.props?.href);
|
|
1602
1619
|
const description = node.props?.description;
|
|
1603
1620
|
const icon = node.props?.icon;
|
|
1621
|
+
// The title's level before the body is written (a heading inside it must not move it)
|
|
1622
|
+
const tag = cardTitleTag(ctx);
|
|
1623
|
+
const before = ctx.lastHeading;
|
|
1604
1624
|
// Child blocks carry their own <p>, so they get a <div>: <p><p>…</p></p> is invalid HTML.
|
|
1605
1625
|
const blocks = wholeCardLink(!!href, () => childrenToAstro(node, ctx));
|
|
1626
|
+
ctx.lastHeading = before;
|
|
1606
1627
|
const inner = blocks
|
|
1607
1628
|
|| (node.inline ? wholeCardLink(!!href, () => inlineToTemplate(node.inline)) : "")
|
|
1608
1629
|
|| (description ? escapeTemplate(description) : "");
|
|
@@ -1610,7 +1631,7 @@ function standaloneCardToAstro(node, ctx) {
|
|
|
1610
1631
|
// Icon renders inline before the title, a name or an image path, as in a card group
|
|
1611
1632
|
const iconHtml = icon ? cardIconHtml(icon, title, ctx) : "";
|
|
1612
1633
|
const titleHtml = title
|
|
1613
|
-
?
|
|
1634
|
+
? `<${tag} class="${CARD_TITLE_CLASS}">${escapeTemplate(title)}</${tag}>`
|
|
1614
1635
|
: "";
|
|
1615
1636
|
// A description and a body are both shown (the description used to be dropped when a body existed).
|
|
1616
1637
|
const lead = blocks && description
|
|
@@ -1629,7 +1650,7 @@ function standaloneCardToAstro(node, ctx) {
|
|
|
1629
1650
|
"title", "href", "description", "icon", "variant", "class", "tone",
|
|
1630
1651
|
]);
|
|
1631
1652
|
if (href) {
|
|
1632
|
-
return `<Card${classAttr}${passthrough}>\n<CardContent class="p-0">\n<a href="${escapeAttr(href)}" class="
|
|
1653
|
+
return `<Card${classAttr}${passthrough}>\n<CardContent class="p-0">\n<a href="${escapeAttr(href)}" data-variant="card" class="[text-decoration:none] text-foreground">\n${iconHtml}\n${titleHtml}\n${desc}\n</a>\n</CardContent>\n</Card>`;
|
|
1633
1654
|
}
|
|
1634
1655
|
return `<Card${classAttr}${passthrough}>\n<CardContent class="p-0">\n${iconHtml}\n${titleHtml}\n${desc}\n</CardContent>\n</Card>`;
|
|
1635
1656
|
}
|
package/dist/theme-css.js
CHANGED
|
@@ -90,21 +90,27 @@ export function generateGlobalCss() {
|
|
|
90
90
|
@source inline("${buildVariantSafelist()}"); /* ASTRO-ONLY: variants, tones, theme scopes */
|
|
91
91
|
|
|
92
92
|
/* Prose typography for rendered content.
|
|
93
|
-
Every element rule is guarded with :not(.dba-api *)
|
|
94
|
-
regions (ApiLayout root carries the dba-api marker) compose
|
|
95
|
-
typography from utility classes, and unguarded .docs-prose rules
|
|
96
|
-
out-specify them
|
|
97
|
-
|
|
93
|
+
Every element rule is guarded with :not(.dba-api *, .not-prose, .not-prose *).
|
|
94
|
+
API-reference regions (ApiLayout root carries the dba-api marker) compose
|
|
95
|
+
their own typography from utility classes, and unguarded .docs-prose rules
|
|
96
|
+
would out-specify them (these rules are unlayered, so they beat every
|
|
97
|
+
utility). The guard lets mixed prose+endpoint pages (MDX imports) keep full
|
|
98
|
+
typography outside the cards. .not-prose means what it means in Tailwind
|
|
99
|
+
Typography: that element and everything inside it is UI, not prose. Only
|
|
100
|
+
the heading and link rules skipped it, and only the element itself, so a
|
|
101
|
+
search page's facet list and result list inside an article got bullets,
|
|
102
|
+
indents and underlines. A wrapper around AUTHORED content (a card group, an
|
|
103
|
+
API symbol) must therefore not carry it. */
|
|
98
104
|
.docs-prose {
|
|
99
105
|
line-height: 1.7;
|
|
100
106
|
|
|
101
107
|
/* A heading that is part of a component (a card title: h3.not-prose) keeps the component look */
|
|
102
|
-
& h1:not(.dba-api
|
|
103
|
-
& h2:not(.dba-api
|
|
104
|
-
& h3:not(.dba-api
|
|
105
|
-
& h4:not(.dba-api
|
|
108
|
+
& h1:not(.dba-api *, .not-prose, .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; }
|
|
109
|
+
& h2:not(.dba-api *, .not-prose, .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; }
|
|
110
|
+
& h3:not(.dba-api *, .not-prose, .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; }
|
|
111
|
+
& h4:not(.dba-api *, .not-prose, .not-prose *) { font-family: var(--font-heading); font-size: 1.1rem; font-weight: 600; margin-top: 1.5rem; margin-bottom: 0.5rem; }
|
|
106
112
|
|
|
107
|
-
& p:not(.dba-api *) { margin-top: 0.75rem; margin-bottom: 0.75rem; }
|
|
113
|
+
& p:not(.dba-api *, .not-prose, .not-prose *) { margin-top: 0.75rem; margin-bottom: 0.75rem; }
|
|
108
114
|
|
|
109
115
|
/* The :not([data-variant]) guard exempts anchors that ARE components. A
|
|
110
116
|
link-button renders as an <a> carrying bg-primary + text-primary-foreground
|
|
@@ -116,48 +122,45 @@ export function generateGlobalCss() {
|
|
|
116
122
|
(No backticks in this comment: the whole stylesheet is a template
|
|
117
123
|
literal.) */
|
|
118
124
|
/* Not a whole-card link (LinkCard, the video card: \`a.not-prose\`): its underline ran under every line of the card */
|
|
119
|
-
& a:not(.dba-api *):not([data-variant])
|
|
120
|
-
& a:hover:not(.dba-api *):not([data-variant])
|
|
125
|
+
& a:not(.dba-api *, .not-prose, .not-prose *):not([data-variant]) { color: var(--primary); text-decoration: underline; text-underline-offset: 2px; }
|
|
126
|
+
& a:hover:not(.dba-api *, .not-prose, .not-prose *):not([data-variant]) { opacity: 0.8; }
|
|
121
127
|
|
|
122
|
-
& strong:not(.dba-api *) { font-weight: 600; }
|
|
123
|
-
& 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); }
|
|
124
|
-
& pre code:not(.dba-api *) { padding: 0; background: none; font-size: 1em; }
|
|
128
|
+
& strong:not(.dba-api *, .not-prose, .not-prose *) { font-weight: 600; }
|
|
129
|
+
& code:not(.dba-api *, .not-prose, .not-prose *) { font-size: 0.875em; padding: 0.15em 0.35em; border-radius: 0.25rem; background: var(--muted); font-family: var(--font-code, ui-monospace, monospace); }
|
|
130
|
+
& pre code:not(.dba-api *, .not-prose, .not-prose *) { padding: 0; background: none; font-size: 1em; }
|
|
125
131
|
|
|
126
132
|
/* Spacing between consecutive block elements (code blocks, alerts, etc.) */
|
|
127
133
|
& > * + * { margin-top: 0.75rem; }
|
|
128
|
-
& li > * + *:not(.dba-api *) { margin-top: 0.75rem; }
|
|
134
|
+
& li > * + *:not(.dba-api *, .not-prose *) { margin-top: 0.75rem; }
|
|
129
135
|
|
|
130
|
-
& ul:not(.dba-api *) { list-style: disc; padding-left: 1.5rem; margin: 0.75rem 0; }
|
|
136
|
+
& ul:not(.dba-api *, .not-prose, .not-prose *) { list-style: disc; padding-left: 1.5rem; margin: 0.75rem 0; }
|
|
131
137
|
/* Not a step list, nor a list inside one: Steps.astro draws their numbers as circles, and this
|
|
132
138
|
rule (one class and an element) outranked its .steps-list { list-style: none }, so each step
|
|
133
139
|
showed its "2." beside its circle */
|
|
134
|
-
& ol:not(.dba-api *):not(.steps-list, .sub-steps-list, .steps-list ol) { list-style: decimal; padding-left: 1.5rem; margin: 0.75rem 0; }
|
|
135
|
-
& li:not(.dba-api *) { margin: 0.25rem 0; }
|
|
136
|
-
& li > ul:not(.dba-api *), & li > ol:not(.dba-api *) { margin: 0.25rem 0; }
|
|
137
|
-
/* A file tree draws its own folder and file icons (files/markup.ts): the list rules above gave it bullets too */
|
|
138
|
-
& [data-component="files"] ul:not(.dba-api *) { list-style: none; padding-left: 0; }
|
|
139
|
-
& [data-component="files"] li > ul:not(.dba-api *) { padding-left: 1rem; }
|
|
140
|
+
& ol:not(.dba-api *, .not-prose, .not-prose *):not(.steps-list, .sub-steps-list, .steps-list ol) { list-style: decimal; padding-left: 1.5rem; margin: 0.75rem 0; }
|
|
141
|
+
& li:not(.dba-api *, .not-prose, .not-prose *) { margin: 0.25rem 0; }
|
|
142
|
+
& li > ul:not(.dba-api *, .not-prose, .not-prose *), & li > ol:not(.dba-api *, .not-prose, .not-prose *) { margin: 0.25rem 0; }
|
|
140
143
|
|
|
141
|
-
& blockquote:not(.dba-api *) { border-left: 4px solid var(--border); padding-left: 1rem; color: var(--muted-foreground); font-style: italic; margin: 1rem 0; }
|
|
144
|
+
& blockquote:not(.dba-api *, .not-prose, .not-prose *) { border-left: 4px solid var(--border); padding-left: 1rem; color: var(--muted-foreground); font-style: italic; margin: 1rem 0; }
|
|
142
145
|
|
|
143
|
-
& hr:not(.dba-api *) { border: none; border-top: 1px solid var(--border); margin: 2rem 0; }
|
|
146
|
+
& hr:not(.dba-api *, .not-prose, .not-prose *) { border: none; border-top: 1px solid var(--border); margin: 2rem 0; }
|
|
144
147
|
|
|
145
|
-
& table:not(.dba-api *) { width: 100%; border-collapse: collapse; margin: 1rem 0; font-size: 0.875rem; }
|
|
146
|
-
& th:not(.dba-api *) { text-align: left; vertical-align: bottom; font-weight: 600; padding: 0.5rem; border-bottom: 2px solid var(--border); }
|
|
147
|
-
& td:not(.dba-api *) { padding: 0.5rem; vertical-align: top; border-bottom: 1px solid var(--border); }
|
|
148
|
+
& table:not(.dba-api *, .not-prose, .not-prose *) { width: 100%; border-collapse: collapse; margin: 1rem 0; font-size: 0.875rem; }
|
|
149
|
+
& th:not(.dba-api *, .not-prose, .not-prose *) { text-align: left; vertical-align: bottom; font-weight: 600; padding: 0.5rem; border-bottom: 2px solid var(--border); }
|
|
150
|
+
& td:not(.dba-api *, .not-prose, .not-prose *) { padding: 0.5rem; vertical-align: top; border-bottom: 1px solid var(--border); }
|
|
148
151
|
/* A table's caption is its title, above it; the same for Table components and raw HTML tables */
|
|
149
|
-
& caption:not(.dba-api *) { caption-side: top; text-align: left; font-weight: 500; color: var(--foreground); padding-bottom: 0.5rem; }
|
|
152
|
+
& caption:not(.dba-api *, .not-prose, .not-prose *) { caption-side: top; text-align: left; font-weight: 500; color: var(--foreground); padding-bottom: 0.5rem; }
|
|
150
153
|
|
|
151
154
|
/* Tailwind Preflight sets display: block on every img, which turns an
|
|
152
155
|
INLINE icon into its own line and splits the sentence around it —
|
|
153
156
|
AsciiDoc UI macros render mid-step and were breaking every procedure
|
|
154
157
|
that used one. inline-block restores the flow and leaves a standalone
|
|
155
158
|
image (the only child of its paragraph) looking exactly as before. */
|
|
156
|
-
& img:not(.dba-api *) { display: inline-block; max-width: 100%; border-radius: 0.5rem; }
|
|
159
|
+
& img:not(.dba-api *, .not-prose, .not-prose *) { display: inline-block; max-width: 100%; border-radius: 0.5rem; }
|
|
157
160
|
/* A standalone image — the only child of its paragraph — keeps block
|
|
158
161
|
layout, so the vertical rhythm and the absence of baseline descender
|
|
159
162
|
space are exactly what they were before inline icons were fixed. */
|
|
160
|
-
& p > img:only-child:not(.dba-api *) { display: block; }
|
|
163
|
+
& p > img:only-child:not(.dba-api *, .not-prose, .not-prose *) { display: block; }
|
|
161
164
|
|
|
162
165
|
& .heading-anchor { text-decoration: none; opacity: 0; margin-right: 0.25rem; transition: opacity 0.2s; }
|
|
163
166
|
& h1:hover .heading-anchor, & h2:hover .heading-anchor, & h3:hover .heading-anchor, & h4:hover .heading-anchor { opacity: 0.4; }
|
|
@@ -166,19 +169,19 @@ export function generateGlobalCss() {
|
|
|
166
169
|
callouts) is a <details> too, with its own border and spacing; it
|
|
167
170
|
carries data-variant, and without the exclusion it got a second border
|
|
168
171
|
and 1rem of padding around its title bar. */
|
|
169
|
-
& details:not(.dba-api *):not([data-variant]) { border: 1px solid var(--border); border-radius: 0.5rem; padding: 1rem; margin: 1rem 0; }
|
|
170
|
-
& summary:not(.dba-api *) { cursor: pointer; font-weight: 600; }
|
|
172
|
+
& details:not(.dba-api *, .not-prose, .not-prose *):not([data-variant]) { border: 1px solid var(--border); border-radius: 0.5rem; padding: 1rem; margin: 1rem 0; }
|
|
173
|
+
& summary:not(.dba-api *, .not-prose, .not-prose *) { cursor: pointer; font-weight: 600; }
|
|
171
174
|
|
|
172
175
|
/* mark is emitted by the highlight directive AND can arrive as raw HTML
|
|
173
176
|
from an importer. Without a rule here it renders as the browser default
|
|
174
177
|
yellow, which clashes in light mode and is unreadable on a dark page
|
|
175
178
|
(nothing declares color-scheme). Token-based so it follows the theme. */
|
|
176
|
-
& mark:not(.dba-api *) { background: var(--dsb-mark-bg, rgb(254 240 138 / 0.6)); color: inherit; border-radius: 0.125rem; padding: 0 0.125rem; }
|
|
177
|
-
&:where(.dark *) mark:not(.dba-api *), .dark & mark:not(.dba-api *) { background: var(--dsb-mark-bg-dark, rgb(234 179 8 / 0.3)); }
|
|
179
|
+
& mark:not(.dba-api *, .not-prose, .not-prose *) { background: var(--dsb-mark-bg, rgb(254 240 138 / 0.6)); color: inherit; border-radius: 0.125rem; padding: 0 0.125rem; }
|
|
180
|
+
&:where(.dark *) mark:not(.dba-api *, .not-prose, .not-prose *), .dark & mark:not(.dba-api *, .not-prose, .not-prose *) { background: var(--dsb-mark-bg-dark, rgb(234 179 8 / 0.3)); }
|
|
178
181
|
|
|
179
|
-
& dl:not(.dba-api *) { margin: 1rem 0; }
|
|
180
|
-
& dt:not(.dba-api *) { font-weight: 600; margin-top: 0.75rem; }
|
|
181
|
-
& dd:not(.dba-api *) { margin-left: 1.5rem; color: var(--muted-foreground); }
|
|
182
|
+
& dl:not(.dba-api *, .not-prose, .not-prose *) { margin: 1rem 0; }
|
|
183
|
+
& dt:not(.dba-api *, .not-prose, .not-prose *) { font-weight: 600; margin-top: 0.75rem; }
|
|
184
|
+
& dd:not(.dba-api *, .not-prose, .not-prose *) { margin-left: 1.5rem; color: var(--muted-foreground); }
|
|
182
185
|
|
|
183
186
|
/* In-cell admonitions. Inside a raw HTML table cell nothing
|
|
184
187
|
markdown-native works, so the AsciiDoc engine emits a neutral
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dogsbay/format-astro",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.154",
|
|
4
4
|
"description": "Astro format plugin for Dogsbay — export TreeNode[] to Astro component markup",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -32,13 +32,13 @@
|
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"markdown-it": "^14.3.0",
|
|
35
|
-
"@dogsbay/format-dogsbay-md": "0.2.0-beta.
|
|
36
|
-
"@dogsbay/icons": "0.2.0-beta.
|
|
37
|
-
"@dogsbay/
|
|
38
|
-
"@dogsbay/
|
|
39
|
-
"@dogsbay/
|
|
40
|
-
"@dogsbay/
|
|
41
|
-
"@dogsbay/ui": "0.2.0-beta.
|
|
35
|
+
"@dogsbay/format-dogsbay-md": "0.2.0-beta.154",
|
|
36
|
+
"@dogsbay/icons": "0.2.0-beta.154",
|
|
37
|
+
"@dogsbay/serialize-core": "0.2.0-beta.154",
|
|
38
|
+
"@dogsbay/render-html": "0.2.0-beta.154",
|
|
39
|
+
"@dogsbay/site-files": "0.2.0-beta.154",
|
|
40
|
+
"@dogsbay/types": "0.2.0-beta.154",
|
|
41
|
+
"@dogsbay/ui": "0.2.0-beta.154"
|
|
42
42
|
},
|
|
43
43
|
"repository": {
|
|
44
44
|
"type": "git",
|