Skip to content

Commit 45bface

Browse files
michaelmcneesclaude
andcommitted
feat(docs): add examples gallery with copy + download buttons
17 YAML workflow examples grouped into 6 categories (Getting Started, AI & LLM, Data Pipelines, Integrations, Triggers & Server, Parallel). Each example has Shiki syntax highlighting, copy-to-clipboard, and download-as-YAML buttons. Files read at build time from examples/. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 400bf92 commit 45bface

2 files changed

Lines changed: 154 additions & 0 deletions

File tree

site/src/data/docs-nav.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export const docsNav: NavGroup[] = [
8484
{
8585
title: 'Resources',
8686
items: [
87+
{ title: 'Examples', href: '/docs/examples' },
8788
{ title: 'Comparison', href: '/docs/comparison' },
8889
],
8990
},

site/src/pages/docs/examples.astro

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
---
2+
import fs from 'node:fs';
3+
import path from 'node:path';
4+
import Docs from '../../layouts/Docs.astro';
5+
import { Code } from 'astro:components';
6+
7+
const examplesDir = new URL('../../../../examples', import.meta.url).pathname;
8+
9+
function readExample(name: string): string {
10+
return fs.readFileSync(path.join(examplesDir, name), 'utf-8');
11+
}
12+
13+
interface Example {
14+
filename: string;
15+
description: string;
16+
content: string;
17+
}
18+
19+
function loadExample(filename: string): Example {
20+
const content = readExample(filename);
21+
// Extract description from YAML
22+
const descMatch = content.match(/^description:\s*>?\s*\n((?:\s{2,}.+\n?)+)/m);
23+
const descSimple = content.match(/^description:\s*(.+)$/m);
24+
let description = filename.replace('.yaml', '').replace(/-/g, ' ');
25+
if (descMatch) {
26+
description = descMatch[1].replace(/\n/g, ' ').replace(/\s+/g, ' ').trim();
27+
} else if (descSimple) {
28+
description = descSimple[1].trim();
29+
}
30+
return { filename, description, content };
31+
}
32+
33+
const categories = [
34+
{
35+
title: 'Getting Started',
36+
slug: 'getting-started',
37+
examples: [
38+
loadExample('hello-world.yaml'),
39+
loadExample('chained-requests.yaml'),
40+
loadExample('conditional-workflow.yaml'),
41+
],
42+
},
43+
{
44+
title: 'AI & LLM',
45+
slug: 'ai-llm',
46+
examples: [
47+
loadExample('fetch-and-summarize.yaml'),
48+
loadExample('ai-structured-extraction.yaml'),
49+
loadExample('ai-tool-use.yaml'),
50+
loadExample('multi-model-comparison.yaml'),
51+
],
52+
},
53+
{
54+
title: 'Data Pipelines',
55+
slug: 'data-pipelines',
56+
examples: [
57+
loadExample('data-pipeline.yaml'),
58+
loadExample('db-to-slack.yaml'),
59+
],
60+
},
61+
{
62+
title: 'Integrations',
63+
slug: 'integrations',
64+
examples: [
65+
loadExample('slack-notification.yaml'),
66+
loadExample('email-report.yaml'),
67+
loadExample('s3-backup.yaml'),
68+
loadExample('api-with-auth.yaml'),
69+
],
70+
},
71+
{
72+
title: 'Triggers & Server',
73+
slug: 'triggers-server',
74+
examples: [
75+
loadExample('webhook-processor.yaml'),
76+
loadExample('scheduled-health-check.yaml'),
77+
loadExample('cron-and-webhook.yaml'),
78+
],
79+
},
80+
{
81+
title: 'Parallel Execution',
82+
slug: 'parallel-execution',
83+
examples: [
84+
loadExample('parallel-fanout.yaml'),
85+
],
86+
},
87+
];
88+
89+
const headings = [
90+
...categories.map((cat) => ({
91+
depth: 2,
92+
slug: cat.slug,
93+
text: cat.title,
94+
})),
95+
];
96+
---
97+
98+
<Docs title="Examples" headings={headings}>
99+
<h1>Examples</h1>
100+
<p class="text-on-surface-variant text-lg mb-8">
101+
Ready-to-use workflow definitions covering common patterns. Copy any example, adjust the credentials
102+
and URLs, and <code class="text-sm bg-surface-container-high px-1.5 py-0.5 rounded">mantle apply</code> it.
103+
</p>
104+
105+
{categories.map((cat) => (
106+
<section class="mb-12">
107+
<h2 id={cat.slug}>{cat.title}</h2>
108+
109+
{cat.examples.map((ex) => (
110+
<div class="mb-8">
111+
<h3 id={ex.filename.replace('.yaml', '')} class="text-base font-semibold text-on-surface mb-1">
112+
{ex.filename}
113+
</h3>
114+
<p class="text-sm text-on-surface-variant mb-3">{ex.description}</p>
115+
116+
<div class="example-block terminal-block border border-outline-variant bg-surface-container-low overflow-hidden rounded-lg not-prose">
117+
<div class="flex items-center gap-2 px-4 py-2 border-b border-outline-variant bg-surface-container-highest">
118+
<span class="text-xs text-on-surface-variant font-mono flex-1">{ex.filename}</span>
119+
<button class="copy-btn text-xs text-on-surface-variant hover:text-primary font-mono transition-colors cursor-pointer">copy</button>
120+
<button class="download-btn text-xs text-on-surface-variant hover:text-primary font-mono transition-colors cursor-pointer" data-filename={ex.filename}>download</button>
121+
</div>
122+
<div class="p-4 overflow-x-auto max-h-[500px] overflow-y-auto [&_pre]:!m-0 [&_pre]:!p-0 [&_pre]:!bg-transparent [&_pre]:!border-0">
123+
<Code code={ex.content} lang="yaml" theme="github-dark" />
124+
</div>
125+
</div>
126+
</div>
127+
))}
128+
</section>
129+
))}
130+
131+
<script is:inline>
132+
function initExamplesPage() {
133+
document.querySelectorAll('.download-btn').forEach((btn) => {
134+
btn.addEventListener('click', () => {
135+
const block = btn.closest('.example-block');
136+
const code = block?.querySelector('code');
137+
if (!code) return;
138+
const text = code.textContent || '';
139+
const filename = btn.getAttribute('data-filename') || 'workflow.yaml';
140+
const blob = new Blob([text], { type: 'text/yaml' });
141+
const url = URL.createObjectURL(blob);
142+
const a = document.createElement('a');
143+
a.href = url;
144+
a.download = filename;
145+
a.click();
146+
URL.revokeObjectURL(url);
147+
});
148+
});
149+
}
150+
initExamplesPage();
151+
document.addEventListener('astro:after-swap', initExamplesPage);
152+
</script>
153+
</Docs>

0 commit comments

Comments
 (0)