Skip to content

Commit adcd155

Browse files
committed
Support custom example title and description via <script module> exports
Examples can now export title and description from a <script module> block to override the filename-derived title and add a description. These are extracted by the catalog generator, displayed in the component layout and example listings, and indexed for search (both browse page filter and command+k).
1 parent 17e310e commit adcd155

49 files changed

Lines changed: 258 additions & 56 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/scripts/generate-example-catalog.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,26 @@ function extractComponentsFromExample(
9696
return components.sort((a, b) => a.lineNumber - b.lineNumber);
9797
}
9898

99+
/**
100+
* Extract module-level exports (title, description) from a <script module> block
101+
*/
102+
function extractModuleExports(filePath: string): { title?: string; description?: string } {
103+
const content = fs.readFileSync(filePath, 'utf-8');
104+
const moduleMatch = content.match(/<script\s+module>([\s\S]*?)<\/script>/);
105+
if (!moduleMatch) return {};
106+
107+
const moduleContent = moduleMatch[1];
108+
const result: { title?: string; description?: string } = {};
109+
110+
const titleMatch = moduleContent.match(/export\s+const\s+title\s*=\s*['"`](.+?)['"`]/);
111+
if (titleMatch) result.title = titleMatch[1];
112+
113+
const descMatch = moduleContent.match(/export\s+const\s+description\s*=\s*['"`](.+?)['"`]/);
114+
if (descMatch) result.description = descMatch[1];
115+
116+
return result;
117+
}
118+
99119
/**
100120
* Get all examples for a specific component
101121
*/
@@ -119,12 +139,21 @@ function getComponentExamples(componentName: string, allComponents: string[]): E
119139
// Extract all components used in this example
120140
const componentsInExample = extractComponentsFromExample(filePath, allComponents);
121141

122-
examples.push({
142+
// Extract module-level exports
143+
const moduleExports = extractModuleExports(filePath);
144+
145+
const info: ExampleInfo = {
123146
name: exampleName,
124-
title: exampleName.replaceAll('-', ' '),
147+
title: moduleExports.title ?? exampleName.replaceAll('-', ' '),
125148
path: `/docs/components/${componentName}/${exampleName}`,
126149
components: componentsInExample
127-
});
150+
};
151+
152+
if (moduleExports.description) {
153+
info.description = moduleExports.description;
154+
}
155+
156+
examples.push(info);
128157
}
129158
}
130159

docs/src/examples/catalog/Axis.json

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4937,6 +4937,48 @@
49374937
"lineNumber": 59,
49384938
"line": "<Axis placement=\"bottom\" rule />"
49394939
},
4940+
{
4941+
"example": "basic",
4942+
"component": "Trail",
4943+
"path": "/docs/components/Trail/basic",
4944+
"lineNumber": 12,
4945+
"line": "<Axis placement=\"left\" grid rule />"
4946+
},
4947+
{
4948+
"example": "basic",
4949+
"component": "Trail",
4950+
"path": "/docs/components/Trail/basic",
4951+
"lineNumber": 13,
4952+
"line": "<Axis placement=\"bottom\" rule />"
4953+
},
4954+
{
4955+
"example": "tdf-stage",
4956+
"component": "Trail",
4957+
"path": "/docs/components/Trail/tdf-stage",
4958+
"lineNumber": 25,
4959+
"line": "<Axis placement=\"left\" grid label=\"Latitude\" />"
4960+
},
4961+
{
4962+
"example": "tdf-stage",
4963+
"component": "Trail",
4964+
"path": "/docs/components/Trail/tdf-stage",
4965+
"lineNumber": 26,
4966+
"line": "<Axis placement=\"bottom\" label=\"Longitude\" />"
4967+
},
4968+
{
4969+
"example": "variable-width",
4970+
"component": "Trail",
4971+
"path": "/docs/components/Trail/variable-width",
4972+
"lineNumber": 13,
4973+
"line": "<Axis placement=\"left\" grid rule />"
4974+
},
4975+
{
4976+
"example": "variable-width",
4977+
"component": "Trail",
4978+
"path": "/docs/components/Trail/variable-width",
4979+
"lineNumber": 14,
4980+
"line": "<Axis placement=\"bottom\" rule />"
4981+
},
49404982
{
49414983
"example": "pan-zoom-axes",
49424984
"component": "TransformContext",
@@ -5057,5 +5099,5 @@
50575099
"line": "<Axis placement=\"bottom\" rule />"
50585100
}
50595101
],
5060-
"updatedAt": "2026-04-02T19:16:00.334Z"
5102+
"updatedAt": "2026-04-03T15:31:23.718Z"
50615103
}

