@omnifyjp/mcp-design-system 0.2.0
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 +133 -0
- package/dist/index.js +1165 -0
- package/dist/index.js.map +1 -0
- package/dist/setup.js +68 -0
- package/dist/setup.js.map +1 -0
- package/package.json +41 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1165 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
|
|
7
|
+
// src/resources/tokens.ts
|
|
8
|
+
var TOKENS_CONTENT = `# @omnifyjp Design Tokens
|
|
9
|
+
|
|
10
|
+
All tokens are CSS custom properties defined in \`@omnifyjp/ui/styles/theme.css\`.
|
|
11
|
+
Consumers override them via CSS: \`:root { --primary: #dc2626; }\`
|
|
12
|
+
|
|
13
|
+
**IMPORTANT**: Never wrap \`var(--token)\` in \`hsl()\`. Use \`var(--foreground)\` directly.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Color Tokens
|
|
18
|
+
|
|
19
|
+
### Core Colors
|
|
20
|
+
| Token | Tailwind Class | Light Default | Usage |
|
|
21
|
+
|-------|---------------|---------------|-------|
|
|
22
|
+
| \`--background\` | \`bg-background\` | \`#ffffff\` | Page background |
|
|
23
|
+
| \`--foreground\` | \`text-foreground\` | \`oklch(0.145 0 0)\` | Default text |
|
|
24
|
+
| \`--card\` | \`bg-card\` | \`#ffffff\` | Card backgrounds |
|
|
25
|
+
| \`--card-foreground\` | \`text-card-foreground\` | \`oklch(0.145 0 0)\` | Card text |
|
|
26
|
+
| \`--popover\` | \`bg-popover\` | \`oklch(1 0 0)\` | Popover/dropdown bg |
|
|
27
|
+
| \`--popover-foreground\` | \`text-popover-foreground\` | \`oklch(0.145 0 0)\` | Popover text |
|
|
28
|
+
| \`--primary\` | \`bg-primary\`, \`text-primary\` | \`#030213\` | Main actions, active states |
|
|
29
|
+
| \`--primary-foreground\` | \`text-primary-foreground\` | \`oklch(1 0 0)\` | Text on primary bg |
|
|
30
|
+
| \`--secondary\` | \`bg-secondary\` | \`oklch(0.95 0.0058 264.53)\` | Secondary actions |
|
|
31
|
+
| \`--secondary-foreground\` | \`text-secondary-foreground\` | \`#030213\` | Text on secondary bg |
|
|
32
|
+
| \`--muted\` | \`bg-muted\` | \`#ececf0\` | Muted backgrounds (replaces bg-gray-50/100) |
|
|
33
|
+
| \`--muted-foreground\` | \`text-muted-foreground\` | \`#717182\` | Subdued text (replaces text-gray-500/600) |
|
|
34
|
+
| \`--accent\` | \`bg-accent\`, \`hover:bg-accent\` | \`#e9ebef\` | Hover/active backgrounds |
|
|
35
|
+
| \`--accent-foreground\` | \`text-accent-foreground\` | \`#030213\` | Text on accent bg |
|
|
36
|
+
| \`--destructive\` | \`bg-destructive\`, \`text-destructive\` | \`#d4183d\` | Delete, errors, danger |
|
|
37
|
+
| \`--destructive-foreground\` | \`text-destructive-foreground\` | \`#ffffff\` | Text on destructive bg |
|
|
38
|
+
| \`--border\` | \`border-border\` | \`rgba(0,0,0,0.1)\` | All borders (replaces border-gray-*) |
|
|
39
|
+
| \`--input\` | \`border-input\` | \`transparent\` | Input borders |
|
|
40
|
+
| \`--input-background\` | \`bg-input-background\` | \`#f3f3f5\` | Input backgrounds |
|
|
41
|
+
| \`--ring\` | \`ring-ring\` | \`oklch(0.708 0 0)\` | Focus rings |
|
|
42
|
+
|
|
43
|
+
### Semantic Colors
|
|
44
|
+
| Token | Tailwind Class | Light Default | Usage |
|
|
45
|
+
|-------|---------------|---------------|-------|
|
|
46
|
+
| \`--success\` | \`bg-success\`, \`text-success\` | \`#10b981\` | Confirmed, approved, completed |
|
|
47
|
+
| \`--warning\` | \`bg-warning\`, \`text-warning\` | \`#f59e0b\` | Caution, needs attention |
|
|
48
|
+
| \`--info\` | \`bg-info\`, \`text-info\` | \`#3b82f6\` | Informational highlights |
|
|
49
|
+
| \`--error\` | \`bg-error\`, \`text-error\` | \`#ef4444\` | Error states |
|
|
50
|
+
|
|
51
|
+
Each has a matching \`--*-foreground\` for text on that background.
|
|
52
|
+
|
|
53
|
+
### Sidebar Colors
|
|
54
|
+
| Token | Tailwind Class | Usage |
|
|
55
|
+
|-------|---------------|-------|
|
|
56
|
+
| \`--sidebar\` | \`bg-sidebar\` | Sidebar background |
|
|
57
|
+
| \`--sidebar-foreground\` | \`text-sidebar-foreground\` | Sidebar text |
|
|
58
|
+
| \`--sidebar-primary\` | \`text-sidebar-primary\` | Active sidebar item |
|
|
59
|
+
| \`--sidebar-accent\` | \`bg-sidebar-accent\` | Sidebar hover state |
|
|
60
|
+
| \`--sidebar-border\` | \`border-sidebar-border\` | Sidebar borders |
|
|
61
|
+
|
|
62
|
+
### Chart Colors
|
|
63
|
+
\`--chart-1\` through \`--chart-5\` \u2192 \`bg-chart-1\` etc.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Density Tokens
|
|
68
|
+
|
|
69
|
+
These create spacing utility classes via Tailwind v4 theme mapping.
|
|
70
|
+
|
|
71
|
+
### Page Layout
|
|
72
|
+
| Token | Tailwind Classes | Default | Usage |
|
|
73
|
+
|-------|-----------------|---------|-------|
|
|
74
|
+
| \`--density-page\` | \`p-page\`, \`px-page\`, \`py-page\`, \`pt-page\`, etc. | \`1rem\` (16px) | Page content padding |
|
|
75
|
+
| \`--density-section\` | \`gap-section\`, \`space-y-section\` | \`1rem\` (16px) | Gap between sections |
|
|
76
|
+
| \`--density-page-title\` | \`text-page-title\` | \`1.25rem\` (20px) | Page title font-size |
|
|
77
|
+
|
|
78
|
+
### Element Heights
|
|
79
|
+
| Token | Tailwind Classes | Default | Usage |
|
|
80
|
+
|-------|-----------------|---------|-------|
|
|
81
|
+
| \`--density-element-xs\` | \`h-element-xs\` | \`1.5rem\` (24px) | Compact tables, inline actions |
|
|
82
|
+
| \`--density-element-sm\` | \`h-element-sm\` | \`1.75rem\` (28px) | Secondary actions |
|
|
83
|
+
| \`--density-element\` | \`h-element\`, \`min-h-element\` | \`2rem\` (32px) | Standard buttons, inputs |
|
|
84
|
+
| \`--density-element-lg\` | \`h-element-lg\` | \`2.25rem\` (36px) | Primary CTAs |
|
|
85
|
+
| \`--density-element-xl\` | \`h-element-xl\` | \`2.75rem\` (44px) | Login forms, hero sections |
|
|
86
|
+
|
|
87
|
+
### Container Spacing
|
|
88
|
+
| Token | Tailwind Classes | Default | Usage |
|
|
89
|
+
|-------|-----------------|---------|-------|
|
|
90
|
+
| \`--density-card\` | \`p-card\`, \`px-card\`, \`pt-card\` | \`1rem\` (16px) | Card internal padding |
|
|
91
|
+
| \`--density-dialog\` | \`p-dialog\` | \`1.25rem\` (20px) | Dialog internal padding |
|
|
92
|
+
| \`--density-table-head\` | \`h-table-head\` | \`2rem\` (32px) | Table header row height |
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Layout Tokens
|
|
97
|
+
|
|
98
|
+
| Token | Default | Usage |
|
|
99
|
+
|-------|---------|-------|
|
|
100
|
+
| \`--header-height\` | \`3rem\` (48px) | Header bar height. Tailwind: \`h-header\` |
|
|
101
|
+
| \`--sidebar-width\` | \`16rem\` (256px) | Expanded sidebar |
|
|
102
|
+
| \`--sidebar-collapsed-width\` | \`4rem\` (64px) | Collapsed sidebar |
|
|
103
|
+
| \`--content-sidebar-width\` | \`20rem\` (320px) | PageContainer sidebar |
|
|
104
|
+
| \`--container-max-width\` | \`1280px\` | Content max width |
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Border & Radius Tokens
|
|
109
|
+
|
|
110
|
+
| Token | Default | Usage |
|
|
111
|
+
|-------|---------|-------|
|
|
112
|
+
| \`--radius\` | \`0.375rem\` (6px) | Base radius |
|
|
113
|
+
| \`--radius-sm\` | calc(--radius - 4px) | Small elements |
|
|
114
|
+
| \`--radius-md\` | calc(--radius - 2px) | Medium elements |
|
|
115
|
+
| \`--radius-lg\` | var(--radius) | Large elements |
|
|
116
|
+
| \`--radius-xl\` | calc(--radius + 4px) | Extra large elements |
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Shadow Tokens
|
|
121
|
+
|
|
122
|
+
\`--shadow-sm\`, \`--shadow\`, \`--shadow-md\`, \`--shadow-lg\`, \`--shadow-xl\`, \`--shadow-2xl\`, \`--shadow-inner\`
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Transition Tokens
|
|
127
|
+
|
|
128
|
+
| Token | Default | Usage |
|
|
129
|
+
|-------|---------|-------|
|
|
130
|
+
| \`--transition-fast\` | \`150ms\` | Hover states |
|
|
131
|
+
| \`--transition-base\` | \`200ms\` | General transitions |
|
|
132
|
+
| \`--transition-slow\` | \`300ms\` | Larger animations |
|
|
133
|
+
| \`--transition-slower\` | \`500ms\` | Complex animations |
|
|
134
|
+
|
|
135
|
+
Easing: \`--ease-in\`, \`--ease-out\`, \`--ease-in-out\`
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Z-Index Tokens
|
|
140
|
+
|
|
141
|
+
| Token | Value | Usage |
|
|
142
|
+
|-------|-------|-------|
|
|
143
|
+
| \`--z-dropdown\` | 1000 | Dropdown menus |
|
|
144
|
+
| \`--z-sticky\` | 1020 | Sticky headers |
|
|
145
|
+
| \`--z-fixed\` | 1030 | Fixed elements |
|
|
146
|
+
| \`--z-modal-backdrop\` | 1040 | Modal backdrops |
|
|
147
|
+
| \`--z-modal\` | 1050 | Modals |
|
|
148
|
+
| \`--z-popover\` | 1060 | Popovers |
|
|
149
|
+
| \`--z-tooltip\` | 1070 | Tooltips |
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Shared Types
|
|
154
|
+
|
|
155
|
+
\`\`\`typescript
|
|
156
|
+
type UIColor = 'primary' | 'destructive' | 'success' | 'warning' | 'info';
|
|
157
|
+
type UIVariant = 'default' | 'secondary' | 'outline' | 'soft' | 'ghost' | 'link';
|
|
158
|
+
type UISize = 'xs' | 'sm' | 'default' | 'lg' | 'xl';
|
|
159
|
+
\`\`\`
|
|
160
|
+
|
|
161
|
+
### UIColor \xD7 CSS Variables
|
|
162
|
+
Each UIColor maps to \`--{color}\` and \`--{color}-foreground\` in theme.css.
|
|
163
|
+
|
|
164
|
+
### UIVariant rendering
|
|
165
|
+
| Variant | Solid bg? | Border? | Description |
|
|
166
|
+
|---------|-----------|---------|-------------|
|
|
167
|
+
| \`default\` | Yes | No | Filled background |
|
|
168
|
+
| \`secondary\` | Muted | No | Gray background |
|
|
169
|
+
| \`outline\` | No | Yes | Border + colored text |
|
|
170
|
+
| \`soft\` | 10% opacity | No | Light tinted bg |
|
|
171
|
+
| \`ghost\` | No \u2192 hover | No | Transparent, hover shows bg |
|
|
172
|
+
| \`link\` | No | No | Underline on hover |
|
|
173
|
+
|
|
174
|
+
### UISize \xD7 height tokens
|
|
175
|
+
| Size | Height Token | Pixels |
|
|
176
|
+
|------|-------------|--------|
|
|
177
|
+
| \`xs\` | \`h-element-xs\` | 24px |
|
|
178
|
+
| \`sm\` | \`h-element-sm\` | 28px |
|
|
179
|
+
| \`default\` | \`h-element\` | 32px |
|
|
180
|
+
| \`lg\` | \`h-element-lg\` | 36px |
|
|
181
|
+
| \`xl\` | \`h-element-xl\` | 44px |
|
|
182
|
+
`;
|
|
183
|
+
function registerTokensResource(server2) {
|
|
184
|
+
server2.resource("design-tokens", "design://tokens", {
|
|
185
|
+
description: "Complete design token reference for @omnifyjp/ui \u2014 colors, density, layout, shadows, z-index, transitions, and shared types (UIColor, UIVariant, UISize)",
|
|
186
|
+
mimeType: "text/markdown"
|
|
187
|
+
}, async () => ({
|
|
188
|
+
contents: [{
|
|
189
|
+
uri: "design://tokens",
|
|
190
|
+
mimeType: "text/markdown",
|
|
191
|
+
text: TOKENS_CONTENT
|
|
192
|
+
}]
|
|
193
|
+
}));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// src/resources/rules.ts
|
|
197
|
+
var RULES_CONTENT = `# @omnifyjp Design Rules
|
|
198
|
+
|
|
199
|
+
## Cheat Sheet: WRONG \u2192 RIGHT
|
|
200
|
+
|
|
201
|
+
### Color Replacements
|
|
202
|
+
| WRONG (never use) | RIGHT (use this) | Context |
|
|
203
|
+
|-------------------|------------------|---------|
|
|
204
|
+
| \`bg-white\` | \`bg-background\` or \`bg-card\` | Page/card backgrounds |
|
|
205
|
+
| \`bg-gray-50\`, \`bg-gray-100\` | \`bg-muted\` | Muted/subtle backgrounds |
|
|
206
|
+
| \`hover:bg-gray-50\` | \`hover:bg-accent\` | Hover states |
|
|
207
|
+
| \`text-gray-900\`, \`text-gray-700\` | \`text-foreground\` | Primary text |
|
|
208
|
+
| \`text-gray-600\`, \`text-gray-500\` | \`text-muted-foreground\` | Secondary/subdued text |
|
|
209
|
+
| \`border-gray-200\`, \`border-gray-300\` | \`border-border\` | All borders |
|
|
210
|
+
| \`text-blue-600\` | \`text-primary\` | Primary brand color |
|
|
211
|
+
| \`bg-blue-50 text-blue-600\` | \`bg-primary/10 text-primary\` | Active/selected state |
|
|
212
|
+
| \`bg-blue-50 text-blue-600\` (in sidebar) | \`bg-sidebar-primary/10 text-sidebar-primary\` | Sidebar active state |
|
|
213
|
+
| \`bg-red-50\` | \`bg-destructive/10\` | Destructive soft bg |
|
|
214
|
+
| \`bg-red-500\` | \`bg-destructive\` | Destructive solid bg |
|
|
215
|
+
| \`bg-green-500\` | \`bg-success\` | Success solid bg |
|
|
216
|
+
|
|
217
|
+
### Density Replacements
|
|
218
|
+
| WRONG (never use) | RIGHT (use this) | Context |
|
|
219
|
+
|-------------------|------------------|---------|
|
|
220
|
+
| \`p-6\`, \`p-8\` | \`p-page\` | Page content padding |
|
|
221
|
+
| \`gap-6\`, \`space-y-6\` | \`gap-section\` or \`space-y-section\` | Section gaps |
|
|
222
|
+
| \`h-9\`, \`h-10\` | \`h-element\` or \`h-element-lg\` | Button/input heights |
|
|
223
|
+
| \`h-14\` | \`h-header\` | Header height |
|
|
224
|
+
| \`text-3xl font-bold\` | \`text-page-title font-semibold\` | Page titles |
|
|
225
|
+
| \`px-6 pt-6\` | \`px-card pt-card\` | Card padding |
|
|
226
|
+
| \`p-6\` (in dialog) | \`p-dialog\` | Dialog padding |
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Rules: DO
|
|
231
|
+
|
|
232
|
+
1. **Use Tailwind classes** \u2014 never inline styles
|
|
233
|
+
2. **Use semantic color tokens only** \u2014 \`bg-primary\`, \`text-foreground\`, \`border-border\`
|
|
234
|
+
3. **Use density tokens** \u2014 \`p-page\`, \`h-element\`, \`gap-section\`
|
|
235
|
+
4. **Every interactive element**: must have hover state + transition + focus ring
|
|
236
|
+
5. **Selection pattern**: \`border-primary bg-primary/5\` + Check icon from lucide-react
|
|
237
|
+
6. **Icons**: \`lucide-react\` only \u2014 never emoji, never other icon libraries
|
|
238
|
+
7. **Badge text**: max 2 characters
|
|
239
|
+
8. **Buttons with icons**: use \`gap-2\`
|
|
240
|
+
9. **Pages inside \`<Layout>\`**: never recreate sidebar/header
|
|
241
|
+
10. **Content wrapper**: \`<div className="p-page">\`
|
|
242
|
+
11. **Page title**: \`text-page-title font-semibold\`
|
|
243
|
+
12. **Gaps between sections**: \`gap-section\` or \`space-y-section\`
|
|
244
|
+
13. **Toast notifications**: \`toast.success()\` / \`toast.error()\` via sonner
|
|
245
|
+
14. **TypeScript strict** \u2014 no \`any\`
|
|
246
|
+
15. **Immutable state updates**
|
|
247
|
+
16. **Import order**: React \u2192 router \u2192 types \u2192 data \u2192 ui \u2192 icons \u2192 sonner
|
|
248
|
+
17. **Drawer content**: wrap in \`<DrawerBody>\`
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Rules: DON'T
|
|
253
|
+
|
|
254
|
+
1. **NEVER** hardcoded Tailwind colors (\`text-blue-600\`, \`bg-red-50\`) for interactive/brand states
|
|
255
|
+
- **Exception**: multi-color status badges where each status is a DIFFERENT color by design
|
|
256
|
+
2. **NEVER** hardcoded spacing/heights (\`p-6\`, \`h-10\`) \u2014 use density tokens
|
|
257
|
+
3. **NEVER** use \`h-screen\` on page components (Root component handles it)
|
|
258
|
+
4. **NEVER** use inline styles
|
|
259
|
+
5. **NEVER** wrap \`var(--token)\` in \`hsl()\` \u2014 tokens use mixed color formats (oklch, hex, rgba)
|
|
260
|
+
6. **NEVER** use emoji as icons
|
|
261
|
+
7. **NEVER** use \`bg-white\` \u2014 use \`bg-background\` or \`bg-card\`
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Sidebar Sizing (CRITICAL)
|
|
266
|
+
|
|
267
|
+
All sidebar elements MUST follow these exact sizes:
|
|
268
|
+
|
|
269
|
+
| Element | Size |
|
|
270
|
+
|---------|------|
|
|
271
|
+
| Badges/avatars | \`w-8 h-8\` (32px) |
|
|
272
|
+
| Mini badges | \`w-5 h-5\` (20px) |
|
|
273
|
+
| Menu icons | \`w-4 h-4\` (16px) |
|
|
274
|
+
| Primary text | \`text-sm font-semibold\` |
|
|
275
|
+
| Menu text | \`text-sm\` |
|
|
276
|
+
| Secondary text | \`text-xs text-muted-foreground\` |
|
|
277
|
+
| Menu padding | \`px-3 py-2 gap-2 rounded-md\` |
|
|
278
|
+
| Logo/Header | \`h-header\` (48px) |
|
|
279
|
+
|
|
280
|
+
**NEVER** in sidebar:
|
|
281
|
+
- \`w-10 h-10\` for badges (too large)
|
|
282
|
+
- \`w-5 h-5\` for menu icons (too large)
|
|
283
|
+
- \`text-base\` or \`text-xl\` for text (too large)
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Interaction Patterns
|
|
288
|
+
|
|
289
|
+
### Hover States
|
|
290
|
+
Every clickable element needs:
|
|
291
|
+
\`\`\`
|
|
292
|
+
hover:bg-accent transition-colors
|
|
293
|
+
\`\`\`
|
|
294
|
+
|
|
295
|
+
### Focus Ring
|
|
296
|
+
All focusable elements:
|
|
297
|
+
\`\`\`
|
|
298
|
+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring
|
|
299
|
+
\`\`\`
|
|
300
|
+
|
|
301
|
+
### Selection (Checkmark Pattern)
|
|
302
|
+
Selected items use:
|
|
303
|
+
\`\`\`tsx
|
|
304
|
+
<div className={cn(
|
|
305
|
+
"border rounded-lg p-4 cursor-pointer transition-colors",
|
|
306
|
+
selected
|
|
307
|
+
? "border-primary bg-primary/5"
|
|
308
|
+
: "border-border hover:border-primary/50"
|
|
309
|
+
)}>
|
|
310
|
+
{selected && <Check className="w-4 h-4 text-primary" />}
|
|
311
|
+
</div>
|
|
312
|
+
\`\`\`
|
|
313
|
+
|
|
314
|
+
### Avatar Border (Active State)
|
|
315
|
+
\`\`\`
|
|
316
|
+
ring-2 ring-primary ring-offset-2 ring-offset-background
|
|
317
|
+
\`\`\`
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## Code Quality
|
|
322
|
+
|
|
323
|
+
### TypeScript
|
|
324
|
+
- Strict mode \u2014 no \`any\` types
|
|
325
|
+
- Use immutable state updates
|
|
326
|
+
- Proper discriminated unions for status types
|
|
327
|
+
|
|
328
|
+
### Import Order
|
|
329
|
+
\`\`\`typescript
|
|
330
|
+
// 1. React
|
|
331
|
+
import { useState } from 'react';
|
|
332
|
+
// 2. Router
|
|
333
|
+
import { useNavigate } from 'react-router';
|
|
334
|
+
// 3. Types
|
|
335
|
+
import type { Task } from '../types';
|
|
336
|
+
// 4. Data
|
|
337
|
+
import { mockTasks } from '../data';
|
|
338
|
+
// 5. UI components
|
|
339
|
+
import { Button, Card } from '@omnifyjp/ui';
|
|
340
|
+
// 6. Icons
|
|
341
|
+
import { Plus, Trash2 } from 'lucide-react';
|
|
342
|
+
// 7. Toast
|
|
343
|
+
import { toast } from 'sonner';
|
|
344
|
+
\`\`\`
|
|
345
|
+
|
|
346
|
+
### Naming Conventions
|
|
347
|
+
- Components: PascalCase (\`TaskDetail\`, \`ProjectBoard\`)
|
|
348
|
+
- Files: kebab-case (\`task-detail.tsx\`, \`project-board.tsx\`)
|
|
349
|
+
- Domain components: prefix by domain (\`Workflow*\`, \`Calendar*\`, \`Scope*\`)
|
|
350
|
+
- Badges: \`*Badge\` suffix
|
|
351
|
+
- Grids: \`*Grid\` suffix
|
|
352
|
+
- Diagrams: \`*Diagram\` suffix
|
|
353
|
+
- Compact variants: \`*Mini\` suffix
|
|
354
|
+
`;
|
|
355
|
+
function registerRulesResource(server2) {
|
|
356
|
+
server2.resource("design-rules", "design://rules", {
|
|
357
|
+
description: "Design system rules \u2014 WRONG\u2192RIGHT cheat sheet, DOs/DON'Ts, sidebar sizing, interaction patterns, code quality standards",
|
|
358
|
+
mimeType: "text/markdown"
|
|
359
|
+
}, async () => ({
|
|
360
|
+
contents: [{
|
|
361
|
+
uri: "design://rules",
|
|
362
|
+
mimeType: "text/markdown",
|
|
363
|
+
text: RULES_CONTENT
|
|
364
|
+
}]
|
|
365
|
+
}));
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// src/resources/components.ts
|
|
369
|
+
var COMPONENTS_CONTENT = `# @omnifyjp/ui Component Inventory
|
|
370
|
+
|
|
371
|
+
## Package: \`@omnifyjp/ui\`
|
|
372
|
+
|
|
373
|
+
Import: \`import { Button, Card, ... } from '@omnifyjp/ui';\`
|
|
374
|
+
Utility: \`import { cn } from '@omnifyjp/ui';\`
|
|
375
|
+
Types: \`import type { UIColor, UIVariant, UISize } from '@omnifyjp/ui';\`
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Primitives (53)
|
|
380
|
+
|
|
381
|
+
### Data Entry
|
|
382
|
+
| Component | Description | Key Props |
|
|
383
|
+
|-----------|-------------|-----------|
|
|
384
|
+
| \`Input\` | Text input | \`size?: UISize\` |
|
|
385
|
+
| \`PasswordInput\` | Password with show/hide toggle | \`size?: UISize\` |
|
|
386
|
+
| \`Textarea\` | Multi-line text | \u2014 |
|
|
387
|
+
| \`Checkbox\` | Checkbox | \`checked, onCheckedChange\` |
|
|
388
|
+
| \`RadioGroup\` + \`RadioGroupItem\` | Radio buttons | \`value, onValueChange\` |
|
|
389
|
+
| \`Select\` + \`SelectTrigger\` + \`SelectContent\` + \`SelectItem\` | Dropdown select | \`value, onValueChange\`. **Never use empty string as value** |
|
|
390
|
+
| \`Switch\` | Toggle switch | \`checked, onCheckedChange\` |
|
|
391
|
+
| \`Slider\` | Range slider | \`value, onValueChange\` |
|
|
392
|
+
| \`DatePicker\` | Date picker | \`value, onChange, locale?\` |
|
|
393
|
+
| \`TimePicker\` | Time picker | \`value, onChange\` |
|
|
394
|
+
| \`InputOTP\` | One-time password input | \`maxLength, value, onChange\` |
|
|
395
|
+
| \`Combobox\` | Searchable select | \`options, value, onValueChange\` |
|
|
396
|
+
| \`ColorPicker\` | Color picker | \`value, onChange\` |
|
|
397
|
+
| \`TagInput\` | Tag/chip input | \`tags, onTagsChange\` |
|
|
398
|
+
| \`Rating\` | Star rating | \`value, onChange\` |
|
|
399
|
+
| \`FileUpload\` | File upload area | \`onFilesSelected\` |
|
|
400
|
+
| \`Form\` + \`FormField\` + \`FormItem\` + \`FormLabel\` + \`FormControl\` + \`FormMessage\` | Form with react-hook-form | \`form (useForm)\` |
|
|
401
|
+
| \`Label\` | Form label | \`htmlFor\` |
|
|
402
|
+
|
|
403
|
+
### Data Display
|
|
404
|
+
| Component | Description | Key Props |
|
|
405
|
+
|-----------|-------------|-----------|
|
|
406
|
+
| \`Table\` + \`TableHeader\` + \`TableBody\` + \`TableRow\` + \`TableHead\` + \`TableCell\` | Data table | \u2014 |
|
|
407
|
+
| \`Card\` + \`CardHeader\` + \`CardTitle\` + \`CardDescription\` + \`CardContent\` + \`CardFooter\` | Card container | \u2014 |
|
|
408
|
+
| \`Badge\` | Status/label badge | \`variant?: UIVariant, color?: UIColor\` |
|
|
409
|
+
| \`Avatar\` + \`AvatarImage\` + \`AvatarFallback\` | User avatar | \`src, alt\` |
|
|
410
|
+
| \`Calendar\` | Calendar grid | \`selected, onSelect, locale?\` |
|
|
411
|
+
| \`Progress\` | Progress bar | \`value (0-100)\` |
|
|
412
|
+
| \`Skeleton\` | Loading placeholder | \`className\` |
|
|
413
|
+
| \`Separator\` | Horizontal/vertical line | \`orientation?\` |
|
|
414
|
+
| \`HoverCard\` + \`HoverCardTrigger\` + \`HoverCardContent\` | Hover info card | \u2014 |
|
|
415
|
+
| \`Chart\` | Recharts wrapper | See chart docs |
|
|
416
|
+
| \`AspectRatio\` | Fixed aspect ratio container | \`ratio\` |
|
|
417
|
+
| \`Carousel\` | Swipeable carousel | \`opts\` |
|
|
418
|
+
|
|
419
|
+
### Actions
|
|
420
|
+
| Component | Description | Key Props |
|
|
421
|
+
|-----------|-------------|-----------|
|
|
422
|
+
| \`Button\` | Button | \`variant?: UIVariant, color?: UIColor, size?: UISize\` |
|
|
423
|
+
| \`Toggle\` | Toggle button | \`pressed, onPressedChange\` |
|
|
424
|
+
| \`ToggleGroup\` | Toggle button group | \`type, value, onValueChange\` |
|
|
425
|
+
|
|
426
|
+
### Navigation
|
|
427
|
+
| Component | Description | Key Props |
|
|
428
|
+
|-----------|-------------|-----------|
|
|
429
|
+
| \`Tabs\` + \`TabsList\` + \`TabsTrigger\` + \`TabsContent\` | Tab navigation | \`value, onValueChange\` |
|
|
430
|
+
| \`Breadcrumb\` + \`BreadcrumbItem\` + \`BreadcrumbLink\` + \`BreadcrumbSeparator\` | Breadcrumb trail | \u2014 |
|
|
431
|
+
| \`NavigationMenu\` | Top navigation menu | \u2014 |
|
|
432
|
+
| \`Menubar\` | Menu bar | \u2014 |
|
|
433
|
+
| \`Pagination\` | Page navigation | \u2014 |
|
|
434
|
+
| \`Sidebar\` + \`SidebarProvider\` + \`SidebarTrigger\` + \`SidebarContent\` + \`SidebarGroup\` + \`SidebarMenu\` + \`SidebarMenuItem\` + \`SidebarMenuButton\` | Sidebar navigation | \u2014 |
|
|
435
|
+
| \`Command\` + \`CommandInput\` + \`CommandList\` + \`CommandItem\` | Command palette (cmdk) | \u2014 |
|
|
436
|
+
|
|
437
|
+
### Overlays
|
|
438
|
+
| Component | Description | Key Props |
|
|
439
|
+
|-----------|-------------|-----------|
|
|
440
|
+
| \`Dialog\` + \`DialogTrigger\` + \`DialogContent\` + \`DialogHeader\` + \`DialogTitle\` + \`DialogDescription\` + \`DialogFooter\` | Modal dialog | \`open, onOpenChange\` |
|
|
441
|
+
| \`Sheet\` + \`SheetTrigger\` + \`SheetContent\` | Side panel | \`open, onOpenChange, side\` |
|
|
442
|
+
| \`Drawer\` + \`DrawerTrigger\` + \`DrawerContent\` + \`DrawerBody\` | Bottom drawer (vaul) | \`open, onOpenChange\`. **Wrap content in DrawerBody** |
|
|
443
|
+
| \`AlertDialog\` | Confirmation dialog | \`open, onOpenChange\` |
|
|
444
|
+
| \`Popover\` + \`PopoverTrigger\` + \`PopoverContent\` | Popover | \u2014 |
|
|
445
|
+
| \`DropdownMenu\` + \`DropdownMenuTrigger\` + \`DropdownMenuContent\` + \`DropdownMenuItem\` | Dropdown menu | \u2014 |
|
|
446
|
+
| \`ContextMenu\` | Right-click menu | \u2014 |
|
|
447
|
+
| \`Tooltip\` + \`TooltipTrigger\` + \`TooltipContent\` | Tooltip | Wrap in \`TooltipProvider\` |
|
|
448
|
+
|
|
449
|
+
### Feedback
|
|
450
|
+
| Component | Description | Key Props |
|
|
451
|
+
|-----------|-------------|-----------|
|
|
452
|
+
| \`Alert\` + \`AlertTitle\` + \`AlertDescription\` | Alert banner | \`variant?: UIVariant, color?: UIColor\` |
|
|
453
|
+
| \`Sonner\` / \`toast\` | Toast notifications | \`toast.success(msg)\`, \`toast.error(msg)\` |
|
|
454
|
+
|
|
455
|
+
### Layout
|
|
456
|
+
| Component | Description | Key Props |
|
|
457
|
+
|-----------|-------------|-----------|
|
|
458
|
+
| \`Accordion\` + \`AccordionItem\` + \`AccordionTrigger\` + \`AccordionContent\` | Collapsible sections | \`type, value\` |
|
|
459
|
+
| \`Collapsible\` | Single collapsible | \`open, onOpenChange\` |
|
|
460
|
+
| \`Resizable\` + \`ResizablePanel\` + \`ResizablePanelGroup\` + \`ResizableHandle\` | Resizable panels | \`direction\` |
|
|
461
|
+
| \`ScrollArea\` | Custom scrollbar area | \`className\` |
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## Domain Components (14)
|
|
466
|
+
|
|
467
|
+
All domain components are **locale-agnostic**: they accept a \`labels\` prop with English defaults. Do NOT use \`useTranslation\` inside domain components.
|
|
468
|
+
|
|
469
|
+
### Calendar Domain
|
|
470
|
+
| Component | Description | Key Props |
|
|
471
|
+
|-----------|-------------|-----------|
|
|
472
|
+
| \`CalendarMini\` | Compact calendar widget | \`selected, onSelect\` |
|
|
473
|
+
| \`CalendarEventChip\` | Event indicator chip | \`event, color\` |
|
|
474
|
+
| \`CalendarEventSheet\` | Event detail side panel | \`event, open, onOpenChange\` |
|
|
475
|
+
| \`CalendarToolbar\` | Calendar navigation toolbar | \`date, view, onDateChange, onViewChange\` |
|
|
476
|
+
| \`CalendarCategoryBadge\` | Category color badge | \`category\` |
|
|
477
|
+
|
|
478
|
+
### Workflow Domain
|
|
479
|
+
| Component | Description | Key Props |
|
|
480
|
+
|-----------|-------------|-----------|
|
|
481
|
+
| \`WorkflowStepper\` | Step-by-step progress indicator | \`steps, currentStep\` |
|
|
482
|
+
| \`WorkflowDiagram\` | Visual workflow flow diagram | \`nodes, edges\` |
|
|
483
|
+
| \`WorkflowStatusBadge\` | Workflow status indicator | \`status\` |
|
|
484
|
+
| \`WorkflowCategoryBadge\` | Workflow category badge | \`category\` |
|
|
485
|
+
| \`StageTypeBadge\` | Stage type indicator | \`type\` |
|
|
486
|
+
|
|
487
|
+
### RBAC Domain
|
|
488
|
+
| Component | Description | Key Props |
|
|
489
|
+
|-----------|-------------|-----------|
|
|
490
|
+
| \`PermissionGrid\` | Permission matrix grid | \`modules, actions, permissions, onChange\`. Uses \`module:action\` ID format |
|
|
491
|
+
| \`ScopeTree\` | Hierarchical scope tree | \`scopes, selectedScope, onSelectScope\` |
|
|
492
|
+
| \`ScopeTypeBadge\` | Scope type indicator | \`type\` |
|
|
493
|
+
|
|
494
|
+
### Utility
|
|
495
|
+
| Component | Description | Key Props |
|
|
496
|
+
|-----------|-------------|-----------|
|
|
497
|
+
| \`SlugInput\` | URL slug input with auto-generation | \`value, onChange, source\` |
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## Shared Types API
|
|
502
|
+
|
|
503
|
+
### Button
|
|
504
|
+
\`\`\`tsx
|
|
505
|
+
<Button
|
|
506
|
+
variant="default" | "secondary" | "outline" | "soft" | "ghost" | "link"
|
|
507
|
+
color="primary" | "destructive" | "success" | "warning" | "info"
|
|
508
|
+
size="xs" | "sm" | "default" | "lg" | "xl"
|
|
509
|
+
>
|
|
510
|
+
Label
|
|
511
|
+
</Button>
|
|
512
|
+
\`\`\`
|
|
513
|
+
|
|
514
|
+
### Badge
|
|
515
|
+
\`\`\`tsx
|
|
516
|
+
<Badge
|
|
517
|
+
variant="default" | "secondary" | "outline" | "soft"
|
|
518
|
+
color="primary" | "destructive" | "success" | "warning" | "info"
|
|
519
|
+
>
|
|
520
|
+
Label
|
|
521
|
+
</Badge>
|
|
522
|
+
\`\`\`
|
|
523
|
+
|
|
524
|
+
### Alert
|
|
525
|
+
\`\`\`tsx
|
|
526
|
+
<Alert
|
|
527
|
+
variant="default" | "soft" | "outline"
|
|
528
|
+
color="primary" | "destructive" | "success" | "warning" | "info"
|
|
529
|
+
>
|
|
530
|
+
<AlertTitle>Title</AlertTitle>
|
|
531
|
+
<AlertDescription>Description</AlertDescription>
|
|
532
|
+
</Alert>
|
|
533
|
+
\`\`\`
|
|
534
|
+
|
|
535
|
+
---
|
|
536
|
+
|
|
537
|
+
## Utility: \`cn()\`
|
|
538
|
+
|
|
539
|
+
Merge Tailwind classes with conflict resolution:
|
|
540
|
+
\`\`\`tsx
|
|
541
|
+
import { cn } from '@omnifyjp/ui';
|
|
542
|
+
|
|
543
|
+
<div className={cn("p-4 bg-card", isActive && "bg-primary/10", className)} />
|
|
544
|
+
\`\`\`
|
|
545
|
+
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
## Package: \`@omnifyjp/shell\`
|
|
549
|
+
|
|
550
|
+
| Component | Description |
|
|
551
|
+
|-----------|-------------|
|
|
552
|
+
| \`AppShell\` | Root layout wrapper (ThemeProvider \u2192 I18nextProvider \u2192 OrganizationProvider) |
|
|
553
|
+
| \`Sidebar\` | Main navigation sidebar |
|
|
554
|
+
| \`Header\` | Top header bar |
|
|
555
|
+
| \`StandardPageContainer\` | Standard page layout with optional sidebar |
|
|
556
|
+
| \`SplitPageContainer\` | Split-pane page layout |
|
|
557
|
+
| \`FullWidthPageContainer\` | Full-width page layout |
|
|
558
|
+
|
|
559
|
+
i18n: \`initOmnifyI18n()\` to add service namespaces.
|
|
560
|
+
|
|
561
|
+
---
|
|
562
|
+
|
|
563
|
+
## Package: \`@omnifyjp/editor\`
|
|
564
|
+
|
|
565
|
+
| Component | Description |
|
|
566
|
+
|-----------|-------------|
|
|
567
|
+
| \`RichTextEditor\` | Tiptap-based rich text editor |
|
|
568
|
+
| \`BlockEditor\` | BlockNote-based block editor |
|
|
569
|
+
|
|
570
|
+
CSS import required: \`import '@omnifyjp/editor/styles/rich-text-editor.css'\`
|
|
571
|
+
`;
|
|
572
|
+
function registerComponentsResource(server2) {
|
|
573
|
+
server2.resource("design-components", "design://components", {
|
|
574
|
+
description: "Complete component inventory \u2014 53 primitives, 14 domain components, shell, editor, with props and usage examples",
|
|
575
|
+
mimeType: "text/markdown"
|
|
576
|
+
}, async () => ({
|
|
577
|
+
contents: [{
|
|
578
|
+
uri: "design://components",
|
|
579
|
+
mimeType: "text/markdown",
|
|
580
|
+
text: COMPONENTS_CONTENT
|
|
581
|
+
}]
|
|
582
|
+
}));
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// src/resources/layout.ts
|
|
586
|
+
var LAYOUT_CONTENT = `# @omnifyjp Layout Patterns
|
|
587
|
+
|
|
588
|
+
## Page Layout
|
|
589
|
+
|
|
590
|
+
Every page component follows this structure:
|
|
591
|
+
|
|
592
|
+
\`\`\`tsx
|
|
593
|
+
import { Layout } from './Layout';
|
|
594
|
+
|
|
595
|
+
export function MyPage() {
|
|
596
|
+
return (
|
|
597
|
+
<Layout>
|
|
598
|
+
<div className="p-page">
|
|
599
|
+
<h1 className="text-page-title font-semibold">Page Title</h1>
|
|
600
|
+
<div className="space-y-section">
|
|
601
|
+
{/* Page content */}
|
|
602
|
+
</div>
|
|
603
|
+
</div>
|
|
604
|
+
</Layout>
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
\`\`\`
|
|
608
|
+
|
|
609
|
+
**Rules:**
|
|
610
|
+
- Always wrap in \`<Layout>\` \u2014 never recreate sidebar/header
|
|
611
|
+
- Page content padding: \`p-page\` (NOT \`p-6\` or \`p-8\`)
|
|
612
|
+
- Page title: \`text-page-title font-semibold\` (NOT \`text-3xl font-bold\`)
|
|
613
|
+
- Section gaps: \`gap-section\` or \`space-y-section\` (NOT \`gap-6\` or \`space-y-6\`)
|
|
614
|
+
- **NO \`h-screen\`** on page components \u2014 the root layout handles full height
|
|
615
|
+
|
|
616
|
+
---
|
|
617
|
+
|
|
618
|
+
## Card Pattern
|
|
619
|
+
|
|
620
|
+
\`\`\`tsx
|
|
621
|
+
<Card>
|
|
622
|
+
<CardHeader className="px-card pt-card">
|
|
623
|
+
<CardTitle>Title</CardTitle>
|
|
624
|
+
<CardDescription>Description</CardDescription>
|
|
625
|
+
</CardHeader>
|
|
626
|
+
<CardContent className="px-card pb-card">
|
|
627
|
+
{/* Content */}
|
|
628
|
+
</CardContent>
|
|
629
|
+
</Card>
|
|
630
|
+
\`\`\`
|
|
631
|
+
|
|
632
|
+
**Rules:**
|
|
633
|
+
- Card padding: \`px-card\`, \`pt-card\`, \`pb-card\` (NOT \`px-6\`, \`pt-6\`)
|
|
634
|
+
- Card background: inherits \`bg-card\` automatically
|
|
635
|
+
|
|
636
|
+
---
|
|
637
|
+
|
|
638
|
+
## Dialog Pattern
|
|
639
|
+
|
|
640
|
+
\`\`\`tsx
|
|
641
|
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
642
|
+
<DialogContent>
|
|
643
|
+
<DialogHeader>
|
|
644
|
+
<DialogTitle>Dialog Title</DialogTitle>
|
|
645
|
+
<DialogDescription>Optional description</DialogDescription>
|
|
646
|
+
</DialogHeader>
|
|
647
|
+
<div className="p-dialog space-y-4">
|
|
648
|
+
{/* Dialog body */}
|
|
649
|
+
</div>
|
|
650
|
+
<DialogFooter>
|
|
651
|
+
<Button variant="outline" onClick={() => onOpenChange(false)}>Cancel</Button>
|
|
652
|
+
<Button>Submit</Button>
|
|
653
|
+
</DialogFooter>
|
|
654
|
+
</DialogContent>
|
|
655
|
+
</Dialog>
|
|
656
|
+
\`\`\`
|
|
657
|
+
|
|
658
|
+
**Rules:**
|
|
659
|
+
- Dialog body padding: \`p-dialog\` (NOT \`p-6\`)
|
|
660
|
+
- Props: \`{ open: boolean; onOpenChange: (open: boolean) => void }\`
|
|
661
|
+
|
|
662
|
+
---
|
|
663
|
+
|
|
664
|
+
## Table Pattern
|
|
665
|
+
|
|
666
|
+
\`\`\`tsx
|
|
667
|
+
<Table>
|
|
668
|
+
<TableHeader>
|
|
669
|
+
<TableRow className="h-table-head">
|
|
670
|
+
<TableHead>Column</TableHead>
|
|
671
|
+
</TableRow>
|
|
672
|
+
</TableHeader>
|
|
673
|
+
<TableBody>
|
|
674
|
+
<TableRow className="hover:bg-accent transition-colors">
|
|
675
|
+
<TableCell>Data</TableCell>
|
|
676
|
+
</TableRow>
|
|
677
|
+
</TableBody>
|
|
678
|
+
</Table>
|
|
679
|
+
\`\`\`
|
|
680
|
+
|
|
681
|
+
**Rules:**
|
|
682
|
+
- Header row: \`h-table-head\` (NOT \`h-10\`)
|
|
683
|
+
- Row hover: \`hover:bg-accent transition-colors\`
|
|
684
|
+
|
|
685
|
+
---
|
|
686
|
+
|
|
687
|
+
## Selection State Pattern
|
|
688
|
+
|
|
689
|
+
For selectable items (cards, list items):
|
|
690
|
+
|
|
691
|
+
\`\`\`tsx
|
|
692
|
+
<div className={cn(
|
|
693
|
+
"border rounded-lg p-4 cursor-pointer transition-colors",
|
|
694
|
+
selected
|
|
695
|
+
? "border-primary bg-primary/5"
|
|
696
|
+
: "border-border hover:border-primary/50"
|
|
697
|
+
)}>
|
|
698
|
+
<div className="flex items-center justify-between">
|
|
699
|
+
<span>{label}</span>
|
|
700
|
+
{selected && <Check className="w-4 h-4 text-primary" />}
|
|
701
|
+
</div>
|
|
702
|
+
</div>
|
|
703
|
+
\`\`\`
|
|
704
|
+
|
|
705
|
+
**Rules:**
|
|
706
|
+
- Selected: \`border-primary bg-primary/5\` + Check icon
|
|
707
|
+
- Unselected hover: \`hover:border-primary/50\`
|
|
708
|
+
- Always include transition: \`transition-colors\`
|
|
709
|
+
|
|
710
|
+
---
|
|
711
|
+
|
|
712
|
+
## Sidebar Layout
|
|
713
|
+
|
|
714
|
+
### Sidebar Structure
|
|
715
|
+
\`\`\`
|
|
716
|
+
Sidebar (w-64 / 256px)
|
|
717
|
+
\u251C\u2500\u2500 Header (h-header / 48px)
|
|
718
|
+
\u2502 \u251C\u2500\u2500 Logo/Avatar (w-8 h-8)
|
|
719
|
+
\u2502 \u2514\u2500\u2500 Title (text-sm font-semibold)
|
|
720
|
+
\u251C\u2500\u2500 Menu Groups
|
|
721
|
+
\u2502 \u251C\u2500\u2500 Group Label (text-xs text-muted-foreground uppercase)
|
|
722
|
+
\u2502 \u2514\u2500\u2500 Menu Items (px-3 py-2 gap-2 rounded-md)
|
|
723
|
+
\u2502 \u251C\u2500\u2500 Icon (w-4 h-4)
|
|
724
|
+
\u2502 \u2514\u2500\u2500 Text (text-sm)
|
|
725
|
+
\u2514\u2500\u2500 Footer
|
|
726
|
+
\u2514\u2500\u2500 User Info
|
|
727
|
+
\`\`\`
|
|
728
|
+
|
|
729
|
+
### Sidebar Token Usage
|
|
730
|
+
| Element | Classes |
|
|
731
|
+
|---------|---------|
|
|
732
|
+
| Background | \`bg-sidebar\` |
|
|
733
|
+
| Text | \`text-sidebar-foreground\` |
|
|
734
|
+
| Active item bg | \`bg-sidebar-primary/10\` |
|
|
735
|
+
| Active item text | \`text-sidebar-primary\` |
|
|
736
|
+
| Hover state | \`hover:bg-sidebar-accent\` |
|
|
737
|
+
| Border | \`border-sidebar-border\` |
|
|
738
|
+
| Badge/avatar | \`w-8 h-8\` (32px) |
|
|
739
|
+
| Mini badge | \`w-5 h-5\` (20px) |
|
|
740
|
+
| Menu icon | \`w-4 h-4\` (16px) |
|
|
741
|
+
|
|
742
|
+
---
|
|
743
|
+
|
|
744
|
+
## Header Layout
|
|
745
|
+
|
|
746
|
+
\`\`\`tsx
|
|
747
|
+
<header className="h-header border-b border-border flex items-center px-page">
|
|
748
|
+
<div className="flex items-center gap-2">
|
|
749
|
+
{/* Header content */}
|
|
750
|
+
</div>
|
|
751
|
+
</header>
|
|
752
|
+
\`\`\`
|
|
753
|
+
|
|
754
|
+
**Rules:**
|
|
755
|
+
- Height: \`h-header\` (48px, NOT \`h-14\` or \`h-16\`)
|
|
756
|
+
- Padding: \`px-page\`
|
|
757
|
+
|
|
758
|
+
---
|
|
759
|
+
|
|
760
|
+
## Drawer Pattern
|
|
761
|
+
|
|
762
|
+
\`\`\`tsx
|
|
763
|
+
<Drawer open={open} onOpenChange={onOpenChange}>
|
|
764
|
+
<DrawerContent>
|
|
765
|
+
<DrawerHeader>
|
|
766
|
+
<DrawerTitle>Title</DrawerTitle>
|
|
767
|
+
</DrawerHeader>
|
|
768
|
+
<DrawerBody>
|
|
769
|
+
{/* Content MUST be wrapped in DrawerBody */}
|
|
770
|
+
</DrawerBody>
|
|
771
|
+
</DrawerContent>
|
|
772
|
+
</Drawer>
|
|
773
|
+
\`\`\`
|
|
774
|
+
|
|
775
|
+
**Rules:**
|
|
776
|
+
- Content MUST be wrapped in \`<DrawerBody>\`
|
|
777
|
+
|
|
778
|
+
---
|
|
779
|
+
|
|
780
|
+
## Avatar Border Pattern (Active User)
|
|
781
|
+
|
|
782
|
+
\`\`\`tsx
|
|
783
|
+
<Avatar className={cn(
|
|
784
|
+
"w-8 h-8",
|
|
785
|
+
isActive && "ring-2 ring-primary ring-offset-2 ring-offset-background"
|
|
786
|
+
)}>
|
|
787
|
+
<AvatarImage src={user.avatar} />
|
|
788
|
+
<AvatarFallback>{user.initials}</AvatarFallback>
|
|
789
|
+
</Avatar>
|
|
790
|
+
\`\`\`
|
|
791
|
+
|
|
792
|
+
---
|
|
793
|
+
|
|
794
|
+
## Responsive Considerations
|
|
795
|
+
|
|
796
|
+
- Sidebar collapses at mobile breakpoint (use \`useIsMobile()\` hook from \`@omnifyjp/ui\`)
|
|
797
|
+
- Page padding \`p-page\` adjusts via density tokens
|
|
798
|
+
- Element heights via \`h-element-*\` tokens scale consistently
|
|
799
|
+
`;
|
|
800
|
+
function registerLayoutResource(server2) {
|
|
801
|
+
server2.resource("design-layout", "design://layout", {
|
|
802
|
+
description: "Layout patterns \u2014 page, card, dialog, table, sidebar, header, drawer, selection state, avatar borders",
|
|
803
|
+
mimeType: "text/markdown"
|
|
804
|
+
}, async () => ({
|
|
805
|
+
contents: [{
|
|
806
|
+
uri: "design://layout",
|
|
807
|
+
mimeType: "text/markdown",
|
|
808
|
+
text: LAYOUT_CONTENT
|
|
809
|
+
}]
|
|
810
|
+
}));
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// src/tools/validate.ts
|
|
814
|
+
import { z } from "zod";
|
|
815
|
+
var VIOLATION_RULES = [
|
|
816
|
+
// Color violations
|
|
817
|
+
{ pattern: /\bbg-white\b/, message: "Hardcoded `bg-white`", suggestion: "Use `bg-background` or `bg-card`", category: "color" },
|
|
818
|
+
{ pattern: /\bbg-gray-50\b/, message: "Hardcoded `bg-gray-50`", suggestion: "Use `bg-muted`", category: "color" },
|
|
819
|
+
{ pattern: /\bbg-gray-100\b/, message: "Hardcoded `bg-gray-100`", suggestion: "Use `bg-muted`", category: "color" },
|
|
820
|
+
{ pattern: /\btext-gray-900\b/, message: "Hardcoded `text-gray-900`", suggestion: "Use `text-foreground`", category: "color" },
|
|
821
|
+
{ pattern: /\btext-gray-700\b/, message: "Hardcoded `text-gray-700`", suggestion: "Use `text-foreground`", category: "color" },
|
|
822
|
+
{ pattern: /\btext-gray-600\b/, message: "Hardcoded `text-gray-600`", suggestion: "Use `text-muted-foreground`", category: "color" },
|
|
823
|
+
{ pattern: /\btext-gray-500\b/, message: "Hardcoded `text-gray-500`", suggestion: "Use `text-muted-foreground`", category: "color" },
|
|
824
|
+
{ pattern: /\btext-gray-400\b/, message: "Hardcoded `text-gray-400`", suggestion: "Use `text-muted-foreground`", category: "color" },
|
|
825
|
+
{ pattern: /\bborder-gray-\d+\b/, message: "Hardcoded `border-gray-*`", suggestion: "Use `border-border`", category: "color" },
|
|
826
|
+
{ pattern: /\btext-blue-\d+\b/, message: "Hardcoded `text-blue-*`", suggestion: "Use `text-primary` or `text-info`", category: "color" },
|
|
827
|
+
{ pattern: /\bbg-blue-\d+\b/, message: "Hardcoded `bg-blue-*`", suggestion: "Use `bg-primary/10` or `bg-info/10`", category: "color" },
|
|
828
|
+
{ pattern: /\bbg-red-\d+\b/, message: "Hardcoded `bg-red-*`", suggestion: "Use `bg-destructive` or `bg-destructive/10`", category: "color" },
|
|
829
|
+
{ pattern: /\btext-red-\d+\b/, message: "Hardcoded `text-red-*`", suggestion: "Use `text-destructive`", category: "color" },
|
|
830
|
+
{ pattern: /\bbg-green-\d+\b/, message: "Hardcoded `bg-green-*`", suggestion: "Use `bg-success` or `bg-success/10`", category: "color" },
|
|
831
|
+
{ pattern: /\btext-green-\d+\b/, message: "Hardcoded `text-green-*`", suggestion: "Use `text-success`", category: "color" },
|
|
832
|
+
{ pattern: /\bbg-yellow-\d+\b/, message: "Hardcoded `bg-yellow-*`", suggestion: "Use `bg-warning` or `bg-warning/10`", category: "color" },
|
|
833
|
+
{ pattern: /\btext-yellow-\d+\b/, message: "Hardcoded `text-yellow-*`", suggestion: "Use `text-warning`", category: "color" },
|
|
834
|
+
{ pattern: /\bbg-orange-\d+\b/, message: "Hardcoded `bg-orange-*`", suggestion: "Use `bg-warning` or `bg-warning/10`", category: "color" },
|
|
835
|
+
{ pattern: /\bhover:bg-gray-\d+\b/, message: "Hardcoded `hover:bg-gray-*`", suggestion: "Use `hover:bg-accent`", category: "color" },
|
|
836
|
+
{ pattern: /\btext-black\b/, message: "Hardcoded `text-black`", suggestion: "Use `text-foreground`", category: "color" },
|
|
837
|
+
{ pattern: /\bbg-black\b/, message: "Hardcoded `bg-black`", suggestion: "Use `bg-foreground` or `bg-primary`", category: "color" },
|
|
838
|
+
// Density violations
|
|
839
|
+
{ pattern: /\bp-6\b/, message: "Hardcoded `p-6`", suggestion: "Use `p-page` for page padding or `p-dialog` for dialog", category: "density" },
|
|
840
|
+
{ pattern: /\bp-8\b/, message: "Hardcoded `p-8`", suggestion: "Use `p-page` for page padding", category: "density" },
|
|
841
|
+
{ pattern: /\bpx-6\b/, message: "Hardcoded `px-6`", suggestion: "Use `px-page` or `px-card`", category: "density" },
|
|
842
|
+
{ pattern: /\bpy-6\b/, message: "Hardcoded `py-6`", suggestion: "Use `py-page` or `py-card`", category: "density" },
|
|
843
|
+
{ pattern: /\bgap-6\b/, message: "Hardcoded `gap-6`", suggestion: "Use `gap-section`", category: "density" },
|
|
844
|
+
{ pattern: /\bspace-y-6\b/, message: "Hardcoded `space-y-6`", suggestion: "Use `space-y-section`", category: "density" },
|
|
845
|
+
{ pattern: /\bh-9\b/, message: "Hardcoded `h-9`", suggestion: "Use `h-element` (32px)", category: "density" },
|
|
846
|
+
{ pattern: /\bh-10\b/, message: "Hardcoded `h-10`", suggestion: "Use `h-element-lg` (36px)", category: "density" },
|
|
847
|
+
{ pattern: /\bh-14\b/, message: "Hardcoded `h-14`", suggestion: "Use `h-header` (48px)", category: "density" },
|
|
848
|
+
// Layout violations
|
|
849
|
+
{ pattern: /\bh-screen\b/, message: "`h-screen` on page component", suggestion: "Remove \u2014 root layout handles full height", category: "layout" },
|
|
850
|
+
// Pattern violations
|
|
851
|
+
{ pattern: /\btext-3xl\s+font-bold\b/, message: "`text-3xl font-bold` for page title", suggestion: "Use `text-page-title font-semibold`", category: "pattern" },
|
|
852
|
+
{ pattern: /\btext-2xl\s+font-bold\b/, message: "`text-2xl font-bold` for page title", suggestion: "Use `text-page-title font-semibold`", category: "pattern" },
|
|
853
|
+
{ pattern: /\bhsl\(var\(--/, message: "`hsl(var(--*))` wrapping", suggestion: "Use `var(--token)` directly \u2014 tokens use mixed color formats", category: "pattern" },
|
|
854
|
+
{ pattern: /style=\{/, message: "Inline style object", suggestion: "Use Tailwind classes instead of inline styles", category: "pattern" }
|
|
855
|
+
];
|
|
856
|
+
var TOKEN_DB = [
|
|
857
|
+
// Colors
|
|
858
|
+
{ token: "--background", tailwind: "bg-background", category: "color", description: "Page background" },
|
|
859
|
+
{ token: "--foreground", tailwind: "text-foreground", category: "color", description: "Default text color" },
|
|
860
|
+
{ token: "--card", tailwind: "bg-card", category: "color", description: "Card background" },
|
|
861
|
+
{ token: "--primary", tailwind: "bg-primary / text-primary", category: "color", description: "Main brand, active states" },
|
|
862
|
+
{ token: "--secondary", tailwind: "bg-secondary", category: "color", description: "Secondary backgrounds" },
|
|
863
|
+
{ token: "--muted", tailwind: "bg-muted", category: "color", description: "Muted/subtle backgrounds (replaces bg-gray-50/100)" },
|
|
864
|
+
{ token: "--muted-foreground", tailwind: "text-muted-foreground", category: "color", description: "Subdued text (replaces text-gray-500/600)" },
|
|
865
|
+
{ token: "--accent", tailwind: "bg-accent / hover:bg-accent", category: "color", description: "Hover backgrounds (replaces hover:bg-gray-50)" },
|
|
866
|
+
{ token: "--destructive", tailwind: "bg-destructive / text-destructive", category: "color", description: "Delete, errors, danger" },
|
|
867
|
+
{ token: "--border", tailwind: "border-border", category: "color", description: "All borders (replaces border-gray-200/300)" },
|
|
868
|
+
{ token: "--success", tailwind: "bg-success / text-success", category: "color", description: "Success states" },
|
|
869
|
+
{ token: "--warning", tailwind: "bg-warning / text-warning", category: "color", description: "Warning states" },
|
|
870
|
+
{ token: "--info", tailwind: "bg-info / text-info", category: "color", description: "Info states" },
|
|
871
|
+
{ token: "--sidebar", tailwind: "bg-sidebar", category: "color", description: "Sidebar background" },
|
|
872
|
+
{ token: "--sidebar-primary", tailwind: "text-sidebar-primary", category: "color", description: "Sidebar active item" },
|
|
873
|
+
// Density
|
|
874
|
+
{ token: "--density-page", tailwind: "p-page", category: "density", description: "Page content padding (16px)" },
|
|
875
|
+
{ token: "--density-section", tailwind: "gap-section / space-y-section", category: "density", description: "Section gap (16px)" },
|
|
876
|
+
{ token: "--density-page-title", tailwind: "text-page-title", category: "density", description: "Page title size (20px)" },
|
|
877
|
+
{ token: "--density-element", tailwind: "h-element", category: "density", description: "Standard element height (32px)" },
|
|
878
|
+
{ token: "--density-element-sm", tailwind: "h-element-sm", category: "density", description: "Small element height (28px)" },
|
|
879
|
+
{ token: "--density-element-lg", tailwind: "h-element-lg", category: "density", description: "Large element height (36px)" },
|
|
880
|
+
{ token: "--density-element-xl", tailwind: "h-element-xl", category: "density", description: "Extra large element height (44px)" },
|
|
881
|
+
{ token: "--density-card", tailwind: "p-card / px-card / pt-card", category: "density", description: "Card internal padding (16px)" },
|
|
882
|
+
{ token: "--density-dialog", tailwind: "p-dialog", category: "density", description: "Dialog internal padding (20px)" },
|
|
883
|
+
{ token: "--density-table-head", tailwind: "h-table-head", category: "density", description: "Table header row height (32px)" },
|
|
884
|
+
// Layout
|
|
885
|
+
{ token: "--header-height", tailwind: "h-header", category: "layout", description: "Header height (48px)" },
|
|
886
|
+
{ token: "--sidebar-width", tailwind: "w-64", category: "layout", description: "Sidebar width (256px)" },
|
|
887
|
+
{ token: "--radius", tailwind: "rounded-lg", category: "border", description: "Default border radius (6px)" }
|
|
888
|
+
];
|
|
889
|
+
var COMPONENT_REGISTRY = {
|
|
890
|
+
button: {
|
|
891
|
+
name: "Button",
|
|
892
|
+
package: "@omnifyjp/ui",
|
|
893
|
+
category: "action",
|
|
894
|
+
description: "Standard button with variant \xD7 color \xD7 size API",
|
|
895
|
+
props: 'variant?: UIVariant ("default"|"secondary"|"outline"|"soft"|"ghost"|"link"), color?: UIColor ("primary"|"destructive"|"success"|"warning"|"info"), size?: UISize ("xs"|"sm"|"default"|"lg"|"xl"), asChild?: boolean, disabled?: boolean',
|
|
896
|
+
example: '<Button variant="soft" color="success" size="sm"><Check className="w-4 h-4" /> Approve</Button>'
|
|
897
|
+
},
|
|
898
|
+
badge: {
|
|
899
|
+
name: "Badge",
|
|
900
|
+
package: "@omnifyjp/ui",
|
|
901
|
+
category: "display",
|
|
902
|
+
description: "Status/label badge. Max 2 characters for icon badges.",
|
|
903
|
+
props: 'variant?: "default"|"secondary"|"outline"|"soft", color?: UIColor',
|
|
904
|
+
example: '<Badge variant="soft" color="warning">Pending</Badge>'
|
|
905
|
+
},
|
|
906
|
+
alert: {
|
|
907
|
+
name: "Alert",
|
|
908
|
+
package: "@omnifyjp/ui",
|
|
909
|
+
category: "feedback",
|
|
910
|
+
description: "Alert banner with icon support",
|
|
911
|
+
props: 'variant?: "default"|"soft"|"outline", color?: UIColor',
|
|
912
|
+
example: '<Alert variant="soft" color="info"><AlertTitle>Tip</AlertTitle><AlertDescription>Use keyboard shortcuts</AlertDescription></Alert>'
|
|
913
|
+
},
|
|
914
|
+
input: {
|
|
915
|
+
name: "Input",
|
|
916
|
+
package: "@omnifyjp/ui",
|
|
917
|
+
category: "form",
|
|
918
|
+
description: "Text input field",
|
|
919
|
+
props: "size?: UISize, type?: string, placeholder?: string, disabled?: boolean",
|
|
920
|
+
example: '<Input size="default" placeholder="Enter text..." />'
|
|
921
|
+
},
|
|
922
|
+
"password-input": {
|
|
923
|
+
name: "PasswordInput",
|
|
924
|
+
package: "@omnifyjp/ui",
|
|
925
|
+
category: "form",
|
|
926
|
+
description: "Password input with show/hide toggle",
|
|
927
|
+
props: "size?: UISize, placeholder?: string, disabled?: boolean",
|
|
928
|
+
example: '<PasswordInput size="xl" placeholder="Enter password" />'
|
|
929
|
+
},
|
|
930
|
+
select: {
|
|
931
|
+
name: "Select",
|
|
932
|
+
package: "@omnifyjp/ui",
|
|
933
|
+
category: "form",
|
|
934
|
+
description: 'Dropdown select. NEVER use empty string as SelectItem value \u2014 Radix crashes. Use value="none" and map to null.',
|
|
935
|
+
props: "value?: string, onValueChange?: (value: string) => void",
|
|
936
|
+
example: '<Select value={value} onValueChange={setValue}><SelectTrigger><SelectValue placeholder="Choose..." /></SelectTrigger><SelectContent><SelectItem value="a">Option A</SelectItem></SelectContent></Select>'
|
|
937
|
+
},
|
|
938
|
+
dialog: {
|
|
939
|
+
name: "Dialog",
|
|
940
|
+
package: "@omnifyjp/ui",
|
|
941
|
+
category: "overlay",
|
|
942
|
+
description: "Modal dialog. Use p-dialog for body padding.",
|
|
943
|
+
props: "open?: boolean, onOpenChange?: (open: boolean) => void",
|
|
944
|
+
example: '<Dialog open={open} onOpenChange={setOpen}><DialogContent><DialogHeader><DialogTitle>Title</DialogTitle></DialogHeader><div className="p-dialog">Content</div><DialogFooter><Button>Save</Button></DialogFooter></DialogContent></Dialog>'
|
|
945
|
+
},
|
|
946
|
+
card: {
|
|
947
|
+
name: "Card",
|
|
948
|
+
package: "@omnifyjp/ui",
|
|
949
|
+
category: "display",
|
|
950
|
+
description: "Card container. Use px-card/pt-card for padding.",
|
|
951
|
+
props: "className?: string",
|
|
952
|
+
example: '<Card><CardHeader className="px-card pt-card"><CardTitle>Title</CardTitle></CardHeader><CardContent className="px-card pb-card">Content</CardContent></Card>'
|
|
953
|
+
},
|
|
954
|
+
table: {
|
|
955
|
+
name: "Table",
|
|
956
|
+
package: "@omnifyjp/ui",
|
|
957
|
+
category: "display",
|
|
958
|
+
description: "Data table. Use h-table-head for header row height.",
|
|
959
|
+
props: "className?: string",
|
|
960
|
+
example: '<Table><TableHeader><TableRow className="h-table-head"><TableHead>Name</TableHead></TableRow></TableHeader><TableBody><TableRow>...</TableRow></TableBody></Table>'
|
|
961
|
+
},
|
|
962
|
+
drawer: {
|
|
963
|
+
name: "Drawer",
|
|
964
|
+
package: "@omnifyjp/ui",
|
|
965
|
+
category: "overlay",
|
|
966
|
+
description: "Bottom drawer (vaul). Content MUST be wrapped in DrawerBody.",
|
|
967
|
+
props: "open?: boolean, onOpenChange?: (open: boolean) => void",
|
|
968
|
+
example: "<Drawer open={open} onOpenChange={setOpen}><DrawerContent><DrawerHeader><DrawerTitle>Title</DrawerTitle></DrawerHeader><DrawerBody>Content here</DrawerBody></DrawerContent></Drawer>"
|
|
969
|
+
},
|
|
970
|
+
tabs: {
|
|
971
|
+
name: "Tabs",
|
|
972
|
+
package: "@omnifyjp/ui",
|
|
973
|
+
category: "navigation",
|
|
974
|
+
description: "Tab navigation",
|
|
975
|
+
props: "value?: string, onValueChange?: (value: string) => void, defaultValue?: string",
|
|
976
|
+
example: '<Tabs value={tab} onValueChange={setTab}><TabsList><TabsTrigger value="a">Tab A</TabsTrigger></TabsList><TabsContent value="a">Content A</TabsContent></Tabs>'
|
|
977
|
+
},
|
|
978
|
+
"permission-grid": {
|
|
979
|
+
name: "PermissionGrid",
|
|
980
|
+
package: "@omnifyjp/ui",
|
|
981
|
+
category: "domain",
|
|
982
|
+
description: "Permission matrix grid. Uses module:action ID format via buildPermissionId().",
|
|
983
|
+
props: "modules: Module[], actions: Action[], permissions: Record<string, boolean>, onChange: (id: string, checked: boolean) => void, labels?: { ... }",
|
|
984
|
+
example: "<PermissionGrid modules={modules} actions={actions} permissions={perms} onChange={handleChange} />"
|
|
985
|
+
},
|
|
986
|
+
"scope-tree": {
|
|
987
|
+
name: "ScopeTree",
|
|
988
|
+
package: "@omnifyjp/ui",
|
|
989
|
+
category: "domain",
|
|
990
|
+
description: "Hierarchical scope tree. Note: ScopeTreeSelectedScope.type is string (generic), cast to ScopeType in consumers.",
|
|
991
|
+
props: "scopes: ScopeNode[], selectedScope?: SelectedScope, onSelectScope?: (scope: SelectedScope) => void, labels?: { ... }",
|
|
992
|
+
example: "<ScopeTree scopes={scopes} selectedScope={selected} onSelectScope={setSelected} />"
|
|
993
|
+
},
|
|
994
|
+
"rich-text-editor": {
|
|
995
|
+
name: "RichTextEditor",
|
|
996
|
+
package: "@omnifyjp/editor",
|
|
997
|
+
category: "editor",
|
|
998
|
+
description: 'Tiptap-based rich text editor. Requires CSS: import "@omnifyjp/editor/styles/rich-text-editor.css"',
|
|
999
|
+
props: "content?: string, onChange?: (html: string) => void, placeholder?: string",
|
|
1000
|
+
example: 'import { RichTextEditor } from "@omnifyjp/editor";\nimport "@omnifyjp/editor/styles/rich-text-editor.css";\n\n<RichTextEditor content={html} onChange={setHtml} />'
|
|
1001
|
+
}
|
|
1002
|
+
};
|
|
1003
|
+
function registerValidateTools(server2) {
|
|
1004
|
+
server2.tool(
|
|
1005
|
+
"validate_component",
|
|
1006
|
+
"Validate React component code against @omnifyjp design system rules. Checks for hardcoded colors, wrong spacing tokens, layout anti-patterns, and other violations.",
|
|
1007
|
+
{ code: z.string().describe("React component source code to validate") },
|
|
1008
|
+
async ({ code }) => {
|
|
1009
|
+
const violations = [];
|
|
1010
|
+
const lines = code.split("\n");
|
|
1011
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1012
|
+
const line = lines[i];
|
|
1013
|
+
for (const rule of VIOLATION_RULES) {
|
|
1014
|
+
if (rule.pattern.test(line)) {
|
|
1015
|
+
violations.push({
|
|
1016
|
+
line: i + 1,
|
|
1017
|
+
message: rule.message,
|
|
1018
|
+
suggestion: rule.suggestion,
|
|
1019
|
+
category: rule.category
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
if (violations.length === 0) {
|
|
1025
|
+
return {
|
|
1026
|
+
content: [{
|
|
1027
|
+
type: "text",
|
|
1028
|
+
text: "No design system violations found. The code follows @omnifyjp conventions."
|
|
1029
|
+
}]
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
const grouped = {
|
|
1033
|
+
color: violations.filter((v) => v.category === "color"),
|
|
1034
|
+
density: violations.filter((v) => v.category === "density"),
|
|
1035
|
+
layout: violations.filter((v) => v.category === "layout"),
|
|
1036
|
+
pattern: violations.filter((v) => v.category === "pattern")
|
|
1037
|
+
};
|
|
1038
|
+
let output = `Found ${violations.length} design system violation(s):
|
|
1039
|
+
|
|
1040
|
+
`;
|
|
1041
|
+
for (const [category, items] of Object.entries(grouped)) {
|
|
1042
|
+
if (items.length === 0) continue;
|
|
1043
|
+
output += `### ${category.charAt(0).toUpperCase() + category.slice(1)} Issues
|
|
1044
|
+
`;
|
|
1045
|
+
for (const v of items) {
|
|
1046
|
+
output += `- **Line ${v.line}**: ${v.message} \u2192 ${v.suggestion}
|
|
1047
|
+
`;
|
|
1048
|
+
}
|
|
1049
|
+
output += "\n";
|
|
1050
|
+
}
|
|
1051
|
+
return {
|
|
1052
|
+
content: [{ type: "text", text: output }]
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
);
|
|
1056
|
+
server2.tool(
|
|
1057
|
+
"suggest_token",
|
|
1058
|
+
'Suggest the correct @omnifyjp design token for a given need. Describe what you need (e.g., "page background", "button height", "section spacing") and get the right token.',
|
|
1059
|
+
{ need: z.string().describe('Description of what design token is needed (e.g., "subdued text color", "page padding", "button height")') },
|
|
1060
|
+
async ({ need }) => {
|
|
1061
|
+
const query = need.toLowerCase();
|
|
1062
|
+
const matches = TOKEN_DB.filter(
|
|
1063
|
+
(t) => t.description.toLowerCase().includes(query) || t.category.includes(query) || t.token.includes(query) || t.tailwind.toLowerCase().includes(query)
|
|
1064
|
+
);
|
|
1065
|
+
const keywordMatches = [];
|
|
1066
|
+
if (matches.length === 0) {
|
|
1067
|
+
const keywords = query.split(/\s+/);
|
|
1068
|
+
for (const token of TOKEN_DB) {
|
|
1069
|
+
const searchText = `${token.description} ${token.token} ${token.tailwind} ${token.category}`.toLowerCase();
|
|
1070
|
+
if (keywords.some((k) => searchText.includes(k))) {
|
|
1071
|
+
keywordMatches.push(token);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
const results = matches.length > 0 ? matches : keywordMatches;
|
|
1076
|
+
if (results.length === 0) {
|
|
1077
|
+
return {
|
|
1078
|
+
content: [{
|
|
1079
|
+
type: "text",
|
|
1080
|
+
text: `No matching token found for "${need}". Check design://tokens resource for the full token reference.`
|
|
1081
|
+
}]
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
let output = `Recommended token(s) for "${need}":
|
|
1085
|
+
|
|
1086
|
+
`;
|
|
1087
|
+
for (const t of results) {
|
|
1088
|
+
output += `- **${t.tailwind}** (CSS: \`${t.token}\`) \u2014 ${t.description}
|
|
1089
|
+
`;
|
|
1090
|
+
}
|
|
1091
|
+
return {
|
|
1092
|
+
content: [{ type: "text", text: output }]
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
);
|
|
1096
|
+
server2.tool(
|
|
1097
|
+
"get_component_api",
|
|
1098
|
+
"Get the API reference for a specific @omnifyjp component \u2014 props, variants, sizes, and usage example.",
|
|
1099
|
+
{ component: z.string().describe('Component name (e.g., "Button", "Badge", "Dialog", "PermissionGrid")') },
|
|
1100
|
+
async ({ component }) => {
|
|
1101
|
+
const key = component.toLowerCase().replace(/\s+/g, "-");
|
|
1102
|
+
const info = COMPONENT_REGISTRY[key];
|
|
1103
|
+
if (!info) {
|
|
1104
|
+
const keys = Object.keys(COMPONENT_REGISTRY);
|
|
1105
|
+
const fuzzy = keys.filter((k) => k.includes(key) || key.includes(k));
|
|
1106
|
+
if (fuzzy.length > 0) {
|
|
1107
|
+
const match = COMPONENT_REGISTRY[fuzzy[0]];
|
|
1108
|
+
return {
|
|
1109
|
+
content: [{
|
|
1110
|
+
type: "text",
|
|
1111
|
+
text: formatComponentInfo(match)
|
|
1112
|
+
}]
|
|
1113
|
+
};
|
|
1114
|
+
}
|
|
1115
|
+
return {
|
|
1116
|
+
content: [{
|
|
1117
|
+
type: "text",
|
|
1118
|
+
text: `Component "${component}" not found. Available: ${keys.map((k) => COMPONENT_REGISTRY[k].name).join(", ")}. Check design://components for the full inventory.`
|
|
1119
|
+
}]
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
return {
|
|
1123
|
+
content: [{
|
|
1124
|
+
type: "text",
|
|
1125
|
+
text: formatComponentInfo(info)
|
|
1126
|
+
}]
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
);
|
|
1130
|
+
}
|
|
1131
|
+
function formatComponentInfo(info) {
|
|
1132
|
+
return `## ${info.name}
|
|
1133
|
+
|
|
1134
|
+
**Package**: \`${info.package}\`
|
|
1135
|
+
**Category**: ${info.category}
|
|
1136
|
+
|
|
1137
|
+
${info.description}
|
|
1138
|
+
|
|
1139
|
+
### Props
|
|
1140
|
+
${info.props}
|
|
1141
|
+
|
|
1142
|
+
### Example
|
|
1143
|
+
\`\`\`tsx
|
|
1144
|
+
${info.example}
|
|
1145
|
+
\`\`\``;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
// src/index.ts
|
|
1149
|
+
var server = new McpServer({
|
|
1150
|
+
name: "@omnifyjp/mcp-design-system",
|
|
1151
|
+
version: "0.1.0"
|
|
1152
|
+
}, {
|
|
1153
|
+
capabilities: {
|
|
1154
|
+
resources: {},
|
|
1155
|
+
tools: {}
|
|
1156
|
+
}
|
|
1157
|
+
});
|
|
1158
|
+
registerTokensResource(server);
|
|
1159
|
+
registerRulesResource(server);
|
|
1160
|
+
registerComponentsResource(server);
|
|
1161
|
+
registerLayoutResource(server);
|
|
1162
|
+
registerValidateTools(server);
|
|
1163
|
+
var transport = new StdioServerTransport();
|
|
1164
|
+
await server.connect(transport);
|
|
1165
|
+
//# sourceMappingURL=index.js.map
|