@writedocs/generator 0.1.0
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/LICENSE +15 -0
- package/README.md +17 -0
- package/astro.config.mjs +419 -0
- package/bin/writedocs.js +73 -0
- package/package.json +79 -0
- package/src/assets/wd_watermark.png +0 -0
- package/src/assets/wd_watermark_dark.png +0 -0
- package/src/cli/build-auth.js +53 -0
- package/src/cli/build.js +40 -0
- package/src/cli/dev.js +12 -0
- package/src/cli/generate-api-pages.js +359 -0
- package/src/cli/init.js +81 -0
- package/src/cli/preflight.js +40 -0
- package/src/cli/run-astro.js +57 -0
- package/src/cli/run-pagefind.js +66 -0
- package/src/cli/write-redirects-file.js +80 -0
- package/src/components/Accordion.astro +164 -0
- package/src/components/AccordionGroup.astro +40 -0
- package/src/components/ApiLangSelect.astro +168 -0
- package/src/components/ApiPlayground.astro +281 -0
- package/src/components/ApiReferencePanel.astro +1754 -0
- package/src/components/ApiSchemaField.astro +54 -0
- package/src/components/AppIcon.astro +32 -0
- package/src/components/Badge.astro +128 -0
- package/src/components/Callout.astro +168 -0
- package/src/components/Card.astro +136 -0
- package/src/components/CardGroup.astro +20 -0
- package/src/components/CodeGroup.astro +184 -0
- package/src/components/CopyPageMenu.astro +246 -0
- package/src/components/Danger.astro +12 -0
- package/src/components/Expandable.astro +126 -0
- package/src/components/Frame.astro +102 -0
- package/src/components/Hint.astro +99 -0
- package/src/components/Icon.astro +70 -0
- package/src/components/Image.astro +147 -0
- package/src/components/Info.astro +12 -0
- package/src/components/Note.astro +12 -0
- package/src/components/Parameter.astro +119 -0
- package/src/components/RequestExample.astro +33 -0
- package/src/components/ResponseExample.astro +19 -0
- package/src/components/Searchbar.astro +117 -0
- package/src/components/Step.astro +10 -0
- package/src/components/Steps.astro +32 -0
- package/src/components/Tab.astro +9 -0
- package/src/components/Tabs.astro +52 -0
- package/src/components/Tip.astro +12 -0
- package/src/components/Video.astro +135 -0
- package/src/components/Warning.astro +12 -0
- package/src/components/index.ts +48 -0
- package/src/content.config.ts +223 -0
- package/src/layout/BaseLayout.astro +750 -0
- package/src/layout/components/AnalyticsScripts.astro +77 -0
- package/src/layout/components/AskAiWidget.astro +37 -0
- package/src/layout/components/Breadcrumbs.astro +97 -0
- package/src/layout/components/ImageZoom.astro +19 -0
- package/src/layout/components/MobileMenu.astro +200 -0
- package/src/layout/components/NavTree.astro +351 -0
- package/src/layout/components/SearchModal.astro +42 -0
- package/src/layout/components/Sidebar.astro +122 -0
- package/src/layout/components/SiteFooter.astro +85 -0
- package/src/layout/components/TableOfContents.astro +117 -0
- package/src/layout/components/TopBar.astro +311 -0
- package/src/layout/styles/banner.css +44 -0
- package/src/layout/styles/base.css +234 -0
- package/src/layout/styles/dropdown.css +133 -0
- package/src/layout/styles/footer.css +108 -0
- package/src/layout/styles/image-zoom.css +50 -0
- package/src/layout/styles/mobile-menu.css +258 -0
- package/src/layout/styles/search-modal.css +122 -0
- package/src/layout/styles/topbar.css +437 -0
- package/src/lib/config.ts +2131 -0
- package/src/lib/mdx-auto-hydrate.js +70 -0
- package/src/lib/mdx-inject-builtins.js +87 -0
- package/src/lib/mdx-substitute-variables.js +66 -0
- package/src/lib/mdx-title-anchor-ids.js +84 -0
- package/src/lib/mermaid-rehype.js +72 -0
- package/src/lib/openapi-render.ts +479 -0
- package/src/lib/shiki-code-block.js +102 -0
- package/src/lib/shiki-copy-button.js +45 -0
- package/src/lib/styles-asset-integration.js +210 -0
- package/src/lib/writedocs-temp-dir.js +93 -0
- package/src/pages/404.astro +62 -0
- package/src/pages/[...slug].astro +1270 -0
- package/src/pages/[...slug].md.ts +78 -0
- package/src/pages/llms-full.txt.ts +71 -0
- package/src/pages/llms.txt.ts +141 -0
- package/src/scripts/banner.ts +20 -0
- package/src/scripts/dropdowns.ts +61 -0
- package/src/scripts/image-zoom.ts +66 -0
- package/src/scripts/mobile-menu.ts +55 -0
- package/src/scripts/search.ts +155 -0
- package/src/scripts/sidebar-scroll.ts +65 -0
- package/src/scripts/theme-toggle.ts +35 -0
- package/src/scripts/topbar-offset.ts +141 -0
- package/src/styles/global.css +18 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
// Recursive renderer for a Body/Response schema tree (SchemaRow[] from
|
|
3
|
+
// openapi-render.ts's schemaRows()) - one .wd-api-param row per field,
|
|
4
|
+
// same name/type/required markup and CSS as ApiPlayground.astro's own
|
|
5
|
+
// path/query/header parameter rows (those classes live in that file's
|
|
6
|
+
// global <style> block, not duplicated here - this component only ever
|
|
7
|
+
// renders inside a page that already has ApiPlayground on it). A field
|
|
8
|
+
// with nested properties (an object, or an array of objects) wraps its
|
|
9
|
+
// own children in an <Expandable> - the same collapsible "Show/Hide
|
|
10
|
+
// properties" control already used for hand-written Parameter/Expandable
|
|
11
|
+
// content elsewhere on API pages (see Expandable.astro's own comment) -
|
|
12
|
+
// rather than the flat, permanently-open, depth-indented list this used
|
|
13
|
+
// to be. Collapsed by default (defaultOpen={false}, overriding
|
|
14
|
+
// Expandable's own open-by-default) - a schema with several levels of
|
|
15
|
+
// nesting (kitchen-sink's price/dimensions/components/manufacturer
|
|
16
|
+
// example) reads better as "click to see what's inside" than as a wall
|
|
17
|
+
// of every field at every depth shown at once. `<Astro.self>` recurses
|
|
18
|
+
// the same way NavTree.astro's own nested sidebar groups do - same
|
|
19
|
+
// established pattern in this codebase for "a tree of unknown depth, one
|
|
20
|
+
// component handles every level."
|
|
21
|
+
import Expandable from './Expandable.astro';
|
|
22
|
+
import type { SchemaRow } from '../lib/openapi-render';
|
|
23
|
+
|
|
24
|
+
interface Props {
|
|
25
|
+
rows: SchemaRow[];
|
|
26
|
+
// Request-body fields show a "required" pill (a caller genuinely needs
|
|
27
|
+
// to know); response-body fields never did, even before this component
|
|
28
|
+
// existed (a response shape doesn't have optional-vs-required in the
|
|
29
|
+
// same actionable sense a request payload does) - kept as an explicit
|
|
30
|
+
// opt-out rather than dropped, so ApiPlayground.astro's two call sites
|
|
31
|
+
// (request body, response body) can each keep their own prior behavior
|
|
32
|
+
// through this one shared, recursive renderer.
|
|
33
|
+
showRequired?: boolean;
|
|
34
|
+
}
|
|
35
|
+
const { rows, showRequired = true } = Astro.props as Props;
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
{
|
|
39
|
+
rows.map((row) => (
|
|
40
|
+
<div class="wd-api-param">
|
|
41
|
+
<div class="wd-api-param-head">
|
|
42
|
+
<span class="wd-api-param-name">{row.name}</span>
|
|
43
|
+
<span class="wd-api-pill wd-api-pill-type">{row.type}</span>
|
|
44
|
+
{showRequired && row.required && <span class="wd-api-pill wd-api-pill-required">required</span>}
|
|
45
|
+
</div>
|
|
46
|
+
{row.description && <p class="wd-api-param-desc">{row.description}</p>}
|
|
47
|
+
{row.children.length > 0 && (
|
|
48
|
+
<Expandable title={`${row.name} properties`} defaultOpen={false}>
|
|
49
|
+
<Astro.self rows={row.children} showRequired={showRequired} />
|
|
50
|
+
</Expandable>
|
|
51
|
+
)}
|
|
52
|
+
</div>
|
|
53
|
+
))
|
|
54
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
// Renders any writedocs.json `icon` string, whichever of the two forms it
|
|
3
|
+
// takes (see resolveIcon() in lib/config.ts): an Iconify icon id (looked
|
|
4
|
+
// up against whatever @iconify-json/* collections are installed - see
|
|
5
|
+
// astro.config.mjs) rendered as a real <svg>, or literal text/emoji
|
|
6
|
+
// rendered as-is. Centralizes the icon/text branch so BaseLayout.astro
|
|
7
|
+
// and Card.astro (the two places writedocs.json icon strings get rendered)
|
|
8
|
+
// don't each need their own copy of the resolveIcon() call + branch.
|
|
9
|
+
import { Icon } from 'astro-icon/components';
|
|
10
|
+
import { resolveIcon } from '../lib/config';
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
icon?: string;
|
|
14
|
+
class?: string;
|
|
15
|
+
// Optional inline style passthrough - Icon.astro is the one real
|
|
16
|
+
// consumer, needing this to apply its own per-instance `size`/`color`
|
|
17
|
+
// props (arbitrary values, not a fixed set of classes like every
|
|
18
|
+
// other AppIcon call site uses). Left off Props' required list and
|
|
19
|
+
// undefined by default so every existing caller (BaseLayout, Card,
|
|
20
|
+
// Badge) is unaffected.
|
|
21
|
+
style?: string;
|
|
22
|
+
}
|
|
23
|
+
const { icon, class: className = 'wd-icon', style } = Astro.props as Props;
|
|
24
|
+
const resolved = icon ? resolveIcon(icon) : null;
|
|
25
|
+
---
|
|
26
|
+
{resolved && (
|
|
27
|
+
resolved.kind === 'iconify' ? (
|
|
28
|
+
<Icon name={resolved.name} class={className} style={style} />
|
|
29
|
+
) : (
|
|
30
|
+
<span class={className} style={style}>{resolved.value}</span>
|
|
31
|
+
)
|
|
32
|
+
)}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
// A small inline label - status indicators, version tags, "Beta"/"New"
|
|
3
|
+
// markers - modeled on Mintlify's own Badge
|
|
4
|
+
// (https://www.mintlify.com/docs/components/badge). Renders as an
|
|
5
|
+
// inline <span> (not a block element) so it drops naturally into a
|
|
6
|
+
// sentence, the same way <Hint> does, rather than needing its own line.
|
|
7
|
+
//
|
|
8
|
+
// Colors are a fixed semantic set (not arbitrary hex) using the same
|
|
9
|
+
// --wd-*-accent CSS-custom-property pattern Callout.astro's own
|
|
10
|
+
// .wd-callout-note/-info/-tip/-warning/-danger classes already
|
|
11
|
+
// establish - one shared color per class name, everything else in the
|
|
12
|
+
// component reads it via var(). Mintlify's own color list additionally
|
|
13
|
+
// has white/surface/white-destructive/surface-destructive variants,
|
|
14
|
+
// tied to their specific light/dark neutral-surface tokens; left out
|
|
15
|
+
// here since they don't map onto this site's own (simpler) light/dark
|
|
16
|
+
// token set, and "gray" already covers the neutral case.
|
|
17
|
+
import AppIcon from './AppIcon.astro';
|
|
18
|
+
|
|
19
|
+
interface Props {
|
|
20
|
+
color?: 'gray' | 'blue' | 'green' | 'yellow' | 'orange' | 'red' | 'purple';
|
|
21
|
+
size?: 'xs' | 'sm' | 'md' | 'lg';
|
|
22
|
+
shape?: 'rounded' | 'pill';
|
|
23
|
+
icon?: string;
|
|
24
|
+
stroke?: boolean;
|
|
25
|
+
disabled?: boolean;
|
|
26
|
+
}
|
|
27
|
+
const { color = 'gray', size = 'md', shape = 'rounded', icon, stroke = false, disabled = false } =
|
|
28
|
+
Astro.props as Props;
|
|
29
|
+
|
|
30
|
+
const KNOWN_COLORS = ['gray', 'blue', 'green', 'yellow', 'orange', 'red', 'purple'];
|
|
31
|
+
const colorClass = KNOWN_COLORS.includes(color) ? color : 'gray';
|
|
32
|
+
const KNOWN_SIZES = ['xs', 'sm', 'md', 'lg'];
|
|
33
|
+
const sizeClass = KNOWN_SIZES.includes(size) ? size : 'md';
|
|
34
|
+
const shapeClass = shape === 'pill' ? 'pill' : 'rounded';
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
<span
|
|
38
|
+
class:list={[
|
|
39
|
+
'wd-badge',
|
|
40
|
+
`wd-badge-${colorClass}`,
|
|
41
|
+
`wd-badge-${sizeClass}`,
|
|
42
|
+
`wd-badge-${shapeClass}`,
|
|
43
|
+
stroke && 'wd-badge-stroke',
|
|
44
|
+
disabled && 'wd-badge-disabled',
|
|
45
|
+
]}
|
|
46
|
+
>
|
|
47
|
+
{icon && <AppIcon icon={icon} class="wd-badge-icon" />}<slot />
|
|
48
|
+
</span>
|
|
49
|
+
<style>
|
|
50
|
+
.wd-badge {
|
|
51
|
+
display: inline-flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
gap: 0.35em;
|
|
54
|
+
line-height: 1;
|
|
55
|
+
font-weight: 600;
|
|
56
|
+
white-space: nowrap;
|
|
57
|
+
vertical-align: middle;
|
|
58
|
+
border: 1px solid transparent;
|
|
59
|
+
/* Filled by default - a tint of the accent color over the page
|
|
60
|
+
background, with the accent itself as the text color. */
|
|
61
|
+
background: color-mix(in srgb, var(--wd-badge-accent) 14%, var(--wd-background));
|
|
62
|
+
color: var(--wd-badge-accent);
|
|
63
|
+
}
|
|
64
|
+
/* :global() is load-bearing here, same reason as Card.astro's own
|
|
65
|
+
identical comment on its .wd-card-icon rules: AppIcon.astro
|
|
66
|
+
renders this span/svg, not Badge.astro, so it never carries
|
|
67
|
+
Badge's own scope attribute. Sized in em so it scales with
|
|
68
|
+
whichever size class is active without needing its own
|
|
69
|
+
xs/sm/md/lg variants. */
|
|
70
|
+
:global(.wd-badge-icon) {
|
|
71
|
+
display: block;
|
|
72
|
+
width: 1em;
|
|
73
|
+
height: 1em;
|
|
74
|
+
flex-shrink: 0;
|
|
75
|
+
}
|
|
76
|
+
.wd-badge-gray {
|
|
77
|
+
--wd-badge-accent: #64748b;
|
|
78
|
+
}
|
|
79
|
+
.wd-badge-blue {
|
|
80
|
+
--wd-badge-accent: #3b82f6;
|
|
81
|
+
}
|
|
82
|
+
.wd-badge-green {
|
|
83
|
+
--wd-badge-accent: #16a34a;
|
|
84
|
+
}
|
|
85
|
+
.wd-badge-yellow {
|
|
86
|
+
--wd-badge-accent: #ca8a04;
|
|
87
|
+
}
|
|
88
|
+
.wd-badge-orange {
|
|
89
|
+
--wd-badge-accent: #d97706;
|
|
90
|
+
}
|
|
91
|
+
.wd-badge-red {
|
|
92
|
+
--wd-badge-accent: #dc2626;
|
|
93
|
+
}
|
|
94
|
+
.wd-badge-purple {
|
|
95
|
+
--wd-badge-accent: #9333ea;
|
|
96
|
+
}
|
|
97
|
+
/* Outline instead of filled - a subtler treatment for the same
|
|
98
|
+
color, per Mintlify's own "stroke" prop. */
|
|
99
|
+
.wd-badge-stroke {
|
|
100
|
+
background: transparent;
|
|
101
|
+
border-color: var(--wd-badge-accent);
|
|
102
|
+
}
|
|
103
|
+
.wd-badge-rounded {
|
|
104
|
+
border-radius: 0.35em;
|
|
105
|
+
}
|
|
106
|
+
.wd-badge-pill {
|
|
107
|
+
border-radius: 999px;
|
|
108
|
+
}
|
|
109
|
+
.wd-badge-xs {
|
|
110
|
+
padding: 0.05em 0.45em;
|
|
111
|
+
font-size: 0.65rem;
|
|
112
|
+
}
|
|
113
|
+
.wd-badge-sm {
|
|
114
|
+
padding: 0.1em 0.55em;
|
|
115
|
+
font-size: 0.72rem;
|
|
116
|
+
}
|
|
117
|
+
.wd-badge-md {
|
|
118
|
+
padding: 0.15em 0.65em;
|
|
119
|
+
font-size: 0.8rem;
|
|
120
|
+
}
|
|
121
|
+
.wd-badge-lg {
|
|
122
|
+
padding: 0.25em 0.8em;
|
|
123
|
+
font-size: 0.92rem;
|
|
124
|
+
}
|
|
125
|
+
.wd-badge-disabled {
|
|
126
|
+
opacity: 0.5;
|
|
127
|
+
}
|
|
128
|
+
</style>
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
---
|
|
2
|
+
// See src/components/{Note,Tip,Warning,Danger,Info}.astro - one thin
|
|
3
|
+
// wrapper per `type` below, each just `<Callout type="x">` under the
|
|
4
|
+
// hood, so a page can write either `<Callout type="info">` or the
|
|
5
|
+
// shorthand `<Info>` and get an identical result. Both forms take the
|
|
6
|
+
// same `title` prop.
|
|
7
|
+
import AppIcon from "./AppIcon.astro";
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
type?: "note" | "tip" | "warning" | "danger" | "info";
|
|
11
|
+
title?: string;
|
|
12
|
+
// Set automatically by remarkCalloutAnchorIds (src/lib/mdx-callout-anchor-ids.js)
|
|
13
|
+
// for every <Callout title="...">/<Note title="...">/etc. it finds in an
|
|
14
|
+
// .mdx file's own JSX, already deduped (-2, -3, ...) against every other
|
|
15
|
+
// callout title on that same page - see that plugin's own comment for
|
|
16
|
+
// why the dedup has to happen there, one level up, rather than here.
|
|
17
|
+
// Not meant to be set by hand in content; the `slugify(title)` fallback
|
|
18
|
+
// below covers every case this doesn't reach (plain .md content, a
|
|
19
|
+
// dynamic `title={expr}`, or this component used directly from a .astro
|
|
20
|
+
// file rather than through MDX).
|
|
21
|
+
_titleId?: string;
|
|
22
|
+
}
|
|
23
|
+
const { type = "note", title, _titleId } = Astro.props as Props;
|
|
24
|
+
// FontAwesome Solid (fa6-solid, already an installed @iconify-json/*
|
|
25
|
+
// collection - see astro.config.mjs's own comment on icon() needing no
|
|
26
|
+
// allowlist) rather than a literal emoji, so every callout gets a real,
|
|
27
|
+
// theme-colored <svg> instead of a platform-dependent glyph.
|
|
28
|
+
const icons: Record<string, string> = {
|
|
29
|
+
note: "fa6-solid:note-sticky",
|
|
30
|
+
info: "fa6-solid:circle-info",
|
|
31
|
+
tip: "fa6-solid:lightbulb",
|
|
32
|
+
warning: "fa6-solid:triangle-exclamation",
|
|
33
|
+
danger: "fa6-solid:circle-xmark",
|
|
34
|
+
};
|
|
35
|
+
// Astro's own heading-id slugging (github-slugger, via the markdown
|
|
36
|
+
// pipeline) never sees this - `title` is a component prop, not a
|
|
37
|
+
// markdown heading, so it gets no id unless we make one ourselves.
|
|
38
|
+
function slugify(value: string): string {
|
|
39
|
+
return value
|
|
40
|
+
.toLowerCase()
|
|
41
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
42
|
+
.replace(/^-+|-+$/g, "");
|
|
43
|
+
}
|
|
44
|
+
const titleId = title ? (_titleId ?? slugify(title)) : undefined;
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
<div class:list={["wd-callout", `wd-callout-${type}`, { "wd-callout-titled": Boolean(title) }]}>
|
|
48
|
+
<AppIcon icon={icons[type]} class="wd-callout-icon" />
|
|
49
|
+
<div class="wd-callout-content">
|
|
50
|
+
{
|
|
51
|
+
title && (
|
|
52
|
+
<div class="wd-callout-title" id={titleId}>
|
|
53
|
+
{title}
|
|
54
|
+
<a class="wd-heading-anchor" href={`#${titleId}`} aria-label="Link to this section">
|
|
55
|
+
#
|
|
56
|
+
</a>
|
|
57
|
+
</div>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
<div class="wd-callout-body"><slot /></div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
<style>
|
|
64
|
+
.wd-callout {
|
|
65
|
+
display: flex;
|
|
66
|
+
gap: 0.7rem;
|
|
67
|
+
padding: 0.85rem 1.1rem;
|
|
68
|
+
border-radius: 0.6rem;
|
|
69
|
+
border-left: 3px solid var(--wd-callout-accent);
|
|
70
|
+
background: color-mix(in srgb, var(--wd-callout-accent) 10%, var(--wd-background));
|
|
71
|
+
margin: 1.25rem 0;
|
|
72
|
+
}
|
|
73
|
+
/* A titled callout has two stacked lines (title, then body) - the icon
|
|
74
|
+
should align with the title's cap-height, not float centered across
|
|
75
|
+
the whole two-line block the way it does in the single-line,
|
|
76
|
+
untitled case above. */
|
|
77
|
+
.wd-callout.wd-callout-titled {
|
|
78
|
+
padding-block: 1rem;
|
|
79
|
+
}
|
|
80
|
+
/* :global() is load-bearing on every rule below, not just this one -
|
|
81
|
+
AppIcon.astro renders the actual <svg class="wd-callout-icon">, not
|
|
82
|
+
Callout.astro, so none of these ever carry Callout's own scope
|
|
83
|
+
attribute. A selector like `.wd-callout-icon { color: ... }` with
|
|
84
|
+
no :global() compiles to `.wd-callout-icon[data-astro-cid-x]`,
|
|
85
|
+
which can never match that svg - it silently does nothing rather
|
|
86
|
+
than erroring, which is exactly what happened here the first time
|
|
87
|
+
around (icon rendered at the right size, via the one rule that
|
|
88
|
+
*was* already global, but stayed un-colored black - the default
|
|
89
|
+
SVG fill - since its color rule was scoped and never matched).
|
|
90
|
+
Same root cause Card.astro's own icon rule already works around. */
|
|
91
|
+
:global(svg.wd-callout-icon) {
|
|
92
|
+
display: block;
|
|
93
|
+
flex-shrink: 0;
|
|
94
|
+
width: 1.15rem;
|
|
95
|
+
height: 1.15rem;
|
|
96
|
+
color: var(--wd-callout-accent);
|
|
97
|
+
fill: currentColor;
|
|
98
|
+
margin-top: 0.2rem;
|
|
99
|
+
}
|
|
100
|
+
.wd-callout-content {
|
|
101
|
+
flex: 1;
|
|
102
|
+
min-width: 0;
|
|
103
|
+
}
|
|
104
|
+
/* --wd-topbar-offset is a custom property set on <html> by
|
|
105
|
+
initTopbarOffset() (src/scripts/topbar-offset.ts) - inherits down to
|
|
106
|
+
here like any other CSS var regardless of Astro's scoping (that only
|
|
107
|
+
affects selector matching, not the ordinary property cascade), so
|
|
108
|
+
this reads the exact same live value [...slug].astro's own h2/h3/h4
|
|
109
|
+
scroll-margin-top rules do. Without it, jumping to a #callout-title
|
|
110
|
+
anchor landed the callout right at the viewport's true top edge,
|
|
111
|
+
partly hidden under the sticky topbar - the same bug those heading
|
|
112
|
+
rules exist to prevent, just missing here since this id didn't exist
|
|
113
|
+
until the anchor feature above was added. The extra flat + 1rem on
|
|
114
|
+
top of the measured topbar height (headings don't need this - a
|
|
115
|
+
heading's own text sits flush at its scroll target; a callout's
|
|
116
|
+
title sits inside the box's own 1rem top padding, `padding-block: 1rem`
|
|
117
|
+
on .wd-callout-titled above, so landing at exactly the topbar's
|
|
118
|
+
height alone still let that padding strip tuck under it, reading as
|
|
119
|
+
"still a bit cut off") is breathing room, not a topbar measurement -
|
|
120
|
+
it doesn't need to come from the live-measured var itself. */
|
|
121
|
+
.wd-callout-title {
|
|
122
|
+
font-weight: 700;
|
|
123
|
+
color: var(--wd-callout-accent);
|
|
124
|
+
margin-bottom: 0.3rem;
|
|
125
|
+
scroll-margin-top: calc(var(--wd-topbar-offset, 5rem) + 1rem);
|
|
126
|
+
}
|
|
127
|
+
/* .wd-heading-anchor's base look (color, opacity: 0, transition) comes
|
|
128
|
+
from [...slug].astro's own <style is:global> block - it already
|
|
129
|
+
applies anywhere that class shows up, headings or not. Only the
|
|
130
|
+
hover trigger needs to live here, and unlike the icon rules above,
|
|
131
|
+
no :global() is needed for it: both .wd-callout-title and the
|
|
132
|
+
anchor inside it are rendered directly by this component's own
|
|
133
|
+
template, so they both carry the same scope attribute and this
|
|
134
|
+
plain selector matches correctly as-is. */
|
|
135
|
+
.wd-callout-title:hover .wd-heading-anchor {
|
|
136
|
+
opacity: 1;
|
|
137
|
+
}
|
|
138
|
+
.wd-callout-body {
|
|
139
|
+
color: var(--wd-text);
|
|
140
|
+
}
|
|
141
|
+
.wd-callout-body :global(p:first-child) {
|
|
142
|
+
margin-top: 0;
|
|
143
|
+
}
|
|
144
|
+
.wd-callout-body :global(p:last-child) {
|
|
145
|
+
margin-bottom: 0;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* One accent color per type, in both light and dark mode - the
|
|
149
|
+
background/border/icon/title all derive from this single value via
|
|
150
|
+
color-mix() against --wd-background (which already resolves
|
|
151
|
+
correctly per [data-theme] on its own), so there's no separate
|
|
152
|
+
[data-theme='dark'] override block needed here at all. */
|
|
153
|
+
.wd-callout-note {
|
|
154
|
+
--wd-callout-accent: #64748b;
|
|
155
|
+
}
|
|
156
|
+
.wd-callout-info {
|
|
157
|
+
--wd-callout-accent: #3b82f6;
|
|
158
|
+
}
|
|
159
|
+
.wd-callout-tip {
|
|
160
|
+
--wd-callout-accent: #16a34a;
|
|
161
|
+
}
|
|
162
|
+
.wd-callout-warning {
|
|
163
|
+
--wd-callout-accent: #d97706;
|
|
164
|
+
}
|
|
165
|
+
.wd-callout-danger {
|
|
166
|
+
--wd-callout-accent: #dc2626;
|
|
167
|
+
}
|
|
168
|
+
</style>
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
---
|
|
2
|
+
import AppIcon from './AppIcon.astro';
|
|
3
|
+
|
|
4
|
+
// Three visual variants, chosen by which optional prop is set - never more
|
|
5
|
+
// than one of icon/img/method actually renders at once, but they're kept
|
|
6
|
+
// as independent props (not a single `variant` union) since `icon` is
|
|
7
|
+
// meant to keep working exactly as before for existing content, and `href`
|
|
8
|
+
// applies identically to all three.
|
|
9
|
+
interface Props {
|
|
10
|
+
title: string;
|
|
11
|
+
icon?: string;
|
|
12
|
+
// Renders a full-width image above the title instead of AppIcon's small
|
|
13
|
+
// icon - takes priority over `icon` if both are somehow set (see the
|
|
14
|
+
// template below), since a real image is the more deliberate/specific
|
|
15
|
+
// choice of the two.
|
|
16
|
+
img?: string;
|
|
17
|
+
// API-reference style: a solid, colored method badge next to the title,
|
|
18
|
+
// top-right. Same 5 known methods + fallback-gray "other" as the
|
|
19
|
+
// sidebar's own OpenAPI badges (NavTree.astro's wd-nav-method-*) and the
|
|
20
|
+
// Try-it playground's header (ApiPlayground.astro's wd-api-method-*) -
|
|
21
|
+
// this is a third, independent copy of that same get/post/put/patch/
|
|
22
|
+
// delete color set rather than a shared import, consistent with how
|
|
23
|
+
// those two already don't share one either (different visual treatment
|
|
24
|
+
// each - tinted pill in the sidebar, solid pill in the playground, solid
|
|
25
|
+
// pill here too but sized for a card heading, not worth forcing into one
|
|
26
|
+
// generic component over a handful of literal hex values).
|
|
27
|
+
method?: string;
|
|
28
|
+
href?: string;
|
|
29
|
+
}
|
|
30
|
+
const { title, icon, img, method, href } = Astro.props as Props;
|
|
31
|
+
const Tag = href ? 'a' : 'div';
|
|
32
|
+
const KNOWN_METHODS = ['get', 'post', 'put', 'patch', 'delete'];
|
|
33
|
+
const methodLower = method?.toLowerCase();
|
|
34
|
+
const methodClass = methodLower && KNOWN_METHODS.includes(methodLower) ? methodLower : 'other';
|
|
35
|
+
---
|
|
36
|
+
<Tag class="wd-card" href={href}>
|
|
37
|
+
{img && <img src={img} alt="" class="wd-card-image" />}
|
|
38
|
+
{!img && <AppIcon icon={icon} class="wd-card-icon" />}
|
|
39
|
+
<div class="wd-card-title-row">
|
|
40
|
+
<h3 class="wd-card-title">{title}</h3>
|
|
41
|
+
{
|
|
42
|
+
method && (
|
|
43
|
+
<span class:list={['wd-card-method', `wd-card-method-${methodClass}`]}>
|
|
44
|
+
{method.toUpperCase()}
|
|
45
|
+
</span>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
</div>
|
|
49
|
+
<div class="wd-card-body"><slot /></div>
|
|
50
|
+
</Tag>
|
|
51
|
+
<style>
|
|
52
|
+
.wd-card {
|
|
53
|
+
display: block;
|
|
54
|
+
border: 1px solid var(--wd-border);
|
|
55
|
+
border-radius: 0.6rem;
|
|
56
|
+
padding: 1.1rem 1.2rem;
|
|
57
|
+
text-decoration: none;
|
|
58
|
+
color: inherit;
|
|
59
|
+
transition: border-color 0.15s ease;
|
|
60
|
+
}
|
|
61
|
+
a.wd-card:hover {
|
|
62
|
+
border-color: var(--wd-primary);
|
|
63
|
+
}
|
|
64
|
+
/* :global() is load-bearing: AppIcon.astro renders this span/svg, not
|
|
65
|
+
Card.astro, so it never carries Card's own scope attribute - without
|
|
66
|
+
:global() these selectors compile to something that can never match. */
|
|
67
|
+
:global(span.wd-card-icon) {
|
|
68
|
+
display: block;
|
|
69
|
+
font-size: 1.3rem;
|
|
70
|
+
}
|
|
71
|
+
:global(svg.wd-card-icon) {
|
|
72
|
+
display: block;
|
|
73
|
+
width: 1.3rem;
|
|
74
|
+
height: 1.3rem;
|
|
75
|
+
}
|
|
76
|
+
.wd-card-image {
|
|
77
|
+
display: block;
|
|
78
|
+
width: 100%;
|
|
79
|
+
height: 9rem;
|
|
80
|
+
object-fit: cover;
|
|
81
|
+
border-radius: 0.4rem;
|
|
82
|
+
background: var(--wd-surface);
|
|
83
|
+
}
|
|
84
|
+
/* Same 0.4rem/0.3rem the title itself used to carry directly, moved up
|
|
85
|
+
onto this wrapper so it stays correct whether the row holds just a
|
|
86
|
+
title (icon/img/no-icon variants) or a title + method badge side by
|
|
87
|
+
side - a flex row's own margin does the same job a block element's
|
|
88
|
+
margin did, without changing how any existing (no-method) card looks. */
|
|
89
|
+
.wd-card-title-row {
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
justify-content: space-between;
|
|
93
|
+
gap: 0.5rem;
|
|
94
|
+
margin: 0.4rem 0 0.3rem;
|
|
95
|
+
}
|
|
96
|
+
.wd-card-title {
|
|
97
|
+
margin: 0;
|
|
98
|
+
font-size: 1rem;
|
|
99
|
+
}
|
|
100
|
+
.wd-card-method {
|
|
101
|
+
flex-shrink: 0;
|
|
102
|
+
display: inline-block;
|
|
103
|
+
padding: 0.2rem 0.55rem;
|
|
104
|
+
border-radius: 0.3rem;
|
|
105
|
+
font-size: 0.72rem;
|
|
106
|
+
font-weight: 700;
|
|
107
|
+
letter-spacing: 0.02em;
|
|
108
|
+
color: #fff;
|
|
109
|
+
}
|
|
110
|
+
.wd-card-method-get {
|
|
111
|
+
background: #16a34a;
|
|
112
|
+
}
|
|
113
|
+
.wd-card-method-post {
|
|
114
|
+
background: #2563eb;
|
|
115
|
+
}
|
|
116
|
+
.wd-card-method-put {
|
|
117
|
+
background: #9333ea;
|
|
118
|
+
}
|
|
119
|
+
.wd-card-method-patch {
|
|
120
|
+
background: #d97706;
|
|
121
|
+
}
|
|
122
|
+
.wd-card-method-delete {
|
|
123
|
+
background: #dc2626;
|
|
124
|
+
}
|
|
125
|
+
.wd-card-method-other {
|
|
126
|
+
background: var(--wd-text-muted);
|
|
127
|
+
}
|
|
128
|
+
.wd-card-body {
|
|
129
|
+
font-size: 0.9rem;
|
|
130
|
+
color: var(--wd-text-muted);
|
|
131
|
+
margin: 0;
|
|
132
|
+
}
|
|
133
|
+
.wd-card-body :global(p) {
|
|
134
|
+
margin: 0;
|
|
135
|
+
}
|
|
136
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
cols?: number;
|
|
4
|
+
}
|
|
5
|
+
const { cols = 2 } = Astro.props as Props;
|
|
6
|
+
---
|
|
7
|
+
<div class="wd-card-group" style={`--wd-cols: ${cols}`}>
|
|
8
|
+
<slot />
|
|
9
|
+
</div>
|
|
10
|
+
<style>
|
|
11
|
+
.wd-card-group {
|
|
12
|
+
display: grid;
|
|
13
|
+
grid-template-columns: repeat(var(--wd-cols), minmax(0, 1fr));
|
|
14
|
+
gap: 1rem;
|
|
15
|
+
margin: 1.25rem 0;
|
|
16
|
+
}
|
|
17
|
+
@media (max-width: 640px) {
|
|
18
|
+
.wd-card-group { grid-template-columns: 1fr; }
|
|
19
|
+
}
|
|
20
|
+
</style>
|