docs/src/examples/catalog/Chart.json

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2084,6 +2084,13 @@
20842084
"lineNumber": 58,
20852085
"line": "<Chart"
20862086
},
2087+
{
2088+
"example": "interpolating-projections",
2089+
"component": "GeoPath",
2090+
"path": "/docs/components/GeoPath/interpolating-projections",
2091+
"lineNumber": 169,
2092+
"line": "<Chart"
2093+
},
20872094
{
20882095
"example": "sketchy-globe",
20892096
"component": "GeoPath",
@@ -3820,6 +3827,27 @@
38203827
"lineNumber": 41,
38213828
"line": "<Chart"
38223829
},
3830+
{
3831+
"example": "basic",
3832+
"component": "Trail",
3833+
"path": "/docs/components/Trail/basic",
3834+
"lineNumber": 10,
3835+
"line": "<Chart {data} x=\"date\" y=\"value\" yDomain={[0, null]} yNice padding={25} height={300}>"
3836+
},
3837+
{
3838+
"example": "tdf-stage",
3839+
"component": "Trail",
3840+
"path": "/docs/components/Trail/tdf-stage",
3841+
"lineNumber": 15,
3842+
"line": "<Chart"
3843+
},
3844+
{
3845+
"example": "variable-width",
3846+
"component": "Trail",
3847+
"path": "/docs/components/Trail/variable-width",
3848+
"lineNumber": 11,
3849+
"line": "<Chart {data} x=\"date\" y=\"value\" yDomain={[0, null]} yNice r=\"value\" rScale={scaleLinear()} rRange={[2, 16]} padding={25} height={300}>"
3850+
},
38233851
{
38243852
"example": "pan-zoom-axes",
38253853
"component": "TransformContext",
@@ -3996,5 +4024,5 @@
39964024
"line": "<Chart {data} x=\"x\" y=\"y\" height={400}>"
39974025
}
39984026
],
3999-
"updatedAt": "2026-04-02T19:16:00.836Z"
4027+
"updatedAt": "2026-04-03T15:31:24.127Z"
40004028
}

docs/src/examples/catalog/GeoPath.json

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,33 @@
144144
}
145145
]
146146
},
147+
{
148+
"name": "interpolating-projections",
149+
"title": "interpolating projections",
150+
"path": "/docs/components/GeoPath/interpolating-projections",
151+
"components": [
152+
{
153+
"component": "Chart",
154+
"lineNumber": 169,
155+
"line": "<Chart"
156+
},
157+
{
158+
"component": "Layer",
159+
"lineNumber": 177,
160+
"line": "<Layer>"
161+
},
162+
{
163+
"component": "GeoPath",
164+
"lineNumber": 178,
165+
"line": "<GeoPath geojson={{ type: 'Sphere' }} class=\"stroke-surface-content/20 fill-none\" />"
166+
},
167+
{
168+
"component": "Graticule",
169+
"lineNumber": 180,
170+
"line": "<Graticule class=\"stroke-surface-content/10\" />"
171+
}
172+
]
173+
},
147174
{
148175
"name": "sketchy-globe",
149176
"title": "sketchy globe",
@@ -835,6 +862,20 @@
835862
"lineNumber": 81,
836863
"line": "<GeoPath"
837864
},
865+
{
866+
"example": "interpolating-projections",
867+
"component": "GeoPath",
868+
"path": "/docs/components/GeoPath/interpolating-projections",
869+
"lineNumber": 178,
870+
"line": "<GeoPath geojson={{ type: 'Sphere' }} class=\"stroke-surface-content/20 fill-none\" />"
871+
},
872+
{
873+
"example": "interpolating-projections",
874+
"component": "GeoPath",
875+
"path": "/docs/components/GeoPath/interpolating-projections",
876+
"lineNumber": 182,
877+
"line": "<GeoPath geojson={land} class=\"fill-surface-content/15\" />"
878+
},
838879
{
839880
"example": "sketchy-globe",
840881
"component": "GeoPath",
@@ -1382,5 +1423,5 @@
13821423
"line": "<GeoPath"
13831424
}
13841425
],
1385-
"updatedAt": "2026-04-02T19:16:01.400Z"
1426+
"updatedAt": "2026-04-03T15:31:24.682Z"
13861427
}

docs/src/examples/catalog/Graticule.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@
7070
"lineNumber": 74,
7171
"line": "<Graticule class=\"stroke-surface-content/20\" />"
7272
},
73+
{
74+
"example": "interpolating-projections",
75+
"component": "GeoPath",
76+
"path": "/docs/components/GeoPath/interpolating-projections",
77+
"lineNumber": 180,
78+
"line": "<Graticule class=\"stroke-surface-content/10\" />"
79+
},
7380
{
7481
"example": "sketchy-globe",
7582
"component": "GeoPath",
@@ -176,5 +183,5 @@
176183
"line": "<Graticule class=\"stroke-surface-content/10\" />"
177184
}
178185
],
179-
"updatedAt": "2026-04-02T19:16:01.558Z"
186+
"updatedAt": "2026-04-03T15:31:24.827Z"
180187
}

