lucent-ui 0.19.1 → 0.21.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/index.cjs +78 -35
- package/dist/index.d.ts +73 -0
- package/dist/index.js +1226 -814
- package/dist-cli/cli/entry.js +0 -0
- package/dist-cli/cli/index.js +0 -0
- package/dist-server/server/index.js +0 -0
- package/dist-server/src/components/atoms/Progress/Progress.manifest.js +33 -0
- package/dist-server/src/components/atoms/Row/Row.manifest.js +102 -0
- package/dist-server/src/components/atoms/Stack/Stack.manifest.js +101 -0
- package/dist-server/src/manifest/validate.test.js +28 -0
- package/package.json +15 -13
package/dist-cli/cli/entry.js
CHANGED
|
File without changes
|
package/dist-cli/cli/index.js
CHANGED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const COMPONENT_MANIFEST = {
|
|
2
|
+
id: 'progress',
|
|
3
|
+
name: 'Progress',
|
|
4
|
+
tier: 'atom',
|
|
5
|
+
domain: 'neutral',
|
|
6
|
+
specVersion: '0.1',
|
|
7
|
+
description: 'A horizontal bar indicating completion, progress, or resource usage.',
|
|
8
|
+
designIntent: 'Use Progress to visualise a bounded numeric value — task completion, disk usage, health bars, etc. ' +
|
|
9
|
+
'Set warnAt/dangerAt thresholds for automatic colour shifts instead of hardcoding variants. ' +
|
|
10
|
+
'Ascending thresholds (warnAt < dangerAt) suit "high is bad" metrics like CPU; ' +
|
|
11
|
+
'descending thresholds (warnAt > dangerAt) suit "low is bad" metrics like battery.',
|
|
12
|
+
props: [
|
|
13
|
+
{ name: 'value', type: 'number', required: true, description: 'Current progress value.' },
|
|
14
|
+
{ name: 'max', type: 'number', required: false, default: '100', description: 'Maximum value.' },
|
|
15
|
+
{ name: 'variant', type: 'enum', required: false, default: 'accent', description: 'Colour variant. Overridden when thresholds are set.', enumValues: ['accent', 'success', 'warning', 'danger'] },
|
|
16
|
+
{ name: 'size', type: 'enum', required: false, default: 'md', description: 'Bar height.', enumValues: ['sm', 'md', 'lg'] },
|
|
17
|
+
{ name: 'warnAt', type: 'number', required: false, description: 'Value at which variant auto-switches to warning.' },
|
|
18
|
+
{ name: 'dangerAt', type: 'number', required: false, description: 'Value at which variant auto-switches to danger.' },
|
|
19
|
+
{ name: 'label', type: 'union', required: false, description: 'true shows percentage; ReactNode shows custom label.' },
|
|
20
|
+
],
|
|
21
|
+
usageExamples: [
|
|
22
|
+
{ title: 'Basic', code: `<Progress value={60} />` },
|
|
23
|
+
{ title: 'With label', code: `<Progress value={42} label />` },
|
|
24
|
+
{ title: 'Thresholds', code: `<Progress value={85} warnAt={70} dangerAt={90} label />` },
|
|
25
|
+
{ title: 'Danger variant', code: `<Progress value={95} variant="danger" size="lg" label />` },
|
|
26
|
+
],
|
|
27
|
+
compositionGraph: [],
|
|
28
|
+
accessibility: {
|
|
29
|
+
role: 'progressbar',
|
|
30
|
+
ariaAttributes: ['aria-valuenow', 'aria-valuemin', 'aria-valuemax'],
|
|
31
|
+
notes: 'Uses native progressbar role. Add aria-label on the wrapping element for screen-reader context.',
|
|
32
|
+
},
|
|
33
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export const COMPONENT_MANIFEST = {
|
|
2
|
+
id: 'row',
|
|
3
|
+
name: 'Row',
|
|
4
|
+
tier: 'atom',
|
|
5
|
+
domain: 'neutral',
|
|
6
|
+
specVersion: '0.1',
|
|
7
|
+
description: 'Horizontal flex layout primitive that spaces children using design-system gap tokens.',
|
|
8
|
+
designIntent: 'Row is the primary horizontal layout container. Use it any time you need to arrange elements ' +
|
|
9
|
+
'side by side — button groups, label-value pairs, icon + text combos, toolbar actions. ' +
|
|
10
|
+
'Default align is "center" (vertical centering), which is the most common horizontal layout need. ' +
|
|
11
|
+
'Use justify="between" for space-between patterns like a header with title on the left and actions ' +
|
|
12
|
+
'on the right. Set wrap=true when items should flow to the next line on narrow screens. ' +
|
|
13
|
+
'Row does not add padding — wrap it in a Card or apply padding on a parent container instead. ' +
|
|
14
|
+
'For vertical arrangement, use Stack instead.',
|
|
15
|
+
props: [
|
|
16
|
+
{
|
|
17
|
+
name: 'gap',
|
|
18
|
+
type: 'enum',
|
|
19
|
+
required: false,
|
|
20
|
+
default: '3',
|
|
21
|
+
description: 'Spacing between children. Maps to --lucent-space-{n} tokens.',
|
|
22
|
+
enumValues: ['0', '1', '2', '3', '4', '5', '6', '8', '10', '12', '16', '20', '24'],
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'align',
|
|
26
|
+
type: 'enum',
|
|
27
|
+
required: false,
|
|
28
|
+
default: 'center',
|
|
29
|
+
description: 'Cross-axis alignment (alignItems). "center" vertically centers items, which is the most common need.',
|
|
30
|
+
enumValues: ['start', 'center', 'end', 'stretch', 'baseline'],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'justify',
|
|
34
|
+
type: 'enum',
|
|
35
|
+
required: false,
|
|
36
|
+
default: 'start',
|
|
37
|
+
description: 'Main-axis distribution (justifyContent). Use "between" for label/action pairs.',
|
|
38
|
+
enumValues: ['start', 'center', 'end', 'between', 'around'],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'as',
|
|
42
|
+
type: 'enum',
|
|
43
|
+
required: false,
|
|
44
|
+
default: 'div',
|
|
45
|
+
description: 'HTML element to render. Use semantic elements when appropriate (nav for navigation, form for forms).',
|
|
46
|
+
enumValues: ['div', 'section', 'nav', 'form', 'fieldset', 'ul', 'ol'],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'wrap',
|
|
50
|
+
type: 'boolean',
|
|
51
|
+
required: false,
|
|
52
|
+
default: 'false',
|
|
53
|
+
description: 'Whether children should wrap to the next line when they exceed the container width.',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'children',
|
|
57
|
+
type: 'ReactNode',
|
|
58
|
+
required: true,
|
|
59
|
+
description: 'The elements to arrange horizontally.',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'style',
|
|
63
|
+
type: 'object',
|
|
64
|
+
required: false,
|
|
65
|
+
description: 'Inline style overrides. Applied after computed layout styles.',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'className',
|
|
69
|
+
type: 'string',
|
|
70
|
+
required: false,
|
|
71
|
+
description: 'CSS class name passthrough.',
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
usageExamples: [
|
|
75
|
+
{
|
|
76
|
+
title: 'Settings toggle',
|
|
77
|
+
code: `<Row gap="3" justify="between">\n <Text size="sm">Push notifications</Text>\n <Toggle />\n</Row>`,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
title: 'Button group',
|
|
81
|
+
code: `<Row gap="2">\n <Button variant="primary">Save</Button>\n <Button variant="outline">Cancel</Button>\n</Row>`,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
title: 'Stats row',
|
|
85
|
+
code: `<Row gap="6" wrap>\n <Stack gap="1">\n <Text size="2xl" weight="bold">1,234</Text>\n <Text size="xs" color="secondary">Users</Text>\n </Stack>\n <Stack gap="1">\n <Text size="2xl" weight="bold">56.7%</Text>\n <Text size="xs" color="secondary">Conversion</Text>\n </Stack>\n</Row>`,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
title: 'Icon + text',
|
|
89
|
+
code: `<Row gap="2" align="center">\n <Icon name="check" size={16} />\n <Text size="sm" color="success">Verified</Text>\n</Row>`,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
title: 'Header with actions',
|
|
93
|
+
code: `<Row justify="between">\n <Text as="h2" size="xl" weight="semibold">Dashboard</Text>\n <Row gap="2">\n <Button variant="outline" size="sm">Export</Button>\n <Button variant="primary" size="sm">New report</Button>\n </Row>\n</Row>`,
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
compositionGraph: [],
|
|
97
|
+
accessibility: {
|
|
98
|
+
notes: 'Row renders a <div> by default, which has no implicit ARIA role. Use the `as` prop to render ' +
|
|
99
|
+
'semantic elements (nav, section) when the content has a specific structural purpose. ' +
|
|
100
|
+
'For toolbar patterns, consider adding role="toolbar" via the role prop.',
|
|
101
|
+
},
|
|
102
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
export const COMPONENT_MANIFEST = {
|
|
2
|
+
id: 'stack',
|
|
3
|
+
name: 'Stack',
|
|
4
|
+
tier: 'atom',
|
|
5
|
+
domain: 'neutral',
|
|
6
|
+
specVersion: '0.1',
|
|
7
|
+
description: 'Vertical flex layout primitive that spaces children using design-system gap tokens.',
|
|
8
|
+
designIntent: 'Stack is the primary vertical layout container. Use it any time you need to arrange elements ' +
|
|
9
|
+
'in a column with consistent spacing — form fields, card content, page sections, sidebar navigation. ' +
|
|
10
|
+
'The gap prop maps to spacing tokens (--lucent-space-*), enforcing consistent vertical rhythm ' +
|
|
11
|
+
'without manual margin management. Prefer Stack over raw inline flex styles for maintainability ' +
|
|
12
|
+
'and readability. Use Row (the horizontal counterpart) when items should flow left-to-right. ' +
|
|
13
|
+
'Stack does not add padding — wrap it in a Card or apply padding on a parent container instead.',
|
|
14
|
+
props: [
|
|
15
|
+
{
|
|
16
|
+
name: 'gap',
|
|
17
|
+
type: 'enum',
|
|
18
|
+
required: false,
|
|
19
|
+
default: '4',
|
|
20
|
+
description: 'Spacing between children. Maps to --lucent-space-{n} tokens.',
|
|
21
|
+
enumValues: ['0', '1', '2', '3', '4', '5', '6', '8', '10', '12', '16', '20', '24'],
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'align',
|
|
25
|
+
type: 'enum',
|
|
26
|
+
required: false,
|
|
27
|
+
default: 'stretch',
|
|
28
|
+
description: 'Cross-axis alignment (alignItems). "stretch" fills the container width.',
|
|
29
|
+
enumValues: ['start', 'center', 'end', 'stretch', 'baseline'],
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'justify',
|
|
33
|
+
type: 'enum',
|
|
34
|
+
required: false,
|
|
35
|
+
default: 'start',
|
|
36
|
+
description: 'Main-axis distribution (justifyContent).',
|
|
37
|
+
enumValues: ['start', 'center', 'end', 'between', 'around'],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'as',
|
|
41
|
+
type: 'enum',
|
|
42
|
+
required: false,
|
|
43
|
+
default: 'div',
|
|
44
|
+
description: 'HTML element to render. Use semantic elements when appropriate (nav for navigation, form for forms).',
|
|
45
|
+
enumValues: ['div', 'section', 'nav', 'form', 'fieldset', 'ul', 'ol'],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'wrap',
|
|
49
|
+
type: 'boolean',
|
|
50
|
+
required: false,
|
|
51
|
+
default: 'false',
|
|
52
|
+
description: 'Whether children should wrap to the next line when they exceed the container width.',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'children',
|
|
56
|
+
type: 'ReactNode',
|
|
57
|
+
required: true,
|
|
58
|
+
description: 'The elements to arrange vertically.',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'style',
|
|
62
|
+
type: 'object',
|
|
63
|
+
required: false,
|
|
64
|
+
description: 'Inline style overrides. Applied after computed layout styles.',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'className',
|
|
68
|
+
type: 'string',
|
|
69
|
+
required: false,
|
|
70
|
+
description: 'CSS class name passthrough.',
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
usageExamples: [
|
|
74
|
+
{
|
|
75
|
+
title: 'Form layout',
|
|
76
|
+
code: `<Stack gap="4">\n <FormField label="Name"><Input /></FormField>\n <FormField label="Email"><Input type="email" /></FormField>\n <Button variant="primary">Submit</Button>\n</Stack>`,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
title: 'Card content',
|
|
80
|
+
code: `<Card padding="lg">\n <Stack gap="3">\n <Text as="h3" size="lg" weight="semibold">Title</Text>\n <Text size="sm" color="secondary">Description goes here.</Text>\n <Button variant="outline" size="sm">Learn more</Button>\n </Stack>\n</Card>`,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
title: 'Tight spacing',
|
|
84
|
+
code: `<Stack gap="1">\n <Text size="sm" weight="medium">Label</Text>\n <Text size="xs" color="secondary">Helper text</Text>\n</Stack>`,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
title: 'Centered content',
|
|
88
|
+
code: `<Stack gap="4" align="center" justify="center" style={{ minHeight: 200 }}>\n <Spinner />\n <Text color="secondary">Loading...</Text>\n</Stack>`,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
title: 'Semantic nav',
|
|
92
|
+
code: `<Stack as="nav" gap="1">\n <NavLink href="/home">Home</NavLink>\n <NavLink href="/settings">Settings</NavLink>\n</Stack>`,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
compositionGraph: [],
|
|
96
|
+
accessibility: {
|
|
97
|
+
notes: 'Stack renders a <div> by default, which has no implicit ARIA role. Use the `as` prop to render ' +
|
|
98
|
+
'semantic elements (nav, section, form) when the content has a specific structural purpose. ' +
|
|
99
|
+
'Add aria-label or aria-labelledby when using landmark elements.',
|
|
100
|
+
},
|
|
101
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, test, expect } from 'vitest';
|
|
2
|
+
import { validateManifest } from './validate.js';
|
|
3
|
+
// Auto-discover all component manifests
|
|
4
|
+
const manifestModules = import.meta.glob('../components/**/*.manifest.ts', { eager: true });
|
|
5
|
+
const manifests = Object.entries(manifestModules).map(([path, mod]) => {
|
|
6
|
+
const m = mod;
|
|
7
|
+
const manifest = m['COMPONENT_MANIFEST'];
|
|
8
|
+
return { path, manifest };
|
|
9
|
+
});
|
|
10
|
+
describe('Component manifests', () => {
|
|
11
|
+
test('at least one manifest was discovered', () => {
|
|
12
|
+
expect(manifests.length).toBeGreaterThan(0);
|
|
13
|
+
});
|
|
14
|
+
for (const { path, manifest } of manifests) {
|
|
15
|
+
const label = path.replace('../components/', '').replace('.manifest.ts', '');
|
|
16
|
+
test(`${label} — exports COMPONENT_MANIFEST`, () => {
|
|
17
|
+
expect(manifest).toBeDefined();
|
|
18
|
+
});
|
|
19
|
+
test(`${label} — passes schema validation`, () => {
|
|
20
|
+
const result = validateManifest(manifest);
|
|
21
|
+
if (!result.valid) {
|
|
22
|
+
const messages = result.errors.map(e => ` ${e.field}: ${e.message}`).join('\n');
|
|
23
|
+
throw new Error(`Invalid manifest:\n${messages}`);
|
|
24
|
+
}
|
|
25
|
+
expect(result.valid).toBe(true);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lucent-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "An AI-first React component library with machine-readable manifests.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -25,6 +25,18 @@
|
|
|
25
25
|
"dist-server",
|
|
26
26
|
"dist-cli"
|
|
27
27
|
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"dev": "vite --config vite.dev.config.ts",
|
|
30
|
+
"build": "vite build",
|
|
31
|
+
"build:server": "tsc -p server/tsconfig.json",
|
|
32
|
+
"build:cli": "tsc -p cli/tsconfig.json && cp cli/template.manifest.json dist-cli/cli/template.manifest.json",
|
|
33
|
+
"test": "vitest run",
|
|
34
|
+
"test:watch": "vitest",
|
|
35
|
+
"prepublishOnly": "tsc --noEmit && pnpm build && pnpm build:server && pnpm build:cli",
|
|
36
|
+
"changeset": "changeset",
|
|
37
|
+
"version-packages": "changeset version",
|
|
38
|
+
"release": "pnpm prepublishOnly && changeset publish"
|
|
39
|
+
},
|
|
28
40
|
"keywords": [
|
|
29
41
|
"react",
|
|
30
42
|
"component-library",
|
|
@@ -39,6 +51,7 @@
|
|
|
39
51
|
},
|
|
40
52
|
"author": "Rozina Szogyenyi",
|
|
41
53
|
"license": "MIT",
|
|
54
|
+
"packageManager": "pnpm@10.30.3",
|
|
42
55
|
"peerDependencies": {
|
|
43
56
|
"react": "^18.0.0 || ^19.0.0",
|
|
44
57
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
@@ -60,16 +73,5 @@
|
|
|
60
73
|
"dependencies": {
|
|
61
74
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
62
75
|
"zod": "^4.3.6"
|
|
63
|
-
},
|
|
64
|
-
"scripts": {
|
|
65
|
-
"dev": "vite --config vite.dev.config.ts",
|
|
66
|
-
"build": "vite build",
|
|
67
|
-
"build:server": "tsc -p server/tsconfig.json",
|
|
68
|
-
"build:cli": "tsc -p cli/tsconfig.json && cp cli/template.manifest.json dist-cli/cli/template.manifest.json",
|
|
69
|
-
"test": "vitest run",
|
|
70
|
-
"test:watch": "vitest",
|
|
71
|
-
"changeset": "changeset",
|
|
72
|
-
"version-packages": "changeset version",
|
|
73
|
-
"release": "pnpm prepublishOnly && changeset publish"
|
|
74
76
|
}
|
|
75
|
-
}
|
|
77
|
+
}
|