@writedocs/generator 0.4.7 → 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.
- package/astro.config.mjs +7 -0
- package/package.json +1 -1
- package/src/components/Color.astro +61 -0
- package/src/components/ColorItem.astro +93 -0
- package/src/components/ColorRow.astro +39 -0
- package/src/components/GitHubRepo.astro +156 -0
- package/src/components/Panel.astro +11 -0
- package/src/components/Prompt.astro +143 -0
- package/src/components/Tile.astro +65 -0
- package/src/components/Tree.astro +213 -0
- package/src/components/TreeFile.astro +17 -0
- package/src/components/TreeFolder.astro +28 -0
- package/src/components/Update.astro +171 -0
- package/src/components/View.astro +214 -0
- package/src/components/Visibility.astro +11 -0
- package/src/components/compound.ts +21 -0
- package/src/components/index.ts +7 -0
- package/src/lib/inline-markdown.js +29 -0
- package/src/lib/mdx-inject-builtins.js +15 -2
- package/src/lib/mdx-mintlify.js +99 -0
- package/src/lib/mdx-title-anchor-ids.js +7 -2
- package/src/lib/visibility.js +29 -0
- package/src/pages/[...slug].astro +23 -1
- package/src/pages/[...slug].md.ts +4 -1
- package/src/pages/llms-full.txt.ts +3 -1
package/astro.config.mjs
CHANGED
|
@@ -26,6 +26,7 @@ import { remarkAutoHydrateSnippets } from './src/lib/mdx-auto-hydrate.js';
|
|
|
26
26
|
import { remarkInjectBuiltinComponents } from './src/lib/mdx-inject-builtins.js';
|
|
27
27
|
import { remarkTitleAnchorIds } from './src/lib/mdx-title-anchor-ids.js';
|
|
28
28
|
import { remarkSubstituteVariables } from './src/lib/mdx-substitute-variables.js';
|
|
29
|
+
import { remarkMintlifyTreeLists, remarkMintlifyPromptText } from './src/lib/mdx-mintlify.js';
|
|
29
30
|
import { writedocsTempDir, writedocsBuildStagingDir } from './src/lib/writedocs-temp-dir.js';
|
|
30
31
|
import { stylesAssetFallback } from './src/lib/styles-asset-integration.js';
|
|
31
32
|
import {
|
|
@@ -363,6 +364,12 @@ export default defineConfig({
|
|
|
363
364
|
// as raw text first.
|
|
364
365
|
remarkPlugins: [
|
|
365
366
|
remarkMath,
|
|
367
|
+
// Mintlify's Markdown-list <Tree> and <Prompt> source text - see
|
|
368
|
+
// lib/mdx-mintlify.js. Before remarkInjectBuiltinComponents, so
|
|
369
|
+
// the <Tree.Folder>/<Tree.File> elements they add are already in
|
|
370
|
+
// the tree when it looks for components to import.
|
|
371
|
+
remarkMintlifyTreeLists,
|
|
372
|
+
remarkMintlifyPromptText,
|
|
366
373
|
remarkInjectBuiltinComponents,
|
|
367
374
|
// Runs after remarkInjectBuiltinComponents (order doesn't actually
|
|
368
375
|
// matter between them - that plugin only ever prepends an import
|
package/package.json
CHANGED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
// Mintlify's <Color> - a palette of <Color.Item> swatches. `variant`:
|
|
3
|
+
// compact (default) - one grid of swatches.
|
|
4
|
+
// table - <Color.Row title="..."> rows, each a titled line of
|
|
5
|
+
// swatches.
|
|
6
|
+
// Color.Row/Color.Item are ColorRow.astro/ColorItem.astro, attached as
|
|
7
|
+
// properties in components/compound.ts so the dotted names resolve in MDX.
|
|
8
|
+
interface Props {
|
|
9
|
+
variant?: 'compact' | 'table';
|
|
10
|
+
}
|
|
11
|
+
const { variant = 'compact' } = Astro.props as Props;
|
|
12
|
+
---
|
|
13
|
+
<div class:list={['wd-color', `wd-color-${variant}`]}><slot /></div>
|
|
14
|
+
<script>
|
|
15
|
+
// Click (or Enter/Space) on a swatch copies the value currently shown -
|
|
16
|
+
// for a light/dark pair, the one matching the site's theme right now.
|
|
17
|
+
function initColorCopy(root: ParentNode) {
|
|
18
|
+
root.querySelectorAll<HTMLElement>('.wd-color-item[data-light]').forEach((item) => {
|
|
19
|
+
if (item.dataset.wdInit) return;
|
|
20
|
+
item.dataset.wdInit = 'true';
|
|
21
|
+
const copy = async () => {
|
|
22
|
+
const dark = document.documentElement.dataset.theme === 'dark';
|
|
23
|
+
const value = (dark ? item.dataset.dark : item.dataset.light) ?? '';
|
|
24
|
+
const label = item.querySelector<HTMLElement>('.wd-color-copied');
|
|
25
|
+
try {
|
|
26
|
+
await navigator.clipboard.writeText(value);
|
|
27
|
+
if (label) label.textContent = 'Copied';
|
|
28
|
+
} catch {
|
|
29
|
+
if (label) label.textContent = 'Copy failed';
|
|
30
|
+
}
|
|
31
|
+
setTimeout(() => label && (label.textContent = ''), 1200);
|
|
32
|
+
};
|
|
33
|
+
item.addEventListener('click', copy);
|
|
34
|
+
item.addEventListener('keydown', (e) => {
|
|
35
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
copy();
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
initColorCopy(document);
|
|
43
|
+
document.addEventListener('astro:page-load', () => initColorCopy(document));
|
|
44
|
+
</script>
|
|
45
|
+
<style>
|
|
46
|
+
.wd-color {
|
|
47
|
+
margin: 1.25rem 0;
|
|
48
|
+
}
|
|
49
|
+
.wd-color-compact {
|
|
50
|
+
display: grid;
|
|
51
|
+
grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
|
|
52
|
+
gap: 1rem;
|
|
53
|
+
}
|
|
54
|
+
.wd-color-table {
|
|
55
|
+
display: flex;
|
|
56
|
+
flex-direction: column;
|
|
57
|
+
border: 1px solid var(--wd-border);
|
|
58
|
+
border-radius: 0.6rem;
|
|
59
|
+
overflow: hidden;
|
|
60
|
+
}
|
|
61
|
+
</style>
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
// Mintlify's <Color.Item name="..." value="..."> - one swatch. `value` is
|
|
3
|
+
// any CSS color, or { light, dark } for a theme-aware pair: the swatch and
|
|
4
|
+
// the value text switch with the site's theme toggle ([data-theme] on
|
|
5
|
+
// <html>), via the two custom properties below. Click copies the value
|
|
6
|
+
// (Color.astro's script).
|
|
7
|
+
interface Props {
|
|
8
|
+
name?: string;
|
|
9
|
+
value?: string | { light: string; dark: string };
|
|
10
|
+
}
|
|
11
|
+
const { name, value } = Astro.props as Props;
|
|
12
|
+
const light = typeof value === 'string' ? value : value?.light;
|
|
13
|
+
const dark = typeof value === 'string' ? value : value?.dark;
|
|
14
|
+
const themed = typeof value === 'object' && value !== null && light !== dark;
|
|
15
|
+
const style = light ? `--wd-swatch-light: ${light}; --wd-swatch-dark: ${dark ?? light}` : undefined;
|
|
16
|
+
---
|
|
17
|
+
<div
|
|
18
|
+
class="wd-color-item"
|
|
19
|
+
data-light={light}
|
|
20
|
+
data-dark={dark}
|
|
21
|
+
role={light ? 'button' : undefined}
|
|
22
|
+
tabindex={light ? 0 : undefined}
|
|
23
|
+
title={light ? 'Copy value' : undefined}
|
|
24
|
+
style={style}
|
|
25
|
+
>
|
|
26
|
+
<div class="wd-color-swatch"></div>
|
|
27
|
+
<div class="wd-color-name">{name}</div>
|
|
28
|
+
{light && (
|
|
29
|
+
<div class="wd-color-value">
|
|
30
|
+
{themed ? (
|
|
31
|
+
<>
|
|
32
|
+
<span class="wd-color-value-light">{light}</span>
|
|
33
|
+
<span class="wd-color-value-dark">{dark}</span>
|
|
34
|
+
</>
|
|
35
|
+
) : (
|
|
36
|
+
light
|
|
37
|
+
)}
|
|
38
|
+
<span class="wd-color-copied" aria-live="polite"></span>
|
|
39
|
+
</div>
|
|
40
|
+
)}
|
|
41
|
+
</div>
|
|
42
|
+
<style>
|
|
43
|
+
.wd-color-item {
|
|
44
|
+
min-width: 0;
|
|
45
|
+
}
|
|
46
|
+
.wd-color-item[role='button'] {
|
|
47
|
+
cursor: pointer;
|
|
48
|
+
}
|
|
49
|
+
/* A checkerboard under the swatch, so a translucent color (rgba, hsla)
|
|
50
|
+
reads as translucent rather than as a lighter solid. */
|
|
51
|
+
.wd-color-swatch {
|
|
52
|
+
height: 3.5rem;
|
|
53
|
+
border: 1px solid var(--wd-border);
|
|
54
|
+
border-radius: 0.5rem;
|
|
55
|
+
background:
|
|
56
|
+
linear-gradient(var(--wd-swatch-light, transparent), var(--wd-swatch-light, transparent)),
|
|
57
|
+
repeating-conic-gradient(var(--wd-surface) 0% 25%, var(--wd-background) 0% 50%) 0 0 / 12px 12px;
|
|
58
|
+
}
|
|
59
|
+
:global([data-theme='dark']) .wd-color-swatch {
|
|
60
|
+
background:
|
|
61
|
+
linear-gradient(var(--wd-swatch-dark, transparent), var(--wd-swatch-dark, transparent)),
|
|
62
|
+
repeating-conic-gradient(var(--wd-surface) 0% 25%, var(--wd-background) 0% 50%) 0 0 / 12px 12px;
|
|
63
|
+
}
|
|
64
|
+
.wd-color-item[role='button']:hover .wd-color-swatch,
|
|
65
|
+
.wd-color-item[role='button']:focus-visible .wd-color-swatch {
|
|
66
|
+
border-color: var(--wd-primary);
|
|
67
|
+
}
|
|
68
|
+
.wd-color-name {
|
|
69
|
+
margin-top: 0.4rem;
|
|
70
|
+
font-size: 0.85rem;
|
|
71
|
+
font-weight: 600;
|
|
72
|
+
overflow-wrap: anywhere;
|
|
73
|
+
}
|
|
74
|
+
.wd-color-value {
|
|
75
|
+
font-family: monospace;
|
|
76
|
+
font-size: 0.75rem;
|
|
77
|
+
color: var(--wd-text-muted);
|
|
78
|
+
overflow-wrap: anywhere;
|
|
79
|
+
}
|
|
80
|
+
.wd-color-value-dark {
|
|
81
|
+
display: none;
|
|
82
|
+
}
|
|
83
|
+
:global([data-theme='dark']) .wd-color-value-light {
|
|
84
|
+
display: none;
|
|
85
|
+
}
|
|
86
|
+
:global([data-theme='dark']) .wd-color-value-dark {
|
|
87
|
+
display: inline;
|
|
88
|
+
}
|
|
89
|
+
.wd-color-copied {
|
|
90
|
+
margin-left: 0.35rem;
|
|
91
|
+
color: var(--wd-primary);
|
|
92
|
+
}
|
|
93
|
+
</style>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
// Mintlify's <Color.Row title="..."> - one titled row inside
|
|
3
|
+
// <Color variant="table">. `title` takes inline Markdown.
|
|
4
|
+
import { inlineMarkdown } from '../lib/inline-markdown.js';
|
|
5
|
+
interface Props {
|
|
6
|
+
title?: string;
|
|
7
|
+
}
|
|
8
|
+
const { title } = Astro.props as Props;
|
|
9
|
+
---
|
|
10
|
+
<div class="wd-color-row">
|
|
11
|
+
<div class="wd-color-row-title" set:html={inlineMarkdown(title ?? '')} />
|
|
12
|
+
<div class="wd-color-row-items"><slot /></div>
|
|
13
|
+
</div>
|
|
14
|
+
<style>
|
|
15
|
+
.wd-color-row {
|
|
16
|
+
display: grid;
|
|
17
|
+
grid-template-columns: 9rem minmax(0, 1fr);
|
|
18
|
+
gap: 1rem;
|
|
19
|
+
align-items: center;
|
|
20
|
+
padding: 1rem;
|
|
21
|
+
}
|
|
22
|
+
.wd-color-row + .wd-color-row {
|
|
23
|
+
border-top: 1px solid var(--wd-border);
|
|
24
|
+
}
|
|
25
|
+
.wd-color-row-title {
|
|
26
|
+
font-weight: 600;
|
|
27
|
+
font-size: 0.9rem;
|
|
28
|
+
}
|
|
29
|
+
.wd-color-row-items {
|
|
30
|
+
display: grid;
|
|
31
|
+
grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr));
|
|
32
|
+
gap: 1rem;
|
|
33
|
+
}
|
|
34
|
+
@media (max-width: 640px) {
|
|
35
|
+
.wd-color-row {
|
|
36
|
+
grid-template-columns: minmax(0, 1fr);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
</style>
|
|
@@ -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">★</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>
|
|
@@ -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,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,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
// Mintlify's <Tile> - a visual preview (the slot, usually an image or a
|
|
3
|
+
// light/dark image pair) on a patterned background, with a title and
|
|
4
|
+
// description under it. Usually laid out in a <Columns> grid. `title`
|
|
5
|
+
// takes inline Markdown (`code`, **bold**, *italic*), like Mintlify's.
|
|
6
|
+
import { inlineMarkdown } from '../lib/inline-markdown.js';
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
href?: string;
|
|
10
|
+
title?: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
}
|
|
13
|
+
const { href, title, description } = Astro.props as Props;
|
|
14
|
+
const Tag = href ? 'a' : 'div';
|
|
15
|
+
---
|
|
16
|
+
<Tag class="wd-tile" href={href}>
|
|
17
|
+
<div class="wd-tile-preview"><slot /></div>
|
|
18
|
+
{title && <div class="wd-tile-title" set:html={inlineMarkdown(title)} />}
|
|
19
|
+
{description && <div class="wd-tile-description">{description}</div>}
|
|
20
|
+
</Tag>
|
|
21
|
+
<style>
|
|
22
|
+
.wd-tile {
|
|
23
|
+
display: block;
|
|
24
|
+
text-decoration: none;
|
|
25
|
+
color: inherit;
|
|
26
|
+
}
|
|
27
|
+
/* Dotted pattern behind the preview, drawn from the theme's own border
|
|
28
|
+
color so it reads in both themes. */
|
|
29
|
+
.wd-tile-preview {
|
|
30
|
+
display: flex;
|
|
31
|
+
align-items: center;
|
|
32
|
+
justify-content: center;
|
|
33
|
+
height: 10rem;
|
|
34
|
+
overflow: hidden;
|
|
35
|
+
border: 1px solid var(--wd-border);
|
|
36
|
+
border-radius: 0.6rem;
|
|
37
|
+
background-color: var(--wd-surface);
|
|
38
|
+
background-image: radial-gradient(var(--wd-border) 1px, transparent 1px);
|
|
39
|
+
background-size: 14px 14px;
|
|
40
|
+
transition: border-color 0.15s ease;
|
|
41
|
+
}
|
|
42
|
+
a.wd-tile:hover .wd-tile-preview {
|
|
43
|
+
border-color: var(--wd-primary);
|
|
44
|
+
}
|
|
45
|
+
.wd-tile-preview :global(img),
|
|
46
|
+
.wd-tile-preview :global(svg) {
|
|
47
|
+
max-width: 100%;
|
|
48
|
+
max-height: 100%;
|
|
49
|
+
object-fit: contain;
|
|
50
|
+
margin: 0;
|
|
51
|
+
}
|
|
52
|
+
.wd-tile-preview :global(p) {
|
|
53
|
+
display: contents;
|
|
54
|
+
}
|
|
55
|
+
.wd-tile-title {
|
|
56
|
+
margin-top: 0.6rem;
|
|
57
|
+
font-weight: 600;
|
|
58
|
+
color: var(--wd-text);
|
|
59
|
+
}
|
|
60
|
+
.wd-tile-description {
|
|
61
|
+
margin-top: 0.15rem;
|
|
62
|
+
font-size: 0.875rem;
|
|
63
|
+
color: var(--wd-text-muted);
|
|
64
|
+
}
|
|
65
|
+
</style>
|