@redsift/ds-mcp-server 12.3.1-muiv6-alpha.2
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/README.md +203 -0
- package/consumer-instructions/.cursorrules +80 -0
- package/consumer-instructions/.windsurfrules +80 -0
- package/consumer-instructions/CLAUDE.md +87 -0
- package/consumer-instructions/redsift-design-system.instructions.md +87 -0
- package/data/demos/patterns/crossfiltered-datagrid-page/example.tsx +750 -0
- package/data/demos/patterns/crossfiltered-datagrid-page/with-empty-state.tsx +111 -0
- package/data/demos/patterns/crossfiltered-datagrid-page/with-error.tsx +122 -0
- package/data/demos/patterns/crossfiltered-datagrid-page/with-loading.tsx +88 -0
- package/data/demos/patterns/datagrid-page/example.tsx +521 -0
- package/data/demos/patterns/datagrid-page/with-empty-state.tsx +80 -0
- package/data/demos/patterns/datagrid-page/with-error.tsx +96 -0
- package/data/demos/patterns/datagrid-page/with-loading.tsx +57 -0
- package/data/demos/patterns/drilldown-datagrid-page/example.tsx +715 -0
- package/data/demos/patterns/drilldown-datagrid-page/with-empty-state.tsx +113 -0
- package/data/demos/patterns/drilldown-datagrid-page/with-error.tsx +125 -0
- package/data/demos/patterns/drilldown-datagrid-page/with-loading.tsx +90 -0
- package/data/demos/patterns/server-datagrid-page/example.tsx +757 -0
- package/data/demos/patterns/server-datagrid-page/with-empty-state.tsx +107 -0
- package/data/demos/patterns/server-datagrid-page/with-error.tsx +107 -0
- package/data/demos/patterns/server-datagrid-page/with-loading.tsx +63 -0
- package/data/docs/components/charts/Arc.json +179 -0
- package/data/docs/components/charts/Arcs.json +104 -0
- package/data/docs/components/charts/Axis.json +269 -0
- package/data/docs/components/charts/Bar.json +236 -0
- package/data/docs/components/charts/BarChart.json +852 -0
- package/data/docs/components/charts/BarChartBars.json +18 -0
- package/data/docs/components/charts/BarChartGroupedTooltip.json +9 -0
- package/data/docs/components/charts/BarChartOverlay.json +34 -0
- package/data/docs/components/charts/BarChartSection.json +94 -0
- package/data/docs/components/charts/BaseBarChart.json +852 -0
- package/data/docs/components/charts/ChartContainerTitle.json +574 -0
- package/data/docs/components/charts/DataPoint.json +160 -0
- package/data/docs/components/charts/Dot.json +191 -0
- package/data/docs/components/charts/EmptyBarChart.json +852 -0
- package/data/docs/components/charts/EmptyLineChart.json +753 -0
- package/data/docs/components/charts/EmptyPieChart.json +826 -0
- package/data/docs/components/charts/EmptyScatterPlot.json +802 -0
- package/data/docs/components/charts/Legend.json +510 -0
- package/data/docs/components/charts/LegendItem.json +128 -0
- package/data/docs/components/charts/Line.json +69 -0
- package/data/docs/components/charts/LineChart.json +753 -0
- package/data/docs/components/charts/LoadingBarChart.json +852 -0
- package/data/docs/components/charts/LoadingLineChart.json +753 -0
- package/data/docs/components/charts/LoadingPieChart.json +826 -0
- package/data/docs/components/charts/LoadingScatterPlot.json +802 -0
- package/data/docs/components/charts/PieChart.json +826 -0
- package/data/docs/components/charts/RenderedLineChart.json +753 -0
- package/data/docs/components/charts/RenderedLinearBarChart.json +823 -0
- package/data/docs/components/charts/RenderedOrdinalBarChart.json +852 -0
- package/data/docs/components/charts/RenderedPieChart.json +826 -0
- package/data/docs/components/charts/RenderedScatterPlot.json +802 -0
- package/data/docs/components/charts/ScatterPlot.json +802 -0
- package/data/docs/components/charts/getAxisType.json +9 -0
- package/data/docs/components/charts/getComponentPosition.json +9 -0
- package/data/docs/components/dashboard/ChartEmptyState.json +42 -0
- package/data/docs/components/dashboard/Dashboard.json +26 -0
- package/data/docs/components/dashboard/DataCard.json +300 -0
- package/data/docs/components/dashboard/DataCardBody.json +431 -0
- package/data/docs/components/dashboard/DataCardHeader.json +304 -0
- package/data/docs/components/dashboard/DataCardListbox.json +529 -0
- package/data/docs/components/dashboard/DataRow.json +539 -0
- package/data/docs/components/dashboard/DrilldownItem.json +342 -0
- package/data/docs/components/dashboard/FilterableBarChart.json +83 -0
- package/data/docs/components/dashboard/FilterableDataGrid.json +83 -0
- package/data/docs/components/dashboard/FilterablePieChart.json +83 -0
- package/data/docs/components/dashboard/FilterableScatterPlot.json +83 -0
- package/data/docs/components/dashboard/PdfDocument.json +58 -0
- package/data/docs/components/dashboard/PdfExportButton.json +458 -0
- package/data/docs/components/dashboard/TimeSeriesBarChart.json +172 -0
- package/data/docs/components/dashboard/WithFilters.json +83 -0
- package/data/docs/components/design-system/ActiveDescendantListbox.json +521 -0
- package/data/docs/components/design-system/Alert.json +349 -0
- package/data/docs/components/design-system/AppBar.json +64 -0
- package/data/docs/components/design-system/AppContainer.json +67 -0
- package/data/docs/components/design-system/AppContent.json +566 -0
- package/data/docs/components/design-system/AppSidePanel.json +87 -0
- package/data/docs/components/design-system/Badge.json +293 -0
- package/data/docs/components/design-system/BaseBreadcrumbs.json +298 -0
- package/data/docs/components/design-system/BaseGrid.json +543 -0
- package/data/docs/components/design-system/BaseSkeleton.json +338 -0
- package/data/docs/components/design-system/BreadcrumbItem.json +231 -0
- package/data/docs/components/design-system/Breadcrumbs.json +298 -0
- package/data/docs/components/design-system/Button.json +402 -0
- package/data/docs/components/design-system/ButtonGroup.json +415 -0
- package/data/docs/components/design-system/ButtonLink.json +568 -0
- package/data/docs/components/design-system/Card.json +328 -0
- package/data/docs/components/design-system/CardActions.json +431 -0
- package/data/docs/components/design-system/CardBody.json +431 -0
- package/data/docs/components/design-system/CardHeader.json +428 -0
- package/data/docs/components/design-system/Checkbox.json +426 -0
- package/data/docs/components/design-system/CheckboxGroup.json +382 -0
- package/data/docs/components/design-system/ConditionalWrapper.json +40 -0
- package/data/docs/components/design-system/DetailedCard.json +401 -0
- package/data/docs/components/design-system/DetailedCardCollapsibleSectionItems.json +29 -0
- package/data/docs/components/design-system/DetailedCardHeader.json +37 -0
- package/data/docs/components/design-system/DetailedCardSection.json +90 -0
- package/data/docs/components/design-system/DetailedCardSectionItem.json +109 -0
- package/data/docs/components/design-system/Flexbox.json +523 -0
- package/data/docs/components/design-system/FocusWithinGroup.json +9 -0
- package/data/docs/components/design-system/Grid.json +543 -0
- package/data/docs/components/design-system/GridItem.json +388 -0
- package/data/docs/components/design-system/Heading.json +390 -0
- package/data/docs/components/design-system/Icon.json +325 -0
- package/data/docs/components/design-system/IconButton.json +371 -0
- package/data/docs/components/design-system/IconButtonLink.json +588 -0
- package/data/docs/components/design-system/Item.json +554 -0
- package/data/docs/components/design-system/Link.json +552 -0
- package/data/docs/components/design-system/LinkButton.json +397 -0
- package/data/docs/components/design-system/Listbox.json +529 -0
- package/data/docs/components/design-system/Number.json +773 -0
- package/data/docs/components/design-system/NumberField.json +594 -0
- package/data/docs/components/design-system/Pill.json +378 -0
- package/data/docs/components/design-system/ProgressBar.json +121 -0
- package/data/docs/components/design-system/RadarSvgLinearGradient.json +9 -0
- package/data/docs/components/design-system/Radio.json +415 -0
- package/data/docs/components/design-system/RadioGroup.json +382 -0
- package/data/docs/components/design-system/RenderedListboxItem.json +18 -0
- package/data/docs/components/design-system/RovingTabindexListbox.json +521 -0
- package/data/docs/components/design-system/Shield.json +360 -0
- package/data/docs/components/design-system/SideNavigationMenu.json +144 -0
- package/data/docs/components/design-system/SideNavigationMenuBar.json +89 -0
- package/data/docs/components/design-system/SideNavigationMenuItem.json +319 -0
- package/data/docs/components/design-system/Skeleton.json +338 -0
- package/data/docs/components/design-system/SkeletonCircle.json +338 -0
- package/data/docs/components/design-system/SkeletonText.json +371 -0
- package/data/docs/components/design-system/Spinner.json +291 -0
- package/data/docs/components/design-system/Switch.json +415 -0
- package/data/docs/components/design-system/SwitchGroup.json +382 -0
- package/data/docs/components/design-system/Text.json +418 -0
- package/data/docs/components/design-system/TextArea.json +501 -0
- package/data/docs/components/design-system/TextField.json +539 -0
- package/data/docs/components/design-system/sizeToDimension.json +9 -0
- package/data/docs/components/pickers/BaseCombobox.json +320 -0
- package/data/docs/components/pickers/BaseComboboxContent.json +453 -0
- package/data/docs/components/pickers/BaseMenuButton.json +240 -0
- package/data/docs/components/pickers/BaseMenuButtonContent.json +442 -0
- package/data/docs/components/pickers/BaseSelect.json +258 -0
- package/data/docs/components/pickers/Combobox.json +320 -0
- package/data/docs/components/pickers/ComboboxContent.json +453 -0
- package/data/docs/components/pickers/ComboboxContentFooter.json +431 -0
- package/data/docs/components/pickers/ComboboxContentHeader.json +431 -0
- package/data/docs/components/pickers/ComboboxContentListbox.json +541 -0
- package/data/docs/components/pickers/ComboboxTrigger.json +336 -0
- package/data/docs/components/pickers/Item.json +554 -0
- package/data/docs/components/pickers/MenuButton.json +240 -0
- package/data/docs/components/pickers/MenuButtonContent.json +442 -0
- package/data/docs/components/pickers/MenuButtonContentFooter.json +431 -0
- package/data/docs/components/pickers/MenuButtonContentHeader.json +431 -0
- package/data/docs/components/pickers/MenuButtonContentMenu.json +523 -0
- package/data/docs/components/pickers/MenuButtonTrigger.json +287 -0
- package/data/docs/components/pickers/Select.json +258 -0
- package/data/docs/components/pickers/SelectContent.json +442 -0
- package/data/docs/components/pickers/SelectTrigger.json +298 -0
- package/data/docs/components/popovers/BaseDialog.json +114 -0
- package/data/docs/components/popovers/BaseDialogContent.json +21 -0
- package/data/docs/components/popovers/BasePopover.json +171 -0
- package/data/docs/components/popovers/BaseToggletip.json +184 -0
- package/data/docs/components/popovers/BaseTooltip.json +121 -0
- package/data/docs/components/popovers/Button.json +402 -0
- package/data/docs/components/popovers/ButtonLink.json +568 -0
- package/data/docs/components/popovers/Dialog.json +114 -0
- package/data/docs/components/popovers/DialogContent.json +21 -0
- package/data/docs/components/popovers/DialogContentActions.json +442 -0
- package/data/docs/components/popovers/DialogContentBody.json +442 -0
- package/data/docs/components/popovers/DialogContentHeader.json +76 -0
- package/data/docs/components/popovers/DialogTrigger.json +276 -0
- package/data/docs/components/popovers/IconButton.json +371 -0
- package/data/docs/components/popovers/IconButtonLink.json +588 -0
- package/data/docs/components/popovers/Link.json +552 -0
- package/data/docs/components/popovers/LinkButton.json +397 -0
- package/data/docs/components/popovers/Popover.json +171 -0
- package/data/docs/components/popovers/PopoverContent.json +442 -0
- package/data/docs/components/popovers/PopoverTrigger.json +276 -0
- package/data/docs/components/popovers/Toast.json +145 -0
- package/data/docs/components/popovers/ToastContainer.json +122 -0
- package/data/docs/components/popovers/Toggletip.json +184 -0
- package/data/docs/components/popovers/ToggletipContent.json +402 -0
- package/data/docs/components/popovers/ToggletipTrigger.json +276 -0
- package/data/docs/components/popovers/Tooltip.json +121 -0
- package/data/docs/components/popovers/TooltipContent.json +402 -0
- package/data/docs/components/popovers/TooltipTrigger.json +276 -0
- package/data/docs/components/products/Dialog.json +106 -0
- package/data/docs/components/products/MenuButton.json +232 -0
- package/data/docs/components/products/PulsedRadarImage.json +9 -0
- package/data/docs/components/products/RadarButton.json +402 -0
- package/data/docs/components/products/RadarItem.json +554 -0
- package/data/docs/components/table/ControlledPagination.json +9 -0
- package/data/docs/components/table/DataGrid.json +93 -0
- package/data/docs/components/table/GridToolbarFilterSemanticField.json +69 -0
- package/data/docs/components/table/ServerSideControlledPagination.json +9 -0
- package/data/docs/components/table/StatefulDataGrid.json +117 -0
- package/data/docs/components/table/TextCell.json +118 -0
- package/data/docs/components/table/Toolbar.json +145 -0
- package/data/docs/components/table/ToolbarWrapper.json +9 -0
- package/data/docs/components-index.json +1206 -0
- package/data/docs/components.json +55694 -0
- package/data/docs/llms-full.txt +8012 -0
- package/data/docs/llms.txt +234 -0
- package/data/docs/patterns-catalog.md +359 -0
- package/data/docs/patterns.json +712 -0
- package/data/metadata.json +4 -0
- package/data/patterns/crossfiltered-datagrid-page.mdx +386 -0
- package/data/patterns/datagrid-page.mdx +214 -0
- package/data/patterns/drilldown-datagrid-page.mdx +291 -0
- package/data/patterns/server-datagrid-page.mdx +301 -0
- package/data/tokens/properties/components/dark-components.json +1108 -0
- package/data/tokens/properties/components/light-components.json +1108 -0
- package/data/tokens/properties/core/border-radius.json +3 -0
- package/data/tokens/properties/core/color.json +280 -0
- package/data/tokens/properties/core/layout.json +14 -0
- package/data/tokens/properties/core/typography.json +199 -0
- package/data/tokens/redsift-design-tokens.css +1391 -0
- package/dist/data-store.d.ts +47 -0
- package/dist/data-store.d.ts.map +1 -0
- package/dist/data-store.js +152 -0
- package/dist/data-store.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +14 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +137 -0
- package/dist/init.js.map +1 -0
- package/dist/paths.d.ts +30 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +53 -0
- package/dist/paths.js.map +1 -0
- package/dist/pattern-store.d.ts +41 -0
- package/dist/pattern-store.d.ts.map +1 -0
- package/dist/pattern-store.js +177 -0
- package/dist/pattern-store.js.map +1 -0
- package/dist/prompts.d.ts +14 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +762 -0
- package/dist/prompts.js.map +1 -0
- package/dist/resources.d.ts +14 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +482 -0
- package/dist/resources.js.map +1 -0
- package/dist/scaffold.d.ts +31 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +239 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/token-store.d.ts +70 -0
- package/dist/token-store.d.ts.map +1 -0
- package/dist/token-store.js +196 -0
- package/dist/token-store.js.map +1 -0
- package/dist/tools.d.ts +15 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +491 -0
- package/dist/tools.js.map +1 -0
- package/dist/types.d.ts +108 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -0
- package/package.json +39 -0
package/dist/prompts.js
ADDED
|
@@ -0,0 +1,762 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Prompts for the Red Sift Design System.
|
|
3
|
+
*
|
|
4
|
+
* Provides reusable prompt templates that guide AI assistants through
|
|
5
|
+
* common workflows like creating components, adding props, and building forms.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { PACKAGES } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Register all MCP prompts on the server.
|
|
11
|
+
*/
|
|
12
|
+
export function registerPrompts(server, store, patternStore) {
|
|
13
|
+
// ─── create-component ────────────────────────────────────────────────
|
|
14
|
+
server.registerPrompt('create-component', {
|
|
15
|
+
description: 'Step-by-step guide to create a new component following design system conventions.',
|
|
16
|
+
argsSchema: {
|
|
17
|
+
name: z.string().describe('Component name in PascalCase (e.g. "StatusCard")'),
|
|
18
|
+
package: z.enum(PACKAGES).optional().describe('Target package (default: "design-system")'),
|
|
19
|
+
element: z.string().optional().describe('HTML element to extend (default: "div"). E.g. "button", "input", "a"'),
|
|
20
|
+
description: z.string().optional().describe('Brief description of the component'),
|
|
21
|
+
},
|
|
22
|
+
}, async ({ name, package: pkg, element, description }) => {
|
|
23
|
+
const targetPkg = pkg || 'design-system';
|
|
24
|
+
const htmlElement = element || 'div';
|
|
25
|
+
const desc = description || `The ${name} component.`;
|
|
26
|
+
const kebabName = toKebabCase(name);
|
|
27
|
+
return {
|
|
28
|
+
messages: [
|
|
29
|
+
{
|
|
30
|
+
role: 'user',
|
|
31
|
+
content: {
|
|
32
|
+
type: 'text',
|
|
33
|
+
text: `Create a new component **${name}** in the \`@redsift/${targetPkg}\` package.
|
|
34
|
+
|
|
35
|
+
## Requirements
|
|
36
|
+
|
|
37
|
+
- **Component name:** ${name}
|
|
38
|
+
- **Package:** @redsift/${targetPkg}
|
|
39
|
+
- **Directory:** packages/${targetPkg}/src/components/${kebabName}/
|
|
40
|
+
- **HTML element:** <${htmlElement}>
|
|
41
|
+
- **Description:** ${desc}
|
|
42
|
+
|
|
43
|
+
## Steps
|
|
44
|
+
|
|
45
|
+
1. **Generate scaffold** — Use the \`generate_component_scaffold\` tool with name="${name}", package="${targetPkg}", element="${htmlElement}", description="${desc}".
|
|
46
|
+
2. **Create the directory** — \`packages/${targetPkg}/src/components/${kebabName}/\`
|
|
47
|
+
3. **Write all 6 files** from the scaffold output:
|
|
48
|
+
- \`${name}.tsx\` — Main component
|
|
49
|
+
- \`types.ts\` — Props interface
|
|
50
|
+
- \`styles.ts\` — Styled component
|
|
51
|
+
- \`index.ts\` — Re-exports
|
|
52
|
+
- \`${name}.test.tsx\` — Tests
|
|
53
|
+
- \`${name}.stories.tsx\` — Storybook stories
|
|
54
|
+
4. **Export the component** — Add \`export * from './components/${kebabName}';\` to \`packages/${targetPkg}/index.ts\`
|
|
55
|
+
5. **Verify** — Run \`yarn check-types:${targetPkg}\` to confirm no type errors.
|
|
56
|
+
|
|
57
|
+
## Conventions Checklist
|
|
58
|
+
|
|
59
|
+
- [ ] Uses \`Comp\` type and \`forwardRef\`
|
|
60
|
+
- [ ] Static \`.className = 'redsift-${kebabName}'\`
|
|
61
|
+
- [ ] Static \`.displayName = '${name}'\`
|
|
62
|
+
- [ ] Uses \`classNames()\` for className concatenation
|
|
63
|
+
- [ ] Props extend \`ComponentProps<'${htmlElement}'>\` and \`StylingProps\`
|
|
64
|
+
- [ ] Styled component uses transient props (\`$\` prefix)
|
|
65
|
+
- [ ] Tests cover: snapshot, default render, className forwarding, ref forwarding
|
|
66
|
+
- [ ] JSDoc comment above export with \`@example\` block`,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
// ─── add-prop ────────────────────────────────────────────────────────
|
|
73
|
+
server.registerPrompt('add-prop', {
|
|
74
|
+
description: 'Guide to add a new prop to an existing component, updating all related files.',
|
|
75
|
+
argsSchema: {
|
|
76
|
+
component: z.string().describe('Existing component name in PascalCase (e.g. "Button")'),
|
|
77
|
+
propName: z.string().describe('New prop name in camelCase (e.g. "isLoading")'),
|
|
78
|
+
propType: z.string().describe('TypeScript type (e.g. "boolean", "string", "\'sm\' | \'md\' | \'lg\'")'),
|
|
79
|
+
required: z.string().optional().describe('Whether the prop is required ("true" or "false", default: "false")'),
|
|
80
|
+
defaultValue: z.string().optional().describe('Default value for the prop'),
|
|
81
|
+
description: z.string().optional().describe('JSDoc description for the prop'),
|
|
82
|
+
},
|
|
83
|
+
}, async ({ component, propName, propType, required, defaultValue, description }) => {
|
|
84
|
+
const isRequired = required === 'true';
|
|
85
|
+
const desc = description || `The ${propName} prop.`;
|
|
86
|
+
const comp = store.getComponent(component);
|
|
87
|
+
const packageName = comp ? comp.package.replace('@redsift/', '') : 'design-system';
|
|
88
|
+
const kebabName = toKebabCase(component);
|
|
89
|
+
const needsTransient = shouldBeTransient(propName);
|
|
90
|
+
return {
|
|
91
|
+
messages: [
|
|
92
|
+
{
|
|
93
|
+
role: 'user',
|
|
94
|
+
content: {
|
|
95
|
+
type: 'text',
|
|
96
|
+
text: `Add a new prop **${propName}** to the **${component}** component in \`@redsift/${packageName}\`.
|
|
97
|
+
|
|
98
|
+
## Prop Details
|
|
99
|
+
|
|
100
|
+
- **Name:** ${propName}
|
|
101
|
+
- **Type:** ${propType}
|
|
102
|
+
- **Required:** ${isRequired ? 'Yes' : 'No'}
|
|
103
|
+
${defaultValue ? `- **Default:** ${defaultValue}` : ''}
|
|
104
|
+
- **Description:** ${desc}
|
|
105
|
+
|
|
106
|
+
## Files to Update
|
|
107
|
+
|
|
108
|
+
### 1. \`packages/${packageName}/src/components/${kebabName}/types.ts\`
|
|
109
|
+
|
|
110
|
+
Add to \`${component}Props\`:
|
|
111
|
+
\`\`\`ts
|
|
112
|
+
/**
|
|
113
|
+
* ${desc}${defaultValue ? `\n * @default ${defaultValue}` : ''}
|
|
114
|
+
*/
|
|
115
|
+
${propName}${isRequired ? '' : '?'}: ${propType};
|
|
116
|
+
\`\`\`
|
|
117
|
+
|
|
118
|
+
${needsTransient
|
|
119
|
+
? `Add to \`Styled${component}Props\`:
|
|
120
|
+
\`\`\`ts
|
|
121
|
+
$${propName}?: ${component}Props['${propName}'];
|
|
122
|
+
\`\`\``
|
|
123
|
+
: ''}
|
|
124
|
+
|
|
125
|
+
### 2. \`packages/${packageName}/src/components/${kebabName}/${component}.tsx\`
|
|
126
|
+
|
|
127
|
+
- Destructure \`${propName}\` from props${defaultValue ? ` with default: \`${propName} = ${defaultValue}\`` : ''}
|
|
128
|
+
${needsTransient
|
|
129
|
+
? `- Pass \`$${propName}={${propName}}\` to the styled component`
|
|
130
|
+
: `- Use \`${propName}\` in the render logic`}
|
|
131
|
+
|
|
132
|
+
### 3. \`packages/${packageName}/src/components/${kebabName}/styles.ts\`
|
|
133
|
+
|
|
134
|
+
${needsTransient
|
|
135
|
+
? `Use the transient prop in styles:
|
|
136
|
+
\`\`\`ts
|
|
137
|
+
\${({ $${propName} }) => $${propName} ? css\\\`/* styles */\\\` : ''}
|
|
138
|
+
\`\`\``
|
|
139
|
+
: 'Add any relevant styles.'}
|
|
140
|
+
|
|
141
|
+
### 4. \`packages/${packageName}/src/components/${kebabName}/${component}.stories.tsx\`
|
|
142
|
+
|
|
143
|
+
Add story variants demonstrating the new prop:
|
|
144
|
+
\`\`\`tsx
|
|
145
|
+
export const With${capitalize(propName)}: Story = {
|
|
146
|
+
args: {
|
|
147
|
+
${propName}: ${getExampleValue(propType, defaultValue)},
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
\`\`\`
|
|
151
|
+
|
|
152
|
+
### 5. \`packages/${packageName}/src/components/${kebabName}/${component}.test.tsx\`
|
|
153
|
+
|
|
154
|
+
Add test for the new prop:
|
|
155
|
+
\`\`\`tsx
|
|
156
|
+
it('should handle ${propName} prop', () => {
|
|
157
|
+
const { container } = render(<${component} ${propName}={${getExampleValue(propType, defaultValue)}} />);
|
|
158
|
+
// Assert expected behavior
|
|
159
|
+
});
|
|
160
|
+
\`\`\`
|
|
161
|
+
|
|
162
|
+
### 6. Update snapshots
|
|
163
|
+
|
|
164
|
+
\`\`\`bash
|
|
165
|
+
yarn test:unit:${packageName} -u
|
|
166
|
+
\`\`\`
|
|
167
|
+
|
|
168
|
+
## Verification
|
|
169
|
+
|
|
170
|
+
\`\`\`bash
|
|
171
|
+
yarn check-types:${packageName}
|
|
172
|
+
yarn lint:${packageName}
|
|
173
|
+
yarn test:unit:${packageName}
|
|
174
|
+
\`\`\``,
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
};
|
|
179
|
+
});
|
|
180
|
+
// ─── build-form ──────────────────────────────────────────────────────
|
|
181
|
+
server.registerPrompt('build-form', {
|
|
182
|
+
description: 'Generate a form composition using design system form components.',
|
|
183
|
+
argsSchema: {
|
|
184
|
+
fields: z
|
|
185
|
+
.string()
|
|
186
|
+
.describe('Comma-separated list of field definitions. Format: "name:type" where type is text|email|password|number|select|checkbox|radio|textarea. Example: "email:email,password:password,name:text,role:select"'),
|
|
187
|
+
submitLabel: z.string().optional().describe('Submit button label (default: "Submit")'),
|
|
188
|
+
layout: z.string().optional().describe('Form layout: "vertical" (default) or "horizontal"'),
|
|
189
|
+
},
|
|
190
|
+
}, async ({ fields, submitLabel, layout }) => {
|
|
191
|
+
const submit = submitLabel || 'Submit';
|
|
192
|
+
const formLayout = layout || 'vertical';
|
|
193
|
+
const parsedFields = fields.split(',').map((f) => {
|
|
194
|
+
const [name, type] = f.trim().split(':');
|
|
195
|
+
return { name: name.trim(), type: (type || 'text').trim() };
|
|
196
|
+
});
|
|
197
|
+
const imports = new Set(['Button']);
|
|
198
|
+
const fieldComponents = [];
|
|
199
|
+
for (const field of parsedFields) {
|
|
200
|
+
const label = capitalize(field.name.replace(/([A-Z])/g, ' $1').trim());
|
|
201
|
+
switch (field.type) {
|
|
202
|
+
case 'text':
|
|
203
|
+
case 'email':
|
|
204
|
+
case 'password':
|
|
205
|
+
case 'number':
|
|
206
|
+
imports.add('TextField');
|
|
207
|
+
fieldComponents.push(` <TextField\n label="${label}"\n name="${field.name}"\n type="${field.type}"${field.type === 'email' ? '\n autoComplete="email"' : ''}${field.type === 'password' ? '\n autoComplete="current-password"' : ''}\n />`);
|
|
208
|
+
break;
|
|
209
|
+
case 'textarea':
|
|
210
|
+
imports.add('TextArea');
|
|
211
|
+
fieldComponents.push(` <TextArea\n label="${label}"\n name="${field.name}"\n rows={4}\n />`);
|
|
212
|
+
break;
|
|
213
|
+
case 'select':
|
|
214
|
+
imports.add('Select');
|
|
215
|
+
imports.add('Option');
|
|
216
|
+
fieldComponents.push(` <Select label="${label}" name="${field.name}">\n <Option value="option1">Option 1</Option>\n <Option value="option2">Option 2</Option>\n </Select>`);
|
|
217
|
+
break;
|
|
218
|
+
case 'checkbox':
|
|
219
|
+
imports.add('Checkbox');
|
|
220
|
+
fieldComponents.push(` <Checkbox label="${label}" name="${field.name}" />`);
|
|
221
|
+
break;
|
|
222
|
+
case 'radio':
|
|
223
|
+
imports.add('RadioGroup');
|
|
224
|
+
imports.add('Radio');
|
|
225
|
+
fieldComponents.push(` <RadioGroup label="${label}" name="${field.name}">\n <Radio value="option1" label="Option 1" />\n <Radio value="option2" label="Option 2" />\n </RadioGroup>`);
|
|
226
|
+
break;
|
|
227
|
+
default:
|
|
228
|
+
imports.add('TextField');
|
|
229
|
+
fieldComponents.push(` <TextField label="${label}" name="${field.name}" type="text" />`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const importList = Array.from(imports).sort().join(', ');
|
|
233
|
+
return {
|
|
234
|
+
messages: [
|
|
235
|
+
{
|
|
236
|
+
role: 'user',
|
|
237
|
+
content: {
|
|
238
|
+
type: 'text',
|
|
239
|
+
text: `Build a form with the following fields using Red Sift Design System components.
|
|
240
|
+
|
|
241
|
+
## Form Specification
|
|
242
|
+
|
|
243
|
+
- **Layout:** ${formLayout}
|
|
244
|
+
- **Fields:** ${parsedFields.map((f) => `${f.name} (${f.type})`).join(', ')}
|
|
245
|
+
- **Submit:** "${submit}"
|
|
246
|
+
|
|
247
|
+
## Generated Code
|
|
248
|
+
|
|
249
|
+
\`\`\`tsx
|
|
250
|
+
import React, { FormEvent } from 'react';
|
|
251
|
+
import { ${importList} } from '@redsift/design-system';
|
|
252
|
+
|
|
253
|
+
interface FormData {
|
|
254
|
+
${parsedFields.map((f) => ` ${f.name}: ${f.type === 'checkbox' ? 'boolean' : 'string'};`).join('\n')}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function MyForm() {
|
|
258
|
+
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
|
|
259
|
+
e.preventDefault();
|
|
260
|
+
const formData = new FormData(e.currentTarget);
|
|
261
|
+
// Process form data
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
return (
|
|
265
|
+
<form onSubmit={handleSubmit}${formLayout === 'horizontal'
|
|
266
|
+
? ' style={{ display: "flex", gap: "16px", alignItems: "flex-end", flexWrap: "wrap" }}'
|
|
267
|
+
: ''}>
|
|
268
|
+
${fieldComponents.join('\n\n')}
|
|
269
|
+
|
|
270
|
+
<Button type="submit">${submit}</Button>
|
|
271
|
+
</form>
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
\`\`\`
|
|
275
|
+
|
|
276
|
+
## Notes
|
|
277
|
+
|
|
278
|
+
- All form components are from \`@redsift/design-system\`
|
|
279
|
+
- Add validation as needed using the \`error\` and \`helperText\` props on \`TextField\`
|
|
280
|
+
- For Select fields, replace placeholder options with real data
|
|
281
|
+
- Use \`useSearchParams\` or a form library for state management if needed`,
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
],
|
|
285
|
+
};
|
|
286
|
+
});
|
|
287
|
+
// ─── pr-description ──────────────────────────────────────────────────
|
|
288
|
+
server.registerPrompt('pr-description', {
|
|
289
|
+
description: 'Generate a pull request description following the repository PR template.',
|
|
290
|
+
argsSchema: {
|
|
291
|
+
title: z.string().describe('PR title'),
|
|
292
|
+
changes: z.string().describe('Description of the changes made'),
|
|
293
|
+
issueNumber: z.string().optional().describe('Related GitHub issue number'),
|
|
294
|
+
packages: z
|
|
295
|
+
.string()
|
|
296
|
+
.optional()
|
|
297
|
+
.describe('Comma-separated list of affected packages (e.g. "design-system, popovers")'),
|
|
298
|
+
},
|
|
299
|
+
}, async ({ title, changes, issueNumber, packages: pkgs }) => {
|
|
300
|
+
const affectedPkgs = pkgs
|
|
301
|
+
? pkgs
|
|
302
|
+
.split(',')
|
|
303
|
+
.map((p) => p.trim())
|
|
304
|
+
.filter(Boolean)
|
|
305
|
+
: [];
|
|
306
|
+
return {
|
|
307
|
+
messages: [
|
|
308
|
+
{
|
|
309
|
+
role: 'user',
|
|
310
|
+
content: {
|
|
311
|
+
type: 'text',
|
|
312
|
+
text: `Generate a pull request description for the following changes.
|
|
313
|
+
|
|
314
|
+
## PR Details
|
|
315
|
+
|
|
316
|
+
- **Title:** ${title}
|
|
317
|
+
- **Changes:** ${changes}
|
|
318
|
+
${issueNumber ? `- **Issue:** #${issueNumber}` : '- **Issue:** (none specified)'}
|
|
319
|
+
${affectedPkgs.length > 0 ? `- **Affected packages:** ${affectedPkgs.map((p) => `@redsift/${p}`).join(', ')}` : ''}
|
|
320
|
+
|
|
321
|
+
## PR Template
|
|
322
|
+
|
|
323
|
+
Fill in this template:
|
|
324
|
+
|
|
325
|
+
\`\`\`markdown
|
|
326
|
+
## Summary
|
|
327
|
+
|
|
328
|
+
<!-- Brief description of the change -->
|
|
329
|
+
|
|
330
|
+
${issueNumber ? `Closes #${issueNumber}` : '<!-- Link to related issue -->'}
|
|
331
|
+
|
|
332
|
+
## Changes
|
|
333
|
+
|
|
334
|
+
${changes}
|
|
335
|
+
|
|
336
|
+
## Checklist
|
|
337
|
+
|
|
338
|
+
- [${issueNumber ? 'x' : ' '}] Linked to a GitHub issue
|
|
339
|
+
- [ ] Added/updated unit tests
|
|
340
|
+
- [ ] Added/updated Storybook stories
|
|
341
|
+
- [ ] Verified accessibility (keyboard navigation, screen reader)
|
|
342
|
+
- [ ] Updated snapshots (\`yarn test:unit:<package> -u\`)
|
|
343
|
+
- [ ] Updated CHANGELOG.md under \`[Unreleased]\`
|
|
344
|
+
- [ ] Ran \`yarn check-types:<package>\` with no errors
|
|
345
|
+
- [ ] Ran \`yarn lint:<package>\` with no errors
|
|
346
|
+
- [ ] Tested in Storybook locally
|
|
347
|
+
|
|
348
|
+
## How to Test
|
|
349
|
+
|
|
350
|
+
1. Check out this branch
|
|
351
|
+
2. Run \`yarn install\`
|
|
352
|
+
3. Run \`yarn start:storybook\`
|
|
353
|
+
4. Navigate to the affected component(s)
|
|
354
|
+
5. Verify the changes work as expected
|
|
355
|
+
|
|
356
|
+
## Screenshots
|
|
357
|
+
|
|
358
|
+
<!-- Add screenshots if visual changes -->
|
|
359
|
+
\`\`\`
|
|
360
|
+
|
|
361
|
+
Also generate a changelog entry for CHANGELOG.md under \`[Unreleased]\`:
|
|
362
|
+
|
|
363
|
+
${affectedPkgs.map((p) => `- \`@redsift/${p}\`: ${changes}`).join('\n') || `- ${changes}`}`,
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
],
|
|
367
|
+
};
|
|
368
|
+
});
|
|
369
|
+
// ─── implement-ui ────────────────────────────────────────────────────
|
|
370
|
+
server.registerPrompt('implement-ui', {
|
|
371
|
+
description: 'Generate a complete UI implementation using proven design system composition patterns. Searches for matching patterns and provides detailed component usage guidance.',
|
|
372
|
+
argsSchema: {
|
|
373
|
+
description: z
|
|
374
|
+
.string()
|
|
375
|
+
.describe('Describe the UI you want to build (e.g. "a data table with search and export", "a settings page with toggles")'),
|
|
376
|
+
target: z
|
|
377
|
+
.string()
|
|
378
|
+
.optional()
|
|
379
|
+
.describe('Target framework or context (e.g. "React", "Next.js"). Defaults to React.'),
|
|
380
|
+
},
|
|
381
|
+
}, async ({ description, target }) => {
|
|
382
|
+
const framework = target || 'React';
|
|
383
|
+
// Search for relevant patterns
|
|
384
|
+
const patterns = patternStore.searchPatterns(description, 5);
|
|
385
|
+
let patternSection;
|
|
386
|
+
if (patterns.length === 0) {
|
|
387
|
+
patternSection = `No exact pattern matches found for "${description}". Use the \`search_patterns\` tool with broader terms, or compose from individual components using \`search_components\`.`;
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
const patternBlocks = patterns.map((p) => {
|
|
391
|
+
const components = p.components.join(', ');
|
|
392
|
+
const packages = p.packages.join(', ');
|
|
393
|
+
return `### ${p.name}
|
|
394
|
+
- **Description:** ${p.description}
|
|
395
|
+
- **Components:** ${components}
|
|
396
|
+
- **Packages:** ${packages}
|
|
397
|
+
- **Layout:** ${p.layout}
|
|
398
|
+
- **Tags:** ${p.tags.join(', ')}
|
|
399
|
+
- **Documentation:** https://design-system.redsift.io/patterns/${p.slug}
|
|
400
|
+
- **Full demo source:** Fetch the resource \`design-system://patterns/${p.slug}\` to get complete working code`;
|
|
401
|
+
});
|
|
402
|
+
patternSection = patternBlocks.join('\n\n');
|
|
403
|
+
}
|
|
404
|
+
return {
|
|
405
|
+
messages: [
|
|
406
|
+
{
|
|
407
|
+
role: 'user',
|
|
408
|
+
content: {
|
|
409
|
+
type: 'text',
|
|
410
|
+
text: `Implement a ${framework} UI for the following requirement:
|
|
411
|
+
|
|
412
|
+
> ${description}
|
|
413
|
+
|
|
414
|
+
## Matching Composition Patterns
|
|
415
|
+
|
|
416
|
+
${patternSection}
|
|
417
|
+
|
|
418
|
+
## Implementation Instructions
|
|
419
|
+
|
|
420
|
+
1. **Fetch the full pattern spec** for each matched pattern above by reading the resource \`design-system://patterns/{slug}\`. This gives you:
|
|
421
|
+
- **When to Use** — decision criteria to confirm pattern fit
|
|
422
|
+
- **Anatomy** — visual structure (which component goes in which slot)
|
|
423
|
+
- **Features** — required vs optional features table
|
|
424
|
+
- **State Management** — every \`useState\`/\`useRef\` hook with TypeScript types and initial values
|
|
425
|
+
- **Data Contract** — TypeScript type definitions for rows and fetch functions
|
|
426
|
+
- **Implementation Checklist** — ordered step-by-step build guide
|
|
427
|
+
- **Complete demo source code** — a full working reference implementation
|
|
428
|
+
- **Variant demos** — loading, empty, and error state implementations
|
|
429
|
+
- **Keyboard & Accessibility** — a11y requirements
|
|
430
|
+
|
|
431
|
+
2. **Follow the Implementation Checklist** step-by-step. It references the other spec sections by name (e.g. "Define your row type per Data Contract").
|
|
432
|
+
|
|
433
|
+
3. **Adapt the Data Contract** — Replace the example \`Row\` type with your actual domain types. Keep the same field structure (id, status fields, date fields, tag arrays) where applicable.
|
|
434
|
+
|
|
435
|
+
4. **Use the State Management section** as your hook scaffold — copy the hooks and adapt types to your data model.
|
|
436
|
+
|
|
437
|
+
5. **Include variant handling** — Implement loading, empty, and error states as shown in the variant demos. These are critical for production quality.
|
|
438
|
+
|
|
439
|
+
6. **Import components** from the listed packages (e.g. \`import { Button, Flexbox, Heading } from '@redsift/design-system';\`).
|
|
440
|
+
|
|
441
|
+
7. **Look up individual component props** using the \`get_component_props\` tool if you need the full API for any component.
|
|
442
|
+
|
|
443
|
+
## Code Quality Requirements
|
|
444
|
+
|
|
445
|
+
- Use TypeScript with proper types (no \`any\`)
|
|
446
|
+
- Use \`forwardRef\` for components that wrap DOM elements
|
|
447
|
+
- Use \`styled-components\` with transient props (\`$\` prefix) for styling
|
|
448
|
+
- Follow the component naming and file structure conventions of the design system
|
|
449
|
+
- Implement loading, empty, and error states (not just the happy path)
|
|
450
|
+
|
|
451
|
+
## Output
|
|
452
|
+
|
|
453
|
+
Provide a complete, working ${framework} implementation with:
|
|
454
|
+
- All necessary imports
|
|
455
|
+
- TypeScript interfaces for props and data types
|
|
456
|
+
- Component implementation with all state hooks
|
|
457
|
+
- Loading, empty, and error state handling
|
|
458
|
+
- Brief usage example`,
|
|
459
|
+
},
|
|
460
|
+
},
|
|
461
|
+
],
|
|
462
|
+
};
|
|
463
|
+
});
|
|
464
|
+
// ─── create-pattern ──────────────────────────────────────────────────
|
|
465
|
+
server.registerPrompt('create-pattern', {
|
|
466
|
+
description: 'Step-by-step guide to create a new pattern page for the design system website.',
|
|
467
|
+
argsSchema: {
|
|
468
|
+
name: z.string().describe('Pattern name in Title Case (e.g. "Dashboard Card")'),
|
|
469
|
+
description: z.string().describe('Brief description of what the pattern is and when to use it'),
|
|
470
|
+
components: z
|
|
471
|
+
.string()
|
|
472
|
+
.describe('Comma-separated list of components used. Format: "ComponentName:/category/slug" (e.g. "Button:/forms/button,Flexbox:/layout/flexbox")'),
|
|
473
|
+
relatedPatterns: z
|
|
474
|
+
.string()
|
|
475
|
+
.optional()
|
|
476
|
+
.describe('Comma-separated list of related patterns. Format: "Pattern Name:slug" (e.g. "Table Toolbar:table-toolbar,Data Table Page:data-table-page")'),
|
|
477
|
+
variants: z
|
|
478
|
+
.string()
|
|
479
|
+
.optional()
|
|
480
|
+
.describe('Comma-separated list of variant demo names beyond the default "example" (e.g. "with-loading,with-empty-state")'),
|
|
481
|
+
},
|
|
482
|
+
}, async ({ name, description, components, relatedPatterns, variants }) => {
|
|
483
|
+
const slug = toKebabCase(name.replace(/\s+/g, ''));
|
|
484
|
+
const parsedComponents = components.split(',').map((c) => {
|
|
485
|
+
const [compName, href] = c.trim().split(':');
|
|
486
|
+
return { name: compName.trim(), href: href?.trim() || '' };
|
|
487
|
+
});
|
|
488
|
+
const parsedRelated = relatedPatterns
|
|
489
|
+
? relatedPatterns.split(',').map((r) => {
|
|
490
|
+
const [rName, rSlug] = r.trim().split(':');
|
|
491
|
+
return { name: rName.trim(), slug: rSlug?.trim() || toKebabCase(rName.trim().replace(/\s+/g, '')) };
|
|
492
|
+
})
|
|
493
|
+
: [];
|
|
494
|
+
const parsedVariants = variants
|
|
495
|
+
? variants
|
|
496
|
+
.split(',')
|
|
497
|
+
.map((v) => v.trim())
|
|
498
|
+
.filter(Boolean)
|
|
499
|
+
: [];
|
|
500
|
+
const componentLines = parsedComponents
|
|
501
|
+
.map((c) => `- <Link href="${c.href}" as={RouterLink}>${c.name}</Link> — TODO: describe role`)
|
|
502
|
+
.join('\n');
|
|
503
|
+
const relatedLines = parsedRelated.length > 0
|
|
504
|
+
? parsedRelated
|
|
505
|
+
.map((r) => `- <Link href="/patterns/${r.slug}" as={RouterLink}>${r.name}</Link> — TODO: describe relationship`)
|
|
506
|
+
.join('\n')
|
|
507
|
+
: '- <!-- Add related patterns -->';
|
|
508
|
+
const variantSections = parsedVariants
|
|
509
|
+
.map((v) => {
|
|
510
|
+
const title = v
|
|
511
|
+
.replace(/-/g, ' ')
|
|
512
|
+
.replace(/\b\w/g, (c) => c.toUpperCase())
|
|
513
|
+
.replace(/^With /, 'With ');
|
|
514
|
+
return `## ${title}
|
|
515
|
+
|
|
516
|
+
TODO: Describe this variant.
|
|
517
|
+
|
|
518
|
+
<DemoBlock withThemeSwitcher path="patterns/${slug}/${v}" />`;
|
|
519
|
+
})
|
|
520
|
+
.join('\n\n');
|
|
521
|
+
const mdxContent = `import { Link } from '@redsift/design-system';
|
|
522
|
+
import { DemoBlock } from '../../components/DemoBlock';
|
|
523
|
+
import { CodeBlock } from '../../components/CodeBlock';
|
|
524
|
+
import { RouterLink } from '../../components/RouterLink';
|
|
525
|
+
|
|
526
|
+
## Introduction
|
|
527
|
+
|
|
528
|
+
${description}
|
|
529
|
+
|
|
530
|
+
### Components
|
|
531
|
+
|
|
532
|
+
{/* prettier-ignore */}
|
|
533
|
+
${componentLines}
|
|
534
|
+
|
|
535
|
+
## Example
|
|
536
|
+
|
|
537
|
+
TODO: Describe the default example.
|
|
538
|
+
|
|
539
|
+
<DemoBlock withThemeSwitcher path="patterns/${slug}/example" />
|
|
540
|
+
|
|
541
|
+
${variantSections ? variantSections + '\n\n' : ''}## Related Patterns
|
|
542
|
+
|
|
543
|
+
{/* prettier-ignore */}
|
|
544
|
+
${relatedLines}`;
|
|
545
|
+
const patternsJsonEntry = JSON.stringify({
|
|
546
|
+
name,
|
|
547
|
+
slug,
|
|
548
|
+
description,
|
|
549
|
+
components: parsedComponents.map((c) => c.name),
|
|
550
|
+
packages: ['@redsift/design-system'],
|
|
551
|
+
layout: 'TODO: describe component layout',
|
|
552
|
+
tags: ['TODO'],
|
|
553
|
+
}, null, 2);
|
|
554
|
+
return {
|
|
555
|
+
messages: [
|
|
556
|
+
{
|
|
557
|
+
role: 'user',
|
|
558
|
+
content: {
|
|
559
|
+
type: 'text',
|
|
560
|
+
text: `Create a new pattern page **${name}** for the design system website.
|
|
561
|
+
|
|
562
|
+
## Pattern Details
|
|
563
|
+
|
|
564
|
+
- **Name:** ${name}
|
|
565
|
+
- **Slug:** ${slug}
|
|
566
|
+
- **Description:** ${description}
|
|
567
|
+
- **Components:** ${parsedComponents.map((c) => c.name).join(', ')}
|
|
568
|
+
${parsedRelated.length > 0 ? `- **Related patterns:** ${parsedRelated.map((r) => r.name).join(', ')}` : ''}
|
|
569
|
+
${parsedVariants.length > 0 ? `- **Variants:** ${parsedVariants.join(', ')}` : ''}
|
|
570
|
+
|
|
571
|
+
## Steps
|
|
572
|
+
|
|
573
|
+
### 1. Create the MDX page
|
|
574
|
+
|
|
575
|
+
**File:** \`apps/website/pages/patterns/${slug}.mdx\`
|
|
576
|
+
|
|
577
|
+
\`\`\`mdx
|
|
578
|
+
${mdxContent}
|
|
579
|
+
\`\`\`
|
|
580
|
+
|
|
581
|
+
### 2. Create demo files
|
|
582
|
+
|
|
583
|
+
**Directory:** \`apps/website/demo/patterns/${slug}/\`
|
|
584
|
+
|
|
585
|
+
Create at minimum:
|
|
586
|
+
- \`example.tsx\` — default demo
|
|
587
|
+
${parsedVariants.map((v) => `- \`${v}.tsx\` — ${v.replace(/-/g, ' ')} variant`).join('\n')}
|
|
588
|
+
|
|
589
|
+
Each demo file should export a default React component demonstrating the pattern.
|
|
590
|
+
|
|
591
|
+
### 3. Register in patterns.json
|
|
592
|
+
|
|
593
|
+
**File:** \`docs/patterns.json\`
|
|
594
|
+
|
|
595
|
+
Add this entry to the array:
|
|
596
|
+
|
|
597
|
+
\`\`\`json
|
|
598
|
+
${patternsJsonEntry}
|
|
599
|
+
\`\`\`
|
|
600
|
+
|
|
601
|
+
### 4. Update CHANGELOG.md
|
|
602
|
+
|
|
603
|
+
Add under \`[Unreleased]\`:
|
|
604
|
+
- \`website\`: Add ${name} pattern page
|
|
605
|
+
|
|
606
|
+
## Critical Conventions
|
|
607
|
+
|
|
608
|
+
- **\`{/* prettier-ignore */}\`** is REQUIRED before every list that contains \`<Link>\` tags (both Components and Related Patterns lists). Without it, Prettier reformats the single-line tags into multi-line JSX which breaks the MDX parser.
|
|
609
|
+
- **Keep all \`<Link>\` tags on a single line** within list items — never split across multiple lines.
|
|
610
|
+
- If a component has sub-components (e.g. Card.Header), list them on the same line: \`<Link ...>Card</Link>, Card.Header, Card.Body — description\`
|
|
611
|
+
- Demo paths in \`<DemoBlock>\` must match the file structure under \`apps/website/demo/\`.
|
|
612
|
+
|
|
613
|
+
## Verification
|
|
614
|
+
|
|
615
|
+
1. Run \`yarn dev:website\` and navigate to \`/patterns/${slug}\`
|
|
616
|
+
2. Verify the page renders without MDX parser errors
|
|
617
|
+
3. Save the file and verify Prettier does not break the Link lists
|
|
618
|
+
4. Confirm all demo blocks render correctly`,
|
|
619
|
+
},
|
|
620
|
+
},
|
|
621
|
+
],
|
|
622
|
+
};
|
|
623
|
+
});
|
|
624
|
+
// ─── add-demo ────────────────────────────────────────────────────────
|
|
625
|
+
server.registerPrompt('add-demo', {
|
|
626
|
+
description: 'Generate a demo file for a component or pattern on the documentation website.',
|
|
627
|
+
argsSchema: {
|
|
628
|
+
component: z
|
|
629
|
+
.string()
|
|
630
|
+
.describe('Component name in PascalCase (e.g. "Button") or pattern slug (e.g. "data-table-page")'),
|
|
631
|
+
demoName: z.string().describe('Demo file name without extension (e.g. "example", "with-loading", "variants")'),
|
|
632
|
+
description: z.string().optional().describe('What this demo should demonstrate'),
|
|
633
|
+
isPattern: z
|
|
634
|
+
.string()
|
|
635
|
+
.optional()
|
|
636
|
+
.describe('"true" if this is a pattern demo, "false" for component demo (default: "false")'),
|
|
637
|
+
},
|
|
638
|
+
}, async ({ component, demoName, description, isPattern }) => {
|
|
639
|
+
const isPatternDemo = isPattern === 'true';
|
|
640
|
+
const slug = isPatternDemo ? component : toKebabCase(component);
|
|
641
|
+
const demoDir = isPatternDemo ? `patterns/${slug}` : slug;
|
|
642
|
+
const demoPath = `apps/website/demo/${demoDir}/${demoName}.tsx`;
|
|
643
|
+
const desc = description || `Demonstrates the ${demoName.replace(/-/g, ' ')} state/variant.`;
|
|
644
|
+
// Look up component info for imports
|
|
645
|
+
const comp = isPatternDemo ? null : store.getComponent(component);
|
|
646
|
+
const packageName = comp ? comp.package : '@redsift/design-system';
|
|
647
|
+
return {
|
|
648
|
+
messages: [
|
|
649
|
+
{
|
|
650
|
+
role: 'user',
|
|
651
|
+
content: {
|
|
652
|
+
type: 'text',
|
|
653
|
+
text: `Create a demo file for **${component}** — **${demoName}**.
|
|
654
|
+
|
|
655
|
+
## Demo Details
|
|
656
|
+
|
|
657
|
+
- **Component:** ${component}
|
|
658
|
+
- **Demo name:** ${demoName}
|
|
659
|
+
- **File:** \`${demoPath}\`
|
|
660
|
+
- **Description:** ${desc}
|
|
661
|
+
- **Package:** ${packageName}
|
|
662
|
+
|
|
663
|
+
## Template
|
|
664
|
+
|
|
665
|
+
\`\`\`tsx
|
|
666
|
+
import React from 'react';
|
|
667
|
+
import { ${isPatternDemo ? '/* components */' : component} } from '${packageName}';
|
|
668
|
+
|
|
669
|
+
export default () => (
|
|
670
|
+
// TODO: implement the ${demoName.replace(/-/g, ' ')} demo
|
|
671
|
+
<${isPatternDemo ? 'div' : component}>Example</${isPatternDemo ? 'div' : component}>
|
|
672
|
+
);
|
|
673
|
+
\`\`\`
|
|
674
|
+
|
|
675
|
+
## Conventions
|
|
676
|
+
|
|
677
|
+
1. **Default export** — every demo MUST use a default export (arrow function or named function)
|
|
678
|
+
2. **Self-contained** — demos should not share utilities or state with other demo files
|
|
679
|
+
3. **Import from packages** — use \`import { X } from '${packageName}'\`, not source paths
|
|
680
|
+
4. **Clean code** — the source is shown in the "Show Code" panel, keep it readable
|
|
681
|
+
5. **No data fetching** — use hardcoded mock data
|
|
682
|
+
6. **Layout** — wrap multiple items in \`<Flexbox>\` with appropriate gap and direction
|
|
683
|
+
7. **Props from DemoBlock** — the demo receives \`isScreenSmall: boolean\` and \`theme: Theme\` as props (use if needed)
|
|
684
|
+
|
|
685
|
+
## MDX Reference
|
|
686
|
+
|
|
687
|
+
After creating the demo file, reference it in the MDX page:
|
|
688
|
+
|
|
689
|
+
\`\`\`mdx
|
|
690
|
+
<DemoBlock withThemeSwitcher path="${demoDir}/${demoName}" />
|
|
691
|
+
\`\`\`
|
|
692
|
+
|
|
693
|
+
## Verification
|
|
694
|
+
|
|
695
|
+
1. Run \`yarn dev:website\`
|
|
696
|
+
2. Navigate to the component/pattern page
|
|
697
|
+
3. Confirm the demo renders (not a stuck spinner)
|
|
698
|
+
4. Toggle the code viewer and verify the source looks clean`,
|
|
699
|
+
},
|
|
700
|
+
},
|
|
701
|
+
],
|
|
702
|
+
};
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
706
|
+
function toKebabCase(str) {
|
|
707
|
+
return str
|
|
708
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
709
|
+
.replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
|
|
710
|
+
.toLowerCase();
|
|
711
|
+
}
|
|
712
|
+
function capitalize(str) {
|
|
713
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Determine if a prop should use a transient ($-prefixed) styled prop.
|
|
717
|
+
* Visual/styling props need transient props; event handlers and refs don't.
|
|
718
|
+
*/
|
|
719
|
+
function shouldBeTransient(propName) {
|
|
720
|
+
// Event handlers, refs, and children don't need transient props
|
|
721
|
+
if (propName.startsWith('on') || propName === 'ref' || propName === 'children') {
|
|
722
|
+
return false;
|
|
723
|
+
}
|
|
724
|
+
// Common visual props
|
|
725
|
+
const visualProps = [
|
|
726
|
+
'variant',
|
|
727
|
+
'size',
|
|
728
|
+
'color',
|
|
729
|
+
'disabled',
|
|
730
|
+
'isDisabled',
|
|
731
|
+
'isLoading',
|
|
732
|
+
'isActive',
|
|
733
|
+
'isOpen',
|
|
734
|
+
'isSelected',
|
|
735
|
+
'fullWidth',
|
|
736
|
+
'align',
|
|
737
|
+
'direction',
|
|
738
|
+
'orientation',
|
|
739
|
+
'placement',
|
|
740
|
+
'appearance',
|
|
741
|
+
];
|
|
742
|
+
if (visualProps.includes(propName))
|
|
743
|
+
return true;
|
|
744
|
+
// Default to transient for non-standard HTML attributes
|
|
745
|
+
return propName.startsWith('is') || propName.startsWith('has');
|
|
746
|
+
}
|
|
747
|
+
function getExampleValue(type, defaultValue) {
|
|
748
|
+
if (defaultValue)
|
|
749
|
+
return defaultValue;
|
|
750
|
+
if (type === 'boolean')
|
|
751
|
+
return 'true';
|
|
752
|
+
if (type === 'string')
|
|
753
|
+
return '"example"';
|
|
754
|
+
if (type === 'number')
|
|
755
|
+
return '42';
|
|
756
|
+
if (type.includes('|')) {
|
|
757
|
+
const first = type.split('|')[0].trim().replace(/['"]/g, '');
|
|
758
|
+
return `"${first}"`;
|
|
759
|
+
}
|
|
760
|
+
return 'undefined';
|
|
761
|
+
}
|
|
762
|
+
//# sourceMappingURL=prompts.js.map
|