@supersoniks/concorde 3.3.2 → 3.3.3
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/build-infos.json +1 -1
- package/concorde-core.bundle.js +2 -2
- package/concorde-core.es.js +2 -2
- package/dist/concorde-core.bundle.js +2 -2
- package/dist/concorde-core.es.js +2 -2
- package/package.json +1 -1
- package/src/core/decorators/subscriber/onAssign.ts +3 -1
- package/src/docs/_misc/on-assign.md +5 -5
- package/src/docs/search/docs-search.json +85 -0
- package/mcp-server/COMPARISON-MCP.md +0 -176
- package/mcp-server/README-MCP-NODEJS.md +0 -284
- package/mcp-server/README-MCP.md +0 -114
- package/mcp-server/README.md +0 -127
- package/mcp-server/TECHNICAL-DOCS.md +0 -269
- package/mcp-server/concorde-mcp-server.js +0 -859
- package/mcp-server/concorde-mcp-server.py +0 -801
- package/mcp-server/cursor-mcp-config-advanced.json +0 -68
- package/mcp-server/cursor-mcp-config-nodejs.json +0 -74
- package/mcp-server/cursor-mcp-config.json +0 -11
- package/mcp-server/install-mcp-nodejs.sh +0 -104
- package/mcp-server/install-mcp.sh +0 -62
- package/mcp-server/package-lock.json +0 -147
- package/mcp-server/package-mcp.json +0 -40
- package/mcp-server/package.json +0 -40
- package/mcp-server/test-mcp.js +0 -107
- package/mcp-server/test-mcp.py +0 -73
|
@@ -1,859 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Serveur MCP pour Concorde - Framework de composants Web
|
|
5
|
-
* Version Node.js
|
|
6
|
-
* Permet à l'IA de Cursor d'accéder facilement à toute la documentation et au code de Concorde
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { promises as fs } from 'fs';
|
|
10
|
-
import path from 'path';
|
|
11
|
-
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
12
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
13
|
-
import {
|
|
14
|
-
CallToolRequestSchema,
|
|
15
|
-
ListResourcesRequestSchema,
|
|
16
|
-
ListToolsRequestSchema,
|
|
17
|
-
ReadResourceRequestSchema,
|
|
18
|
-
} from '@modelcontextprotocol/sdk/types.js';
|
|
19
|
-
|
|
20
|
-
// Configuration du projet Concorde
|
|
21
|
-
const CONCORDE_ROOT = path.dirname(path.dirname(new URL(import.meta.url).pathname));
|
|
22
|
-
const SRC_PATH = path.join(CONCORDE_ROOT, 'src');
|
|
23
|
-
const DOCS_PATH = path.join(CONCORDE_ROOT, 'docs');
|
|
24
|
-
const CORE_PATH = path.join(SRC_PATH, 'core');
|
|
25
|
-
const COMPONENTS_PATH = path.join(CORE_PATH, 'components');
|
|
26
|
-
|
|
27
|
-
class ComponentInfo {
|
|
28
|
-
constructor(name, tagName, category, filePath, docPath, description, props, examples) {
|
|
29
|
-
this.name = name;
|
|
30
|
-
this.tagName = tagName;
|
|
31
|
-
this.category = category;
|
|
32
|
-
this.filePath = filePath;
|
|
33
|
-
this.docPath = docPath;
|
|
34
|
-
this.description = description;
|
|
35
|
-
this.props = props;
|
|
36
|
-
this.examples = examples;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
class ConcordeMCPServer {
|
|
41
|
-
constructor() {
|
|
42
|
-
this.server = new Server(
|
|
43
|
-
{
|
|
44
|
-
name: 'concorde-mcp',
|
|
45
|
-
version: '1.0.0',
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
capabilities: {
|
|
49
|
-
resources: {},
|
|
50
|
-
tools: {},
|
|
51
|
-
},
|
|
52
|
-
}
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
this.components = new Map();
|
|
56
|
-
this.documentation = new Map();
|
|
57
|
-
|
|
58
|
-
this.setupHandlers();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async initialize() {
|
|
62
|
-
await this.loadComponents();
|
|
63
|
-
await this.loadDocumentation();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
setupHandlers() {
|
|
67
|
-
// Handler pour lister les ressources
|
|
68
|
-
this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
69
|
-
const resources = [];
|
|
70
|
-
|
|
71
|
-
// Ressources de composants
|
|
72
|
-
for (const [compName, compInfo] of this.components) {
|
|
73
|
-
resources.push({
|
|
74
|
-
uri: `concorde://component/${compName}`,
|
|
75
|
-
name: `Composant ${compName}`,
|
|
76
|
-
description: compInfo.description,
|
|
77
|
-
mimeType: 'text/markdown'
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Ressources de documentation
|
|
82
|
-
for (const [docName, docContent] of this.documentation) {
|
|
83
|
-
resources.push({
|
|
84
|
-
uri: `concorde://docs/${docName}`,
|
|
85
|
-
name: `Documentation ${docName}`,
|
|
86
|
-
description: `Documentation Concorde: ${docName}`,
|
|
87
|
-
mimeType: 'text/markdown'
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Ressources d'exemples
|
|
92
|
-
resources.push({
|
|
93
|
-
uri: 'concorde://examples/ui-components',
|
|
94
|
-
name: 'Exemples composants UI',
|
|
95
|
-
description: 'Exemples d\'utilisation des composants UI',
|
|
96
|
-
mimeType: 'text/markdown'
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
resources.push({
|
|
100
|
-
uri: 'concorde://examples/functional-components',
|
|
101
|
-
name: 'Exemples composants fonctionnels',
|
|
102
|
-
description: 'Exemples d\'utilisation des composants fonctionnels',
|
|
103
|
-
mimeType: 'text/markdown'
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
return { resources };
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
// Handler pour lire les ressources
|
|
110
|
-
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
111
|
-
const { uri } = request.params;
|
|
112
|
-
|
|
113
|
-
if (uri.startsWith('concorde://component/')) {
|
|
114
|
-
const compName = uri.replace('concorde://component/', '');
|
|
115
|
-
if (this.components.has(compName)) {
|
|
116
|
-
const compInfo = this.components.get(compName);
|
|
117
|
-
const content = this.generateComponentDoc(compInfo);
|
|
118
|
-
return {
|
|
119
|
-
contents: [{
|
|
120
|
-
type: 'text',
|
|
121
|
-
text: content
|
|
122
|
-
}]
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (uri.startsWith('concorde://docs/')) {
|
|
128
|
-
const docName = uri.replace('concorde://docs/', '');
|
|
129
|
-
if (this.documentation.has(docName)) {
|
|
130
|
-
return {
|
|
131
|
-
contents: [{
|
|
132
|
-
type: 'text',
|
|
133
|
-
text: this.documentation.get(docName)
|
|
134
|
-
}]
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (uri === 'concorde://examples/ui-components') {
|
|
140
|
-
const content = this.generateUIExamples();
|
|
141
|
-
return {
|
|
142
|
-
contents: [{
|
|
143
|
-
type: 'text',
|
|
144
|
-
text: content
|
|
145
|
-
}]
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (uri === 'concorde://examples/functional-components') {
|
|
150
|
-
const content = this.generateFunctionalExamples();
|
|
151
|
-
return {
|
|
152
|
-
contents: [{
|
|
153
|
-
type: 'text',
|
|
154
|
-
text: content
|
|
155
|
-
}]
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return {
|
|
160
|
-
contents: [{
|
|
161
|
-
type: 'text',
|
|
162
|
-
text: 'Ressource non trouvée'
|
|
163
|
-
}]
|
|
164
|
-
};
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
// Handler pour lister les outils
|
|
168
|
-
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
169
|
-
const tools = [
|
|
170
|
-
{
|
|
171
|
-
name: 'search_component',
|
|
172
|
-
description: 'Recherche un composant Concorde par nom ou fonctionnalité',
|
|
173
|
-
inputSchema: {
|
|
174
|
-
type: 'object',
|
|
175
|
-
properties: {
|
|
176
|
-
query: {
|
|
177
|
-
type: 'string',
|
|
178
|
-
description: 'Terme de recherche (nom du composant, fonctionnalité, etc.)'
|
|
179
|
-
}
|
|
180
|
-
},
|
|
181
|
-
required: ['query']
|
|
182
|
-
}
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
name: 'get_component_info',
|
|
186
|
-
description: 'Obtient des informations détaillées sur un composant spécifique',
|
|
187
|
-
inputSchema: {
|
|
188
|
-
type: 'object',
|
|
189
|
-
properties: {
|
|
190
|
-
component_name: {
|
|
191
|
-
type: 'string',
|
|
192
|
-
description: 'Nom du composant (ex: button, modal, input)'
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
required: ['component_name']
|
|
196
|
-
}
|
|
197
|
-
},
|
|
198
|
-
{
|
|
199
|
-
name: 'generate_component_code',
|
|
200
|
-
description: 'Génère du code HTML pour un composant Concorde avec ses propriétés',
|
|
201
|
-
inputSchema: {
|
|
202
|
-
type: 'object',
|
|
203
|
-
properties: {
|
|
204
|
-
component_name: {
|
|
205
|
-
type: 'string',
|
|
206
|
-
description: 'Nom du composant'
|
|
207
|
-
},
|
|
208
|
-
properties: {
|
|
209
|
-
type: 'object',
|
|
210
|
-
description: 'Propriétés du composant',
|
|
211
|
-
additionalProperties: true
|
|
212
|
-
},
|
|
213
|
-
content: {
|
|
214
|
-
type: 'string',
|
|
215
|
-
description: 'Contenu du composant (optionnel)'
|
|
216
|
-
}
|
|
217
|
-
},
|
|
218
|
-
required: ['component_name']
|
|
219
|
-
}
|
|
220
|
-
},
|
|
221
|
-
{
|
|
222
|
-
name: 'get_form_example',
|
|
223
|
-
description: 'Génère un exemple de formulaire avec des composants Concorde',
|
|
224
|
-
inputSchema: {
|
|
225
|
-
type: 'object',
|
|
226
|
-
properties: {
|
|
227
|
-
fields: {
|
|
228
|
-
type: 'array',
|
|
229
|
-
description: 'Liste des champs du formulaire',
|
|
230
|
-
items: {
|
|
231
|
-
type: 'object',
|
|
232
|
-
properties: {
|
|
233
|
-
name: { type: 'string' },
|
|
234
|
-
type: { type: 'string' },
|
|
235
|
-
label: { type: 'string' },
|
|
236
|
-
required: { type: 'boolean' }
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
},
|
|
243
|
-
{
|
|
244
|
-
name: 'get_theme_info',
|
|
245
|
-
description: 'Obtient des informations sur le système de thèmes Concorde',
|
|
246
|
-
inputSchema: {
|
|
247
|
-
type: 'object',
|
|
248
|
-
properties: {
|
|
249
|
-
theme: {
|
|
250
|
-
type: 'string',
|
|
251
|
-
description: 'Nom du thème (light, dark, auto)',
|
|
252
|
-
enum: ['light', 'dark', 'auto']
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
},
|
|
257
|
-
{
|
|
258
|
-
name: 'get_icons_list',
|
|
259
|
-
description: 'Liste toutes les icônes disponibles dans Concorde',
|
|
260
|
-
inputSchema: {
|
|
261
|
-
type: 'object',
|
|
262
|
-
properties: {
|
|
263
|
-
library: {
|
|
264
|
-
type: 'string',
|
|
265
|
-
description: 'Bibliothèque d\'icônes (core, iconoir, fontAwesome)',
|
|
266
|
-
enum: ['core', 'iconoir', 'fontAwesome']
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
];
|
|
272
|
-
|
|
273
|
-
return { tools };
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
// Handler pour exécuter les outils
|
|
277
|
-
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
278
|
-
const { name, arguments: args } = request.params;
|
|
279
|
-
|
|
280
|
-
switch (name) {
|
|
281
|
-
case 'search_component':
|
|
282
|
-
return await this.searchComponent(args.query);
|
|
283
|
-
case 'get_component_info':
|
|
284
|
-
return await this.getComponentInfo(args.component_name);
|
|
285
|
-
case 'generate_component_code':
|
|
286
|
-
return await this.generateComponentCode(
|
|
287
|
-
args.component_name,
|
|
288
|
-
args.properties || {},
|
|
289
|
-
args.content || ''
|
|
290
|
-
);
|
|
291
|
-
case 'get_form_example':
|
|
292
|
-
return await this.getFormExample(args.fields || []);
|
|
293
|
-
case 'get_theme_info':
|
|
294
|
-
return await this.getThemeInfo(args.theme || 'light');
|
|
295
|
-
case 'get_icons_list':
|
|
296
|
-
return await this.getIconsList(args.library || 'core');
|
|
297
|
-
default:
|
|
298
|
-
return {
|
|
299
|
-
content: [{
|
|
300
|
-
type: 'text',
|
|
301
|
-
text: `Outil inconnu: ${name}`
|
|
302
|
-
}]
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
async loadComponents() {
|
|
309
|
-
// Charger les composants UI
|
|
310
|
-
const uiPath = path.join(COMPONENTS_PATH, 'ui');
|
|
311
|
-
await this.loadComponentsFromDirectory(uiPath, 'ui');
|
|
312
|
-
|
|
313
|
-
// Charger les composants fonctionnels
|
|
314
|
-
const functionalPath = path.join(COMPONENTS_PATH, 'functional');
|
|
315
|
-
await this.loadComponentsFromDirectory(functionalPath, 'functional');
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
async loadComponentsFromDirectory(dirPath, category) {
|
|
319
|
-
try {
|
|
320
|
-
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
321
|
-
|
|
322
|
-
for (const entry of entries) {
|
|
323
|
-
if (entry.isDirectory() && !entry.name.startsWith('_')) {
|
|
324
|
-
await this.loadComponent(path.join(dirPath, entry.name), category);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
} catch (error) {
|
|
328
|
-
console.warn(`Impossible de lire le répertoire ${dirPath}:`, error.message);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
async loadComponent(compDir, category) {
|
|
333
|
-
const compName = path.basename(compDir);
|
|
334
|
-
const tagName = `sonic-${compName}`;
|
|
335
|
-
|
|
336
|
-
// Trouve le fichier principal
|
|
337
|
-
const mainFile = path.join(compDir, `${compName}.ts`);
|
|
338
|
-
|
|
339
|
-
try {
|
|
340
|
-
const content = await fs.readFile(mainFile, 'utf-8');
|
|
341
|
-
|
|
342
|
-
// Trouve le fichier de documentation
|
|
343
|
-
const docFile = path.join(compDir, `${compName}.md`);
|
|
344
|
-
let docContent = '';
|
|
345
|
-
try {
|
|
346
|
-
docContent = await fs.readFile(docFile, 'utf-8');
|
|
347
|
-
} catch {
|
|
348
|
-
// Pas de documentation
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// Extrait les propriétés du composant
|
|
352
|
-
const props = this.extractProperties(content);
|
|
353
|
-
|
|
354
|
-
// Extrait la description
|
|
355
|
-
const description = this.extractDescription(content);
|
|
356
|
-
|
|
357
|
-
// Extrait les exemples de la documentation
|
|
358
|
-
const examples = this.extractExamples(docContent);
|
|
359
|
-
|
|
360
|
-
const compInfo = new ComponentInfo(
|
|
361
|
-
compName,
|
|
362
|
-
tagName,
|
|
363
|
-
category,
|
|
364
|
-
mainFile,
|
|
365
|
-
docFile,
|
|
366
|
-
description,
|
|
367
|
-
props,
|
|
368
|
-
examples
|
|
369
|
-
);
|
|
370
|
-
|
|
371
|
-
this.components.set(compName, compInfo);
|
|
372
|
-
} catch (error) {
|
|
373
|
-
console.warn(`Impossible de charger le composant ${compName}:`, error.message);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
extractProperties(content) {
|
|
378
|
-
const props = [];
|
|
379
|
-
|
|
380
|
-
// Recherche les décorateurs @property
|
|
381
|
-
const propertyPattern = /@property\([^)]*\)\s+(\w+)(?::\s*([^=]+))?/g;
|
|
382
|
-
let match;
|
|
383
|
-
|
|
384
|
-
while ((match = propertyPattern.exec(content)) !== null) {
|
|
385
|
-
const propName = match[1];
|
|
386
|
-
const propType = match[2] ? match[2].trim() : 'string';
|
|
387
|
-
|
|
388
|
-
// Recherche les commentaires JSDoc pour cette propriété
|
|
389
|
-
const commentPattern = new RegExp(`/\\*\\*\\s*\\n\\s*\\*\\s*([^\\n]+)`, 'g');
|
|
390
|
-
const commentMatch = commentPattern.exec(content);
|
|
391
|
-
const description = commentMatch ? commentMatch[1].trim() : '';
|
|
392
|
-
|
|
393
|
-
props.push({
|
|
394
|
-
name: propName,
|
|
395
|
-
type: propType,
|
|
396
|
-
description: description
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
return props;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
extractDescription(content) {
|
|
404
|
-
// Recherche les commentaires de classe
|
|
405
|
-
const classPattern = /\/\*\*\s*\n\s*\*\s*([^\n]+)/;
|
|
406
|
-
const match = classPattern.exec(content);
|
|
407
|
-
if (match) {
|
|
408
|
-
return match[1].trim();
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// Recherche le nom de la classe
|
|
412
|
-
const classNamePattern = /export class (\w+)/;
|
|
413
|
-
const classNameMatch = classNamePattern.exec(content);
|
|
414
|
-
if (classNameMatch) {
|
|
415
|
-
return `Composant ${classNameMatch[1]}`;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
return 'Composant Concorde';
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
extractExamples(docContent) {
|
|
422
|
-
const examples = [];
|
|
423
|
-
|
|
424
|
-
// Recherche les blocs sonic-code
|
|
425
|
-
const codePattern = /<sonic-code>(.*?)<\/sonic-code>/gs;
|
|
426
|
-
let match;
|
|
427
|
-
|
|
428
|
-
while ((match = codePattern.exec(docContent)) !== null) {
|
|
429
|
-
const example = match[1].trim();
|
|
430
|
-
if (example) {
|
|
431
|
-
examples.push(example);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
return examples;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
async loadDocumentation() {
|
|
439
|
-
// Documentation des concepts de base
|
|
440
|
-
this.documentation.set('architecture', `
|
|
441
|
-
# Architecture Concorde
|
|
442
|
-
|
|
443
|
-
Concorde est un framework de composants Web basé sur Lit Element avec les concepts suivants :
|
|
444
|
-
|
|
445
|
-
## Mixins principaux
|
|
446
|
-
|
|
447
|
-
### Subscriber
|
|
448
|
-
- Permet de lier un composant à un publisher
|
|
449
|
-
- Gestion automatique des données réactives
|
|
450
|
-
- Support du data binding
|
|
451
|
-
|
|
452
|
-
### FormElement
|
|
453
|
-
- Gestion des formulaires
|
|
454
|
-
- Validation automatique
|
|
455
|
-
- Liaison avec les publishers de formulaire
|
|
456
|
-
|
|
457
|
-
### Fetcher
|
|
458
|
-
- Gestion des appels API
|
|
459
|
-
- Chargement automatique des données
|
|
460
|
-
- Support des filtres et pagination
|
|
461
|
-
|
|
462
|
-
## Système de thèmes
|
|
463
|
-
- Thèmes light/dark/auto
|
|
464
|
-
- Variables CSS personnalisables
|
|
465
|
-
- Support des couleurs sémantiques
|
|
466
|
-
|
|
467
|
-
## Composants disponibles
|
|
468
|
-
- Composants UI : boutons, inputs, modales, etc.
|
|
469
|
-
- Composants fonctionnels : fetch, list, router, etc.
|
|
470
|
-
- Système d'icônes intégré
|
|
471
|
-
`);
|
|
472
|
-
|
|
473
|
-
this.documentation.set('data-binding', `
|
|
474
|
-
# Data Binding Concorde
|
|
475
|
-
|
|
476
|
-
## Syntaxe
|
|
477
|
-
- \`data-bind ::attribute="expression"\` : Liaison d'attribut
|
|
478
|
-
- \`data-bind ::inner-html="expression"\` : Liaison de contenu HTML
|
|
479
|
-
- \`data-bind ::text-content="expression"\` : Liaison de contenu texte
|
|
480
|
-
|
|
481
|
-
## Expressions
|
|
482
|
-
- \`$property\` : Accès à une propriété du publisher
|
|
483
|
-
- \`$property.subproperty\` : Accès à une sous-propriété
|
|
484
|
-
- \`function|$property\` : Application d'une fonction
|
|
485
|
-
|
|
486
|
-
## Exemples
|
|
487
|
-
\`\`\`html
|
|
488
|
-
<sonic-input data-bind ::value="$email"></sonic-input>
|
|
489
|
-
<span data-bind ::inner-html="$name"></span>
|
|
490
|
-
<div data-bind ::class="active|$isActive"></div>
|
|
491
|
-
\`\`\`
|
|
492
|
-
`);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
async searchComponent(query) {
|
|
496
|
-
const results = [];
|
|
497
|
-
const queryLower = query.toLowerCase();
|
|
498
|
-
|
|
499
|
-
for (const [compName, compInfo] of this.components) {
|
|
500
|
-
if (compName.toLowerCase().includes(queryLower) ||
|
|
501
|
-
compInfo.description.toLowerCase().includes(queryLower) ||
|
|
502
|
-
compInfo.tagName.toLowerCase().includes(queryLower)) {
|
|
503
|
-
results.push(`- **${compInfo.tagName}** (${compInfo.category}): ${compInfo.description}`);
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
const content = results.length > 0
|
|
508
|
-
? `Composants trouvés pour '${query}':\n\n${results.join('\n')}`
|
|
509
|
-
: `Aucun composant trouvé pour '${query}'`;
|
|
510
|
-
|
|
511
|
-
return {
|
|
512
|
-
content: [{
|
|
513
|
-
type: 'text',
|
|
514
|
-
text: content
|
|
515
|
-
}]
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
async getComponentInfo(componentName) {
|
|
520
|
-
if (!this.components.has(componentName)) {
|
|
521
|
-
return {
|
|
522
|
-
content: [{
|
|
523
|
-
type: 'text',
|
|
524
|
-
text: `Composant '${componentName}' non trouvé`
|
|
525
|
-
}]
|
|
526
|
-
};
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
const compInfo = this.components.get(componentName);
|
|
530
|
-
const content = this.generateComponentDoc(compInfo);
|
|
531
|
-
|
|
532
|
-
return {
|
|
533
|
-
content: [{
|
|
534
|
-
type: 'text',
|
|
535
|
-
text: content
|
|
536
|
-
}]
|
|
537
|
-
};
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
async generateComponentCode(componentName, properties, content = '') {
|
|
541
|
-
if (!this.components.has(componentName)) {
|
|
542
|
-
return {
|
|
543
|
-
content: [{
|
|
544
|
-
type: 'text',
|
|
545
|
-
text: `Composant '${componentName}' non trouvé`
|
|
546
|
-
}]
|
|
547
|
-
};
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
const compInfo = this.components.get(componentName);
|
|
551
|
-
const tagName = compInfo.tagName;
|
|
552
|
-
|
|
553
|
-
// Génère les attributs
|
|
554
|
-
const attributes = [];
|
|
555
|
-
for (const [key, value] of Object.entries(properties)) {
|
|
556
|
-
if (typeof value === 'boolean') {
|
|
557
|
-
if (value) {
|
|
558
|
-
attributes.push(key);
|
|
559
|
-
}
|
|
560
|
-
} else {
|
|
561
|
-
attributes.push(`${key}="${value}"`);
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
// Génère le code HTML
|
|
566
|
-
const htmlCode = content
|
|
567
|
-
? `<${tagName} ${attributes.join(' ')}>${content}</${tagName}>`
|
|
568
|
-
: `<${tagName} ${attributes.join(' ')}></${tagName}>`;
|
|
569
|
-
|
|
570
|
-
return {
|
|
571
|
-
content: [{
|
|
572
|
-
type: 'text',
|
|
573
|
-
text: htmlCode
|
|
574
|
-
}]
|
|
575
|
-
};
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
async getFormExample(fields) {
|
|
579
|
-
if (fields.length === 0) {
|
|
580
|
-
// Exemple par défaut
|
|
581
|
-
fields = [
|
|
582
|
-
{ name: 'email', type: 'email', label: 'Email', required: true },
|
|
583
|
-
{ name: 'password', type: 'password', label: 'Mot de passe', required: true },
|
|
584
|
-
{ name: 'remember', type: 'checkbox', label: 'Se souvenir de moi', required: false }
|
|
585
|
-
];
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
let formHtml = '<div formDataProvider="exampleForm">\n';
|
|
589
|
-
|
|
590
|
-
for (const field of fields) {
|
|
591
|
-
const { name, type, label, required } = field;
|
|
592
|
-
|
|
593
|
-
if (type === 'checkbox') {
|
|
594
|
-
formHtml += ` <sonic-checkbox name="${name}"`;
|
|
595
|
-
if (required) {
|
|
596
|
-
formHtml += ' required';
|
|
597
|
-
}
|
|
598
|
-
formHtml += `>${label}</sonic-checkbox>\n`;
|
|
599
|
-
} else {
|
|
600
|
-
formHtml += ` <sonic-input name="${name}" type="${type}" label="${label}"`;
|
|
601
|
-
if (required) {
|
|
602
|
-
formHtml += ' required';
|
|
603
|
-
}
|
|
604
|
-
formHtml += '></sonic-input>\n';
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
formHtml += ' <sonic-form-actions>\n';
|
|
609
|
-
formHtml += ' <sonic-button type="primary">Valider</sonic-button>\n';
|
|
610
|
-
formHtml += ' <sonic-button variant="outline">Annuler</sonic-button>\n';
|
|
611
|
-
formHtml += ' </sonic-form-actions>\n';
|
|
612
|
-
formHtml += '</div>';
|
|
613
|
-
|
|
614
|
-
return {
|
|
615
|
-
content: [{
|
|
616
|
-
type: 'text',
|
|
617
|
-
text: formHtml
|
|
618
|
-
}]
|
|
619
|
-
};
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
async getThemeInfo(theme) {
|
|
623
|
-
const themeInfo = {
|
|
624
|
-
light: {
|
|
625
|
-
description: 'Thème clair par défaut',
|
|
626
|
-
colors: {
|
|
627
|
-
primary: '#1e293b',
|
|
628
|
-
info: '#2563eb',
|
|
629
|
-
danger: '#f43f5e',
|
|
630
|
-
warning: '#f97316',
|
|
631
|
-
success: '#14b8a6'
|
|
632
|
-
}
|
|
633
|
-
},
|
|
634
|
-
dark: {
|
|
635
|
-
description: 'Thème sombre',
|
|
636
|
-
colors: {
|
|
637
|
-
primary: '#334155',
|
|
638
|
-
info: '#3abff8',
|
|
639
|
-
danger: '#f87272',
|
|
640
|
-
warning: '#fbbd23',
|
|
641
|
-
success: '#36d399'
|
|
642
|
-
}
|
|
643
|
-
},
|
|
644
|
-
auto: {
|
|
645
|
-
description: 'Thème automatique basé sur les préférences système',
|
|
646
|
-
colors: 'Utilise les couleurs du thème sombre si l\'utilisateur préfère le mode sombre'
|
|
647
|
-
}
|
|
648
|
-
};
|
|
649
|
-
|
|
650
|
-
const info = themeInfo[theme] || themeInfo.light;
|
|
651
|
-
|
|
652
|
-
let content = `# Thème ${theme}\n\n${info.description}\n\n## Utilisation\n\n\`\`\`html\n<sonic-theme theme="${theme}">\n <!-- Vos composants ici -->\n</sonic-theme>\n\`\`\`\n\n`;
|
|
653
|
-
|
|
654
|
-
if (info.colors && typeof info.colors === 'object') {
|
|
655
|
-
content += '## Couleurs\n\n';
|
|
656
|
-
for (const [colorName, colorValue] of Object.entries(info.colors)) {
|
|
657
|
-
content += `- **${colorName}**: \`${colorValue}\`\n`;
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
return {
|
|
662
|
-
content: [{
|
|
663
|
-
type: 'text',
|
|
664
|
-
text: content
|
|
665
|
-
}]
|
|
666
|
-
};
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
async getIconsList(library) {
|
|
670
|
-
const iconsInfo = {
|
|
671
|
-
core: [
|
|
672
|
-
'cancel', 'check-circled-outline', 'check', 'emoji-puzzled',
|
|
673
|
-
'info-empty', 'loader', 'minus-small', 'more-horiz', 'more-vert',
|
|
674
|
-
'nav-arrow-down', 'warning-circled-outline'
|
|
675
|
-
],
|
|
676
|
-
iconoir: 'Bibliothèque d\'icônes externe avec de nombreuses icônes',
|
|
677
|
-
fontAwesome: 'Bibliothèque FontAwesome avec icônes solid et regular'
|
|
678
|
-
};
|
|
679
|
-
|
|
680
|
-
let content;
|
|
681
|
-
|
|
682
|
-
if (library === 'core') {
|
|
683
|
-
const icons = iconsInfo.core;
|
|
684
|
-
content = `# Icônes Core Concorde\n\n## Utilisation\n\n\`\`\`html\n<sonic-icon library="core" name="nom-de-l-icone"></sonic-icon>\n\`\`\`\n\n## Icônes disponibles\n\n`;
|
|
685
|
-
for (const icon of icons) {
|
|
686
|
-
content += `- **${icon}**: \`<sonic-icon library="core" name="${icon}"></sonic-icon>\`\n`;
|
|
687
|
-
}
|
|
688
|
-
} else {
|
|
689
|
-
content = `# Bibliothèque d'icônes ${library}\n\n${iconsInfo[library]}\n\n## Utilisation\n\n\`\`\`html\n<sonic-icon library="${library}" name="nom-de-l-icone"></sonic-icon>\n\`\`\`\n\nPour les icônes FontAwesome, vous pouvez spécifier le préfixe :\n\`\`\`html\n<sonic-icon library="fontAwesome" prefix="solid" name="home"></sonic-icon>\n<sonic-icon library="fontAwesome" prefix="regular" name="user"></sonic-icon>\n\`\`\`\n`;
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
return {
|
|
693
|
-
content: [{
|
|
694
|
-
type: 'text',
|
|
695
|
-
text: content
|
|
696
|
-
}]
|
|
697
|
-
};
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
generateComponentDoc(compInfo) {
|
|
701
|
-
let content = `# ${compInfo.tagName}\n\n**Catégorie:** ${compInfo.category}\n**Description:** ${compInfo.description}\n\n## Propriétés\n\n`;
|
|
702
|
-
|
|
703
|
-
for (const prop of compInfo.props) {
|
|
704
|
-
content += `- **${prop.name}** (${prop.type}): ${prop.description}\n`;
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
if (compInfo.examples.length > 0) {
|
|
708
|
-
content += '\n## Exemples\n\n';
|
|
709
|
-
for (let i = 0; i < Math.min(compInfo.examples.length, 3); i++) {
|
|
710
|
-
content += `### Exemple ${i + 1}\n\n\`\`\`html\n${compInfo.examples[i]}\n\`\`\`\n\n`;
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
return content;
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
generateUIExamples() {
|
|
718
|
-
return `# Exemples de composants UI Concorde
|
|
719
|
-
|
|
720
|
-
## Bouton avec icône
|
|
721
|
-
|
|
722
|
-
\`\`\`html
|
|
723
|
-
<sonic-button type="primary">
|
|
724
|
-
<sonic-icon library="core" name="check" slot="prefix"></sonic-icon>
|
|
725
|
-
Valider
|
|
726
|
-
</sonic-button>
|
|
727
|
-
\`\`\`
|
|
728
|
-
|
|
729
|
-
## Formulaire complet
|
|
730
|
-
|
|
731
|
-
\`\`\`html
|
|
732
|
-
<div formDataProvider="userForm">
|
|
733
|
-
<sonic-fieldset label="Informations utilisateur">
|
|
734
|
-
<sonic-form-layout>
|
|
735
|
-
<sonic-input name="firstName" label="Prénom" required></sonic-input>
|
|
736
|
-
<sonic-input name="lastName" label="Nom" required></sonic-input>
|
|
737
|
-
<sonic-input name="email" type="email" label="Email" required></sonic-input>
|
|
738
|
-
</sonic-form-layout>
|
|
739
|
-
</sonic-fieldset>
|
|
740
|
-
<sonic-form-actions>
|
|
741
|
-
<sonic-button type="primary">Enregistrer</sonic-button>
|
|
742
|
-
<sonic-button variant="outline">Annuler</sonic-button>
|
|
743
|
-
</sonic-form-actions>
|
|
744
|
-
</div>
|
|
745
|
-
\`\`\`
|
|
746
|
-
|
|
747
|
-
## Modal avec contenu
|
|
748
|
-
|
|
749
|
-
\`\`\`html
|
|
750
|
-
<sonic-modal id="exampleModal">
|
|
751
|
-
<sonic-modal-title>Confirmation</sonic-modal-title>
|
|
752
|
-
<sonic-modal-content>
|
|
753
|
-
Êtes-vous sûr de vouloir continuer ?
|
|
754
|
-
</sonic-modal-content>
|
|
755
|
-
<sonic-modal-actions>
|
|
756
|
-
<sonic-button hideModal>Annuler</sonic-button>
|
|
757
|
-
<sonic-button type="primary" hideModal>Confirmer</sonic-button>
|
|
758
|
-
</sonic-modal-actions>
|
|
759
|
-
</sonic-modal>
|
|
760
|
-
\`\`\`
|
|
761
|
-
`;
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
generateFunctionalExamples() {
|
|
765
|
-
return `# Exemples de composants fonctionnels Concorde
|
|
766
|
-
|
|
767
|
-
## Chargement de données avec Fetch
|
|
768
|
-
|
|
769
|
-
\`\`\`html
|
|
770
|
-
<sonic-fetch
|
|
771
|
-
serviceURL="https://api.example.com"
|
|
772
|
-
endPoint="users"
|
|
773
|
-
dataProvider="usersData">
|
|
774
|
-
</sonic-fetch>
|
|
775
|
-
|
|
776
|
-
<sonic-list dataProvider="usersData" props='$usersData'>
|
|
777
|
-
<template>
|
|
778
|
-
<div class="user-card">
|
|
779
|
-
<h3 data-bind ::inner-html="$name"></h3>
|
|
780
|
-
<p data-bind ::inner-html="$email"></p>
|
|
781
|
-
</div>
|
|
782
|
-
</template>
|
|
783
|
-
</sonic-list>
|
|
784
|
-
\`\`\`
|
|
785
|
-
|
|
786
|
-
## Navigation avec Router
|
|
787
|
-
|
|
788
|
-
\`\`\`html
|
|
789
|
-
<sonic-router>
|
|
790
|
-
<template data-route="#home">
|
|
791
|
-
<h1>Page d'accueil</h1>
|
|
792
|
-
</template>
|
|
793
|
-
<template data-route="#about">
|
|
794
|
-
<h1>À propos</h1>
|
|
795
|
-
</template>
|
|
796
|
-
<template data-route="#contact">
|
|
797
|
-
<h1>Contact</h1>
|
|
798
|
-
</template>
|
|
799
|
-
</sonic-router>
|
|
800
|
-
|
|
801
|
-
<nav>
|
|
802
|
-
<sonic-button href="#home">Accueil</sonic-button>
|
|
803
|
-
<sonic-button href="#about">À propos</sonic-button>
|
|
804
|
-
<sonic-button href="#contact">Contact</sonic-button>
|
|
805
|
-
</nav>
|
|
806
|
-
\`\`\`
|
|
807
|
-
|
|
808
|
-
## États conditionnels
|
|
809
|
-
|
|
810
|
-
\`\`\`html
|
|
811
|
-
<sonic-states dataProvider="userData" data-path="status">
|
|
812
|
-
<template data-value="loading">
|
|
813
|
-
<sonic-loader></sonic-loader>
|
|
814
|
-
</template>
|
|
815
|
-
<template data-value="success">
|
|
816
|
-
<div class="success-message">Données chargées avec succès</div>
|
|
817
|
-
</template>
|
|
818
|
-
<template data-value="error">
|
|
819
|
-
<sonic-alert type="danger">Erreur lors du chargement</sonic-alert>
|
|
820
|
-
</template>
|
|
821
|
-
</sonic-states>
|
|
822
|
-
\`\`\`
|
|
823
|
-
`;
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
async run() {
|
|
827
|
-
await this.initialize();
|
|
828
|
-
|
|
829
|
-
const transport = new StdioServerTransport();
|
|
830
|
-
await this.server.connect(transport);
|
|
831
|
-
|
|
832
|
-
console.error('Serveur MCP Concorde démarré');
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
// Point d'entrée principal
|
|
837
|
-
async function main() {
|
|
838
|
-
const server = new ConcordeMCPServer();
|
|
839
|
-
await server.run();
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
// Gestion des erreurs non capturées
|
|
843
|
-
process.on('uncaughtException', (error) => {
|
|
844
|
-
console.error('Erreur non capturée:', error);
|
|
845
|
-
process.exit(1);
|
|
846
|
-
});
|
|
847
|
-
|
|
848
|
-
process.on('unhandledRejection', (reason, promise) => {
|
|
849
|
-
console.error('Promesse rejetée non gérée:', reason);
|
|
850
|
-
process.exit(1);
|
|
851
|
-
});
|
|
852
|
-
|
|
853
|
-
// Point d'entrée principal
|
|
854
|
-
main().catch((error) => {
|
|
855
|
-
console.error('Erreur lors du démarrage du serveur:', error);
|
|
856
|
-
process.exit(1);
|
|
857
|
-
});
|
|
858
|
-
|
|
859
|
-
export { ConcordeMCPServer };
|