@ng-solid/mcp 22.0.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/GUIDE.md +292 -0
- package/README.md +159 -0
- package/data/design-system.json +24204 -0
- package/data/icons.json +1 -0
- package/data/skills/ns-solid-components/SKILL.md +140 -0
- package/data/skills/ns-solid-forms/SKILL.md +183 -0
- package/data/skills/ns-solid-icons/SKILL.md +95 -0
- package/data/skills/ns-solid-install/SKILL.md +120 -0
- package/data/skills/ns-solid-style-contract/SKILL.md +84 -0
- package/data/skills/ns-solid-theming/SKILL.md +123 -0
- package/index.d.ts +3 -0
- package/index.js +38 -0
- package/lib/data.d.ts +26 -0
- package/lib/data.js +51 -0
- package/lib/install-skills.d.ts +1 -0
- package/lib/install-skills.js +159 -0
- package/lib/preset-validator.d.ts +44 -0
- package/lib/preset-validator.js +172 -0
- package/lib/prompts.d.ts +2 -0
- package/lib/prompts.js +173 -0
- package/lib/resources.d.ts +2 -0
- package/lib/resources.js +328 -0
- package/lib/server.d.ts +3 -0
- package/lib/server.js +31 -0
- package/lib/style-contract.d.ts +37 -0
- package/lib/style-contract.js +167 -0
- package/lib/template-parser.d.ts +29 -0
- package/lib/template-parser.js +230 -0
- package/lib/tools.components.d.ts +44 -0
- package/lib/tools.components.js +836 -0
- package/lib/tools.d.ts +9 -0
- package/lib/tools.guides.d.ts +15 -0
- package/lib/tools.guides.js +103 -0
- package/lib/tools.icons.d.ts +7 -0
- package/lib/tools.icons.js +106 -0
- package/lib/tools.js +18 -0
- package/lib/tools.tokens.d.ts +7 -0
- package/lib/tools.tokens.js +278 -0
- package/lib/types.d.ts +349 -0
- package/lib/types.js +11 -0
- package/package.json +52 -0
- package/skills/ns-solid-components/SKILL.md +77 -0
- package/skills/ns-solid-forms/SKILL.md +166 -0
- package/skills/ns-solid-icons/SKILL.md +95 -0
- package/skills/ns-solid-install/SKILL.md +119 -0
- package/skills/ns-solid-style-contract/SKILL.md +84 -0
- package/skills/ns-solid-theming/SKILL.md +123 -0
package/lib/types.d.ts
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshot types for the ng-solid MCP server.
|
|
3
|
+
*
|
|
4
|
+
* The snapshot is generated at build time by `scripts/prebuild.mjs` (Fase 2/3 del plan)
|
|
5
|
+
* from the design-system AST, the Arvisfy preset, the icon catalog and the docs registry.
|
|
6
|
+
* These types mirror the *actual* shape written to `data/design-system.json` /
|
|
7
|
+
* `data/icons.json` -- verified field-by-field against the real snapshot rather than
|
|
8
|
+
* assumed from the plan.
|
|
9
|
+
*/
|
|
10
|
+
export type NsVisibility = 'public' | 'internal';
|
|
11
|
+
export type NsSelectorType = 'element' | 'attribute';
|
|
12
|
+
export type MemberKind = 'input' | 'output' | 'model' | 'content-child' | 'content-children' | 'view-child' | 'view-children';
|
|
13
|
+
export type MemberTransformKind = 'angular' | 'custom';
|
|
14
|
+
/** A single member (input/output/model) exposed by a component or a base class it extends. */
|
|
15
|
+
export interface MemberDoc {
|
|
16
|
+
/** Property name as declared in the class. */
|
|
17
|
+
name: string;
|
|
18
|
+
kind: MemberKind;
|
|
19
|
+
required: boolean;
|
|
20
|
+
/** Public binding name if different from `name` (e.g. `input.required({ alias: 'nsTooltip' })`). */
|
|
21
|
+
alias: string | null;
|
|
22
|
+
/** The name an agent should actually bind to in a template -- `alias ?? name`. */
|
|
23
|
+
bindingName: string;
|
|
24
|
+
/** Resolved TypeScript type text (the explicit type argument, never the transform's output type). */
|
|
25
|
+
type: string;
|
|
26
|
+
/** Expanded string-literal union members, when the type resolves to a closed set. */
|
|
27
|
+
values: string[] | null;
|
|
28
|
+
default: string | null;
|
|
29
|
+
defaultKind: 'literal' | 'expression' | null;
|
|
30
|
+
transform: string | null;
|
|
31
|
+
transformKind: MemberTransformKind | null;
|
|
32
|
+
/** True when `booleanAttribute`/`numberAttribute` allow the bare-attribute form (`<ns-x loading>`). */
|
|
33
|
+
acceptsAttribute: boolean;
|
|
34
|
+
docs: string;
|
|
35
|
+
/** Name of the base class this member was inherited from, if not declared on the component itself. */
|
|
36
|
+
inheritedFrom: string | null;
|
|
37
|
+
/** Primary template binding form, e.g. `[variant]="variant"` or the bare-attribute `loading`. */
|
|
38
|
+
binding: string;
|
|
39
|
+
/** Additional valid binding forms (e.g. the property-binding form of a boolean-attribute input). */
|
|
40
|
+
bindingAlt?: string[];
|
|
41
|
+
/** Present only on `kind: 'model'` members -- the companion `Change` output name, e.g. `valueChange`. */
|
|
42
|
+
outputName?: string;
|
|
43
|
+
}
|
|
44
|
+
/** One entry of the `host: {...}` block's `[class.x]` bindings, decoded into a structured form. */
|
|
45
|
+
export interface BemModifierDoc {
|
|
46
|
+
/** Bound input name driving this modifier, or `null` for a free host-expression modifier. */
|
|
47
|
+
input: string | null;
|
|
48
|
+
/** Literal value the input must equal to apply the modifier (`true` for boolean modifiers). */
|
|
49
|
+
value: string | boolean | null;
|
|
50
|
+
kind: 'boolean' | 'enum' | 'expression';
|
|
51
|
+
className: string;
|
|
52
|
+
/** Raw host-binding expression text, present for `kind: 'expression'`. */
|
|
53
|
+
expression?: string;
|
|
54
|
+
}
|
|
55
|
+
export interface BemAttrDoc {
|
|
56
|
+
attr: string;
|
|
57
|
+
raw: string;
|
|
58
|
+
}
|
|
59
|
+
export interface BemListenerDoc {
|
|
60
|
+
event: string;
|
|
61
|
+
raw: string;
|
|
62
|
+
}
|
|
63
|
+
export interface BemOtherHostDoc {
|
|
64
|
+
key: string;
|
|
65
|
+
raw: string;
|
|
66
|
+
}
|
|
67
|
+
/** Parsed `host: {...}` metadata block, decoded into a BEM shape. */
|
|
68
|
+
export interface BemDoc {
|
|
69
|
+
block: string;
|
|
70
|
+
/** Class names present unconditionally on the host (rare -- most styling is via modifiers). */
|
|
71
|
+
staticClasses: string[];
|
|
72
|
+
/** `ns-block__element` class names referenced anywhere in the component's own template/host. */
|
|
73
|
+
elements: string[];
|
|
74
|
+
modifiers: BemModifierDoc[];
|
|
75
|
+
/** modifier input -> { boundValue -> css modifier class }, used to cross-check against `values`. */
|
|
76
|
+
modifierMap: Record<string, Record<string, string>>;
|
|
77
|
+
/** Non-class host attribute bindings, e.g. `[attr.data-selected]`. */
|
|
78
|
+
attrs: BemAttrDoc[];
|
|
79
|
+
/** Host event bindings, e.g. `(mouseenter)="pause()"`. */
|
|
80
|
+
listeners: BemListenerDoc[];
|
|
81
|
+
/** Host-bound CSS custom properties (`[style.--x]`), when present. */
|
|
82
|
+
styleVars: string[];
|
|
83
|
+
/** Any other host block entry that isn't a class/attr/listener/style (e.g. `role`). */
|
|
84
|
+
other: BemOtherHostDoc[];
|
|
85
|
+
}
|
|
86
|
+
export interface SlotDoc {
|
|
87
|
+
/** `'default'` for the unnamed content-projection slot. */
|
|
88
|
+
name: string;
|
|
89
|
+
/** The `select` attribute of the matching `<ng-content>`, or `null` for the default slot. */
|
|
90
|
+
select: string | null;
|
|
91
|
+
}
|
|
92
|
+
export interface A11yDoc {
|
|
93
|
+
roles: string[];
|
|
94
|
+
ariaAttributes: string[];
|
|
95
|
+
focusTrap: boolean;
|
|
96
|
+
focusable: boolean;
|
|
97
|
+
}
|
|
98
|
+
export interface FormControlDoc {
|
|
99
|
+
isValueAccessor: boolean;
|
|
100
|
+
/** Resolved generic value type of the CVA, e.g. `string`, when statically known. */
|
|
101
|
+
valueType: string | null;
|
|
102
|
+
/** Base class providing the `ControlValueAccessor` implementation, e.g. `NsValueAccessor`. */
|
|
103
|
+
baseClass: string | null;
|
|
104
|
+
}
|
|
105
|
+
export interface CompositionDoc {
|
|
106
|
+
/** Selector of the component this one is designed to be projected into, if any. */
|
|
107
|
+
parent: string | null;
|
|
108
|
+
/** Selectors of child components this component queries via `contentChildren`/`contentChild`. */
|
|
109
|
+
children: string[];
|
|
110
|
+
}
|
|
111
|
+
export interface ComponentDoc {
|
|
112
|
+
slug: string;
|
|
113
|
+
selector: string;
|
|
114
|
+
selectorType: NsSelectorType;
|
|
115
|
+
className: string;
|
|
116
|
+
/** Angular `exportAs` value used in template reference variables (`#ref="exportedAs"`), when set. */
|
|
117
|
+
exportedAs: string;
|
|
118
|
+
visibility: NsVisibility;
|
|
119
|
+
/** Always `@ng-solid/design-system` today (single entrypoint) -- kept explicit for future secondary entrypoints. */
|
|
120
|
+
importFrom: string;
|
|
121
|
+
/** Feature directory, e.g. `input` for `form/components/input`. */
|
|
122
|
+
family: string;
|
|
123
|
+
/** Top-level grouping, e.g. `form`. `null` for components outside a grouped feature area. */
|
|
124
|
+
group: string | null;
|
|
125
|
+
standalone: boolean;
|
|
126
|
+
templateExportAs: string | null;
|
|
127
|
+
/** Repo-relative path to the component's `.ts` source file. */
|
|
128
|
+
filePath: string;
|
|
129
|
+
/** Class names this component's `imports: [...]` array references (its own dependencies, not its consumers'). */
|
|
130
|
+
imports: string[];
|
|
131
|
+
/** Raw source text of the `providers: [...]` array, if any. */
|
|
132
|
+
providers: string;
|
|
133
|
+
formControl: FormControlDoc | null;
|
|
134
|
+
inputs: MemberDoc[];
|
|
135
|
+
outputs: MemberDoc[];
|
|
136
|
+
models: MemberDoc[];
|
|
137
|
+
composition: CompositionDoc;
|
|
138
|
+
bem: BemDoc;
|
|
139
|
+
slots: SlotDoc[];
|
|
140
|
+
a11y: A11yDoc;
|
|
141
|
+
tokens: string[];
|
|
142
|
+
docs: string;
|
|
143
|
+
/** From the docs registry join (`apps/demo/src/app/docs.registry.ts`) -- only present for the ~44 registry slugs. */
|
|
144
|
+
category?: string;
|
|
145
|
+
summary?: string;
|
|
146
|
+
snippet?: string;
|
|
147
|
+
variants?: string[];
|
|
148
|
+
/** Hand-authored pitfalls from `overrides/component-notes.json`, keyed by selector. */
|
|
149
|
+
pitfalls?: string[];
|
|
150
|
+
/** Selectors of components worth cross-referencing (from `overrides/component-notes.json`, in addition to `composition`). */
|
|
151
|
+
relatedComponents?: string[];
|
|
152
|
+
}
|
|
153
|
+
export interface ServiceMethodDoc {
|
|
154
|
+
name: string;
|
|
155
|
+
signature: string;
|
|
156
|
+
docs?: string;
|
|
157
|
+
}
|
|
158
|
+
export interface ServiceDoc {
|
|
159
|
+
name: string;
|
|
160
|
+
exportedAs: string;
|
|
161
|
+
importFrom: string;
|
|
162
|
+
filePath: string;
|
|
163
|
+
providedIn: string | null;
|
|
164
|
+
docs?: string;
|
|
165
|
+
methods: ServiceMethodDoc[];
|
|
166
|
+
/** Selectors of components this service creates/manages (e.g. `NsToastService` -> `ns-toast`). */
|
|
167
|
+
relatedComponents: string[];
|
|
168
|
+
}
|
|
169
|
+
/** A function provider (`provideNsTheme`, `provideIcons`, `provideIconsPatch`). */
|
|
170
|
+
export interface ProviderDoc {
|
|
171
|
+
name: string;
|
|
172
|
+
importFrom: string;
|
|
173
|
+
signature: string;
|
|
174
|
+
docs?: string;
|
|
175
|
+
example: string;
|
|
176
|
+
}
|
|
177
|
+
/** One `## `-delimited section of a skill's markdown body. */
|
|
178
|
+
export interface GuideSectionDoc {
|
|
179
|
+
/** Kebab-case id derived from the heading text (accent/punctuation-stripped). */
|
|
180
|
+
id: string;
|
|
181
|
+
title: string;
|
|
182
|
+
/** Markdown body of the section, with every `<!-- ns:… -->` marker already resolved. */
|
|
183
|
+
content: string;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* A guide generated from `packages/mcp/skills/<name>/SKILL.md` (the single
|
|
187
|
+
* source of truth -- see `scripts/lib/extract-guides.mjs`). `name` matches
|
|
188
|
+
* both the skill's directory name and its front-matter `name:`, so it is
|
|
189
|
+
* simultaneously the `get_guide`/`list_guides` identifier and the
|
|
190
|
+
* `install-skills` distribution directory name.
|
|
191
|
+
*/
|
|
192
|
+
export interface GuideDoc {
|
|
193
|
+
name: string;
|
|
194
|
+
/** Always equal to `name` -- kept as an alias so callers can use either the `slug`/`name` convention consistently with `ComponentDoc.slug`/`ServiceDoc.name`. */
|
|
195
|
+
slug: string;
|
|
196
|
+
/** First `# ` heading in the skill body, or `name` if the body has none. */
|
|
197
|
+
title: string;
|
|
198
|
+
/** Front-matter `description:`. */
|
|
199
|
+
description: string;
|
|
200
|
+
sections: GuideSectionDoc[];
|
|
201
|
+
/**
|
|
202
|
+
* Package-root-relative path to the *resolved* copy of this skill (markers
|
|
203
|
+
* already interpolated), e.g. `data/skills/ns-solid-install/SKILL.md`.
|
|
204
|
+
* This is what `install-skills` copies and what `ns://skill/{slug}`
|
|
205
|
+
* resolves -- never the raw authored source under `skills/`, which may
|
|
206
|
+
* still contain unresolved `<!-- ns:... -->` markers.
|
|
207
|
+
*/
|
|
208
|
+
skillPath: string;
|
|
209
|
+
}
|
|
210
|
+
/** One flattened token entry in the `primitive` tier: no reference, just the raw value. */
|
|
211
|
+
export interface PrimitiveTokenDoc {
|
|
212
|
+
path: string;
|
|
213
|
+
cssVariable: string;
|
|
214
|
+
value: string | number;
|
|
215
|
+
}
|
|
216
|
+
/** One flattened token entry in the `semantic` tier: references a primitive token. */
|
|
217
|
+
export interface SemanticTokenDoc {
|
|
218
|
+
path: string;
|
|
219
|
+
cssVariable: string;
|
|
220
|
+
reference: string;
|
|
221
|
+
resolvedLiteral: string | number | null;
|
|
222
|
+
broken?: string;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* One flattened token entry in the `components` tier, resolved all the way
|
|
226
|
+
* down to its primitive literal for both color schemes -- this is what lets
|
|
227
|
+
* an agent reason about contrast without running Angular.
|
|
228
|
+
*/
|
|
229
|
+
export interface ComponentTokenDoc {
|
|
230
|
+
path: string;
|
|
231
|
+
cssVariable: string;
|
|
232
|
+
reference: string;
|
|
233
|
+
resolved: {
|
|
234
|
+
light: string | number | null;
|
|
235
|
+
dark: string | number | null;
|
|
236
|
+
};
|
|
237
|
+
broken?: {
|
|
238
|
+
light?: string;
|
|
239
|
+
dark?: string;
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
/** One naming/validation rule enforced by `validateNsPreset`, extracted from `theme.utils.ts`. */
|
|
243
|
+
export interface ThemeRuleDoc {
|
|
244
|
+
id: string;
|
|
245
|
+
severity: 'error' | 'warning';
|
|
246
|
+
scope: string;
|
|
247
|
+
pattern: string | null;
|
|
248
|
+
message: string;
|
|
249
|
+
}
|
|
250
|
+
export interface ThemeDoc {
|
|
251
|
+
presetName: string;
|
|
252
|
+
tiers: {
|
|
253
|
+
primitive: PrimitiveTokenDoc[];
|
|
254
|
+
semantic: {
|
|
255
|
+
common: SemanticTokenDoc[];
|
|
256
|
+
light: SemanticTokenDoc[];
|
|
257
|
+
dark: SemanticTokenDoc[];
|
|
258
|
+
};
|
|
259
|
+
component: ComponentTokenDoc[];
|
|
260
|
+
};
|
|
261
|
+
/** Top-level keys of the `components` tier (e.g. `button`, `modal`) -- ~43 groups. */
|
|
262
|
+
componentGroups: string[];
|
|
263
|
+
drift: {
|
|
264
|
+
/** `--ns-component-*` used in component SCSS but not defined by the preset. */
|
|
265
|
+
undefinedInPreset: string[];
|
|
266
|
+
/** `--ns-component-*` defined by the preset but never referenced in component SCSS. */
|
|
267
|
+
unusedInScss: string[];
|
|
268
|
+
};
|
|
269
|
+
rules: ThemeRuleDoc[];
|
|
270
|
+
}
|
|
271
|
+
export interface IconDoc {
|
|
272
|
+
name: string;
|
|
273
|
+
symbol: string | null;
|
|
274
|
+
keywords: string[];
|
|
275
|
+
}
|
|
276
|
+
export interface IconsSnapshot {
|
|
277
|
+
schemaVersion: number;
|
|
278
|
+
version: string;
|
|
279
|
+
generatedAt: string;
|
|
280
|
+
meta: {
|
|
281
|
+
sourceUrl: string;
|
|
282
|
+
sourceCommit: string;
|
|
283
|
+
license: string;
|
|
284
|
+
count: number;
|
|
285
|
+
};
|
|
286
|
+
icons: IconDoc[];
|
|
287
|
+
/** `NsIconsComponent`'s own API (selector `ns-icon`), so an agent doesn't need a second lookup. */
|
|
288
|
+
component: ComponentDoc;
|
|
289
|
+
stats: {
|
|
290
|
+
total: number;
|
|
291
|
+
};
|
|
292
|
+
__stub?: boolean;
|
|
293
|
+
}
|
|
294
|
+
export interface NsSnapshotPackages {
|
|
295
|
+
designSystem: string;
|
|
296
|
+
icons: string;
|
|
297
|
+
core?: string;
|
|
298
|
+
}
|
|
299
|
+
export interface NsSnapshotStats {
|
|
300
|
+
componentsPublic: number;
|
|
301
|
+
componentsInternal: number;
|
|
302
|
+
services: number;
|
|
303
|
+
providers: number;
|
|
304
|
+
guides: number;
|
|
305
|
+
icons: number;
|
|
306
|
+
tokensPrimitive: number;
|
|
307
|
+
tokensSemantic: number;
|
|
308
|
+
tokensComponent: number;
|
|
309
|
+
}
|
|
310
|
+
export interface NsFrameworkDoc {
|
|
311
|
+
name: string;
|
|
312
|
+
version: string;
|
|
313
|
+
}
|
|
314
|
+
/** A feature directory grouping (`family`, e.g. `input`) and its optional top-level `group` (e.g. `form`). */
|
|
315
|
+
export interface FamilyDoc {
|
|
316
|
+
family: string;
|
|
317
|
+
group: string | null;
|
|
318
|
+
components: string[];
|
|
319
|
+
}
|
|
320
|
+
export interface StyleContractRuleDoc {
|
|
321
|
+
id: string;
|
|
322
|
+
severity: 'error' | 'warning';
|
|
323
|
+
scope: string;
|
|
324
|
+
message: string;
|
|
325
|
+
}
|
|
326
|
+
export interface StyleContractDoc {
|
|
327
|
+
/** Stringified `RegExp` source, e.g. `"/^ns-.../"` -- parse with `parseRegexLiteral` before using. */
|
|
328
|
+
bemClassPattern: string;
|
|
329
|
+
utilityClassPattern: string;
|
|
330
|
+
rawStyleValuePattern: string;
|
|
331
|
+
rules: StyleContractRuleDoc[];
|
|
332
|
+
}
|
|
333
|
+
export interface NsSnapshot {
|
|
334
|
+
schemaVersion: number;
|
|
335
|
+
version: string;
|
|
336
|
+
generatedAt: string;
|
|
337
|
+
framework: NsFrameworkDoc;
|
|
338
|
+
packages: NsSnapshotPackages;
|
|
339
|
+
categories: string[];
|
|
340
|
+
families: FamilyDoc[];
|
|
341
|
+
components: ComponentDoc[];
|
|
342
|
+
services: ServiceDoc[];
|
|
343
|
+
providers: ProviderDoc[];
|
|
344
|
+
guides: GuideDoc[];
|
|
345
|
+
theme?: ThemeDoc;
|
|
346
|
+
styleContract?: StyleContractDoc;
|
|
347
|
+
stats: NsSnapshotStats;
|
|
348
|
+
__stub?: boolean;
|
|
349
|
+
}
|
package/lib/types.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshot types for the ng-solid MCP server.
|
|
3
|
+
*
|
|
4
|
+
* The snapshot is generated at build time by `scripts/prebuild.mjs` (Fase 2/3 del plan)
|
|
5
|
+
* from the design-system AST, the Arvisfy preset, the icon catalog and the docs registry.
|
|
6
|
+
* These types mirror the *actual* shape written to `data/design-system.json` /
|
|
7
|
+
* `data/icons.json` -- verified field-by-field against the real snapshot rather than
|
|
8
|
+
* assumed from the plan.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=types.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ng-solid/mcp",
|
|
3
|
+
"version": "22.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"description": "MCP server for the ng-solid Arvisfy design system — serves component, token, icon and style-contract metadata to AI agents.",
|
|
7
|
+
"author": "Ulises Familia Rodríguez <chicromedia@hotmail.com>",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/Solid-Techologies/ng-solid.git"
|
|
11
|
+
},
|
|
12
|
+
"private": false,
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"main": "./index.js",
|
|
17
|
+
"types": "./index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./index.d.ts",
|
|
21
|
+
"default": "./index.js"
|
|
22
|
+
},
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
25
|
+
"bin": {
|
|
26
|
+
"ng-solid-mcp": "./index.js"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"**/*.js",
|
|
30
|
+
"**/*.d.ts",
|
|
31
|
+
"data",
|
|
32
|
+
"skills",
|
|
33
|
+
"README.md",
|
|
34
|
+
"GUIDE.md"
|
|
35
|
+
],
|
|
36
|
+
"keywords": [
|
|
37
|
+
"ng-solid",
|
|
38
|
+
"mcp",
|
|
39
|
+
"model-context-protocol",
|
|
40
|
+
"design-system",
|
|
41
|
+
"arvisfy",
|
|
42
|
+
"angular"
|
|
43
|
+
],
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": "^24.0.0",
|
|
46
|
+
"pnpm": ">=11.22.0"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
50
|
+
"zod": "^4.1.13"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ns-solid-components
|
|
3
|
+
description: Reference for @ng-solid/design-system's public component catalog — parent/child composition rules, service-driven components, and internal subcomponents that are not part of the public API.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Catálogo de componentes de ng-solid
|
|
7
|
+
|
|
8
|
+
## Tabla generada
|
|
9
|
+
|
|
10
|
+
<!-- ns:component-list -->
|
|
11
|
+
|
|
12
|
+
Todos se importan desde `@ng-solid/design-system` (entry point único —
|
|
13
|
+
ver la skill `ns-solid-install`) y son standalone: deben declararse en
|
|
14
|
+
`imports: [...]` de quien los usa.
|
|
15
|
+
|
|
16
|
+
## Reglas de composición padre → hijo
|
|
17
|
+
|
|
18
|
+
Estos pares no son componentes independientes: el hijo sólo tiene sentido
|
|
19
|
+
proyectado dentro del padre, y ambos deben estar en `imports`:
|
|
20
|
+
|
|
21
|
+
| Padre | Hijo(s) | Notas |
|
|
22
|
+
| ---------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
23
|
+
| `ns-tabs` | `ns-tab` | Cada `ns-tab` es una pestaña; `ns-tabs` las descubre vía `contentChildren(NsTabComponent)`. |
|
|
24
|
+
| `ns-card` | `ns-card-header`, `ns-card-body`, `ns-card-footer` | Tres selectores adicionales del mismo fichero `card.component.ts`; son slots con nombre, no requieren los tres a la vez. |
|
|
25
|
+
| `ns-accordion` | `ns-accordion-item` | `ns-accordion` gestiona `expandedKeys` como `model()`; los items son sólo presentación + trigger. |
|
|
26
|
+
| `ns-dropdown` | `ns-dropdown-item` | `ns-dropdown-item` es una **directiva** (`NsDropdownItemDirective`), no un componente — se aplica sobre el elemento que hace de opción. |
|
|
27
|
+
| `ns-tag-group` | `ns-tag` | Agrupa chips seleccionables/removibles. |
|
|
28
|
+
| `ns-breadcrumbs` | `ns-breadcrumb-item` | Cada nivel de la ruta es un `ns-breadcrumb-item`. |
|
|
29
|
+
| `ns-radio-group` | `ns-radio` | `ns-radio-group` es el `ControlValueAccessor` (hereda de `NsValueAccessor`, ver `ns-solid-forms`); `ns-radio` por sí solo **no** implementa CVA. |
|
|
30
|
+
|
|
31
|
+
`ns-input-group` es sólo un wrapper de layout — no tiene `label` ni `wrap`;
|
|
32
|
+
los inputs de formulario y sus labels van en el `ns-input`/`ns-input-number`
|
|
33
|
+
proyectado dentro (`ns-input-group-addon` para prefijos/sufijos).
|
|
34
|
+
|
|
35
|
+
## Componentes que se usan vía servicio
|
|
36
|
+
|
|
37
|
+
Estos no se instancian con una etiqueta en el template del consumidor — se
|
|
38
|
+
abren/gestionan imperativamente inyectando el servicio correspondiente:
|
|
39
|
+
|
|
40
|
+
- **`NsToastService`** → produce instancias de `ns-toast` (y el
|
|
41
|
+
subcomponente interno `ns-toast-region` que las hostea).
|
|
42
|
+
- **`NsModalService`** → abre `ns-modal`, con contenido proyectado o un
|
|
43
|
+
`contentComponent` tipado.
|
|
44
|
+
- **`NsDrawerService`** → abre `ns-drawer`.
|
|
45
|
+
- **`NsScheduleService`** → coordina el estado del calendario detrás de
|
|
46
|
+
`ns-schedule` y sus celdas/eventos internos.
|
|
47
|
+
|
|
48
|
+
`NsThemeService` (ver `ns-solid-theming`) y `NsOverlayService` (posicionamiento,
|
|
49
|
+
focus trap, scroll lock, `inert` — usado internamente por
|
|
50
|
+
popover/tooltip/drawer/modal) también son servicios públicos, pero no
|
|
51
|
+
"envuelven" un componente de la misma forma.
|
|
52
|
+
|
|
53
|
+
## Subcomponentes internos — no exportados
|
|
54
|
+
|
|
55
|
+
Existen en el código pero **no** están en el barrel público
|
|
56
|
+
(`visibility: 'internal'` en el snapshot). Un agente no debe importarlos ni
|
|
57
|
+
sugerirlos; si un usuario pregunta por ellos, la respuesta correcta es
|
|
58
|
+
señalar el componente público que los engloba:
|
|
59
|
+
|
|
60
|
+
- `ns-schedule-cell`, `ns-schedule-day`, `ns-schedule-event`,
|
|
61
|
+
`ns-schedule-week`, `ns-schedule-workweek` → usar `ns-schedule`.
|
|
62
|
+
- `ns-date-picker-calendar` → usar `ns-date-picker` / `ns-date-range-picker`.
|
|
63
|
+
- `ns-toast-region` → se instancia sólo a través de `NsToastService`, nunca
|
|
64
|
+
directamente.
|
|
65
|
+
|
|
66
|
+
## Variantes visuales — lenguaje HeroUI
|
|
67
|
+
|
|
68
|
+
Los nombres de variante son consistentes entre componentes:
|
|
69
|
+
|
|
70
|
+
- `solid` — relleno de acción sólido.
|
|
71
|
+
- `soft` — versión plana (`flat` en HeroUI).
|
|
72
|
+
- `outline` — bordeada (`bordered` en HeroUI).
|
|
73
|
+
- `ghost` — ligera (`light` en HeroUI).
|
|
74
|
+
|
|
75
|
+
Cada familia posee sus propios tokens `--ns-component-<familia>-*` — no
|
|
76
|
+
tomes prestados los tokens de otro componente aunque el valor visual
|
|
77
|
+
coincida.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ns-solid-forms
|
|
3
|
+
description: Build forms with @ng-solid/design-system's ControlValueAccessor controls — formControlName/[(value)] bindings, the 14 inputs inherited from NsValueAccessor, and [nsField] wiring.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Formularios con ng-solid
|
|
7
|
+
|
|
8
|
+
## Controles CVA y sus inputs heredados
|
|
9
|
+
|
|
10
|
+
<!-- ns:form-controls -->
|
|
11
|
+
|
|
12
|
+
Todos implementan `ControlValueAccessor` extendiendo la clase base abstracta
|
|
13
|
+
`NsValueAccessor<T>`
|
|
14
|
+
(`packages/design-system/src/lib/form/models/value-accessor.ts`). Muchos
|
|
15
|
+
componentes (`NsInputComponent`, `NsSwitchComponent`, `NsCheckboxComponent`…)
|
|
16
|
+
tienen cuerpo de clase vacío — **toda** su API pública viene por herencia. Si
|
|
17
|
+
consultas la API de un control y ves 0 inputs propios, no significa que el
|
|
18
|
+
componente no acepte nada: revisa `inheritedFrom: 'NsValueAccessor'`.
|
|
19
|
+
|
|
20
|
+
`NsValueAccessor` expone, en todos los controles por igual:
|
|
21
|
+
|
|
22
|
+
- `type`, `placeholder`, `label`, `description`, `errorMessage`, `required`,
|
|
23
|
+
`invalid`, `readonly`, `name`, `ariaLabel`, `ariaDescribedBy` — `input()`.
|
|
24
|
+
- `size: 'sm' | 'md' | 'lg'` (default `'md'`), `color: 'neutral' | 'primary'
|
|
25
|
+
| 'success' | 'warning' | 'danger'` (default `'neutral'`), `variant:
|
|
26
|
+
'solid' | 'soft' | 'outline' | 'ghost'` (default `'outline'`) — `input()`.
|
|
27
|
+
- `disabled` — **`model()`**, no `input()`. Por eso `formControl.disable()` /
|
|
28
|
+
`.enable()` funciona de forma transparente: `setDisabledState` (parte del
|
|
29
|
+
contrato `ControlValueAccessor`) llama a `this.disabled.set(isDisabled)`
|
|
30
|
+
por debajo.
|
|
31
|
+
- `value` — también `model()`, escrito por Angular Forms vía `writeValue` y
|
|
32
|
+
leído/emitido por el propio control con `setValue()`.
|
|
33
|
+
|
|
34
|
+
## Bindings — los tres equivalentes
|
|
35
|
+
|
|
36
|
+
```html
|
|
37
|
+
<!-- Reactive Forms -->
|
|
38
|
+
<ns-input formControlName="email" label="Correo" />
|
|
39
|
+
|
|
40
|
+
<!-- Template-driven -->
|
|
41
|
+
<ns-input [(ngModel)]="email" label="Correo" />
|
|
42
|
+
|
|
43
|
+
<!-- Binding directo al model, sin FormsModule -->
|
|
44
|
+
<ns-input [(value)]="email" label="Correo" />
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Los tres funcionan porque el control implementa `ControlValueAccessor` (los
|
|
48
|
+
dos primeros) y expone `value` como `model()` (el tercero). Para
|
|
49
|
+
`disabled` como two-way binding equivalente: `[(disabled)]="isDisabled"`.
|
|
50
|
+
|
|
51
|
+
## `[nsField]` — vincular label, descripción y error
|
|
52
|
+
|
|
53
|
+
`NsFieldDirective` (selector `[nsField]`,
|
|
54
|
+
`packages/design-system/src/lib/a11y/field-context.ts`) provee un
|
|
55
|
+
`NsFieldContext` al subárbol. El control lo consume vía `inject(NsFieldContext,
|
|
56
|
+
{ optional: true, skipSelf: true })` para resolver su `id` real
|
|
57
|
+
(`controlId()`), y componentes como `NsDescriptionComponent` (hint) se
|
|
58
|
+
registran contra el mismo contexto para el `aria-describedby`:
|
|
59
|
+
|
|
60
|
+
```html
|
|
61
|
+
<div nsField="email">
|
|
62
|
+
<ns-input formControlName="email" label="Correo" />
|
|
63
|
+
<ns-description id="email-hint"
|
|
64
|
+
>Usaremos este correo para notificarte.</ns-description
|
|
65
|
+
>
|
|
66
|
+
</div>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Sin `[nsField]`, cada control genera su propio id vía `NsAriaIdService`; con
|
|
70
|
+
él, el id explícito (`nsField="email"`) gana y el `aria-describedby` se
|
|
71
|
+
compone automáticamente con las descripciones registradas en el contexto.
|
|
72
|
+
|
|
73
|
+
## `errorMessage` + `invalid`
|
|
74
|
+
|
|
75
|
+
El mensaje de error sólo se renderiza cuando **ambos** están presentes:
|
|
76
|
+
`invalid()` es `true` y `errorMessage()` no es vacío/undefined (ver
|
|
77
|
+
`input.component.html`: `@if (invalid() && errorMessage())`). Calcula
|
|
78
|
+
`invalid` desde el estado del `FormControl`, no lo dejes fijo:
|
|
79
|
+
|
|
80
|
+
```html
|
|
81
|
+
<ns-input
|
|
82
|
+
[invalid]="control.invalid && control.touched"
|
|
83
|
+
[errorMessage]="
|
|
84
|
+
control.hasError('required') ? 'Este campo es obligatorio' : undefined
|
|
85
|
+
"
|
|
86
|
+
/>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Ejemplo completo con `FormBuilder`
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { Component, inject } from '@angular/core';
|
|
93
|
+
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
94
|
+
import {
|
|
95
|
+
NsButtonComponent,
|
|
96
|
+
NsFieldDirective,
|
|
97
|
+
NsInputComponent,
|
|
98
|
+
NsPasswordComponent,
|
|
99
|
+
NsSwitchComponent
|
|
100
|
+
} from '@ng-solid/design-system';
|
|
101
|
+
|
|
102
|
+
@Component({
|
|
103
|
+
selector: 'app-signup-form',
|
|
104
|
+
standalone: true,
|
|
105
|
+
imports: [
|
|
106
|
+
ReactiveFormsModule,
|
|
107
|
+
NsFieldDirective,
|
|
108
|
+
NsInputComponent,
|
|
109
|
+
NsPasswordComponent,
|
|
110
|
+
NsSwitchComponent,
|
|
111
|
+
NsButtonComponent
|
|
112
|
+
],
|
|
113
|
+
template: `
|
|
114
|
+
<form [formGroup]="form" (ngSubmit)="submit()">
|
|
115
|
+
<div nsField="signup-email">
|
|
116
|
+
<ns-input
|
|
117
|
+
formControlName="email"
|
|
118
|
+
label="Correo"
|
|
119
|
+
type="email"
|
|
120
|
+
[invalid]="form.controls.email.invalid && form.controls.email.touched"
|
|
121
|
+
[errorMessage]="'Ingresa un correo válido'"
|
|
122
|
+
/>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<div nsField="signup-password">
|
|
126
|
+
<ns-password
|
|
127
|
+
formControlName="password"
|
|
128
|
+
label="Contraseña"
|
|
129
|
+
[invalid]="
|
|
130
|
+
form.controls.password.invalid && form.controls.password.touched
|
|
131
|
+
"
|
|
132
|
+
[errorMessage]="'Mínimo 8 caracteres'"
|
|
133
|
+
/>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<ns-switch formControlName="acceptTerms" label="Acepto los términos" />
|
|
137
|
+
|
|
138
|
+
<ns-button variant="primary" type="submit" [disabled]="form.invalid">
|
|
139
|
+
Crear cuenta
|
|
140
|
+
</ns-button>
|
|
141
|
+
</form>
|
|
142
|
+
`
|
|
143
|
+
})
|
|
144
|
+
export class SignupFormComponent {
|
|
145
|
+
private readonly fb = inject(FormBuilder);
|
|
146
|
+
|
|
147
|
+
readonly form = this.fb.group({
|
|
148
|
+
email: this.fb.control('', [Validators.required, Validators.email]),
|
|
149
|
+
password: this.fb.control('', [
|
|
150
|
+
Validators.required,
|
|
151
|
+
Validators.minLength(8)
|
|
152
|
+
]),
|
|
153
|
+
acceptTerms: this.fb.control(false, [Validators.requiredTrue])
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
submit(): void {
|
|
157
|
+
if (this.form.valid) {
|
|
158
|
+
// this.form.getRawValue()
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Nota: `NsPasswordComponent` añade `forgotLink` y `showAndHide` propios sobre
|
|
165
|
+
la base heredada; `NsSwitchComponent` no añade inputs propios (todo viene de
|
|
166
|
+
`NsValueAccessor`).
|