@writedocs/generator 0.4.6 → 0.4.8

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.
Files changed (48) hide show
  1. package/astro.config.mjs +37 -3
  2. package/package.json +1 -1
  3. package/src/components/Accordion.astro +22 -5
  4. package/src/components/AppIcon.astro +5 -0
  5. package/src/components/Callout.astro +15 -5
  6. package/src/components/Card.astro +64 -13
  7. package/src/components/Check.astro +13 -0
  8. package/src/components/Color.astro +61 -0
  9. package/src/components/ColorItem.astro +93 -0
  10. package/src/components/ColorRow.astro +39 -0
  11. package/src/components/Columns.astro +11 -0
  12. package/src/components/Frame.astro +10 -1
  13. package/src/components/GitHubRepo.astro +156 -0
  14. package/src/components/Hint.astro +50 -4
  15. package/src/components/Panel.astro +11 -0
  16. package/src/components/ParamField.astro +25 -0
  17. package/src/components/Parameter.astro +41 -4
  18. package/src/components/Prompt.astro +143 -0
  19. package/src/components/ResponseField.astro +18 -0
  20. package/src/components/Step.astro +22 -3
  21. package/src/components/Steps.astro +22 -0
  22. package/src/components/Tab.astro +7 -1
  23. package/src/components/Tabs.astro +27 -4
  24. package/src/components/Tile.astro +65 -0
  25. package/src/components/Tooltip.astro +13 -0
  26. package/src/components/Tree.astro +213 -0
  27. package/src/components/TreeFile.astro +17 -0
  28. package/src/components/TreeFolder.astro +28 -0
  29. package/src/components/Update.astro +171 -0
  30. package/src/components/View.astro +214 -0
  31. package/src/components/Visibility.astro +11 -0
  32. package/src/components/compound.ts +21 -0
  33. package/src/components/index.ts +12 -0
  34. package/src/content.config.ts +68 -2
  35. package/src/layout/components/NavTree.astro +34 -0
  36. package/src/layout/styles/base.css +12 -0
  37. package/src/lib/config.ts +1412 -1306
  38. package/src/lib/inline-markdown.js +29 -0
  39. package/src/lib/mdx-inject-builtins.js +20 -2
  40. package/src/lib/mdx-mintlify.js +99 -0
  41. package/src/lib/mdx-title-anchor-ids.js +7 -2
  42. package/src/lib/shiki-code-block.js +140 -26
  43. package/src/lib/visibility.js +29 -0
  44. package/src/pages/[...slug].astro +110 -7
  45. package/src/pages/[...slug].md.ts +7 -1
  46. package/src/pages/llms-full.txt.ts +5 -1
  47. package/src/pages/llms.txt.ts +3 -0
  48. package/src/styles/global.css +10 -0
