inai-react-components 1.6.2 → 2.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/dist/commands/add.d.ts +7 -1
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +126 -0
- package/dist/commands/demo.d.ts +20 -0
- package/dist/commands/demo.d.ts.map +1 -0
- package/dist/commands/demo.js +15 -0
- package/dist/commands/generate.d.ts +47 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +204 -0
- package/dist/commands/init.d.ts +17 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +100 -2
- package/dist/commands/list.js +1 -1
- package/dist/commands/manifest.d.ts +9 -0
- package/dist/commands/manifest.d.ts.map +1 -0
- package/dist/commands/manifest.js +89 -0
- package/dist/commands/mcp.d.ts +208 -0
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +511 -0
- package/dist/commands/status.d.ts +30 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/theme.d.ts +32 -0
- package/dist/commands/theme.d.ts.map +1 -1
- package/dist/commands/theme.js +113 -0
- package/dist/index.js +45 -3
- package/dist/schemas/init-config.schema.json +77 -0
- package/dist/schemas/registry-item.schema.json +9 -0
- package/dist/utils/registry-resolver.d.ts +4 -0
- package/dist/utils/registry-resolver.d.ts.map +1 -1
- package/dist/utils/registry-resolver.js +35 -2
- package/dist/utils/themes.d.ts.map +1 -1
- package/dist/utils/themes.js +7 -0
- package/package.json +1 -1
package/dist/commands/mcp.js
CHANGED
|
@@ -3,11 +3,13 @@ import path from "node:path";
|
|
|
3
3
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
5
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
6
7
|
import { runList } from "./list.js";
|
|
7
8
|
import { runDiff } from "./diff.js";
|
|
8
9
|
import { runAdd } from "./add.js";
|
|
9
10
|
import { runUpdate } from "./update.js";
|
|
10
11
|
import { runStatus } from "./status.js";
|
|
12
|
+
import { runThemeFromHex } from "./theme.js";
|
|
11
13
|
import { THEMES } from "../utils/themes.js";
|
|
12
14
|
import { resolveRegistryDir } from "../utils/registry-resolver.js";
|
|
13
15
|
import { readRegistryJson } from "./status.js";
|
|
@@ -75,6 +77,39 @@ export async function handleViewComponent(args, cwd) {
|
|
|
75
77
|
}
|
|
76
78
|
return textResult(sections.join("\n"));
|
|
77
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Read the prop schema for a component from the registry's generated
|
|
82
|
+
* `props-data.json` (produced by `scripts/extract-props.ts`). Returns
|
|
83
|
+
* the schema as JSON text so AI assistants can validate props before
|
|
84
|
+
* generating JSX.
|
|
85
|
+
*/
|
|
86
|
+
export async function handleViewComponentSchema(args, cwd) {
|
|
87
|
+
const name = args?.name;
|
|
88
|
+
if (!name)
|
|
89
|
+
return textResult("Error: 'name' argument is required.", true);
|
|
90
|
+
const registryDir = resolveRegistryDir(cwd);
|
|
91
|
+
const propsDataPath = path.join(registryDir, "apps", "docs", "src", "generated", "props-data.json");
|
|
92
|
+
if (!fs.existsSync(propsDataPath)) {
|
|
93
|
+
return textResult(`Error: props-data.json not found at ${propsDataPath}. Run \`pnpm run extract-props\` in the registry repo to generate it.`, true);
|
|
94
|
+
}
|
|
95
|
+
let propsData;
|
|
96
|
+
try {
|
|
97
|
+
propsData = JSON.parse(fs.readFileSync(propsDataPath, "utf-8"));
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
101
|
+
return textResult(`Error: props-data.json is malformed: ${msg}`, true);
|
|
102
|
+
}
|
|
103
|
+
const componentSchema = propsData[name];
|
|
104
|
+
if (!componentSchema) {
|
|
105
|
+
const available = Object.keys(propsData).slice(0, 20).join(", ");
|
|
106
|
+
return textResult(`Error: No schema found for "${name}". First 20 available: ${available}, ...`, true);
|
|
107
|
+
}
|
|
108
|
+
return textResult(JSON.stringify({
|
|
109
|
+
name,
|
|
110
|
+
props: componentSchema.props,
|
|
111
|
+
}, null, 2));
|
|
112
|
+
}
|
|
78
113
|
export async function handleAddComponent(args, cwd) {
|
|
79
114
|
const name = args?.name;
|
|
80
115
|
if (!name)
|
|
@@ -158,6 +193,351 @@ export async function handleStatus(args, cwd) {
|
|
|
158
193
|
const output = await runStatus(cwd, { json: args?.json ?? true });
|
|
159
194
|
return textResult(output);
|
|
160
195
|
}
|
|
196
|
+
function findWorkspaceRoot(cwd) {
|
|
197
|
+
let dir = cwd;
|
|
198
|
+
for (let i = 0; i < 8; i++) {
|
|
199
|
+
if (fs.existsSync(path.join(dir, "pnpm-workspace.yaml")))
|
|
200
|
+
return dir;
|
|
201
|
+
const parent = path.dirname(dir);
|
|
202
|
+
if (parent === dir)
|
|
203
|
+
break;
|
|
204
|
+
dir = parent;
|
|
205
|
+
}
|
|
206
|
+
return cwd;
|
|
207
|
+
}
|
|
208
|
+
export function parseMoonshotsFile(sourceText) {
|
|
209
|
+
const results = [];
|
|
210
|
+
// Each moonshot entry is a top-level object literal inside the `moonshots`
|
|
211
|
+
// array. We split on the `{ id: N,` header then read the key/value lines.
|
|
212
|
+
const blocks = sourceText.split(/\{\s*id:\s*/).slice(1);
|
|
213
|
+
for (const block of blocks) {
|
|
214
|
+
const idMatch = block.match(/^(\d+)\s*,/);
|
|
215
|
+
if (!idMatch)
|
|
216
|
+
continue;
|
|
217
|
+
const id = Number.parseInt(idMatch[1], 10);
|
|
218
|
+
const slug = block.match(/slug:\s*"([^"]+)"/)?.[1] ?? "";
|
|
219
|
+
const title = block.match(/title:\s*"([^"]+)"/)?.[1] ?? "";
|
|
220
|
+
const description = block.match(/description:\s*\n?\s*"([^"]+)"/)?.[1] ?? "";
|
|
221
|
+
const status = block.match(/status:\s*"([^"]+)"/)?.[1] ?? "";
|
|
222
|
+
const category = block.match(/category:\s*"([^"]+)"/)?.[1];
|
|
223
|
+
const demoPath = block.match(/demoPath:\s*"([^"]+)"/)?.[1];
|
|
224
|
+
const reducedMotionStrategy = block.match(/reducedMotionStrategy:\s*\n?\s*"([^"]+)"/)?.[1];
|
|
225
|
+
const componentsRaw = block.match(/components:\s*\[([^\]]*)\]/)?.[1] ?? "";
|
|
226
|
+
const components = componentsRaw
|
|
227
|
+
.split(",")
|
|
228
|
+
.map((s) => s.trim().replace(/^"|"$/g, ""))
|
|
229
|
+
.filter(Boolean);
|
|
230
|
+
results.push({
|
|
231
|
+
id,
|
|
232
|
+
slug,
|
|
233
|
+
title,
|
|
234
|
+
description,
|
|
235
|
+
status,
|
|
236
|
+
components,
|
|
237
|
+
...(category ? { category } : {}),
|
|
238
|
+
...(demoPath ? { demoPath } : {}),
|
|
239
|
+
...(reducedMotionStrategy ? { reducedMotionStrategy } : {}),
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
return results;
|
|
243
|
+
}
|
|
244
|
+
export async function handleListMoonshots(args, cwd) {
|
|
245
|
+
const root = findWorkspaceRoot(cwd);
|
|
246
|
+
const moonshotsPath = path.join(root, "packages/ui/src/lib/moonshots.ts");
|
|
247
|
+
if (!fs.existsSync(moonshotsPath)) {
|
|
248
|
+
return textResult(`Error: moonshots.ts not found at ${moonshotsPath}. Run from inside the InAI UI workspace.`, true);
|
|
249
|
+
}
|
|
250
|
+
const src = fs.readFileSync(moonshotsPath, "utf-8");
|
|
251
|
+
let moonshots = parseMoonshotsFile(src);
|
|
252
|
+
if (args?.status) {
|
|
253
|
+
moonshots = moonshots.filter((m) => m.status === args.status);
|
|
254
|
+
}
|
|
255
|
+
if (args?.category) {
|
|
256
|
+
moonshots = moonshots.filter((m) => m.category === args.category);
|
|
257
|
+
}
|
|
258
|
+
return textResult(JSON.stringify(moonshots, null, 2));
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Ranks blocks by keyword overlap between the user's brief and each
|
|
262
|
+
* block's name + description. Intentionally simple — the point is to
|
|
263
|
+
* surface 5–8 relevant candidates the agent can read in full via
|
|
264
|
+
* `view_component`, not to replace a proper embedding search.
|
|
265
|
+
*/
|
|
266
|
+
function scoreBlock(query, text) {
|
|
267
|
+
const terms = query
|
|
268
|
+
.toLowerCase()
|
|
269
|
+
.split(/[^a-z0-9]+/)
|
|
270
|
+
.filter((t) => t.length >= 3);
|
|
271
|
+
if (terms.length === 0)
|
|
272
|
+
return 0;
|
|
273
|
+
const haystack = text.toLowerCase();
|
|
274
|
+
let score = 0;
|
|
275
|
+
for (const term of terms) {
|
|
276
|
+
if (haystack.includes(term))
|
|
277
|
+
score += 1;
|
|
278
|
+
// Bonus for exact word match.
|
|
279
|
+
const wordRegex = new RegExp(`\\b${term}\\b`);
|
|
280
|
+
if (wordRegex.test(haystack))
|
|
281
|
+
score += 1;
|
|
282
|
+
}
|
|
283
|
+
return score;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Pascalize a kebab/snake block slug so we can use it as an import
|
|
287
|
+
* identifier in the generated file (e.g. `signature-pricing-table-morph`
|
|
288
|
+
* → `SignaturePricingTableMorph`).
|
|
289
|
+
*/
|
|
290
|
+
function pascalizeSlug(slug) {
|
|
291
|
+
return slug
|
|
292
|
+
.split(/[-_]/)
|
|
293
|
+
.filter(Boolean)
|
|
294
|
+
.map((part) => part[0].toUpperCase() + part.slice(1))
|
|
295
|
+
.join("");
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Given a brief and a target path, compose a React `.tsx` page that
|
|
299
|
+
* imports the top-matching blocks from the local registry and renders
|
|
300
|
+
* them in order. Used by the MCP `scaffold_page` tool so an agent can
|
|
301
|
+
* drop a proposal page into the current project during a live client
|
|
302
|
+
* demo without hand-typing import boilerplate.
|
|
303
|
+
*
|
|
304
|
+
* Exported for unit tests; the CLI command wraps it.
|
|
305
|
+
*/
|
|
306
|
+
export function scaffoldPageSource(brief, blocks) {
|
|
307
|
+
const lines = [
|
|
308
|
+
`// Generated by InAI UI MCP scaffold_page for brief:`,
|
|
309
|
+
`// "${brief.replace(/\n/g, " ")}"`,
|
|
310
|
+
`// Edit freely — the generator does not write this file again unless`,
|
|
311
|
+
`// you delete it.`,
|
|
312
|
+
``,
|
|
313
|
+
];
|
|
314
|
+
for (const block of blocks) {
|
|
315
|
+
const pascal = pascalizeSlug(block.name);
|
|
316
|
+
lines.push(`import ${pascal} from "./blocks/${block.name}/page";`);
|
|
317
|
+
}
|
|
318
|
+
lines.push("");
|
|
319
|
+
lines.push("export default function GeneratedPage() {");
|
|
320
|
+
lines.push(" return (");
|
|
321
|
+
lines.push(` <div className="flex flex-col">`);
|
|
322
|
+
for (const block of blocks) {
|
|
323
|
+
const pascal = pascalizeSlug(block.name);
|
|
324
|
+
lines.push(` <${pascal} />`);
|
|
325
|
+
}
|
|
326
|
+
lines.push(" </div>");
|
|
327
|
+
lines.push(" );");
|
|
328
|
+
lines.push("}");
|
|
329
|
+
lines.push("");
|
|
330
|
+
return lines.join("\n");
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Wrap the CLI's `runThemeFromHex` so an agent can generate a layered
|
|
334
|
+
* brand override from a single hex value during a live demo. Writes
|
|
335
|
+
* the CSS layer under `packages/tokens/src/themes/<name>.css` (when
|
|
336
|
+
* invoked from the monorepo root) or a project-local themes dir.
|
|
337
|
+
*/
|
|
338
|
+
export async function handleGenerateTheme(args, cwd) {
|
|
339
|
+
const name = args?.name?.trim();
|
|
340
|
+
const hex = args?.hex?.trim();
|
|
341
|
+
if (!name) {
|
|
342
|
+
return textResult("Error: 'name' argument is required.", true);
|
|
343
|
+
}
|
|
344
|
+
if (!hex) {
|
|
345
|
+
return textResult("Error: 'hex' argument is required.", true);
|
|
346
|
+
}
|
|
347
|
+
const root = findWorkspaceRoot(cwd);
|
|
348
|
+
const result = await runThemeFromHex({ name, hex }, root);
|
|
349
|
+
if (!result.success) {
|
|
350
|
+
return textResult(result.message, true);
|
|
351
|
+
}
|
|
352
|
+
return textResult([
|
|
353
|
+
`✓ ${result.message}`,
|
|
354
|
+
"",
|
|
355
|
+
"Layer it on top of a base theme:",
|
|
356
|
+
"```css",
|
|
357
|
+
`@import "@company/tokens/themes/ultimate.css";`,
|
|
358
|
+
`@import "@company/tokens/themes/${name}.css";`,
|
|
359
|
+
"```",
|
|
360
|
+
"",
|
|
361
|
+
`Then set \`data-theme="${name}"\` on <html> (or use the live theme switcher dock) to activate it.`,
|
|
362
|
+
].join("\n"));
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Seed a state file that the docs app's `LiveThemeSwitcher` picks up on
|
|
366
|
+
* boot, so an agent can say "use the brand theme we just generated" and
|
|
367
|
+
* the next `pnpm --filter docs dev` run opens already themed.
|
|
368
|
+
*
|
|
369
|
+
* Implementation: writes `{ theme: "<slug>" }` JSON to `.inai/state.json`
|
|
370
|
+
* in the current project (matches the convention used by other CLI
|
|
371
|
+
* commands). Non-destructive: merges with existing state if present.
|
|
372
|
+
*/
|
|
373
|
+
export async function handleApplyTheme(args, cwd) {
|
|
374
|
+
const name = args?.name?.trim();
|
|
375
|
+
if (!name) {
|
|
376
|
+
return textResult("Error: 'name' argument is required.", true);
|
|
377
|
+
}
|
|
378
|
+
const stateDir = path.join(cwd, ".inai");
|
|
379
|
+
const stateFile = path.join(stateDir, "state.json");
|
|
380
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
381
|
+
let existing = {};
|
|
382
|
+
if (fs.existsSync(stateFile)) {
|
|
383
|
+
try {
|
|
384
|
+
existing = JSON.parse(fs.readFileSync(stateFile, "utf-8"));
|
|
385
|
+
}
|
|
386
|
+
catch {
|
|
387
|
+
// Corrupt state file — rewrite from scratch.
|
|
388
|
+
existing = {};
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const merged = { ...existing, theme: name, updatedAt: new Date().toISOString() };
|
|
392
|
+
fs.writeFileSync(stateFile, JSON.stringify(merged, null, 2));
|
|
393
|
+
return textResult([
|
|
394
|
+
`✓ Applied theme "${name}".`,
|
|
395
|
+
"",
|
|
396
|
+
`State file: \`${path.relative(cwd, stateFile) || stateFile}\``,
|
|
397
|
+
"",
|
|
398
|
+
"Next steps:",
|
|
399
|
+
"1. Reload the docs app — the pitch mode shell reads this state on mount.",
|
|
400
|
+
"2. Or open the live theme switcher dock (bottom-right FAB) and click the preset — that also sets the active theme without a reload.",
|
|
401
|
+
].join("\n"));
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Spawn `scripts/capture-moonshots.mjs` for the agent. Streams stdout
|
|
405
|
+
* + stderr back through the MCP result. Intended for the case where
|
|
406
|
+
* the team wants fresh video loops without dropping to the terminal.
|
|
407
|
+
*/
|
|
408
|
+
export async function handleCaptureMoonshots(args, cwd) {
|
|
409
|
+
const root = findWorkspaceRoot(cwd);
|
|
410
|
+
const scriptPath = path.join(root, "scripts/capture-moonshots.mjs");
|
|
411
|
+
if (!fs.existsSync(scriptPath)) {
|
|
412
|
+
return textResult(`Error: capture script not found at ${scriptPath}. Run from inside the InAI UI workspace.`, true);
|
|
413
|
+
}
|
|
414
|
+
const extra = [];
|
|
415
|
+
if (args?.slug)
|
|
416
|
+
extra.push(`--slug=${args.slug}`);
|
|
417
|
+
if (args?.force)
|
|
418
|
+
extra.push("--force");
|
|
419
|
+
return await new Promise((resolve) => {
|
|
420
|
+
const proc = spawn("node", [scriptPath, ...extra], {
|
|
421
|
+
cwd: root,
|
|
422
|
+
env: { ...process.env },
|
|
423
|
+
});
|
|
424
|
+
let out = "";
|
|
425
|
+
let err = "";
|
|
426
|
+
proc.stdout.on("data", (d) => {
|
|
427
|
+
out += d.toString();
|
|
428
|
+
});
|
|
429
|
+
proc.stderr.on("data", (d) => {
|
|
430
|
+
err += d.toString();
|
|
431
|
+
});
|
|
432
|
+
proc.on("close", (code) => {
|
|
433
|
+
const body = [
|
|
434
|
+
`\`node scripts/capture-moonshots.mjs ${extra.join(" ")}\` exited with code ${code ?? "?"}.`,
|
|
435
|
+
"",
|
|
436
|
+
"```",
|
|
437
|
+
out.trim(),
|
|
438
|
+
err.trim() ? `\n[stderr]\n${err.trim()}` : "",
|
|
439
|
+
"```",
|
|
440
|
+
].join("\n");
|
|
441
|
+
resolve(textResult(body, (code ?? 0) !== 0));
|
|
442
|
+
});
|
|
443
|
+
proc.on("error", (e) => {
|
|
444
|
+
resolve(textResult(`Spawn failed: ${e.message}`, true));
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
export async function handleScaffoldPage(args, cwd) {
|
|
449
|
+
const brief = args?.brief?.trim();
|
|
450
|
+
if (!brief) {
|
|
451
|
+
return textResult("Error: 'brief' argument is required.", true);
|
|
452
|
+
}
|
|
453
|
+
const targetRel = args?.target_path?.trim();
|
|
454
|
+
if (!targetRel) {
|
|
455
|
+
return textResult("Error: 'target_path' argument is required (relative to the current project).", true);
|
|
456
|
+
}
|
|
457
|
+
const limit = Math.max(1, Math.min(args?.limit ?? 6, 12));
|
|
458
|
+
const registryDir = resolveRegistryDir(cwd);
|
|
459
|
+
const registryPath = path.join(registryDir, "registry.json");
|
|
460
|
+
const registry = readRegistryJson(registryPath);
|
|
461
|
+
if (!registry) {
|
|
462
|
+
return textResult(`Error: Registry not found at ${registryPath}.`, true);
|
|
463
|
+
}
|
|
464
|
+
const candidates = [...registry.blocks, ...registry.templates];
|
|
465
|
+
const scored = candidates
|
|
466
|
+
.map((item) => {
|
|
467
|
+
const combined = `${item.name} ${item.description}`;
|
|
468
|
+
return { item, score: scoreBlock(brief, combined) };
|
|
469
|
+
})
|
|
470
|
+
.filter((s) => s.score > 0)
|
|
471
|
+
.sort((a, b) => b.score - a.score)
|
|
472
|
+
.slice(0, limit);
|
|
473
|
+
if (scored.length === 0) {
|
|
474
|
+
return textResult(`No blocks or templates matched brief "${brief}". Nothing scaffolded.`, true);
|
|
475
|
+
}
|
|
476
|
+
const targetAbs = path.resolve(cwd, targetRel);
|
|
477
|
+
if (fs.existsSync(targetAbs)) {
|
|
478
|
+
return textResult(`Error: target file already exists at ${targetAbs}. Remove it or pick a new path.`, true);
|
|
479
|
+
}
|
|
480
|
+
const source = scaffoldPageSource(brief, scored.map((s) => ({
|
|
481
|
+
name: s.item.name,
|
|
482
|
+
type: s.item.type,
|
|
483
|
+
description: s.item.description,
|
|
484
|
+
})));
|
|
485
|
+
fs.mkdirSync(path.dirname(targetAbs), { recursive: true });
|
|
486
|
+
fs.writeFileSync(targetAbs, source, "utf-8");
|
|
487
|
+
const lines = [
|
|
488
|
+
`# Scaffolded page at ${targetRel}`,
|
|
489
|
+
"",
|
|
490
|
+
`**Brief**: ${brief}`,
|
|
491
|
+
"",
|
|
492
|
+
`Composed ${scored.length} block${scored.length === 1 ? "" : "s"}:`,
|
|
493
|
+
"",
|
|
494
|
+
];
|
|
495
|
+
for (const { item, score } of scored) {
|
|
496
|
+
lines.push(`- \`${item.name}\` (${item.type}, score ${score}) — ${item.description}`);
|
|
497
|
+
}
|
|
498
|
+
lines.push("");
|
|
499
|
+
lines.push("Next steps:");
|
|
500
|
+
lines.push("1. Make sure each referenced block is installed in your project (run `inai-ui add <slug>` for any that aren't yet).");
|
|
501
|
+
lines.push("2. Import the generated page from your router / entry point.");
|
|
502
|
+
lines.push("3. Theme it with the live theme switcher or `inai-ui theme from-hex` to lock in the client's brand.");
|
|
503
|
+
return textResult(lines.join("\n"));
|
|
504
|
+
}
|
|
505
|
+
export async function handleSuggestBlocks(args, cwd) {
|
|
506
|
+
const brief = args?.brief?.trim();
|
|
507
|
+
if (!brief) {
|
|
508
|
+
return textResult("Error: 'brief' argument is required.", true);
|
|
509
|
+
}
|
|
510
|
+
const limit = Math.max(1, Math.min(args?.limit ?? 6, 20));
|
|
511
|
+
const registryDir = resolveRegistryDir(cwd);
|
|
512
|
+
const registryPath = path.join(registryDir, "registry.json");
|
|
513
|
+
const registry = readRegistryJson(registryPath);
|
|
514
|
+
if (!registry) {
|
|
515
|
+
return textResult(`Error: Registry not found at ${registryPath}.`, true);
|
|
516
|
+
}
|
|
517
|
+
const candidates = [...registry.blocks, ...registry.templates];
|
|
518
|
+
const scored = candidates
|
|
519
|
+
.map((item) => {
|
|
520
|
+
const combined = `${item.name} ${item.description}`;
|
|
521
|
+
return { item, score: scoreBlock(brief, combined) };
|
|
522
|
+
})
|
|
523
|
+
.filter((s) => s.score > 0)
|
|
524
|
+
.sort((a, b) => b.score - a.score)
|
|
525
|
+
.slice(0, limit);
|
|
526
|
+
if (scored.length === 0) {
|
|
527
|
+
return textResult(`No blocks or templates matched brief "${brief}". Try broader keywords.`);
|
|
528
|
+
}
|
|
529
|
+
const lines = [
|
|
530
|
+
`# ${scored.length} suggestion${scored.length === 1 ? "" : "s"} for: ${brief}`,
|
|
531
|
+
"",
|
|
532
|
+
];
|
|
533
|
+
for (const { item, score } of scored) {
|
|
534
|
+
lines.push(`## ${item.name} (score ${score})`);
|
|
535
|
+
lines.push(`- type: \`${item.type}\``);
|
|
536
|
+
lines.push(`- ${item.description}`);
|
|
537
|
+
lines.push("");
|
|
538
|
+
}
|
|
539
|
+
return textResult(lines.join("\n"));
|
|
540
|
+
}
|
|
161
541
|
// ─── MCP server bootstrap ────────────────────────────────────────────
|
|
162
542
|
export const MCP_TOOLS = [
|
|
163
543
|
{
|
|
@@ -184,6 +564,17 @@ export const MCP_TOOLS = [
|
|
|
184
564
|
required: ["name"],
|
|
185
565
|
},
|
|
186
566
|
},
|
|
567
|
+
{
|
|
568
|
+
name: "view_component_schema",
|
|
569
|
+
description: "Return the prop schema (name, type, required, description, default) for a component. Use before generating JSX so prop names, variants and types are correct without reading the source.",
|
|
570
|
+
inputSchema: {
|
|
571
|
+
type: "object",
|
|
572
|
+
properties: {
|
|
573
|
+
name: { type: "string", description: "Component name" },
|
|
574
|
+
},
|
|
575
|
+
required: ["name"],
|
|
576
|
+
},
|
|
577
|
+
},
|
|
187
578
|
{
|
|
188
579
|
name: "add_component",
|
|
189
580
|
description: "Install a component (and its transitive registry dependencies) into the current project.",
|
|
@@ -236,6 +627,112 @@ export const MCP_TOOLS = [
|
|
|
236
627
|
},
|
|
237
628
|
},
|
|
238
629
|
},
|
|
630
|
+
{
|
|
631
|
+
name: "list_moonshots",
|
|
632
|
+
description: "List the creative signature moonshots registered in InAI UI (kinetic typography, gradient mesh, magnetic cursor, decrypt text, etc.). Filter by status ('stable' | 'experimental') and/or category ('interaction' | 'typography' | 'layout' | 'dataviz' | 'color' | 'ambient'). Returns JSON metadata including reduced-motion strategy so the agent can choose moonshots responsibly.",
|
|
633
|
+
inputSchema: {
|
|
634
|
+
type: "object",
|
|
635
|
+
properties: {
|
|
636
|
+
status: {
|
|
637
|
+
type: "string",
|
|
638
|
+
description: "Filter by maturity: 'stable' (default returns all).",
|
|
639
|
+
},
|
|
640
|
+
category: {
|
|
641
|
+
type: "string",
|
|
642
|
+
description: "Filter by thematic category: interaction, typography, layout, dataviz, color, ambient.",
|
|
643
|
+
},
|
|
644
|
+
},
|
|
645
|
+
},
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
name: "suggest_blocks",
|
|
649
|
+
description: "Given a free-text brief (e.g. 'fintech dashboard with live KPIs and dark hero'), rank the blocks and templates in registry.json by keyword overlap and return the top matches. Use this to scaffold a client proposal page without reading the entire registry.",
|
|
650
|
+
inputSchema: {
|
|
651
|
+
type: "object",
|
|
652
|
+
properties: {
|
|
653
|
+
brief: {
|
|
654
|
+
type: "string",
|
|
655
|
+
description: "Short description of the desired page or section.",
|
|
656
|
+
},
|
|
657
|
+
limit: {
|
|
658
|
+
type: "number",
|
|
659
|
+
description: "Max number of suggestions. Defaults to 6.",
|
|
660
|
+
},
|
|
661
|
+
},
|
|
662
|
+
required: ["brief"],
|
|
663
|
+
},
|
|
664
|
+
},
|
|
665
|
+
{
|
|
666
|
+
name: "scaffold_page",
|
|
667
|
+
description: "Compose a fresh React .tsx page that imports and renders the top blocks matching a brief. Writes the file to `target_path` (relative to cwd) and returns a Markdown summary. Pair with `suggest_blocks` if the caller wants to preview the picks before writing anything.",
|
|
668
|
+
inputSchema: {
|
|
669
|
+
type: "object",
|
|
670
|
+
properties: {
|
|
671
|
+
brief: {
|
|
672
|
+
type: "string",
|
|
673
|
+
description: "Short description of the desired page (e.g. 'fintech landing with pricing and stats').",
|
|
674
|
+
},
|
|
675
|
+
target_path: {
|
|
676
|
+
type: "string",
|
|
677
|
+
description: "Relative path where the generated .tsx file will be written (e.g. 'src/pages/client-x-landing.tsx'). Must not exist yet.",
|
|
678
|
+
},
|
|
679
|
+
limit: {
|
|
680
|
+
type: "number",
|
|
681
|
+
description: "Max number of blocks to compose. Defaults to 6, capped at 12.",
|
|
682
|
+
},
|
|
683
|
+
},
|
|
684
|
+
required: ["brief", "target_path"],
|
|
685
|
+
},
|
|
686
|
+
},
|
|
687
|
+
{
|
|
688
|
+
name: "generate_theme",
|
|
689
|
+
description: "Generate a brand override theme from a single hex color. Uses the same hex → OKLCH pipeline as ThemeStudio and the live theme switcher, and writes a layered `.css` file under `packages/tokens/src/themes/` that should be imported after a base theme.",
|
|
690
|
+
inputSchema: {
|
|
691
|
+
type: "object",
|
|
692
|
+
properties: {
|
|
693
|
+
name: {
|
|
694
|
+
type: "string",
|
|
695
|
+
description: "Theme slug, used as the CSS filename (e.g. 'client-orbis').",
|
|
696
|
+
},
|
|
697
|
+
hex: {
|
|
698
|
+
type: "string",
|
|
699
|
+
description: "Brand hex color, e.g. '#6b46c1'.",
|
|
700
|
+
},
|
|
701
|
+
},
|
|
702
|
+
required: ["name", "hex"],
|
|
703
|
+
},
|
|
704
|
+
},
|
|
705
|
+
{
|
|
706
|
+
name: "apply_theme",
|
|
707
|
+
description: "Seed `.inai/state.json` with the active theme slug so the next docs app boot picks it up automatically. Non-destructive: merges with existing state. The live theme switcher also honours this state on mount.",
|
|
708
|
+
inputSchema: {
|
|
709
|
+
type: "object",
|
|
710
|
+
properties: {
|
|
711
|
+
name: {
|
|
712
|
+
type: "string",
|
|
713
|
+
description: "Theme slug to activate (must already exist).",
|
|
714
|
+
},
|
|
715
|
+
},
|
|
716
|
+
required: ["name"],
|
|
717
|
+
},
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
name: "capture_moonshots",
|
|
721
|
+
description: "Run `scripts/capture-moonshots.mjs` to regenerate the WebM loops consumed by the gallery. Accepts an optional `slug` filter and a `force` flag to re-record already-captured moonshots. Returns the script's stdout/stderr.",
|
|
722
|
+
inputSchema: {
|
|
723
|
+
type: "object",
|
|
724
|
+
properties: {
|
|
725
|
+
slug: {
|
|
726
|
+
type: "string",
|
|
727
|
+
description: "Optional moonshot slug to record in isolation (e.g. 'gradient-mesh-live').",
|
|
728
|
+
},
|
|
729
|
+
force: {
|
|
730
|
+
type: "boolean",
|
|
731
|
+
description: "Re-capture slugs that already have a video. Defaults to false.",
|
|
732
|
+
},
|
|
733
|
+
},
|
|
734
|
+
},
|
|
735
|
+
},
|
|
239
736
|
];
|
|
240
737
|
export async function runMcp(cwd = process.cwd()) {
|
|
241
738
|
const server = new Server({ name: "inai-ui-mcp", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
@@ -251,6 +748,8 @@ export async function runMcp(cwd = process.cwd()) {
|
|
|
251
748
|
return await handleListComponents(args, cwd);
|
|
252
749
|
case "view_component":
|
|
253
750
|
return await handleViewComponent(args, cwd);
|
|
751
|
+
case "view_component_schema":
|
|
752
|
+
return await handleViewComponentSchema(args, cwd);
|
|
254
753
|
case "add_component":
|
|
255
754
|
return await handleAddComponent(args, cwd);
|
|
256
755
|
case "diff_component":
|
|
@@ -261,6 +760,18 @@ export async function runMcp(cwd = process.cwd()) {
|
|
|
261
760
|
return await handleListThemes();
|
|
262
761
|
case "project_status":
|
|
263
762
|
return await handleStatus(args, cwd);
|
|
763
|
+
case "list_moonshots":
|
|
764
|
+
return await handleListMoonshots(args, cwd);
|
|
765
|
+
case "suggest_blocks":
|
|
766
|
+
return await handleSuggestBlocks(args, cwd);
|
|
767
|
+
case "scaffold_page":
|
|
768
|
+
return await handleScaffoldPage(args, cwd);
|
|
769
|
+
case "generate_theme":
|
|
770
|
+
return await handleGenerateTheme(args, cwd);
|
|
771
|
+
case "apply_theme":
|
|
772
|
+
return await handleApplyTheme(args, cwd);
|
|
773
|
+
case "capture_moonshots":
|
|
774
|
+
return await handleCaptureMoonshots(args, cwd);
|
|
264
775
|
default:
|
|
265
776
|
return {
|
|
266
777
|
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
@@ -53,6 +53,13 @@ export interface RegistryJson {
|
|
|
53
53
|
components: RegistryComponent[];
|
|
54
54
|
blocks: RegistryComponent[];
|
|
55
55
|
templates: RegistryComponent[];
|
|
56
|
+
/**
|
|
57
|
+
* Installable theme presets — each one is a plain `.css` file that the
|
|
58
|
+
* CLI can drop into a consumer project's styles tree and wire into the
|
|
59
|
+
* user's Tailwind v4 `@theme` layer. Optional for backward-compat with
|
|
60
|
+
* pre-2.0 registries; v2.0+ registries always populate this.
|
|
61
|
+
*/
|
|
62
|
+
themes?: RegistryTheme[];
|
|
56
63
|
}
|
|
57
64
|
export interface RegistryComponent {
|
|
58
65
|
name: string;
|
|
@@ -64,12 +71,35 @@ export interface RegistryComponent {
|
|
|
64
71
|
tokenUsage: string[];
|
|
65
72
|
tanstackCompatibility: Record<string, boolean>;
|
|
66
73
|
fieldWrappers?: string[];
|
|
74
|
+
/**
|
|
75
|
+
* Thematic category used by `inai-ui list --category <name>`. Optional
|
|
76
|
+
* because older registry snapshots may not carry it. Common values:
|
|
77
|
+
* layout, data-display, feedback, overlay, navigation, forms, data-entry,
|
|
78
|
+
* misc, typography, dataviz.
|
|
79
|
+
*/
|
|
80
|
+
category?: string;
|
|
81
|
+
/**
|
|
82
|
+
* Tags used for fuzzy discovery by `inai-ui list`/`add` search. Optional
|
|
83
|
+
* because older registry snapshots may not carry them.
|
|
84
|
+
*/
|
|
85
|
+
tags?: string[];
|
|
67
86
|
/**
|
|
68
87
|
* Names of other registry components this component depends on. Used by
|
|
69
88
|
* `inai-ui add` to install transitive deps automatically. Defaults to [].
|
|
70
89
|
*/
|
|
71
90
|
registryDependencies?: string[];
|
|
72
91
|
}
|
|
92
|
+
export interface RegistryTheme {
|
|
93
|
+
/** Slug used with `inai-ui add theme <name>`. Matches the CSS selector. */
|
|
94
|
+
name: string;
|
|
95
|
+
type: "theme";
|
|
96
|
+
description: string;
|
|
97
|
+
/** "brand" | "shadcn" | "creative" — drives galleries and grouping. */
|
|
98
|
+
category: "brand" | "shadcn" | "creative";
|
|
99
|
+
/** Single `.css` file in `packages/tokens/src/themes/`. */
|
|
100
|
+
files: string[];
|
|
101
|
+
tags: string[];
|
|
102
|
+
}
|
|
73
103
|
export declare function readComponentsJson(rootDir: string): ComponentsJsonFile | null;
|
|
74
104
|
export declare function readRegistryJson(registryPath: string): RegistryJson | null;
|
|
75
105
|
export type ComponentHealth = "HEALTHY" | "DRIFT" | "OUTDATED" | "MISSING";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAE9D,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE;QACP,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,QAAQ,EAAE;QACR,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,OAAO,EAAE;QACP,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd;;;;;;;;;WASG;QACH,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;IACF,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE;QACR,MAAM,EAAE,OAAO,CAAC;QAChB,KAAK,EAAE,OAAO,CAAC;QACf,IAAI,EAAE,OAAO,CAAC;QACd,KAAK,EAAE,OAAO,CAAC;KAChB,CAAC;IACF,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAC5C;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,SAAS,EAAE,iBAAiB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAE9D,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE;QACP,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,QAAQ,EAAE;QACR,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,OAAO,EAAE;QACP,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd;;;;;;;;;WASG;QACH,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;IACF,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE;QACR,MAAM,EAAE,OAAO,CAAC;QAChB,KAAK,EAAE,OAAO,CAAC;QACf,IAAI,EAAE,OAAO,CAAC;QACd,KAAK,EAAE,OAAO,CAAC;KAChB,CAAC;IACF,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAC5C;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,SAAS,EAAE,iBAAiB,EAAE,CAAC;IAC/B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/C,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,QAAQ,EAAE,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC;IAC1C,2DAA2D;IAC3D,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAW7E;AAED,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAU1E;AAED,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;AAE3E,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,eAAe,CAAC;IACxB,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,uDAAuD;IACvD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,oBAAoB,EAAE,CAAC;CACpC;AAOD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,kBAAkB,EAClC,QAAQ,EAAE,YAAY,GAAG,IAAI,GAC5B,YAAY,CAsFd;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAkD9D;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,MAAM,CAAC,CA4BjB;AAED,wBAAsB,aAAa,CACjC,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAcf"}
|
package/dist/commands/theme.d.ts
CHANGED
|
@@ -2,6 +2,12 @@ export interface ThemeCreateOptions {
|
|
|
2
2
|
name: string;
|
|
3
3
|
base: string;
|
|
4
4
|
}
|
|
5
|
+
export interface ThemeFromHexOptions {
|
|
6
|
+
/** Theme slug used as filename. */
|
|
7
|
+
name: string;
|
|
8
|
+
/** Brand hex string, e.g. "#6b46c1". */
|
|
9
|
+
hex: string;
|
|
10
|
+
}
|
|
5
11
|
export declare function resolveThemesDir(rootDir: string): string;
|
|
6
12
|
export declare function readBaseTheme(themesDir: string, baseName: string): string | null;
|
|
7
13
|
export declare function generateThemeCss(baseCss: string, newName: string, baseName: string): string;
|
|
@@ -10,5 +16,31 @@ export declare function runThemeCreate(options: ThemeCreateOptions, rootDir: str
|
|
|
10
16
|
filePath: string;
|
|
11
17
|
message: string;
|
|
12
18
|
}>;
|
|
19
|
+
/**
|
|
20
|
+
* Convert an sRGB hex string to approximate OKLCH. Uses the same
|
|
21
|
+
* pipeline as `packages/ui/src/lib/color-utils.ts` so themes generated
|
|
22
|
+
* by the CLI match what ThemeStudio and the live theme switcher pick.
|
|
23
|
+
*
|
|
24
|
+
* Kept self-contained so the CLI has no runtime dep on the UI package.
|
|
25
|
+
*/
|
|
26
|
+
export interface OklchTriple {
|
|
27
|
+
l: number;
|
|
28
|
+
c: number;
|
|
29
|
+
h: number;
|
|
30
|
+
}
|
|
31
|
+
export declare function hexToOklch(hex: string): OklchTriple;
|
|
32
|
+
/**
|
|
33
|
+
* Produce a minimal CSS theme layer that overrides the brand-centric
|
|
34
|
+
* token slots. It is meant to be stacked on top of an existing theme
|
|
35
|
+
* (e.g. by `@import`-ing this file after `ultimate.css`) so the rest of
|
|
36
|
+
* the scheme stays coherent and accessible.
|
|
37
|
+
*/
|
|
38
|
+
export declare function generateThemeFromHexCss(name: string, hex: string, oklch: OklchTriple): string;
|
|
39
|
+
export declare function runThemeFromHex(options: ThemeFromHexOptions, rootDir: string): Promise<{
|
|
40
|
+
success: boolean;
|
|
41
|
+
filePath: string;
|
|
42
|
+
message: string;
|
|
43
|
+
}>;
|
|
44
|
+
export declare function themeFromHexCommand(options: ThemeFromHexOptions): Promise<void>;
|
|
13
45
|
export declare function themeCreateCommand(options: ThemeCreateOptions): Promise<void>;
|
|
14
46
|
//# sourceMappingURL=theme.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/commands/theme.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMhF;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgB3F;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAoDlE;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBnF"}
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/commands/theme.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,mBAAmB;IAClC,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMhF;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgB3F;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAoDlE;AAID;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAOD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAwBnD;AASD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,WAAW,GACjB,MAAM,CAkBR;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,mBAAmB,EAC5B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CA6BlE;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBrF;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBnF"}
|