@mk-kit/mcp 0.49.0 → 0.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,6 +43,7 @@ Runs over stdio; nothing to host. Node ≥ 20.
43
43
  | `get_mk_kit_export` | Full API of one export as Markdown — import, selector, description + example, inputs, outputs, methods, docs link. Accepts class names or selectors. |
44
44
  | `list_mk_kit_exports` | Browse every entry point, or one entry point / kind, with one-line summaries. |
45
45
  | `get_mk_kit_overview` | What mk-kit is, install + setup conventions, links to every docs page (the site's `llms.txt`). |
46
+ | `scaffold_mk_kit` | Paste-ready, entity-aware code for the first patterns an app builds: `crud-schematic` (the `ng g @mk-kit/ui:crud` command), `table-page`, `dynamic-form`, `dialog`, `embed`. Takes `entity` and a `fields` spec (`"name!:string,price:currency,status:select=draft|published"`). |
46
47
 
47
48
  Resources: `mk-kit://llms.txt`, `mk-kit://llms-full.txt`, `mk-kit://api.json`.
48
49
 
package/data/api.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "@mk-kit/ui",
3
- "version": "0.49.0",
3
+ "version": "0.50.0",
4
4
  "site": "https://mk-kit.dev",
5
5
  "entries": [
6
6
  {
@@ -22670,6 +22670,18 @@
22670
22670
  "description": "CSS text adopted into every element's shadow root (and the overlay host).\nPass the mk-kit theme through `mkShadowCss` so its `:root` token\nblocks target `:host`; append your own widget CSS after it. Shared as\nconstructable stylesheets when the browser supports them (one parse for\nany number of instances), `<style>` elements otherwise.",
22671
22671
  "optional": true
22672
22672
  },
22673
+ {
22674
+ "name": "styleUrls",
22675
+ "type": "readonly string[] | undefined",
22676
+ "description": "Stylesheet URLs loaded as `<link rel=\"stylesheet\">` into every shadow\nroot (and the overlay host) — an alternative to inlining `styles` when\nthe theme lives on a CDN. Loaded per shadow root by the browser's cache,\nso the network cost is paid once.",
22677
+ "optional": true
22678
+ },
22679
+ {
22680
+ "name": "nonce",
22681
+ "type": "string | undefined",
22682
+ "description": "CSP nonce applied to every style and link element this app creates —\nincluding Angular's own component styles (provided as `CSP_NONCE`) and\nthe `<style>` fallback when constructable stylesheets are unavailable.\nFor host pages with a `style-src` policy that forbids `'unsafe-inline'`.",
22683
+ "optional": true
22684
+ },
22673
22685
  {
22674
22686
  "name": "providers",
22675
22687
  "type": "(Provider | EnvironmentProviders)[] | undefined",
@@ -1,4 +1,4 @@
1
- # @mk-kit/ui 0.49.0 — full API reference
1
+ # @mk-kit/ui 0.50.0 — full API reference
2
2
 
3
3
  Generated from the library sources. Browsable version: https://mk-kit.dev/api · JSON: https://mk-kit.dev/api.json · Guides: https://mk-kit.dev/llms.txt
4
4
 
@@ -15178,6 +15178,8 @@ Members:
15178
15178
  | Name | Type | Description |
15179
15179
  | --- | --- | --- |
15180
15180
  | `styles`? | `string \| readonly string[] \| undefined` | CSS text adopted into every element's shadow root (and the overlay host). Pass the mk-kit theme through `mkShadowCss` so its `:root` token blocks target `:host`; append your own widget CSS after it. Shared as constructable stylesheets when the browser supports them (one parse for any number of instances), `<style>` elements otherwise. |
15181
+ | `styleUrls`? | `readonly string[] \| undefined` | Stylesheet URLs loaded as `<link rel="stylesheet">` into every shadow root (and the overlay host) — an alternative to inlining `styles` when the theme lives on a CDN. Loaded per shadow root by the browser's cache, so the network cost is paid once. |
15182
+ | `nonce`? | `string \| undefined` | CSP nonce applied to every style and link element this app creates — including Angular's own component styles (provided as `CSP_NONCE`) and the `<style>` fallback when constructable stylesheets are unavailable. For host pages with a `style-src` policy that forbids `'unsafe-inline'`. |
15181
15183
  | `providers`? | `(Provider \| EnvironmentProviders)[] \| undefined` | Extra providers for the shared application — `provideMkI18n(…)`, `provideMkExtendedIcons()`, `provideHttpClient()`, your services. |
15182
15184
  | `overlays`? | `boolean \| undefined` | Mount mk-kit overlays (dialogs, anchored panels, toasts, tours) inside a page-level shadow host that carries the same `styles`, instead of bare `document.body`. Default `true`; set `false` to keep the application default (overlays styled by the page's own stylesheets). |
15183
15185
 
package/data/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # mk-kit
2
2
 
3
- > Themable, accessible Angular 22 component library for admin dashboards and UIs. Signals-based, WCAG 2.1 AA, controlled entirely through CSS variables with light/dark out of the box. — `@mk-kit/ui` 0.49.0. 170 components, 41 directives, 16 services and 144 helper functions across 29 tree-shakeable entry points. MIT licensed, zero runtime dependencies beyond Angular. Standalone components, signals, OnPush, zoneless-ready, SSR-safe, RTL and i18n via `provideMkI18n`.
3
+ > Themable, accessible Angular 22 component library for admin dashboards and UIs. Signals-based, WCAG 2.1 AA, controlled entirely through CSS variables with light/dark out of the box. — `@mk-kit/ui` 0.50.0. 170 components, 41 directives, 16 services and 144 helper functions across 29 tree-shakeable entry points. MIT licensed, zero runtime dependencies beyond Angular. Standalone components, signals, OnPush, zoneless-ready, SSR-safe, RTL and i18n via `provideMkI18n`.
4
4
 
5
5
  Install with `ng add @mk-kit/ui` (or `npm i @mk-kit/ui` + import `@mk-kit/ui/styles/mk-kit.css`). Import from the group entry points (`@mk-kit/ui/forms`, `@mk-kit/ui/table`, …) so each lazy chunk only carries what it uses; the root `@mk-kit/ui` entry re-exports everything. Every component is standalone: add the class to a component's `imports`. Theme with `--mk-*` CSS custom properties; `MkThemeService` switches light/dark/system and density.
6
6
 
package/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { z } from 'zod';
3
+ import { SCAFFOLD_RECIPES, scaffold } from './scaffolds.js';
4
+ export { SCAFFOLD_RECIPES, parseFields, scaffold } from './scaffolds.js';
3
5
  export const API_KINDS = [
4
6
  'component',
5
7
  'directive',
@@ -141,7 +143,8 @@ export function createMkKitServer(data) {
141
143
  instructions: `Reference for ${version}, the Angular component library (mk-kit.dev). ` +
142
144
  `Use search_mk_kit to find a component/directive/service/helper by name, selector or what it does, ` +
143
145
  `then get_mk_kit_export for its full API (inputs, outputs, methods, import path). ` +
144
- `Call get_mk_kit_overview once for install/setup conventions. ` +
146
+ `Call get_mk_kit_overview once for install/setup conventions, and scaffold_mk_kit for paste-ready ` +
147
+ `starting points (CRUD slice, table page, dynamic form, dialogs, custom-element embedding). ` +
145
148
  `Every component is standalone: import the class from the entry point shown and add it to \`imports\`.`,
146
149
  });
147
150
  server.registerTool('search_mk_kit', {
@@ -218,6 +221,28 @@ export function createMkKitServer(data) {
218
221
  inputSchema: {},
219
222
  annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
220
223
  }, () => ({ content: [{ type: 'text', text: data.llms }] }));
224
+ server.registerTool('scaffold_mk_kit', {
225
+ title: 'Scaffold an mk-kit pattern',
226
+ description: `Paste-ready ${version} code for the patterns apps build first — pass a recipe and optionally the entity ` +
227
+ `name and fields. Recipes: crud-schematic (the ng g @mk-kit/ui:crud command that generates a whole slice — ` +
228
+ `prefer it when the user wants list + form + service), table-page (mk-table + MkTableDataSource against a ` +
229
+ `REST endpoint), dynamic-form (schema-driven mk-dynamic-form), dialog (MkDialogService confirm + custom ` +
230
+ `dialog), embed (ship a component as a shadow-DOM custom element). Field grammar: "key:type" comma-separated, ` +
231
+ `"!" marks required, selects list options — "name!:string,price:currency,status:select=draft|published".`,
232
+ inputSchema: {
233
+ recipe: z.enum([...SCAFFOLD_RECIPES]).describe('Which pattern to scaffold'),
234
+ entity: z.string().optional().describe('Singular entity name, e.g. "product" or "OrderLine" (default "item")'),
235
+ fields: z.string().optional().describe('Field spec, e.g. "name!:string,price:currency" (default "name!:string")'),
236
+ },
237
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
238
+ }, ({ recipe, entity, fields }) => {
239
+ try {
240
+ return { content: [{ type: 'text', text: scaffold({ recipe, entity, fields }) }] };
241
+ }
242
+ catch (err) {
243
+ return { content: [{ type: 'text', text: String(err instanceof Error ? err.message : err) }], isError: true };
244
+ }
245
+ });
221
246
  server.registerResource('llms.txt', 'mk-kit://llms.txt', { title: 'mk-kit llms.txt', description: 'Docs index for LLMs', mimeType: 'text/markdown' }, (uri) => ({ contents: [{ uri: uri.href, mimeType: 'text/markdown', text: data.llms }] }));
222
247
  server.registerResource('llms-full.txt', 'mk-kit://llms-full.txt', { title: 'mk-kit full API reference', description: 'Every export as Markdown', mimeType: 'text/markdown' }, (uri) => ({ contents: [{ uri: uri.href, mimeType: 'text/markdown', text: data.llmsFull }] }));
223
248
  server.registerResource('api.json', 'mk-kit://api.json', { title: 'mk-kit api.json', description: 'Machine-readable API surface', mimeType: 'application/json' }, (uri) => ({ contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(api) }] }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mk-kit/mcp",
3
- "version": "0.49.0",
3
+ "version": "0.50.0",
4
4
  "description": "MCP server for @mk-kit/ui — lets AI coding assistants look up every component, directive, service and helper of the mk-kit Angular library (inputs, outputs, methods, import paths, docs links).",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/scaffolds.js ADDED
@@ -0,0 +1,368 @@
1
+ /**
2
+ * Recipes for the `scaffold_mk_kit` tool: ready-to-paste, entity-aware code
3
+ * for the patterns people build first with mk-kit. Pure functions over the
4
+ * inputs — no I/O — so they are unit-testable and never drift silently: the
5
+ * snippets mirror what `ng g @mk-kit/ui:crud` generates and what the docs
6
+ * pages show.
7
+ */
8
+ export const SCAFFOLD_RECIPES = ['crud-schematic', 'table-page', 'dynamic-form', 'dialog', 'embed'];
9
+ const FIELD_TYPES = [
10
+ 'string', 'textarea', 'email', 'url', 'number', 'currency',
11
+ 'boolean', 'date', 'datetime', 'select', 'tags',
12
+ ];
13
+ /* ---------------------------------------------------------------- naming */
14
+ function words(name) {
15
+ return name
16
+ .replace(/([a-z\d])([A-Z])/g, '$1 $2')
17
+ .split(/[\s_-]+/)
18
+ .filter(Boolean)
19
+ .map((w) => w.toLowerCase());
20
+ }
21
+ function classify(name) {
22
+ return words(name).map((w) => w[0].toUpperCase() + w.slice(1)).join('');
23
+ }
24
+ function camelize(name) {
25
+ const c = classify(name);
26
+ return c ? c[0].toLowerCase() + c.slice(1) : c;
27
+ }
28
+ function humanize(key) {
29
+ const ws = words(key);
30
+ return ws.length ? [ws[0][0].toUpperCase() + ws[0].slice(1), ...ws.slice(1)].join(' ') : key;
31
+ }
32
+ function plural(word) {
33
+ if (/(s|x|z|ch|sh)$/i.test(word))
34
+ return `${word}es`;
35
+ if (/[^aeiou]y$/i.test(word))
36
+ return `${word.slice(0, -1)}ies`;
37
+ return `${word}s`;
38
+ }
39
+ /* ---------------------------------------------------------- field grammar */
40
+ /**
41
+ * Parse the crud-schematic field grammar: comma-separated `key:type`, `!`
42
+ * after the key marks it required, selects list options after `=` —
43
+ * `name!:string,price:currency,status:select=draft|published`. Throws with a
44
+ * correctable message on bad input.
45
+ */
46
+ export function parseFields(spec) {
47
+ const fields = spec
48
+ .split(',')
49
+ .map((s) => s.trim())
50
+ .filter(Boolean)
51
+ .map((segment) => {
52
+ const m = /^([A-Za-z][A-Za-z\d_-]*)(!?)(?::([A-Za-z-]+)(?:=(.+))?)?$/.exec(segment);
53
+ if (!m)
54
+ throw new Error(`Cannot parse field "${segment}". Write "key:type", "key!:type" or "key:select=a|b|c".`);
55
+ const [, rawKey, bang, rawType = 'string', rawOptions] = m;
56
+ const type = rawType.toLowerCase();
57
+ if (!FIELD_TYPES.includes(type)) {
58
+ throw new Error(`Unknown field type "${rawType}" in "${segment}". Valid: ${FIELD_TYPES.join(', ')}.`);
59
+ }
60
+ const options = rawOptions ? rawOptions.split('|').map((o) => o.trim()).filter(Boolean) : [];
61
+ if (type === 'select' && !options.length) {
62
+ throw new Error(`Field "${rawKey}" is a select but lists no options — write "${rawKey}:select=draft|published".`);
63
+ }
64
+ const key = camelize(rawKey);
65
+ return { key, type, required: bang === '!', options, label: humanize(key) };
66
+ });
67
+ if (!fields.length)
68
+ throw new Error('At least one field is required, e.g. "name!:string".');
69
+ return fields;
70
+ }
71
+ function tsType(f) {
72
+ switch (f.type) {
73
+ case 'number':
74
+ case 'currency':
75
+ return 'number';
76
+ case 'boolean':
77
+ return 'boolean';
78
+ case 'date':
79
+ case 'datetime':
80
+ return 'Date | null';
81
+ case 'select':
82
+ return f.options.map((o) => `'${o}'`).join(' | ');
83
+ case 'tags':
84
+ return 'string[]';
85
+ default:
86
+ return 'string';
87
+ }
88
+ }
89
+ function dynamicType(f) {
90
+ if (f.type === 'string')
91
+ return 'text';
92
+ if (f.type === 'boolean')
93
+ return 'switch';
94
+ return f.type;
95
+ }
96
+ /** Render one recipe as paste-ready Markdown. Throws on invalid input. */
97
+ export function scaffold(input) {
98
+ const entity = input.entity ?? 'item';
99
+ if (!/^[A-Za-z][A-Za-z\d_-]*$/.test(entity)) {
100
+ throw new Error(`"${entity}" is not a valid entity name — letters/digits like "product" or "OrderLine".`);
101
+ }
102
+ const fields = parseFields(input.fields ?? 'name!:string');
103
+ const n = {
104
+ cls: classify(entity),
105
+ prop: camelize(entity),
106
+ human: words(entity).join(' '),
107
+ plural: plural(camelize(entity)),
108
+ pluralFile: plural(words(entity).join('-')),
109
+ };
110
+ switch (input.recipe) {
111
+ case 'crud-schematic':
112
+ return crudSchematic(n, input.fields ?? 'name!:string');
113
+ case 'table-page':
114
+ return tablePage(n, fields);
115
+ case 'dynamic-form':
116
+ return dynamicForm(n, fields);
117
+ case 'dialog':
118
+ return dialog(n);
119
+ case 'embed':
120
+ return embed(n);
121
+ }
122
+ }
123
+ function crudSchematic(n, fieldSpec) {
124
+ return `## Generate a complete CRUD slice
125
+
126
+ mk-kit ships a generator that produces a working admin slice — run it instead
127
+ of hand-writing the pattern:
128
+
129
+ \`\`\`bash
130
+ ng g @mk-kit/ui:crud ${n.prop} --fields "${fieldSpec}"
131
+ \`\`\`
132
+
133
+ It creates \`src/app/${n.pluralFile}/\` with:
134
+
135
+ | File | Contents |
136
+ |---|---|
137
+ | \`${words(n.cls).join('-')}.model.ts\` | interface + \`MkTableColumn[]\` + \`MkDynamicSchema\` — one source of truth |
138
+ | \`${words(n.cls).join('-')}.service.ts\` | list/get/create/update/remove — in-memory (runs immediately); \`--api /api/${n.pluralFile}\` for HttpClient |
139
+ | \`${words(n.cls).join('-')}-list-page.ts\` | \`mk-table\` + \`MkTableDataSource\`, search, sort, pagination, delete confirm |
140
+ | \`${words(n.cls).join('-')}-form-page.ts\` | \`mk-dynamic-form\` for /new and /:id/edit |
141
+ | \`${n.pluralFile}.routes.ts\` | lazy routes, auto-wired into \`app.routes.ts\` |
142
+ | \`${n.pluralFile}.spec.ts\` | harness-driven tests (\`--no-spec\` to skip) |
143
+
144
+ Field grammar: \`key:type\` (comma-separated), \`!\` = required,
145
+ \`select=a|b|c\` for options. Types: ${FIELD_TYPES.join(', ')}.
146
+ Other options: \`--plural\`, \`--path\`, \`--project\`, \`--route=false\`.
147
+ Guide: https://mk-kit.dev/crud`;
148
+ }
149
+ function tablePage(n, fields) {
150
+ const columns = fields
151
+ .map((f) => {
152
+ const extra = f.type === 'number' || f.type === 'currency'
153
+ ? `, align: 'end'`
154
+ : f.type === 'boolean'
155
+ ? `, format: (v) => (v ? 'Yes' : 'No')`
156
+ : '';
157
+ return ` { key: '${f.key}', header: '${f.label}', sortable: true${extra} },`;
158
+ })
159
+ .join('\n');
160
+ const iface = fields.map((f) => ` ${f.key}: ${tsType(f)};`).join('\n');
161
+ return `## Server-driven table page for ${n.human}
162
+
163
+ \`MkTableDataSource\` is the page/sort/filter plumbing: give it a fetcher and
164
+ bind its signals. Stale responses never overwrite newer state, \`setFilter\`
165
+ is debounced, and rows persist while loading (the table never blanks).
166
+
167
+ \`\`\`ts
168
+ import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
169
+ import { HttpClient } from '@angular/common/http';
170
+ import { MkInput } from '@mk-kit/ui/forms';
171
+ import { MkPagination } from '@mk-kit/ui/navigation';
172
+ import { MkTable, MkTableDataSource, type MkDataPage, type MkTableColumn } from '@mk-kit/ui/table';
173
+
174
+ interface ${n.cls} {
175
+ id: string;
176
+ ${iface}
177
+ }
178
+
179
+ @Component({
180
+ selector: 'app-${n.pluralFile}-page',
181
+ imports: [MkInput, MkPagination, MkTable],
182
+ changeDetection: ChangeDetectionStrategy.OnPush,
183
+ template: \`
184
+ <input
185
+ mkInput
186
+ type="search"
187
+ placeholder="Search ${n.plural}…"
188
+ (input)="ds.setFilter($any($event.target).value)"
189
+ />
190
+
191
+ <mk-table [columns]="columns" [data]="ds.rows()" (sortChange)="ds.setSort($event)" />
192
+ @if (ds.error()) {
193
+ <p role="alert">Loading failed.</p>
194
+ } @else if (ds.empty()) {
195
+ <p>No ${n.plural} yet.</p>
196
+ }
197
+
198
+ <mk-pagination
199
+ [total]="ds.total()"
200
+ [pageSize]="ds.pageSize()"
201
+ [page]="ds.page()"
202
+ (pageChange)="ds.setPage($event)"
203
+ />
204
+ \`,
205
+ })
206
+ export class ${n.cls}ListPage {
207
+ private readonly http = inject(HttpClient);
208
+
209
+ protected readonly columns: MkTableColumn<${n.cls}>[] = [
210
+ ${columns}
211
+ ];
212
+
213
+ protected readonly ds = new MkTableDataSource<${n.cls}>((req) =>
214
+ this.http.get<MkDataPage<${n.cls}>>('/api/${n.pluralFile}', {
215
+ params: {
216
+ page: req.page,
217
+ pageSize: req.pageSize,
218
+ ...(req.filter && { filter: req.filter }),
219
+ ...(req.sort && { sort: \`\${req.sort.active},\${req.sort.direction}\` }),
220
+ },
221
+ }),
222
+ );
223
+ }
224
+ \`\`\`
225
+
226
+ The server answers \`{ rows: ${n.cls}[], total: number }\`. Custom cells:
227
+ project \`<ng-template mkTableCell="key" let-value let-row="row">\` (import
228
+ \`MkTableCell\`). Refresh after a mutation with \`ds.refresh()\`.
229
+ Docs: https://mk-kit.dev/components/table`;
230
+ }
231
+ function dynamicForm(n, fields) {
232
+ const schemaFields = fields
233
+ .map((f) => {
234
+ const parts = [`key: '${f.key}'`, `type: '${dynamicType(f)}'`, `label: '${f.label}'`];
235
+ if (f.required)
236
+ parts.push('required: true');
237
+ if (f.type === 'select') {
238
+ parts.push(`options: [${f.options.map((o) => `{ label: '${humanize(o)}', value: '${o}' }`).join(', ')}]`);
239
+ }
240
+ return ` { ${parts.join(', ')} },`;
241
+ })
242
+ .join('\n');
243
+ return `## Schema-driven form for ${n.human}
244
+
245
+ \`mk-dynamic-form\` renders a whole form from data — fields, validators,
246
+ layout, conditions. Project your own action buttons; \`(formSubmit)\` fires
247
+ only when valid (\`(invalidSubmit)\` marks everything touched).
248
+
249
+ \`\`\`ts
250
+ import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
251
+ import { MkButton } from '@mk-kit/ui/button';
252
+ import { MkDynamicForm, type MkDynamicSchema } from '@mk-kit/ui/dynamic-form';
253
+
254
+ @Component({
255
+ selector: 'app-${words(n.cls).join('-')}-form',
256
+ imports: [MkButton, MkDynamicForm],
257
+ changeDetection: ChangeDetectionStrategy.OnPush,
258
+ template: \`
259
+ <mk-dynamic-form [schema]="schema" [(value)]="value" (formSubmit)="save($event)">
260
+ <button mkButton type="submit">Save</button>
261
+ </mk-dynamic-form>
262
+ \`,
263
+ })
264
+ export class ${n.cls}Form {
265
+ protected readonly value = signal<Record<string, unknown>>({});
266
+
267
+ protected readonly schema: MkDynamicSchema = {
268
+ columns: 2,
269
+ fields: [
270
+ ${schemaFields}
271
+ ],
272
+ };
273
+
274
+ protected save(value: Record<string, unknown>): void {
275
+ console.log('valid submit', value);
276
+ }
277
+ }
278
+ \`\`\`
279
+
280
+ Also available: \`group\`/\`array\`/\`section\` field kinds, \`showWhen\`
281
+ conditions, \`span\`, custom renderers via \`ng-template[mkDynamicField]\`,
282
+ and \`mkDynamicFormToSignalSchema\` for Signal Forms.
283
+ Docs: https://mk-kit.dev/components/dynamic-form`;
284
+ }
285
+ function dialog(n) {
286
+ return `## Dialogs for ${n.human}
287
+
288
+ \`MkDialogService\` opens components (returns a typed ref) and ships a
289
+ \`confirm()\` convenience. No modules, no boilerplate:
290
+
291
+ \`\`\`ts
292
+ import { Component, inject } from '@angular/core';
293
+ import { MK_OVERLAY_DATA, MkOverlayRef } from '@mk-kit/ui/core';
294
+ import { MkButton } from '@mk-kit/ui/button';
295
+ import { MkDialogService, MkToastService } from '@mk-kit/ui/feedback';
296
+
297
+ // Anywhere in the app — a danger confirm:
298
+ export class ${n.cls}Actions {
299
+ private readonly dialog = inject(MkDialogService);
300
+ private readonly toasts = inject(MkToastService);
301
+
302
+ async remove(name: string): Promise<void> {
303
+ const confirmed = await this.dialog.confirm({
304
+ title: \`Delete "\${name}"?\`,
305
+ message: 'This cannot be undone.',
306
+ confirmText: 'Delete',
307
+ tone: 'danger',
308
+ });
309
+ if (!confirmed) return;
310
+ // …delete…
311
+ this.toasts.success('${n.cls} deleted');
312
+ }
313
+
314
+ async edit(item: unknown): Promise<void> {
315
+ const ref = this.dialog.open<Edit${n.cls}Dialog, unknown>(Edit${n.cls}Dialog, { data: item });
316
+ const result = await ref.afterClosed;
317
+ if (result) this.toasts.success('Saved');
318
+ }
319
+ }
320
+
321
+ // The dialog component reads its data and closes itself via the ref:
322
+ @Component({
323
+ selector: 'app-edit-${words(n.cls).join('-')}-dialog',
324
+ imports: [MkButton],
325
+ template: \`
326
+ <h2>Edit ${n.human}</h2>
327
+ <button mkButton (click)="ref.close(true)">Save</button>
328
+ <button mkButton variant="ghost" (click)="ref.close()">Cancel</button>
329
+ \`,
330
+ })
331
+ export class Edit${n.cls}Dialog {
332
+ protected readonly data = inject(MK_OVERLAY_DATA);
333
+ protected readonly ref = inject(MkOverlayRef);
334
+ }
335
+ \`\`\`
336
+
337
+ \`confirm\` resolves \`true\`/\`false\`; \`open\` takes \`size\`,
338
+ \`hasBackdrop\`, \`autoFocus\`… and \`mk-dialog\` supports \`draggable\` /
339
+ \`resizable\`. Docs: https://mk-kit.dev/components/dialogs`;
340
+ }
341
+ function embed(n) {
342
+ return `## Ship ${n.human} widgets as custom elements
343
+
344
+ \`@mk-kit/ui/embed\` renders mk-kit components behind shadow DOM on pages you
345
+ don't control — host CSS can't break them, \`--mk-*\` tokens still theme them.
346
+
347
+ \`\`\`ts
348
+ import { mkEmbed, mkShadowCss } from '@mk-kit/ui/embed';
349
+ import themeCss from '@mk-kit/ui/styles.css' with { type: 'text' };
350
+
351
+ mkEmbed({
352
+ styles: mkShadowCss(themeCss), // :root token blocks retargeted to :host
353
+ // styleUrls: ['https://cdn.example.com/theme.css'], // CDN alternative
354
+ // nonce: document.currentScript?.nonce, // CSP host pages
355
+ // providers: [provideMkI18n({ … })],
356
+ }).element('acme-${words(n.cls).join('-')}', ${n.cls}Widget);
357
+ \`\`\`
358
+
359
+ \`\`\`html
360
+ <acme-${words(n.cls).join('-')} some-input="42" style="--mk-primary: #7c3aed"></acme-${words(n.cls).join('-')}>
361
+ \`\`\`
362
+
363
+ Inputs become dash-cased attributes (transforms coerce) and element
364
+ properties; outputs bubble as composed CustomEvents; overlays (dialogs,
365
+ selects, toasts) mount in a themed shadow host, not the bare page.
366
+ \`el.mkReady\` resolves once rendered; \`mkComponent\` exposes the instance.
367
+ Docs: https://mk-kit.dev/embed`;
368
+ }