lucent-ui 0.30.0 → 0.32.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.
Files changed (27) hide show
  1. package/dist/index.cjs +78 -42
  2. package/dist/index.d.ts +34 -0
  3. package/dist/index.js +1347 -1041
  4. package/dist-cli/cli/entry.js +0 -0
  5. package/dist-cli/cli/index.js +0 -0
  6. package/dist-server/server/index.js +29 -29
  7. package/dist-server/server/pattern-registry.js +18 -0
  8. package/dist-server/src/components/molecules/Card/Card.manifest.js +2 -2
  9. package/dist-server/src/components/molecules/Collapsible/Collapsible.manifest.js +4 -4
  10. package/dist-server/src/components/molecules/Stepper/Stepper.manifest.js +115 -0
  11. package/dist-server/src/manifest/{recipes/action-bar.recipe.js → patterns/action-bar.pattern.js} +1 -1
  12. package/dist-server/src/manifest/{recipes/collapsible-card.recipe.js → patterns/collapsible-card.pattern.js} +1 -1
  13. package/dist-server/src/manifest/patterns/dashboard-header.pattern.js +98 -0
  14. package/dist-server/src/manifest/{recipes/empty-state-card.recipe.js → patterns/empty-state-card.pattern.js} +1 -1
  15. package/dist-server/src/manifest/{recipes/form-layout.recipe.js → patterns/form-layout.pattern.js} +1 -1
  16. package/dist-server/src/manifest/patterns/index.js +12 -0
  17. package/dist-server/src/manifest/patterns/notification-feed.pattern.js +91 -0
  18. package/dist-server/src/manifest/patterns/onboarding-flow.pattern.js +107 -0
  19. package/dist-server/src/manifest/patterns/pricing-table.pattern.js +108 -0
  20. package/dist-server/src/manifest/{recipes/profile-card.recipe.js → patterns/profile-card.pattern.js} +1 -1
  21. package/dist-server/src/manifest/{recipes/search-filter-bar.recipe.js → patterns/search-filter-bar.pattern.js} +1 -1
  22. package/dist-server/src/manifest/{recipes/settings-panel.recipe.js → patterns/settings-panel.pattern.js} +1 -1
  23. package/dist-server/src/manifest/{recipes/stats-row.recipe.js → patterns/stats-row.pattern.js} +1 -1
  24. package/package.json +13 -15
  25. package/dist-server/server/recipe-registry.js +0 -18
  26. package/dist-server/src/manifest/recipes/index.js +0 -8
  27. package/dist-server/src/manifest/validate.test.js +0 -28
