@lumeo-ui/mcp-server 2.0.0-rc.1
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 +129 -0
- package/dist/components.js +709 -0
- package/dist/index.js +357 -0
- package/dist/registry.js +86 -0
- package/package.json +39 -0
- package/src/registry.json +2538 -0
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-curated catalog of Lumeo's most-used components.
|
|
3
|
+
*
|
|
4
|
+
* Scope: top ~30 of the ~135 shipping components, focused on the ones
|
|
5
|
+
* LLMs most often need to emit markup for. Each entry carries enough
|
|
6
|
+
* information for an LLM to produce compilable, idiomatic Lumeo Razor
|
|
7
|
+
* without having to inspect the library source.
|
|
8
|
+
*/
|
|
9
|
+
// Shared base parameters every Lumeo component exposes.
|
|
10
|
+
const baseParams = [
|
|
11
|
+
{ name: "Class", type: "string?", default: "null", description: "Additional Tailwind classes merged onto the root element." },
|
|
12
|
+
{ name: "AdditionalAttributes", type: "Dictionary<string, object>?", default: "null", description: "Unmatched HTML attributes forwarded to the root element." },
|
|
13
|
+
];
|
|
14
|
+
const p = (items) => [...items, ...baseParams];
|
|
15
|
+
export const components = [
|
|
16
|
+
// ─────────────────────────── Forms ───────────────────────────
|
|
17
|
+
{
|
|
18
|
+
name: "Button",
|
|
19
|
+
category: "Forms",
|
|
20
|
+
description: "Versatile button with variants, sizes, icons, and loading state.",
|
|
21
|
+
params: p([
|
|
22
|
+
{ name: "Variant", type: "Button.ButtonVariant", default: "Default", description: "Default, Destructive, Outline, Secondary, Ghost, Link." },
|
|
23
|
+
{ name: "Size", type: "Button.ButtonSize", default: "Default", description: "Default, Sm, Lg, Icon." },
|
|
24
|
+
{ name: "Loading", type: "bool", default: "false", description: "Shows spinner and disables the button." },
|
|
25
|
+
{ name: "Disabled", type: "bool", default: "false", description: "Disables the button." },
|
|
26
|
+
{ name: "Type", type: "string", default: "\"button\"", description: "HTML button type: button, submit, reset." },
|
|
27
|
+
{ name: "OnClick", type: "EventCallback<MouseEventArgs>", default: "—", description: "Click handler." },
|
|
28
|
+
]),
|
|
29
|
+
slots: [
|
|
30
|
+
{ name: "ChildContent", description: "Button body (text)." },
|
|
31
|
+
{ name: "LeftIcon", description: "Icon rendered before the label." },
|
|
32
|
+
{ name: "RightIcon", description: "Icon rendered after the label." },
|
|
33
|
+
],
|
|
34
|
+
example: `<Button Variant="Button.ButtonVariant.Outline" Size="Button.ButtonSize.Sm" OnClick="HandleClick">
|
|
35
|
+
<LeftIcon><Icon Name="Plus" Size="Icon.IconSize.Sm" /></LeftIcon>
|
|
36
|
+
Create
|
|
37
|
+
</Button>`,
|
|
38
|
+
cssVars: ["--color-primary", "--color-primary-foreground", "--color-destructive", "--color-secondary"],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "Input",
|
|
42
|
+
category: "Forms",
|
|
43
|
+
description: "Single-line text input with two-way binding.",
|
|
44
|
+
params: p([
|
|
45
|
+
{ name: "Value", type: "string?", default: "null", description: "Current value (two-way bindable)." },
|
|
46
|
+
{ name: "ValueChanged", type: "EventCallback<string?>", default: "—", description: "Raised on input change." },
|
|
47
|
+
{ name: "Placeholder", type: "string?", default: "null", description: "Placeholder text." },
|
|
48
|
+
{ name: "Type", type: "string", default: "\"text\"", description: "HTML input type." },
|
|
49
|
+
{ name: "Disabled", type: "bool", default: "false", description: "Disables the input." },
|
|
50
|
+
{ name: "Invalid", type: "bool", default: "false", description: "Renders in error state." },
|
|
51
|
+
]),
|
|
52
|
+
slots: [
|
|
53
|
+
{ name: "Prefix", description: "Content before the input (icon, text)." },
|
|
54
|
+
{ name: "Suffix", description: "Content after the input." },
|
|
55
|
+
],
|
|
56
|
+
example: `<Input @bind-Value="_email" Type="email" Placeholder="you@example.com" />`,
|
|
57
|
+
cssVars: ["--color-input", "--color-ring"],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "PasswordInput",
|
|
61
|
+
category: "Forms",
|
|
62
|
+
description: "Password field with show/hide toggle and optional strength meter.",
|
|
63
|
+
params: p([
|
|
64
|
+
{ name: "Value", type: "string?", default: "null", description: "Current value (two-way bindable)." },
|
|
65
|
+
{ name: "ValueChanged", type: "EventCallback<string?>", default: "—", description: "Raised on change." },
|
|
66
|
+
{ name: "Placeholder", type: "string?", default: "null", description: "Placeholder text." },
|
|
67
|
+
{ name: "ShowStrengthMeter", type: "bool", default: "false", description: "Renders a strength meter below the field." },
|
|
68
|
+
]),
|
|
69
|
+
slots: [],
|
|
70
|
+
example: `<PasswordInput @bind-Value="_password" ShowStrengthMeter="true" />`,
|
|
71
|
+
cssVars: ["--color-input", "--color-ring"],
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: "NumberInput",
|
|
75
|
+
category: "Forms",
|
|
76
|
+
description: "Numeric input with step, min, max, and increment/decrement buttons.",
|
|
77
|
+
params: p([
|
|
78
|
+
{ name: "Value", type: "decimal?", default: "null", description: "Current value (two-way bindable)." },
|
|
79
|
+
{ name: "ValueChanged", type: "EventCallback<decimal?>", default: "—", description: "Raised on change." },
|
|
80
|
+
{ name: "Min", type: "decimal?", default: "null", description: "Minimum allowed value." },
|
|
81
|
+
{ name: "Max", type: "decimal?", default: "null", description: "Maximum allowed value." },
|
|
82
|
+
{ name: "Step", type: "decimal", default: "1", description: "Increment step." },
|
|
83
|
+
]),
|
|
84
|
+
slots: [],
|
|
85
|
+
example: `<NumberInput @bind-Value="_quantity" Min="0" Max="100" Step="1" />`,
|
|
86
|
+
cssVars: ["--color-input"],
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: "Checkbox",
|
|
90
|
+
category: "Forms",
|
|
91
|
+
description: "Boolean checkbox with optional label.",
|
|
92
|
+
params: p([
|
|
93
|
+
{ name: "Checked", type: "bool", default: "false", description: "Checked state (two-way bindable as Checked/CheckedChanged)." },
|
|
94
|
+
{ name: "CheckedChanged", type: "EventCallback<bool>", default: "—", description: "Raised on toggle." },
|
|
95
|
+
{ name: "Disabled", type: "bool", default: "false", description: "Disables the checkbox." },
|
|
96
|
+
{ name: "Label", type: "string?", default: "null", description: "Text label next to the checkbox." },
|
|
97
|
+
]),
|
|
98
|
+
slots: [
|
|
99
|
+
{ name: "ChildContent", description: "Custom label content (overrides Label)." },
|
|
100
|
+
],
|
|
101
|
+
example: `<Checkbox @bind-Checked="_agree" Label="I agree to the terms" />`,
|
|
102
|
+
cssVars: ["--color-primary", "--color-primary-foreground"],
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "Switch",
|
|
106
|
+
category: "Forms",
|
|
107
|
+
description: "On/off toggle switch.",
|
|
108
|
+
params: p([
|
|
109
|
+
{ name: "Checked", type: "bool", default: "false", description: "On/off state (two-way bindable)." },
|
|
110
|
+
{ name: "CheckedChanged", type: "EventCallback<bool>", default: "—", description: "Raised on toggle." },
|
|
111
|
+
{ name: "Disabled", type: "bool", default: "false", description: "Disables the switch." },
|
|
112
|
+
]),
|
|
113
|
+
slots: [],
|
|
114
|
+
example: `<Switch @bind-Checked="_notifications" />`,
|
|
115
|
+
cssVars: ["--color-primary"],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: "Select",
|
|
119
|
+
category: "Forms",
|
|
120
|
+
description: "Dropdown select with options.",
|
|
121
|
+
params: p([
|
|
122
|
+
{ name: "Value", type: "TValue?", default: "default", description: "Current selection (two-way bindable)." },
|
|
123
|
+
{ name: "ValueChanged", type: "EventCallback<TValue?>", default: "—", description: "Raised on selection." },
|
|
124
|
+
{ name: "Placeholder", type: "string?", default: "null", description: "Placeholder when no value selected." },
|
|
125
|
+
]),
|
|
126
|
+
slots: [
|
|
127
|
+
{ name: "ChildContent", description: "One or more <SelectItem Value=\"...\">…</SelectItem> entries." },
|
|
128
|
+
],
|
|
129
|
+
example: `<Select TValue="string" @bind-Value="_country" Placeholder="Select a country">
|
|
130
|
+
<SelectItem Value="@("us")">United States</SelectItem>
|
|
131
|
+
<SelectItem Value="@("de")">Germany</SelectItem>
|
|
132
|
+
<SelectItem Value="@("fr")">France</SelectItem>
|
|
133
|
+
</Select>`,
|
|
134
|
+
cssVars: ["--color-popover", "--color-popover-foreground", "--color-accent"],
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: "Combobox",
|
|
138
|
+
category: "Forms",
|
|
139
|
+
description: "Searchable dropdown select, supports async data and custom item templates.",
|
|
140
|
+
params: p([
|
|
141
|
+
{ name: "Items", type: "IEnumerable<TItem>", default: "[]", description: "Source list." },
|
|
142
|
+
{ name: "Value", type: "TItem?", default: "default", description: "Current selection (two-way bindable)." },
|
|
143
|
+
{ name: "ValueChanged", type: "EventCallback<TItem?>", default: "—", description: "Raised on selection." },
|
|
144
|
+
{ name: "ItemLabel", type: "Func<TItem, string>", default: "—", description: "Maps item to display label." },
|
|
145
|
+
{ name: "Placeholder", type: "string?", default: "null", description: "Placeholder text." },
|
|
146
|
+
]),
|
|
147
|
+
slots: [
|
|
148
|
+
{ name: "ItemTemplate", description: "Custom template per item (RenderFragment<TItem>)." },
|
|
149
|
+
],
|
|
150
|
+
example: `<Combobox TItem="string" Items="_countries" @bind-Value="_country" ItemLabel="c => c" Placeholder="Search country..." />`,
|
|
151
|
+
cssVars: ["--color-popover", "--color-accent"],
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: "DatePicker",
|
|
155
|
+
category: "Forms",
|
|
156
|
+
description: "Calendar date picker with popover.",
|
|
157
|
+
params: p([
|
|
158
|
+
{ name: "Value", type: "DateOnly?", default: "null", description: "Selected date (two-way bindable)." },
|
|
159
|
+
{ name: "ValueChanged", type: "EventCallback<DateOnly?>", default: "—", description: "Raised on selection." },
|
|
160
|
+
{ name: "MinDate", type: "DateOnly?", default: "null", description: "Earliest selectable date." },
|
|
161
|
+
{ name: "MaxDate", type: "DateOnly?", default: "null", description: "Latest selectable date." },
|
|
162
|
+
{ name: "Placeholder", type: "string?", default: "null", description: "Placeholder text when empty." },
|
|
163
|
+
]),
|
|
164
|
+
slots: [],
|
|
165
|
+
example: `<DatePicker @bind-Value="_dob" Placeholder="Pick a date" />`,
|
|
166
|
+
cssVars: ["--color-popover", "--color-accent"],
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
name: "Slider",
|
|
170
|
+
category: "Forms",
|
|
171
|
+
description: "Range slider for numeric values.",
|
|
172
|
+
params: p([
|
|
173
|
+
{ name: "Value", type: "double", default: "0", description: "Current value (two-way bindable)." },
|
|
174
|
+
{ name: "ValueChanged", type: "EventCallback<double>", default: "—", description: "Raised on change." },
|
|
175
|
+
{ name: "Min", type: "double", default: "0", description: "Minimum value." },
|
|
176
|
+
{ name: "Max", type: "double", default: "100", description: "Maximum value." },
|
|
177
|
+
{ name: "Step", type: "double", default: "1", description: "Step size." },
|
|
178
|
+
]),
|
|
179
|
+
slots: [],
|
|
180
|
+
example: `<Slider @bind-Value="_volume" Min="0" Max="100" Step="5" />`,
|
|
181
|
+
cssVars: ["--color-primary", "--color-muted"],
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: "Textarea",
|
|
185
|
+
category: "Forms",
|
|
186
|
+
description: "Multi-line text input with optional auto-resize.",
|
|
187
|
+
params: p([
|
|
188
|
+
{ name: "Value", type: "string?", default: "null", description: "Current value (two-way bindable)." },
|
|
189
|
+
{ name: "ValueChanged", type: "EventCallback<string?>", default: "—", description: "Raised on input." },
|
|
190
|
+
{ name: "Rows", type: "int", default: "3", description: "Visible rows." },
|
|
191
|
+
{ name: "AutoResize", type: "bool", default: "false", description: "Grow height to fit content." },
|
|
192
|
+
{ name: "Placeholder", type: "string?", default: "null", description: "Placeholder text." },
|
|
193
|
+
]),
|
|
194
|
+
slots: [],
|
|
195
|
+
example: `<Textarea @bind-Value="_message" Rows="4" Placeholder="Your message..." AutoResize="true" />`,
|
|
196
|
+
cssVars: ["--color-input"],
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
name: "Form",
|
|
200
|
+
category: "Forms",
|
|
201
|
+
description: "Form wrapper integrating with EditForm + DataAnnotations validation.",
|
|
202
|
+
params: p([
|
|
203
|
+
{ name: "Model", type: "object", default: "—", description: "Bound model instance." },
|
|
204
|
+
{ name: "OnValidSubmit", type: "EventCallback<EditContext>", default: "—", description: "Raised when the form validates and submits." },
|
|
205
|
+
]),
|
|
206
|
+
slots: [
|
|
207
|
+
{ name: "ChildContent", description: "FormField entries and submit button." },
|
|
208
|
+
],
|
|
209
|
+
example: `<Form Model="_model" OnValidSubmit="HandleSubmit">
|
|
210
|
+
<FormField For="() => _model.Email" Label="Email">
|
|
211
|
+
<Input @bind-Value="_model.Email" Type="email" />
|
|
212
|
+
</FormField>
|
|
213
|
+
<Button Type="submit">Save</Button>
|
|
214
|
+
</Form>`,
|
|
215
|
+
cssVars: [],
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: "FormField",
|
|
219
|
+
category: "Forms",
|
|
220
|
+
description: "Labelled form row with validation message binding.",
|
|
221
|
+
params: p([
|
|
222
|
+
{ name: "For", type: "Expression<Func<object?>>", default: "—", description: "Lambda selecting the model property." },
|
|
223
|
+
{ name: "Label", type: "string?", default: "null", description: "Label text." },
|
|
224
|
+
{ name: "HelpText", type: "string?", default: "null", description: "Description beneath the field." },
|
|
225
|
+
{ name: "Required", type: "bool", default: "false", description: "Shows required asterisk." },
|
|
226
|
+
]),
|
|
227
|
+
slots: [
|
|
228
|
+
{ name: "ChildContent", description: "The input control (Input, Select, etc.)." },
|
|
229
|
+
],
|
|
230
|
+
example: `<FormField For="() => _model.Name" Label="Full name" Required="true">
|
|
231
|
+
<Input @bind-Value="_model.Name" />
|
|
232
|
+
</FormField>`,
|
|
233
|
+
cssVars: [],
|
|
234
|
+
},
|
|
235
|
+
// ─────────────────────────── Layout ───────────────────────────
|
|
236
|
+
{
|
|
237
|
+
name: "Card",
|
|
238
|
+
category: "Layout",
|
|
239
|
+
description: "Content surface with optional header, body and footer slots.",
|
|
240
|
+
params: p([]),
|
|
241
|
+
slots: [
|
|
242
|
+
{ name: "ChildContent", description: "Full card body (use CardHeader/CardContent/CardFooter inside)." },
|
|
243
|
+
],
|
|
244
|
+
example: `<Card>
|
|
245
|
+
<CardHeader>
|
|
246
|
+
<CardTitle>Team members</CardTitle>
|
|
247
|
+
<CardDescription>Invite your team to collaborate.</CardDescription>
|
|
248
|
+
</CardHeader>
|
|
249
|
+
<CardContent>
|
|
250
|
+
<Lumeo.Text>Content goes here.</Lumeo.Text>
|
|
251
|
+
</CardContent>
|
|
252
|
+
<CardFooter>
|
|
253
|
+
<Button>Invite</Button>
|
|
254
|
+
</CardFooter>
|
|
255
|
+
</Card>`,
|
|
256
|
+
cssVars: ["--color-card", "--color-card-foreground", "--color-border"],
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
name: "Bento",
|
|
260
|
+
category: "Layout",
|
|
261
|
+
description: "Bento grid — asymmetric feature grid with spannable cells.",
|
|
262
|
+
params: p([
|
|
263
|
+
{ name: "Columns", type: "int", default: "3", description: "Base column count on md+ screens." },
|
|
264
|
+
{ name: "Gap", type: "int", default: "4", description: "Tailwind gap value (0-12)." },
|
|
265
|
+
]),
|
|
266
|
+
slots: [
|
|
267
|
+
{ name: "ChildContent", description: "One or more <BentoCell> children." },
|
|
268
|
+
],
|
|
269
|
+
example: `<Bento Columns="3" Gap="4">
|
|
270
|
+
<BentoCell ColSpan="2" RowSpan="2">
|
|
271
|
+
<Heading Level="3">Analytics</Heading>
|
|
272
|
+
</BentoCell>
|
|
273
|
+
<BentoCell>Stat 1</BentoCell>
|
|
274
|
+
<BentoCell>Stat 2</BentoCell>
|
|
275
|
+
</Bento>`,
|
|
276
|
+
cssVars: ["--color-card", "--color-border"],
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
name: "Splitter",
|
|
280
|
+
category: "Layout",
|
|
281
|
+
description: "Resizable split pane container (horizontal or vertical).",
|
|
282
|
+
params: p([
|
|
283
|
+
{ name: "Orientation", type: "Splitter.Direction", default: "Horizontal", description: "Horizontal or Vertical split." },
|
|
284
|
+
{ name: "DefaultSize", type: "double", default: "50", description: "Initial first-pane size percent." },
|
|
285
|
+
]),
|
|
286
|
+
slots: [
|
|
287
|
+
{ name: "First", description: "Left/top pane." },
|
|
288
|
+
{ name: "Second", description: "Right/bottom pane." },
|
|
289
|
+
],
|
|
290
|
+
example: `<Splitter Orientation="Splitter.Direction.Horizontal" DefaultSize="30">
|
|
291
|
+
<First><Sidebar /></First>
|
|
292
|
+
<Second><main>Content</main></Second>
|
|
293
|
+
</Splitter>`,
|
|
294
|
+
cssVars: ["--color-border"],
|
|
295
|
+
},
|
|
296
|
+
// ─────────────────────────── Overlay ───────────────────────────
|
|
297
|
+
{
|
|
298
|
+
name: "Dialog",
|
|
299
|
+
category: "Overlay",
|
|
300
|
+
description: "Modal dialog with backdrop, focus trap, and scroll lock.",
|
|
301
|
+
params: p([
|
|
302
|
+
{ name: "Open", type: "bool", default: "false", description: "Open state (two-way bindable)." },
|
|
303
|
+
{ name: "OpenChanged", type: "EventCallback<bool>", default: "—", description: "Raised on open/close." },
|
|
304
|
+
]),
|
|
305
|
+
slots: [
|
|
306
|
+
{ name: "ChildContent", description: "DialogHeader / DialogContent / DialogFooter." },
|
|
307
|
+
],
|
|
308
|
+
example: `<Dialog @bind-Open="_dialogOpen">
|
|
309
|
+
<DialogHeader>
|
|
310
|
+
<DialogTitle>Are you sure?</DialogTitle>
|
|
311
|
+
<DialogDescription>This cannot be undone.</DialogDescription>
|
|
312
|
+
</DialogHeader>
|
|
313
|
+
<DialogFooter>
|
|
314
|
+
<Button Variant="Button.ButtonVariant.Outline" OnClick='() => _dialogOpen = false'>Cancel</Button>
|
|
315
|
+
<Button Variant="Button.ButtonVariant.Destructive" OnClick="Confirm">Delete</Button>
|
|
316
|
+
</DialogFooter>
|
|
317
|
+
</Dialog>`,
|
|
318
|
+
cssVars: ["--color-background", "--color-foreground", "--color-border"],
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
name: "Sheet",
|
|
322
|
+
category: "Overlay",
|
|
323
|
+
description: "Slide-in side panel (left, right, top, bottom).",
|
|
324
|
+
params: p([
|
|
325
|
+
{ name: "Open", type: "bool", default: "false", description: "Open state (two-way bindable)." },
|
|
326
|
+
{ name: "OpenChanged", type: "EventCallback<bool>", default: "—", description: "Raised on open/close." },
|
|
327
|
+
{ name: "Side", type: "Sheet.SheetSide", default: "Right", description: "Left, Right, Top, Bottom." },
|
|
328
|
+
]),
|
|
329
|
+
slots: [
|
|
330
|
+
{ name: "ChildContent", description: "SheetHeader / SheetContent / SheetFooter." },
|
|
331
|
+
],
|
|
332
|
+
example: `<Sheet @bind-Open="_sheetOpen" Side="Sheet.SheetSide.Right">
|
|
333
|
+
<SheetHeader>
|
|
334
|
+
<SheetTitle>Edit profile</SheetTitle>
|
|
335
|
+
</SheetHeader>
|
|
336
|
+
<SheetContent>
|
|
337
|
+
<Input @bind-Value="_name" />
|
|
338
|
+
</SheetContent>
|
|
339
|
+
</Sheet>`,
|
|
340
|
+
cssVars: ["--color-background", "--color-border"],
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
name: "Popover",
|
|
344
|
+
category: "Overlay",
|
|
345
|
+
description: "Anchored floating panel, click to open/close.",
|
|
346
|
+
params: p([
|
|
347
|
+
{ name: "Open", type: "bool", default: "false", description: "Open state (two-way bindable)." },
|
|
348
|
+
{ name: "OpenChanged", type: "EventCallback<bool>", default: "—", description: "Raised on toggle." },
|
|
349
|
+
{ name: "Placement", type: "string", default: "\"bottom\"", description: "top, right, bottom, left (with -start/-end variants)." },
|
|
350
|
+
]),
|
|
351
|
+
slots: [
|
|
352
|
+
{ name: "TriggerContent", description: "The element that toggles the popover." },
|
|
353
|
+
{ name: "ChildContent", description: "Popover body." },
|
|
354
|
+
],
|
|
355
|
+
example: `<Popover Placement="bottom-start">
|
|
356
|
+
<TriggerContent><Button>Options</Button></TriggerContent>
|
|
357
|
+
<ChildContent>
|
|
358
|
+
<Stack Gap="2" Class="p-3">
|
|
359
|
+
<Lumeo.Text>Hello from the popover.</Lumeo.Text>
|
|
360
|
+
</Stack>
|
|
361
|
+
</ChildContent>
|
|
362
|
+
</Popover>`,
|
|
363
|
+
cssVars: ["--color-popover", "--color-popover-foreground"],
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
name: "Tooltip",
|
|
367
|
+
category: "Overlay",
|
|
368
|
+
description: "Hover/focus tooltip.",
|
|
369
|
+
params: p([
|
|
370
|
+
{ name: "Content", type: "string?", default: "null", description: "Tooltip text (shortcut for Content slot)." },
|
|
371
|
+
{ name: "Placement", type: "string", default: "\"top\"", description: "top, right, bottom, left." },
|
|
372
|
+
{ name: "Delay", type: "int", default: "300", description: "Open delay in ms." },
|
|
373
|
+
]),
|
|
374
|
+
slots: [
|
|
375
|
+
{ name: "ChildContent", description: "Element that triggers the tooltip." },
|
|
376
|
+
{ name: "ContentTemplate", description: "Custom tooltip body (overrides Content)." },
|
|
377
|
+
],
|
|
378
|
+
example: `<Tooltip Content="Save the document" Placement="top">
|
|
379
|
+
<Button Size="Button.ButtonSize.Icon"><Icon Name="Save" /></Button>
|
|
380
|
+
</Tooltip>`,
|
|
381
|
+
cssVars: ["--color-popover", "--color-popover-foreground"],
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
name: "Toast",
|
|
385
|
+
category: "Overlay",
|
|
386
|
+
description: "Transient notification. Trigger from C# via ToastService.",
|
|
387
|
+
params: p([]),
|
|
388
|
+
slots: [],
|
|
389
|
+
example: `@inject ToastService Toasts
|
|
390
|
+
|
|
391
|
+
<Button OnClick='() => Toasts.Show("Saved", variant: ToastVariant.Success)'>Save</Button>
|
|
392
|
+
|
|
393
|
+
@* Required once in MainLayout: *@
|
|
394
|
+
<ToastProvider />`,
|
|
395
|
+
cssVars: ["--color-background", "--color-foreground", "--color-border"],
|
|
396
|
+
},
|
|
397
|
+
// ─────────────────────────── Data ───────────────────────────
|
|
398
|
+
{
|
|
399
|
+
name: "DataGrid",
|
|
400
|
+
category: "Data",
|
|
401
|
+
description: "Feature-rich data grid — sort, filter, paginate, select, group, virtualize, export.",
|
|
402
|
+
params: p([
|
|
403
|
+
{ name: "Items", type: "IEnumerable<TItem>", default: "[]", description: "Source data." },
|
|
404
|
+
{ name: "Sortable", type: "bool", default: "true", description: "Enable column sorting." },
|
|
405
|
+
{ name: "Filterable", type: "bool", default: "false", description: "Show column filters." },
|
|
406
|
+
{ name: "PageSize", type: "int?", default: "null", description: "Rows per page (null disables pagination)." },
|
|
407
|
+
{ name: "Selectable", type: "bool", default: "false", description: "Render a selection checkbox column." },
|
|
408
|
+
]),
|
|
409
|
+
slots: [
|
|
410
|
+
{ name: "Columns", description: "One or more <DataGridColumn TItem=\"...\"> children." },
|
|
411
|
+
],
|
|
412
|
+
example: `<DataGrid TItem="User" Items="_users" Sortable="true" PageSize="10">
|
|
413
|
+
<Columns>
|
|
414
|
+
<DataGridColumn TItem="User" Field="@(u => u.Name)" Title="Name" />
|
|
415
|
+
<DataGridColumn TItem="User" Field="@(u => u.Email)" Title="Email" />
|
|
416
|
+
<DataGridColumn TItem="User" Field="@(u => u.Role)" Title="Role" />
|
|
417
|
+
</Columns>
|
|
418
|
+
</DataGrid>`,
|
|
419
|
+
cssVars: ["--color-border", "--color-muted", "--color-card"],
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
name: "Avatar",
|
|
423
|
+
category: "Data",
|
|
424
|
+
description: "User avatar with image, initials fallback, optional status dot.",
|
|
425
|
+
params: p([
|
|
426
|
+
{ name: "Src", type: "string?", default: "null", description: "Image URL." },
|
|
427
|
+
{ name: "Alt", type: "string?", default: "null", description: "Alt text." },
|
|
428
|
+
{ name: "Fallback", type: "string?", default: "null", description: "Initials fallback (e.g. \"MB\")." },
|
|
429
|
+
{ name: "Size", type: "Avatar.AvatarSize", default: "Md", description: "Xs, Sm, Md, Lg, Xl." },
|
|
430
|
+
]),
|
|
431
|
+
slots: [],
|
|
432
|
+
example: `<Avatar Src="/img/mike.jpg" Fallback="MB" Size="Avatar.AvatarSize.Md" Alt="Mike Berger" />`,
|
|
433
|
+
cssVars: ["--color-muted", "--color-muted-foreground"],
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
name: "Badge",
|
|
437
|
+
category: "Data",
|
|
438
|
+
description: "Small status/label pill.",
|
|
439
|
+
params: p([
|
|
440
|
+
{ name: "Variant", type: "Badge.BadgeVariant", default: "Default", description: "Default, Secondary, Destructive, Outline, Success, Warning." },
|
|
441
|
+
]),
|
|
442
|
+
slots: [
|
|
443
|
+
{ name: "ChildContent", description: "Badge content." },
|
|
444
|
+
{ name: "IconContent", description: "Leading icon." },
|
|
445
|
+
],
|
|
446
|
+
example: `<Badge Variant="Badge.BadgeVariant.Success">Active</Badge>`,
|
|
447
|
+
cssVars: ["--color-primary", "--color-destructive", "--color-secondary"],
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
name: "Table",
|
|
451
|
+
category: "Data",
|
|
452
|
+
description: "Lightweight table primitive (manual rows). For feature-rich data use DataGrid.",
|
|
453
|
+
params: p([]),
|
|
454
|
+
slots: [
|
|
455
|
+
{ name: "ChildContent", description: "TableHeader, TableBody, TableRow, TableCell children." },
|
|
456
|
+
],
|
|
457
|
+
example: `<Table>
|
|
458
|
+
<TableHeader>
|
|
459
|
+
<TableRow>
|
|
460
|
+
<TableHead>Name</TableHead>
|
|
461
|
+
<TableHead>Email</TableHead>
|
|
462
|
+
</TableRow>
|
|
463
|
+
</TableHeader>
|
|
464
|
+
<TableBody>
|
|
465
|
+
@foreach (var u in _users)
|
|
466
|
+
{
|
|
467
|
+
<TableRow>
|
|
468
|
+
<TableCell>@u.Name</TableCell>
|
|
469
|
+
<TableCell>@u.Email</TableCell>
|
|
470
|
+
</TableRow>
|
|
471
|
+
}
|
|
472
|
+
</TableBody>
|
|
473
|
+
</Table>`,
|
|
474
|
+
cssVars: ["--color-border", "--color-muted"],
|
|
475
|
+
},
|
|
476
|
+
// ─────────────────────────── Navigation ───────────────────────────
|
|
477
|
+
{
|
|
478
|
+
name: "Tabs",
|
|
479
|
+
category: "Navigation",
|
|
480
|
+
description: "Tabbed content switcher.",
|
|
481
|
+
params: p([
|
|
482
|
+
{ name: "Value", type: "string?", default: "null", description: "Active tab value (two-way bindable)." },
|
|
483
|
+
{ name: "ValueChanged", type: "EventCallback<string?>", default: "—", description: "Raised on change." },
|
|
484
|
+
]),
|
|
485
|
+
slots: [
|
|
486
|
+
{ name: "ChildContent", description: "TabsList + TabsContent blocks." },
|
|
487
|
+
],
|
|
488
|
+
example: `<Tabs @bind-Value="_activeTab">
|
|
489
|
+
<TabsList>
|
|
490
|
+
<TabsTrigger Value="@("overview")">Overview</TabsTrigger>
|
|
491
|
+
<TabsTrigger Value="@("settings")">Settings</TabsTrigger>
|
|
492
|
+
</TabsList>
|
|
493
|
+
<TabsContent Value="@("overview")">Overview content</TabsContent>
|
|
494
|
+
<TabsContent Value="@("settings")">Settings content</TabsContent>
|
|
495
|
+
</Tabs>`,
|
|
496
|
+
cssVars: ["--color-muted", "--color-background"],
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
name: "Sidebar",
|
|
500
|
+
category: "Navigation",
|
|
501
|
+
description: "App sidebar with collapsible menu groups, items and icons.",
|
|
502
|
+
params: p([
|
|
503
|
+
{ name: "Collapsed", type: "bool", default: "false", description: "Collapsed state (two-way bindable)." },
|
|
504
|
+
{ name: "CollapsedChanged", type: "EventCallback<bool>", default: "—", description: "Raised on toggle." },
|
|
505
|
+
]),
|
|
506
|
+
slots: [
|
|
507
|
+
{ name: "ChildContent", description: "SidebarHeader, SidebarContent (with SidebarMenu/SidebarMenuButton), SidebarFooter." },
|
|
508
|
+
],
|
|
509
|
+
example: `<Sidebar @bind-Collapsed="_collapsed">
|
|
510
|
+
<SidebarHeader><Heading Level="4">Acme</Heading></SidebarHeader>
|
|
511
|
+
<SidebarContent>
|
|
512
|
+
<SidebarMenu>
|
|
513
|
+
<SidebarMenuButton Href="/dashboard">
|
|
514
|
+
<IconContent><Icon Name="LayoutDashboard" /></IconContent>
|
|
515
|
+
Dashboard
|
|
516
|
+
</SidebarMenuButton>
|
|
517
|
+
</SidebarMenu>
|
|
518
|
+
</SidebarContent>
|
|
519
|
+
</Sidebar>`,
|
|
520
|
+
cssVars: ["--color-sidebar", "--color-sidebar-foreground", "--color-sidebar-accent"],
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
name: "Breadcrumb",
|
|
524
|
+
category: "Navigation",
|
|
525
|
+
description: "Hierarchical navigation trail.",
|
|
526
|
+
params: p([]),
|
|
527
|
+
slots: [
|
|
528
|
+
{ name: "ChildContent", description: "BreadcrumbItem / BreadcrumbSeparator entries." },
|
|
529
|
+
],
|
|
530
|
+
example: `<Breadcrumb>
|
|
531
|
+
<BreadcrumbItem Href="/">Home</BreadcrumbItem>
|
|
532
|
+
<BreadcrumbSeparator />
|
|
533
|
+
<BreadcrumbItem Href="/settings">Settings</BreadcrumbItem>
|
|
534
|
+
<BreadcrumbSeparator />
|
|
535
|
+
<BreadcrumbItem>Profile</BreadcrumbItem>
|
|
536
|
+
</Breadcrumb>`,
|
|
537
|
+
cssVars: ["--color-muted-foreground", "--color-foreground"],
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
name: "Pagination",
|
|
541
|
+
category: "Navigation",
|
|
542
|
+
description: "Page navigation control.",
|
|
543
|
+
params: p([
|
|
544
|
+
{ name: "Page", type: "int", default: "1", description: "Current page (1-based, two-way bindable)." },
|
|
545
|
+
{ name: "PageChanged", type: "EventCallback<int>", default: "—", description: "Raised on page change." },
|
|
546
|
+
{ name: "TotalPages", type: "int", default: "1", description: "Total page count." },
|
|
547
|
+
{ name: "SiblingCount", type: "int", default: "1", description: "Pages shown around the current one." },
|
|
548
|
+
]),
|
|
549
|
+
slots: [],
|
|
550
|
+
example: `<Pagination @bind-Page="_page" TotalPages="10" />`,
|
|
551
|
+
cssVars: ["--color-border", "--color-accent"],
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
name: "BottomNav",
|
|
555
|
+
category: "Navigation",
|
|
556
|
+
description: "Mobile bottom navigation bar.",
|
|
557
|
+
params: p([
|
|
558
|
+
{ name: "Value", type: "string?", default: "null", description: "Active item value (two-way bindable)." },
|
|
559
|
+
{ name: "ValueChanged", type: "EventCallback<string?>", default: "—", description: "Raised on selection." },
|
|
560
|
+
]),
|
|
561
|
+
slots: [
|
|
562
|
+
{ name: "ChildContent", description: "One or more BottomNavItem children." },
|
|
563
|
+
],
|
|
564
|
+
example: `<BottomNav @bind-Value="_tab">
|
|
565
|
+
<BottomNavItem Value="@("home")"><Icon Name="Home" />Home</BottomNavItem>
|
|
566
|
+
<BottomNavItem Value="@("search")"><Icon Name="Search" />Search</BottomNavItem>
|
|
567
|
+
<BottomNavItem Value="@("profile")"><Icon Name="User" />Profile</BottomNavItem>
|
|
568
|
+
</BottomNav>`,
|
|
569
|
+
cssVars: ["--color-background", "--color-border", "--color-primary"],
|
|
570
|
+
},
|
|
571
|
+
// ─────────────────────────── AI ───────────────────────────
|
|
572
|
+
{
|
|
573
|
+
name: "PromptInput",
|
|
574
|
+
category: "AI",
|
|
575
|
+
description: "Chat/prompt input — auto-resizing textarea with send button and attachments.",
|
|
576
|
+
params: p([
|
|
577
|
+
{ name: "Value", type: "string?", default: "null", description: "Draft prompt (two-way bindable)." },
|
|
578
|
+
{ name: "ValueChanged", type: "EventCallback<string?>", default: "—", description: "Raised on change." },
|
|
579
|
+
{ name: "OnSubmit", type: "EventCallback<string>", default: "—", description: "Raised on send (Enter or button)." },
|
|
580
|
+
{ name: "Placeholder", type: "string?", default: "\"Ask anything...\"", description: "Placeholder text." },
|
|
581
|
+
{ name: "Disabled", type: "bool", default: "false", description: "Disable send while streaming." },
|
|
582
|
+
]),
|
|
583
|
+
slots: [
|
|
584
|
+
{ name: "Actions", description: "Extra buttons (attach, model picker) beside send." },
|
|
585
|
+
],
|
|
586
|
+
example: `<PromptInput @bind-Value="_draft" OnSubmit="HandleSend" Disabled="_isStreaming" />`,
|
|
587
|
+
cssVars: ["--color-input", "--color-ring", "--color-primary"],
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
name: "StreamingText",
|
|
591
|
+
category: "AI",
|
|
592
|
+
description: "Renders a token stream with cursor, supports markdown and auto-scroll.",
|
|
593
|
+
params: p([
|
|
594
|
+
{ name: "Text", type: "string", default: "\"\"", description: "Current accumulated text." },
|
|
595
|
+
{ name: "IsStreaming", type: "bool", default: "false", description: "Show blinking cursor while true." },
|
|
596
|
+
{ name: "Markdown", type: "bool", default: "true", description: "Render as markdown." },
|
|
597
|
+
]),
|
|
598
|
+
slots: [],
|
|
599
|
+
example: `<StreamingText Text="@_assistantMessage" IsStreaming="_isStreaming" Markdown="true" />`,
|
|
600
|
+
cssVars: ["--color-foreground", "--color-muted-foreground"],
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
name: "AgentMessage",
|
|
604
|
+
category: "AI",
|
|
605
|
+
description: "Single chat message bubble — user or assistant.",
|
|
606
|
+
params: p([
|
|
607
|
+
{ name: "Role", type: "AgentMessage.MessageRole", default: "Assistant", description: "User, Assistant, System." },
|
|
608
|
+
{ name: "Avatar", type: "string?", default: "null", description: "Avatar URL." },
|
|
609
|
+
{ name: "Name", type: "string?", default: "null", description: "Display name." },
|
|
610
|
+
]),
|
|
611
|
+
slots: [
|
|
612
|
+
{ name: "ChildContent", description: "Message body (markdown, tool calls, etc.)." },
|
|
613
|
+
{ name: "Actions", description: "Per-message actions (copy, regenerate)." },
|
|
614
|
+
],
|
|
615
|
+
example: `<AgentMessage Role="AgentMessage.MessageRole.Assistant" Name="Claude">
|
|
616
|
+
<StreamingText Text="@_message" IsStreaming="false" />
|
|
617
|
+
</AgentMessage>`,
|
|
618
|
+
cssVars: ["--color-card", "--color-muted", "--color-border"],
|
|
619
|
+
},
|
|
620
|
+
// ─────────────────────────── Motion ───────────────────────────
|
|
621
|
+
{
|
|
622
|
+
name: "Marquee",
|
|
623
|
+
category: "Motion",
|
|
624
|
+
description: "Auto-scrolling horizontal or vertical marquee.",
|
|
625
|
+
params: p([
|
|
626
|
+
{ name: "Direction", type: "Marquee.MarqueeDirection", default: "Left", description: "Left, Right, Up, Down." },
|
|
627
|
+
{ name: "Speed", type: "double", default: "40", description: "Pixels per second." },
|
|
628
|
+
{ name: "PauseOnHover", type: "bool", default: "true", description: "Pause animation on hover." },
|
|
629
|
+
]),
|
|
630
|
+
slots: [
|
|
631
|
+
{ name: "ChildContent", description: "Items to scroll." },
|
|
632
|
+
],
|
|
633
|
+
example: `<Marquee Direction="Marquee.MarqueeDirection.Left" Speed="40" PauseOnHover="true">
|
|
634
|
+
<Badge>React</Badge>
|
|
635
|
+
<Badge>Blazor</Badge>
|
|
636
|
+
<Badge>Vue</Badge>
|
|
637
|
+
<Badge>Svelte</Badge>
|
|
638
|
+
</Marquee>`,
|
|
639
|
+
cssVars: [],
|
|
640
|
+
},
|
|
641
|
+
{
|
|
642
|
+
name: "NumberTicker",
|
|
643
|
+
category: "Motion",
|
|
644
|
+
description: "Animated numeric counter that counts up/down to a value.",
|
|
645
|
+
params: p([
|
|
646
|
+
{ name: "Value", type: "double", default: "0", description: "Target value." },
|
|
647
|
+
{ name: "Duration", type: "int", default: "1500", description: "Animation duration in ms." },
|
|
648
|
+
{ name: "Decimals", type: "int", default: "0", description: "Decimal places to display." },
|
|
649
|
+
{ name: "Prefix", type: "string?", default: "null", description: "Text before the number (e.g. \"$\")." },
|
|
650
|
+
{ name: "Suffix", type: "string?", default: "null", description: "Text after the number (e.g. \"%\")." },
|
|
651
|
+
]),
|
|
652
|
+
slots: [],
|
|
653
|
+
example: `<NumberTicker Value="12450" Duration="2000" Prefix="$" />`,
|
|
654
|
+
cssVars: ["--color-foreground"],
|
|
655
|
+
},
|
|
656
|
+
];
|
|
657
|
+
import { loadRegistry } from "./registry.js";
|
|
658
|
+
function slugify(name) {
|
|
659
|
+
return name
|
|
660
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
|
|
661
|
+
.replace(/([A-Z])([A-Z][a-z])/g, "$1-$2")
|
|
662
|
+
.toLowerCase();
|
|
663
|
+
}
|
|
664
|
+
/** The loaded registry document (may be null if the JSON wasn't synced). */
|
|
665
|
+
export const registry = loadRegistry();
|
|
666
|
+
/**
|
|
667
|
+
* Build the unified catalog:
|
|
668
|
+
* - All 125 components from the registry
|
|
669
|
+
* - Hand-curated rich entries override the thin registry entries for their name
|
|
670
|
+
*/
|
|
671
|
+
function buildCatalog() {
|
|
672
|
+
const curated = new Map(components.map((c) => [c.name.toLowerCase(), c]));
|
|
673
|
+
const fromRegistry = registry?.components ?? [];
|
|
674
|
+
const seen = new Set();
|
|
675
|
+
const out = [];
|
|
676
|
+
for (const r of fromRegistry) {
|
|
677
|
+
const key = r.name.toLowerCase();
|
|
678
|
+
seen.add(key);
|
|
679
|
+
const rich = curated.get(key);
|
|
680
|
+
if (rich) {
|
|
681
|
+
out.push({ ...rich, slug: r.slug, thin: false });
|
|
682
|
+
}
|
|
683
|
+
else {
|
|
684
|
+
out.push({
|
|
685
|
+
name: r.name,
|
|
686
|
+
category: r.category,
|
|
687
|
+
description: r.description,
|
|
688
|
+
files: r.files,
|
|
689
|
+
dependencies: r.dependencies,
|
|
690
|
+
cssVars: r.cssVars,
|
|
691
|
+
registryUrl: r.registryUrl,
|
|
692
|
+
slug: r.slug,
|
|
693
|
+
thin: true,
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
// Include any curated entries that aren't in the registry (defensive —
|
|
698
|
+
// shouldn't happen if the registry is current, but keeps us safe).
|
|
699
|
+
for (const c of components) {
|
|
700
|
+
if (seen.has(c.name.toLowerCase()))
|
|
701
|
+
continue;
|
|
702
|
+
out.push({ ...c, slug: slugify(c.name), thin: false });
|
|
703
|
+
}
|
|
704
|
+
// Stable alphabetical sort by name for predictable listings.
|
|
705
|
+
out.sort((a, b) => a.name.localeCompare(b.name));
|
|
706
|
+
return out;
|
|
707
|
+
}
|
|
708
|
+
export const catalog = buildCatalog();
|
|
709
|
+
export const CATEGORIES = Array.from(new Set(catalog.map((c) => c.category))).sort();
|