@dgplsoares/singulai-ds-mcp 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Diogo Soares
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # @dgplsoares/singulai-ds-mcp
2
+
3
+ > MCP server exposing the [Singulai Design System](https://design.singulai.ai) (Angular 20) to AI agents — Claude Desktop, Claude Code, Cursor, Continue, and any other [MCP](https://modelcontextprotocol.io/)-compatible client.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@dgplsoares/singulai-ds-mcp.svg)](https://www.npmjs.com/package/@dgplsoares/singulai-ds-mcp)
6
+ [![Live Showcase](https://img.shields.io/badge/showcase-design.singulai.ai-2563EB)](https://design.singulai.ai)
7
+ [![License](https://img.shields.io/badge/license-MIT-22C55E)](./LICENSE)
8
+
9
+ 🚧 **Alpha (v0.1.x)** — funcional, não recomendado para produção alheia ainda. API das tools e shape do catálogo estabilizam em v1.0.
10
+
11
+ ## O que é?
12
+
13
+ Um [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server que expõe o catálogo do **Singulai Design System** (15 componentes Angular 20 standalone + signals + neumorphic) como tools que qualquer AI agent consegue chamar. Casos de uso típicos:
14
+
15
+ - _"Quais componentes existem nesse design system?"_ → tool retorna catálogo
16
+ - _"Componente para botão com loading state?"_ → tool retorna `<ds-button>` + exemplo
17
+ - _"Quais @Input existem em `<ds-form-field>`?"_ → tool retorna props tipados
18
+
19
+ O agente AI usa as tools automaticamente quando o usuário pede algo relacionado ao DS — sem precisar ler docs.
20
+
21
+ ## Tools v0.1
22
+
23
+ | Tool | Função |
24
+ |---|---|
25
+ | **`list_components()`** | Lista os 15 componentes (nome + selector + descrição curta + tags) |
26
+ | **`get_component(name)`** | Info completa: descrição, props tipados (com defaults), outputs, exemplos. Aceita selector (`ds-button`) ou nome de classe (`ButtonComponent`) |
27
+ | **`search_components(query, limit?)`** | Busca por palavras-chave em selector + nome + tags + descrição, com scoring |
28
+
29
+ ## Instalação
30
+
31
+ ### Opção 1 — Claude Code (CLI)
32
+
33
+ Adicione ao seu `~/.claude/settings.json`:
34
+
35
+ ```json
36
+ {
37
+ "mcpServers": {
38
+ "singulai-ds": {
39
+ "command": "npx",
40
+ "args": ["-y", "@dgplsoares/singulai-ds-mcp"]
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ Reinicie o Claude Code. Pergunte: _"quais componentes existem no Singulai Design System?"_ — o agente vai chamar `list_components` automaticamente.
47
+
48
+ ### Opção 2 — Claude Desktop
49
+
50
+ Edite o arquivo de config (location varia por SO):
51
+
52
+ - **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
53
+ - **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
54
+ - **Linux:** `~/.config/Claude/claude_desktop_config.json`
55
+
56
+ ```json
57
+ {
58
+ "mcpServers": {
59
+ "singulai-ds": {
60
+ "command": "npx",
61
+ "args": ["-y", "@dgplsoares/singulai-ds-mcp"]
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ Reinicie o Claude Desktop.
68
+
69
+ ### Opção 3 — Cursor
70
+
71
+ Em Settings → Features → Model Context Protocol:
72
+
73
+ ```json
74
+ {
75
+ "mcpServers": {
76
+ "singulai-ds": {
77
+ "command": "npx",
78
+ "args": ["-y", "@dgplsoares/singulai-ds-mcp"]
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ ### Opção 4 — Outros clientes MCP
85
+
86
+ Qualquer cliente compatível com [MCP stdio transport](https://modelcontextprotocol.io/docs/concepts/transports#standard-input-output-stdio) pode rodar este server. Comando: `npx -y @dgplsoares/singulai-ds-mcp`.
87
+
88
+ ## Verificação rápida
89
+
90
+ Após adicionar a config e reiniciar o cliente, peça ao agente:
91
+
92
+ ```
93
+ Quais componentes existem no Singulai Design System?
94
+ ```
95
+
96
+ A resposta deve listar os 15 componentes (button, card, form-field, sidebar-left-nav, page-layout, page-header, nav-footer, ai-assistant-panel, statsbar-card, segmented-tabs, icon-neumorphic, card-panel, ai-assistant-button, dark-mode-button, notifications-button).
97
+
98
+ Outras perguntas para testar:
99
+
100
+ - _"Como uso o `<ds-button>`?"_ — chama `get_component`
101
+ - _"Componente para chat de AI?"_ — chama `search_components`
102
+ - _"Quais props o FormFieldComponent aceita?"_ — chama `get_component`
103
+
104
+ ## Status
105
+
106
+ | Fase | Tools |
107
+ |---|---|
108
+ | ✅ MCP-1/2/3 (atual) | `list_components`, `get_component`, `search_components` |
109
+ | ✅ MCP-4 | npm publish + docs de instalação |
110
+ | 🟡 MCP-5 | validação em Claude Code |
111
+ | 🔜 v0.2+ | `get_tokens`, `get_examples`, `get_theme_overrides`, catálogo auto-gerado via ts-morph |
112
+
113
+ ## Stack
114
+
115
+ - TypeScript + Node 20+
116
+ - [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) (oficial Anthropic)
117
+ - [zod](https://zod.dev) para validação de schema
118
+ - Stdio transport (padrão MCP)
119
+
120
+ ## Desenvolvimento
121
+
122
+ ```bash
123
+ git clone git@github.com:dgplsoares/singulai-design-system-mcp.git
124
+ cd singulai-design-system-mcp
125
+ npm install
126
+ npm run dev # tsx src/index.ts
127
+ npm run build # tsc + copia src/data → dist/data
128
+ npm start # node dist/index.js
129
+ ```
130
+
131
+ Smoke test stdio (cole inteiro em um terminal):
132
+
133
+ ```bash
134
+ echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"smoke","version":"1.0"}}}
135
+ {"jsonrpc":"2.0","method":"notifications/initialized"}
136
+ {"jsonrpc":"2.0","id":2,"method":"tools/list"}' | node dist/index.js
137
+ ```
138
+
139
+ ## License
140
+
141
+ [MIT](./LICENSE) — Diogo Soares · 2026
142
+
143
+ ## Links
144
+
145
+ - 🌐 Live showcase: https://design.singulai.ai
146
+ - 📦 Design System (mirror público): https://github.com/dgplsoares/singulai-design-system
147
+ - 📦 NPM: https://www.npmjs.com/package/@dgplsoares/singulai-ds-mcp
148
+ - 👤 Autor: [LinkedIn](https://www.linkedin.com/in/dgsoares/) · [GitHub](https://github.com/dgplsoares)
@@ -0,0 +1,272 @@
1
+ /**
2
+ * Carrega e valida o catalog.json em memoria.
3
+ * Schema zod garante consistencia entre v0.1 (manual) e v0.2 (auto-gerado
4
+ * via ts-morph) — se a forma mudar incompativelmente, o servidor falha
5
+ * imediatamente em vez de retornar dados malformados aos AI agents.
6
+ */
7
+ import { z } from 'zod';
8
+ declare const PropSchema: z.ZodObject<{
9
+ name: z.ZodString;
10
+ type: z.ZodString;
11
+ required: z.ZodOptional<z.ZodBoolean>;
12
+ default: z.ZodOptional<z.ZodString>;
13
+ description: z.ZodString;
14
+ }, "strip", z.ZodTypeAny, {
15
+ name: string;
16
+ type: string;
17
+ description: string;
18
+ required?: boolean | undefined;
19
+ default?: string | undefined;
20
+ }, {
21
+ name: string;
22
+ type: string;
23
+ description: string;
24
+ required?: boolean | undefined;
25
+ default?: string | undefined;
26
+ }>;
27
+ declare const ComponentSchema: z.ZodObject<{
28
+ name: z.ZodString;
29
+ selector: z.ZodString;
30
+ description: z.ZodString;
31
+ tags: z.ZodArray<z.ZodString, "many">;
32
+ props: z.ZodArray<z.ZodObject<{
33
+ name: z.ZodString;
34
+ type: z.ZodString;
35
+ required: z.ZodOptional<z.ZodBoolean>;
36
+ default: z.ZodOptional<z.ZodString>;
37
+ description: z.ZodString;
38
+ }, "strip", z.ZodTypeAny, {
39
+ name: string;
40
+ type: string;
41
+ description: string;
42
+ required?: boolean | undefined;
43
+ default?: string | undefined;
44
+ }, {
45
+ name: string;
46
+ type: string;
47
+ description: string;
48
+ required?: boolean | undefined;
49
+ default?: string | undefined;
50
+ }>, "many">;
51
+ outputs: z.ZodOptional<z.ZodArray<z.ZodObject<{
52
+ name: z.ZodString;
53
+ type: z.ZodString;
54
+ }, "strip", z.ZodTypeAny, {
55
+ name: string;
56
+ type: string;
57
+ }, {
58
+ name: string;
59
+ type: string;
60
+ }>, "many">>;
61
+ examples: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
62
+ }, "strip", z.ZodTypeAny, {
63
+ name: string;
64
+ description: string;
65
+ selector: string;
66
+ tags: string[];
67
+ props: {
68
+ name: string;
69
+ type: string;
70
+ description: string;
71
+ required?: boolean | undefined;
72
+ default?: string | undefined;
73
+ }[];
74
+ examples: string[];
75
+ outputs?: {
76
+ name: string;
77
+ type: string;
78
+ }[] | undefined;
79
+ }, {
80
+ name: string;
81
+ description: string;
82
+ selector: string;
83
+ tags: string[];
84
+ props: {
85
+ name: string;
86
+ type: string;
87
+ description: string;
88
+ required?: boolean | undefined;
89
+ default?: string | undefined;
90
+ }[];
91
+ outputs?: {
92
+ name: string;
93
+ type: string;
94
+ }[] | undefined;
95
+ examples?: string[] | undefined;
96
+ }>;
97
+ declare const CatalogSchema: z.ZodObject<{
98
+ version: z.ZodString;
99
+ generated_at: z.ZodString;
100
+ design_system: z.ZodObject<{
101
+ name: z.ZodString;
102
+ version: z.ZodString;
103
+ homepage: z.ZodString;
104
+ repo: z.ZodString;
105
+ stack: z.ZodArray<z.ZodString, "many">;
106
+ principles: z.ZodArray<z.ZodString, "many">;
107
+ }, "strip", z.ZodTypeAny, {
108
+ name: string;
109
+ version: string;
110
+ homepage: string;
111
+ repo: string;
112
+ stack: string[];
113
+ principles: string[];
114
+ }, {
115
+ name: string;
116
+ version: string;
117
+ homepage: string;
118
+ repo: string;
119
+ stack: string[];
120
+ principles: string[];
121
+ }>;
122
+ types: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>;
123
+ components: z.ZodArray<z.ZodObject<{
124
+ name: z.ZodString;
125
+ selector: z.ZodString;
126
+ description: z.ZodString;
127
+ tags: z.ZodArray<z.ZodString, "many">;
128
+ props: z.ZodArray<z.ZodObject<{
129
+ name: z.ZodString;
130
+ type: z.ZodString;
131
+ required: z.ZodOptional<z.ZodBoolean>;
132
+ default: z.ZodOptional<z.ZodString>;
133
+ description: z.ZodString;
134
+ }, "strip", z.ZodTypeAny, {
135
+ name: string;
136
+ type: string;
137
+ description: string;
138
+ required?: boolean | undefined;
139
+ default?: string | undefined;
140
+ }, {
141
+ name: string;
142
+ type: string;
143
+ description: string;
144
+ required?: boolean | undefined;
145
+ default?: string | undefined;
146
+ }>, "many">;
147
+ outputs: z.ZodOptional<z.ZodArray<z.ZodObject<{
148
+ name: z.ZodString;
149
+ type: z.ZodString;
150
+ }, "strip", z.ZodTypeAny, {
151
+ name: string;
152
+ type: string;
153
+ }, {
154
+ name: string;
155
+ type: string;
156
+ }>, "many">>;
157
+ examples: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
158
+ }, "strip", z.ZodTypeAny, {
159
+ name: string;
160
+ description: string;
161
+ selector: string;
162
+ tags: string[];
163
+ props: {
164
+ name: string;
165
+ type: string;
166
+ description: string;
167
+ required?: boolean | undefined;
168
+ default?: string | undefined;
169
+ }[];
170
+ examples: string[];
171
+ outputs?: {
172
+ name: string;
173
+ type: string;
174
+ }[] | undefined;
175
+ }, {
176
+ name: string;
177
+ description: string;
178
+ selector: string;
179
+ tags: string[];
180
+ props: {
181
+ name: string;
182
+ type: string;
183
+ description: string;
184
+ required?: boolean | undefined;
185
+ default?: string | undefined;
186
+ }[];
187
+ outputs?: {
188
+ name: string;
189
+ type: string;
190
+ }[] | undefined;
191
+ examples?: string[] | undefined;
192
+ }>, "many">;
193
+ }, "strip", z.ZodTypeAny, {
194
+ version: string;
195
+ generated_at: string;
196
+ design_system: {
197
+ name: string;
198
+ version: string;
199
+ homepage: string;
200
+ repo: string;
201
+ stack: string[];
202
+ principles: string[];
203
+ };
204
+ types: Record<string, string[]>;
205
+ components: {
206
+ name: string;
207
+ description: string;
208
+ selector: string;
209
+ tags: string[];
210
+ props: {
211
+ name: string;
212
+ type: string;
213
+ description: string;
214
+ required?: boolean | undefined;
215
+ default?: string | undefined;
216
+ }[];
217
+ examples: string[];
218
+ outputs?: {
219
+ name: string;
220
+ type: string;
221
+ }[] | undefined;
222
+ }[];
223
+ }, {
224
+ version: string;
225
+ generated_at: string;
226
+ design_system: {
227
+ name: string;
228
+ version: string;
229
+ homepage: string;
230
+ repo: string;
231
+ stack: string[];
232
+ principles: string[];
233
+ };
234
+ types: Record<string, string[]>;
235
+ components: {
236
+ name: string;
237
+ description: string;
238
+ selector: string;
239
+ tags: string[];
240
+ props: {
241
+ name: string;
242
+ type: string;
243
+ description: string;
244
+ required?: boolean | undefined;
245
+ default?: string | undefined;
246
+ }[];
247
+ outputs?: {
248
+ name: string;
249
+ type: string;
250
+ }[] | undefined;
251
+ examples?: string[] | undefined;
252
+ }[];
253
+ }>;
254
+ export type Catalog = z.infer<typeof CatalogSchema>;
255
+ export type CatalogComponent = z.infer<typeof ComponentSchema>;
256
+ export type CatalogProp = z.infer<typeof PropSchema>;
257
+ export declare function loadCatalog(): Catalog;
258
+ /**
259
+ * Encontra um componente por name (ButtonComponent) ou selector (ds-button).
260
+ * Case-insensitive. Retorna null se nao encontrar.
261
+ */
262
+ export declare function findComponent(catalog: Catalog, query: string): CatalogComponent | null;
263
+ /**
264
+ * Busca componentes por palavra-chave em name + selector + description + tags.
265
+ * Score simples: match em selector/name pesa mais que em description/tags.
266
+ * Retorna ate `limit` resultados ordenados por score desc.
267
+ */
268
+ export declare function searchComponents(catalog: Catalog, query: string, limit?: number): Array<{
269
+ component: CatalogComponent;
270
+ score: number;
271
+ }>;
272
+ export {};
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Carrega e valida o catalog.json em memoria.
3
+ * Schema zod garante consistencia entre v0.1 (manual) e v0.2 (auto-gerado
4
+ * via ts-morph) — se a forma mudar incompativelmente, o servidor falha
5
+ * imediatamente em vez de retornar dados malformados aos AI agents.
6
+ */
7
+ import { readFileSync } from 'node:fs';
8
+ import { fileURLToPath } from 'node:url';
9
+ import { dirname, resolve } from 'node:path';
10
+ import { z } from 'zod';
11
+ // ---------------------------------------------------------------------------
12
+ // Schema
13
+ // ---------------------------------------------------------------------------
14
+ const PropSchema = z.object({
15
+ name: z.string(),
16
+ type: z.string(),
17
+ required: z.boolean().optional(),
18
+ default: z.string().optional(),
19
+ description: z.string(),
20
+ });
21
+ const OutputSchema = z.object({
22
+ name: z.string(),
23
+ type: z.string(),
24
+ });
25
+ const ComponentSchema = z.object({
26
+ name: z.string(),
27
+ selector: z.string(),
28
+ description: z.string(),
29
+ tags: z.array(z.string()),
30
+ props: z.array(PropSchema),
31
+ outputs: z.array(OutputSchema).optional(),
32
+ examples: z.array(z.string()).default([]),
33
+ });
34
+ const CatalogSchema = z.object({
35
+ version: z.string(),
36
+ generated_at: z.string(),
37
+ design_system: z.object({
38
+ name: z.string(),
39
+ version: z.string(),
40
+ homepage: z.string(),
41
+ repo: z.string(),
42
+ stack: z.array(z.string()),
43
+ principles: z.array(z.string()),
44
+ }),
45
+ types: z.record(z.string(), z.array(z.string())),
46
+ components: z.array(ComponentSchema),
47
+ });
48
+ // ---------------------------------------------------------------------------
49
+ // Loader
50
+ // ---------------------------------------------------------------------------
51
+ const __filename = fileURLToPath(import.meta.url);
52
+ const __dirname = dirname(__filename);
53
+ const CATALOG_PATH = resolve(__dirname, 'data/catalog.json');
54
+ let cachedCatalog = null;
55
+ export function loadCatalog() {
56
+ if (cachedCatalog)
57
+ return cachedCatalog;
58
+ const raw = readFileSync(CATALOG_PATH, 'utf-8');
59
+ const parsed = JSON.parse(raw);
60
+ const validated = CatalogSchema.parse(parsed);
61
+ cachedCatalog = validated;
62
+ return validated;
63
+ }
64
+ // ---------------------------------------------------------------------------
65
+ // Helpers de busca (usados pelas tools)
66
+ // ---------------------------------------------------------------------------
67
+ /**
68
+ * Encontra um componente por name (ButtonComponent) ou selector (ds-button).
69
+ * Case-insensitive. Retorna null se nao encontrar.
70
+ */
71
+ export function findComponent(catalog, query) {
72
+ const q = query.trim().toLowerCase();
73
+ return (catalog.components.find((c) => c.name.toLowerCase() === q || c.selector.toLowerCase() === q) ?? null);
74
+ }
75
+ /**
76
+ * Busca componentes por palavra-chave em name + selector + description + tags.
77
+ * Score simples: match em selector/name pesa mais que em description/tags.
78
+ * Retorna ate `limit` resultados ordenados por score desc.
79
+ */
80
+ export function searchComponents(catalog, query, limit = 5) {
81
+ const terms = query
82
+ .trim()
83
+ .toLowerCase()
84
+ .split(/\s+/)
85
+ .filter((t) => t.length > 0);
86
+ if (terms.length === 0)
87
+ return [];
88
+ const scored = catalog.components
89
+ .map((component) => {
90
+ const haystack = {
91
+ selector: component.selector.toLowerCase(),
92
+ name: component.name.toLowerCase(),
93
+ description: component.description.toLowerCase(),
94
+ tags: component.tags.join(' ').toLowerCase(),
95
+ };
96
+ let score = 0;
97
+ for (const term of terms) {
98
+ if (haystack.selector.includes(term))
99
+ score += 5;
100
+ if (haystack.name.includes(term))
101
+ score += 4;
102
+ if (haystack.tags.includes(term))
103
+ score += 3;
104
+ if (haystack.description.includes(term))
105
+ score += 1;
106
+ }
107
+ return { component, score };
108
+ })
109
+ .filter((r) => r.score > 0)
110
+ .sort((a, b) => b.score - a.score)
111
+ .slice(0, limit);
112
+ return scored;
113
+ }
114
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,358 @@
1
+ {
2
+ "version": "0.1.0",
3
+ "generated_at": "2026-05-08",
4
+ "design_system": {
5
+ "name": "Singulai Design System",
6
+ "version": "DS-2.1",
7
+ "homepage": "https://design.singulai.ai",
8
+ "repo": "https://github.com/dgplsoares/singulai-design-system",
9
+ "stack": [
10
+ "Angular 20",
11
+ "Standalone Components",
12
+ "Signals",
13
+ "SCSS Tokens",
14
+ "Neumorphic"
15
+ ],
16
+ "principles": [
17
+ "Standalone components — no NgModule required",
18
+ "Signal-based inputs (input(), input.required())",
19
+ "Mobile-first with 100dvh + overscroll-behavior:none",
20
+ "Themeable via CSS variables (no Tailwind inside DS)",
21
+ "Brand assets configurable via @Input (split-ready)"
22
+ ]
23
+ },
24
+ "types": {
25
+ "ButtonVariant": ["primary-cta", "solid", "outline", "action", "icon", "submit", "sidebar", "modal-cancel"],
26
+ "ButtonSize": ["sm", "md", "lg"],
27
+ "ButtonVariantColor": ["primary", "secondary", "tertiary", "success", "danger", "warning", "dark", "goal", "apex"],
28
+ "ButtonType": ["button", "submit", "reset"],
29
+ "CardVariant": ["default", "dashboard", "with-tabs", "simple", "inner-card"],
30
+ "CardState": ["default", "loading", "empty", "error"],
31
+ "FormFieldVariant": ["text", "textarea", "select", "toggle", "search"],
32
+ "FormFieldSize": ["sm", "md", "lg"],
33
+ "FormFieldType": ["text", "email", "password", "number", "tel", "url"],
34
+ "IconNeumorphicSize": ["sm", "md", "lg"],
35
+ "IconNeumorphicVariant": ["default", "apex", "success", "warning", "danger", "info", "purple"],
36
+ "PageHeaderVariant": ["default", "module-dashboard", "screen-list", "screen-create", "screen-detail"],
37
+ "PageLayoutVariant": ["dashboard", "list", "wizard", "tabs-internas", "detail-fullwidth"],
38
+ "AiAssistantLayoutState": ["closed", "open"],
39
+ "NavFooterPanel": ["navegue", "ai", "alertas", "conta"],
40
+ "AiTab": ["current", "new", "chats"],
41
+ "SidebarState": ["expanded", "collapsed"],
42
+ "StatsbarCardVariant": ["success", "purple", "warning", "info", "danger", "neutral"],
43
+ "StatsbarCardDeltaDirection": ["up", "down"]
44
+ },
45
+ "components": [
46
+ {
47
+ "name": "ButtonComponent",
48
+ "selector": "ds-button",
49
+ "description": "Botão unificado do DS — 8 variants estruturais (solid, outline, icon, sidebar, etc.) × 9 cores semânticas. Suporta ícones left/right (heroicons ou imagens), estados loading/disabled e flag active para nav-items.",
50
+ "tags": ["button", "cta", "action", "form", "navigation", "icon", "loading"],
51
+ "props": [
52
+ { "name": "variant", "type": "ButtonVariant", "required": true, "description": "Tipo estrutural do botão." },
53
+ { "name": "size", "type": "ButtonSize", "default": "'md'", "description": "Tamanho." },
54
+ { "name": "variantColor", "type": "ButtonVariantColor", "default": "'apex'", "description": "Cor semântica." },
55
+ { "name": "type", "type": "ButtonType", "default": "'button'", "description": "Atributo HTML type." },
56
+ { "name": "iconLeft", "type": "string | null", "default": "null", "description": "Nome de ícone heroX (left)." },
57
+ { "name": "iconImageSrc", "type": "string | null", "default": "null", "description": "Path de SVG/PNG (left). Tem precedência sobre iconLeft." },
58
+ { "name": "iconImageSize", "type": "string", "default": "'20px'", "description": "Tamanho do ícone via path." },
59
+ { "name": "iconRight", "type": "string | null", "default": "null", "description": "Nome de ícone heroX (right)." },
60
+ { "name": "disabled", "type": "boolean", "default": "false", "description": "Estado disabled." },
61
+ { "name": "loading", "type": "boolean", "default": "false", "description": "Mostra spinner e desabilita interação." },
62
+ { "name": "ariaLabel", "type": "string | null", "default": "null", "description": "Aria-label customizado." },
63
+ { "name": "active", "type": "boolean", "default": "false", "description": "Estado active (sidebar nav, toggle, etc.)." }
64
+ ],
65
+ "outputs": [
66
+ { "name": "clicked", "type": "EventEmitter<MouseEvent>" }
67
+ ],
68
+ "examples": [
69
+ "<ds-button variant=\"solid\" variantColor=\"primary\" (clicked)=\"onSave()\">Salvar</ds-button>",
70
+ "<ds-button variant=\"icon\" iconLeft=\"heroPencil\" ariaLabel=\"Editar\" (clicked)=\"onEdit()\" />",
71
+ "<ds-button variant=\"primary-cta\" [loading]=\"saving()\" type=\"submit\">Criar conta</ds-button>"
72
+ ]
73
+ },
74
+ {
75
+ "name": "CardComponent",
76
+ "selector": "ds-card",
77
+ "description": "Container genérico do DS — 5 variants de chrome (default, dashboard, with-tabs, simple, inner-card) com slots para título, ícone, tabs de navegação interna e dropdown de ações.",
78
+ "tags": ["card", "container", "panel", "tabs", "dropdown"],
79
+ "props": [
80
+ { "name": "title", "type": "string | null", "default": "null", "description": "Título no header." },
81
+ { "name": "icon", "type": "string | null", "default": "null", "description": "Ícone heroX no header." },
82
+ { "name": "variant", "type": "CardVariant", "default": "'default'", "description": "Variant de chrome." },
83
+ { "name": "state", "type": "CardState", "default": "'default'", "description": "Estado: default, loading, empty, error." },
84
+ { "name": "navTabs", "type": "CardNavTab[] | null", "default": "null", "description": "Tabs de navegação interna (variant with-tabs)." },
85
+ { "name": "dropdownItems", "type": "CardDropdownItem[] | null", "default": "null", "description": "Itens de dropdown de ações no header." },
86
+ { "name": "ariaLabel", "type": "string | null", "default": "null", "description": "Aria-label customizado." }
87
+ ],
88
+ "examples": [
89
+ "<ds-card title=\"Receita mensal\" icon=\"heroChartBar\">\n <p>R$ 12.480,00</p>\n</ds-card>",
90
+ "<ds-card variant=\"dashboard\" [state]=\"loading() ? 'loading' : 'default'\">...</ds-card>"
91
+ ]
92
+ },
93
+ {
94
+ "name": "CardPanelComponent",
95
+ "selector": "ds-card-panel",
96
+ "description": "Painel containerizado simples com título obrigatório e estado de loading. Útil para sub-seções dentro de páginas.",
97
+ "tags": ["panel", "container", "loading", "section"],
98
+ "props": [
99
+ { "name": "title", "type": "string", "required": true, "description": "Título do painel." },
100
+ { "name": "loading", "type": "boolean", "default": "false", "description": "Estado de loading." }
101
+ ],
102
+ "examples": [
103
+ "<ds-card-panel title=\"Pagamentos pendentes\">\n <ds-data-table [rows]=\"rows()\" />\n</ds-card-panel>"
104
+ ]
105
+ },
106
+ {
107
+ "name": "FormFieldComponent",
108
+ "selector": "ds-form-field",
109
+ "description": "Campo de formulário unificado — 5 variants (text, textarea, select, toggle, search) com ControlValueAccessor para integração com Reactive Forms e Template-driven forms. Suporta hint, errorMessage, required, loading e estados disabled.",
110
+ "tags": ["form", "input", "textarea", "select", "toggle", "search", "validation", "reactive-forms", "ngmodel"],
111
+ "props": [
112
+ { "name": "variant", "type": "FormFieldVariant", "required": true, "description": "Tipo do campo." },
113
+ { "name": "size", "type": "FormFieldSize", "default": "'md'", "description": "Tamanho." },
114
+ { "name": "type", "type": "FormFieldType", "default": "'text'", "description": "Atributo HTML type (apenas variant=text)." },
115
+ { "name": "label", "type": "string | null", "default": "null", "description": "Label exibido acima do campo." },
116
+ { "name": "placeholder", "type": "string", "default": "''", "description": "Placeholder." },
117
+ { "name": "hint", "type": "string | null", "default": "null", "description": "Hint abaixo do campo." },
118
+ { "name": "errorMessage", "type": "string | null", "default": "null", "description": "Mensagem de erro (override do estado interno)." },
119
+ { "name": "required", "type": "boolean", "default": "false", "description": "Marca como obrigatório (asterisco + aria-required)." },
120
+ { "name": "disabled", "type": "boolean", "default": "false", "description": "Estado disabled." },
121
+ { "name": "loading", "type": "boolean", "default": "false", "description": "Spinner no canto direito." },
122
+ { "name": "rows", "type": "number", "default": "4", "description": "Linhas (apenas variant=textarea)." },
123
+ { "name": "maxLength", "type": "number | null", "default": "null", "description": "Máximo de caracteres." },
124
+ { "name": "options", "type": "FormFieldOption[] | null", "default": "null", "description": "Opções (apenas variant=select)." },
125
+ { "name": "externalDescribedBy", "type": "string | null", "default": "null", "description": "Prefix de aria-describedby externo." },
126
+ { "name": "chevronDownIconSrc", "type": "string", "default": "'branding/icons/form/chevron-down.svg'", "description": "Override do ícone chevron (split-ready)." },
127
+ { "name": "searchIconSrc", "type": "string", "default": "'branding/icons/form/search.svg'", "description": "Override do ícone search (split-ready)." }
128
+ ],
129
+ "outputs": [
130
+ { "name": "valueChange", "type": "EventEmitter<unknown>" },
131
+ { "name": "blurred", "type": "EventEmitter<FocusEvent>" }
132
+ ],
133
+ "examples": [
134
+ "<ds-form-field variant=\"text\" label=\"Nome\" formControlName=\"name\" [required]=\"true\" />",
135
+ "<ds-form-field variant=\"select\" label=\"Categoria\" [(ngModel)]=\"categoria\" [options]=\"categoriasOptions\" />",
136
+ "<ds-form-field variant=\"toggle\" label=\"Ativo\" formControlName=\"active\" />",
137
+ "<ds-form-field variant=\"search\" placeholder=\"Pesquisar...\" [(ngModel)]=\"q\" />"
138
+ ]
139
+ },
140
+ {
141
+ "name": "IconNeumorphicComponent",
142
+ "selector": "ds-icon-neumorphic",
143
+ "description": "Ícone em moldura neumorphic — 3 sizes (sm 28px, md 40px, lg 50px) × 7 variants de cor. Aceita ícone heroX (icon prop) ou path de SVG/PNG (iconImageSrc). Bounding-box quadrada com object-fit:contain garante render correto em mobile.",
144
+ "tags": ["icon", "neumorphic", "decoration", "kpi"],
145
+ "props": [
146
+ { "name": "icon", "type": "string | null", "default": "null", "description": "Nome de ícone heroX (use APENAS se não houver iconImageSrc)." },
147
+ { "name": "iconImageSrc", "type": "string | null", "default": "null", "description": "Path de SVG/PNG. Tem precedência sobre icon." },
148
+ { "name": "size", "type": "IconNeumorphicSize", "default": "'md'", "description": "Tamanho do container." },
149
+ { "name": "variant", "type": "IconNeumorphicVariant", "default": "'default'", "description": "Variant de cor do ícone interno." },
150
+ { "name": "ariaLabel", "type": "string | null", "default": "null", "description": "Aria-label. Sem isso, o container é decorativo." }
151
+ ],
152
+ "examples": [
153
+ "<ds-icon-neumorphic icon=\"heroChartBar\" size=\"md\" variant=\"apex\" />",
154
+ "<ds-icon-neumorphic iconImageSrc=\"branding/icons/statsbar/01-revenue.svg\" size=\"lg\" />"
155
+ ]
156
+ },
157
+ {
158
+ "name": "PageLayoutComponent",
159
+ "selector": "ds-page-layout",
160
+ "description": "Skeleton root das telas autenticadas. Container de viewport (100dvh) que organiza sidebar (desktop) + main scrollable + nav-footer (mobile) + AI Assistant overlay. Mobile-first com overscroll-behavior:none.",
161
+ "tags": ["layout", "shell", "skeleton", "viewport", "responsive", "mobile-first"],
162
+ "props": [
163
+ { "name": "variant", "type": "PageLayoutVariant", "default": "'dashboard'", "description": "Variant do layout (afeta padding/scroll do main)." },
164
+ { "name": "aiAssistantState", "type": "AiAssistantLayoutState", "default": "'closed'", "description": "Estado do AI Assistant (closed | open)." },
165
+ { "name": "sidebarWidth", "type": "string", "default": "'280px'", "description": "Largura da sidebar expandida." },
166
+ { "name": "aiAssistantWidth", "type": "string", "default": "'400px'", "description": "Largura do AI Assistant aberto." },
167
+ { "name": "mainAriaLabel", "type": "string", "default": "'Conteudo principal'", "description": "Aria-label do <main>." },
168
+ { "name": "dashboardActive", "type": "boolean", "default": "false", "description": "True quando rota = /dashboard (afeta nav-footer)." },
169
+ { "name": "aiMaximized", "type": "boolean", "default": "false", "description": "AI Assistant em modo maximizado." }
170
+ ],
171
+ "examples": [
172
+ "<ds-page-layout variant=\"dashboard\">\n <ds-sidebar-left-nav page-layout-sidebar [menuItems]=\"items\" />\n <ds-page-header page-layout-header title=\"Dashboard\" />\n <main page-layout-main>...</main>\n</ds-page-layout>"
173
+ ]
174
+ },
175
+ {
176
+ "name": "PageHeaderComponent",
177
+ "selector": "ds-page-header",
178
+ "description": "Header sticky presente em todas as telas autenticadas. Estrutura em 3 zonas: navegação (back/forward + título), breadcrumbs (centro), actions (direita). Variant module-dashboard tem botões Reorganizar/Personalizar.",
179
+ "tags": ["header", "navigation", "breadcrumbs", "title", "sticky"],
180
+ "props": [
181
+ { "name": "variant", "type": "PageHeaderVariant", "default": "'module-dashboard'", "description": "Variant do header." },
182
+ { "name": "title", "type": "string", "required": true, "description": "Título da tela." },
183
+ { "name": "icon", "type": "string | null", "default": "null", "description": "Ícone heroX antes do título." },
184
+ { "name": "breadcrumbs", "type": "PageHeaderBreadcrumb[]", "default": "[]", "description": "Caminho de navegação." },
185
+ { "name": "canGoBack", "type": "boolean", "default": "false", "description": "Habilita botão voltar." },
186
+ { "name": "canGoForward", "type": "boolean", "default": "false", "description": "Habilita botão avançar." },
187
+ { "name": "reorganizeActive", "type": "boolean", "default": "false", "description": "Estado active do botão Reorganizar." },
188
+ { "name": "customizeActive", "type": "boolean", "default": "false", "description": "Estado active do botão Personalizar." },
189
+ { "name": "mobileLogoSrc", "type": "string", "default": "'branding/logo-singulai-icon.png'", "description": "Logo mobile (split-ready)." },
190
+ { "name": "mobileLogoAlt", "type": "string", "default": "'Singulai'", "description": "Alt text do logo mobile." }
191
+ ],
192
+ "examples": [
193
+ "<ds-page-header title=\"Cursos\" icon=\"heroBookOpen\" [breadcrumbs]=\"[{label: 'Ensino'}, {label: 'Cursos'}]\" />"
194
+ ]
195
+ },
196
+ {
197
+ "name": "SidebarLeftNavComponent",
198
+ "selector": "ds-sidebar-left-nav",
199
+ "description": "Sidebar principal collapsible — 5 estados visuais (collapsed 44px / expanded ~280px / item-active / item-default / item-hover). Persiste expanded/collapsed em localStorage. Submenus expansíveis com mutex (apenas 1 aberto por vez).",
200
+ "tags": ["sidebar", "navigation", "menu", "collapsible", "submenu"],
201
+ "props": [
202
+ { "name": "menuItems", "type": "SidebarMenuItem[]", "default": "[]", "description": "Items do menu raiz com submenus." },
203
+ { "name": "user", "type": "SidebarUser | null", "default": "null", "description": "Usuário logado (footer avatar)." },
204
+ { "name": "initialState", "type": "SidebarState", "default": "'collapsed'", "description": "Estado inicial. Override do localStorage." },
205
+ { "name": "logoFullSrc", "type": "string", "default": "'branding/logo-singulai-full.png'", "description": "Logo completo (expanded — split-ready)." },
206
+ { "name": "logoIconSrc", "type": "string", "default": "'branding/logo-singulai-icon.png'", "description": "Logo ícone (collapsed — split-ready)." },
207
+ { "name": "logoAlt", "type": "string", "default": "'Singulai'", "description": "Alt do logo (split-ready)." },
208
+ { "name": "menuToggleExpandedIconSrc", "type": "string", "default": "'branding/icons/icon-menu-expanded.svg'", "description": "Ícone toggle expandido (split-ready)." },
209
+ { "name": "menuToggleCollapsedIconSrc", "type": "string", "default": "'branding/icons/icon-menu-default.svg'", "description": "Ícone toggle colapsado (split-ready)." },
210
+ { "name": "userIconSrc", "type": "string", "default": "'branding/icons/menu/user.svg'", "description": "Ícone usuário no footer (split-ready)." }
211
+ ],
212
+ "outputs": [
213
+ { "name": "stateChange", "type": "EventEmitter<SidebarState>" },
214
+ { "name": "searchClick", "type": "EventEmitter<void>" },
215
+ { "name": "accountClick", "type": "EventEmitter<void>" },
216
+ { "name": "submenuItemClick", "type": "EventEmitter<string>" }
217
+ ],
218
+ "examples": [
219
+ "<ds-sidebar-left-nav [menuItems]=\"items\" [user]=\"currentUser()\" (accountClick)=\"openAccount()\" />"
220
+ ]
221
+ },
222
+ {
223
+ "name": "NavFooterComponent",
224
+ "selector": "ds-nav-footer",
225
+ "description": "Bottom navigation mobile com 5 botões (Dashboard / Navegue / Agente IA / Alertas / Conta). Painéis crescem para cima com mutex (apenas 1 aberto por vez). Visível apenas <992px via @media SCSS.",
226
+ "tags": ["navigation", "mobile", "bottom-nav", "footer", "tab-bar", "panels"],
227
+ "props": [
228
+ { "name": "activePanel", "type": "NavFooterPanel | null", "default": "null", "description": "Painel atualmente aberto (mutex)." },
229
+ { "name": "dashboardActive", "type": "boolean", "default": "false", "description": "Dashboard active (rota = /dashboard)." },
230
+ { "name": "navegueActive", "type": "boolean", "default": "false", "description": "Navegue active (rota raiz que não /dashboard)." },
231
+ { "name": "homeIconSrc", "type": "string", "default": "'branding/icons/nav-footer/01-home.svg'", "description": "Ícone Home (split-ready)." },
232
+ { "name": "navegueIconSrc", "type": "string", "default": "'branding/icons/nav-footer/02-navegue.svg'", "description": "Ícone Navegue (split-ready)." },
233
+ { "name": "aiIconSrc", "type": "string", "default": "'branding/icons/nav-footer/03-agente-ia.svg'", "description": "Ícone AI (split-ready)." },
234
+ { "name": "alertasIconSrc", "type": "string", "default": "'branding/icons/nav-footer/04-alertas.svg'", "description": "Ícone Alertas (split-ready)." },
235
+ { "name": "contaIconSrc", "type": "string", "default": "'branding/icons/nav-footer/05-conta.svg'", "description": "Ícone Conta (split-ready)." }
236
+ ],
237
+ "outputs": [
238
+ { "name": "panelToggle", "type": "EventEmitter<NavFooterPanel>" },
239
+ { "name": "dashboardClick", "type": "EventEmitter<void>" },
240
+ { "name": "closePanels", "type": "EventEmitter<void>" }
241
+ ],
242
+ "examples": [
243
+ "<ds-nav-footer page-layout-nav-footer [activePanel]=\"layout.navFooterPanel()\" (panelToggle)=\"layout.togglePanel($event)\" />"
244
+ ]
245
+ },
246
+ {
247
+ "name": "AiAssistantPanelComponent",
248
+ "selector": "ds-ai-assistant-panel",
249
+ "description": "Painel lateral do Agente IA — header com tabs (current/new/chats) + créditos pill + actions (settings, maximize/minimize, close) + timeline de mensagens + action prompts + composer. Modo embedded para projeção em mobile bottom-sheet.",
250
+ "tags": ["ai", "assistant", "chat", "panel", "agent", "llm"],
251
+ "props": [
252
+ { "name": "showTitle", "type": "boolean", "default": "false", "description": "Exibe label 'Agente IA' (true=desktop, false=mobile)." },
253
+ { "name": "credits", "type": "number", "default": "0", "description": "Saldo de créditos IA na pill." },
254
+ { "name": "messages", "type": "AiMessage[]", "default": "[]", "description": "Mensagens da timeline." },
255
+ { "name": "actionPrompts", "type": "string[]", "default": "[]", "description": "Sugestões de prompts clicáveis." },
256
+ { "name": "activeTab", "type": "AiTab", "default": "'current'", "description": "Tab ativo." },
257
+ { "name": "hasCurrentChat", "type": "boolean", "default": "true", "description": "Mostra tab 'Chat atual...' closable." },
258
+ { "name": "currentChatLabel", "type": "string", "default": "'Chat atual...'", "description": "Label do tab atual." },
259
+ { "name": "maximized", "type": "boolean", "default": "false", "description": "Visual do botão maximize (icon contract vs expand)." },
260
+ { "name": "embedded", "type": "boolean", "default": "false", "description": "Modo embedded (remove card chrome — para nesting em mobile bottom-sheet)." },
261
+ { "name": "settingsIconSrc", "type": "string", "default": "'branding/icons/ai-assistant/settings.svg'", "description": "Ícone settings (split-ready)." },
262
+ { "name": "maximizeIconSrc", "type": "string", "default": "'branding/icons/ai-assistant/maximize.svg'", "description": "Ícone maximize (split-ready)." },
263
+ { "name": "minimizeIconSrc", "type": "string", "default": "'branding/icons/ai-assistant/minimize.svg'", "description": "Ícone minimize (split-ready)." },
264
+ { "name": "closeIconSrc", "type": "string", "default": "'branding/icons/ai-assistant/close.svg'", "description": "Ícone close (split-ready)." }
265
+ ],
266
+ "examples": [
267
+ "<ds-ai-assistant-panel page-layout-ai [showTitle]=\"true\" [credits]=\"creditsLeft()\" [messages]=\"chatMessages()\" [actionPrompts]=\"suggestions()\" />"
268
+ ]
269
+ },
270
+ {
271
+ "name": "SegmentedTabsComponent",
272
+ "selector": "ds-segmented-tabs",
273
+ "description": "Tabs segmentados pill-style com items closable opcionais e radius dinâmico baseado em $first/$last. Generic <K> para tipagem das keys (ex: 'day' | 'week' | 'month').",
274
+ "tags": ["tabs", "segmented", "pills", "filter", "period"],
275
+ "props": [
276
+ { "name": "items", "type": "SegmentedTabItem<K>[]", "required": true, "description": "Items dos tabs (key + label + closable opcional)." },
277
+ { "name": "activeKey", "type": "K | null", "default": "null", "description": "Key do tab ativo." }
278
+ ],
279
+ "outputs": [
280
+ { "name": "tabSelect", "type": "EventEmitter<K>" },
281
+ { "name": "tabClose", "type": "EventEmitter<K>" }
282
+ ],
283
+ "examples": [
284
+ "<ds-segmented-tabs [items]=\"[{key:'day',label:'Dia'},{key:'week',label:'Semana'}]\" [activeKey]=\"'week'\" (tabSelect)=\"setPeriod($event)\" />"
285
+ ]
286
+ },
287
+ {
288
+ "name": "StatsbarCardComponent",
289
+ "selector": "ds-statsbar-card",
290
+ "description": "Card de KPI para statsbar — title + value + delta (com seta up/down) + ícone neumorphic embutido. 6 variants de cor (success/purple/warning/info/danger/neutral). Polaridade invertida disponível (delta down como positivo).",
291
+ "tags": ["kpi", "statsbar", "metric", "card", "dashboard"],
292
+ "props": [
293
+ { "name": "title", "type": "string", "required": true, "description": "Título do KPI." },
294
+ { "name": "value", "type": "string | number", "required": true, "description": "Valor principal." },
295
+ { "name": "delta", "type": "string | null", "default": "null", "description": "Variação (ex: '+12%' ou 'R$ 480')." },
296
+ { "name": "deltaDirection", "type": "StatsbarCardDeltaDirection", "default": "'up'", "description": "Seta do delta." },
297
+ { "name": "inversePolarity", "type": "boolean", "default": "false", "description": "Inverte semântica de cor (delta down como positivo)." },
298
+ { "name": "variant", "type": "StatsbarCardVariant", "default": "'neutral'", "description": "Variant de cor do ícone." },
299
+ { "name": "iconImageSrc", "type": "string | null", "default": "null", "description": "Path do ícone (ex: 'branding/icons/statsbar/01-revenue.svg')." },
300
+ { "name": "iconWidth", "type": "number", "default": "22", "description": "Largura intrínseca do SVG (HTML attr para mobile)." },
301
+ { "name": "iconHeight", "type": "number", "default": "22", "description": "Altura intrínseca do SVG (HTML attr para mobile)." }
302
+ ],
303
+ "examples": [
304
+ "<ds-statsbar-card title=\"Receita\" [value]=\"12480\" delta=\"+12%\" deltaDirection=\"up\" variant=\"success\" iconImageSrc=\"branding/icons/statsbar/01-revenue.svg\" />"
305
+ ]
306
+ },
307
+ {
308
+ "name": "AiAssistantButtonComponent",
309
+ "selector": "ds-ai-assistant-button",
310
+ "description": "Botão de toggle do AI Assistant para o combo do header (32px height). Estado active (panel aberto) com triple-ring neumorphic + bg #EFF3F8.",
311
+ "tags": ["ai", "button", "toggle", "header", "combo"],
312
+ "props": [
313
+ { "name": "ariaLabel", "type": "string", "default": "'Abrir Assistente IA'", "description": "Aria-label." },
314
+ { "name": "active", "type": "boolean", "default": "false", "description": "AI Assistant panel aberto." },
315
+ { "name": "sparklesIconSrc", "type": "string", "default": "'branding/icons/sparkles.svg'", "description": "Override do ícone sparkles (split-ready)." }
316
+ ],
317
+ "outputs": [
318
+ { "name": "clicked", "type": "EventEmitter<MouseEvent>" }
319
+ ],
320
+ "examples": [
321
+ "<ds-ai-assistant-button [active]=\"aiState() === 'open'\" (clicked)=\"toggleAi()\" />"
322
+ ]
323
+ },
324
+ {
325
+ "name": "DarkModeButtonComponent",
326
+ "selector": "ds-dark-mode-button",
327
+ "description": "Toggle de tema (light/dark) para o combo do header. Composite SVG com lua + cone de luz rotacionado + 2 estrelas em cruz, todos com filter neumorphic unificado.",
328
+ "tags": ["theme", "dark-mode", "button", "toggle", "header", "combo"],
329
+ "props": [
330
+ { "name": "ariaLabel", "type": "string", "default": "'Alternar tema claro/escuro'", "description": "Aria-label." },
331
+ { "name": "moonIconSrc", "type": "string", "default": "'branding/icons/moon-vector217.svg'", "description": "Lua principal (split-ready)." },
332
+ { "name": "moonGlowIconSrc", "type": "string", "default": "'branding/icons/moon-vector.svg'", "description": "Cone de luz (split-ready)." }
333
+ ],
334
+ "outputs": [
335
+ { "name": "clicked", "type": "EventEmitter<MouseEvent>" }
336
+ ],
337
+ "examples": [
338
+ "<ds-dark-mode-button (clicked)=\"themeService.toggle()\" />"
339
+ ]
340
+ },
341
+ {
342
+ "name": "NotificationsButtonComponent",
343
+ "selector": "ds-notifications-button",
344
+ "description": "Botão de sino de notificações para o combo do header (32px). Renderiza apenas o SVG bell com filter neumorphic.",
345
+ "tags": ["notifications", "bell", "button", "header", "combo"],
346
+ "props": [
347
+ { "name": "ariaLabel", "type": "string", "default": "'Notificacoes'", "description": "Aria-label." },
348
+ { "name": "bellIconSrc", "type": "string", "default": "'branding/icons/bell.svg'", "description": "Override do ícone bell (split-ready)." }
349
+ ],
350
+ "outputs": [
351
+ { "name": "clicked", "type": "EventEmitter<MouseEvent>" }
352
+ ],
353
+ "examples": [
354
+ "<ds-notifications-button (clicked)=\"openNotifications()\" />"
355
+ ]
356
+ }
357
+ ]
358
+ }
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Singulai Design System — MCP Server
4
+ *
5
+ * Expoe o catalogo do Design System Singulai (Angular 20, Standalone +
6
+ * Signals, neumorphic) para AI agents via Model Context Protocol.
7
+ *
8
+ * Tools v0.1:
9
+ * - list_components() — array nome+selector+description+tags
10
+ * - get_component(name|selector) — info completa: props tipados, outputs, exemplos
11
+ * - search_components(query, limit) — busca por keyword com score
12
+ *
13
+ * Transport: stdio (padrao MCP — clientes spawnam o processo).
14
+ */
15
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,235 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Singulai Design System — MCP Server
4
+ *
5
+ * Expoe o catalogo do Design System Singulai (Angular 20, Standalone +
6
+ * Signals, neumorphic) para AI agents via Model Context Protocol.
7
+ *
8
+ * Tools v0.1:
9
+ * - list_components() — array nome+selector+description+tags
10
+ * - get_component(name|selector) — info completa: props tipados, outputs, exemplos
11
+ * - search_components(query, limit) — busca por keyword com score
12
+ *
13
+ * Transport: stdio (padrao MCP — clientes spawnam o processo).
14
+ */
15
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
16
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
17
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
18
+ import { findComponent, loadCatalog, searchComponents, } from './catalog.js';
19
+ const SERVER_NAME = 'singulai-design-system-mcp';
20
+ const SERVER_VERSION = '0.1.0';
21
+ // Carrega catalogo no boot — falha rapido se JSON invalido
22
+ const catalog = loadCatalog();
23
+ const server = new Server({
24
+ name: SERVER_NAME,
25
+ version: SERVER_VERSION,
26
+ }, {
27
+ capabilities: {
28
+ tools: {},
29
+ },
30
+ });
31
+ // ---------------------------------------------------------------------------
32
+ // Formatadores (saida amigavel para AI agents)
33
+ // ---------------------------------------------------------------------------
34
+ function formatComponentSummary(c) {
35
+ return `- ${c.selector} (${c.name}): ${c.description}`;
36
+ }
37
+ function formatComponentDetail(c) {
38
+ const lines = [
39
+ `# ${c.name}`,
40
+ `**Selector:** \`${c.selector}\``,
41
+ '',
42
+ c.description,
43
+ '',
44
+ `**Tags:** ${c.tags.join(', ')}`,
45
+ '',
46
+ '## Props (@Input)',
47
+ ];
48
+ if (c.props.length === 0) {
49
+ lines.push('_Nenhum @Input declarado._');
50
+ }
51
+ else {
52
+ lines.push('| Nome | Tipo | Default | Descricao |');
53
+ lines.push('|------|------|---------|-----------|');
54
+ for (const p of c.props) {
55
+ const def = p.required ? '_required_' : (p.default ?? '_(sem default)_');
56
+ const type = '`' + p.type + '`';
57
+ lines.push(`| \`${p.name}\` | ${type} | ${def} | ${p.description} |`);
58
+ }
59
+ }
60
+ if (c.outputs && c.outputs.length > 0) {
61
+ lines.push('', '## Outputs (@Output)', '');
62
+ for (const o of c.outputs) {
63
+ lines.push(`- \`${o.name}\`: \`${o.type}\``);
64
+ }
65
+ }
66
+ if (c.examples.length > 0) {
67
+ lines.push('', '## Exemplos de uso', '');
68
+ for (const ex of c.examples) {
69
+ lines.push('```html');
70
+ lines.push(ex);
71
+ lines.push('```');
72
+ lines.push('');
73
+ }
74
+ }
75
+ return lines.join('\n');
76
+ }
77
+ // ---------------------------------------------------------------------------
78
+ // Tools registry
79
+ // ---------------------------------------------------------------------------
80
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
81
+ return {
82
+ tools: [
83
+ {
84
+ name: 'list_components',
85
+ description: 'Lista todos os componentes do Singulai Design System com nome, selector e descricao curta. Use quando o usuario pergunta "quais componentes existem" ou "o que tem disponivel no DS".',
86
+ inputSchema: {
87
+ type: 'object',
88
+ properties: {},
89
+ },
90
+ },
91
+ {
92
+ name: 'get_component',
93
+ description: 'Retorna info completa de um componente: descricao, tags, props (@Input) tipados com defaults, outputs (@Output), e exemplos de uso. Use quando o usuario pergunta sobre um componente especifico (ex: "como uso o ds-button" ou "props do FormFieldComponent").',
94
+ inputSchema: {
95
+ type: 'object',
96
+ properties: {
97
+ name: {
98
+ type: 'string',
99
+ description: 'Nome do componente (ex: "ButtonComponent") OU selector (ex: "ds-button"). Case-insensitive.',
100
+ },
101
+ },
102
+ required: ['name'],
103
+ },
104
+ },
105
+ {
106
+ name: 'search_components',
107
+ description: 'Busca componentes por palavra-chave em selector, nome, descricao e tags. Use quando o usuario descreve uma necessidade sem saber o nome exato (ex: "preciso de um botao com loading", "componente para tabela", "AI assistant", "form com select").',
108
+ inputSchema: {
109
+ type: 'object',
110
+ properties: {
111
+ query: {
112
+ type: 'string',
113
+ description: 'Palavras-chave separadas por espaco. Ex: "form select", "ai chat", "kpi dashboard".',
114
+ },
115
+ limit: {
116
+ type: 'number',
117
+ description: 'Maximo de resultados (default 5, max 15).',
118
+ minimum: 1,
119
+ maximum: 15,
120
+ },
121
+ },
122
+ required: ['query'],
123
+ },
124
+ },
125
+ ],
126
+ };
127
+ });
128
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
129
+ const { name, arguments: args } = request.params;
130
+ switch (name) {
131
+ case 'list_components': {
132
+ const lines = [
133
+ `# ${catalog.design_system.name} (${catalog.design_system.version})`,
134
+ catalog.design_system.homepage,
135
+ '',
136
+ `${catalog.components.length} componentes disponiveis:`,
137
+ '',
138
+ ...catalog.components.map(formatComponentSummary),
139
+ '',
140
+ '_Use `get_component(name)` para ver props, outputs e exemplos de cada um._',
141
+ ];
142
+ return {
143
+ content: [{ type: 'text', text: lines.join('\n') }],
144
+ };
145
+ }
146
+ case 'get_component': {
147
+ const queryName = args?.name?.trim();
148
+ if (!queryName) {
149
+ return {
150
+ content: [
151
+ {
152
+ type: 'text',
153
+ text: 'Erro: parametro `name` e obrigatorio. Ex: `get_component({ name: "ds-button" })`.',
154
+ },
155
+ ],
156
+ isError: true,
157
+ };
158
+ }
159
+ const component = findComponent(catalog, queryName);
160
+ if (!component) {
161
+ const suggestions = searchComponents(catalog, queryName, 3);
162
+ const suggestionText = suggestions.length > 0
163
+ ? `\n\nVoce talvez quis dizer:\n${suggestions
164
+ .map((s) => `- ${s.component.selector} (${s.component.name})`)
165
+ .join('\n')}`
166
+ : '\n\nUse `list_components()` para ver todos os componentes disponiveis.';
167
+ return {
168
+ content: [
169
+ {
170
+ type: 'text',
171
+ text: `Componente '${queryName}' nao encontrado.${suggestionText}`,
172
+ },
173
+ ],
174
+ isError: true,
175
+ };
176
+ }
177
+ return {
178
+ content: [{ type: 'text', text: formatComponentDetail(component) }],
179
+ };
180
+ }
181
+ case 'search_components': {
182
+ const query = args?.query?.trim();
183
+ if (!query) {
184
+ return {
185
+ content: [
186
+ {
187
+ type: 'text',
188
+ text: 'Erro: parametro `query` e obrigatorio. Ex: `search_components({ query: "form select" })`.',
189
+ },
190
+ ],
191
+ isError: true,
192
+ };
193
+ }
194
+ const limitArg = args?.limit;
195
+ const limit = Math.min(Math.max(limitArg ?? 5, 1), 15);
196
+ const results = searchComponents(catalog, query, limit);
197
+ if (results.length === 0) {
198
+ return {
199
+ content: [
200
+ {
201
+ type: 'text',
202
+ text: `Nenhum componente correspondeu a "${query}". Use \`list_components()\` para ver todos os componentes disponiveis.`,
203
+ },
204
+ ],
205
+ };
206
+ }
207
+ const lines = [
208
+ `# Resultados para "${query}" (${results.length} match${results.length > 1 ? 'es' : ''})`,
209
+ '',
210
+ ...results.map(({ component, score }) => `**${component.selector}** (${component.name}) — score ${score}\n${component.description}\n_Tags: ${component.tags.join(', ')}_`),
211
+ '',
212
+ '_Use `get_component(name)` para ver props, outputs e exemplos de cada um._',
213
+ ];
214
+ return {
215
+ content: [{ type: 'text', text: lines.join('\n\n') }],
216
+ };
217
+ }
218
+ default:
219
+ throw new Error(`Tool desconhecida: ${name}`);
220
+ }
221
+ });
222
+ // ---------------------------------------------------------------------------
223
+ // Bootstrap
224
+ // ---------------------------------------------------------------------------
225
+ async function main() {
226
+ const transport = new StdioServerTransport();
227
+ await server.connect(transport);
228
+ // Log no stderr para nao poluir o stdout (que e o canal MCP JSON-RPC)
229
+ console.error(`[${SERVER_NAME}] v${SERVER_VERSION} ready (stdio) — ${catalog.components.length} components in catalog`);
230
+ }
231
+ main().catch((error) => {
232
+ console.error('[fatal]', error);
233
+ process.exit(1);
234
+ });
235
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@dgplsoares/singulai-ds-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server exposing the Singulai Design System (Angular 20) to AI agents — Claude Desktop, Claude Code, Cursor, Continue.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "singulai-ds-mcp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsc && cp -r src/data dist/",
17
+ "dev": "tsx src/index.ts",
18
+ "start": "node dist/index.js",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "keywords": [
22
+ "mcp",
23
+ "model-context-protocol",
24
+ "design-system",
25
+ "angular",
26
+ "neumorphism",
27
+ "singulai",
28
+ "claude",
29
+ "ai-agents"
30
+ ],
31
+ "author": "Diogo Soares <https://www.linkedin.com/in/dgsoares/>",
32
+ "license": "MIT",
33
+ "homepage": "https://design.singulai.ai",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/dgplsoares/singulai-design-system-mcp.git"
37
+ },
38
+ "bugs": {
39
+ "url": "https://github.com/dgplsoares/singulai-design-system-mcp/issues"
40
+ },
41
+ "engines": {
42
+ "node": ">=20"
43
+ },
44
+ "dependencies": {
45
+ "@modelcontextprotocol/sdk": "^1.0.4",
46
+ "zod": "^3.23.8"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^22.10.2",
50
+ "tsx": "^4.19.2",
51
+ "typescript": "^5.7.2"
52
+ }
53
+ }