mcp-dndgrid 0.1.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/PROJECT_SUMMARY.md +482 -0
- package/QUICKSTART.md +223 -0
- package/README.md +365 -0
- package/STATUS.md +315 -0
- package/USAGE_GUIDE.md +547 -0
- package/dist/chunk-CMGEAPA5.js +157 -0
- package/dist/chunk-CMGEAPA5.js.map +1 -0
- package/dist/chunk-QZHBI6ZI.js +5281 -0
- package/dist/chunk-QZHBI6ZI.js.map +1 -0
- package/dist/chunk-SEGVTWSK.js +44 -0
- package/dist/chunk-SEGVTWSK.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +248012 -0
- package/dist/index.js.map +1 -0
- package/dist/stdio-FWYJXSU7.js +101 -0
- package/dist/stdio-FWYJXSU7.js.map +1 -0
- package/dist/template-JDMAVVX7.js +9 -0
- package/dist/template-JDMAVVX7.js.map +1 -0
- package/examples/claude_desktop_config.example.json +12 -0
- package/examples/example-complex-editor.tsx +107 -0
- package/examples/example-dashboard.tsx +65 -0
- package/examples/example-ide-layout.tsx +53 -0
- package/examples/test-generator.ts +37 -0
- package/examples/test-parser.ts +121 -0
- package/examples/test-scenarios.md +496 -0
- package/package.json +42 -0
- package/src/index.ts +16 -0
- package/src/server.ts +314 -0
- package/src/tools/analyze-layout.ts +193 -0
- package/src/tools/apply-template.ts +125 -0
- package/src/tools/generate-layout.ts +235 -0
- package/src/tools/interactive-builder.ts +100 -0
- package/src/tools/validate-layout.ts +113 -0
- package/src/types/layout.ts +48 -0
- package/src/types/template.ts +181 -0
- package/src/utils/ast-parser.ts +264 -0
- package/src/utils/code-generator.ts +123 -0
- package/src/utils/layout-analyzer.ts +105 -0
- package/src/utils/layout-builder.ts +127 -0
- package/src/utils/validator.ts +263 -0
- package/stderr.log +1 -0
- package/stdout.log +0 -0
- package/test-mcp.js +27 -0
- package/tsconfig.json +29 -0
- package/tsup.config.ts +16 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
LayoutTree,
|
|
3
|
+
LayoutNode,
|
|
4
|
+
ValidationError,
|
|
5
|
+
ValidationWarning,
|
|
6
|
+
} from '../types/layout.js';
|
|
7
|
+
import { LayoutAnalyzer } from './layout-analyzer.js';
|
|
8
|
+
|
|
9
|
+
export interface ValidationResult {
|
|
10
|
+
valid: boolean;
|
|
11
|
+
errors: ValidationError[];
|
|
12
|
+
warnings: ValidationWarning[];
|
|
13
|
+
suggestions: string[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Validates DndGrid layout structure and constraints
|
|
18
|
+
*/
|
|
19
|
+
export class Validator {
|
|
20
|
+
private errors: ValidationError[] = [];
|
|
21
|
+
private warnings: ValidationWarning[] = [];
|
|
22
|
+
private suggestions: string[] = [];
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Validate a layout tree
|
|
26
|
+
*/
|
|
27
|
+
validate(layout: LayoutTree, strict: boolean = false): ValidationResult {
|
|
28
|
+
this.errors = [];
|
|
29
|
+
this.warnings = [];
|
|
30
|
+
this.suggestions = [];
|
|
31
|
+
|
|
32
|
+
// Validate container dimensions
|
|
33
|
+
this.validateContainer(layout);
|
|
34
|
+
|
|
35
|
+
// Validate tree structure
|
|
36
|
+
this.validateNode(layout.child);
|
|
37
|
+
|
|
38
|
+
// Strict mode validations
|
|
39
|
+
if (strict) {
|
|
40
|
+
this.validatePerformance(layout);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
valid: this.errors.length === 0,
|
|
45
|
+
errors: this.errors,
|
|
46
|
+
warnings: this.warnings,
|
|
47
|
+
suggestions: this.suggestions,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Validate container properties
|
|
53
|
+
*/
|
|
54
|
+
private validateContainer(layout: LayoutTree): void {
|
|
55
|
+
if (layout.width <= 0) {
|
|
56
|
+
this.errors.push({
|
|
57
|
+
type: 'constraint',
|
|
58
|
+
message: `Invalid container width: ${layout.width}. Must be positive.`,
|
|
59
|
+
fix: 'width={1200}',
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (layout.height <= 0) {
|
|
64
|
+
this.errors.push({
|
|
65
|
+
type: 'constraint',
|
|
66
|
+
message: `Invalid container height: ${layout.height}. Must be positive.`,
|
|
67
|
+
fix: 'height={800}',
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (layout.width < 100 || layout.height < 100) {
|
|
72
|
+
this.warnings.push({
|
|
73
|
+
message: `Very small container dimensions (${layout.width}x${layout.height}). Consider larger sizes.`,
|
|
74
|
+
suggestion: 'Minimum recommended: 400x300',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Validate a layout node
|
|
81
|
+
*/
|
|
82
|
+
private validateNode(node: LayoutNode, depth: number = 1): void {
|
|
83
|
+
if (node.type === 'split') {
|
|
84
|
+
this.validateSplit(node, depth);
|
|
85
|
+
this.validateNode(node.primary, depth + 1);
|
|
86
|
+
this.validateNode(node.secondary, depth + 1);
|
|
87
|
+
} else {
|
|
88
|
+
this.validateItem(node, depth);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Validate Split node
|
|
94
|
+
*/
|
|
95
|
+
private validateSplit(node: LayoutNode, depth: number): void {
|
|
96
|
+
if (node.type !== 'split') return;
|
|
97
|
+
|
|
98
|
+
// Validate ratio
|
|
99
|
+
if (node.ratio <= 0 || node.ratio >= 1) {
|
|
100
|
+
this.errors.push({
|
|
101
|
+
type: 'constraint',
|
|
102
|
+
message: `Invalid split ratio: ${node.ratio}. Must be between 0 and 1 (exclusive).`,
|
|
103
|
+
fix: 'ratio={0.5}',
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Warn about extreme ratios
|
|
108
|
+
if (node.ratio < 0.1 || node.ratio > 0.9) {
|
|
109
|
+
this.warnings.push({
|
|
110
|
+
message: `Extreme split ratio: ${node.ratio}. This creates very small panels.`,
|
|
111
|
+
suggestion: 'Consider ratios between 0.2 and 0.8 for better UX',
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Validate direction
|
|
116
|
+
if (node.direction !== 'horizontal' && node.direction !== 'vertical') {
|
|
117
|
+
this.errors.push({
|
|
118
|
+
type: 'constraint',
|
|
119
|
+
message: `Invalid direction: ${node.direction}. Must be 'horizontal' or 'vertical'.`,
|
|
120
|
+
fix: 'direction="horizontal"',
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Check if both children exist
|
|
125
|
+
if (!node.primary || !node.secondary) {
|
|
126
|
+
this.errors.push({
|
|
127
|
+
type: 'structure',
|
|
128
|
+
message: 'Split must have exactly 2 children (primary and secondary).',
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Warn about deep nesting
|
|
133
|
+
if (depth > 5) {
|
|
134
|
+
this.warnings.push({
|
|
135
|
+
message: `Deep nesting detected (depth: ${depth}). This may impact performance.`,
|
|
136
|
+
suggestion: 'Consider flattening the layout structure',
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Validate Item node
|
|
143
|
+
*/
|
|
144
|
+
private validateItem(node: LayoutNode, depth: number): void {
|
|
145
|
+
if (node.type !== 'item') return;
|
|
146
|
+
|
|
147
|
+
// Validate component name
|
|
148
|
+
if (!node.component || node.component.trim().length === 0) {
|
|
149
|
+
this.errors.push({
|
|
150
|
+
type: 'structure',
|
|
151
|
+
message: 'Item must have a component name.',
|
|
152
|
+
fix: '<ComponentName />',
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Check component naming convention
|
|
157
|
+
if (node.component && !/^[A-Z]/.test(node.component)) {
|
|
158
|
+
this.warnings.push({
|
|
159
|
+
message: `Component name "${node.component}" should start with uppercase letter.`,
|
|
160
|
+
suggestion: `Use "${node.component.charAt(0).toUpperCase() + node.component.slice(1)}"`,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Validate performance constraints (strict mode)
|
|
167
|
+
*/
|
|
168
|
+
private validatePerformance(layout: LayoutTree): void {
|
|
169
|
+
const metadata = LayoutAnalyzer.calculateMetadata(layout);
|
|
170
|
+
|
|
171
|
+
// Check item count
|
|
172
|
+
if (metadata.itemCount > 50) {
|
|
173
|
+
this.errors.push({
|
|
174
|
+
type: 'constraint',
|
|
175
|
+
message: `Too many items: ${metadata.itemCount}. Maximum recommended: 50`,
|
|
176
|
+
});
|
|
177
|
+
} else if (metadata.itemCount > 20) {
|
|
178
|
+
this.warnings.push({
|
|
179
|
+
message: `High item count: ${metadata.itemCount}. Consider reducing for better performance.`,
|
|
180
|
+
suggestion: 'Recommended: < 20 items',
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Check depth
|
|
185
|
+
if (metadata.maxDepth > 6) {
|
|
186
|
+
this.errors.push({
|
|
187
|
+
type: 'constraint',
|
|
188
|
+
message: `Tree too deep: ${metadata.maxDepth} levels. Maximum recommended: 6`,
|
|
189
|
+
});
|
|
190
|
+
} else if (metadata.maxDepth > 4) {
|
|
191
|
+
this.warnings.push({
|
|
192
|
+
message: `Deep tree structure: ${metadata.maxDepth} levels.`,
|
|
193
|
+
suggestion: 'Recommended: < 4 levels for optimal performance',
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Add performance suggestions
|
|
198
|
+
if (metadata.estimatedPerformance === 'fair' || metadata.estimatedPerformance === 'poor') {
|
|
199
|
+
this.suggestions.push(
|
|
200
|
+
`Performance score: ${metadata.estimatedPerformance}. Consider simplifying the layout.`
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Check for optimization opportunities
|
|
205
|
+
if (metadata.splitCount > metadata.itemCount) {
|
|
206
|
+
this.suggestions.push(
|
|
207
|
+
'More splits than items detected. Some splits may be unnecessary.'
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Validate code string (wrapper for AST parsing)
|
|
214
|
+
*/
|
|
215
|
+
validateCode(code: string, strict: boolean = false): ValidationResult {
|
|
216
|
+
this.errors = [];
|
|
217
|
+
this.warnings = [];
|
|
218
|
+
this.suggestions = [];
|
|
219
|
+
|
|
220
|
+
// Check for "use client" directive (Next.js)
|
|
221
|
+
if (!code.includes('"use client"') && !code.includes("'use client'")) {
|
|
222
|
+
this.warnings.push({
|
|
223
|
+
message: 'Missing "use client" directive for Next.js App Router.',
|
|
224
|
+
suggestion: 'Add "use client"; at the top of the file',
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Check for required imports
|
|
229
|
+
if (!code.includes('DndGridContainer')) {
|
|
230
|
+
this.errors.push({
|
|
231
|
+
type: 'structure',
|
|
232
|
+
message: 'Missing DndGridContainer import.',
|
|
233
|
+
fix: "import { DndGridContainer } from 'zerojin/components';",
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Basic syntax checks
|
|
238
|
+
const openBraces = (code.match(/{/g) || []).length;
|
|
239
|
+
const closeBraces = (code.match(/}/g) || []).length;
|
|
240
|
+
if (openBraces !== closeBraces) {
|
|
241
|
+
this.errors.push({
|
|
242
|
+
type: 'syntax',
|
|
243
|
+
message: `Mismatched braces: ${openBraces} opening, ${closeBraces} closing.`,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const openTags = (code.match(/<(?!\/)[A-Z]/g) || []).length;
|
|
248
|
+
const closeTags = (code.match(/<\/[A-Z]/g) || []).length;
|
|
249
|
+
if (openTags !== closeTags) {
|
|
250
|
+
this.errors.push({
|
|
251
|
+
type: 'syntax',
|
|
252
|
+
message: `Mismatched JSX tags: ${openTags} opening, ${closeTags} closing.`,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
valid: this.errors.length === 0,
|
|
258
|
+
errors: this.errors,
|
|
259
|
+
warnings: this.warnings,
|
|
260
|
+
suggestions: this.suggestions,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
}
|
package/stderr.log
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
DndGrid MCP Server started
|
package/stdout.log
ADDED
|
File without changes
|
package/test-mcp.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3
|
+
import { ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
|
|
5
|
+
async function test() {
|
|
6
|
+
const transport = new StdioClientTransport({
|
|
7
|
+
command: "node",
|
|
8
|
+
args: ["/Users/zero/Desktop/zerojin-core/mcp/dist/index.cjs"],
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const client = new Client({
|
|
12
|
+
name: "test-client",
|
|
13
|
+
version: "1.0.0",
|
|
14
|
+
}, {
|
|
15
|
+
capabilities: {}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
await client.connect(transport);
|
|
19
|
+
console.log("Connected to server");
|
|
20
|
+
|
|
21
|
+
const tools = await client.listTools();
|
|
22
|
+
console.log("Tools:", JSON.stringify(tools, null, 2));
|
|
23
|
+
|
|
24
|
+
await transport.close();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
test().catch(console.error);
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"lib": ["ES2022"],
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"allowSyntheticDefaultImports": true,
|
|
10
|
+
|
|
11
|
+
"strict": true,
|
|
12
|
+
"noUnusedLocals": true,
|
|
13
|
+
"noUnusedParameters": true,
|
|
14
|
+
"noFallthroughCasesInSwitch": true,
|
|
15
|
+
"noUncheckedIndexedAccess": true,
|
|
16
|
+
"exactOptionalPropertyTypes": true,
|
|
17
|
+
|
|
18
|
+
"declaration": true,
|
|
19
|
+
"declarationMap": true,
|
|
20
|
+
"sourceMap": true,
|
|
21
|
+
"outDir": "./dist",
|
|
22
|
+
"rootDir": "./src",
|
|
23
|
+
|
|
24
|
+
"resolveJsonModule": true,
|
|
25
|
+
"isolatedModules": true
|
|
26
|
+
},
|
|
27
|
+
"include": ["src/**/*"],
|
|
28
|
+
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
29
|
+
}
|
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineConfig } from 'tsup';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
entry: ['src/index.ts'],
|
|
5
|
+
format: ['esm'],
|
|
6
|
+
dts: true,
|
|
7
|
+
sourcemap: true,
|
|
8
|
+
clean: true,
|
|
9
|
+
outDir: 'dist',
|
|
10
|
+
target: 'node18',
|
|
11
|
+
shims: false,
|
|
12
|
+
noExternal: [/.*/],
|
|
13
|
+
banner: {
|
|
14
|
+
js: '#!/usr/bin/env node',
|
|
15
|
+
},
|
|
16
|
+
});
|