@@ -0,0 +1,156 @@
1
+ ---
2
+ // Mintlify's <GitHub.Repo repo="owner/name" variant="inset|flat"> - a card
3
+ // linking to a public GitHub repository. Attached as GitHub.Repo in
4
+ // components/compound.ts. `repo` is an "owner/name" slug or a full
5
+ // github.com URL.
6
+ //
7
+ // The repository's name and link are built into the page; its description,
8
+ // star count and fork count are fetched from the public GitHub API when the
9
+ // page loads, as on Mintlify. The unauthenticated API allows 60 requests an
10
+ // hour per reader's IP, so the response is cached for the browser session.
11
+ // If the request fails, the card stays a plain link to the repository.
12
+ import AppIcon from './AppIcon.astro';
13
+
14
+ interface Props {
15
+ repo: string;
16
+ variant?: 'inset' | 'flat';
17
+ }
18
+ const { repo, variant = 'inset' } = Astro.props as Props;
19
+ const slug = String(repo ?? '')
20
+ .trim()
21
+ .replace(/^(?:https?:\/\/)?(?:www\.)?github\.com\//i, '')
22
+ .replace(/\.git$/i, '')
23
+ .split(/[/?#]/)
24
+ .slice(0, 2)
25
+ .join('/');
26
+ const valid = /^[\w.-]+\/[\w.-]+$/.test(slug);
27
+ ---
28
+ {valid && (
29
+ <a
30
+ class:list={['wd-github-repo', `wd-github-repo-${variant}`]}
31
+ href={`https://github.com/${slug}`}
32
+ target="_blank"
33
+ rel="noopener noreferrer"
34
+ data-repo={slug}
35
+ >
36
+ <div class="wd-github-repo-inner">
37
+ <div class="wd-github-repo-name">
38
+ <AppIcon icon="fa6-brands:github" class="wd-github-repo-icon" />
39
+ <span>{slug}</span>
40
+ </div>
41
+ <p class="wd-github-repo-description" hidden></p>
42
+ <div class="wd-github-repo-stats" hidden>
43
+ <span><span aria-hidden="true">&#9733;</span> <span data-stat="stars"></span> stars</span>
44
+ <span><span data-stat="forks"></span> forks</span>
45
+ </div>
46
+ </div>
47
+ </a>
48
+ )}
49
+ <script>
50
+ type RepoInfo = { description: string | null; stargazers_count: number; forks_count: number };
51
+ const compact = (n: number) =>
52
+ new Intl.NumberFormat('en', { notation: 'compact', maximumFractionDigits: 1 }).format(n);
53
+
54
+ async function loadRepo(slug: string): Promise<RepoInfo | null> {
55
+ const key = `wd-github-repo:${slug}`;
56
+ try {
57
+ const cached = sessionStorage.getItem(key);
58
+ if (cached) return JSON.parse(cached);
59
+ } catch {}
60
+ try {
61
+ const res = await fetch(`https://api.github.com/repos/${slug}`, {
62
+ headers: { Accept: 'application/vnd.github+json' },
63
+ });
64
+ if (!res.ok) return null;
65
+ const data = await res.json();
66
+ const info: RepoInfo = {
67
+ description: data.description ?? null,
68
+ stargazers_count: data.stargazers_count ?? 0,
69
+ forks_count: data.forks_count ?? 0,
70
+ };
71
+ try {
72
+ sessionStorage.setItem(key, JSON.stringify(info));
73
+ } catch {}
74
+ return info;
75
+ } catch {
76
+ return null;
77
+ }
78
+ }
79
+
80
+ function initGitHubRepos(root: ParentNode) {
81
+ root.querySelectorAll<HTMLElement>('.wd-github-repo[data-repo]').forEach(async (card) => {
82
+ if (card.dataset.wdInit) return;
83
+ card.dataset.wdInit = 'true';
84
+ const info = await loadRepo(card.dataset.repo!);
85
+ if (!info) return;
86
+ const desc = card.querySelector<HTMLElement>('.wd-github-repo-description');
87
+ if (desc && info.description) {
88
+ desc.textContent = info.description;
89
+ desc.hidden = false;
90
+ }
91
+ const stats = card.querySelector<HTMLElement>('.wd-github-repo-stats');
92
+ if (stats) {
93
+ stats.querySelector('[data-stat="stars"]')!.textContent = compact(info.stargazers_count);
94
+ stats.querySelector('[data-stat="forks"]')!.textContent = compact(info.forks_count);
95
+ stats.hidden = false;
96
+ }
97
+ });
98
+ }
99
+ initGitHubRepos(document);
100
+ document.addEventListener('astro:page-load', () => initGitHubRepos(document));
101
+ </script>
102
+ <style>
103
+ .wd-github-repo {
104
+ display: block;
105
+ margin: 1.25rem 0;
106
+ border: 1px solid var(--wd-border);
107
+ border-radius: 0.75rem;
108
+ text-decoration: none;
109
+ color: inherit;
110
+ transition: border-color 0.15s ease;
111
+ }
112
+ .wd-github-repo:hover {
113
+ border-color: var(--wd-primary);
114
+ }
115
+ /* inset (the default): the details sit on a rounded inner surface. */
116
+ .wd-github-repo-inset {
117
+ padding: 0.4rem;
118
+ }
119
+ .wd-github-repo-inset .wd-github-repo-inner {
120
+ padding: 0.9rem 1rem;
121
+ border-radius: 0.5rem;
122
+ background: var(--wd-surface);
123
+ }
124
+ /* flat: one surface, tighter padding. */
125
+ .wd-github-repo-flat .wd-github-repo-inner {
126
+ padding: 0.75rem 0.9rem;
127
+ }
128
+ .wd-github-repo-name {
129
+ display: flex;
130
+ align-items: center;
131
+ gap: 0.5rem;
132
+ font-weight: 600;
133
+ color: var(--wd-text);
134
+ }
135
+ :global(svg.wd-github-repo-icon) {
136
+ width: 1.1rem;
137
+ height: 1.1rem;
138
+ flex-shrink: 0;
139
+ }
140
+ .wd-github-repo-description {
141
+ margin: 0.4rem 0 0;
142
+ font-size: 0.875rem;
143
+ color: var(--wd-text-muted);
144
+ }
145
+ .wd-github-repo-stats {
146
+ display: flex;
147
+ gap: 1rem;
148
+ margin-top: 0.5rem;
149
+ font-size: 0.8rem;
150
+ color: var(--wd-text-muted);
151
+ }
152
+ .wd-github-repo-stats[hidden],
153
+ .wd-github-repo-description[hidden] {
154
+ display: none;
155
+ }
156
+ </style>
@@ -11,15 +11,30 @@
11
11
  // position predictably; a second <slot> would let an author put
12
12
  // arbitrary markup (another Hint, a link, a whole paragraph) in there,
13
13
  // which the hover-bubble layout below was never built to hold.
14
+ //
15
+ // `headline`, `cta` and `href` exist for Mintlify's <Tooltip> (see
16
+ // Tooltip.astro): an optional bold line above the tip, and an optional
17
+ // link below it. They're still plain strings, not slots - same reasoning
18
+ // as above. A tooltip with a link has to stay open while the pointer moves
19
+ // from the trigger onto the bubble, so the bubble takes pointer events
20
+ // once shown, and a transparent ::before bridges the gap between them.
14
21
  interface Props {
15
22
  tip: string;
23
+ headline?: string;
24
+ cta?: string;
25
+ href?: string;
16
26
  }
17
- const { tip } = Astro.props as Props;
27
+ const { tip, headline, cta, href } = Astro.props as Props;
28
+ const hasLink = Boolean(cta && href);
18
29
  ---
19
30
 
20
- <span class="wd-hint" tabindex="0">
31
+ <span class:list={["wd-hint", { "wd-hint-rich": Boolean(headline) || hasLink }]} tabindex="0">
21
32
  <span class="wd-hint-trigger"><slot /></span>
22
- <span class="wd-hint-tooltip" role="tooltip">{tip}</span>
33
+ <span class="wd-hint-tooltip" role="tooltip">
34
+ {headline && <span class="wd-hint-headline">{headline}</span>}
35
+ <span class="wd-hint-tip">{tip}</span>
36
+ {hasLink && <a class="wd-hint-cta" href={href}>{cta}</a>}
37
+ </span>
23
38
  </span>
24
39
  <style>
25
40
  .wd-hint {
@@ -91,9 +106,40 @@ const { tip } = Astro.props as Props;
91
106
  border-top-color: #18181b;
92
107
  }
93
108
  .wd-hint:hover .wd-hint-tooltip,
94
- .wd-hint:focus-visible .wd-hint-tooltip {
109
+ .wd-hint:focus-visible .wd-hint-tooltip,
110
+ .wd-hint:focus-within .wd-hint-tooltip {
95
111
  opacity: 1;
96
112
  visibility: visible;
97
113
  transform: translateX(-50%) translateY(0);
98
114
  }
115
+ /* Only a tooltip with a headline or link is interactive - a plain one
116
+ keeps pointer-events: none so it never blocks the text under it. */
117
+ .wd-hint-rich:hover .wd-hint-tooltip,
118
+ .wd-hint-rich:focus-within .wd-hint-tooltip {
119
+ pointer-events: auto;
120
+ }
121
+ .wd-hint-rich .wd-hint-tooltip::before {
122
+ content: "";
123
+ position: absolute;
124
+ top: 100%;
125
+ left: 0;
126
+ right: 0;
127
+ height: 0.6rem;
128
+ }
129
+ .wd-hint-tip,
130
+ .wd-hint-headline,
131
+ .wd-hint-cta {
132
+ display: block;
133
+ }
134
+ .wd-hint-rich .wd-hint-tip {
135
+ font-weight: 400;
136
+ }
137
+ .wd-hint-headline {
138
+ margin-bottom: 0.2rem;
139
+ }
140
+ .wd-hint-cta {
141
+ margin-top: 0.35rem;
142
+ color: #93c5fd;
143
+ text-decoration: underline;
144
+ }
99
145
  </style>
@@ -0,0 +1,11 @@
1
+ ---
2
+ // Mintlify's <Panel> - content pinned to the right-hand column on desktop,
3
+ // in place of the table of contents. It's the same docking
4
+ // RequestExample/ResponseExample already use (initExamplePanels() in
5
+ // [...slug].astro): the `wd-example-panel` class is what that script moves
6
+ // into the column at >= 1151px wide, and back into the page below that.
7
+ // A RequestExample/ResponseExample inside a Panel moves with it. On a page
8
+ // with no table-of-contents column (any mode but `default`), the Panel
9
+ // stays where it is in the page.
10
+ ---
11
+ <div class="wd-example-panel wd-panel"><slot /></div>
@@ -0,0 +1,25 @@
1
+ ---
2
+ // Mintlify's <ParamField> - a request parameter row, rendered by
3
+ // Parameter.astro. Mintlify names the parameter through whichever
4
+ // location prop it's in (`path="id"`, `query="limit"`, `body="email"`,
5
+ // `header="Authorization"`) rather than `name`; the first one present
6
+ // becomes the name. The location itself isn't shown - on a Mintlify page
7
+ // it's already implied by the section the field sits in.
8
+ import Parameter from './Parameter.astro';
9
+ interface Props {
10
+ name?: string;
11
+ path?: string;
12
+ query?: string;
13
+ body?: string;
14
+ header?: string;
15
+ type?: string;
16
+ required?: boolean;
17
+ default?: unknown;
18
+ deprecated?: boolean;
19
+ pre?: string[];
20
+ post?: string[];
21
+ }
22
+ const { name, path, query, body, header, ...rest } = Astro.props as Props;
23
+ const fieldName = name ?? path ?? query ?? body ?? header ?? '';
24
+ ---
25
+ <Parameter name={fieldName} {...rest}><slot /></Parameter>
@@ -16,21 +16,37 @@
16
16
  // text, an <Expandable> wrapping more Parameters for a nested object's
17
17
  // own properties, or both - see Expandable.astro's own comment for that
18
18
  // half of the pattern.
19
+ //
20
+ // `deprecated`, `pre` and `post` exist for Mintlify parity (see
21
+ // ParamField.astro/ResponseField.astro, the Mintlify-named wrappers around
22
+ // this): `pre`/`post` are extra labels Mintlify renders before/after the
23
+ // name. `default` accepts any value, not just a string - Mintlify content
24
+ // routinely writes `default={10}` or `default={false}`, and a falsy one
25
+ // (0, false) is still a real default worth showing, so only
26
+ // undefined/null/"" hide the chip.
19
27
  interface Props {
20
28
  name: string;
21
29
  type?: string;
22
30
  required?: boolean;
23
- default?: string;
31
+ default?: unknown;
32
+ deprecated?: boolean;
33
+ pre?: string[];
34
+ post?: string[];
24
35
  }
25
- const { name, type, required = false, default: defaultValue } = Astro.props as Props;
36
+ const { name, type, required = false, default: defaultValue, deprecated = false, pre = [], post = [] } =
37
+ Astro.props as Props;
38
+ const hasDefault = defaultValue !== undefined && defaultValue !== null && defaultValue !== "";
26
39
  ---
27
40
 
28
- <div class="wd-parameter">
41
+ <div class:list={["wd-parameter", { "wd-parameter-deprecated": deprecated }]}>
29
42
  <div class="wd-parameter-head">
43
+ {pre.map((label) => <span class="wd-parameter-label">{label}</span>)}
30
44
  <span class="wd-parameter-name">{name}</span>
31
45
  {type && <span class="wd-parameter-type">{type}</span>}
32
46
  {required && <span class="wd-parameter-required">required</span>}
33
- {defaultValue && <span class="wd-parameter-default">default: {defaultValue}</span>}
47
+ {deprecated && <span class="wd-parameter-deprecated-badge">deprecated</span>}
48
+ {post.map((label) => <span class="wd-parameter-label">{label}</span>)}
49
+ {hasDefault && <span class="wd-parameter-default">default: {String(defaultValue)}</span>}
34
50
  </div>
35
51
  <div class="wd-parameter-body">
36
52
  <slot />
@@ -96,6 +112,27 @@ const { name, type, required = false, default: defaultValue } = Astro.props as P
96
112
  padding: 0.1em 0.5em;
97
113
  color: #dc2626;
98
114
  }
115
+ /* Same chip as .wd-parameter-type, in amber - a warning, not an error
116
+ like .wd-parameter-required. The name itself is struck through below
117
+ so the field reads as "don't use this" even before the badge. */
118
+ .wd-parameter-deprecated-badge {
119
+ font-family: monospace;
120
+ font-size: 0.8rem;
121
+ font-weight: 600;
122
+ background: color-mix(in srgb, #d97706 8%, var(--wd-surface));
123
+ border: 1px solid color-mix(in srgb, #d97706 35%, var(--wd-border));
124
+ border-radius: 0.35em;
125
+ padding: 0.1em 0.5em;
126
+ color: #d97706;
127
+ }
128
+ .wd-parameter-deprecated .wd-parameter-name {
129
+ text-decoration: line-through;
130
+ opacity: 0.7;
131
+ }
132
+ .wd-parameter-label {
133
+ font-size: 0.78rem;
134
+ color: var(--wd-text-muted);
135
+ }
99
136
  .wd-parameter-default {
100
137
  font-size: 0.78rem;
101
138
  color: var(--wd-text-muted);
@@ -0,0 +1,143 @@
1
+ ---
2
+ // Mintlify's <Prompt> - a ready-made AI prompt: a card showing
3
+ // `description` (inline Markdown) and an optional icon, with actions to
4
+ // copy the prompt or open it in Cursor. The prompt itself is the slot; it's
5
+ // also viewable in a collapsed "Show prompt" section under the card.
6
+ //
7
+ // What gets copied/sent is the prompt's *source* text - `_text`, read from
8
+ // the MDX file by remarkMintlifyPromptText (lib/mdx-mintlify.js) - so its
9
+ // Markdown survives. Without it (a Prompt used outside MDX), the copy falls
10
+ // back to the rendered text.
11
+ //
12
+ // `actions` defaults to ["copy"]; add "cursor" for an "Open in Cursor"
13
+ // link (Cursor's own https://cursor.com/link/prompt?text=... deeplink,
14
+ // which also works for readers without Cursor installed). Cursor caps a
15
+ // deeplink at 10,000 URL-encoded characters, so a longer prompt gets no
16
+ // Cursor link - just the copy button.
17
+ import AppIcon from './AppIcon.astro';
18
+ import { inlineMarkdown } from '../lib/inline-markdown.js';
19
+
20
+ interface Props {
21
+ description?: string;
22
+ icon?: string;
23
+ actions?: Array<'copy' | 'cursor'>;
24
+ _text?: string;
25
+ }
26
+ const { description, icon, actions = ['copy'], _text } = Astro.props as Props;
27
+ const cursorHref = _text ? `https://cursor.com/link/prompt?text=${encodeURIComponent(_text)}` : null;
28
+ const showCursor = actions.includes('cursor') && cursorHref !== null && cursorHref.length <= 10000;
29
+ ---
30
+ <div class="wd-prompt">
31
+ <div class="wd-prompt-head">
32
+ {icon && <AppIcon icon={icon} class="wd-prompt-icon" />}
33
+ {description && <div class="wd-prompt-description" set:html={inlineMarkdown(description)} />}
34
+ <div class="wd-prompt-actions">
35
+ {actions.includes('copy') && (
36
+ <button type="button" class="wd-prompt-action" data-role="copy-prompt">Copy prompt</button>
37
+ )}
38
+ {showCursor && (
39
+ <a class="wd-prompt-action" href={cursorHref} target="_blank" rel="noopener noreferrer">Open in Cursor</a>
40
+ )}
41
+ </div>
42
+ </div>
43
+ <details class="wd-prompt-text">
44
+ <summary>Show prompt</summary>
45
+ <div class="wd-prompt-body"><slot /></div>
46
+ </details>
47
+ {_text && <template class="wd-prompt-source">{_text}</template>}
48
+ </div>
49
+ <script>
50
+ function initPrompts(root: ParentNode) {
51
+ root.querySelectorAll<HTMLButtonElement>('[data-role="copy-prompt"]').forEach((btn) => {
52
+ if (btn.dataset.wdInit) return;
53
+ btn.dataset.wdInit = 'true';
54
+ btn.addEventListener('click', async () => {
55
+ const card = btn.closest('.wd-prompt');
56
+ const source = card?.querySelector<HTMLTemplateElement>('template.wd-prompt-source');
57
+ const text = source ? source.content.textContent ?? '' : card?.querySelector('.wd-prompt-body')?.textContent?.trim() ?? '';
58
+ try {
59
+ await navigator.clipboard.writeText(text);
60
+ btn.textContent = 'Copied';
61
+ } catch {
62
+ btn.textContent = 'Copy failed';
63
+ }
64
+ setTimeout(() => (btn.textContent = 'Copy prompt'), 1500);
65
+ });
66
+ });
67
+ }
68
+ initPrompts(document);
69
+ document.addEventListener('astro:page-load', () => initPrompts(document));
70
+ </script>
71
+ <style>
72
+ .wd-prompt {
73
+ margin: 1.25rem 0;
74
+ border: 1px solid var(--wd-border);
75
+ border-radius: 0.6rem;
76
+ overflow: hidden;
77
+ }
78
+ .wd-prompt-head {
79
+ display: flex;
80
+ align-items: center;
81
+ gap: 0.75rem;
82
+ padding: 0.9rem 1.1rem;
83
+ }
84
+ :global(svg.wd-prompt-icon),
85
+ :global(span.wd-prompt-icon) {
86
+ flex-shrink: 0;
87
+ width: 1.1rem;
88
+ height: 1.1rem;
89
+ color: var(--wd-primary);
90
+ }
91
+ .wd-prompt-description {
92
+ flex: 1;
93
+ min-width: 0;
94
+ color: var(--wd-text);
95
+ }
96
+ .wd-prompt-actions {
97
+ display: flex;
98
+ flex-shrink: 0;
99
+ gap: 0.4rem;
100
+ }
101
+ .wd-prompt-action {
102
+ padding: 0.3rem 0.7rem;
103
+ border: 1px solid var(--wd-border);
104
+ border-radius: 0.4rem;
105
+ background: var(--wd-background);
106
+ color: var(--wd-text);
107
+ font-size: 0.8rem;
108
+ text-decoration: none;
109
+ cursor: pointer;
110
+ white-space: nowrap;
111
+ }
112
+ .wd-prompt-action:hover {
113
+ border-color: var(--wd-primary);
114
+ color: var(--wd-primary);
115
+ }
116
+ .wd-prompt-text {
117
+ border-top: 1px solid var(--wd-border);
118
+ background: var(--wd-surface);
119
+ }
120
+ .wd-prompt-text summary {
121
+ padding: 0.5rem 1.1rem;
122
+ font-size: 0.8rem;
123
+ color: var(--wd-text-muted);
124
+ cursor: pointer;
125
+ }
126
+ .wd-prompt-body {
127
+ padding: 0 1.1rem 0.9rem;
128
+ font-size: 0.9rem;
129
+ color: var(--wd-text-muted);
130
+ }
131
+ .wd-prompt-body :global(p:first-child) {
132
+ margin-top: 0;
133
+ }
134
+ .wd-prompt-body :global(p:last-child),
135
+ .wd-prompt-body :global(ul:last-child) {
136
+ margin-bottom: 0;
137
+ }
138
+ @media (max-width: 640px) {
139
+ .wd-prompt-head {
140
+ flex-wrap: wrap;
141
+ }
142
+ }
143
+ </style>
@@ -0,0 +1,18 @@
1
+ ---
2
+ // Mintlify's <ResponseField> - a response field row. Same props as
3
+ // Parameter.astro (`name`, `type`, `required`, `default`, `deprecated`,
4
+ // `pre`, `post`), so this is a straight pass-through; see Parameter.astro
5
+ // for why one component covers both directions.
6
+ import Parameter from './Parameter.astro';
7
+ interface Props {
8
+ name: string;
9
+ type?: string;
10
+ required?: boolean;
11
+ default?: unknown;
12
+ deprecated?: boolean;
13
+ pre?: string[];
14
+ post?: string[];
15
+ }
16
+ const props = Astro.props as Props;
17
+ ---
18
+ <Parameter {...props}><slot /></Parameter>
@@ -1,10 +1,29 @@
1
1
  ---
2
+ import AppIcon from './AppIcon.astro';
3
+ // `icon`, `stepNumber` and `titleSize` are Mintlify's. `icon` replaces the
4
+ // number in the step's circle. `stepNumber` sets this step's number; the
5
+ // steps after it keep counting from there (counter-set on the step itself
6
+ // runs before its own ::before increments the counter - see Steps.astro).
7
+ // `titleSize` renders the title as an h2/h3 instead of bold text.
2
8
  interface Props {
3
9
  title?: string;
10
+ icon?: string;
11
+ stepNumber?: number;
12
+ titleSize?: 'p' | 'h2' | 'h3';
4
13
  }
5
- const { title } = Astro.props as Props;
14
+ const { title, icon, stepNumber, titleSize = 'p' } = Astro.props as Props;
15
+ const style = typeof stepNumber === 'number' ? `counter-set: wd-step ${stepNumber - 1}` : undefined;
16
+ const TitleTag = titleSize === 'h2' || titleSize === 'h3' ? titleSize : 'p';
6
17
  ---
7
- <div class="wd-step">
8
- {title && <p class="wd-step-title"><strong>{title}</strong></p>}
18
+ <div class:list={['wd-step', { 'wd-step-has-icon': Boolean(icon) }]} style={style}>
19
+ {icon && <AppIcon icon={icon} class="wd-step-icon" />}
20
+ {
21
+ title &&
22
+ (TitleTag === 'p' ? (
23
+ <p class="wd-step-title"><strong>{title}</strong></p>
24
+ ) : (
25
+ <TitleTag class="wd-step-title">{title}</TitleTag>
26
+ ))
27
+ }
9
28
  <slot />
10
29
  </div>
@@ -12,6 +12,28 @@
12
12
  margin-left: 0.9rem;
13
13
  }
14
14
  .wd-step:last-child { border-color: transparent; padding-bottom: 0; }
15
+ /* A Step with an `icon` (see Step.astro) keeps the same circle - the
16
+ ::before below still increments the counter so later steps number
17
+ correctly - and draws the icon on top of it instead of the digit. */
18
+ .wd-step.wd-step-has-icon::before { color: transparent; }
19
+ .wd-step-icon {
20
+ position: absolute;
21
+ left: -0.95rem;
22
+ top: 0;
23
+ width: 1.8rem;
24
+ height: 1.8rem;
25
+ padding: 0.45rem;
26
+ box-sizing: border-box;
27
+ color: white;
28
+ z-index: 1;
29
+ }
30
+ span.wd-step-icon {
31
+ display: flex;
32
+ align-items: center;
33
+ justify-content: center;
34
+ padding: 0;
35
+ font-size: 0.85rem;
36
+ }
15
37
  .wd-step::before {
16
38
  counter-increment: wd-step;
17
39
  content: counter(wd-step);
@@ -1,9 +1,15 @@
1
1
  ---
2
+ import AppIcon from './AppIcon.astro';
3
+ // `icon` is Mintlify's: shown before the title in the tab's button. The
4
+ // button is built client-side by Tabs.astro, which moves this hidden,
5
+ // server-rendered icon into it.
2
6
  interface Props {
3
7
  title: string;
8
+ icon?: string;
4
9
  }
5
- const { title } = Astro.props as Props;
10
+ const { title, icon } = Astro.props as Props;
6
11
  ---
7
12
  <div class="wd-tab" data-title={title}>
13
+ {icon && <span class="wd-tab-icon-src" hidden><AppIcon icon={icon} class="wd-tabs-btn-icon" /></span>}
8
14
  <slot />
9
15
  </div>
@@ -1,4 +1,11 @@
1
- <div class="wd-tabs">
1
+ ---
2
+ // `defaultTabIndex` is Mintlify's: which tab (0-based) starts selected.
3
+ interface Props {
4
+ defaultTabIndex?: number;
5
+ }
6
+ const { defaultTabIndex = 0 } = Astro.props as Props;
7
+ ---
8
+ <div class="wd-tabs" data-default-index={defaultTabIndex}>
2
9
  <slot />
3
10
  </div>
4
11
  <script>
@@ -7,13 +14,20 @@
7
14
  if (tabs.dataset.wdInit) return;
8
15
  tabs.dataset.wdInit = 'true';
9
16
  const panels = Array.from(tabs.querySelectorAll<HTMLElement>(':scope > .wd-tab'));
17
+ const requested = Number(tabs.dataset.defaultIndex ?? 0);
18
+ const initial = Number.isInteger(requested) && requested >= 0 && requested < panels.length ? requested : 0;
10
19
  const nav = document.createElement('div');
11
20
  nav.className = 'wd-tabs-nav';
12
21
  panels.forEach((panel, i) => {
13
22
  const btn = document.createElement('button');
14
23
  btn.type = 'button';
15
- btn.textContent = panel.dataset.title ?? `Tab ${i + 1}`;
16
- btn.className = 'wd-tabs-btn' + (i === 0 ? ' active' : '');
24
+ // A Tab's `icon` is rendered server-side (astro-icon only runs at
25
+ // build time) into a hidden holder inside the panel - see
26
+ // Tab.astro - and moved into its button here.
27
+ const icon = panel.querySelector<HTMLElement>(':scope > .wd-tab-icon-src > *');
28
+ if (icon) btn.appendChild(icon);
29
+ btn.appendChild(document.createTextNode(panel.dataset.title ?? `Tab ${i + 1}`));
30
+ btn.className = 'wd-tabs-btn' + (i === initial ? ' active' : '');
17
31
  btn.addEventListener('click', () => {
18
32
  nav.querySelectorAll('.wd-tabs-btn').forEach((b) => b.classList.remove('active'));
19
33
  panels.forEach((p) => (p.style.display = 'none'));
@@ -21,7 +35,7 @@
21
35
  panel.style.display = 'block';
22
36
  });
23
37
  nav.appendChild(btn);
24
- panel.style.display = i === 0 ? 'block' : 'none';
38
+ panel.style.display = i === initial ? 'block' : 'none';
25
39
  });
26
40
  tabs.prepend(nav);
27
41
  });
@@ -45,6 +59,15 @@
45
59
  color: var(--wd-text-muted);
46
60
  border-bottom: 2px solid transparent;
47
61
  }
62
+ .wd-tabs-btn {
63
+ display: inline-flex;
64
+ align-items: center;
65
+ gap: 0.4rem;
66
+ }
67
+ svg.wd-tabs-btn-icon {
68
+ width: 1em;
69
+ height: 1em;
70
+ }
48
71
  .wd-tabs-btn.active {
49
72
  color: var(--wd-primary);
50
73
  border-bottom-color: var(--wd-primary);