docs/src/examples/catalog/Layer.json

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1885,6 +1885,13 @@
18851885
"lineNumber": 72,
18861886
"line": "<Layer>"
18871887
},
1888+
{
1889+
"example": "interpolating-projections",
1890+
"component": "GeoPath",
1891+
"path": "/docs/components/GeoPath/interpolating-projections",
1892+
"lineNumber": 177,
1893+
"line": "<Layer>"
1894+
},
18881895
{
18891896
"example": "sketchy-globe",
18901897
"component": "GeoPath",
@@ -3726,6 +3733,27 @@
37263733
"lineNumber": 57,
37273734
"line": "<Layer>"
37283735
},
3736+
{
3737+
"example": "basic",
3738+
"component": "Trail",
3739+
"path": "/docs/components/Trail/basic",
3740+
"lineNumber": 11,
3741+
"line": "<Layer>"
3742+
},
3743+
{
3744+
"example": "tdf-stage",
3745+
"component": "Trail",
3746+
"path": "/docs/components/Trail/tdf-stage",
3747+
"lineNumber": 24,
3748+
"line": "<Layer>"
3749+
},
3750+
{
3751+
"example": "variable-width",
3752+
"component": "Trail",
3753+
"path": "/docs/components/Trail/variable-width",
3754+
"lineNumber": 12,
3755+
"line": "<Layer>"
3756+
},
37293757
{
37303758
"example": "pan-zoom-axes",
37313759
"component": "TransformContext",
@@ -3916,5 +3944,5 @@
39163944
"line": "<Layer onpointermove={onPointerMove}>"
39173945
}
39183946
],
3919-
"updatedAt": "2026-04-02T19:16:01.850Z"
3947+
"updatedAt": "2026-04-03T15:31:25.004Z"
39203948
}

docs/src/examples/catalog/Spline.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,13 @@
13381338
"lineNumber": 45,
13391339
"line": "<Spline y=\"value\" {curve} class=\"stroke-[1.5]\" />"
13401340
},
1341+
{
1342+
"example": "tdf-stage",
1343+
"component": "Trail",
1344+
"path": "/docs/components/Trail/tdf-stage",
1345+
"lineNumber": 28,
1346+
"line": "<Spline class=\"stroke-1 stroke-surface-content\" />"
1347+
},
13411348
{
13421349
"example": "playground",
13431350
"component": "TransformContext",
@@ -1346,5 +1353,5 @@
13461353
"line": "<Spline curve={config.curve} motion=\"tween\" />"
13471354
}
13481355
],
1349-
"updatedAt": "2026-04-02T19:16:02.594Z"
1356+
"updatedAt": "2026-04-03T15:31:25.665Z"
13501357
}

docs/src/examples/catalog/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ export interface ComponentUsageInExample {
2323
export interface ExampleInfo {
2424
/** The name of the example (filename without .svelte extension) */
2525
name: string;
26-
/** Human-readable title (name with hyphens replaced by spaces) */
26+
/** Human-readable title (from <script module> export or name with hyphens replaced by spaces) */
2727
title: string;
28+
/** Optional description (from <script module> export) */
29+
description?: string;
2830
/** The URL path to view the example */
2931
path: string;
3032
/** All components used in this example */

docs/src/lib/components/ExampleLink.svelte

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@
1212
let {
1313
component,
1414
example,
15+
title,
1516
showComponent,
1617
variant = 'default',
1718
aspect = undefined,
1819
...restProps
1920
}: {
2021
component: string;
2122
example: string;
23+
title?: string;
2224
showComponent?: boolean;
2325
variant?: ComponentProps<typeof ImageLink>['variant'];
2426
aspect?: ComponentProps<typeof ExampleScreenshot>['aspect'];
@@ -67,6 +69,6 @@
6769
class="shrink-0 text-surface-content/50 group-hover:text-primary-content/50"
6870
/>
6971
{/if}
70-
<span class="first-letter:capitalize truncate">{example.replaceAll('-', ' ')}</span>
72+
<span class="first-letter:capitalize truncate">{title ?? example.replaceAll('-', ' ')}</span>
7173
{/snippet}
7274
</ImageLink>

docs/src/lib/components/ExampleListing.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@
124124
class="grid grid-cols-(--column-count) gap-4"
125125
>
126126
{#each examples as example (example.name)}
127-
<ExampleLink component={catalog.component} example={example.name} />
127+
<ExampleLink component={catalog.component} example={example.name} title={example.title} />
128128
{/each}
129129
</div>
130130
{:else if catalog.examples?.length}

0 commit comments

Comments
 (0)