@@ -0,0 +1,107 @@
1
+ export const PATTERN = {
2
+ id: 'onboarding-flow',
3
+ name: 'Onboarding Flow',
4
+ description: 'Multi-step onboarding form with step indicator, progress bar, form fields, and back/next navigation. Steps: Profile → Preferences → Confirmation.',
5
+ category: 'form',
6
+ components: ['card', 'text', 'button', 'progress', 'form-field', 'input', 'select', 'checkbox', 'avatar', 'chip', 'stack', 'row', 'divider'],
7
+ structure: `
8
+ Card (elevated, padding="lg")
9
+ └── Stack gap="6"
10
+ ├── Stack gap="1" ← header
11
+ │ ├── Text (lg, semibold, display) ← title
12
+ │ └── Text (sm, secondary) ← step counter
13
+ ├── Progress (sm) ← progress bar
14
+ ├── StepIndicator ← custom step circles
15
+ │ ├── Row justify="between"
16
+ │ │ └── circle[] + connector lines
17
+ │ └── Row justify="between"
18
+ │ └── Text[] (xs, step labels)
19
+ ├── Divider
20
+ ├── Step content (conditional)
21
+ │ ├── Step 0: Stack of FormFields (name, email, role Select)
22
+ │ ├── Step 1: FormFields (team size, use case) + Checkbox
23
+ │ └── Step 2: Avatar + name + email + role Chip (confirmation)
24
+ └── Row justify="between" ← navigation
25
+ ├── Button (ghost, sm) ← Back (disabled on step 0)
26
+ └── Button (primary, sm) ← Continue / Complete
27
+ `.trim(),
28
+ code: `const [step, setStep] = useState(0);
29
+ const STEPS = ['Profile', 'Preferences', 'Confirm'];
30
+
31
+ <Card variant="elevated" padding="lg" style={{ width: 440 }}>
32
+ <Stack gap="6">
33
+ <Stack gap="1">
34
+ <Text size="lg" weight="semibold" family="display">Create your account</Text>
35
+ <Text size="sm" color="secondary">Step {step + 1} of {STEPS.length}</Text>
36
+ </Stack>
37
+ <Progress value={((step + 1) / STEPS.length) * 100} size="sm" />
38
+ <Divider />
39
+ {step === 0 && (
40
+ <Stack gap="4">
41
+ <FormField label="Full name" htmlFor="onb-name" required>
42
+ <Input id="onb-name" placeholder="Jane Doe" />
43
+ </FormField>
44
+ <FormField label="Email address" htmlFor="onb-email" required>
45
+ <Input id="onb-email" type="email" placeholder="jane@example.com" />
46
+ </FormField>
47
+ <FormField label="Role" htmlFor="onb-role">
48
+ <Select
49
+ id="onb-role"
50
+ defaultValue="engineer"
51
+ options={[
52
+ { value: 'engineer', label: 'Engineer' },
53
+ { value: 'designer', label: 'Designer' },
54
+ { value: 'product', label: 'Product Manager' },
55
+ { value: 'other', label: 'Other' },
56
+ ]}
57
+ />
58
+ </FormField>
59
+ </Stack>
60
+ )}
61
+ {step === 1 && (
62
+ <Stack gap="4">
63
+ <FormField label="Team size" htmlFor="onb-team">
64
+ <Select
65
+ id="onb-team"
66
+ defaultValue="small"
67
+ options={[
68
+ { value: 'solo', label: 'Just me' },
69
+ { value: 'small', label: '2–10 people' },
70
+ { value: 'medium', label: '11–50 people' },
71
+ { value: 'large', label: '50+ people' },
72
+ ]}
73
+ />
74
+ </FormField>
75
+ <Checkbox label="I agree to the terms of service" />
76
+ </Stack>
77
+ )}
78
+ {step === 2 && (
79
+ <Stack gap="4" align="center">
80
+ <Avatar alt="Jane Doe" size="xl" />
81
+ <Stack gap="1" align="center">
82
+ <Text size="lg" weight="semibold" family="display">Jane Doe</Text>
83
+ <Text size="sm" color="secondary">jane@example.com</Text>
84
+ </Stack>
85
+ <Chip variant="accent" size="sm">Engineer</Chip>
86
+ <Text size="sm" color="secondary" align="center">
87
+ Everything looks good. Click "Complete" to finish setup.
88
+ </Text>
89
+ </Stack>
90
+ )}
91
+ <Row justify="between">
92
+ <Button variant="ghost" size="sm" onClick={() => setStep(s => s - 1)} disabled={step === 0}>Back</Button>
93
+ <Button variant="primary" size="sm" onClick={() => setStep(s => Math.min(s + 1, 2))}>
94
+ {step === 2 ? 'Complete' : 'Continue'}
95
+ </Button>
96
+ </Row>
97
+ </Stack>
98
+ </Card>`,
99
+ designNotes: 'The Progress bar gives an at-a-glance sense of completion. The step indicator uses ' +
100
+ 'accent-colored circles for completed/current steps and neutral borders for future ' +
101
+ 'steps, with connector lines between them. Each step is a conditional render block ' +
102
+ 'so the form content swaps without layout shift (the Card maintains constant width). ' +
103
+ 'The confirmation step centers content with Avatar + name for a personal touch. ' +
104
+ 'Back button uses ghost variant and is disabled on step 0 to prevent confusion. ' +
105
+ 'justify="between" on the navigation row pushes Back and Continue to opposite edges, ' +
106
+ 'giving Back room to breathe so users don\'t accidentally go backwards.',
107
+ };
@@ -0,0 +1,108 @@
1
+ export const PATTERN = {
2
+ id: 'pricing-table',
3
+ name: 'Pricing Table',
4
+ description: 'Side-by-side pricing tier cards with feature lists and CTA buttons. Middle tier highlighted with accent border and primary button.',
5
+ category: 'card',
6
+ components: ['card', 'text', 'button', 'chip', 'stack', 'row', 'divider'],
7
+ structure: `
8
+ Row gap="4" wrap align="stretch"
9
+ ├── Card (outline, flex=1) ← free tier
10
+ │ └── Stack gap="5" (full height)
11
+ │ ├── Stack gap="2" ← tier header
12
+ │ │ ├── Text (sm, secondary) ← tier name
13
+ │ │ ├── Row align="baseline"
14
+ │ │ │ ├── Text (3xl, bold, display) ← price
15
+ │ │ │ └── Text (sm, secondary) ← period
16
+ │ │ └── Text (xs, secondary) ← tagline
17
+ │ ├── Divider
18
+ │ ├── Stack gap="3" (flex=1) ← feature list
19
+ │ │ └── Row[] gap="2" align="center"
20
+ │ │ ├── ✓ icon (success color)
21
+ │ │ └── Text (sm)
22
+ │ └── Button (outline, full width)
23
+ ├── Card (elevated, accent border, flex=1) ← pro tier (highlighted)
24
+ │ └── (same structure + Chip "Popular")
25
+ │ └── Button (primary, full width)
26
+ └── Card (outline, flex=1) ← enterprise tier
27
+ └── (same structure)
28
+ └── Button (outline, full width)
29
+ `.trim(),
30
+ code: `<Row gap="4" wrap align="stretch">
31
+ <Card variant="outline" padding="lg" style={{ flex: 1, minWidth: 240, maxWidth: 320 }}>
32
+ <Stack gap="5" style={{ height: '100%' }}>
33
+ <Stack gap="2">
34
+ <Text size="sm" weight="medium" color="secondary">Free</Text>
35
+ <Row gap="1" align="baseline">
36
+ <Text size="3xl" weight="bold" family="display">$0</Text>
37
+ <Text size="sm" color="secondary">/ month</Text>
38
+ </Row>
39
+ <Text size="xs" color="secondary">For individuals getting started.</Text>
40
+ </Stack>
41
+ <Divider />
42
+ <Stack gap="3" style={{ flex: 1 }}>
43
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Up to 3 projects</Text></Row>
44
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">1 GB storage</Text></Row>
45
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Community support</Text></Row>
46
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Basic analytics</Text></Row>
47
+ </Stack>
48
+ <Button variant="outline" style={{ width: '100%' }}>Get started</Button>
49
+ </Stack>
50
+ </Card>
51
+ <Card variant="elevated" padding="lg" style={{ flex: 1, minWidth: 240, maxWidth: 320, border: '2px solid var(--lucent-accent-default)' }}>
52
+ <Stack gap="5" style={{ height: '100%' }}>
53
+ <Stack gap="2">
54
+ <Row gap="2" align="center">
55
+ <Text size="sm" weight="medium" color="secondary">Pro</Text>
56
+ <Chip variant="accent" size="sm">Popular</Chip>
57
+ </Row>
58
+ <Row gap="1" align="baseline">
59
+ <Text size="3xl" weight="bold" family="display">$29</Text>
60
+ <Text size="sm" color="secondary">/ month</Text>
61
+ </Row>
62
+ <Text size="xs" color="secondary">For professionals and small teams.</Text>
63
+ </Stack>
64
+ <Divider />
65
+ <Stack gap="3" style={{ flex: 1 }}>
66
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Unlimited projects</Text></Row>
67
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">50 GB storage</Text></Row>
68
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Priority support</Text></Row>
69
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Advanced analytics</Text></Row>
70
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Custom domains</Text></Row>
71
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Team collaboration</Text></Row>
72
+ </Stack>
73
+ <Button variant="primary" style={{ width: '100%' }}>Upgrade to Pro</Button>
74
+ </Stack>
75
+ </Card>
76
+ <Card variant="outline" padding="lg" style={{ flex: 1, minWidth: 240, maxWidth: 320 }}>
77
+ <Stack gap="5" style={{ height: '100%' }}>
78
+ <Stack gap="2">
79
+ <Text size="sm" weight="medium" color="secondary">Enterprise</Text>
80
+ <Row gap="1" align="baseline">
81
+ <Text size="3xl" weight="bold" family="display">$99</Text>
82
+ <Text size="sm" color="secondary">/ month</Text>
83
+ </Row>
84
+ <Text size="xs" color="secondary">For organizations at scale.</Text>
85
+ </Stack>
86
+ <Divider />
87
+ <Stack gap="3" style={{ flex: 1 }}>
88
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Everything in Pro</Text></Row>
89
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Unlimited storage</Text></Row>
90
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">24/7 dedicated support</Text></Row>
91
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">SSO & SAML</Text></Row>
92
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Audit logs</Text></Row>
93
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">SLA guarantee</Text></Row>
94
+ <Row gap="2" align="center"><CheckIcon /><Text size="sm">Custom integrations</Text></Row>
95
+ </Stack>
96
+ <Button variant="outline" style={{ width: '100%' }}>Contact sales</Button>
97
+ </Stack>
98
+ </Card>
99
+ </Row>`,
100
+ designNotes: 'Three tiers in a Row with flex: 1 and minWidth/maxWidth constraints ensure equal ' +
101
+ 'sizing that wraps gracefully on narrow viewports. align="stretch" makes all cards ' +
102
+ 'equal height. The Pro tier is visually distinguished with an accent border and ' +
103
+ 'elevated variant so it draws the eye first. Each card uses Stack with height: 100% ' +
104
+ 'and the feature list has flex: 1 to push the CTA button to the bottom regardless ' +
105
+ 'of feature count. Price uses display font at 3xl for maximum impact; align="baseline" ' +
106
+ 'on the Row anchors the period text to the price baseline. The "Popular" Chip reinforces ' +
107
+ 'the recommended tier. CheckIcon uses success color for positive feature connotation.',
108
+ };
@@ -1,4 +1,4 @@
1
- export const RECIPE = {
1
+ export const PATTERN = {
2
2
  id: 'profile-card',
3
3
  name: 'Profile Card',
4
4
  description: 'User profile card with avatar, name/role, bio, tag chips, stats row, and action buttons.',
@@ -1,4 +1,4 @@
1
- export const RECIPE = {
1
+ export const PATTERN = {
2
2
  id: 'search-filter-bar',
3
3
  name: 'Search / Filter Bar',
4
4
  description: 'Compact toolbar of filter molecules — FilterSearch, FilterSelect, FilterMultiSelect, FilterDateRange — with sort control and view toggle. Designed to sit above a DataTable or list.',
@@ -1,4 +1,4 @@
1
- export const RECIPE = {
1
+ export const PATTERN = {
2
2
  id: 'settings-panel',
3
3
  name: 'Settings Panel',
4
4
  description: 'Settings card with section header, toggle rows with descriptions, select dropdown, and action buttons.',
@@ -1,4 +1,4 @@
1
- export const RECIPE = {
1
+ export const PATTERN = {
2
2
  id: 'stats-row',
3
3
  name: 'Stats Row',
4
4
  description: 'Row of individual stat cards with label, large display-font value, and trend chip with comparison.',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lucent-ui",
3
- "version": "0.30.0",
3
+ "version": "0.32.0",
4
4
  "description": "An AI-first React component library with machine-readable manifests.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -30,18 +30,6 @@
30
30
  "dist-server",
31
31
  "dist-cli"
32
32
  ],
33
- "scripts": {
34
- "dev": "vite --config vite.dev.config.ts",
35
- "build": "vite build",
36
- "build:server": "tsc -p server/tsconfig.json",
37
- "build:cli": "tsc -p cli/tsconfig.json && cp cli/template.manifest.json dist-cli/cli/template.manifest.json",
38
- "test": "vitest run",
39
- "test:watch": "vitest",
40
- "prepublishOnly": "tsc --noEmit && pnpm build && pnpm build:server && pnpm build:cli",
41
- "changeset": "changeset",
42
- "version-packages": "changeset version",
43
- "release": "pnpm prepublishOnly && changeset publish"
44
- },
45
33
  "keywords": [
46
34
  "react",
47
35
  "component-library",
@@ -56,7 +44,6 @@
56
44
  },
57
45
  "author": "Rozina Szogyenyi",
58
46
  "license": "MIT",
59
- "packageManager": "pnpm@10.30.3",
60
47
  "peerDependencies": {
61
48
  "react": "^18.0.0 || ^19.0.0",
62
49
  "react-dom": "^18.0.0 || ^19.0.0"
@@ -78,5 +65,16 @@
78
65
  "dependencies": {
79
66
  "@modelcontextprotocol/sdk": "^1.27.1",
80
67
  "zod": "^4.3.6"
68
+ },
69
+ "scripts": {
70
+ "dev": "vite --config vite.dev.config.ts",
71
+ "build": "vite build",
72
+ "build:server": "tsc -p server/tsconfig.json",
73
+ "build:cli": "tsc -p cli/tsconfig.json && cp cli/template.manifest.json dist-cli/cli/template.manifest.json",
74
+ "test": "vitest run",
75
+ "test:watch": "vitest",
76
+ "changeset": "changeset",
77
+ "version-packages": "changeset version",
78
+ "release": "pnpm prepublishOnly && changeset publish"
81
79
  }
82
- }
80
+ }
@@ -1,18 +0,0 @@
1
- import { RECIPE as ProfileCard } from '../src/manifest/recipes/profile-card.recipe.js';
2
- import { RECIPE as SettingsPanel } from '../src/manifest/recipes/settings-panel.recipe.js';
3
- import { RECIPE as StatsRow } from '../src/manifest/recipes/stats-row.recipe.js';
4
- import { RECIPE as ActionBar } from '../src/manifest/recipes/action-bar.recipe.js';
5
- import { RECIPE as FormLayout } from '../src/manifest/recipes/form-layout.recipe.js';
6
- import { RECIPE as EmptyStateCard } from '../src/manifest/recipes/empty-state-card.recipe.js';
7
- import { RECIPE as CollapsibleCard } from '../src/manifest/recipes/collapsible-card.recipe.js';
8
- import { RECIPE as SearchFilterBar } from '../src/manifest/recipes/search-filter-bar.recipe.js';
9
- export const ALL_RECIPES = [
10
- ProfileCard,
11
- SettingsPanel,
12
- StatsRow,
13
- ActionBar,
14
- FormLayout,
15
- EmptyStateCard,
16
- CollapsibleCard,
17
- SearchFilterBar,
18
- ];
@@ -1,8 +0,0 @@
1
- export { RECIPE as ProfileCard } from './profile-card.recipe.js';
2
- export { RECIPE as SettingsPanel } from './settings-panel.recipe.js';
3
- export { RECIPE as StatsRow } from './stats-row.recipe.js';
4
- export { RECIPE as ActionBar } from './action-bar.recipe.js';
5
- export { RECIPE as FormLayout } from './form-layout.recipe.js';
6
- export { RECIPE as EmptyStateCard } from './empty-state-card.recipe.js';
7
- export { RECIPE as CollapsibleCard } from './collapsible-card.recipe.js';
8
- export { RECIPE as SearchFilterBar } from './search-filter-bar.recipe.js';
@@ -1,28 +0,0 @@
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
- });